summaryrefslogtreecommitdiffstats
path: root/core/js/shareitemmodel.js
diff options
context:
space:
mode:
Diffstat (limited to 'core/js/shareitemmodel.js')
-rw-r--r--core/js/shareitemmodel.js750
1 files changed, 750 insertions, 0 deletions
diff --git a/core/js/shareitemmodel.js b/core/js/shareitemmodel.js
new file mode 100644
index 00000000000..ff0d3a6d800
--- /dev/null
+++ b/core/js/shareitemmodel.js
@@ -0,0 +1,750 @@
+/*
+ * Copyright (c) 2015
+ *
+ * This file is licensed under the Affero General Public License version 3
+ * or later.
+ *
+ * See the COPYING-README file.
+ *
+ */
+
+(function() {
+ if(!OC.Share) {
+ OC.Share = {};
+ OC.Share.Types = {};
+ }
+
+ /**
+ * @typedef {object} OC.Share.Types.LinkShareInfo
+ * @property {bool} isLinkShare
+ * @property {string} token
+ * @property {string|null} password
+ * @property {string} link
+ * @property {number} permissions
+ * @property {Date} expiration
+ * @property {number} stime share time
+ */
+
+ /**
+ * @typedef {object} OC.Share.Types.Collection
+ * @property {string} item_type
+ * @property {string} path
+ * @property {string} item_source TODO: verify
+ */
+
+ /**
+ * @typedef {object} OC.Share.Types.Reshare
+ * @property {string} uid_owner
+ * @property {number} share_type
+ * @property {string} share_with
+ * @property {string} displayname_owner
+ * @property {number} permissions
+ */
+
+ /**
+ * @typedef {object} OC.Share.Types.ShareInfo
+ * @property {number} share_type
+ * @property {number} permissions
+ * @property {number} file_source optional
+ * @property {number} item_source
+ * @property {string} token
+ * @property {string} share_with
+ * @property {string} share_with_displayname
+ * @property {string} share_mail_send
+ * @property {OC.Share.Types.Collection|undefined} collection
+ * @property {Date} expiration optional?
+ * @property {number} stime optional?
+ */
+
+ /**
+ * @typedef {object} OC.Share.Types.ShareItemInfo
+ * @property {OC.Share.Types.Reshare} reshare
+ * @property {OC.Share.Types.ShareInfo[]} shares
+ * @property {OC.Share.Types.LinkShareInfo|undefined} linkShare
+ */
+
+ /**
+ * @class OCA.Share.ShareItemModel
+ * @classdesc
+ *
+ * Represents the GUI of the share dialogue
+ *
+ * // FIXME: use OC Share API once #17143 is done
+ *
+ * // TODO: this really should be a collection of share item models instead,
+ * where the link share is one of them
+ */
+ var ShareItemModel = OC.Backbone.Model.extend({
+ initialize: function(attributes, options) {
+ if(!_.isUndefined(options.configModel)) {
+ this.configModel = options.configModel;
+ }
+ if(!_.isUndefined(options.fileInfoModel)) {
+ /** @type {OC.Files.FileInfo} **/
+ this.fileInfoModel = options.fileInfoModel;
+ }
+
+ _.bindAll(this, 'addShare');
+ },
+
+ defaults: {
+ allowPublicUploadStatus: false,
+ permissions: 0,
+ linkShare: {}
+ },
+
+ /**
+ * Saves the current link share information.
+ *
+ * This will trigger an ajax call and refetch the model afterwards.
+ *
+ * TODO: this should be a separate model
+ */
+ saveLinkShare: function(attributes, options) {
+ var model = this;
+ var itemType = this.get('itemType');
+ var itemSource = this.get('itemSource');
+
+ // TODO: use backbone's default value mechanism once this is a separate model
+ var requiredAttributes = [
+ { name: 'password', defaultValue: '' },
+ { name: 'permissions', defaultValue: OC.PERMISSION_READ },
+ { name: 'expiration', defaultValue: this.configModel.getDefaultExpirationDateString() }
+ ];
+
+ attributes = attributes || {};
+
+ // get attributes from the model and fill in with default values
+ _.each(requiredAttributes, function(attribute) {
+ // a provided options overrides a present value of the link
+ // share. If neither is given, the default value is used.
+ if(_.isUndefined(attribute[attribute.name])) {
+ attributes[attribute.name] = attribute.defaultValue;
+ var currentValue = model.get('linkShare')[attribute.name];
+ if(!_.isUndefined(currentValue)) {
+ attributes[attribute.name] = currentValue;
+ }
+ }
+ });
+
+ OC.Share.share(
+ itemType,
+ itemSource,
+ OC.Share.SHARE_TYPE_LINK,
+ attributes.password,
+ attributes.permissions,
+ this.fileInfoModel.get('name'),
+ attributes.expiration,
+ function(result) {
+ if (!result || result.status !== 'success') {
+ model.fetch({
+ success: function() {
+ if (options && _.isFunction(options.success)) {
+ options.success(model);
+ }
+ }
+ });
+ } else {
+ if (options && _.isFunction(options.error)) {
+ options.error(model);
+ }
+ }
+ },
+ function(result) {
+ var msg = t('core', 'Error');
+ if (result.data && result.data.message) {
+ msg = result.data.message;
+ }
+
+ if (options && _.isFunction(options.error)) {
+ options.error(model, msg);
+ } else {
+ OC.dialogs.alert(msg, t('core', 'Error while sharing'));
+ }
+ }
+ );
+ },
+
+ removeLinkShare: function() {
+ this.removeShare(OC.Share.SHARE_TYPE_LINK, '');
+ },
+
+ /**
+ * Sets the public upload flag
+ *
+ * @param {bool} allow whether public upload is allowed
+ */
+ setPublicUpload: function(allow) {
+ var permissions = OC.PERMISSION_READ;
+ if(allow) {
+ permissions = OC.PERMISSION_UPDATE | OC.PERMISSION_CREATE | OC.PERMISSION_READ;
+ }
+
+ this.get('linkShare').permissions = permissions;
+ },
+
+ /**
+ * Sets the expiration date of the public link
+ *
+ * @param {string} expiration expiration date
+ */
+ setExpirationDate: function(expiration) {
+ this.get('linkShare').expiration = expiration;
+ },
+
+ /**
+ * Set password of the public link share
+ *
+ * @param {string} password
+ */
+ setPassword: function(password) {
+ this.get('linkShare').password = password;
+ },
+
+ addShare: function(attributes, options) {
+ var shareType = attributes.shareType;
+ var shareWith = attributes.shareWith;
+ var fileName = this.fileInfoModel.get('name');
+ options = options || {};
+
+ // Default permissions are Edit (CRUD) and Share
+ // Check if these permissions are possible
+ var permissions = OC.PERMISSION_READ;
+ if (shareType === OC.Share.SHARE_TYPE_REMOTE) {
+ permissions = OC.PERMISSION_CREATE | OC.PERMISSION_UPDATE | OC.PERMISSION_READ;
+ } else {
+ if (this.updatePermissionPossible()) {
+ permissions = permissions | OC.PERMISSION_UPDATE;
+ }
+ if (this.createPermissionPossible()) {
+ permissions = permissions | OC.PERMISSION_CREATE;
+ }
+ if (this.deletePermissionPossible()) {
+ permissions = permissions | OC.PERMISSION_DELETE;
+ }
+ if (this.configModel.get('isResharingAllowed') && (this.sharePermissionPossible())) {
+ permissions = permissions | OC.PERMISSION_SHARE;
+ }
+ }
+
+ var model = this;
+ var itemType = this.get('itemType');
+ var itemSource = this.get('itemSource');
+ OC.Share.share(itemType, itemSource, shareType, shareWith, permissions, fileName, options.expiration, function() {
+ model.fetch();
+ });
+ },
+
+ setPermissions: function(shareType, shareWith, permissions) {
+ var itemType = this.get('itemType');
+ var itemSource = this.get('itemSource');
+
+ // TODO: in the future, only set the permissions on the model but don't save directly
+ OC.Share.setPermissions(itemType, itemSource, shareType, shareWith, permissions);
+ },
+
+ removeShare: function(shareType, shareWith) {
+ var model = this;
+ var itemType = this.get('itemType');
+ var itemSource = this.get('itemSource');
+
+ OC.Share.unshare(itemType, itemSource, shareType, shareWith, function() {
+ model.fetch();
+ });
+ },
+
+ /**
+ * @returns {boolean}
+ */
+ isPublicUploadAllowed: function() {
+ return this.get('allowPublicUploadStatus');
+ },
+
+ /**
+ * @returns {boolean}
+ */
+ isFolder: function() {
+ return this.get('itemType') === 'folder';
+ },
+
+ /**
+ * @returns {boolean}
+ */
+ isFile: function() {
+ return this.get('itemType') === 'file';
+ },
+
+ /**
+ * whether this item has reshare information
+ * @returns {boolean}
+ */
+ hasReshare: function() {
+ var reshare = this.get('reshare');
+ return _.isObject(reshare) && !_.isUndefined(reshare.uid_owner);
+ },
+
+ /**
+ * whether this item has user share information
+ * @returns {boolean}
+ */
+ hasUserShares: function() {
+ var shares = this.get('shares');
+ return _.isArray(shares) && shares.length > 0;
+ },
+
+ /**
+ * Returns whether this item has a link share
+ *
+ * @return {bool} true if a link share exists, false otherwise
+ */
+ hasLinkShare: function() {
+ var linkShare = this.get('linkShare');
+ if (linkShare && linkShare.isLinkShare) {
+ return true;
+ }
+ return false;
+ },
+
+ /**
+ * @param {number} shareIndex
+ * @returns {string}
+ */
+ getCollectionType: function(shareIndex) {
+ /** @type OC.Share.Types.ShareInfo **/
+ var share = this.get('shares')[shareIndex];
+ if(!_.isObject(share)) {
+ throw "Unknown Share";
+ } else if(_.isUndefined(share.collection)) {
+ throw "Share is not a collection";
+ }
+
+ return share.collection.item_type;
+ },
+
+ /**
+ * @param {number} shareIndex
+ * @returns {string}
+ */
+ getCollectionPath: function(shareIndex) {
+ /** @type OC.Share.Types.ShareInfo **/
+ var share = this.get('shares')[shareIndex];
+ if(!_.isObject(share)) {
+ throw "Unknown Share";
+ } else if(_.isUndefined(share.collection)) {
+ throw "Share is not a collection";
+ }
+
+ return share.collection.path;
+ },
+
+ /**
+ * @param {number} shareIndex
+ * @returns {string}
+ */
+ getCollectionSource: function(shareIndex) {
+ /** @type OC.Share.Types.ShareInfo **/
+ var share = this.get('shares')[shareIndex];
+ if(!_.isObject(share)) {
+ throw "Unknown Share";
+ } else if(_.isUndefined(share.collection)) {
+ throw "Share is not a collection";
+ }
+
+ return share.collection.item_source;
+ },
+
+ /**
+ * @param {number} shareIndex
+ * @returns {boolean}
+ */
+ isCollection: function(shareIndex) {
+ /** @type OC.Share.Types.ShareInfo **/
+ var share = this.get('shares')[shareIndex];
+ if(!_.isObject(share)) {
+ throw "Unknown Share";
+ }
+ if(_.isUndefined(share.collection)) {
+ return false;
+ }
+ return true;
+ },
+
+
+ /**
+ * @returns {string}
+ */
+ getReshareOwner: function() {
+ return this.get('reshare').uid_owner;
+ },
+
+ /**
+ * @returns {string}
+ */
+ getReshareOwnerDisplayname: function() {
+ return this.get('reshare').displayname_owner;
+ },
+
+ /**
+ * @returns {string}
+ */
+ getReshareWith: function() {
+ return this.get('reshare').share_with;
+ },
+
+ /**
+ * @returns {number}
+ */
+ getReshareType: function() {
+ return this.get('reshare').share_type;
+ },
+
+ /**
+ * @param shareIndex
+ * @returns {string}
+ */
+ getShareWith: function(shareIndex) {
+ /** @type OC.Share.Types.ShareInfo **/
+ var share = this.get('shares')[shareIndex];
+ if(!_.isObject(share)) {
+ throw "Unknown Share";
+ }
+ return share.share_with;
+ },
+
+ /**
+ * @param shareIndex
+ * @returns {string}
+ */
+ getShareWithDisplayName: function(shareIndex) {
+ /** @type OC.Share.Types.ShareInfo **/
+ var share = this.get('shares')[shareIndex];
+ if(!_.isObject(share)) {
+ throw "Unknown Share";
+ }
+ return share.share_with_displayname;
+ },
+
+ getShareType: function(shareIndex) {
+ /** @type OC.Share.Types.ShareInfo **/
+ var share = this.get('shares')[shareIndex];
+ if(!_.isObject(share)) {
+ throw "Unknown Share";
+ }
+ return share.share_type;
+ },
+
+ /**
+ * whether a share from shares has the requested permission
+ *
+ * @param {number} shareIndex
+ * @param {number} permission
+ * @returns {boolean}
+ * @private
+ */
+ _shareHasPermission: function(shareIndex, permission) {
+ /** @type OC.Share.Types.ShareInfo **/
+ var share = this.get('shares')[shareIndex];
+ if(!_.isObject(share)) {
+ throw "Unknown Share";
+ }
+ if( share.share_type === OC.Share.SHARE_TYPE_REMOTE
+ && ( permission === OC.PERMISSION_SHARE
+ || permission === OC.PERMISSION_DELETE))
+ {
+ return false;
+ }
+ return (share.permissions & permission) === permission;
+ },
+
+ notificationMailWasSent: function(shareIndex) {
+ /** @type OC.Share.Types.ShareInfo **/
+ var share = this.get('shares')[shareIndex];
+ if(!_.isObject(share)) {
+ throw "Unknown Share";
+ }
+ return share.share_mail_send === '1';
+ },
+
+ /**
+ * Sends an email notification for the given share
+ *
+ * @param {int} shareType share type
+ * @param {string} shareWith recipient
+ * @param {bool} state whether to set the notification flag or remove it
+ */
+ sendNotificationForShare: function(shareType, shareWith, state) {
+ var itemType = this.get('itemType');
+ var itemSource = this.get('itemSource');
+
+ return $.post(
+ OC.generateUrl('core/ajax/share.php'),
+ {
+ action: state ? 'informRecipients' : 'informRecipientsDisabled',
+ recipient: shareWith,
+ shareType: shareType,
+ itemSource: itemSource,
+ itemType: itemType
+ },
+ function(result) {
+ if (result.status !== 'success') {
+ // FIXME: a model should not show dialogs
+ OC.dialogs.alert(t('core', result.data.message), t('core', 'Warning'));
+ }
+ }
+ );
+ },
+
+ /**
+ * Send the link share information by email
+ *
+ * @param {string} recipientEmail recipient email address
+ */
+ sendEmailPrivateLink: function(recipientEmail) {
+ var itemType = this.get('itemType');
+ var itemSource = this.get('itemSource');
+ var linkShare = this.get('linkShare');
+
+ return $.post(
+ OC.generateUrl('core/ajax/share.php'), {
+ action: 'email',
+ toaddress: recipientEmail,
+ link: linkShare.link,
+ itemType: itemType,
+ itemSource: itemSource,
+ file: this.fileInfoModel.get('name'),
+ expiration: linkShare.expiration || ''
+ },
+ function(result) {
+ if (!result || result.status !== 'success') {
+ // FIXME: a model should not show dialogs
+ OC.dialogs.alert(result.data.message, t('core', 'Error while sending notification'));
+ }
+ });
+ },
+
+ /**
+ * @returns {boolean}
+ */
+ sharePermissionPossible: function() {
+ return (this.get('permissions') & OC.PERMISSION_SHARE) === OC.PERMISSION_SHARE;
+ },
+
+ /**
+ * @param {number} shareIndex
+ * @returns {boolean}
+ */
+ hasSharePermission: function(shareIndex) {
+ return this._shareHasPermission(shareIndex, OC.PERMISSION_SHARE);
+ },
+
+ /**
+ * @returns {boolean}
+ */
+ createPermissionPossible: function() {
+ return (this.get('permissions') & OC.PERMISSION_CREATE) === OC.PERMISSION_CREATE;
+ },
+
+ /**
+ * @param {number} shareIndex
+ * @returns {boolean}
+ */
+ hasCreatePermission: function(shareIndex) {
+ return this._shareHasPermission(shareIndex, OC.PERMISSION_CREATE);
+ },
+
+ /**
+ * @returns {boolean}
+ */
+ updatePermissionPossible: function() {
+ return (this.get('permissions') & OC.PERMISSION_UPDATE) === OC.PERMISSION_UPDATE;
+ },
+
+ /**
+ * @param {number} shareIndex
+ * @returns {boolean}
+ */
+ hasUpdatePermission: function(shareIndex) {
+ return this._shareHasPermission(shareIndex, OC.PERMISSION_UPDATE);
+ },
+
+ /**
+ * @returns {boolean}
+ */
+ deletePermissionPossible: function() {
+ return (this.get('permissions') & OC.PERMISSION_DELETE) === OC.PERMISSION_DELETE;
+ },
+
+ /**
+ * @param {number} shareIndex
+ * @returns {boolean}
+ */
+ hasDeletePermission: function(shareIndex) {
+ return this._shareHasPermission(shareIndex, OC.PERMISSION_DELETE);
+ },
+
+ /**
+ * @returns {boolean}
+ */
+ editPermissionPossible: function() {
+ return this.createPermissionPossible()
+ || this.updatePermissionPossible()
+ || this.deletePermissionPossible();
+ },
+
+ /**
+ * @returns {boolean}
+ */
+ hasEditPermission: function(shareIndex) {
+ return this.hasCreatePermission(shareIndex)
+ || this.hasUpdatePermission(shareIndex)
+ || this.hasDeletePermission(shareIndex);
+ },
+
+ fetch: function() {
+ var model = this;
+ OC.Share.loadItem(this.get('itemType'), this.get('itemSource'), function(data) {
+ model.set(model.parse(data));
+ });
+ },
+
+ /**
+ * Updates OC.Share.itemShares and OC.Share.statuses.
+ *
+ * This is required in case the user navigates away and comes back,
+ * the share statuses from the old arrays are still used to fill in the icons
+ * in the file list.
+ */
+ _legacyFillCurrentShares: function(shares) {
+ var fileId = this.fileInfoModel.get('id');
+ if (!shares || !shares.length) {
+ delete OC.Share.statuses[fileId];
+ return;
+ }
+
+ var currentShareStatus = OC.Share.statuses[fileId];
+ if (!currentShareStatus) {
+ currentShareStatus = {link: false};
+ OC.Share.statuses[fileId] = currentShareStatus;
+ }
+ currentShareStatus.link = false;
+
+ OC.Share.currentShares = {};
+ OC.Share.itemShares = [];
+ _.each(shares,
+ /**
+ * @param {OC.Share.Types.ShareInfo} share
+ */
+ function(share) {
+ if (share.share_type === OC.Share.SHARE_TYPE_LINK) {
+ OC.Share.itemShares[share.share_type] = true;
+ currentShareStatus.link = true;
+ } else {
+ if (!OC.Share.itemShares[share.share_type]) {
+ OC.Share.itemShares[share.share_type] = [];
+ }
+ OC.Share.itemShares[share.share_type].push(share.share_with);
+ }
+ }
+ );
+ },
+
+ parse: function(data) {
+ if(data === false) {
+ console.warn('no data was returned');
+ trigger('fetchError');
+ return {};
+ }
+
+ var permissions = this.get('possiblePermissions');
+ if(!_.isUndefined(data.reshare) && !_.isUndefined(data.reshare.permissions) && data.reshare.uid_owner !== OC.currentUser) {
+ permissions = permissions & data.reshare.permissions;
+ }
+
+ var allowPublicUploadStatus = false;
+ if(!_.isUndefined(data.shares)) {
+ $.each(data.shares, function (key, value) {
+ if (value.share_type === OC.Share.SHARE_TYPE_LINK) {
+ allowPublicUploadStatus = (value.permissions & OC.PERMISSION_CREATE) ? true : false;
+ return true;
+ }
+ });
+ }
+
+ /** @type {OC.Share.Types.ShareInfo[]} **/
+ var shares = _.toArray(data.shares);
+ this._legacyFillCurrentShares(shares);
+
+ var linkShare = { isLinkShare: false };
+ // filter out the share by link
+ shares = _.reject(shares,
+ /**
+ * @param {OC.Share.Types.ShareInfo} share
+ */
+ function(share) {
+ var isShareLink =
+ share.share_type === OC.Share.SHARE_TYPE_LINK
+ && ( share.file_source === this.get('itemSource')
+ || share.item_source === this.get('itemSource'));
+
+ if (isShareLink) {
+ var link = window.location.protocol + '//' + window.location.host;
+ if (!share.token) {
+ // pre-token link
+ var fullPath = this.fileInfoModel.get('path') + '/' +
+ this.fileInfoModel.get('name');
+ var location = '/' + OC.currentUser + '/files' + fullPath;
+ var type = this.fileInfoModel.isDirectory() ? 'folder' : 'file';
+ link += OC.linkTo('', 'public.php') + '?service=files&' +
+ type + '=' + encodeURIComponent(location);
+ } else {
+ link += OC.generateUrl('/s/') + share.token;
+ }
+ linkShare = {
+ isLinkShare: true,
+ token: share.token,
+ password: share.share_with,
+ link: link,
+ permissions: share.permissions,
+ // currently expiration is only effective for link shares.
+ expiration: share.expiration,
+ stime: share.stime
+ };
+
+ return share;
+ }
+ },
+ this
+ );
+
+ return {
+ reshare: data.reshare,
+ shares: shares,
+ linkShare: linkShare,
+ permissions: permissions,
+ allowPublicUploadStatus: allowPublicUploadStatus
+ };
+ },
+
+ /**
+ * Parses a string to an valid integer (unix timestamp)
+ * @param time
+ * @returns {*}
+ * @internal Only used to work around a bug in the backend
+ */
+ _parseTime: function(time) {
+ if (_.isString(time)) {
+ // skip empty strings and hex values
+ if (time === '' || (time.length > 1 && time[0] === '0' && time[1] === 'x')) {
+ return null;
+ }
+ time = parseInt(time, 10);
+ if(isNaN(time)) {
+ time = null;
+ }
+ }
+ return time;
+ }
+ });
+
+ OC.Share.ShareItemModel = ShareItemModel;
+})();