aboutsummaryrefslogtreecommitdiffstats
path: root/core/js
diff options
context:
space:
mode:
Diffstat (limited to 'core/js')
-rw-r--r--core/js/files/client.js36
-rw-r--r--core/js/js.js46
-rw-r--r--core/js/lostpassword.js2
-rw-r--r--core/js/mimetypelist.js1
-rw-r--r--core/js/sharedialoglinkshareview.js39
-rw-r--r--core/js/sharedialogresharerinfoview.js2
-rw-r--r--core/js/sharedialogshareelistview.js196
-rw-r--r--core/js/shareitemmodel.js50
-rw-r--r--core/js/systemtags/systemtagmodel.js21
-rw-r--r--core/js/systemtags/systemtagsinputfield.js7
-rw-r--r--core/js/tests/specs/coreSpec.js34
-rw-r--r--core/js/tests/specs/l10nSpec.js7
-rw-r--r--core/js/tests/specs/sharedialogviewSpec.js23
-rw-r--r--core/js/tests/specs/shareitemmodelSpec.js5
-rw-r--r--core/js/tests/specs/systemtags/systemtagsinputfieldSpec.js87
-rw-r--r--core/js/update.js2
16 files changed, 449 insertions, 109 deletions
diff --git a/core/js/files/client.js b/core/js/files/client.js
index 87559b2084c..cde3afde9d7 100644
--- a/core/js/files/client.js
+++ b/core/js/files/client.js
@@ -31,9 +31,9 @@
this._root = this._root.substr(0, this._root.length - 1);
}
- var url = 'http://';
+ var url = Client.PROTOCOL_HTTP + '://';
if (options.useHTTPS) {
- url = 'https://';
+ url = Client.PROTOCOL_HTTPS + '://';
}
url += options.host + this._root;
@@ -64,6 +64,19 @@
Client.NS_OWNCLOUD = 'http://owncloud.org/ns';
Client.NS_NEXTCLOUD = 'http://nextcloud.org/ns';
Client.NS_DAV = 'DAV:';
+
+ Client.PROPERTY_GETLASTMODIFIED = '{' + Client.NS_DAV + '}getlastmodified';
+ Client.PROPERTY_GETETAG = '{' + Client.NS_DAV + '}getetag';
+ Client.PROPERTY_GETCONTENTTYPE = '{' + Client.NS_DAV + '}getcontenttype';
+ Client.PROPERTY_RESOURCETYPE = '{' + Client.NS_DAV + '}resourcetype';
+ Client.PROPERTY_INTERNAL_FILEID = '{' + Client.NS_OWNCLOUD + '}fileid';
+ Client.PROPERTY_PERMISSIONS = '{' + Client.NS_OWNCLOUD + '}permissions';
+ Client.PROPERTY_SIZE = '{' + Client.NS_OWNCLOUD + '}size';
+ Client.PROPERTY_GETCONTENTLENGTH = '{' + Client.NS_DAV + '}getcontentlength';
+
+ Client.PROTOCOL_HTTP = 'http';
+ Client.PROTOCOL_HTTPS = 'https';
+
Client._PROPFIND_PROPERTIES = [
/**
* Modified time
@@ -259,23 +272,23 @@
var props = response.propStat[0].properties;
var data = {
- id: props['{' + Client.NS_OWNCLOUD + '}fileid'],
+ id: props[Client.PROPERTY_INTERNAL_FILEID],
path: OC.dirname(path) || '/',
name: OC.basename(path),
- mtime: (new Date(props['{' + Client.NS_DAV + '}getlastmodified'])).getTime()
+ mtime: (new Date(props[Client.PROPERTY_GETLASTMODIFIED])).getTime()
};
- var etagProp = props['{' + Client.NS_DAV + '}getetag'];
+ var etagProp = props[Client.PROPERTY_GETETAG];
if (!_.isUndefined(etagProp)) {
data.etag = this._parseEtag(etagProp);
}
- var sizeProp = props['{' + Client.NS_DAV + '}getcontentlength'];
+ var sizeProp = props[Client.PROPERTY_GETCONTENTLENGTH];
if (!_.isUndefined(sizeProp)) {
data.size = parseInt(sizeProp, 10);
}
- sizeProp = props['{' + Client.NS_OWNCLOUD + '}size'];
+ sizeProp = props[Client.PROPERTY_SIZE];
if (!_.isUndefined(sizeProp)) {
data.size = parseInt(sizeProp, 10);
}
@@ -294,12 +307,12 @@
data.isFavorite = false;
}
- var contentType = props['{' + Client.NS_DAV + '}getcontenttype'];
+ var contentType = props[Client.PROPERTY_GETCONTENTTYPE];
if (!_.isUndefined(contentType)) {
data.mimetype = contentType;
}
- var resType = props['{' + Client.NS_DAV + '}resourcetype'];
+ var resType = props[Client.PROPERTY_RESOURCETYPE];
var isFile = true;
if (!data.mimetype && resType) {
var xmlvalue = resType[0];
@@ -310,7 +323,7 @@
}
data.permissions = OC.PERMISSION_READ;
- var permissionProp = props['{' + Client.NS_OWNCLOUD + '}permissions'];
+ var permissionProp = props[Client.PROPERTY_PERMISSIONS];
if (!_.isUndefined(permissionProp)) {
var permString = permissionProp || '';
data.mountType = null;
@@ -752,7 +765,7 @@
},
/**
- * Returns the password
+ * Returns the password
*
* @since 11.0.0
* @return {String} password
@@ -816,4 +829,3 @@
OC.Files.Client = Client;
})(OC, OC.Files.FileInfo);
-
diff --git a/core/js/js.js b/core/js/js.js
index 972f0e63144..3651635541a 100644
--- a/core/js/js.js
+++ b/core/js/js.js
@@ -1669,6 +1669,52 @@ OC.Util = {
humanFileSize: humanFileSize,
/**
+ * Returns a file size in bytes from a humanly readable string
+ * Makes 2kB to 2048.
+ * Inspired by computerFileSize in helper.php
+ * @param {string} string file size in human readable format
+ * @return {number} or null if string could not be parsed
+ *
+ *
+ */
+ computerFileSize: function (string) {
+ if (typeof string != 'string') {
+ return null;
+ }
+
+ var s = string.toLowerCase();
+ var bytes = parseFloat(s)
+
+ if (!isNaN(bytes) && isFinite(s)) {
+ return bytes;
+ }
+
+ var bytesArray = {
+ 'b' : 1,
+ 'k' : 1024,
+ 'kb': 1024,
+ 'mb': 1024 * 1024,
+ 'm' : 1024 * 1024,
+ 'gb': 1024 * 1024 * 1024,
+ 'g' : 1024 * 1024 * 1024,
+ 'tb': 1024 * 1024 * 1024 * 1024,
+ 't' : 1024 * 1024 * 1024 * 1024,
+ 'pb': 1024 * 1024 * 1024 * 1024 * 1024,
+ 'p' : 1024 * 1024 * 1024 * 1024 * 1024
+ };
+
+ var matches = s.match(/([kmgtp]?b?)$/i);
+ if (matches[1]) {
+ bytes = bytes * bytesArray[matches[1]];
+ } else {
+ return null;
+ }
+
+ bytes = Math.round(bytes);
+ return bytes;
+ },
+
+ /**
* @param timestamp
* @param format
* @returns {string} timestamp formatted as requested
diff --git a/core/js/lostpassword.js b/core/js/lostpassword.js
index 30d7b98f4e8..6e18dcc1f8b 100644
--- a/core/js/lostpassword.js
+++ b/core/js/lostpassword.js
@@ -4,7 +4,7 @@ OC.Lostpassword = {
sendSuccessMsg : t('core', 'The link to reset your password has been sent to your email. If you do not receive it within a reasonable amount of time, check your spam/junk folders.<br>If it is not there ask your local administrator.'),
- encryptedMsg : t('core', "Your files are encrypted. If you haven't enabled the recovery key, there will be no way to get your data back after your password is reset.<br />If you are not sure what to do, please contact your administrator before you continue. <br />Do you really want to continue?")
+ encryptedMsg : t('core', "Your files are encrypted. There will be no way to get your data back after your password is reset.<br />If you are not sure what to do, please contact your administrator before you continue. <br />Do you really want to continue?")
+ ('<br /><input type="checkbox" id="encrypted-continue" value="Yes" />')
+ '<label for="encrypted-continue">'
+ t('core', 'I know what I\'m doing')
diff --git a/core/js/mimetypelist.js b/core/js/mimetypelist.js
index e1b9dba14af..89558e21086 100644
--- a/core/js/mimetypelist.js
+++ b/core/js/mimetypelist.js
@@ -86,6 +86,7 @@ OC.MimeTypeList={
"text/x-c": "text/code",
"text/x-c++src": "text/code",
"text/x-h": "text/code",
+ "text/x-ldif": "text/code",
"text/x-java-source": "text/code",
"text/x-python": "text/code",
"text/x-shellscript": "text/code",
diff --git a/core/js/sharedialoglinkshareview.js b/core/js/sharedialoglinkshareview.js
index cb553947de6..84a3d18942f 100644
--- a/core/js/sharedialoglinkshareview.js
+++ b/core/js/sharedialoglinkshareview.js
@@ -43,6 +43,13 @@
'</div>' +
' {{/if}}' +
' {{/if}}' +
+ ' {{#if publicEditing}}' +
+ '<div id="allowPublicEditingWrapper">' +
+ ' <span class="icon-loading-small hidden"></span>' +
+ ' <input type="checkbox" value="1" name="allowPublicEditing" id="sharingDialogAllowPublicEditing-{{cid}}" class="checkbox publicEditingCheckbox" {{{publicEditingChecked}}} />' +
+ '<label for="sharingDialogAllowPublicEditing-{{cid}}">{{publicEditingLabel}}</label>' +
+ '</div>' +
+ ' {{/if}}' +
' {{#if showPasswordCheckBox}}' +
'<input type="checkbox" name="showPassword" id="showPassword-{{cid}}" class="checkbox showPasswordCheckbox" {{#if isPasswordSet}}checked="checked"{{/if}} value="1" />' +
'<label for="showPassword-{{cid}}">{{enablePasswordLabel}}</label>' +
@@ -87,6 +94,7 @@
'click .linkCheckbox': 'onLinkCheckBoxChange',
'click .linkText': 'onLinkTextClick',
'change .publicUploadCheckbox': 'onAllowPublicUploadChange',
+ 'change .publicEditingCheckbox': 'onAllowPublicEditingChange',
'change .hideFileListCheckbox': 'onHideFileListChange',
'click .showPasswordCheckbox': 'onShowPasswordClick'
},
@@ -128,7 +136,8 @@
'onLinkTextClick',
'onShowPasswordClick',
'onHideFileListChange',
- 'onAllowPublicUploadChange'
+ 'onAllowPublicUploadChange',
+ 'onAllowPublicEditingChange'
);
var clipboard = new Clipboard('.clipboardButton');
@@ -266,6 +275,20 @@
});
},
+ onAllowPublicEditingChange: function() {
+ var $checkbox = this.$('.publicEditingCheckbox');
+ $checkbox.siblings('.icon-loading-small').removeClass('hidden').addClass('inlineblock');
+
+ var permissions = OC.PERMISSION_READ;
+ if($checkbox.is(':checked')) {
+ permissions = OC.PERMISSION_UPDATE | OC.PERMISSION_READ;
+ }
+
+ this.model.saveLinkShare({
+ permissions: permissions
+ });
+ },
+
onHideFileListChange: function () {
var $checkbox = this.$('.hideFileListCheckbox');
$checkbox.siblings('.icon-loading-small').removeClass('hidden').addClass('inlineblock');
@@ -307,6 +330,12 @@
publicUploadChecked = 'checked="checked"';
}
+ var publicEditingChecked = '';
+ if(this.model.isPublicEditingAllowed()) {
+ publicEditingChecked = 'checked="checked"';
+ }
+
+
var hideFileList = publicUploadChecked;
var hideFileListChecked = '';
@@ -320,6 +349,11 @@
&& ( !this.configModel.get('enforcePasswordForPublicLink')
|| !this.model.get('linkShare').password);
+ var publicEditable =
+ !this.model.isFolder()
+ && isLinkShare
+ && this.model.updatePermissionPossible();
+
this.$el.html(linkShareTemplate({
cid: this.cid,
shareAllowed: true,
@@ -337,6 +371,9 @@
publicUploadChecked: publicUploadChecked,
hideFileListChecked: hideFileListChecked,
publicUploadLabel: t('core', 'Allow upload and editing'),
+ publicEditing: publicEditable,
+ publicEditingChecked: publicEditingChecked,
+ publicEditingLabel: t('core', 'Allow editing'),
hideFileListLabel: t('core', 'File drop (upload only)'),
mailPrivatePlaceholder: t('core', 'Email link to person'),
mailButtonText: t('core', 'Send')
diff --git a/core/js/sharedialogresharerinfoview.js b/core/js/sharedialogresharerinfoview.js
index 654eebf4997..9a9d95cfb60 100644
--- a/core/js/sharedialogresharerinfoview.js
+++ b/core/js/sharedialogresharerinfoview.js
@@ -80,7 +80,7 @@
'core',
'Shared with you and the group {group} by {owner}',
{
- group: this.model.getReshareWith(),
+ group: this.model.getReshareWithDisplayName(),
owner: ownerDisplayName
}
);
diff --git a/core/js/sharedialogshareelistview.js b/core/js/sharedialogshareelistview.js
index a0a7bbfa2dc..4647dedd722 100644
--- a/core/js/sharedialogshareelistview.js
+++ b/core/js/sharedialogshareelistview.js
@@ -35,47 +35,7 @@
'{{/unless}}' +
'{{/if}}' +
'<a href="#"><span class="icon icon-more"></span></a>' +
- '<div class="popovermenu bubble hidden menu">' +
- '<ul>' +
- '{{#if isResharingAllowed}} {{#if sharePermissionPossible}} {{#unless isMailShare}}' +
- '<li>' +
- '<span class="shareOption">' +
- '<input id="canShare-{{cid}}-{{shareWith}}" type="checkbox" name="share" class="permissions checkbox" {{#if hasSharePermission}}checked="checked"{{/if}} data-permissions="{{sharePermission}}" />' +
- '<label for="canShare-{{cid}}-{{shareWith}}">{{canShareLabel}}</label>' +
- '</span>' +
- '</li>' +
- '{{/unless}} {{/if}} {{/if}}' +
- '{{#if isFolder}}' +
- '{{#if createPermissionPossible}}{{#unless isMailShare}}' +
- '<li>' +
- '<span class="shareOption">' +
- '<input id="canCreate-{{cid}}-{{shareWith}}" type="checkbox" name="create" class="permissions checkbox" {{#if hasCreatePermission}}checked="checked"{{/if}} data-permissions="{{createPermission}}"/>' +
- '<label for="canCreate-{{cid}}-{{shareWith}}">{{createPermissionLabel}}</label>' +
- '</span>' +
- '</li>' +
- '{{/unless}}{{/if}}' +
- '{{#if updatePermissionPossible}}{{#unless isMailShare}}' +
- '<li>' +
- '<span class="shareOption">' +
- '<input id="canUpdate-{{cid}}-{{shareWith}}" type="checkbox" name="update" class="permissions checkbox" {{#if hasUpdatePermission}}checked="checked"{{/if}} data-permissions="{{updatePermission}}"/>' +
- '<label for="canUpdate-{{cid}}-{{shareWith}}">{{updatePermissionLabel}}</label>' +
- '</span>' +
- '</li>' +
- '{{/unless}}{{/if}}' +
- '{{#if deletePermissionPossible}}{{#unless isMailShare}}' +
- '<li>' +
- '<span class="shareOption">' +
- '<input id="canDelete-{{cid}}-{{shareWith}}" type="checkbox" name="delete" class="permissions checkbox" {{#if hasDeletePermission}}checked="checked"{{/if}} data-permissions="{{deletePermission}}"/>' +
- '<label for="canDelete-{{cid}}-{{shareWith}}">{{deletePermissionLabel}}</label>' +
- '</span>' +
- '</li>' +
- '{{/unless}}{{/if}}' +
- '{{/if}}' +
- '<li>' +
- '<a href="#" class="unshare"><span class="icon-loading-small hidden"></span><span class="icon icon-delete"></span><span>{{unshareLabel}}</span></a>' +
- '</li>' +
- '</ul>' +
- '</div>' +
+ '{{{popoverMenu}}}' +
'</span>' +
'</li>' +
'{{/each}}' +
@@ -94,6 +54,49 @@
'</ul>'
;
+ var TEMPLATE_POPOVER_MENU =
+ '<div class="popovermenu bubble hidden menu">' +
+ '<ul>' +
+ '{{#if isResharingAllowed}} {{#if sharePermissionPossible}} {{#unless isMailShare}}' +
+ '<li>' +
+ '<span class="shareOption menuitem">' +
+ '<input id="canShare-{{cid}}-{{shareWith}}" type="checkbox" name="share" class="permissions checkbox" {{#if hasSharePermission}}checked="checked"{{/if}} data-permissions="{{sharePermission}}" />' +
+ '<label for="canShare-{{cid}}-{{shareWith}}">{{canShareLabel}}</label>' +
+ '</span>' +
+ '</li>' +
+ '{{/unless}} {{/if}} {{/if}}' +
+ '{{#if isFolder}}' +
+ '{{#if createPermissionPossible}}{{#unless isMailShare}}' +
+ '<li>' +
+ '<span class="shareOption menuitem">' +
+ '<input id="canCreate-{{cid}}-{{shareWith}}" type="checkbox" name="create" class="permissions checkbox" {{#if hasCreatePermission}}checked="checked"{{/if}} data-permissions="{{createPermission}}"/>' +
+ '<label for="canCreate-{{cid}}-{{shareWith}}">{{createPermissionLabel}}</label>' +
+ '</span>' +
+ '</li>' +
+ '{{/unless}}{{/if}}' +
+ '{{#if updatePermissionPossible}}{{#unless isMailShare}}' +
+ '<li>' +
+ '<span class="shareOption menuitem">' +
+ '<input id="canUpdate-{{cid}}-{{shareWith}}" type="checkbox" name="update" class="permissions checkbox" {{#if hasUpdatePermission}}checked="checked"{{/if}} data-permissions="{{updatePermission}}"/>' +
+ '<label for="canUpdate-{{cid}}-{{shareWith}}">{{updatePermissionLabel}}</label>' +
+ '</span>' +
+ '</li>' +
+ '{{/unless}}{{/if}}' +
+ '{{#if deletePermissionPossible}}{{#unless isMailShare}}' +
+ '<li>' +
+ '<span class="shareOption menuitem">' +
+ '<input id="canDelete-{{cid}}-{{shareWith}}" type="checkbox" name="delete" class="permissions checkbox" {{#if hasDeletePermission}}checked="checked"{{/if}} data-permissions="{{deletePermission}}"/>' +
+ '<label for="canDelete-{{cid}}-{{shareWith}}">{{deletePermissionLabel}}</label>' +
+ '</span>' +
+ '</li>' +
+ '{{/unless}}{{/if}}' +
+ '{{/if}}' +
+ '<li>' +
+ '<a href="#" class="unshare"><span class="icon-loading-small hidden"></span><span class="icon icon-delete"></span><span>{{unshareLabel}}</span></a>' +
+ '</li>' +
+ '</ul>' +
+ '</div>';
+
/**
* @class OCA.Share.ShareDialogShareeListView
* @member {OC.Share.ShareItemModel} model
@@ -114,8 +117,14 @@
/** @type {Function} **/
_template: undefined,
+ /** @type {Function} **/
+ _popoverMenuTemplate: undefined,
+
_menuOpen: false,
+ /** @type {boolean|number} **/
+ _renderPermissionChange: false,
+
events: {
'click .unshare': 'onUnshare',
'click .icon-more': 'onToggleMenu',
@@ -182,8 +191,8 @@
});
},
- getShareeList: function() {
- var universal = {
+ getShareProperties: function() {
+ return {
avatarEnabled: this.configModel.areAvatarsEnabled(),
unshareLabel: t('core', 'Unshare'),
canShareLabel: t('core', 'can reshare'),
@@ -205,6 +214,15 @@
deletePermission: OC.PERMISSION_DELETE,
isFolder: this.model.isFolder()
};
+ },
+
+ /**
+ * get an array of sharees' share properties
+ *
+ * @returns {Array}
+ */
+ getShareeList: function() {
+ var universal = this.getShareProperties();
if(!this.model.hasUserShares()) {
return [];
@@ -256,29 +274,45 @@
},
render: function() {
- this.$el.html(this.template({
- cid: this.cid,
- sharees: this.getShareeList(),
- linkReshares: this.getLinkReshares()
- }));
-
- if(this.configModel.areAvatarsEnabled()) {
- this.$('.avatar').each(function() {
- var $this = $(this);
- if ($this.hasClass('imageplaceholderseed')) {
- $this.css({width: 32, height: 32});
- $this.imageplaceholder($this.data('seed'));
- } else {
- // user, size, ie8fix, hidedefault, callback, displayname
- $this.avatar($this.data('username'), 32, undefined, undefined, undefined, $this.data('displayname'));
- }
+ if(!this._renderPermissionChange) {
+ this.$el.html(this.template({
+ cid: this.cid,
+ sharees: this.getShareeList(),
+ linkReshares: this.getLinkReshares()
+ }));
+
+ if (this.configModel.areAvatarsEnabled()) {
+ this.$('.avatar').each(function () {
+ var $this = $(this);
+ if ($this.hasClass('imageplaceholderseed')) {
+ $this.css({width: 32, height: 32});
+ $this.imageplaceholder($this.data('seed'));
+ } else {
+ // user, size, ie8fix, hidedefault, callback, displayname
+ $this.avatar($this.data('username'), 32, undefined, undefined, undefined, $this.data('displayname'));
+ }
+ });
+ }
+
+ this.$('.has-tooltip').tooltip({
+ placement: 'bottom'
});
+ } else {
+ var permissionChangeShareId = parseInt(this._renderPermissionChange, 10);
+ var shareWithIndex = this.model.findShareWithIndex(permissionChangeShareId);
+ var sharee = this.getShareeObject(shareWithIndex);
+ $.extend(sharee, this.getShareProperties());
+ var $li = this.$('li[data-share-id=' + permissionChangeShareId + ']');
+ $li.find('.popovermenu').replaceWith(this.popoverMenuTemplate(sharee));
+
+ var checkBoxId = 'canEdit-' + this.cid + '-' + sharee.shareWith;
+ checkBoxId = '#' + checkBoxId.replace( /(:|\.|\[|\]|,|=|@)/g, "\\$1");
+ var $edit = $li.parent().find(checkBoxId);
+ if($edit.length === 1) {
+ $edit.prop('checked', sharee.hasEditPermission);
+ }
}
- this.$('.has-tooltip').tooltip({
- placement: 'bottom'
- });
-
var _this = this;
this.$('.popovermenu').on('afterHide', function() {
_this._menuOpen = false;
@@ -292,6 +326,8 @@
}
}
+ this._renderPermissionChange = false;
+
this.delegateEvents();
return this;
@@ -305,9 +341,28 @@
if (!this._template) {
this._template = Handlebars.compile(TEMPLATE);
}
+ var sharees = data.sharees;
+ if(_.isArray(sharees)) {
+ for (var i = 0; i < sharees.length; i++) {
+ data.sharees[i].popoverMenu = this.popoverMenuTemplate(sharees[i]);
+ }
+ }
return this._template(data);
},
+ /**
+ * renders the popover template and returns the resulting HTML
+ *
+ * @param {Object} data
+ * @returns {string}
+ */
+ popoverMenuTemplate: function(data) {
+ if(!this._popoverMenuTemplate) {
+ this._popoverMenuTemplate = Handlebars.compile(TEMPLATE_POPOVER_MENU);
+ }
+ return this._popoverMenuTemplate(data);
+ },
+
onUnshare: function(event) {
event.preventDefault();
event.stopPropagation();
@@ -385,7 +440,20 @@
permissions |= $(checkbox).data('permissions');
});
- this.model.updateShare(shareId, {permissions: permissions});
+
+ /** disable checkboxes during save operation to avoid race conditions **/
+ $li.find('input[type=checkbox]').prop('disabled', true);
+ var enableCb = function() {
+ $li.find('input[type=checkbox]').prop('disabled', false);
+ };
+ var errorCb = function(elem, msg) {
+ OC.dialogs.alert(msg, t('core', 'Error while sharing'));
+ enableCb();
+ };
+
+ this.model.updateShare(shareId, {permissions: permissions}, {error: errorCb, success: enableCb});
+
+ this._renderPermissionChange = shareId;
},
});
diff --git a/core/js/shareitemmodel.js b/core/js/shareitemmodel.js
index a784f59f67f..ae4c07e3f4e 100644
--- a/core/js/shareitemmodel.js
+++ b/core/js/shareitemmodel.js
@@ -272,6 +272,10 @@
return this.get('allowPublicUploadStatus');
},
+ isPublicEditingAllowed: function() {
+ return this.get('allowPublicEditingStatus');
+ },
+
/**
* @returns {boolean}
*/
@@ -345,6 +349,14 @@
},
/**
+ * @returns {string}
+ */
+ getReshareWithDisplayName: function() {
+ var reshare = this.get('reshare');
+ return reshare.share_with_displayname || reshare.share_with;
+ },
+
+ /**
* @returns {number}
*/
getReshareType: function() {
@@ -391,6 +403,26 @@
return share.share_with_displayname;
},
+ /**
+ * returns the array index of a sharee for a provided shareId
+ *
+ * @param shareId
+ * @returns {number}
+ */
+ findShareWithIndex: function(shareId) {
+ var shares = this.get('shares');
+ if(!_.isArray(shares)) {
+ throw "Unknown Share";
+ }
+ for(var i = 0; i < shares.length; i++) {
+ var shareWith = shares[i];
+ if(shareWith.id === shareId) {
+ return i;
+ }
+ }
+ throw "Unknown Sharee";
+ },
+
getShareType: function(shareIndex) {
/** @type OC.Share.Types.ShareInfo **/
var share = this.get('shares')[shareIndex];
@@ -553,7 +585,7 @@
return superShare;
},
- fetch: function() {
+ fetch: function(options) {
var model = this;
this.trigger('request', this);
@@ -577,6 +609,10 @@
shares: sharesMap,
reshare: reshare
}));
+
+ if(!_.isUndefined(options) && _.isFunction(options.success)) {
+ options.success();
+ }
});
return deferred;
@@ -647,6 +683,17 @@
});
}
+ var allowPublicEditingStatus = true;
+ if(!_.isUndefined(data.shares)) {
+ $.each(data.shares, function (key, value) {
+ if (value.share_type === OC.Share.SHARE_TYPE_LINK) {
+ allowPublicEditingStatus = (value.permissions & OC.PERMISSION_UPDATE) ? true : false;
+ return true;
+ }
+ });
+ }
+
+
var hideFileListStatus = false;
if(!_.isUndefined(data.shares)) {
$.each(data.shares, function (key, value) {
@@ -730,6 +777,7 @@
linkShare: linkShare,
permissions: permissions,
allowPublicUploadStatus: allowPublicUploadStatus,
+ allowPublicEditingStatus: allowPublicEditingStatus,
hideFileListStatus: hideFileListStatus
};
},
diff --git a/core/js/systemtags/systemtagmodel.js b/core/js/systemtags/systemtagmodel.js
index 89728357e25..72450a2abd0 100644
--- a/core/js/systemtags/systemtagmodel.js
+++ b/core/js/systemtags/systemtagmodel.js
@@ -9,7 +9,15 @@
*/
(function(OC) {
- var NS_OWNCLOUD = 'http://owncloud.org/ns';
+
+ _.extend(OC.Files.Client, {
+ PROPERTY_FILEID: '{' + OC.Files.Client.NS_OWNCLOUD + '}id',
+ PROPERTY_CAN_ASSIGN:'{' + OC.Files.Client.NS_OWNCLOUD + '}can-assign',
+ PROPERTY_DISPLAYNAME: '{' + OC.Files.Client.NS_OWNCLOUD + '}display-name',
+ PROPERTY_USERVISIBLE: '{' + OC.Files.Client.NS_OWNCLOUD + '}user-visible',
+ PROPERTY_USERASSIGNABLE:'{' + OC.Files.Client.NS_OWNCLOUD + '}user-assignable',
+ });
+
/**
* @class OCA.SystemTags.SystemTagsCollection
* @classdesc
@@ -28,12 +36,12 @@
},
davProperties: {
- 'id': '{' + NS_OWNCLOUD + '}id',
- 'name': '{' + NS_OWNCLOUD + '}display-name',
- 'userVisible': '{' + NS_OWNCLOUD + '}user-visible',
- 'userAssignable': '{' + NS_OWNCLOUD + '}user-assignable',
+ 'id': OC.Files.Client.PROPERTY_FILEID,
+ 'name': OC.Files.Client.PROPERTY_DISPLAYNAME,
+ 'userVisible': OC.Files.Client.PROPERTY_USERVISIBLE,
+ 'userAssignable': OC.Files.Client.PROPERTY_USERASSIGNABLE,
// read-only, effective permissions computed by the server,
- 'canAssign': '{' + NS_OWNCLOUD + '}can-assign'
+ 'canAssign': OC.Files.Client.PROPERTY_CAN_ASSIGN
},
parse: function(data) {
@@ -50,4 +58,3 @@
OC.SystemTags = OC.SystemTags || {};
OC.SystemTags.SystemTagModel = SystemTagModel;
})(OC);
-
diff --git a/core/js/systemtags/systemtagsinputfield.js b/core/js/systemtags/systemtagsinputfield.js
index 690525c0ebb..d5f6bd5f97e 100644
--- a/core/js/systemtags/systemtagsinputfield.js
+++ b/core/js/systemtags/systemtagsinputfield.js
@@ -40,7 +40,9 @@
'<form class="systemtags-rename-form">' +
' <label class="hidden-visually" for="{{cid}}-rename-input">{{renameLabel}}</label>' +
' <input id="{{cid}}-rename-input" type="text" value="{{name}}">' +
- ' <a href="#" class="delete icon icon-delete" title="{{deleteTooltip}}"></a>' +
+ ' {{#if isAdmin}}' +
+ ' <a href="#" class="delete icon icon-delete" title="{{deleteTooltip}}"></a>' +
+ ' {{/if}}' +
'</form>';
/**
@@ -148,7 +150,8 @@
cid: this.cid,
name: oldName,
deleteTooltip: t('core', 'Delete'),
- renameLabel: t('core', 'Rename')
+ renameLabel: t('core', 'Rename'),
+ isAdmin: this._isAdmin
}));
$item.find('.label').after($renameForm);
$item.find('.label, .systemtags-actions').addClass('hidden');
diff --git a/core/js/tests/specs/coreSpec.js b/core/js/tests/specs/coreSpec.js
index d1734a9f3d1..d83c0cd9a38 100644
--- a/core/js/tests/specs/coreSpec.js
+++ b/core/js/tests/specs/coreSpec.js
@@ -590,6 +590,36 @@ describe('Core base tests', function() {
}
});
});
+ describe('computerFileSize', function() {
+ it('correctly parses file sizes from a human readable formated string', function() {
+ var data = [
+ ['125', 125],
+ ['125.25', 125.25],
+ ['0 B', 0],
+ ['125 B', 125],
+ ['125b', 125],
+ ['125 KB', 128000],
+ ['125kb', 128000],
+ ['122.1 MB', 128031130],
+ ['122.1mb', 128031130],
+ ['119.2 GB', 127990025421],
+ ['119.2gb', 127990025421],
+ ['116.4 TB', 127983153473126],
+ ['116.4tb', 127983153473126]
+ ];
+ for (var i = 0; i < data.length; i++) {
+ expect(OC.Util.computerFileSize(data[i][0])).toEqual(data[i][1]);
+ }
+ });
+ it('returns null if the parameter is not a string', function() {
+ expect(OC.Util.computerFileSize(NaN)).toEqual(null);
+ expect(OC.Util.computerFileSize(125)).toEqual(null);
+ });
+ it('returns null if the string is unparsable', function() {
+ expect(OC.Util.computerFileSize('')).toEqual(null);
+ expect(OC.Util.computerFileSize('foobar')).toEqual(null);
+ });
+ });
describe('stripTime', function() {
it('strips time from dates', function() {
expect(OC.Util.stripTime(new Date(2014, 2, 24, 15, 4, 45, 24)))
@@ -937,8 +967,9 @@ describe('Core base tests', function() {
fadeOutStub.restore();
});
it('hides the first notification when calling hide without arguments', function() {
- var $row1 = OC.Notification.show('One');
+ OC.Notification.show('One');
var $row2 = OC.Notification.show('Two');
+ spyOn(console, 'warn');
var $el = $('#notification');
var $rows = $el.find('.row');
@@ -946,6 +977,7 @@ describe('Core base tests', function() {
OC.Notification.hide();
+ expect(console.warn).toHaveBeenCalled();
$rows = $el.find('.row');
expect($rows.length).toEqual(1);
expect($rows.eq(0).is($row2)).toEqual(true);
diff --git a/core/js/tests/specs/l10nSpec.js b/core/js/tests/specs/l10nSpec.js
index 2ceb2f4a916..064b27aa34a 100644
--- a/core/js/tests/specs/l10nSpec.js
+++ b/core/js/tests/specs/l10nSpec.js
@@ -21,6 +21,7 @@ describe('OC.L10N tests', function() {
describe('text translation', function() {
beforeEach(function() {
+ spyOn(console, 'warn');
OC.L10N.register(TEST_APP, {
'Hello world!': 'Hallo Welt!',
'Hello {name}, the weather is {weather}': 'Hallo {name}, das Wetter ist {weather}',
@@ -78,8 +79,10 @@ describe('OC.L10N tests', function() {
}
it('generates plural for default text when translation does not exist', function() {
+ spyOn(console, 'warn');
OC.L10N.register(TEST_APP, {
});
+ expect(console.warn).toHaveBeenCalled();
expect(
n(TEST_APP, 'download %n file', 'download %n files', 0)
).toEqual('download 0 files');
@@ -94,10 +97,12 @@ describe('OC.L10N tests', function() {
).toEqual('download 1024 files');
});
it('generates plural with default function when no forms specified', function() {
+ spyOn(console, 'warn');
OC.L10N.register(TEST_APP, {
'_download %n file_::_download %n files_':
['%n Datei herunterladen', '%n Dateien herunterladen']
});
+ expect(console.warn).toHaveBeenCalled();
checkPlurals();
});
it('generates plural with generated function when forms is specified', function() {
@@ -150,9 +155,11 @@ describe('OC.L10N tests', function() {
it('calls callback if translation already available', function() {
var promiseStub = sinon.stub();
var callbackStub = sinon.stub();
+ spyOn(console, 'warn');
OC.L10N.register(TEST_APP, {
'Hello world!': 'Hallo Welt!'
});
+ expect(console.warn).toHaveBeenCalled();
OC.L10N.load(TEST_APP, callbackStub).then(promiseStub);
expect(callbackStub.calledOnce).toEqual(true);
expect(promiseStub.calledOnce).toEqual(true);
diff --git a/core/js/tests/specs/sharedialogviewSpec.js b/core/js/tests/specs/sharedialogviewSpec.js
index 985610d51fb..cbb74714ff7 100644
--- a/core/js/tests/specs/sharedialogviewSpec.js
+++ b/core/js/tests/specs/sharedialogviewSpec.js
@@ -975,16 +975,35 @@ describe('OC.Share.ShareDialogView', function() {
dialog.render();
expect(dialog.$el.find('.shareWithField').prop('disabled')).toEqual(true);
});
- it('shows reshare owner', function() {
+ it('shows reshare owner for single user share', function() {
shareModel.set({
reshare: {
- uid_owner: 'user1'
+ uid_owner: 'user1',
+ displayname_owner: 'User One',
+ share_type: OC.Share.SHARE_TYPE_USER
},
shares: [],
permissions: OC.PERMISSION_READ
});
dialog.render();
expect(dialog.$el.find('.resharerInfoView .reshare').length).toEqual(1);
+ expect(dialog.$el.find('.resharerInfoView .reshare').text().trim()).toEqual('Shared with you by User One');
+ });
+ it('shows reshare owner for single user share', function() {
+ shareModel.set({
+ reshare: {
+ uid_owner: 'user1',
+ displayname_owner: 'User One',
+ share_with: 'group2',
+ share_with_displayname: 'Group Two',
+ share_type: OC.Share.SHARE_TYPE_GROUP
+ },
+ shares: [],
+ permissions: OC.PERMISSION_READ
+ });
+ dialog.render();
+ expect(dialog.$el.find('.resharerInfoView .reshare').length).toEqual(1);
+ expect(dialog.$el.find('.resharerInfoView .reshare').text().trim()).toEqual('Shared with you and the group Group Two by User One');
});
it('does not show reshare owner if owner is current user', function() {
shareModel.set({
diff --git a/core/js/tests/specs/shareitemmodelSpec.js b/core/js/tests/specs/shareitemmodelSpec.js
index 1cddcb2acaa..3d3baf75d15 100644
--- a/core/js/tests/specs/shareitemmodelSpec.js
+++ b/core/js/tests/specs/shareitemmodelSpec.js
@@ -190,6 +190,7 @@ describe('OC.Share.ShareItemModel', function() {
uid_owner: 'owner',
displayname_owner: 'Owner',
share_with: 'root',
+ share_with_displayname: 'Wurzel',
permissions: 1
},
{
@@ -221,7 +222,11 @@ describe('OC.Share.ShareItemModel', function() {
// user share has higher priority
expect(reshare.share_type).toEqual(OC.Share.SHARE_TYPE_USER);
expect(reshare.share_with).toEqual('root');
+ expect(reshare.share_with_displayname).toEqual('Wurzel');
expect(reshare.id).toEqual('1');
+
+ expect(model.getReshareWith()).toEqual('root');
+ expect(model.getReshareWithDisplayName()).toEqual('Wurzel');
});
it('does not parse link share when for a different file', function() {
/* jshint camelcase: false */
diff --git a/core/js/tests/specs/systemtags/systemtagsinputfieldSpec.js b/core/js/tests/specs/systemtags/systemtagsinputfieldSpec.js
index 503bef7cf2b..8d3f67bfa0d 100644
--- a/core/js/tests/specs/systemtags/systemtagsinputfieldSpec.js
+++ b/core/js/tests/specs/systemtags/systemtagsinputfieldSpec.js
@@ -267,20 +267,6 @@ describe('OC.SystemTags.SystemTagsInputField tests', function() {
saveStub.restore();
});
- it('deletes model and submits change when clicking delete', function() {
- var destroyStub = sinon.stub(OC.SystemTags.SystemTagModel.prototype, 'destroy');
-
- expect($dropdown.find('.delete').length).toEqual(0);
- $dropdown.find('.rename').mouseup();
- // delete button appears
- expect($dropdown.find('.delete').length).toEqual(1);
- $dropdown.find('.delete').mouseup();
-
- expect(destroyStub.calledOnce).toEqual(true);
- expect(destroyStub.calledOn(view.collection.get('1')));
-
- destroyStub.restore();
- });
});
describe('setting data', function() {
it('sets value when calling setValues', function() {
@@ -299,12 +285,18 @@ describe('OC.SystemTags.SystemTagsInputField tests', function() {
});
describe('as admin', function() {
+ var $dropdown;
+
beforeEach(function() {
view = new OC.SystemTags.SystemTagsInputField({
isAdmin: true
});
- view.render();
$('.testInputContainer').append(view.$el);
+ $dropdown = $('<div class="select2-dropdown"></div>');
+ select2Stub.withArgs('dropdown').returns($dropdown);
+ $('#testArea').append($dropdown);
+
+ view.render();
});
it('formatResult renders tag name with visibility', function() {
var opts = select2Stub.getCall(0).args[0];
@@ -431,15 +423,50 @@ describe('OC.SystemTags.SystemTagsInputField tests', function() {
]);
});
});
+ describe('tag actions', function() {
+ var opts;
+
+ beforeEach(function() {
+
+ opts = select2Stub.getCall(0).args[0];
+
+ view.collection.add([
+ new OC.SystemTags.SystemTagModel({id: '1', name: 'abc'}),
+ ]);
+
+ $dropdown.append(opts.formatResult(view.collection.get('1').toJSON()));
+
+ });
+ it('deletes model and submits change when clicking delete', function() {
+ var destroyStub = sinon.stub(OC.SystemTags.SystemTagModel.prototype, 'destroy');
+
+ expect($dropdown.find('.delete').length).toEqual(0);
+ $dropdown.find('.rename').mouseup();
+ // delete button appears
+ expect($dropdown.find('.delete').length).toEqual(1);
+ $dropdown.find('.delete').mouseup();
+
+ expect(destroyStub.calledOnce).toEqual(true);
+ expect(destroyStub.calledOn(view.collection.get('1')));
+
+ destroyStub.restore();
+ });
+ });
});
describe('as user', function() {
+ var $dropdown;
+
beforeEach(function() {
view = new OC.SystemTags.SystemTagsInputField({
isAdmin: false
});
- view.render();
$('.testInputContainer').append(view.$el);
+ $dropdown = $('<div class="select2-dropdown"></div>');
+ select2Stub.withArgs('dropdown').returns($dropdown);
+ $('#testArea').append($dropdown);
+
+ view.render();
});
it('formatResult renders tag name only', function() {
var opts = select2Stub.getCall(0).args[0];
@@ -570,5 +597,33 @@ describe('OC.SystemTags.SystemTagsInputField tests', function() {
]);
});
});
+ describe('tag actions', function() {
+ var opts;
+
+ beforeEach(function() {
+
+ opts = select2Stub.getCall(0).args[0];
+
+ view.collection.add([
+ new OC.SystemTags.SystemTagModel({id: '1', name: 'abc'}),
+ ]);
+
+ $dropdown.append(opts.formatResult(view.collection.get('1').toJSON()));
+
+ });
+ it('deletes model and submits change when clicking delete', function() {
+ var destroyStub = sinon.stub(OC.SystemTags.SystemTagModel.prototype, 'destroy');
+
+ expect($dropdown.find('.delete').length).toEqual(0);
+ $dropdown.find('.rename').mouseup();
+ // delete button appears only for admins
+ expect($dropdown.find('.delete').length).toEqual(0);
+ $dropdown.find('.delete').mouseup();
+
+ expect(destroyStub.notCalled).toEqual(true);
+
+ destroyStub.restore();
+ });
+ });
});
});
diff --git a/core/js/update.js b/core/js/update.js
index 32cf2ce5ecc..e849d8a16ce 100644
--- a/core/js/update.js
+++ b/core/js/update.js
@@ -72,7 +72,7 @@
var span = $('<span>')
.addClass('bold');
if(message === 'Exception: Updates between multiple major versions and downgrades are unsupported.') {
- span.append(t('core', 'The update was unsuccessful. For more information <a href="{url}">check our forum post</a> covering this issue.', {'url': 'https://forum.owncloud.org/viewtopic.php?f=17&t=32087'}));
+ span.append(t('core', 'The update was unsuccessful. For more information <a href="{url}">check our forum post</a> covering this issue.', {'url': 'https://help.nextcloud.com/t/updates-between-multiple-major-versions-are-unsupported/7094'}));
} else {
span.append(t('core', 'The update was unsuccessful. ' +
'Please report this issue to the ' +