summaryrefslogtreecommitdiffstats
path: root/apps
diff options
context:
space:
mode:
authorArthur Schiwon <blizzz@arthur-schiwon.de>2023-08-31 12:22:10 +0200
committerArthur Schiwon <blizzz@arthur-schiwon.de>2023-08-31 12:22:10 +0200
commit7cdafeac363329cea0baa0f440923316cf2770f3 (patch)
tree62d28ce147efd55846472694adfb6870ed6b8403 /apps
parentbfd7b49be7bdfbe79c3b093c6bc3cdef476c669c (diff)
downloadnextcloud-server-7cdafeac363329cea0baa0f440923316cf2770f3.tar.gz
nextcloud-server-7cdafeac363329cea0baa0f440923316cf2770f3.zip
Revert "Improve sharing flow"
This reverts commit 8b42fb033fdcd3775b4850de6faf6091c8dcc716.
Diffstat (limited to 'apps')
-rw-r--r--apps/files_sharing/src/components/SharingEntry.vue407
-rw-r--r--apps/files_sharing/src/components/SharingEntryLink.vue178
-rw-r--r--apps/files_sharing/src/components/SharingEntryQuickShareSelect.vue186
-rw-r--r--apps/files_sharing/src/components/SharingEntrySimple.vue4
-rw-r--r--apps/files_sharing/src/components/SharingInput.vue10
-rw-r--r--apps/files_sharing/src/lib/SharePermissionsToolBox.js1
-rw-r--r--apps/files_sharing/src/mixins/ShareDetails.js43
-rw-r--r--apps/files_sharing/src/mixins/ShareRequests.js23
-rw-r--r--apps/files_sharing/src/mixins/SharesMixin.js28
-rw-r--r--apps/files_sharing/src/models/Share.js2
-rw-r--r--apps/files_sharing/src/utils/SharedWithMe.js10
-rw-r--r--apps/files_sharing/src/views/SharingDetailsTab.vue1053
-rw-r--r--apps/files_sharing/src/views/SharingLinkList.vue6
-rw-r--r--apps/files_sharing/src/views/SharingList.vue22
-rw-r--r--apps/files_sharing/src/views/SharingTab.vue49
15 files changed, 554 insertions, 1468 deletions
diff --git a/apps/files_sharing/src/components/SharingEntry.vue b/apps/files_sharing/src/components/SharingEntry.vue
index 7399617a79c..46b65c695ee 100644
--- a/apps/files_sharing/src/components/SharingEntry.vue
+++ b/apps/files_sharing/src/components/SharingEntry.vue
@@ -29,64 +29,147 @@
:menu-position="'left'"
:url="share.shareWithAvatar" />
- <div class="sharing-entry__summary" @click.prevent="toggleQuickShareSelect">
- <component :is="share.shareWithLink ? 'a' : 'div'"
- :title="tooltip"
- :aria-label="tooltip"
- :href="share.shareWithLink"
- class="sharing-entry__desc">
- <span>{{ title }}<span v-if="!isUnique" class="sharing-entry__desc-unique"> ({{
- share.shareWithDisplayNameUnique }})</span></span>
- <p v-if="hasStatus">
- <span>{{ share.status.icon || '' }}</span>
- <span>{{ share.status.message || '' }}</span>
- </p>
- </component>
- <QuickShareSelect :share="share"
- :file-info="fileInfo"
- :toggle="showDropdown"
- @open-sharing-details="openShareDetailsForCustomSettings(share)" />
- </div>
- <NcButton class="sharing-entry__action"
- :aria-label="t('files_sharing', 'Open Sharing Details')"
- type="tertiary-no-background"
- @click="openSharingDetails(share)">
- <template #icon>
- <DotsHorizontalIcon :size="20" />
+ <component :is="share.shareWithLink ? 'a' : 'div'"
+ :title="tooltip"
+ :aria-label="tooltip"
+ :href="share.shareWithLink"
+ class="sharing-entry__desc">
+ <span>{{ title }}<span v-if="!isUnique" class="sharing-entry__desc-unique"> ({{ share.shareWithDisplayNameUnique }})</span></span>
+ <p v-if="hasStatus">
+ <span>{{ share.status.icon || '' }}</span>
+ <span>{{ share.status.message || '' }}</span>
+ </p>
+ </component>
+ <NcActions menu-align="right"
+ class="sharing-entry__actions"
+ @close="onMenuClose">
+ <template v-if="share.canEdit">
+ <!-- edit permission -->
+ <NcActionCheckbox ref="canEdit"
+ :checked.sync="canEdit"
+ :value="permissionsEdit"
+ :disabled="saving || !canSetEdit">
+ {{ t('files_sharing', 'Allow editing') }}
+ </NcActionCheckbox>
+
+ <!-- create permission -->
+ <NcActionCheckbox v-if="isFolder"
+ ref="canCreate"
+ :checked.sync="canCreate"
+ :value="permissionsCreate"
+ :disabled="saving || !canSetCreate">
+ {{ t('files_sharing', 'Allow creating') }}
+ </NcActionCheckbox>
+
+ <!-- delete permission -->
+ <NcActionCheckbox v-if="isFolder"
+ ref="canDelete"
+ :checked.sync="canDelete"
+ :value="permissionsDelete"
+ :disabled="saving || !canSetDelete">
+ {{ t('files_sharing', 'Allow deleting') }}
+ </NcActionCheckbox>
+
+ <!-- reshare permission -->
+ <NcActionCheckbox v-if="config.isResharingAllowed"
+ ref="canReshare"
+ :checked.sync="canReshare"
+ :value="permissionsShare"
+ :disabled="saving || !canSetReshare">
+ {{ t('files_sharing', 'Allow resharing') }}
+ </NcActionCheckbox>
+
+ <NcActionCheckbox v-if="isSetDownloadButtonVisible"
+ ref="canDownload"
+ :checked.sync="canDownload"
+ :disabled="saving || !canSetDownload">
+ {{ allowDownloadText }}
+ </NcActionCheckbox>
+
+ <!-- expiration date -->
+ <NcActionCheckbox :checked.sync="hasExpirationDate"
+ :disabled="config.isDefaultInternalExpireDateEnforced || saving"
+ @uncheck="onExpirationDisable">
+ {{ config.isDefaultInternalExpireDateEnforced
+ ? t('files_sharing', 'Expiration date enforced')
+ : t('files_sharing', 'Set expiration date') }}
+ </NcActionCheckbox>
+ <NcActionInput v-if="hasExpirationDate"
+ ref="expireDate"
+ :is-native-picker="true"
+ :hide-label="true"
+ :class="{ error: errors.expireDate}"
+ :disabled="saving"
+ :value="new Date(share.expireDate)"
+ type="date"
+ :min="dateTomorrow"
+ :max="dateMaxEnforced"
+ @input="onExpirationChange">
+ {{ t('files_sharing', 'Enter a date') }}
+ </NcActionInput>
+
+ <!-- note -->
+ <template v-if="canHaveNote">
+ <NcActionCheckbox :checked.sync="hasNote"
+ :disabled="saving"
+ @uncheck="queueUpdate('note')">
+ {{ t('files_sharing', 'Note to recipient') }}
+ </NcActionCheckbox>
+ <NcActionTextEditable v-if="hasNote"
+ ref="note"
+ :class="{ error: errors.note}"
+ :disabled="saving"
+ :value="share.newNote || share.note"
+ icon="icon-edit"
+ @update:value="onNoteChange"
+ @submit="onNoteSubmit" />
+ </template>
</template>
- </NcButton>
+
+ <NcActionButton v-if="share.canDelete"
+ icon="icon-close"
+ :disabled="saving"
+ @click.prevent="onDelete">
+ {{ t('files_sharing', 'Unshare') }}
+ </NcActionButton>
+ </NcActions>
</li>
</template>
<script>
-import NcButton from '@nextcloud/vue/dist/Components/NcButton.js'
-import NcSelect from '@nextcloud/vue/dist/Components/NcSelect.js'
import NcAvatar from '@nextcloud/vue/dist/Components/NcAvatar.js'
-import DotsHorizontalIcon from 'vue-material-design-icons/DotsHorizontal.vue'
-
-import QuickShareSelect from './SharingEntryQuickShareSelect.vue'
+import NcActions from '@nextcloud/vue/dist/Components/NcActions.js'
+import NcActionButton from '@nextcloud/vue/dist/Components/NcActionButton.js'
+import NcActionCheckbox from '@nextcloud/vue/dist/Components/NcActionCheckbox.js'
+import NcActionInput from '@nextcloud/vue/dist/Components/NcActionInput.js'
+import NcActionTextEditable from '@nextcloud/vue/dist/Components/NcActionTextEditable.js'
import SharesMixin from '../mixins/SharesMixin.js'
-import ShareDetails from '../mixins/ShareDetails.js'
export default {
name: 'SharingEntry',
components: {
- NcButton,
+ NcActions,
+ NcActionButton,
+ NcActionCheckbox,
+ NcActionInput,
+ NcActionTextEditable,
NcAvatar,
- DotsHorizontalIcon,
- NcSelect,
- QuickShareSelect,
},
- mixins: [SharesMixin, ShareDetails],
+ mixins: [SharesMixin],
data() {
return {
- showDropdown: false,
+ permissionsEdit: OC.PERMISSION_UPDATE,
+ permissionsCreate: OC.PERMISSION_CREATE,
+ permissionsDelete: OC.PERMISSION_DELETE,
+ permissionsRead: OC.PERMISSION_READ,
+ permissionsShare: OC.PERMISSION_SHARE,
}
},
+
computed: {
title() {
let title = this.share.shareWithDisplayName
@@ -103,6 +186,7 @@ export default {
}
return title
},
+
tooltip() {
if (this.share.owner !== this.share.uidFileOwner) {
const data = {
@@ -122,6 +206,182 @@ export default {
return null
},
+ canHaveNote() {
+ return !this.isRemote
+ },
+
+ isRemote() {
+ return this.share.type === this.SHARE_TYPES.SHARE_TYPE_REMOTE
+ || this.share.type === this.SHARE_TYPES.SHARE_TYPE_REMOTE_GROUP
+ },
+
+ /**
+ * Can the sharer set whether the sharee can edit the file ?
+ *
+ * @return {boolean}
+ */
+ canSetEdit() {
+ // If the owner revoked the permission after the resharer granted it
+ // the share still has the permission, and the resharer is still
+ // allowed to revoke it too (but not to grant it again).
+ return (this.fileInfo.sharePermissions & OC.PERMISSION_UPDATE) || this.canEdit
+ },
+
+ /**
+ * Can the sharer set whether the sharee can create the file ?
+ *
+ * @return {boolean}
+ */
+ canSetCreate() {
+ // If the owner revoked the permission after the resharer granted it
+ // the share still has the permission, and the resharer is still
+ // allowed to revoke it too (but not to grant it again).
+ return (this.fileInfo.sharePermissions & OC.PERMISSION_CREATE) || this.canCreate
+ },
+
+ /**
+ * Can the sharer set whether the sharee can delete the file ?
+ *
+ * @return {boolean}
+ */
+ canSetDelete() {
+ // If the owner revoked the permission after the resharer granted it
+ // the share still has the permission, and the resharer is still
+ // allowed to revoke it too (but not to grant it again).
+ return (this.fileInfo.sharePermissions & OC.PERMISSION_DELETE) || this.canDelete
+ },
+
+ /**
+ * Can the sharer set whether the sharee can reshare the file ?
+ *
+ * @return {boolean}
+ */
+ canSetReshare() {
+ // If the owner revoked the permission after the resharer granted it
+ // the share still has the permission, and the resharer is still
+ // allowed to revoke it too (but not to grant it again).
+ return (this.fileInfo.sharePermissions & OC.PERMISSION_SHARE) || this.canReshare
+ },
+
+ /**
+ * Can the sharer set whether the sharee can download the file ?
+ *
+ * @return {boolean}
+ */
+ canSetDownload() {
+ // If the owner revoked the permission after the resharer granted it
+ // the share still has the permission, and the resharer is still
+ // allowed to revoke it too (but not to grant it again).
+ return (this.fileInfo.canDownload() || this.canDownload)
+ },
+
+ /**
+ * Can the sharee edit the shared file ?
+ */
+ canEdit: {
+ get() {
+ return this.share.hasUpdatePermission
+ },
+ set(checked) {
+ this.updatePermissions({ isEditChecked: checked })
+ },
+ },
+
+ /**
+ * Can the sharee create the shared file ?
+ */
+ canCreate: {
+ get() {
+ return this.share.hasCreatePermission
+ },
+ set(checked) {
+ this.updatePermissions({ isCreateChecked: checked })
+ },
+ },
+
+ /**
+ * Can the sharee delete the shared file ?
+ */
+ canDelete: {
+ get() {
+ return this.share.hasDeletePermission
+ },
+ set(checked) {
+ this.updatePermissions({ isDeleteChecked: checked })
+ },
+ },
+
+ /**
+ * Can the sharee reshare the file ?
+ */
+ canReshare: {
+ get() {
+ return this.share.hasSharePermission
+ },
+ set(checked) {
+ this.updatePermissions({ isReshareChecked: checked })
+ },
+ },
+
+ /**
+ * Can the sharee download files or only view them ?
+ */
+ canDownload: {
+ get() {
+ return this.share.hasDownloadPermission
+ },
+ set(checked) {
+ this.updatePermissions({ isDownloadChecked: checked })
+ },
+ },
+
+ /**
+ * Is this share readable
+ * Needed for some federated shares that might have been added from file drop links
+ */
+ hasRead: {
+ get() {
+ return this.share.hasReadPermission
+ },
+ },
+
+ /**
+ * Is the current share a folder ?
+ *
+ * @return {boolean}
+ */
+ isFolder() {
+ return this.fileInfo.type === 'dir'
+ },
+
+ /**
+ * Does the current share have an expiration date
+ *
+ * @return {boolean}
+ */
+ hasExpirationDate: {
+ get() {
+ return this.config.isDefaultInternalExpireDateEnforced || !!this.share.expireDate
+ },
+ set(enabled) {
+ const defaultExpirationDate = this.config.defaultInternalExpirationDate
+ || new Date(new Date().setDate(new Date().getDate() + 1))
+ this.share.expireDate = enabled
+ ? this.formatDateToString(defaultExpirationDate)
+ : ''
+ console.debug('Expiration date status', enabled, this.share.expireDate)
+ },
+ },
+
+ dateMaxEnforced() {
+ if (!this.isRemote && this.config.isDefaultInternalExpireDateEnforced) {
+ return new Date(new Date().setDate(new Date().getDate() + 1 + this.config.defaultInternalExpireDate))
+ } else if (this.config.isDefaultRemoteExpireDateEnforced) {
+ return new Date(new Date().setDate(new Date().getDate() + 1 + this.config.defaultRemoteExpireDate))
+ }
+ return null
+ },
+
/**
* @return {boolean}
*/
@@ -132,18 +392,70 @@ export default {
return (typeof this.share.status === 'object' && !Array.isArray(this.share.status))
},
+
+ /**
+ * @return {string}
+ */
+ allowDownloadText() {
+ return t('files_sharing', 'Allow download')
+ },
+
+ /**
+ * @return {boolean}
+ */
+ isSetDownloadButtonVisible() {
+ // TODO: Implement download permission for circle shares instead of hiding the option.
+ // https://github.com/nextcloud/server/issues/39161
+ if (this.share && this.share.type === this.SHARE_TYPES.SHARE_TYPE_CIRCLE) {
+ return false
+ }
+
+ const allowedMimetypes = [
+ // Office documents
+ 'application/msword',
+ 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
+ 'application/vnd.ms-powerpoint',
+ 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
+ 'application/vnd.ms-excel',
+ 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
+ 'application/vnd.oasis.opendocument.text',
+ 'application/vnd.oasis.opendocument.spreadsheet',
+ 'application/vnd.oasis.opendocument.presentation',
+ ]
+
+ return this.isFolder || allowedMimetypes.includes(this.fileInfo.mimetype)
+ },
},
methods: {
+ updatePermissions({
+ isEditChecked = this.canEdit,
+ isCreateChecked = this.canCreate,
+ isDeleteChecked = this.canDelete,
+ isReshareChecked = this.canReshare,
+ isDownloadChecked = this.canDownload,
+ } = {}) {
+ // calc permissions if checked
+ const permissions = 0
+ | (this.hasRead ? this.permissionsRead : 0)
+ | (isCreateChecked ? this.permissionsCreate : 0)
+ | (isDeleteChecked ? this.permissionsDelete : 0)
+ | (isEditChecked ? this.permissionsEdit : 0)
+ | (isReshareChecked ? this.permissionsShare : 0)
+
+ this.share.permissions = permissions
+ if (this.share.hasDownloadPermission !== isDownloadChecked) {
+ this.share.hasDownloadPermission = isDownloadChecked
+ }
+ this.queueUpdate('permissions', 'attributes')
+ },
+
/**
* Save potential changed data on menu close
*/
onMenuClose() {
this.onNoteSubmit()
},
- toggleQuickShareSelect() {
- this.showDropdown = !this.showDropdown
- },
},
}
</script>
@@ -153,34 +465,21 @@ export default {
display: flex;
align-items: center;
height: 44px;
-
&__desc {
display: flex;
flex-direction: column;
justify-content: space-between;
- padding-bottom: 0;
+ padding: 8px;
line-height: 1.2em;
-
p {
color: var(--color-text-maxcontrast);
}
-
&-unique {
color: var(--color-text-maxcontrast);
}
}
-
&__actions {
margin-left: auto;
}
-
- &__summary {
- padding: 8px;
- display: flex;
- flex-direction: column;
- justify-content: center;
- width: 100%;
- }
-
}
</style>
diff --git a/apps/files_sharing/src/components/SharingEntryLink.vue b/apps/files_sharing/src/components/SharingEntryLink.vue
index 7cf0804b841..06c9cb70851 100644
--- a/apps/files_sharing/src/components/SharingEntryLink.vue
+++ b/apps/files_sharing/src/components/SharingEntryLink.vue
@@ -25,18 +25,13 @@
<NcAvatar :is-no-user="true"
:icon-class="isEmailShareType ? 'avatar-link-share icon-mail-white' : 'avatar-link-share icon-public-white'"
class="sharing-entry__avatar" />
- <div class="sharing-entry__desc" @click.prevent="toggleQuickShareSelect">
+ <div class="sharing-entry__desc">
<span class="sharing-entry__title" :title="title">
{{ title }}
</span>
<p v-if="subtitle">
{{ subtitle }}
</p>
- <QuickShareSelect v-if="share && share.permissions !== undefined"
- :share="share"
- :file-info="fileInfo"
- :toggle="showDropdown"
- @open-sharing-details="openShareDetailsForCustomSettings(share)" />
</div>
<!-- clipboard -->
@@ -128,13 +123,110 @@
@close="onMenuClose">
<template v-if="share">
<template v-if="share.canEdit && canReshare">
- <NcActionButton :disabled="saving"
- @click.prevent="openSharingDetails">
- <template #icon>
- <Tune />
- </template>
- {{ t('files_sharing', 'Customize link') }}
- </NcActionButton>
+ <!-- Custom Label -->
+ <NcActionInput ref="label"
+ :class="{ error: errors.label }"
+ :disabled="saving"
+ :label="t('files_sharing', 'Share label')"
+ :value="share.newLabel !== undefined ? share.newLabel : share.label"
+ icon="icon-edit"
+ maxlength="255"
+ @update:value="onLabelChange"
+ @submit="onLabelSubmit" />
+
+ <SharePermissionsEditor :can-reshare="canReshare"
+ :share.sync="share"
+ :file-info="fileInfo" />
+
+ <NcActionSeparator />
+
+ <NcActionCheckbox :checked.sync="share.hideDownload"
+ :disabled="saving || canChangeHideDownload"
+ @change="queueUpdate('hideDownload')">
+ {{ t('files_sharing', 'Hide download') }}
+ </NcActionCheckbox>
+
+ <!-- password -->
+ <NcActionCheckbox :checked.sync="isPasswordProtected"
+ :disabled="config.enforcePasswordForPublicLink || saving"
+ class="share-link-password-checkbox"
+ @uncheck="onPasswordDisable">
+ {{ config.enforcePasswordForPublicLink
+ ? t('files_sharing', 'Password protection (enforced)')
+ : t('files_sharing', 'Password protect') }}
+ </NcActionCheckbox>
+
+ <NcActionInput v-if="isPasswordProtected"
+ ref="password"
+ class="share-link-password"
+ :class="{ error: errors.password}"
+ :disabled="saving"
+ :show-trailing-button="hasUnsavedPassword"
+ :required="config.enforcePasswordForPublicLink"
+ :value="hasUnsavedPassword ? share.newPassword : '***************'"
+ icon="icon-password"
+ autocomplete="new-password"
+ :type="hasUnsavedPassword ? 'text': 'password'"
+ @update:value="onPasswordChange"
+ @submit="onPasswordSubmit">
+ {{ t('files_sharing', 'Enter a password') }}
+ </NcActionInput>
+ <NcActionText v-if="isEmailShareType && passwordExpirationTime" icon="icon-info">
+ {{ t('files_sharing', 'Password expires {passwordExpirationTime}', {passwordExpirationTime}) }}
+ </NcActionText>
+ <NcActionText v-else-if="isEmailShareType && passwordExpirationTime !== null" icon="icon-error">
+ {{ t('files_sharing', 'Password expired') }}
+ </NcActionText>
+
+ <!-- password protected by Talk -->
+ <NcActionCheckbox v-if="isPasswordProtectedByTalkAvailable"
+ :checked.sync="isPasswordProtectedByTalk"
+ :disabled="!canTogglePasswordProtectedByTalkAvailable || saving"
+ class="share-link-password-talk-checkbox"
+ @change="onPasswordProtectedByTalkChange">
+ {{ t('files_sharing', 'Video verification') }}
+ </NcActionCheckbox>
+
+ <!-- expiration date -->
+ <NcActionCheckbox :checked.sync="hasExpirationDate"
+ :disabled="config.isDefaultExpireDateEnforced || saving"
+ class="share-link-expire-date-checkbox"
+ @uncheck="onExpirationDisable">
+ {{ config.isDefaultExpireDateEnforced
+ ? t('files_sharing', 'Expiration date (enforced)')
+ : t('files_sharing', 'Set expiration date') }}
+ </NcActionCheckbox>
+ <NcActionInput v-if="hasExpirationDate"
+ ref="expireDate"
+ :is-native-picker="true"
+ :hide-label="true"
+ class="share-link-expire-date"
+ :class="{ error: errors.expireDate}"
+ :disabled="saving"
+ :value="new Date(share.expireDate)"
+ type="date"
+ :min="dateTomorrow"
+ :max="dateMaxEnforced"
+ @input="onExpirationChange">
+ {{ t('files_sharing', 'Enter a date') }}
+ </NcActionInput>
+
+ <!-- note -->
+ <NcActionCheckbox :checked.sync="hasNote"
+ :disabled="saving"
+ @uncheck="queueUpdate('note')">
+ {{ t('files_sharing', 'Note to recipient') }}
+ </NcActionCheckbox>
+
+ <NcActionTextEditable v-if="hasNote"
+ ref="note"
+ :class="{ error: errors.note}"
+ :disabled="saving"
+ :placeholder="t('files_sharing', 'Enter a note for the share recipient')"
+ :value="share.newNote || share.note"
+ icon="icon-edit"
+ @update:value="onNoteChange"
+ @submit="onNoteSubmit" />
</template>
<NcActionSeparator />
@@ -156,19 +248,18 @@
{{ name }}
</NcActionLink>
- <NcActionButton v-if="!isEmailShareType && canReshare"
- class="new-share-link"
- icon="icon-add"
- @click.prevent.stop="onNewLinkShare">
- {{ t('files_sharing', 'Add another link') }}
- </NcActionButton>
-
<NcActionButton v-if="share.canDelete"
icon="icon-close"
:disabled="saving"
@click.prevent="onDelete">
{{ t('files_sharing', 'Unshare') }}
</NcActionButton>
+ <NcActionButton v-if="!isEmailShareType && canReshare"
+ class="new-share-link"
+ icon="icon-add"
+ @click.prevent.stop="onNewLinkShare">
+ {{ t('files_sharing', 'Add another link') }}
+ </NcActionButton>
</template>
<!-- Create new share -->
@@ -192,40 +283,39 @@ import { Type as ShareTypes } from '@nextcloud/sharing'
import Vue from 'vue'
import NcActionButton from '@nextcloud/vue/dist/Components/NcActionButton.js'
+import NcActionCheckbox from '@nextcloud/vue/dist/Components/NcActionCheckbox.js'
import NcActionInput from '@nextcloud/vue/dist/Components/NcActionInput.js'
import NcActionLink from '@nextcloud/vue/dist/Components/NcActionLink.js'
import NcActionText from '@nextcloud/vue/dist/Components/NcActionText.js'
import NcActionSeparator from '@nextcloud/vue/dist/Components/NcActionSeparator.js'
+import NcActionTextEditable from '@nextcloud/vue/dist/Components/NcActionTextEditable.js'
import NcActions from '@nextcloud/vue/dist/Components/NcActions.js'
import NcAvatar from '@nextcloud/vue/dist/Components/NcAvatar.js'
-import Tune from 'vue-material-design-icons/Tune.vue'
-
-import QuickShareSelect from './SharingEntryQuickShareSelect.vue'
-
import ExternalShareAction from './ExternalShareAction.vue'
+import SharePermissionsEditor from './SharePermissionsEditor.vue'
import GeneratePassword from '../utils/GeneratePassword.js'
import Share from '../models/Share.js'
import SharesMixin from '../mixins/SharesMixin.js'
-import ShareDetails from '../mixins/ShareDetails.js'
export default {
name: 'SharingEntryLink',
components: {
- ExternalShareAction,
NcActions,
NcActionButton,
+ NcActionCheckbox,
NcActionInput,
NcActionLink,
NcActionText,
+ NcActionTextEditable,
NcActionSeparator,
NcAvatar,
- Tune,
- QuickShareSelect,
+ ExternalShareAction,
+ SharePermissionsEditor,
},
- mixins: [SharesMixin, ShareDetails],
+ mixins: [SharesMixin],
props: {
canReshare: {
@@ -240,7 +330,6 @@ export default {
data() {
return {
- showDropdown: false,
copySuccess: true,
copied: false,
@@ -504,6 +593,7 @@ export default {
canChangeHideDownload() {
const hasDisabledDownload = (shareAttribute) => shareAttribute.key === 'download' && shareAttribute.scope === 'permissions' && shareAttribute.enabled === false
+
return this.fileInfo.shareAttributes.some(hasDisabledDownload)
},
},
@@ -581,7 +671,7 @@ export default {
* accordingly
*
* @param {Share} share the new share
- * @param {boolean} [update] do we update the current share ?
+ * @param {boolean} [update=false] do we update the current share ?
*/
async pushNewLinkShare(share, update) {
try {
@@ -658,6 +748,26 @@ export default {
this.loading = false
}
},
+
+ /**
+ * Label changed, let's save it to a different key
+ *
+ * @param {string} label the share label
+ */
+ onLabelChange(label) {
+ this.$set(this.share, 'newLabel', label.trim())
+ },
+
+ /**
+ * When the note change, we trim, save and dispatch
+ */
+ onLabelSubmit() {
+ if (typeof this.share.newLabel === 'string') {
+ this.share.label = this.share.newLabel
+ this.$delete(this.share, 'newLabel')
+ this.queueUpdate('label')
+ }
+ },
async copyLink() {
try {
await navigator.clipboard.writeText(this.shareLink)
@@ -760,10 +870,6 @@ export default {
// YET. We can safely delete the share :)
this.$emit('remove:share', this.share)
},
-
- toggleQuickShareSelect() {
- this.showDropdown = !this.showDropdown
- },
},
}
</script>
@@ -773,13 +879,13 @@ export default {
display: flex;
align-items: center;
min-height: 44px;
-
&__desc {
display: flex;
flex-direction: column;
justify-content: space-between;
padding: 8px;
line-height: 1.2em;
+ overflow: hidden;
p {
color: var(--color-text-maxcontrast);
diff --git a/apps/files_sharing/src/components/SharingEntryQuickShareSelect.vue b/apps/files_sharing/src/components/SharingEntryQuickShareSelect.vue
deleted file mode 100644
index 8128b3925ee..00000000000
--- a/apps/files_sharing/src/components/SharingEntryQuickShareSelect.vue
+++ /dev/null
@@ -1,186 +0,0 @@
-<template>
- <div :class="{ 'active': showDropdown, 'share-select': true }">
- <span class="trigger-text" @click="toggleDropdown">
- {{ selectedOption }}
- <DropdownIcon :size="15" />
- </span>
- <div v-if="showDropdown" class="share-select-dropdown-container">
- <div v-for="option in options"
- :key="option"
- :class="{ 'dropdown-item': true, 'selected': option === selectedOption }"
- @click="selectOption(option)">
- {{ option }}
- </div>
- </div>
- </div>
-</template>
-
-<script>
-import DropdownIcon from 'vue-material-design-icons/TriangleSmallDown.vue'
-import SharesMixin from '../mixins/SharesMixin.js'
-import ShareDetails from '../mixins/ShareDetails.js'
-import ShareTypes from '../mixins/ShareTypes.js'
-
-import {
- BUNDLED_PERMISSIONS,
- ATOMIC_PERMISSIONS,
-} from '../lib/SharePermissionsToolBox.js'
-
-export default {
- components: {
- DropdownIcon,
- },
- mixins: [SharesMixin, ShareDetails, ShareTypes],
- props: {
- share: {
- type: Object,
- required: true,
- },
- toggle: {
- type: Boolean,
- default: false,
- },
- },
- data() {
- return {
- selectedOption: '',
- showDropdown: this.toggle,
- }
- },
- computed: {
- canViewText() {
- return t('files_sharing', 'View only')
- },
- canEditText() {
- return t('files_sharing', 'Can edit')
- },
- fileDropText() {
- return t('files_sharing', 'File drop')
- },
- customPermissionsText() {
- return t('files_sharing', 'Custom permissions')
- },
- preSelectedOption() {
- // We remove the share permission for the comparison as it is not relevant for bundled permissions.
- if ((this.share.permissions & ~ATOMIC_PERMISSIONS.SHARE) === BUNDLED_PERMISSIONS.READ_ONLY) {
- return this.canViewText
- } else if (this.share.permissions === BUNDLED_PERMISSIONS.ALL || this.share.permissions === BUNDLED_PERMISSIONS.ALL_FILE) {
- return this.canEditText
- } else if ((this.share.permissions & ~ATOMIC_PERMISSIONS.SHARE) === BUNDLED_PERMISSIONS.FILE_DROP) {
- return this.fileDropText
- }
-
- return this.customPermissionsText
-
- },
- options() {
- const options = [this.canViewText, this.canEditText]
- if (this.supportsFileDrop) {
- options.push(this.fileDropText)
- }
- options.push(this.customPermissionsText)
-
- return options
- },
- supportsFileDrop() {
- if (this.isFolder) {
- const shareType = this.share.type ?? this.share.shareType
- return [this.SHARE_TYPES.SHARE_TYPE_LINK, this.SHARE_TYPES.SHARE_TYPE_EMAIL].includes(shareType)
- }
- return false
- },
- dropDownPermissionValue() {
- switch (this.selectedOption) {
- case this.canEditText:
- return this.isFolder ? BUNDLED_PERMISSIONS.ALL : BUNDLED_PERMISSIONS.ALL_FILE
- case this.fileDropText:
- return BUNDLED_PERMISSIONS.FILE_DROP
- case this.customPermissionsText:
- return 'custom'
- case this.canViewText:
- default:
- return BUNDLED_PERMISSIONS.READ_ONLY
- }
- },
- },
- watch: {
- toggle(toggleValue) {
- this.showDropdown = toggleValue
- },
- },
- mounted() {
- this.initializeComponent()
- },
- methods: {
- toggleDropdown() {
- this.showDropdown = !this.showDropdown
- },
- selectOption(option) {
- this.selectedOption = option
- if (option === this.customPermissionsText) {
- this.$emit('open-sharing-details')
- } else {
- this.share.permissions = this.dropDownPermissionValue
- this.queueUpdate('permissions')
- }
- this.showDropdown = false
- },
- initializeComponent() {
- this.selectedOption = this.preSelectedOption
- },
- },
-
-}
-</script>
-
-<style lang="scss" scoped>
-.share-select {
- position: relative;
- cursor: pointer;
-
- .trigger-text {
- display: flex;
- flex-direction: row;
- align-items: center;
- font-size: 12.5px;
- gap: 2px;
- color: var(--color-primary-element);
- }
-
- .share-select-dropdown-container {
- position: absolute;
- top: 100%;
- left: 0;
- background-color: var(--color-main-background);
- border-radius: 8px;
- box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
- padding: 4px 0;
- z-index: 1;
-
- .dropdown-item {
- padding: 8px;
- font-size: 12px;
-
- &:hover {
- background-color: #f2f2f2;
- }
-
- &.selected {
- background-color: #f0f0f0;
- }
- }
- }
-
- /* Optional: Add a transition effect for smoother dropdown animation */
- .share-select-dropdown-container {
- max-height: 0;
- overflow: hidden;
- transition: max-height 0.3s ease;
- }
-
- &.active .share-select-dropdown-container {
- max-height: 200px;
- /* Adjust the value to your desired height */
- }
-}
-</style>
diff --git a/apps/files_sharing/src/components/SharingEntrySimple.vue b/apps/files_sharing/src/components/SharingEntrySimple.vue
index 5e858de990b..daff947fe80 100644
--- a/apps/files_sharing/src/components/SharingEntrySimple.vue
+++ b/apps/files_sharing/src/components/SharingEntrySimple.vue
@@ -29,8 +29,8 @@
{{ subtitle }}
</p>
</div>
- <NcActions v-if="$slots['default']"
- ref="actionsComponent"
+ <NcActions ref="actionsComponent"
+ v-if="$slots['default']"
class="sharing-entry__actions"
menu-align="right"
:aria-expanded="ariaExpandedValue">
diff --git a/apps/files_sharing/src/components/SharingInput.vue b/apps/files_sharing/src/components/SharingInput.vue
index c5ed27477b6..8b740c1bac3 100644
--- a/apps/files_sharing/src/components/SharingInput.vue
+++ b/apps/files_sharing/src/components/SharingInput.vue
@@ -24,7 +24,6 @@
<div class="sharing-search">
<label for="sharing-search-input">{{ t('files_sharing', 'Search for share recipients') }}</label>
<NcSelect ref="select"
- v-model="value"
input-id="sharing-search-input"
class="sharing-search__input"
:disabled="!canReshare"
@@ -34,9 +33,10 @@
:clear-search-on-blur="() => false"
:user-select="true"
:options="options"
+ v-model="value"
@open="handleOpen"
@search="asyncFind"
- @option:selected="openSharingDetails">
+ @option:selected="addShare">
<template #no-options="{ search }">
{{ search ? noResultText : t('files_sharing', 'No recommendations. Start typing.') }}
</template>
@@ -57,7 +57,6 @@ import GeneratePassword from '../utils/GeneratePassword.js'
import Share from '../models/Share.js'
import ShareRequests from '../mixins/ShareRequests.js'
import ShareTypes from '../mixins/ShareTypes.js'
-import ShareDetails from '../mixins/ShareDetails.js'
export default {
name: 'SharingInput',
@@ -66,7 +65,7 @@ export default {
NcSelect,
},
- mixins: [ShareTypes, ShareRequests, ShareDetails],
+ mixins: [ShareTypes, ShareRequests],
props: {
shares: {
@@ -177,7 +176,7 @@ export default {
* Get suggestions
*
* @param {string} search the search query
- * @param {boolean} [lookup] search on lookup server
+ * @param {boolean} [lookup=false] search on lookup server
*/
async getSuggestions(search, lookup = false) {
this.loading = true
@@ -453,6 +452,7 @@ export default {
}
return {
+ id: `${result.value.shareType}-${result.value.shareWith}`,
shareWith: result.value.shareWith,
shareType: result.value.shareType,
user: result.uuid || result.value.shareWith,
diff --git a/apps/files_sharing/src/lib/SharePermissionsToolBox.js b/apps/files_sharing/src/lib/SharePermissionsToolBox.js
index d86f8827b2c..f5806df70bf 100644
--- a/apps/files_sharing/src/lib/SharePermissionsToolBox.js
+++ b/apps/files_sharing/src/lib/SharePermissionsToolBox.js
@@ -34,7 +34,6 @@ export const BUNDLED_PERMISSIONS = {
UPLOAD_AND_UPDATE: ATOMIC_PERMISSIONS.READ | ATOMIC_PERMISSIONS.UPDATE | ATOMIC_PERMISSIONS.CREATE | ATOMIC_PERMISSIONS.DELETE,
FILE_DROP: ATOMIC_PERMISSIONS.CREATE,
ALL: ATOMIC_PERMISSIONS.UPDATE | ATOMIC_PERMISSIONS.CREATE | ATOMIC_PERMISSIONS.READ | ATOMIC_PERMISSIONS.DELETE | ATOMIC_PERMISSIONS.SHARE,
- ALL_FILE: ATOMIC_PERMISSIONS.UPDATE | ATOMIC_PERMISSIONS.READ | ATOMIC_PERMISSIONS.SHARE,
}
/**
diff --git a/apps/files_sharing/src/mixins/ShareDetails.js b/apps/files_sharing/src/mixins/ShareDetails.js
deleted file mode 100644
index 53ec8bfe16a..00000000000
--- a/apps/files_sharing/src/mixins/ShareDetails.js
+++ /dev/null
@@ -1,43 +0,0 @@
-import Share from '../models/Share.js'
-
-export default {
- methods: {
- openSharingDetails(share) {
- const shareRequestObject = {
- fileInfo: this.fileInfo,
- share: this.mapShareRequestToShareObject(share),
- }
- this.$emit('open-sharing-details', shareRequestObject)
- },
- openShareDetailsForCustomSettings(share) {
- share.setCustomPermissions = true
- this.openSharingDetails(share)
- },
- mapShareRequestToShareObject(shareRequestObject) {
-
- if (shareRequestObject.id) {
- return shareRequestObject
- }
-
- const share = {
- attributes: [
- {
- enabled: true,
- key: 'download',
- scope: 'permissions',
- },
- ],
- 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,
- expiration: '',
- }
-
- return new Share(share)
- },
- },
-}
diff --git a/apps/files_sharing/src/mixins/ShareRequests.js b/apps/files_sharing/src/mixins/ShareRequests.js
index 5f301108f6f..e1d246b7400 100644
--- a/apps/files_sharing/src/mixins/ShareRequests.js
+++ b/apps/files_sharing/src/mixins/ShareRequests.js
@@ -42,20 +42,19 @@ export default {
* @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] allow public upload to a public shared folder
+ * @param {boolean} [data.publicUpload=false] allow public upload to a public shared folder
* @param {string} [data.password] password to protect public link Share with
- * @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 shareautomatically after
- * @param {string} [data.label] custom label
- * @param {string} [data.attributes] Share attributes encoded as json
- * @param data.note
+ * @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
+ * @param {string} [data.attributes=null] Share attributes encoded as json
* @return {Share} the new share
* @throws {Error}
*/
- async createShare({ path, permissions, shareType, shareWith, publicUpload, password, sendPasswordByTalk, expireDate, label, note, attributes }) {
+ async createShare({ path, permissions, shareType, shareWith, publicUpload, password, sendPasswordByTalk, expireDate, label, attributes }) {
try {
- const request = await axios.post(shareUrl, { path, permissions, shareType, shareWith, publicUpload, password, sendPasswordByTalk, expireDate, label, note, attributes })
+ const request = await axios.post(shareUrl, { path, permissions, shareType, shareWith, publicUpload, password, sendPasswordByTalk, expireDate, label, attributes })
if (!request?.data?.ocs) {
throw request
}
@@ -67,7 +66,7 @@ export default {
const errorMessage = error?.response?.data?.ocs?.meta?.message
OC.Notification.showTemporary(
errorMessage ? t('files_sharing', 'Error creating the share: {errorMessage}', { errorMessage }) : t('files_sharing', 'Error creating the share'),
- { type: 'error' },
+ { type: 'error' }
)
throw error
}
@@ -92,7 +91,7 @@ export default {
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
}
@@ -119,7 +118,7 @@ export default {
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/SharesMixin.js b/apps/files_sharing/src/mixins/SharesMixin.js
index aba1462248a..a29e1a91b02 100644
--- a/apps/files_sharing/src/mixins/SharesMixin.js
+++ b/apps/files_sharing/src/mixins/SharesMixin.js
@@ -36,17 +36,13 @@ import SharesRequests from './ShareRequests.js'
import ShareTypes from './ShareTypes.js'
import Config from '../services/ConfigService.js'
-import {
- BUNDLED_PERMISSIONS,
-} from '../lib/SharePermissionsToolBox.js'
-
export default {
mixins: [SharesRequests, ShareTypes],
props: {
fileInfo: {
type: Object,
- default: () => { },
+ default: () => {},
required: true,
},
share: {
@@ -125,24 +121,11 @@ export default {
monthFormat: 'MMM',
}
},
- isFolder() {
- return this.fileInfo.type === 'dir'
- },
- isPublicShare() {
- const shareType = this.share.shareType ?? this.share.type
- return [this.SHARE_TYPES.SHARE_TYPE_LINK, this.SHARE_TYPES.SHARE_TYPE_EMAIL].includes(shareType)
- },
+
isShareOwner() {
return this.share && this.share.owner === getCurrentUser().uid
},
- hasCustomPermissions() {
- const bundledPermissions = [
- BUNDLED_PERMISSIONS.ALL,
- BUNDLED_PERMISSIONS.READ_ONLY,
- BUNDLED_PERMISSIONS.FILE_DROP,
- ]
- return !bundledPermissions.includes(this.share.permissions)
- },
+
},
methods: {
@@ -197,7 +180,8 @@ export default {
* @param {Date} date
*/
onExpirationChange(date) {
- this.share.expireDate = this.formatDateToString(new Date(date))
+ this.share.expireDate = this.formatDateToString(date)
+ this.queueUpdate('expireDate')
},
/**
@@ -208,6 +192,7 @@ export default {
*/
onExpirationDisable() {
this.share.expireDate = ''
+ this.queueUpdate('expireDate')
},
/**
@@ -350,6 +335,7 @@ export default {
}
}
},
+
/**
* Debounce queueUpdate to avoid requests spamming
* more importantly for text data
diff --git a/apps/files_sharing/src/models/Share.js b/apps/files_sharing/src/models/Share.js
index 5504c63b345..9b1535184a0 100644
--- a/apps/files_sharing/src/models/Share.js
+++ b/apps/files_sharing/src/models/Share.js
@@ -579,7 +579,7 @@ export default class Share {
for (const i in this._share.attributes) {
const attr = this._share.attributes[i]
if (attr.scope === attrUpdate.scope && attr.key === attrUpdate.key) {
- this._share.attributes.splice(i, 1, attrUpdate)
+ this._share.attributes[i] = attrUpdate
return
}
}
diff --git a/apps/files_sharing/src/utils/SharedWithMe.js b/apps/files_sharing/src/utils/SharedWithMe.js
index 34de3f017ef..bd39c765221 100644
--- a/apps/files_sharing/src/utils/SharedWithMe.js
+++ b/apps/files_sharing/src/utils/SharedWithMe.js
@@ -33,7 +33,7 @@ const shareWithTitle = function(share) {
owner: share.ownerDisplayName,
},
undefined,
- { escape: false },
+ { escape: false }
)
} else if (share.type === ShareTypes.SHARE_TYPE_CIRCLE) {
return t(
@@ -44,7 +44,7 @@ const shareWithTitle = function(share) {
owner: share.ownerDisplayName,
},
undefined,
- { escape: false },
+ { escape: false }
)
} else if (share.type === ShareTypes.SHARE_TYPE_ROOM) {
if (share.shareWithDisplayName) {
@@ -56,7 +56,7 @@ const shareWithTitle = function(share) {
owner: share.ownerDisplayName,
},
undefined,
- { escape: false },
+ { escape: false }
)
} else {
return t(
@@ -66,7 +66,7 @@ const shareWithTitle = function(share) {
owner: share.ownerDisplayName,
},
undefined,
- { escape: false },
+ { escape: false }
)
}
} else {
@@ -75,7 +75,7 @@ const shareWithTitle = function(share) {
'Shared with you by {owner}',
{ owner: share.ownerDisplayName },
undefined,
- { escape: false },
+ { escape: false }
)
}
}
diff --git a/apps/files_sharing/src/views/SharingDetailsTab.vue b/apps/files_sharing/src/views/SharingDetailsTab.vue
deleted file mode 100644
index dbc8c1508e4..00000000000
--- a/apps/files_sharing/src/views/SharingDetailsTab.vue
+++ /dev/null
@@ -1,1053 +0,0 @@
-<template>
- <div class="sharingTabDetailsView">
- <div class="sharingTabDetailsView__header">
- <span>
- <NcAvatar v-if="isUserShare"
- class="sharing-entry__avatar"
- :is-no-user="share.shareType !== SHARE_TYPES.SHARE_TYPE_USER"
- :user="share.shareWith"
- :display-name="share.shareWithDisplayName"
- :menu-position="'left'"
- :url="share.shareWithAvatar" />
- <component :is="getShareTypeIcon(share.type)" :size="32" />
- </span>
- <span>
- <h1>{{ title }}</h1>
- </span>
- </div>
- <div class="sharingTabDetailsView__quick-permissions">
- <div>
- <NcCheckboxRadioSwitch :button-variant="true"
- :checked.sync="sharingPermission"
- :value="bundledPermissions.READ_ONLY.toString()"
- name="sharing_permission_radio"
- type="radio"
- button-variant-grouped="vertical"
- @update:checked="toggleCustomPermissions">
- {{ t('files_sharing', 'View only') }}
- <template #icon>
- <ViewIcon :size="20" />
- </template>
- </NcCheckboxRadioSwitch>
- <NcCheckboxRadioSwitch :button-variant="true"
- :checked.sync="sharingPermission"
- :value="bundledPermissions.ALL.toString()"
- name="sharing_permission_radio"
- type="radio"
- button-variant-grouped="vertical"
- @update:checked="toggleCustomPermissions">
- {{ t('files_sharing', 'Allow upload and editing') }}
- <template #icon>
- <EditIcon :size="20" />
- </template>
- </NcCheckboxRadioSwitch>
- <NcCheckboxRadioSwitch v-if="allowsFileDrop"
- :button-variant="true"
- :checked.sync="sharingPermission"
- :value="bundledPermissions.FILE_DROP.toString()"
- name="sharing_permission_radio"
- type="radio"
- button-variant-grouped="vertical"
- @update:checked="toggleCustomPermissions">
- {{ t('files_sharing', 'File drop') }}
- <small>{{ t('files_sharing', 'Upload only') }}</small>
- <template #icon>
- <UploadIcon :size="20" />
- </template>
- </NcCheckboxRadioSwitch>
- <NcCheckboxRadioSwitch :button-variant="true"
- :checked.sync="sharingPermission"
- :value="'custom'"
- name="sharing_permission_radio"
- type="radio"
- button-variant-grouped="vertical"
- @update:checked="toggleCustomPermissions">
- {{ t('files_sharing', 'Custom permissions') }}
- <small>{{ t('files_sharing', customPermissionsList) }}</small>
- <template #icon>
- <DotsHorizontalIcon :size="20" />
- </template>
- </NcCheckboxRadioSwitch>
- </div>
- </div>
- <div class="sharingTabDetailsView__advanced-control">
- <NcButton type="tertiary"
- alignment="end-reverse"
- @click="advancedSectionAccordionExpanded = !advancedSectionAccordionExpanded">
- {{ t('files_sharing', 'Advanced settings') }}
- <template #icon>
- <MenuDownIcon />
- </template>
- </NcButton>
- </div>
- <div v-if="advancedSectionAccordionExpanded" class="sharingTabDetailsView__advanced">
- <section>
- <NcInputField v-if="isPublicShare"
- :value.sync="share.label"
- type="text"
- :label="t('file_sharing', 'Share label')" />
- <template v-if="isPublicShare">
- <NcCheckboxRadioSwitch :checked.sync="isPasswordProtected" :disabled="isPasswordEnforced">
- {{ t('file_sharing', 'Set password') }}
- </NcCheckboxRadioSwitch>
- <NcInputField v-if="isPasswordProtected"
- :type="hasUnsavedPassword ? 'text' : 'password'"
- :value="hasUnsavedPassword ? share.newPassword : '***************'"
- :error="passwordError"
- :required="isPasswordEnforced"
- :label="t('file_sharing', 'Password')"
- @update:value="onPasswordChange" />
-
- <!-- Migrate icons and remote -> icon="icon-info"-->
- <span v-if="isEmailShareType && passwordExpirationTime" icon="icon-info">
- {{ t('files_sharing', 'Password expires {passwordExpirationTime}', { passwordExpirationTime }) }}
- </span>
- <span v-else-if="isEmailShareType && passwordExpirationTime !== null" icon="icon-error">
- {{ t('files_sharing', 'Password expired') }}
- </span>
- </template>
- <NcCheckboxRadioSwitch :checked.sync="hasExpirationDate" :disabled="isExpiryDateEnforced">
- {{ isExpiryDateEnforced
- ? t('files_sharing', 'Expiration date (enforced)')
- : t('files_sharing', 'Set expiration date') }}
- </NcCheckboxRadioSwitch>
- <NcDateTimePickerNative v-if="hasExpirationDate"
- id="share-date-picker"
- :value="new Date(share.expireDate)"
- :min="dateTomorrow"
- :max="dateMaxEnforced"
- :hide-label="true"
- :disabled="isExpiryDateEnforced"
- :placeholder="t('file_sharing', 'Expiration date')"
- type="date"
- @input="onExpirationChange" />
- <NcCheckboxRadioSwitch v-if="isPublicShare"
- :disabled="canChangeHideDownload"
- :checked.sync="share.hideDownload"
- @update:checked="queueUpdate('hideDownload')">
- {{ t('file_sharing', 'Hide download') }}
- </NcCheckboxRadioSwitch>
- <NcCheckboxRadioSwitch v-if="canTogglePasswordProtectedByTalkAvailable"
- :checked.sync="isPasswordProtectedByTalk"
- @update:checked="onPasswordProtectedByTalkChange">
- {{ t('file_sharing', 'Video verification') }}
- </NcCheckboxRadioSwitch>
- <NcCheckboxRadioSwitch :checked.sync="writeNoteToRecipientIsChecked">
- {{ t('file_sharing', 'Note to recipient') }}
- </NcCheckboxRadioSwitch>
- <template v-if="writeNoteToRecipientIsChecked">
- <textarea :value="share.note" @input="share.note = $event.target.value" />
- </template>
- <NcCheckboxRadioSwitch :checked.sync="setCustomPermissions">
- {{ t('file_sharing', 'Custom permissions') }}
- </NcCheckboxRadioSwitch>
- <section v-if="setCustomPermissions" class="custom-permissions-group">
- <NcCheckboxRadioSwitch :disabled="!allowsFileDrop && share.type === SHARE_TYPES.SHARE_TYPE_LINK" :checked.sync="hasRead">
- {{ t('file_sharing', 'Read') }}
- </NcCheckboxRadioSwitch>
- <NcCheckboxRadioSwitch v-if="isFolder" :disabled="!canSetCreate" :checked.sync="canCreate">
- {{ t('file_sharing', 'Create') }}
- </NcCheckboxRadioSwitch>
- <NcCheckboxRadioSwitch :disabled="!canSetEdit" :checked.sync="canEdit">
- {{ t('file_sharing', 'Update') }}
- </NcCheckboxRadioSwitch>
- <NcCheckboxRadioSwitch v-if="config.isResharingAllowed && share.type !== SHARE_TYPES.SHARE_TYPE_LINK" :disabled="!canSetReshare" :checked.sync="canReshare">
- {{ t('file_sharing', 'Share') }}
- </NcCheckboxRadioSwitch>
- <NcCheckboxRadioSwitch v-if="!isPublicShare" :disabled="!canSetDownload" :checked.sync="canDownload">
- {{ t('file_sharing', 'Download') }}
- </NcCheckboxRadioSwitch>
- <NcCheckboxRadioSwitch :disabled="!canSetDelete" :checked.sync="canDelete">
- {{ t('file_sharing', 'Delete') }}
- </NcCheckboxRadioSwitch>
- </section>
- </section>
- </div>
-
- <div class="sharingTabDetailsView__footer">
- <NcButton v-if="!isNewShare"
- :aria-label="t('files_sharing', 'Delete share')"
- :disabled="false"
- :readonly="false"
- type="tertiary"
- @click.prevent="removeShare">
- <template #icon>
- <CloseIcon :size="16" />
- </template>
- {{ t('files_sharing', 'Delete share') }}
- </NcButton>
- <div class="button-group">
- <NcButton @click="$emit('close-sharing-details')">
- {{ t('file_sharing', 'Cancel') }}
- </NcButton>
- <NcButton type="primary" @click="saveShare">
- {{ shareButtonText }}
- </NcButton>
- </div>
- </div>
- </div>
-</template>
-
-<script>
-import NcButton from '@nextcloud/vue/dist/Components/NcButton.js'
-import NcInputField from '@nextcloud/vue/dist/Components/NcInputField.js'
-import NcAvatar from '@nextcloud/vue/dist/Components/NcAvatar.js'
-import NcDatetimePicker from '@nextcloud/vue/dist/Components/NcDatetimePicker.js'
-import NcDateTimePickerNative from '@nextcloud/vue/dist/Components/NcDateTimePickerNative.js'
-import NcCheckboxRadioSwitch from '@nextcloud/vue/dist/Components/NcCheckboxRadioSwitch.js'
-import CircleIcon from 'vue-material-design-icons/CircleOutline.vue'
-import CloseIcon from 'vue-material-design-icons/Close.vue'
-import EditIcon from 'vue-material-design-icons/Pencil.vue'
-import EmailIcon from 'vue-material-design-icons/Email.vue'
-import LinkIcon from 'vue-material-design-icons/Link.vue'
-import GroupIcon from 'vue-material-design-icons/AccountGroup.vue'
-import ShareIcon from 'vue-material-design-icons/ShareCircle.vue'
-import UserIcon from 'vue-material-design-icons/AccountCircleOutline.vue'
-import ViewIcon from 'vue-material-design-icons/Eye.vue'
-import UploadIcon from 'vue-material-design-icons/Upload.vue'
-import MenuDownIcon from 'vue-material-design-icons/MenuDown.vue'
-import DotsHorizontalIcon from 'vue-material-design-icons/DotsHorizontal.vue'
-
-import GeneratePassword from '../utils/GeneratePassword.js'
-import Share from '../models/Share.js'
-import ShareRequests from '../mixins/ShareRequests.js'
-import ShareTypes from '../mixins/ShareTypes.js'
-import SharesMixin from '../mixins/SharesMixin.js'
-
-import {
- ATOMIC_PERMISSIONS,
- BUNDLED_PERMISSIONS,
- hasPermissions,
-} from '../lib/SharePermissionsToolBox.js'
-
-export default {
- name: 'SharingDetailsTab',
- components: {
- NcAvatar,
- NcButton,
- NcInputField,
- NcDatetimePicker,
- NcDateTimePickerNative,
- NcCheckboxRadioSwitch,
- CloseIcon,
- CircleIcon,
- EditIcon,
- LinkIcon,
- GroupIcon,
- ShareIcon,
- UserIcon,
- UploadIcon,
- ViewIcon,
- MenuDownIcon,
- DotsHorizontalIcon,
- },
- mixins: [ShareTypes, ShareRequests, SharesMixin],
- props: {
- shareRequestValue: {
- type: Object,
- required: false,
- },
- fileInfo: {
- type: Object,
- required: true,
- },
- share: {
- type: Object,
- required: true,
- },
- },
- data() {
- return {
- writeNoteToRecipientIsChecked: false,
- sharingPermission: BUNDLED_PERMISSIONS.ALL.toString(),
- revertSharingPermission: null,
- setCustomPermissions: false,
- passwordError: false,
- advancedSectionAccordionExpanded: false,
- bundledPermissions: BUNDLED_PERMISSIONS,
- isFirstComponentLoad: true,
- test: false,
- }
- },
-
- computed: {
- title() {
- let title = t('files_sharing', 'Share with ')
- if (this.share.type === this.SHARE_TYPES.SHARE_TYPE_USER) {
- title = title + this.share.shareWithDisplayName
- } else if (this.share.type === this.SHARE_TYPES.SHARE_TYPE_LINK) {
- title = t('files_sharing', 'Share link')
- } else if (this.share.type === this.SHARE_TYPES.SHARE_TYPE_GROUP) {
- title += ` (${t('files_sharing', 'group')})`
- } else if (this.share.type === this.SHARE_TYPES.SHARE_TYPE_ROOM) {
- title += ` (${t('files_sharing', 'conversation')})`
- } else if (this.share.type === this.SHARE_TYPES.SHARE_TYPE_REMOTE) {
- title += ` (${t('files_sharing', 'remote')})`
- } else if (this.share.type === this.SHARE_TYPES.SHARE_TYPE_REMOTE_GROUP) {
- title += ` (${t('files_sharing', 'remote group')})`
- } else if (this.share.type === this.SHARE_TYPES.SHARE_TYPE_GUEST) {
- title += ` (${t('files_sharing', 'guest')})`
- }
-
- return title
- },
- /**
- * Can the sharee edit the shared file ?
- */
- canEdit: {
- get() {
- return this.share.hasUpdatePermission
- },
- set(checked) {
- this.updateAtomicPermissions({ isEditChecked: checked })
- },
- },
- /**
- * Can the sharee create the shared file ?
- */
- canCreate: {
- get() {
- return this.share.hasCreatePermission
- },
- set(checked) {
- this.updateAtomicPermissions({ isCreateChecked: checked })
- },
- },
- /**
- * Can the sharee delete the shared file ?
- */
- canDelete: {
- get() {
- return this.share.hasDeletePermission
- },
- set(checked) {
- this.updateAtomicPermissions({ isDeleteChecked: checked })
- },
- },
- /**
- * Can the sharee reshare the file ?
- */
- canReshare: {
- get() {
- return this.share.hasSharePermission
- },
- set(checked) {
- this.updateAtomicPermissions({ isReshareChecked: checked })
- },
- },
- /**
- * Can the sharee download files or only view them ?
- */
- canDownload: {
- get() {
- return this.share.hasDownloadPermission
- },
- set(checked) {
- this.updateAtomicPermissions({ isDownloadChecked: checked })
- },
- },
- /**
- * Is this share readable
- * Needed for some federated shares that might have been added from file drop links
- */
- hasRead: {
- get() {
- return this.share.hasReadPermission
- },
- set(checked) {
- this.updateAtomicPermissions({ isReadChecked: checked })
- },
- },
- /**
- * Does the current share have an expiration date
- *
- * @return {boolean}
- */
- hasExpirationDate: {
- get() {
- return !!this.share.expireDate || this.config.isDefaultInternalExpireDateEnforced
- },
- set(enabled) {
- this.share.expireDate = enabled
- ? this.formatDateToString(this.defaultExpiryDate)
- : ''
- },
- },
- /**
- * Is the current share password protected ?
- *
- * @return {boolean}
- */
- isPasswordProtected: {
- get() {
- return this.config.enforcePasswordForPublicLink
- || !!this.share.password
- },
- async set(enabled) {
- // TODO: directly save after generation to make sure the share is always protected
- this.share.password = enabled ? await GeneratePassword() : ''
- this.$set(this.share, 'newPassword', this.share.password)
- },
- },
- /**
- * Is the current share a folder ?
- *
- * @return {boolean}
- */
- isFolder() {
- return this.fileInfo.type === 'dir'
- },
- dateMaxEnforced() {
- if (!this.isRemote && this.config.isDefaultInternalExpireDateEnforced) {
- return new Date(new Date().setDate(new Date().getDate() + 1 + this.config.defaultInternalExpireDate))
- } else if (this.config.isDefaultRemoteExpireDateEnforced) {
- return new Date(new Date().setDate(new Date().getDate() + 1 + this.config.defaultRemoteExpireDate))
- }
- return null
- },
- /**
- * @return {boolean}
- */
- isSetDownloadButtonVisible() {
- // TODO: Implement download permission for circle shares instead of hiding the option.
- // https://github.com/nextcloud/server/issues/39161
- if (this.share && this.share.type === this.SHARE_TYPES.SHARE_TYPE_CIRCLE) {
- return false
- }
-
- const allowedMimetypes = [
- // Office documents
- 'application/msword',
- 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
- 'application/vnd.ms-powerpoint',
- 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
- 'application/vnd.ms-excel',
- 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
- 'application/vnd.oasis.opendocument.text',
- 'application/vnd.oasis.opendocument.spreadsheet',
- 'application/vnd.oasis.opendocument.presentation',
- ]
-
- return this.isFolder || allowedMimetypes.includes(this.fileInfo.mimetype)
- },
- isPasswordEnforced() {
- return this.isPublicShare && this.config.enforcePasswordForPublicLink
- },
- isExpiryDateEnforced() {
- return this.config.isDefaultInternalExpireDateEnforced
- },
- defaultExpiryDate() {
- if ((this.isGroupShare || this.isUserShare) && this.config.isDefaultInternalExpireDateEnabled) {
- return new Date(this.config.defaultInternalExpirationDate)
- } else if (this.isRemoteShare && this.config.isDefaultRemoteExpireDateEnabled) {
- return new Date(this.config.defaultRemoteExpireDateEnabled)
- } else if (this.isPublicShare && this.config.isDefaultExpireDateEnabled) {
- return new Date(this.config.defaultExpirationDate)
- }
- return new Date(new Date().setDate(new Date().getDate() + 1))
- },
- isUserShare() {
- return this.share.type === this.SHARE_TYPES.SHARE_TYPE_USER
- },
- isGroupShare() {
- return this.share.type === this.SHARE_TYPES.SHARE_TYPE_GROUP
- },
- isRemoteShare() {
- return this.share.type === this.SHARE_TYPES.SHARE_TYPE_REMOTE_GROUP || this.share.type === this.SHARE_TYPES.SHARE_TYPE_REMOTE
- },
- isNewShare() {
- return this.share.id === null || this.share.id === undefined
- },
- allowsFileDrop() {
- if (this.isFolder) {
- if (this.share.type === this.SHARE_TYPES.SHARE_TYPE_LINK || this.share.type === this.SHARE_TYPES.SHARE_TYPE_EMAIL) {
- return true
- }
- }
- return false
- },
- hasFileDropPermissions() {
- return this.share.permissions === this.bundledPermissions.FILE_DROP
- },
- shareButtonText() {
- if (this.isNewShare) {
- return t('file_sharing', 'Save share')
- }
- return t('file_sharing', 'Update share')
-
- },
- /**
- * Can the sharer set whether the sharee can edit the file ?
- *
- * @return {boolean}
- */
- canSetEdit() {
- // If the owner revoked the permission after the resharer granted it
- // the share still has the permission, and the resharer is still
- // allowed to revoke it too (but not to grant it again).
- return (this.fileInfo.sharePermissions & OC.PERMISSION_UPDATE) || this.canEdit
- },
-
- /**
- * Can the sharer set whether the sharee can create the file ?
- *
- * @return {boolean}
- */
- canSetCreate() {
- // If the owner revoked the permission after the resharer granted it
- // the share still has the permission, and the resharer is still
- // allowed to revoke it too (but not to grant it again).
- return (this.fileInfo.sharePermissions & OC.PERMISSION_CREATE) || this.canCreate
- },
-
- /**
- * Can the sharer set whether the sharee can delete the file ?
- *
- * @return {boolean}
- */
- canSetDelete() {
- // If the owner revoked the permission after the resharer granted it
- // the share still has the permission, and the resharer is still
- // allowed to revoke it too (but not to grant it again).
- return (this.fileInfo.sharePermissions & OC.PERMISSION_DELETE) || this.canDelete
- },
- /**
- * Can the sharer set whether the sharee can reshare the file ?
- *
- * @return {boolean}
- */
- canSetReshare() {
- // If the owner revoked the permission after the resharer granted it
- // the share still has the permission, and the resharer is still
- // allowed to revoke it too (but not to grant it again).
- return (this.fileInfo.sharePermissions & OC.PERMISSION_SHARE) || this.canReshare
- },
- /**
- * Can the sharer set whether the sharee can download the file ?
- *
- * @return {boolean}
- */
- canSetDownload() {
- // If the owner revoked the permission after the resharer granted it
- // the share still has the permission, and the resharer is still
- // allowed to revoke it too (but not to grant it again).
- return (this.fileInfo.canDownload() || this.canDownload)
- },
- // if newPassword exists, but is empty, it means
- // the user deleted the original password
- hasUnsavedPassword() {
- return this.share.newPassword !== undefined
- },
- passwordExpirationTime() {
- if (this.share.passwordExpirationTime === null) {
- return null
- }
-
- const expirationTime = moment(this.share.passwordExpirationTime)
-
- if (expirationTime.diff(moment()) < 0) {
- return false
- }
-
- return expirationTime.fromNow()
- },
-
- /**
- * Is Talk enabled?
- *
- * @return {boolean}
- */
- isTalkEnabled() {
- return OC.appswebroots.spreed !== undefined
- },
-
- /**
- * Is it possible to protect the password by Talk?
- *
- * @return {boolean}
- */
- isPasswordProtectedByTalkAvailable() {
- return this.isPasswordProtected && this.isTalkEnabled
- },
- /**
- * Is the current share password protected by Talk?
- *
- * @return {boolean}
- */
- isPasswordProtectedByTalk: {
- get() {
- return this.share.sendPasswordByTalk
- },
- async set(enabled) {
- this.share.sendPasswordByTalk = enabled
- },
- },
- /**
- * Is the current share an email share ?
- *
- * @return {boolean}
- */
- isEmailShareType() {
- return this.share
- ? this.share.type === this.SHARE_TYPES.SHARE_TYPE_EMAIL
- : false
- },
- canTogglePasswordProtectedByTalkAvailable() {
- if (!this.isPublicShare || !this.isPasswordProtected) {
- // Makes no sense
- return false
- } else if (this.isEmailShareType && !this.hasUnsavedPassword) {
- // For email shares we need a new password in order to enable or
- // disable
- return false
- }
-
- // Anything else should be fine
- return true
- },
- canChangeHideDownload() {
- const hasDisabledDownload = (shareAttribute) => shareAttribute.key === 'download' && shareAttribute.scope === 'permissions' && shareAttribute.enabled === false
- return this.fileInfo.shareAttributes.some(hasDisabledDownload)
- },
- customPermissionsList() {
- const perms = []
- if (hasPermissions(this.share.permissions, ATOMIC_PERMISSIONS.READ)) {
- perms.push('read')
- }
- if (hasPermissions(this.share.permissions, ATOMIC_PERMISSIONS.CREATE)) {
- perms.push('create')
- }
- if (hasPermissions(this.share.permissions, ATOMIC_PERMISSIONS.UPDATE)) {
- perms.push('update')
- }
- if (hasPermissions(this.share.permissions, ATOMIC_PERMISSIONS.DELETE)) {
- perms.push('delete')
- }
- if (hasPermissions(this.share.permissions, ATOMIC_PERMISSIONS.SHARE)) {
- perms.push('share')
- }
- if (this.share.hasDownloadPermission) {
- perms.push('download')
- }
- const capitalizeFirstAndJoin = array => array.map((item, index) => index === 0 ? item[0].toUpperCase() + item.substring(1) : item).join(', ')
-
- return capitalizeFirstAndJoin(perms)
-
- },
- },
- watch: {
- setCustomPermissions(isChecked) {
- if (isChecked) {
- this.sharingPermission = 'custom'
- } else {
- this.sharingPermission = this.revertSharingPermission
- }
- },
- },
- beforeMount() {
- this.initializePermissions()
- this.initializeAttributes()
- console.debug('shareSentIn', this.share)
- console.debug('config', this.config)
- },
-
- methods: {
- updateAtomicPermissions({
- isReadChecked = this.hasRead,
- isEditChecked = this.canEdit,
- isCreateChecked = this.canCreate,
- isDeleteChecked = this.canDelete,
- isReshareChecked = this.canReshare,
- isDownloadChecked = this.canDownload,
- } = {}) {
- // calc permissions if checked
- const permissions = 0
- | (isReadChecked ? ATOMIC_PERMISSIONS.READ : 0)
- | (isCreateChecked ? ATOMIC_PERMISSIONS.CREATE : 0)
- | (isDeleteChecked ? ATOMIC_PERMISSIONS.DELETE : 0)
- | (isEditChecked ? ATOMIC_PERMISSIONS.UPDATE : 0)
- | (isReshareChecked ? ATOMIC_PERMISSIONS.SHARE : 0)
- this.share.permissions = permissions
- if (this.share.hasDownloadPermission !== isDownloadChecked) {
- this.$set(this.share, 'hasDownloadPermission', isDownloadChecked)
- }
- },
-
- toggleCustomPermissions(selectedPermission) {
- if (this.sharingPermission === 'custom') {
- this.advancedSectionAccordionExpanded = true
- this.setCustomPermissions = true
- } else {
- this.advancedSectionAccordionExpanded = false
- this.revertSharingPermission = selectedPermission
- this.setCustomPermissions = false
- }
- },
- initializeAttributes() {
-
- if (this.isNewShare) return
-
- let hasAdvancedAttributes = false
- if (this.isValidShareAttribute(this.share.note)) {
- this.writeNoteToRecipientIsChecked = true
- hasAdvancedAttributes = true
- }
-
- if (this.isValidShareAttribute(this.share.password)) {
- hasAdvancedAttributes = true
- }
-
- if (this.isValidShareAttribute(this.share.expireDate)) {
- hasAdvancedAttributes = true
- }
-
- if (this.isValidShareAttribute(this.share.label)) {
- hasAdvancedAttributes = true
- }
-
- if (hasAdvancedAttributes) {
- this.advancedSectionAccordionExpanded = true
- }
-
- },
- initializePermissions() {
- if (this.share.share_type) {
- this.share.type = this.share.share_type
- }
- // shareType 0 (USER_SHARE) would evaluate to zero
- // Hence the use of hasOwnProperty
- if ('shareType' in this.share) {
- this.share.type = this.share.shareType
- }
- if (this.isNewShare) {
- if (this.isPublicShare) {
- this.sharingPermission = BUNDLED_PERMISSIONS.READ_ONLY.toString()
- } else {
- this.sharingPermission = BUNDLED_PERMISSIONS.ALL.toString()
- }
-
- } else {
- if (this.hasCustomPermissions || this.share.setCustomPermissions) {
- this.sharingPermission = 'custom'
- this.advancedSectionAccordionExpanded = true
- this.setCustomPermissions = true
- } else {
- this.sharingPermission = this.share.permissions.toString()
- }
- }
- },
- async saveShare() {
- const permissionsAndAttributes = ['permissions', 'attributes', 'note', 'expireDate']
- const publicShareAttributes = ['label', 'password', 'hideDownload']
- if (this.isPublicShare) {
- permissionsAndAttributes.push(...publicShareAttributes)
- }
- const sharePermissionsSet = parseInt(this.sharingPermission)
- if (this.setCustomPermissions) {
- this.updateAtomicPermissions()
- } else {
- this.share.permissions = sharePermissionsSet
- }
-
- if (!this.isFolder && this.share.permissions === BUNDLED_PERMISSIONS.ALL) {
- // It's not possible to create an existing file.
- this.share.permissions = BUNDLED_PERMISSIONS.ALL_FILE
- }
- if (!this.writeNoteToRecipientIsChecked) {
- this.share.note = ''
- }
-
- if (this.isPasswordProtected) {
- if (this.isValidShareAttribute(this.share.newPassword)) {
- this.share.password = this.share.newPassword
- this.$delete(this.share, 'newPassword')
- } else {
- if (this.isPasswordEnforced) {
- this.passwordError = true
- return
- }
- }
- } else {
- this.share.password = ''
- }
-
- if (!this.hasExpirationDate) {
- this.share.expireDate = ''
- }
-
- if (this.isNewShare) {
- const incomingShare = {
- permissions: this.share.permissions,
- shareType: this.share.type,
- shareWith: this.share.shareWith,
- attributes: this.share.attributes,
- note: this.share.note,
- }
-
- if (this.hasExpirationDate) {
- incomingShare.expireDate = this.share.expireDate
- }
-
- if (this.isPasswordProtected) {
- incomingShare.password = this.share.password
- }
-
- const share = await this.addShare(incomingShare, this.fileInfo, this.config)
- this.share = share
- this.$emit('add:share', this.share)
- } else {
- this.queueUpdate(...permissionsAndAttributes)
- }
-
- this.$emit('close-sharing-details')
- },
- /**
- * Process the new share request
- *
- * @param {object} value the multiselect option
- * @param {object} fileInfo file data
- * @param {Config} config instance configs
- */
- async addShare(value, fileInfo, config) {
- // Clear the displayed selection
- this.value = null
-
- // handle externalResults from OCA.Sharing.ShareSearch
- if (value.handler) {
- const share = await value.handler(this)
- this.$emit('add:share', new Share(share))
- return true
- }
-
- // this.loading = true // Are we adding loaders the new share flow?
- console.debug('Adding a new share from the input for', value)
- try {
- const path = (fileInfo.path + '/' + fileInfo.name).replace('//', '/')
- const share = await this.createShare({
- path,
- shareType: value.shareType,
- shareWith: value.shareWith,
- permissions: value.permissions,
- attributes: JSON.stringify(fileInfo.shareAttributes),
- ...(value.note ? { note: value.note } : {}),
- ...(value.password ? { password: value.password } : {}),
- ...(value.expireDate ? { expireDate: value.expireDate } : {}),
- })
- return share
- } catch (error) {
- console.error('Error while adding new share', error)
- } finally {
- // this.loading = false // No loader here yet
- }
- },
- async removeShare() {
- await this.onDelete()
- this.$emit('close-sharing-details')
- },
- /**
- * Update newPassword values
- * of share. If password is set but not newPassword
- * then the user did not changed the password
- * If both co-exists, the password have changed and
- * we show it in plain text.
- * Then on submit (or menu close), we sync it.
- *
- * @param {string} password the changed password
- */
- onPasswordChange(password) {
- this.passwordError = !this.isValidShareAttribute(password)
- this.$set(this.share, 'newPassword', password)
- },
- /**
- * Update the password along with "sendPasswordByTalk".
- *
- * If the password was modified the new password is sent; otherwise
- * updating a mail share would fail, as in that case it is required that
- * a new password is set when enabling or disabling
- * "sendPasswordByTalk".
- */
- onPasswordProtectedByTalkChange() {
- if (this.hasUnsavedPassword) {
- this.share.password = this.share.newPassword.trim()
- }
-
- this.queueUpdate('sendPasswordByTalk', 'password')
- },
- isValidShareAttribute(value) {
- if ([null, undefined].includes(value)) {
- return false
- }
-
- if (!(value.trim().length > 0)) {
- return false
- }
-
- return true
- },
- getShareTypeIcon(type) {
- switch (type) {
- case this.SHARE_TYPES.SHARE_TYPE_LINK:
- return LinkIcon
- case this.SHARE_TYPES.SHARE_TYPE_GUEST:
- return UserIcon
- case this.SHARE_TYPES.SHARE_TYPE_REMOTE_GROUP:
- case this.SHARE_TYPES.SHARE_TYPE_GROUP:
- return GroupIcon
- case this.SHARE_TYPES.SHARE_TYPE_EMAIL:
- return EmailIcon
- case this.SHARE_TYPES.SHARE_TYPE_CIRCLE:
- return CircleIcon
- case this.SHARE_TYPES.SHARE_TYPE_ROOM:
- return ShareIcon
- case this.SHARE_TYPES.SHARE_TYPE_DECK:
- return ShareIcon
- case this.SHARE_TYPES.SHARE_TYPE_SCIENCEMESH:
- return ShareIcon
- default:
- return null // Or a default icon component if needed
- }
- },
- },
-}
-</script>
-
-<style lang="scss" scoped>
-.sharingTabDetailsView {
- display: flex;
- flex-direction: column;
- align-items: flex-start;
- width: 96%;
- margin: 0 auto;
-
- &__header {
- display: flex;
- align-items: center;
- box-sizing: border-box;
- margin: 0.2em;
-
- span {
- display: flex;
- align-items: center;
-
- h1 {
- font-size: 15px;
- padding-left: 0.3em;
- }
-
- }
- }
-
- &__quick-permissions {
- display: flex;
- justify-content: center;
- margin-bottom: 0.2em;
- width: 100%;
- margin: 0 auto;
- border-radius: 0;
-
- div {
- width: 100%;
-
- span {
- width: 100%;
-
- span:nth-child(1) {
- align-items: center;
- justify-content: center;
- color: var(--color-primary-element);
- padding: 0.1em;
- }
-
- ::v-deep label {
-
- span {
- display: flex;
- flex-direction: column;
- }
- }
- }
-
- }
- }
-
- &__advanced-control {
- width: 100%;
-
- button {
- margin-top: 0.5em;
- }
-
- }
-
- &__advanced {
- width: 100%;
- margin-bottom: 0.5em;
- text-align: left;
- padding-left: 0;
-
- section {
-
- textarea,
- div.mx-datepicker {
- width: 100%;
- }
-
- textarea {
- height: 80px;
- }
-
- /*
- The following style is applied out of the component's scope
- to remove padding from the label.checkbox-radio-switch__label,
- which is used to group radio checkbox items. The use of ::v-deep
- ensures that the padding is modified without being affected by
- the component's scoping.
- Without this achieving left alignment for the checkboxes would not
- be possible.
- */
- span {
- ::v-deep label {
- padding-left: 0 !important;
- background-color: initial !important;
- border: none !important;
- }
- }
-
- section.custom-permissions-group {
- padding-left: 1.5em;
- }
- }
- }
-
- &__footer {
- width: 100%;
- display: flex;
- position: sticky;
- bottom: 0;
- flex-direction: column;
- justify-content: space-between;
- align-items: flex-start;
-
- >button:first-child {
- font-size: 12px;
- color: rgb(223, 7, 7);
- background-color: #f5f5f5;
- }
-
- .button-group {
- display: flex;
- justify-content: space-between;
- width: 100%;
- margin-top: 16px;
-
- button {
- margin-left: 16px;
-
- &:first-child {
- margin-left: 0;
- }
- }
- }
- }
-}
-</style>
diff --git a/apps/files_sharing/src/views/SharingLinkList.vue b/apps/files_sharing/src/views/SharingLinkList.vue
index 899424be1d8..c3f1425cb70 100644
--- a/apps/files_sharing/src/views/SharingLinkList.vue
+++ b/apps/files_sharing/src/views/SharingLinkList.vue
@@ -39,8 +39,7 @@
:file-info="fileInfo"
@add:share="addShare(...arguments)"
@update:share="awaitForShare(...arguments)"
- @remove:share="removeShare"
- @open-sharing-details="openSharingDetails(share)" />
+ @remove:share="removeShare" />
</template>
</ul>
</template>
@@ -50,7 +49,6 @@
import Share from '../models/Share.js'
import ShareTypes from '../mixins/ShareTypes.js'
import SharingEntryLink from '../components/SharingEntryLink.vue'
-import ShareDetails from '../mixins/ShareDetails.js'
export default {
name: 'SharingLinkList',
@@ -59,7 +57,7 @@ export default {
SharingEntryLink,
},
- mixins: [ShareTypes, ShareDetails],
+ mixins: [ShareTypes],
props: {
fileInfo: {
diff --git a/apps/files_sharing/src/views/SharingList.vue b/apps/files_sharing/src/views/SharingList.vue
index 6da48c2eece..05dc87d9b07 100644
--- a/apps/files_sharing/src/views/SharingList.vue
+++ b/apps/files_sharing/src/views/SharingList.vue
@@ -27,15 +27,15 @@
:file-info="fileInfo"
:share="share"
:is-unique="isUnique(share)"
- @open-sharing-details="openSharingDetails(share)" />
+ @remove:share="removeShare" />
</ul>
</template>
<script>
// eslint-disable-next-line no-unused-vars
+import Share from '../models/Share.js'
import SharingEntry from '../components/SharingEntry.vue'
import ShareTypes from '../mixins/ShareTypes.js'
-import ShareDetails from '../mixins/ShareDetails.js'
export default {
name: 'SharingList',
@@ -44,12 +44,12 @@ export default {
SharingEntry,
},
- mixins: [ShareTypes, ShareDetails],
+ mixins: [ShareTypes],
props: {
fileInfo: {
type: Object,
- default: () => { },
+ default: () => {},
required: true,
},
shares: {
@@ -58,6 +58,7 @@ export default {
required: true,
},
},
+
computed: {
hasShares() {
return this.shares.length === 0
@@ -70,5 +71,18 @@ export default {
}
},
},
+
+ methods: {
+ /**
+ * Remove a share from the shares list
+ *
+ * @param {Share} share the share to remove
+ */
+ removeShare(share) {
+ const index = this.shares.findIndex(item => item === share)
+ // eslint-disable-next-line vue/no-mutating-props
+ this.shares.splice(index, 1)
+ },
+ },
}
</script>
diff --git a/apps/files_sharing/src/views/SharingTab.vue b/apps/files_sharing/src/views/SharingTab.vue
index e5d26156750..bfaf8a766ee 100644
--- a/apps/files_sharing/src/views/SharingTab.vue
+++ b/apps/files_sharing/src/views/SharingTab.vue
@@ -29,7 +29,7 @@
</div>
<!-- shares content -->
- <div v-if="!showSharingDetailsView" class="sharingTab__content">
+ <div v-else class="sharingTab__content">
<!-- shared with me information -->
<SharingEntrySimple v-if="isSharedWithMe" v-bind="sharedWithMe" class="sharing-entry__reshare">
<template #avatar>
@@ -46,22 +46,20 @@
:link-shares="linkShares"
:reshare="reshare"
:shares="shares"
- @open-sharing-details="toggleShareDetailsView" />
+ @add:share="addShare" />
<!-- link shares list -->
<SharingLinkList v-if="!loading"
ref="linkShareList"
:can-reshare="canReshare"
:file-info="fileInfo"
- :shares="linkShares"
- @open-sharing-details="toggleShareDetailsView" />
+ :shares="linkShares" />
<!-- other shares list -->
<SharingList v-if="!loading"
ref="shareList"
:shares="shares"
- :file-info="fileInfo"
- @open-sharing-details="toggleShareDetailsView" />
+ :file-info="fileInfo" />
<!-- inherited shares -->
<SharingInherited v-if="canReshare && !loading" :file-info="fileInfo" />
@@ -76,15 +74,6 @@
:name="fileInfo.name" />
</div>
- <!-- share details -->
- <div v-else>
- <SharingDetailsTab :file-info="shareDetailsData.fileInfo"
- :share="shareDetailsData.share"
- @close-sharing-details="toggleShareDetailsView"
- @add:share="addShare"
- @remove:share="removeShare" />
- </div>
-
<!-- additional entries, use it with cautious -->
<div v-for="(section, index) in sections"
:ref="'section-' + index"
@@ -113,7 +102,6 @@ import SharingInput from '../components/SharingInput.vue'
import SharingInherited from './SharingInherited.vue'
import SharingLinkList from './SharingLinkList.vue'
import SharingList from './SharingList.vue'
-import SharingDetailsTab from './SharingDetailsTab.vue'
export default {
name: 'SharingTab',
@@ -127,7 +115,6 @@ export default {
SharingInput,
SharingLinkList,
SharingList,
- SharingDetailsTab,
},
mixins: [ShareTypes],
@@ -135,7 +122,7 @@ export default {
data() {
return {
config: new Config(),
- deleteEvent: null,
+
error: '',
expirationInterval: null,
loading: true,
@@ -150,8 +137,6 @@ export default {
sections: OCA.Sharing.ShareTabSections.getSections(),
projectsEnabled: loadState('core', 'projects_enabled', false),
- showSharingDetailsView: false,
- shareDetailsData: {},
}
},
@@ -240,8 +225,6 @@ export default {
this.sharedWithMe = {}
this.shares = []
this.linkShares = []
- this.showSharingDetailsView = false
- this.shareDetailsData = {}
},
/**
@@ -324,7 +307,7 @@ export default {
'Shared with you by {owner}',
{ owner: this.fileInfo.shareOwner },
undefined,
- { escape: false },
+ { escape: false }
),
user: this.fileInfo.shareOwnerId,
}
@@ -338,7 +321,7 @@ export default {
* @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, resolve = () => { }) {
+ 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) {
@@ -348,16 +331,7 @@ export default {
}
this.awaitForShare(share, resolve)
},
- /**
- * Remove a share from the shares list
- *
- * @param {Share} share the share to remove
- */
- removeShare(share) {
- const index = this.shares.findIndex(item => item.id === share.id)
- // eslint-disable-next-line vue/no-mutating-props
- this.shares.splice(index, 1)
- },
+
/**
* Await for next tick and render after the list updated
* Then resolve with the matched vue component of the
@@ -381,12 +355,6 @@ export default {
}
})
},
- toggleShareDetailsView(eventData) {
- if (eventData) {
- this.shareDetailsData = eventData
- }
- this.showSharingDetailsView = !this.showSharingDetailsView
- },
},
}
</script>
@@ -400,7 +368,6 @@ export default {
&__content {
padding: 0 6px;
}
-
&__additionalContent {
margin: 44px 0;
}