aboutsummaryrefslogtreecommitdiffstats
path: root/apps/settings/src/components/AdminSettingsSharingForm.vue
diff options
context:
space:
mode:
Diffstat (limited to 'apps/settings/src/components/AdminSettingsSharingForm.vue')
-rw-r--r--apps/settings/src/components/AdminSettingsSharingForm.vue405
1 files changed, 405 insertions, 0 deletions
diff --git a/apps/settings/src/components/AdminSettingsSharingForm.vue b/apps/settings/src/components/AdminSettingsSharingForm.vue
new file mode 100644
index 00000000000..b0e142d8480
--- /dev/null
+++ b/apps/settings/src/components/AdminSettingsSharingForm.vue
@@ -0,0 +1,405 @@
+<!--
+ - SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
+ - SPDX-License-Identifier: AGPL-3.0-or-later
+-->
+<template>
+ <form class="sharing">
+ <NcCheckboxRadioSwitch aria-controls="settings-sharing-api settings-sharing-api-settings settings-sharing-default-permissions settings-sharing-privary-related"
+ type="switch"
+ :checked.sync="settings.enabled">
+ {{ t('settings', 'Allow apps to use the Share API') }}
+ </NcCheckboxRadioSwitch>
+
+ <div v-show="settings.enabled" id="settings-sharing-api-settings" class="sharing__sub-section">
+ <NcCheckboxRadioSwitch :checked.sync="settings.allowResharing">
+ {{ t('settings', 'Allow resharing') }}
+ </NcCheckboxRadioSwitch>
+ <NcCheckboxRadioSwitch :checked.sync="settings.allowGroupSharing">
+ {{ t('settings', 'Allow sharing with groups') }}
+ </NcCheckboxRadioSwitch>
+ <NcCheckboxRadioSwitch :checked.sync="settings.onlyShareWithGroupMembers">
+ {{ t('settings', 'Restrict users to only share with users in their groups') }}
+ </NcCheckboxRadioSwitch>
+ <div v-show="settings.onlyShareWithGroupMembers" id="settings-sharing-api-excluded-groups" class="sharing__labeled-entry sharing__input">
+ <label for="settings-sharing-only-group-members-excluded-groups">{{ t('settings', 'Ignore the following groups when checking group membership') }}</label>
+ <NcSettingsSelectGroup id="settings-sharing-only-group-members-excluded-groups"
+ v-model="settings.onlyShareWithGroupMembersExcludeGroupList"
+ :label="t('settings', 'Ignore the following groups when checking group membership')"
+ style="width: 100%" />
+ </div>
+ <NcCheckboxRadioSwitch :checked.sync="settings.allowViewWithoutDownload">
+ {{ t('settings', 'Allow users to preview files even if download is disabled') }}
+ </NcCheckboxRadioSwitch>
+ <NcNoteCard v-show="settings.allowViewWithoutDownload"
+ id="settings-sharing-api-view-without-download-hint"
+ class="sharing__note"
+ type="warning">
+ {{ t('settings', 'Users will still be able to screenshot or record the screen. This does not provide any definitive protection.') }}
+ </NcNoteCard>
+ </div>
+
+ <div v-show="settings.enabled" id="settings-sharing-api" class="sharing__section">
+ <NcCheckboxRadioSwitch type="switch"
+ aria-controls="settings-sharing-api-public-link"
+ :checked.sync="settings.allowLinks">
+ {{ t('settings', 'Allow users to share via link and emails') }}
+ </NcCheckboxRadioSwitch>
+ <fieldset v-show="settings.allowLinks" id="settings-sharing-api-public-link" class="sharing__sub-section">
+ <NcCheckboxRadioSwitch :checked.sync="settings.allowPublicUpload">
+ {{ t('settings', 'Allow public uploads') }}
+ </NcCheckboxRadioSwitch>
+ <NcCheckboxRadioSwitch v-model="settings.allowFederationOnPublicShares">
+ {{ t('settings', 'Allow public shares to be added to other clouds by federation.') }}
+ {{ t('settings', 'This will add share permissions to all newly created link shares.') }}
+ </NcCheckboxRadioSwitch>
+ <NcCheckboxRadioSwitch :checked.sync="settings.enableLinkPasswordByDefault">
+ {{ t('settings', 'Always ask for a password') }}
+ </NcCheckboxRadioSwitch>
+ <NcCheckboxRadioSwitch :checked.sync="settings.enforceLinksPassword" :disabled="!settings.enableLinkPasswordByDefault">
+ {{ t('settings', 'Enforce password protection') }}
+ </NcCheckboxRadioSwitch>
+ <label v-if="settings.enforceLinksPasswordExcludedGroupsEnabled" class="sharing__labeled-entry sharing__input">
+ <span>{{ t('settings', 'Exclude groups from password requirements') }}</span>
+ <NcSettingsSelectGroup v-model="settings.enforceLinksPasswordExcludedGroups"
+ style="width: 100%"
+ :disabled="!settings.enforceLinksPassword || !settings.enableLinkPasswordByDefault" />
+ </label>
+ <label class="sharing__labeled-entry sharing__input">
+ <span>{{ t('settings', 'Exclude groups from creating link shares') }}</span>
+ <NcSettingsSelectGroup v-model="settings.allowLinksExcludeGroups"
+ :label="t('settings', 'Exclude groups from creating link shares')"
+ style="width: 100%" />
+ </label>
+ </fieldset>
+
+ <NcCheckboxRadioSwitch type="switch"
+ aria-describedby="settings-sharing-custom-token-disable-hint settings-sharing-custom-token-access-hint"
+ :checked.sync="settings.allowCustomTokens">
+ {{ t('settings', 'Allow users to set custom share link tokens') }}
+ </NcCheckboxRadioSwitch>
+ <div class="sharing__sub-section">
+ <NcNoteCard id="settings-sharing-custom-token-disable-hint"
+ class="sharing__note"
+ type="info">
+ {{ t('settings', 'Shares with custom tokens will continue to be accessible after this setting has been disabled') }}
+ </NcNoteCard>
+ <NcNoteCard id="settings-sharing-custom-token-access-hint"
+ class="sharing__note"
+ type="warning">
+ {{ t('settings', 'Shares with guessable tokens may be accessed easily') }}
+ </NcNoteCard>
+ </div>
+
+ <label>{{ t('settings', 'Limit sharing based on groups') }}</label>
+ <div class="sharing__sub-section">
+ <NcCheckboxRadioSwitch :checked.sync="settings.excludeGroups"
+ name="excludeGroups"
+ value="no"
+ type="radio"
+ @update:checked="onUpdateExcludeGroups">
+ {{ t('settings', 'Allow sharing for everyone (default)') }}
+ </NcCheckboxRadioSwitch>
+ <NcCheckboxRadioSwitch :checked.sync="settings.excludeGroups"
+ name="excludeGroups"
+ value="yes"
+ type="radio"
+ @update:checked="onUpdateExcludeGroups">
+ {{ t('settings', 'Exclude some groups from sharing') }}
+ </NcCheckboxRadioSwitch>
+ <NcCheckboxRadioSwitch :checked.sync="settings.excludeGroups"
+ name="excludeGroups"
+ value="allow"
+ type="radio"
+ @update:checked="onUpdateExcludeGroups">
+ {{ t('settings', 'Limit sharing to some groups') }}
+ </NcCheckboxRadioSwitch>
+ <div v-show="settings.excludeGroups !== 'no'" class="sharing__labeled-entry sharing__input">
+ <NcSettingsSelectGroup id="settings-sharing-excluded-groups"
+ v-model="settings.excludeGroupsList"
+ aria-describedby="settings-sharing-excluded-groups-desc"
+ :label="settings.excludeGroups === 'allow' ? t('settings', 'Groups allowed to share') : t('settings', 'Groups excluded from sharing')"
+ :disabled="settings.excludeGroups === 'no'"
+ style="width: 100%" />
+ <em id="settings-sharing-excluded-groups-desc">{{ t('settings', 'Not allowed groups will still be able to receive shares, but not to initiate them.') }}</em>
+ </div>
+ </div>
+
+ <NcCheckboxRadioSwitch type="switch"
+ aria-controls="settings-sharing-api-expiration"
+ :checked.sync="settings.defaultInternalExpireDate">
+ {{ t('settings', 'Set default expiration date for internal shares') }}
+ </NcCheckboxRadioSwitch>
+ <fieldset v-show="settings.defaultInternalExpireDate" id="settings-sharing-api-expiration" class="sharing__sub-section">
+ <NcCheckboxRadioSwitch :checked.sync="settings.enforceInternalExpireDate">
+ {{ t('settings', 'Enforce expiration date') }}
+ </NcCheckboxRadioSwitch>
+ <NcTextField type="number"
+ class="sharing__input"
+ :label="t('settings', 'Default expiration time of new shares in days')"
+ :placeholder="t('settings', 'Expire shares after x days')"
+ :value.sync="settings.internalExpireAfterNDays" />
+ </fieldset>
+
+ <NcCheckboxRadioSwitch type="switch"
+ aria-controls="settings-sharing-remote-api-expiration"
+ :checked.sync="settings.defaultRemoteExpireDate">
+ {{ t('settings', 'Set default expiration date for shares to other servers') }}
+ </NcCheckboxRadioSwitch>
+ <fieldset v-show="settings.defaultRemoteExpireDate" id="settings-sharing-remote-api-expiration" class="sharing__sub-section">
+ <NcCheckboxRadioSwitch :checked.sync="settings.enforceRemoteExpireDate">
+ {{ t('settings', 'Enforce expiration date for remote shares') }}
+ </NcCheckboxRadioSwitch>
+ <NcTextField type="number"
+ class="sharing__input"
+ :label="t('settings', 'Default expiration time of remote shares in days')"
+ :placeholder="t('settings', 'Expire remote shares after x days')"
+ :value.sync="settings.remoteExpireAfterNDays" />
+ </fieldset>
+
+ <NcCheckboxRadioSwitch type="switch"
+ aria-controls="settings-sharing-api-api-expiration"
+ :checked.sync="settings.defaultExpireDate"
+ :disabled="!settings.allowLinks">
+ {{ t('settings', 'Set default expiration date for shares via link or mail') }}
+ </NcCheckboxRadioSwitch>
+ <fieldset v-show="settings.allowLinks && settings.defaultExpireDate" id="settings-sharing-api-api-expiration" class="sharing__sub-section">
+ <NcCheckboxRadioSwitch :checked.sync="settings.enforceExpireDate">
+ {{ t('settings', 'Enforce expiration date for link or mail shares') }}
+ </NcCheckboxRadioSwitch>
+ <NcTextField type="number"
+ class="sharing__input"
+ :label="t('settings', 'Default expiration time of shares in days')"
+ :placeholder="t('settings', 'Expire shares after x days')"
+ :value.sync="settings.expireAfterNDays" />
+ </fieldset>
+ </div>
+
+ <div v-show="settings.enabled" id="settings-sharing-privary-related" class="sharing__section">
+ <h3>{{ t('settings', 'Privacy settings for sharing') }}</h3>
+
+ <NcCheckboxRadioSwitch type="switch"
+ aria-controls="settings-sharing-privacy-user-enumeration"
+ :checked.sync="settings.allowShareDialogUserEnumeration">
+ {{ t('settings', 'Allow account name autocompletion in share dialog and allow access to the system address book') }}
+ </NcCheckboxRadioSwitch>
+ <fieldset v-show="settings.allowShareDialogUserEnumeration" id="settings-sharing-privacy-user-enumeration" class="sharing__sub-section">
+ <em>
+ {{ t('settings', 'If autocompletion "same group" and "phone number integration" are enabled a match in either is enough to show the user.') }}
+ </em>
+ <NcCheckboxRadioSwitch :checked.sync="settings.restrictUserEnumerationToGroup">
+ {{ t('settings', 'Restrict account name autocompletion and system address book access to users within the same groups') }}
+ </NcCheckboxRadioSwitch>
+ <NcCheckboxRadioSwitch :checked.sync="settings.restrictUserEnumerationToPhone">
+ {{ t('settings', 'Restrict account name autocompletion to users based on phone number integration') }}
+ </NcCheckboxRadioSwitch>
+ </fieldset>
+
+ <NcCheckboxRadioSwitch type="switch" :checked.sync="settings.restrictUserEnumerationFullMatch">
+ {{ t('settings', 'Allow autocompletion when entering the full name or email address (ignoring missing phonebook match and being in the same group)') }}
+ </NcCheckboxRadioSwitch>
+
+ <NcCheckboxRadioSwitch type="switch" :checked.sync="publicShareDisclaimerEnabled">
+ {{ t('settings', 'Show disclaimer text on the public link upload page (only shown when the file list is hidden)') }}
+ </NcCheckboxRadioSwitch>
+ <div v-if="publicShareDisclaimerEnabled"
+ aria-describedby="settings-sharing-privary-related-disclaimer-hint"
+ class="sharing__sub-section">
+ <NcTextArea class="sharing__input"
+ :label="t('settings', 'Disclaimer text')"
+ aria-describedby="settings-sharing-privary-related-disclaimer-hint"
+ :value="settings.publicShareDisclaimerText"
+ @update:value="onUpdateDisclaimer" />
+ <em id="settings-sharing-privary-related-disclaimer-hint" class="sharing__input">
+ {{ t('settings', 'This text will be shown on the public link upload page when the file list is hidden.') }}
+ </em>
+ </div>
+ </div>
+
+ <div id="settings-sharing-default-permissions" class="sharing__section">
+ <h3>{{ t('settings', 'Default share permissions') }}</h3>
+ <SelectSharingPermissions :value.sync="settings.defaultPermissions" />
+ </div>
+ </form>
+</template>
+
+<script lang="ts">
+import { showError, showSuccess } from '@nextcloud/dialogs'
+import { loadState } from '@nextcloud/initial-state'
+import { t } from '@nextcloud/l10n'
+import { snakeCase } from 'lodash'
+import { defineComponent } from 'vue'
+import debounce from 'debounce'
+
+import NcCheckboxRadioSwitch from '@nextcloud/vue/components/NcCheckboxRadioSwitch'
+import NcNoteCard from '@nextcloud/vue/components/NcNoteCard'
+import NcSettingsSelectGroup from '@nextcloud/vue/components/NcSettingsSelectGroup'
+import NcTextArea from '@nextcloud/vue/components/NcTextArea'
+import NcTextField from '@nextcloud/vue/components/NcTextField'
+import SelectSharingPermissions from './SelectSharingPermissions.vue'
+
+interface IShareSettings {
+ enabled: boolean
+ allowGroupSharing: boolean
+ allowLinks: boolean
+ allowLinksExcludeGroups: unknown
+ allowPublicUpload: boolean
+ allowResharing: boolean
+ allowShareDialogUserEnumeration: boolean
+ allowFederationOnPublicShares: boolean
+ restrictUserEnumerationToGroup: boolean
+ restrictUserEnumerationToPhone: boolean
+ restrictUserEnumerationFullMatch: boolean
+ restrictUserEnumerationFullMatchUserId: boolean
+ restrictUserEnumerationFullMatchEmail: boolean
+ restrictUserEnumerationFullMatchIgnoreSecondDN: boolean
+ enforceLinksPassword: boolean
+ enforceLinksPasswordExcludedGroups: string[]
+ enforceLinksPasswordExcludedGroupsEnabled: boolean
+ onlyShareWithGroupMembers: boolean
+ onlyShareWithGroupMembersExcludeGroupList: string[]
+ defaultExpireDate: boolean
+ expireAfterNDays: string
+ enforceExpireDate: boolean
+ excludeGroups: string
+ excludeGroupsList: string[]
+ publicShareDisclaimerText: string
+ enableLinkPasswordByDefault: boolean
+ defaultPermissions: number
+ defaultInternalExpireDate: boolean
+ internalExpireAfterNDays: string
+ enforceInternalExpireDate: boolean
+ defaultRemoteExpireDate: boolean
+ remoteExpireAfterNDays: string
+ enforceRemoteExpireDate: boolean
+ allowCustomTokens: boolean
+ allowViewWithoutDownload: boolean
+}
+
+export default defineComponent({
+ name: 'AdminSettingsSharingForm',
+ components: {
+ NcCheckboxRadioSwitch,
+ NcSettingsSelectGroup,
+ NcNoteCard,
+ NcTextArea,
+ NcTextField,
+ SelectSharingPermissions,
+ },
+ data() {
+ const settingsData = loadState<IShareSettings>('settings', 'sharingSettings')
+ return {
+ settingsData,
+ publicShareDisclaimerEnabled: settingsData.publicShareDisclaimerText !== '',
+ }
+ },
+ computed: {
+ settings() {
+ console.warn('new proxy')
+ return new Proxy(this.settingsData, {
+ get(target, property) {
+ return target[property]
+ },
+ set(target, property: string, newValue) {
+ const configName = `shareapi_${snakeCase(property)}`
+ const value = typeof newValue === 'boolean' ? (newValue ? 'yes' : 'no') : (typeof newValue === 'string' ? newValue : JSON.stringify(newValue))
+ window.OCP.AppConfig.setValue('core', configName, value)
+ target[property] = newValue
+ return true
+ },
+ })
+ },
+ },
+
+ watch: {
+ publicShareDisclaimerEnabled() {
+ // When disabled we just remove the disclaimer content
+ if (this.publicShareDisclaimerEnabled === false) {
+ this.onUpdateDisclaimer('')
+ }
+ },
+ },
+
+ methods: {
+ t,
+
+ onUpdateDisclaimer: debounce(function(value: string) {
+ const options = {
+ success() {
+ if (value !== '') {
+ showSuccess(t('settings', 'Changed disclaimer text'))
+ } else {
+ showSuccess(t('settings', 'Deleted disclaimer text'))
+ }
+ },
+ error() {
+ showError(t('settings', 'Could not set disclaimer text'))
+ },
+ }
+ if (value === '') {
+ window.OCP.AppConfig.deleteKey('core', 'shareapi_public_link_disclaimertext', options)
+ } else {
+ window.OCP.AppConfig.setValue('core', 'shareapi_public_link_disclaimertext', value, options)
+ }
+ this.settingsData.publicShareDisclaimerText = value
+ }, 500) as (v?: string) => void,
+ onUpdateExcludeGroups: debounce(function(value: string) {
+ window.OCP.AppConfig.setValue('core', 'excludeGroups', value)
+ this.settings.excludeGroups = value
+ }, 500) as (v?: string) => void,
+ },
+})
+</script>
+
+<style scoped lang="scss">
+.sharing {
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+
+ &__labeled-entry {
+ display: flex;
+ flex: 1 0;
+ flex-direction: column;
+ gap: 4px;
+ }
+
+ &__section {
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+ margin-block-end: 12px
+ }
+
+ &__sub-section {
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+
+ margin-inline-start: 44px;
+ margin-block-end: 12px
+ }
+
+ &__input {
+ max-width: 500px;
+ // align with checkboxes
+ margin-inline-start: 14px;
+
+ :deep(.v-select.select) {
+ width: 100%;
+ }
+ }
+
+ & &__note {
+ margin: 2px 0;
+ }
+}
+
+@media only screen and (max-width: 350px) {
+ // ensure no overflow happens on small devices (required for WCAG)
+ .sharing {
+ &__sub-section {
+ margin-inline-start: 14px;
+ }
+ }
+}
+</style>