aboutsummaryrefslogtreecommitdiffstats
path: root/apps/files_sharing/src
diff options
context:
space:
mode:
authorRoeland Jago Douma <rullzer@users.noreply.github.com>2021-03-22 07:56:10 +0100
committerGitHub <noreply@github.com>2021-03-22 07:56:10 +0100
commitd3647dcc17ca585bb5cf4758014d72890f1f3c3e (patch)
tree581c2bac717f7123bd21bf7558c165df35904181 /apps/files_sharing/src
parentae19cb168f9d3c45964ed50d868258798edb3f55 (diff)
parent15767643f253f5e685cb01b2980cfbde0b7aacea (diff)
downloadnextcloud-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.vue63
-rw-r--r--apps/files_sharing/src/components/SharingInput.vue40
-rw-r--r--apps/files_sharing/src/utils/GeneratePassword.js56
-rw-r--r--apps/files_sharing/src/views/SharingTab.vue35
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)
+ }
+ })
},
},
}