Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F256881
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
130 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/plugins/kolab_files/kolab_files.js b/plugins/kolab_files/kolab_files.js
index a525c395..898a6828 100644
--- a/plugins/kolab_files/kolab_files.js
+++ b/plugins/kolab_files/kolab_files.js
@@ -1,1730 +1,1935 @@
/**
* Kolab files plugin
*
* @author Aleksander Machniak <machniak@kolabsys.com>
*
* @licstart The following is the entire license notice for the
* JavaScript code in this file.
*
* Copyright (C) 2011, Kolab Systems AG <contact@kolabsys.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @licend The above is the entire license notice
* for the JavaScript code in this file.
*/
window.rcmail && rcmail.addEventListener('init', function() {
if (rcmail.task == 'mail') {
// mail compose
if (rcmail.env.action == 'compose') {
var elem = $('#compose-attachments > div'),
input = $('<input class="button" type="button">')
.attr('tabindex', $('input', elem).attr('tabindex') || 0)
.val(rcmail.gettext('kolab_files.fromcloud'))
.click(function() { kolab_files_selector_dialog(); })
.appendTo(elem);
if (rcmail.gui_objects.filelist) {
rcmail.file_list = new rcube_list_widget(rcmail.gui_objects.filelist, {
multiselect: true,
keyboard: true,
column_movable: false,
dblclick_time: rcmail.dblclick_time
});
rcmail.file_list.addEventListener('select', function(o) { kolab_files_list_select(o); })
.addEventListener('listupdate', function(e) { rcmail.triggerEvent('listupdate', e); });
rcmail.gui_objects.filelist.parentNode.onmousedown = function(e){ return kolab_files_click_on_list(e); };
rcmail.enable_command('files-sort', 'files-search', 'files-search-reset', true);
rcmail.file_list.init();
kolab_files_list_coltypes();
}
// register some commands to skip warning message on compose page
$.merge(rcmail.env.compose_commands, ['files-list', 'files-sort', 'files-search', 'files-search-reset']);
}
// mail preview
else if (rcmail.env.action == 'show' || rcmail.env.action == 'preview') {
var attachment_list = $('#attachment-list');
if ($('li', attachment_list).length) {
var link = $('<a href="#" class="button filesaveall">')
.text(rcmail.gettext('kolab_files.saveall'))
.click(function() { kolab_directory_selector_dialog(); })
.insertAfter(attachment_list);
}
rcmail.addEventListener('menu-open', kolab_files_attach_menu_open);
rcmail.enable_command('folder-create', true);
}
kolab_files_init();
}
else if (rcmail.task == 'files') {
if (rcmail.gui_objects.filelist) {
rcmail.file_list = new rcube_list_widget(rcmail.gui_objects.filelist, {
multiselect: true,
draggable: true,
keyboard: true,
column_movable: rcmail.env.col_movable,
dblclick_time: rcmail.dblclick_time
});
/*
rcmail.file_list.row_init = function(o){ kolab_files_init_file_row(o); };
rcmail.file_list.addEventListener('dblclick', function(o){ p.msglist_dbl_click(o); });
rcmail.file_list.addEventListener('click', function(o){ p.msglist_click(o); });
rcmail.file_list.addEventListener('keypress', function(o){ p.msglist_keypress(o); });
rcmail.file_list.addEventListener('dragstart', function(o){ p.drag_start(o); });
rcmail.file_list.addEventListener('dragmove', function(e){ p.drag_move(e); });
*/
rcmail.file_list.addEventListener('dblclick', function(o) { kolab_files_list_dblclick(o); })
.addEventListener('select', function(o) { kolab_files_list_select(o); })
.addEventListener('keypress', function(o) { kolab_files_list_keypress(o); })
.addEventListener('dragend', function(e) { kolab_files_drag_end(e); })
.addEventListener('column_replace', function(e) { kolab_files_set_coltypes(e); })
.addEventListener('listupdate', function(e) { rcmail.triggerEvent('listupdate', e); });
rcmail.gui_objects.filelist.parentNode.onmousedown = function(e) { return kolab_files_click_on_list(e); };
rcmail.enable_command('menu-open', 'menu-save', 'files-sort', 'files-search', 'files-search-reset', 'folder-create', true);
rcmail.file_list.init();
kolab_files_list_coltypes();
}
// "one file only" commands
rcmail.env.file_commands = ['files-get'];
// "one or more file" commands
rcmail.env.file_commands_all = ['files-delete', 'files-move', 'files-copy'];
kolab_files_init();
if (rcmail.env.action == 'open') {
rcmail.enable_command('files-get', 'files-delete', rcmail.env.file);
}
else {
file_api.folder_list();
file_api.browser_capabilities_check();
+ rcmail.enable_command('folder-mount', rcmail.env.external_sources);
}
}
});
/**********************************************************/
/********* Shared functionality **********/
/**********************************************************/
// Initializes API object
function kolab_files_init()
{
if (window.file_api)
return;
// Initialize application object (don't change var name!)
file_api = $.extend(new files_api(), new kolab_files_ui());
file_api.set_env({
token: kolab_files_token(),
url: rcmail.env.files_url,
sort_col: 'name',
sort_reverse: false,
search_threads: rcmail.env.search_threads,
resources_dir: 'program/resources',
supported_mimetypes: rcmail.env.file_mimetypes
});
file_api.translations = rcmail.labels;
};
// returns API authorization token
function kolab_files_token()
{
// consider the token from parent window more reliable (fresher) than in framed window
// it's because keep-alive is not requested in frames
return window.parent && parent.rcmail && parent.rcmail.env.files_token ? parent.rcmail.env.files_token : rcmail.env.files_token;
};
// folder selection dialog
function kolab_directory_selector_dialog(id)
{
var dialog = $('#files-dialog'),
input = $('#file-save-as-input'),
form = $('#file-save-as'),
list = $('#folderlistbox'),
buttons = {}, label = 'saveto',
win = window, fn;
// attachment is specified
if (id) {
var attach = $('#attach' + id + '> a').first(),
filename = attach.attr('title');
if (!filename) {
attach = attach.clone();
$('.attachment-size', attach).remove();
filename = attach.text();
}
form.show();
dialog.addClass('saveas');
input.val(filename);
}
// attachment preview page
else if (rcmail.env.action == 'get') {
id = rcmail.env.part;
form.show();
dialog.addClass('saveas');
input.val(rcmail.env.filename);
}
else {
form.hide();
dialog.removeClass('saveas');
label = 'saveall';
}
$('#foldercreatelink').attr('tabindex', 0);
buttons[rcmail.gettext('kolab_files.save')] = function () {
var lock = rcmail.set_busy(true, 'saving'),
request = {
act: 'save-file',
source: rcmail.env.mailbox,
uid: rcmail.env.uid,
dest: file_api.env.folder
};
if (id) {
request.id = id;
request.name = input.val();
}
rcmail.http_post('plugin.kolab_files', request, lock);
kolab_dialog_close(this);
};
buttons[rcmail.gettext('kolab_files.cancel')] = function () {
kolab_dialog_close(this);
};
if (!rcmail.env.folders_loaded) {
fn = function() {
file_api.folder_list();
rcmail.env.folders_loaded = true;
};
}
// show dialog window
kolab_dialog_show(dialog, {
title: rcmail.gettext('kolab_files.' + label),
buttons: buttons,
minWidth: 250,
minHeight: 300,
height: 350,
width: 300
}, fn);
// "enable" folder creation when dialog is displayed in parent window
if (rcmail.is_framed() && !parent.rcmail.folder_create) {
parent.rcmail.enable_command('folder-create', true);
parent.rcmail.folder_create = function() {
win.kolab_files_folder_create_dialog();
};
}
};
// file selection dialog
function kolab_files_selector_dialog()
{
var dialog = $('#files-compose-dialog'), buttons = {};
buttons[rcmail.gettext('kolab_files.attachsel')] = function () {
var list = [];
$('#filelist tr.selected').each(function() {
list.push($(this).data('file'));
});
kolab_dialog_close(this);
if (list.length) {
// display upload indicator and cancel button
var content = '<span>' + rcmail.get_label('kolab_files.attaching') + '</span>',
id = new Date().getTime();
rcmail.add2attachment_list(id, {name:'', html:content, classname:'uploading', complete:false});
// send request
rcmail.http_post('plugin.kolab_files', {
act: 'attach-file',
files: list,
id: rcmail.env.compose_id,
uploadid: id
});
}
};
buttons[rcmail.gettext('kolab_files.cancel')] = function () {
kolab_dialog_close(this);
};
// show dialog window
kolab_dialog_show(dialog, {
title: rcmail.gettext('kolab_files.selectfiles'),
buttons: buttons,
minWidth: 500,
minHeight: 300,
width: 700,
height: 500
});
if (!rcmail.env.files_loaded) {
file_api.folder_list();
rcmail.env.files_loaded = true;
}
else {
rcmail.file_list.clear_selection();
}
};
function kolab_files_attach_menu_open(p)
{
if (!p || !p.props || p.props.menu != 'attachmentmenu')
return;
var id = p.props.id;
$('#attachmenusaveas').unbind('click').attr('onclick', '').click(function(e) {
return kolab_directory_selector_dialog(id);
});
};
// folder creation dialog
function kolab_files_folder_create_dialog()
{
var dialog = $('#files-folder-create-dialog'),
buttons = {},
select = $('select[name="parent"]', dialog).html(''),
input = $('input[name="name"]', dialog).val('');
buttons[rcmail.gettext('kolab_files.create')] = function () {
var folder = '', name = input.val(), parent = select.val();
if (!name)
return;
if (parent)
folder = parent + file_api.env.directory_separator;
folder += name;
file_api.folder_create(folder);
kolab_dialog_close(this);
};
buttons[rcmail.gettext('kolab_files.cancel')] = function () {
kolab_dialog_close(this);
};
// show dialog window
kolab_dialog_show(dialog, {
title: rcmail.gettext('kolab_files.foldercreate'),
- buttons: buttons,
- minWidth: 400,
- minHeight: 300,
- width: 500,
- height: 400
+ buttons: buttons
});
// Fix submitting form with Enter
$('form', dialog).submit(kolab_dialog_submit_handler);
// build parent selector
select.append($('<option>').val('').text('---'));
$.each(file_api.env.folders, function(i, f) {
var n, option = $('<option>'), name = escapeHTML(f.name);
for (n=0; n<f.depth; n++)
name = ' ' + name;
option.val(i).html(name).appendTo(select);
if (i == file_api.env.folder)
option.attr('selected', true);
});
};
+// folder mounting dialog
+function kolab_files_folder_mount_dialog()
+{
+ var args = {buttons: {}, title: rcmail.gettext('kolab_files.foldermount')},
+ dialog = $('#files-folder-mount-dialog'),
+ input = $('#folder-mount-name').val('');
+
+ args.buttons[rcmail.gettext('kolab_files.save')] = function () {
+ var args = {}, folder = input.val(),
+ driver = $('input[name="driver"]:checked', dialog).val();
+
+ if (!folder || !driver)
+ return;
+
+ args.folder = folder;
+ args.driver = driver;
+
+ $('#source-' + driver + ' input').each(function() {
+ if (this.name.startsWith(driver + '[')) {
+ args[this.name.substring(driver.length + 1, this.name.length - 1)] = this.value;
+ }
+ });
+
+ file_api.folder_mount(args);
+ kolab_dialog_close(this);
+ };
+
+ args.buttons[rcmail.gettext('kolab_files.cancel')] = function () {
+ kolab_dialog_close(this);
+ };
+
+ // close folderoption menu
+ rcmail.hide_menu('folderoptions');
+
+ // initialize drivers list
+ if (!rcmail.drivers_list_initialized) {
+ rcmail.drivers_list_initialized = true;
+
+ $('td.source', dialog).each(function() {
+ $(this).click(function() {
+ $('td.selected', dialog).removeClass('selected');
+ dialog.find('.driverform').hide();
+ $(this).addClass('selected').find('.driverform').show();
+ $('input[type="radio"]', this).prop('checked', true);
+ });
+ });
+ }
+
+ // show dialog window
+ kolab_dialog_show(dialog, args, function() {
+ $('td.source:first', dialog).click();
+ input.focus();
+ });
+};
+
// file edition dialog
function kolab_files_file_edit_dialog(file)
{
var dialog = $('#files-file-edit-dialog'),
buttons = {}, name = file_api.file_name(file)
input = $('input[name="name"]', dialog).val(name);
buttons[rcmail.gettext('kolab_files.save')] = function () {
var folder = file_api.file_path(file), name = input.val();
if (!name)
return;
name = folder + file_api.env.directory_separator + name;
// @TODO: now we only update filename
if (name != file)
file_api.file_rename(file, name);
kolab_dialog_close(this);
};
buttons[rcmail.gettext('kolab_files.cancel')] = function () {
kolab_dialog_close(this);
};
// Fix submitting form with Enter
$('form', dialog).submit(kolab_dialog_submit_handler);
// show dialog window
kolab_dialog_show(dialog, {
title: rcmail.gettext('kolab_files.fileedit'),
buttons: buttons
});
};
function kolab_dialog_show(content, params, onopen)
{
params = $.extend({
modal: true,
resizable: true,
closeOnEscape: (!bw.ie6 && !bw.ie7), // disabled for performance reasons
minWidth: 400,
minHeight: 300,
width: 500,
height: 400
}, params || {});
// dialog close handler
params.close = function(e, ui) {
var elem, stack = rcmail.dialog_stack;
content.appendTo(document.body).hide();
$(this).parent().remove(); // remove dialog
// focus previously focused element (guessed)
stack.pop();
if (stack.length) {
elem = stack[stack.length-1].find('input[type!="hidden"]:not(:hidden):first');
if (!elem.length)
elem = stack[stack.length-1].parent().find('a[role="button"], .ui-dialog-buttonpane button').first();
}
(elem && elem.length ? elem : window).focus();
};
// display it as popup
var dialog = rcmail.show_popup_dialog('', params.title, params.buttons, params);
content.appendTo(dialog).show().find('input[type!="hidden"]:not(:hidden):first').focus();
if (onopen) onopen(content);
// save dialog reference, to handle focus when closing one of opened dialogs
if (!rcmail.dialog_stack)
rcmail.dialog_stack = [];
rcmail.dialog_stack.push(dialog);
};
// Handle form submit with Enter key, click first dialog button instead
function kolab_dialog_submit_handler()
{
$(this).parents('.ui-dialog').find('.ui-button').first().click();
return false;
};
// Hides dialog
function kolab_dialog_close(dialog)
{
(rcmail.is_framed() ? window.parent : window).$(dialog).dialog('close');
};
// smart upload button
function kolab_files_upload_input(button)
{
var link = $(button),
file = $('<input>'),
offset = link.offset();
function move_file_input(e) {
file.css({top: (e.pageY - offset.top - 10) + 'px', left: (e.pageX - offset.left - 10) + 'px'});
}
file.attr({name: 'file[]', type: 'file', multiple: 'multiple', size: 5, title: link.attr('title'), tabindex: "-1"})
.change(function() { rcmail.files_upload('#filesuploadform'); })
.click(function() { setTimeout(function() { link.mouseleave(); }, 20); })
// opacity:0 does the trick, display/visibility doesn't work
.css({opacity: 0, cursor: 'pointer', outline: 'none', position: 'absolute'});
// In FF and IE we need to move the browser file-input's button under the cursor
// Thanks to the size attribute above we know the length of the input field
if (bw.mz || bw.ie)
file.css({marginLeft: '-80px'});
// Note: now, I observe problem with cursor style on FF < 4 only
// Need position: relative (Bug #2615)
link.css({overflow: 'hidden', cursor: 'pointer', position: 'relative'})
.mouseenter(function() { this.__active = true; })
// place button under the cursor
.mousemove(function(e) {
if (rcmail.commands['files-upload'] && this.__active)
move_file_input(e);
// move the input away if button is disabled
else
$(this).mouseleave();
})
.mouseleave(function() {
file.css({top: '-10000px', left: '-10000px'});
this.__active = false;
})
.attr('onclick', '') // remove default button action
.click(function(e) {
// forward click if mouse-enter event was missed
if (rcmail.commands['files-upload'] && !this.__active) {
this.__active = true;
move_file_input(e);
file.trigger(e);
}
})
.mouseleave() // initially disable/hide input
.append(file);
};
/***********************************************************/
/********** Main functionality **********/
/***********************************************************/
// for reordering column array (Konqueror workaround)
// and for setting some message list global variables
kolab_files_list_coltypes = function()
{
var n, list = rcmail.file_list;
rcmail.env.subject_col = null;
if ((n = $.inArray('name', rcmail.env.coltypes)) >= 0) {
rcmail.env.subject_col = n;
list.subject_col = n;
}
list.init_header();
};
kolab_files_set_list_options = function(cols, sort_col, sort_order)
{
var update = 0, i, idx, name, newcols = [], oldcols = rcmail.env.coltypes;
if (sort_col === undefined)
sort_col = rcmail.env.sort_col;
if (!sort_order)
sort_order = rcmail.env.sort_order;
if (rcmail.env.sort_col != sort_col || rcmail.env.sort_order != sort_order) {
update = 1;
rcmail.set_list_sorting(sort_col, sort_order);
}
if (cols && cols.length) {
// make sure new columns are added at the end of the list
for (i=0; i<oldcols.length; i++) {
name = oldcols[i];
idx = $.inArray(name, cols);
if (idx != -1) {
newcols.push(name);
delete cols[idx];
}
}
for (i=0; i<cols.length; i++)
if (cols[i])
newcols.push(cols[i]);
if (newcols.join() != oldcols.join()) {
update += 2;
oldcols = newcols;
}
}
if (update == 1)
rcmail.command('files-list', {sort: sort_col, reverse: sort_order == 'DESC'});
else if (update) {
rcmail.http_post('files/prefs', {
kolab_files_list_cols: oldcols,
kolab_files_sort_col: sort_col,
kolab_files_sort_order: sort_order
}, rcmail.set_busy(true, 'loading'));
}
};
kolab_files_set_coltypes = function(list)
{
var i, found, name, cols = list.list.tHead.rows[0].cells;
rcmail.env.coltypes = [];
for (i=0; i<cols.length; i++)
if (cols[i].id && cols[i].id.match(/^rcm/)) {
name = cols[i].id.replace(/^rcm/, '');
rcmail.env.coltypes.push(name);
}
// if ((found = $.inArray('name', rcmail.env.coltypes)) >= 0)
// rcmail.env.subject_col = found;
rcmail.env.subject_col = list.subject_col;
rcmail.http_post('files/prefs', {kolab_files_list_cols: rcmail.env.coltypes});
};
kolab_files_click_on_list = function(e)
{
if (rcmail.gui_objects.qsearchbox)
rcmail.gui_objects.qsearchbox.blur();
if (rcmail.file_list)
rcmail.file_list.focus();
return true;
};
kolab_files_list_dblclick = function(list)
{
rcmail.command('files-open');
};
kolab_files_list_select = function(list)
{
var selected = list.selection.length;
rcmail.enable_command(rcmail.env.file_commands_all, selected);
rcmail.enable_command(rcmail.env.file_commands, selected == 1);
// reset all-pages-selection
// if (list.selection.length && list.selection.length != list.rowcount)
// rcmail.select_all_mode = false;
// enable files-
if (selected == 1) {
// get file mimetype
var type = $('tr.selected', list.list).data('type');
rcmail.env.viewer = file_api.file_type_supported(type);
}
else
rcmail.env.viewer = 0;
/*
) {
// caps = this.browser_capabilities().join();
href = '?' + $.param({_task: 'files', _action: 'open', file: file, viewer: viewer == 2 ? 1 : 0});
var win = window.open(href, rcmail.html_identifier('rcubefile'+file));
if (win)
setTimeout(function() { win.focus(); }, 10);
}
*/
rcmail.enable_command('files-open', rcmail.env.viewer);
};
kolab_files_list_keypress = function(list)
{
if (list.modkey == CONTROL_KEY)
return;
if (list.key_pressed == list.ENTER_KEY)
rcmail.command('files-open');
else if (list.key_pressed == list.DELETE_KEY || list.key_pressed == list.BACKSPACE_KEY)
rcmail.command('files-delete');
};
kolab_files_drag_end = function(e)
{
var folder = $('#files-folder-list li.droptarget').removeClass('droptarget');
if (folder.length) {
folder = folder.data('folder');
var modkey = rcube_event.get_modifier(e),
menu = rcmail.gui_objects.file_dragmenu;
if (menu && modkey == SHIFT_KEY && rcmail.commands['files-copy']) {
var pos = rcube_event.get_mouse_pos(e);
rcmail.env.drag_target = folder;
$(menu).css({top: (pos.y-10)+'px', left: (pos.x-10)+'px'}).show();
return;
}
rcmail.command('files-move', folder);
}
};
kolab_files_drag_menu_action = function(command)
{
var menu = rcmail.gui_objects.file_dragmenu;
if (menu)
$(menu).hide();
rcmail.command(command, rcmail.env.drag_target);
};
kolab_files_selected = function()
{
var files = [];
$.each(rcmail.file_list.get_selection(), function(i, v) {
var name, row = $('#rcmrow'+v);
if (row.length == 1 && (name = row.data('file')))
files.push(name);
});
return files;
};
kolab_files_frame_load = function(frame)
{
var win = frame.contentWindow;
rcmail.file_editor = win.file_editor && win.file_editor.editable ? win.file_editor : null;
if (rcmail.file_editor)
rcmail.enable_command('files-edit', true);
rcmail.enable_command('files-print', (rcmail.file_editor && rcmail.file_editor.printable) ||
(rcmail.env.file_data && /^image\//i.test(rcmail.env.file_data.type)));
// detect Print button and check if it can be accessed
try {
if ($('#fileframe').contents().find('#print').length)
rcmail.enable_command('files-print', true);
}
catch(e) {};
};
/***********************************************************/
/********** Commands **********/
/***********************************************************/
rcube_webmail.prototype.files_sort = function(props)
{
var params = {},
sort_order = this.env.sort_order,
sort_col = !this.env.disabled_sort_col ? props : this.env.sort_col;
if (!this.env.disabled_sort_order)
sort_order = this.env.sort_col == sort_col && sort_order == 'ASC' ? 'DESC' : 'ASC';
// set table header and update env
this.set_list_sorting(sort_col, sort_order);
this.http_post('files/prefs', {kolab_files_sort_col: sort_col, kolab_files_sort_order: sort_order});
params.sort = sort_col;
params.reverse = sort_order == 'DESC';
this.command('files-list', params);
};
rcube_webmail.prototype.files_search = function()
{
var value = $(this.gui_objects.filesearchbox).val();
if (value)
file_api.file_search(value, $('#search_all_folders').is(':checked'));
else
file_api.file_search_reset();
};
rcube_webmail.prototype.files_search_reset = function()
{
$(this.gui_objects.filesearchbox).val('');
file_api.file_search_reset();
};
rcube_webmail.prototype.files_folder_delete = function()
{
if (confirm(this.get_label('kolab_files.folderdeleteconfirm')))
file_api.folder_delete(file_api.env.folder);
};
rcube_webmail.prototype.files_delete = function()
{
if (!confirm(this.get_label('kolab_files.filedeleteconfirm')))
return;
var files = this.env.file ? [this.env.file] : kolab_files_selected();
file_api.file_delete(files);
};
rcube_webmail.prototype.files_move = function(folder)
{
var files = kolab_files_selected();
file_api.file_move(files, folder);
};
rcube_webmail.prototype.files_copy = function(folder)
{
var files = kolab_files_selected();
file_api.file_copy(files, folder);
};
rcube_webmail.prototype.files_upload = function(form)
{
if (form)
file_api.file_upload(form);
};
rcube_webmail.prototype.files_list = function(param)
{
// just rcmail wrapper, to handle command busy states
file_api.file_list(param);
}
rcube_webmail.prototype.files_list_update = function(head)
{
var list = this.file_list;
list.clear();
$('thead', list.fixed_header ? list.fixed_header : list.list).html(head);
kolab_files_list_coltypes();
file_api.file_list();
};
rcube_webmail.prototype.files_get = function()
{
var files = this.env.file ? [this.env.file] : kolab_files_selected();
if (files.length == 1)
file_api.file_get(files[0], {'force-download': true});
};
rcube_webmail.prototype.files_open = function()
{
var files = kolab_files_selected();
if (files.length == 1)
file_api.file_open(files[0], rcmail.env.viewer);
};
// enable file editor
rcube_webmail.prototype.files_edit = function()
{
if (this.file_editor) {
this.file_editor.enable();
this.enable_command('files-save', true);
}
};
rcube_webmail.prototype.files_save = function()
{
if (!this.file_editor)
return;
// binary files like ODF need to be updated using FormData
if (this.file_editor.getContentCallback) {
if (!file_api.file_uploader_support())
return;
file_api.req = file_api.set_busy(true, 'saving');
// this.file_editor.disable();
this.file_editor.getContentCallback(function(content, filename) {
file_api.file_uploader([content], {
action: 'file_update',
params: {file: rcmail.env.file, info: 1, token: file_api.env.token},
response_handler: 'file_save_response',
fieldname: 'content',
single: true
});
});
return;
}
var content = this.file_editor.getContent();
file_api.file_save(this.env.file, content);
};
rcube_webmail.prototype.files_print = function()
{
if (this.file_editor && this.file_editor.printable)
this.file_editor.print();
else if (/^image\//i.test(this.env.file_data.type)) {
var frame = $('#fileframe').get(0),
win = frame ? frame.contentWindow : null;
if (win) {
win.focus();
win.print();
}
}
else {
// e.g. Print button in PDF viewer
try {
$('#fileframe').contents().find('#print').click();
}
catch(e) {};
}
};
rcube_webmail.prototype.files_set_quota = function(p)
{
if (p.total) {
p.used *= 1024;
p.total *= 1024;
p.title = file_api.file_size(p.used) + ' / ' + file_api.file_size(p.total)
+ ' (' + p.percent + '%)';
}
p.type = this.env.quota_type;
this.set_quota(p);
};
rcube_webmail.prototype.folder_create = function()
{
kolab_files_folder_create_dialog();
};
+rcube_webmail.prototype.folder_mount = function()
+{
+ kolab_files_folder_mount_dialog();
+};
+
/**********************************************************/
/********* Files API handler **********/
/**********************************************************/
function kolab_files_ui()
{
this.requests = {};
/*
// Called on "session expired" session
this.logout = function(response) {};
// called when a request timed out
this.request_timed_out = function() {};
// called on start of the request
this.set_request_time = function() {};
// called on request response
this.update_request_time = function() {};
*/
// set state
this.set_busy = function(a, message)
{
if (this.req)
rcmail.hide_message(this.req);
return rcmail.set_busy(a, message);
};
// displays error message
this.display_message = function(label, type)
{
return rcmail.display_message(this.t(label), type);
};
this.http_error = function(request, status, err)
{
rcmail.http_error(request, status, err);
};
// folders list request
this.folder_list = function()
{
this.req = this.set_busy(true, 'loading');
this.request('folder_list', {}, 'folder_list_response');
};
// folder list response handler
this.folder_list_response = function(response)
{
if (!this.response(response))
return;
var first, elem = $('#files-folder-list'),
list = $('<ul class="listing"></ul>'),
collections = !rcmail.env.action.match(/^(preview|show)$/) ? ['audio', 'video', 'image', 'document'] : [];
// try parent window if the list element does not exist
// i.e. called from dialog in parent window
if (!elem.length && window.parent && parent.rcmail) {
elem = $('#files-folder-list', window.parent.document.body);
}
elem.html('').append(list);
- this.env.folders = this.folder_list_parse(response.result);
+ this.env.folders = this.folder_list_parse(response.result ? response.result.list : []);
$.each(this.env.folders, function(i, f) {
- var row = $('<li class="mailbox"><span class="branch"></span></li>');
-
- row.attr('id', f.id).data('folder', i)
- .append($('<span class="name"></span>').text(f.name));
-
- if (f.depth) {
- $('span.branch', row).width(15 * f.depth);
- row.addClass('child');
- }
-
- if (f.virtual)
- row.addClass('virtual');
- else
- row.attr('tabindex', 0)
- .keypress(function(e) { if (e.which == 13 || e.which == 32) file_api.folder_select(i); })
- .click(function() { file_api.folder_select(i); })
- .mouseenter(function() {
- if (rcmail.file_list && rcmail.file_list.drag_active && !$(this).hasClass('selected'))
- $(this).addClass('droptarget');
- })
- .mouseleave(function() {
- if (rcmail.file_list && rcmail.file_list.drag_active)
- $(this).removeClass('droptarget');
- });
-
- list.append(row);
-
+ list.append(file_api.folder_list_row(i, f));
if (!first)
first = i;
});
// add virtual collections
$.each(collections, function(i, n) {
var row = $('<li class="mailbox collection ' + n + '"></li>');
row.attr({id: 'folder-collection-' + n, tabindex: 0})
.append($('<span class="name"></span>').text(rcmail.gettext('kolab_files.collection_' + n)))
.click(function() { file_api.folder_select(n, true); });
list.append(row);
});
- // select first folder?
- if (this.env.folder)
- this.folder_select(this.env.folder);
- else if (this.env.collection)
- this.folder_select(this.env.collection, true);
- else if (first)
- this.folder_select(first);
+ // select first folder?
+ if (this.env.folder)
+ this.folder_select(this.env.folder);
+ else if (this.env.collection)
+ this.folder_select(this.env.collection, true);
+ else if (first)
+ this.folder_select(first);
// add tree icons
this.folder_list_tree(this.env.folders);
+
+ // handle authentication errors on external sources
+ this.folder_list_auth_errors(response.result);
};
this.folder_select = function(folder, is_collection)
{
if (rcmail.busy)
return;
var list = $('#files-folder-list > ul');
// try parent window if the list element does not exist
// i.e. called from dialog in parent window
if (!list.length && window.parent && parent.rcmail) {
list = $('#files-folder-list > ul', window.parent.document.body);
}
$('li.selected', list).removeClass('selected');
rcmail.enable_command('files-list', true);
if (is_collection) {
var found = $('#folder-collection-' + folder, list).addClass('selected');
rcmail.enable_command('files-folder-delete', 'files-upload', false);
this.env.folder = null;
rcmail.command('files-list', {collection: folder});
}
else {
var found = $('#' + this.env.folders[folder].id, list).addClass('selected');
rcmail.enable_command('files-folder-delete', 'files-upload', true);
this.env.folder = folder;
this.env.collection = null;
rcmail.command('files-list', {folder: folder});
}
this.quota();
};
this.folder_unselect = function()
{
var list = $('#files-folder-list > ul');
$('li.selected', list).removeClass('selected');
rcmail.enable_command('files-folder-delete', 'files-upload', false);
this.env.folder = null;
this.env.collection = null;
};
+ this.folder_list_row = function(i, folder)
+ {
+ var row = $('<li class="mailbox"><span class="branch"></span></li>');
+
+ row.attr('id', folder.id).data('folder', i)
+ .append($('<span class="name"></span>').text(folder.name));
+
+ if (folder.depth) {
+ $('span.branch', row).width(15 * folder.depth);
+ row.addClass('child');
+ }
+
+ if (folder.virtual)
+ row.addClass('virtual');
+ else
+ row.attr('tabindex', 0)
+ .keypress(function(e) { if (e.which == 13 || e.which == 32) file_api.folder_select(i); })
+ .click(function() { file_api.folder_select(i); })
+ .mouseenter(function() {
+ if (rcmail.file_list && rcmail.file_list.drag_active && !$(this).hasClass('selected'))
+ $(this).addClass('droptarget');
+ })
+ .mouseleave(function() {
+ if (rcmail.file_list && rcmail.file_list.drag_active)
+ $(this).removeClass('droptarget');
+ });
+
+ return row;
+ };
+
// folder create request
this.folder_create = function(folder)
{
this.req = this.set_busy(true, 'kolab_files.foldercreating');
this.request('folder_create', {folder: folder}, 'folder_create_response');
};
// folder create response handler
this.folder_create_response = function(response)
{
if (!this.response(response))
return;
this.display_message('kolab_files.foldercreatenotice', 'confirmation');
// refresh folders list
this.folder_list();
};
+ // folder mount (external storage) request
+ this.folder_mount = function(data)
+ {
+ this.req = this.set_busy(true, 'kolab_files.foldermounting');
+ this.request('folder_create', data, 'folder_mount_response');
+ };
+
+ // folder create response handler
+ this.folder_mount_response = function(response)
+ {
+ if (!this.response(response))
+ return;
+
+ this.display_message('kolab_files.foldermountnotice', 'confirmation');
+
+ // refresh folders list
+ this.folder_list();
+ };
+
// folder delete request
this.folder_delete = function(folder)
{
this.req = this.set_busy(true, 'kolab_files.folderdeleting');
this.request('folder_delete', {folder: folder}, 'folder_delete_response');
};
// folder delete response handler
this.folder_delete_response = function(response)
{
if (!this.response(response))
return;
this.env.folder = null;
rcmail.enable_command('files-folder-delete', 'files-folder-rename', 'files-list', false);
this.display_message('kolab_files.folderdeletenotice', 'confirmation');
// refresh folders list
this.folder_list();
this.quota();
};
// quota request
this.quota = function()
{
if (rcmail.env.files_quota)
this.request('quota', {folder: this.env.folder}, 'quota_response');
};
// quota response handler
this.quota_response = function(response)
{
if (!this.response(response))
return;
rcmail.files_set_quota(response.result);
};
this.file_list = function(params)
{
if (!rcmail.gui_objects.filelist)
return;
if (!params)
params = {};
// reset all pending list requests
for (i in this.requests) {
this.requests[i].abort();
rcmail.hide_message(i);
delete this.requests[i];
}
if (params.all_folders) {
params.collection = null;
params.folder = null;
this.folder_unselect();
}
if (params.collection == undefined)
params.collection = this.env.collection;
if (params.folder == undefined)
params.folder = this.env.folder;
if (params.sort == undefined)
params.sort = this.env.sort_col;
if (params.reverse == undefined)
params.reverse = this.env.sort_reverse;
if (params.search == undefined)
params.search = this.env.search;
this.env.folder = params.folder;
this.env.collection = params.collection;
this.env.sort_col = params.sort;
this.env.sort_reverse = params.reverse;
rcmail.enable_command(rcmail.env.file_commands, false);
rcmail.enable_command(rcmail.env.file_commands_all, false);
// empty the list
this.env.file_list = [];
rcmail.file_list.clear(true);
// request
if (params.collection || params.all_folders)
this.file_list_loop(params);
else if (this.env.folder) {
params.req_id = this.set_busy(true, 'loading');
this.requests[params.req_id] = this.request('file_list', params, 'file_list_response');
}
};
// file list response handler
this.file_list_response = function(response)
{
if (response.req_id)
rcmail.hide_message(response.req_id);
if (!this.response(response))
return;
var i = 0, list = [], table = $('#filelist');
$.each(response.result, function(key, data) {
var row = file_api.file_list_row(key, data, ++i);
rcmail.file_list.insert_row(row);
data.row = row;
data.filename = key;
list.push(data);
});
this.env.file_list = list;
rcmail.file_list.resize();
};
// call file_list request for every folder (used for search and virt. collections)
this.file_list_loop = function(params)
{
var i, folders = [], limit = Math.max(this.env.search_threads || 1, 1);
if (params.collection) {
if (!params.search)
params.search = {};
params.search['class'] = params.collection;
delete params['collection'];
}
delete params['all_folders'];
$.each(this.env.folders, function(i, f) {
if (!f.virtual)
folders.push(i);
});
this.env.folders_loop = folders;
this.env.folders_loop_params = params;
this.env.folders_loop_lock = false;
for (i=0; i<folders.length && i<limit; i++) {
params.req_id = this.set_busy(true, 'loading');
params.folder = folders.shift();
this.requests[params.req_id] = this.request('file_list', params, 'file_list_loop_response');
}
};
// file list response handler for loop'ed request
this.file_list_loop_response = function(response)
{
var i, folders = this.env.folders_loop,
params = this.env.folders_loop_params,
limit = Math.max(this.env.search_threads || 1, 1),
valid = this.response(response);
if (response.req_id)
rcmail.hide_message(response.req_id);
for (i=0; i<folders.length && i<limit; i++) {
params.req_id = this.set_busy(true, 'loading');
params.folder = folders.shift();
this.requests[params.req_id] = this.request('file_list', params, 'file_list_loop_response');
}
rcmail.file_list.resize();
if (!valid)
return;
this.file_list_loop_result_add(response.result);
};
// add files from list request to the table (with sorting)
this.file_list_loop_result_add = function(result)
{
// chack if result (hash-array) is empty
if (!object_is_empty(result))
return;
if (this.env.folders_loop_lock) {
setTimeout(function() { file_api.file_list_loop_result_add(result); }, 100);
return;
}
// lock table, other list responses will wait
this.env.folders_loop_lock = true;
var n, i, len, elem, list = [], rows = [],
index = this.env.file_list.length,
table = rcmail.file_list;
for (n=0, len=index; n<len; n++) {
elem = this.env.file_list[n];
for (i in result) {
if (this.sort_compare(elem, result[i]) < 0)
break;
var row = this.file_list_row(i, result[i], ++index);
table.insert_row(row, elem.row);
result[i].row = row;
result[i].filename = i;
list.push(result[i]);
delete result[i];
}
list.push(elem);
}
// add the rest of rows
$.each(result, function(key, data) {
var row = file_api.file_list_row(key, data, ++index);
table.insert_row(row);
result[key].row = row;
result[key].filename = key;
list.push(result[key]);
});
this.env.file_list = list;
this.env.folders_loop_lock = false;
};
// sort files list (without API request)
this.file_list_sort = function(col, reverse)
{
var n, len, list = this.env.file_list,
table = $('#filelist'), tbody = $('<tbody>');
this.env.sort_col = col;
this.env.sort_reverse = reverse;
if (!list || !list.length)
return;
// sort the list
list.sort(function (a, b) {
return file_api.sort_compare(a, b);
});
// add rows to the new body
for (n=0, len=list.length; n<len; n++) {
tbody.append(list[n].row);
}
// replace table bodies
$('tbody', table).replaceWith(tbody);
};
this.file_list_row = function(file, data, index)
{
var c, col, row = '';
for (c in rcmail.env.coltypes) {
c = rcmail.env.coltypes[c];
if (c == 'name')
col = '<td class="name filename ' + this.file_type_class(data.type) + '">'
+ '<span>' + escapeHTML(data.name) + '</span></td>';
else if (c == 'mtime')
col = '<td class="mtime">' + data.mtime + '</td>';
else if (c == 'size')
col = '<td class="size">' + this.file_size(data.size) + '</td>';
else if (c == 'options')
col = '<td class="options"><span></span></td>';
else
col = '<td class="' + c + '"></td>';
row += col;
}
row = $('<tr>')
.html(row)
.attr({id: 'rcmrow' + index, 'data-file': file, 'data-type': data.type});
$('td.options > span', row).click(function(e) {
kolab_files_file_edit_dialog(file);
});
// collection (or search) lists files from all folders
// display file name with full path as title
if (!this.env.folder)
$('td.name span', row).attr('title', file);
return row.get(0);
};
this.file_search = function(value, all_folders)
{
if (value) {
this.env.search = {name: value};
rcmail.command('files-list', {search: this.env.search, all_folders: all_folders});
}
else
this.search_reset();
};
this.file_search_reset = function()
{
if (this.env.search) {
this.env.search = null;
rcmail.command('files-list');
}
};
this.file_get = function(file, params)
{
if (!params)
params = {};
params.token = this.env.token;
params.file = file;
rcmail.redirect(this.env.url + this.url('file_get', params));
};
// file(s) delete request
this.file_delete = function(files)
{
this.req = this.set_busy(true, 'kolab_files.filedeleting');
this.request('file_delete', {file: files}, 'file_delete_response');
};
// file(s) delete response handler
this.file_delete_response = function(response)
{
if (!this.response(response))
return;
var rco, dir, self = this;
this.display_message('kolab_files.filedeletenotice', 'confirmation');
if (rcmail.env.file) {
rco = rcmail.opener();
dir = this.file_path(rcmail.env.file);
// check if opener window contains files list, if not we can just close current window
if (rco && rco.file_list && (opener.file_api.env.folder == dir || !opener.file_api.env.folder))
self = opener.file_api;
else
window.close();
}
// @TODO: consider list modification "in-place" instead of full reload
self.file_list();
self.quota();
if (rcmail.env.file)
window.close();
};
// file(s) move request
this.file_move = function(files, folder)
{
if (!files || !files.length || !folder)
return;
var count = 0, list = {};
$.each(files, function(i, v) {
var name = folder + file_api.env.directory_separator + file_api.file_name(v);
if (name != v) {
list[v] = name;
count++;
}
});
if (!count)
return;
this.req = this.set_busy(true, 'kolab_files.filemoving');
this.request('file_move', {file: list}, 'file_move_response');
};
// file(s) move response handler
this.file_move_response = function(response)
{
if (!this.response(response))
return;
if (response.result && response.result.already_exist && response.result.already_exist.length)
this.file_move_ask_user(response.result.already_exist, true);
else {
this.display_message('kolab_files.filemovenotice', 'confirmation');
this.file_list();
}
};
// file(s) copy request
this.file_copy = function(files, folder)
{
if (!files || !files.length || !folder)
return;
var count = 0, list = {};
$.each(files, function(i, v) {
var name = folder + file_api.env.directory_separator + file_api.file_name(v);
if (name != v) {
list[v] = name;
count++;
}
});
if (!count)
return;
this.req = this.set_busy(true, 'kolab_files.filecopying');
this.request('file_copy', {file: list}, 'file_copy_response');
};
// file(s) copy response handler
this.file_copy_response = function(response)
{
if (!this.response(response))
return;
if (response.result && response.result.already_exist && response.result.already_exist.length)
this.file_move_ask_user(response.result.already_exist);
else {
this.display_message('kolab_files.filecopynotice', 'confirmation');
this.quota();
}
};
// when file move/copy operation returns file-exists error
// this displays a dialog where user can decide to skip
// or overwrite destination file(s)
this.file_move_ask_user = function(list, move)
{
var file = list[0], buttons = {},
text = rcmail.gettext('kolab_files.filemoveconfirm').replace('$file', file.dst)
dialog = $('<div></div>');
buttons[rcmail.gettext('kolab_files.fileoverwrite')] = function() {
var file = list.shift(), f = {},
action = move ? 'file_move' : 'file_copy';
f[file.src] = file.dst;
file_api.file_move_ask_list = list;
file_api.file_move_ask_mode = move;
dialog.dialog('destroy').remove();
file_api.req = file_api.set_busy(true, move ? 'kolab_files.filemoving' : 'kolab_files.filecopying');
file_api.request(action, {file: f, overwrite: 1}, 'file_move_ask_user_response');
};
if (list.length > 1)
buttons[rcmail.gettext('kolab_files.fileoverwriteall')] = function() {
var f = {}, action = move ? 'file_move' : 'file_copy';
$.each(list, function() { f[this.src] = this.dst; });
dialog.dialog('destroy').remove();
file_api.req = file_api.set_busy(true, move ? 'kolab_files.filemoving' : 'kolab_files.filecopying');
file_api.request(action, {file: f, overwrite: 1}, action + '_response');
};
var skip_func = function() {
list.shift();
dialog.dialog('destroy').remove();
if (list.length)
file_api.file_move_ask_user(list, move);
else if (move)
file_api.file_list();
};
buttons[rcmail.gettext('kolab_files.fileskip')] = skip_func;
if (list.length > 1)
buttons[rcmail.gettext('kolab_files.fileskipall')] = function() {
dialog.dialog('destroy').remove();
if (move)
file_api.file_list();
};
// open jquery UI dialog
kolab_dialog_show(dialog.html(text), {
close: skip_func,
buttons: buttons,
minWidth: 400,
width: 400
});
};
// file move (with overwrite) response handler
this.file_move_ask_user_response = function(response)
{
var move = this.file_move_ask_mode, list = this.file_move_ask_list;
this.response(response);
if (list && list.length)
this.file_move_ask_user(list, mode);
else {
this.display_message('kolab_files.file' + (move ? 'move' : 'copy') + 'notice', 'confirmation');
if (move)
this.file_list();
}
};
// file(s) rename request
this.file_rename = function(oldfile, newfile)
{
this.req = this.set_busy(true, 'kolab_files.fileupdating');
this.request('file_move', {file: oldfile, 'new': newfile}, 'file_rename_response');
};
// file(s) move response handler
this.file_rename_response = function(response)
{
if (!this.response(response))
return;
// @TODO: we could update metadata instead
this.file_list();
};
// file upload request
this.file_upload = function(form)
{
var i, size = 0, maxsize = rcmail.env.files_max_upload,
form = $(form),
field = $('input[type=file]', form).get(0),
files = field.files ? field.files.length : field.value ? 1 : 0;
if (files) {
// check upload max size
if (field.files && maxsize) {
for (i=0; i < files; i++)
size += field.files[i].size;
if (size > maxsize) {
alert(rcmail.get_label('kolab_files.uploadsizeerror').replace('$size', this.file_size(maxsize)));
return;
}
}
// submit form and read server response
this.file_upload_form(form, 'file_upload', function(event) {
var doc, response;
try {
doc = this.contentDocument ? this.contentDocument : this.contentWindow.document;
response = doc.body.innerHTML;
// response may be wrapped in <pre> tag
if (response.slice(0, 5).toLowerCase() == '<pre>' && response.slice(-6).toLowerCase() == '</pre>') {
response = doc.body.firstChild.firstChild.nodeValue;
}
response = eval('(' + response + ')');
} catch (err) {
response = {status: 'ERROR'};
}
rcmail.hide_message(event.data.ts);
// refresh the list on upload success
if (file_api.response_parse(response))
file_api.file_list();
file_api.quota();
});
}
};
// post the given form to a hidden iframe
this.file_upload_form = function(form, action, onload)
{
var ts = rcmail.display_message(rcmail.get_label('kolab_files.uploading'), 'loading', 1000),
frame_name = 'fileupload'+ts;
/*
// upload progress support
if (this.env.upload_progress_name) {
var fname = this.env.upload_progress_name,
field = $('input[name='+fname+']', form);
if (!field.length) {
field = $('<input>').attr({type: 'hidden', name: fname});
field.prependTo(form);
}
field.val(ts);
}
*/
// have to do it this way for IE
// otherwise the form will be posted to a new window
if (document.all) {
var html = '<iframe id="'+frame_name+'" name="'+frame_name+'"'
+ ' src="program/resources/blank.gif" style="width:0;height:0;visibility:hidden;"></iframe>';
document.body.insertAdjacentHTML('BeforeEnd', html);
}
// for standards-compliant browsers
else
$('<iframe>')
.attr({name: frame_name, id: frame_name})
.css({border: 'none', width: 0, height: 0, visibility: 'hidden'})
.appendTo(document.body);
// handle upload errors, parsing iframe content in onload
$('#'+frame_name).on('load', {ts:ts}, onload);
$(form).attr({
target: frame_name,
action: this.env.url + this.url(action, {folder: this.env.folder, token: this.env.token, uploadid:ts}),
method: 'POST'
}).attr(form.encoding ? 'encoding' : 'enctype', 'multipart/form-data')
.submit();
};
// open file in new window, using file API viewer
this.file_open = function(file, viewer)
{
var href = '?' + $.param({_task: 'files', _action: 'open', file: file, viewer: viewer == 2 ? 1 : 0});
rcmail.open_window(href, false, true);
};
// save file
this.file_save = function(file, content)
{
rcmail.enable_command('files-save', false);
// because we currently can edit only text files
// and we do not expect them to be very big, we save
// file in a very simple way, no upload progress, etc.
this.req = this.set_busy(true, 'saving');
this.request('file_update', {file: file, content: content, info: 1}, 'file_save_response');
};
// file save response handler
this.file_save_response = function(response)
{
rcmail.enable_command('files-save', true);
if (!this.response(response))
return;
// update file properties table
var table = $('#fileinfobox table'), file = response.result;
if (file) {
$('td.filetype', table).text(file.type);
$('td.filesize', table).text(this.file_size(file.size));
$('td.filemtime', table).text(file.mtime);
}
};
+ // handle auth errors on folder list
+ this.folder_list_auth_errors = function(result)
+ {
+ if (result && result.auth_errors) {
+ if (!this.auth_errors)
+ this.auth_errors = {};
+
+ $.extend(this.auth_errors, result.auth_errors);
+ }
+
+ // ask for password to the first storage on the list
+ $.each(this.auth_errors || [], function(i, v) {
+ file_api.folder_list_auth_dialog(i, v);
+ return false;
+ });
+ };
+
+ // create dialog for user credentials of external storage
+ this.folder_list_auth_dialog = function(label, driver)
+ {
+ var args = {width: 400, height: 300, buttons: {}},
+ dialog = $('#files-folder-auth-dialog'),
+ content = this.folder_list_auth_form(driver);
+
+ dialog.find('table.propform').remove();
+ dialog.append(content);
+
+ args.buttons[this.t('kolab_files.save')] = function() {
+ var data = {folder: label, list: 1};
+
+ $('input', dialog).each(function() {
+ data[this.name] = this.value;
+ });
+
+ file_api.open_dialog = this;
+ file_api.req = file_api.set_busy(true, 'kolab_files.authenticating');
+ file_api.request('folder_auth', data, 'folder_auth_response');
+ };
+
+ args.buttons[this.t('kolab_files.cancel')] = function() {
+ delete file_api.auth_errors[label];
+ kolab_dialog_close(this);
+ // go to the next one
+ file_api.folder_list_auth_errors();
+ };
+
+ args.title = this.t('kolab_files.folderauthtitle').replace('$title', label);
+
+ // show dialog window
+ kolab_dialog_show(dialog, args, function() {
+ // focus first empty input
+ $('input', dialog).each(function() {
+ if (!this.value) {
+ this.focus();
+ return false;
+ }
+ });
+ });
+ };
+
+ // folder_auth handler
+ this.folder_auth_response = function(response)
+ {
+ if (!this.response(response))
+ return;
+
+ var cnt = 0, folders,
+ folder = response.result.folder,
+ parent = $('#' + this.env.folders[folder].id);
+
+ // try parent window if the folder element does not exist
+ if (!parent.length && window.parent && window.parent.rcmail) {
+ parent = $('#' + this.env.folders[folder].id, window.parent.document.body);
+ }
+
+ delete this.auth_errors[folder];
+ kolab_dialog_close(this.open_dialog);
+
+ // go to the next one
+ this.folder_list_auth_errors();
+
+ // count folders on the list
+ $.each(this.env.folders, function() { cnt++; });
+
+ // parse result
+ folders = this.folder_list_parse(response.result.list, cnt);
+ delete folders[folder]; // remove root added in folder_list_parse()
+
+ // add folders from the external source to the list
+ $.each(folders, function(i, f) {
+ var row = file_api.folder_list_row(i, f);
+ parent.after(row);
+ parent = row;
+ });
+
+ // add tree icons
+ this.folder_list_tree(folders);
+
+ $.extend(this.env.folders, folders);
+ };
+
+ // returns content of the external storage authentication form
+ this.folder_list_auth_form = function(driver)
+ {
+ var rows = [];
+
+ $.each(driver.form, function(fi, fv) {
+ var id = 'authinput' + fi,
+ attrs = {type: fi.match(/pass/) ? 'password' : 'text', size: 25, name: fi, id: id},
+ input = $('<input>').attr(attrs);
+
+ if (driver.form_values && driver.form_values[fi])
+ input.attr({value: driver.form_values[fi]});
+
+ rows.push($('<tr>')
+ .append($('<td class="title">').append($('<label>').attr('for', id).text(fv)))
+ .append($('<td>').append(input))
+ );
+ });
+
+ return $('<table class="propform">').append(rows);
+ };
};
diff --git a/plugins/kolab_files/lib/kolab_files_engine.php b/plugins/kolab_files/lib/kolab_files_engine.php
index 8cb408bd..4dce8eb2 100644
--- a/plugins/kolab_files/lib/kolab_files_engine.php
+++ b/plugins/kolab_files/lib/kolab_files_engine.php
@@ -1,1020 +1,1123 @@
<?php
/**
* Kolab files storage engine
*
* @version @package_version@
* @author Aleksander Machniak <machniak@kolabsys.com>
*
* Copyright (C) 2013, Kolab Systems AG <contact@kolabsys.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
class kolab_files_engine
{
private $plugin;
private $rc;
- private $timeout = 60;
+ private $timeout = 600;
private $sort_cols = array('name', 'mtime', 'size');
/**
* Class constructor
*/
public function __construct($plugin, $url)
{
- $this->url = rcube_utils::resolve_url($url);
- $this->plugin = $plugin;
- $this->rc = $plugin->rc;
+ $this->url = rcube_utils::resolve_url($url);
+ $this->plugin = $plugin;
+ $this->rc = $plugin->rc;
+ $this->timeout = $this->rc->config->get('session_lifetime') * 60;
}
/**
* User interface initialization
*/
public function ui()
{
$this->plugin->add_texts('localization/');
// set templates of Files UI and widgets
if ($this->rc->task == 'mail') {
if ($this->rc->action == 'compose') {
$template = 'compose_plugin';
}
else if (in_array($this->rc->action, array('show', 'preview', 'get'))) {
$template = 'message_plugin';
if ($this->rc->action == 'get') {
// add "Save as" button into attachment toolbar
$this->plugin->add_button(array(
'id' => 'saveas',
'name' => 'saveas',
'type' => 'link',
'onclick' => 'kolab_directory_selector_dialog()',
'class' => 'button buttonPas saveas',
'classact' => 'button saveas',
'label' => 'kolab_files.save',
'title' => 'kolab_files.saveto',
), 'toolbar');
}
else {
// add "Save as" button into attachment menu
$this->plugin->add_button(array(
'id' => 'attachmenusaveas',
'name' => 'attachmenusaveas',
'type' => 'link',
'wrapper' => 'li',
'onclick' => 'return false',
'class' => 'icon active saveas',
'classact' => 'icon active saveas',
'innerclass' => 'icon active saveas',
'label' => 'kolab_files.saveto',
), 'attachmentmenu');
}
}
$this->plugin->add_label('save', 'cancel', 'saveto',
'saveall', 'fromcloud', 'attachsel', 'selectfiles', 'attaching',
- 'collection_audio', 'collection_video', 'collection_image', 'collection_document'
+ 'collection_audio', 'collection_video', 'collection_image', 'collection_document',
+ 'folderauthtitle', 'authenticating'
);
}
else if ($this->rc->task == 'files') {
$template = 'files';
+
+ // get list of external sources
+ $this->get_external_storage_drivers();
}
// add taskbar button
if (empty($_REQUEST['framed'])) {
$this->plugin->add_button(array(
'command' => 'files',
'class' => 'button-files',
'classsel' => 'button-files button-selected',
'innerclass' => 'button-inner',
'label' => 'kolab_files.files',
), 'taskbar');
}
$this->plugin->include_stylesheet($this->plugin->local_skin_path().'/style.css');
if (!empty($template)) {
$this->plugin->include_script($this->url . '/js/files_api.js');
$this->plugin->include_script('kolab_files.js');
$this->rc->output->set_env('files_url', $this->url . '/api/');
$this->rc->output->set_env('files_token', $this->get_api_token());
// register template objects for dialogs (and main interface)
$this->rc->output->add_handlers(array(
'folder-create-form' => array($this, 'folder_create_form'),
+ 'folder-mount-form' => array($this, 'folder_mount_form'),
'file-search-form' => array($this, 'file_search_form'),
'file-edit-form' => array($this, 'file_edit_form'),
'filelist' => array($this, 'file_list'),
'filequotadisplay' => array($this, 'quota_display'),
));
if ($this->rc->task != 'files') {
// add dialog content at the end of page body
$this->rc->output->add_footer(
$this->rc->output->parse('kolab_files.' . $template, false, false));
}
}
}
/**
* Engine actions handler
*/
public function actions()
{
if ($this->rc->task == 'files' && $this->rc->action) {
$action = $this->rc->action;
}
else if ($this->rc->task != 'files' && $_POST['act']) {
$action = $_POST['act'];
}
else {
$action = 'index';
}
$method = 'action_' . str_replace('-', '_', $action);
if (method_exists($this, $method)) {
$this->plugin->add_texts('localization/');
$this->{$method}();
}
}
/**
* Template object for folder creation form
*/
public function folder_create_form($attrib)
{
$attrib['name'] = 'folder-create-form';
if (empty($attrib['id'])) {
$attrib['id'] = 'folder-create-form';
}
$input_name = new html_inputfield(array('id' => 'folder-name', 'name' => 'name', 'size' => 30));
$select_parent = new html_select(array('id' => 'folder-parent', 'name' => 'parent'));
$table = new html_table(array('cols' => 2, 'class' => 'propform'));
- $table->add('title', html::label('folder-name', Q($this->plugin->gettext('foldername'))));
+ $table->add('title', html::label('folder-name', rcube::Q($this->plugin->gettext('foldername'))));
$table->add(null, $input_name->show());
- $table->add('title', html::label('folder-parent', Q($this->plugin->gettext('folderinside'))));
+ $table->add('title', html::label('folder-parent', rcube::Q($this->plugin->gettext('folderinside'))));
$table->add(null, $select_parent->show());
$out = $table->show();
// add form tag around text field
if (empty($attrib['form'])) {
$out = $this->rc->output->form_tag($attrib, $out);
}
$this->plugin->add_label('foldercreating', 'foldercreatenotice', 'create', 'foldercreate', 'cancel');
$this->rc->output->add_gui_object('folder-create-form', $attrib['id']);
return $out;
}
+ /**
+ * Template object for folder mounting form
+ */
+ public function folder_mount_form($attrib)
+ {
+ $sources = $this->rc->output->get_env('external_sources');
+
+ if (empty($sources) || !is_array($sources)) {
+ return '';
+ }
+
+ $attrib['name'] = 'folder-mount-form';
+ if (empty($attrib['id'])) {
+ $attrib['id'] = 'folder-mount-form';
+ }
+
+ // build form content
+ $table = new html_table(array('cols' => 2, 'class' => 'propform'));
+ $input_name = new html_inputfield(array('id' => 'folder-mount-name', 'name' => 'name', 'size' => 30));
+ $input_driver = new html_radiobutton(array('name' => 'driver', 'size' => 30));
+
+ $table->add('title', html::label('folder-mount-name', rcube::Q($this->plugin->gettext('name'))));
+ $table->add(null, $input_name->show());
+
+ foreach ($sources as $key => $source) {
+ $id = 'source-' . $key;
+ $form = new html_table(array('cols' => 2, 'class' => 'propform driverform'));
+
+ foreach ((array) $source['form'] as $idx => $label) {
+ $iid = $id . '-' . $idx;
+ $type = stripos($idx, 'pass') !== false ? 'html_passwordfield' : 'html_inputfield';
+ $input = new $type(array('size' => 30));
+
+ $form->add('title', html::label($iid, rcube::Q($label)));
+ $form->add(null, $input->show('', array(
+ 'id' => $iid,
+ 'name' => $key . '[' . $idx . ']'
+ )));
+ }
+
+ $row = $input_driver->show(null, array('value' => $key))
+ . html::img(array('src' => $source['image'], 'alt' => $key, 'title' => $source['name']))
+ . html::div(null, html::span('name', rcube::Q($source['name']))
+ . html::br()
+ . html::span('description', rcube::Q($source['description']))
+ . $form->show()
+ );
+
+ $table->add(array('id' => $id, 'colspan' => 2, 'class' => 'source'), $row);
+ }
+
+ $out = $table->show();
+
+ // add form tag around text field
+ if (empty($attrib['form'])) {
+ $out = $this->rc->output->form_tag($attrib, $out);
+ }
+
+ $this->plugin->add_label('foldermounting', 'foldermountnotice', 'foldermount',
+ 'save', 'cancel', 'folderauthtitle', 'authenticating'
+ );
+ $this->rc->output->add_gui_object('folder-mount-form', $attrib['id']);
+
+ return $out;
+ }
+
/**
* Template object for file_edit form
*/
public function file_edit_form($attrib)
{
$attrib['name'] = 'file-edit-form';
if (empty($attrib['id'])) {
$attrib['id'] = 'file-edit-form';
}
- $input_name = new html_inputfield(array('id' => 'file-name', 'name' => 'name', 'size' => 30));
- $table = new html_table(array('cols' => 2, 'class' => 'propform'));
+ $input_name = new html_inputfield(array('id' => 'file-name', 'name' => 'name', 'size' => 30));
+ $table = new html_table(array('cols' => 2, 'class' => 'propform'));
- $table->add('title', html::label('file-name', Q($this->plugin->gettext('filename'))));
+ $table->add('title', html::label('file-name', rcube::Q($this->plugin->gettext('filename'))));
$table->add(null, $input_name->show());
$out = $table->show();
// add form tag around text field
if (empty($attrib['form'])) {
$out = $this->rc->output->form_tag($attrib, $out);
}
$this->plugin->add_label('save', 'cancel', 'fileupdating', 'fileedit');
$this->rc->output->add_gui_object('file-edit-form', $attrib['id']);
return $out;
}
/**
* Template object for file search form in "From cloud" dialog
*/
public function file_search_form($attrib)
{
$attrib['name'] = '_q';
if (empty($attrib['id'])) {
$attrib['id'] = 'filesearchbox';
}
if ($attrib['type'] == 'search' && !$this->rc->output->browser->khtml) {
unset($attrib['type'], $attrib['results']);
}
$input_q = new html_inputfield($attrib);
$out = $input_q->show();
// add some labels to client
$this->rc->output->add_label('searching');
$this->rc->output->add_gui_object('filesearchbox', $attrib['id']);
// add form tag around text field
if (empty($attrib['form'])) {
$out = $this->rc->output->form_tag(array(
'action' => '?_task=files',
'name' => "filesearchform",
'onsubmit' => rcmail_output::JS_OBJECT_NAME . ".command('files-search'); return false",
), $out);
}
return $out;
}
/**
* Template object for files list
*/
public function file_list($attrib)
{
// define list of cols to be displayed based on parameter or config
if (empty($attrib['columns'])) {
$list_cols = $this->rc->config->get('kolab_files_list_cols');
$dont_override = $this->rc->config->get('dont_override');
$a_show_cols = is_array($list_cols) ? $list_cols : array('name');
$this->rc->output->set_env('col_movable', !in_array('kolab_files_list_cols', (array)$dont_override));
}
else {
$a_show_cols = preg_split('/[\s,;]+/', strip_quotes($attrib['columns']));
}
// make sure 'name' and 'options' column is present
if (!in_array('name', $a_show_cols)) {
array_unshift($a_show_cols, 'name');
}
if (!in_array('options', $a_show_cols)) {
array_unshift($a_show_cols, 'options');
}
$attrib['columns'] = $a_show_cols;
// save some variables for use in ajax list
$_SESSION['kolab_files_list_attrib'] = $attrib;
// For list in dialog(s) remove all option-like columns
if ($this->rc->task != 'files') {
$a_show_cols = array_intersect($a_show_cols, $this->sort_cols);
}
// set default sort col/order to session
if (!isset($_SESSION['kolab_files_sort_col']))
$_SESSION['kolab_files_sort_col'] = $this->rc->config->get('kolab_files_sort_col') ?: 'name';
if (!isset($_SESSION['kolab_files_sort_order']))
$_SESSION['kolab_files_sort_order'] = strtoupper($this->rc->config->get('kolab_files_sort_order') ?: 'asc');
// set client env
$this->rc->output->add_gui_object('filelist', $attrib['id']);
$this->rc->output->set_env('sort_col', $_SESSION['kolab_files_sort_col']);
$this->rc->output->set_env('sort_order', $_SESSION['kolab_files_sort_order']);
$this->rc->output->set_env('coltypes', $a_show_cols);
$this->rc->output->set_env('search_threads', $this->rc->config->get('kolab_files_search_threads'));
$this->rc->output->include_script('list.js');
// attach css rules for mimetype icons
$this->plugin->include_stylesheet($this->url . '/skins/default/images/mimetypes/style.css');
$thead = '';
foreach ($this->file_list_head($attrib, $a_show_cols) as $cell) {
$thead .= html::tag('th', array('class' => $cell['className'], 'id' => $cell['id']), $cell['html']);
}
return html::tag('table', $attrib,
html::tag('thead', null, html::tag('tr', null, $thead)) . html::tag('tbody', null, ''),
array('style', 'class', 'id', 'cellpadding', 'cellspacing', 'border', 'summary'));
}
/**
* Creates <THEAD> for message list table
*/
protected function file_list_head($attrib, $a_show_cols)
{
$skin_path = $_SESSION['skin_path'];
// $image_tag = html::img(array('src' => "%s%s", 'alt' => "%s"));
// check to see if we have some settings for sorting
$sort_col = $_SESSION['kolab_files_sort_col'];
$sort_order = $_SESSION['kolab_files_sort_order'];
$dont_override = (array)$this->rc->config->get('dont_override');
$disabled_sort = in_array('message_sort_col', $dont_override);
$disabled_order = in_array('message_sort_order', $dont_override);
$this->rc->output->set_env('disabled_sort_col', $disabled_sort);
$this->rc->output->set_env('disabled_sort_order', $disabled_order);
// define sortable columns
if ($disabled_sort)
$a_sort_cols = $sort_col && !$disabled_order ? array($sort_col) : array();
else
$a_sort_cols = $this->sort_cols;
if (!empty($attrib['optionsmenuicon'])) {
$onclick = 'return ' . JS_OBJECT_NAME . ".command('menu-open', 'filelistmenu', this, event)";
$inner = $this->rc->gettext('listoptions');
if (is_string($attrib['optionsmenuicon']) && $attrib['optionsmenuicon'] != 'true') {
$inner = html::img(array('src' => $skin_path . $attrib['optionsmenuicon'], 'alt' => $RCMAIL->gettext('listoptions')));
}
$list_menu = html::a(array(
'href' => '#list-options',
'onclick' => $onclick,
'class' => 'listmenu',
'id' => 'listmenulink',
'title' => $this->rc->gettext('listoptions'),
'tabindex' => '0',
), $inner);
}
else {
$list_menu = '';
}
$cells = array();
foreach ($a_show_cols as $col) {
// get column name
switch ($col) {
/*
case 'status':
$col_name = '<span class="' . $col .'"> </span>';
break;
*/
case 'options':
$col_name = $list_menu;
break;
default:
- $col_name = Q($this->plugin->gettext($col));
+ $col_name = rcube::Q($this->plugin->gettext($col));
}
// make sort links
if (in_array($col, $a_sort_cols))
$col_name = html::a(array('href'=>"#sort", 'onclick' => 'return '.JS_OBJECT_NAME.".command('files-sort','".$col."',this)", 'title' => rcube_label('sortby')), $col_name);
else if ($col_name[0] != '<')
$col_name = '<span class="' . $col .'">' . $col_name . '</span>';
$sort_class = $col == $sort_col && !$disabled_order ? " sorted$sort_order" : '';
$class_name = $col.$sort_class;
// put it all together
$cells[] = array('className' => $class_name, 'id' => "rcm$col", 'html' => $col_name);
}
return $cells;
}
/**
* Update files list object
*/
protected function file_list_update($prefs)
{
$attrib = $_SESSION['kolab_files_list_attrib'];
if (!empty($prefs['kolab_files_list_cols'])) {
$attrib['columns'] = $prefs['kolab_files_list_cols'];
$_SESSION['kolab_files_list_attrib'] = $attrib;
}
$a_show_cols = $attrib['columns'];
$head = '';
foreach ($this->file_list_head($attrib, $a_show_cols) as $cell) {
$head .= html::tag('td', array('class' => $cell['className'], 'id' => $cell['id']), $cell['html']);
}
$head = html::tag('tr', null, $head);
$this->rc->output->set_env('coltypes', $a_show_cols);
$this->rc->output->command('files_list_update', $head);
}
/**
* Template object for file info box
*/
public function file_info_box($attrib)
{
// print_r($this->file_data, true);
$table = new html_table(array('cols' => 2, 'class' => $attrib['class']));
// file name
$table->add('label', $this->plugin->gettext('name').':');
$table->add('data filename', $this->file_data['name']);
// file type
// @TODO: human-readable type name
$table->add('label', $this->plugin->gettext('type').':');
$table->add('data filetype', $this->file_data['type']);
// file size
$table->add('label', $this->plugin->gettext('size').':');
$table->add('data filesize', $this->rc->show_bytes($this->file_data['size']));
// file modification time
$table->add('label', $this->plugin->gettext('mtime').':');
$table->add('data filemtime', $this->file_data['mtime']);
// @TODO: for images: width, height, color depth, etc.
// @TODO: for text files: count of characters, lines, words
return $table->show();
}
/**
* Template object for file preview frame
*/
public function file_preview_frame($attrib)
{
if (empty($attrib['id'])) {
$attrib['id'] = 'filepreviewframe';
}
if ($frame = $this->file_data['viewer']['frame']) {
return $frame;
}
if ($href = $this->file_data['viewer']['href']) {
// file href attribute must be an absolute URL (Bug #2063)
if (!empty($href)) {
if (!preg_match('|^https?://|', $href)) {
$href = $this->url . '/api/' . $href;
}
}
}
else {
$token = $this->get_api_token();
$href = $this->url . '/api/?method=file_get'
. '&file=' . urlencode($this->file_data['filename'])
. '&token=' . urlencode($token);
}
$this->rc->output->add_gui_object('preview_frame', $attrib['id']);
$attrib['allowfullscreen'] = true;
$attrib['src'] = $href;
$attrib['onload'] = 'kolab_files_frame_load(this)';
return html::iframe($attrib);
}
/**
* Template object for quota display
*/
public function quota_display($attrib)
{
if (!$attrib['id']) {
$attrib['id'] = 'rcmquotadisplay';
}
$quota_type = !empty($attrib['display']) ? $attrib['display'] : 'text';
$this->rc->output->add_gui_object('quotadisplay', $attrib['id']);
$this->rc->output->set_env('quota_type', $quota_type);
// get quota
$token = $this->get_api_token();
$request = $this->get_request(array('method' => 'quota'), $token);
// send request to the API
try {
$response = $request->send();
$status = $response->getStatus();
$body = @json_decode($response->getBody(), true);
if ($status == 200 && $body['status'] == 'OK') {
$quota = $body['result'];
}
else {
throw new Exception($body['reason']);
}
}
catch (Exception $e) {
+ rcube::raise_error($e, true, false);
$quota = array('total' => 0, 'percent' => 0);
}
$quota = rcube_output::json_serialize($quota);
$this->rc->output->add_script(rcmail_output::JS_OBJECT_NAME . ".files_set_quota($quota);", 'docready');
return html::span($attrib, '');
}
/**
* Get API token for current user session, authenticate if needed
*/
public function get_api_token()
{
$token = $_SESSION['kolab_files_token'];
$time = $_SESSION['kolab_files_time'];
if ($token && time() - $this->timeout < $time) {
- return $token;
+ if (time() - $time <= $this->timeout / 2) {
+ return $token;
+ }
}
- if (!($request = $this->get_request())) {
- return $token;
- }
+ $request = $this->get_request(array('method' => 'ping'), $token);
try {
$url = $request->getUrl();
// Send ping request
if ($token) {
$url->setQueryVariables(array('method' => 'ping'));
$request->setUrl($url);
$response = $request->send();
$status = $response->getStatus();
if ($status == 200 && ($body = json_decode($response->getBody(), true))) {
if ($body['status'] == 'OK') {
$_SESSION['kolab_files_time'] = time();
return $token;
}
}
}
// Go with authenticate request
$url->setQueryVariables(array('method' => 'authenticate'));
$request->setUrl($url);
$request->setAuth($this->rc->user->get_username(), $this->rc->decrypt($_SESSION['password']));
$response = $request->send();
$status = $response->getStatus();
if ($status == 200 && ($body = json_decode($response->getBody(), true))) {
$token = $body['result']['token'];
if ($token) {
$_SESSION['kolab_files_token'] = $token;
$_SESSION['kolab_files_time'] = time();
$_SESSION['kolab_files_caps'] = $body['result']['capabilities'];
}
}
else {
throw new Exception(sprintf("Authenticate error (Status: %d)", $status));
}
}
catch (Exception $e) {
rcube::raise_error($e, true, false);
}
return $token;
}
/**
* Initialize HTTP_Request object
*/
protected function get_request($get = null, $token = null)
{
$url = $this->url . '/api/';
if (!$this->request) {
$config = array(
'store_body' => true,
'follow_redirects' => true,
);
$this->request = libkolab::http_request($url, 'GET', $config);
}
else {
// cleanup
try {
$this->request->setBody('');
$this->request->setUrl($url);
$this->request->setMethod(HTTP_Request2::METHOD_GET);
}
catch (Exception $e) {
rcube::raise_error($e, true, true);
}
}
if ($token) {
$this->request->setHeader('X-Session-Token', $token);
}
if (!empty($get)) {
$url = $this->request->getUrl();
$url->setQueryVariables($get);
$this->request->setUrl($url);
}
// some HTTP server configurations require this header
$this->request->setHeader('accept', "application/json,text/javascript,*/*");
return $this->request;
}
/**
* Handler for main files interface (Files task)
*/
protected function action_index()
{
$this->plugin->add_label(
'folderdeleting', 'folderdeleteconfirm', 'folderdeletenotice',
'uploading', 'attaching', 'uploadsizeerror',
'filedeleting', 'filedeletenotice', 'filedeleteconfirm',
'filemoving', 'filemovenotice', 'filemoveconfirm', 'filecopying', 'filecopynotice',
'collection_audio', 'collection_video', 'collection_image', 'collection_document',
'fileskip', 'fileskipall', 'fileoverwrite', 'fileoverwriteall'
);
$this->rc->output->set_pagetitle($this->plugin->gettext('files'));
$this->rc->output->set_env('file_mimetypes', $this->get_mimetypes());
$this->rc->output->set_env('files_quota', $_SESSION['kolab_files_caps']['QUOTA']);
$this->rc->output->set_env('files_max_upload', $_SESSION['kolab_files_caps']['MAX_UPLOAD']);
$this->rc->output->send('kolab_files.files');
}
/**
* Handler for preferences save action
*/
protected function action_prefs()
{
$dont_override = (array)$this->rc->config->get('dont_override');
$prefs = array();
$opts = array(
'kolab_files_sort_col' => true,
'kolab_files_sort_order' => true,
'kolab_files_list_cols' => false,
);
foreach ($opts as $o => $sess) {
if (isset($_POST[$o]) && !in_array($o, $dont_override)) {
$prefs[$o] = rcube_utils::get_input_value($o, rcube_utils::INPUT_POST);
if ($sess) {
$_SESSION[$o] = $prefs[$o];
}
if ($o == 'kolab_files_list_cols') {
$update_list = true;
}
}
}
// save preference values
if (!empty($prefs)) {
$this->rc->user->save_prefs($prefs);
}
if (!empty($update_list)) {
$this->file_list_update($prefs);
}
$this->rc->output->send();
}
/**
* Handler for file open action
*/
protected function action_open()
{
$file = rcube_utils::get_input_value('file', rcube_utils::INPUT_GET);
// get file info
$token = $this->get_api_token();
$request = $this->get_request(array(
'method' => 'file_info',
'file' => $file,
'viewer' => !empty($_GET['viewer']),
), $token);
// send request to the API
try {
$response = $request->send();
$status = $response->getStatus();
$body = @json_decode($response->getBody(), true);
if ($status == 200 && $body['status'] == 'OK') {
$this->file_data = $body['result'];
}
else {
throw new Exception($body['reason']);
}
}
catch (Exception $e) {
rcube::raise_error(array(
'code' => 500, 'type' => 'php', 'line' => __LINE__, 'file' => __FILE__,
'message' => $e->getMessage()),
true, true);
}
$this->file_data['filename'] = $file;
$this->plugin->add_label('filedeleteconfirm', 'filedeleting', 'filedeletenotice');
// register template objects for dialogs (and main interface)
$this->rc->output->add_handlers(array(
'fileinfobox' => array($this, 'file_info_box'),
'filepreviewframe' => array($this, 'file_preview_frame'),
));
// this one is for styling purpose
$this->rc->output->set_env('extwin', true);
$this->rc->output->set_env('file', $file);
$this->rc->output->set_env('file_data', $this->file_data);
$this->rc->output->set_pagetitle(rcube::Q($file));
$this->rc->output->send('kolab_files.filepreview');
}
/**
* Handler for "save all attachments into cloud" action
*/
protected function action_save_file()
{
// $source = rcube_utils::get_input_value('source', rcube_utils::INPUT_POST);
$uid = rcube_utils::get_input_value('uid', rcube_utils::INPUT_POST);
$dest = rcube_utils::get_input_value('dest', rcube_utils::INPUT_POST);
$id = rcube_utils::get_input_value('id', rcube_utils::INPUT_POST);
$name = rcube_utils::get_input_value('name', rcube_utils::INPUT_POST);
$temp_dir = unslashify($this->rc->config->get('temp_dir'));
$message = new rcube_message($uid);
$request = $this->get_request();
$url = $request->getUrl();
$files = array();
$errors = array();
$attachments = array();
$request->setMethod(HTTP_Request2::METHOD_POST);
$request->setHeader('X-Session-Token', $this->get_api_token());
$url->setQueryVariables(array('method' => 'file_upload', 'folder' => $dest));
$request->setUrl($url);
foreach ($message->attachments as $attach_prop) {
if (empty($id) || $id == $attach_prop->mime_id) {
$filename = strlen($name) ? $name : rcmail_attachment_name($attach_prop, true);
$attachments[$filename] = $attach_prop;
}
}
// @TODO: handle error
// @TODO: implement file upload using file URI instead of body upload
foreach ($attachments as $attach_name => $attach_prop) {
$path = tempnam($temp_dir, 'rcmAttmnt');
// save attachment to file
if ($fp = fopen($path, 'w+')) {
$message->get_part_content($attach_prop->mime_id, $fp, true);
}
else {
$errors[] = true;
rcube::raise_error(array(
'code' => 500, 'type' => 'php', 'line' => __LINE__, 'file' => __FILE__,
'message' => "Unable to save attachment into file $path"),
true, false);
continue;
}
fclose($fp);
// send request to the API
try {
$request->setBody('');
$request->addUpload('file[]', $path, $attach_name, $attach_prop->mimetype);
$response = $request->send();
$status = $response->getStatus();
$body = @json_decode($response->getBody(), true);
if ($status == 200 && $body['status'] == 'OK') {
$files[] = $attach_name;
}
else {
throw new Exception($body['reason']);
}
}
catch (Exception $e) {
unlink($path);
$errors[] = $e->getMessage();
rcube::raise_error(array(
'code' => 500, 'type' => 'php', 'line' => __LINE__, 'file' => __FILE__,
'message' => $e->getMessage()),
true, false);
continue;
}
// clean up
unlink($path);
$request->setBody('');
}
if ($count = count($files)) {
$msg = $this->plugin->gettext(array('name' => 'saveallnotice', 'vars' => array('n' => $count)));
$this->rc->output->show_message($msg, 'confirmation');
}
if ($count = count($errors)) {
$msg = $this->plugin->gettext(array('name' => 'saveallerror', 'vars' => array('n' => $count)));
$this->rc->output->show_message($msg, 'error');
}
// @TODO: update quota indicator, make this optional in case files aren't stored in IMAP
$this->rc->output->send();
}
/**
* Handler for "add attachments from the cloud" action
*/
protected function action_attach_file()
{
$files = rcube_utils::get_input_value('files', rcube_utils::INPUT_POST);
$uploadid = rcube_utils::get_input_value('uploadid', rcube_utils::INPUT_POST);
$COMPOSE_ID = rcube_utils::get_input_value('id', rcube_utils::INPUT_POST);
$COMPOSE = null;
$errors = array();
if ($COMPOSE_ID && $_SESSION['compose_data_'.$COMPOSE_ID]) {
$COMPOSE =& $_SESSION['compose_data_'.$COMPOSE_ID];
}
if (!$COMPOSE) {
die("Invalid session var!");
}
// attachment upload action
if (!is_array($COMPOSE['attachments'])) {
$COMPOSE['attachments'] = array();
}
// clear all stored output properties (like scripts and env vars)
$this->rc->output->reset();
$temp_dir = unslashify($this->rc->config->get('temp_dir'));
$request = $this->get_request();
$url = $request->getUrl();
// Use observer object to store HTTP response into a file
require_once $this->plugin->home . DIRECTORY_SEPARATOR . 'lib' . DIRECTORY_SEPARATOR . 'kolab_files_observer.php';
$observer = new kolab_files_observer();
$request->setHeader('X-Session-Token', $this->get_api_token());
// download files from the API and attach them
foreach ($files as $file) {
// decode filename
$file = urldecode($file);
// get file information
try {
$url->setQueryVariables(array('method' => 'file_info', 'file' => $file));
$request->setUrl($url);
$response = $request->send();
$status = $response->getStatus();
$body = @json_decode($response->getBody(), true);
if ($status == 200 && $body['status'] == 'OK') {
$file_params = $body['result'];
}
else {
throw new Exception($body['reason']);
}
}
catch (Exception $e) {
$errors[] = $e->getMessage();
rcube::raise_error(array(
'code' => 500, 'type' => 'php', 'line' => __LINE__, 'file' => __FILE__,
'message' => $e->getMessage()),
true, false);
continue;
}
// set location of downloaded file
$path = tempnam($temp_dir, 'rcmAttmnt');
$observer->set_file($path);
// download file
try {
$url->setQueryVariables(array('method' => 'file_get', 'file' => $file));
$request->setUrl($url);
$request->attach($observer);
$response = $request->send();
$status = $response->getStatus();
$response->getBody(); // returns nothing
$request->detach($observer);
if ($status != 200 || !file_exists($path)) {
throw new Exception("Unable to save file");
}
}
catch (Exception $e) {
$errors[] = $e->getMessage();
rcube::raise_error(array(
'code' => 500, 'type' => 'php', 'line' => __LINE__, 'file' => __FILE__,
'message' => $e->getMessage()),
true, false);
continue;
}
$attachment = array(
'path' => $path,
'size' => $file_params['size'],
'name' => $file_params['name'],
'mimetype' => $file_params['type'],
'group' => $COMPOSE_ID,
);
$attachment = $this->rc->plugins->exec_hook('attachment_save', $attachment);
if ($attachment['status'] && !$attachment['abort']) {
$id = $attachment['id'];
// store new attachment in session
unset($attachment['data'], $attachment['status'], $attachment['abort']);
$COMPOSE['attachments'][$id] = $attachment;
if (($icon = $COMPOSE['deleteicon']) && is_file($icon)) {
$button = html::img(array(
'src' => $icon,
'alt' => $this->rc->gettext('delete')
));
}
else {
- $button = Q($this->rc->gettext('delete'));
+ $button = rcube::Q($this->rc->gettext('delete'));
}
$content = html::a(array(
'href' => "#delete",
'onclick' => sprintf("return %s.command('remove-attachment','rcmfile%s', this)", JS_OBJECT_NAME, $id),
'title' => $this->rc->gettext('delete'),
'class' => 'delete',
), $button);
- $content .= Q($attachment['name']);
+ $content .= rcube::Q($attachment['name']);
$this->rc->output->command('add2attachment_list', "rcmfile$id", array(
'html' => $content,
'name' => $attachment['name'],
'mimetype' => $attachment['mimetype'],
'classname' => rcmail_filetype2classname($attachment['mimetype'], $attachment['name']),
'complete' => true), $uploadid);
}
else if ($attachment['error']) {
$errors[] = $attachment['error'];
}
else {
$errors[] = $this->plugin->gettext('attacherror');
}
}
if (!empty($errors)) {
$this->rc->output->command('display_message', $this->plugin->gettext('attacherror'), 'error');
$this->rc->output->command('remove_from_attachment_list', $uploadid);
}
// send html page with JS calls as response
$this->rc->output->command('auto_save_start', false);
$this->rc->output->send();
}
/**
* Returns mimetypes supported by File API viewers
*/
protected function get_mimetypes()
{
$token = $this->get_api_token();
$request = $this->get_request(array('method' => 'mimetypes'), $token);
// send request to the API
try {
$response = $request->send();
$status = $response->getStatus();
$body = @json_decode($response->getBody(), true);
if ($status == 200 && $body['status'] == 'OK') {
$mimetypes = $body['result'];
}
else {
throw new Exception($body['reason']);
}
}
catch (Exception $e) {
rcube::raise_error(array(
'code' => 500, 'type' => 'php', 'line' => __LINE__, 'file' => __FILE__,
'message' => $e->getMessage()),
true, false);
}
return $mimetypes;
}
+
+ /**
+ * Get list of available external storage drivers
+ */
+ protected function get_external_storage_drivers()
+ {
+ // first get configured sources from Chwala
+ $token = $this->get_api_token();
+ $request = $this->get_request(array('method' => 'folder_types'), $token);
+
+ // send request to the API
+ try {
+ $response = $request->send();
+ $status = $response->getStatus();
+ $body = @json_decode($response->getBody(), true);
+
+ if ($status == 200 && $body['status'] == 'OK') {
+ $sources = $body['result'];
+ }
+ else {
+ throw new Exception($body['reason']);
+ }
+ }
+ catch (Exception $e) {
+ rcube::raise_error($e, true, false);
+ return;
+ }
+
+ $this->rc->output->set_env('external_sources', $sources);
+ }
}
diff --git a/plugins/kolab_files/localization/en_US.inc b/plugins/kolab_files/localization/en_US.inc
index a1fc8145..3ecd1fce 100644
--- a/plugins/kolab_files/localization/en_US.inc
+++ b/plugins/kolab_files/localization/en_US.inc
@@ -1,92 +1,99 @@
<?php
/**
* Localizations for the Kolab Files plugin
*
* Copyright (C) 2014, Kolab Systems AG
*
* For translation see https://www.transifex.com/projects/p/kolab/resource/kolab_files/
*/
$labels['files'] = 'Files';
$labels['filepreview'] = 'File preview';
$labels['saveall'] = 'Save all to cloud...';
$labels['saveto'] = 'Save to cloud...';
$labels['saveas'] = 'Save as:';
$labels['create'] = 'Create';
$labels['save'] = 'Save';
$labels['cancel'] = 'Cancel';
$labels['fromcloud'] = 'From cloud...';
$labels['selectfiles'] = 'Select file(s) to attach...';
$labels['attachsel'] = 'Attach selected';
$labels['foldercreate'] = 'Create folder';
+$labels['foldermount'] = 'Add storage';
$labels['folderrename'] = 'Rename folder';
$labels['folderdelete'] = 'Delete folder';
$labels['folderinside'] = 'Insert inside';
$labels['foldername'] = 'Folder name';
$labels['name'] = 'Name';
$labels['mtime'] = 'Modified';
$labels['type'] = 'Type';
$labels['upload'] = 'Upload';
$labels['uploadfile'] = 'Upload file(s)';
$labels['get'] = 'Download';
$labels['getfile'] = 'Download file';
$labels['view'] = 'View';
$labels['viewfile'] = 'View file';
$labels['rename'] = 'Rename file';
$labels['deletefile'] = 'Delete file(s)';
$labels['edit'] = 'Edit';
$labels['editfile'] = 'Edit file';
$labels['save'] = 'Save';
$labels['savefile'] = 'Save file';
$labels['printfile'] = 'Print file';
$labels['fileedit'] = 'File properties';
$labels['collection_audio'] = 'Audio';
$labels['collection_video'] = 'Video';
$labels['collection_image'] = 'Images';
$labels['collection_document'] = 'Documents';
$labels['uploading'] = 'Uploading file(s)...';
$labels['attaching'] = 'Attaching file(s)...';
+$labels['authenticating'] = 'Authenticating...';
$labels['foldercreating'] = 'Creating folder...';
+$labels['foldermounting'] = 'Adding external storage...';
$labels['folderdeleting'] = 'Deleting folder...';
$labels['folderdeleteconfirm'] = 'Are you sure you want to delete selected folder?';
$labels['folderdeletenotice'] = 'Folder deleted successfully.';
$labels['foldercreatenotice'] = 'Folder created successfully.';
+$labels['foldermountnotice'] = 'Storage added successfully.';
+$labels['folderauthtitle'] = 'Logon to $title';
$labels['saveallnotice'] = 'Successfully saved $n file(s).';
$labels['saveallerror'] = 'Saving $n file(s) failed.';
$labels['attacherror'] = 'Failed to attach file(s) from the cloud';
$labels['fileupdating'] = 'Updating file...';
$labels['filemoving'] = 'Moving file(s)...';
$labels['filecopying'] = 'Copying file(s)...';
$labels['filedeleting'] = 'Deleting file(s)...';
$labels['filedeleteconfirm'] = 'Are you sure you want to delete selected files?';
$labels['filedeletenotice'] = 'File(s) deleted successfully.';
$labels['filemovenotice'] = 'File(s) moved successfully.';
$labels['filecopynotice'] = 'File(s) copied successfully.';
$labels['uploadsizeerror'] = 'Maximum file size ($size) exceeded!';
$labels['allfolders'] = 'search in all folders';
$labels['fileskip'] = 'Skip';
$labels['fileskipall'] = 'Skip all';
$labels['fileoverwrite'] = 'Overwrite';
$labels['fileoverwriteall'] = 'Overwrite all';
$labels['filemoveconfirm'] = 'This action is going to overwrite the destination file: <b>$file</b>.';
$labels['arialabelsearchform'] = 'Files search form';
$labels['arialabelquicksearchbox'] = 'Search input';
$labels['arialabellistoptions'] = 'Files list options';
$labels['arialabelfolderoptions'] = 'Folder actions';
$labels['arialabelfileeditform'] = 'File editing form';
$labels['arialabelfoldercreateform'] = 'Folder creation form';
+$labels['arialabelfoldermountform'] = 'External storage form';
+$labels['arialabelfolderauthform'] = 'External storage authentication form';
$labels['arialabelfolderlist'] = 'Folder/Collection selection';
$labels['arialabelfileselectdialog'] = 'File selection dialog';
$labels['arialabelattachmentoptions'] = 'Attachment save options';
$labels['arialabelfilesavedialog'] = 'File(s) saving dialog';
$labels['arialabelfileprops'] = 'File properties';
$labels['arialabelfilecontent'] = 'File content';
?>
diff --git a/plugins/kolab_files/package.xml b/plugins/kolab_files/package.xml
index 8ac6e5b7..e8552ea2 100644
--- a/plugins/kolab_files/package.xml
+++ b/plugins/kolab_files/package.xml
@@ -1,78 +1,78 @@
<?xml version="1.0" encoding="UTF-8"?>
<package xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" packagerversion="1.9.0" version="2.0" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0
http://pear.php.net/dtd/tasks-1.0.xsd
http://pear.php.net/dtd/package-2.0
http://pear.php.net/dtd/package-2.0.xsd">
<name>kolab_files</name>
<uri>http://git.kolab.org/roundcubemail-plugins-kolab/</uri>
<summary>User interface for Kolab File Manager (Chwala)</summary>
<description>Adds file storage functionality.</description>
<lead>
<name>Alensader Machniak</name>
<user>machniak</user>
<email>machniak@kolabsys.com</email>
<active>yes</active>
</lead>
- <date>2013-08-25</date>
+ <date>2014-10-15</date>
<version>
- <release>1.0</release>
+ <release>1.1</release>
<api>1.0</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</stability>
<license uri="http://www.gnu.org/licenses/agpl.html">GNU AGPLv3</license>
<notes>-</notes>
<contents>
<dir baseinstalldir="/" name="/">
<file name="kolab_files.php" role="php">
<tasks:replace from="@name@" to="name" type="package-info"/>
<tasks:replace from="@package_version@" to="version" type="package-info"/>
</file>
<file name="kolab_files.js" role="data">
<tasks:replace from="@name@" to="name" type="package-info"/>
<tasks:replace from="@package_version@" to="version" type="package-info"/>
</file>
<file name="lib/kolab_files_engine.php" role="data">
<tasks:replace from="@name@" to="name" type="package-info"/>
<tasks:replace from="@package_version@" to="version" type="package-info"/>
</file>
<file name="lib/kolab_files_observer.php" role="data">
<tasks:replace from="@name@" to="name" type="package-info"/>
<tasks:replace from="@package_version@" to="version" type="package-info"/>
</file>
<file name="skins/larry/images/buttons.png" role="data"></file>
<file name="skins/larry/images/folders.png" role="data"></file>
<file name="skins/larry/images/unknown.png" role="data"></file>
<file name="skins/larry/templates/compose_plugin.html" role="data"></file>
<file name="skins/larry/templates/filepreview.html" role="data"></file>
<file name="skins/larry/templates/files.html" role="data"></file>
<file name="skins/larry/templates/message_plugin.html" role="data"></file>
<file name="skins/larry/style.css" role="data"></file>
<file name="skins/larry/ui.js" role="data"></file>
<file name="config.inc.php.dist" role="data"></file>
<file name="LICENSE" role="data"></file>
<file name="localization/en_US.inc" role="data"></file>
</dir>
<!-- / -->
</contents>
<dependencies>
<required>
<php>
<min>5.3.1</min>
</php>
<pearinstaller>
<min>1.7.0</min>
</pearinstaller>
<package>
<name>libkolab</name>
<uri>http://kolabsys.com</uri>
</package>
</required>
</dependencies>
<phprelease/>
</package>
diff --git a/plugins/kolab_files/skins/larry/style.css b/plugins/kolab_files/skins/larry/style.css
index 20621e83..de0735c1 100644
--- a/plugins/kolab_files/skins/larry/style.css
+++ b/plugins/kolab_files/skins/larry/style.css
@@ -1,364 +1,402 @@
/* Taskbar button */
#taskbar a.button-files span.button-inner {
background: url(images/buttons.png) 0 0 no-repeat;
height: 22px;
}
#taskbar a.button-files:hover span.button-inner,
#taskbar a.button-files.button-selected span.button-inner {
background: url(images/buttons.png) 0 -26px no-repeat;
height: 22px;
}
/* Attachment toolbar button */
.toolbar a.button.saveas {
background: url(images/buttons.png) center -277px no-repeat;
}
/* Files main interface */
#filestoolbar {
position: absolute;
height: 40px;
left: 0;
top: -6px;
z-index: 10;
}
#filestoolbar a.button {
background-image: url(images/buttons.png);
}
#filestoolbar a.button.upload {
background-position: center -52px;
}
#filestoolbar a.button.get {
background-position: center -93px;
}
#filestoolbar a.button.open {
background-position: center -131px;
}
#filestoolbar a.button.delete {
background-image: url(../../../../skins/larry/images/buttons.png);
}
#filestoolbar a.button.edit {
background-position: center -173px;
}
#filestoolbar a.button.save {
background-position: center -213px;
}
#filestoolbar a.button.print {
background-image: url(../../../../skins/larry/images/buttons.png);
background-position: center -810px;
background-repeat: no-repeat;
}
#filestoolbar form {
display: inline;
}
#quicksearchbar #filesearchmenulink {
position: absolute;
top: 5px;
left: 6px;
}
#folderlistbox,
#fileinfobox {
position: absolute;
top: 42px;
left: 0;
width: 220px;
bottom: 0;
}
#filelistcontainer,
#filecontent {
position: absolute;
top: 42px;
left: 232px;
right: 0;
bottom: 0;
overflow: auto;
}
#filelistbox {
overflow: auto;
top: 0;
left: 0;
position: absolute;
width: 100%;
}
#filecontent {
overflow: hidden;
}
#filecontent iframe {
width: 100%;
height: 100%;
margin: 0;
border: 0;
}
#files-folder-list ul li span.name {
background: url(../../../../skins/larry/images/listicons.png) 6px 3px no-repeat;
padding: 6px 8px 6px 32px;
line-height: 25px;
color: #376572;
}
#files-folder-list ul li.mailbox.collection span.name {
background: url(images/folders.png) 5px 0 no-repeat;
}
#files-folder-list ul li.mailbox.collection.audio span.name {
background-position: 5px -95px;
}
#files-folder-list ul li.mailbox.collection.video span.name {
background-position: 5px -143px;
}
#files-folder-list ul li.mailbox.collection.document span.name {
background-position: 5px 0;
}
#files-folder-list ul li.mailbox.collection.image span.name {
background-position: 5px -48px;
}
#files-folder-list ul li.mailbox.collection.audio.selected span.name {
background-position: 5px -119px;
}
#files-folder-list ul li.mailbox.collection.video.selected span.name {
background-position: 5px -167px;
}
#files-folder-list ul li.mailbox.collection.document.selected span.name {
background-position: 5px -24px;
}
#files-folder-list ul li.mailbox.collection.image.selected span.name {
background-position: 5px -72px;
}
#files-folder-list ul li span.branch {
display: inline-block;
}
#files-folder-list ul li:first-child {
border-radius: 4px 4px 0 0;
border-top: 0;
}
#files-folder-list ul li.virtual span {
color: #aaa;
}
.filelist thead tr th {
padding: 0;
}
.filelist tbody tr td {
padding: 2px 7px;
height: 18px;
line-height: 18px;
}
.filelist tr > .size {
width: 80px;
text-align: right;
}
.filelist thead tr > .size {
text-align: left;
}
.filelist tr > .mtime {
width: 125px;
}
.filelist tr > .options {
width: 32px;
cursor: pointer;
}
.filelist thead tr th.filename,
.filelist tbody tr td.filename {
width: 99%;
white-space: nowrap;
}
.filelist thead tr th.sortedASC a,
.filelist thead tr th.sortedDESC a {
color: #004458;
text-decoration: underline;
background-image: url(../../../../skins/larry/images/listicons.png);
background-position: right -912px;
background-repeat: no-repeat;
}
.filelist thead tr th.sortedASC a {
background-position: right -944px;
}
.filelist img {
vertical-align: middle;
display: inline-block;
}
.filelist tr > .options a.listmenu,
.filelist tr > .flag span.flagged,
.filelist tr > .flag span.unflagged,
.filelist tr > .flag span.unflagged:hover {
display: inline-block;
vertical-align: middle;
height: 18px;
width: 20px;
padding: 0;
background: url(../../../../skins/larry/images/listicons.png) -100px 0 no-repeat;
}
.filelist thead tr th.options {
padding: 0;
border-left: none;
}
.filelist thead tr th.options .listmenu {
background-position: 3px -970px;
cursor: pointer;
width: 24px;
height: 20px;
padding: 4px 4px 5px;
text-indent: -5000px;
}
.filelist thead tr th.options .listmenu:focus {
outline: none;
background-color: rgba(73,180,210,0.7);
}
.filelist thead tr th:first-child {
border-radius: 4px 0 0 0;
}
.filelist tbody tr td.options:hover span {
width: 26px;
height: 18px;
background: url(../../../../skins/larry/images/buttons.png) -5px -417px no-repeat;
display: block;
}
.filelist td.filename {
padding: 0 4px;
}
.filelist tbody td.filename span {
background: url(images/unknown.png) 0 0 no-repeat;
padding: 1px 0 3px 20px;
height: 16px;
}
/*
.filelist tbody td.filename span input {
padding: 0 2px;
height: 18px;
}
*/
#fileinfobox table {
table-layout: fixed;
}
#fileinfobox table td.label {
width: 60px;
font-weight: bold;
padding-right: 0;
}
#fileinfobox table td.data {
overflow: hidden;
text-overflow: ellipsis;
}
#fileinfobox table td.data.filename {
font-weight: bold;
}
#fileinfobox table tr:first-child td {
border-top: 0;
}
/* plugin dialogs */
#files-dialog,
#files-compose-dialog,
#files-file-edit-dialog,
+#files-folder-mount-dialog,
+#files-folder-auth-dialog,
#files-folder-create-dialog {
display: none;
}
#files-compose-dialog #folderlistbox {
right: auto;
width: 190px;
top: 45px;
bottom: 5px;
box-shadow: none;
}
#files-dialog #folderlistbox {
top: 5px;
bottom: 5px;
left: 0;
right: 0;
width: auto;
box-shadow: none;
}
#files-dialog.saveas #folderlistbox {
top: 50px;
}
#files-compose-dialog #filelistcontainer {
position: absolute;
top: 45px;
bottom: 5px;
left: 200px;
right: 0;
box-shadow: none;
}
#files-compose-dialog #searchmenulink {
width: 15px;
}
#files-compose-dialog .searchbox input {
background-color: white;
}
#files-compose-dialog #quicksearchbar {
top: 10px;
right: 5px;
}
#files-compose-dialog #searchreset {
cursor: pointer;
}
a.filesaveall {
display: inline-block;
margin-top: .5em;
padding: 3px 5px 4px 5px;
}
#file-save-as {
height: 25px;
white-space: nowrap;
}
#file-save-as input {
width: 200px;
}
ul.toolbarmenu li span.saveas {
background: url(images/buttons.png) -5px -253px no-repeat;
}
+
+
+table.propform td.source.selected {
+ background-color: #c7e3ef;
+}
+
+table.propform td.source div {
+ display: inline-block;
+ padding-left: 3px;
+ vertical-align: middle;
+}
+
+table.propform td.source .name {
+ font-weight: bold;
+}
+
+table.propform td.source .description {
+ font-size: 9px;
+ color: #666;
+}
+
+table.propform td.source img {
+ vertical-align: middle;
+ background-color: #e0e0e0;
+ border-radius: 3px;
+ margin: 3px;
+ background-image: -moz-linear-gradient(center top, #888, #333);
+ background-image: -webkit-linear-gradient(top, #888, #333);
+ background-image: -ms-linear-gradient(top, #888, #333);
+}
+
+table.propform td.source table.propform td {
+ border-bottom: 0;
+ padding: 2px 10px;
+ background-color: inherit;
+}
diff --git a/plugins/kolab_files/skins/larry/templates/compose_plugin.html b/plugins/kolab_files/skins/larry/templates/compose_plugin.html
index 613a24a4..ceb56cce 100644
--- a/plugins/kolab_files/skins/larry/templates/compose_plugin.html
+++ b/plugins/kolab_files/skins/larry/templates/compose_plugin.html
@@ -1,27 +1,31 @@
<div id="files-compose-dialog" class="uidialog" role="dialog" aria-labelledby="aria-label-fileselect" aria-hidden="true">
<h2 id="aria-label-fileselect" class="voice"><roundcube:label name="kolab_files.arialabelfileselectdialog" /></h2>
<div id="quicksearchbar" class="searchbox" role="search" aria-labelledby="aria-label-searchform">
<h3 id="aria-label-searchform" class="voice"><roundcube:label name="kolab_files.arialabelsearchform" /></h3>
<label for="quicksearchbox" class="voice"><roundcube:label name="arialabelquicksearchbox" /></label>
<roundcube:button name="filesearchmenulink" id="filesearchmenulink" class="iconbutton searchoptions" onclick="return UI.toggle_popup('filesearchmenu', event)" title="searchmod" label="options" aria-haspopup="true" aria-expanded="false" aria-owns="filesearchmenu-menu" />
<roundcube:object name="file-search-form" id="filesearchbox" />
<roundcube:button command="files-search-reset" id="searchreset" class="iconbutton reset" title="resetsearch" content=" " />
</div>
<div id="folderlistbox" class="uibox listbox" role="navigation" aria-labelledby="aria-label-folderlist">
<h3 id="aria-label-folderlist" class="voice"><roundcube:label name="kolab_files.arialabelfolderlist" /></h3>
<div id="files-folder-list" class="scroller"></div>
</div>
<div id="filelistcontainer" class="boxlistcontent uibox">
<h3 id="aria-label-filelist" class="voice"><roundcube:label name="arialabelfilelist" /></h3>
<roundcube:object name="filelist" id="filelist" class="records-table filelist sortheader fixedheader" aria-labelledby="aria-label-filelist" />
</div>
</div>
<div id="filesearchmenu" class="popupmenu" style="z-index: 2000" data-editable="true" aria-hidden="true">
<h4 id="aria-label-searchmenu" class="voice"><roundcube:label name="searchmod" /></h4>
<ul id="filesearchmenu-menu" class="toolbarmenu" role="menu" aria-labelledby="aria-label-searchmenu">
<li role="menuitem"><label><input type="checkbox" name="all_folders" value="1" id="search_all_folders" /> <span><roundcube:label name="kolab_files.allfolders" /></span></label></li>
</ul>
</div>
+
+<div id="files-folder-auth-dialog" role="dialog" aria-labelledby="aria-label-folderauthform" aria-hidden="true">
+ <h3 id="aria-label-folderauthform" class="voice"><roundcube:label name="kolab_files.arialabelfolderauthform" /></h3>
+</div>
diff --git a/plugins/kolab_files/skins/larry/templates/files.html b/plugins/kolab_files/skins/larry/templates/files.html
index e6e25005..e33d1d09 100644
--- a/plugins/kolab_files/skins/larry/templates/files.html
+++ b/plugins/kolab_files/skins/larry/templates/files.html
@@ -1,136 +1,146 @@
<roundcube:object name="doctype" value="html5" />
<html>
<head>
<title><roundcube:object name="pagetitle" /></title>
<roundcube:include file="/includes/links.html" />
<script src="plugins/kolab_files/skins/larry/ui.js" type="text/javascript"></script>
</head>
<body class="files noscroll">
<roundcube:include file="/includes/header.html" />
<div id="mainscreen">
<h1 class="voice"><roundcube:label name="kolab_files.files" /></h1>
<div id="filestoolbar" class="toolbar" role="toolbar" aria-labelledby="aria-label-toolbar">
<h2 id="aria-label-toolbar" class="voice"><roundcube:label name="arialabeltoolbar" /></h2>
<form id="filesuploadform">
<roundcube:button command="files-upload" type="link" class="button upload disabled" classAct="button upload" classSel="button upload pressed" label="kolab_files.upload" title="kolab_files.uploadfile" />
</form>
<roundcube:button command="files-get" type="link" class="button get disabled" classAct="button get" classSel="button get pressed" label="kolab_files.get" title="kolab_files.getfile" />
<roundcube:button command="files-open" type="link" class="button open disabled" classAct="button open" classSel="button open pressed" label="kolab_files.view" title="kolab_files.viewfile" />
<roundcube:button command="files-delete" type="link" class="button delete disabled" classAct="button delete" classSel="button delete pressed" label="delete" title="kolab_files.deletefile" />
</div>
<div id="quicksearchbar" class="quicksearchbox" role="search" aria-labelledby="aria-label-searchform">
<h2 id="aria-label-searchform" class="voice"><roundcube:label name="kolab_files.arialabelsearchform" /></h2>
<label for="quicksearchbox" class="voice"><roundcube:label name="arialabelquicksearchbox" /></label>
- <roundcube:button name="filesearchmenulink" id="filesearchmenulink" class="iconbutton searchoptions" onclick="return UI.toggle_popup('filesearchmenu', event)" title="searchmod" label="options" aria-haspopup="true" aria-expanded="false" aria-owns="filesearchmenu-menu" />
+ <roundcube:button name="filesearchmenulink" id="filesearchmenulink" class="iconbutton searchoptions" onclick="UI.toggle_popup('filesearchmenu', event); return false" title="searchmod" label="options" aria-haspopup="true" aria-expanded="false" aria-owns="filesearchmenu-menu" />
<roundcube:object name="file-search-form" id="quicksearchbox" />
<roundcube:button command="files-search-reset" id="searchreset" class="iconbutton reset" title="resetsearch" label="resetsearch" />
</div>
<div id="folderlistbox" class="uibox listbox" role="navigation" aria-labelledby="aria-label-folderlist">
<h2 id="aria-label-folderlist" class="voice"><roundcube:label name="kolab_files.arialabelfolderlist" /></h2>
<div id="files-folder-list" class="scroller withfooter">
</div>
<div id="folderlist-footer" class="boxfooter">
<roundcube:button command="folder-create" type="link" title="kolab_files.foldercreate" class="listbutton add" classAct="listbutton add" innerClass="inner" content="+" /><roundcube:button name="folderoptions" id="folderoptionslink" type="link" title="moreactions" class="listbutton groupactions" onclick="return UI.toggle_popup('folderoptions', event)" innerClass="inner" content="⚙" aria-haspopup="true" aria-expanded="false" aria-owns="folderoptionsmenu" />
<roundcube:if condition="env:files_quota" />
<span class="voice"><roundcube:label name="quota"></span>
<roundcube:object name="filequotadisplay" id="quotadisplay" class="countdisplay" display="text" />
<roundcube:endif />
</div>
</div>
<div id="filelistcontainer" class="uibox">
<h2 id="aria-label-filelist" class="voice"><roundcube:label name="arialabelfilelist" /></h2>
<div id="filelistbox" class="boxlistcontent">
<roundcube:object name="filelist" id="filelist" class="records-table filelist sortheader fixedheader" optionsmenuIcon="true" aria-labelledby="aria-label-filelist" />
</div>
</div>
</div>
<div id="folderoptions" class="popupmenu" data-editable="true" aria-hidden="true">
<h3 id="aria-label-folderoptions" class="voice"><roundcube:label name="kolab_files.folderoptions" /></h3>
<ul id="folderoptionsmenu" class="toolbarmenu" role="menu" aria-labelledby="aria-label-folderoptions">
<!--
<li role="menuitem"><roundcube:button command="files-folder-edit" label="edit" classAct="active" /></li>
-->
<li role="menuitem"><roundcube:button command="files-folder-delete" label="delete" classAct="active" /></li>
+ <roundcube:if condition="!empty(env:external_sources)" />
+ <li role="menuitem"><roundcube:button command="folder-mount" label="kolab_files.foldermount" classAct="active" /></li>
+ <roundcube:endif />
<li role="menuitem"><roundcube:button command="folders" task="settings" type="link" label="managefolders" classAct="active" /></li>
<roundcube:container name="filesfolderoptions" id="folderoptionsmenu" />
</ul>
</div>
<div id="files-folder-create-dialog" role="dialog" aria-labelledby="aria-label-foldercreateform" aria-hidden="true">
<h3 id="aria-label-foldercreateform" class="voice"><roundcube:label name="kolab_files.arialabelfoldercreateform" /></h3>
<roundcube:object name="folder-create-form" />
</div>
+<div id="files-folder-mount-dialog" role="dialog" aria-labelledby="aria-label-foldermountform" aria-hidden="true">
+ <h3 id="aria-label-foldermountform" class="voice"><roundcube:label name="kolab_files.arialabelfoldermountform" /></h3>
+ <roundcube:object name="folder-mount-form" />
+</div>
<div id="files-file-edit-dialog" role="dialog" aria-labelledby="aria-label-fileeditform" aria-hidden="true">
<h3 id="aria-label-fileeditform" class="voice"><roundcube:label name="kolab_files.arialabelfileeditform" /></h3>
<roundcube:object name="file-edit-form" />
</div>
+<div id="files-folder-auth-dialog" role="dialog" aria-labelledby="aria-label-folderauthform" aria-hidden="true">
+ <h3 id="aria-label-folderauthform" class="voice"><roundcube:label name="kolab_files.arialabelfolderauthform" /></h3>
+</div>
<div id="listoptions" class="propform popupdialog" data-editable="true" role="dialog" aria-labelledby="aria-label-listoptions" aria-hidden="true">
<h3 id="aria-label-listoptions" class="voice"><roundcube:label name="kolab_files.arialabellistoptions" /></h3>
-<roundcube:if condition="!in_array('kolab_files_list_cols', (array)config:dont_override)" />
+ <roundcube:if condition="!in_array('kolab_files_list_cols', (array)config:dont_override)" />
<fieldset class="floating">
<legend><roundcube:label name="listcolumns" /></legend>
<ul class="proplist">
<li><label class="disabled"><input type="checkbox" name="list_col[]" value="options" checked="checked" disabled="disabled" /> <span><roundcube:label name="options" /></span></label></li>
<li><label class="disabled"><input type="checkbox" name="list_col[]" value="name" checked="checked" disabled="disabled" /> <span><roundcube:label name="kolab_files.name" /></span></label></li>
<li><label><input type="checkbox" name="list_col[]" value="mtime" /> <span><roundcube:label name="kolab_files.mtime" /></span></label></li>
<li><label><input type="checkbox" name="list_col[]" value="size" /> <span><roundcube:label name="size" /></span></label></li>
</ul>
</fieldset>
<roundcube:endif />
<roundcube:if condition="!in_array('kolab_files_sort_col', (array)config:dont_override)" />
<fieldset class="floating">
<legend><roundcube:label name="listsorting" /></legend>
<ul class="proplist">
<li><label><input type="radio" name="sort_col" value="name" /> <span><roundcube:label name="kolab_files.name" /></span></label></li>
<li><label><input type="radio" name="sort_col" value="mtime" /> <span><roundcube:label name="kolab_files.mtime" /></span></label></li>
<li><label><input type="radio" name="sort_col" value="size" /> <span><roundcube:label name="size" /></span></label></li>
</ul>
</fieldset>
<roundcube:endif />
<roundcube:if condition="!in_array('kolab_files_sort_order', (array)config:dont_override)" />
<fieldset class="floating">
<legend><roundcube:label name="listorder" /></legend>
<ul class="proplist">
<li><label><input type="radio" name="sort_ord" value="ASC" /> <span><roundcube:label name="asc" /></span></label></li>
<li><label><input type="radio" name="sort_ord" value="DESC" /> <span><roundcube:label name="desc" /></span></label></li>
</ul>
</fieldset>
<roundcube:endif />
<br style="clear:both" />
<div class="formbuttons">
<roundcube:button command="menu-save" id="listmenusave" type="input" class="button mainaction" label="save" />
<roundcube:button command="menu-close" prop="filelistmenu" id="listmenucancel" type="input" class="button" label="cancel" />
</div>
</div>
<div id="dragfilemenu" class="popupmenu" aria-hidden="true">
<ul class="toolbarmenu" role="menu">
<li role="menuitem"><roundcube:button command="files-move" onclick="return kolab_files_drag_menu_action('files-move')" label="move" classAct="active" /></li>
<li role="menuitem"><roundcube:button command="files-copy" onclick="return kolab_files_drag_menu_action('files-copy')" label="copy" classAct="active" /></li>
</ul>
</div>
<div id="filesearchmenu" class="popupmenu" data-editable="true" aria-hidden="true">
<h3 id="aria-label-searchmenu" class="voice"><roundcube:label name="searchmod" /></h3>
<ul id="filesearchmenu-menu" class="toolbarmenu" role="menu" aria-labelledby="aria-label-searchmenu">
<li role="menuitem"><label><input type="checkbox" name="all_folders" value="1" id="search_all_folders" /> <span><roundcube:label name="kolab_files.allfolders" /></span></label></li>
</ul>
</div>
<roundcube:include file="/includes/footer.html" />
<script type="text/javascript">
kolab_files_ui_init();
</script>
</body>
</html>
diff --git a/plugins/kolab_files/skins/larry/templates/message_plugin.html b/plugins/kolab_files/skins/larry/templates/message_plugin.html
index eeb78fd8..26c25256 100644
--- a/plugins/kolab_files/skins/larry/templates/message_plugin.html
+++ b/plugins/kolab_files/skins/larry/templates/message_plugin.html
@@ -1,21 +1,25 @@
<div id="files-dialog" class="uidialog" data-editable="true" role="dialog" aria-labelledby="aria-label-filesavedialog" aria-hidden="true">
<h3 id="aria-label-filesavedialog" class="voice"><roundcube:label name="kolab_files.arialabelfilesavedialog" /></h3>
<div id="file-save-as">
<label for="file-save-as-input"><roundcube:label name="kolab_files.saveas" /></label>
<input id="file-save-as-input" type="text" value="">
</div>
<div id="folderlistbox" class="uibox listbox" role="navigation" aria-labelledby="aria-label-folderlist">
<h3 id="aria-label-folderlist" class="voice"><roundcube:label name="kolab_files.arialabelfolderlist" /></h3>
<div id="files-folder-list" class="scroller withfooter"></div>
<div id="folderlist-footer" class="boxfooter">
<roundcube:button command="folder-create" name="foldercreatelink" id="foldercreatelink" type="link" title="createfolder" class="listbutton add" classAct="listbutton add" innerClass="inner" content="+" />
</div>
</div>
</div>
<div id="files-folder-create-dialog" role="dialog" aria-labelledby="aria-label-foldercreateform" aria-hidden="true">
<h3 id="aria-label-foldercreateform" class="voice"><roundcube:label name="kolab_files.arialabelfoldercreateform" /></h3>
<roundcube:object name="folder-create-form" />
</div>
+<div id="files-folder-auth-dialog" role="dialog" aria-labelledby="aria-label-folderauthform" aria-hidden="true">
+ <h3 id="aria-label-folderauthform" class="voice"><roundcube:label name="kolab_files.arialabelfolderauthform" /></h3>
+</div>
+
<script src="plugins/kolab_files/skins/larry/ui.js" type="text/javascript"></script>
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Tue, Jun 10, 3:23 AM (1 d, 17 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
196937
Default Alt Text
(130 KB)
Attached To
Mode
R14 roundcubemail-plugins-kolab
Attached
Detach File
Event Timeline
Log In to Comment