diff options
author | Roeland Jago Douma <rullzer@users.noreply.github.com> | 2021-03-22 07:56:10 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-03-22 07:56:10 +0100 |
commit | d3647dcc17ca585bb5cf4758014d72890f1f3c3e (patch) | |
tree | 581c2bac717f7123bd21bf7558c165df35904181 /apps/files_sharing/src | |
parent | ae19cb168f9d3c45964ed50d868258798edb3f55 (diff) | |
parent | 15767643f253f5e685cb01b2980cfbde0b7aacea (diff) | |
download | nextcloud-server-d3647dcc17ca585bb5cf4758014d72890f1f3c3e.tar.gz nextcloud-server-d3647dcc17ca585bb5cf4758014d72890f1f3c3e.zip |
Merge pull request #24364 from nextcloud/fix/sharing-mail-link-parity
Sharing link & mail parity
Diffstat (limited to 'apps/files_sharing/src')
-rw-r--r-- | apps/files_sharing/src/components/SharingEntryLink.vue | 63 | ||||
-rw-r--r-- | apps/files_sharing/src/components/SharingInput.vue | 40 | ||||
-rw-r--r-- | apps/files_sharing/src/utils/GeneratePassword.js | 56 | ||||
-rw-r--r-- | apps/files_sharing/src/views/SharingTab.vue | 35 |
4 files changed, 149 insertions, 45 deletions
diff --git a/apps/files_sharing/src/components/SharingEntryLink.vue b/apps/files_sharing/src/components/SharingEntryLink.vue index 44c3c986e17..c214bc12503 100644 --- a/apps/files_sharing/src/components/SharingEntryLink.vue +++ b/apps/files_sharing/src/components/SharingEntryLink.vue @@ -29,6 +29,9 @@ <h5 :title="title"> {{ title }} </h5> + <p v-if="subtitle"> + {{ subtitle }} + </p> </div> <!-- clipboard --> @@ -321,7 +324,6 @@ <script> import { generateUrl } from '@nextcloud/router' -import axios from '@nextcloud/axios' import Vue from 'vue' import ActionButton from '@nextcloud/vue/dist/Components/ActionButton' @@ -335,11 +337,10 @@ import Actions from '@nextcloud/vue/dist/Components/Actions' import Avatar from '@nextcloud/vue/dist/Components/Avatar' import Tooltip from '@nextcloud/vue/dist/Directives/Tooltip' +import GeneratePassword from '../utils/GeneratePassword' import Share from '../models/Share' import SharesMixin from '../mixins/SharesMixin' -const passwordSet = 'abcdefgijkmnopqrstwxyzABCDEFGHJKLMNPQRSTWXYZ23456789' - export default { name: 'SharingEntryLink', @@ -406,7 +407,6 @@ export default { /** * Link share label - * TODO: allow editing * @returns {string} */ title() { @@ -424,6 +424,11 @@ export default { }) } if (this.share.label && this.share.label.trim() !== '') { + if (this.isEmailShareType) { + return t('files_sharing', 'Mail share ({label})', { + label: this.share.label.trim(), + }) + } return t('files_sharing', 'Share link ({label})', { label: this.share.label.trim(), }) @@ -436,6 +441,18 @@ export default { }, /** + * Show the email on a second line if a label is set for mail shares + * @returns {string} + */ + subtitle() { + if (this.isEmailShareType + && this.title !== this.share.shareWith) { + return this.share.shareWith + } + return null + }, + + /** * Does the current share have an expiration date * @returns {boolean} */ @@ -472,7 +489,7 @@ export default { }, async set(enabled) { // TODO: directly save after generation to make sure the share is always protected - Vue.set(this.share, 'password', enabled ? await this.generatePassword() : '') + Vue.set(this.share, 'password', enabled ? await GeneratePassword() : '') Vue.set(this.share, 'newPassword', this.share.password) }, }, @@ -635,7 +652,7 @@ export default { shareDefaults.expiration = this.config.defaultExpirationDateString } if (this.config.enableLinkPasswordByDefault) { - shareDefaults.password = await this.generatePassword() + shareDefaults.password = await GeneratePassword() } // do not push yet if we need a password or an expiration date: show pending menu @@ -658,7 +675,7 @@ export default { // ELSE, show the pending popovermenu // if password enforced, pre-fill with random one if (this.config.enforcePasswordForPublicLink) { - shareDefaults.password = await this.generatePassword() + shareDefaults.password = await GeneratePassword() } // create share & close menu @@ -781,35 +798,6 @@ export default { this.queueUpdate('label') } }, - - /** - * Generate a valid policy password or - * request a valid password if password_policy - * is enabled - * - * @returns {string} a valid password - */ - async generatePassword() { - // password policy is enabled, let's request a pass - if (this.config.passwordPolicy.api && this.config.passwordPolicy.api.generate) { - try { - const request = await axios.get(this.config.passwordPolicy.api.generate) - if (request.data.ocs.data.password) { - return request.data.ocs.data.password - } - } catch (error) { - console.info('Error generating password from password_policy', error) - } - } - - // generate password of 10 length based on passwordSet - return Array(10).fill(0) - .reduce((prev, curr) => { - prev += passwordSet.charAt(Math.floor(Math.random() * passwordSet.length)) - return prev - }, '') - }, - async copyLink() { try { await this.$copyText(this.shareLink) @@ -933,6 +921,9 @@ export default { overflow: hidden; white-space: nowrap; } + p { + color: var(--color-text-maxcontrast); + } } &:not(.sharing-entry--share) &__actions { diff --git a/apps/files_sharing/src/components/SharingInput.vue b/apps/files_sharing/src/components/SharingInput.vue index ab079369f73..b30301d2290 100644 --- a/apps/files_sharing/src/components/SharingInput.vue +++ b/apps/files_sharing/src/components/SharingInput.vue @@ -56,6 +56,7 @@ import debounce from 'debounce' import Multiselect from '@nextcloud/vue/dist/Components/Multiselect' import Config from '../services/ConfigService' +import GeneratePassword from '../utils/GeneratePassword' import Share from '../models/Share' import ShareRequests from '../mixins/ShareRequests' import ShareTypes from '../mixins/ShareTypes' @@ -448,9 +449,6 @@ export default { return true } - // TODO: reset the search string when done - // https://github.com/shentao/vue-multiselect/issues/633 - // handle externalResults from OCA.Sharing.ShareSearch if (value.handler) { const share = await value.handler(this) @@ -459,25 +457,55 @@ export default { } this.loading = true + console.debug('Adding a new share from the input for', value) try { + let password = null + + if (this.config.enforcePasswordForPublicLink + && value.shareType === this.SHARE_TYPES.SHARE_TYPE_EMAIL) { + password = await GeneratePassword() + } + const path = (this.fileInfo.path + '/' + this.fileInfo.name).replace('//', '/') const share = await this.createShare({ path, shareType: value.shareType, shareWith: value.shareWith, + password, permissions: this.fileInfo.sharePermissions & OC.getCapabilities().files_sharing.default_permissions, }) - this.$emit('add:share', share) - this.getRecommendations() + // If we had a password, we need to show it to the user as it was generated + if (password) { + share.newPassword = password + // Wait for the newly added share + const component = await new Promise(resolve => { + this.$emit('add:share', share, resolve) + }) + + // open the menu on the + // freshly created share component + component.open = true + } else { + // Else we just add it normally + this.$emit('add:share', share) + } + + // reset the search string when done + // FIXME: https://github.com/shentao/vue-multiselect/issues/633 + if (this.$refs.multiselect?.$refs?.VueMultiselect?.search) { + this.$refs.multiselect.$refs.VueMultiselect.search = '' + } - } catch (response) { + await this.getRecommendations() + } catch (error) { // focus back if any error const input = this.$refs.multiselect.$el.querySelector('input') if (input) { input.focus() } this.query = value.shareWith + console.error('Error while adding new share', error) } finally { this.loading = false } diff --git a/apps/files_sharing/src/utils/GeneratePassword.js b/apps/files_sharing/src/utils/GeneratePassword.js new file mode 100644 index 00000000000..6e1a79f3f51 --- /dev/null +++ b/apps/files_sharing/src/utils/GeneratePassword.js @@ -0,0 +1,56 @@ + +/** + * @copyright Copyright (c) 2020 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/>. + * + */ + +import axios from '@nextcloud/axios' +import Config from '../services/ConfigService' + +const config = new Config() +const passwordSet = 'abcdefgijkmnopqrstwxyzABCDEFGHJKLMNPQRSTWXYZ23456789' + +/** + * Generate a valid policy password or + * request a valid password if password_policy + * is enabled + * + * @returns {string} a valid password + */ +export default async function() { + // password policy is enabled, let's request a pass + if (config.passwordPolicy.api && config.passwordPolicy.api.generate) { + try { + const request = await axios.get(config.passwordPolicy.api.generate) + if (request.data.ocs.data.password) { + return request.data.ocs.data.password + } + } catch (error) { + console.info('Error generating password from password_policy', error) + } + } + + // generate password of 10 length based on passwordSet + return Array(10).fill(0) + .reduce((prev, curr) => { + prev += passwordSet.charAt(Math.floor(Math.random() * passwordSet.length)) + return prev + }, '') +} diff --git a/apps/files_sharing/src/views/SharingTab.vue b/apps/files_sharing/src/views/SharingTab.vue index 979e296d8f9..8a8d6ecf46a 100644 --- a/apps/files_sharing/src/views/SharingTab.vue +++ b/apps/files_sharing/src/views/SharingTab.vue @@ -52,12 +52,14 @@ <!-- link shares list --> <SharingLinkList v-if="!loading" + ref="linkShareList" :can-reshare="canReshare" :file-info="fileInfo" :shares="linkShares" /> <!-- other shares list --> <SharingList v-if="!loading" + ref="shareList" :shares="shares" :file-info="fileInfo" /> @@ -295,11 +297,13 @@ export default { }, /** - * Insert share at top of arrays + * Add a new share into the shares list + * and return the newly created share component * - * @param {Share} share the share to insert + * @param {Share} share the share to add to the array + * @param {Function} resolve a function to run after the share is added and its component initialized */ - addShare(share) { + addShare(share, resolve) { // only catching share type MAIL as link shares are added differently // meaning: not from the ShareInput if (share.type === this.SHARE_TYPES.SHARE_TYPE_EMAIL) { @@ -307,6 +311,31 @@ export default { } else { this.shares.unshift(share) } + this.awaitForShare(share, resolve) + }, + + /** + * Await for next tick and render after the list updated + * Then resolve with the matched vue component of the + * provided share object + * + * @param {Share} share newly created share + * @param {Function} resolve a function to execute after + */ + awaitForShare(share, resolve) { + let listComponent = this.$refs.shareList + // Only mail shares comes from the input, link shares + // are managed internally in the SharingLinkList component + if (share.type === this.SHARE_TYPES.SHARE_TYPE_EMAIL) { + listComponent = this.$refs.linkShareList + } + + this.$nextTick(() => { + const newShare = listComponent.$children.find(component => component.share === share) + if (newShare) { + resolve(newShare) + } + }) }, }, } |