Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nextcloud/server.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLouis Chemineau <louis@chmn.me>2022-02-02 18:10:52 +0300
committerLouis Chemineau <louis@chmn.me>2022-02-15 13:54:17 +0300
commitda435b1e67930e85fc30fd1b94c6214caa086f4f (patch)
tree53eab82dc4e89e8da2e94185588e45a1f21687f7 /apps/files_sharing/src
parentacba237ec56a8987e246d1046bd8f5b763db1336 (diff)
Support CRUD share permissions
Signed-off-by: Louis Chemineau <louis@chmn.me>
Diffstat (limited to 'apps/files_sharing/src')
-rw-r--r--apps/files_sharing/src/components/SharePermissionsEditor.vue291
-rw-r--r--apps/files_sharing/src/components/SharingEntryLink.vue119
-rw-r--r--apps/files_sharing/src/lib/SharePermissionsToolBox.js123
-rw-r--r--apps/files_sharing/src/lib/SharePermissionsToolBox.spec.js96
-rw-r--r--apps/files_sharing/src/mixins/ShareRequests.js5
5 files changed, 523 insertions, 111 deletions
diff --git a/apps/files_sharing/src/components/SharePermissionsEditor.vue b/apps/files_sharing/src/components/SharePermissionsEditor.vue
new file mode 100644
index 00000000000..41c8c0b5cce
--- /dev/null
+++ b/apps/files_sharing/src/components/SharePermissionsEditor.vue
@@ -0,0 +1,291 @@
+<!--
+ - @copyright Copyright (c) 2022 Louis Chmn <louis@chmn.me>
+ -
+ - @author Louis Chmn <louis@chmn.me>
+ -
+ - @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/>.
+ -
+ -->
+
+<template>
+ <span>
+ <!-- file -->
+ <ActionCheckbox v-if="!isFolder"
+ :checked="shareHasPermissions(atomicPermissions.UPDATE)"
+ :disabled="saving"
+ @update:checked="toggleSharePermissions(atomicPermissions.UPDATE)">
+ {{ t('files_sharing', 'Allow editing') }}
+ </ActionCheckbox>
+
+ <!-- folder -->
+ <template v-if="isFolder && fileHasCreatePermission && config.isPublicUploadEnabled">
+ <template v-if="!showCustomPermissionsForm">
+ <ActionRadio :checked="sharePermissionEqual(bundledPermissions.READ_ONLY)"
+ :value="bundledPermissions.READ_ONLY"
+ :name="randomFormName"
+ :disabled="saving"
+ @change="setSharePermissions(bundledPermissions.READ_ONLY)">
+ {{ t('files_sharing', 'Read only') }}
+ </ActionRadio>
+
+ <ActionRadio :checked="sharePermissionEqual(bundledPermissions.UPLOAD_AND_UPDATE)"
+ :value="bundledPermissions.UPLOAD_AND_UPDATE"
+ :disabled="saving"
+ :name="randomFormName"
+ @change="setSharePermissions(bundledPermissions.UPLOAD_AND_UPDATE)">
+ {{ t('files_sharing', 'Allow upload and editing') }}
+ </ActionRadio>
+ <ActionRadio :checked="sharePermissionEqual(bundledPermissions.FILE_DROP)"
+ :value="bundledPermissions.FILE_DROP"
+ :disabled="saving"
+ :name="randomFormName"
+ class="sharing-entry__action--public-upload"
+ @change="setSharePermissions(bundledPermissions.FILE_DROP)">
+ {{ t('files_sharing', 'File drop (upload only)') }}
+ </ActionRadio>
+
+ <!-- custom permissions button -->
+ <ActionButton :title="t('files_sharing', 'Custom permissions')"
+ @click="showCustomPermissionsForm = true">
+ <template #icon>
+ <Tune />
+ </template>
+ {{ sharePermissionsIsBundle ? "" : sharePermissionsSummary }}
+ </ActionButton>
+ </template>
+
+ <!-- custom permissions -->
+ <span v-else :class="{error: !sharePermissionsSetIsValid}">
+ <ActionCheckbox :checked="shareHasPermissions(atomicPermissions.READ)"
+ :disabled="saving || !canToggleSharePermissions(atomicPermissions.READ)"
+ @update:checked="toggleSharePermissions(atomicPermissions.READ)">
+ {{ t('files_sharing', 'Read') }}
+ </ActionCheckbox>
+ <ActionCheckbox :checked="shareHasPermissions(atomicPermissions.CREATE)"
+ :disabled="saving || !canToggleSharePermissions(atomicPermissions.CREATE)"
+ @update:checked="toggleSharePermissions(atomicPermissions.CREATE)">
+ {{ t('files_sharing', 'Upload') }}
+ </ActionCheckbox>
+ <ActionCheckbox :checked="shareHasPermissions(atomicPermissions.UPDATE)"
+ :disabled="saving || !canToggleSharePermissions(atomicPermissions.UPDATE)"
+ @update:checked="toggleSharePermissions(atomicPermissions.UPDATE)">
+ {{ t('files_sharing', 'Edit') }}
+ </ActionCheckbox>
+ <ActionCheckbox :checked="shareHasPermissions(atomicPermissions.DELETE)"
+ :disabled="saving || !canToggleSharePermissions(atomicPermissions.DELETE)"
+ @update:checked="toggleSharePermissions(atomicPermissions.DELETE)">
+ {{ t('files_sharing', 'Delete') }}
+ </ActionCheckbox>
+
+ <ActionButton @click="showCustomPermissionsForm = false">
+ <template #icon>
+ <ChevronLeft />
+ </template>
+ {{ t('files_sharing', 'Bundled permissions') }}
+ </ActionButton>
+ </span>
+ </template>
+ </span>
+</template>
+
+<script>
+import ActionButton from '@nextcloud/vue/dist/Components/ActionButton'
+import ActionRadio from '@nextcloud/vue/dist/Components/ActionRadio'
+import ActionCheckbox from '@nextcloud/vue/dist/Components/ActionCheckbox'
+
+import SharesMixin from '../mixins/SharesMixin'
+import {
+ ATOMIC_PERMISSIONS,
+ BUNDLED_PERMISSIONS,
+ hasPermissions,
+ permissionsSetIsValid,
+ togglePermissions,
+ canTogglePermissions,
+} from '../lib/SharePermissionsToolBox'
+
+import Tune from 'vue-material-design-icons/Tune'
+import ChevronLeft from 'vue-material-design-icons/ChevronLeft'
+
+export default {
+ name: 'SharePermissionsEditor',
+
+ components: {
+ ActionButton,
+ ActionCheckbox,
+ ActionRadio,
+ Tune,
+ ChevronLeft,
+ },
+
+ mixins: [SharesMixin],
+
+ data() {
+ return {
+ randomFormName: Math.random().toString(27).substring(2),
+
+ showCustomPermissionsForm: false,
+
+ atomicPermissions: ATOMIC_PERMISSIONS,
+ bundledPermissions: BUNDLED_PERMISSIONS,
+ }
+ },
+
+ computed: {
+ /**
+ * Return the summary of custom checked permissions.
+ *
+ * @return {string}
+ */
+ sharePermissionsSummary() {
+ return Object.values(this.atomicPermissions)
+ .filter(permission => this.shareHasPermissions(permission))
+ .map(permission => {
+ switch (permission) {
+ case this.atomicPermissions.CREATE:
+ return this.t('files_sharing', 'Upload')
+ case this.atomicPermissions.READ:
+ return this.t('files_sharing', 'Read')
+ case this.atomicPermissions.UPDATE:
+ return this.t('files_sharing', 'Edit')
+ case this.atomicPermissions.DELETE:
+ return this.t('files_sharing', 'Delete')
+ default:
+ return ''
+ }
+ })
+ .join(', ')
+ },
+
+ /**
+ * Return whether the share's permission is a bundle.
+ *
+ * @return {boolean}
+ */
+ sharePermissionsIsBundle() {
+ return Object.values(BUNDLED_PERMISSIONS)
+ .map(bundle => this.sharePermissionEqual(bundle))
+ .filter(isBundle => isBundle)
+ .length > 0
+ },
+
+ /**
+ * Return whether the share's permission is valid.
+ *
+ * @return {boolean}
+ */
+ sharePermissionsSetIsValid() {
+ return permissionsSetIsValid(this.share.permissions)
+ },
+
+ /**
+ * Is the current share a folder ?
+ * TODO: move to a proper FileInfo model?
+ *
+ * @return {boolean}
+ */
+ isFolder() {
+ return this.fileInfo.type === 'dir'
+ },
+
+ /**
+ * Does the current file/folder have create permissions.
+ * TODO: move to a proper FileInfo model?
+ *
+ * @return {boolean}
+ */
+ fileHasCreatePermission() {
+ return !!(this.fileInfo.permissions & ATOMIC_PERMISSIONS.CREATE)
+ },
+ },
+
+ mounted() {
+ // Show the Custom Permissions view on open if the permissions set is not a bundle.
+ this.showCustomPermissionsForm = !this.sharePermissionsIsBundle
+ },
+
+ methods: {
+ /**
+ * Return whether the share has the exact given permissions.
+ *
+ * @param {number} permissions - the permissions to check.
+ *
+ * @return {boolean}
+ */
+ sharePermissionEqual(permissions) {
+ // We use the share's permission without PERMISSION_SHARE as it is not relevant here.
+ return (this.share.permissions & ~ATOMIC_PERMISSIONS.SHARE) === permissions
+ },
+
+ /**
+ * Return whether the share has the given permissions.
+ *
+ * @param {number} permissions - the permissions to check.
+ *
+ * @return {boolean}
+ */
+ shareHasPermissions(permissions) {
+ return hasPermissions(this.share.permissions, permissions)
+ },
+
+ /**
+ * Set the share permissions to the given permissions.
+ *
+ * @param {number} permissions - the permissions to set.
+ *
+ * @return {void}
+ */
+ setSharePermissions(permissions) {
+ this.share.permissions = permissions
+ this.queueUpdate('permissions')
+ },
+
+ /**
+ * Return whether some given permissions can be toggled.
+ *
+ * @param {number} permissionsToToggle - the permissions to toggle.
+ *
+ * @return {boolean}
+ */
+ canToggleSharePermissions(permissionsToToggle) {
+ return canTogglePermissions(this.share.permissions, permissionsToToggle)
+ },
+
+ /**
+ * Toggle a given permission.
+ *
+ * @param {number} permissions - the permissions to toggle.
+ *
+ * @return {void}
+ */
+ toggleSharePermissions(permissions) {
+ this.share.permissions = togglePermissions(this.share.permissions, permissions)
+
+ if (!this.permissionsSetIsValid(this.share.permissions)) {
+ return
+ }
+
+ this.queueUpdate('permissions')
+ },
+ },
+}
+</script>
+<style lang="scss" scoped>
+.error {
+ ::v-deep .action-checkbox__label:before {
+ border: 1px solid var(--color-error);
+ }
+}
+</style>
diff --git a/apps/files_sharing/src/components/SharingEntryLink.vue b/apps/files_sharing/src/components/SharingEntryLink.vue
index 9fefa9b6f90..52215d37ec8 100644
--- a/apps/files_sharing/src/components/SharingEntryLink.vue
+++ b/apps/files_sharing/src/components/SharingEntryLink.vue
@@ -150,39 +150,12 @@
@submit="onLabelSubmit">
{{ t('files_sharing', 'Share label') }}
</ActionInput>
- <!-- folder -->
- <template v-if="isFolder && fileHasCreatePermission && config.isPublicUploadEnabled">
- <ActionRadio :checked="sharePermissions === publicUploadRValue"
- :value="publicUploadRValue"
- :name="randomId"
- :disabled="saving"
- @change="togglePermissions">
- {{ t('files_sharing', 'Read only') }}
- </ActionRadio>
- <ActionRadio :checked="sharePermissions === publicUploadRWValue"
- :value="publicUploadRWValue"
- :disabled="saving"
- :name="randomId"
- @change="togglePermissions">
- {{ t('files_sharing', 'Allow upload and editing') }}
- </ActionRadio>
- <ActionRadio :checked="sharePermissions === publicUploadWValue"
- :value="publicUploadWValue"
- :disabled="saving"
- :name="randomId"
- class="sharing-entry__action--public-upload"
- @change="togglePermissions">
- {{ t('files_sharing', 'File drop (upload only)') }}
- </ActionRadio>
- </template>
-
- <!-- file -->
- <ActionCheckbox v-if="!isFolder"
- :checked.sync="canUpdate"
- :disabled="saving"
- @change="queueUpdate('permissions')">
- {{ t('files_sharing', 'Allow editing') }}
- </ActionCheckbox>
+
+ <SharePermissionsEditor :can-reshare="canReshare"
+ :share.sync="share"
+ :file-info="fileInfo" />
+
+ <ActionSeparator />
<ActionCheckbox :checked.sync="share.hideDownload"
:disabled="saving"
@@ -282,6 +255,8 @@
@submit="onNoteSubmit" />
</template>
+ <ActionSeparator />
+
<!-- external actions -->
<ExternalShareAction v-for="action in externalLinkActions"
:id="action.id"
@@ -336,14 +311,15 @@ import ActionButton from '@nextcloud/vue/dist/Components/ActionButton'
import ActionCheckbox from '@nextcloud/vue/dist/Components/ActionCheckbox'
import ActionInput from '@nextcloud/vue/dist/Components/ActionInput'
import ActionLink from '@nextcloud/vue/dist/Components/ActionLink'
-import ActionRadio from '@nextcloud/vue/dist/Components/ActionRadio'
import ActionText from '@nextcloud/vue/dist/Components/ActionText'
+import ActionSeparator from '@nextcloud/vue/dist/Components/ActionSeparator'
import ActionTextEditable from '@nextcloud/vue/dist/Components/ActionTextEditable'
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 ExternalShareAction from './ExternalShareAction'
+import SharePermissionsEditor from './SharePermissionsEditor'
import GeneratePassword from '../utils/GeneratePassword'
import Share from '../models/Share'
import SharesMixin from '../mixins/SharesMixin'
@@ -355,13 +331,14 @@ export default {
Actions,
ActionButton,
ActionCheckbox,
- ActionRadio,
ActionInput,
ActionLink,
ActionText,
ActionTextEditable,
+ ActionSeparator,
Avatar,
ExternalShareAction,
+ SharePermissionsEditor,
},
directives: {
@@ -385,10 +362,6 @@ export default {
// Are we waiting for password/expiration date
pending: false,
- publicUploadRWValue: OC.PERMISSION_UPDATE | OC.PERMISSION_CREATE | OC.PERMISSION_READ | OC.PERMISSION_DELETE,
- publicUploadRValue: OC.PERMISSION_READ,
- publicUploadWValue: OC.PERMISSION_CREATE,
-
ExternalLegacyLinkActions: OCA.Sharing.ExternalLinkActions.state,
ExternalShareActions: OCA.Sharing.ExternalShareActions.state,
}
@@ -396,27 +369,6 @@ export default {
computed: {
/**
- * Return the current share permissions
- * We always ignore the SHARE permission as this is used for the
- * federated sharing.
- *
- * @return {number}
- */
- sharePermissions() {
- return this.share.permissions & ~OC.PERMISSION_SHARE
- },
- /**
- * Generate a unique random id for this SharingEntryLink only
- * This allows ActionRadios to have the same name prop
- * but not to impact others SharingEntryLink
- *
- * @return {string}
- */
- randomId() {
- return Math.random().toString(27).substr(2)
- },
-
- /**
* Link share label
*
* @return {string}
@@ -580,22 +532,6 @@ export default {
return this.config.isDefaultExpireDateEnforced && this.share && !this.share.id
},
- /**
- * Can the recipient edit the file ?
- *
- * @return {boolean}
- */
- canUpdate: {
- get() {
- return this.share.hasUpdatePermission
- },
- set(enabled) {
- this.share.permissions = enabled
- ? OC.PERMISSION_READ | OC.PERMISSION_UPDATE
- : OC.PERMISSION_READ
- },
- },
-
// if newPassword exists, but is empty, it means
// the user deleted the original password
hasUnsavedPassword() {
@@ -603,26 +539,6 @@ export default {
},
/**
- * Is the current share a folder ?
- * TODO: move to a proper FileInfo model?
- *
- * @return {boolean}
- */
- isFolder() {
- return this.fileInfo.type === 'dir'
- },
-
- /**
- * Does the current file/folder have create permissions
- * TODO: move to a proper FileInfo model?
- *
- * @return {boolean}
- */
- fileHasCreatePermission() {
- return !!(this.fileInfo.permissions & OC.PERMISSION_CREATE)
- },
-
- /**
* Return the public share link
*
* @return {string}
@@ -810,17 +726,6 @@ export default {
},
/**
- * On permissions change
- *
- * @param {Event} event js event
- */
- togglePermissions(event) {
- const permissions = parseInt(event.target.value, 10)
- this.share.permissions = permissions
- this.queueUpdate('permissions')
- },
-
- /**
* Label changed, let's save it to a different key
*
* @param {string} label the share label
diff --git a/apps/files_sharing/src/lib/SharePermissionsToolBox.js b/apps/files_sharing/src/lib/SharePermissionsToolBox.js
new file mode 100644
index 00000000000..f5806df70bf
--- /dev/null
+++ b/apps/files_sharing/src/lib/SharePermissionsToolBox.js
@@ -0,0 +1,123 @@
+/**
+ * @copyright 2022 Louis Chmn <louis@chmn.me>
+ *
+ * @author Louis Chmn <louis@chmn.me>
+ *
+ * @license AGPL-3.0-or-later
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+export const ATOMIC_PERMISSIONS = {
+ NONE: 0,
+ READ: 1,
+ UPDATE: 2,
+ CREATE: 4,
+ DELETE: 8,
+ SHARE: 16,
+}
+
+export const BUNDLED_PERMISSIONS = {
+ READ_ONLY: ATOMIC_PERMISSIONS.READ,
+ 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,
+}
+
+/**
+ * Return whether a given permissions set contains some permissions.
+ *
+ * @param {number} initialPermissionSet - the permissions set.
+ * @param {number} permissionsToCheck - the permissions to check.
+ * @return {boolean}
+ */
+export function hasPermissions(initialPermissionSet, permissionsToCheck) {
+ return initialPermissionSet !== ATOMIC_PERMISSIONS.NONE && (initialPermissionSet & permissionsToCheck) === permissionsToCheck
+}
+
+/**
+ * Return whether a given permissions set is valid.
+ *
+ * @param {number} permissionsSet - the permissions set.
+ *
+ * @return {boolean}
+ */
+export function permissionsSetIsValid(permissionsSet) {
+ // Must have at least READ or CREATE permission.
+ if (!hasPermissions(permissionsSet, ATOMIC_PERMISSIONS.READ) && !hasPermissions(permissionsSet, ATOMIC_PERMISSIONS.CREATE)) {
+ return false
+ }
+
+ // Must have READ permission if have UPDATE or DELETE.
+ if (!hasPermissions(permissionsSet, ATOMIC_PERMISSIONS.READ) && (
+ hasPermissions(permissionsSet, ATOMIC_PERMISSIONS.UPDATE) || hasPermissions(permissionsSet, ATOMIC_PERMISSIONS.DELETE)
+ )) {
+ return false
+ }
+
+ return true
+}
+
+/**
+ * Add some permissions to an initial set of permissions.
+ *
+ * @param {number} initialPermissionSet - the initial permissions.
+ * @param {number} permissionsToAdd - the permissions to add.
+ *
+ * @return {number}
+ */
+export function addPermissions(initialPermissionSet, permissionsToAdd) {
+ return initialPermissionSet | permissionsToAdd
+}
+
+/**
+ * Remove some permissions from an initial set of permissions.
+ *
+ * @param {number} initialPermissionSet - the initial permissions.
+ * @param {number} permissionsToSubtract - the permissions to remove.
+ *
+ * @return {number}
+ */
+export function subtractPermissions(initialPermissionSet, permissionsToSubtract) {
+ return initialPermissionSet & ~permissionsToSubtract
+}
+
+/**
+ * Toggle some permissions from an initial set of permissions.
+ *
+ * @param {number} initialPermissionSet - the permissions set.
+ * @param {number} permissionsToToggle - the permissions to toggle.
+ *
+ * @return {number}
+ */
+export function togglePermissions(initialPermissionSet, permissionsToToggle) {
+ if (hasPermissions(initialPermissionSet, permissionsToToggle)) {
+ return subtractPermissions(initialPermissionSet, permissionsToToggle)
+ } else {
+ return addPermissions(initialPermissionSet, permissionsToToggle)
+ }
+}
+
+/**
+ * Return whether some given permissions can be toggled from a permission set.
+ *
+ * @param {number} permissionSet - the initial permissions set.
+ * @param {number} permissionsToToggle - the permissions to toggle.
+ *
+ * @return {boolean}
+ */
+export function canTogglePermissions(permissionSet, permissionsToToggle) {
+ return permissionsSetIsValid(togglePermissions(permissionSet, permissionsToToggle))
+}
diff --git a/apps/files_sharing/src/lib/SharePermissionsToolBox.spec.js b/apps/files_sharing/src/lib/SharePermissionsToolBox.spec.js
new file mode 100644
index 00000000000..7ae29c7134a
--- /dev/null
+++ b/apps/files_sharing/src/lib/SharePermissionsToolBox.spec.js
@@ -0,0 +1,96 @@
+/**
+ * @copyright 2022 Louis Chmn <louis@chmn.me>
+ *
+ * @author Louis Chmn <louis@chmn.me>
+ *
+ * @license AGPL-3.0-or-later
+ *
+ * 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 {
+ ATOMIC_PERMISSIONS,
+ BUNDLED_PERMISSIONS,
+ addPermissions,
+ subtractPermissions,
+ hasPermissions,
+ permissionsSetIsValid,
+ togglePermissions,
+ canTogglePermissions,
+} from '../lib/SharePermissionsToolBox'
+
+describe('SharePermissionsToolBox', () => {
+ test('Adding permissions', () => {
+ expect(addPermissions(ATOMIC_PERMISSIONS.NONE, ATOMIC_PERMISSIONS.NONE)).toBe(ATOMIC_PERMISSIONS.NONE)
+ expect(addPermissions(ATOMIC_PERMISSIONS.NONE, ATOMIC_PERMISSIONS.READ)).toBe(ATOMIC_PERMISSIONS.READ)
+ expect(addPermissions(ATOMIC_PERMISSIONS.READ, ATOMIC_PERMISSIONS.READ)).toBe(ATOMIC_PERMISSIONS.READ)
+ expect(addPermissions(ATOMIC_PERMISSIONS.READ, ATOMIC_PERMISSIONS.UPDATE)).toBe(ATOMIC_PERMISSIONS.READ | ATOMIC_PERMISSIONS.UPDATE)
+ expect(addPermissions(ATOMIC_PERMISSIONS.READ | ATOMIC_PERMISSIONS.UPDATE, ATOMIC_PERMISSIONS.CREATE | ATOMIC_PERMISSIONS.DELETE | ATOMIC_PERMISSIONS.SHARE)).toBe(BUNDLED_PERMISSIONS.ALL)
+ expect(addPermissions(BUNDLED_PERMISSIONS.ALL, ATOMIC_PERMISSIONS.READ)).toBe(BUNDLED_PERMISSIONS.ALL)
+ expect(addPermissions(BUNDLED_PERMISSIONS.ALL, ATOMIC_PERMISSIONS.NONE)).toBe(BUNDLED_PERMISSIONS.ALL)
+ })
+
+ test('Subtract permissions', () => {
+ expect(subtractPermissions(ATOMIC_PERMISSIONS.READ, ATOMIC_PERMISSIONS.NONE)).toBe(ATOMIC_PERMISSIONS.READ)
+ expect(subtractPermissions(ATOMIC_PERMISSIONS.READ, ATOMIC_PERMISSIONS.READ)).toBe(ATOMIC_PERMISSIONS.NONE)
+ expect(subtractPermissions(ATOMIC_PERMISSIONS.READ, ATOMIC_PERMISSIONS.UPDATE)).toBe(ATOMIC_PERMISSIONS.READ)
+ expect(subtractPermissions(ATOMIC_PERMISSIONS.READ | ATOMIC_PERMISSIONS.UPDATE, ATOMIC_PERMISSIONS.UPDATE)).toBe(ATOMIC_PERMISSIONS.READ)
+ expect(subtractPermissions(ATOMIC_PERMISSIONS.READ | ATOMIC_PERMISSIONS.UPDATE, ATOMIC_PERMISSIONS.CREATE | ATOMIC_PERMISSIONS.DELETE)).toBe(ATOMIC_PERMISSIONS.READ | ATOMIC_PERMISSIONS.UPDATE)
+ expect(subtractPermissions(ATOMIC_PERMISSIONS.READ | ATOMIC_PERMISSIONS.UPDATE, ATOMIC_PERMISSIONS.UPDATE | ATOMIC_PERMISSIONS.DELETE)).toBe(ATOMIC_PERMISSIONS.READ)
+ expect(subtractPermissions(BUNDLED_PERMISSIONS.ALL, ATOMIC_PERMISSIONS.READ)).toBe(ATOMIC_PERMISSIONS.UPDATE | ATOMIC_PERMISSIONS.CREATE | ATOMIC_PERMISSIONS.DELETE | ATOMIC_PERMISSIONS.SHARE)
+ })
+
+ test('Has permissions', () => {
+ expect(hasPermissions(ATOMIC_PERMISSIONS.NONE, ATOMIC_PERMISSIONS.READ)).toBe(false)
+ expect(hasPermissions(ATOMIC_PERMISSIONS.READ, ATOMIC_PERMISSIONS.NONE)).toBe(true)
+ expect(hasPermissions(BUNDLED_PERMISSIONS.READ_ONLY, ATOMIC_PERMISSIONS.READ)).toBe(true)
+ expect(hasPermissions(BUNDLED_PERMISSIONS.READ_ONLY, ATOMIC_PERMISSIONS.UPDATE)).toBe(false)
+ expect(hasPermissions(BUNDLED_PERMISSIONS.READ_ONLY, ATOMIC_PERMISSIONS.DELETE)).toBe(false)
+ expect(hasPermissions(BUNDLED_PERMISSIONS.ALL, ATOMIC_PERMISSIONS.DELETE)).toBe(true)
+ })
+
+ test('Toggle permissions', () => {
+ expect(togglePermissions(BUNDLED_PERMISSIONS.ALL, BUNDLED_PERMISSIONS.UPLOAD_AND_UPDATE)).toBe(ATOMIC_PERMISSIONS.SHARE)
+ expect(togglePermissions(BUNDLED_PERMISSIONS.ALL, BUNDLED_PERMISSIONS.FILE_DROP)).toBe(ATOMIC_PERMISSIONS.READ | ATOMIC_PERMISSIONS.UPDATE | ATOMIC_PERMISSIONS.DELETE | ATOMIC_PERMISSIONS.SHARE)
+ expect(togglePermissions(BUNDLED_PERMISSIONS.ALL, ATOMIC_PERMISSIONS.NONE)).toBe(BUNDLED_PERMISSIONS.ALL)
+ expect(togglePermissions(ATOMIC_PERMISSIONS.NONE, BUNDLED_PERMISSIONS.ALL)).toBe(BUNDLED_PERMISSIONS.ALL)
+ expect(togglePermissions(ATOMIC_PERMISSIONS.READ, BUNDLED_PERMISSIONS.ALL)).toBe(BUNDLED_PERMISSIONS.ALL)
+ })
+
+ test('Permissions set is valid', () => {
+ expect(permissionsSetIsValid(ATOMIC_PERMISSIONS.NONE)).toBe(false)
+ expect(permissionsSetIsValid(ATOMIC_PERMISSIONS.READ)).toBe(true)
+ expect(permissionsSetIsValid(ATOMIC_PERMISSIONS.CREATE)).toBe(true)
+ expect(permissionsSetIsValid(ATOMIC_PERMISSIONS.UPDATE)).toBe(false)
+ expect(permissionsSetIsValid(ATOMIC_PERMISSIONS.DELETE)).toBe(false)
+ expect(permissionsSetIsValid(ATOMIC_PERMISSIONS.READ | ATOMIC_PERMISSIONS.UPDATE)).toBe(true)
+ expect(permissionsSetIsValid(ATOMIC_PERMISSIONS.READ | ATOMIC_PERMISSIONS.DELETE)).toBe(true)
+ expect(permissionsSetIsValid(ATOMIC_PERMISSIONS.CREATE | ATOMIC_PERMISSIONS.UPDATE)).toBe(false)
+ expect(permissionsSetIsValid(ATOMIC_PERMISSIONS.CREATE | ATOMIC_PERMISSIONS.DELETE)).toBe(false)
+ expect(permissionsSetIsValid(ATOMIC_PERMISSIONS.READ | ATOMIC_PERMISSIONS.CREATE | ATOMIC_PERMISSIONS.UPDATE)).toBe(true)
+ expect(permissionsSetIsValid(ATOMIC_PERMISSIONS.READ | ATOMIC_PERMISSIONS.CREATE | ATOMIC_PERMISSIONS.DELETE)).toBe(true)
+ })
+
+ test('Toggle permissions', () => {
+ expect(canTogglePermissions(ATOMIC_PERMISSIONS.READ, ATOMIC_PERMISSIONS.READ)).toBe(false)
+ expect(canTogglePermissions(ATOMIC_PERMISSIONS.CREATE, ATOMIC_PERMISSIONS.READ)).toBe(true)
+ expect(canTogglePermissions(ATOMIC_PERMISSIONS.READ | ATOMIC_PERMISSIONS.UPDATE, ATOMIC_PERMISSIONS.READ)).toBe(false)
+ expect(canTogglePermissions(ATOMIC_PERMISSIONS.READ | ATOMIC_PERMISSIONS.DELETE, ATOMIC_PERMISSIONS.READ)).toBe(false)
+ expect(canTogglePermissions(ATOMIC_PERMISSIONS.READ | ATOMIC_PERMISSIONS.CREATE | ATOMIC_PERMISSIONS.UPDATE, ATOMIC_PERMISSIONS.READ)).toBe(false)
+ expect(canTogglePermissions(ATOMIC_PERMISSIONS.READ | ATOMIC_PERMISSIONS.CREATE | ATOMIC_PERMISSIONS.DELETE, ATOMIC_PERMISSIONS.READ)).toBe(false)
+ expect(canTogglePermissions(ATOMIC_PERMISSIONS.READ | ATOMIC_PERMISSIONS.CREATE | ATOMIC_PERMISSIONS.UPDATE, ATOMIC_PERMISSIONS.CREATE)).toBe(true)
+ expect(canTogglePermissions(ATOMIC_PERMISSIONS.READ | ATOMIC_PERMISSIONS.CREATE | ATOMIC_PERMISSIONS.DELETE, ATOMIC_PERMISSIONS.CREATE)).toBe(true)
+ })
+})
diff --git a/apps/files_sharing/src/mixins/ShareRequests.js b/apps/files_sharing/src/mixins/ShareRequests.js
index 15585ec49cf..bc6e3bf1644 100644
--- a/apps/files_sharing/src/mixins/ShareRequests.js
+++ b/apps/files_sharing/src/mixins/ShareRequests.js
@@ -31,9 +31,6 @@ import axios from '@nextcloud/axios'
import Share from '../models/Share'
const shareUrl = generateOcsUrl('apps/files_sharing/api/v1/shares')
-const headers = {
- 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
-}
export default {
methods: {
@@ -103,7 +100,7 @@ export default {
*/
async updateShare(id, properties) {
try {
- const request = await axios.put(shareUrl + `/${id}`, properties, headers)
+ const request = await axios.put(shareUrl + `/${id}`, properties)
if (!request?.data?.ocs) {
throw request
}