aboutsummaryrefslogtreecommitdiffstats
path: root/apps/files_sharing/src/mixins
diff options
context:
space:
mode:
Diffstat (limited to 'apps/files_sharing/src/mixins')
-rw-r--r--apps/files_sharing/src/mixins/ShareDetails.js82
-rw-r--r--apps/files_sharing/src/mixins/ShareRequests.js72
-rw-r--r--apps/files_sharing/src/mixins/ShareTypes.js40
-rw-r--r--apps/files_sharing/src/mixins/SharesMixin.js335
4 files changed, 339 insertions, 190 deletions
diff --git a/apps/files_sharing/src/mixins/ShareDetails.js b/apps/files_sharing/src/mixins/ShareDetails.js
new file mode 100644
index 00000000000..6ccdf8d63d0
--- /dev/null
+++ b/apps/files_sharing/src/mixins/ShareDetails.js
@@ -0,0 +1,82 @@
+/**
+ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+import Share from '../models/Share.ts'
+import Config from '../services/ConfigService.ts'
+import { ATOMIC_PERMISSIONS } from '../lib/SharePermissionsToolBox.js'
+import logger from '../services/logger.ts'
+
+export default {
+ methods: {
+ async openSharingDetails(shareRequestObject) {
+ let share = {}
+ // handle externalResults from OCA.Sharing.ShareSearch
+ // TODO : Better name/interface for handler required
+ // For example `externalAppCreateShareHook` with proper documentation
+ if (shareRequestObject.handler) {
+ const handlerInput = {}
+ if (this.suggestions) {
+ handlerInput.suggestions = this.suggestions
+ handlerInput.fileInfo = this.fileInfo
+ handlerInput.query = this.query
+ }
+ const externalShareRequestObject = await shareRequestObject.handler(handlerInput)
+ share = this.mapShareRequestToShareObject(externalShareRequestObject)
+ } else {
+ share = this.mapShareRequestToShareObject(shareRequestObject)
+ }
+
+ if (this.fileInfo.type !== 'dir') {
+ const originalPermissions = share.permissions
+ const strippedPermissions = originalPermissions
+ & ~ATOMIC_PERMISSIONS.CREATE
+ & ~ATOMIC_PERMISSIONS.DELETE
+
+ if (originalPermissions !== strippedPermissions) {
+ logger.debug('Removed create/delete permissions from file share (only valid for folders)')
+ share.permissions = strippedPermissions
+ }
+ }
+
+ const shareDetails = {
+ fileInfo: this.fileInfo,
+ share,
+ }
+
+ this.$emit('open-sharing-details', shareDetails)
+ },
+ openShareDetailsForCustomSettings(share) {
+ share.setCustomPermissions = true
+ this.openSharingDetails(share)
+ },
+ mapShareRequestToShareObject(shareRequestObject) {
+
+ if (shareRequestObject.id) {
+ return shareRequestObject
+ }
+
+ const share = {
+ attributes: [
+ {
+ value: true,
+ key: 'download',
+ scope: 'permissions',
+ },
+ ],
+ hideDownload: false,
+ share_type: shareRequestObject.shareType,
+ share_with: shareRequestObject.shareWith,
+ is_no_user: shareRequestObject.isNoUser,
+ user: shareRequestObject.shareWith,
+ share_with_displayname: shareRequestObject.displayName,
+ subtitle: shareRequestObject.subtitle,
+ permissions: shareRequestObject.permissions ?? new Config().defaultPermissions,
+ expiration: '',
+ }
+
+ return new Share(share)
+ },
+ },
+}
diff --git a/apps/files_sharing/src/mixins/ShareRequests.js b/apps/files_sharing/src/mixins/ShareRequests.js
index e6867514c2a..2c33fa3b0c7 100644
--- a/apps/files_sharing/src/mixins/ShareRequests.js
+++ b/apps/files_sharing/src/mixins/ShareRequests.js
@@ -1,68 +1,55 @@
/**
- * @copyright Copyright (c) 2019 John Molakvoæ <skjnldsv@protonmail.com>
- *
- * @author John Molakvoæ <skjnldsv@protonmail.com>
- *
- * @license GNU AGPL version 3 or any later version
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program 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 program. If not, see <http://www.gnu.org/licenses/>.
- *
+ * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
// TODO: remove when ie not supported
import 'url-search-params-polyfill'
+import { emit } from '@nextcloud/event-bus'
+import { showError } from '@nextcloud/dialogs'
import { generateOcsUrl } from '@nextcloud/router'
import axios from '@nextcloud/axios'
-import Share from '../models/Share'
-const shareUrl = generateOcsUrl('apps/files_sharing/api/v1', 2) + 'shares'
-const headers = {
- 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
-}
+import Share from '../models/Share.ts'
+
+const shareUrl = generateOcsUrl('apps/files_sharing/api/v1/shares')
export default {
methods: {
/**
* Create a new share
*
- * @param {Object} data destructuring object
+ * @param {object} data destructuring object
* @param {string} data.path path to the file/folder which should be shared
* @param {number} data.shareType 0 = user; 1 = group; 3 = public link; 6 = federated cloud share
* @param {string} data.shareWith user/group id with which the file should be shared (optional for shareType > 1)
- * @param {boolean} [data.publicUpload=false] allow public upload to a public shared folder
+ * @param {boolean} [data.publicUpload] allow public upload to a public shared folder
* @param {string} [data.password] password to protect public link Share with
- * @param {number} [data.permissions=31] 1 = read; 2 = update; 4 = create; 8 = delete; 16 = share; 31 = all (default: 31, for public shares: 1)
- * @param {boolean} [data.sendPasswordByTalk=false] send the password via a talk conversation
- * @param {string} [data.expireDate=''] expire the shareautomatically after
- * @param {string} [data.label=''] custom label
- * @returns {Share} the new share
+ * @param {number} [data.permissions] 1 = read; 2 = update; 4 = create; 8 = delete; 16 = share; 31 = all (default: 31, for public shares: 1)
+ * @param {boolean} [data.sendPasswordByTalk] send the password via a talk conversation
+ * @param {string} [data.expireDate] expire the share automatically after
+ * @param {string} [data.label] custom label
+ * @param {string} [data.attributes] Share attributes encoded as json
+ * @param {string} data.note custom note to recipient
+ * @return {Share} the new share
* @throws {Error}
*/
- async createShare({ path, permissions, shareType, shareWith, publicUpload, password, sendPasswordByTalk, expireDate, label }) {
+ async createShare({ path, permissions, shareType, shareWith, publicUpload, password, sendPasswordByTalk, expireDate, label, note, attributes }) {
try {
- const request = await axios.post(shareUrl, { path, permissions, shareType, shareWith, publicUpload, password, sendPasswordByTalk, expireDate, label })
+ const request = await axios.post(shareUrl, { path, permissions, shareType, shareWith, publicUpload, password, sendPasswordByTalk, expireDate, label, note, attributes })
if (!request?.data?.ocs) {
throw request
}
- return new Share(request.data.ocs.data)
+ const share = new Share(request.data.ocs.data)
+ emit('files_sharing:share:created', { share })
+ return share
} catch (error) {
console.error('Error while creating share', error)
const errorMessage = error?.response?.data?.ocs?.meta?.message
- OC.Notification.showTemporary(
+ showError(
errorMessage ? t('files_sharing', 'Error creating the share: {errorMessage}', { errorMessage }) : t('files_sharing', 'Error creating the share'),
- { type: 'error' }
+ { type: 'error' },
)
throw error
}
@@ -80,13 +67,14 @@ export default {
if (!request?.data?.ocs) {
throw request
}
+ emit('files_sharing:share:deleted', { id })
return true
} catch (error) {
console.error('Error while deleting share', error)
const errorMessage = error?.response?.data?.ocs?.meta?.message
OC.Notification.showTemporary(
errorMessage ? t('files_sharing', 'Error deleting the share: {errorMessage}', { errorMessage }) : t('files_sharing', 'Error deleting the share'),
- { type: 'error' }
+ { type: 'error' },
)
throw error
}
@@ -96,22 +84,24 @@ export default {
* Update a share
*
* @param {number} id share id
- * @param {Object} properties key-value object of the properties to update
+ * @param {object} properties key-value object of the properties to update
*/
async updateShare(id, properties) {
try {
- const request = await axios.put(shareUrl + `/${id}`, properties, headers)
+ const request = await axios.put(shareUrl + `/${id}`, properties)
+ emit('files_sharing:share:updated', { id })
if (!request?.data?.ocs) {
throw request
+ } else {
+ return request.data.ocs.data
}
- return true
} catch (error) {
console.error('Error while updating share', error)
if (error.response.status !== 400) {
const errorMessage = error?.response?.data?.ocs?.meta?.message
OC.Notification.showTemporary(
errorMessage ? t('files_sharing', 'Error updating the share: {errorMessage}', { errorMessage }) : t('files_sharing', 'Error updating the share'),
- { type: 'error' }
+ { type: 'error' },
)
}
const message = error.response.data.ocs.meta.message
diff --git a/apps/files_sharing/src/mixins/ShareTypes.js b/apps/files_sharing/src/mixins/ShareTypes.js
deleted file mode 100644
index 6e9524ce952..00000000000
--- a/apps/files_sharing/src/mixins/ShareTypes.js
+++ /dev/null
@@ -1,40 +0,0 @@
-/**
- * @copyright Copyright (c) 2019 John Molakvoæ <skjnldsv@protonmail.com>
- *
- * @author John Molakvoæ <skjnldsv@protonmail.com>
- *
- * @license GNU AGPL version 3 or any later version
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program 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 program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-export default {
- data() {
- return {
- SHARE_TYPES: {
- SHARE_TYPE_USER: OC.Share.SHARE_TYPE_USER,
- SHARE_TYPE_GROUP: OC.Share.SHARE_TYPE_GROUP,
- SHARE_TYPE_LINK: OC.Share.SHARE_TYPE_LINK,
- SHARE_TYPE_EMAIL: OC.Share.SHARE_TYPE_EMAIL,
- SHARE_TYPE_REMOTE: OC.Share.SHARE_TYPE_REMOTE,
- SHARE_TYPE_CIRCLE: OC.Share.SHARE_TYPE_CIRCLE,
- SHARE_TYPE_GUEST: OC.Share.SHARE_TYPE_GUEST,
- SHARE_TYPE_DECK: OC.Share.SHARE_TYPE_DECK,
- SHARE_TYPE_REMOTE_GROUP: OC.Share.SHARE_TYPE_REMOTE_GROUP,
- SHARE_TYPE_ROOM: OC.Share.SHARE_TYPE_ROOM,
- },
- }
- },
-}
diff --git a/apps/files_sharing/src/mixins/SharesMixin.js b/apps/files_sharing/src/mixins/SharesMixin.js
index aef543a5fc6..a461da56d85 100644
--- a/apps/files_sharing/src/mixins/SharesMixin.js
+++ b/apps/files_sharing/src/mixins/SharesMixin.js
@@ -1,41 +1,34 @@
/**
- * @copyright Copyright (c) 2019 John Molakvoæ <skjnldsv@protonmail.com>
- *
- * @author John Molakvoæ <skjnldsv@protonmail.com>
- *
- * @license GNU AGPL version 3 or any later version
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program 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 program. If not, see <http://www.gnu.org/licenses/>.
- *
+ * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
+import { getCurrentUser } from '@nextcloud/auth'
+import { showError, showSuccess } from '@nextcloud/dialogs'
+import { ShareType } from '@nextcloud/sharing'
+import { emit } from '@nextcloud/event-bus'
+
import PQueue from 'p-queue'
import debounce from 'debounce'
-import Share from '../models/Share'
-import SharesRequests from './ShareRequests'
-import ShareTypes from './ShareTypes'
-import Config from '../services/ConfigService'
-import { getCurrentUser } from '@nextcloud/auth'
+import GeneratePassword from '../utils/GeneratePassword.ts'
+import Share from '../models/Share.ts'
+import SharesRequests from './ShareRequests.js'
+import Config from '../services/ConfigService.ts'
+import logger from '../services/logger.ts'
+
+import {
+ BUNDLED_PERMISSIONS,
+} from '../lib/SharePermissionsToolBox.js'
+import { fetchNode } from '../../../files/src/services/WebdavClient.ts'
export default {
- mixins: [SharesRequests, ShareTypes],
+ mixins: [SharesRequests],
props: {
fileInfo: {
type: Object,
- default: () => {},
+ default: () => { },
required: true,
},
share: {
@@ -51,6 +44,8 @@ export default {
data() {
return {
config: new Config(),
+ node: null,
+ ShareType,
// errors helpers
errors: {},
@@ -69,26 +64,17 @@ export default {
* ! do not remove it ot you'll lose all reactivity here
*/
reactiveState: this.share?.state,
-
- SHARE_TYPES: {
- SHARE_TYPE_USER: OC.Share.SHARE_TYPE_USER,
- SHARE_TYPE_GROUP: OC.Share.SHARE_TYPE_GROUP,
- SHARE_TYPE_LINK: OC.Share.SHARE_TYPE_LINK,
- SHARE_TYPE_EMAIL: OC.Share.SHARE_TYPE_EMAIL,
- SHARE_TYPE_REMOTE: OC.Share.SHARE_TYPE_REMOTE,
- SHARE_TYPE_CIRCLE: OC.Share.SHARE_TYPE_CIRCLE,
- SHARE_TYPE_GUEST: OC.Share.SHARE_TYPE_GUEST,
- SHARE_TYPE_REMOTE_GROUP: OC.Share.SHARE_TYPE_REMOTE_GROUP,
- SHARE_TYPE_ROOM: OC.Share.SHARE_TYPE_ROOM,
- },
}
},
computed: {
-
+ path() {
+ return (this.fileInfo.path + '/' + this.fileInfo.name).replace('//', '/')
+ },
/**
* Does the current share have a note
- * @returns {boolean}
+ *
+ * @return {boolean}
*/
hasNote: {
get() {
@@ -102,49 +88,119 @@ export default {
},
dateTomorrow() {
- return moment().add(1, 'days')
+ return new Date(new Date().setDate(new Date().getDate() + 1))
},
- /**
- * Datepicker lang values
- * https://github.com/nextcloud/nextcloud-vue/pull/146
- * TODO: have this in vue-components
- *
- * @returns {int}
- */
- firstDay() {
- return window.firstDay
- ? window.firstDay
- : 0 // sunday as default
- },
+ // Datepicker language
lang() {
- // fallback to default in case of unavailable data
+ const weekdaysShort = window.dayNamesShort
+ ? window.dayNamesShort // provided by Nextcloud
+ : ['Sun.', 'Mon.', 'Tue.', 'Wed.', 'Thu.', 'Fri.', 'Sat.']
+ const monthsShort = window.monthNamesShort
+ ? window.monthNamesShort // provided by Nextcloud
+ : ['Jan.', 'Feb.', 'Mar.', 'Apr.', 'May.', 'Jun.', 'Jul.', 'Aug.', 'Sep.', 'Oct.', 'Nov.', 'Dec.']
+ const firstDayOfWeek = window.firstDay ? window.firstDay : 0
+
return {
- days: window.dayNamesShort
- ? window.dayNamesShort // provided by nextcloud
- : ['Sun.', 'Mon.', 'Tue.', 'Wed.', 'Thu.', 'Fri.', 'Sat.'],
- months: window.monthNamesShort
- ? window.monthNamesShort // provided by nextcloud
- : ['Jan.', 'Feb.', 'Mar.', 'Apr.', 'May.', 'Jun.', 'Jul.', 'Aug.', 'Sep.', 'Oct.', 'Nov.', 'Dec.'],
- placeholder: {
- date: 'Select Date', // TODO: Translate
+ formatLocale: {
+ firstDayOfWeek,
+ monthsShort,
+ weekdaysMin: weekdaysShort,
+ weekdaysShort,
},
+ monthFormat: 'MMM',
}
},
-
+ isNewShare() {
+ return !this.share.id
+ },
+ isFolder() {
+ return this.fileInfo.type === 'dir'
+ },
+ isPublicShare() {
+ const shareType = this.share.shareType ?? this.share.type
+ return [ShareType.Link, ShareType.Email].includes(shareType)
+ },
+ isRemoteShare() {
+ return this.share.type === ShareType.RemoteGroup || this.share.type === ShareType.Remote
+ },
isShareOwner() {
return this.share && this.share.owner === getCurrentUser().uid
},
-
+ isExpiryDateEnforced() {
+ if (this.isPublicShare) {
+ return this.config.isDefaultExpireDateEnforced
+ }
+ if (this.isRemoteShare) {
+ return this.config.isDefaultRemoteExpireDateEnforced
+ }
+ return this.config.isDefaultInternalExpireDateEnforced
+ },
+ hasCustomPermissions() {
+ const bundledPermissions = [
+ BUNDLED_PERMISSIONS.ALL,
+ BUNDLED_PERMISSIONS.READ_ONLY,
+ BUNDLED_PERMISSIONS.FILE_DROP,
+ ]
+ return !bundledPermissions.includes(this.share.permissions)
+ },
+ maxExpirationDateEnforced() {
+ if (this.isExpiryDateEnforced) {
+ if (this.isPublicShare) {
+ return this.config.defaultExpirationDate
+ }
+ if (this.isRemoteShare) {
+ return this.config.defaultRemoteExpirationDateString
+ }
+ // If it get's here then it must be an internal share
+ return this.config.defaultInternalExpirationDate
+ }
+ return null
+ },
+ /**
+ * Is the current share password protected ?
+ *
+ * @return {boolean}
+ */
+ isPasswordProtected: {
+ get() {
+ return this.config.enforcePasswordForPublicLink
+ || this.share.password !== ''
+ || this.share.newPassword !== undefined
+ },
+ async set(enabled) {
+ if (enabled) {
+ this.$set(this.share, 'newPassword', await GeneratePassword(true))
+ } else {
+ this.share.password = ''
+ this.$delete(this.share, 'newPassword')
+ }
+ },
+ },
},
methods: {
/**
+ * Fetch WebDAV node
+ *
+ * @return {Node}
+ */
+ async getNode() {
+ const node = { path: this.path }
+ try {
+ this.node = await fetchNode(node.path)
+ logger.info('Fetched node:', { node: this.node })
+ } catch (error) {
+ logger.error('Error:', error)
+ }
+ },
+
+ /**
* Check if a share is valid before
* firing the request
*
* @param {Share} share the share to check
- * @returns {Boolean}
+ * @return {boolean}
*/
checkShare(share) {
if (share.password) {
@@ -153,7 +209,7 @@ export default {
}
}
if (share.expirationDate) {
- const date = moment(share.expirationDate)
+ const date = share.expirationDate
if (!date.isValid()) {
return false
}
@@ -162,33 +218,35 @@ export default {
},
/**
- * ActionInput can be a little tricky to work with.
- * Since we expect a string and not a Date,
- * we need to process the value here
- *
- * @param {Date} date js date to be parsed by moment.js
+ * @param {Date} date the date to format
+ * @return {string} date a date with YYYY-MM-DD format
*/
- onExpirationChange(date) {
- // format to YYYY-MM-DD
- const value = moment(date).format('YYYY-MM-DD')
- this.share.expireDate = value
- this.queueUpdate('expireDate')
+ formatDateToString(date) {
+ // Force utc time. Drop time information to be timezone-less
+ const utcDate = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()))
+ // Format to YYYY-MM-DD
+ return utcDate.toISOString().split('T')[0]
},
/**
- * Uncheck expire date
- * We need this method because @update:checked
- * is ran simultaneously as @uncheck, so
- * so we cannot ensure data is up-to-date
+ * Save given value to expireDate and trigger queueUpdate
+ *
+ * @param {Date} date
*/
- onExpirationDisable() {
- this.share.expireDate = ''
- this.queueUpdate('expireDate')
+ onExpirationChange(date) {
+ if (!date) {
+ this.share.expireDate = null
+ this.$set(this.share, 'expireDate', null)
+ return
+ }
+ const parsedDate = (date instanceof Date) ? date : new Date(date)
+ this.share.expireDate = this.formatDateToString(parsedDate)
},
/**
* Note changed, let's save it to a different key
- * @param {String} note the share note
+ *
+ * @param {string} note the share note
*/
onNoteChange(note) {
this.$set(this.share, 'newNote', note.trim())
@@ -197,7 +255,6 @@ export default {
/**
* When the note change, we trim, save and dispatch
*
- * @param {string} note the note
*/
onNoteSubmit() {
if (this.share.newNote) {
@@ -215,8 +272,14 @@ export default {
this.loading = true
this.open = false
await this.deleteShare(this.share.id)
- console.debug('Share deleted', this.share.id)
+ logger.debug('Share deleted', { shareId: this.share.id })
+ const message = this.share.itemType === 'file'
+ ? t('files_sharing', 'File "{path}" has been unshared', { path: this.share.path })
+ : t('files_sharing', 'Folder "{path}" has been unshared', { path: this.share.path })
+ showSuccess(message)
this.$emit('remove:share', this.share)
+ await this.getNode()
+ emit('files:node:updated', this.node)
} catch (error) {
// re-open menu if error
this.open = true
@@ -228,7 +291,7 @@ export default {
/**
* Send an update of the share to the queue
*
- * @param {string} propertyNames the properties to sync
+ * @param {Array<string>} propertyNames the properties to sync
*/
queueUpdate(...propertyNames) {
if (propertyNames.length === 0) {
@@ -240,38 +303,104 @@ export default {
const properties = {}
// force value to string because that is what our
// share api controller accepts
- propertyNames.map(p => (properties[p] = this.share[p].toString()))
+ for (const name of propertyNames) {
+ if (name === 'password') {
+ properties[name] = this.share.newPassword ?? this.share.password
+ continue
+ }
+
+ if (this.share[name] === null || this.share[name] === undefined) {
+ properties[name] = ''
+ } else if ((typeof this.share[name]) === 'object') {
+ properties[name] = JSON.stringify(this.share[name])
+ } else {
+ properties[name] = this.share[name].toString()
+ }
+ }
- this.updateQueue.add(async() => {
+ return this.updateQueue.add(async () => {
this.saving = true
this.errors = {}
try {
- await this.updateShare(this.share.id, properties)
+ const updatedShare = await this.updateShare(this.share.id, properties)
+
+ if (propertyNames.includes('password')) {
+ // reset password state after sync
+ this.share.password = this.share.newPassword ?? ''
+ this.$delete(this.share, 'newPassword')
+
+ // updates password expiration time after sync
+ this.share.passwordExpirationTime = updatedShare.password_expiration_time
+ }
// clear any previous errors
- this.$delete(this.errors, propertyNames[0])
+ for (const property of propertyNames) {
+ this.$delete(this.errors, property)
+ }
+ showSuccess(this.updateSuccessMessage(propertyNames))
+ } catch (error) {
+ logger.error('Could not update share', { error, share: this.share, propertyNames })
- // reset password state after sync
- this.$delete(this.share, 'newPassword')
- } catch ({ message }) {
+ const { message } = error
if (message && message !== '') {
- this.onSyncError(propertyNames[0], message)
+ for (const property of propertyNames) {
+ this.onSyncError(property, message)
+ }
+ showError(message)
+ } else {
+ // We do not have information what happened, but we should still inform the user
+ showError(t('files_sharing', 'Could not update share'))
}
} finally {
this.saving = false
}
})
- } else {
- console.error('Cannot update share.', this.share, 'No valid id')
+ }
+
+ // This share does not exists on the server yet
+ console.debug('Updated local share', this.share)
+ },
+
+ /**
+ * @param {string[]} names Properties changed
+ */
+ updateSuccessMessage(names) {
+ if (names.length !== 1) {
+ return t('files_sharing', 'Share saved')
+ }
+
+ switch (names[0]) {
+ case 'expireDate':
+ return t('files_sharing', 'Share expiry date saved')
+ case 'hideDownload':
+ return t('files_sharing', 'Share hide-download state saved')
+ case 'label':
+ return t('files_sharing', 'Share label saved')
+ case 'note':
+ return t('files_sharing', 'Share note for recipient saved')
+ case 'password':
+ return t('files_sharing', 'Share password saved')
+ case 'permissions':
+ return t('files_sharing', 'Share permissions saved')
+ default:
+ return t('files_sharing', 'Share saved')
}
},
/**
* Manage sync errors
+ *
* @param {string} property the errored property, e.g. 'password'
* @param {string} message the error message
*/
onSyncError(property, message) {
+ if (property === 'password' && this.share.newPassword) {
+ if (this.share.newPassword === this.share.password) {
+ this.share.password = ''
+ }
+ this.$delete(this.share, 'newPassword')
+ }
+
// re-open menu if closed
this.open = true
switch (property) {
@@ -306,7 +435,6 @@ export default {
}
}
},
-
/**
* Debounce queueUpdate to avoid requests spamming
* more importantly for text data
@@ -316,16 +444,5 @@ export default {
debounceQueueUpdate: debounce(function(property) {
this.queueUpdate(property)
}, 500),
-
- /**
- * Returns which dates are disabled for the datepicker
- * @param {Date} date date to check
- * @returns {boolean}
- */
- disabledDate(date) {
- const dateMoment = moment(date)
- return (this.dateTomorrow && dateMoment.isBefore(this.dateTomorrow, 'day'))
- || (this.dateMaxEnforced && dateMoment.isSameOrAfter(this.dateMaxEnforced, 'day'))
- },
},
}