From 01c2c3107a8370fc57dc6144dcdcc108a92347a9 Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Fri, 6 Jun 2014 13:16:47 +0200 Subject: [PATCH 1/4] Added external storage list in files app --- apps/files_external/appinfo/app.php | 12 +++ apps/files_external/appinfo/routes.php | 27 ++++++ apps/files_external/js/app.js | 71 ++++++++++++++ apps/files_external/js/mountsfilelist.js | 116 +++++++++++++++++++++++ apps/files_external/lib/api.php | 64 +++++++++++++ apps/files_external/list.php | 11 +++ apps/files_external/templates/list.php | 25 +++++ 7 files changed, 326 insertions(+) create mode 100644 apps/files_external/appinfo/routes.php create mode 100644 apps/files_external/js/app.js create mode 100644 apps/files_external/js/mountsfilelist.js create mode 100644 apps/files_external/lib/api.php create mode 100644 apps/files_external/list.php create mode 100644 apps/files_external/templates/list.php diff --git a/apps/files_external/appinfo/app.php b/apps/files_external/appinfo/app.php index ca164784fb0..e88880fe516 100644 --- a/apps/files_external/appinfo/app.php +++ b/apps/files_external/appinfo/app.php @@ -20,12 +20,23 @@ OC::$CLASSPATH['OC\Files\Storage\AmazonS3'] = 'files_external/lib/amazons3.php'; OC::$CLASSPATH['OC\Files\Storage\Dropbox'] = 'files_external/lib/dropbox.php'; OC::$CLASSPATH['OC\Files\Storage\SFTP'] = 'files_external/lib/sftp.php'; OC::$CLASSPATH['OC_Mount_Config'] = 'files_external/lib/config.php'; +OC::$CLASSPATH['OCA\Files\External\Api'] = 'files_external/lib/api.php'; OCP\App::registerAdmin('files_external', 'settings'); if (OCP\Config::getAppValue('files_external', 'allow_user_mounting', 'yes') == 'yes') { OCP\App::registerPersonal('files_external', 'personal'); } +\OCA\Files\App::getNavigationManager()->add( + array( + "id" => 'extstoragemounts', + "appname" => 'files_external', + "script" => 'list.php', + "order" => 30, + "name" => $l->t('External storage') + ) +); + // connecting hooks OCP\Util::connectHook('OC_Filesystem', 'post_initMountPoints', '\OC_Mount_Config', 'initMountPointsHook'); OCP\Util::connectHook('OC_User', 'post_login', 'OC\Files\Storage\SMB_OC', 'login'); @@ -164,3 +175,4 @@ OC_Mount_Config::registerBackend('\OC\Files\Storage\SFTP', array( 'user' => (string)$l->t('Username'), 'password' => '*'.$l->t('Password'), 'root' => '&'.$l->t('Root')))); + diff --git a/apps/files_external/appinfo/routes.php b/apps/files_external/appinfo/routes.php new file mode 100644 index 00000000000..cb950364e5d --- /dev/null +++ b/apps/files_external/appinfo/routes.php @@ -0,0 +1,27 @@ + + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library 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 library. If not, see . + * + */ + +OC_API::register('get', + '/apps/files_external/api/v1/mounts', + array('\OCA\Files\External\Api', 'getUserMounts'), + 'files_external'); + diff --git a/apps/files_external/js/app.js b/apps/files_external/js/app.js new file mode 100644 index 00000000000..58ad1a0f6ef --- /dev/null +++ b/apps/files_external/js/app.js @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2014 Vincent Petry + * + * This file is licensed under the Affero General Public License version 3 + * or later. + * + * See the COPYING-README file. + * + */ + +if (!OCA.External) { + OCA.External = {}; +} +OCA.External.App = { + + fileList: null, + + initList: function($el) { + if (this.fileList) { + return this.fileList; + } + + this.fileList = new OCA.External.FileList( + $el, + { + scrollContainer: $('#app-content'), + fileActions: this._createFileActions() + } + ); + + this._extendFileList(this.fileList); + this.fileList.appName = t('files_external', 'External storage'); + return this.fileList; + }, + + removeList: function() { + if (this.fileList) { + this.fileList.$fileList.empty(); + } + }, + + _createFileActions: function() { + // inherit file actions from the files app + var fileActions = new OCA.Files.FileActions(); + fileActions.registerDefaultActions(); + + // when the user clicks on a folder, redirect to the corresponding + // folder in the files app instead of opening it directly + fileActions.register('dir', 'Open', OC.PERMISSION_READ, '', function (filename, context) { + OCA.Files.App.setActiveView('files', {silent: true}); + OCA.Files.App.fileList.changeDirectory(context.$file.attr('data-path') + '/' + filename, true, true); + }); + fileActions.setDefault('dir', 'Open'); + return fileActions; + }, + + _extendFileList: function(fileList) { + // remove size column from summary + fileList.fileSummary.$el.find('.filesize').remove(); + } +}; + +$(document).ready(function() { + $('#app-content-extstoragemounts').on('show', function(e) { + OCA.External.App.initList($(e.target)); + }); + $('#app-content-extstoragemounts').on('hide', function() { + OCA.External.App.removeList(); + }); +}); + diff --git a/apps/files_external/js/mountsfilelist.js b/apps/files_external/js/mountsfilelist.js new file mode 100644 index 00000000000..52a9aff728c --- /dev/null +++ b/apps/files_external/js/mountsfilelist.js @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2014 Vincent Petry + * + * This file is licensed under the Affero General Public License version 3 + * or later. + * + * See the COPYING-README file. + * + */ +(function() { + + /** + * External storage file list + */ + var FileList = function($el, options) { + this.initialize($el, options); + }; + + FileList.prototype = _.extend({}, OCA.Files.FileList.prototype, { + appName: 'External storage', + + initialize: function($el, options) { + OCA.Files.FileList.prototype.initialize.apply(this, arguments); + if (this.initialized) { + return; + } + }, + + _createRow: function(fileData) { + // TODO: hook earlier and render the whole row here + var $tr = OCA.Files.FileList.prototype._createRow.apply(this, arguments); + $tr.find('.filesize,.date').remove(); + $tr.find('td.filename').after(''); + $tr.find('td.filename input:checkbox').remove(); + return $tr; + }, + + updateEmptyContent: function() { + var dir = this.getCurrentDirectory(); + if (dir === '/') { + // root has special permissions + this.$el.find('#emptycontent').toggleClass('hidden', !this.isEmpty); + this.$el.find('#filestable thead th').toggleClass('hidden', this.isEmpty); + } + else { + OCA.Files.FileList.prototype.updateEmptyContent.apply(this, arguments); + } + }, + + getDirectoryPermissions: function() { + return OC.PERMISSION_READ | OC.PERMISSION_DELETE; + }, + + updateStorageStatistics: function() { + // no op because it doesn't have + // storage info like free space / used space + }, + + reload: function() { + var self = this; + this.showMask(); + if (this._reloadCall) { + this._reloadCall.abort(); + } + this._reloadCall = $.ajax({ + url: OC.linkToOCS('apps/files_external/api/v1') + 'mounts', + data: { + format: 'json' + }, + type: 'GET', + beforeSend: function(xhr) { + xhr.setRequestHeader('OCS-APIREQUEST', 'true'); + }, + error: function(result) { + self.reloadCallback(result); + }, + success: function(result) { + self.reloadCallback(result); + } + }); + }, + + reloadCallback: function(result) { + delete this._reloadCall; + this.hideMask(); + + if (result.ocs && result.ocs.data) { + this.setFiles(this._makeFiles(result.ocs.data)); + } + else { + // TODO: error handling + } + }, + + /** + * Converts the OCS API response data to a file info + * list + * @param OCS API mounts array + * @return array of file info maps + */ + _makeFiles: function(data) { + var files = _.chain(data) + .map(function(fileData) { + fileData.icon = OC.imagePath('core', 'filetypes/folder-external'); + return fileData; + }) + // Sort by expected sort comparator + .sortBy(this._sortComparator) + .value(); + + return files; + } + }); + + OCA.External.FileList = FileList; +})(); diff --git a/apps/files_external/lib/api.php b/apps/files_external/lib/api.php new file mode 100644 index 00000000000..d94dbcffc8f --- /dev/null +++ b/apps/files_external/lib/api.php @@ -0,0 +1,64 @@ +. + * + */ + +namespace OCA\Files\External; + +class Api { + + /** + * Returns the mount points visible for this user. + * + * @param array $params + * @return \OC_OCS_Result share information + */ + public static function getUserMounts($params) { + $entries = array(); + $user = \OC_User::getUser(); + $mounts = \OC_Mount_Config::getAbsoluteMountPoints($user); + + foreach ($mounts as $mountPoint => $config) { + // split user name from mount point + $parts = explode('/', ltrim($mountPoint, '/')); + array_shift($parts); // use name + array_shift($parts); // files + $mountPoint = implode('/', $parts); + + $path = dirname($mountPoint); + if ($path === '.') { + $path = ''; + } + + // TODO: give delete permissions if mount type is personal + $permissions = \OCP\PERMISSION_READ; + + // TODO: add storageType, might need to use another OC_Mount_Config method + $entries[] = array( + 'name' => basename($mountPoint), + 'path' => $path, + 'type' => 'dir', + 'permissions' => $permissions + ); + } + + return new \OC_OCS_Result($entries); + } +} diff --git a/apps/files_external/list.php b/apps/files_external/list.php new file mode 100644 index 00000000000..e9517724f2f --- /dev/null +++ b/apps/files_external/list.php @@ -0,0 +1,11 @@ +printPage(); diff --git a/apps/files_external/templates/list.php b/apps/files_external/templates/list.php new file mode 100644 index 00000000000..225616e8cf2 --- /dev/null +++ b/apps/files_external/templates/list.php @@ -0,0 +1,25 @@ + +
+
+
+
+ + + + + + + + + + + + + + + +
From a47ef52ae2958a08ff3aca272c73973eb74d20ed Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Fri, 6 Jun 2014 13:32:01 +0200 Subject: [PATCH 2/4] Fixed ext storage list sorting --- apps/files_external/js/mountsfilelist.js | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/apps/files_external/js/mountsfilelist.js b/apps/files_external/js/mountsfilelist.js index 52a9aff728c..fffe994268f 100644 --- a/apps/files_external/js/mountsfilelist.js +++ b/apps/files_external/js/mountsfilelist.js @@ -99,14 +99,12 @@ * @return array of file info maps */ _makeFiles: function(data) { - var files = _.chain(data) - .map(function(fileData) { - fileData.icon = OC.imagePath('core', 'filetypes/folder-external'); - return fileData; - }) - // Sort by expected sort comparator - .sortBy(this._sortComparator) - .value(); + var files = _.map(data, function(fileData) { + fileData.icon = OC.imagePath('core', 'filetypes/folder-external'); + return fileData; + }); + + files.sort(this._sortComparator); return files; } From 710d017eec7a97c1e58ef0cd4556555cf84b9b8f Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Fri, 6 Jun 2014 13:50:41 +0200 Subject: [PATCH 3/4] Added more columns to ext storage list Now using system mount and personal mount calls to get the relevant mount points including backend information. --- apps/files_external/js/mountsfilelist.js | 10 +++- apps/files_external/lib/api.php | 61 ++++++++++++++++-------- apps/files_external/templates/list.php | 6 +++ 3 files changed, 55 insertions(+), 22 deletions(-) diff --git a/apps/files_external/js/mountsfilelist.js b/apps/files_external/js/mountsfilelist.js index fffe994268f..95b596bca8d 100644 --- a/apps/files_external/js/mountsfilelist.js +++ b/apps/files_external/js/mountsfilelist.js @@ -29,8 +29,16 @@ _createRow: function(fileData) { // TODO: hook earlier and render the whole row here var $tr = OCA.Files.FileList.prototype._createRow.apply(this, arguments); + var $scopeColumn = $(''); + var $backendColumn = $(''); + var scopeText = t('files_external', 'Personal'); + if (fileData.scope === 'system') { + scopeText = t('files_external', 'System'); + } $tr.find('.filesize,.date').remove(); - $tr.find('td.filename').after(''); + $scopeColumn.text(scopeText); + $backendColumn.text(fileData.backend); + $tr.find('td.filename').after($scopeColumn).after($backendColumn); $tr.find('td.filename input:checkbox').remove(); return $tr; }, diff --git a/apps/files_external/lib/api.php b/apps/files_external/lib/api.php index d94dbcffc8f..51c48427aa3 100644 --- a/apps/files_external/lib/api.php +++ b/apps/files_external/lib/api.php @@ -24,6 +24,39 @@ namespace OCA\Files\External; class Api { + /** + * Formats the given mount config to a mount entry. + * + * @param bool $isSystemMount true for system mount, false + * for personal mount + * + * @return array entry + */ + private static function formatMount($mountConfig, $isSystemMount = false) { + // split user name from mount point + $path = dirname($mountConfig['mountpoint']); + if ($path === '.') { + $path = ''; + } + + $permissions = \OCP\PERMISSION_READ; + // personal mounts can be deleted + if (!$isSystemMount) { + $permissions |= \OCP\PERMISSION_DELETE; + } + + // TODO: add storageType, might need to use another OC_Mount_Config method + $entry = array( + 'name' => basename($mountConfig['mountpoint']), + 'path' => $path, + 'type' => 'dir', + 'backend' => $mountConfig['backend'], + 'scope' => ( $isSystemMount ? 'system' : 'personal' ), + 'permissions' => $permissions + ); + return $entry; + } + /** * Returns the mount points visible for this user. * @@ -33,30 +66,16 @@ class Api { public static function getUserMounts($params) { $entries = array(); $user = \OC_User::getUser(); - $mounts = \OC_Mount_Config::getAbsoluteMountPoints($user); - foreach ($mounts as $mountPoint => $config) { - // split user name from mount point - $parts = explode('/', ltrim($mountPoint, '/')); - array_shift($parts); // use name - array_shift($parts); // files - $mountPoint = implode('/', $parts); + $personalMounts = \OC_Mount_Config::getPersonalMountPoints(); + $systemMounts = \OC_Mount_Config::getSystemMountPoints(); - $path = dirname($mountPoint); - if ($path === '.') { - $path = ''; - } - - // TODO: give delete permissions if mount type is personal - $permissions = \OCP\PERMISSION_READ; + foreach ($systemMounts as $mountConfig) { + $entries[] = self::formatMount($mountConfig, true); + } - // TODO: add storageType, might need to use another OC_Mount_Config method - $entries[] = array( - 'name' => basename($mountPoint), - 'path' => $path, - 'type' => 'dir', - 'permissions' => $permissions - ); + foreach ($personalMounts as $mountConfig) { + $entries[] = self::formatMount($mountConfig, false); } return new \OC_OCS_Result($entries); diff --git a/apps/files_external/templates/list.php b/apps/files_external/templates/list.php index 225616e8cf2..900a17be717 100644 --- a/apps/files_external/templates/list.php +++ b/apps/files_external/templates/list.php @@ -16,6 +16,12 @@ t( 'Name' )); ?> + + t('Storage type')); ?> + + + t('Scope')); ?> + From e39e6a5584e04d998f75e987aef398dd572b8538 Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Fri, 6 Jun 2014 15:33:33 +0200 Subject: [PATCH 4/4] Fixed delete button CSS in ext storage list --- apps/files/css/files.css | 11 ++++++----- apps/files_external/js/mountsfilelist.js | 6 +++--- apps/files_external/templates/list.php | 2 +- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/apps/files/css/files.css b/apps/files/css/files.css index 55f3d17ac2b..4dac2b66a59 100644 --- a/apps/files/css/files.css +++ b/apps/files/css/files.css @@ -186,7 +186,8 @@ table th#headerSize, table td.filesize { table table td.filesize { padding: 0 16px; } -table th#headerDate, table td.date { +table th#headerDate, table td.date, +table th.column-last, table td.column-last { -moz-box-sizing: border-box; box-sizing: border-box; position: relative; @@ -237,9 +238,9 @@ table td.filename input.filename { cursor: text; } table td.filename a, table td.login, table td.logout, table td.download, table td.upload, table td.create, table td.delete { padding:3px 8px 8px 3px; } -table td.filename .nametext, .uploadtext, .modified { float:left; padding:14px 0; } +table td.filename .nametext, .uploadtext, .modified, .column-last>span:first-child { float:left; padding:14px 0; } -.modified { +.modified, .column-last>span:first-child { position: relative; padding-left: 15px; overflow: hidden; @@ -247,8 +248,8 @@ table td.filename .nametext, .uploadtext, .modified { float:left; padding:14px 0 width: 90%; } /* ellipsize long modified dates to make room for showing delete button */ -#fileList tr:hover .modified, -#fileList tr:focus .modified { +#fileList tr:hover .modified, #fileList tr:hover .column-last>span:first-child, +#fileList tr:focus .modified, #fileList tr:focus .column-last>span:first-child { width: 75%; } diff --git a/apps/files_external/js/mountsfilelist.js b/apps/files_external/js/mountsfilelist.js index 95b596bca8d..70b5b81e65b 100644 --- a/apps/files_external/js/mountsfilelist.js +++ b/apps/files_external/js/mountsfilelist.js @@ -29,14 +29,14 @@ _createRow: function(fileData) { // TODO: hook earlier and render the whole row here var $tr = OCA.Files.FileList.prototype._createRow.apply(this, arguments); - var $scopeColumn = $(''); - var $backendColumn = $(''); + var $scopeColumn = $(''); + var $backendColumn = $(''); var scopeText = t('files_external', 'Personal'); if (fileData.scope === 'system') { scopeText = t('files_external', 'System'); } $tr.find('.filesize,.date').remove(); - $scopeColumn.text(scopeText); + $scopeColumn.find('span').text(scopeText); $backendColumn.text(fileData.backend); $tr.find('td.filename').after($scopeColumn).after($backendColumn); $tr.find('td.filename input:checkbox').remove(); diff --git a/apps/files_external/templates/list.php b/apps/files_external/templates/list.php index 900a17be717..4e06bc7024c 100644 --- a/apps/files_external/templates/list.php +++ b/apps/files_external/templates/list.php @@ -19,7 +19,7 @@ t('Storage type')); ?> - + t('Scope')); ?>