diff --git a/plugins/kolab_files/kolab_files.js b/plugins/kolab_files/kolab_files.js index c32ea009..abad8b39 100644 --- a/plugins/kolab_files/kolab_files.js +++ b/plugins/kolab_files/kolab_files.js @@ -185,7 +185,7 @@ function kolab_files_init() } else if (rcmail.env.action == 'open') { // initialize folders list (for dialogs) - file_api.folder_list(); + // 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'); @@ -259,6 +259,11 @@ function kolab_files_init() 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 @@ -1858,9 +1863,14 @@ rcube_webmail.prototype.files_copy = function(folder, obj, event, files) // 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, - id = folder.id, row = $('
  • '); if (folder.virtual || folder.readonly) @@ -2092,6 +2102,7 @@ function kolab_files_ui() this.requests = {}; this.uploads = []; this.workers = {}; + this.list_updates = 0; /* // Called on "session expired" session @@ -2155,28 +2166,39 @@ function kolab_files_ui() var root = folder.split(this.env.directory_separator)[0], caps = this.env.caps; - if (this.env.caps.MOUNTPOINTS && this.env.caps.MOUNTPOINTS[root]) + 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) + this.folder_list = function(params, update) { if (!params) params = {} params.permissions = 1; - params.req = this.set_busy(true, 'loading'); - this.request('folder_list', this.list_params = params, 'folder_list_response'); + 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) + this.folder_list_response = function(response, params) { - rcmail.hide_message(this.list_params.req); + rcmail.hide_message(params.req); if (!this.response(response)) return; @@ -2197,12 +2219,14 @@ function kolab_files_ui() 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', true); + rcmail.enable_command('files-create', response.result && response.result.list && response.result.list.length > 0); if (!elem.length) return; @@ -2288,8 +2312,10 @@ function kolab_files_ui() else if (this.env.collection) rcmail.folder_list.select('folder-collection-' + this.env.collection); else if (folder = this.env.init_folder) { - this.env.init_folder = null; - rcmail.folder_list.select(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; @@ -2298,12 +2324,19 @@ function kolab_files_ui() else if (first) rcmail.folder_list.select(first); - // add tree icons -// this.folder_list_tree(this.env.folders); - // handle authentication errors on external sources this.folder_list_auth_errors(response.result); + // 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(rcmail.env.files_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'); @@ -2313,6 +2346,34 @@ function kolab_files_ui() 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) @@ -2404,6 +2465,7 @@ function kolab_files_ui() if (folder.depth) { // find parent folder parent_name = i.replace(/\/[^/]+$/, ''); + if (!parent) parent = $(this.env.folders[parent_name].ref); @@ -2733,6 +2795,7 @@ function kolab_files_ui() this.display_message('kolab_files.foldercreatenotice', 'confirmation'); // refresh folders list + // TODO: Don't reload the whole list this.folder_list(); }; @@ -2758,6 +2821,7 @@ function kolab_files_ui() if (this.env.folder == data.folder) this.env.folder = data['new']; + // TODO: Don't reload the whole list this.folder_list(); }; @@ -2769,14 +2833,22 @@ function kolab_files_ui() }; // folder create response handler - this.folder_mount_response = function(response) + this.folder_mount_response = function(response, params) { if (!this.response(response)) return; this.display_message('kolab_files.foldermountnotice', 'confirmation'); + if (response.result.capabilities) { + rcmail.env.files_caps.MOUNTPOINTS[params.folder] = response.result.capabilities; + } + + // 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(); }; @@ -2788,18 +2860,24 @@ function kolab_files_ui() }; // folder delete response handler - this.folder_delete_response = function(response, data) + this.folder_delete_response = function(response, params) { if (!this.response(response)) return; this.display_message('kolab_files.folderdeletenotice', 'confirmation'); - if (this.env.folder == data.folder) { + 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 (rcmail.env.files_caps.MOUNTPOINTS[params.folder]) { + delete rcmail.env.files_caps.MOUNTPOINTS[params.folder]; + rcmail.http_post('files/reset', {}); + } + // refresh folders list this.folder_list(); this.quota(); @@ -4060,13 +4138,14 @@ function kolab_files_ui() 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) { file_api.folder_list_auth_dialog(i, v); + delete ref.auth_errors[i]; return false; }); }; @@ -4082,15 +4161,15 @@ function kolab_files_ui() $('.auth-options', dialog).before(content); args.buttons[this.t('kolab_files.save')] = function() { - var data = {folder: label, list: 1}; + 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.open_dialog = this; 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() { @@ -4116,45 +4195,94 @@ function kolab_files_ui() }; // folder_auth handler - this.folder_auth_response = function(response) + this.folder_auth_response = function(response, params) { if (!this.response(response)) return; - var folders, found, - folder = response.result.folder, - id = 'rcmli' + rcmail.html_identifier_encode(folder), - parent = $('#' + id); - - // try parent window if the folder element does not exist - if (!parent.length && rcmail.is_framed()) { - parent = $('#' + id, window.parent.document.body); - } - - delete this.auth_errors[folder]; - kolab_dialog_close(this.open_dialog); + delete this.auth_errors[response.result.folder]; // go to the next one this.folder_list_auth_errors(); - // parse result - folders = this.folder_list_parse(response.result.list); - delete folders[folder]; // remove root added in folder_list_parse() + // update the list + this.folder_list_merge(response.result.folder, response.result.list, params.level); + }; - // add folders from the external source to the list + // 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) { - file_api.folder_list_row(i, f, parent.get(0)); - found = true; + 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 - if (found) - rcmail.folder_list.reset(true); + // Reset folders list widget + rcmail.folder_list.reset(true, true); - // add tree icons -// this.folder_list_tree(folders); + // 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]; + } + } - $.extend(this.env.folders, folders); + 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 diff --git a/plugins/kolab_files/kolab_files.php b/plugins/kolab_files/kolab_files.php index ccbcb4e8..f03ae1ce 100644 --- a/plugins/kolab_files/kolab_files.php +++ b/plugins/kolab_files/kolab_files.php @@ -51,6 +51,7 @@ class kolab_files extends rcube_plugin $this->register_action('open', array($this, 'actions')); $this->register_action('edit', array($this, 'actions')); $this->register_action('share', array($this, 'actions')); + $this->register_action('reset', array($this, 'actions')); $this->register_action('autocomplete', array($this, 'autocomplete')); // we use libkolab::http_request() from libkolab with its configuration diff --git a/plugins/kolab_files/lib/kolab_files_engine.php b/plugins/kolab_files/lib/kolab_files_engine.php index 03e682fb..5c092e90 100644 --- a/plugins/kolab_files/lib/kolab_files_engine.php +++ b/plugins/kolab_files/lib/kolab_files_engine.php @@ -131,10 +131,7 @@ class kolab_files_engine ), 'taskbar'); } - 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'; - } + $caps = $this->capabilities(); $this->plugin->include_stylesheet($this->plugin->local_skin_path().'/style.css'); $this->plugin->include_script($this->url . '/js/files_api.js'); @@ -142,11 +139,11 @@ class kolab_files_engine $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', $_SESSION['kolab_files_caps']); - $this->rc->output->set_env('files_api_version', $_SESSION['kolab_files_caps']['VERSION'] ?: 3); + $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 ($_SESSION['kolab_files_caps']['DOCEDIT']) { + if ($caps['DOCEDIT']) { $this->plugin->add_label('declinednotice', 'invitednotice', 'acceptedownernotice', 'declinedownernotice', 'requestednotice', 'acceptednotice', 'declinednotice', 'more', 'accept', 'decline', 'join', 'status', 'when', 'file', 'comment', @@ -1030,6 +1027,46 @@ class kolab_files_engine 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 */ @@ -1130,16 +1167,29 @@ class kolab_files_engine $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', $_SESSION['kolab_files_caps']['QUOTA']); - $this->rc->output->set_env('files_max_upload', $_SESSION['kolab_files_caps']['MAX_UPLOAD']); - $this->rc->output->set_env('files_progress_name', $_SESSION['kolab_files_caps']['PROGRESS_NAME']); - $this->rc->output->set_env('files_progress_time', $_SESSION['kolab_files_caps']['PROGRESS_TIME']); + $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 */ @@ -1623,6 +1673,7 @@ class kolab_files_engine $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(); @@ -1646,7 +1697,7 @@ class kolab_files_engine 'text/plain' => 'txt', 'text/html' => 'html', ); - if (!empty($_SESSION['kolab_files_caps']['MANTICORE'])) { + if (!empty($caps['MANTICORE'])) { $mimetypes = array_merge(array('application/vnd.oasis.opendocument.text' => 'odt'), $mimetypes); }