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>
pull/41581/head
Ferdinand Thiessen 6 months ago
parent e5b996722a
commit 22163c60d4
No known key found for this signature in database
GPG Key ID: 45FAE7268762B400

@ -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';
}
/**

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

@ -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),
})

@ -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')
})

@ -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>

@ -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>

@ -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>

@ -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>

@ -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'),

Loading…
Cancel
Save