diff options
Diffstat (limited to 'apps/settings/lib/Settings/Admin')
-rw-r--r-- | apps/settings/lib/Settings/Admin/ArtificialIntelligence.php | 218 | ||||
-rw-r--r-- | apps/settings/lib/Settings/Admin/Delegation.php | 120 | ||||
-rw-r--r-- | apps/settings/lib/Settings/Admin/Mail.php | 84 | ||||
-rw-r--r-- | apps/settings/lib/Settings/Admin/MailProvider.php | 52 | ||||
-rw-r--r-- | apps/settings/lib/Settings/Admin/Overview.php | 60 | ||||
-rw-r--r-- | apps/settings/lib/Settings/Admin/Security.php | 73 | ||||
-rw-r--r-- | apps/settings/lib/Settings/Admin/Server.php | 112 | ||||
-rw-r--r-- | apps/settings/lib/Settings/Admin/Sharing.php | 125 | ||||
-rw-r--r-- | apps/settings/lib/Settings/Admin/Users.php | 61 |
9 files changed, 905 insertions, 0 deletions
diff --git a/apps/settings/lib/Settings/Admin/ArtificialIntelligence.php b/apps/settings/lib/Settings/Admin/ArtificialIntelligence.php new file mode 100644 index 00000000000..aaec0049b20 --- /dev/null +++ b/apps/settings/lib/Settings/Admin/ArtificialIntelligence.php @@ -0,0 +1,218 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OCA\Settings\Settings\Admin; + +use OCP\AppFramework\Http\TemplateResponse; +use OCP\AppFramework\Services\IInitialState; +use OCP\IAppConfig; +use OCP\IL10N; +use OCP\Settings\IDelegatedSettings; +use OCP\SpeechToText\ISpeechToTextManager; +use OCP\SpeechToText\ISpeechToTextProviderWithId; +use OCP\TextProcessing\IManager; +use OCP\TextProcessing\IProvider; +use OCP\TextProcessing\IProviderWithId; +use OCP\TextProcessing\ITaskType; +use OCP\Translation\ITranslationManager; +use OCP\Translation\ITranslationProviderWithId; +use Psr\Container\ContainerExceptionInterface; +use Psr\Container\ContainerInterface; +use Psr\Container\NotFoundExceptionInterface; +use Psr\Log\LoggerInterface; + +class ArtificialIntelligence implements IDelegatedSettings { + public function __construct( + private IAppConfig $appConfig, + private IL10N $l, + private IInitialState $initialState, + private ITranslationManager $translationManager, + private ISpeechToTextManager $sttManager, + private IManager $textProcessingManager, + private ContainerInterface $container, + private \OCP\TextToImage\IManager $text2imageManager, + private \OCP\TaskProcessing\IManager $taskProcessingManager, + private LoggerInterface $logger, + ) { + } + + /** + * @return TemplateResponse + */ + public function getForm() { + $translationProviders = []; + $translationPreferences = []; + foreach ($this->translationManager->getProviders() as $provider) { + $translationProviders[] = [ + 'class' => $provider instanceof ITranslationProviderWithId ? $provider->getId() : $provider::class, + 'name' => $provider->getName(), + ]; + $translationPreferences[] = $provider instanceof ITranslationProviderWithId ? $provider->getId() : $provider::class; + } + + $sttProviders = []; + foreach ($this->sttManager->getProviders() as $provider) { + $sttProviders[] = [ + 'class' => $provider instanceof ISpeechToTextProviderWithId ? $provider->getId() : $provider::class, + 'name' => $provider->getName(), + ]; + } + + $textProcessingProviders = []; + /** @var array<class-string<ITaskType>, string|class-string<IProvider>> $textProcessingSettings */ + $textProcessingSettings = []; + foreach ($this->textProcessingManager->getProviders() as $provider) { + $textProcessingProviders[] = [ + 'class' => $provider instanceof IProviderWithId ? $provider->getId() : $provider::class, + 'name' => $provider->getName(), + 'taskType' => $provider->getTaskType(), + ]; + if (!isset($textProcessingSettings[$provider->getTaskType()])) { + $textProcessingSettings[$provider->getTaskType()] = $provider instanceof IProviderWithId ? $provider->getId() : $provider::class; + } + } + $textProcessingTaskTypes = []; + foreach ($textProcessingSettings as $taskTypeClass => $providerClass) { + /** @var ITaskType $taskType */ + try { + $taskType = $this->container->get($taskTypeClass); + } catch (NotFoundExceptionInterface $e) { + continue; + } catch (ContainerExceptionInterface $e) { + continue; + } + $textProcessingTaskTypes[] = [ + 'class' => $taskTypeClass, + 'name' => $taskType->getName(), + 'description' => $taskType->getDescription(), + ]; + } + + $text2imageProviders = []; + foreach ($this->text2imageManager->getProviders() as $provider) { + $text2imageProviders[] = [ + 'id' => $provider->getId(), + 'name' => $provider->getName(), + ]; + } + + $taskProcessingProviders = []; + /** @var array<class-string<ITaskType>, string|class-string<IProvider>> $taskProcessingSettings */ + $taskProcessingSettings = []; + foreach ($this->taskProcessingManager->getProviders() as $provider) { + $taskProcessingProviders[] = [ + 'id' => $provider->getId(), + 'name' => $provider->getName(), + 'taskType' => $provider->getTaskTypeId(), + ]; + if (!isset($taskProcessingSettings[$provider->getTaskTypeId()])) { + $taskProcessingSettings[$provider->getTaskTypeId()] = $provider->getId(); + } + } + $taskProcessingTaskTypes = []; + $taskProcessingTypeSettings = []; + foreach ($this->taskProcessingManager->getAvailableTaskTypes(true) as $taskTypeId => $taskTypeDefinition) { + $taskProcessingTaskTypes[] = [ + 'id' => $taskTypeId, + 'name' => $taskTypeDefinition['name'], + 'description' => $taskTypeDefinition['description'], + ]; + $taskProcessingTypeSettings[$taskTypeId] = true; + } + + + $this->initialState->provideInitialState('ai-stt-providers', $sttProviders); + $this->initialState->provideInitialState('ai-translation-providers', $translationProviders); + $this->initialState->provideInitialState('ai-text-processing-providers', $textProcessingProviders); + $this->initialState->provideInitialState('ai-text-processing-task-types', $textProcessingTaskTypes); + $this->initialState->provideInitialState('ai-text2image-providers', $text2imageProviders); + $this->initialState->provideInitialState('ai-task-processing-providers', $taskProcessingProviders); + $this->initialState->provideInitialState('ai-task-processing-task-types', $taskProcessingTaskTypes); + + $settings = [ + 'ai.stt_provider' => count($sttProviders) > 0 ? $sttProviders[0]['class'] : null, + 'ai.translation_provider_preferences' => $translationPreferences, + 'ai.textprocessing_provider_preferences' => $textProcessingSettings, + 'ai.text2image_provider' => count($text2imageProviders) > 0 ? $text2imageProviders[0]['id'] : null, + 'ai.taskprocessing_provider_preferences' => $taskProcessingSettings, + 'ai.taskprocessing_type_preferences' => $taskProcessingTypeSettings, + 'ai.taskprocessing_guests' => false, + ]; + foreach ($settings as $key => $defaultValue) { + $value = $defaultValue; + $json = $this->appConfig->getValueString('core', $key, '', lazy: in_array($key, \OC\TaskProcessing\Manager::LAZY_CONFIG_KEYS, true)); + if ($json !== '') { + try { + $value = json_decode($json, true, flags: JSON_THROW_ON_ERROR); + } catch (\JsonException $e) { + $this->logger->error('Failed to get settings. JSON Error in ' . $key, ['exception' => $e]); + if ($key === 'ai.taskprocessing_type_preferences') { + $value = []; + foreach ($taskProcessingTypeSettings as $taskTypeId => $taskTypeValue) { + $value[$taskTypeId] = false; + } + $settings[$key] = $value; + } + continue; + } + + switch ($key) { + case 'ai.taskprocessing_provider_preferences': + case 'ai.taskprocessing_type_preferences': + case 'ai.textprocessing_provider_preferences': + // fill $value with $defaultValue values + $value = array_merge($defaultValue, $value); + break; + case 'ai.translation_provider_preferences': + // Only show entries from $value (saved pref list) that are in $defaultValue (enabled providers) + // and add all providers that are enabled but not in the pref list + if (!is_array($defaultValue)) { + break; + } + $value = array_values(array_unique(array_merge(array_intersect($value, $defaultValue), $defaultValue), SORT_STRING)); + break; + default: + break; + } + } + $settings[$key] = $value; + } + + $this->initialState->provideInitialState('ai-settings', $settings); + + return new TemplateResponse('settings', 'settings/admin/ai'); + } + + /** + * @return string the section ID, e.g. 'sharing' + */ + public function getSection() { + return 'ai'; + } + + /** + * @return int whether the form should be rather on the top or bottom of + * the admin section. The forms are arranged in ascending order of the + * priority values. It is required to return a value between 0 and 100. + * + * E.g.: 70 + */ + public function getPriority() { + return 10; + } + + public function getName(): ?string { + return $this->l->t('Artificial Intelligence'); + } + + public function getAuthorizedAppConfig(): array { + return [ + 'core' => ['/ai..*/'], + ]; + } +} diff --git a/apps/settings/lib/Settings/Admin/Delegation.php b/apps/settings/lib/Settings/Admin/Delegation.php new file mode 100644 index 00000000000..59a26d1ac04 --- /dev/null +++ b/apps/settings/lib/Settings/Admin/Delegation.php @@ -0,0 +1,120 @@ +<?php + +/** + * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OCA\Settings\Settings\Admin; + +use OCA\Settings\AppInfo\Application; +use OCA\Settings\Service\AuthorizedGroupService; +use OCP\AppFramework\Http\TemplateResponse; +use OCP\AppFramework\Services\IInitialState; +use OCP\IGroupManager; +use OCP\IURLGenerator; +use OCP\Settings\IDelegatedSettings; +use OCP\Settings\IManager; +use OCP\Settings\ISettings; + +class Delegation implements ISettings { + public function __construct( + private IManager $settingManager, + private IInitialState $initialStateService, + private IGroupManager $groupManager, + private AuthorizedGroupService $authorizedGroupService, + private IURLGenerator $urlGenerator, + ) { + } + + /** + * Filter out the ISettings that are not IDelegatedSettings from $innerSection + * and add them to $settings. + * + * @param IDelegatedSettings[] $settings + * @param ISettings[] $innerSection + * @return IDelegatedSettings[] + */ + private function getDelegatedSettings(array $settings, array $innerSection): array { + foreach ($innerSection as $setting) { + if ($setting instanceof IDelegatedSettings) { + $settings[] = $setting; + } + } + return $settings; + } + + private function initSettingState(): void { + // Available settings page initialization + $sections = $this->settingManager->getAdminSections(); + $settings = []; + foreach ($sections as $sectionPriority) { + foreach ($sectionPriority as $section) { + $sectionSettings = $this->settingManager->getAdminSettings($section->getId()); + $sectionSettings = array_reduce($sectionSettings, [$this, 'getDelegatedSettings'], []); + $settings = array_merge( + $settings, + array_map(function (IDelegatedSettings $setting) use ($section) { + $sectionName = $section->getName() . ($setting->getName() !== null ? ' - ' . $setting->getName() : ''); + return [ + 'class' => get_class($setting), + 'sectionName' => $sectionName, + 'id' => mb_strtolower(str_replace(' ', '-', $sectionName)), + 'priority' => $section->getPriority(), + ]; + }, $sectionSettings) + ); + } + } + usort($settings, function (array $a, array $b) { + if ($a['priority'] == $b['priority']) { + return 0; + } + return ($a['priority'] < $b['priority']) ? -1 : 1; + }); + $this->initialStateService->provideInitialState('available-settings', $settings); + } + + public function initAvailableGroupState(): void { + // Available groups initialization + $groups = []; + $groupsClass = $this->groupManager->search(''); + foreach ($groupsClass as $group) { + if ($group->getGID() === 'admin') { + continue; // Admin already have access to everything + } + $groups[] = [ + 'displayName' => $group->getDisplayName(), + 'gid' => $group->getGID(), + ]; + } + $this->initialStateService->provideInitialState('available-groups', $groups); + } + + public function initAuthorizedGroupState(): void { + // Already set authorized groups + $this->initialStateService->provideInitialState('authorized-groups', $this->authorizedGroupService->findAll()); + } + + public function getForm(): TemplateResponse { + $this->initSettingState(); + $this->initAvailableGroupState(); + $this->initAuthorizedGroupState(); + $this->initialStateService->provideInitialState('authorized-settings-doc-link', $this->urlGenerator->linkToDocs('admin-delegation')); + + return new TemplateResponse(Application::APP_ID, 'settings/admin/delegation', [], ''); + } + + /** + * @return string the section ID, e.g. 'sharing' + */ + public function getSection() { + return 'admindelegation'; + } + + /* + * @inheritdoc + */ + public function getPriority() { + return 75; + } +} diff --git a/apps/settings/lib/Settings/Admin/Mail.php b/apps/settings/lib/Settings/Admin/Mail.php new file mode 100644 index 00000000000..8bf2342a59c --- /dev/null +++ b/apps/settings/lib/Settings/Admin/Mail.php @@ -0,0 +1,84 @@ +<?php + +/** + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OCA\Settings\Settings\Admin; + +use OCP\AppFramework\Http\TemplateResponse; +use OCP\IBinaryFinder; +use OCP\IConfig; +use OCP\IL10N; +use OCP\Server; +use OCP\Settings\IDelegatedSettings; + +class Mail implements IDelegatedSettings { + /** + * @param IConfig $config + * @param IL10N $l + */ + public function __construct( + private IConfig $config, + private IL10N $l, + ) { + } + + /** + * @return TemplateResponse + */ + public function getForm() { + $finder = Server::get(IBinaryFinder::class); + + $parameters = [ + // Mail + 'sendmail_is_available' => $finder->findBinaryPath('sendmail') !== false, + 'mail_domain' => $this->config->getSystemValue('mail_domain', ''), + 'mail_from_address' => $this->config->getSystemValue('mail_from_address', ''), + 'mail_smtpmode' => $this->config->getSystemValue('mail_smtpmode', ''), + 'mail_smtpsecure' => $this->config->getSystemValue('mail_smtpsecure', ''), + 'mail_smtphost' => $this->config->getSystemValue('mail_smtphost', ''), + 'mail_smtpport' => $this->config->getSystemValue('mail_smtpport', ''), + 'mail_smtpauth' => $this->config->getSystemValue('mail_smtpauth', false), + 'mail_smtpname' => $this->config->getSystemValue('mail_smtpname', ''), + 'mail_smtppassword' => $this->config->getSystemValue('mail_smtppassword', ''), + 'mail_sendmailmode' => $this->config->getSystemValue('mail_sendmailmode', 'smtp'), + ]; + + if ($parameters['mail_smtppassword'] !== '') { + $parameters['mail_smtppassword'] = '********'; + } + + if ($parameters['mail_smtpmode'] === '' || $parameters['mail_smtpmode'] === 'php') { + $parameters['mail_smtpmode'] = 'smtp'; + } + + return new TemplateResponse('settings', 'settings/admin/additional-mail', $parameters, ''); + } + + /** + * @return string the section ID, e.g. 'sharing' + */ + public function getSection() { + return 'server'; + } + + /** + * @return int whether the form should be rather on the top or bottom of + * the admin section. The forms are arranged in ascending order of the + * priority values. It is required to return a value between 0 and 100. + * + * E.g.: 70 + */ + public function getPriority() { + return 10; + } + + public function getName(): ?string { + return $this->l->t('Email server'); + } + + public function getAuthorizedAppConfig(): array { + return []; + } +} diff --git a/apps/settings/lib/Settings/Admin/MailProvider.php b/apps/settings/lib/Settings/Admin/MailProvider.php new file mode 100644 index 00000000000..c1e72378d20 --- /dev/null +++ b/apps/settings/lib/Settings/Admin/MailProvider.php @@ -0,0 +1,52 @@ +<?php + +declare(strict_types=1); +/** + * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OCA\Settings\Settings\Admin; + +use OCP\IL10N; +use OCP\Settings\DeclarativeSettingsTypes; +use OCP\Settings\IDeclarativeSettingsForm; + +class MailProvider implements IDeclarativeSettingsForm { + + public function __construct( + private IL10N $l, + ) { + } + + public function getSchema(): array { + return [ + 'id' => 'mail-provider-support', + 'priority' => 10, + 'section_type' => DeclarativeSettingsTypes::SECTION_TYPE_ADMIN, + 'section_id' => 'server', + 'storage_type' => DeclarativeSettingsTypes::STORAGE_TYPE_EXTERNAL, + 'title' => $this->l->t('Mail Providers'), + 'description' => $this->l->t('Mail provider enables sending emails directly through the user\'s personal email account. At present, this functionality is limited to calendar invitations. It requires Nextcloud Mail 4.1 and an email account in Nextcloud Mail that matches the user\'s email address in Nextcloud.'), + + 'fields' => [ + [ + 'id' => 'mail_providers_enabled', + 'title' => $this->l->t('Send emails using'), + 'type' => DeclarativeSettingsTypes::RADIO, + 'default' => 1, + 'options' => [ + [ + 'name' => $this->l->t('User\'s email account'), + 'value' => 1 + ], + [ + 'name' => $this->l->t('System email account'), + 'value' => 0 + ], + ], + ], + ], + ]; + } + +} diff --git a/apps/settings/lib/Settings/Admin/Overview.php b/apps/settings/lib/Settings/Admin/Overview.php new file mode 100644 index 00000000000..355200372f1 --- /dev/null +++ b/apps/settings/lib/Settings/Admin/Overview.php @@ -0,0 +1,60 @@ +<?php + +/** + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OCA\Settings\Settings\Admin; + +use OCP\AppFramework\Http\TemplateResponse; +use OCP\IConfig; +use OCP\IL10N; +use OCP\ServerVersion; +use OCP\Settings\IDelegatedSettings; + +class Overview implements IDelegatedSettings { + public function __construct( + private ServerVersion $serverVersion, + private IConfig $config, + private IL10N $l, + ) { + } + + /** + * @return TemplateResponse + */ + public function getForm() { + $parameters = [ + 'checkForWorkingWellKnownSetup' => $this->config->getSystemValue('check_for_working_wellknown_setup', true), + 'version' => $this->serverVersion->getHumanVersion(), + ]; + + return new TemplateResponse('settings', 'settings/admin/overview', $parameters, ''); + } + + /** + * @return string the section ID, e.g. 'sharing' + */ + public function getSection() { + return 'overview'; + } + + /** + * @return int whether the form should be rather on the top or bottom of + * the admin section. The forms are arranged in ascending order of the + * priority values. It is required to return a value between 0 and 100. + * + * E.g.: 70 + */ + public function getPriority() { + return 10; + } + + public function getName(): ?string { + return $this->l->t('Security & setup checks'); + } + + public function getAuthorizedAppConfig(): array { + return []; + } +} diff --git a/apps/settings/lib/Settings/Admin/Security.php b/apps/settings/lib/Settings/Admin/Security.php new file mode 100644 index 00000000000..c4efdb478c7 --- /dev/null +++ b/apps/settings/lib/Settings/Admin/Security.php @@ -0,0 +1,73 @@ +<?php + +/** + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OCA\Settings\Settings\Admin; + +use OC\Authentication\TwoFactorAuth\MandatoryTwoFactor; +use OCP\AppFramework\Http\TemplateResponse; +use OCP\AppFramework\Services\IInitialState; +use OCP\Encryption\IManager; +use OCP\IURLGenerator; +use OCP\IUserManager; +use OCP\Settings\ISettings; + +class Security implements ISettings { + private MandatoryTwoFactor $mandatoryTwoFactor; + + public function __construct( + private IManager $manager, + private IUserManager $userManager, + MandatoryTwoFactor $mandatoryTwoFactor, + private IInitialState $initialState, + private IURLGenerator $urlGenerator, + ) { + $this->mandatoryTwoFactor = $mandatoryTwoFactor; + } + + /** + * @return TemplateResponse + */ + public function getForm(): TemplateResponse { + $encryptionModules = $this->manager->getEncryptionModules(); + $defaultEncryptionModuleId = $this->manager->getDefaultEncryptionModuleId(); + $encryptionModuleList = []; + foreach ($encryptionModules as $module) { + $encryptionModuleList[$module['id']]['displayName'] = $module['displayName']; + $encryptionModuleList[$module['id']]['default'] = false; + if ($module['id'] === $defaultEncryptionModuleId) { + $encryptionModuleList[$module['id']]['default'] = true; + } + } + + $this->initialState->provideInitialState('mandatory2FAState', $this->mandatoryTwoFactor->getState()); + $this->initialState->provideInitialState('two-factor-admin-doc', $this->urlGenerator->linkToDocs('admin-2fa')); + $this->initialState->provideInitialState('encryption-enabled', $this->manager->isEnabled()); + $this->initialState->provideInitialState('encryption-ready', $this->manager->isReady()); + $this->initialState->provideInitialState('external-backends-enabled', count($this->userManager->getBackends()) > 1); + $this->initialState->provideInitialState('encryption-modules', $encryptionModuleList); + $this->initialState->provideInitialState('encryption-admin-doc', $this->urlGenerator->linkToDocs('admin-encryption')); + + return new TemplateResponse('settings', 'settings/admin/security', [], ''); + } + + /** + * @return string the section ID, e.g. 'sharing' + */ + public function getSection(): string { + return 'security'; + } + + /** + * @return int whether the form should be rather on the top or bottom of + * the admin section. The forms are arranged in ascending order of the + * priority values. It is required to return a value between 0 and 100. + * + * E.g.: 70 + */ + public function getPriority(): int { + return 10; + } +} diff --git a/apps/settings/lib/Settings/Admin/Server.php b/apps/settings/lib/Settings/Admin/Server.php new file mode 100644 index 00000000000..c0f29ce8f34 --- /dev/null +++ b/apps/settings/lib/Settings/Admin/Server.php @@ -0,0 +1,112 @@ +<?php + +/** + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OCA\Settings\Settings\Admin; + +use OC\Profile\ProfileManager; +use OC\Profile\TProfileHelper; +use OCP\AppFramework\Http\TemplateResponse; +use OCP\AppFramework\Services\IInitialState; +use OCP\AppFramework\Utility\ITimeFactory; +use OCP\IAppConfig; +use OCP\IConfig; +use OCP\IDBConnection; +use OCP\IL10N; +use OCP\IURLGenerator; +use OCP\Settings\IDelegatedSettings; + +class Server implements IDelegatedSettings { + use TProfileHelper; + + public function __construct( + private IDBConnection $connection, + private IInitialState $initialStateService, + private ProfileManager $profileManager, + private ITimeFactory $timeFactory, + private IURLGenerator $urlGenerator, + private IConfig $config, + private IAppConfig $appConfig, + private IL10N $l, + ) { + } + + /** + * @return TemplateResponse + */ + public function getForm() { + $ownerConfigFile = fileowner(\OC::$configDir . 'config.php'); + $cliBasedCronPossible = function_exists('posix_getpwuid') && $ownerConfigFile !== false; + $cliBasedCronUser = $cliBasedCronPossible ? (posix_getpwuid($ownerConfigFile)['name'] ?? '') : ''; + + // Background jobs + $this->initialStateService->provideInitialState('backgroundJobsMode', $this->appConfig->getValueString('core', 'backgroundjobs_mode', 'ajax')); + $this->initialStateService->provideInitialState('lastCron', $this->appConfig->getValueInt('core', 'lastcron', 0)); + $this->initialStateService->provideInitialState('cronMaxAge', $this->cronMaxAge()); + $this->initialStateService->provideInitialState('cronErrors', $this->config->getAppValue('core', 'cronErrors')); + $this->initialStateService->provideInitialState('cliBasedCronPossible', $cliBasedCronPossible); + $this->initialStateService->provideInitialState('cliBasedCronUser', $cliBasedCronUser); + $this->initialStateService->provideInitialState('backgroundJobsDocUrl', $this->urlGenerator->linkToDocs('admin-background-jobs')); + + // Profile page + $this->initialStateService->provideInitialState('profileEnabledGlobally', $this->profileManager->isProfileEnabled()); + $this->initialStateService->provideInitialState('profileEnabledByDefault', $this->isProfileEnabledByDefault($this->config)); + + // Basic settings + $this->initialStateService->provideInitialState('restrictSystemTagsCreationToAdmin', $this->appConfig->getValueBool('systemtags', 'restrict_creation_to_admin', false)); + + return new TemplateResponse('settings', 'settings/admin/server', [ + 'profileEnabledGlobally' => $this->profileManager->isProfileEnabled(), + ], ''); + } + + protected function cronMaxAge(): int { + $query = $this->connection->getQueryBuilder(); + $query->select('last_checked') + ->from('jobs') + ->orderBy('last_checked', 'ASC') + ->setMaxResults(1); + + $result = $query->execute(); + if ($row = $result->fetch()) { + $maxAge = (int)$row['last_checked']; + } else { + $maxAge = $this->timeFactory->getTime(); + } + $result->closeCursor(); + + return $maxAge; + } + + /** + * @return string the section ID, e.g. 'sharing' + */ + public function getSection(): string { + return 'server'; + } + + /** + * @return int whether the form should be rather on the top or bottom of + * the admin section. The forms are arranged in ascending order of the + * priority values. It is required to return a value between 0 and 100. + * + * E.g.: 70 + */ + public function getPriority(): int { + return 0; + } + + public function getName(): ?string { + return $this->l->t('Background jobs'); + } + + public function getAuthorizedAppConfig(): array { + return [ + 'core' => [ + '/mail_general_settings/', + ], + ]; + } +} diff --git a/apps/settings/lib/Settings/Admin/Sharing.php b/apps/settings/lib/Settings/Admin/Sharing.php new file mode 100644 index 00000000000..ec5dcdf624d --- /dev/null +++ b/apps/settings/lib/Settings/Admin/Sharing.php @@ -0,0 +1,125 @@ +<?php + +/** + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OCA\Settings\Settings\Admin; + +use OC\Core\AppInfo\ConfigLexicon; +use OCP\App\IAppManager; +use OCP\AppFramework\Http\TemplateResponse; +use OCP\AppFramework\Services\IInitialState; +use OCP\Constants; +use OCP\IAppConfig; +use OCP\IConfig; +use OCP\IL10N; +use OCP\IURLGenerator; +use OCP\Settings\IDelegatedSettings; +use OCP\Share\IManager; +use OCP\Util; + +class Sharing implements IDelegatedSettings { + public function __construct( + private IConfig $config, + private IAppConfig $appConfig, + private IL10N $l, + private IManager $shareManager, + private IAppManager $appManager, + private IURLGenerator $urlGenerator, + private IInitialState $initialState, + private string $appName, + ) { + } + + /** + * @return TemplateResponse + */ + public function getForm() { + $excludedGroups = $this->config->getAppValue('core', 'shareapi_exclude_groups_list', ''); + $linksExcludedGroups = $this->config->getAppValue('core', 'shareapi_allow_links_exclude_groups', ''); + $excludedPasswordGroups = $this->config->getAppValue('core', 'shareapi_enforce_links_password_excluded_groups', ''); + $onlyShareWithGroupMembersExcludeGroupList = $this->config->getAppValue('core', 'shareapi_only_share_with_group_members_exclude_group_list', ''); + + $parameters = [ + // Built-In Sharing + '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), + 'allowFederationOnPublicShares' => $this->appConfig->getValueBool('core', ConfigLexicon::SHAREAPI_ALLOW_FEDERATION_ON_PUBLIC_SHARES), + '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), + 'enforceLinksPasswordExcludedGroups' => json_decode($excludedPasswordGroups) ?? [], + 'enforceLinksPasswordExcludedGroupsEnabled' => $this->config->getSystemValueBool('sharing.allow_disabled_password_enforcement_groups', false), + 'onlyShareWithGroupMembers' => $this->shareManager->shareWithGroupMembersOnly(), + 'onlyShareWithGroupMembersExcludeGroupList' => json_decode($onlyShareWithGroupMembersExcludeGroupList) ?? [], + '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->config->getAppValue('core', 'shareapi_exclude_groups', 'no'), + 'excludeGroupsList' => json_decode($excludedGroups, true) ?? [], + 'publicShareDisclaimerText' => $this->config->getAppValue('core', 'shareapi_public_link_disclaimertext'), + 'enableLinkPasswordByDefault' => $this->appConfig->getValueBool('core', ConfigLexicon::SHARE_LINK_PASSWORD_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'), + 'allowCustomTokens' => $this->shareManager->allowCustomTokens(), + 'allowViewWithoutDownload' => $this->shareManager->allowViewWithoutDownload(), + ]; + + $this->initialState->provideInitialState('sharingAppEnabled', $this->appManager->isEnabledForUser('files_sharing')); + $this->initialState->provideInitialState('sharingDocumentation', $this->urlGenerator->linkToDocs('admin-sharing')); + $this->initialState->provideInitialState('sharingSettings', $parameters); + + Util::addScript($this->appName, 'vue-settings-admin-sharing'); + return new TemplateResponse($this->appName, 'settings/admin/sharing', [], ''); + } + + /** + * Helper function to retrive boolean values from human readable strings ('yes' / 'no') + */ + private function getHumanBooleanConfig(string $app, string $key, bool $default = false): bool { + return $this->config->getAppValue($app, $key, $default ? 'yes' : 'no') === 'yes'; + } + + /** + * @return string the section ID, e.g. 'sharing' + */ + public function getSection() { + return 'sharing'; + } + + /** + * @return int whether the form should be rather on the top or bottom of + * the admin section. The forms are arranged in ascending order of the + * priority values. It is required to return a value between 0 and 100. + * + * E.g.: 70 + */ + public function getPriority() { + return 0; + } + + public function getAuthorizedAppConfig(): array { + return [ + 'core' => ['/shareapi_.*/'], + ]; + } + + public function getName(): ?string { + return null; + } +} diff --git a/apps/settings/lib/Settings/Admin/Users.php b/apps/settings/lib/Settings/Admin/Users.php new file mode 100644 index 00000000000..c569890a0dc --- /dev/null +++ b/apps/settings/lib/Settings/Admin/Users.php @@ -0,0 +1,61 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace OCA\Settings\Settings\Admin; + +use OCP\AppFramework\Http\TemplateResponse; +use OCP\IL10N; +use OCP\Settings\IDelegatedSettings; + +/** + * Empty settings class, used only for admin delegation. + */ +class Users implements IDelegatedSettings { + + public function __construct( + protected string $appName, + private IL10N $l10n, + ) { + } + + /** + * Empty template response + */ + public function getForm(): TemplateResponse { + + return new /** @template-extends TemplateResponse<\OCP\AppFramework\Http::STATUS_OK, array{}> */ class($this->appName, '') extends TemplateResponse { + public function render(): string { + return ''; + } + }; + } + + public function getSection(): ?string { + return 'admindelegation'; + } + + /** + * @return int whether the form should be rather on the top or bottom of + * the admin section. The forms are arranged in ascending order of the + * priority values. It is required to return a value between 0 and 100. + * + * E.g.: 70 + */ + public function getPriority(): int { + return 0; + } + + public function getName(): string { + return $this->l10n->t('Users'); + } + + public function getAuthorizedAppConfig(): array { + return []; + } +} |