Browse Source

enh(settings): Migrate admin settings for sharing to vue

This is required to get the fixes for a11y from `@nextcloud/vue`.

Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
Ferdinand Thiessen 5 months ago
parent
commit
22163c60d4

+ 50 - 83
apps/settings/lib/Settings/Admin/Sharing.php

@@ -13,8 +13,9 @@
  * @author Sascha Wiswedel <sascha.wiswedel@nextcloud.com>
  * @author Vincent Petry <vincent@nextcloud.com>
  * @author Thomas Citharel <nextcloud@tcit.fr>
+ * @author Ferdinand Thiessen <opensource@fthiessen.de>
  *
- * @license GNU AGPL version 3 or any later version
+ * @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
@@ -34,31 +35,25 @@ namespace OCA\Settings\Settings\Admin;
 
 use OCP\App\IAppManager;
 use OCP\AppFramework\Http\TemplateResponse;
+use OCP\AppFramework\Services\IInitialState;
 use OCP\Constants;
 use OCP\IConfig;
 use OCP\IL10N;
+use OCP\IURLGenerator;
 use OCP\Settings\IDelegatedSettings;
 use OCP\Share\IManager;
 use OCP\Util;
 
 class Sharing implements IDelegatedSettings {
-	/** @var IConfig */
-	private $config;
-
-	/** @var IL10N */
-	private $l;
-
-	/** @var IManager */
-	private $shareManager;
-
-	/** @var IAppManager */
-	private $appManager;
-
-	public function __construct(IConfig $config, IL10N $l, IManager $shareManager, IAppManager $appManager) {
-		$this->config = $config;
-		$this->l = $l;
-		$this->shareManager = $shareManager;
-		$this->appManager = $appManager;
+	public function __construct(
+		private IConfig $config,
+		private IL10N $l,
+		private IManager $shareManager,
+		private IAppManager $appManager,
+		private IURLGenerator $urlGenerator,
+		private IInitialState $initialState,
+		private string $appName,
+	) {
 	}
 
 	/**
@@ -66,85 +61,57 @@ class Sharing implements IDelegatedSettings {
 	 */
 	public function getForm() {
 		$excludedGroups = $this->config->getAppValue('core', 'shareapi_exclude_groups_list', '');
-		$excludeGroupsList = !is_null(json_decode($excludedGroups))
-			? implode('|', json_decode($excludedGroups, true)) : '';
 		$linksExcludedGroups = $this->config->getAppValue('core', 'shareapi_allow_links_exclude_groups', '');
-		$linksExcludeGroupsList = !is_null(json_decode($linksExcludedGroups))
-			? implode('|', json_decode($linksExcludedGroups, true)) : '';
-
 		$excludedPasswordGroups = $this->config->getAppValue('core', 'shareapi_enforce_links_password_excluded_groups', '');
-		$excludedPasswordGroupsList = !is_null(json_decode($excludedPasswordGroups))
-			? implode('|', json_decode($excludedPasswordGroups, true)) : '';
-
 
 		$parameters = [
 			// Built-In Sharing
-			'sharingAppEnabled' => $this->appManager->isEnabledForUser('files_sharing'),
-			'allowGroupSharing' => $this->config->getAppValue('core', 'shareapi_allow_group_sharing', 'yes'),
-			'allowLinks' => $this->config->getAppValue('core', 'shareapi_allow_links', 'yes'),
-			'allowLinksExcludeGroups' => $linksExcludeGroupsList,
-			'allowPublicUpload' => $this->config->getAppValue('core', 'shareapi_allow_public_upload', 'yes'),
-			'allowResharing' => $this->config->getAppValue('core', 'shareapi_allow_resharing', 'yes'),
-			'allowShareDialogUserEnumeration' => $this->config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes'),
-			'restrictUserEnumerationToGroup' => $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_group', 'no'),
-			'restrictUserEnumerationToPhone' => $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_phone', 'no'),
-			'restrictUserEnumerationFullMatch' => $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_full_match', 'yes'),
-			'restrictUserEnumerationFullMatchUserId' => $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_full_match_userid', 'yes'),
-			'restrictUserEnumerationFullMatchEmail' => $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_full_match_email', 'yes'),
-			'restrictUserEnumerationFullMatchIgnoreSecondDN' => $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_full_match_ignore_second_dn', 'no'),
-			'enforceLinkPassword' => Util::isPublicLinkPasswordRequired(false),
-			'passwordExcludedGroups' => $excludedPasswordGroupsList,
+			'enabled' => $this->getHumanBooleanConfig('core', 'shareapi_enabled', true),
+			'allowGroupSharing' => $this->getHumanBooleanConfig('core', 'shareapi_allow_group_sharing', true),
+			'allowLinks' => $this->getHumanBooleanConfig('core', 'shareapi_allow_links', true),
+			'allowLinksExcludeGroups' => json_decode($linksExcludedGroups, true) ?? [],
+			'allowPublicUpload' => $this->getHumanBooleanConfig('core', 'shareapi_allow_public_upload', true),
+			'allowResharing' => $this->getHumanBooleanConfig('core', 'shareapi_allow_resharing', true),
+			'allowShareDialogUserEnumeration' => $this->getHumanBooleanConfig('core', 'shareapi_allow_share_dialog_user_enumeration', true),
+			'restrictUserEnumerationToGroup' => $this->getHumanBooleanConfig('core', 'shareapi_restrict_user_enumeration_to_group'),
+			'restrictUserEnumerationToPhone' => $this->getHumanBooleanConfig('core', 'shareapi_restrict_user_enumeration_to_phone'),
+			'restrictUserEnumerationFullMatch' => $this->getHumanBooleanConfig('core', 'shareapi_restrict_user_enumeration_full_match', true),
+			'restrictUserEnumerationFullMatchUserId' => $this->getHumanBooleanConfig('core', 'shareapi_restrict_user_enumeration_full_match_userid', true),
+			'restrictUserEnumerationFullMatchEmail' => $this->getHumanBooleanConfig('core', 'shareapi_restrict_user_enumeration_full_match_email', true),
+			'restrictUserEnumerationFullMatchIgnoreSecondDN' => $this->getHumanBooleanConfig('core', 'shareapi_restrict_user_enumeration_full_match_ignore_second_dn'),
+			'enforceLinksPassword' => Util::isPublicLinkPasswordRequired(false),
+			'passwordExcludedGroups' => json_decode($excludedPasswordGroups) ?? [],
 			'passwordExcludedGroupsFeatureEnabled' => $this->config->getSystemValueBool('sharing.allow_disabled_password_enforcement_groups', false),
 			'onlyShareWithGroupMembers' => $this->shareManager->shareWithGroupMembersOnly(),
-			'shareAPIEnabled' => $this->config->getAppValue('core', 'shareapi_enabled', 'yes'),
-			'shareDefaultExpireDateSet' => $this->config->getAppValue('core', 'shareapi_default_expire_date', 'no'),
-			'shareExpireAfterNDays' => $this->config->getAppValue('core', 'shareapi_expire_after_n_days', '7'),
-			'shareEnforceExpireDate' => $this->config->getAppValue('core', 'shareapi_enforce_expire_date', 'no'),
-			'shareExcludeGroups' => $this->config->getAppValue('core', 'shareapi_exclude_groups', 'no') === 'yes',
-			'shareExcludedGroupsList' => $excludeGroupsList,
+			'defaultExpireDate' => $this->getHumanBooleanConfig('core', 'shareapi_default_expire_date'),
+			'expireAfterNDays' => $this->config->getAppValue('core', 'shareapi_expire_after_n_days', '7'),
+			'enforceExpireDate' => $this->getHumanBooleanConfig('core', 'shareapi_enforce_expire_date'),
+			'excludeGroups' => $this->getHumanBooleanConfig('core', 'shareapi_exclude_groups'),
+			'excludeGroupsList' => json_decode($excludedGroups, true) ?? [],
 			'publicShareDisclaimerText' => $this->config->getAppValue('core', 'shareapi_public_link_disclaimertext', null),
-			'enableLinkPasswordByDefault' => $this->config->getAppValue('core', 'shareapi_enable_link_password_by_default', 'no'),
-			'shareApiDefaultPermissions' => (int)$this->config->getAppValue('core', 'shareapi_default_permissions', (string)Constants::PERMISSION_ALL),
-			'shareApiDefaultPermissionsCheckboxes' => $this->getSharePermissionList(),
-			'shareDefaultInternalExpireDateSet' => $this->config->getAppValue('core', 'shareapi_default_internal_expire_date', 'no'),
-			'shareInternalExpireAfterNDays' => $this->config->getAppValue('core', 'shareapi_internal_expire_after_n_days', '7'),
-			'shareInternalEnforceExpireDate' => $this->config->getAppValue('core', 'shareapi_enforce_internal_expire_date', 'no'),
-			'shareDefaultRemoteExpireDateSet' => $this->config->getAppValue('core', 'shareapi_default_remote_expire_date', 'no'),
-			'shareRemoteExpireAfterNDays' => $this->config->getAppValue('core', 'shareapi_remote_expire_after_n_days', '7'),
-			'shareRemoteEnforceExpireDate' => $this->config->getAppValue('core', 'shareapi_enforce_remote_expire_date', 'no'),
+			'enableLinkPasswordByDefault' => $this->getHumanBooleanConfig('core', 'shareapi_enable_link_password_by_default'),
+			'defaultPermissions' => (int)$this->config->getAppValue('core', 'shareapi_default_permissions', (string)Constants::PERMISSION_ALL),
+			'defaultInternalExpireDate' => $this->getHumanBooleanConfig('core', 'shareapi_default_internal_expire_date'),
+			'internalExpireAfterNDays' => $this->config->getAppValue('core', 'shareapi_internal_expire_after_n_days', '7'),
+			'enforceInternalExpireDate' => $this->getHumanBooleanConfig('core', 'shareapi_enforce_internal_expire_date'),
+			'defaultRemoteExpireDate' => $this->getHumanBooleanConfig('core', 'shareapi_default_remote_expire_date'),
+			'remoteExpireAfterNDays' => $this->config->getAppValue('core', 'shareapi_remote_expire_after_n_days', '7'),
+			'enforceRemoteExpireDate' => $this->getHumanBooleanConfig('core', 'shareapi_enforce_remote_expire_date'),
 		];
 
-		return new TemplateResponse('settings', 'settings/admin/sharing', $parameters, '');
+		$this->initialState->provideInitialState('sharingAppEnabled', $this->appManager->isEnabledForUser('files_sharing'));
+		$this->initialState->provideInitialState('sharingDocumentation', $this->urlGenerator->linkToDocs('admin-sharing'));
+		$this->initialState->provideInitialState('sharingSettings', $parameters);
+
+		\OCP\Util::addScript($this->appName, 'vue-settings-admin-sharing');
+		return new TemplateResponse($this->appName, 'settings/admin/sharing', [], '');
 	}
 
 	/**
-	 * get share permission list for template
-	 *
-	 * @return array
+	 * Helper function to retrive boolean values from human readable strings ('yes' / 'no')
 	 */
-	private function getSharePermissionList() {
-		return [
-			[
-				'id' => 'cancreate',
-				'label' => $this->l->t('Create'),
-				'value' => Constants::PERMISSION_CREATE
-			],
-			[
-				'id' => 'canupdate',
-				'label' => $this->l->t('Change'),
-				'value' => Constants::PERMISSION_UPDATE
-			],
-			[
-				'id' => 'candelete',
-				'label' => $this->l->t('Delete'),
-				'value' => Constants::PERMISSION_DELETE
-			],
-			[
-				'id' => 'canshare',
-				'label' => $this->l->t('Reshare'),
-				'value' => Constants::PERMISSION_SHARE
-			],
-		];
+	private function getHumanBooleanConfig(string $app, string $key, bool $default = false): bool {
+		return $this->config->getAppValue($app, $key, $default ? 'yes' : 'no') === 'yes';
 	}
 
 	/**

+ 0 - 3
apps/settings/src/.jshintrc

@@ -1,3 +0,0 @@
-{
-	"esversion": 6
-}

+ 30 - 0
apps/settings/src/admin-settings-sharing.ts

@@ -0,0 +1,30 @@
+/**
+ * @copyright Copyright (c) 2023 Ferdinand Thiessen <opensource@fthiessen.de>
+ *
+ * @author Ferdinand Thiessen <opensource@fthiessen.de>
+ *
+ * @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 Vue from 'vue'
+import AdminSettingsSharing from './views/AdminSettingsSharing.vue'
+
+export default new Vue({
+	name: 'AdminSettingsSharingSection',
+	el: '#vue-admin-settings-sharing',
+	render: (h) => h(AdminSettingsSharing),
+})

+ 0 - 133
apps/settings/src/admin.js

@@ -1,133 +1,10 @@
 window.addEventListener('DOMContentLoaded', () => {
-	$('#linksExcludedGroups,#passwordsExcludedGroups').each(function(index, element) {
-		OC.Settings.setupGroupsSelect($(element))
-		$(element).change(function(ev) {
-			let groups = ev.val || []
-			groups = JSON.stringify(groups)
-			OCP.AppConfig.setValue('core', $(this).attr('name'), groups)
-		})
-	})
-
 	$('#loglevel').change(function() {
 		$.post(OC.generateUrl('/settings/admin/log/level'), { level: $(this).val() }, () => {
 			OC.Log.reload()
 		})
 	})
 
-	$('#shareAPIEnabled').change(function() {
-		$('#shareAPI p:not(#enable)').toggleClass('hidden', !this.checked)
-	})
-
-	$('#shareapiExpireAfterNDays').on('input', function() {
-		this.value = this.value.replace(/\D/g, '')
-	})
-
-	$('#shareAPI input:not(.noJSAutoUpdate)').change(function() {
-		let value = $(this).val()
-		if ($(this).attr('type') === 'checkbox') {
-			if (this.checked) {
-				value = 'yes'
-			} else {
-				value = 'no'
-			}
-		}
-		OCP.AppConfig.setValue('core', $(this).attr('name'), value)
-	})
-
-	$('#shareapiDefaultExpireDate').change(function() {
-		$('#setDefaultExpireDate').toggleClass('hidden', !this.checked)
-	})
-
-	$('#shareapiDefaultInternalExpireDate').change(function() {
-		$('#setDefaultInternalExpireDate').toggleClass('hidden', !this.checked)
-	})
-
-	$('#shareapiDefaultRemoteExpireDate').change(function() {
-		$('#setDefaultRemoteExpireDate').toggleClass('hidden', !this.checked)
-	})
-
-	$('#enableLinkPasswordByDefault').change(function() {
-		if (this.checked) {
-			$('#enforceLinkPassword').removeAttr('disabled')
-			$('#passwordsExcludedGroups').removeAttr('disabled')
-		} else {
-			$('#enforceLinkPassword').attr('disabled', '')
-			$('#passwordsExcludedGroups').attr('disabled', '')
-
-			// Uncheck "Enforce password protection" when "Always asks for a
-			// password" is unchecked; the change event needs to be explicitly
-			// triggered so it behaves like a change done by the user.
-			$('#enforceLinkPassword').removeAttr('checked').trigger('change')
-		}
-	})
-
-	$('#enforceLinkPassword').change(function() {
-		$('#selectPasswordsExcludedGroups').toggleClass('hidden', !this.checked)
-	})
-
-	$('#publicShareDisclaimer').change(function() {
-		$('#publicShareDisclaimerText').toggleClass('hidden', !this.checked)
-		if (!this.checked) {
-			savePublicShareDisclaimerText('')
-		}
-	})
-
-	$('#shareApiDefaultPermissionsSection input').change(function(ev) {
-		const $el = $('#shareApiDefaultPermissions')
-		const $target = $(ev.target)
-
-		let value = $el.val()
-		if ($target.is(':checked')) {
-			value = value | $target.val()
-		} else {
-			value = value & ~$target.val()
-		}
-
-		// always set read permission
-		value |= OC.PERMISSION_READ
-
-		// this will trigger the field's change event and will save it
-		$el.val(value).change()
-
-		ev.preventDefault()
-
-		return false
-	})
-
-	const savePublicShareDisclaimerText = _.debounce(function(value) {
-		const options = {
-			success: () => {
-				OC.msg.finishedSuccess('#publicShareDisclaimerStatus', t('settings', 'Saved'))
-			},
-			error: () => {
-				OC.msg.finishedError('#publicShareDisclaimerStatus', t('settings', 'Not saved'))
-			},
-		}
-
-		OC.msg.startSaving('#publicShareDisclaimerStatus')
-		if (_.isString(value) && value !== '') {
-			OCP.AppConfig.setValue('core', 'shareapi_public_link_disclaimertext', value, options)
-		} else {
-			$('#publicShareDisclaimerText').val('')
-			OCP.AppConfig.deleteKey('core', 'shareapi_public_link_disclaimertext', options)
-		}
-	}, 500)
-
-	$('#publicShareDisclaimerText').on('change, keyup', function() {
-		savePublicShareDisclaimerText(this.value)
-	})
-
-	$('#shareapi_allow_share_dialog_user_enumeration').on('change', function() {
-		$('#shareapi_restrict_user_enumeration_to_group_setting').toggleClass('hidden', !this.checked)
-		$('#shareapi_restrict_user_enumeration_to_phone_setting').toggleClass('hidden', !this.checked)
-		$('#shareapi_restrict_user_enumeration_combinewarning_setting').toggleClass('hidden', !this.checked)
-	})
-
-	$('#allowLinks').change(function() {
-		$('#publicLinkSettings').toggleClass('hidden', !this.checked)
-		$('#setDefaultExpireDate').toggleClass('hidden', !(this.checked && $('#shareapiDefaultExpireDate')[0].checked))
-	})
-
 	$('#mail_smtpauth').change(function() {
 		if (!this.checked) {
 			$('#mail_credentials').addClass('hidden')
@@ -221,14 +98,6 @@ window.addEventListener('DOMContentLoaded', () => {
 		})
 	})
 
-	$('#allowGroupSharing').change(function() {
-		$('#allowGroupSharing').toggleClass('hidden', !this.checked)
-	})
-
-	$('#shareapiExcludeGroups').change(function() {
-		$('#selectExcludedGroups').toggleClass('hidden', !this.checked)
-	})
-
 	const setupChecks = () => {
 		// run setup checks then gather error messages
 		$.when(
@@ -301,6 +170,4 @@ window.addEventListener('DOMContentLoaded', () => {
 	if (document.getElementById('security-warning') !== null) {
 		setupChecks()
 	}
-
-	$('#shareAPI').removeClass('loading')
 })

+ 355 - 0
apps/settings/src/components/AdminSettingsSharingForm.vue

@@ -0,0 +1,355 @@
+<!--
+	- @copyright 2023 Ferdinand Thiessen <opensource@fthiessen.de>
+	-
+	- @author Ferdinand Thiessen <opensource@fthiessen.de>
+	-
+	- @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/>.
+	-
+-->
+<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>
+
+		<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 :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.passwordExcludedGroupsFeatureEnabled" class="sharing__labeled-entry sharing__input">
+					<span>{{ t('settings', 'Exclude groups from password requirements') }}</span>
+					<NcSettingsSelectGroup v-model="settings.passwordExcludedGroups"
+						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" :checked.sync="settings.excludeGroups">
+				{{ t('settings', 'Exclude groups from sharing') }}
+			</NcCheckboxRadioSwitch>
+			<div v-show="settings.excludeGroups" class="sharing__sub-section">
+				<div class="sharing__labeled-entry sharing__input">
+					<label for="settings-sharing-excluded-groups">{{ t('settings', 'Groups excluded from sharing') }}</label>
+					<NcSettingsSelectGroup id="settings-sharing-excluded-groups"
+						v-model="settings.excludeGroupsList"
+						aria-describedby="settings-sharing-excluded-groups-desc"
+						:label="t('settings', 'Groups excluded from sharing')"
+						:disabled="!settings.excludeGroups"
+						style="width: 100%" />
+					<em id="settings-sharing-excluded-groups-desc">{{ t('settings', 'These 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 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-link-api-expiration" class="sharing__sub-section">
+				<NcCheckboxRadioSwitch :checked.sync="settings.enforceExpireDate">
+					{{ t('settings', 'Enforce expiration date for remote 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 username 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', 'Allow username autocompletion to users within the same groups and limit system address books to users in the same groups') }}
+				</NcCheckboxRadioSwitch>
+				<NcCheckboxRadioSwitch :checked.sync="settings.restrictUserEnumerationToPhone">
+					{{ t('settings', 'Allow username 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"
+				aria-controls="settings-sharing-privary-related-disclaimer"
+				: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="typeof settings.publicShareDisclaimerText === 'string'"
+				id="settings-sharing-privary-related-disclaimer"
+				aria-describedby="settings-sharing-privary-related-disclaimer-hint"
+				class="sharing__sub-section">
+				<NcTextArea class="sharing__input"
+					:label="t('settings', 'Disclaimer text')"
+					: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 {
+	NcCheckboxRadioSwitch,
+	NcSettingsSelectGroup,
+	NcTextArea,
+	NcTextField,
+} from '@nextcloud/vue'
+import { showError, showSuccess } from '@nextcloud/dialogs'
+import { translate as t } from '@nextcloud/l10n'
+import { loadState } from '@nextcloud/initial-state'
+import { defineComponent } from 'vue'
+
+import SelectSharingPermissions from './SelectSharingPermissions.vue'
+import { snakeCase, debounce } from 'lodash'
+
+interface IShareSettings {
+	enabled: boolean
+	allowGroupSharing: boolean
+	allowLinks: boolean
+	allowLinksExcludeGroups: unknown
+	allowPublicUpload: boolean
+	allowResharing: boolean
+	allowShareDialogUserEnumeration: boolean
+	restrictUserEnumerationToGroup: boolean
+	restrictUserEnumerationToPhone: boolean
+	restrictUserEnumerationFullMatch: boolean
+	restrictUserEnumerationFullMatchUserId: boolean
+	restrictUserEnumerationFullMatchEmail: boolean
+	restrictUserEnumerationFullMatchIgnoreSecondDN: boolean
+	enforceLinksPassword: boolean
+	passwordExcludedGroups: string[]
+	passwordExcludedGroupsFeatureEnabled: boolean
+	onlyShareWithGroupMembers: boolean
+	defaultExpireDate: boolean
+	expireAfterNDays: string
+	enforceExpireDate: boolean
+	excludeGroups: boolean
+	excludeGroupsList: string[]
+	publicShareDisclaimerText?: string
+	enableLinkPasswordByDefault: boolean
+	defaultPermissions: number
+	defaultInternalExpireDate: boolean
+	internalExpireAfterNDays: string
+	enforceInternalExpireDate: boolean
+	defaultRemoteExpireDate: boolean
+	remoteExpireAfterNDays: string
+	enforceRemoteExpireDate: boolean
+}
+
+export default defineComponent({
+	name: 'AdminSettingsSharingForm',
+	components: {
+		NcCheckboxRadioSwitch,
+		NcSettingsSelectGroup,
+		NcTextArea,
+		NcTextField,
+		SelectSharingPermissions,
+	},
+	data() {
+		return {
+			settingsData: loadState<IShareSettings>('settings', 'sharingSettings'),
+		}
+	},
+	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
+				},
+			})
+		},
+		publicShareDisclaimerEnabled: {
+			get() {
+				return typeof this.settingsData.publicShareDisclaimerText === 'string'
+			},
+			set(value) {
+				if (value) {
+					this.settingsData.publicShareDisclaimerText = ''
+				} else {
+					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,
+	},
+})
+</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%;
+		}
+	}
+}
+
+@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>

+ 100 - 0
apps/settings/src/components/SelectSharingPermissions.vue

@@ -0,0 +1,100 @@
+<!--
+	- @copyright 2023 Ferdinand Thiessen <opensource@fthiessen.de>
+	-
+	- @author Ferdinand Thiessen <opensource@fthiessen.de>
+	-
+	- @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/>.
+	-
+-->
+<template>
+	<fieldset class="permissions-select">
+		<NcCheckboxRadioSwitch :checked="canCreate" @update:checked="toggle(PERMISSION_CREATE)">
+			{{ t('settings', 'Create') }}
+		</NcCheckboxRadioSwitch>
+		<NcCheckboxRadioSwitch :checked="canUpdate" @update:checked="toggle(PERMISSION_UPDATE)">
+			{{ t('settings', 'Change') }}
+		</NcCheckboxRadioSwitch>
+		<NcCheckboxRadioSwitch :checked="canDelete" @update:checked="toggle(PERMISSION_DELETE)">
+			{{ t('settings', 'Delete') }}
+		</NcCheckboxRadioSwitch>
+		<NcCheckboxRadioSwitch :checked="canShare" @update:checked="toggle(PERMISSION_SHARE)">
+			{{ t('settings', 'Reshare') }}
+		</NcCheckboxRadioSwitch>
+	</fieldset>
+</template>
+
+<script lang="ts">
+import { translate } from '@nextcloud/l10n'
+import { NcCheckboxRadioSwitch } from '@nextcloud/vue'
+import { defineComponent } from 'vue'
+
+export default defineComponent({
+	name: 'SelectSharingPermissions',
+	components: {
+		NcCheckboxRadioSwitch,
+	},
+	props: {
+		value: {
+			type: Number,
+			required: true,
+		},
+	},
+	emits: {
+		'update:value': (value: number) => typeof value === 'number',
+	},
+	data() {
+		return {
+			PERMISSION_UPDATE: 2,
+			PERMISSION_CREATE: 4,
+			PERMISSION_DELETE: 8,
+			PERMISSION_SHARE: 16,
+		}
+	},
+	computed: {
+		canCreate() {
+			return (this.value & this.PERMISSION_CREATE) !== 0
+		},
+		canUpdate() {
+			return (this.value & this.PERMISSION_UPDATE) !== 0
+		},
+		canDelete() {
+			return (this.value & this.PERMISSION_DELETE) !== 0
+		},
+		canShare() {
+			return (this.value & this.PERMISSION_SHARE) !== 0
+		},
+	},
+	methods: {
+		t: translate,
+		/**
+		 * Toggle a permission
+		 * @param permission The permission (bit) to toggle
+		 */
+		toggle(permission: number) {
+			// xor to toggle the bit
+			this.$emit('update:value', this.value ^ permission)
+		},
+	},
+})
+</script>
+
+<style scoped>
+.permissions-select {
+	display: flex;
+	flex-wrap: wrap;
+	gap: 4px;
+}
+</style>

+ 62 - 0
apps/settings/src/views/AdminSettingsSharing.vue

@@ -0,0 +1,62 @@
+<!--
+	- @copyright 2023 Ferdinand Thiessen <opensource@fthiessen.de>
+	-
+	- @author Ferdinand Thiessen <opensource@fthiessen.de>
+	-
+	- @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/>.
+	-
+-->
+<template>
+	<NcSettingsSection data-cy-settings-sharing-section
+		:limit-width="true"
+		:doc-url="documentationLink"
+		:name="t('settings', 'Sharing')"
+		:description="t('settings', 'As admin you can fine-tune the sharing behavior. Please see the documentation for more information.')">
+		<NcNoteCard v-if="!sharingAppEnabled" type="warning">
+			{{ t('settings', 'You need to enable the File sharing App.') }}
+		</NcNoteCard>
+		<AdminSettingsSharingForm v-else />
+	</NcSettingsSection>
+</template>
+
+<script lang="ts">
+import {
+	NcNoteCard,
+	NcSettingsSection,
+} from '@nextcloud/vue'
+import { loadState } from '@nextcloud/initial-state'
+import { translate as t } from '@nextcloud/l10n'
+import { defineComponent } from 'vue'
+import AdminSettingsSharingForm from '../components/AdminSettingsSharingForm.vue'
+
+export default defineComponent({
+	name: 'AdminSettingsSharing',
+	components: {
+		AdminSettingsSharingForm,
+		NcNoteCard,
+		NcSettingsSection,
+	},
+	data() {
+		return {
+			documentationLink: loadState<string>('settings', 'sharingDocumentation', ''),
+			sharingAppEnabled: loadState<boolean>('settings', 'sharingAppEnabled', false),
+		}
+	},
+	methods: {
+		t,
+	},
+})
+</script>

+ 5 - 260
apps/settings/templates/settings/admin/sharing.php

@@ -1,11 +1,10 @@
 <?php
 /**
- * @copyright Copyright (c) 2016 Arthur Schiwon <blizzz@arthur-schiwon.de>
+ * @copyright Copyright (c) 2023 Ferdinand Thiessen <opensource@fthiessen.de>
  *
- * @author Arthur Schiwon <blizzz@arthur-schiwon.de>
- * @author Thomas Citharel <nextcloud@tcit.fr>
+ * @author Ferdinand Thiessen <opensource@fthiessen.de>
  *
- * @license GNU AGPL version 3 or any later version
+ * @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
@@ -18,263 +17,9 @@
  * 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/>.
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
  *
  */
-
-/** @var \OCP\IL10N $l */
-/** @var array $_ */
-
 ?>
 
-<div class="section loading" id="shareAPI">
-	<h2><?php p($l->t('Sharing'));?></h2>
-	<?php if ($_['sharingAppEnabled'] === false) { ?>
-		<p class="warning"><?php p($l->t('You need to enable the File sharing App.')); ?></p>
-	<?php } else { ?>
-		<a target="_blank" rel="noreferrer noopener" class="icon-info"
-		   title="<?php p($l->t('Open documentation'));?>"
-		   href="<?php p(link_to_docs('admin-sharing')); ?>"></a>
-	<div>
-			<p class="settings-hint"><?php p($l->t('As admin you can fine-tune the sharing behavior. Please see the documentation for more information.'));?></p>
-		<p id="enable">
-			<input type="checkbox" name="shareapi_enabled" id="shareAPIEnabled" class="checkbox"
-				   value="1" <?php if ($_['shareAPIEnabled'] === 'yes') {
-	print_unescaped('checked="checked"');
-} ?> />
-			<label for="shareAPIEnabled"><?php p($l->t('Allow apps to use the Share API'));?></label><br/>
-		</p>
-
-		<p id="internalShareSettings" class="indent <?php if ($_['shareAPIEnabled'] === 'no') {
-	p('hidden');
-} ?>">
-			<input type="checkbox" name="shareapi_default_internal_expire_date" id="shareapiDefaultInternalExpireDate" class="checkbox"
-				   value="1" <?php if ($_['shareDefaultInternalExpireDateSet'] === 'yes') {
-	print_unescaped('checked="checked"');
-} ?> />
-			<label for="shareapiDefaultInternalExpireDate"><?php p($l->t('Set default expiration date for shares'));?></label><br/>
-		</p>
-		<p id="setDefaultInternalExpireDate" class="double-indent <?php if ($_['shareDefaultInternalExpireDateSet'] === 'no' || $_['shareAPIEnabled'] === 'no') {
-	p('hidden');
-}?>">
-			<?php p($l->t('Expire after') . ' '); ?>
-			<input type="text" name='shareapi_internal_expire_after_n_days' id="shareapiInternalExpireAfterNDays" placeholder="<?php p('7')?>"
-				   value='<?php p($_['shareInternalExpireAfterNDays']) ?>' />
-			<?php p($l->t('day(s)')); ?>
-			<input type="checkbox" name="shareapi_enforce_internal_expire_date" id="shareapiInternalEnforceExpireDate" class="checkbox"
-				   value="1" <?php if ($_['shareInternalEnforceExpireDate'] === 'yes') {
-	print_unescaped('checked="checked"');
-} ?> />
-			<label for="shareapiInternalEnforceExpireDate"><?php p($l->t('Enforce expiration date'));?></label><br/>
-		</p>
-
-		<p id="remoteShareSettings" class="indent <?php if ($_['shareAPIEnabled'] === 'no') {
-	p('hidden');
-} ?>">
-			<input type="checkbox" name="shareapi_default_remote_expire_date" id="shareapiDefaultRemoteExpireDate" class="checkbox"
-				   value="1" <?php if ($_['shareDefaultRemoteExpireDateSet'] === 'yes') {
-	print_unescaped('checked="checked"');
-} ?> />
-			<label for="shareapiDefaultRemoteExpireDate"><?php p($l->t('Set default expiration date for shares to other servers'));?></label><br/>
-		</p>
-		<p id="setDefaultRemoteExpireDate" class="double-indent <?php if ($_['shareDefaultRemoteExpireDateSet'] === 'no' || $_['shareAPIEnabled'] === 'no') {
-	p('hidden');
-}?>">
-			<?php p($l->t('Expire after'). ' '); ?>
-			<input type="text" name='shareapi_remote_expire_after_n_days' id="shareapiRemoteExpireAfterNDays" placeholder="<?php p('7')?>"
-				   value='<?php p($_['shareRemoteExpireAfterNDays']) ?>' />
-			<?php p($l->t('day(s)')); ?>
-			<input type="checkbox" name="shareapi_enforce_remote_expire_date" id="shareapiRemoteEnforceExpireDate" class="checkbox"
-				   value="1" <?php if ($_['shareRemoteEnforceExpireDate'] === 'yes') {
-	print_unescaped('checked="checked"');
-} ?> />
-			<label for="shareapiRemoteEnforceExpireDate"><?php p($l->t('Enforce expiration date'));?></label><br/>
-		</p>
-
-		<p class="<?php if ($_['shareAPIEnabled'] === 'no') {
-	p('hidden');
-}?>">
-			<input type="checkbox" name="shareapi_allow_links" id="allowLinks" class="checkbox"
-				   value="1" <?php if ($_['allowLinks'] === 'yes') {
-	print_unescaped('checked="checked"');
-} ?> />
-			<label for="allowLinks"><?php p($l->t('Allow users to share via link and emails'));?></label><br/>
-		</p>
-
-		<p id="publicLinkSettings" class="indent <?php if ($_['allowLinks'] !== 'yes' || $_['shareAPIEnabled'] === 'no') {
-	p('hidden');
-} ?>">
-			<input type="checkbox" name="shareapi_allow_public_upload" id="allowPublicUpload" class="checkbox"
-				   value="1" <?php if ($_['allowPublicUpload'] == 'yes') {
-	print_unescaped('checked="checked"');
-} ?> />
-			<label for="allowPublicUpload"><?php p($l->t('Allow public uploads'));?></label><br/>
-			<input type="checkbox" name="shareapi_enable_link_password_by_default" id="enableLinkPasswordByDefault" class="checkbox"
-				   value="1" <?php if ($_['enableLinkPasswordByDefault'] === 'yes') {
-	print_unescaped('checked="checked"');
-} ?> />
-			<label for="enableLinkPasswordByDefault"><?php p($l->t('Always ask for a password'));?></label><br/>
-			<input type="checkbox" name="shareapi_enforce_links_password" id="enforceLinkPassword" class="checkbox"
-				   value="1" <?php if ($_['enforceLinkPassword']) {
-	print_unescaped('checked="checked"');
-} ?> <?php if ($_['enableLinkPasswordByDefault'] !== 'yes') {
-	print_unescaped('disabled');
-} ?> />
-			<label for="enforceLinkPassword" class="indent"><?php p($l->t('Enforce password protection'));?></label><br/>
-
-<?php if ($_['passwordExcludedGroupsFeatureEnabled']) { ?>
-			<span id="selectPasswordsExcludedGroups" class="double-indent <?php if (!$_['enforceLinkPassword']) { p('hidden'); } ?>">
-				<label for="shareapi_enforce_links_password_excluded_groups"><?php p($l->t('Exclude groups from password requirements:'));?></label><br/>
-				<input name="shareapi_enforce_links_password_excluded_groups" id="passwordsExcludedGroups" value="<?php p($_['passwordExcludedGroups']) ?>"
-					   style="width: 400px" class="noJSAutoUpdate double-indent" <?php if ($_['enableLinkPasswordByDefault'] !== 'yes') {
-	print_unescaped('disabled');
-} ?> />
-			</span><br/>
-<?php } ?>
-
-			<input type="checkbox" name="shareapi_default_expire_date" id="shareapiDefaultExpireDate" class="checkbox"
-				   value="1" <?php if ($_['shareDefaultExpireDateSet'] === 'yes') {
-	print_unescaped('checked="checked"');
-} ?> />
-			<label for="shareapiDefaultExpireDate"><?php p($l->t('Set default expiration date'));?></label><br/>
-
-		</p>
-		<p id="setDefaultExpireDate" class="double-indent <?php if ($_['allowLinks'] !== 'yes' || $_['shareDefaultExpireDateSet'] === 'no' || $_['shareAPIEnabled'] === 'no') {
-	p('hidden');
-}?>">
-			<?php p($l->t('Expire after'). ' '); ?>
-			<input type="text" name='shareapi_expire_after_n_days' id="shareapiExpireAfterNDays" placeholder="<?php p('7')?>"
-				   value='<?php p($_['shareExpireAfterNDays']) ?>' />
-			<?php p($l->t('day(s)')); ?>
-			<input type="checkbox" name="shareapi_enforce_expire_date" id="shareapiEnforceExpireDate" class="checkbox"
-				   value="1" <?php if ($_['shareEnforceExpireDate'] === 'yes') {
-	print_unescaped('checked="checked"');
-} ?> />
-			<label for="shareapiEnforceExpireDate"><?php p($l->t('Enforce expiration date'));?></label><br/>
-		</p>
-		<p class="<?php if ($_['shareAPIEnabled'] === 'no') {
-	p('hidden');
-}?>">
-		<p class="indent">
-			<?php p($l->t('Exclude groups from creating link shares:'));?>
-		</p>
-		<p id="selectLinksExcludedGroups" class="indent <?php if ($_['allowLinks'] === 'no') {
-	p('hidden');
-} ?>">
-			<input name="shareapi_allow_links_exclude_groups" type="hidden" id="linksExcludedGroups" value="<?php p($_['allowLinksExcludeGroups']) ?>" style="width: 400px" class="noJSAutoUpdate"/>
-		</p>
-			<input type="checkbox" name="shareapi_allow_resharing" id="allowResharing" class="checkbox"
-				   value="1" <?php if ($_['allowResharing'] === 'yes') {
-	print_unescaped('checked="checked"');
-} ?> />
-			<label for="allowResharing"><?php p($l->t('Allow resharing'));?></label><br/>
-		</p>
-		<p class="<?php if ($_['shareAPIEnabled'] === 'no') {
-	p('hidden');
-}?>">
-			<input type="checkbox" name="shareapi_allow_group_sharing" id="allowGroupSharing" class="checkbox"
-				   value="1" <?php if ($_['allowGroupSharing'] === 'yes') {
-	print_unescaped('checked="checked"');
-} ?> />
-			<label for="allowGroupSharing"><?php p($l->t('Allow sharing with groups'));?></label><br />
-		</p>
-		<p class="<?php if ($_['shareAPIEnabled'] === 'no') {
-	p('hidden');
-}?>">
-			<input type="checkbox" name="shareapi_only_share_with_group_members" id="onlyShareWithGroupMembers" class="checkbox"
-				   value="1" <?php if ($_['onlyShareWithGroupMembers']) {
-	print_unescaped('checked="checked"');
-} ?> />
-			<label for="onlyShareWithGroupMembers"><?php p($l->t('Restrict users to only share with users in their groups'));?></label><br/>
-		</p>
-		<p class="<?php if ($_['shareAPIEnabled'] === 'no') {
-	p('hidden');
-}?>">
-			<input type="checkbox" name="shareapi_exclude_groups" id="shareapiExcludeGroups" class="checkbox"
-				   value="1" <?php if ($_['shareExcludeGroups']) {
-	print_unescaped('checked="checked"');
-} ?> />
-			<label for="shareapiExcludeGroups"><?php p($l->t('Exclude groups from sharing'));?></label><br/>
-		</p>
-		<p id="selectExcludedGroups" class="indent <?php if (!$_['shareExcludeGroups'] || $_['shareAPIEnabled'] === 'no') {
-	p('hidden');
-} ?>">
-			<input name="shareapi_exclude_groups_list" type="hidden" id="excludedGroups" value="<?php p($_['shareExcludedGroupsList']) ?>" style="width: 400px" class="noJSAutoUpdate"/>
-			<br />
-			 <em><?php p($l->t('These groups will still be able to receive shares, but not to initiate them.')); ?></em>
-		</p>
-
-		<p class="<?php if ($_['shareAPIEnabled'] === 'no') {
-	p('hidden');
-}?>">
-			<input type="checkbox" name="shareapi_allow_share_dialog_user_enumeration" value="1" id="shareapi_allow_share_dialog_user_enumeration" class="checkbox"
-				<?php if ($_['allowShareDialogUserEnumeration'] === 'yes') {
-	print_unescaped('checked="checked"');
-} ?> />
-			<label for="shareapi_allow_share_dialog_user_enumeration"><?php p($l->t('Allow username autocompletion in share dialog and allow access to the system address book'));?></label><br />
-		</p>
-
-		<p id="shareapi_restrict_user_enumeration_to_group_setting" class="indent <?php if ($_['shareAPIEnabled'] === 'no' || $_['allowShareDialogUserEnumeration'] === 'no') {
-	p('hidden');
-}?>">
-			<input type="checkbox" name="shareapi_restrict_user_enumeration_to_group" value="1" id="shareapi_restrict_user_enumeration_to_group" class="checkbox"
-				<?php if ($_['restrictUserEnumerationToGroup'] === 'yes') {
-	print_unescaped('checked="checked"');
-} ?> />
-			<label for="shareapi_restrict_user_enumeration_to_group"><?php p($l->t('Allow username autocompletion to users within the same groups and limit system address books to users in the same groups'));?></label><br />
-		</p>
-
-		<p id="shareapi_restrict_user_enumeration_to_phone_setting" class="indent <?php if ($_['shareAPIEnabled'] === 'no' || $_['allowShareDialogUserEnumeration'] === 'no') {
-	p('hidden');
-}?>">
-			<input type="checkbox" name="shareapi_restrict_user_enumeration_to_phone" value="1" id="shareapi_restrict_user_enumeration_to_phone" class="checkbox"
-				<?php if ($_['restrictUserEnumerationToPhone'] === 'yes') {
-	print_unescaped('checked="checked"');
-} ?> />
-			<label for="shareapi_restrict_user_enumeration_to_phone"><?php p($l->t('Allow username autocompletion to users based on phone number integration'));?></label><br />
-		</p>
-		<p id="shareapi_restrict_user_enumeration_combinewarning_setting" class="indent <?php if ($_['shareAPIEnabled'] === 'no' || $_['allowShareDialogUserEnumeration'] === 'no') {
-	p('hidden');
-}?>">
-			<em><?php p($l->t('If autocompletion "same group" and "phone number integration" are enabled a match in either is enough to show the user.'));?></em><br />
-		</p>
-		<p id="shareapi_restrict_user_enumeration_full_match_setting" class="indent <?php if ($_['shareAPIEnabled'] === 'no') {
-	p('hidden');
-}?>">
-			<input type="checkbox" name="shareapi_restrict_user_enumeration_full_match" value="1" id="shareapi_restrict_user_enumeration_full_match" class="checkbox"
-					<?php if ($_['restrictUserEnumerationFullMatch'] === 'yes') {
-	print_unescaped('checked="checked"');
-} ?> />
-			<label for="shareapi_restrict_user_enumeration_full_match"><?php p($l->t('Allow autocompletion when entering the full name or email address (ignoring missing phonebook match and being in the same group)'));?></label><br />
-		</p>
-
-		<p>
-			<input type="checkbox" id="publicShareDisclaimer" class="checkbox noJSAutoUpdate"
-				<?php if ($_['publicShareDisclaimerText'] !== null) {
-	print_unescaped('checked="checked"');
-} ?> />
-			<label for="publicShareDisclaimer"><?php p($l->t('Show disclaimer text on the public link upload page (only shown when the file list is hidden)'));?></label>
-			<span id="publicShareDisclaimerStatus" class="msg" style="display:none"></span>
-			<br/>
-			<textarea placeholder="<?php p($l->t('This text will be shown on the public link upload page when the file list is hidden.')) ?>" id="publicShareDisclaimerText" <?php if ($_['publicShareDisclaimerText'] === null) {
-	print_unescaped('class="hidden"');
-} ?>><?php p($_['publicShareDisclaimerText']) ?></textarea>
-		</p>
-
-		<h3><?php p($l->t('Default share permissions'));?></h3>
-		<input type="hidden" name="shareapi_default_permissions" id="shareApiDefaultPermissions" class="checkbox"
-			   value="<?php p($_['shareApiDefaultPermissions']) ?>" />
-		<p id="shareApiDefaultPermissionsSection" class="indent <?php if ($_['shareAPIEnabled'] === 'no') {
-	p('hidden');
-} ?>">
-			<?php foreach ($_['shareApiDefaultPermissionsCheckboxes'] as $perm): ?>
-				<input type="checkbox" name="shareapi_default_permission_<?php p($perm['id']) ?>" id="shareapi_default_permission_<?php p($perm['id']) ?>"
-					   class="noautosave checkbox" value="<?php p($perm['value']) ?>" <?php if (($_['shareApiDefaultPermissions'] & $perm['value']) !== 0) {
-	print_unescaped('checked="checked"');
-} ?> />
-				<label for="shareapi_default_permission_<?php p($perm['id']) ?>"><?php p($perm['label']);?></label>
-			<?php endforeach ?>
-		</p>
-	</div>
-	<?php } ?>
-</div>
+<div id="vue-admin-settings-sharing"></div>

+ 1 - 0
webpack.modules.js

@@ -91,6 +91,7 @@ module.exports = {
 		'vue-settings-admin-ai': path.join(__dirname, 'apps/settings/src', 'main-admin-ai.js'),
 		'vue-settings-admin-delegation': path.join(__dirname, 'apps/settings/src', 'main-admin-delegation.js'),
 		'vue-settings-admin-security': path.join(__dirname, 'apps/settings/src', 'main-admin-security.js'),
+		'vue-settings-admin-sharing': path.join(__dirname, 'apps/settings/src', 'admin-settings-sharing.ts'),
 		'vue-settings-apps-users-management': path.join(__dirname, 'apps/settings/src', 'main-apps-users-management.js'),
 		'vue-settings-nextcloud-pdf': path.join(__dirname, 'apps/settings/src', 'main-nextcloud-pdf.js'),
 		'vue-settings-personal-info': path.join(__dirname, 'apps/settings/src', 'main-personal-info.js'),