diff options
Diffstat (limited to 'apps/files_external/js')
-rw-r--r-- | apps/files_external/js/app.js | 37 | ||||
-rw-r--r-- | apps/files_external/js/rollingqueue.js | 137 | ||||
-rw-r--r-- | apps/files_external/js/statusmanager.js | 539 |
3 files changed, 713 insertions, 0 deletions
diff --git a/apps/files_external/js/app.js b/apps/files_external/js/app.js index 1bff3014bd6..d3ce2010ecd 100644 --- a/apps/files_external/js/app.js +++ b/apps/files_external/js/app.js @@ -73,5 +73,42 @@ $(document).ready(function() { $('#app-content-extstoragemounts').on('hide', function() { OCA.External.App.removeList(); }); + + /* Status Manager */ + if ($('#filesApp').val()) { + + $('#app-content-files') + .add('#app-content-extstoragemounts') + .on('changeDirectory', function(e){ + if (e.dir === '/') { + var mount_point = e.previousDir.split('/', 2)[1]; + // Every time that we return to / root folder from a mountpoint, mount_point status is rechecked + OCA.External.StatusManager.getMountPointList(function() { + OCA.External.StatusManager.recheckConnectivityForMount([mount_point], true); + }); + } + }) + .on('fileActionsReady', function(e){ + if ($.isArray(e.$files)) { + if (OCA.External.StatusManager.mountStatus === null || + OCA.External.StatusManager.mountPointList === null || + _.size(OCA.External.StatusManager.mountStatus) !== _.size(OCA.External.StatusManager.mountPointList)) { + // Will be the very first check when the files view will be loaded + OCA.External.StatusManager.launchFullConnectivityCheckOneByOne(); + } else { + // When we change between general files view and external files view + OCA.External.StatusManager.getMountPointList(function(){ + var fileNames = []; + $.each(e.$files, function(key, value){ + fileNames.push(value.attr('data-file')); + }); + // Recheck if launched but work from cache + OCA.External.StatusManager.recheckConnectivityForMount(fileNames, false); + }); + } + } + }); + } + /* End Status Manager */ }); diff --git a/apps/files_external/js/rollingqueue.js b/apps/files_external/js/rollingqueue.js new file mode 100644 index 00000000000..58cb0fb22f0 --- /dev/null +++ b/apps/files_external/js/rollingqueue.js @@ -0,0 +1,137 @@ +/** + * ownCloud + * + * @author Juan Pablo VillafaƱez Ramos <jvillafanez@owncloud.com> + * @author Jesus Macias Portela <jesus@owncloud.com> + * @copyright (C) 2014 ownCloud, Inc. + * + * This file is licensed under the Affero General Public License version 3 + * or later. + * + * See the COPYING-README file. + * + */ + +(function(){ +/** + * Launch several functions at thee same time. The number of functions + * running at the same time is controlled by the queueWindow param + * + * The function list come in the following format: + * + * var flist = [ + * { + * funcName: function () { + * var d = $.Deferred(); + * setTimeout(function(){d.resolve();}, 1000); + * return d; + * } + * }, + * { + * funcName: $.get, + * funcArgs: [ + * OC.filePath('files_external', 'ajax', 'connectivityCheck.php'), + * {}, + * function () { + * console.log('titoooo'); + * } + * ] + * }, + * { + * funcName: $.get, + * funcArgs: [ + * OC.filePath('files_external', 'ajax', 'connectivityCheck.php') + * ], + * done: function () { + * console.log('yuupi'); + * }, + * always: function () { + * console.log('always done'); + * } + * } + *]; + * + * functions MUST implement the deferred interface + * + * @param functionList list of functions that the queue will run + * (check example above for the expected format) + * @param queueWindow specify the number of functions that will + * be executed at the same time + */ +var RollingQueue = function (functionList, queueWindow, callback) { + this.queueWindow = queueWindow || 1; + this.functionList = functionList; + this.callback = callback; + this.counter = 0; + this.runQueue = function() { + this.callbackCalled = false; + this.deferredsList = []; + if (!$.isArray(this.functionList)) { + throw "functionList must be an array"; + } + + for (i = 0; i < this.queueWindow; i++) { + this.launchNext(); + } + }; + + this.hasNext = function() { + return (this.counter in this.functionList); + }; + + this.launchNext = function() { + var currentCounter = this.counter++; + if (currentCounter in this.functionList) { + var funcData = this.functionList[currentCounter]; + if ($.isFunction(funcData.funcName)) { + var defObj = funcData.funcName.apply(funcData.funcName, funcData.funcArgs); + this.deferredsList.push(defObj); + if ($.isFunction(funcData.done)) { + defObj.done(funcData.done); + } + + if ($.isFunction(funcData.fail)) { + defObj.fail(funcData.fail); + } + + if ($.isFunction(funcData.always)) { + defObj.always(funcData.always); + } + + if (this.hasNext()) { + var self = this; + defObj.always(function(){ + _.defer($.proxy(function(){ + self.launchNext(); + }, self)); + }); + } else { + if (!this.callbackCalled) { + this.callbackCalled = true; + if ($.isFunction(this.callback)) { + $.when.apply($, this.deferredsList) + .always($.proxy(function(){ + this.callback(); + }, this) + ); + } + } + } + return defObj; + } + } + return false; + }; +}; + +if (!OCA.External) { + OCA.External = {}; +} + +if (!OCA.External.StatusManager) { + OCA.External.StatusManager = {}; +} + +OCA.External.StatusManager.RollingQueue = RollingQueue; + +})();
\ No newline at end of file diff --git a/apps/files_external/js/statusmanager.js b/apps/files_external/js/statusmanager.js new file mode 100644 index 00000000000..4048bfc31bc --- /dev/null +++ b/apps/files_external/js/statusmanager.js @@ -0,0 +1,539 @@ +/** + * ownCloud + * + * @author Juan Pablo VillafaƱez Ramos <jvillafanez@owncloud.com> + * @author Jesus Macias Portela <jesus@owncloud.com> + * @copyright (C) 2014 ownCloud, Inc. + * + * This file is licensed under the Affero General Public License version 3 + * or later. + * + * See the COPYING-README file. + * + */ + +if (!OCA.External) { + OCA.External = {}; +} + +if (!OCA.External.StatusManager) { + OCA.External.StatusManager = {}; +} + +OCA.External.StatusManager = { + + mountStatus : null, + mountPointList : null, + + /** + * Function + * @param {callback} afterCallback + */ + + getMountStatus : function(afterCallback) { + var self = this; + if (typeof afterCallback !== 'function' || self.isGetMountStatusRunning) { + return; + } + + if (self.mountStatus) { + afterCallback(self.mountStatus); + } + }, + + /** + * Function Check mount point status from cache + * @param {string} mount_point + */ + + getMountPointListElement : function(mount_point) { + var element; + $.each(this.mountPointList, function(key, value){ + if (value.mount_point === mount_point) { + element = value; + return false; + } + }); + return element; + }, + + /** + * Function Check mount point status from cache + * @param {string} mount_point + * @param {string} mount_point + */ + + getMountStatusForMount : function(mountData, afterCallback) { + var self = this; + if (typeof afterCallback !== 'function' || self.isGetMountStatusRunning) { + return $.Deferred().resolve(); + } + + var defObj; + if (self.mountStatus[mountData.mount_point]) { + defObj = $.Deferred(); + afterCallback(mountData, self.mountStatus[mountData.mount_point]); + defObj.resolve(); // not really useful, but it'll keep the same behaviour + } else { + defObj = $.ajax({ + type : 'GET', + url: OC.webroot + '/index.php/apps/files_external/' + ((mountData.type === 'personal') ? 'userstorages' : 'globalstorages') + '/' + mountData.id, + success : function(response) { + if (response && response.status === 0) { + self.mountStatus[mountData.mount_point] = response; + } else { + if (response && response.statusMessage) { + // failure response with error message + self.mountStatus[mountData.mount_point] = { type: mountData.type, + status: 1, + error: response.statusMessage}; + } else { + self.mountStatus[mountData.mount_point] = { type: mountData.type, + status: 1, + error: t('files_external', 'Empty response from the server')}; + } + } + afterCallback(mountData, self.mountStatus[mountData.mount_point]); + }, + error : function(jqxhr, state, error) { + var message; + if(mountData.location === 3){ + // In this case the error is because mount point use Login credentials and don't exist in the session + message = t('files_external', 'Couldn\'t access. Please logout and login to activate this mount point'); + } else { + message = t('files_external', 'Couldn\'t get the information from the ownCloud server: {code} {type}', {code: jqxhr.status, type: error}); + } + self.mountStatus[mountData.mount_point] = { type: mountData.type, + status: 1, + location: mountData.location, + error: message}; + afterCallback(mountData, self.mountStatus[mountData.mount_point]); + } + }); + } + return defObj; + }, + + /** + * Function to get external mount point list from the files_external API + * @param {function} afterCallback function to be executed + */ + + getMountPointList : function(afterCallback) { + var self = this; + if (typeof afterCallback !== 'function' || self.isGetMountPointListRunning) { + return; + } + + if (self.mountPointList) { + afterCallback(self.mountPointList); + } else { + self.isGetMountPointListRunning = true; + $.ajax({ + type : 'GET', + url : OC.linkToOCS('apps/files_external/api/v1') + 'mounts?format=json', + success : function(response) { + self.mountPointList = []; + _.each(response.ocs.data, function(mount){ + var element = {}; + element.mount_point = mount.name; + element.type = mount.scope; + element.location = ""; + element.id = mount.id; + element.backendText = mount.backend; + element.backend = mount.class; + + self.mountPointList.push(element); + }); + afterCallback(self.mountPointList); + }, + error : function(jqxhr, state, error) { + self.mountPointList = []; + OC.Notification.showTemporary(t('files_external', 'Couldn\'t get the list of external mount points: {type}', {type : error})); + }, + complete : function() { + self.isGetMountPointListRunning = false; + } + }); + } + }, + + /** + * Function to manage action when a mountpoint status = 1 (Errored). Show a dialog to be redirected to settings page. + * @param {string} name MountPoint Name + */ + + manageMountPointError : function(name) { + var self = this; + this.getMountStatus($.proxy(function(allMountStatus) { + if (typeof allMountStatus[name] !== 'undefined' || allMountStatus[name].status === 1) { + var mountData = allMountStatus[name]; + if (mountData.type === "system") { + OC.dialogs.confirm(t('files_external', 'There was an error with message: ') + mountData.error + '. Do you want to review mount point config in admin settings page?', t('files_external', 'External mount error'), function(e){ + if(e === true) { + window.location.href = OC.generateUrl('/settings/admin#files_external'); + } + }); + } else { + OC.dialogs.confirm(t('files_external', 'There was an error with message: ') + mountData.error + '. Do you want to review mount point config in personal settings page?', t('files_external', 'External mount error'), function(e){ + if(e === true) { + window.location.href = OC.generateUrl('/settings/personal#' + t('files_external', 'goto-external-storage')); + } + }); + } + } + }, this)); + }, + + /** + * Function to process a mount point in relation with their status, Called from Async Queue. + * @param {object} mountData + * @param {object} mountStatus + */ + + processMountStatusIndividual : function(mountData, mountStatus) { + + var mountPoint = mountData.mount_point; + if (mountStatus.status === 1) { + var trElement = FileList.findFileEl(OCA.External.StatusManager.Utils.jqSelEscape(mountPoint)); + + route = OCA.External.StatusManager.Utils.getIconRoute(trElement) + '-error'; + + if (OCA.External.StatusManager.Utils.isCorrectViewAndRootFolder()) { + OCA.External.StatusManager.Utils.showIconError(mountPoint, $.proxy(OCA.External.StatusManager.manageMountPointError, OCA.External.StatusManager), route); + } + return false; + } else { + if (OCA.External.StatusManager.Utils.isCorrectViewAndRootFolder()) { + OCA.External.StatusManager.Utils.restoreFolder(mountPoint); + OCA.External.StatusManager.Utils.toggleLink(mountPoint, true, true); + } + return true; + } + }, + + /** + * Function to process a mount point in relation with their status + * @param {object} mountData + * @param {object} mountStatus + */ + + processMountList : function(mountList) { + var elementList = null; + $.each(mountList, function(name, value){ + var trElement = $('#fileList tr[data-file=\"' + OCA.External.StatusManager.Utils.jqSelEscape(value.mount_point) + '\"]'); //FileList.findFileEl(OCA.External.StatusManager.Utils.jqSelEscape(value.mount_point)); + trElement.attr('data-external-backend', value.backend); + if (elementList) { + elementList = elementList.add(trElement); + } else { + elementList = trElement; + } + }); + + if (elementList instanceof $) { + if (OCA.External.StatusManager.Utils.isCorrectViewAndRootFolder()) { + // Put their custom icon + OCA.External.StatusManager.Utils.changeFolderIcon(elementList); + // Save default view + OCA.External.StatusManager.Utils.storeDefaultFolderIconAndBgcolor(elementList); + // Disable row until check status + elementList.addClass('externalDisabledRow'); + OCA.External.StatusManager.Utils.toggleLink(elementList.find('a.name'), false, false); + } + } + }, + + /** + * Function to process the whole mount point list in relation with their status (Async queue) + */ + + launchFullConnectivityCheckOneByOne : function() { + var self = this; + this.getMountPointList(function(list){ + // check if we have a list first + if (list === undefined && !self.emptyWarningShown) { + self.emptyWarningShown = true; + OC.Notification.showTemporary(t('files_external', 'Couldn\'t get the list of Windows network drive mount points: empty response from the server')); + return; + } + if (list && list.length > 0) { + self.processMountList(list); + + if (!self.mountStatus) { + self.mountStatus = {}; + } + + var ajaxQueue = []; + $.each(list, function(key, value){ + var queueElement = {funcName: $.proxy(self.getMountStatusForMount, self), + funcArgs: [value, + $.proxy(self.processMountStatusIndividual, self)]}; + ajaxQueue.push(queueElement); + }); + + var rolQueue = new OCA.External.StatusManager.RollingQueue(ajaxQueue, 4, function(){ + if (!self.notificationHasShown) { + var showNotification = false; + $.each(self.mountStatus, function(key, value){ + if (value.status === 1) { + self.notificationHasShown = true; + showNotification = true; + } + }); + if (showNotification) { + OC.Notification.showTemporary(t('files_external', 'Some of the configured external mount points are not connected. Please click on the red row(s) for more information')); + } + } + }); + rolQueue.runQueue(); + } + }); + }, + + + /** + * Function to process a mount point list in relation with their status (Async queue) + * @param {object} mountListData + * @param {boolean} recheck delete cached info and force api call to check mount point status + */ + + launchPartialConnectivityCheck : function(mountListData, recheck) { + if (mountListData.length === 0) { + return; + } + + var self = this; + var ajaxQueue = []; + $.each(mountListData, function(key, value){ + if (recheck && value.mount_point in self.mountStatus) { + delete self.mountStatus[value.mount_point]; + } + var queueElement = {funcName: $.proxy(self.getMountStatusForMount, self), + funcArgs: [value, + $.proxy(self.processMountStatusIndividual, self)]}; + ajaxQueue.push(queueElement); + }); + new OCA.External.StatusManager.RollingQueue(ajaxQueue, 4).runQueue(); + }, + + + /** + * Function to relaunch some mount point status check + * @param {string} mountListNames + * @param {boolean} recheck delete cached info and force api call to check mount point status + */ + + recheckConnectivityForMount : function(mountListNames, recheck) { + if (mountListNames.length === 0) { + return; + } + + var self = this; + var mountListData = []; + var recheckPersonalGlobal = false; + var recheckAdminGlobal = false; + + if (!self.mountStatus) { + self.mountStatus = {}; + } + + $.each(mountListNames, function(key, value){ + var mountData = self.getMountPointListElement(value); + if (mountData) { + mountListData.push(mountData); + } + }); + + // for all mounts in the list, delete the cached status values + if (recheck) { + $.each(mountListData, function(key, value){ + if (value.mount_point in self.mountStatus) { + delete self.mountStatus[value.mount_point]; + } + }); + } + + self.processMountList(mountListData); + self.launchPartialConnectivityCheck(mountListData, recheck); + } +}; + +OCA.External.StatusManager.Utils = { + + showIconError: function(folder, clickAction, errorImageUrl) { + var imageUrl = "url(" + errorImageUrl + ")"; + var trFolder = $('#fileList tr[data-file=\"' + OCA.External.StatusManager.Utils.jqSelEscape(folder) + '\"]'); //FileList.findFileEl(OCA.External.StatusManager.Utils.jqSelEscape(folder)); + this.changeFolderIcon(folder, imageUrl); + this.toggleLink(folder, false, clickAction); + trFolder.addClass('externalErroredRow'); + }, + + /** + * @param folder string with the folder or jQuery element pointing to the tr element + */ + storeDefaultFolderIconAndBgcolor: function(folder) { + var trFolder; + if (folder instanceof $) { + trFolder = folder; + } else { + trFolder = $('#fileList tr[data-file=\"' + OCA.External.StatusManager.Utils.jqSelEscape(folder) + '\"]'); //FileList.findFileEl(OCA.External.StatusManager.Utils.jqSelEscape(folder)); //$('#fileList tr[data-file=\"' + OCA.External.StatusManager.Utils.jqSelEscape(folder) + '\"]'); + } + trFolder.each(function(){ + var thisElement = $(this); + if (thisElement.data('oldbgcolor') === undefined) { + thisElement.data('oldbgcolor', thisElement.css('background-color')); + } + }); + + var icon = trFolder.find('td:first-child div.thumbnail'); + icon.each(function(){ + var thisElement = $(this); + if (thisElement.data('oldImage') === undefined) { + thisElement.data('oldImage', thisElement.css('background-image')); + } + }); + }, + + /** + * @param folder string with the folder or jQuery element pointing to the tr element + */ + restoreFolder: function(folder) { + var trFolder; + if (folder instanceof $) { + trFolder = folder; + } else { + // cant use here FileList.findFileEl(OCA.External.StatusManager.Utils.jqSelEscape(folder)); return incorrect instance of filelist + trFolder = $('#fileList tr[data-file=\"' + OCA.External.StatusManager.Utils.jqSelEscape(folder) + '\"]'); + } + trFolder.removeClass('externalErroredRow').removeClass('externalDisabledRow'); + tdChilds = trFolder.find("td:first-child div.thumbnail"); + tdChilds.each(function(){ + var thisElement = $(this); + thisElement.css('background-image', thisElement.data('oldImage')); + }); + }, + + /** + * @param folder string with the folder or jQuery element pointing to the first td element + * of the tr matching the folder name + */ + changeFolderIcon: function(filename) { + var file; + var route; + if (filename instanceof $) { + //trElementList + $.each(filename, function(index){ + route = OCA.External.StatusManager.Utils.getIconRoute($(this)); + $(this).attr("data-icon", route); + $(this).find('td:first-child div.thumbnail').css('background-image', "url(" + route + ")").css('display', 'none').css('display', 'inline'); + }); + } else { + file = $("#fileList tr[data-file=\"" + this.jqSelEscape(filename) + "\"] > td:first-child div.thumbnail"); + parentTr = file.parents('tr:first'); + route = OCA.External.StatusManager.Utils.getIconRoute(parentTr); + parentTr.attr("data-icon", route); + file.css('background-image', "url(" + route + ")").css('display', 'none').css('display', 'inline'); + } + }, + + /** + * @param backend string with the name of the external storage backend + * of the tr matching the folder name + */ + getIconRoute: function(tr) { + var icon = OC.imagePath('core', 'filetypes/folder-external'); + var backend = null; + + if (tr instanceof $) { + backend = tr.attr('data-external-backend'); + } + + switch (backend) { + case 'smb': + icon = OC.imagePath('windows_network_drive', 'folder-windows'); + break; + case 'sharepoint': + icon = OC.imagePath('sharepoint', 'folder-sharepoint'); + break; + case 'amazons3': + icon = OC.imagePath('core', 'filetypes/folder-external'); + break; + case 'dav': + icon = OC.imagePath('core', 'filetypes/folder-external'); + break; + case 'dropbox': + icon = OC.imagePath('core', 'filetypes/folder-external'); + break; + case 'ftp': + icon = OC.imagePath('core', 'filetypes/folder-external'); + break; + case 'google': + icon = OC.imagePath('core', 'filetypes/folder-external'); + break; + case 'owncloud': + icon = OC.imagePath('core', 'filetypes/folder-external'); + break; + case 'sftp': + icon = OC.imagePath('core', 'filetypes/folder-external'); + break; + case 'swift': + icon = OC.imagePath('core', 'filetypes/folder-external'); + break; + } + + return icon; + }, + + toggleLink: function(filename, active, action) { + var link; + if (filename instanceof $) { + link = filename; + } else { + link = $("#fileList tr[data-file=\"" + this.jqSelEscape(filename) + "\"] > td:first-child a.name"); + } + if (active) { + link.off('click.connectivity'); + OCA.Files.App.fileList.fileActions.display(link.parent(), true, OCA.Files.App.fileList); + } else { + link.find('.fileactions, .nametext .action').remove(); // from files/js/fileactions (display) + link.off('click.connectivity'); + link.on('click.connectivity', function(e){ + if (action && $.isFunction(action)) { + action(filename); + } + e.preventDefault(); + return false; + }); + } + }, + + isCorrectViewAndRootFolder: function() { + // correct views = files & extstoragemounts + if (OCA.Files.App.getActiveView() === 'files' || OCA.Files.App.getActiveView() === 'extstoragemounts') { + return OCA.Files.App.getCurrentAppContainer().find('#dir').val() === '/'; + } + return false; + }, + + /* escape a selector expression for jQuery */ + jqSelEscape: function(expression) { + if(expression){ + return expression.replace(/[!"#$%&'()*+,.\/:;<=>?@\[\\\]^`{|}~]/g, '\\$&'); + } + return null; + }, + + /* Copied from http://stackoverflow.com/questions/2631001/javascript-test-for-existence-of-nested-object-key */ + checkNested: function(cobj /*, level1, level2, ... levelN*/) { + var args = Array.prototype.slice.call(arguments), + obj = args.shift(); + + for (var i = 0; i < args.length; i++) { + if (!obj || !obj.hasOwnProperty(args[i])) { + return false; + } + obj = obj[args[i]]; + } + return true; + } +}; |