Merge pull request #41581 from nextcloud/fix/migrate-admin-settings-sharing-vue

enh(settings): Migrate admin settings for sharing to vue
This commit is contained in:
Ferdinand Thiessen 2023-11-20 17:32:05 +01:00 committed by GitHub
commit 02807a37b0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
101 changed files with 746 additions and 650 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

4
dist/2048-2048.js vendored
View file

@ -1,3 +1,3 @@
/*! For license information please see 2048-2048.js.LICENSE.txt */
"use strict";(self.webpackChunknextcloud=self.webpackChunknextcloud||[]).push([[2048],{72048:(e,n,c)=>{c.d(n,{FilePickerVue:()=>s});const s=(0,c(20144).defineAsyncComponent)((()=>Promise.all([c.e(7874),c.e(3738)]).then(c.bind(c,80860))))}}]);
//# sourceMappingURL=2048-2048.js.map?v=f867b6277c0da9696d0c
"use strict";(self.webpackChunknextcloud=self.webpackChunknextcloud||[]).push([[2048],{72048:(e,n,c)=>{c.d(n,{FilePickerVue:()=>s});const s=(0,c(20144).defineAsyncComponent)((()=>Promise.all([c.e(7874),c.e(4180)]).then(c.bind(c,90680))))}}]);
//# sourceMappingURL=2048-2048.js.map?v=78a82614529a3cefcde7

View file

@ -1 +1 @@
{"version":3,"file":"2048-2048.js?v=f867b6277c0da9696d0c","mappings":";oIAsBA,MAAMA,GAAI,kCAAE,IAAM","sources":["webpack:///nextcloud/node_modules/@nextcloud/dialogs/dist/chunks/index-27e9ea0a.mjs"],"sourcesContent":["import { defineAsyncComponent as e } from \"vue\";\n/**\n * @copyright Copyright (c) 2023 Ferdinand Thiessen <opensource@fthiessen.de>\n *\n * @author Ferdinand Thiessen <opensource@fthiessen.de>\n *\n * @license AGPL-3.0-or-later\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n *\n */\nconst i = e(() => import(\"./FilePicker-728d3f78.mjs\"));\nexport {\n i as FilePickerVue\n};\n"],"names":["i"],"sourceRoot":""}
{"version":3,"file":"2048-2048.js?v=78a82614529a3cefcde7","mappings":";oIAsBA,MAAMA,GAAI,kCAAE,IAAM","sources":["webpack:///nextcloud/node_modules/@nextcloud/dialogs/dist/chunks/index-27e9ea0a.mjs"],"sourcesContent":["import { defineAsyncComponent as e } from \"vue\";\n/**\n * @copyright Copyright (c) 2023 Ferdinand Thiessen <opensource@fthiessen.de>\n *\n * @author Ferdinand Thiessen <opensource@fthiessen.de>\n *\n * @license AGPL-3.0-or-later\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n *\n */\nconst i = e(() => import(\"./FilePicker-728d3f78.mjs\"));\nexport {\n i as FilePickerVue\n};\n"],"names":["i"],"sourceRoot":""}

3
dist/3738-3738.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

3
dist/4180-4180.js vendored Normal file

File diff suppressed because one or more lines are too long

View file

@ -43,28 +43,6 @@
*
*/
/**
* @copyright Copyright (c) 2019 John Molakvoæ <skjnldsv@protonmail.com>
*
* @author John Molakvoæ <skjnldsv@protonmail.com>
*
* @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/>.
*
*/
/**
* @copyright Copyright (c) 2021 John Molakvoæ <skjnldsv@protonmail.com>
*

1
dist/4180-4180.js.map vendored Normal file

File diff suppressed because one or more lines are too long

2
dist/6318-6318.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

4
dist/7255-7255.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

4
dist/core-common.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

4
dist/core-login.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

4
dist/core-main.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

4
dist/files-init.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

4
dist/files-main.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -61,28 +61,6 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @copyright Copyright (c) 2019 John Molakvoæ <skjnldsv@protonmail.com>
*
* @author John Molakvoæ <skjnldsv@protonmail.com>
*
* @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/>.
*
*/
/**
* @copyright Copyright (c) 2021 John Molakvoæ <skjnldsv@protonmail.com>
*

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,8 @@
/**
* @license
* Lodash <https://lodash.com/>
* Copyright OpenJS Foundation and other contributors <https://openjsf.org/>
* Released under MIT license <https://lodash.com/license>
* Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE>
* Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
*/

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -7,6 +7,9 @@ const NodePolyfillPlugin = require('node-polyfill-webpack-plugin')
const WorkboxPlugin = require('workbox-webpack-plugin')
const modules = require('./webpack.modules.js')
const { readFileSync } = require('fs')
const appVersion = readFileSync('./version.php').toString().match(/OC_VersionString[^']+'([^']+)/)?.[1] ?? 'unknown'
const formatOutputFromModules = (modules) => {
// merge all configs into one object, and use AppID to generate the fileNames
@ -205,6 +208,10 @@ module.exports = {
},
}],
}),
// Make appName & appVersion available as a constants for '@nextcloud/vue' components
new webpack.DefinePlugin({ appName: JSON.stringify('Nextcloud') }),
new webpack.DefinePlugin({ appVersion: JSON.stringify(appVersion) }),
],
externals: {
OC: 'OC',

Some files were not shown because too many files have changed in this diff Show more