aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--apps/files/css/files.css4
-rw-r--r--apps/files_sharing/app/sharing.php25
-rw-r--r--apps/files_sharing/appinfo/app.php3
-rw-r--r--apps/files_sharing/appinfo/routes.php15
-rw-r--r--apps/files_sharing/controller/adminsettingscontroller.php60
-rw-r--r--apps/files_sharing/css/settings-admin.css33
-rw-r--r--apps/files_sharing/http/mailtemplateresponse.php55
-rw-r--r--apps/files_sharing/js/app.js4
-rw-r--r--apps/files_sharing/js/settings-admin.js78
-rw-r--r--apps/files_sharing/js/share.js239
-rw-r--r--apps/files_sharing/js/sharedfilelist.js43
-rw-r--r--apps/files_sharing/lib/mailtemplate.php126
-rw-r--r--apps/files_sharing/settings-admin.php21
-rw-r--r--apps/files_sharing/templates/settings-admin.php41
-rw-r--r--apps/files_sharing/tests/js/appSpec.js4
-rw-r--r--apps/files_sharing/tests/js/shareSpec.js450
-rw-r--r--apps/files_sharing/tests/js/sharedfilelistSpec.js10
-rw-r--r--apps/files_trashbin/appinfo/update.php3
-rw-r--r--apps/files_trashbin/lib/trashbin.php4
-rw-r--r--apps/files_versions/appinfo/database.xml35
-rw-r--r--apps/files_versions/appinfo/update.php12
-rw-r--r--apps/files_versions/appinfo/version2
-rw-r--r--apps/files_versions/lib/versions.php72
-rw-r--r--core/css/apps.css1
-rw-r--r--core/img/actions/caret.pngbin196 -> 296 bytes
-rw-r--r--core/img/actions/caret.svg83
-rw-r--r--core/img/actions/logout.pngbin352 -> 761 bytes
-rw-r--r--core/img/actions/logout.svg59
-rw-r--r--core/img/filetypes/folder-shared.pngbin1155 -> 1229 bytes
-rw-r--r--core/img/filetypes/folder-shared.svg44
-rw-r--r--core/js/share.js135
-rw-r--r--core/js/tests/specs/shareSpec.js138
-rw-r--r--lib/private/files/cache/changepropagator.php98
-rw-r--r--lib/private/files/cache/homecache.php2
-rw-r--r--lib/private/files/cache/scanner.php95
-rw-r--r--lib/private/files/utils/scanner.php21
-rw-r--r--settings/css/settings.css11
-rw-r--r--tests/karma.config.js3
-rw-r--r--tests/lib/files/cache/changepropagator.php72
-rw-r--r--tests/lib/files/cache/scanner.php8
-rw-r--r--tests/lib/files/utils/scanner.php64
-rw-r--r--tests/lib/files/view.php30
42 files changed, 1828 insertions, 375 deletions
diff --git a/apps/files/css/files.css b/apps/files/css/files.css
index 501a216043c..36dd6e78ed7 100644
--- a/apps/files/css/files.css
+++ b/apps/files/css/files.css
@@ -87,10 +87,6 @@
#filestable tbody tr { background-color:#fff; height:51px; }
-.app-files #app-content {
- position: relative;
-}
-
/* fit app list view heights */
.app-files #app-content>.viewcontainer {
height: 100%;
diff --git a/apps/files_sharing/app/sharing.php b/apps/files_sharing/app/sharing.php
new file mode 100644
index 00000000000..427269755b9
--- /dev/null
+++ b/apps/files_sharing/app/sharing.php
@@ -0,0 +1,25 @@
+<?php
+
+namespace OCA\Files_Sharing\App;
+
+use \OCP\AppFramework\App;
+use \OCA\Files_Sharing\Controller\AdminSettingsController;
+
+class Sharing extends App {
+
+ public function __construct(array $urlParams=array()){
+ parent::__construct('files_sharing', $urlParams);
+
+ $container = $this->getContainer();
+
+ /**
+ * Controllers
+ */
+ $container->registerService('AdminSettingsController', function($c) {
+ return new AdminSettingsController(
+ $c->query('AppName'),
+ $c->query('Request')
+ );
+ });
+ }
+}
diff --git a/apps/files_sharing/appinfo/app.php b/apps/files_sharing/appinfo/app.php
index 21b2646c5ea..81f91b60d30 100644
--- a/apps/files_sharing/appinfo/app.php
+++ b/apps/files_sharing/appinfo/app.php
@@ -21,6 +21,9 @@ OCP\Util::addScript('files_sharing', 'share');
\OC_Hook::connect('OC_Filesystem', 'post_rename', '\OC\Files\Cache\Shared_Updater', 'renameHook');
\OC_Hook::connect('OC_Appconfig', 'post_set_value', '\OCA\Files\Share\Maintainer', 'configChangeHook');
+// Register settings scripts for mail template editing
+OCP\App::registerAdmin('files_sharing', 'settings-admin');
+
OC_FileProxy::register(new OCA\Files\Share\Proxy());
\OCA\Files\App::getNavigationManager()->add(
diff --git a/apps/files_sharing/appinfo/routes.php b/apps/files_sharing/appinfo/routes.php
index 7c2834dc9c2..5b6286e2bfb 100644
--- a/apps/files_sharing/appinfo/routes.php
+++ b/apps/files_sharing/appinfo/routes.php
@@ -5,6 +5,21 @@ $this->create('core_ajax_public_preview', '/publicpreview')->action(
require_once __DIR__ . '/../ajax/publicpreview.php';
});
+use \OCA\Files_Sharing\App\Sharing;
+
+$app = new Sharing();
+
+$app->registerRoutes($this, array('routes' => array(
+
+ // mailTemplate settings
+ array('name' => 'admin_settings#render', 'url' => '/settings/mailtemplate', 'verb' => 'GET'),
+
+ array('name' => 'admin_settings#update', 'url' => '/settings/mailtemplate', 'verb' => 'POST'),
+
+ array('name' => 'admin_settings#reset', 'url' => '/settings/mailtemplate', 'verb' => 'DELETE')
+
+)));
+
// OCS API
//TODO: SET: mail notification, waiting for PR #4689 to be accepted
diff --git a/apps/files_sharing/controller/adminsettingscontroller.php b/apps/files_sharing/controller/adminsettingscontroller.php
new file mode 100644
index 00000000000..fed3147a99c
--- /dev/null
+++ b/apps/files_sharing/controller/adminsettingscontroller.php
@@ -0,0 +1,60 @@
+<?php
+
+namespace OCA\Files_Sharing\Controller;
+
+use \OCP\AppFramework\ApiController;
+use \OCP\IRequest;
+use \OCP\AppFramework\Http\JSONResponse;
+
+class AdminSettingsController extends ApiController {
+
+ public function __construct($appName, IRequest $request) {
+ parent::__construct($appName, $request);
+ }
+
+ /**
+ * @param string $theme
+ * @param string $template
+ * @return \OCA\Files_Sharing\Http\MailTemplateResponse
+ */
+ public function render( $theme, $template ) {
+ try {
+ $template = new \OCA\Files_Sharing\MailTemplate( $theme, $template );
+ return $template->getResponse();
+ } catch (\Exception $ex) {
+ return new JSONResponse(array('message' => $ex->getMessage()), $ex->getCode());
+ }
+ }
+
+ /**
+ * @param string $theme
+ * @param string $template
+ * @param string $content
+ * @return JSONResponse
+ */
+ public function update( $theme, $template, $content ) {
+ try {
+ $template = new \OCA\Files_Sharing\MailTemplate( $theme, $template );
+ $template->setContent( $content );
+ return new JSONResponse();
+ } catch (\Exception $ex) {
+ return new JSONResponse(array('message' => $ex->getMessage()), $ex->getCode());
+ }
+ }
+
+ /**
+ * @param string $theme
+ * @param string $template
+ * @return JSONResponse
+ */
+ public function reset( $theme, $template ) {
+ try {
+ $template = new \OCA\Files_Sharing\MailTemplate( $theme, $template );
+ $template->reset();
+ return new JSONResponse();
+ } catch (\Exception $ex) {
+ return new JSONResponse(array('message' => $ex->getMessage()), $ex->getCode());
+ }
+ }
+
+}
diff --git a/apps/files_sharing/css/settings-admin.css b/apps/files_sharing/css/settings-admin.css
new file mode 100644
index 00000000000..7ee71963436
--- /dev/null
+++ b/apps/files_sharing/css/settings-admin.css
@@ -0,0 +1,33 @@
+#mailTemplateSettings .actions div {
+ display: inline-block;
+}
+
+#mailTemplateSettings div label {
+ display: block
+}
+
+#mailTemplateSettings textarea {
+ box-sizing: border-box;
+ width: 100%;
+ height: 150px;
+}
+
+#mailTemplateSettings .templateEditor + .actions {
+ height:28px;
+}
+
+
+#mailTemplateSettings .actions .reset {
+ margin: 0;
+}
+
+#mailTemplateSettings .actions .save {
+ float: right;
+ margin: 0;
+}
+
+#mailTemplateSettings #mts-msg {
+ float: right;
+ margin: 1px 5px;
+ padding:3px;
+}
diff --git a/apps/files_sharing/http/mailtemplateresponse.php b/apps/files_sharing/http/mailtemplateresponse.php
new file mode 100644
index 00000000000..98a2dfcc94e
--- /dev/null
+++ b/apps/files_sharing/http/mailtemplateresponse.php
@@ -0,0 +1,55 @@
+<?php
+
+/**
+ * ownCloud - App Framework
+ *
+ * @author Jörn Dreyer
+ * @copyright 2014 Jörn Dreyer <jfd@owncloud.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OCA\Files_Sharing\Http;
+
+/**
+ * Prompts the user to download the a file
+ */
+class MailTemplateResponse extends \OCP\AppFramework\Http\Response {
+
+ private $filename;
+ private $contentType;
+
+ /**
+ * Creates a response that prompts the user to download the file
+ * @param string $filename the name that the downloaded file should have
+ * @param string $contentType the mimetype that the downloaded file should have
+ */
+ public function __construct($filename, $contentType = 'text/php') {
+ $this->filename = $filename;
+ $this->contentType = $contentType;
+
+ $this->addHeader('Content-Disposition', 'attachment; filename="' . $filename . '"');
+ $this->addHeader('Content-Type', $contentType);
+ }
+
+ /**
+ * Returns the raw template content
+ * @return string the file
+ */
+ public function render(){
+ return file_get_contents($this->filename);
+ }
+
+}
diff --git a/apps/files_sharing/js/app.js b/apps/files_sharing/js/app.js
index 3764328a5d0..800873cd638 100644
--- a/apps/files_sharing/js/app.js
+++ b/apps/files_sharing/js/app.js
@@ -8,7 +8,9 @@
*
*/
-OCA.Sharing = {};
+if (!OCA.Sharing) {
+ OCA.Sharing = {};
+}
OCA.Sharing.App = {
_inFileList: null,
diff --git a/apps/files_sharing/js/settings-admin.js b/apps/files_sharing/js/settings-admin.js
new file mode 100644
index 00000000000..fa9b236ea98
--- /dev/null
+++ b/apps/files_sharing/js/settings-admin.js
@@ -0,0 +1,78 @@
+$(document).ready(function() {
+
+ var loadTemplate = function (theme, template) {
+ $.get(
+ OC.generateUrl('apps/files_sharing/settings/mailtemplate'),
+ { theme: theme, template: template }
+ ).done(function( result ) {
+ $( '#mailTemplateSettings textarea' ).val(result);
+ }).fail(function( result ) {
+ OC.dialogs.alert(result.message, t('files_sharing', 'Could not load template'));
+ });
+ };
+
+ // load default template
+ var theme = $( '#mts-theme' ).val();
+ var template = $( '#mts-template' ).val();
+ loadTemplate(theme, template);
+
+ $( '#mts-template' ).change(
+ function() {
+ var theme = $( '#mts-theme' ).val();
+ var template = $( this ).val();
+ loadTemplate(theme, template);
+ }
+ );
+
+ $( '#mts-theme' ).change(
+ function() {
+ var theme = $( this ).val();
+ var template = $( '#mts-template' ).val();
+ loadTemplate(theme, template);
+ }
+ );
+
+ $( '#mailTemplateSettings .actions' ).on('click', '.save',
+ function() {
+ var theme = $( '#mts-theme' ).val();
+ var template = $( '#mts-template' ).val();
+ var content = $( '#mailTemplateSettings textarea' ).val();
+ OC.msg.startSaving('#mts-msg');
+ $.post(
+ OC.generateUrl('apps/files_sharing/settings/mailtemplate'),
+ { theme: theme, template: template, content: content }
+ ).done(function() {
+ var data = { status:'success', data:{message:t('files_sharing', 'Saved')} };
+ OC.msg.finishedSaving('#mts-msg', data);
+ }).fail(function(result) {
+ var data = { status: 'error', data:{message:result.responseJSON.message} };
+ OC.msg.finishedSaving('#mts-msg', data);
+ });
+ }
+ );
+
+ $( '#mailTemplateSettings .actions' ).on('click', '.reset',
+ function() {
+ var theme = $( '#mts-theme' ).val();
+ var template = $( '#mts-template' ).val();
+ OC.msg.startSaving('#mts-msg');
+ $.ajax({
+ type: "DELETE",
+ url: OC.generateUrl('apps/files_sharing/settings/mailtemplate'),
+ data: { theme: theme, template: template }
+ }).done(function() {
+ var data = { status:'success', data:{message:t('files_sharing', 'Reset')} };
+ OC.msg.finishedSaving('#mts-msg', data);
+
+ // load default template
+ var theme = $( '#mts-theme' ).val();
+ var template = $( '#mts-template' ).val();
+ loadTemplate(theme, template);
+ }).fail(function(result) {
+ var data = { status: 'error', data:{message:result.responseJSON.message} };
+ OC.msg.finishedSaving('#mts-msg', data);
+ });
+ }
+ );
+
+});
diff --git a/apps/files_sharing/js/share.js b/apps/files_sharing/js/share.js
index 5a42604c866..d63a590fb8e 100644
--- a/apps/files_sharing/js/share.js
+++ b/apps/files_sharing/js/share.js
@@ -8,103 +8,166 @@
*
*/
-$(document).ready(function() {
- if (!_.isUndefined(OC.Share) && !_.isUndefined(OCA.Files)) {
- // TODO: make a separate class for this or a hook or jQuery event ?
- if (OCA.Files.FileList) {
- var oldCreateRow = OCA.Files.FileList.prototype._createRow;
- OCA.Files.FileList.prototype._createRow = function(fileData) {
- var tr = oldCreateRow.apply(this, arguments);
- if (fileData.shareOwner) {
- tr.attr('data-share-owner', fileData.shareOwner);
- // user should always be able to rename a mount point
- if (fileData.isShareMountPoint) {
- tr.attr('data-permissions', fileData.permissions | OC.PERMISSION_UPDATE);
- tr.attr('data-reshare-permissions', fileData.permissions);
+(function() {
+ if (!OCA.Sharing) {
+ OCA.Sharing = {};
+ }
+ OCA.Sharing.Util = {
+ initialize: function(fileActions) {
+ if (OCA.Files.FileList) {
+ var oldCreateRow = OCA.Files.FileList.prototype._createRow;
+ OCA.Files.FileList.prototype._createRow = function(fileData) {
+ var tr = oldCreateRow.apply(this, arguments);
+ if (fileData.shareOwner) {
+ tr.attr('data-share-owner', fileData.shareOwner);
+ // user should always be able to rename a mount point
+ if (fileData.isShareMountPoint) {
+ tr.attr('data-permissions', fileData.permissions | OC.PERMISSION_UPDATE);
+ tr.attr('data-reshare-permissions', fileData.permissions);
+ }
}
- }
- return tr;
- };
- }
+ if (fileData.recipientsDisplayName) {
+ tr.attr('data-share-recipients', fileData.recipientsDisplayName);
+ }
+ return tr;
+ };
- // use delegate to catch the case with multiple file lists
- $('#content').delegate('#fileList', 'fileActionsReady',function(ev){
- // if no share action exists because the admin disabled sharing for this user
- // we create a share notification action to inform the user about files
- // shared with him otherwise we just update the existing share action.
- var fileList = ev.fileList;
- var $fileList = $(this);
- $fileList.find('[data-share-owner]').each(function() {
- var $tr = $(this);
- var $action;
- var owner;
- var message;
- var permissions = $tr.data('permissions');
- if(permissions & OC.PERMISSION_SHARE) {
- $action = $tr.find('[data-Action="Share"]');
- $action.addClass('permanent');
- owner = $tr.closest('tr').attr('data-share-owner');
- message = ' ' + t('files_sharing', 'Shared by {owner}', {owner: owner});
- $action.find('span').text(message);
- } else {
- var shareNotification = '<a class="action action-share-notification permanent"' +
- ' data-action="Share-Notification" href="#" original-title="">' +
- ' <img class="svg" src="' + OC.imagePath('core', 'actions/share') + '"></img>';
- $tr.find('.fileactions').append(function() {
- var owner = $(this).closest('tr').attr('data-share-owner');
- var shareBy = t('files_sharing', 'Shared by {owner}', {owner: owner});
- var $result = $(shareNotification + '<span> ' + shareBy + '</span></span>');
- $result.on('click', function() {
- return false;
- });
- return $result;
- });
+ var oldRenderRow = OCA.Files.FileList.prototype._renderRow;
+ OCA.Files.FileList.prototype._renderRow = function(fileData) {
+ var $tr = oldRenderRow.apply(this, arguments);
+ // if the statuses are loaded already, use them for the icon
+ // (needed when scrolling to the next page)
+ var shareStatus = OC.Share.statuses[fileData.id];
+ if (fileData.shareOwner || fileData.recipientsDisplayName || shareStatus) {
+ var permissions = $tr.data('permissions');
+ var hasLink = !!(shareStatus && shareStatus.link);
+ if (permissions & OC.PERMISSION_SHARE) {
+ OC.Share.markFileAsShared($tr, true, hasLink);
+ } else {
+ // if no share action exists because the admin disabled sharing for this user
+ // we create a share notification action to inform the user about files
+ // shared with him otherwise we just update the existing share action.
+ // TODO: make this work like/with OC.Share.markFileAsShared()
+ var shareNotification = '<a class="action action-share-notification permanent"' +
+ ' data-action="Share-Notification" href="#" original-title="">' +
+ ' <img class="svg" src="' + OC.imagePath('core', 'actions/share') + '"></img>';
+ $tr.find('.fileactions').append(function() {
+ var shareBy = t('files_sharing', 'Shared by {owner}', {owner: fileData.shareOwner});
+ var $result = $(shareNotification + '<span> ' + shareBy + '</span></span>');
+ $result.on('click', function() {
+ return false;
+ });
+ return $result;
+ });
+ }
+ }
+ return $tr;
+ };
+ }
+
+ // use delegate to catch the case with multiple file lists
+ $('#content').delegate('#fileList', 'fileActionsReady',function(ev){
+ var fileList = ev.fileList;
+ if (!OCA.Sharing.sharesLoaded){
+ OC.Share.loadIcons('file', fileList);
+ // assume that we got all shares, so switching directories
+ // will not invalidate that list
+ OCA.Sharing.sharesLoaded = true;
+ }
+ else{
+ // this will update the icons for all the currently visible elements
+ // additionally added elements when scrolling down will be
+ // updated in the _renderRow override
+ OC.Share.updateIcons('file', fileList);
}
});
- if (!OCA.Sharing.sharesLoaded){
- OC.Share.loadIcons('file', fileList);
- // assume that we got all shares, so switching directories
- // will not invalidate that list
- OCA.Sharing.sharesLoaded = true;
- }
- else{
- OC.Share.updateIcons('file', fileList);
- }
- });
-
- OCA.Files.fileActions.register(
- 'all',
- 'Share',
- OC.PERMISSION_SHARE,
- OC.imagePath('core', 'actions/share'),
- function(filename, context) {
+ fileActions.register(
+ 'all',
+ 'Share',
+ OC.PERMISSION_SHARE,
+ OC.imagePath('core', 'actions/share'),
+ function(filename, context) {
- var $tr = context.$file;
- var itemType = 'file';
- if ($tr.data('type') === 'dir') {
- itemType = 'folder';
- }
- var possiblePermissions = $tr.data('reshare-permissions');
- if (_.isUndefined(possiblePermissions)) {
- possiblePermissions = $tr.data('permissions');
- }
+ var $tr = context.$file;
+ var itemType = 'file';
+ if ($tr.data('type') === 'dir') {
+ itemType = 'folder';
+ }
+ var possiblePermissions = $tr.data('reshare-permissions');
+ if (_.isUndefined(possiblePermissions)) {
+ possiblePermissions = $tr.data('permissions');
+ }
- var appendTo = $tr.find('td.filename');
- // Check if drop down is already visible for a different file
- if (OC.Share.droppedDown) {
- if ($tr.data('id') !== $('#dropdown').attr('data-item-source')) {
- OC.Share.hideDropDown(function () {
- $tr.addClass('mouseOver');
- OC.Share.showDropDown(itemType, $tr.data('id'), appendTo, true, possiblePermissions, filename);
- });
+ var appendTo = $tr.find('td.filename');
+ // Check if drop down is already visible for a different file
+ if (OC.Share.droppedDown) {
+ if ($tr.data('id') !== $('#dropdown').attr('data-item-source')) {
+ OC.Share.hideDropDown(function () {
+ $tr.addClass('mouseOver');
+ OC.Share.showDropDown(itemType, $tr.data('id'), appendTo, true, possiblePermissions, filename);
+ });
+ } else {
+ OC.Share.hideDropDown();
+ }
} else {
- OC.Share.hideDropDown();
+ $tr.addClass('mouseOver');
+ OC.Share.showDropDown(itemType, $tr.data('id'), appendTo, true, possiblePermissions, filename);
}
- } else {
- $tr.addClass('mouseOver');
- OC.Share.showDropDown(itemType, $tr.data('id'), appendTo, true, possiblePermissions, filename);
+ $('#dropdown').on('sharesChanged', function(ev) {
+ // files app current cannot show recipients on load, so we don't update the
+ // icon when changed for consistency
+ if (context.fileList.$el.closest('#app-content-files').length) {
+ return;
+ }
+ var recipients = _.pluck(ev.shares[OC.Share.SHARE_TYPE_USER], 'share_with_displayname');
+ var groupRecipients = _.pluck(ev.shares[OC.Share.SHARE_TYPE_GROUP], 'share_with_displayname');
+ recipients = recipients.concat(groupRecipients);
+ // note: we only update the data attribute because updateIcon()
+ // is called automatically after this event
+ if (recipients.length) {
+ $tr.attr('data-share-recipients', OCA.Sharing.Util.formatRecipients(recipients));
+ }
+ else {
+ $tr.removeAttr('data-share-recipients');
+ }
+ });
+ });
+ },
+
+ /**
+ * Formats a recipients array to be displayed.
+ * The first four recipients will be shown and the
+ * other ones will be shown as "+x" where "x" is the number of
+ * remaining recipients.
+ *
+ * @param recipients recipients array
+ * @param count optional total recipients count (in case the array was shortened)
+ * @return formatted recipients display text
+ */
+ formatRecipients: function(recipients, count) {
+ var maxRecipients = 4;
+ var text;
+ if (!_.isNumber(count)) {
+ count = recipients.length;
+ }
+ // TODO: use natural sort
+ recipients = _.first(recipients, maxRecipients).sort();
+ text = recipients.join(', ');
+ if (count > maxRecipients) {
+ text += ', +' + (count - maxRecipients);
}
- });
+ return text;
+ }
+ };
+})();
+
+$(document).ready(function() {
+ // FIXME: HACK: do not init when running unit tests, need a better way
+ if (!window.TESTING) {
+ if (!_.isUndefined(OC.Share) && !_.isUndefined(OCA.Files)) {
+ OCA.Sharing.Util.initialize(OCA.Files.fileActions);
+ }
}
});
+
diff --git a/apps/files_sharing/js/sharedfilelist.js b/apps/files_sharing/js/sharedfilelist.js
index ef1034ecfdc..97fabf87131 100644
--- a/apps/files_sharing/js/sharedfilelist.js
+++ b/apps/files_sharing/js/sharedfilelist.js
@@ -38,6 +38,13 @@
}
},
+ _renderRow: function() {
+ // HACK: needed to call the overridden _renderRow
+ // this is because at the time this class is created
+ // the overriding hasn't been done yet...
+ return OCA.Files.FileList.prototype._renderRow.apply(this, arguments);
+ },
+
_createRow: function(fileData) {
// TODO: hook earlier and render the whole row here
var $tr = OCA.Files.FileList.prototype._createRow.apply(this, arguments);
@@ -46,7 +53,7 @@
$tr.find('td.filename input:checkbox').remove();
$tr.attr('data-share-id', _.pluck(fileData.shares, 'id').join(','));
if (this._sharedWithUser) {
- $tr.attr('data-share-owner', fileData.shares[0].ownerDisplayName);
+ $tr.attr('data-share-owner', fileData.shareOwner);
}
return $tr;
},
@@ -159,7 +166,7 @@
stime: share.stime * 1000,
};
if (self._sharedWithUser) {
- file.share.ownerDisplayName = share.displayname_owner;
+ file.shareOwner = share.displayname_owner;
file.name = OC.basename(share.file_target);
file.path = OC.dirname(share.file_target);
file.permissions = share.permissions;
@@ -179,15 +186,15 @@
// inside the same file object (by file id).
.reduce(function(memo, file) {
var data = memo[file.id];
- var counterPart = file.share.ownerDisplayName || file.share.targetDisplayName;
+ var recipient = file.share.targetDisplayName;
if (!data) {
data = memo[file.id] = file;
data.shares = [file.share];
// using a hash to make them unique,
// this is only a list to be displayed
- data.counterParts = {};
+ data.recipients = {};
// counter is cheaper than calling _.keys().length
- data.counterPartsCount = 0;
+ data.recipientsCount = 0;
data.mtime = file.share.stime;
}
else {
@@ -198,12 +205,14 @@
data.shares.push(file.share);
}
- if (file.share.type === OC.Share.SHARE_TYPE_LINK) {
- data.hasLinkShare = true;
- } else if (counterPart && data.counterPartsCount < 10) {
+ if (recipient) {
// limit counterparts for output
- data.counterParts[counterPart] = true;
- data.counterPartsCount++;
+ if (data.recipientsCount < 4) {
+ // only store the first ones, they will be the only ones
+ // displayed
+ data.recipients[recipient] = true;
+ }
+ data.recipientsCount++;
}
delete file.share;
@@ -213,14 +222,14 @@
.values()
// Clean up
.each(function(data) {
- // convert the counterParts map to a flat
+ // convert the recipients map to a flat
// array of sorted names
- data.counterParts = _.chain(data.counterParts).keys().sort().value();
- if (data.hasLinkShare) {
- data.counterParts.unshift(t('files_sharing', 'link'));
- delete data.hasLinkShare;
- }
- delete data.counterPartsCount;
+ data.recipients = _.keys(data.recipients);
+ data.recipientsDisplayName = OCA.Sharing.Util.formatRecipients(
+ data.recipients,
+ data.recipientsCount
+ );
+ delete data.recipientsCount;
})
// Sort by expected sort comparator
.sortBy(this._sortComparator)
diff --git a/apps/files_sharing/lib/mailtemplate.php b/apps/files_sharing/lib/mailtemplate.php
new file mode 100644
index 00000000000..ca1b0234ccf
--- /dev/null
+++ b/apps/files_sharing/lib/mailtemplate.php
@@ -0,0 +1,126 @@
+<?php
+
+namespace OCA\Files_Sharing;
+
+use \OCP\Files\NotPermittedException;
+use \OC\AppFramework\Middleware\Security\SecurityException;
+use OCA\Files_Sharing\Http\MailTemplateResponse;
+
+class MailTemplate extends \OC_Template {
+
+ private $path;
+ private $theme;
+ private $editableThemes;
+ private $editableTemplates;
+
+ public function __construct($theme, $path) {
+ $this->theme = $theme;
+ $this->path = $path;
+
+ //determine valid theme names
+ $this->editableThemes = self::getEditableThemes();
+ //for now hard code the valid mail template paths
+ $this->editableTemplates = self::getEditableTemplates();
+ }
+
+ /**
+ *
+ * @return \OCA\Files_Sharing\Http\MailTemplateResponse
+ */
+ public function getResponse() {
+ if($this->isEditable()) {
+ list($app, $filename) = explode('/templates/', $this->path, 2);
+ $name = substr($filename, 0, -4);
+ list($path, $template) = $this->findTemplate($this->theme, $app, $name, '');
+ return new MailTemplateResponse($template);
+ }
+ throw new SecurityException('Template not editable.', 403);
+ }
+
+ public function renderContent() {
+ if($this->isEditable()) {
+ list($app, $filename) = explode('/templates/', $this->path, 2);
+ $name = substr($filename, 0, -4);
+ list($path, $template) = $this->findTemplate($this->theme, $app, $name, '');
+ \OC_Response::sendFile($template);
+ } else {
+ throw new SecurityException('Template not editable.', 403);
+ }
+ }
+
+ public function isEditable() {
+ if ($this->editableThemes[$this->theme]
+ && $this->editableTemplates[$this->path]
+ ) {
+ return true;
+ }
+ return false;
+ }
+
+ public function setContent($data) {
+ if($this->isEditable()) {
+ //save default templates in default folder to overwrite core template
+ $absolutePath = \OC::$SERVERROOT.'/themes/'.$this->theme.'/'.$this->path;
+ $parent = dirname($absolutePath);
+ if ( ! is_dir($parent) ) {
+ if ( ! mkdir(dirname($absolutePath), 0777, true) ){
+ throw new \Exception('Could not create directory.', 500);
+ }
+ }
+ if ( $this->theme !== 'default' && is_file($absolutePath) ) {
+ if ( ! copy($absolutePath, $absolutePath.'.bak') ){
+ throw new \Exception('Could not overwrite template.', 500);
+ }
+ }
+ //overwrite theme templates? versioning?
+ return file_put_contents($absolutePath, $data);
+ }
+ throw new SecurityException('Template not editable.', 403);
+ }
+
+ public function reset(){
+ if($this->isEditable()) {
+ $absolutePath = \OC::$SERVERROOT.'/themes/'.$this->theme.'/'.$this->path;
+ if ($this->theme === 'default') {
+ //templates can simply be deleted in the themes folder
+ if (unlink($absolutePath)) {
+ return true;
+ }
+ } else {
+ //if a bak file exists overwrite the template with it
+ if (is_file($absolutePath.'.bak')) {
+ if (rename($absolutePath.'.bak', $absolutePath)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+ throw new NotPermittedException('Template not editable.', 403);
+ }
+
+ public static function getEditableThemes() {
+ $themes = array(
+ 'default' => true
+ );
+ if ($handle = opendir(\OC::$SERVERROOT.'/themes')) {
+ while (false !== ($entry = readdir($handle))) {
+ if ($entry != '.' && $entry != '..' && $entry != 'default') {
+ if (is_dir(\OC::$SERVERROOT.'/themes/'.$entry)) {
+ $themes[$entry] = true;
+ }
+ }
+ }
+ closedir($handle);
+ }
+ return $themes;
+ }
+
+ public static function getEditableTemplates() {
+ return array(
+ 'core/templates/mail.php' => true,
+ 'core/templates/altmail.php' => true,
+ 'core/lostpassword/templates/email.php' => true,
+ );
+ }
+}
diff --git a/apps/files_sharing/settings-admin.php b/apps/files_sharing/settings-admin.php
new file mode 100644
index 00000000000..8f15e272312
--- /dev/null
+++ b/apps/files_sharing/settings-admin.php
@@ -0,0 +1,21 @@
+<?php
+/**
+ * Copyright (c) 2011 Robin Appelman <icewind@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+\OC_Util::checkAdminUser();
+
+\OCP\Util::addStyle('files_sharing', 'settings-admin');
+\OCP\Util::addScript('files_sharing', 'settings-admin');
+
+$themes = \OCA\Files_Sharing\MailTemplate::getEditableThemes();
+$editableTemplates = \OCA\Files_Sharing\MailTemplate::getEditableTemplates();
+
+$tmpl = new OCP\Template('files_sharing', 'settings-admin');
+$tmpl->assign('themes', $themes);
+$tmpl->assign('editableTemplates', $editableTemplates);
+
+return $tmpl->fetchPage();
diff --git a/apps/files_sharing/templates/settings-admin.php b/apps/files_sharing/templates/settings-admin.php
new file mode 100644
index 00000000000..4021be871cd
--- /dev/null
+++ b/apps/files_sharing/templates/settings-admin.php
@@ -0,0 +1,41 @@
+<div class="section" id="mailTemplateSettings" >
+
+ <h2><?php p($l->t('Mail templates'));?></h2>
+
+ <div class="actions">
+
+ <div>
+ <label for="mts-theme"><?php p($l->t('Theme'));?></label>
+ <select id="mts-theme">
+ <?php foreach($_['themes'] as $theme => $editable): ?>
+ <option><?php p($theme); ?></option>
+ <?php endforeach; ?>
+ </select>
+ </div>
+
+ <div>
+ <label for="mts-template"><?php p($l->t('Template'));?></label>
+ <select id="mts-template">
+ <?php foreach($_['editableTemplates'] as $template => $editable): ?>
+ <option><?php p($template); ?></option>
+ <?php endforeach; ?>
+ </select>
+ </div>
+
+ </div>
+
+ <div class="templateEditor">
+ <textarea></textarea>
+ </div>
+
+ <div class="actions">
+
+ <button class="reset"><?php p($l->t('Reset'));?></button>
+
+ <button class="save"><?php p($l->t('Save'));?></button>
+
+ <span id="mts-msg" class="msg"></span>
+
+ </div>
+
+</div>
diff --git a/apps/files_sharing/tests/js/appSpec.js b/apps/files_sharing/tests/js/appSpec.js
index 3a1b495a16b..9c46b7caf1b 100644
--- a/apps/files_sharing/tests/js/appSpec.js
+++ b/apps/files_sharing/tests/js/appSpec.js
@@ -122,9 +122,7 @@ describe('OCA.Sharing.App tests', function() {
type: 'dir',
path: '/somewhere/inside/subdir',
counterParts: ['user2'],
- shares: [{
- ownerDisplayName: 'user2'
- }]
+ shareOwner: 'user2'
}]);
fileListIn.findFileEl('testdir').find('td a.name').click();
diff --git a/apps/files_sharing/tests/js/shareSpec.js b/apps/files_sharing/tests/js/shareSpec.js
new file mode 100644
index 00000000000..455addaccc0
--- /dev/null
+++ b/apps/files_sharing/tests/js/shareSpec.js
@@ -0,0 +1,450 @@
+/*
+ * Copyright (c) 2014 Vincent Petry <pvince81@owncloud.com>
+ *
+ * This file is licensed under the Affero General Public License version 3
+ * or later.
+ *
+ * See the COPYING-README file.
+ *
+ */
+
+describe('OCA.Sharing.Util tests', function() {
+ var oldFileListPrototype;
+ var fileList;
+ var testFiles;
+
+ function getImageUrl($el) {
+ // might be slightly different cross-browser
+ var url = $el.css('background-image');
+ var r = url.match(/url\(['"]?([^'")]*)['"]?\)/);
+ if (!r) {
+ return url;
+ }
+ return r[1];
+ }
+
+ beforeEach(function() {
+ // back up prototype, as it will be extended by
+ // the sharing code
+ oldFileListPrototype = _.extend({}, OCA.Files.FileList.prototype);
+
+ var $content = $('<div id="content"></div>');
+ $('#testArea').append($content);
+ // dummy file list
+ var $div = $(
+ '<div>' +
+ '<table id="filestable">' +
+ '<thead></thead>' +
+ '<tbody id="fileList"></tbody>' +
+ '</table>' +
+ '</div>');
+ $('#content').append($div);
+
+ var fileActions = new OCA.Files.FileActions();
+ OCA.Sharing.Util.initialize(fileActions);
+ fileList = new OCA.Files.FileList(
+ $div, {
+ fileActions : fileActions
+ }
+ );
+
+ testFiles = [{
+ id: 1,
+ type: 'file',
+ name: 'One.txt',
+ path: '/subdir',
+ mimetype: 'text/plain',
+ size: 12,
+ permissions: OC.PERMISSION_ALL,
+ etag: 'abc',
+ shareOwner: 'User One',
+ isShareMountPoint: false
+ }];
+
+ OCA.Sharing.sharesLoaded = true;
+ OC.Share.statuses = {
+ 1: {link: false, path: '/subdir'}
+ };
+ });
+ afterEach(function() {
+ OCA.Files.FileList.prototype = oldFileListPrototype;
+ delete OCA.Sharing.sharesLoaded;
+ delete OC.Share.droppedDown;
+ OC.Share.statuses = {};
+ OC.Share.currentShares = {};
+ });
+
+ describe('Sharing data in table row', function() {
+ // TODO: test data-permissions, data-share-owner, etc
+ });
+ describe('Share action icon', function() {
+ it('do not shows share text when not shared', function() {
+ var $action, $tr;
+ OC.Share.statuses = {};
+ fileList.setFiles([{
+ id: 1,
+ type: 'dir',
+ name: 'One',
+ path: '/subdir',
+ mimetype: 'text/plain',
+ size: 12,
+ permissions: OC.PERMISSION_ALL,
+ etag: 'abc'
+ }]);
+ $tr = fileList.$el.find('tbody tr:first');
+ $action = $tr.find('.action-share');
+ expect($action.hasClass('permanent')).toEqual(false);
+ expect(OC.basename($action.find('img').attr('src'))).toEqual('share.svg');
+ expect(OC.basename(getImageUrl($tr.find('.filename')))).toEqual('folder.svg');
+ expect($action.find('img').length).toEqual(1);
+ });
+ it('shows simple share text with share icon', function() {
+ var $action, $tr;
+ fileList.setFiles([{
+ id: 1,
+ type: 'dir',
+ name: 'One',
+ path: '/subdir',
+ mimetype: 'text/plain',
+ size: 12,
+ permissions: OC.PERMISSION_ALL,
+ etag: 'abc'
+ }]);
+ $tr = fileList.$el.find('tbody tr:first');
+ $action = $tr.find('.action-share');
+ expect($action.hasClass('permanent')).toEqual(true);
+ expect($action.find('>span').text()).toEqual('Shared');
+ expect(OC.basename($action.find('img').attr('src'))).toEqual('share.svg');
+ expect(OC.basename(getImageUrl($tr.find('.filename')))).toEqual('folder-shared.svg');
+ expect($action.find('img').length).toEqual(1);
+ });
+ it('shows simple share text with public icon when shared with link', function() {
+ var $action, $tr;
+ OC.Share.statuses = {1: {link: true, path: '/subdir'}};
+ fileList.setFiles([{
+ id: 1,
+ type: 'dir',
+ name: 'One',
+ path: '/subdir',
+ mimetype: 'text/plain',
+ size: 12,
+ permissions: OC.PERMISSION_ALL,
+ etag: 'abc'
+ }]);
+ $tr = fileList.$el.find('tbody tr:first');
+ $action = $tr.find('.action-share');
+ expect($action.hasClass('permanent')).toEqual(true);
+ expect($action.find('>span').text()).toEqual('Shared');
+ expect(OC.basename($action.find('img').attr('src'))).toEqual('public.svg');
+ expect(OC.basename(getImageUrl($tr.find('.filename')))).toEqual('folder-public.svg');
+ expect($action.find('img').length).toEqual(1);
+ });
+ it('shows owner name when owner is available', function() {
+ var $action, $tr;
+ fileList.setFiles([{
+ id: 1,
+ type: 'dir',
+ name: 'One.txt',
+ path: '/subdir',
+ mimetype: 'text/plain',
+ size: 12,
+ permissions: OC.PERMISSION_ALL,
+ shareOwner: 'User One',
+ etag: 'abc'
+ }]);
+ $tr = fileList.$el.find('tbody tr:first');
+ $action = $tr.find('.action-share');
+ expect($action.hasClass('permanent')).toEqual(true);
+ expect($action.find('>span').text()).toEqual('Shared by User One');
+ expect(OC.basename($action.find('img').attr('src'))).toEqual('share.svg');
+ expect(OC.basename(getImageUrl($tr.find('.filename')))).toEqual('folder-shared.svg');
+ });
+ it('shows recipients when recipients are available', function() {
+ var $action, $tr;
+ fileList.setFiles([{
+ id: 1,
+ type: 'dir',
+ name: 'One.txt',
+ path: '/subdir',
+ mimetype: 'text/plain',
+ size: 12,
+ permissions: OC.PERMISSION_ALL,
+ recipientsDisplayName: 'User One, User Two',
+ etag: 'abc'
+ }]);
+ $tr = fileList.$el.find('tbody tr:first');
+ $action = $tr.find('.action-share');
+ expect($action.hasClass('permanent')).toEqual(true);
+ expect($action.find('>span').text()).toEqual('Shared with User One, User Two');
+ expect(OC.basename($action.find('img').attr('src'))).toEqual('share.svg');
+ expect(OC.basename(getImageUrl($tr.find('.filename')))).toEqual('folder-shared.svg');
+ expect($action.find('img').length).toEqual(1);
+ });
+ it('shows static share text when file shared with user that has no share permission', function() {
+ var $action, $tr;
+ fileList.setFiles([{
+ id: 1,
+ type: 'dir',
+ name: 'One',
+ path: '/subdir',
+ mimetype: 'text/plain',
+ size: 12,
+ permissions: OC.PERMISSION_CREATE,
+ etag: 'abc',
+ shareOwner: 'User One'
+ }]);
+ $tr = fileList.$el.find('tbody tr:first');
+ expect($tr.find('.action-share').length).toEqual(0);
+ $action = $tr.find('.action-share-notification');
+ expect($action.hasClass('permanent')).toEqual(true);
+ expect($action.find('>span').text().trim()).toEqual('Shared by User One');
+ expect(OC.basename($action.find('img').attr('src'))).toEqual('share.svg');
+ expect(OC.basename(getImageUrl($tr.find('.filename')))).toEqual('folder-shared.svg');
+ expect($action.find('img').length).toEqual(1);
+ });
+ });
+ describe('Share action', function() {
+ var showDropDownStub;
+
+ function makeDummyShareItem(displayName) {
+ return {
+ share_with_displayname: displayName
+ };
+ }
+
+ beforeEach(function() {
+ showDropDownStub = sinon.stub(OC.Share, 'showDropDown', function() {
+ $('#testArea').append($('<div id="dropdown"></div>'));
+ });
+ });
+ afterEach(function() {
+ showDropDownStub.restore();
+ });
+ it('adds share icon after sharing a non-shared file', function() {
+ var $action, $tr;
+ OC.Share.statuses = {};
+ fileList.setFiles([{
+ id: 1,
+ type: 'file',
+ name: 'One.txt',
+ path: '/subdir',
+ mimetype: 'text/plain',
+ size: 12,
+ permissions: OC.PERMISSION_ALL,
+ etag: 'abc'
+ }]);
+ $action = fileList.$el.find('tbody tr:first .action-share');
+ $tr = fileList.$el.find('tr:first');
+
+ expect($action.hasClass('permanent')).toEqual(false);
+
+ $tr.find('.action-share').click();
+
+ expect(showDropDownStub.calledOnce).toEqual(true);
+
+ // simulate what the dropdown does
+ var shares = {};
+ OC.Share.itemShares[OC.Share.SHARE_TYPE_USER] = ['user1', 'user2'];
+ OC.Share.itemShares[OC.Share.SHARE_TYPE_GROUP] = ['group1', 'group2'];
+ shares[OC.Share.SHARE_TYPE_USER] = _.map(['User One', 'User Two'], makeDummyShareItem);
+ shares[OC.Share.SHARE_TYPE_GROUP] = _.map(['Group One', 'Group Two'], makeDummyShareItem);
+ $('#dropdown').trigger(new $.Event('sharesChanged', {shares: shares}));
+
+ expect($tr.attr('data-share-recipients')).toEqual('Group One, Group Two, User One, User Two');
+
+ OC.Share.updateIcon('file', 1);
+ expect($action.hasClass('permanent')).toEqual(true);
+ expect($action.find('>span').text()).toEqual('Shared with Group One, Group Two, User One, User Two');
+ expect(OC.basename($action.find('img').attr('src'))).toEqual('share.svg');
+ });
+ it('updates share icon after updating shares of a file', function() {
+ var $action, $tr;
+ OC.Share.statuses = {1: {link: false, path: '/subdir'}};
+ fileList.setFiles([{
+ id: 1,
+ type: 'file',
+ name: 'One.txt',
+ path: '/subdir',
+ mimetype: 'text/plain',
+ size: 12,
+ permissions: OC.PERMISSION_ALL,
+ etag: 'abc'
+ }]);
+ $action = fileList.$el.find('tbody tr:first .action-share');
+ $tr = fileList.$el.find('tr:first');
+
+ expect($action.hasClass('permanent')).toEqual(true);
+
+ $tr.find('.action-share').click();
+
+ expect(showDropDownStub.calledOnce).toEqual(true);
+
+ // simulate what the dropdown does
+ var shares = {};
+ OC.Share.itemShares[OC.Share.SHARE_TYPE_USER] = ['user1', 'user2', 'user3'];
+ shares[OC.Share.SHARE_TYPE_USER] = _.map(['User One', 'User Two', 'User Three'], makeDummyShareItem);
+ $('#dropdown').trigger(new $.Event('sharesChanged', {shares: shares}));
+
+ expect($tr.attr('data-share-recipients')).toEqual('User One, User Three, User Two');
+
+ OC.Share.updateIcon('file', 1);
+
+ expect($action.hasClass('permanent')).toEqual(true);
+ expect($action.find('>span').text()).toEqual('Shared with User One, User Three, User Two');
+ expect(OC.basename($action.find('img').attr('src'))).toEqual('share.svg');
+ });
+ it('removes share icon after removing all shares from a file', function() {
+ var $action, $tr;
+ OC.Share.statuses = {1: {link: false, path: '/subdir'}};
+ fileList.setFiles([{
+ id: 1,
+ type: 'file',
+ name: 'One.txt',
+ path: '/subdir',
+ mimetype: 'text/plain',
+ size: 12,
+ permissions: OC.PERMISSION_ALL,
+ etag: 'abc',
+ recipients: 'User One, User Two'
+ }]);
+ $action = fileList.$el.find('tbody tr:first .action-share');
+ $tr = fileList.$el.find('tr:first');
+
+ expect($action.hasClass('permanent')).toEqual(true);
+
+ $tr.find('.action-share').click();
+
+ expect(showDropDownStub.calledOnce).toEqual(true);
+
+ // simulate what the dropdown does
+ OC.Share.itemShares = {};
+ $('#dropdown').trigger(new $.Event('sharesChanged', {shares: {}}));
+
+ expect($tr.attr('data-share-recipients')).not.toBeDefined();
+
+ OC.Share.updateIcon('file', 1);
+ expect($action.hasClass('permanent')).toEqual(false);
+ });
+ it('keep share text after updating reshare', function() {
+ var $action, $tr;
+ OC.Share.statuses = {1: {link: false, path: '/subdir'}};
+ fileList.setFiles([{
+ id: 1,
+ type: 'file',
+ name: 'One.txt',
+ path: '/subdir',
+ mimetype: 'text/plain',
+ size: 12,
+ permissions: OC.PERMISSION_ALL,
+ etag: 'abc',
+ shareOwner: 'User One'
+ }]);
+ $action = fileList.$el.find('tbody tr:first .action-share');
+ $tr = fileList.$el.find('tr:first');
+
+ expect($action.hasClass('permanent')).toEqual(true);
+
+ $tr.find('.action-share').click();
+
+ expect(showDropDownStub.calledOnce).toEqual(true);
+
+ // simulate what the dropdown does
+ var shares = {};
+ OC.Share.itemShares[OC.Share.SHARE_TYPE_USER] = ['user2'];
+ shares[OC.Share.SHARE_TYPE_USER] = _.map(['User Two'], makeDummyShareItem);
+ $('#dropdown').trigger(new $.Event('sharesChanged', {shares: shares}));
+
+ expect($tr.attr('data-share-recipients')).toEqual('User Two');
+
+ OC.Share.updateIcon('file', 1);
+
+ expect($action.hasClass('permanent')).toEqual(true);
+ expect($action.find('>span').text()).toEqual('Shared by User One');
+ expect(OC.basename($action.find('img').attr('src'))).toEqual('share.svg');
+ });
+ it('keep share text after unsharing reshare', function() {
+ var $action, $tr;
+ OC.Share.statuses = {1: {link: false, path: '/subdir'}};
+ fileList.setFiles([{
+ id: 1,
+ type: 'file',
+ name: 'One.txt',
+ path: '/subdir',
+ mimetype: 'text/plain',
+ size: 12,
+ permissions: OC.PERMISSION_ALL,
+ etag: 'abc',
+ shareOwner: 'User One',
+ recipients: 'User Two'
+ }]);
+ $action = fileList.$el.find('tbody tr:first .action-share');
+ $tr = fileList.$el.find('tr:first');
+
+ expect($action.hasClass('permanent')).toEqual(true);
+
+ $tr.find('.action-share').click();
+
+ expect(showDropDownStub.calledOnce).toEqual(true);
+
+ // simulate what the dropdown does
+ OC.Share.itemShares = {};
+ $('#dropdown').trigger(new $.Event('sharesChanged', {shares: {}}));
+
+ expect($tr.attr('data-share-recipients')).not.toBeDefined();
+
+ OC.Share.updateIcon('file', 1);
+
+ expect($action.hasClass('permanent')).toEqual(true);
+ expect($action.find('>span').text()).toEqual('Shared by User One');
+ expect(OC.basename($action.find('img').attr('src'))).toEqual('share.svg');
+ });
+ });
+ describe('formatRecipients', function() {
+ it('returns a single recipient when one passed', function() {
+ expect(OCA.Sharing.Util.formatRecipients(['User one']))
+ .toEqual('User one');
+ });
+ it('returns two recipients when two passed', function() {
+ expect(OCA.Sharing.Util.formatRecipients(['User one', 'User two']))
+ .toEqual('User one, User two');
+ });
+ it('returns four recipients with plus when five passed', function() {
+ var recipients = [
+ 'User one',
+ 'User two',
+ 'User three',
+ 'User four',
+ 'User five'
+ ];
+ expect(OCA.Sharing.Util.formatRecipients(recipients))
+ .toEqual('User four, User one, User three, User two, +1');
+ });
+ it('returns four recipients with plus when ten passed', function() {
+ var recipients = [
+ 'User one',
+ 'User two',
+ 'User three',
+ 'User four',
+ 'User five',
+ 'User six',
+ 'User seven',
+ 'User eight',
+ 'User nine',
+ 'User ten'
+ ];
+ expect(OCA.Sharing.Util.formatRecipients(recipients))
+ .toEqual('User four, User one, User three, User two, +6');
+ });
+ it('returns four recipients with plus when four passed with counter', function() {
+ var recipients = [
+ 'User one',
+ 'User two',
+ 'User three',
+ 'User four'
+ ];
+ expect(OCA.Sharing.Util.formatRecipients(recipients, 10))
+ .toEqual('User four, User one, User three, User two, +6');
+ });
+ });
+
+});
diff --git a/apps/files_sharing/tests/js/sharedfilelistSpec.js b/apps/files_sharing/tests/js/sharedfilelistSpec.js
index 7aec8322a44..f53e79e277f 100644
--- a/apps/files_sharing/tests/js/sharedfilelistSpec.js
+++ b/apps/files_sharing/tests/js/sharedfilelistSpec.js
@@ -9,7 +9,8 @@
*/
describe('OCA.Sharing.FileList tests', function() {
- var testFiles, alertStub, notificationStub, fileList;
+ var testFiles, alertStub, notificationStub, fileList, fileActions;
+ var oldFileListPrototype;
beforeEach(function() {
alertStub = sinon.stub(OC.dialogs, 'alert');
@@ -45,10 +46,17 @@ describe('OCA.Sharing.FileList tests', function() {
'<div id="emptycontent">Empty content message</div>' +
'</div>'
);
+ // back up prototype, as it will be extended by
+ // the sharing code
+ oldFileListPrototype = _.extend({}, OCA.Files.FileList.prototype);
+ fileActions = new OCA.Files.FileActions();
+ OCA.Sharing.Util.initialize(fileActions);
});
afterEach(function() {
+ OCA.Files.FileList.prototype = oldFileListPrototype;
testFiles = undefined;
fileList = undefined;
+ fileActions = undefined;
notificationStub.restore();
alertStub.restore();
diff --git a/apps/files_trashbin/appinfo/update.php b/apps/files_trashbin/appinfo/update.php
index ca7b87a8681..191d3cf25d9 100644
--- a/apps/files_trashbin/appinfo/update.php
+++ b/apps/files_trashbin/appinfo/update.php
@@ -5,6 +5,5 @@ $installedVersion=OCP\Config::getAppValue('files_trashbin', 'installed_version')
if (version_compare($installedVersion, '0.6', '<')) {
//size of the trash bin could be incorrect, remove it for all users to
//enforce a recalculation during next usage.
- $query = \OC_DB::prepare('DELETE FROM `*PREFIX*files_trashsize`');
- $result = $query->execute();
+ \OC_DB::dropTable('files_trashsize');
}
diff --git a/apps/files_trashbin/lib/trashbin.php b/apps/files_trashbin/lib/trashbin.php
index fb23b516208..a8e66b06bbd 100644
--- a/apps/files_trashbin/lib/trashbin.php
+++ b/apps/files_trashbin/lib/trashbin.php
@@ -905,12 +905,12 @@ class Trashbin {
* get current size of trash bin from a given user
*
* @param string $user user who owns the trash bin
- * @return mixed trash bin size or false if no trash bin size is stored
+ * @return integer trash bin size
*/
private static function getTrashbinSize($user) {
$view = new \OC\Files\View('/' . $user);
$fileInfo = $view->getFileInfo('/files_trashbin');
- return $fileInfo['size'];
+ return isset($fileInfo['size']) ? $fileInfo['size'] : 0;
}
/**
diff --git a/apps/files_versions/appinfo/database.xml b/apps/files_versions/appinfo/database.xml
deleted file mode 100644
index d3854776980..00000000000
--- a/apps/files_versions/appinfo/database.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="ISO-8859-1" ?>
-<database>
-
- <name>*dbname*</name>
- <create>true</create>
- <overwrite>false</overwrite>
-
- <charset>utf8</charset>
-
- <table>
-
- <name>*dbprefix*files_versions</name>
-
- <declaration>
-
- <field>
- <name>user</name>
- <type>text</type>
- <default></default>
- <notnull>true</notnull>
- <length>64</length>
- </field>
- <field>
- <name>size</name>
- <type>text</type>
- <default></default>
- <notnull>true</notnull>
- <length>50</length>
- </field>
-
- </declaration>
-
- </table>
-
-</database>
diff --git a/apps/files_versions/appinfo/update.php b/apps/files_versions/appinfo/update.php
index 52a4850758a..fdc6b1e5f1b 100644
--- a/apps/files_versions/appinfo/update.php
+++ b/apps/files_versions/appinfo/update.php
@@ -2,14 +2,6 @@
$installedVersion=OCP\Config::getAppValue('files_versions', 'installed_version');
// move versions to new directory
-if (version_compare($installedVersion, '1.0.2', '<')) {
- $users = \OCP\User::getUsers();
- $datadir = \OCP\Config::getSystemValue('datadirectory').'/';
- foreach ($users as $user) {
- $oldPath = $datadir.$user.'/versions';
- $newPath = $datadir.$user.'/files_versions';
- if(is_dir($oldPath)) {
- rename($oldPath, $newPath);
- }
- }
+if (version_compare($installedVersion, '1.0.4', '<')) {
+ \OC_DB::dropTable("files_versions");
}
diff --git a/apps/files_versions/appinfo/version b/apps/files_versions/appinfo/version
index e4c0d46e55f..ee90284c27f 100644
--- a/apps/files_versions/appinfo/version
+++ b/apps/files_versions/appinfo/version
@@ -1 +1 @@
-1.0.3 \ No newline at end of file
+1.0.4
diff --git a/apps/files_versions/lib/versions.php b/apps/files_versions/lib/versions.php
index 56e1dfc2e24..15d0e032ea0 100644
--- a/apps/files_versions/lib/versions.php
+++ b/apps/files_versions/lib/versions.php
@@ -54,31 +54,12 @@ class Storage {
* get current size of all versions from a given user
*
* @param string $user user who owns the versions
- * @return mixed versions size or false if no versions size is stored
+ * @return int versions size
*/
private static function getVersionsSize($user) {
- $query = \OC_DB::prepare('SELECT `size` FROM `*PREFIX*files_versions` WHERE `user`=?');
- $result = $query->execute(array($user))->fetchAll();
-
- if ($result) {
- return $result[0]['size'];
- }
- return false;
- }
-
- /**
- * write to the database how much space is in use for versions
- *
- * @param string $user owner of the versions
- * @param int $size size of the versions
- */
- private static function setVersionsSize($user, $size) {
- if ( self::getVersionsSize($user) === false) {
- $query = \OC_DB::prepare('INSERT INTO `*PREFIX*files_versions` (`size`, `user`) VALUES (?, ?)');
- }else {
- $query = \OC_DB::prepare('UPDATE `*PREFIX*files_versions` SET `size`=? WHERE `user`=?');
- }
- $query->execute(array($size, $user));
+ $view = new \OC\Files\View('/' . $user);
+ $fileInfo = $view->getFileInfo('/files_versions');
+ return isset($fileInfo['size']) ? $fileInfo['size'] : 0;
}
/**
@@ -115,16 +96,13 @@ class Storage {
self::createMissingDirectories($filename, $users_view);
$versionsSize = self::getVersionsSize($uid);
- if ( $versionsSize === false || $versionsSize < 0 ) {
- $versionsSize = self::calculateSize($uid);
- }
// assumption: we need filesize($filename) for the new version +
// some more free space for the modified file which might be
// 1.5 times as large as the current version -> 2.5
$neededSpace = $files_view->filesize($filename) * 2.5;
- $versionsSize = self::expire($filename, $versionsSize, $neededSpace);
+ self::expire($filename, $versionsSize, $neededSpace);
// disable proxy to prevent multiple fopen calls
$proxyStatus = \OC_FileProxy::$enabled;
@@ -138,10 +116,6 @@ class Storage {
// reset proxy state
\OC_FileProxy::$enabled = $proxyStatus;
-
- $versionsSize += $users_view->filesize('files'.$filename);
-
- self::setVersionsSize($uid, $versionsSize);
}
}
@@ -173,17 +147,11 @@ class Storage {
$abs_path = $versions_fileview->getLocalFile($filename . '.v');
$versions = self::getVersions($uid, $filename);
if (!empty($versions)) {
- $versionsSize = self::getVersionsSize($uid);
- if ($versionsSize === false || $versionsSize < 0) {
- $versionsSize = self::calculateSize($uid);
- }
foreach ($versions as $v) {
\OC_Hook::emit('\OCP\Versions', 'preDelete', array('path' => $abs_path . $v['version']));
unlink($abs_path . $v['version']);
\OC_Hook::emit('\OCP\Versions', 'delete', array('path' => $abs_path . $v['version']));
- $versionsSize -= $v['size'];
}
- self::setVersionsSize($uid, $versionsSize);
}
}
unset(self::$deletedFiles[$path]);
@@ -345,33 +313,6 @@ class Storage {
}
/**
- * get the size of all stored versions from a given user
- * @param string $uid id from the user
- * @return int size of versions
- */
- private static function calculateSize($uid) {
- if (\OCP\Config::getSystemValue('files_versions', Storage::DEFAULTENABLED) == 'true') {
- $view = new \OC\Files\View('/' . $uid . '/files_versions');
-
- $size = 0;
-
- $dirContent = $view->getDirectoryContent('/');
-
- while (!empty($dirContent)) {
- $path = reset($dirContent);
- if ($path['type'] === 'dir') {
- $dirContent = array_merge($dirContent, $view->getDirectoryContent(substr($path['path'], strlen('files_versions'))));
- } else {
- $size += $view->filesize(substr($path['path'], strlen('files_versions')));
- }
- unset($dirContent[key($dirContent)]);
- }
-
- return $size;
- }
- }
-
- /**
* returns all stored file versions from a given user
* @param string $uid id of the user
* @return array with contains two arrays 'all' which contains all versions sorted by age and 'by_file' which contains all versions sorted by filename
@@ -500,9 +441,6 @@ class Storage {
// make sure that we have the current size of the version history
if ( $versionsSize === null ) {
$versionsSize = self::getVersionsSize($uid);
- if ( $versionsSize === false || $versionsSize < 0 ) {
- $versionsSize = self::calculateSize($uid);
- }
}
// calculate available space for version history
diff --git a/core/css/apps.css b/core/css/apps.css
index 18e299552e5..4bcf89c7ee4 100644
--- a/core/css/apps.css
+++ b/core/css/apps.css
@@ -168,6 +168,7 @@
/* Part where the content will be loaded into */
#app-content {
+ position: relative;
height: 100%;
overflow-y: auto;
}
diff --git a/core/img/actions/caret.png b/core/img/actions/caret.png
index 7066b767a28..f6d76b0628b 100644
--- a/core/img/actions/caret.png
+++ b/core/img/actions/caret.png
Binary files differ
diff --git a/core/img/actions/caret.svg b/core/img/actions/caret.svg
index 8cd758daf84..41e880e0311 100644
--- a/core/img/actions/caret.svg
+++ b/core/img/actions/caret.svg
@@ -1,11 +1,76 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" height="10" width="10" version="1.0" xmlns:cc="http://creativecommons.org/ns#" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:dc="http://purl.org/dc/elements/1.1/">
- <defs>
- <linearGradient id="a" x1="8.4965" gradientUnits="userSpaceOnUse" y1="-.061574" gradientTransform="matrix(1.0526 0 0 .98436 -3.4211 1.0602)" x2="8.4965" y2="8.0832">
- <stop stop-color="#fff" offset="0"/>
- <stop stop-color="#e6e6e6" offset="1"/>
- </linearGradient>
- </defs>
- <path opacity=".5" style="block-progression:tb;color:#000000;text-transform:none;text-indent:0" d="m1 2 4 8 4-7.989z"/>
- <path style="block-progression:tb;color:#000000;text-transform:none;text-indent:0" d="m1 1 4 8 4-7.989z" fill="url(#a)"/>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ height="10"
+ width="10"
+ version="1.0"
+ id="svg2"
+ inkscape:version="0.48.4 r9939"
+ sodipodi:docname="caret.svg"
+ inkscape:export-filename="caret.png"
+ inkscape:export-xdpi="179.59"
+ inkscape:export-ydpi="179.59">
+ <metadata
+ id="metadata17">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1920"
+ inkscape:window-height="1002"
+ id="namedview15"
+ showgrid="false"
+ inkscape:zoom="36"
+ inkscape:cx="-0.75"
+ inkscape:cy="5"
+ inkscape:window-x="0"
+ inkscape:window-y="34"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="svg2" />
+ <defs
+ id="defs4">
+ <linearGradient
+ id="a"
+ x1="8.4965"
+ gradientUnits="userSpaceOnUse"
+ y1="-.061574"
+ gradientTransform="matrix(1.0526 0 0 .98436 -3.4211 1.0602)"
+ x2="8.4965"
+ y2="8.0832">
+ <stop
+ stop-color="#fff"
+ offset="0"
+ id="stop7" />
+ <stop
+ stop-color="#e6e6e6"
+ offset="1"
+ id="stop9" />
+ </linearGradient>
+ </defs>
+ <path
+ style="block-progression:tb;color:#000000;text-transform:none;text-indent:0;fill:#ffffff;fill-opacity:1"
+ d="m1 1 4 8 4-7.989z"
+ fill="url(#a)"
+ id="path13" />
</svg>
diff --git a/core/img/actions/logout.png b/core/img/actions/logout.png
index 5b94147732c..64126a55625 100644
--- a/core/img/actions/logout.png
+++ b/core/img/actions/logout.png
Binary files differ
diff --git a/core/img/actions/logout.svg b/core/img/actions/logout.svg
index 895080dab8f..1a0ee167184 100644
--- a/core/img/actions/logout.svg
+++ b/core/img/actions/logout.svg
@@ -1,5 +1,58 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" height="16" width="16" version="1.0" xmlns:cc="http://creativecommons.org/ns#" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:dc="http://purl.org/dc/elements/1.1/">
- <path style="block-progression:tb;color:#000000;text-transform:none;text-indent:0" d="m8.0001 0c-0.4714 0-0.96103 0.5419-0.95 1v6c-0.00747 0.52831 0.42163 1 0.95 1s0.95747-0.47169 0.95-1v-6c0.014622-0.6051-0.4786-1-0.95-1zm-3.3438 2.5c-0.087186 0.019294-0.17163 0.050959-0.25 0.09375-2.9995 1.5715-3.9184 4.7979-3.125 7.4688 0.7934 2.67 3.2799 4.937 6.6875 4.937 3.3592 0 5.8772-2.149 6.7192-4.781 0.841-2.6321-0.058-5.8234-3.125-7.594-0.434-0.2536-1.059-0.0899-1.313 0.3437-0.2536 0.4336-0.09 1.0589 0.344 1.3125 2.3908 1.3798 2.8825 3.4944 2.2812 5.375-0.6012 1.8806-2.344 3.4375-4.9062 3.4375-2.5759 0-4.2976-1.6502-4.875-3.5938-0.5776-1.9435-0.047-4.048 2.1873-5.2187 0.3787-0.2063 0.5791-0.6925 0.4558-1.1057-0.1232-0.4133-0.5572-0.7103-0.987-0.6755-0.0313-0.0015-0.0626-0.0015-0.0938 0z"/>
- <path style="block-progression:tb;color:#000000;text-transform:none;text-indent:0" d="m8.0001 1c-0.4714 0-0.96103 0.5419-0.95 1v6c-0.00747 0.52831 0.42163 1 0.95 1s0.95747-0.47169 0.95-1v-6c0.014622-0.6051-0.4786-1-0.95-1zm-3.3438 2.5c-0.087186 0.019294-0.17163 0.050959-0.25 0.09375-2.9995 1.5715-3.9184 4.7979-3.125 7.4688 0.7934 2.67 3.2799 4.937 6.6875 4.937 3.3592 0 5.8772-2.149 6.7192-4.781 0.841-2.6321-0.058-5.8234-3.125-7.594-0.434-0.2536-1.059-0.0899-1.313 0.3437-0.2536 0.4336-0.09 1.0589 0.344 1.3125 2.3908 1.3798 2.8825 3.4944 2.2812 5.375-0.6012 1.8806-2.344 3.4375-4.9062 3.4375-2.5759 0-4.2976-1.6502-4.875-3.5938-0.5776-1.9436-0.047-4.0481 2.1873-5.2188 0.3787-0.2063 0.5791-0.6925 0.4558-1.1057-0.1232-0.4133-0.5572-0.7103-0.987-0.6755-0.0313-0.0015-0.0626-0.0015-0.0938 0z" fill="#fff"/>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ height="16"
+ width="16"
+ version="1.0"
+ id="svg2"
+ inkscape:version="0.48.4 r9939"
+ sodipodi:docname="logout.svg"
+ inkscape:export-filename="logout.png"
+ inkscape:export-xdpi="179.59"
+ inkscape:export-ydpi="179.59">
+ <metadata
+ id="metadata12">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs10" />
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1920"
+ inkscape:window-height="1002"
+ id="namedview8"
+ showgrid="false"
+ inkscape:zoom="22.5"
+ inkscape:cx="8"
+ inkscape:cy="8"
+ inkscape:window-x="0"
+ inkscape:window-y="34"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="svg2" />
+ <path
+ style="text-indent:0;text-transform:none;block-progression:tb;color:#000000;fill:#ffffff"
+ d="m 8.0001,4.5e-4 c -0.4714,0 -0.96103,0.5419 -0.95,1 v 6 c -0.00747,0.52831 0.42163,1 0.95,1 0.52837,0 0.95747,-0.47169 0.95,-1 v -6 c 0.014622,-0.6051 -0.4786,-1 -0.95,-1 z m -3.3438,2.5 C 4.569114,2.519744 4.48467,2.551409 4.4063,2.5942 1.4068,4.1657 0.4879,7.3921 1.2813,10.063 2.0747,12.733 4.5612,15 7.9688,15 11.328,15 13.846,12.851 14.688,10.219 15.529,7.5869 14.63,4.3956 11.563,2.625 11.129,2.3714 10.504,2.5351 10.25,2.9687 c -0.2536,0.4336 -0.09,1.0589 0.344,1.3125 2.3908,1.3798 2.8825,3.4944 2.2812,5.375 -0.6012,1.8806 -2.344,3.4375 -4.9062,3.4375 -2.5759,0 -4.2976,-1.6502 -4.875,-3.5938 C 2.5164,7.5563 3.047,5.4518 5.2813,4.2811 5.66,4.0748 5.8604,3.5886 5.7371,3.1754 5.6139,2.7621 5.1799,2.4651 4.7501,2.4999 4.7188,2.4984 4.6875,2.4984 4.6563,2.4999 z"
+ id="path6"
+ inkscape:connector-curvature="0" />
</svg>
diff --git a/core/img/filetypes/folder-shared.png b/core/img/filetypes/folder-shared.png
index e3c0ee9815a..b4f02ac7399 100644
--- a/core/img/filetypes/folder-shared.png
+++ b/core/img/filetypes/folder-shared.png
Binary files differ
diff --git a/core/img/filetypes/folder-shared.svg b/core/img/filetypes/folder-shared.svg
index a389e535439..aba8cadbe28 100644
--- a/core/img/filetypes/folder-shared.svg
+++ b/core/img/filetypes/folder-shared.svg
@@ -1,60 +1,64 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" height="32px" width="32px" version="1.1" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/">
<defs>
- <linearGradient id="p" x1="27.557" gradientUnits="userSpaceOnUse" y1="7.1627" gradientTransform="matrix(.89186 0 0 1.0539 3.1208 5.4125)" x2="27.557" y2="21.387">
+ <linearGradient id="c" y2="21.387" gradientUnits="userSpaceOnUse" x2="27.557" gradientTransform="matrix(.89186 0 0 1.0539 3.1208 5.4125)" y1="7.1627" x1="27.557">
<stop stop-color="#fff" offset="0"/>
<stop stop-color="#fff" stop-opacity=".23529" offset=".0097359"/>
<stop stop-color="#fff" stop-opacity=".15686" offset=".99001"/>
<stop stop-color="#fff" stop-opacity=".39216" offset="1"/>
</linearGradient>
- <linearGradient id="o" x1="22.935" gradientUnits="userSpaceOnUse" y1="49.629" gradientTransform="matrix(.74675 0 0 .65549 -1.9219 1.1676)" x2="22.809" y2="36.658">
+ <linearGradient id="d" y2="36.658" gradientUnits="userSpaceOnUse" x2="22.809" gradientTransform="matrix(.74675 0 0 .65549 -1.9219 1.1676)" y1="49.629" x1="22.935">
<stop stop-color="#0a0a0a" stop-opacity=".498" offset="0"/>
<stop stop-color="#0a0a0a" stop-opacity="0" offset="1"/>
</linearGradient>
- <linearGradient id="n" x1="35.793" gradientUnits="userSpaceOnUse" y1="17.118" gradientTransform="matrix(.64444 0 0 .64286 .53352 .89286)" x2="35.793" y2="43.761">
+ <linearGradient id="e" y2="43.761" gradientUnits="userSpaceOnUse" x2="35.793" gradientTransform="matrix(.64444 0 0 .64286 .53352 .89286)" y1="17.118" x1="35.793">
<stop stop-color="#b4cee1" offset="0"/>
<stop stop-color="#5d9fcd" offset="1"/>
</linearGradient>
- <linearGradient id="m" x1="302.86" gradientUnits="userSpaceOnUse" y1="366.65" gradientTransform="matrix(.051143 0 0 .015916 -2.49 22.299)" x2="302.86" y2="609.51">
+ <linearGradient id="f" y2="609.51" gradientUnits="userSpaceOnUse" x2="302.86" gradientTransform="matrix(.051143 0 0 .015916 -2.49 22.299)" y1="366.65" x1="302.86">
<stop stop-opacity="0" offset="0"/>
<stop offset=".5"/>
<stop stop-opacity="0" offset="1"/>
</linearGradient>
- <radialGradient id="q" gradientUnits="userSpaceOnUse" cy="486.65" cx="605.71" gradientTransform="matrix(.019836 0 0 .015916 16.388 22.299)" r="117.14">
+ <radialGradient id="b" gradientUnits="userSpaceOnUse" cy="486.65" cx="605.71" gradientTransform="matrix(.019836 0 0 .015916 16.388 22.299)" r="117.14">
<stop offset="0"/>
<stop stop-opacity="0" offset="1"/>
</radialGradient>
- <radialGradient id="r" gradientUnits="userSpaceOnUse" cy="486.65" cx="605.71" gradientTransform="matrix(-.019836 0 0 .015916 15.601 22.299)" r="117.14">
+ <radialGradient id="a" gradientUnits="userSpaceOnUse" cy="486.65" cx="605.71" gradientTransform="matrix(-.019836 0 0 .015916 15.601 22.299)" r="117.14">
<stop offset="0"/>
<stop stop-opacity="0" offset="1"/>
</radialGradient>
- <linearGradient id="l" x1="21.37" gradientUnits="userSpaceOnUse" y1="4.7324" gradientTransform="matrix(.54384 0 0 .61466 3.2689 5.0911)" x2="21.37" y2="34.143">
+ <linearGradient id="g" y2="34.143" gradientUnits="userSpaceOnUse" x2="21.37" gradientTransform="matrix(.54384 0 0 .61466 3.2689 5.0911)" y1="4.7324" x1="21.37">
<stop stop-color="#fff" offset="0"/>
<stop stop-color="#fff" stop-opacity=".23529" offset=".11063"/>
<stop stop-color="#fff" stop-opacity=".15686" offset=".99001"/>
<stop stop-color="#fff" stop-opacity=".39216" offset="1"/>
</linearGradient>
- <linearGradient id="k" x1="62.989" gradientUnits="userSpaceOnUse" y1="13" gradientTransform="matrix(.61905 0 0 .61905 -30.392 1.4286)" x2="62.989" y2="16">
+ <linearGradient id="h" y2="16" gradientUnits="userSpaceOnUse" x2="62.989" gradientTransform="matrix(.61905 0 0 .61905 -30.392 1.4286)" y1="13" x1="62.989">
<stop stop-color="#f9f9f9" offset="0"/>
<stop stop-color="#d8d8d8" offset="1"/>
</linearGradient>
- <linearGradient id="j" x1="-51.786" gradientUnits="userSpaceOnUse" x2="-51.786" gradientTransform="matrix(.50703 0 0 .503 68.029 1.3298)" y1="53.514" y2="3.6337">
+ <linearGradient id="i" y2="3.6337" gradientUnits="userSpaceOnUse" y1="53.514" gradientTransform="matrix(.50703 0 0 .503 68.029 1.3298)" x2="-51.786" x1="-51.786">
<stop stop-opacity=".32174" offset="0"/>
<stop stop-opacity=".27826" offset="1"/>
</linearGradient>
</defs>
- <path opacity=".8" style="color:#000000" d="m4.0002 6.5001c-0.43342 0.005-0.5 0.21723-0.5 0.6349v1.365c-1.2457 0-1-0.002-1 0.54389 0.0216 6.5331 0 6.9014 0 7.4561 0.90135 0 27-2.349 27-3.36v-4.0961c0-0.41767-0.34799-0.54876-0.78141-0.54389h-14.219v-1.365c0-0.41767-0.26424-0.63977-0.69767-0.6349h-9.8023z" stroke="url(#j)" fill="none"/>
- <path style="color:#000000" d="m4.0002 7v2h-1v4h26v-4h-15v-2h-10z" fill="url(#k)"/>
- <path style="color:#000000" stroke-linecap="round" d="m4.5002 7.5v2h-1v4h25v-4h-15v-2h-9z" stroke="url(#l)" fill="none"/>
+ <path opacity=".8" style="color:#000000" d="m4.0002 6.5001c-0.43342 0.005-0.5 0.21723-0.5 0.6349v1.365c-1.2457 0-1-0.002-1 0.54389 0.0216 6.5331 0 6.9014 0 7.4561 0.90135 0 27-2.349 27-3.36v-4.0961c0-0.41767-0.34799-0.54876-0.78141-0.54389h-14.219v-1.365c0-0.41767-0.26424-0.63977-0.69767-0.6349h-9.8023z" stroke="url(#i)" fill="none"/>
+ <path style="color:#000000" d="m4.0002 7v2h-1v4h26v-4h-15v-2h-10z" fill="url(#h)"/>
+ <path style="color:#000000" d="m4.5002 7.5v2h-1v4h25v-4h-15v-2h-9z" stroke="url(#g)" stroke-linecap="round" fill="none"/>
<g transform="translate(.00017936 -1)">
- <rect opacity=".3" height="3.8653" width="24.695" y="28.135" x="3.6472" fill="url(#m)"/>
- <path opacity=".3" d="m28.342 28.135v3.865c1.0215 0.0073 2.4695-0.86596 2.4695-1.9328s-1.1399-1.9323-2.4695-1.9323z" fill="url(#q)"/>
- <path opacity=".3" d="m3.6472 28.135v3.865c-1.0215 0.0073-2.4695-0.86596-2.4695-1.9328s1.1399-1.9323 2.4695-1.9323z" fill="url(#r)"/>
+ <rect opacity=".3" height="3.8653" width="24.695" y="28.135" x="3.6472" fill="url(#f)"/>
+ <path opacity=".3" d="m28.342 28.135v3.865c1.0215 0.0073 2.4695-0.86596 2.4695-1.9328s-1.1399-1.9323-2.4695-1.9323z" fill="url(#b)"/>
+ <path opacity=".3" d="m3.6472 28.135v3.865c-1.0215 0.0073-2.4695-0.86596-2.4695-1.9328s1.1399-1.9323 2.4695-1.9323z" fill="url(#a)"/>
</g>
- <path style="color:#000000" d="m1.927 11.5c-0.69105 0.0796-0.32196 0.90258-0.37705 1.3654 0.0802 0.29906 0.59771 15.718 0.59771 16.247 0 0.46018 0.22667 0.38222 0.80101 0.38222h26.397c0.61872 0.0143 0.48796 0.007 0.48796-0.38947 0.0452-0.20269 0.63993-16.978 0.66282-17.243 0-0.279 0.0581-0.3621-0.30493-0.3621h-28.265z" fill="url(#n)"/>
- <path opacity=".4" d="m1.682 11 28.636 0.00027c0.4137 0 0.68181 0.29209 0.68181 0.65523l-0.6735 17.712c0.01 0.45948-0.1364 0.64166-0.61707 0.63203l-27.256-0.0115c-0.4137 0-0.83086-0.27118-0.83086-0.63432l-0.62244-17.698c0-0.36314 0.26812-0.65549 0.68182-0.65549z" fill="url(#o)"/>
- <path opacity=".5" style="color:#000000" d="m2.5002 12.5 0.62498 16h25.749l0.62498-16z" stroke="url(#p)" stroke-linecap="round" fill="none"/>
+ <path style="color:#000000" d="m1.927 11.5c-0.69105 0.0796-0.32196 0.90258-0.37705 1.3654 0.0802 0.29906 0.59771 15.718 0.59771 16.247 0 0.46018 0.22667 0.38222 0.80101 0.38222h26.397c0.61872 0.0143 0.48796 0.007 0.48796-0.38947 0.0452-0.20269 0.63993-16.978 0.66282-17.243 0-0.279 0.0581-0.3621-0.30493-0.3621h-28.265z" fill="url(#e)"/>
+ <path opacity=".4" d="m1.682 11 28.636 0.00027c0.4137 0 0.68181 0.29209 0.68181 0.65523l-0.6735 17.712c0.01 0.45948-0.1364 0.64166-0.61707 0.63203l-27.256-0.0115c-0.4137 0-0.83086-0.27118-0.83086-0.63432l-0.62244-17.698c0-0.36314 0.26812-0.65549 0.68182-0.65549z" fill="url(#d)"/>
+ <path opacity=".5" style="color:#000000" d="m2.5002 12.5 0.62498 16h25.749l0.62498-16z" stroke="url(#c)" stroke-linecap="round" fill="none"/>
<path opacity=".3" stroke-linejoin="round" style="color:#000000" d="m1.927 11.5c-0.69105 0.0796-0.32196 0.90258-0.37705 1.3654 0.0802 0.29906 0.59771 15.718 0.59771 16.247 0 0.46018 0.22667 0.38222 0.80101 0.38222h26.397c0.61872 0.0143 0.48796 0.007 0.48796-0.38947 0.0452-0.20269 0.63993-16.978 0.66282-17.243 0-0.279 0.0581-0.3621-0.30493-0.3621h-28.265z" stroke="#000" stroke-linecap="round" fill="none"/>
- <path opacity=".3" style="block-progression:tb;color:#000000;text-transform:none;text-indent:0" d="m12.388 16.483c-0.96482 0-1.7833 0.70559-1.7833 1.6162 0.0069 0.28781 0.03259 0.64272 0.20434 1.3933v0.01858l0.01857 0.01857c0.05513 0.15793 0.13537 0.24827 0.24149 0.37154 0.10612 0.12326 0.23263 0.26834 0.35294 0.39011 0.01415 0.01433 0.02323 0.0232 0.03715 0.03716 0.02386 0.10383 0.05276 0.21557 0.0743 0.3158 0.05732 0.26668 0.05144 0.45553 0.03716 0.52015-0.4146 0.1454-0.9304 0.3187-1.3932 0.5199-0.2598 0.113-0.4949 0.2139-0.6873 0.3344-0.1923 0.1206-0.3836 0.2116-0.4458 0.483-0.000797 0.01237-0.000797 0.02479 0 0.03716-0.06076 0.55788-0.15266 1.3783-0.22291 1.932-0.015166 0.11656 0.046264 0.23943 0.14861 0.29723 0.84033 0.45393 2.1312 0.63663 3.418 0.63161 1.2868-0.005 2.5674-0.19845 3.3808-0.63161 0.10234-0.0578 0.16378-0.18067 0.14861-0.29723-0.0224-0.173-0.05-0.5633-0.0743-0.9474-0.0243-0.384-0.0454-0.7617-0.0743-0.9845-0.0101-0.0552-0.0362-0.1074-0.0743-0.1486-0.2584-0.3086-0.6445-0.4973-1.096-0.6874-0.4122-0.1735-0.8954-0.3538-1.3746-0.5573-0.02682-0.05975-0.05346-0.23358 0-0.50157 0.01436-0.07196 0.03684-0.14903 0.05573-0.22292 0.04503-0.05044 0.08013-0.09166 0.13003-0.14861 0.1064-0.1215 0.2207-0.2489 0.3157-0.3715 0.0951-0.1226 0.1728-0.2279 0.223-0.3715l0.01857-0.01858c0.1941-0.7837 0.1942-1.1107 0.2043-1.3933v-0.01857c0-0.91058-0.81848-1.6162-1.7833-1.6162zm5.101-1.4831c-1.4067 0-2.6 1.0287-2.6 2.3562 0.01 0.4196 0.04751 0.93701 0.29791 2.0312v0.02708l0.02708 0.02708c0.08038 0.23025 0.19736 0.36196 0.35208 0.54166s0.33917 0.39121 0.51458 0.56874c0.02064 0.02089 0.03386 0.03383 0.05416 0.05418 0.03479 0.15137 0.07693 0.31428 0.10833 0.46041 0.08357 0.38879 0.07499 0.66411 0.05417 0.75832-0.6045 0.2122-1.3565 0.465-2.0312 0.7583-0.3789 0.1647-0.7217 0.3118-1.0021 0.4875-0.28044 0.17574-0.55934 0.30851-0.64999 0.70416-0.0012 0.01804-0.0012 0.03613 0 0.05418-0.08858 0.81334-0.22257 2.0094-0.325 2.8166-0.02211 0.16993 0.06745 0.34906 0.21666 0.43333 1.2252 0.66179 3.1072 0.92814 4.9833 0.92082 1.8761-0.0073 3.7431-0.28932 4.9291-0.92082 0.14921-0.08427 0.23878-0.2634 0.21666-0.43333-0.0327-0.25234-0.07287-0.82136-0.10833-1.3812-0.03546-0.55988-0.06625-1.1106-0.10833-1.4354-0.01468-0.0805-0.05274-0.15661-0.10833-0.21666-0.377-0.4498-0.94-0.7248-1.598-1.002-0.601-0.253-1.306-0.5158-2.004-0.8125-0.0391-0.08711-0.07795-0.34054 0-0.73124 0.02093-0.10491 0.05371-0.21727 0.08125-0.325 0.06566-0.07354 0.11683-0.13363 0.18958-0.21666 0.15516-0.17709 0.32189-0.36287 0.46041-0.54166s0.25186-0.33217 0.325-0.54166l0.02708-0.02708c0.28309-1.1425 0.28324-1.6193 0.29792-2.0312v-0.02708c0-1.3275-1.1933-2.3562-2.6-2.3562z" fill="#FFF"/>
- <path opacity=".7" style="block-progression:tb;color:#000000;text-transform:none;text-indent:0" d="m12.388 15.483c-0.96482 0-1.7833 0.70559-1.7833 1.6162 0.0069 0.28781 0.03259 0.64272 0.20434 1.3933v0.01858l0.01857 0.01857c0.05513 0.15793 0.13537 0.24827 0.24149 0.37154 0.10612 0.12326 0.23263 0.26834 0.35294 0.39011 0.01415 0.01433 0.02323 0.0232 0.03715 0.03716 0.02386 0.10383 0.05276 0.21557 0.0743 0.3158 0.05732 0.26668 0.05144 0.45553 0.03716 0.52015-0.4146 0.1454-0.9304 0.3187-1.3932 0.5199-0.2598 0.113-0.4949 0.2139-0.6873 0.3344-0.1923 0.1206-0.3836 0.2116-0.4458 0.483-0.000797 0.01237-0.000797 0.02479 0 0.03716-0.06076 0.55788-0.15266 1.3783-0.22291 1.932-0.015166 0.11656 0.046264 0.23943 0.14861 0.29723 0.84033 0.45393 2.1312 0.63663 3.418 0.63161 1.2868-0.005 2.5674-0.19845 3.3808-0.63161 0.10234-0.0578 0.16378-0.18067 0.14861-0.29723-0.0224-0.173-0.05-0.5633-0.0743-0.9474-0.0243-0.384-0.0454-0.7617-0.0743-0.9845-0.0101-0.0552-0.0362-0.1074-0.0743-0.1486-0.2584-0.3086-0.6445-0.4973-1.096-0.6874-0.4122-0.1735-0.8954-0.3538-1.3746-0.5573-0.02682-0.05975-0.05346-0.23358 0-0.50157 0.01436-0.07196 0.03684-0.14903 0.05573-0.22292 0.04503-0.05044 0.08013-0.09166 0.13003-0.14861 0.1064-0.1215 0.2207-0.2489 0.3157-0.3715 0.0951-0.1226 0.1728-0.2279 0.223-0.3715l0.01857-0.01858c0.1941-0.7837 0.1942-1.1107 0.2043-1.3933v-0.01857c0-0.91058-0.81848-1.6162-1.7833-1.6162zm5.101-1.4831c-1.4067 0-2.6 1.0287-2.6 2.3562 0.01 0.4196 0.04751 0.93701 0.29791 2.0312v0.02708l0.02708 0.02708c0.08038 0.23025 0.19736 0.36196 0.35208 0.54166s0.33917 0.39121 0.51458 0.56874c0.02064 0.02089 0.03386 0.03383 0.05416 0.05418 0.03479 0.15137 0.07693 0.31428 0.10833 0.46041 0.08357 0.38879 0.07499 0.66411 0.05417 0.75832-0.6045 0.2122-1.3565 0.465-2.0312 0.7583-0.3789 0.1647-0.7217 0.3118-1.0021 0.4875-0.28044 0.17574-0.55934 0.30851-0.64999 0.70416-0.0012 0.01804-0.0012 0.03613 0 0.05418-0.08858 0.81334-0.22257 2.0094-0.325 2.8166-0.02211 0.16993 0.06745 0.34906 0.21666 0.43333 1.2252 0.66179 3.1072 0.92814 4.9833 0.92082 1.8761-0.0073 3.7431-0.28932 4.9291-0.92082 0.14921-0.08427 0.23878-0.2634 0.21666-0.43333-0.0327-0.25234-0.07287-0.82136-0.10833-1.3812-0.03546-0.55988-0.06625-1.1106-0.10833-1.4354-0.01468-0.0805-0.05274-0.15661-0.10833-0.21666-0.377-0.4498-0.94-0.7248-1.598-1.002-0.601-0.253-1.306-0.5158-2.004-0.8125-0.0391-0.08711-0.07795-0.34054 0-0.73124 0.02093-0.10491 0.05371-0.21727 0.08125-0.325 0.06566-0.07354 0.11683-0.13363 0.18958-0.21666 0.15516-0.17709 0.32189-0.36287 0.46041-0.54166s0.25186-0.33217 0.325-0.54166l0.02708-0.02708c0.28309-1.1425 0.28324-1.6193 0.29792-2.0312v-0.02708c0-1.3275-1.1933-2.3562-2.6-2.3562z"/>
+ <g opacity=".3" transform="translate(6.9998,-1023.4)" fill="#fff">
+ <path fill="#fff" d="m12.228 1037.4c-1.3565 0-2.4592 1.0977-2.4592 2.4542 0 0.075 0.0084 0.1504 0.0149 0.2236l-4.7346 2.4145c-0.4291-0.3667-0.98611-0.5863-1.5947-0.5863-1.3565 0-2.4542 1.0977-2.4542 2.4543 0 1.3565 1.0977 2.4542 2.4542 2.4542 0.54607 0 1.0528-0.1755 1.4606-0.477l4.8637 2.4741c-0.0024 0.044-0.0099 0.089-0.0099 0.1342 0 1.3565 1.1027 2.4542 2.4592 2.4542s2.4542-1.0977 2.4542-2.4542-1.0977-2.4592-2.4542-2.4592c-0.63653 0-1.218 0.2437-1.6544 0.6409l-4.6953-2.4c0.01892-0.1228 0.03478-0.2494 0.03478-0.3775 0-0.072-0.0089-0.1437-0.0149-0.2137l4.7395-2.4145c0.42802 0.3627 0.98488 0.5813 1.5898 0.5813 1.3565 0 2.4542-1.1027 2.4542-2.4592s-1.0977-2.4542-2.4542-2.4542z"/>
+ </g>
+ <g opacity=".7" transform="translate(6.9998,-1024.4)">
+ <path d="m12.228 1037.4c-1.3565 0-2.4592 1.0977-2.4592 2.4542 0 0.075 0.0084 0.1504 0.0149 0.2236l-4.7346 2.4145c-0.4291-0.3667-0.98611-0.5863-1.5947-0.5863-1.3565 0-2.4542 1.0977-2.4542 2.4543 0 1.3565 1.0977 2.4542 2.4542 2.4542 0.54607 0 1.0528-0.1755 1.4606-0.477l4.8637 2.4741c-0.0024 0.044-0.0099 0.089-0.0099 0.1342 0 1.3565 1.1027 2.4542 2.4592 2.4542s2.4542-1.0977 2.4542-2.4542-1.0977-2.4592-2.4542-2.4592c-0.63653 0-1.218 0.2437-1.6544 0.6409l-4.6953-2.4c0.01892-0.1228 0.03478-0.2494 0.03478-0.3775 0-0.072-0.0089-0.1437-0.0149-0.2137l4.7395-2.4145c0.42802 0.3627 0.98488 0.5813 1.5898 0.5813 1.3565 0 2.4542-1.1027 2.4542-2.4592s-1.0977-2.4542-2.4542-2.4542z"/>
+ </g>
</svg>
diff --git a/core/js/share.js b/core/js/share.js
index bcc04225b9f..7e3b0d8c65d 100644
--- a/core/js/share.js
+++ b/core/js/share.js
@@ -3,8 +3,25 @@ OC.Share={
SHARE_TYPE_GROUP:1,
SHARE_TYPE_LINK:3,
SHARE_TYPE_EMAIL:4,
+ /**
+ * @deprecated use OC.Share.currentShares instead
+ */
itemShares:[],
+ /**
+ * Full list of all share statuses
+ */
statuses:{},
+ /**
+ * Shares for the currently selected file.
+ * (for which the dropdown is open)
+ *
+ * Key is item type and value is an array or
+ * shares of the given item type.
+ */
+ currentShares: {},
+ /**
+ * Whether the share dropdown is opened.
+ */
droppedDown:false,
/**
* Loads ALL share statuses from server, stores them in
@@ -54,8 +71,9 @@ OC.Share={
$fileList = fileList.$fileList;
currentDir = fileList.getCurrentDirectory();
}
+ // TODO: iterating over the files might be more efficient
for (item in OC.Share.statuses){
- var image = OC.imagePath('core', 'actions/shared');
+ var image = OC.imagePath('core', 'actions/share');
var data = OC.Share.statuses[item];
var hasLink = data.link;
// Links override shared in terms of icon display
@@ -63,20 +81,14 @@ OC.Share={
image = OC.imagePath('core', 'actions/public');
}
if (itemType !== 'file' && itemType !== 'folder') {
- $fileList.find('a.share[data-item="'+item+'"]').css('background', 'url('+image+') no-repeat center');
+ $('a.share[data-item="'+item+'"]').css('background', 'url('+image+') no-repeat center');
} else {
+ // TODO: ultimately this part should be moved to files_sharing app
var file = $fileList.find('tr[data-id="'+item+'"]');
var shareFolder = OC.imagePath('core', 'filetypes/folder-shared');
var img;
if (file.length > 0) {
- var type = file.data('type');
- if (type === 'dir') {
- file.children('.filename').css('background-image', 'url('+shareFolder+')');
- }
- var action = $(file).find('.fileactions .action[data-action="Share"]');
- img = action.find('img').attr('src', image);
- action.addClass('permanent');
- action.html(' <span>'+t('core', 'Shared')+'</span>').prepend(img);
+ this.markFileAsShared(file, true, hasLink);
} else {
var dir = currentDir;
if (dir.length > 1) {
@@ -89,6 +101,7 @@ OC.Share={
var files = $fileList.find('.filename');
var i;
for (i = 0; i < actions.length; i++) {
+ // TODO: use this.markFileAsShared()
img = $(actions[i]).find('img');
if (img.attr('src') !== OC.imagePath('core', 'actions/public')) {
img.attr('src', image);
@@ -125,35 +138,19 @@ OC.Share={
}
} else if (OC.Share.itemShares[index].length > 0) {
shares = true;
- image = OC.imagePath('core', 'actions/shared');
+ image = OC.imagePath('core', 'actions/share');
}
}
});
if (itemType != 'file' && itemType != 'folder') {
$('a.share[data-item="'+itemSource+'"]').css('background', 'url('+image+') no-repeat center');
} else {
- var file = $('tr').filterAttr('data-id', String(itemSource));
- if (file.length > 0) {
- var type = file.data('type');
- var shareFolder = OC.imagePath('core', 'filetypes/folder');
- if (type === 'dir' && shares) {
- shareFolder = OC.imagePath('core', 'filetypes/folder-shared');
- file.children('.filename').css('background-image', 'url('+shareFolder+')');
- } else if (type === 'dir') {
- file.children('.filename').css('background-image', 'url('+shareFolder+')');
- }
- var action = $(file).find('.fileactions .action').filterAttr('data-action', 'Share');
- // in case of multiple lists/rows, there might be more than one visible
- action.each(function() {
- var action = $(this);
- var img = action.find('img').attr('src', image);
- if (shares) {
- action.addClass('permanent');
- action.html(' <span>'+ escapeHTML(t('core', 'Shared'))+'</span>').prepend(img);
- } else {
- action.removeClass('permanent');
- action.html(' <span>'+ escapeHTML(t('core', 'Share'))+'</span>').prepend(img);
- }
+ var $tr = $('tr').filterAttr('data-id', String(itemSource));
+ if ($tr.length > 0) {
+ // it might happen that multiple lists exist in the DOM
+ // with the same id
+ $tr.each(function() {
+ OC.Share.markFileAsShared($(this), shares, link);
});
}
}
@@ -164,6 +161,60 @@ OC.Share={
delete OC.Share.statuses[itemSource];
}
},
+ /**
+ * Marks/unmarks a given file as shared by changing its action icon
+ * and folder icon.
+ *
+ * @param $tr file element to mark as shared
+ * @param hasShares whether shares are available
+ * @param hasLink whether link share is available
+ */
+ markFileAsShared: function($tr, hasShares, hasLink) {
+ var action = $tr.find('.fileactions .action[data-action="Share"]');
+ var type = $tr.data('type');
+ var img = action.find('img');
+ var message;
+ var recipients;
+ var owner = $tr.attr('data-share-owner');
+ var shareFolderIcon;
+ var image = OC.imagePath('core', 'actions/share');
+ // update folder icon
+ if (type === 'dir' && (hasShares || hasLink)) {
+ if (hasLink) {
+ shareFolderIcon = OC.imagePath('core', 'filetypes/folder-public');
+ }
+ else {
+ shareFolderIcon = OC.imagePath('core', 'filetypes/folder-shared');
+ }
+ $tr.children('.filename').css('background-image', 'url(' + shareFolderIcon + ')');
+ } else if (type === 'dir') {
+ shareFolderIcon = OC.imagePath('core', 'filetypes/folder');
+ $tr.children('.filename').css('background-image', 'url(' + shareFolderIcon + ')');
+ }
+ // update share action text / icon
+ if (hasShares || owner) {
+ recipients = $tr.attr('data-share-recipients');
+
+ action.addClass('permanent');
+ message = t('core', 'Shared');
+ // even if reshared, only show "Shared by"
+ if (owner) {
+ message = t('files_sharing', 'Shared by {owner}', {owner: owner});
+ }
+ else if (recipients) {
+ message = t('core', 'Shared with {recipients}', {recipients: recipients});
+ }
+ action.html(' <span>'+ message + '</span>').prepend(img);
+ }
+ else {
+ action.removeClass('permanent');
+ action.html(' <span>'+ escapeHTML(t('core', 'Share'))+'</span>').prepend(img);
+ }
+ if (hasLink) {
+ image = OC.imagePath('core', 'actions/public');
+ }
+ img.attr('src', image);
+ },
loadItem:function(itemType, itemSource) {
var data = '';
var checkReshare = true;
@@ -319,6 +370,7 @@ OC.Share={
dropDownEl = dropDownEl.appendTo(appendTo);
// Reset item shares
OC.Share.itemShares = [];
+ OC.Share.currentShares = {};
if (data.shares) {
$.each(data.shares, function(index, share) {
if (share.share_type == OC.Share.SHARE_TYPE_LINK) {
@@ -392,6 +444,7 @@ OC.Share={
OC.Share.share(itemType, itemSource, shareType, shareWith, permissions, itemSourceName, expirationDate, function() {
OC.Share.addShareWith(shareType, shareWith, selected.item.label, permissions, possiblePermissions);
$('#shareWith').val('');
+ $('#dropdown').trigger(new $.Event('sharesChanged', {shares: OC.Share.currentShares}));
OC.Share.updateIcon(itemType, itemSource);
});
return false;
@@ -448,6 +501,7 @@ OC.Share={
$('#shareWith').focus();
},
hideDropDown:function(callback) {
+ OC.Share.currentShares = null;
$('#dropdown').hide('blind', function() {
OC.Share.droppedDown = false;
$('#dropdown').remove();
@@ -460,6 +514,12 @@ OC.Share={
});
},
addShareWith:function(shareType, shareWith, shareWithDisplayName, permissions, possiblePermissions, mailSend, collection) {
+ var shareItem = {
+ share_type: shareType,
+ share_with: shareWith,
+ share_with_displayname: shareWithDisplayName,
+ permissions: permissions
+ };
if (shareType === 1) {
shareWithDisplayName = shareWithDisplayName + " (" + t('core', 'group') + ')';
}
@@ -538,6 +598,10 @@ OC.Share={
html.find('.cruds').before(showCrudsButton);
}
$('#expiration').show();
+ if (!OC.Share.currentShares[shareType]) {
+ OC.Share.currentShares[shareType] = [];
+ }
+ OC.Share.currentShares[shareType].push(shareItem);
}
},
showLink:function(token, password, itemSource) {
@@ -673,6 +737,9 @@ $(document).ready(function() {
$li.remove();
var index = OC.Share.itemShares[shareType].indexOf(shareWith);
OC.Share.itemShares[shareType].splice(index, 1);
+ // updated list of shares
+ OC.Share.currentShares[shareType].splice(index, 1);
+ $('#dropdown').trigger(new $.Event('sharesChanged', {shares: OC.Share.currentShares}));
OC.Share.updateIcon(itemType, itemSource);
if (typeof OC.Share.statuses[itemSource] === 'undefined') {
$('#expiration').hide('blind');
@@ -731,6 +798,7 @@ $(document).ready(function() {
if (oc_appconfig.core.enforcePasswordForPublicLink === false) {
OC.Share.share(itemType, itemSource, OC.Share.SHARE_TYPE_LINK, '', OC.PERMISSION_READ, itemSourceName, expirationDate, function(data) {
OC.Share.showLink(data.token, null, itemSource);
+ $('#dropdown').trigger(new $.Event('sharesChanged', {shares: OC.Share.currentShares}));
OC.Share.updateIcon(itemType, itemSource);
});
} else {
@@ -743,6 +811,7 @@ $(document).ready(function() {
if ($('#linkText').val() !== '') {
OC.Share.unshare(itemType, itemSource, OC.Share.SHARE_TYPE_LINK, '', function() {
OC.Share.itemShares[OC.Share.SHARE_TYPE_LINK] = false;
+ $('#dropdown').trigger(new $.Event('sharesChanged', {shares: OC.Share.currentShares}));
OC.Share.updateIcon(itemType, itemSource);
if (typeof OC.Share.statuses[itemSource] === 'undefined') {
$('#expiration').hide('blind');
diff --git a/core/js/tests/specs/shareSpec.js b/core/js/tests/specs/shareSpec.js
index a487b71fdbb..458bc41b6a1 100644
--- a/core/js/tests/specs/shareSpec.js
+++ b/core/js/tests/specs/shareSpec.js
@@ -26,13 +26,17 @@ describe('OC.Share tests', function() {
var oldAppConfig;
var loadItemStub;
var autocompleteStub;
+
beforeEach(function() {
$('#testArea').append($('<div id="shareContainer"></div>'));
+ // horrible parameters
+ $('#testArea').append('<input id="allowShareWithLink" type="hidden" value="yes">');
$container = $('#shareContainer');
/* jshint camelcase:false */
- oldAppConfig = oc_appconfig.core;
- loadItemStub = sinon.stub(OC.Share, 'loadItem');
+ oldAppConfig = _.extend({}, oc_appconfig.core);
+ oc_appconfig.core.enforcePasswordForPublicLink = false;
+ loadItemStub = sinon.stub(OC.Share, 'loadItem');
loadItemStub.returns({
reshare: [],
shares: []
@@ -89,9 +93,133 @@ describe('OC.Share tests', function() {
oc_appconfig.core.defaultExpireDate = '';
// TODO: expect that default date was NOT set
});
- // TODO: test password field visibility (whenever enforced or not)
- // TODO: check link share field visibility based on whether it is allowed
- // TODO: check public upload visibility based on config
+ describe('Share with link', function() {
+ // TODO: test ajax calls
+ // TODO: test password field visibility (whenever enforced or not)
+ // TODO: check public upload visibility based on config
+ it('shows share with link checkbox when allowed', function() {
+ $('#allowShareWithLink').val('yes');
+ OC.Share.showDropDown(
+ 'file',
+ 123,
+ $container,
+ 'http://localhost/dummylink',
+ 31,
+ 'shared_file_name.txt'
+ );
+ expect($('#dropdown #linkCheckbox').length).toEqual(1);
+ });
+ it('does not show share with link checkbox when not allowed', function() {
+ $('#allowShareWithLink').val('no');
+ OC.Share.showDropDown(
+ 'file',
+ 123,
+ $container,
+ 'http://localhost/dummylink',
+ 31,
+ 'shared_file_name.txt'
+ );
+ expect($('#dropdown #linkCheckbox').length).toEqual(0);
+ });
+ });
+ describe('"sharesChanged" event', function() {
+ var autocompleteOptions;
+ var handler;
+ beforeEach(function() {
+ handler = sinon.stub();
+ loadItemStub.returns({
+ reshare: [],
+ shares: [{
+ id: 100,
+ item_source: 123,
+ permissions: 31,
+ share_type: OC.Share.SHARE_TYPE_USER,
+ share_with: 'user1',
+ share_with_displayname: 'User One'
+ }]
+ });
+ OC.Share.showDropDown(
+ 'file',
+ 123,
+ $container,
+ 'http://localhost/dummylink',
+ 31,
+ 'shared_file_name.txt'
+ );
+ $('#dropdown').on('sharesChanged', handler);
+ autocompleteOptions = autocompleteStub.getCall(0).args[0];
+ });
+ afterEach(function() {
+ autocompleteOptions = null;
+ handler = null;
+ });
+ it('triggers "sharesChanged" event when adding shares', function() {
+ // simulate autocomplete selection
+ autocompleteOptions.select(new $.Event('select'), {
+ item: {
+ label: 'User Two',
+ value: {
+ shareType: OC.Share.SHARE_TYPE_USER,
+ shareWith: 'user2'
+ }
+ }
+ });
+ fakeServer.requests[0].respond(
+ 200,
+ { 'Content-Type': 'application/json' },
+ JSON.stringify({status: 'success'})
+ );
+ expect(handler.calledOnce).toEqual(true);
+ var shares = handler.getCall(0).args[0].shares;
+ expect(shares).toBeDefined();
+ expect(shares[OC.Share.SHARE_TYPE_USER][0].share_with_displayname).toEqual('User One');
+ expect(shares[OC.Share.SHARE_TYPE_USER][1].share_with_displayname).toEqual('User Two');
+ expect(shares[OC.Share.SHARE_TYPE_GROUP]).not.toBeDefined();
+ });
+ it('triggers "sharesChanged" event when deleting shares', function() {
+ $('#dropdown .unshare:eq(0)').click();
+ fakeServer.requests[0].respond(
+ 200,
+ { 'Content-Type': 'application/json' },
+ JSON.stringify({status: 'success'})
+ );
+ expect(handler.calledOnce).toEqual(true);
+ var shares = handler.getCall(0).args[0].shares;
+ expect(shares).toBeDefined();
+ expect(shares[OC.Share.SHARE_TYPE_USER]).toEqual([]);
+ expect(shares[OC.Share.SHARE_TYPE_GROUP]).not.toBeDefined();
+ });
+ it('triggers "sharesChanged" event when toggling link share', function() {
+ // simulate autocomplete selection
+ $('#dropdown #linkCheckbox').click();
+ fakeServer.requests[0].respond(
+ 200,
+ { 'Content-Type': 'application/json' },
+ JSON.stringify({status: 'success', data: { token: 'abc' }})
+ );
+ expect(handler.calledOnce).toEqual(true);
+ var shares = handler.getCall(0).args[0].shares;
+ expect(shares).toBeDefined();
+ expect(shares[OC.Share.SHARE_TYPE_USER][0].share_with_displayname).toEqual('User One');
+ expect(shares[OC.Share.SHARE_TYPE_GROUP]).not.toBeDefined();
+
+ handler.reset();
+
+ // uncheck checkbox
+ $('#dropdown #linkCheckbox').click();
+ fakeServer.requests[1].respond(
+ 200,
+ { 'Content-Type': 'application/json' },
+ JSON.stringify({status: 'success'})
+ );
+
+ expect(handler.calledOnce).toEqual(true);
+ shares = handler.getCall(0).args[0].shares;
+ expect(shares).toBeDefined();
+ expect(shares[OC.Share.SHARE_TYPE_USER][0].share_with_displayname).toEqual('User One');
+ expect(shares[OC.Share.SHARE_TYPE_GROUP]).not.toBeDefined();
+ });
+ });
});
});
diff --git a/lib/private/files/cache/changepropagator.php b/lib/private/files/cache/changepropagator.php
new file mode 100644
index 00000000000..30f2e675e2e
--- /dev/null
+++ b/lib/private/files/cache/changepropagator.php
@@ -0,0 +1,98 @@
+<?php
+/**
+ * Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+namespace OC\Files\Cache;
+
+/**
+ * Propagates changes in etag and mtime up the filesystem tree
+ *
+ * @package OC\Files\Cache
+ */
+class ChangePropagator {
+ /**
+ * @var string[]
+ */
+ protected $changedFiles = array();
+
+ /**
+ * @var \OC\Files\View
+ */
+ protected $view;
+
+ /**
+ * @param \OC\Files\View $view
+ */
+ public function __construct(\OC\Files\View $view) {
+ $this->view = $view;
+ }
+
+ public function addChange($path) {
+ $this->changedFiles[] = $path;
+ }
+
+ public function getChanges() {
+ return $this->changedFiles;
+ }
+
+ /**
+ * propagate the registered changes to their parent folders
+ *
+ * @param int $time (optional) the mtime to set for the folders, if not set the current time is used
+ */
+ public function propagateChanges($time = null) {
+ $parents = $this->getAllParents();
+ $this->changedFiles = array();
+ if (!$time) {
+ $time = time();
+ }
+ foreach ($parents as $parent) {
+ /**
+ * @var \OC\Files\Storage\Storage $storage
+ * @var string $internalPath
+ */
+
+ list($storage, $internalPath) = $this->view->resolvePath($parent);
+ $cache = $storage->getCache();
+ $id = $cache->getId($internalPath);
+ $cache->update($id, array('mtime' => $time, 'etag' => $storage->getETag($internalPath)));
+ }
+ }
+
+ /**
+ * @return string[]
+ */
+ public function getAllParents() {
+ $parents = array();
+ foreach ($this->getChanges() as $path) {
+ $parents = array_values(array_unique(array_merge($parents, $this->getParents($path))));
+ }
+ return $parents;
+ }
+
+ /**
+ * get all parent folders of $path
+ *
+ * @param string $path
+ * @return string[]
+ */
+ protected function getParents($path) {
+ $parts = explode('/', $path);
+
+ // remove the singe file
+ array_pop($parts);
+ $result = array('/');
+ $resultPath = '';
+ foreach ($parts as $part) {
+ if ($part) {
+ $resultPath .= '/' . $part;
+ $result[] = $resultPath;
+ }
+ }
+ return $result;
+ }
+}
diff --git a/lib/private/files/cache/homecache.php b/lib/private/files/cache/homecache.php
index f61769f0b9b..06ae62015a5 100644
--- a/lib/private/files/cache/homecache.php
+++ b/lib/private/files/cache/homecache.php
@@ -17,7 +17,7 @@ class HomeCache extends Cache {
* @return int
*/
public function calculateFolderSize($path, $entry = null) {
- if ($path !== '/' and $path !== '' and $path !== 'files' and $path !== 'files_trashbin') {
+ if ($path !== '/' and $path !== '' and $path !== 'files' and $path !== 'files_trashbin' and $path !== 'files_versions') {
return parent::calculateFolderSize($path, $entry);
} elseif ($path === '' or $path === '/') {
// since the size of / isn't used (the size of /files is used instead) there is no use in calculating it
diff --git a/lib/private/files/cache/scanner.php b/lib/private/files/cache/scanner.php
index b97070fcdf0..6cb9807f5a7 100644
--- a/lib/private/files/cache/scanner.php
+++ b/lib/private/files/cache/scanner.php
@@ -124,10 +124,8 @@ class Scanner extends BasicEmitter {
// prevent empty etag
if (empty($cacheData['etag'])) {
$etag = $data['etag'];
- $propagateETagChange = true;
} else {
$etag = $cacheData['etag'];
- $propagateETagChange = false;
}
// only reuse data if the file hasn't explicitly changed
if (isset($data['storage_mtime']) && isset($cacheData['storage_mtime']) && $data['storage_mtime'] === $cacheData['storage_mtime']) {
@@ -136,22 +134,6 @@ class Scanner extends BasicEmitter {
}
if ($reuseExisting & self::REUSE_ETAG) {
$data['etag'] = $etag;
- if ($propagateETagChange) {
- $parent = $file;
- while ($parent !== '') {
- $parent = dirname($parent);
- if ($parent === '.') {
- $parent = '';
- }
- $parentCacheData = $this->cache->get($parent);
- \OC_Hook::emit('Scanner', 'updateCache', array('file' => $file, 'data' => $data));
- if($this->cacheActive) {
- $this->cache->update($parentCacheData['fileid'], array(
- 'etag' => $this->storage->getETag($parent),
- ));
- }
- }
- }
}
}
// Only update metadata that has changed
@@ -166,24 +148,53 @@ class Scanner extends BasicEmitter {
}
}
if (!empty($newData)) {
- \OC_Hook::emit('Scanner', 'addToCache', array('file' => $file, 'data' => $newData));
- if($this->cacheActive) {
- $data['fileid'] = $this->cache->put($file, $newData);
- }
+ $data['fileid'] = $this->addToCache($file, $newData);
$this->emit('\OC\Files\Cache\Scanner', 'postScanFile', array($file, $this->storageId));
\OC_Hook::emit('\OC\Files\Cache\Scanner', 'post_scan_file', array('path' => $file, 'storage' => $this->storageId));
}
} else {
- \OC_Hook::emit('Scanner', 'removeFromCache', array('file' => $file));
- if($this->cacheActive) {
- $this->cache->remove($file);
- }
+ $this->removeFromCache($file);
}
return $data;
}
return null;
}
+ protected function removeFromCache($path) {
+ \OC_Hook::emit('Scanner', 'removeFromCache', array('file' => $path));
+ $this->emit('\OC\Files\Cache\Scanner', 'removeFromCache', array($path));
+ if ($this->cacheActive) {
+ $this->cache->remove($path);
+ }
+ }
+
+ /**
+ * @param string $path
+ * @param array $data
+ * @return int the id of the added file
+ */
+ protected function addToCache($path, $data) {
+ \OC_Hook::emit('Scanner', 'addToCache', array('file' => $path, 'data' => $data));
+ $this->emit('\OC\Files\Cache\Scanner', 'addToCache', array($path, $this->storageId, $data));
+ if ($this->cacheActive) {
+ return $this->cache->put($path, $data);
+ } else {
+ return -1;
+ }
+ }
+
+ /**
+ * @param string $path
+ * @param array $data
+ */
+ protected function updateCache($path, $data) {
+ \OC_Hook::emit('Scanner', 'addToCache', array('file' => $path, 'data' => $data));
+ $this->emit('\OC\Files\Cache\Scanner', 'updateCache', array($path, $this->storageId, $data));
+ if ($this->cacheActive) {
+ $this->cache->put($path, $data);
+ }
+ }
+
/**
* scan a folder and all it's children
*
@@ -236,18 +247,15 @@ class Scanner extends BasicEmitter {
try {
$data = $this->scanFile($child, $reuse, true);
if ($data) {
- if ($data['size'] === -1) {
- if ($recursive === self::SCAN_RECURSIVE) {
- $childQueue[] = $child;
- } else {
- $size = -1;
- }
+ if ($data['mimetype'] === 'httpd/unix-directory' and $recursive === self::SCAN_RECURSIVE) {
+ $childQueue[] = $child;
+ } else if ($data['size'] === -1) {
+ $size = -1;
} else if ($size !== -1) {
$size += $data['size'];
}
}
- }
- catch (\Doctrine\DBAL\DBALException $ex){
+ } catch (\Doctrine\DBAL\DBALException $ex) {
// might happen if inserting duplicate while a scanning
// process is running in parallel
// log and ignore
@@ -260,13 +268,10 @@ class Scanner extends BasicEmitter {
$removedChildren = \array_diff($existingChildren, $newChildren);
foreach ($removedChildren as $childName) {
$child = ($path) ? $path . '/' . $childName : $childName;
- \OC_Hook::emit('Scanner', 'removeFromCache', array('file' => $child));
- if($this->cacheActive) {
- $this->cache->remove($child);
- }
+ $this->removeFromCache($child);
}
\OC_DB::commit();
- if ($exceptionOccurred){
+ if ($exceptionOccurred) {
// It might happen that the parallel scan process has already
// inserted mimetypes but those weren't available yet inside the transaction
// To make sure to have the updated mime types in such cases,
@@ -278,15 +283,11 @@ class Scanner extends BasicEmitter {
$childSize = $this->scanChildren($child, self::SCAN_RECURSIVE, $reuse);
if ($childSize === -1) {
$size = -1;
- } else {
+ } else if ($size !== -1) {
$size += $childSize;
}
}
- $newData = array('size' => $size);
- \OC_Hook::emit('Scanner', 'addToCache', array('file' => $child, 'data' => $newData));
- if($this->cacheActive) {
- $this->cache->put($path, $newData);
- }
+ $this->updateCache($path, array('size' => $size));
}
$this->emit('\OC\Files\Cache\Scanner', 'postScanFolder', array($path, $this->storageId));
return $size;
@@ -296,6 +297,7 @@ class Scanner extends BasicEmitter {
* check if the file should be ignored when scanning
* NOTE: files with a '.part' extension are ignored as well!
* prevents unfinished put requests to be scanned
+ *
* @param string $file
* @return boolean
*/
@@ -314,7 +316,7 @@ class Scanner extends BasicEmitter {
while (($path = $this->cache->getIncomplete()) !== false && $path !== $lastPath) {
$this->scan($path, self::SCAN_RECURSIVE, self::REUSE_ETAG);
\OC_Hook::emit('Scanner', 'correctFolderSize', array('path' => $path));
- if($this->cacheActive) {
+ if ($this->cacheActive) {
$this->cache->correctFolderSize($path);
}
$lastPath = $path;
@@ -323,6 +325,7 @@ class Scanner extends BasicEmitter {
/**
* Set whether the cache is affected by scan operations
+ *
* @param boolean $active The active state of the cache
*/
public function setCacheActive($active) {
diff --git a/lib/private/files/utils/scanner.php b/lib/private/files/utils/scanner.php
index a802a8fcb8b..1bb3e694c96 100644
--- a/lib/private/files/utils/scanner.php
+++ b/lib/private/files/utils/scanner.php
@@ -8,6 +8,8 @@
namespace OC\Files\Utils;
+use OC\Files\View;
+use OC\Files\Cache\ChangePropagator;
use OC\Files\Filesystem;
use OC\Hooks\PublicEmitter;
@@ -27,10 +29,16 @@ class Scanner extends PublicEmitter {
private $user;
/**
+ * @var \OC\Files\Cache\ChangePropagator
+ */
+ protected $propagator;
+
+ /**
* @param string $user
*/
public function __construct($user) {
$this->user = $user;
+ $this->propagator = new ChangePropagator(new View(''));
}
/**
@@ -67,6 +75,15 @@ class Scanner extends PublicEmitter {
$scanner->listen('\OC\Files\Cache\Scanner', 'scanFolder', function ($path) use ($mount, $emitter) {
$emitter->emit('\OC\Files\Utils\Scanner', 'scanFolder', array($mount->getMountPoint() . $path));
});
+
+ // propagate etag and mtimes when files are changed or removed
+ $propagator = $this->propagator;
+ $propagatorListener = function ($path) use ($mount, $propagator) {
+ $fullPath = Filesystem::normalizePath($mount->getMountPoint() . $path);
+ $propagator->addChange($fullPath);
+ };
+ $scanner->listen('\OC\Files\Cache\Scanner', 'addToCache', $propagatorListener);
+ $scanner->listen('\OC\Files\Cache\Scanner', 'removeFromCache', $propagatorListener);
}
/**
@@ -82,6 +99,7 @@ class Scanner extends PublicEmitter {
$this->attachListener($mount);
$scanner->backgroundScan();
}
+ $this->propagator->propagateChanges(time());
}
/**
@@ -95,8 +113,9 @@ class Scanner extends PublicEmitter {
}
$scanner = $mount->getStorage()->getScanner();
$this->attachListener($mount);
- $scanner->scan('', \OC\Files\Cache\Scanner::SCAN_RECURSIVE, \OC\Files\Cache\Scanner::REUSE_ETAG);
+ $scanner->scan('', \OC\Files\Cache\Scanner::SCAN_RECURSIVE, \OC\Files\Cache\Scanner::REUSE_ETAG | \OC\Files\Cache\Scanner::REUSE_SIZE);
}
+ $this->propagator->propagateChanges(time());
}
}
diff --git a/settings/css/settings.css b/settings/css/settings.css
index b67c88a9c8f..3a6e40ad361 100644
--- a/settings/css/settings.css
+++ b/settings/css/settings.css
@@ -20,10 +20,13 @@ input#openid, input#webdav { width:20em; }
}
/* Sync clients */
-.clientsbox { margin:12px; }
-.clientsbox h1 { font-size:40px; font-weight:bold; margin:50px 0 20px; }
-.clientsbox h2 { font-size:20px; font-weight:bold; margin:35px 0 10px; }
-.clientsbox .center { margin-top:10px; }
+.clientsbox h2 {
+ font-size: 20px;
+ margin: 35px 0 10px;
+}
+.clientsbox .center {
+ margin-top: 10px;
+}
#passworderror { display:none; }
#passwordchanged { display:none; }
diff --git a/tests/karma.config.js b/tests/karma.config.js
index 846e8f7be91..1f903f58210 100644
--- a/tests/karma.config.js
+++ b/tests/karma.config.js
@@ -52,7 +52,8 @@ module.exports = function(config) {
// only test these files, others are not ready and mess
// up with the global namespace/classes/state
'apps/files_sharing/js/app.js',
- 'apps/files_sharing/js/sharedfilelist.js'
+ 'apps/files_sharing/js/sharedfilelist.js',
+ 'apps/files_sharing/js/share.js'
],
testFiles: ['apps/files_sharing/tests/js/*.js']
}];
diff --git a/tests/lib/files/cache/changepropagator.php b/tests/lib/files/cache/changepropagator.php
new file mode 100644
index 00000000000..9beff27d50e
--- /dev/null
+++ b/tests/lib/files/cache/changepropagator.php
@@ -0,0 +1,72 @@
+<?php
+/**
+ * Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+namespace Test\Files\Cache;
+
+use OC\Files\Filesystem;
+use OC\Files\Storage\Temporary;
+use OC\Files\View;
+
+class ChangePropagator extends \PHPUnit_Framework_TestCase {
+ /**
+ * @var \OC\Files\Cache\ChangePropagator
+ */
+ private $propagator;
+
+ /**
+ * @var \OC\Files\View
+ */
+ private $view;
+
+ public function setUp() {
+ $storage = new Temporary(array());
+ $root = '/' . uniqid();
+ Filesystem::mount($storage, array(), $root);
+ $this->view = new View($root);
+ $this->propagator = new \OC\Files\Cache\ChangePropagator($this->view);
+ }
+
+ public function testGetParentsSingle() {
+ $this->propagator->addChange('/foo/bar/asd');
+ $this->assertEquals(array('/', '/foo', '/foo/bar'), $this->propagator->getAllParents());
+ }
+
+ public function testGetParentsMultiple() {
+ $this->propagator->addChange('/foo/bar/asd');
+ $this->propagator->addChange('/foo/qwerty');
+ $this->propagator->addChange('/foo/asd/bar');
+ $this->assertEquals(array('/', '/foo', '/foo/bar', '/foo/asd'), $this->propagator->getAllParents());
+ }
+
+ public function testSinglePropagate() {
+ $this->view->mkdir('/foo');
+ $this->view->mkdir('/foo/bar');
+ $this->view->file_put_contents('/foo/bar/sad.txt', 'qwerty');
+
+ $oldInfo1 = $this->view->getFileInfo('/');
+ $oldInfo2 = $this->view->getFileInfo('/foo');
+ $oldInfo3 = $this->view->getFileInfo('/foo/bar');
+
+ $time = time() + 50;
+
+ $this->propagator->addChange('/foo/bar/sad.txt');
+ $this->propagator->propagateChanges($time);
+
+ $newInfo1 = $this->view->getFileInfo('/');
+ $newInfo2 = $this->view->getFileInfo('/foo');
+ $newInfo3 = $this->view->getFileInfo('/foo/bar');
+
+ $this->assertEquals($newInfo1->getMTime(), $time);
+ $this->assertEquals($newInfo2->getMTime(), $time);
+ $this->assertEquals($newInfo3->getMTime(), $time);
+
+ $this->assertNotEquals($oldInfo1->getEtag(), $newInfo1->getEtag());
+ $this->assertNotEquals($oldInfo2->getEtag(), $newInfo2->getEtag());
+ $this->assertNotEquals($oldInfo3->getEtag(), $newInfo3->getEtag());
+ }
+}
diff --git a/tests/lib/files/cache/scanner.php b/tests/lib/files/cache/scanner.php
index fb06f2dff3d..56c7b65bb08 100644
--- a/tests/lib/files/cache/scanner.php
+++ b/tests/lib/files/cache/scanner.php
@@ -234,13 +234,5 @@ class Scanner extends \PHPUnit_Framework_TestCase {
$newData0 = $this->cache->get('folder/bar.txt');
$this->assertInternalType('string', $newData0['etag']);
$this->assertNotEmpty($newData0['etag']);
-
- $newData1 = $this->cache->get('folder');
- $this->assertInternalType('string', $newData1['etag']);
- $this->assertNotSame($data1['etag'], $newData1['etag']);
-
- $newData2 = $this->cache->get('');
- $this->assertInternalType('string', $newData2['etag']);
- $this->assertNotSame($data2['etag'], $newData2['etag']);
}
}
diff --git a/tests/lib/files/utils/scanner.php b/tests/lib/files/utils/scanner.php
index a021d215ae5..159a2a48677 100644
--- a/tests/lib/files/utils/scanner.php
+++ b/tests/lib/files/utils/scanner.php
@@ -8,6 +8,7 @@
namespace Test\Files\Utils;
+use OC\Files\Filesystem;
use OC\Files\Mount\Mount;
use OC\Files\Storage\Temporary;
@@ -27,12 +28,21 @@ class TestScanner extends \OC\Files\Utils\Scanner {
protected function getMounts($dir) {
return $this->mounts;
}
+
+ public function getPropagator() {
+ return $this->propagator;
+ }
+
+ public function setPropagator($propagator) {
+ $this->propagator = $propagator;
+ }
}
class Scanner extends \PHPUnit_Framework_TestCase {
public function testReuseExistingRoot() {
$storage = new Temporary(array());
$mount = new Mount($storage, '');
+ Filesystem::getMountManager()->addMount($mount);
$cache = $storage->getCache();
$storage->mkdir('folder');
@@ -54,6 +64,7 @@ class Scanner extends \PHPUnit_Framework_TestCase {
public function testReuseExistingFile() {
$storage = new Temporary(array());
$mount = new Mount($storage, '');
+ Filesystem::getMountManager()->addMount($mount);
$cache = $storage->getCache();
$storage->mkdir('folder');
@@ -71,4 +82,57 @@ class Scanner extends \PHPUnit_Framework_TestCase {
$new = $cache->get('folder/bar.txt');
$this->assertEquals($old, $new);
}
+
+ public function testChangePropagator() {
+ /**
+ * @var \OC\Files\Cache\ChangePropagator $propagator
+ */
+ $propagator = $this->getMock('\OC\Files\Cache\ChangePropagator', array('propagateChanges'), array(), '', false);
+
+ $storage = new Temporary(array());
+ $mount = new Mount($storage, '/foo');
+ Filesystem::getMountManager()->addMount($mount);
+ $cache = $storage->getCache();
+
+ $storage->mkdir('folder');
+ $storage->file_put_contents('foo.txt', 'qwerty');
+ $storage->file_put_contents('folder/bar.txt', 'qwerty');
+
+ $scanner = new TestScanner('');
+ $originalPropagator = $scanner->getPropagator();
+ $scanner->setPropagator($propagator);
+ $scanner->addMount($mount);
+
+ $scanner->scan('');
+
+ $changes = $propagator->getChanges();
+ $parents = $propagator->getAllParents();
+ sort($changes);
+ sort($parents);
+ $this->assertEquals(array('/foo', '/foo/folder', '/foo/folder/bar.txt', '/foo/foo.txt'), $changes);
+ $this->assertEquals(array('/', '/foo', '/foo/folder'), $parents);
+
+ $cache->put('foo.txt', array('mtime' => time() - 50));
+
+ $propagator = $this->getMock('\OC\Files\Cache\ChangePropagator', array('propagateChanges'), array(), '', false);
+ $scanner->setPropagator($propagator);
+ $storage->file_put_contents('foo.txt', 'asdasd');
+
+ $scanner->scan('');
+
+ $changes = $propagator->getChanges();
+ $parents = $propagator->getAllParents();
+ $this->assertEquals(array('/foo/foo.txt'), $changes);
+ $this->assertEquals(array('/', '/foo'), $parents);
+
+ $scanner->setPropagator($originalPropagator);
+
+ $oldInfo = $cache->get('');
+ $cache->put('foo.txt', array('mtime' => time() - 70));
+ $storage->file_put_contents('foo.txt', 'asdasd');
+
+ $scanner->scan('');
+ $newInfo = $cache->get('');
+ $this->assertNotEquals($oldInfo['etag'], $newInfo['etag']);
+ }
}
diff --git a/tests/lib/files/view.php b/tests/lib/files/view.php
index b5e4d792350..1d8e729fb7b 100644
--- a/tests/lib/files/view.php
+++ b/tests/lib/files/view.php
@@ -52,14 +52,18 @@ class View extends \PHPUnit_Framework_TestCase {
$storage1 = $this->getTestStorage();
$storage2 = $this->getTestStorage();
$storage3 = $this->getTestStorage();
- \OC\Files\Filesystem::mount($storage1, array(), '/');
- \OC\Files\Filesystem::mount($storage2, array(), '/substorage');
- \OC\Files\Filesystem::mount($storage3, array(), '/folder/anotherstorage');
+ $root = '/' . uniqid();
+ \OC\Files\Filesystem::mount($storage1, array(), $root . '/');
+ \OC\Files\Filesystem::mount($storage2, array(), $root . '/substorage');
+ \OC\Files\Filesystem::mount($storage3, array(), $root . '/folder/anotherstorage');
$textSize = strlen("dummy file data\n");
$imageSize = filesize(\OC::$SERVERROOT . '/core/img/logo.png');
$storageSize = $textSize * 2 + $imageSize;
- $rootView = new \OC\Files\View('');
+ $storageInfo = $storage3->getCache()->get('');
+ $this->assertEquals($storageSize, $storageInfo['size']);
+
+ $rootView = new \OC\Files\View($root);
$cachedData = $rootView->getFileInfo('/foo.txt');
$this->assertEquals($textSize, $cachedData['size']);
@@ -110,7 +114,7 @@ class View extends \PHPUnit_Framework_TestCase {
$this->assertEquals('foo.png', $folderData[1]['name']);
$this->assertEquals('foo.txt', $folderData[2]['name']);
- $folderView = new \OC\Files\View('/folder');
+ $folderView = new \OC\Files\View($root . '/folder');
$this->assertEquals($rootView->getFileInfo('/folder'), $folderView->getFileInfo('/'));
$cachedData = $rootView->getFileInfo('/foo.txt');
@@ -580,9 +584,9 @@ class View extends \PHPUnit_Framework_TestCase {
$longPath = '';
// 4000 is the maximum path length in file_cache.path
$folderName = 'abcdefghijklmnopqrstuvwxyz012345678901234567890123456789';
- $depth = (4000/57);
- foreach (range(0, $depth-1) as $i) {
- $longPath .= '/'.$folderName;
+ $depth = (4000 / 57);
+ foreach (range(0, $depth - 1) as $i) {
+ $longPath .= '/' . $folderName;
$result = $rootView->mkdir($longPath);
$this->assertTrue($result, "mkdir failed on $i - path length: " . strlen($longPath));
@@ -598,7 +602,7 @@ class View extends \PHPUnit_Framework_TestCase {
$scanner->scan('');
$longPath = $folderName;
- foreach (range(0, $depth-1) as $i) {
+ foreach (range(0, $depth - 1) as $i) {
$cachedFolder = $cache->get($longPath);
$this->assertTrue(is_array($cachedFolder), "No cache entry for folder at $i");
$this->assertEquals($folderName, $cachedFolder['name'], "Wrong cache entry for folder at $i");
@@ -652,14 +656,14 @@ class View extends \PHPUnit_Framework_TestCase {
* @dataProvider tooLongPathDataProvider
* @expectedException \OCP\Files\InvalidPathException
*/
- public function testTooLongPath($operation, $param0 = NULL) {
+ public function testTooLongPath($operation, $param0 = null) {
$longPath = '';
// 4000 is the maximum path length in file_cache.path
$folderName = 'abcdefghijklmnopqrstuvwxyz012345678901234567890123456789';
- $depth = (4000/57);
- foreach (range(0, $depth+1) as $i) {
- $longPath .= '/'.$folderName;
+ $depth = (4000 / 57);
+ foreach (range(0, $depth + 1) as $i) {
+ $longPath .= '/' . $folderName;
}
$storage = new \OC\Files\Storage\Temporary(array());