123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880 |
- /*
- * 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.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} mail_send
- * @property {Date} expiration optional?
- * @property {number} stime optional?
- * @property {string} uid_owner
- * @property {string} displayname_owner
- */
- /**
- * @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
- */
- /**
- * These properties are sometimes returned by the server as strings instead
- * of integers, so we need to convert them accordingly...
- */
- var SHARE_RESPONSE_INT_PROPS = [
- 'id', 'file_parent', 'mail_send', 'file_source', 'item_source', 'permissions',
- 'storage', 'share_type', 'parent', 'stime'
- ];
- /**
- * @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({
- /**
- * @type share id of the link share, if applicable
- */
- _linkShareId: null,
- 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, if successful, refetch the model
- * afterwards. Callbacks "success", "error" and "complete" can be given
- * in the options object; "success" is called after a successful save
- * once the model is refetch, "error" is called after a failed save, and
- * "complete" is called both after a successful save and after a failed
- * save. Note that "complete" is called before "success" and "error" are
- * called (unlike in jQuery, in which it is called after them); this
- * ensures that "complete" is called even if refetching the model fails.
- *
- * TODO: this should be a separate model
- */
- saveLinkShare: function(attributes, options) {
- options = options || {};
- attributes = _.extend({}, attributes);
- var shareId = null;
- var call;
- // oh yeah...
- if (attributes.expiration) {
- attributes.expireDate = attributes.expiration;
- delete attributes.expiration;
- }
- if (this.get('linkShare') && this.get('linkShare').isLinkShare) {
- shareId = this.get('linkShare').id;
- // note: update can only update a single value at a time
- call = this.updateShare(shareId, attributes, options);
- } else {
- attributes = _.defaults(attributes, {
- password: '',
- passwordChanged: false,
- permissions: OC.PERMISSION_READ,
- expireDate: this.configModel.getDefaultExpirationDateString(),
- shareType: OC.Share.SHARE_TYPE_LINK
- });
- call = this.addShare(attributes, options);
- }
- return call;
- },
- removeLinkShare: function() {
- if (this.get('linkShare')) {
- return this.removeShare(this.get('linkShare').id);
- }
- },
- addShare: function(attributes, options) {
- var shareType = attributes.shareType;
- attributes = _.extend({}, attributes);
- // Default permissions are Edit (CRUD) and Share
- // Check if these permissions are possible
- var permissions = OC.PERMISSION_READ;
- 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;
- }
- attributes.permissions = permissions;
- if (_.isUndefined(attributes.path)) {
- attributes.path = this.fileInfoModel.getFullPath();
- }
- return this._addOrUpdateShare({
- type: 'POST',
- url: this._getUrl('shares'),
- data: attributes,
- dataType: 'json'
- }, options);
- },
- updateShare: function(shareId, attrs, options) {
- return this._addOrUpdateShare({
- type: 'PUT',
- url: this._getUrl('shares/' + encodeURIComponent(shareId)),
- data: attrs,
- dataType: 'json'
- }, options);
- },
- _addOrUpdateShare: function(ajaxSettings, options) {
- var self = this;
- options = options || {};
- return $.ajax(
- ajaxSettings
- ).always(function() {
- if (_.isFunction(options.complete)) {
- options.complete(self);
- }
- }).done(function() {
- self.fetch().done(function() {
- if (_.isFunction(options.success)) {
- options.success(self);
- }
- });
- }).fail(function(xhr) {
- var msg = t('core', 'Error');
- var result = xhr.responseJSON;
- if (result && result.ocs && result.ocs.meta) {
- msg = result.ocs.meta.message;
- }
- if (_.isFunction(options.error)) {
- options.error(self, msg);
- } else {
- OC.dialogs.alert(msg, t('core', 'Error while sharing'));
- }
- });
- },
- /**
- * Deletes the share with the given id
- *
- * @param {int} shareId share id
- * @return {jQuery}
- */
- removeShare: function(shareId, options) {
- var self = this;
- options = options || {};
- return $.ajax({
- type: 'DELETE',
- url: this._getUrl('shares/' + encodeURIComponent(shareId)),
- }).done(function() {
- self.fetch({
- success: function() {
- if (_.isFunction(options.success)) {
- options.success(self);
- }
- }
- });
- }).fail(function(xhr) {
- var msg = t('core', 'Error');
- var result = xhr.responseJSON;
- if (result.ocs && result.ocs.meta) {
- msg = result.ocs.meta.message;
- }
- if (_.isFunction(options.error)) {
- options.error(self, msg);
- } else {
- OC.dialogs.alert(msg, t('core', 'Error removing share'));
- }
- });
- },
- /**
- * @returns {boolean}
- */
- isPublicUploadAllowed: function() {
- return this.get('allowPublicUploadStatus');
- },
- isPublicEditingAllowed: function() {
- return this.get('allowPublicEditingStatus');
- },
- /**
- * @returns {boolean}
- */
- isHideFileListSet: function() {
- return this.get('hideFileListStatus');
- },
- /**
- * @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() {
- return this.getSharesWithCurrentItem().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;
- },
- /**
- * @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 {string}
- */
- getReshareWithDisplayName: function() {
- var reshare = this.get('reshare');
- return reshare.share_with_displayname || reshare.share_with;
- },
- /**
- * @returns {number}
- */
- getReshareType: function() {
- return this.get('reshare').share_type;
- },
- getExpireDate: function(shareIndex) {
- return this._shareExpireDate(shareIndex);
- },
- /**
- * Returns all share entries that only apply to the current item
- * (file/folder)
- *
- * @return {Array.<OC.Share.Types.ShareInfo>}
- */
- getSharesWithCurrentItem: function() {
- var shares = this.get('shares') || [];
- var fileId = this.fileInfoModel.get('id');
- return _.filter(shares, function(share) {
- return share.item_source === fileId;
- });
- },
- /**
- * @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;
- },
- /**
- * @param shareIndex
- * @returns {string}
- */
- getSharedBy: function(shareIndex) {
- /** @type OC.Share.Types.ShareInfo **/
- var share = this.get('shares')[shareIndex];
- if(!_.isObject(share)) {
- throw "Unknown Share";
- }
- return share.uid_owner;
- },
- /**
- * @param shareIndex
- * @returns {string}
- */
- getSharedByDisplayName: function(shareIndex) {
- /** @type OC.Share.Types.ShareInfo **/
- var share = this.get('shares')[shareIndex];
- if(!_.isObject(share)) {
- throw "Unknown Share";
- }
- return share.displayname_owner;
- },
- /**
- * 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];
- 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";
- }
- return (share.permissions & permission) === permission;
- },
- _shareExpireDate: function(shareIndex) {
- var share = this.get('shares')[shareIndex];
- if(!_.isObject(share)) {
- throw "Unknown Share";
- }
- var date2 = share.expiration;
- return date2;
- },
- /**
- * @return {int}
- */
- getPermissions: function() {
- return this.get('permissions');
- },
- /**
- * @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);
- },
- hasReadPermission: function(shareIndex) {
- return this._shareHasPermission(shareIndex, OC.PERMISSION_READ);
- },
- /**
- * @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);
- },
- /**
- * @returns {int}
- */
- linkSharePermissions: function() {
- if (!this.hasLinkShare()) {
- return -1;
- } else {
- return this.get('linkShare').permissions;
- }
- },
- _getUrl: function(base, params) {
- params = _.extend({format: 'json'}, params || {});
- return OC.linkToOCS('apps/files_sharing/api/v1', 2) + base + '?' + OC.buildQueryString(params);
- },
- _fetchShares: function() {
- var path = this.fileInfoModel.getFullPath();
- return $.ajax({
- type: 'GET',
- url: this._getUrl('shares', {path: path, reshares: true})
- });
- },
- _fetchReshare: function() {
- // only fetch original share once
- if (!this._reshareFetched) {
- var path = this.fileInfoModel.getFullPath();
- this._reshareFetched = true;
- return $.ajax({
- type: 'GET',
- url: this._getUrl('shares', {path: path, shared_with_me: true})
- });
- } else {
- return $.Deferred().resolve([{
- ocs: {
- data: [this.get('reshare')]
- }
- }]);
- }
- },
- /**
- * Group reshares into a single super share element.
- * Does this by finding the most precise share and
- * combines the permissions to be the most permissive.
- *
- * @param {Array} reshares
- * @return {Object} reshare
- */
- _groupReshares: function(reshares) {
- if (!reshares || !reshares.length) {
- return false;
- }
- var superShare = reshares.shift();
- var combinedPermissions = superShare.permissions;
- _.each(reshares, function(reshare) {
- // use share have higher priority than group share
- if (reshare.share_type === OC.Share.SHARE_TYPE_USER && superShare.share_type === OC.Share.SHARE_TYPE_GROUP) {
- superShare = reshare;
- }
- combinedPermissions |= reshare.permissions;
- });
- superShare.permissions = combinedPermissions;
- return superShare;
- },
- fetch: function(options) {
- var model = this;
- this.trigger('request', this);
- var deferred = $.when(
- this._fetchShares(),
- this._fetchReshare()
- );
- deferred.done(function(data1, data2) {
- model.trigger('sync', 'GET', this);
- var sharesMap = {};
- _.each(data1[0].ocs.data, function(shareItem) {
- sharesMap[shareItem.id] = shareItem;
- });
- var reshare = false;
- if (data2[0].ocs.data.length) {
- reshare = model._groupReshares(data2[0].ocs.data);
- }
- model.set(model.parse({
- shares: sharesMap,
- reshare: reshare
- }));
- if(!_.isUndefined(options) && _.isFunction(options.success)) {
- options.success();
- }
- });
- return deferred;
- },
- /**
- * 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];
- OC.Share.currentShares = {};
- OC.Share.itemShares = [];
- 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');
- this.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;
- }
- });
- }
- 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) {
- if (value.share_type === OC.Share.SHARE_TYPE_LINK) {
- hideFileListStatus = (value.permissions & OC.PERMISSION_READ) ? false : true;
- return true;
- }
- });
- }
- /** @type {OC.Share.Types.ShareInfo[]} **/
- var shares = _.map(data.shares, function(share) {
- // properly parse some values because sometimes the server
- // returns integers as string...
- var i;
- for (i = 0; i < SHARE_RESPONSE_INT_PROPS.length; i++) {
- var prop = SHARE_RESPONSE_INT_PROPS[i];
- if (!_.isUndefined(share[prop])) {
- share[prop] = parseInt(share[prop], 10);
- }
- }
- return share;
- });
- 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) {
- /*
- * Ignore reshared link shares for now
- * FIXME: Find a way to display properly
- */
- if (share.uid_owner !== OC.currentUser) {
- return;
- }
- 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,
- id: share.id,
- 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,
- allowPublicEditingStatus: allowPublicEditingStatus,
- hideFileListStatus: hideFileListStatus
- };
- },
- /**
- * 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;
- },
- /**
- * Returns a list of share types from the existing shares.
- *
- * @return {Array.<int>} array of share types
- */
- getShareTypes: function() {
- var result;
- result = _.pluck(this.getSharesWithCurrentItem(), 'share_type');
- if (this.hasLinkShare()) {
- result.push(OC.Share.SHARE_TYPE_LINK);
- }
- return _.uniq(result);
- }
- });
- OC.Share.ShareItemModel = ShareItemModel;
- })();
|