roundcubemail-plugins-kolab/plugins/kolab_files/kolab_files.js
Aleksander Machniak 9f5a0da9d9 Files: Fix token refresh (when using SSO and Kolab4 files API)
Summary: Fixes order of registering hook handlers. Properly propagates the refreshed token to the client-side

Reviewers: #roundcube_kolab_plugins_developers, mollekopf

Reviewed By: #roundcube_kolab_plugins_developers, mollekopf

Subscribers: #roundcube_kolab_plugins_developers

Differential Revision: https://git.kolab.org/D5139
2025-03-24 10:58:10 +01:00

4406 lines
132 KiB
JavaScript

/**
* 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-2015, 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 && window.files_api && rcmail.addEventListener('init', function() {
if (rcmail.task == 'mail') {
// mail compose
if (rcmail.env.action == 'compose') {
kolab_files_from_cloud_widget($('#compose-attachments > div'));
// 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($('.header-content > .header-links').length ? $('.header-links > a:last') : attachment_list);
}
rcmail.addEventListener('menu-open', kolab_files_attach_menu_open);
rcmail.enable_command('folder-create', true);
}
// attachment preview
else if (rcmail.env.action == 'get') {
rcmail.enable_command('folder-create', true);
}
if (!rcmail.env.action || rcmail.env.action == 'show' || rcmail.env.action == 'preview') {
// add "attach from cloud" button for event/task dialog in mail
rcmail.addEventListener('plugin.mail2event_dialog', function() {
if (!$('#calendar-attachment-form a.fromcloud').length)
kolab_files_from_cloud_widget($('#calendar-attachment-form > div.buttons'));
});
}
}
else if (rcmail.task == 'calendar') {
// add "attach from cloud" button for event dialog
if (!rcmail.env.action)
kolab_files_from_cloud_widget($('#calendar-attachment-form > div.buttons, #edit-attachments-form'));
}
else if (rcmail.task == 'tasks') {
// add "attach from cloud" button for task dialog
if (!rcmail.env.action)
kolab_files_from_cloud_widget($('#taskedit-attachment-form > div.buttons, #taskedit-attachments-form'));
}
else if (rcmail.task == 'files') {
if (rcmail.gui_objects.fileslist) {
rcmail.fileslist = new rcube_list_widget(rcmail.gui_objects.fileslist, {
multiselect: true,
draggable: true,
keyboard: true,
column_movable: rcmail.env.files_col_movable,
dblclick_time: rcmail.dblclick_time
});
rcmail.fileslist.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('dragstart', function(e) { kolab_files_drag_start(e); })
.addEventListener('dragmove', function(e) { kolab_files_drag_move(e); })
.addEventListener('dragend', function(e) { kolab_files_drag_end(e); })
.addEventListener('column_replace', function(e) { kolab_files_set_coltypes(e, 'files'); });
rcmail.enable_command('menu-open', 'menu-save', 'files-sort', 'files-search', 'files-search-reset', 'folder-create', true);
rcmail.fileslist.init();
kolab_files_list_coltypes('files');
kolab_files_drag_drop_init($(rcmail.gui_objects.fileslist).parents('.droptarget'));
}
if (rcmail.gui_objects.sessionslist) {
rcmail.sessionslist = new rcube_list_widget(rcmail.gui_objects.sessionslist, {
keyboard: true,
column_movable: rcmail.env.sessions_col_movable,
dblclick_time: rcmail.dblclick_time
});
rcmail.sessionslist.addEventListener('dblclick', function(o) { kolab_files_sessions_list_dblclick(o); })
.addEventListener('select', function(o) { kolab_files_sessions_list_select(o); })
.addEventListener('keypress', function(o) { kolab_files_sessions_list_keypress(o); })
.addEventListener('column_replace', function(e) { kolab_files_set_coltypes(e, 'sessions'); });
rcmail.sessionslist.init();
kolab_files_list_coltypes('sessions');
}
// "one file only" commands
rcmail.env.file_commands = ['files-get', 'files-rename'];
// "one or more file" commands
rcmail.env.file_commands_all = ['files-delete', 'files-move', 'files-copy'];
if (rcmail.env.action == 'open' || rcmail.env.action == 'edit') {
rcmail.enable_command('files-get', true);
rcmail.enable_command('files-delete', rcmail.env.file_data.writable);
}
else {
rcmail.enable_command('folder-mount', rcmail.env.external_sources);
}
}
kolab_files_init();
rcmail.addEventListener('responsebeforerefresh', kolab_files_token_refresh);
rcmail.addEventListener('responsebeforekeep-alive', kolab_files_token_refresh);
});
/**********************************************************/
/********* Shared functionality **********/
/**********************************************************/
// Initializes API object
function kolab_files_init()
{
if (window.file_api)
return;
var editor_config = {};
// 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: rcmail.env.files_url.replace(/\/api\/?$/, '/resources'),
caps: rcmail.env.files_caps,
supported_mimetypes: rcmail.env.file_mimetypes
});
file_api.translations = rcmail.labels;
if (rcmail.task == 'files') {
if (rcmail.env.action == 'edit' && rcmail.env.editor_type) {
// Extract the domain here, it can't be done by Chwala
// when using WOPI, which does not set iframe src attribute
var domain, href = rcmail.env.file_data.viewer.href;
if (href && /^(https?:\/\/[^/]+)/i.test(href))
domain = RegExp.$1;
editor_config = {
// UI elements
iframe: $('#fileframe').get(0),
domain: domain,
export_menu: rcmail.gui_objects.exportmenu ? $('ul', rcmail.gui_objects.exportmenu).get(0) : null,
title_input: $('#document-title').get(0),
members_list: $('#members').get(0),
photo_url: '?_task=addressbook&_action=photo&_error=1&_email=%email',
photo_default_url: rcmail.env.photo_placeholder,
// events
ready: function(data) { document_editor_init(); },
sessionClosed: function(data) { return document_editor_close(); }
};
if (rcmail.env.file_data.writable)
editor_config.documentChanged = function(data) { rcmail.enable_command('document-save', true); };
}
else if (rcmail.env.action == 'open') {
// initialize folders list (for dialogs)
// file_api.folder_list();
// get ongoing sessions
file_api.request('folder_info', {folder: file_api.file_path(rcmail.env.file), sessions: 1}, 'folder_info_response');
}
else if (rcmail.env.action == 'share') {
kolab_files_share_form_init();
}
else {
file_api.env.init_folder = rcmail.env.folder;
file_api.env.init_collection = rcmail.env.collection;
file_api.folder_list();
file_api.browser_capabilities_check();
if (rcmail.env.contextmenu) {
rcmail.env.folders_cm = rcmail.contextmenu.init({menu_name: 'foldercontextmenu', menu_source: '#folderoptions > ul', list_object: 'folder_list'}, {
addmenuitem: function(p) {
// don't add Mount option to the menu
var str = $(p.el).children('a').first().attr('onclick');
if (str && str.match(/folder-mount/))
return {result: false, abort: true};
},
activate: function(p) {
var folder = rcmail.env.context_menu_source_id;
switch (p.command) {
case 'files-folder-delete':
case 'folder-rename':
return !folder.match(/^folder-collection-(.*)$/);
case 'folder-share':
return !folder.match(/^folder-collection-(.*)$/) && file_api.is_shareable(folder);
case 'folder-create':
case 'folder-mount':
return true;
}
},
beforecommand: function(e) {
rcmail.env.file_api_context = [file_api.env.folder, file_api.env.collection];
file_api.env.folder = rcmail.env.context_menu_source_id;
},
aftercommand: function(e) {
file_api.env.folder = rcmail.env.file_api_context[0];
file_api.env.collection = rcmail.env.file_api_context[1];
}
});
}
}
}
if (rcmail.env.files_caps && !rcmail.env.framed && rcmail.env.files_caps.DOCEDIT)
$.extend(editor_config, {
// invitation notifications
api: file_api,
owner: rcmail.env.files_user,
interval: rcmail.env.files_interval || 60,
invitationMore: true,
invitationChange: document_editor_invitation_handler
});
$.extend(editor_config, {
// notifications/alerts
gettext: function(label) { return rcmail.get_label(label, 'kolab_files'); },
set_busy: function(state, message) { return rcmail.set_busy(state, message ? 'kolab_files.' + message : ''); },
hide_message: function(id) { return rcmail.hide_message(id); },
display_message: function(label, type, is_txt, timeout) {
if (!is_txt)
label = 'kolab_files.' + label;
return rcmail.display_message(label, type, timeout * 1000);
}
});
if (window.document_editor_api)
document_editor = new document_editor_api(editor_config);
else
document_editor = new manticore_api(editor_config);
rcmail.addEventListener('responseafterreset', function(o) {
// update caps/mountpoints on reset
file_api.set_env({caps: rcmail.env.files_caps});
});
};
// 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 rcmail.is_framed() && parent.rcmail.env.files_token ? parent.rcmail.env.files_token : rcmail.env.files_token;
};
// Event handler for refresh/keep-alive action that can bring a refreshed access token
function kolab_files_token_refresh(data)
{
var i, win, env = data && data.response ? data.response.env : {};
if (!env.files_token || rcmail.env.files_token == env.files_token) {
return;
}
file_api.set_env({ token: env.files_token });
// Propagate the change to all iframes
for (i = 0; i < window.frames.length; i++) {
win = window.frames[i];
try {
win.file_api.set_env({ token: env.files_token });
} catch (e) {
// ignore
}
}
};
function kolab_files_from_cloud_widget(elem)
{
$('<a class="button btn btn-secondary fromcloud">')
.attr('tabindex', $('button,input', elem).first().attr('tabindex') || 0)
.text(rcmail.gettext('kolab_files.fromcloud'))
.click(function() { kolab_files_selector_dialog(); })
.appendTo(elem);
if (rcmail.gui_objects.fileslist) {
rcmail.fileslist = new rcube_list_widget(rcmail.gui_objects.fileslist, {
multiselect: true,
keyboard: true,
column_movable: false,
dblclick_time: rcmail.dblclick_time
});
rcmail.fileslist.addEventListener('select', function(o) { kolab_files_list_select(o); });
rcmail.enable_command('files-sort', 'files-search', 'files-search-reset', true);
rcmail.fileslist.init();
kolab_files_list_coltypes();
}
}
// 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 = $.trim(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 () {
if (!file_api.env.folder)
return;
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() {
rcmail.env.folder_list_selector = '#files-dialog #files-folder-list';
rcmail.env.folder_search_selector = '#files-dialog #foldersearch';
file_api.folder_list({writable: 1});
rcmail.env.folders_loaded = true;
};
}
// show dialog window
kolab_dialog_show(dialog, {
title: rcmail.gettext('kolab_files.' + label),
buttons: buttons,
button_classes: ['mainaction save', 'cancel'],
classes: {'ui-dialog': 'selection-dialog files-dialog'},
minWidth: 250,
minHeight: 300,
height: 400,
width: 300
}, fn);
// add link for "more options" drop-down
if (!dialog.find('foldercreatelink').length)
$('<a>')
.attr({href: '#', 'class': 'btn btn-link options add-folder'})
.text(rcmail.gettext('kolab_files.addfolder'))
.click(function(e) { rcmail.command('folder-create', '', this, e); })
.prependTo(dialog.parent().parent().find('.ui-dialog-buttonset'));
// "enable" folder creation when dialog is displayed in parent window
if (rcmail.is_framed()) {
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,
button_classes: ['mainaction save', 'cancel'],
classes: {'ui-dialog': 'selection-dialog files-dialog'},
minWidth: 500,
minHeight: 300,
width: 700,
height: 500
}, function() { rcmail.fileslist.resize(); });
if (!rcmail.env.files_loaded) {
rcmail.env.folder_list_selector = '#files-compose-dialog #files-folder-list';
rcmail.env.folder_search_selector = '#files-compose-dialog #foldersearch';
file_api.folder_list();
rcmail.env.files_loaded = true;
}
else {
rcmail.fileslist.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,
button_classes: ['mainaction save', 'cancel'],
height: 200
});
// Fix submitting form with Enter
$('form', dialog).submit(kolab_dialog_submit_handler);
// build parent selector
file_api.folder_select_element(select, {empty: !rcmail.env.files_caps.NOROOT, writable: true});
};
// folder edit dialog
function kolab_files_folder_edit_dialog()
{
var dialog = $('#files-folder-edit-dialog'),
buttons = {},
separator = file_api.env.directory_separator,
current_folder = file_api.env.folder,
arr = current_folder.split(separator),
folder = arr.pop(),
path = arr.join(separator),
select = $('select[name="parent"]', dialog).html(''),
input = $('input[name="name"]', dialog).val(folder);
buttons[rcmail.gettext('kolab_files.save')] = function () {
var folder = '', name = input.val(), parent = select.val();
if (!name)
return;
if (parent)
folder = parent + separator;
folder += name;
file_api.folder_rename(current_folder, 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.folderedit'),
buttons: buttons,
button_classes: ['mainaction save', 'cancel'],
height: 200
});
// Fix submitting form with Enter
$('form', dialog).submit(kolab_dialog_submit_handler);
// build parent selector
file_api.folder_select_element(select, {selected: path, empty: !rcmail.env.files_caps.NOROOT});
};
// folder sharing dialog
function kolab_files_folder_share_dialog()
{
var dialog = $('<iframe>').attr('src', rcmail.url('share', {_folder: file_api.env.folder, _framed: 1}));
rcmail.simple_dialog(dialog, rcmail.gettext('kolab_files.foldershare'), null, {
cancel_button: 'close',
width: 600,
height: 500
});
};
// 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;
}
});
$('.auth-options input', dialog).each(function() {
args[this.name] = this.type == 'checkbox' && !this.checked ? '' : this.value;
});
file_api.folder_mount(args);
kolab_dialog_close(this);
};
args.buttons[rcmail.gettext('kolab_files.cancel')] = function () {
kolab_dialog_close(this);
};
// initialize drivers list
if (!rcmail.drivers_list_initialized) {
rcmail.drivers_list_initialized = true;
$('td.source', dialog).each(function() {
var td = $(this),
id = td.attr('id').replace('source-', ''),
meta = rcmail.env.external_sources[id];
$.each(meta.form_values || [], function(i, v) {
td.find('#source-' + id + '-' + i).val(v);
});
td.click(function() {
$('td.selected', dialog).removeClass('selected');
dialog.find('.driverform').hide();
$(this).addClass('selected').find('.driverform').show();
$('input[type="radio"]', this).prop('checked', true);
});
});
}
args.button_classes = ['mainaction save', 'cancel'];
// show dialog window
kolab_dialog_show(dialog, args, function() {
$('td.source:first', dialog).click();
input.focus();
});
};
// file edit dialog
function kolab_files_file_edit_dialog(file, sessions, readonly)
{
var content = [], items = [], height = 300,
dialog = $('#files-file-edit-dialog'),
buttons = {}, name = file_api.file_name(file),
title = rcmail.gettext('kolab_files.editfiledialog'),
mainaction = rcmail.gettext('kolab_files.select'),
item_fn = function(id, txt, classes) {
return $('<label>').attr('class', 'session' + (classes ? ' ' + classes : ''))
.append($('<input>').attr({name: 'opt', value: id, type: 'radio'})).append($('<span>').text(txt));
},
select_fn = function(dlg) {
var session, input = $('input:checked', dialog), id = input.val();
if (dlg)
kolab_dialog_close(dlg);
if (id && input.parent().is('.session.request')) {
document_editor.invitation_request({session_id: id});
return;
}
if (readonly && (id == 0 || !input.length))
return kolab_files_file_create_dialog(file);
rcmail.files_edit(id ? id : true);
};
// Create sessions selection
if (sessions && sessions.length) {
items.push($('<div>').text(rcmail.gettext('kolab_files.editfilesessions')));
// first display owned sessions, then invited, other at the end
$.each(sessions, function() {
if (this.is_owner) {
var txt = rcmail.gettext('kolab_files.ownedsession');
items.push(item_fn(this.id, txt, 'owner'));
}
});
if (items.length == 1)
items.push(item_fn(0, rcmail.gettext('kolab_files.newsession' + (readonly ? 'ro' : ''))));
$.each(sessions, function() {
if (this.is_invited) {
var txt = rcmail.gettext('kolab_files.invitedsession')
.replace('$user', this.owner_name ? this.owner_name : this.owner);
items.push(item_fn(this.id, txt, 'invited'));
}
});
$.each(sessions, function() {
if (!this.is_owner && !this.is_invited) {
var txt = rcmail.gettext('kolab_files.joinsession')
.replace('$user', this.owner_name ? this.owner_name : this.owner);
items.push(item_fn(this.id, txt, 'request'));
}
});
// check the first option
$('input', items[1]).attr('checked', true);
$('div', dialog).html(items);
// if there's only one session and it's owned, skip the dialog
if (!readonly && items.length == 2 && $('input:checked', dialog).parent().is('.owner'))
return select_fn();
}
// no ongoing session, folder is readonly warning
else {
title = rcmail.gettext('kolab_files.editfilerotitle');
height = 150;
$('div', dialog).text(rcmail.gettext('kolab_files.editfilero'));
mainaction = rcmail.gettext('kolab_files.create');
}
buttons[mainaction] = function() { select_fn(this); };
buttons[rcmail.gettext('kolab_files.cancel')] = function () {
kolab_dialog_close(this);
};
// show dialog window
kolab_dialog_show(dialog, {
title: title,
buttons: buttons,
button_classes: ['mainaction save', 'cancel'],
minHeight: height - 100,
height: height
});
};
// file rename dialog
function kolab_files_file_rename_dialog(file)
{
var dialog = $('#files-file-rename-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;
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.renamefile'),
buttons: buttons,
button_classes: ['mainaction save', 'cancel'],
minHeight: 100,
height: 100
});
};
// file creation (or cloning) dialog
function kolab_files_file_create_dialog(file)
{
var buttons = {}, action = file ? 'copy' : 'create',
button_classes = ['mainaction save edit'],
dialog = $('#files-file-create-dialog'),
type_select = $('select[name="type"]', dialog),
select = $('select[name="parent"]', dialog).html(''),
input = $('input[name="name"]', dialog).val(''),
create_func = function(dialog, editaction) {
var sel, folder = select.val(), type = type_select.val(), name = input.val();
if (!name || !folder || !file_api.is_writable(folder))
return;
if (!/\.[a-z0-9]{1,5}$/.test(name)) {
name += '.' + rcmail.env.file_extensions[type];
}
name = folder + file_api.env.directory_separator + name;
// get type of cloned file
if (file) {
if (rcmail.env.file_data)
type = rcmail.env.file_data.type;
else {
sel = rcmail.fileslist.get_selection();
type = $('#rcmrow' + sel[0]).data('type');
}
}
file_api.file_create(name, type, editaction, file);
kolab_dialog_close(dialog);
};
buttons[rcmail.gettext('kolab_files.' + action + 'andedit')] = function() {
create_func(this, true);
};
if (action == 'create') {
button_classes.push('save');
buttons[rcmail.gettext('kolab_files.create')] = function() {
create_func(this);
};
type_select.parent('tr').show();
}
else {
input.val(file_api.file_name(file));
type_select.parent('tr').hide();
}
button_classes.push('cancel');
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.' + action + 'file'),
buttons: buttons,
button_classes: button_classes,
minHeight: 150,
height: 250
});
// build folder selector
file_api.folder_select_element(select, {writable: true});
};
// file session dialog
function kolab_files_session_dialog(session)
{
var buttons = {}, button_classes = [],
dialog = $('#files-session-dialog'),
filename = file_api.file_name(session.file),
owner = session.owner_name || session.owner,
title = rcmail.gettext('kolab_files.sessiondialog'),
content = rcmail.gettext('kolab_files.sessiondialogcontent'),
join_session = function(id) {
var viewer = file_api.file_type_supported('application/vnd.oasis.opendocument.text', rcmail.env.files_caps);
params = {action: 'edit', session: id};
file_api.file_open('', viewer, params);
};
content = content.replace('$file', filename).replace('$owner', owner);
$('div', dialog).text(content);
if (session.is_owner) {
buttons[rcmail.gettext('kolab_files.open')] = function() {
kolab_dialog_close(this);
join_session(session.id);
};
buttons[rcmail.gettext('kolab_files.close')] = function() {
kolab_dialog_close(this);
file_api.document_delete(session.id);
};
button_classes = ['mainaction edit', 'delete'];
}
else if (session.is_invited) {
button_classes = ['mainaction edit'];
// @TODO: check if not-accepted and provide "Decline invitation" button
// @TODO: Add "Accept button", add comment field to the dialog
buttons[rcmail.gettext('kolab_files.join')] = function() {
kolab_dialog_close(this);
join_session(session.id);
};
}
else {
button_classes = ['mainaction session request'];
buttons[rcmail.gettext('kolab_files.request')] = function() {
kolab_dialog_close(this);
// @TODO: Add comment field to the dialog
document_editor.invitation_request({session_id: session.id});
};
}
button_classes.push('cancel');
buttons[rcmail.gettext('kolab_files.cancel')] = function() {
kolab_dialog_close(this);
};
// show dialog window
kolab_dialog_show(dialog, {
title: title,
buttons: buttons,
button_classes: button_classes,
minHeight: 100,
height: 150
});
};
function kolab_dialog_show(content, params, onopen)
{
params = $.extend({
modal: true,
resizable: true,
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);
content.hide(); // for Larry's dialogs
$(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();
rcmail.ksearch_blur();
};
// This is required for Larry's dialogs
params.create = function() { content.show(); };
// display it as popup
var dialog = rcmail.show_popup_dialog(content, params.title, params.buttons, params);
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-dialog-buttonpane button').first().click();
return false;
};
// Hides dialog
function kolab_dialog_close(dialog, destroy)
{
(rcmail.is_framed() ? window.parent : window).$(dialog).dialog(destroy ? 'destroy' : '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 files/sessions list global variables
function kolab_files_list_coltypes(type)
{
if (!type) type = 'files';
var n, list = rcmail[type + 'list'];
rcmail.env.subject_col = null;
if ((n = $.inArray('name', rcmail.env[type + '_coltypes'])) >= 0) {
rcmail.env.subject_col = n;
list.subject_col = n;
}
list.init_header();
};
function kolab_files_set_list_options(cols, sort_col, sort_order, type)
{
var update = 0, i, idx, name, newcols = [], oldcols = rcmail.env[type + '_coltypes'];
if (sort_col === undefined)
sort_col = rcmail.env[type + '_sort_col'];
if (!sort_order)
sort_order = rcmail.env[type + '_sort_order'];
if (rcmail.env[type + '_sort_col'] != sort_col || rcmail.env[type + '_sort_order'] != sort_order) {
update = 1;
// set table header class
kolab_files_set_list_sorting(sort_col, sort_order, type);
}
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(type + '-list', {sort: sort_col, reverse: sort_order == 'DESC'});
else if (update) {
rcmail.http_post('files/prefs', {
type: type,
kolab_files_list_cols: oldcols,
kolab_files_sort_col: sort_col,
kolab_files_sort_order: sort_order
}, rcmail.set_busy(true, 'loading'));
}
};
function kolab_files_set_list_sorting(sort_col, sort_order, type)
{
// set table header class
var old_col = rcmail.env[type + '_sort_col'],
old_sort = rcmail.env[type + '_sort_order'];
$('#rcm' + old_col).removeClass('sortedASC sortedDESC');
$('#rcm' + sort_col).addClass('sorted' + sort_order);
rcmail.env[type + '_sort_col'] = sort_col;
rcmail.env[type + '_sort_order'] = sort_order;
};
function kolab_files_set_coltypes(list, type)
{
var i, found, name, cols = list.list.tHead.rows[0].cells;
rcmail.env[type + '_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[type + '_coltypes'].push(name);
}
// if ((found = $.inArray('name', rcmail.env.files_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[type + '_coltypes'], type: type});
};
function kolab_files_sessions_list_dblclick(list)
{
rcmail.command('sessions-open');
};
function kolab_files_sessions_list_select(list)
{
var selected = list.selection.length;
rcmail.enable_command('sessions-open', selected == 1);
};
function kolab_files_sessions_list_keypress(list)
{
if (list.modkey == CONTROL_KEY)
return;
if (list.key_pressed == list.ENTER_KEY) {
// use setTimeout(), otherwise the opened dialog will be immediately closed
setTimeout(function() { rcmail.command('sessions-open'); }, 50);
}
// else if (list.key_pressed == list.DELETE_KEY || list.key_pressed == list.BACKSPACE_KEY)
// rcmail.command('sessions-delete');
};
function kolab_files_list_dblclick(list)
{
rcmail.command('files-open');
};
function kolab_files_list_select(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;
if (selected == 1) {
// get file mimetype
var elem = $('tr.selected', list.list),
type = elem.data('type'),
folder = file_api.file_path(elem.data('file'));
rcmail.env.viewer = file_api.file_type_supported(type, rcmail.env.files_caps);
if (!file_api.is_writable(folder))
rcmail.enable_command('files-delete', 'files-rename', false);
}
else
rcmail.env.viewer = 0;
rcmail.enable_command('files-edit', (rcmail.env.viewer & 4) == 4);
rcmail.enable_command('files-open', rcmail.env.viewer);
};
function kolab_files_list_keypress(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');
};
function kolab_files_drag_start(e)
{
rcmail.env.drag_target = null;
if (rcmail.folder_list)
rcmail.folder_list.drag_start();
};
function kolab_files_drag_end(e)
{
if (rcmail.folder_list) {
rcmail.folder_list.drag_end();
if (rcmail.env.drag_target) {
var modkey = rcube_event.get_modifier(e),
menu = rcmail.gui_objects.file_dragmenu;
rcmail.fileslist.draglayer.hide();
if (menu && modkey == SHIFT_KEY && rcmail.commands['files-copy']) {
var pos = rcube_event.get_mouse_pos(e);
rcmail.show_menu(menu.id, true, e);
$(menu).css({top: (pos.y-10)+'px', left: (pos.x-10)+'px'});
return;
}
rcmail.command('files-move', rcmail.env.drag_target);
rcmail.env.drag_target = null;
}
}
};
function kolab_files_drag_move(e)
{
if (rcmail.folder_list) {
var mouse = rcube_event.get_mouse_pos(e);
rcmail.env.drag_target = rcmail.folder_list.intersects(mouse, true);
}
};
function kolab_files_drag_menu_action(command)
{
var menu = rcmail.gui_objects.file_dragmenu;
if (menu)
$(menu).hide();
rcmail.command(command, rcmail.env.drag_target);
};
function kolab_files_selected()
{
var files = [];
$.each(rcmail.fileslist.get_selection(), function(i, v) {
var name, row = $('#rcmrow'+v);
if (row.length == 1 && (name = row.data('file')))
files.push(name);
});
return files;
};
function kolab_files_frame_load(frame)
{
var win = frame.contentWindow,
info = rcmail.env.file_data;
try {
rcmail.file_editor = win.file_editor;
}
catch (e) {};
rcmail.enable_command('files-edit', (rcmail.file_editor && rcmail.file_editor.editable)
|| rcmail.env.editor_type
|| (file_api.file_type_supported(rcmail.env.file_data.type, rcmail.env.files_caps) & 4));
rcmail.enable_command('files-print', (rcmail.file_editor && rcmail.file_editor.printable)
|| (info && /^image\//i.test(info.type)));
// Enable image tools
rcmail.enable_command('image-scale', 'image-rotate', info && !!/^image\//.test(info.type));
rcmail.gui_objects.messagepartframe = frame;
// on edit page switch immediately to edit mode
if (rcmail.file_editor && rcmail.file_editor.editable && rcmail.env.action == 'edit')
rcmail.files_edit();
// 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) {};
};
// activate html5 file drop feature (if browser supports it)
function kolab_files_drag_drop_init(container)
{
if (!window.FormData && !(window.XMLHttpRequest && XMLHttpRequest.prototype && XMLHttpRequest.prototype.sendAsBinary)) {
return;
}
if (!container.length)
return;
$(document.body).bind('dragover dragleave drop', function(e) {
if (!file_api.is_writable())
return;
e.preventDefault();
container[e.type == 'dragover' ? 'addClass' : 'removeClass']('active');
});
container.bind('dragover dragleave', function(e) {
return kolab_files_drag_hover(e);
})
container.children('div').bind('dragover dragleave', function(e) {
return kolab_files_drag_hover(e);
})
container.get(0).addEventListener('drop', function(e) {
// abort event and reset UI
kolab_files_drag_hover(e);
return file_api.file_drop(e);
}, false);
};
// handler for drag/drop on element
function kolab_files_drag_hover(e)
{
if (!file_api.is_writable())
return;
e.preventDefault();
e.stopPropagation();
var elem = $(e.target);
if (!elem.hasClass('droptarget'))
elem = elem.parents('.droptarget');
elem[e.type == 'dragover' ? 'addClass' : 'removeClass']('hover');
};
// returns localized file size
function kolab_files_file_size(size)
{
var i, units = ['GB', 'MB', 'KB', 'B'];
size = file_api.file_size(size);
for (i = 0; i < units.length; i++)
if (size.toUpperCase().indexOf(units[i]) > 0)
return size.replace(units[i], rcmail.gettext(units[i]));
return size;
};
function kolab_files_progress_str(param)
{
var current, total = file_api.file_size(param.total).toUpperCase();
if (total.indexOf('GB') > 0)
current = parseFloat(param.current/1073741824).toFixed(1);
else if (total.indexOf('MB') > 0)
current = parseFloat(param.current/1048576).toFixed(1);
else if (total.indexOf('KB') > 0)
current = parseInt(param.current/1024);
else
current = param.current;
total = kolab_files_file_size(param.total);
return rcmail.gettext('uploadprogress')
.replace(/\$percent/, param.percent + '%')
.replace(/\$current/, current)
.replace(/\$total/, total);
};
function kolab_files_share_form_init()
{
$('fieldset > table', rcmail.gui_objects.shareform).each(function() {
var cnt = 0,
mode = $(this).data('mode'),
single = $(this).data('single');
$('tbody > tr', this).each(function(i, row) {
if (!i) {
$('button.submit', row).on('click', function() { file_api.sharing_submit(rcmail.env.folder, row, mode); });
$('input[data-autocomplete]').each(function() {
var input = $(this), req_name = 'filesac-' + mode;
kolab_files_autocomplete(input, req_name, function(e) {
$(row).find('input[type=hidden]').each(function() {
$(this).val(e.data[this.name] || '');
});
});
});
}
else {
cnt++;
$('button.delete', row).on('click', function() { file_api.sharing_delete(rcmail.env.folder, row, mode); });
$('select,input[type=text]', row).on('change', function() { file_api.sharing_update(rcmail.env.folder, row, mode); });
}
});
if (single && cnt) {
$('tbody > tr:first', this).find('button.submit, input, select').prop('disabled', true);
}
});
};
function kolab_files_autocomplete(input, action, insert_callback)
{
rcmail.init_address_input_events(input, {action: action});
// "Redirect" autocomplete requests to Chwala API
if (rcmail.env.files_api_version > 3) {
rcmail.addEventListener('request' + action, function(post) {
var params = {search: post._search, mode: input.data('autocomplete') || 'user', folder: rcmail.env.folder},
callback = function(response) {
var result = response.result || [];
$.each(result, function() {
// add fake email to skip Roundcube's group expanding code
if (this.type == 'group' && !this.email)
this.email = 1;
});
rcmail.ksearch_query_results(result, post._search, post._reqid);
},
error_callback = function(o, status, err, data) {
file_api.http_error(o, status, err, data);
rcmail.ksearch_query_results([], post._search, post._reqid);
};
return file_api.get('autocomplete', params, callback, error_callback);
});
}
// Update hidden fields on selection
rcmail.addEventListener('autocomplete_insert', function(e) {
if (e.field == input[0]) {
input.val(e.insert.replace(/[, ]+$/, ''));
}
if (insert_callback)
insert_callback(e);
});
};
/**********************************************************/
/********* document editor functionality **********/
/**********************************************************/
// Initialize document toolbar functionality
function document_editor_init()
{
var info = rcmail.env.file_data;
rcmail.enable_command('document-export', 'document-print', true);
if (info && info.session && info.session.is_owner)
rcmail.enable_command('document-close', 'document-editors', true);
};
// executed on editing session termination
function document_editor_close()
{
var dialog = $('<div>').addClass('popupdialog').attr('role', 'alertdialog')
.html($('<span>').text(rcmail.gettext('kolab_files.sessionterminated')));
rcmail.alert_dialog(dialog, function() { window.close(); }, {
title: rcmail.gettext('kolab_files.sessionterminatedtitle')
});
return false; // skip Chwala's error message
};
rcube_webmail.prototype.document_save = function()
{
document_editor.save(function(data) {
rcmail.enable_command('document-save', false);
});
};
rcube_webmail.prototype.document_export = function(type)
{
document_editor.export(type || 'odt');
};
rcube_webmail.prototype.document_print = function()
{
document_editor.print();
};
rcube_webmail.prototype.document_editors = function()
{
kolab_files_editors_dialog();
};
// close editing session
rcube_webmail.prototype.document_close = function()
{
var delete_fn = function() {
document_editor.terminate(function() {
var win = window.opener || window.parent;
if (win && win.rcmail && win.file_api)
win.file_api.document_delete(rcmail.env.file_data.session.id);
// For Elastic: hide the parent dialog
if (rcmail.is_framed()) {
parent.$('.ui-dialog:visible button.cancel').click();
}
window.close();
});
};
// check document "unsaved changes" state and display a warning
// skip the warning for WOPI, Collabora will save the document anyway on session close
if (this.commands['document-save'] && (!this.env.file_data.viewer || !this.env.file_data.viewer.wopi))
this.confirm_dialog(this.gettext('kolab_files.unsavedchanges'), 'kolab_files.terminate', delete_fn, {button_class: 'delete'});
else
delete_fn();
};
// document editors management dialog
function kolab_files_editors_dialog(session)
{
var items = [], buttons = {},
info = rcmail.env.file_data,
dialog = $('#document-editors-dialog'),
comment = $('#invitation-comment');
if (!info || !info.session || !info.session.is_owner)
return;
// always add the session organizer
items.push(kolab_files_attendee_record(info.session.owner, 'organizer'));
$.each(info.session.invitations || [], function(i, u) {
var record = kolab_files_attendee_record(u.user, u.status, u.user_name);
items.push(record);
info.session.invitations[i].record = record;
});
$('table > tbody', dialog).html(items);
buttons[rcmail.gettext('kolab_files.close')] = function() {
kolab_dialog_close(this);
};
// show dialog window
kolab_dialog_show(dialog, {
title: rcmail.gettext('kolab_files.manageeditors'),
buttons: buttons,
button_classes: ['cancel']
});
if (!rcmail.env.editors_dialog) {
rcmail.env.editors_dialog = dialog;
kolab_files_autocomplete($('#invitation-editor-name'), 'files/autocomplete', function(e) {
var success = false;
if (e.field.name == 'participant') {
// e.data && e.data.type == 'group' ? 'GROUP' : 'INDIVIDUAL'
success = kolab_files_add_attendees(e.insert, comment.val());
}
if (e.field && success) {
e.field.value = '';
}
});
$('#invitation-editor-add').click(function() {
var input = $('#invitation-editor-name');
rcmail.ksearch_blur();
if (kolab_files_add_attendees(input.val(), comment.val())) {
input.val('');
}
});
}
};
// add the given list of participants
function kolab_files_add_attendees(names, comment)
{
var i, item, success, email, name, attendees = {}, counter = 0;
names = file_api.explode_quoted_string(names.replace(/,\s*$/, ''), ',');
// parse name/email pairs
for (i = 0; i < names.length; i++) {
email = name = '';
item = $.trim(names[i]);
if (!item.length) {
continue;
} // address in brackets without name (do nothing)
else if (item.match(/^<[^@]+@[^>]+>$/)) {
email = item.replace(/[<>]/g, '');
} // address without brackets and without name (add brackets)
else if (rcube_check_email(item)) {
email = item;
} // address with name
else if (item.match(/([^\s<@]+@[^>]+)>*$/)) {
email = RegExp.$1;
name = item.replace(email, '').replace(/^["\s<>]+/, '').replace(/["\s<>]+$/, '');
}
if (email) {
attendees[email] = {user: email, name: name};
counter++;
}
else {
rcmail.alert_dialog(rcmail.gettext('noemailwarning'));
}
}
success = counter > 0;
// remove already existing entries
if (counter) {
if (attendees[rcmail.env.file_data.session.owner]) {
delete attendees[this.user];
counter--;
}
$.each(rcmail.env.file_data.session.invitations || [], function() {
if (this.user in attendees) {
delete attendees[this.user];
counter--;
}
});
}
if (counter)
file_api.document_invite(rcmail.env.file_data.session.id, attendees, comment);
return success;
};
function kolab_files_attendee_record(user, status, username)
{
var options = [], select,
type = status ? status.replace(/-.*$/, '') : '',
name = $('<td class="name">').text(user),
buttons = $('<td class="options">'),
state = $('<td class="status">').text(rcmail.gettext('kolab_files.status' + type));
// @todo: accept/decline invitation request
if (type == 'requested' || status == 'accepted-owner' || status == 'declined-owner') {
select = $('<select>').change(function() {
var val = $(this).val(), map = {accepted: 'invitation_accept', declined: 'invitation_decline'};
if (map[val])
document_editor[map[val]]({user: user, session_id: rcmail.env.file_data.session.id});
});
if (type == 'requested')
options.push($('<option>').text(rcmail.gettext('kolab_files.statusrequested')).attr('value', 'requested'));
options.push($('<option>').text(rcmail.gettext('kolab_files.statusaccepted')).attr('value', 'accepted'));
options.push($('<option>').text(rcmail.gettext('kolab_files.statusdeclined')).attr('value', 'declined'));
state.html(select.html(options).val(type));
}
// delete button
if (status != 'organizer') {
$('<a>').attr({'class': 'delete', href: '#', title: rcmail.gettext('kolab_files.removeparticipant')})
.click(function() {
file_api.document_cancel(rcmail.env.file_data.session.id, [user]);
})
.appendTo(buttons);
}
if (username && status != 'organizer')
name.html($('<a>').attr({href: 'mailto:' + user, 'class': 'mailtolink'}).text(username))
.click(function(e) { rcmail.command('compose', user, e.target, e); return false; });
return $('<tr>').attr('class', 'invitation' + (type ? ' ' + type : ''))
.append(name).append(state).append(buttons);
};
function document_editor_invitation_handler(invitation)
{
// make the "More" link clickable
$('#' + invitation.id).parent('div').click(function() { kolab_files_invitation_dialog(invitation); });
};
function kolab_files_invitation_dialog(invitation)
{
var text, records = [], content = [], buttons = {}, button_classes = [],
dialog = $('#document-invitation-dialog'),
data_map = {status: 'status', 'changed': 'when', filename: 'file', comment: 'comment'},
record_fn = function(type, label, value) {
records.push($('<tr>').attr('class', type)
.append($('<td class="label">').text(rcmail.gettext('kolab_files.'+ label)))
.append($('<td>').text(value))
);
};
join_session = function() {
var viewer = file_api.file_type_supported('application/vnd.oasis.opendocument.text', rcmail.env.files_caps);
params = {action: 'edit', session: invitation.session_id};
file_api.file_open('', viewer, params);
};
if (!dialog.length)
dialog = $('<div>').attr({id: 'document-invitation-dialog', role: 'dialog', 'aria-hidden': 'true'})
.append($('<div>'))
.appendTo(document.body);
if (!invitation.is_session_owner) {
if (invitation.status == 'invited') {
text = document_editor.invitation_msg(invitation);
button_classes = ['session join', 'session accept', 'session decline delete'];
buttons[rcmail.gettext('kolab_files.join')] = function() {
join_session();
kolab_dialog_close(this);
};
buttons[rcmail.gettext('kolab_files.accept')] = function() {
document_editor.invitation_accept(invitation);
kolab_dialog_close(this);
};
buttons[rcmail.gettext('kolab_files.decline')] = function() {
document_editor.invitation_decline(invitation);
kolab_dialog_close(this);
};
}
else if (invitation.status == 'declined-owner') {
// @todo: add option to request for an invitation again?
text = document_editor.invitation_msg(invitation);
}
else if (invitation.status == 'accepted-owner') {
text = document_editor.invitation_msg(invitation);
button_classes = ['mainaction session join'];
buttons[rcmail.gettext('kolab_files.join')] = function() {
join_session();
kolab_dialog_close(this);
};
}
}
else {
if (invitation.status == 'accepted') {
text = document_editor.invitation_msg(invitation);
}
else if (invitation.status == 'declined') {
// @todo: add option to invite the user again?
text = document_editor.invitation_msg(invitation);
}
else if (invitation.status == 'requested') {
text = document_editor.invitation_msg(invitation);
button_classes = ['session accept', 'session decline delete'];
buttons[rcmail.gettext('kolab_files.accept')] = function() {
document_editor.invitation_accept(invitation);
kolab_dialog_close(this);
};
buttons[rcmail.gettext('kolab_files.decline')] = function() {
document_editor.invitation_decline(invitation);
kolab_dialog_close(this);
};
}
}
if (text) {
$.each(data_map, function(i, label) {
var value = invitation[i];
if (value) {
if (i == 'status')
value = rcmail.gettext('kolab_files.status' + value.replace(/-.*$/, ''));
record_fn(i, label, value);
}
});
content.push($('<div>').text(text));
content.push($('<table class="propform">').html(records));
}
button_classes.push('cancel');
buttons[rcmail.gettext('kolab_files.close')] = function() {
kolab_dialog_close(this);
};
$('div', dialog).html(content);
// show dialog window
kolab_dialog_show(dialog, {
title: rcmail.gettext('kolab_files.invitationtitle').replace('$file', invitation.filename),
buttons: buttons,
button_classes: button_classes
});
};
/***********************************************************/
/********** Commands **********/
/***********************************************************/
rcube_webmail.prototype.files_sort = function(props)
{
this.files_sort_handler(props, 'files');
};
rcube_webmail.prototype.sessions_sort = function(props)
{
this.files_sort_handler(props, 'sessions');
};
rcube_webmail.prototype.files_sort_handler = function(col, type)
{
var params = {},
c = type == 'files' ? '' : ('_' + type),
sort_order = this.env[type + '_sort_order'],
sort_col = !this.env['kolab_files' + c + '_disabled_sort_col'] ? col : this.env[type + '_sort_col'];
if (!this.env['kolab_files' + c + '_disabled_sort_order'])
sort_order = this.env[type + '_sort_col'] == sort_col && sort_order == 'ASC' ? 'DESC' : 'ASC';
// set table header and update env
kolab_files_set_list_sorting(sort_col, sort_order, type);
this.http_post('files/prefs', {kolab_files_sort_col: sort_col, kolab_files_sort_order: sort_order, type: type});
params.sort = sort_col;
params.reverse = sort_order == 'DESC';
this.command(type + '-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(prop, elem, event)
{
this.hide_menu('folderoptions', event);
this.confirm_dialog(this.get_label('kolab_files.folderdeleteconfirm'), 'delete', function() {
file_api.folder_delete(file_api.env.folder);
});
};
rcube_webmail.prototype.files_delete = function()
{
this.confirm_dialog(this.get_label('kolab_files.filedeleteconfirm'), 'delete', function() {
var files = rcmail.env.file ? [rcmail.env.file] : kolab_files_selected();
file_api.file_delete(files);
});
};
rcube_webmail.prototype.files_move = function(folder, obj, event, files)
{
if (!files)
files = kolab_files_selected();
if (!folder) {
var ref = this;
return this.files_folder_selector(event, function(folder) {
ref.files_move(folder, null, event, files);
});
}
file_api.file_move(files, folder);
};
rcube_webmail.prototype.files_copy = function(folder, obj, event, files)
{
if (!files)
files = kolab_files_selected();
if (!folder) {
var ref = this;
return this.files_folder_selector(event, function(folder) {
ref.files_copy(folder, null, event, files);
});
}
file_api.file_copy(files, folder);
};
// create folder selector popup
rcube_webmail.prototype.files_folder_selector = function(event, callback)
{
if (this.folder_selector_reset)
this.destroy_entity_selector('folder-selector');
// The list is incomplete, reset needed before next use
this.folder_selector_reset = file_api.list_updates > 0;
this.entity_selector('folder-selector', callback, file_api.env.folders, function(folder, a, folder_fullname) {
var n = folder.depth || 0,
row = $('<li>');
if (folder.virtual || folder.readonly)
a.addClass('virtual').attr({'aria-disabled': 'true', tabindex: '-1'});
else
a.addClass('active').data('id', folder_fullname);
a.css('padding-left', n ? (n * 16) + 'px' : 0);
// add folder name element
a.append($('<span>').text(folder.name));
return row.append(a);
}, event);
};
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.fileslist;
$('thead', list.fixed_header ? list.fixed_header : list.list).html(head);
kolab_files_list_coltypes();
file_api.file_list();
};
rcube_webmail.prototype.sessions_list = function(param)
{
// just rcmail wrapper, to handle command busy states
file_api.session_list(param);
};
rcube_webmail.prototype.sessions_list_update = function(head)
{
var list = this.sessionslist;
$('thead', list.fixed_header ? list.fixed_header : list.list).html(head);
kolab_files_list_coltypes('sessions');
file_api.sessions_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(session)
{
var files, readonly, sessions, file = this.env.file,
params = {action: 'edit'};
if (!file && !this.env.action) {
files = kolab_files_selected();
if (files.length == 1)
file = files[0];
readonly = !file_api.is_writable(file_api.file_path(file));
}
else {
readonly = !this.env.file_data.writable;
}
// check if the folder is read-only or there are ongoing sessions
// in such cases display dialog for the user to decide what to do
if (!session) {
sessions = file_api.file_sessions(file);
if (sessions.length || readonly) {
kolab_files_file_edit_dialog(file, sessions, readonly);
return;
}
}
else if (session !== true)
params.session = session;
if (this.file_editor && this.file_editor.editable && !session) {
this.file_editor.enable();
this.enable_command('files-save', true);
}
else if (this.env.file) {
var viewer = file_api.file_type_supported(this.env.file_data.type, this.env.files_caps);
params.local = true;
file_api.file_open(file, viewer, params);
}
else if (file) {
file_api.file_open(file, this.env.viewer, params);
}
};
// save changes to the file
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 && window.file_api) {
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.files_create = function()
{
kolab_files_file_create_dialog();
};
rcube_webmail.prototype.files_rename = function()
{
var files = kolab_files_selected();
kolab_files_file_rename_dialog(files[0]);
};
rcube_webmail.prototype.folder_create = function()
{
kolab_files_folder_create_dialog();
};
rcube_webmail.prototype.folder_rename = function(prop, elem, event)
{
this.hide_menu('folderoptions', event);
kolab_files_folder_edit_dialog();
};
rcube_webmail.prototype.folder_share = function(prop, elem, event)
{
this.hide_menu('folderoptions', event);
kolab_files_folder_share_dialog();
};
rcube_webmail.prototype.folder_mount = function(prop, elem, event)
{
this.hide_menu('folderoptions', event);
kolab_files_folder_mount_dialog();
};
// open a session dialog
rcube_webmail.prototype.sessions_open = function()
{
var id = this.sessionslist.get_selection(),
session = id ? file_api.env.sessions_list[id] : null;
if (session)
kolab_files_session_dialog(session);
};
/**********************************************************/
/********* Files API handler **********/
/**********************************************************/
function kolab_files_ui()
{
this.requests = {};
this.uploads = [];
this.list_updates = 0;
/*
// 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, data)
{
rcmail.http_error(request, status, err, data ? data.req_id : null);
};
// check if specified/current folder/view is writable
this.is_writable = function(folder)
{
if (!folder)
folder = this.env.folder;
if (!folder)
return false;
var all_folders = $.extend({}, this.env.folders, this.search_results);
if (!all_folders[folder] || all_folders[folder].readonly || all_folders[folder].virtual)
return false;
return true;
};
// Check if specified folder (hierarchy) supports sharing
this.is_shareable = function(folder)
{
if (!folder)
folder = this.env.folder;
if (!folder)
return false;
var root = folder.split(this.env.directory_separator)[0],
caps = this.env.caps;
if (this.env.caps.MOUNTPOINTS[root])
caps = root != folder ? this.env.caps.MOUNTPOINTS[root] : {};
return !!caps.ACL;
};
// folders list request
this.folder_list = function(params, update)
{
if (!params)
params = {}
params.permissions = 1;
if (params.level === undefined)
params.level = -1;
if (update) {
this.list_updates++;
params.req = rcmail.display_message('', 'loading');
}
else {
params.req = this.set_busy(true, 'loading');
this.list_params = params;
}
this.request('folder_list', params, update ? 'folder_list_update_response' : 'folder_list_response');
};
// folder list response handler
this.folder_list_response = function(response, params)
{
rcmail.hide_message(params.req);
if (!this.response(response))
return;
var folder, first, body, rows = [],
list_selector = rcmail.env.folder_list_selector || '#files-folder-list',
search_selector = rcmail.env.folder_search_selector || '#foldersearch',
elem = $(list_selector),
searchbox = $(search_selector),
list = $('<ul class="treelist listing folderlist"></ul>'),
collections = ['audio', 'video', 'image', 'document'];
// try parent window if the list element does not exist
// i.e. called from a dialog in parent window
if (!elem.length && rcmail.is_framed() && (rcmail.env.task != 'files' || (rcmail.env.action != 'open' && rcmail.env.action != 'edit'))) {
body = window.parent.document.body;
elem = $(list_selector, body);
searchbox = $(search_selector, body);
}
this.list_element = list;
if (elem.data('no-collections') == true)
collections = [];
this.env.folders = this.folder_list_parse(response.result && response.result.list ? response.result.list : response.result);
rcmail.enable_command('files-create', response.result && response.result.list && response.result.list.length > 0);
if (!elem.length)
return;
elem.html('');
$.each(this.env.folders, function(i, f) {
var row;
if (row = file_api.folder_list_row(i, f)) {
if (!first)
first = i;
rows.push(row);
}
});
// add virtual collections
$.each(collections, function(i, n) {
var row = $('<li class="mailbox collection ' + n + '"></li>');
row.attr('id', 'rcmli' + rcmail.html_identifier_encode('folder-collection-' + n))
.append($('<a class="name"></a>').text(rcmail.gettext('kolab_files.collection_' + n)))
rows.push(row);
});
// add Sessions entry
if (rcmail.task == 'files' && !rcmail.env.action && this.env.caps && this.env.caps.DOCEDIT) {
rows.push($('<li class="mailbox collection sessions"></li>')
.attr('id', 'rcmli' + rcmail.html_identifier_encode('folder-collection-sessions'))
.append($('<a class="name"></a>').text(rcmail.gettext('kolab_files.sessions')))
);
}
list.append(rows).appendTo(elem)
.on('click', 'a.subscription', function(e) {
return file_api.folder_list_subscription_button_click(this);
})
.on('mouseover', 'a.name', function() {
rcube_webmail.long_subject_title_ex(this);
});
if (rcmail.task == 'files' && rcmail.env.contextmenu)
list.on('contextmenu', function(e) {
var elem = $(e.target).closest('li');
id = rcmail.html_identifier_decode(elem.attr('id').replace(/^rcmli/, ''));
rcmail.contextmenu.show_one(e, elem[0], id, rcmail.env.folders_cm);
});
if (rcmail.folder_list) {
rcmail.folder_list.reset();
this.search_results_widget = null;
}
// init treelist widget
rcmail.folder_list = new rcube_treelist_widget(list, {
selectable: true,
id_prefix: 'rcmli',
parent_focus: true,
searchbox: searchbox,
id_encode: rcmail.html_identifier_encode,
id_decode: rcmail.html_identifier_decode,
check_droptarget: function(node) {
return !node.virtual
&& node.id != file_api.env.folder
&& $.inArray('readonly', node.classes) == -1
&& $.inArray('collection', node.classes) == -1;
}
});
rcmail.folder_list
.addEventListener('collapse', function(node) { file_api.folder_collapsed(node); })
.addEventListener('expand', function(node) { file_api.folder_collapsed(node); })
.addEventListener('beforeselect', function(node) { return !rcmail.busy; })
.addEventListener('search', function(search) { file_api.folder_search(search); })
.addEventListener('select', function(node) {
if (file_api.search_results_widget)
file_api.search_results_widget.select();
file_api.folder_select(node.id);
});
// select first/current folder
if (response.result.auth_errors && response.result.auth_errors.length)
this.env.folder = this.env.collection = null;
else if (this.env.folder)
rcmail.folder_list.select(this.env.folder);
else if (this.env.collection)
rcmail.folder_list.select('folder-collection-' + this.env.collection);
else if (folder = this.env.init_folder) {
if (this.env.folders[folder]) {
this.env.init_folder = null;
rcmail.folder_list.select(folder);
}
}
else if (folder = this.env.init_collection) {
this.env.init_collection = null;
rcmail.folder_list.select('folder-collection-' + folder);
}
else if (first)
rcmail.folder_list.select(first);
// handle authentication errors on external sources
this.folder_list_auth_errors(response.result);
// Fetch 2 levels of folder hierarchy for all mount points that
// do not support fast folders list
if (rcmail.env.files_api_version > 4) {
var ref = this;
$.each(this.env.caps.MOUNTPOINTS || [], function(k, v) {
if (!v.FAST_FOLDER_LIST)
ref.folder_list({level: 2, folder: k}, true);
});
}
// Elastic: Set notree class on the folder list
var callback = function() {
list[list.find('.treetoggle').length > 0 ? 'removeClass' : 'addClass']('notree');
};
if (window.MutationObserver)
(new MutationObserver(callback)).observe(list.get(0), {childList: true, subtree: true});
callback();
};
// folder list response handler
this.folder_list_update_response = function(response, params)
{
rcmail.hide_message(params.req);
this.list_updates--;
if (!this.response(response))
return;
// handle authentication errors on external sources
this.folder_list_auth_errors(response.result);
// Update the list
this.folder_list_merge(params.folder, response.result.list, params.level);
};
this.folder_list_update_wait = function(folder)
{
var ref = this;
// do maximum 10 parallel requests
if (this.list_updates > 10)
return setTimeout(function() { ref.folder_list_update_wait(folder); }, 20);
this.folder_list({folder: folder, level: 0}, true);
};
this.folder_select = function(folder)
{
if (rcmail.busy || !folder)
return;
rcmail.triggerEvent('files-folder-select', {folder: folder});
var is_collection = folder.match(/^folder-collection-(.*)$/),
collection = RegExp.$1 || null;
if (rcmail.task == 'files' && !rcmail.env.action)
rcmail.update_state(is_collection ? {collection: collection} : {folder: folder});
if (collection == 'sessions') {
rcmail.enable_command('files-list', 'files-folder-delete', 'folder-rename', 'files-upload', 'files-open', 'files-edit', false);
this.sessions_list();
return;
}
if (is_collection)
folder = null;
// search-reset can re-select the same folder, skip
if (this.env.folder == folder && this.env.collection == collection)
return;
this.env.folder = folder;
this.env.collection = collection;
rcmail.enable_command('files-list', true);
rcmail.enable_command('files-folder-delete', 'folder-rename', !is_collection);
rcmail.enable_command('files-upload', !is_collection && this.is_writable());
rcmail.enable_command('folder-share', !is_collection && this.is_shareable());
rcmail.command('files-list', is_collection ? {collection: collection} : {folder: folder});
this.quota();
};
this.folder_unselect = function()
{
rcmail.folder_list.select();
this.env.folder = null;
this.env.collection = null;
rcmail.enable_command('files-folder-delete', 'files-upload', false);
};
this.folder_collapsed = function(node)
{
var prefname = 'kolab_files_collapsed_folders',
old = rcmail.env[prefname],
entry = '&' + urlencode(node.id) + '&';
if (node.collapsed) {
rcmail.env[prefname] = rcmail.env[prefname] + entry;
// select the folder if one of its childs is currently selected
// don't select if it's virtual (#1488346)
if (!node.virtual && this.env.folder && this.env.folder.startsWith(node.id + '/')) {
rcmail.folder_list.select(node.id);
}
}
else {
rcmail.env[prefname] = rcmail.env[prefname].replace(entry, '');
}
if (old !== rcmail.env[prefname] && (!rcmail.fileslist || !rcmail.fileslist.drag_active))
rcmail.command('save-pref', {name: prefname, value: rcmail.env[prefname]});
};
this.folder_list_row = function(i, folder, parent)
{
var toggle, sublist, collapsed, parent, parent_name, classes = ['mailbox'],
row = $('<li>'),
id = 'rcmli' + rcmail.html_identifier_encode(i),
content = $('<div>').append($('<a class="name">').text(folder.name));
if (folder.virtual)
classes.push('virtual');
else {
if (folder.subscribed !== undefined)
content.append(this.folder_list_subscription_button(folder.subscribed));
if (folder.readonly)
classes.push('readonly');
}
folder.ref = row.attr({id: id, 'class': classes.join(' ')}).append(content);
if (folder.depth) {
// find parent folder
parent_name = i.replace(/\/[^/]+$/, '');
if (!parent)
parent = $(this.env.folders[parent_name].ref);
toggle = $('div.treetoggle', parent);
sublist = $('> ul', parent);
if (!toggle.length) {
collapsed = rcmail.env.kolab_files_collapsed_folders.indexOf('&' + urlencode(parent_name) + '&') > -1;
toggle = $('<div>').attr('class', 'treetoggle' + (collapsed ? ' collapsed' : ' expanded'))
.html('&nbsp;').appendTo(parent);
sublist = $('<ul>').attr({role: 'group'}).appendTo(parent);
if (collapsed)
sublist.hide();
}
sublist.append(row);
}
else {
return row;
}
};
// create subscription button element
this.folder_list_subscription_button = function(subscribed)
{
return $('<a>').attr({
title: rcmail.gettext('kolab_files.listpermanent'),
'class': 'subscription' + (subscribed ? ' subscribed' : ''),
'aria-checked': subscribed,
role: 'checkbox'
});
};
// subscription button handler
this.folder_list_subscription_button_click = function(elem)
{
var folder = $(elem).parents('li:first').prop('id').replace(/^rcmli/, ''),
selected = $(elem).hasClass('subscribed');
folder = folder.replace(/--xsR$/, ''); // this might be a search result
folder = rcmail.html_identifier_decode(folder);
file_api['folder_' + (selected ? 'unsubscribe' : 'subscribe')](folder);
return false;
};
// sets subscription button status
this.folder_list_subscription_state = function(elem, status)
{
$(elem).find('a.subscription:first')
.prop('aria-checked', status)[status ? 'addClass' : 'removeClass']('subscribed');
};
// Folder searching handler (for unsubscribed folders)
this.folder_search = function(search)
{
// hide search results
if (this.search_results_widget) {
this.search_results_container.hide();
this.search_results_widget.reset();
}
this.search_results = {};
// send search request to the server
if (search.query && search.execute) {
// cancel previous search request
if (this.listsearch_request) {
this.listsearch_request.abort();
this.listsearch_request = null;
}
var params = $.extend({search: search.query, unsubscribed: 1}, this.list_params);
this.req = this.set_busy(true, rcmail.gettext('searching'));
this.listsearch_request = this.request('folder_list', params, 'folder_search_response');
}
else if (!search.query) {
if (this.listsearch_request) {
this.listsearch_request.abort();
this.listsearch_request = null;
}
// any subscription changed, make sure the newly added records
// are listed before collections not after
if (this.folder_subscribe) {
var r, last, move = [], rows = $(rcmail.folder_list.container).children('li');
if (rows.length && !$(rows[rows.length-1]).hasClass('collection')) {
// collect all folders to move
while (rows.length--) {
r = $(rows[rows.length]);
if (r.hasClass('collection'))
last = r;
else if (last)
break;
else
move.push(r);
}
if (last)
$.each(move, function() {
this.remove();
last.before(this);
});
}
}
}
};
// folder search response handler
this.folder_search_response = function(response)
{
if (!this.response(response))
return;
var folders = response.result && response.result.list ? response.result.list : response.result;
if (!folders.length)
return;
folders = this.folder_list_parse(folders, 10000, false);
if (!this.search_results_widget) {
var list = rcmail.folder_list.container,
title = rcmail.gettext('kolab_files.additionalfolders'),
list_id = list.attr('id') || '0';
this.search_results_container = $('<div class="searchresults"></div>')
.append($('<h2 class="boxtitle" id="st:' + list_id + '"></h2>').text(title))
.insertAfter(list);
this.search_results_widget = new rcube_treelist_widget('<ul>', {
id_prefix: 'rcmli',
id_encode: rcmail.html_identifier_encode,
id_decode: rcmail.html_identifier_decode,
selectable: true
});
this.search_results_widget
.addEventListener('beforeselect', function(node) { return !rcmail.busy; })
.addEventListener('select', function(node) {
rcmail.folder_list.select();
file_api.folder_select(node.id);
});
this.search_results_widget.container
// copy classes from main list
.addClass(list.attr('class')).attr('aria-labelledby', 'st:' + list_id)
.appendTo(this.search_results_container)
.on('click', 'a.subscription', function(e) {
return file_api.folder_list_subscription_button_click(this);
});
}
// add results to the list
$.each(folders, function(i, folder) {
var node, separator = file_api.env.directory_separator,
path = i.split(separator),
classes = ['mailbox'],
html = [$('<a>').text(folder.name)];
if (!folder.virtual) {
// add subscription button
html.push(file_api.folder_list_subscription_button(false));
if (folder.readonly)
classes.push('readonly');
}
path.pop();
file_api.search_results_widget.insert({
id: i,
classes: classes,
text: folder.name,
html: $('<div>').append(html),
collapsed: false,
virtual: folder.virtual
}, path.length ? path.join(separator) : null);
});
this.search_results = folders;
this.search_results_container.show();
};
// folder subscribe request
this.folder_subscribe = function(folder)
{
this.env.folder_subscribe = folder;
this.req = this.set_busy(true, 'foldersubscribing');
this.request('folder_subscribe', {folder: folder}, 'folder_subscribe_response');
}
// folder subscribe response handler
this.folder_subscribe_response = function(response)
{
if (!this.response(response))
return;
this.display_message('foldersubscribed', 'confirmation');
var item, node = rcmail.folder_list.get_item(this.env.folder_subscribe);
if (this.search_results && this.search_results[this.env.folder_subscribe]) {
item = this.search_results_widget.get_item(this.env.folder_subscribe);
this.folder_list_subscription_state(item, true);
if (item = $(item).attr('id'))
this.folder_list_subscription_state($('#' + item.replace(/--xsR$/, '')), true);
}
// search result, move from search to main list widget
if (!node && this.search_results && this.search_results[this.env.folder_subscribe]) {
var i, html, dir, folder, separator = this.env.directory_separator,
path = this.env.folder_subscribe.split(separator);
// add all folders in a path to the main list if needed
// including the subscribed folder
for (i=0; i<path.length; i++) {
dir = path.slice(0, i + 1).join(separator);
node = rcmail.folder_list.get_node(dir);
if (!node) {
node = this.search_results_widget.get_node(dir);
if (!node) {
// sanity check
return;
}
if (i == path.length - 1) {
item = this.search_results_widget.get_item(dir);
this.folder_list_subscription_state(item, true);
}
folder = this.search_results[dir];
html = [$('<a>').text(folder.name)];
if (!folder.virtual)
html.push(this.folder_list_subscription_button(true));
node.html = $('<div>').append(html);
delete node.children;
rcmail.folder_list.insert(node, i > 0 ? path.slice(0, i).join(separator) : null);
// we're in search result, so there will be two records,
// add subscription button to the visible one, it was not cloned
if (!folder.virtual) {
node = rcmail.folder_list.get_item(dir);
$(node).append(file_api.folder_list_subscription_button(true));
}
this.env.folders[dir] = folder;
}
}
// now remove them from the search widget
while (path.length) {
dir = path.join(separator);
node = this.search_results_widget.get_item(dir);
if ($('ul[role="group"] > li', node).length)
break;
this.search_results_widget.remove(dir);
path.pop();
}
node = null;
}
if (node)
this.folder_list_subscription_state(node, true);
this.env.folders[this.env.folder_subscribe].subscribed = true;
};
// folder unsubscribe request
this.folder_unsubscribe = function(folder)
{
this.env.folder_subscribe = folder;
this.req = this.set_busy(true, 'folderunsubscribing');
this.request('folder_unsubscribe', {folder: folder}, 'folder_unsubscribe_response');
}
// folder unsubscribe response handler
this.folder_unsubscribe_response = function(response)
{
if (!this.response(response))
return;
this.display_message('folderunsubscribed', 'confirmation');
var folder = this.env.folders[this.env.folder_subscribe],
node = rcmail.folder_list.get_item(this.env.folder_subscribe);
if (this.search_results && this.search_results[this.env.folder_subscribe]) {
item = this.search_results_widget.get_item(this.env.folder_subscribe);
if (item) {
this.folder_list_subscription_state(item, false);
item = $('#' + $(item).attr('id').replace(/--xsR$/, ''));
}
else
item = $('#rcmli' + rcmail.html_identifier_encode(this.env.folder_subscribe), rcmail.folder_list.container);
this.folder_list_subscription_state(item, false);
}
this.folder_list_subscription_state(node, false);
folder.subscribed = false;
};
// 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
// TODO: Don't reload the whole list
this.folder_list();
};
// folder rename request
this.folder_rename = function(folder, new_name)
{
if (folder == new_name)
return;
this.req = this.set_busy(true, 'kolab_files.folderupdating');
this.request('folder_move', {folder: folder, 'new': new_name}, 'folder_rename_response');
};
// folder create response handler
this.folder_rename_response = function(response, params)
{
if (!this.response(response))
return;
this.display_message('kolab_files.folderupdatenotice', 'confirmation');
// refresh folders and files list
if (this.env.folder == params.folder)
this.env.folder = params['new'];
// Removed mount point, refresh capabilities stored in session
if (this.env.caps.MOUNTPOINTS[params.folder]) {
this.env.caps.MOUNTPOINTS[params['new']] = this.env.caps.MOUNTPOINTS[params.folder];
delete this.env.caps.MOUNTPOINTS[params.folder];
rcmail.http_post('files/reset', {});
}
// TODO: Don't reload the whole 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, params)
{
if (!this.response(response))
return;
this.display_message('kolab_files.foldermountnotice', 'confirmation');
if (response.result.capabilities) {
// we make sure the result is an object not array
// when the list is empty it is an array, because of how works JSON encoding from PHP
var add = {};
add[params.folder] = response.result.capabilities;
this.env.caps.MOUNTPOINTS = $.extend({}, this.env.caps.MOUNTPOINTS, add);
}
// Refresh capabilities stored in session
rcmail.http_post('files/reset', {});
// refresh folders list
// TODO: load only folders from the created mount point
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, params)
{
if (!this.response(response))
return;
this.display_message('kolab_files.folderdeletenotice', 'confirmation');
if (this.env.folder == params.folder) {
this.env.folder = null;
rcmail.enable_command('files-folder-delete', 'folder-rename', 'files-list', false);
}
// Removed mount point, refresh capabilities stored in session
if (this.env.caps.MOUNTPOINTS[params.folder]) {
delete this.env.caps.MOUNTPOINTS[params.folder];
rcmail.http_post('files/reset', {});
}
// refresh folders list
this.folder_list();
this.quota();
};
this.sharing_submit = function(folder, row, mode)
{
var post = this.sharing_data(row, {action: 'submit', folder: folder, mode: mode});
if (post === false)
return;
this.sharing_submit_post = post;
this.sharing_submit_row = row;
this.req = this.set_busy(true, 'kolab_files.updatingfolder' + mode);
this.post('sharing', post, 'sharing_submit_response');
};
this.sharing_submit_response = function(response)
{
if (!this.response(response))
return;
// reset inputs
$(this.sharing_submit_row).find('input[type=text]').val('');
var hidden = [],
post = $.extend({}, this.sharing_submit_post, response.result || {}),
form_info = rcmail.env.form_info[post.mode],
table = $(this.sharing_submit_row).closest('table'),
row = $('<tr>'),
btn = $('<button type="button" class="btn btn-secondary btn-danger delete">')
.text(rcmail.gettext('delete'))
.on('click', function() { file_api.sharing_delete(post.folder, $(this).closest('tr'), post.mode); });
if (form_info.list_column) {
row.append($('<td>').append($('<span class="name">')
.text(response.result.display || post[form_info.list_column])
.attr('title', response.result.title))
);
}
else {
$.each(form_info.form || [], function(i, v) {
var content, opts = [];
if (v.type == 'select') {
content = $('<select>').attr('name', i)
.on('change', function() { file_api.sharing_update(post.folder, $(this).closest('tr'), post.mode); });
$.each(v.options, function(i, v) {
opts.push($('<option>').attr('value', i).text(v));
});
content.append(opts).val(post[i]);
}
else {
content = $('<span class="name">').text(response.result.display || post[i]).attr('title', response.result.title);
hidden.push($('<input>').attr({type: 'hidden', name: i, value: post[i] || ''}));
}
row.append($('<td>').append(content));
});
}
$.each(form_info.extra_fields || [], function(i, v) {
hidden.push($('<input>').attr({type: 'hidden', name: i, value: post[i] || ''}));
});
row.append($('<td>').append(btn).append(hidden));
$(this.sharing_submit_row).parent().append(row);
if (window.UI && UI.pretty_select)
row.find('select').each(function() { UI.pretty_select(this); }); // for Elastic
if (table.data('single')) {
$('tbody > tr:first', table).find('button,input,select').prop('disabled', true);
}
$('tbody > tr:first', table).find('input[type=text],input[type=password]').val('');
};
this.sharing_update = function(folder, row, mode)
{
var post = this.sharing_data(row, {action: 'update', folder: folder, mode: mode});
this.req = this.set_busy(true, 'kolab_files.updatingfolder' + mode);
this.post('sharing', post, 'sharing_update_response');
};
this.sharing_update_response = function(response)
{
if (!this.response(response))
return;
// todo: on error reset fields?
};
this.sharing_delete = function(folder, row, mode)
{
var post = this.sharing_data(row, {action: 'delete', folder: folder, mode: mode});
this.sharing_delete_row = row;
this.req = this.set_busy(true, 'kolab_files.updatingfolder' + mode);
this.post('sharing', post, 'sharing_delete_response');
};
this.sharing_delete_response = function(response)
{
if (!this.response(response))
return;
var table = $(this.sharing_delete_row).closest('table');
if (table.data('single')) {
$('tbody > tr:first', table).find('button,input,select').prop('disabled', false);
}
$(this.sharing_delete_row).remove();
};
this.sharing_data = function(row, data)
{
var error;
$('select,input', row).each(function() {
if (this.type == 'password' && !this.name.match(/confirm$/) && this.value != $('input[name=' + this.name + 'confirm]', row).val())
error = rcmail.display_message('kolab_files.passwordconflict', 'error');
data[this.name] = $(this).val();
});
return error ? false : data;
};
// quota request
this.quota = function()
{
if (rcmail.env.files_quota && (this.env.folder || !this.env.caps.NOROOT))
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);
};
// List of sessions
this.sessions_list = function(params)
{
if (!rcmail.gui_objects.sessionslist)
return;
this.file_list_abort(true);
if (!params)
params = {};
if (rcmail.gui_objects.fileslist) {
$(rcmail.gui_objects.fileslist).parent().hide();
$(rcmail.gui_objects.sessionslist).parent().show();
rcmail.fileslist.clear();
}
this.env.folder = null;
this.env.collection = null;
// empty the list
this.env.sessions_list = {};
rcmail.sessionslist.clear(true);
rcmail.enable_command(rcmail.env.file_commands, false);
rcmail.enable_command(rcmail.env.file_commands_all, false);
params.req_id = this.set_busy(true, 'loading');
this.requests[params.req_id] = this.request('sessions', params, 'sessions_list_response');
};
// file list response handler
this.sessions_list_response = function(response)
{
if (response.req_id)
rcmail.hide_message(response.req_id);
if (!this.response(response))
return;
$.each(response.result, function(sess_id, data) {
var row = file_api.session_list_row(sess_id, data);
rcmail.sessionslist.insert_row(row);
response.result[sess_id].row = row;
});
this.env.sessions_list = response.result;
rcmail.sessionslist.resize();
};
// Files list
this.file_list = function(params)
{
if (!rcmail.gui_objects.fileslist)
return;
this.file_list_abort(true);
if (!params)
params = {};
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.files_sort_col;
if (params.reverse == undefined)
params.reverse = this.env.files_sort_reverse;
if (params.search == undefined)
params.search = this.env.search;
this.env.folder = params.folder;
this.env.collection = params.collection;
this.env.files_sort_col = params.sort;
this.env.files_sort_reverse = params.reverse;
rcmail.enable_command(rcmail.env.file_commands, false);
rcmail.enable_command(rcmail.env.file_commands_all, false);
if (rcmail.gui_objects.sessionslist) {
$(rcmail.gui_objects.sessionslist).parent().hide();
$(rcmail.gui_objects.fileslist).parent().show();
rcmail.sessionslist.clear(true)
}
// empty the list
this.env.file_list = [];
rcmail.fileslist.clear(true);
rcmail.triggerEvent('listupdate', {list: rcmail.fileslist, rowcount: 0});
// request
if (params.collection || params.all_folders)
this.file_list_loop(params);
else if (this.env.folder) {
params.req_id = this.env.file_list_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 = [];
$.each(response.result, function(key, data) {
var row = file_api.file_list_row(key, data, ++i);
rcmail.fileslist.insert_row(row);
data.row = row;
data.filename = key;
list.push(data);
});
this.env.file_list = list;
rcmail.fileslist.resize();
rcmail.triggerEvent('listupdate', {list: rcmail.fileslist, rowcount: rcmail.fileslist.rowcount});
// update document sessions info of this folder
if (list && list.length)
this.request('folder_info', {folder: this.file_path(list[0].filename), sessions: 1}, 'folder_info_response');
};
this.file_list_abort = function(all)
{
if (all) {
clearTimeout(this.file_list_worker);
this.file_list_worker = null;
}
// reset all pending list requests
$.each(this.requests, function(i, v) {
v.abort();
rcmail.hide_message(i);
});
this.requests = {};
rcmail.set_busy(false, null, this.env.file_list_req_id);
};
// 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),
msg = rcmail.get_label('searching') + ' <a onclick="file_api.file_list_abort()">' + rcmail.get_label('kolab_files.abort') + '</a>';
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_lock = false;
this.env.file_list_req_id = rcmail.display_message(msg, 'loading', 5 * 60 * 1000, 'files-file-search');
rcmail.set_busy(true);
for (i=0; i<folders.length && i<limit; i++) {
params.req_id = new Date().getTime();
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, params)
{
var folders = this.env.folders_loop,
valid = this.response(response);
if (folders.length) {
params.req_id = new Date().getTime();
params.folder = folders.shift();
this.requests[params.req_id] = this.request('file_list', params, 'file_list_loop_response');
}
else {
rcmail.set_busy(false, null, this.env.file_list_req_id);
}
// refresh sessions info in time intervals (one request for all folders)
if (!this.file_list_worker && this.env.caps && this.env.caps.DOCEDIT && (rcmail.fileslist || rcmail.env.file))
this.file_list_worker = setTimeout(function() {
var params = {req_id: file_api.set_busy(true, 'loading')};
file_api.request('sessions', params, 'file_list_sessions_response');
}, 0);
rcmail.fileslist.resize();
if (!valid)
return;
this.file_list_loop_result_add(response.result);
};
// Update sessions metadata for files list (in multifolder mode)
this.file_list_sessions_response = function(response)
{
this.file_list_worker = setTimeout(function() {
var params = {req_id: file_api.set_busy(true, 'loading')};
file_api.request('sessions', params, 'file_list_sessions_response');
}, (rcmail.env.files_interval || 60) * 1000);
if (response.req_id)
rcmail.hide_message(response.req_id);
if (!this.response(response))
return;
this.sessions = [];
$.each(response.result || [], function(sess_id, data) {
var folder = file_api.file_path(data.file);
if (!file_api.sessions[folder])
file_api.sessions[folder] = {};
file_api.sessions[folder][sess_id] = data;
});
// update files list with document session info
$.each(file_api.env.file_list || [], function(i, file) {
var folder = file_api.file_path(file.filename);
file_api.file_session_data_set(file, file_api.sessions[folder]);
});
};
// 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, folder, list = [],
index = this.env.file_list.length,
table = rcmail.fileslist,
fn = function(result, key, before) {
var row = file_api.file_list_row(key, result[key], ++index);
table.insert_row(row, before);
result[key].row = row;
result[key].filename = key;
list.push(result[key]);
if (!folder)
folder = file_api.file_path(key);
};
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;
fn(result, i, elem.row);
delete result[i];
}
list.push(elem);
}
// add the rest of rows
for (i in result) fn(result, i);
this.env.file_list = list;
// update files list with document session info
if (folder && this.sessions[folder])
$.each(list, function(i, file) {
if (folder == file_api.file_path(file.filename))
file_api.file_session_data_set(file, file_api.sessions[folder]);
});
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>', table);
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);
};
// Files list record
this.file_list_row = function(file, data, index)
{
var c, col, row = '';
for (c in rcmail.env.files_coltypes) {
c = rcmail.env.files_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});
// 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);
};
// Sessions list record
this.session_list_row = function(id, data)
{
var c, col, row = '', classes = ['session'];
for (c in rcmail.env.sessions_coltypes) {
c = rcmail.env.sessions_coltypes[c];
if (c == 'name')
col = '<td class="name filename ' + this.file_type_class(data.type) + '">'
+ '<span>' + escapeHTML(file_api.file_name(data.file)) + '</span></td>';
else if (c == 'owner')
col = '<td class="owner">' + escapeHTML(data.owner_name || data.owner) + '</td>';
else if (c == 'options')
col = '<td class="options"><span></span></td>';
row += col;
}
if (data.is_owner)
classes.push('owner');
else if (data.is_invited)
classes.push('invited');
row = $('<tr>')
.html(row)
.attr({id: 'rcmrow' + id, 'class': classes.join(' ')});
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');
}
};
// handler for folder info response
this.folder_info_response = function(response)
{
if (!this.response(response) || !response.result)
return;
var folder = response.result.folder,
prefix = folder + file_api.env.directory_separator;
if (response.result.sessions)
this.sessions[folder] = response.result.sessions;
// update files list with document session info
$.each(file_api.env.file_list || [], function(i, file) {
// skip files from a different folder (in multi-folder listing)
if (file.filename.indexOf(prefix) >= 0)
file_api.file_session_data_set(file, response.result.sessions)
});
// refresh sessions info in time intervals
if (this.env.caps && this.env.caps.DOCEDIT && (rcmail.fileslist || rcmail.env.file))
this.file_list_worker = setTimeout(function() {
file_api.request('folder_info', {folder: folder, sessions: 1}, 'folder_info_response');
}, (rcmail.env.files_interval || 60) * 1000);
};
// Set html classes on a file list entry according to defined document sessions
this.file_session_data_set = function(file, sessions)
{
var classes = [], old_class = file.row.className;
if ($(file.row).is('.selected'))
classes.push('selected');
$.each(sessions || [], function(session_id, session) {
if (file.filename == session.file) {
if ($.inArray('session', classes) < 0)
classes.push('session');
if (session.is_owner && $.inArray('owner', classes) < 0)
classes.push('owner');
else if (session.is_invited && $.inArray('invited', classes) < 0)
classes.push('invited');
}
});
if (classes.length || old_class.length)
$(file.row).attr('class', classes.join(' '));
};
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), false);
};
// 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.fileslist && (opener.file_api.env.folder == dir || !opener.file_api.env.folder))
self = opener.file_api;
// also try parent for framed UI (Elastic)
else if (rcmail.is_framed() && parent.rcmail.fileslist && (parent.file_api.env.folder == dir || !parent.file_api.env.folder))
self = parent.file_api;
}
// @TODO: consider list modification "in-place" instead of full reload
self.file_list();
self.quota();
if (rcmail.env.file) {
if (rcmail.is_framed()) {
parent.$('.ui-dialog:visible > .ui-dialog-buttonpane button.cancel').click();
}
else
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 = {}, button_classes = ['mainaction save file overwrite'],
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;
kolab_dialog_close(this, true);
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) {
button_classes.push('save file overwrite all');
buttons[rcmail.gettext('kolab_files.fileoverwriteall')] = function() {
var f = {}, action = move ? 'file_move' : 'file_copy';
$.each(list, function() { f[this.src] = this.dst; });
kolab_dialog_close(this, true);
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();
kolab_dialog_close(this, true);
if (list.length)
file_api.file_move_ask_user(list, move);
else if (move)
file_api.file_list();
};
button_classes.push('cancel file skip');
buttons[rcmail.gettext('kolab_files.fileskip')] = skip_func;
if (list.length > 1) {
button_classes.push('cancel file skip all');
buttons[rcmail.gettext('kolab_files.fileskipall')] = function() {
kolab_dialog_close(this, true);
if (move)
file_api.file_list();
};
}
// open jquery UI dialog
kolab_dialog_show(dialog.html(text), {
title: rcmail.gettext('confirmationtitle'),
close: skip_func,
buttons: buttons,
button_classes: button_classes,
height: 50,
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) create (or clone) request
this.file_create = function(file, type, edit, cloneof)
{
this.file_create_edit_file = edit ? file : null;
this.file_create_edit_type = edit ? type : null;
this.file_create_folder = this.file_path(file);
if (cloneof) {
this.req = this.set_busy(true, 'kolab_files.filecopying');
this.request('file_copy', {file: cloneof, 'new': file}, 'file_create_response');
}
else {
this.req = this.set_busy(true, 'kolab_files.filecreating');
this.request('file_create', {file: file, 'content-type': type, content: ''}, 'file_create_response');
}
};
// file(s) create response handler
this.file_create_response = function(response)
{
if (!this.response(response))
return;
// @TODO: we could update metadata instead
if (this.file_create_folder == this.env.folder)
this.file_list();
// open the file for editing if editable
if (this.file_create_edit_file) {
var viewer = this.file_type_supported(this.file_create_edit_type, this.env.caps);
this.file_open(this.file_create_edit_file, viewer, {action: 'edit'});
}
};
// 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 form = $(form),
field = $('input[type=file]', form).get(0),
files = field.files ? field.files.length : field.value ? 1 : 0;
if (!files || !this.file_upload_size_check(field.files))
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'};
}
file_api.file_upload_progress_stop(event.data.ts);
// refresh the list on upload success
file_api.file_upload_response(response);
});
};
// refresh the list on upload success
this.file_upload_response = function(response)
{
if (this.response_parse(response)) {
this.file_list();
this.quota();
}
};
// check upload max size
this.file_upload_size_check = function(files)
{
var i, size = 0, maxsize = rcmail.env.files_max_upload;
if (maxsize && files) {
for (i=0; i < files.length; i++)
size += files[i].size || files[i].fileSize;
if (size > maxsize) {
rcmail.alert_dialog(rcmail.get_label('kolab_files.uploadsizeerror').replace('$size', kolab_files_file_size(maxsize)));
return false;
}
}
return true;
};
// post the given form to a hidden iframe
this.file_upload_form = function(form, action, onload)
{
var ts = new Date().getTime(),
frame_name = 'fileupload' + ts;
// upload progress support
if (rcmail.env.files_progress_name) {
var fname = rcmail.env.files_progress_name,
field = $('input[name='+fname+']', form);
if (!field.length) {
field = $('<input>').attr({type: 'hidden', name: fname});
field.prependTo(form);
}
field.val(ts);
this.file_upload_progress(ts, true);
}
rcmail.display_progress({name: 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="' + rcmail.assets_path('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}),
method: 'POST'
}).attr(form.encoding ? 'encoding' : 'enctype', 'multipart/form-data')
.submit();
};
// handler when files are dropped to a designated area.
// compose a multipart form data and submit it to the server
this.file_drop = function(e)
{
var files = e.target.files || e.dataTransfer.files;
if (!files || !files.length || !this.file_upload_size_check(files))
return;
// prepare multipart form data composition
var ts = new Date().getTime(),
formdata = window.FormData ? new FormData() : null,
fieldname = 'file[]',
boundary = '------multipartformboundary' + (new Date).getTime(),
dashdash = '--', crlf = '\r\n',
multipart = dashdash + boundary + crlf;
// inline function to submit the files to the server
var submit_data = function() {
var multiple = files.length > 1;
rcmail.display_progress({name: ts});
if (rcmail.env.files_progress_name)
file_api.file_upload_progress(ts, true);
// complete multipart content and post request
multipart += dashdash + boundary + dashdash + crlf;
$.ajax({
type: 'POST',
dataType: 'json',
url: file_api.env.url + file_api.url('file_upload', {folder: file_api.env.folder}),
contentType: formdata ? false : 'multipart/form-data; boundary=' + boundary,
processData: false,
timeout: 0, // disable default timeout set in ajaxSetup()
data: formdata || multipart,
headers: {'X-Session-Token': file_api.env.token},
success: function(data) {
file_api.file_upload_progress_stop(ts);
file_api.file_upload_response(data);
},
error: function(o, status, err) {
file_api.file_upload_progress_stop(ts);
rcmail.http_error(o, status, err);
},
xhr: function() {
var xhr = jQuery.ajaxSettings.xhr();
if (!formdata && xhr.sendAsBinary)
xhr.send = xhr.sendAsBinary;
return xhr;
}
});
};
// upload progress supported (and handler exists)
// add progress ID to the request - need to be added before files
if (rcmail.env.files_progress_name) {
if (formdata)
formdata.append(rcmail.env.files_progress_name, ts);
else
multipart += 'Content-Disposition: form-data; name="' + rcmail.env.files_progress_name + '"'
+ crlf + crlf + ts + crlf + dashdash + boundary + crlf;
}
// get contents of all dropped files
var f, j, i = 0, last = files.length - 1;
for (j = 0; j <= last && (f = files[i]); i++) {
if (!f.name) f.name = f.fileName;
if (!f.size) f.size = f.fileSize;
if (!f.type) f.type = 'application/octet-stream';
// file name contains non-ASCII characters, do UTF8-binary string conversion.
if (!formdata && /[^\x20-\x7E]/.test(f.name))
f.name_bin = unescape(encodeURIComponent(f.name));
// do it the easy way with FormData (FF 4+, Chrome 5+, Safari 5+)
if (formdata) {
formdata.append(fieldname, f);
if (j == last)
return submit_data();
}
// use FileReader supporetd by Firefox 3.6
else if (window.FileReader) {
var reader = new FileReader();
// closure to pass file properties to async callback function
reader.onload = (function(file, j) {
return function(e) {
multipart += 'Content-Disposition: form-data; name="' + fieldname + '"';
multipart += '; filename="' + (f.name_bin || file.name) + '"' + crlf;
multipart += 'Content-Length: ' + file.size + crlf;
multipart += 'Content-Type: ' + file.type + crlf + crlf;
multipart += reader.result + crlf;
multipart += dashdash + boundary + crlf;
if (j == last) // we're done, submit the data
return submit_data();
}
})(f,j);
reader.readAsBinaryString(f);
}
j++;
}
};
// upload progress requests
this.file_upload_progress = function(id, init)
{
if (init && id)
this.uploads[id] = this.env.folder;
setTimeout(function() {
if (id && file_api.uploads[id])
file_api.request('upload_progress', {id: id}, 'file_upload_progress_response');
}, rcmail.env.files_progress_time * 1000);
};
// upload progress response
this.file_upload_progress_response = function(response)
{
if (!this.response(response))
return;
var param = response.result;
if (!param.id || !this.uploads[param.id])
return;
if (param.total) {
param.name = param.id;
if (!param.done)
param.text = kolab_files_progress_str(param);
rcmail.display_progress(param);
}
if (!param.done && param.total)
this.file_upload_progress(param.id);
else
delete this.uploads[param.id];
};
this.file_upload_progress_stop = function(id)
{
if (id) {
delete this.uploads[id];
rcmail.display_progress({name: id});
}
};
// open file in new window, using file API viewer
this.file_open = function(file, viewer, params)
{
var args = {
_task: 'files',
_action: params && params.action ? params.action : 'open',
_file: file,
_viewer: viewer || 0,
_frame: 1
};
if (rcmail.is_framed())
args._framed = 1;
if (params && params.session)
args._session = params.session;
if (rcmail.env.extwin)
args._extwin = 1;
href = '?' + $.param(args);
if (params && params.local)
location.href = href;
else
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);
}
};
// document session delete request
this.document_delete = function(id)
{
this.req = this.set_busy(true, 'kolab_files.sessionterminating');
this.request('document_delete', {id: id}, 'document_delete_response');
};
// document session delete response handler
this.document_delete_response = function(response, params)
{
if (!this.response(response))
return;
var list = rcmail.sessionslist;
// remove session from the list (if sessions list exist)
if (list)
list.remove_row(params.id);
if (this.env.sessions_list)
delete this.env.sessions_list[params.id];
// @todo: force sessions info update, update files list
};
// Invite document session participants
this.document_invite = function(id, attendees, comment)
{
var list = [];
// expect attendees to be email => name hash
$.each(attendees || {}, function() { list.push(this); });
if (list.length) {
this.req = this.set_busy(true, 'kolab_files.documentinviting');
this.request('document_invite', {id: id, users: list, comment: comment || ''}, 'document_invite_response');
}
};
// document invite response handler
this.document_invite_response = function(response)
{
if (!this.response(response) || !response.result)
return;
var info = rcmail.env.file_data,
table = $('#document-editors-dialog table > tbody');
$.each(response.result.list || {}, function() {
var record = kolab_files_attendee_record(this.user, this.status, this.user_name);
table.append(record);
if (info.session && info.session.invitations)
info.session.invitations.push($.extend({status: 'invited', record: record}, this));
});
};
// Cancel invitations to an editing session
this.document_cancel = function(id, attendees)
{
if (attendees.length) {
this.req = this.set_busy(true, 'kolab_files.documentcancelling');
this.request('document_cancel', {id: id, users: attendees}, 'document_cancel_response');
}
};
// document_cancel response handler
this.document_cancel_response = function(response)
{
if (!this.response(response) || !response.result)
return;
var info = rcmail.env.file_data;
$.each(response.result.list || {}, function(i, user) {
var invitations = [];
$.each(info.session.invitations, function(i, u) {
if (u.user == user && u.record)
u.record.remove();
else
invitations.push(u);
});
info.session.invitations = invitations;
});
};
// 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 = {};
$.each(result.auth_errors || {}, function(i, v) {
// for normal errors we display only an error message, other will display a dialog
if (v.error) {
if (v.error == 580) {
rcmail.display_message(rcmail.gettext('kolab_files.storageautherror').replace('$title', i), 'error');
}
delete result.auth_errors[i];
}
});
$.extend(this.auth_errors, result.auth_errors);
}
// ask for password to the first storage on the list
var ref = this;
$.each(this.auth_errors || [], function(i, v) {
if (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);
if ($('#files-folder-auth-dialog').is(':visible'))
return false;
dialog.find('table.propform').remove();
$('.auth-options', dialog).before(content);
args.buttons[this.t('kolab_files.save')] = function() {
var data = {folder: label, list: 1, permissions: 1, level: -2};
$('input', dialog).each(function() {
data[this.name] = this.type == 'checkbox' && !this.checked ? '' : this.value;
});
file_api.req = file_api.set_busy(true, 'kolab_files.authenticating');
file_api.request('folder_auth', data, 'folder_auth_response');
kolab_dialog_close(this);
};
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);
args.button_classes = ['mainaction save', 'cancel'];
// show dialog window
kolab_dialog_show(dialog, args, function() {
// focus first empty input
$('input', dialog).each(function() {
if (!this.value) {
this.focus();
return false;
}
});
});
return true;
};
// folder_auth handler
this.folder_auth_response = function(response, params)
{
if (!this.response(response)) {
this.folder_list_auth_errors();
return;
}
delete this.auth_errors[response.result.folder];
// go to the next one
this.folder_list_auth_errors();
// update the list
this.folder_list_merge(response.result.folder, response.result.list, params.level);
};
// Update folders list with additional folders
this.folder_list_merge = function(folder, list, level)
{
if (!list || !list.length)
return;
var i, last, folders, result = {}, index = [], ref = this;
if (this.list_merge_lock) {
return setTimeout(function() { ref.folder_list_merge(folder, list); }, 50);
}
this.list_merge_lock = true;
// Parse result
folders = this.folder_list_parse(list);
if (level < 0)
level *= -1;
// Add folders from the external source to the list
$.each(folders, function(i, f) {
if (ref.env.folders[i]) {
last = i;
return;
}
// We don't use this id
delete f.id;
// Append row to the list
var row = file_api.folder_list_row(i, f);
if (row)
ref.list_element.append(row);
// Need env.folders update so all parents are available
// in successive folder_list_row() calls
ref.env.folders[i] = f;
index.push(i);
// Load deeper folder hierarchies
if (level && f.depth == level)
ref.folder_list_update_wait(i);
});
// Reset folders list widget
rcmail.folder_list.reset(true, true);
// Rebuild folders list with correct order
for (i in this.env.folders) {
result[i] = this.env.folders[i];
if (i === last)
break;
}
for (i in index) {
result[index[i]] = this.env.folders[index[i]];
}
for (i in this.env.folders) {
if (!result[i]) {
result[i] = this.env.folders[i];
}
}
this.env.folders = result;
if ((folder = this.env.folder) || (folder = this.env.init_folder)) {
if (this.env.folders[folder]) {
this.env.init_folder = null;
if (folder != rcmail.folder_list.get_selection())
rcmail.folder_list.select(folder);
}
}
this.list_merge_lock = false;
};
// 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);
};
};