Page MenuHomePhorge

No OneTemporary

Size
204 KB
Referenced Files
None
Subscribers
None
diff --git a/plugins/kolab_files/kolab_files.js b/plugins/kolab_files/kolab_files.js
index 730e2f14..87d64218 100644
--- a/plugins/kolab_files/kolab_files.js
+++ b/plugins/kolab_files/kolab_files.js
@@ -1,4379 +1,4382 @@
/**
* 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();
});
/**********************************************************/
/********* 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('kolab_files.' + label); },
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;
};
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()
{
// check document "unsaved changes" state and display a warning
if (this.commands['document-save'])
this.confirm_dialog(this.gettext('kolab_files.unsavedchanges'), 'kolab_files.terminate', function() {
file_api.document_delete(rcmail.env.file_data.session.id);
}, {button_class: 'delete'});
};
// 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.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(post[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(post[i]);
+ 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.deleted_session = id;
this.request('document_delete', {id: id}, 'document_delete_response');
};
// document session delete response handler
this.document_delete_response = function(response)
{
if (!this.response(response))
return;
if (rcmail.task == 'files' && rcmail.env.action == 'edit') {
if (document_editor && document_editor.terminate)
document_editor.terminate();
// use timeout to have a chance to properly propagate termination request
setTimeout(function() { window.close(); }, 500);
}
// @todo: force sessions info update
var win = window, list = rcmail.sessionslist;
if (!list) {
win = window.opener || window.parent;
if (win && win.rcmail && win.file_api)
list = win.rcmail.sessionslist;
}
// remove session from the list (if sessions list exist)
if (list)
list.remove_row(this.deleted_session);
if (win && win.file_api && win.file_api.env.sessions_list)
delete win.file_api.env.sessions_list[this.deleted_session];
// For Elastic: hide the parent dialog
if (rcmail.is_framed()) {
parent.$('.ui-dialog:visible button.cancel').click();
}
};
// 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);
};
};
diff --git a/plugins/kolab_files/lib/kolab_files_engine.php b/plugins/kolab_files/lib/kolab_files_engine.php
index 97582154..1e5ac8b4 100644
--- a/plugins/kolab_files/lib/kolab_files_engine.php
+++ b/plugins/kolab_files/lib/kolab_files_engine.php
@@ -1,1808 +1,1808 @@
<?php
/**
* Kolab files storage engine
*
* @version @package_version@
* @author Aleksander Machniak <machniak@kolabsys.com>
*
* Copyright (C) 2013-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/>.
*/
class kolab_files_engine
{
private $plugin;
private $rc;
private $url;
private $url_srv;
private $timeout = 600;
private $files_sort_cols = array('name', 'mtime', 'size');
private $sessions_sort_cols = array('name');
const API_VERSION = 4;
/**
* Class constructor
*/
public function __construct($plugin, $client_url, $server_url = null)
{
$this->url = rtrim(rcube_utils::resolve_url($client_url), '/ ');
$this->url_srv = $server_url ? rtrim(rcube_utils::resolve_url($server_url), '/ ') : $this->url;
$this->plugin = $plugin;
$this->rc = $plugin->rc;
$this->timeout = $this->rc->config->get('session_lifetime') * 60;
}
/**
* User interface initialization
*/
public function ui()
{
$this->plugin->add_texts('localization/');
$templates = array();
// set templates of Files UI and widgets
if ($this->rc->task == 'mail') {
if (in_array($this->rc->action, array('', 'show', 'compose'))) {
$templates[] = 'compose_plugin';
}
if (in_array($this->rc->action, array('show', 'preview', 'get'))) {
$templates[] = 'message_plugin';
if ($this->rc->action == 'get') {
// add "Save as" button into attachment toolbar
$this->plugin->add_button(array(
'id' => 'saveas',
'name' => 'saveas',
'type' => 'link',
'onclick' => 'kolab_directory_selector_dialog()',
'class' => 'button buttonPas saveas',
'classact' => 'button saveas',
'label' => 'kolab_files.save',
'title' => 'kolab_files.saveto',
), 'toolbar');
}
else {
// add "Save as" button into attachment menu
$this->plugin->add_button(array(
'id' => 'attachmenusaveas',
'name' => 'attachmenusaveas',
'type' => 'link',
'wrapper' => 'li',
'onclick' => 'return false',
'class' => 'icon active saveas',
'classact' => 'icon active saveas',
'innerclass' => 'icon active saveas',
'label' => 'kolab_files.saveto',
), 'attachmentmenu');
}
}
$list_widget = true;
}
else if (!$this->rc->action && in_array($this->rc->task, array('calendar', 'tasks'))) {
$list_widget = true;
$templates[] = 'compose_plugin';
}
else if ($this->rc->task == 'files') {
$templates[] = 'files';
// get list of external sources
$this->get_external_storage_drivers();
// these labels may be needed even if fetching ext sources failed
$this->plugin->add_label('folderauthtitle', 'authenticating', 'foldershare');
}
if ($list_widget) {
$this->folder_list_env();
$this->plugin->add_label('save', 'cancel', 'saveto',
'saveall', 'fromcloud', 'attachsel', 'selectfiles', 'attaching',
'collection_audio', 'collection_video', 'collection_image', 'collection_document',
'folderauthtitle', 'authenticating'
);
}
// add taskbar button
if (empty($_REQUEST['framed'])) {
$this->plugin->add_button(array(
'command' => 'files',
'class' => 'button-files',
'classsel' => 'button-files button-selected',
'innerclass' => 'button-inner',
'label' => 'kolab_files.files',
'type' => 'link'
), 'taskbar');
}
$caps = $this->capabilities();
$this->plugin->include_stylesheet($this->plugin->local_skin_path().'/style.css');
$this->plugin->include_script($this->url . '/js/files_api.js');
$this->plugin->include_script('kolab_files.js');
$this->rc->output->set_env('files_url', $this->url . '/api/');
$this->rc->output->set_env('files_token', $this->get_api_token());
$this->rc->output->set_env('files_caps', $caps);
$this->rc->output->set_env('files_api_version', $caps['VERSION'] ?: 3);
$this->rc->output->set_env('files_user', $this->rc->get_user_name());
if ($caps['DOCEDIT']) {
$this->plugin->add_label('declinednotice', 'invitednotice', 'acceptedownernotice',
'declinedownernotice', 'requestednotice', 'acceptednotice', 'declinednotice',
'more', 'accept', 'decline', 'join', 'status', 'when', 'file', 'comment',
'statusaccepted', 'statusinvited', 'statusdeclined', 'statusrequested',
'invitationaccepting', 'invitationdeclining', 'invitationrequesting',
'close', 'invitationtitle', 'sessions');
}
if (!empty($templates)) {
$collapsed_folders = (string) $this->rc->config->get('kolab_files_collapsed_folders');
$this->rc->output->include_script('treelist.js');
$this->rc->output->set_env('kolab_files_collapsed_folders', $collapsed_folders);
// register template objects for dialogs (and main interface)
$this->rc->output->add_handlers(array(
'folder-create-form' => array($this, 'folder_create_form'),
'folder-edit-form' => array($this, 'folder_edit_form'),
'folder-mount-form' => array($this, 'folder_mount_form'),
'folder-auth-options'=> array($this, 'folder_auth_options'),
'file-search-form' => array($this, 'file_search_form'),
'file-rename-form' => array($this, 'file_rename_form'),
'file-create-form' => array($this, 'file_create_form'),
'file-edit-dialog' => array($this, 'file_edit_dialog'),
'file-session-dialog' => array($this, 'file_session_dialog'),
'filelist' => array($this, 'file_list'),
'sessionslist' => array($this, 'sessions_list'),
'filequotadisplay' => array($this, 'quota_display'),
'document-editors-dialog' => array($this, 'document_editors_dialog'),
));
if ($this->rc->task != 'files') {
// add dialog(s) content at the end of page body
foreach ($templates as $template) {
$this->rc->output->add_footer(
$this->rc->output->parse('kolab_files.' . $template, false, false));
}
}
}
}
/**
* Engine actions handler
*/
public function actions()
{
if ($this->rc->task == 'files' && $this->rc->action) {
$action = $this->rc->action;
}
else if ($this->rc->task != 'files' && $_POST['act']) {
$action = $_POST['act'];
}
else {
$action = 'index';
}
$method = 'action_' . str_replace('-', '_', $action);
if (method_exists($this, $method)) {
$this->plugin->add_texts('localization/');
$this->{$method}();
}
}
/**
* Template object for folder creation form
*/
public function folder_create_form($attrib)
{
$attrib['name'] = 'folder-create-form';
if (empty($attrib['id'])) {
$attrib['id'] = 'folder-create-form';
}
$input_name = new html_inputfield(array('id' => 'folder-name', 'name' => 'name', 'size' => 30));
$select_parent = new html_select(array('id' => 'folder-parent', 'name' => 'parent'));
$table = new html_table(array('cols' => 2, 'class' => 'propform'));
$table->add('title', html::label('folder-name', rcube::Q($this->plugin->gettext('foldername'))));
$table->add(null, $input_name->show());
$table->add('title', html::label('folder-parent', rcube::Q($this->plugin->gettext('folderinside'))));
$table->add(null, $select_parent->show());
$out = $table->show();
// add form tag around text field
if (empty($attrib['form'])) {
$out = $this->rc->output->form_tag($attrib, $out);
}
$this->plugin->add_label('foldercreating', 'foldercreatenotice', 'create', 'foldercreate', 'cancel', 'addfolder');
$this->rc->output->add_gui_object('folder-create-form', $attrib['id']);
return $out;
}
/**
* Template object for folder editing form
*/
public function folder_edit_form($attrib)
{
$attrib['name'] = 'folder-edit-form';
if (empty($attrib['id'])) {
$attrib['id'] = 'folder-edit-form';
}
$input_name = new html_inputfield(array('id' => 'folder-edit-name', 'name' => 'name', 'size' => 30));
$select_parent = new html_select(array('id' => 'folder-edit-parent', 'name' => 'parent'));
$table = new html_table(array('cols' => 2, 'class' => 'propform'));
$table->add('title', html::label('folder-edit-name', rcube::Q($this->plugin->gettext('foldername'))));
$table->add(null, $input_name->show());
$table->add('title', html::label('folder-edit-parent', rcube::Q($this->plugin->gettext('folderinside'))));
$table->add(null, $select_parent->show());
$out = $table->show();
// add form tag around text field
if (empty($attrib['form'])) {
$out = $this->rc->output->form_tag($attrib, $out);
}
$this->plugin->add_label('folderupdating', 'folderupdatenotice', 'save', 'folderedit', 'cancel');
$this->rc->output->add_gui_object('folder-edit-form', $attrib['id']);
return $out;
}
/**
* Template object for folder mounting form
*/
public function folder_mount_form($attrib)
{
$sources = $this->rc->output->get_env('external_sources');
if (empty($sources) || !is_array($sources)) {
return '';
}
$attrib['name'] = 'folder-mount-form';
if (empty($attrib['id'])) {
$attrib['id'] = 'folder-mount-form';
}
// build form content
$table = new html_table(array('cols' => 2, 'class' => 'propform'));
$input_name = new html_inputfield(array('id' => 'folder-mount-name', 'name' => 'name', 'size' => 30));
$input_driver = new html_radiobutton(array('name' => 'driver', 'size' => 30));
$table->add('title', html::label('folder-mount-name', rcube::Q($this->plugin->gettext('name'))));
$table->add(null, $input_name->show());
foreach ($sources as $key => $source) {
$id = 'source-' . $key;
$form = new html_table(array('cols' => 2, 'class' => 'propform driverform'));
foreach ((array) $source['form'] as $idx => $label) {
$iid = $id . '-' . $idx;
$type = stripos($idx, 'pass') !== false ? 'html_passwordfield' : 'html_inputfield';
$input = new $type(array('size' => 30));
$form->add('title', html::label($iid, rcube::Q($label)));
$form->add(null, $input->show('', array(
'id' => $iid,
'name' => $key . '[' . $idx . ']'
)));
}
$row = $input_driver->show(null, array('value' => $key))
. html::img(array('src' => $source['image'], 'alt' => $key, 'title' => $source['name']))
. html::div(null, html::span('name', rcube::Q($source['name']))
. html::br()
. html::span('description hint', rcube::Q($source['description']))
. $form->show()
);
$table->add(array('id' => $id, 'colspan' => 2, 'class' => 'source'), $row);
}
$out = $table->show() . $this->folder_auth_options(array('suffix' => '-form'));
// add form tag around text field
if (empty($attrib['form'])) {
$out = $this->rc->output->form_tag($attrib, $out);
}
$this->plugin->add_label('foldermounting', 'foldermountnotice', 'foldermount',
'save', 'cancel', 'folderauthtitle', 'authenticating'
);
$this->rc->output->add_gui_object('folder-mount-form', $attrib['id']);
return $out;
}
/**
* Template object for folder authentication options
*/
public function folder_auth_options($attrib)
{
$checkbox = new html_checkbox(array(
'name' => 'store_passwords',
'value' => '1',
'class' => 'pretty-checkbox',
));
return html::div('auth-options',
html::label(null, $checkbox->show() . ' ' . $this->plugin->gettext('storepasswords'))
. html::p('description hint', $this->plugin->gettext('storepasswordsdesc'))
);
}
/**
* Template object for sharing form
*/
public function folder_share_form($attrib)
{
$folder = rcube_utils::get_input_value('_folder', rcube_utils::INPUT_GET, true);
$info = $this->get_share_info($folder);
if (empty($info) || empty($info['form'])) {
$msg = $this->plugin->gettext($info === false ? 'sharepermissionerror' : 'sharestorageerror');
return html::div(array('class' => 'boxerror', 'id' => 'share-notice'), rcube::Q($msg));
}
if (empty($attrib['id'])) {
$attrib['id'] = 'foldershareform';
}
$out = '';
foreach ($info['form'] as $mode => $tab) {
$table = new html_table(array(
'cols' => ($tab['list_column'] ? 1 : count($tab['form'])) + 1,
'data-mode' => $mode,
'data-single' => $tab['single'] ? 1 : 0,
));
$submit = new html_button(array('class' => 'btn btn-secondary submit'));
$delete = new html_button(array('class' => 'btn btn-secondary btn-danger delete'));
$fields = array();
// Table header
if (!empty($tab['list_column'])) {
$table->add_header(null, rcube::Q($tab['list_column_label']));
}
else {
foreach ($tab['form'] as $field) {
$table->add_header(null, rcube::Q($field['title']));
}
}
$table->add_header(null, '');
// Submit form
$record = '';
foreach ($tab['form'] as $index => $field) {
$add = '';
if ($field['type'] == 'select') {
$ff = new html_select(array('name' => $index));
foreach ($field['options'] as $opt_idx => $opt) {
$ff->add($opt, $opt_idx);
}
}
else if ($field['type'] == 'password') {
$ff = new html_passwordfield(array(
'name' => $index,
'placeholder' => $this->rc->gettext('password'),
));
$add = new html_passwordfield(array(
'name' => $index . 'confirm',
'placeholder' => $this->plugin->gettext('confirmpassword'),
));
$add = $add->show();
}
else {
$ff = new html_inputfield(array(
'name' => $index,
'data-autocomplete' => $field['autocomplete'],
'placeholder' => $field['placeholder'],
));
}
if (!empty($tab['list_column'])) {
$record .= $ff->show() . $add;
}
else {
$table->add(null, $ff->show() . $add);
}
$fields[$index] = $ff;
}
if (!empty($tab['list_column'])) {
$table->add('form', $record);
}
$hidden = '';
foreach ((array) $tab['extra_fields'] as $key => $default) {
$h = new html_hiddenfield(array('name' => $key, 'value' => $default));
$hidden .= $h->show();
}
$table->add(null, $hidden . $submit->show(rcube::Q($tab['label'] ?: $this->plugin->gettext('submit'))));
// Existing entries
foreach ((array) $info['rights'] as $entry) {
if ($entry['mode'] == $mode) {
if (!empty($tab['list_column'])) {
- $table->add(null, html::span('name', rcube::Q($entry[$tab['list_column']])));
+ $table->add(null, html::span(array('title' => $entry['title'], 'class' => 'name'), rcube::Q($entry[$tab['list_column']])));
}
else {
foreach ($tab['form'] as $index => $field) {
if ($fields[$index] instanceof html_select) {
$table->add(null, $fields[$index]->show($entry[$index]));
}
else if ($fields[$index] instanceof html_inputfield) {
- $table->add(null, html::span('name', rcube::Q($entry[$index])));
+ $table->add(null, html::span(array('title' => $entry['title'], 'class' => 'name'), rcube::Q($entry[$index])));
}
}
}
$hidden = '';
foreach ((array) $tab['extra_fields'] as $key => $default) {
if (isset($entry[$key])) {
$h = new html_hiddenfield(array('name' => $key, 'value' => $entry[$key]));
$hidden .= $h->show();
}
}
$table->add(null, $hidden . $delete->show(rcube::Q($this->rc->gettext('delete'))));
}
}
$this->rc->output->add_label('kolab_files.updatingfolder' . $mode);
$out .= html::tag('fieldset', $mode, html::tag('legend', null, rcube::Q($tab['title'])) . $table->show()) . "\n";
}
$this->rc->autocomplete_init();
$this->rc->output->set_env('folder', $folder);
$this->rc->output->set_env('form_info', $info['form']);
$this->rc->output->add_gui_object('shareform', $attrib['id']);
$this->rc->output->add_label('kolab_files.submit', 'kolab_files.passwordconflict', 'delete');
return html::div($attrib, $out);
}
/**
* Template object for file edit dialog/warnings
*/
public function file_edit_dialog($attrib)
{
$this->plugin->add_label('select', 'create', 'cancel', 'editfiledialog', 'editfilesessions',
'newsession', 'ownedsession', 'invitedsession', 'joinsession', 'editfilero', 'editfilerotitle',
'newsessionro'
);
return '<div></div>';
}
/**
* Template object for file session dialog
*/
public function file_session_dialog($attrib)
{
$this->plugin->add_label('join', 'open', 'close', 'request', 'cancel',
'sessiondialog', 'sessiondialogcontent');
return '<div></div>';
}
/**
* Template object for dcument editors dialog
*/
public function document_editors_dialog($attrib)
{
$table = new html_table($attrib + array('cols' => 3, 'border' => 0, 'cellpadding' => 0));
$table->add_header('username', $this->plugin->gettext('participant'));
$table->add_header('status', $this->plugin->gettext('status'));
$table->add_header('options', null);
$input = new html_inputfield(array('name' => 'participant', 'id' => 'invitation-editor-name', 'size' => 30, 'class' => 'form-control'));
$textarea = new html_textarea(array('name' => 'comment', 'id' => 'invitation-comment',
'rows' => 4, 'cols' => 55, 'class' => 'form-control', 'title' => $this->plugin->gettext('invitationtexttitle')));
$button = new html_inputfield(array('type' => 'button', 'class' => 'button', 'id' => 'invitation-editor-add',
'value' => $this->plugin->gettext('addparticipant')));
$this->plugin->add_label('manageeditors', 'statusorganizer', 'addparticipant');
// initialize attendees autocompletion
$this->rc->autocomplete_init();
return html::div(null, $table->show() . html::div(null,
html::div('form-searchbar', $input->show() . " " . $button->show())
. html::p('attendees-commentbox', html::label(null,
$this->plugin->gettext('invitationtextlabel') . $textarea->show())
)
));
}
/**
* Template object for file_rename form
*/
public function file_rename_form($attrib)
{
$attrib['name'] = 'file-rename-form';
if (empty($attrib['id'])) {
$attrib['id'] = 'file-rename-form';
}
$input_name = new html_inputfield(array('id' => 'file-rename-name', 'name' => 'name', 'size' => 50));
$table = new html_table(array('cols' => 2, 'class' => 'propform'));
$table->add('title', html::label('file-rename-name', rcube::Q($this->plugin->gettext('filename'))));
$table->add(null, $input_name->show());
$out = $table->show();
// add form tag around text field
if (empty($attrib['form'])) {
$out = $this->rc->output->form_tag($attrib, $out);
}
$this->plugin->add_label('save', 'cancel', 'fileupdating', 'renamefile');
$this->rc->output->add_gui_object('file-rename-form', $attrib['id']);
return $out;
}
/**
* Template object for file_create form
*/
public function file_create_form($attrib)
{
$attrib['name'] = 'file-create-form';
if (empty($attrib['id'])) {
$attrib['id'] = 'file-create-form';
}
$input_name = new html_inputfield(array('id' => 'file-create-name', 'name' => 'name', 'size' => 30));
$select_parent = new html_select(array('id' => 'file-create-parent', 'name' => 'parent'));
$select_type = new html_select(array('id' => 'file-create-type', 'name' => 'type'));
$table = new html_table(array('cols' => 2, 'class' => 'propform'));
$types = array();
foreach ($this->get_mimetypes('edit') as $type => $mimetype) {
$types[$type] = $mimetype['ext'];
$select_type->add($mimetype['label'], $type);
}
$table->add('title', html::label('file-create-name', rcube::Q($this->plugin->gettext('filename'))));
$table->add(null, $input_name->show());
$table->add('title', html::label('file-create-type', rcube::Q($this->plugin->gettext('type'))));
$table->add(null, $select_type->show());
$table->add('title', html::label('file-create-parent', rcube::Q($this->plugin->gettext('folderinside'))));
$table->add(null, $select_parent->show());
$out = $table->show();
// add form tag around text field
if (empty($attrib['form'])) {
$out = $this->rc->output->form_tag($attrib, $out);
}
$this->plugin->add_label('create', 'cancel', 'filecreating', 'createfile', 'createandedit',
'copyfile', 'copyandedit');
$this->rc->output->add_gui_object('file-create-form', $attrib['id']);
$this->rc->output->set_env('file_extensions', $types);
return $out;
}
/**
* Template object for file search form in "From cloud" dialog
*/
public function file_search_form($attrib)
{
$attrib += array(
'name' => '_q',
'gui-object' => 'filesearchbox',
'form-name' => 'filesearchform',
'command' => 'files-search',
'reset-command' => 'files-search-reset',
);
// add form tag around text field
return $this->rc->output->search_form($attrib);
}
/**
* Template object for files list
*/
public function file_list($attrib)
{
return $this->list_handler($attrib, 'files');
}
/**
* Template object for sessions list
*/
public function sessions_list($attrib)
{
return $this->list_handler($attrib, 'sessions');
}
/**
* Creates unified template object for files|sessions list
*/
protected function list_handler($attrib, $type = 'files')
{
$prefix = 'kolab_' . $type . '_';
$c_prefix = 'kolab_files' . ($type != 'files' ? '_' . $type : '') . '_';
// define list of cols to be displayed based on parameter or config
if (empty($attrib['columns'])) {
$list_cols = $this->rc->config->get($c_prefix . 'list_cols');
$dont_override = $this->rc->config->get('dont_override');
$a_show_cols = is_array($list_cols) ? $list_cols : array('name');
$this->rc->output->set_env($type . '_col_movable', !in_array($c_prefix . 'list_cols', (array)$dont_override));
}
else {
$columns = str_replace(array("'", '"'), '', $attrib['columns']);
$a_show_cols = preg_split('/[\s,;]+/', $columns);
}
// make sure 'name' and 'options' column is present
if (!in_array('name', $a_show_cols)) {
array_unshift($a_show_cols, 'name');
}
if (!in_array('options', $a_show_cols)) {
array_unshift($a_show_cols, 'options');
}
$attrib['columns'] = $a_show_cols;
// save some variables for use in ajax list
$_SESSION[$prefix . 'list_attrib'] = $attrib;
// For list in dialog(s) remove all option-like columns
if ($this->rc->task != 'files') {
$a_show_cols = array_intersect($a_show_cols, $this->{$type . '_sort_cols'});
}
// set default sort col/order to session
if (!isset($_SESSION[$prefix . 'sort_col']))
$_SESSION[$prefix . 'sort_col'] = $this->rc->config->get($c_prefix . 'sort_col') ?: 'name';
if (!isset($_SESSION[$prefix . 'sort_order']))
$_SESSION[$prefix . 'sort_order'] = strtoupper($this->rc->config->get($c_prefix . 'sort_order') ?: 'asc');
// set client env
$this->rc->output->add_gui_object($type . 'list', $attrib['id']);
$this->rc->output->set_env($type . '_sort_col', $_SESSION[$prefix . 'sort_col']);
$this->rc->output->set_env($type . '_sort_order', $_SESSION[$prefix . 'sort_order']);
$this->rc->output->set_env($type . '_coltypes', $a_show_cols);
$this->rc->output->include_script('list.js');
$this->rc->output->add_label('kolab_files.abort', 'searching');
// attach css rules for mimetype icons
if (!$this->filetypes_style) {
$this->plugin->include_stylesheet($this->url . '/skins/default/images/mimetypes/style.css');
$this->filetypes_style = true;
}
$thead = '';
foreach ($this->list_head($attrib, $a_show_cols, $type) as $cell) {
$thead .= html::tag('th', array('class' => $cell['className'], 'id' => $cell['id']), $cell['html']);
}
return html::tag('table', $attrib,
html::tag('thead', null, html::tag('tr', null, $thead)) . html::tag('tbody', null, ''),
array('style', 'class', 'id', 'cellpadding', 'cellspacing', 'border', 'summary'));
}
/**
* Creates <THEAD> for message list table
*/
protected function list_head($attrib, $a_show_cols, $type = 'files')
{
$prefix = 'kolab_' . $type . '_';
$c_prefix = 'kolab_files_' . ($type != 'files' ? $type : '') . '_';
$skin_path = $_SESSION['skin_path'];
// check to see if we have some settings for sorting
$sort_col = $_SESSION[$prefix . 'sort_col'];
$sort_order = $_SESSION[$prefix . 'sort_order'];
$dont_override = (array)$this->rc->config->get('dont_override');
$disabled_sort = in_array($c_prefix . 'sort_col', $dont_override);
$disabled_order = in_array($c_prefix . 'sort_order', $dont_override);
$this->rc->output->set_env($prefix . 'disabled_sort_col', $disabled_sort);
$this->rc->output->set_env($prefix . 'disabled_sort_order', $disabled_order);
// define sortable columns
if ($disabled_sort)
$a_sort_cols = $sort_col && !$disabled_order ? array($sort_col) : array();
else
$a_sort_cols = $this->{$type . '_sort_cols'};
if (!empty($attrib['optionsmenuicon'])) {
$onclick = 'return ' . rcmail_output::JS_OBJECT_NAME . ".command('menu-open', '{$type}listmenu', this, event)";
$inner = $this->rc->gettext('listoptions');
if (is_string($attrib['optionsmenuicon']) && $attrib['optionsmenuicon'] != 'true') {
$inner = html::img(array('src' => $skin_path . $attrib['optionsmenuicon'], 'alt' => $this->rc->gettext('listoptions')));
}
$list_menu = html::a(array(
'href' => '#list-options',
'onclick' => $onclick,
'class' => 'listmenu',
'id' => $type . 'listmenulink',
'title' => $this->rc->gettext('listoptions'),
'tabindex' => '0',
), $inner);
}
else {
$list_menu = '';
}
$cells = array();
foreach ($a_show_cols as $col) {
// get column name
switch ($col) {
case 'options':
$col_name = $list_menu;
break;
default:
$col_name = rcube::Q($this->plugin->gettext($col));
}
// make sort links
if (in_array($col, $a_sort_cols)) {
$col_name = html::a(array(
'href' => "#sort",
'onclick' => 'return ' . rcmail_output::JS_OBJECT_NAME . ".command('$type-sort','$col',this)",
'title' => $this->plugin->gettext('sortby')
), $col_name);
}
else if ($col_name[0] != '<') {
$col_name = '<span class="' . $col .'">' . $col_name . '</span>';
}
$sort_class = $col == $sort_col && !$disabled_order ? " sorted$sort_order" : '';
$class_name = $col.$sort_class;
// put it all together
$cells[] = array('className' => $class_name, 'id' => "rcm$col", 'html' => $col_name);
}
return $cells;
}
/**
* Update files|sessions list object
*/
protected function list_update($prefs, $type = 'files')
{
$prefix = 'kolab_' . $type . '_list_';
$c_prefix = 'kolab_files' . ($type != 'files' ? '_' . $type : '') . '_list_';
$attrib = $_SESSION[$prefix . 'attrib'];
if (!empty($prefs[$c_prefix . 'cols'])) {
$attrib['columns'] = $prefs[$c_prefix . 'cols'];
$_SESSION[$prefix . 'attrib'] = $attrib;
}
$a_show_cols = $attrib['columns'];
$head = '';
foreach ($this->list_head($attrib, $a_show_cols, $type) as $cell) {
$head .= html::tag('th', array('class' => $cell['className'], 'id' => $cell['id']), $cell['html']);
}
$head = html::tag('tr', null, $head);
$this->rc->output->set_env($type . '_coltypes', $a_show_cols);
$this->rc->output->command($type . '_list_update', $head);
}
/**
* Template object for file info box
*/
public function file_info_box($attrib)
{
// print_r($this->file_data, true);
$table = new html_table(array('cols' => 2, 'class' => $attrib['class']));
// file name
$table->add('title', $this->plugin->gettext('name').':');
$table->add('data filename', $this->file_data['name']);
// file type
// @TODO: human-readable type name
$table->add('title', $this->plugin->gettext('type').':');
$table->add('data filetype', $this->file_data['type']);
// file size
$table->add('title', $this->plugin->gettext('size').':');
$table->add('data filesize', $this->rc->show_bytes($this->file_data['size']));
// file modification time
$table->add('title', $this->plugin->gettext('mtime').':');
$table->add('data filemtime', $this->file_data['mtime']);
// @TODO: for images: width, height, color depth, etc.
// @TODO: for text files: count of characters, lines, words
return $table->show();
}
/**
* Template object for file preview frame
*/
public function file_preview_frame($attrib)
{
if (empty($attrib['id'])) {
$attrib['id'] = 'filepreviewframe';
}
if ($frame = $this->file_data['viewer']['frame']) {
return $frame;
}
if ($href = $this->file_data['viewer']['href']) {
// file href attribute must be an absolute URL (Bug #2063)
if (!empty($href)) {
if (!preg_match('|^https?://|', $href)) {
$href = $this->url . '/api/' . $href;
}
}
}
else {
$token = $this->get_api_token();
$href = $this->url . '/api/?method=file_get'
. '&file=' . urlencode($this->file_data['filename'])
. '&token=' . urlencode($token);
}
$this->rc->output->add_gui_object('preview_frame', $attrib['id']);
$attrib['allowfullscreen'] = true;
$attrib['src'] = $href;
$attrib['onload'] = 'kolab_files_frame_load(this)';
// editor requires additional arguments via POST
if (!empty($this->file_data['viewer']['post'])) {
$attrib['src'] = 'program/resources/blank.gif';
$form_content = new html_hiddenfield();
$form_attrib = array(
'action' => $href,
'id' => $attrib['id'] . '-form',
'target' => $attrib['name'],
'method' => 'post',
);
foreach ($this->file_data['viewer']['post'] as $name => $value) {
$form_content->add(array('name' => $name, 'value' => $value));
}
$form = html::tag('form', $form_attrib, $form_content->show())
. html::script(array(), "\$('#{$attrib['id']}-form').submit()");
}
return html::iframe($attrib) . $form;
}
/**
* Template object for quota display
*/
public function quota_display($attrib)
{
if (!$attrib['id']) {
$attrib['id'] = 'rcmquotadisplay';
}
$quota_type = !empty($attrib['display']) ? $attrib['display'] : 'text';
$this->rc->output->add_gui_object('quotadisplay', $attrib['id']);
$this->rc->output->set_env('quota_type', $quota_type);
// get quota
$token = $this->get_api_token();
$request = $this->get_request(array('method' => 'quota'), $token);
// send request to the API
try {
$response = $request->send();
$status = $response->getStatus();
$body = @json_decode($response->getBody(), true);
if ($status == 200 && $body['status'] == 'OK') {
$quota = $body['result'];
}
else {
throw new Exception($body['reason'] ?: "Failed to get quota. Status: $status");
}
}
catch (Exception $e) {
rcube::raise_error($e, true, false);
$quota = array('total' => 0, 'percent' => 0);
}
$quota = rcube_output::json_serialize($quota);
$this->rc->output->add_script(rcmail_output::JS_OBJECT_NAME . ".files_set_quota($quota);", 'docready');
return html::span($attrib, '');
}
/**
* Get API token for current user session, authenticate if needed
*/
public function get_api_token($configure = true)
{
$token = $_SESSION['kolab_files_token'];
$time = $_SESSION['kolab_files_time'];
if ($token && time() - $this->timeout < $time) {
if (time() - $time <= $this->timeout / 2) {
return $token;
}
}
$request = $this->get_request(array('method' => 'ping'), $token);
try {
$url = $request->getUrl();
// Send ping request
if ($token) {
$url->setQueryVariables(array('method' => 'ping'));
$request->setUrl($url);
$response = $request->send();
$status = $response->getStatus();
if ($status == 200 && ($body = json_decode($response->getBody(), true))) {
if ($body['status'] == 'OK') {
$_SESSION['kolab_files_time'] = time();
return $token;
}
}
}
// Go with authenticate request
$url->setQueryVariables(array('method' => 'authenticate', 'version' => self::API_VERSION));
$request->setUrl($url);
$request->setAuth($this->rc->user->get_username(), $this->rc->decrypt($_SESSION['password']));
// Allow plugins (e.g. kolab_sso) to modify the request
$this->rc->plugins->exec_hook('chwala_authenticate', array('request' => $request));
$response = $request->send();
$status = $response->getStatus();
if ($status == 200 && ($body = json_decode($response->getBody(), true))) {
$token = $body['result']['token'];
if ($token) {
$_SESSION['kolab_files_token'] = $token;
$_SESSION['kolab_files_time'] = time();
$_SESSION['kolab_files_caps'] = $body['result']['capabilities'];
}
}
else {
throw new Exception(sprintf("Authenticate error (Status: %d)", $status));
}
// Configure session
if ($configure && $token) {
$this->configure($token);
}
}
catch (Exception $e) {
rcube::raise_error($e, true, false);
}
return $token;
}
protected function capabilities()
{
if (empty($_SESSION['kolab_files_caps'])) {
$token = $this->get_api_token();
if (empty($_SESSION['kolab_files_caps'])) {
$request = $this->get_request(array('method' => 'capabilities'), $token);
// send request to the API
try {
$response = $request->send();
$status = $response->getStatus();
$body = @json_decode($response->getBody(), true);
if ($status == 200 && $body['status'] == 'OK') {
$_SESSION['kolab_files_caps'] = $body['result'];
}
else {
throw new Exception($body['reason'] ?: "Failed to get capabilities. Status: $status");
}
}
catch (Exception $e) {
rcube::raise_error($e, true, false);
return array();
}
}
}
if ($_SESSION['kolab_files_caps']['MANTICORE'] || $_SESSION['kolab_files_caps']['WOPI']) {
$_SESSION['kolab_files_caps']['DOCEDIT'] = true;
$_SESSION['kolab_files_caps']['DOCTYPE'] = $_SESSION['kolab_files_caps']['MANTICORE'] ? 'manticore' : 'wopi';
}
if (!empty($_SESSION['kolab_files_caps']) && !isset($_SESSION['kolab_files_caps']['MOUNTPOINTS'])) {
$_SESSION['kolab_files_caps']['MOUNTPOINTS'] = array();
}
return $_SESSION['kolab_files_caps'];
}
/**
* Initialize HTTP_Request object
*/
protected function get_request($get = null, $token = null)
{
$url = $this->url_srv . '/api/';
if (!$this->request) {
$config = array(
'store_body' => true,
'follow_redirects' => true,
);
$this->request = libkolab::http_request($url, 'GET', $config);
}
else {
// cleanup
try {
$this->request->setBody('');
$this->request->setUrl($url);
$this->request->setMethod(HTTP_Request2::METHOD_GET);
}
catch (Exception $e) {
rcube::raise_error($e, true, true);
}
}
if ($token) {
$this->request->setHeader('X-Session-Token', $token);
}
if (!empty($get)) {
$url = $this->request->getUrl();
$url->setQueryVariables($get);
$this->request->setUrl($url);
}
// some HTTP server configurations require this header
$this->request->setHeader('accept', "application/json,text/javascript,*/*");
// Localization
$this->request->setHeader('accept-language', $_SESSION['language']);
// set Referer which is used as an origin for cross-window
// communication with document editor iframe
$host = $_SERVER['REQUEST_SCHEME'] . '://' . $_SERVER['HTTP_HOST'];
$this->request->setHeader('referer', $host);
return $this->request;
}
/**
* Configure chwala session
*/
public function configure($token = null, $prefs = array())
{
if (!$token) {
$token = $this->get_api_token(false);
}
try {
// Configure session
$query = array(
'method' => 'configure',
'timezone' => $prefs['timezone'] ?: $this->rc->config->get('timezone'),
'date_format' => $prefs['date_long'] ?: $this->rc->config->get('date_long', 'Y-m-d H:i'),
);
$request = $this->get_request($query, $token);
$response = $request->send();
$status = $response->getStatus();
if ($status != 200) {
throw new Exception(sprintf("Failed to configure chwala session (Status: %d)", $status));
}
}
catch (Exception $e) {
rcube::raise_error($e, true, false);
}
}
/**
* Handler for main files interface (Files task)
*/
protected function action_index()
{
$this->plugin->add_label(
'uploading', 'attaching', 'uploadsizeerror',
'filedeleting', 'filedeletenotice', 'filedeleteconfirm',
'filemoving', 'filemovenotice', 'filemoveconfirm', 'filecopying', 'filecopynotice',
'fileskip', 'fileskipall', 'fileoverwrite', 'fileoverwriteall'
);
$this->folder_list_env();
if ($this->rc->task == 'files') {
$this->rc->output->set_env('folder', rcube_utils::get_input_value('folder', rcube_utils::INPUT_GET));
$this->rc->output->set_env('collection', rcube_utils::get_input_value('collection', rcube_utils::INPUT_GET));
}
$caps = $this->capabilities();
$this->rc->output->add_label('uploadprogress', 'GB', 'MB', 'KB', 'B');
$this->rc->output->set_pagetitle($this->plugin->gettext('files'));
$this->rc->output->set_env('file_mimetypes', $this->get_mimetypes());
$this->rc->output->set_env('files_quota', $caps['QUOTA']);
$this->rc->output->set_env('files_max_upload', $caps['MAX_UPLOAD']);
$this->rc->output->set_env('files_progress_name', $caps['PROGRESS_NAME']);
$this->rc->output->set_env('files_progress_time', $caps['PROGRESS_TIME']);
$this->rc->output->send('kolab_files.files');
}
/**
* Handler for resetting some session/cached information
*/
protected function action_reset()
{
$this->rc->session->remove('kolab_files_caps');
if (($caps = $this->capabilities()) && !empty($caps)) {
$this->rc->output->set_env('files_caps', $caps);
}
}
/**
* Handler for preferences save action
*/
protected function action_prefs()
{
$dont_override = (array)$this->rc->config->get('dont_override');
$prefs = array();
$type = rcube_utils::get_input_value('type', rcube_utils::INPUT_POST);
$opts = array(
'kolab_files_sort_col' => true,
'kolab_files_sort_order' => true,
'kolab_files_list_cols' => false,
);
foreach ($opts as $o => $sess) {
if (isset($_POST[$o])) {
$value = rcube_utils::get_input_value($o, rcube_utils::INPUT_POST);
$session_key = $o;
$config_key = $o;
if ($type != 'files') {
$config_key = str_replace('files', 'files_' . $type, $config_key);
}
if (in_array($config_key, $dont_override)) {
continue;
}
if ($o == 'kolab_files_list_cols') {
$update_list = true;
}
$prefs[$config_key] = $value;
if ($sess) {
$_SESSION[$session_key] = $prefs[$config_key];
}
}
}
// save preference values
if (!empty($prefs)) {
$this->rc->user->save_prefs($prefs);
}
if (!empty($update_list)) {
$this->list_update($prefs, $type);
}
$this->rc->output->send();
}
/**
* Handler for file open action
*/
protected function action_open()
{
$this->rc->output->set_env('file_mimetypes', $this->get_mimetypes());
$this->file_opener(intval($_GET['_viewer']) & ~4);
}
/**
* Handler for file open action
*/
protected function action_edit()
{
$this->plugin->add_label('sessionterminating', 'unsavedchanges', 'documentinviting',
'documentcancelling', 'removeparticipant', 'sessionterminated', 'sessionterminatedtitle');
$this->file_opener(intval($_GET['_viewer']));
}
/**
* Handler for folder sharing action
*/
protected function action_share()
{
$this->rc->output->add_handler('share-form', array($this, 'folder_share_form'));
$this->rc->output->send('kolab_files.share');
}
/**
* Handler for "save all attachments into cloud" action
*/
protected function action_save_file()
{
// $source = rcube_utils::get_input_value('source', rcube_utils::INPUT_POST);
$uid = rcube_utils::get_input_value('uid', rcube_utils::INPUT_POST);
$dest = rcube_utils::get_input_value('dest', rcube_utils::INPUT_POST);
$id = rcube_utils::get_input_value('id', rcube_utils::INPUT_POST);
$name = rcube_utils::get_input_value('name', rcube_utils::INPUT_POST);
$temp_dir = unslashify($this->rc->config->get('temp_dir'));
$message = new rcube_message($uid);
$request = $this->get_request();
$url = $request->getUrl();
$files = array();
$errors = array();
$attachments = array();
$request->setMethod(HTTP_Request2::METHOD_POST);
$request->setHeader('X-Session-Token', $this->get_api_token());
$url->setQueryVariables(array('method' => 'file_upload', 'folder' => $dest));
$request->setUrl($url);
foreach ($message->attachments as $attach_prop) {
if (empty($id) || $id == $attach_prop->mime_id) {
$filename = strlen($name) ? $name : rcmail_attachment_name($attach_prop, true);
$attachments[$filename] = $attach_prop;
}
}
// @TODO: handle error
// @TODO: implement file upload using file URI instead of body upload
foreach ($attachments as $attach_name => $attach_prop) {
$path = tempnam($temp_dir, 'rcmAttmnt');
// save attachment to file
if ($fp = fopen($path, 'w+')) {
$message->get_part_body($attach_prop->mime_id, false, 0, $fp);
}
else {
$errors[] = true;
rcube::raise_error(array(
'code' => 500, 'type' => 'php', 'line' => __LINE__, 'file' => __FILE__,
'message' => "Unable to save attachment into file $path"),
true, false);
continue;
}
fclose($fp);
// send request to the API
try {
$request->setBody('');
$request->addUpload('file[]', $path, $attach_name, $attach_prop->mimetype);
$response = $request->send();
$status = $response->getStatus();
$body = @json_decode($response->getBody(), true);
if ($status == 200 && $body['status'] == 'OK') {
$files[] = $attach_name;
}
else {
throw new Exception($body['reason'] ?: "Failed to post file_upload. Status: $status");
}
}
catch (Exception $e) {
unlink($path);
$errors[] = $e->getMessage();
rcube::raise_error(array(
'code' => 500, 'type' => 'php', 'line' => __LINE__, 'file' => __FILE__,
'message' => $e->getMessage()),
true, false);
continue;
}
// clean up
unlink($path);
$request->setBody('');
}
if ($count = count($files)) {
$msg = $this->plugin->gettext(array('name' => 'saveallnotice', 'vars' => array('n' => $count)));
$this->rc->output->show_message($msg, 'confirmation');
}
if ($count = count($errors)) {
$msg = $this->plugin->gettext(array('name' => 'saveallerror', 'vars' => array('n' => $count)));
$this->rc->output->show_message($msg, 'error');
}
// @TODO: update quota indicator, make this optional in case files aren't stored in IMAP
$this->rc->output->send();
}
/**
* Handler for "add attachments from the cloud" action
*/
protected function action_attach_file()
{
$files = rcube_utils::get_input_value('files', rcube_utils::INPUT_POST);
$uploadid = rcube_utils::get_input_value('uploadid', rcube_utils::INPUT_POST);
$COMPOSE_ID = rcube_utils::get_input_value('id', rcube_utils::INPUT_POST);
$COMPOSE = null;
$errors = array();
$attachments = array();
if ($this->rc->task == 'mail') {
if ($COMPOSE_ID && $_SESSION['compose_data_'.$COMPOSE_ID]) {
$COMPOSE =& $_SESSION['compose_data_'.$COMPOSE_ID];
}
if (!$COMPOSE) {
die("Invalid session var!");
}
// attachment upload action
if (!is_array($COMPOSE['attachments'])) {
$COMPOSE['attachments'] = array();
}
}
// clear all stored output properties (like scripts and env vars)
$this->rc->output->reset();
$temp_dir = unslashify($this->rc->config->get('temp_dir'));
$request = $this->get_request();
$url = $request->getUrl();
// Use observer object to store HTTP response into a file
require_once $this->plugin->home . DIRECTORY_SEPARATOR . 'lib' . DIRECTORY_SEPARATOR . 'kolab_files_observer.php';
$observer = new kolab_files_observer();
$request->setHeader('X-Session-Token', $this->get_api_token());
// download files from the API and attach them
foreach ($files as $file) {
// decode filename
$file = urldecode($file);
// get file information
try {
$url->setQueryVariables(array('method' => 'file_info', 'file' => $file));
$request->setUrl($url);
$response = $request->send();
$status = $response->getStatus();
$body = @json_decode($response->getBody(), true);
if ($status == 200 && $body['status'] == 'OK') {
$file_params = $body['result'];
}
else {
throw new Exception($body['reason'] ?: "Failed to get file_info. Status: $status");
}
}
catch (Exception $e) {
$errors[] = $e->getMessage();
rcube::raise_error(array(
'code' => 500, 'type' => 'php', 'line' => __LINE__, 'file' => __FILE__,
'message' => $e->getMessage()),
true, false);
continue;
}
// set location of downloaded file
$path = tempnam($temp_dir, 'rcmAttmnt');
$observer->set_file($path);
// download file
try {
$url->setQueryVariables(array('method' => 'file_get', 'file' => $file));
$request->setUrl($url);
$request->attach($observer);
$response = $request->send();
$status = $response->getStatus();
$response->getBody(); // returns nothing
$request->detach($observer);
if ($status != 200 || !file_exists($path)) {
throw new Exception("Unable to save file");
}
}
catch (Exception $e) {
$errors[] = $e->getMessage();
rcube::raise_error(array(
'code' => 500, 'type' => 'php', 'line' => __LINE__, 'file' => __FILE__,
'message' => $e->getMessage()),
true, false);
continue;
}
$attachment = array(
'path' => $path,
'size' => $file_params['size'],
'name' => $file_params['name'],
'mimetype' => $file_params['type'],
'group' => $COMPOSE_ID,
);
if ($this->rc->task != 'mail') {
$attachments[] = $attachment;
continue;
}
$attachment = $this->rc->plugins->exec_hook('attachment_save', $attachment);
if ($attachment['status'] && !$attachment['abort']) {
$this->compose_attach_success($attachment, $COMPOSE, $COMPOSE_ID, $uploadid);
}
else if ($attachment['error']) {
$errors[] = $attachment['error'];
}
else {
$errors[] = $this->plugin->gettext('attacherror');
}
}
if (!empty($errors)) {
$this->rc->output->command('display_message', $this->plugin->gettext('attacherror'), 'error');
$this->rc->output->command('remove_from_attachment_list', $uploadid);
}
else if ($this->rc->task == 'calendar' || $this->rc->task == 'tasks') {
// for uploads in events/tasks we'll use its standard upload handler,
// for this we have to fake $_FILES and some other POST args
foreach ($attachments as $attach) {
$_FILES['_attachments']['tmp_name'][] = $attachment['path'];
$_FILES['_attachments']['name'][] = $attachment['name'];
$_FILES['_attachments']['size'][] = $attachment['size'];
$_FILES['_attachments']['type'][] = $attachment['mimetype'];
$_FILES['_attachments']['error'][] = null;
}
$_GET['_uploadid'] = $uploadid;
$_GET['_id'] = $COMPOSE_ID;
switch ($this->rc->task) {
case 'tasks':
$handler = new kolab_attachments_handler();
$handler->attachment_upload(tasklist::SESSION_KEY);
break;
case 'calendar':
$handler = new kolab_attachments_handler();
$handler->attachment_upload(calendar::SESSION_KEY, 'cal-');
break;
}
}
// send html page with JS calls as response
$this->rc->output->command('auto_save_start', false);
$this->rc->output->send();
}
protected function compose_attach_success($attachment, $COMPOSE, $COMPOSE_ID, $uploadid)
{
$id = $attachment['id'];
// store new attachment in session
unset($attachment['data'], $attachment['status'], $attachment['abort']);
$this->rc->session->append('compose_data_' . $COMPOSE_ID . '.attachments', $id, $attachment);
if (($icon = $COMPOSE['deleteicon']) && is_file($icon)) {
$button = html::img(array(
'src' => $icon,
'alt' => $this->rc->gettext('delete')
));
}
else if ($COMPOSE['textbuttons']) {
$button = rcube::Q($this->rc->gettext('delete'));
}
else {
$button = '';
}
if (version_compare(version_parse(RCMAIL_VERSION), '1.3.0', '>=')) {
$link_content = sprintf('%s <span class="attachment-size"> (%s)</span>',
rcube::Q($attachment['name']), $this->rc->show_bytes($attachment['size']));
$content_link = html::a(array(
'href' => "#load",
'class' => 'filename',
'onclick' => sprintf("return %s.command('load-attachment','rcmfile%s', this, event)", rcmail_output::JS_OBJECT_NAME, $id),
), $link_content);
$delete_link = html::a(array(
'href' => "#delete",
'onclick' => sprintf("return %s.command('remove-attachment','rcmfile%s', this, event)", rcmail_output::JS_OBJECT_NAME, $id),
'title' => $this->rc->gettext('delete'),
'class' => 'delete',
'aria-label' => $this->rc->gettext('delete') . ' ' . $attachment['name'],
), $button);
$content = $COMPOSE['icon_pos'] == 'left' ? $delete_link.$content_link : $content_link.$delete_link;
}
else {
$content = html::a(array(
'href' => "#delete",
'onclick' => sprintf("return %s.command('remove-attachment','rcmfile%s', this)", rcmail_output::JS_OBJECT_NAME, $id),
'title' => $this->rc->gettext('delete'),
'class' => 'delete',
), $button);
$content .= rcube::Q($attachment['name']);
}
$this->rc->output->command('add2attachment_list', "rcmfile$id", array(
'html' => $content,
'name' => $attachment['name'],
'mimetype' => $attachment['mimetype'],
'classname' => rcube_utils::file2class($attachment['mimetype'], $attachment['name']),
'complete' => true), $uploadid);
}
/**
* Handler for file open/edit action
*/
protected function file_opener($viewer)
{
$file = rcube_utils::get_input_value('_file', rcube_utils::INPUT_GET);
$session = rcube_utils::get_input_value('_session', rcube_utils::INPUT_GET);
// get file info
$token = $this->get_api_token();
$request = $this->get_request(array(
'method' => 'file_info',
'file' => $file,
'viewer' => $viewer,
'session' => $session,
), $token);
// send request to the API
try {
$response = $request->send();
$status = $response->getStatus();
$body = @json_decode($response->getBody(), true);
if ($status == 200 && $body['status'] == 'OK') {
$this->file_data = $body['result'];
}
else {
throw new Exception($body['reason'] ?: "Failed to get file_info. Status: $status");
}
}
catch (Exception $e) {
rcube::raise_error(array(
'code' => 500, 'type' => 'php', 'line' => __LINE__, 'file' => __FILE__,
'message' => $e->getMessage()),
true, true);
}
if ($file === null || $file === '') {
$file = $this->file_data['file'];
}
$this->file_data['filename'] = $file;
$this->plugin->add_label('filedeleteconfirm', 'filedeleting', 'filedeletenotice', 'terminate');
// register template objects for dialogs (and main interface)
$this->rc->output->add_handlers(array(
'fileinfobox' => array($this, 'file_info_box'),
'filepreviewframe' => array($this, 'file_preview_frame'),
));
$placeholder = $this->rc->output->asset_url('program/resources/blank.gif');
if ($this->file_data['viewer']['wopi']) {
$editor_type = 'wopi';
$got_editor = ($viewer & 4);
}
else if ($this->file_data['viewer']['manticore']) {
$editor_type = 'manticore';
$got_editor = ($viewer & 4);
}
// this one is for styling purpose
$this->rc->output->set_env('extwin', true);
$this->rc->output->set_env('file', $file);
$this->rc->output->set_env('file_data', $this->file_data);
$this->rc->output->set_env('mimetype', $this->file_data['type']);
$this->rc->output->set_env('filename', pathinfo($file, PATHINFO_BASENAME));
$this->rc->output->set_env('editor_type', $editor_type);
$this->rc->output->set_env('photo_placeholder', $placeholder);
$this->rc->output->set_pagetitle(rcube::Q($file));
$this->rc->output->send('kolab_files.' . ($got_editor ? 'docedit' : 'filepreview'));
}
/**
* Returns mimetypes supported by File API viewers
*/
protected function get_mimetypes($type = 'view')
{
$mimetypes = array();
// send request to the API
try {
if ($this->mimetypes === null) {
$this->mimetypes = false;
$token = $this->get_api_token();
$caps = $this->capabilities();
$request = $this->get_request(array('method' => 'mimetypes'), $token);
$response = $request->send();
$status = $response->getStatus();
$body = @json_decode($response->getBody(), true);
if ($status == 200 && $body['status'] == 'OK') {
$this->mimetypes = $body['result'];
}
else {
throw new Exception($body['reason'] ?: "Failed to get mimetypes. Status: $status");
}
}
if (is_array($this->mimetypes)) {
if (array_key_exists($type, $this->mimetypes)) {
$mimetypes = $this->mimetypes[$type];
}
// fallback to static definition if old Chwala is used
else if ($type == 'edit') {
$mimetypes = array(
'text/plain' => 'txt',
'text/html' => 'html',
);
if (!empty($caps['MANTICORE'])) {
$mimetypes = array_merge(array('application/vnd.oasis.opendocument.text' => 'odt'), $mimetypes);
}
foreach (array_keys($mimetypes) as $type) {
list ($app, $label) = explode('/', $type);
$label = preg_replace('/[^a-z]/', '', $label);
$mimetypes[$type] = array(
'ext' => $mimetypes[$type],
'label' => $this->plugin->gettext('type.' . $label),
);
}
}
else {
$mimetypes = $this->mimetypes;
}
}
}
catch (Exception $e) {
rcube::raise_error(array(
'code' => 500, 'type' => 'php', 'line' => __LINE__, 'file' => __FILE__,
'message' => $e->getMessage()),
true, false);
}
return $mimetypes;
}
/**
* Get list of available external storage drivers
*/
protected function get_external_storage_drivers()
{
// first get configured sources from Chwala
$token = $this->get_api_token();
$request = $this->get_request(array('method' => 'folder_types'), $token);
// send request to the API
try {
$response = $request->send();
$status = $response->getStatus();
$body = @json_decode($response->getBody(), true);
if ($status == 200 && $body['status'] == 'OK') {
$sources = $body['result'];
}
else {
throw new Exception($body['reason'] ?: "Failed to get folder_types. Status: $status");
}
}
catch (Exception $e) {
rcube::raise_error($e, true, false);
return;
}
$this->rc->output->set_env('external_sources', $sources);
}
/**
* Get folder share dialog data
*/
protected function get_share_info($folder)
{
// first get configured sources from Chwala
$token = $this->get_api_token();
$request = $this->get_request(array('method' => 'sharing', 'folder' => $folder), $token);
// send request to the API
try {
$response = $request->send();
$status = $response->getStatus();
$body = @json_decode($response->getBody(), true);
if ($status == 200 && $body['status'] == 'OK') {
$info = $body['result'];
}
else if ($body['code'] == 530) {
return false;
}
else {
throw new Exception($body['reason'] ?: "Failed to get sharing form information. Status: $status");
}
}
catch (Exception $e) {
rcube::raise_error($e, true, false);
return;
}
return $info;
}
/**
* Registers translation labels for folder lists in UI
*/
protected function folder_list_env()
{
// folder list and actions
$this->plugin->add_label(
'folderdeleting', 'folderdeleteconfirm', 'folderdeletenotice',
'collection_audio', 'collection_video', 'collection_image', 'collection_document',
'additionalfolders', 'listpermanent', 'storageautherror'
);
$this->rc->output->add_label('foldersubscribing', 'foldersubscribed',
'folderunsubscribing', 'folderunsubscribed', 'searching'
);
}
}

File Metadata

Mime Type
text/x-diff
Expires
Fri, May 1, 3:35 PM (18 m, 11 s)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
661788
Default Alt Text
(204 KB)

Event Timeline