aboutsummaryrefslogtreecommitdiffstats
path: root/apps/settings/lib
diff options
context:
space:
mode:
Diffstat (limited to 'apps/settings/lib')
-rw-r--r--apps/settings/lib/Activity/GroupProvider.php62
-rw-r--r--apps/settings/lib/Activity/GroupSetting.php37
-rw-r--r--apps/settings/lib/Activity/Provider.php72
-rw-r--r--apps/settings/lib/Activity/SecurityFilter.php35
-rw-r--r--apps/settings/lib/Activity/SecurityProvider.php49
-rw-r--r--apps/settings/lib/Activity/SecuritySetting.php30
-rw-r--r--apps/settings/lib/Activity/Setting.php37
-rw-r--r--apps/settings/lib/AppInfo/Application.php165
-rw-r--r--apps/settings/lib/BackgroundJobs/VerifyUserData.php99
-rw-r--r--apps/settings/lib/Command/AdminDelegation/Add.php59
-rw-r--r--apps/settings/lib/Command/AdminDelegation/Remove.php55
-rw-r--r--apps/settings/lib/Command/AdminDelegation/Show.php77
-rw-r--r--apps/settings/lib/Controller/AISettingsController.php46
-rw-r--r--apps/settings/lib/Controller/AdminSettingsController.php101
-rw-r--r--apps/settings/lib/Controller/AppSettingsController.php401
-rw-r--r--apps/settings/lib/Controller/AuthSettingsController.php121
-rw-r--r--apps/settings/lib/Controller/AuthorizedGroupController.php37
-rw-r--r--apps/settings/lib/Controller/ChangePasswordController.php125
-rw-r--r--apps/settings/lib/Controller/CheckSetupController.php848
-rw-r--r--apps/settings/lib/Controller/CommonSettingsTrait.php133
-rw-r--r--apps/settings/lib/Controller/DeclarativeSettingsController.php131
-rw-r--r--apps/settings/lib/Controller/HelpController.php78
-rw-r--r--apps/settings/lib/Controller/LogSettingsController.php48
-rw-r--r--apps/settings/lib/Controller/MailSettingsController.php122
-rw-r--r--apps/settings/lib/Controller/PersonalSettingsController.php90
-rw-r--r--apps/settings/lib/Controller/ReasonsController.php29
-rw-r--r--apps/settings/lib/Controller/TwoFactorSettingsController.php25
-rw-r--r--apps/settings/lib/Controller/UsersController.php303
-rw-r--r--apps/settings/lib/Controller/WebAuthnController.php75
-rw-r--r--apps/settings/lib/Events/BeforeTemplateRenderedEvent.php22
-rw-r--r--apps/settings/lib/Hooks.php81
-rw-r--r--apps/settings/lib/Listener/AppPasswordCreatedActivityListener.php41
-rw-r--r--apps/settings/lib/Listener/GroupRemovedListener.php32
-rw-r--r--apps/settings/lib/Listener/MailProviderListener.php61
-rw-r--r--apps/settings/lib/Listener/UserAddedToGroupActivityListener.php40
-rw-r--r--apps/settings/lib/Listener/UserRemovedFromGroupActivityListener.php40
-rw-r--r--apps/settings/lib/Mailer/NewUserMailHelper.php83
-rw-r--r--apps/settings/lib/Middleware/SubadminMiddleware.php65
-rw-r--r--apps/settings/lib/ResponseDefinitions.php40
-rw-r--r--apps/settings/lib/Search/AppSearch.php67
-rw-r--r--apps/settings/lib/Search/SectionSearch.php73
-rw-r--r--apps/settings/lib/Search/UserSearch.php40
-rw-r--r--apps/settings/lib/Sections/Admin/Additional.php34
-rw-r--r--apps/settings/lib/Sections/Admin/ArtificialIntelligence.php38
-rw-r--r--apps/settings/lib/Sections/Admin/Delegation.php34
-rw-r--r--apps/settings/lib/Sections/Admin/Groupware.php34
-rw-r--r--apps/settings/lib/Sections/Admin/Overview.php34
-rw-r--r--apps/settings/lib/Sections/Admin/Security.php34
-rw-r--r--apps/settings/lib/Sections/Admin/Server.php34
-rw-r--r--apps/settings/lib/Sections/Admin/Sharing.php34
-rw-r--r--apps/settings/lib/Sections/Personal/Availability.php31
-rw-r--r--apps/settings/lib/Sections/Personal/Calendar.php30
-rw-r--r--apps/settings/lib/Sections/Personal/PersonalInfo.php34
-rw-r--r--apps/settings/lib/Sections/Personal/Security.php34
-rw-r--r--apps/settings/lib/Sections/Personal/SyncClients.php34
-rw-r--r--apps/settings/lib/Service/AuthorizedGroupService.php42
-rw-r--r--apps/settings/lib/Service/NotFoundException.php21
-rw-r--r--apps/settings/lib/Service/ServiceException.php21
-rw-r--r--apps/settings/lib/Settings/Admin/ArtificialIntelligence.php218
-rw-r--r--apps/settings/lib/Settings/Admin/Delegation.php50
-rw-r--r--apps/settings/lib/Settings/Admin/Mail.php50
-rw-r--r--apps/settings/lib/Settings/Admin/MailProvider.php52
-rw-r--r--apps/settings/lib/Settings/Admin/Overview.php47
-rw-r--r--apps/settings/lib/Settings/Admin/Security.php52
-rw-r--r--apps/settings/lib/Settings/Admin/Server.php79
-rw-r--r--apps/settings/lib/Settings/Admin/Sharing.php178
-rw-r--r--apps/settings/lib/Settings/Admin/Users.php61
-rw-r--r--apps/settings/lib/Settings/Personal/Additional.php27
-rw-r--r--apps/settings/lib/Settings/Personal/PersonalInfo.php111
-rw-r--r--apps/settings/lib/Settings/Personal/Security/Authtokens.php65
-rw-r--r--apps/settings/lib/Settings/Personal/Security/Password.php40
-rw-r--r--apps/settings/lib/Settings/Personal/Security/TwoFactor.php54
-rw-r--r--apps/settings/lib/Settings/Personal/Security/WebAuthn.php44
-rw-r--r--apps/settings/lib/Settings/Personal/ServerDevNotice.php77
-rw-r--r--apps/settings/lib/SetupChecks/AllowedAdminRanges.php63
-rw-r--r--apps/settings/lib/SetupChecks/AppDirsWithDifferentOwner.php87
-rw-r--r--apps/settings/lib/SetupChecks/BruteForceThrottler.php57
-rw-r--r--apps/settings/lib/SetupChecks/CheckUserCertificates.php74
-rw-r--r--apps/settings/lib/SetupChecks/CodeIntegrity.php66
-rw-r--r--apps/settings/lib/SetupChecks/CronErrors.php45
-rw-r--r--apps/settings/lib/SetupChecks/CronInfo.php66
-rw-r--r--apps/settings/lib/SetupChecks/DataDirectoryProtected.php71
-rw-r--r--apps/settings/lib/SetupChecks/DatabaseHasMissingColumns.php72
-rw-r--r--apps/settings/lib/SetupChecks/DatabaseHasMissingIndices.php93
-rw-r--r--apps/settings/lib/SetupChecks/DatabaseHasMissingPrimaryKeys.php72
-rw-r--r--apps/settings/lib/SetupChecks/DatabasePendingBigIntConversions.php82
-rw-r--r--apps/settings/lib/SetupChecks/DebugMode.php38
-rw-r--r--apps/settings/lib/SetupChecks/DefaultPhoneRegionSet.php41
-rw-r--r--apps/settings/lib/SetupChecks/EmailTestSuccessful.php61
-rw-r--r--apps/settings/lib/SetupChecks/FileLocking.php61
-rw-r--r--apps/settings/lib/SetupChecks/ForwardedForHeaders.php87
-rw-r--r--apps/settings/lib/SetupChecks/HttpsUrlGeneration.php65
-rw-r--r--apps/settings/lib/SetupChecks/InternetConnectivity.php80
-rw-r--r--apps/settings/lib/SetupChecks/JavaScriptModules.php60
-rw-r--r--apps/settings/lib/SetupChecks/JavaScriptSourceMaps.php52
-rw-r--r--apps/settings/lib/SetupChecks/LdapInvalidUuids.php69
-rw-r--r--apps/settings/lib/SetupChecks/LegacySSEKeyFormat.php62
-rw-r--r--apps/settings/lib/SetupChecks/LoggingLevel.php55
-rw-r--r--apps/settings/lib/SetupChecks/MaintenanceWindowStart.php53
-rw-r--r--apps/settings/lib/SetupChecks/MemcacheConfigured.php98
-rw-r--r--apps/settings/lib/SetupChecks/MimeTypeMigrationAvailable.php41
-rw-r--r--apps/settings/lib/SetupChecks/MysqlRowFormat.php70
-rw-r--r--apps/settings/lib/SetupChecks/MysqlUnicodeSupport.php46
-rw-r--r--apps/settings/lib/SetupChecks/OcxProviders.php83
-rw-r--r--apps/settings/lib/SetupChecks/OverwriteCliUrl.php64
-rw-r--r--apps/settings/lib/SetupChecks/PhpApcuConfig.php70
-rw-r--r--apps/settings/lib/SetupChecks/PhpDefaultCharset.php48
-rw-r--r--apps/settings/lib/SetupChecks/PhpDisabledFunctions.php39
-rw-r--r--apps/settings/lib/SetupChecks/PhpFreetypeSupport.php46
-rw-r--r--apps/settings/lib/SetupChecks/PhpGetEnv.php38
-rw-r--r--apps/settings/lib/SetupChecks/PhpMaxFileSize.php80
-rw-r--r--apps/settings/lib/SetupChecks/PhpMemoryLimit.php40
-rw-r--r--apps/settings/lib/SetupChecks/PhpModules.php103
-rw-r--r--apps/settings/lib/SetupChecks/PhpOpcacheSetup.php136
-rw-r--r--apps/settings/lib/SetupChecks/PhpOutdated.php47
-rw-r--r--apps/settings/lib/SetupChecks/PhpOutputBuffering.php48
-rw-r--r--apps/settings/lib/SetupChecks/PushService.php70
-rw-r--r--apps/settings/lib/SetupChecks/RandomnessSecure.php46
-rw-r--r--apps/settings/lib/SetupChecks/ReadOnlyConfig.php38
-rw-r--r--apps/settings/lib/SetupChecks/SchedulingTableSize.php52
-rw-r--r--apps/settings/lib/SetupChecks/SecurityHeaders.php139
-rw-r--r--apps/settings/lib/SetupChecks/SupportedDatabase.php189
-rw-r--r--apps/settings/lib/SetupChecks/SystemIs64bit.php50
-rw-r--r--apps/settings/lib/SetupChecks/TaskProcessingPickupSpeed.php63
-rw-r--r--apps/settings/lib/SetupChecks/TempSpaceAvailable.php113
-rw-r--r--apps/settings/lib/SetupChecks/TransactionIsolation.php58
-rw-r--r--apps/settings/lib/SetupChecks/WellKnownUrls.php95
-rw-r--r--apps/settings/lib/SetupChecks/Woff2Loading.php81
-rw-r--r--apps/settings/lib/UserMigration/AccountMigrator.php48
-rw-r--r--apps/settings/lib/UserMigration/AccountMigratorException.php21
-rw-r--r--apps/settings/lib/WellKnown/ChangePasswordHandler.php27
-rw-r--r--apps/settings/lib/WellKnown/SecurityTxtHandler.php27
132 files changed, 5782 insertions, 3967 deletions
diff --git a/apps/settings/lib/Activity/GroupProvider.php b/apps/settings/lib/Activity/GroupProvider.php
index 52798d2e428..2d492265cf4 100644
--- a/apps/settings/lib/Activity/GroupProvider.php
+++ b/apps/settings/lib/Activity/GroupProvider.php
@@ -1,29 +1,12 @@
<?php
+
/**
- * @copyright Copyright (c) 2016 Christoph Wurst <christoph@winzerhof-wurst.at>
- *
- * @author Christoph Wurst <christoph@winzerhof-wurst.at>
- * @author Joas Schilling <coding@schilljs.com>
- *
- * @license GNU AGPL version 3 or any later version
- *
- * 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/>.
- *
+ * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\Settings\Activity;
-use InvalidArgumentException;
+use OCP\Activity\Exceptions\UnknownActivityException;
use OCP\Activity\IEvent;
use OCP\Activity\IManager;
use OCP\Activity\IProvider;
@@ -37,36 +20,22 @@ class GroupProvider implements IProvider {
public const ADDED_TO_GROUP = 'group_added';
public const REMOVED_FROM_GROUP = 'group_removed';
- /** @var L10nFactory */
- private $l10n;
- /** @var IURLGenerator */
- private $urlGenerator;
- /** @var IManager */
- private $activityManager;
- /** @var IUserManager */
- protected $userManager;
- /** @var IGroupManager */
- protected $groupManager;
-
/** @var string[] */
protected $groupDisplayNames = [];
- public function __construct(L10nFactory $l10n,
- IURLGenerator $urlGenerator,
- IManager $activityManager,
- IUserManager $userManager,
- IGroupManager $groupManager) {
- $this->urlGenerator = $urlGenerator;
- $this->l10n = $l10n;
- $this->activityManager = $activityManager;
- $this->userManager = $userManager;
- $this->groupManager = $groupManager;
+ public function __construct(
+ private L10nFactory $l10n,
+ private IURLGenerator $urlGenerator,
+ private IManager $activityManager,
+ protected IUserManager $userManager,
+ protected IGroupManager $groupManager,
+ ) {
}
- public function parse($language, IEvent $event, IEvent $previousEvent = null) {
+ public function parse($language, IEvent $event, ?IEvent $previousEvent = null) {
if ($event->getType() !== 'group_settings') {
- throw new InvalidArgumentException();
+ throw new UnknownActivityException();
}
$l = $this->l10n->get('settings', $language);
@@ -113,7 +82,7 @@ class GroupProvider implements IProvider {
}
break;
default:
- throw new InvalidArgumentException();
+ throw new UnknownActivityException();
}
$this->setSubjects($event, $subject, $parsedParameters);
@@ -121,9 +90,6 @@ class GroupProvider implements IProvider {
return $event;
}
- /**
- * @throws \InvalidArgumentException
- */
protected function setSubjects(IEvent $event, string $subject, array $parameters): void {
$event->setRichSubject($subject, $parameters);
}
diff --git a/apps/settings/lib/Activity/GroupSetting.php b/apps/settings/lib/Activity/GroupSetting.php
index adc7717bf4b..917f4a7ef26 100644
--- a/apps/settings/lib/Activity/GroupSetting.php
+++ b/apps/settings/lib/Activity/GroupSetting.php
@@ -1,25 +1,8 @@
<?php
+
/**
- * @copyright Copyright (c) 2017 Joas Schilling <coding@schilljs.com>
- *
- * @author Christoph Wurst <christoph@winzerhof-wurst.at>
- * @author Joas Schilling <coding@schilljs.com>
- *
- * @license GNU AGPL version 3 or any later version
- *
- * 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/>.
- *
+ * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\Settings\Activity;
@@ -28,14 +11,12 @@ use OCP\IL10N;
class GroupSetting implements ISetting {
- /** @var IL10N */
- protected $l;
-
/**
- * @param IL10N $l10n
+ * @param IL10N $l
*/
- public function __construct(IL10N $l10n) {
- $this->l = $l10n;
+ public function __construct(
+ protected IL10N $l,
+ ) {
}
/**
@@ -56,8 +37,8 @@ class GroupSetting implements ISetting {
/**
* @return int whether the filter should be rather on the top or bottom of
- * the admin section. The filters are arranged in ascending order of the
- * priority values. It is required to return a value between 0 and 100.
+ * the admin section. The filters are arranged in ascending order of the
+ * priority values. It is required to return a value between 0 and 100.
* @since 11.0.0
*/
public function getPriority(): int {
diff --git a/apps/settings/lib/Activity/Provider.php b/apps/settings/lib/Activity/Provider.php
index c2ef77f3d9a..c31a900abd5 100644
--- a/apps/settings/lib/Activity/Provider.php
+++ b/apps/settings/lib/Activity/Provider.php
@@ -3,31 +3,12 @@
declare(strict_types=1);
/**
- * @copyright Copyright (c) 2017 Joas Schilling <coding@schilljs.com>
- *
- * @author Christoph Wurst <christoph@winzerhof-wurst.at>
- * @author Daniel Kesselberg <mail@danielkesselberg.de>
- * @author Joas Schilling <coding@schilljs.com>
- * @author Thomas Citharel <nextcloud@tcit.fr>
- *
- * @license GNU AGPL version 3 or any later version
- *
- * 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/>.
- *
+ * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\Settings\Activity;
+use OCP\Activity\Exceptions\UnknownActivityException;
use OCP\Activity\IEvent;
use OCP\Activity\IManager;
use OCP\Activity\IProvider;
@@ -50,29 +31,15 @@ class Provider implements IProvider {
public const APP_TOKEN_FILESYSTEM_GRANTED = 'app_token_filesystem_granted';
public const APP_TOKEN_FILESYSTEM_REVOKED = 'app_token_filesystem_revoked';
- /** @var IFactory */
- protected $languageFactory;
-
/** @var IL10N */
protected $l;
- /** @var IURLGenerator */
- protected $url;
-
- /** @var IUserManager */
- protected $userManager;
-
- /** @var IManager */
- private $activityManager;
-
- public function __construct(IFactory $languageFactory,
- IURLGenerator $url,
- IUserManager $userManager,
- IManager $activityManager) {
- $this->languageFactory = $languageFactory;
- $this->url = $url;
- $this->userManager = $userManager;
- $this->activityManager = $activityManager;
+ public function __construct(
+ protected IFactory $languageFactory,
+ protected IURLGenerator $url,
+ protected IUserManager $userManager,
+ private IManager $activityManager,
+ ) {
}
/**
@@ -80,12 +47,12 @@ class Provider implements IProvider {
* @param IEvent $event
* @param IEvent|null $previousEvent
* @return IEvent
- * @throws \InvalidArgumentException
+ * @throws UnknownActivityException
* @since 11.0.0
*/
- public function parse($language, IEvent $event, IEvent $previousEvent = null): IEvent {
+ public function parse($language, IEvent $event, ?IEvent $previousEvent = null): IEvent {
if ($event->getApp() !== 'settings') {
- throw new \InvalidArgumentException('Unknown app');
+ throw new UnknownActivityException('Unknown app');
}
$this->l = $this->languageFactory->get('settings', $language);
@@ -125,7 +92,7 @@ class Provider implements IProvider {
} elseif ($event->getSubject() === self::APP_TOKEN_FILESYSTEM_REVOKED) {
$subject = $this->l->t('You revoked filesystem access from app password "{token}"');
} else {
- throw new \InvalidArgumentException('Unknown subject');
+ throw new UnknownActivityException('Unknown subject');
}
$parsedParameters = $this->getParameters($event);
@@ -137,7 +104,7 @@ class Provider implements IProvider {
/**
* @param IEvent $event
* @return array
- * @throws \InvalidArgumentException
+ * @throws UnknownActivityException
*/
protected function getParameters(IEvent $event): array {
$subject = $event->getSubject();
@@ -162,7 +129,7 @@ class Provider implements IProvider {
return [
'token' => [
'type' => 'highlight',
- 'id' => $event->getObjectId(),
+ 'id' => (string)$event->getObjectId(),
'name' => $parameters['name'],
]
];
@@ -170,23 +137,20 @@ class Provider implements IProvider {
return [
'token' => [
'type' => 'highlight',
- 'id' => $event->getObjectId(),
+ 'id' => (string)$event->getObjectId(),
'name' => $parameters['name'],
],
'newToken' => [
'type' => 'highlight',
- 'id' => $event->getObjectId(),
+ 'id' => (string)$event->getObjectId(),
'name' => $parameters['newName'],
]
];
}
- throw new \InvalidArgumentException('Unknown subject');
+ throw new UnknownActivityException('Unknown subject');
}
- /**
- * @throws \InvalidArgumentException
- */
protected function setSubjects(IEvent $event, string $subject, array $parameters): void {
$event->setRichSubject($subject, $parameters);
}
diff --git a/apps/settings/lib/Activity/SecurityFilter.php b/apps/settings/lib/Activity/SecurityFilter.php
index 48cf6e74f29..9a32e82a984 100644
--- a/apps/settings/lib/Activity/SecurityFilter.php
+++ b/apps/settings/lib/Activity/SecurityFilter.php
@@ -1,24 +1,8 @@
<?php
+
/**
- * @copyright Copyright (c) 2016 Christoph Wurst <christoph@winzerhof-wurst.at>
- *
- * @author Christoph Wurst <christoph@winzerhof-wurst.at>
- *
- * @license GNU AGPL version 3 or any later version
- *
- * 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/>.
- *
+ * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\Settings\Activity;
@@ -28,15 +12,10 @@ use OCP\IURLGenerator;
class SecurityFilter implements IFilter {
- /** @var IURLGenerator */
- private $urlGenerator;
-
- /** @var IL10N */
- private $l10n;
-
- public function __construct(IURLGenerator $urlGenerator, IL10N $l10n) {
- $this->urlGenerator = $urlGenerator;
- $this->l10n = $l10n;
+ public function __construct(
+ private IURLGenerator $urlGenerator,
+ private IL10N $l10n,
+ ) {
}
public function allowedApps() {
diff --git a/apps/settings/lib/Activity/SecurityProvider.php b/apps/settings/lib/Activity/SecurityProvider.php
index cdf7b80298d..658e2e7b949 100644
--- a/apps/settings/lib/Activity/SecurityProvider.php
+++ b/apps/settings/lib/Activity/SecurityProvider.php
@@ -3,31 +3,12 @@
declare(strict_types=1);
/**
- * @copyright Copyright (c) 2016 Christoph Wurst <christoph@winzerhof-wurst.at>
- *
- * @author Christoph Wurst <christoph@winzerhof-wurst.at>
- * @author Joas Schilling <coding@schilljs.com>
- * @author Roeland Jago Douma <roeland@famdouma.nl>
- *
- * @license GNU AGPL version 3 or any later version
- *
- * 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/>.
- *
+ * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\Settings\Activity;
-use InvalidArgumentException;
+use OCP\Activity\Exceptions\UnknownActivityException;
use OCP\Activity\IEvent;
use OCP\Activity\IManager;
use OCP\Activity\IProvider;
@@ -36,24 +17,16 @@ use OCP\L10N\IFactory as L10nFactory;
class SecurityProvider implements IProvider {
- /** @var L10nFactory */
- private $l10n;
-
- /** @var IURLGenerator */
- private $urlGenerator;
-
- /** @var IManager */
- private $activityManager;
-
- public function __construct(L10nFactory $l10n, IURLGenerator $urlGenerator, IManager $activityManager) {
- $this->urlGenerator = $urlGenerator;
- $this->l10n = $l10n;
- $this->activityManager = $activityManager;
+ public function __construct(
+ private L10nFactory $l10n,
+ private IURLGenerator $urlGenerator,
+ private IManager $activityManager,
+ ) {
}
- public function parse($language, IEvent $event, IEvent $previousEvent = null) {
+ public function parse($language, IEvent $event, ?IEvent $previousEvent = null) {
if ($event->getType() !== 'security') {
- throw new InvalidArgumentException();
+ throw new UnknownActivityException();
}
$l = $this->l10n->get('settings', $language);
@@ -104,7 +77,7 @@ class SecurityProvider implements IProvider {
}
break;
default:
- throw new InvalidArgumentException();
+ throw new UnknownActivityException();
}
return $event;
}
diff --git a/apps/settings/lib/Activity/SecuritySetting.php b/apps/settings/lib/Activity/SecuritySetting.php
index 3dc6df4ef2a..9226b5aea5b 100644
--- a/apps/settings/lib/Activity/SecuritySetting.php
+++ b/apps/settings/lib/Activity/SecuritySetting.php
@@ -1,24 +1,8 @@
<?php
+
/**
- * @copyright Copyright (c) 2016 Christoph Wurst <christoph@winzerhof-wurst.at>
- *
- * @author Christoph Wurst <christoph@winzerhof-wurst.at>
- *
- * @license GNU AGPL version 3 or any later version
- *
- * 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/>.
- *
+ * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\Settings\Activity;
@@ -27,11 +11,9 @@ use OCP\IL10N;
class SecuritySetting implements ISetting {
- /** @var IL10N */
- private $l10n;
-
- public function __construct(IL10N $l10n) {
- $this->l10n = $l10n;
+ public function __construct(
+ private IL10N $l10n,
+ ) {
}
public function canChangeMail() {
diff --git a/apps/settings/lib/Activity/Setting.php b/apps/settings/lib/Activity/Setting.php
index dc92cea4d8a..f9c659594d6 100644
--- a/apps/settings/lib/Activity/Setting.php
+++ b/apps/settings/lib/Activity/Setting.php
@@ -1,25 +1,8 @@
<?php
+
/**
- * @copyright Copyright (c) 2017 Joas Schilling <coding@schilljs.com>
- *
- * @author Christoph Wurst <christoph@winzerhof-wurst.at>
- * @author Joas Schilling <coding@schilljs.com>
- *
- * @license GNU AGPL version 3 or any later version
- *
- * 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/>.
- *
+ * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\Settings\Activity;
@@ -28,14 +11,12 @@ use OCP\IL10N;
class Setting implements ISetting {
- /** @var IL10N */
- protected $l;
-
/**
- * @param IL10N $l10n
+ * @param IL10N $l
*/
- public function __construct(IL10N $l10n) {
- $this->l = $l10n;
+ public function __construct(
+ protected IL10N $l,
+ ) {
}
/**
@@ -56,8 +37,8 @@ class Setting implements ISetting {
/**
* @return int whether the filter should be rather on the top or bottom of
- * the admin section. The filters are arranged in ascending order of the
- * priority values. It is required to return a value between 0 and 100.
+ * the admin section. The filters are arranged in ascending order of the
+ * priority values. It is required to return a value between 0 and 100.
* @since 11.0.0
*/
public function getPriority() {
diff --git a/apps/settings/lib/AppInfo/Application.php b/apps/settings/lib/AppInfo/Application.php
index 8d99f7f4f86..6e59e56fe82 100644
--- a/apps/settings/lib/AppInfo/Application.php
+++ b/apps/settings/lib/AppInfo/Application.php
@@ -1,35 +1,10 @@
<?php
declare(strict_types=1);
-
/**
- * @copyright Copyright (c) 2016, ownCloud, Inc.
- *
- * @author Arthur Schiwon <blizzz@arthur-schiwon.de>
- * @author Björn Schießle <bjoern@schiessle.org>
- * @author Christoph Wurst <christoph@winzerhof-wurst.at>
- * @author Daniel Kesselberg <mail@danielkesselberg.de>
- * @author Joas Schilling <coding@schilljs.com>
- * @author Lukas Reschke <lukas@statuscode.ch>
- * @author Morris Jobke <hey@morrisjobke.de>
- * @author Robin Appelman <robin@icewind.nl>
- * @author Roeland Jago Douma <roeland@famdouma.nl>
- * @author zulan <git@zulan.net>
- *
- * @license AGPL-3.0
- *
- * This code is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * 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, version 3,
- * along with this program. If not, see <http://www.gnu.org/licenses/>
- *
+ * SPDX-FileCopyrightText: 2019-2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
+ * SPDX-License-Identifier: AGPL-3.0-only
*/
namespace OCA\Settings\AppInfo;
@@ -40,12 +15,66 @@ use OC\Server;
use OCA\Settings\Hooks;
use OCA\Settings\Listener\AppPasswordCreatedActivityListener;
use OCA\Settings\Listener\GroupRemovedListener;
+use OCA\Settings\Listener\MailProviderListener;
use OCA\Settings\Listener\UserAddedToGroupActivityListener;
use OCA\Settings\Listener\UserRemovedFromGroupActivityListener;
use OCA\Settings\Mailer\NewUserMailHelper;
use OCA\Settings\Middleware\SubadminMiddleware;
use OCA\Settings\Search\AppSearch;
use OCA\Settings\Search\SectionSearch;
+use OCA\Settings\Search\UserSearch;
+use OCA\Settings\Settings\Admin\MailProvider;
+use OCA\Settings\SetupChecks\AllowedAdminRanges;
+use OCA\Settings\SetupChecks\AppDirsWithDifferentOwner;
+use OCA\Settings\SetupChecks\BruteForceThrottler;
+use OCA\Settings\SetupChecks\CheckUserCertificates;
+use OCA\Settings\SetupChecks\CodeIntegrity;
+use OCA\Settings\SetupChecks\CronErrors;
+use OCA\Settings\SetupChecks\CronInfo;
+use OCA\Settings\SetupChecks\DatabaseHasMissingColumns;
+use OCA\Settings\SetupChecks\DatabaseHasMissingIndices;
+use OCA\Settings\SetupChecks\DatabaseHasMissingPrimaryKeys;
+use OCA\Settings\SetupChecks\DatabasePendingBigIntConversions;
+use OCA\Settings\SetupChecks\DataDirectoryProtected;
+use OCA\Settings\SetupChecks\DebugMode;
+use OCA\Settings\SetupChecks\DefaultPhoneRegionSet;
+use OCA\Settings\SetupChecks\EmailTestSuccessful;
+use OCA\Settings\SetupChecks\FileLocking;
+use OCA\Settings\SetupChecks\ForwardedForHeaders;
+use OCA\Settings\SetupChecks\HttpsUrlGeneration;
+use OCA\Settings\SetupChecks\InternetConnectivity;
+use OCA\Settings\SetupChecks\JavaScriptModules;
+use OCA\Settings\SetupChecks\JavaScriptSourceMaps;
+use OCA\Settings\SetupChecks\LegacySSEKeyFormat;
+use OCA\Settings\SetupChecks\MaintenanceWindowStart;
+use OCA\Settings\SetupChecks\MemcacheConfigured;
+use OCA\Settings\SetupChecks\MimeTypeMigrationAvailable;
+use OCA\Settings\SetupChecks\MysqlRowFormat;
+use OCA\Settings\SetupChecks\MysqlUnicodeSupport;
+use OCA\Settings\SetupChecks\OcxProviders;
+use OCA\Settings\SetupChecks\OverwriteCliUrl;
+use OCA\Settings\SetupChecks\PhpApcuConfig;
+use OCA\Settings\SetupChecks\PhpDefaultCharset;
+use OCA\Settings\SetupChecks\PhpDisabledFunctions;
+use OCA\Settings\SetupChecks\PhpFreetypeSupport;
+use OCA\Settings\SetupChecks\PhpGetEnv;
+use OCA\Settings\SetupChecks\PhpMemoryLimit;
+use OCA\Settings\SetupChecks\PhpModules;
+use OCA\Settings\SetupChecks\PhpOpcacheSetup;
+use OCA\Settings\SetupChecks\PhpOutdated;
+use OCA\Settings\SetupChecks\PhpOutputBuffering;
+use OCA\Settings\SetupChecks\PushService;
+use OCA\Settings\SetupChecks\RandomnessSecure;
+use OCA\Settings\SetupChecks\ReadOnlyConfig;
+use OCA\Settings\SetupChecks\SchedulingTableSize;
+use OCA\Settings\SetupChecks\SecurityHeaders;
+use OCA\Settings\SetupChecks\SupportedDatabase;
+use OCA\Settings\SetupChecks\SystemIs64bit;
+use OCA\Settings\SetupChecks\TaskProcessingPickupSpeed;
+use OCA\Settings\SetupChecks\TempSpaceAvailable;
+use OCA\Settings\SetupChecks\TransactionIsolation;
+use OCA\Settings\SetupChecks\WellKnownUrls;
+use OCA\Settings\SetupChecks\Woff2Loading;
use OCA\Settings\UserMigration\AccountMigrator;
use OCA\Settings\WellKnown\ChangePasswordHandler;
use OCA\Settings\WellKnown\SecurityTxtHandler;
@@ -54,11 +83,14 @@ use OCP\AppFramework\Bootstrap\IBootContext;
use OCP\AppFramework\Bootstrap\IBootstrap;
use OCP\AppFramework\Bootstrap\IRegistrationContext;
use OCP\AppFramework\IAppContainer;
+use OCP\AppFramework\QueryException;
use OCP\Defaults;
use OCP\Group\Events\GroupDeletedEvent;
use OCP\Group\Events\UserAddedEvent;
use OCP\Group\Events\UserRemovedEvent;
use OCP\IServerContainer;
+use OCP\Settings\Events\DeclarativeSettingsGetValueEvent;
+use OCP\Settings\Events\DeclarativeSettingsSetValueEvent;
use OCP\Settings\IManager;
use OCP\Util;
@@ -78,6 +110,7 @@ class Application extends App implements IBootstrap {
$context->registerMiddleware(SubadminMiddleware::class);
$context->registerSearchProvider(SectionSearch::class);
$context->registerSearchProvider(AppSearch::class);
+ $context->registerSearchProvider(UserSearch::class);
// Register listeners
$context->registerEventListener(AppPasswordCreatedEvent::class, AppPasswordCreatedActivityListener::class);
@@ -85,26 +118,20 @@ class Application extends App implements IBootstrap {
$context->registerEventListener(UserRemovedEvent::class, UserRemovedFromGroupActivityListener::class);
$context->registerEventListener(GroupDeletedEvent::class, GroupRemovedListener::class);
+ // Register Mail Provider listeners
+ $context->registerEventListener(DeclarativeSettingsGetValueEvent::class, MailProviderListener::class);
+ $context->registerEventListener(DeclarativeSettingsSetValueEvent::class, MailProviderListener::class);
+
// Register well-known handlers
$context->registerWellKnownHandler(SecurityTxtHandler::class);
$context->registerWellKnownHandler(ChangePasswordHandler::class);
+ // Register Settings Form(s)
+ $context->registerDeclarativeSettings(MailProvider::class);
+
/**
* Core class wrappers
*/
- /** FIXME: Remove once OC_User is non-static and mockable */
- $context->registerService('isAdmin', function () {
- return \OC_User::isAdminUser(\OC_User::getUser());
- });
- /** FIXME: Remove once OC_SubAdmin is non-static and mockable */
- $context->registerService('isSubAdmin', function () {
- $userObject = \OC::$server->getUserSession()->getUser();
- $isSubAdmin = false;
- if ($userObject !== null) {
- $isSubAdmin = \OC::$server->getGroupManager()->getSubAdmin()->isSubAdmin($userObject);
- }
- return $isSubAdmin;
- });
$context->registerService(IProvider::class, function (IAppContainer $appContainer) {
/** @var IServerContainer $serverContainer */
$serverContainer = $appContainer->query(IServerContainer::class);
@@ -134,6 +161,58 @@ class Application extends App implements IBootstrap {
Util::getDefaultEmailAddress('no-reply')
);
});
+ $context->registerSetupCheck(AllowedAdminRanges::class);
+ $context->registerSetupCheck(AppDirsWithDifferentOwner::class);
+ $context->registerSetupCheck(BruteForceThrottler::class);
+ $context->registerSetupCheck(CheckUserCertificates::class);
+ $context->registerSetupCheck(CodeIntegrity::class);
+ $context->registerSetupCheck(CronErrors::class);
+ $context->registerSetupCheck(CronInfo::class);
+ $context->registerSetupCheck(DatabaseHasMissingColumns::class);
+ $context->registerSetupCheck(DatabaseHasMissingIndices::class);
+ $context->registerSetupCheck(DatabaseHasMissingPrimaryKeys::class);
+ $context->registerSetupCheck(DatabasePendingBigIntConversions::class);
+ $context->registerSetupCheck(DataDirectoryProtected::class);
+ $context->registerSetupCheck(DebugMode::class);
+ $context->registerSetupCheck(DefaultPhoneRegionSet::class);
+ $context->registerSetupCheck(EmailTestSuccessful::class);
+ $context->registerSetupCheck(FileLocking::class);
+ $context->registerSetupCheck(ForwardedForHeaders::class);
+ $context->registerSetupCheck(HttpsUrlGeneration::class);
+ $context->registerSetupCheck(InternetConnectivity::class);
+ $context->registerSetupCheck(JavaScriptSourceMaps::class);
+ $context->registerSetupCheck(JavaScriptModules::class);
+ $context->registerSetupCheck(LegacySSEKeyFormat::class);
+ $context->registerSetupCheck(MaintenanceWindowStart::class);
+ $context->registerSetupCheck(MemcacheConfigured::class);
+ $context->registerSetupCheck(MimeTypeMigrationAvailable::class);
+ $context->registerSetupCheck(MysqlRowFormat::class);
+ $context->registerSetupCheck(MysqlUnicodeSupport::class);
+ $context->registerSetupCheck(OcxProviders::class);
+ $context->registerSetupCheck(OverwriteCliUrl::class);
+ $context->registerSetupCheck(PhpDefaultCharset::class);
+ $context->registerSetupCheck(PhpDisabledFunctions::class);
+ $context->registerSetupCheck(PhpFreetypeSupport::class);
+ $context->registerSetupCheck(PhpApcuConfig::class);
+ $context->registerSetupCheck(PhpGetEnv::class);
+ // Temporarily disabled $context->registerSetupCheck(PhpMaxFileSize::class);
+ $context->registerSetupCheck(PhpMemoryLimit::class);
+ $context->registerSetupCheck(PhpModules::class);
+ $context->registerSetupCheck(PhpOpcacheSetup::class);
+ $context->registerSetupCheck(PhpOutdated::class);
+ $context->registerSetupCheck(PhpOutputBuffering::class);
+ $context->registerSetupCheck(RandomnessSecure::class);
+ $context->registerSetupCheck(ReadOnlyConfig::class);
+ $context->registerSetupCheck(SecurityHeaders::class);
+ $context->registerSetupCheck(SchedulingTableSize::class);
+ $context->registerSetupCheck(SupportedDatabase::class);
+ $context->registerSetupCheck(SystemIs64bit::class);
+ $context->registerSetupCheck(TaskProcessingPickupSpeed::class);
+ $context->registerSetupCheck(TempSpaceAvailable::class);
+ $context->registerSetupCheck(TransactionIsolation::class);
+ $context->registerSetupCheck(PushService::class);
+ $context->registerSetupCheck(WellKnownUrls::class);
+ $context->registerSetupCheck(Woff2Loading::class);
$context->registerUserMigrator(AccountMigrator::class);
}
@@ -148,7 +227,7 @@ class Application extends App implements IBootstrap {
* @throws \InvalidArgumentException
* @throws \BadMethodCallException
* @throws \Exception
- * @throws \OCP\AppFramework\QueryException
+ * @throws QueryException
*/
public function onChangePassword(array $parameters) {
/** @var Hooks $hooks */
@@ -161,7 +240,7 @@ class Application extends App implements IBootstrap {
* @throws \InvalidArgumentException
* @throws \BadMethodCallException
* @throws \Exception
- * @throws \OCP\AppFramework\QueryException
+ * @throws QueryException
*/
public function onChangeInfo(array $parameters) {
if ($parameters['feature'] !== 'eMailAddress') {
diff --git a/apps/settings/lib/BackgroundJobs/VerifyUserData.php b/apps/settings/lib/BackgroundJobs/VerifyUserData.php
index ec75f4243be..eb66644ad91 100644
--- a/apps/settings/lib/BackgroundJobs/VerifyUserData.php
+++ b/apps/settings/lib/BackgroundJobs/VerifyUserData.php
@@ -1,31 +1,10 @@
<?php
+
+declare(strict_types=1);
+
/**
- * @copyright Copyright (c) 2017 Bjoern Schiessle <bjoern@schiessle.org>
- *
- * @author Arthur Schiwon <blizzz@arthur-schiwon.de>
- * @author Bjoern Schiessle <bjoern@schiessle.org>
- * @author Christoph Wurst <christoph@winzerhof-wurst.at>
- * @author Joas Schilling <coding@schilljs.com>
- * @author Lukas Reschke <lukas@statuscode.ch>
- * @author Morris Jobke <hey@morrisjobke.de>
- * @author Patrik Kernstock <info@pkern.at>
- * @author Roeland Jago Douma <roeland@famdouma.nl>
- *
- * @license GNU AGPL version 3 or any later version
- *
- * 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/>.
- *
+ * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\Settings\BackgroundJobs;
@@ -37,65 +16,37 @@ use OCP\BackgroundJob\IJobList;
use OCP\BackgroundJob\Job;
use OCP\Http\Client\IClientService;
use OCP\IConfig;
-use OCP\ILogger;
use OCP\IUserManager;
+use Psr\Log\LoggerInterface;
class VerifyUserData extends Job {
-
- /** @var bool */
- private $retainJob = true;
+ /** @var bool */
+ private bool $retainJob = true;
/** @var int max number of attempts to send the request */
- private $maxTry = 24;
+ private int $maxTry = 24;
/** @var int how much time should be between two tries (1 hour) */
- private $interval = 3600;
-
- /** @var IAccountManager */
- private $accountManager;
-
- /** @var IUserManager */
- private $userManager;
-
- /** @var IClientService */
- private $httpClientService;
-
- /** @var ILogger */
- private $logger;
-
- /** @var string */
- private $lookupServerUrl;
-
- /** @var IConfig */
- private $config;
-
- public function __construct(IAccountManager $accountManager,
- IUserManager $userManager,
- IClientService $clientService,
- ILogger $logger,
- ITimeFactory $timeFactory,
- IConfig $config
+ private int $interval = 3600;
+ private string $lookupServerUrl;
+
+ public function __construct(
+ private IAccountManager $accountManager,
+ private IUserManager $userManager,
+ private IClientService $httpClientService,
+ private LoggerInterface $logger,
+ ITimeFactory $timeFactory,
+ private IConfig $config,
) {
parent::__construct($timeFactory);
- $this->accountManager = $accountManager;
- $this->userManager = $userManager;
- $this->httpClientService = $clientService;
- $this->logger = $logger;
$lookupServerUrl = $config->getSystemValue('lookup_server', 'https://lookup.nextcloud.com');
$this->lookupServerUrl = rtrim($lookupServerUrl, '/');
- $this->config = $config;
}
- /**
- * run the job, then remove it from the jobList
- *
- * @param IJobList $jobList
- * @param ILogger|null $logger
- */
- public function execute(IJobList $jobList, ILogger $logger = null) {
+ public function start(IJobList $jobList): void {
if ($this->shouldRun($this->argument)) {
- parent::execute($jobList, $logger);
+ parent::start($jobList);
$jobList->remove($this, $this->argument);
if ($this->retainJob) {
$this->reAddJob($jobList, $this->argument);
@@ -169,9 +120,11 @@ class VerifyUserData extends Job {
}
protected function verifyViaLookupServer(array $argument, string $dataType): bool {
- if (empty($this->lookupServerUrl) ||
- $this->config->getAppValue('files_sharing', 'lookupServerUploadEnabled', 'yes') !== 'yes' ||
- $this->config->getSystemValue('has_internet_connection', true) === false) {
+ // TODO: Consider to enable for non-global-scale setups by checking 'files_sharing', 'lookupServerUploadEnabled'
+ if (!$this->config->getSystemValueBool('gs.enabled', false)
+ || empty($this->lookupServerUrl)
+ || $this->config->getSystemValue('has_internet_connection', true) === false
+ ) {
return true;
}
diff --git a/apps/settings/lib/Command/AdminDelegation/Add.php b/apps/settings/lib/Command/AdminDelegation/Add.php
new file mode 100644
index 00000000000..5cbef5c5d15
--- /dev/null
+++ b/apps/settings/lib/Command/AdminDelegation/Add.php
@@ -0,0 +1,59 @@
+<?php
+
+declare(strict_types = 1);
+/**
+ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\Settings\Command\AdminDelegation;
+
+use OC\Core\Command\Base;
+use OCA\Settings\Service\AuthorizedGroupService;
+use OCP\IGroupManager;
+use OCP\Settings\IDelegatedSettings;
+use OCP\Settings\IManager;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+use Symfony\Component\Console\Style\SymfonyStyle;
+
+class Add extends Base {
+ public function __construct(
+ private IManager $settingManager,
+ private AuthorizedGroupService $authorizedGroupService,
+ private IGroupManager $groupManager,
+ ) {
+ parent::__construct();
+ }
+
+ protected function configure(): void {
+ $this
+ ->setName('admin-delegation:add')
+ ->setDescription('add setting delegation to a group')
+ ->addArgument('settingClass', InputArgument::REQUIRED, 'Admin setting class')
+ ->addArgument('groupId', InputArgument::REQUIRED, 'Delegate to group ID')
+ ->addUsage('\'OCA\Settings\Settings\Admin\Server\' mygroup')
+ ;
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output): int {
+ $io = new SymfonyStyle($input, $output);
+ $settingClass = $input->getArgument('settingClass');
+ if (!in_array(IDelegatedSettings::class, (array)class_implements($settingClass), true)) {
+ $io->error('The specified class isn’t a valid delegated setting.');
+ return 2;
+ }
+
+ $groupId = $input->getArgument('groupId');
+ if (!$this->groupManager->groupExists($groupId)) {
+ $io->error('The specified group didn’t exist.');
+ return 3;
+ }
+
+ $this->authorizedGroupService->create($groupId, $settingClass);
+
+ $io->success('Administration of ' . $settingClass . ' delegated to ' . $groupId . '.');
+
+ return 0;
+ }
+}
diff --git a/apps/settings/lib/Command/AdminDelegation/Remove.php b/apps/settings/lib/Command/AdminDelegation/Remove.php
new file mode 100644
index 00000000000..6b5347ce89f
--- /dev/null
+++ b/apps/settings/lib/Command/AdminDelegation/Remove.php
@@ -0,0 +1,55 @@
+<?php
+
+declare(strict_types = 1);
+/**
+ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\Settings\Command\AdminDelegation;
+
+use OC\Core\Command\Base;
+use OCA\Settings\Service\AuthorizedGroupService;
+use OCP\IGroupManager;
+use OCP\Settings\IManager;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+use Symfony\Component\Console\Style\SymfonyStyle;
+
+class Remove extends Base {
+ public function __construct(
+ private IManager $settingManager,
+ private AuthorizedGroupService $authorizedGroupService,
+ private IGroupManager $groupManager,
+ ) {
+ parent::__construct();
+ }
+
+ protected function configure(): void {
+ $this
+ ->setName('admin-delegation:remove')
+ ->setDescription('remove settings delegation from a group')
+ ->addArgument('settingClass', InputArgument::REQUIRED, 'Admin setting class')
+ ->addArgument('groupId', InputArgument::REQUIRED, 'Group ID to remove')
+ ->addUsage('\'OCA\Settings\Settings\Admin\Server\' mygroup')
+ ;
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output): int {
+ $io = new SymfonyStyle($input, $output);
+ $settingClass = $input->getArgument('settingClass');
+ $groups = $this->authorizedGroupService->findExistingGroupsForClass($settingClass);
+ $groupId = $input->getArgument('groupId');
+ foreach ($groups as $group) {
+ if ($group->getGroupId() === $groupId) {
+ $this->authorizedGroupService->delete($group->getId());
+ $io->success('Removed delegation of ' . $settingClass . ' to ' . $groupId . '.');
+ return 0;
+ }
+ }
+
+ $io->success('Group ' . $groupId . ' didn’t have delegation for ' . $settingClass . '.');
+
+ return 0;
+ }
+}
diff --git a/apps/settings/lib/Command/AdminDelegation/Show.php b/apps/settings/lib/Command/AdminDelegation/Show.php
new file mode 100644
index 00000000000..9aba6bc0cb7
--- /dev/null
+++ b/apps/settings/lib/Command/AdminDelegation/Show.php
@@ -0,0 +1,77 @@
+<?php
+
+declare(strict_types = 1);
+/**
+ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\Settings\Command\AdminDelegation;
+
+use OC\Core\Command\Base;
+use OC\Settings\AuthorizedGroup;
+use OCA\Settings\Service\AuthorizedGroupService;
+use OCP\Settings\IDelegatedSettings;
+use OCP\Settings\IManager;
+use OCP\Settings\ISettings;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+use Symfony\Component\Console\Style\SymfonyStyle;
+
+class Show extends Base {
+ public function __construct(
+ private IManager $settingManager,
+ private AuthorizedGroupService $authorizedGroupService,
+ ) {
+ parent::__construct();
+ }
+
+ protected function configure(): void {
+ $this
+ ->setName('admin-delegation:show')
+ ->setDescription('show delegated settings')
+ ;
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output): int {
+ $io = new SymfonyStyle($input, $output);
+ $io->title('Current delegations');
+
+ $sections = $this->settingManager->getAdminSections();
+ $settings = [];
+ $headers = ['Name', 'SettingId', 'Delegated to groups'];
+ foreach ($sections as $sectionPriority) {
+ foreach ($sectionPriority as $section) {
+ $sectionSettings = $this->settingManager->getAdminSettings($section->getId());
+ $sectionSettings = array_reduce($sectionSettings, [$this, 'getDelegatedSettings'], []);
+ if (empty($sectionSettings)) {
+ continue;
+ }
+
+ $io->section('Section: ' . $section->getID());
+ $io->table($headers, array_map(function (IDelegatedSettings $setting) use ($section) {
+ $className = get_class($setting);
+ $groups = array_map(
+ static fn (AuthorizedGroup $group) => $group->getGroupId(),
+ $this->authorizedGroupService->findExistingGroupsForClass($className)
+ );
+ natsort($groups);
+ return [
+ $setting->getName() ?: 'Global',
+ $className,
+ implode(', ', $groups),
+ ];
+ }, $sectionSettings));
+ }
+ }
+
+ return 0;
+ }
+
+ /**
+ * @param IDelegatedSettings[] $settings
+ * @param array $innerSection
+ */
+ private function getDelegatedSettings(array $settings, array $innerSection): array {
+ return $settings + array_filter($innerSection, fn (ISettings $setting) => $setting instanceof IDelegatedSettings);
+ }
+}
diff --git a/apps/settings/lib/Controller/AISettingsController.php b/apps/settings/lib/Controller/AISettingsController.php
new file mode 100644
index 00000000000..114cbf61514
--- /dev/null
+++ b/apps/settings/lib/Controller/AISettingsController.php
@@ -0,0 +1,46 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+namespace OCA\Settings\Controller;
+
+use OCA\Settings\Settings\Admin\ArtificialIntelligence;
+use OCP\AppFramework\Controller;
+use OCP\AppFramework\Http\Attribute\AuthorizedAdminSetting;
+use OCP\AppFramework\Http\DataResponse;
+use OCP\IAppConfig;
+use OCP\IRequest;
+
+class AISettingsController extends Controller {
+
+ public function __construct(
+ $appName,
+ IRequest $request,
+ private IAppConfig $appConfig,
+ ) {
+ parent::__construct($appName, $request);
+ }
+
+ /**
+ * Sets the email settings
+ *
+ * @param array $settings
+ * @return DataResponse
+ */
+ #[AuthorizedAdminSetting(settings: ArtificialIntelligence::class)]
+ public function update($settings) {
+ $keys = ['ai.stt_provider', 'ai.textprocessing_provider_preferences', 'ai.taskprocessing_provider_preferences','ai.taskprocessing_type_preferences', 'ai.translation_provider_preferences', 'ai.text2image_provider', 'ai.taskprocessing_guests'];
+ foreach ($keys as $key) {
+ if (!isset($settings[$key])) {
+ continue;
+ }
+ $this->appConfig->setValueString('core', $key, json_encode($settings[$key]), lazy: in_array($key, \OC\TaskProcessing\Manager::LAZY_CONFIG_KEYS, true));
+ }
+
+ return new DataResponse();
+ }
+}
diff --git a/apps/settings/lib/Controller/AdminSettingsController.php b/apps/settings/lib/Controller/AdminSettingsController.php
index dfaa26ff695..15e2c392148 100644
--- a/apps/settings/lib/Controller/AdminSettingsController.php
+++ b/apps/settings/lib/Controller/AdminSettingsController.php
@@ -1,42 +1,26 @@
<?php
+
/**
- * @copyright Copyright (c) 2016 Arthur Schiwon <blizzz@arthur-schiwon.de>
- *
- * @author Arthur Schiwon <blizzz@arthur-schiwon.de>
- * @author Christoph Wurst <christoph@winzerhof-wurst.at>
- * @author Lukas Reschke <lukas@statuscode.ch>
- * @author Robin Appelman <robin@icewind.nl>
- *
- * @license GNU AGPL version 3 or any later version
- *
- * 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/>.
- *
+ * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\Settings\Controller;
-use OC\AppFramework\Middleware\Security\Exceptions\NotAdminException;
use OCP\AppFramework\Controller;
+use OCP\AppFramework\Http\Attribute\NoAdminRequired;
+use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
+use OCP\AppFramework\Http\Attribute\OpenAPI;
use OCP\AppFramework\Http\TemplateResponse;
+use OCP\AppFramework\Services\IInitialState;
use OCP\Group\ISubAdmin;
use OCP\IGroupManager;
use OCP\INavigationManager;
use OCP\IRequest;
-use OCP\IUser;
use OCP\IUserSession;
+use OCP\Settings\IDeclarativeManager;
use OCP\Settings\IManager as ISettingsManager;
-use OCP\Template;
+#[OpenAPI(scope: OpenAPI::SCOPE_IGNORE)]
class AdminSettingsController extends Controller {
use CommonSettingsTrait;
@@ -47,7 +31,9 @@ class AdminSettingsController extends Controller {
ISettingsManager $settingsManager,
IUserSession $userSession,
IGroupManager $groupManager,
- ISubAdmin $subAdmin
+ ISubAdmin $subAdmin,
+ IDeclarativeManager $declarativeSettingsManager,
+ IInitialState $initialState,
) {
parent::__construct($appName, $request);
$this->navigationManager = $navigationManager;
@@ -55,66 +41,21 @@ class AdminSettingsController extends Controller {
$this->userSession = $userSession;
$this->groupManager = $groupManager;
$this->subAdmin = $subAdmin;
+ $this->declarativeSettingsManager = $declarativeSettingsManager;
+ $this->initialState = $initialState;
}
/**
- * @NoCSRFRequired
- * @NoAdminRequired
* @NoSubAdminRequired
* We are checking the permissions in the getSettings method. If there is no allowed
- * settings for the given section. The user will be gretted by an error message.
+ * settings for the given section. The user will be greeted by an error message.
*/
+ #[NoAdminRequired]
+ #[NoCSRFRequired]
public function index(string $section): TemplateResponse {
- return $this->getIndexResponse('admin', $section);
- }
-
- /**
- * @param string $section
- * @return array
- */
- protected function getSettings($section) {
- /** @var IUser $user */
- $user = $this->userSession->getUser();
- $isSubAdmin = !$this->groupManager->isAdmin($user->getUID()) && $this->subAdmin->isSubAdmin($user);
- $settings = $this->settingsManager->getAllowedAdminSettings($section, $user);
- if (empty($settings)) {
- throw new NotAdminException("Logged in user doesn't have permission to access these settings.");
- }
- $formatted = $this->formatSettings($settings);
- // Do not show legacy forms for sub admins
- if ($section === 'additional' && !$isSubAdmin) {
- $formatted['content'] .= $this->getLegacyForms();
- }
- return $formatted;
- }
-
- /**
- * @return bool|string
- */
- private function getLegacyForms() {
- $forms = \OC_App::getForms('admin');
-
- $forms = array_map(function ($form) {
- if (preg_match('%(<h2(?P<class>[^>]*)>.*?</h2>)%i', $form, $regs)) {
- $sectionName = str_replace('<h2' . $regs['class'] . '>', '', $regs[0]);
- $sectionName = str_replace('</h2>', '', $sectionName);
- $anchor = strtolower($sectionName);
- $anchor = str_replace(' ', '-', $anchor);
-
- return [
- 'anchor' => $anchor,
- 'section-name' => $sectionName,
- 'form' => $form
- ];
- }
- return [
- 'form' => $form
- ];
- }, $forms);
-
- $out = new Template('settings', 'settings/additional');
- $out->assign('forms', $forms);
-
- return $out->fetchPage();
+ return $this->getIndexResponse(
+ 'admin',
+ $section,
+ );
}
}
diff --git a/apps/settings/lib/Controller/AppSettingsController.php b/apps/settings/lib/Controller/AppSettingsController.php
index a4addfc5b35..a85ee8cc20a 100644
--- a/apps/settings/lib/Controller/AppSettingsController.php
+++ b/apps/settings/lib/Controller/AppSettingsController.php
@@ -1,149 +1,251 @@
<?php
+
/**
- * @copyright Copyright (c) 2016, ownCloud, Inc.
- * @copyright Copyright (c) 2016, Lukas Reschke <lukas@statuscode.ch>
- *
- * @author Arthur Schiwon <blizzz@arthur-schiwon.de>
- * @author Christoph Wurst <christoph@winzerhof-wurst.at>
- * @author Daniel Kesselberg <mail@danielkesselberg.de>
- * @author Joas Schilling <coding@schilljs.com>
- * @author John Molakvoæ <skjnldsv@protonmail.com>
- * @author Julius Härtl <jus@bitgrid.net>
- * @author Lukas Reschke <lukas@statuscode.ch>
- * @author Morris Jobke <hey@morrisjobke.de>
- * @author Roeland Jago Douma <roeland@famdouma.nl>
- * @author Thomas Müller <thomas.mueller@tmit.eu>
- *
- * @license AGPL-3.0
- *
- * This code is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * 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, version 3,
- * along with this program. If not, see <http://www.gnu.org/licenses/>
- *
+ * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
+ * SPDX-License-Identifier: AGPL-3.0-only
*/
namespace OCA\Settings\Controller;
+use OC\App\AppManager;
use OC\App\AppStore\Bundles\BundleFetcher;
+use OC\App\AppStore\Fetcher\AppDiscoverFetcher;
use OC\App\AppStore\Fetcher\AppFetcher;
use OC\App\AppStore\Fetcher\CategoryFetcher;
use OC\App\AppStore\Version\VersionParser;
use OC\App\DependencyAnalyzer;
use OC\App\Platform;
use OC\Installer;
-use OC_App;
-use OCP\App\IAppManager;
+use OCA\AppAPI\Service\ExAppsPageService;
+use OCP\App\AppPathNotFoundException;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http;
+use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
+use OCP\AppFramework\Http\Attribute\OpenAPI;
+use OCP\AppFramework\Http\Attribute\PasswordConfirmationRequired;
use OCP\AppFramework\Http\ContentSecurityPolicy;
+use OCP\AppFramework\Http\FileDisplayResponse;
use OCP\AppFramework\Http\JSONResponse;
+use OCP\AppFramework\Http\NotFoundResponse;
+use OCP\AppFramework\Http\Response;
use OCP\AppFramework\Http\TemplateResponse;
+use OCP\AppFramework\Services\IInitialState;
+use OCP\Files\AppData\IAppDataFactory;
+use OCP\Files\IAppData;
+use OCP\Files\NotFoundException;
+use OCP\Files\NotPermittedException;
+use OCP\Files\SimpleFS\ISimpleFile;
+use OCP\Files\SimpleFS\ISimpleFolder;
+use OCP\Http\Client\IClientService;
use OCP\IConfig;
+use OCP\IGroup;
+use OCP\IGroupManager;
use OCP\IL10N;
use OCP\INavigationManager;
use OCP\IRequest;
use OCP\IURLGenerator;
+use OCP\IUserSession;
use OCP\L10N\IFactory;
+use OCP\Security\RateLimiting\ILimiter;
+use OCP\Server;
+use OCP\Util;
use Psr\Log\LoggerInterface;
+#[OpenAPI(scope: OpenAPI::SCOPE_IGNORE)]
class AppSettingsController extends Controller {
- /** @var \OCP\IL10N */
- private $l10n;
- /** @var IConfig */
- private $config;
- /** @var INavigationManager */
- private $navigationManager;
- /** @var IAppManager */
- private $appManager;
- /** @var CategoryFetcher */
- private $categoryFetcher;
- /** @var AppFetcher */
- private $appFetcher;
- /** @var IFactory */
- private $l10nFactory;
- /** @var BundleFetcher */
- private $bundleFetcher;
- /** @var Installer */
- private $installer;
- /** @var IURLGenerator */
- private $urlGenerator;
- /** @var LoggerInterface */
- private $logger;
-
/** @var array */
private $allApps = [];
- /**
- * @param string $appName
- * @param IRequest $request
- * @param IL10N $l10n
- * @param IConfig $config
- * @param INavigationManager $navigationManager
- * @param IAppManager $appManager
- * @param CategoryFetcher $categoryFetcher
- * @param AppFetcher $appFetcher
- * @param IFactory $l10nFactory
- * @param BundleFetcher $bundleFetcher
- * @param Installer $installer
- * @param IURLGenerator $urlGenerator
- * @param LoggerInterface $logger
- */
- public function __construct(string $appName,
- IRequest $request,
- IL10N $l10n,
- IConfig $config,
- INavigationManager $navigationManager,
- IAppManager $appManager,
- CategoryFetcher $categoryFetcher,
- AppFetcher $appFetcher,
- IFactory $l10nFactory,
- BundleFetcher $bundleFetcher,
- Installer $installer,
- IURLGenerator $urlGenerator,
- LoggerInterface $logger) {
+ private IAppData $appData;
+
+ public function __construct(
+ string $appName,
+ IRequest $request,
+ IAppDataFactory $appDataFactory,
+ private IL10N $l10n,
+ private IConfig $config,
+ private INavigationManager $navigationManager,
+ private AppManager $appManager,
+ private CategoryFetcher $categoryFetcher,
+ private AppFetcher $appFetcher,
+ private IFactory $l10nFactory,
+ private BundleFetcher $bundleFetcher,
+ private Installer $installer,
+ private IURLGenerator $urlGenerator,
+ private LoggerInterface $logger,
+ private IInitialState $initialState,
+ private AppDiscoverFetcher $discoverFetcher,
+ private IClientService $clientService,
+ ) {
parent::__construct($appName, $request);
- $this->l10n = $l10n;
- $this->config = $config;
- $this->navigationManager = $navigationManager;
- $this->appManager = $appManager;
- $this->categoryFetcher = $categoryFetcher;
- $this->appFetcher = $appFetcher;
- $this->l10nFactory = $l10nFactory;
- $this->bundleFetcher = $bundleFetcher;
- $this->installer = $installer;
- $this->urlGenerator = $urlGenerator;
- $this->logger = $logger;
+ $this->appData = $appDataFactory->get('appstore');
}
/**
- * @NoCSRFRequired
+ * @psalm-suppress UndefinedClass AppAPI is shipped since 30.0.1
*
* @return TemplateResponse
*/
+ #[NoCSRFRequired]
public function viewApps(): TemplateResponse {
- $params = [];
- $params['appstoreEnabled'] = $this->config->getSystemValueBool('appstoreenabled', true);
- $params['updateCount'] = count($this->getAppsWithUpdates());
- $params['developerDocumentation'] = $this->urlGenerator->linkToDocs('developer-manual');
- $params['bundles'] = $this->getBundles();
$this->navigationManager->setActiveEntry('core_apps');
- $templateResponse = new TemplateResponse('settings', 'settings-vue', ['serverData' => $params]);
+ $this->initialState->provideInitialState('appstoreEnabled', $this->config->getSystemValueBool('appstoreenabled', true));
+ $this->initialState->provideInitialState('appstoreBundles', $this->getBundles());
+ $this->initialState->provideInitialState('appstoreDeveloperDocs', $this->urlGenerator->linkToDocs('developer-manual'));
+ $this->initialState->provideInitialState('appstoreUpdateCount', count($this->getAppsWithUpdates()));
+
+ if ($this->appManager->isEnabledForAnyone('app_api')) {
+ try {
+ Server::get(ExAppsPageService::class)->provideAppApiState($this->initialState);
+ } catch (\Psr\Container\NotFoundExceptionInterface|\Psr\Container\ContainerExceptionInterface $e) {
+ }
+ }
+
$policy = new ContentSecurityPolicy();
$policy->addAllowedImageDomain('https://usercontent.apps.nextcloud.com');
+
+ $templateResponse = new TemplateResponse('settings', 'settings/empty', ['pageTitle' => $this->l10n->t('Settings')]);
$templateResponse->setContentSecurityPolicy($policy);
+ Util::addStyle('settings', 'settings');
+ Util::addScript('settings', 'vue-settings-apps-users-management');
+
return $templateResponse;
}
+ /**
+ * Get all active entries for the app discover section
+ */
+ #[NoCSRFRequired]
+ public function getAppDiscoverJSON(): JSONResponse {
+ $data = $this->discoverFetcher->get(true);
+ return new JSONResponse(array_values($data));
+ }
+
+ /**
+ * Get a image for the app discover section - this is proxied for privacy and CSP reasons
+ *
+ * @param string $image
+ * @throws \Exception
+ */
+ #[NoCSRFRequired]
+ public function getAppDiscoverMedia(string $fileName, ILimiter $limiter, IUserSession $session): Response {
+ $getEtag = $this->discoverFetcher->getETag() ?? date('Y-m');
+ $etag = trim($getEtag, '"');
+
+ $folder = null;
+ try {
+ $folder = $this->appData->getFolder('app-discover-cache');
+ $this->cleanUpImageCache($folder, $etag);
+ } catch (\Throwable $e) {
+ $folder = $this->appData->newFolder('app-discover-cache');
+ }
+
+ // Get the current cache folder
+ try {
+ $folder = $folder->getFolder($etag);
+ } catch (NotFoundException $e) {
+ $folder = $folder->newFolder($etag);
+ }
+
+ $info = pathinfo($fileName);
+ $hashName = md5($fileName);
+ $allFiles = $folder->getDirectoryListing();
+ // Try to find the file
+ $file = array_filter($allFiles, function (ISimpleFile $file) use ($hashName) {
+ return str_starts_with($file->getName(), $hashName);
+ });
+ // Get the first entry
+ $file = reset($file);
+ // If not found request from Web
+ if ($file === false) {
+ $user = $session->getUser();
+ // this route is not public thus we can assume a user is logged-in
+ assert($user !== null);
+ // Register a user request to throttle fetching external data
+ // this will prevent using the server for DoS of other systems.
+ $limiter->registerUserRequest(
+ 'settings-discover-media',
+ // allow up to 24 media requests per hour
+ // this should be a sane default when a completely new section is loaded
+ // keep in mind browsers request all files from a source-set
+ 24,
+ 60 * 60,
+ $user,
+ );
+
+ if (!$this->checkCanDownloadMedia($fileName)) {
+ $this->logger->warning('Tried to load media files for app discover section from untrusted source');
+ return new NotFoundResponse(Http::STATUS_BAD_REQUEST);
+ }
+
+ try {
+ $client = $this->clientService->newClient();
+ $fileResponse = $client->get($fileName);
+ $contentType = $fileResponse->getHeader('Content-Type');
+ $extension = $info['extension'] ?? '';
+ $file = $folder->newFile($hashName . '.' . base64_encode($contentType) . '.' . $extension, $fileResponse->getBody());
+ } catch (\Throwable $e) {
+ $this->logger->warning('Could not load media file for app discover section', ['media_src' => $fileName, 'exception' => $e]);
+ return new NotFoundResponse();
+ }
+ } else {
+ // File was found so we can get the content type from the file name
+ $contentType = base64_decode(explode('.', $file->getName())[1] ?? '');
+ }
+
+ $response = new FileDisplayResponse($file, Http::STATUS_OK, ['Content-Type' => $contentType]);
+ // cache for 7 days
+ $response->cacheFor(604800, false, true);
+ return $response;
+ }
+
+ private function checkCanDownloadMedia(string $filename): bool {
+ $urlInfo = parse_url($filename);
+ if (!isset($urlInfo['host']) || !isset($urlInfo['path'])) {
+ return false;
+ }
+
+ // Always allowed hosts
+ if ($urlInfo['host'] === 'nextcloud.com') {
+ return true;
+ }
+
+ // Hosts that need further verification
+ // Github is only allowed if from our organization
+ $ALLOWED_HOSTS = ['github.com', 'raw.githubusercontent.com'];
+ if (!in_array($urlInfo['host'], $ALLOWED_HOSTS)) {
+ return false;
+ }
+
+ if (str_starts_with($urlInfo['path'], '/nextcloud/') || str_starts_with($urlInfo['path'], '/nextcloud-gmbh/')) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Remove orphaned folders from the image cache that do not match the current etag
+ * @param ISimpleFolder $folder The folder to clear
+ * @param string $etag The etag (directory name) to keep
+ */
+ private function cleanUpImageCache(ISimpleFolder $folder, string $etag): void {
+ // Cleanup old cache folders
+ $allFiles = $folder->getDirectoryListing();
+ foreach ($allFiles as $dir) {
+ try {
+ if ($dir->getName() !== $etag) {
+ $dir->delete();
+ }
+ } catch (NotPermittedException $e) {
+ // ignore folder for now
+ }
+ }
+ }
+
private function getAppsWithUpdates() {
$appClass = new \OC_App();
$apps = $appClass->listAllApps();
@@ -181,17 +283,21 @@ class AppSettingsController extends Controller {
private function getAllCategories() {
$currentLanguage = substr($this->l10nFactory->findLanguage(), 0, 2);
- $formattedCategories = [];
$categories = $this->categoryFetcher->get();
- foreach ($categories as $category) {
- $formattedCategories[] = [
- 'id' => $category['id'],
- 'ident' => $category['id'],
- 'displayName' => isset($category['translations'][$currentLanguage]['name']) ? $category['translations'][$currentLanguage]['name'] : $category['translations']['en']['name'],
- ];
- }
+ return array_map(fn ($category) => [
+ 'id' => $category['id'],
+ 'displayName' => $category['translations'][$currentLanguage]['name'] ?? $category['translations']['en']['name'],
+ ], $categories);
+ }
- return $formattedCategories;
+ /**
+ * Convert URL to proxied URL so CSP is no problem
+ */
+ private function createProxyPreviewUrl(string $url): string {
+ if ($url === '') {
+ return '';
+ }
+ return 'https://usercontent.apps.nextcloud.com/' . base64_encode($url);
}
private function fetchApps() {
@@ -199,6 +305,16 @@ class AppSettingsController extends Controller {
$apps = $appClass->listAllApps();
foreach ($apps as $app) {
$app['installed'] = true;
+
+ if (isset($app['screenshot'][0])) {
+ $appScreenshot = $app['screenshot'][0] ?? null;
+ if (is_array($appScreenshot)) {
+ // Screenshot with thumbnail
+ $appScreenshot = $appScreenshot['@value'];
+ }
+
+ $app['screenshot'] = $this->createProxyPreviewUrl($appScreenshot);
+ }
$this->allApps[$app['id']] = $app;
}
@@ -234,6 +350,7 @@ class AppSettingsController extends Controller {
private function getAllApps() {
return $this->allApps;
}
+
/**
* Get all available apps in a category
*
@@ -256,7 +373,7 @@ class AppSettingsController extends Controller {
$apps = array_map(function (array $appData) use ($dependencyAnalyzer, $ignoreMaxApps) {
if (isset($appData['appstoreData'])) {
$appstoreData = $appData['appstoreData'];
- $appData['screenshot'] = isset($appstoreData['screenshots'][0]['url']) ? 'https://usercontent.apps.nextcloud.com/' . base64_encode($appstoreData['screenshots'][0]['url']) : '';
+ $appData['screenshot'] = $this->createProxyPreviewUrl($appstoreData['screenshots'][0]['url'] ?? '');
$appData['category'] = $appstoreData['categories'];
$appData['releases'] = $appstoreData['releases'];
}
@@ -270,6 +387,10 @@ class AppSettingsController extends Controller {
$groups = [];
if (is_string($appData['groups'])) {
$groups = json_decode($appData['groups']);
+ // ensure 'groups' is an array
+ if (!is_array($groups)) {
+ $groups = [$groups];
+ }
}
$appData['groups'] = $groups;
$appData['canUnInstall'] = !$appData['active'] && $appData['removable'];
@@ -335,7 +456,14 @@ class AppSettingsController extends Controller {
$nextCloudVersionDependencies['nextcloud']['@attributes']['max-version'] = $nextCloudVersion->getMaximumVersion();
}
$phpVersion = $versionParser->getVersion($app['releases'][0]['rawPhpVersionSpec']);
- $existsLocally = \OC_App::getAppPath($app['id']) !== false;
+
+ try {
+ $this->appManager->getAppPath($app['id']);
+ $existsLocally = true;
+ } catch (AppPathNotFoundException) {
+ $existsLocally = false;
+ }
+
$phpDependencies = [];
if ($phpVersion->getMinimumVersion() !== '') {
$phpDependencies['php']['@attributes']['min-version'] = $phpVersion->getMinimumVersion();
@@ -354,7 +482,7 @@ class AppSettingsController extends Controller {
}
}
- $currentLanguage = substr(\OC::$server->getL10NFactory()->findLanguage(), 0, 2);
+ $currentLanguage = substr($this->l10nFactory->findLanguage(), 0, 2);
$enabledValue = $this->config->getAppValue($app['id'], 'enabled', 'no');
$groups = null;
if ($enabledValue !== 'no' && $enabledValue !== 'yes') {
@@ -362,20 +490,21 @@ class AppSettingsController extends Controller {
}
$currentVersion = '';
- if ($this->appManager->isInstalled($app['id'])) {
+ if ($this->appManager->isEnabledForAnyone($app['id'])) {
$currentVersion = $this->appManager->getAppVersion($app['id']);
} else {
- $currentLanguage = $app['releases'][0]['version'];
+ $currentVersion = $app['releases'][0]['version'];
}
$formattedApps[] = [
'id' => $app['id'],
- 'name' => isset($app['translations'][$currentLanguage]['name']) ? $app['translations'][$currentLanguage]['name'] : $app['translations']['en']['name'],
- 'description' => isset($app['translations'][$currentLanguage]['description']) ? $app['translations'][$currentLanguage]['description'] : $app['translations']['en']['description'],
- 'summary' => isset($app['translations'][$currentLanguage]['summary']) ? $app['translations'][$currentLanguage]['summary'] : $app['translations']['en']['summary'],
+ 'app_api' => false,
+ 'name' => $app['translations'][$currentLanguage]['name'] ?? $app['translations']['en']['name'],
+ 'description' => $app['translations'][$currentLanguage]['description'] ?? $app['translations']['en']['description'],
+ 'summary' => $app['translations'][$currentLanguage]['summary'] ?? $app['translations']['en']['summary'],
'license' => $app['releases'][0]['licenses'],
'author' => $authors,
- 'shipped' => false,
+ 'shipped' => $this->appManager->isShipped($app['id']),
'version' => $currentVersion,
'default_enable' => '',
'types' => [],
@@ -395,7 +524,7 @@ class AppSettingsController extends Controller {
'missingMaxOwnCloudVersion' => false,
'missingMinOwnCloudVersion' => false,
'canInstall' => true,
- 'screenshot' => isset($app['screenshots'][0]['url']) ? 'https://usercontent.apps.nextcloud.com/'.base64_encode($app['screenshots'][0]['url']) : '',
+ 'screenshot' => isset($app['screenshots'][0]['url']) ? 'https://usercontent.apps.nextcloud.com/' . base64_encode($app['screenshots'][0]['url']) : '',
'score' => $app['ratingOverall'],
'ratingNumOverall' => $app['ratingNumOverall'],
'ratingNumThresholdReached' => $app['ratingNumOverall'] > 5,
@@ -412,12 +541,11 @@ class AppSettingsController extends Controller {
}
/**
- * @PasswordConfirmationRequired
- *
* @param string $appId
* @param array $groups
* @return JSONResponse
*/
+ #[PasswordConfirmationRequired]
public function enableApp(string $appId, array $groups = []): JSONResponse {
return $this->enableApps([$appId], $groups);
}
@@ -427,21 +555,21 @@ class AppSettingsController extends Controller {
*
* apps will be enabled for specific groups only if $groups is defined
*
- * @PasswordConfirmationRequired
* @param array $appIds
* @param array $groups
* @return JSONResponse
*/
+ #[PasswordConfirmationRequired]
public function enableApps(array $appIds, array $groups = []): JSONResponse {
try {
$updateRequired = false;
foreach ($appIds as $appId) {
- $appId = OC_App::cleanAppId($appId);
+ $appId = $this->appManager->cleanAppId($appId);
// Check if app is already downloaded
/** @var Installer $installer */
- $installer = \OC::$server->query(Installer::class);
+ $installer = Server::get(Installer::class);
$isDownloaded = $installer->isDownloaded($appId);
if (!$isDownloaded) {
@@ -460,18 +588,18 @@ class AppSettingsController extends Controller {
}
}
return new JSONResponse(['data' => ['update_required' => $updateRequired]]);
- } catch (\Exception $e) {
+ } catch (\Throwable $e) {
$this->logger->error('could not enable apps', ['exception' => $e]);
return new JSONResponse(['data' => ['message' => $e->getMessage()]], Http::STATUS_INTERNAL_SERVER_ERROR);
}
}
private function getGroupList(array $groups) {
- $groupManager = \OC::$server->getGroupManager();
+ $groupManager = Server::get(IGroupManager::class);
$groupsList = [];
foreach ($groups as $group) {
$groupItem = $groupManager->get($group);
- if ($groupItem instanceof \OCP\IGroup) {
+ if ($groupItem instanceof IGroup) {
$groupsList[] = $groupManager->get($group);
}
}
@@ -479,25 +607,23 @@ class AppSettingsController extends Controller {
}
/**
- * @PasswordConfirmationRequired
- *
* @param string $appId
* @return JSONResponse
*/
+ #[PasswordConfirmationRequired]
public function disableApp(string $appId): JSONResponse {
return $this->disableApps([$appId]);
}
/**
- * @PasswordConfirmationRequired
- *
* @param array $appIds
* @return JSONResponse
*/
+ #[PasswordConfirmationRequired]
public function disableApps(array $appIds): JSONResponse {
try {
foreach ($appIds as $appId) {
- $appId = OC_App::cleanAppId($appId);
+ $appId = $this->appManager->cleanAppId($appId);
$this->appManager->disableApp($appId);
}
return new JSONResponse([]);
@@ -508,15 +634,16 @@ class AppSettingsController extends Controller {
}
/**
- * @PasswordConfirmationRequired
- *
* @param string $appId
* @return JSONResponse
*/
+ #[PasswordConfirmationRequired]
public function uninstallApp(string $appId): JSONResponse {
- $appId = OC_App::cleanAppId($appId);
+ $appId = $this->appManager->cleanAppId($appId);
$result = $this->installer->removeApp($appId);
if ($result !== false) {
+ // If this app was force enabled, remove the force-enabled-state
+ $this->appManager->removeOverwriteNextcloudRequirement($appId);
$this->appManager->clearAppsCache();
return new JSONResponse(['data' => ['appid' => $appId]]);
}
@@ -528,7 +655,7 @@ class AppSettingsController extends Controller {
* @return JSONResponse
*/
public function updateApp(string $appId): JSONResponse {
- $appId = OC_App::cleanAppId($appId);
+ $appId = $this->appManager->cleanAppId($appId);
$this->config->setSystemValue('maintenance', true);
try {
@@ -555,8 +682,8 @@ class AppSettingsController extends Controller {
}
public function force(string $appId): JSONResponse {
- $appId = OC_App::cleanAppId($appId);
- $this->appManager->ignoreNextcloudRequirementForApp($appId);
+ $appId = $this->appManager->cleanAppId($appId);
+ $this->appManager->overwriteNextcloudRequirement($appId);
return new JSONResponse();
}
}
diff --git a/apps/settings/lib/Controller/AuthSettingsController.php b/apps/settings/lib/Controller/AuthSettingsController.php
index 38db7be1e91..8652a49fb1d 100644
--- a/apps/settings/lib/Controller/AuthSettingsController.php
+++ b/apps/settings/lib/Controller/AuthSettingsController.php
@@ -1,50 +1,29 @@
<?php
+
/**
- * @copyright Copyright (c) 2016, ownCloud, Inc.
- *
- * @author Christoph Wurst <christoph@winzerhof-wurst.at>
- * @author Daniel Kesselberg <mail@danielkesselberg.de>
- * @author Fabrizio Steiner <fabrizio.steiner@gmail.com>
- * @author Greta Doci <gretadoci@gmail.com>
- * @author Joas Schilling <coding@schilljs.com>
- * @author Lukas Reschke <lukas@statuscode.ch>
- * @author Marcel Waldvogel <marcel.waldvogel@uni-konstanz.de>
- * @author Morris Jobke <hey@morrisjobke.de>
- * @author Robin Appelman <robin@icewind.nl>
- * @author Roeland Jago Douma <roeland@famdouma.nl>
- * @author Sergej Nikolaev <kinolaev@gmail.com>
- *
- * @license AGPL-3.0
- *
- * This code is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * 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, version 3,
- * along with this program. If not, see <http://www.gnu.org/licenses/>
- *
+ * SPDX-FileCopyrightText: 2019-2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
+ * SPDX-License-Identifier: AGPL-3.0-only
*/
namespace OCA\Settings\Controller;
use BadMethodCallException;
-use OC\Authentication\Exceptions\ExpiredTokenException;
-use OC\Authentication\Exceptions\InvalidTokenException;
+use OC\Authentication\Exceptions\InvalidTokenException as OcInvalidTokenException;
use OC\Authentication\Exceptions\PasswordlessTokenException;
-use OC\Authentication\Exceptions\WipeTokenException;
use OC\Authentication\Token\INamedToken;
use OC\Authentication\Token\IProvider;
-use OC\Authentication\Token\IToken;
use OC\Authentication\Token\RemoteWipe;
use OCA\Settings\Activity\Provider;
use OCP\Activity\IManager;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http;
+use OCP\AppFramework\Http\Attribute\NoAdminRequired;
+use OCP\AppFramework\Http\Attribute\PasswordConfirmationRequired;
use OCP\AppFramework\Http\JSONResponse;
+use OCP\Authentication\Exceptions\ExpiredTokenException;
+use OCP\Authentication\Exceptions\InvalidTokenException;
+use OCP\Authentication\Exceptions\WipeTokenException;
+use OCP\Authentication\Token\IToken;
use OCP\IRequest;
use OCP\ISession;
use OCP\IUserSession;
@@ -53,31 +32,12 @@ use OCP\Session\Exceptions\SessionNotAvailableException;
use Psr\Log\LoggerInterface;
class AuthSettingsController extends Controller {
-
/** @var IProvider */
private $tokenProvider;
- /** @var ISession */
- private $session;
-
- /** IUserSession */
- private $userSession;
-
- /** @var string */
- private $uid;
-
- /** @var ISecureRandom */
- private $random;
-
- /** @var IManager */
- private $activityManager;
-
/** @var RemoteWipe */
private $remoteWipe;
- /** @var LoggerInterface */
- private $logger;
-
/**
* @param string $appName
* @param IRequest $request
@@ -90,35 +50,31 @@ class AuthSettingsController extends Controller {
* @param RemoteWipe $remoteWipe
* @param LoggerInterface $logger
*/
- public function __construct(string $appName,
- IRequest $request,
- IProvider $tokenProvider,
- ISession $session,
- ISecureRandom $random,
- ?string $userId,
- IUserSession $userSession,
- IManager $activityManager,
- RemoteWipe $remoteWipe,
- LoggerInterface $logger) {
+ public function __construct(
+ string $appName,
+ IRequest $request,
+ IProvider $tokenProvider,
+ private ISession $session,
+ private ISecureRandom $random,
+ private ?string $userId,
+ private IUserSession $userSession,
+ private IManager $activityManager,
+ RemoteWipe $remoteWipe,
+ private LoggerInterface $logger,
+ ) {
parent::__construct($appName, $request);
$this->tokenProvider = $tokenProvider;
- $this->uid = $userId;
- $this->userSession = $userSession;
- $this->session = $session;
- $this->random = $random;
- $this->activityManager = $activityManager;
$this->remoteWipe = $remoteWipe;
- $this->logger = $logger;
}
/**
- * @NoAdminRequired
* @NoSubAdminRequired
- * @PasswordConfirmationRequired
*
* @param string $name
* @return JSONResponse
*/
+ #[NoAdminRequired]
+ #[PasswordConfirmationRequired]
public function create($name) {
if ($this->checkAppToken()) {
return $this->getServiceNotAvailableResponse();
@@ -150,7 +106,7 @@ class AuthSettingsController extends Controller {
}
$token = $this->generateRandomDeviceToken();
- $deviceToken = $this->tokenProvider->generateToken($token, $this->uid, $loginName, $password, $name, IToken::PERMANENT_TOKEN);
+ $deviceToken = $this->tokenProvider->generateToken($token, $this->userId, $loginName, $password, $name, IToken::PERMANENT_TOKEN);
$tokenData = $deviceToken->jsonSerialize();
$tokenData['canDelete'] = true;
$tokenData['canRename'] = true;
@@ -193,12 +149,12 @@ class AuthSettingsController extends Controller {
}
/**
- * @NoAdminRequired
* @NoSubAdminRequired
*
* @param int $id
* @return array|JSONResponse
*/
+ #[NoAdminRequired]
public function destroy($id) {
if ($this->checkAppToken()) {
return new JSONResponse([], Http::STATUS_BAD_REQUEST);
@@ -213,13 +169,12 @@ class AuthSettingsController extends Controller {
return new JSONResponse([], Http::STATUS_NOT_FOUND);
}
- $this->tokenProvider->invalidateTokenById($this->uid, $token->getId());
+ $this->tokenProvider->invalidateTokenById($this->userId, $token->getId());
$this->publishActivity(Provider::APP_TOKEN_DELETED, $token->getId(), ['name' => $token->getName()]);
return [];
}
/**
- * @NoAdminRequired
* @NoSubAdminRequired
*
* @param int $id
@@ -227,6 +182,7 @@ class AuthSettingsController extends Controller {
* @param string $name
* @return array|JSONResponse
*/
+ #[NoAdminRequired]
public function update($id, array $scope, string $name) {
if ($this->checkAppToken()) {
return new JSONResponse([], Http::STATUS_BAD_REQUEST);
@@ -241,8 +197,8 @@ class AuthSettingsController extends Controller {
$currentName = $token->getName();
if ($scope !== $token->getScopeAsArray()) {
- $token->setScope(['filesystem' => $scope['filesystem']]);
- $this->publishActivity($scope['filesystem'] ? Provider::APP_TOKEN_FILESYSTEM_GRANTED : Provider::APP_TOKEN_FILESYSTEM_REVOKED, $token->getId(), ['name' => $currentName]);
+ $token->setScope([IToken::SCOPE_FILESYSTEM => $scope[IToken::SCOPE_FILESYSTEM]]);
+ $this->publishActivity($scope[IToken::SCOPE_FILESYSTEM] ? Provider::APP_TOKEN_FILESYSTEM_GRANTED : Provider::APP_TOKEN_FILESYSTEM_REVOKED, $token->getId(), ['name' => $currentName]);
}
if (mb_strlen($name) > 128) {
@@ -267,8 +223,8 @@ class AuthSettingsController extends Controller {
$event = $this->activityManager->generateEvent();
$event->setApp('settings')
->setType('security')
- ->setAffectedUser($this->uid)
- ->setAuthor($this->uid)
+ ->setAffectedUser($this->userId)
+ ->setAuthor($this->userId)
->setSubject($subject, $parameters)
->setObject('app_token', $id, 'App Password');
@@ -292,22 +248,23 @@ class AuthSettingsController extends Controller {
} catch (ExpiredTokenException $e) {
$token = $e->getToken();
}
- if ($token->getUID() !== $this->uid) {
- throw new InvalidTokenException('This token does not belong to you!');
+ if ($token->getUID() !== $this->userId) {
+ /** @psalm-suppress DeprecatedClass We have to throw the OC version so both OC and OCP catches catch it */
+ throw new OcInvalidTokenException('This token does not belong to you!');
}
return $token;
}
/**
- * @NoAdminRequired
* @NoSubAdminRequired
- * @PasswordConfirmationRequired
*
* @param int $id
* @return JSONResponse
* @throws InvalidTokenException
- * @throws \OC\Authentication\Exceptions\ExpiredTokenException
+ * @throws ExpiredTokenException
*/
+ #[NoAdminRequired]
+ #[PasswordConfirmationRequired]
public function wipe(int $id): JSONResponse {
if ($this->checkAppToken()) {
return new JSONResponse([], Http::STATUS_BAD_REQUEST);
diff --git a/apps/settings/lib/Controller/AuthorizedGroupController.php b/apps/settings/lib/Controller/AuthorizedGroupController.php
index dcae5b31332..82a1ca4703e 100644
--- a/apps/settings/lib/Controller/AuthorizedGroupController.php
+++ b/apps/settings/lib/Controller/AuthorizedGroupController.php
@@ -1,43 +1,26 @@
<?php
/**
- * @copyright Copyright (c) 2021 Carl Schwan <carl@carlschwan.eu>
- *
- * @author Carl Schwan <carl@carlschwan.eu>
- *
- * @license GNU AGPL version 3 or any later version
- *
- * 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 <https://www.gnu.org/licenses/>.
- *
+ * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\Settings\Controller;
use OC\Settings\AuthorizedGroup;
use OCA\Settings\Service\AuthorizedGroupService;
use OCA\Settings\Service\NotFoundException;
+use OCP\AppFramework\Controller;
+use OCP\AppFramework\Http\DataResponse;
use OCP\DB\Exception;
use OCP\IRequest;
-use OCP\AppFramework\Http\DataResponse;
-use OCP\AppFramework\Controller;
class AuthorizedGroupController extends Controller {
- /** @var AuthorizedGroupService $authorizedGroupService */
- private $authorizedGroupService;
-
- public function __construct(string $AppName, IRequest $request, AuthorizedGroupService $authorizedGroupService) {
+ public function __construct(
+ string $AppName,
+ IRequest $request,
+ private AuthorizedGroupService $authorizedGroupService,
+ ) {
parent::__construct($AppName, $request);
- $this->authorizedGroupService = $authorizedGroupService;
}
/**
@@ -74,7 +57,7 @@ class AuthorizedGroupController extends Controller {
$this->authorizedGroupService->create($groupData['gid'], $class);
}
}
-
+
return new DataResponse(['valid' => true]);
}
}
diff --git a/apps/settings/lib/Controller/ChangePasswordController.php b/apps/settings/lib/Controller/ChangePasswordController.php
index 20ec28220a5..a874a47c16a 100644
--- a/apps/settings/lib/Controller/ChangePasswordController.php
+++ b/apps/settings/lib/Controller/ChangePasswordController.php
@@ -1,33 +1,8 @@
<?php
+
/**
- * @copyright Copyright (c) 2016 Roeland Jago Douma <roeland@famdouma.nl>
- *
- * @author Arthur Schiwon <blizzz@arthur-schiwon.de>
- * @author Christoph Wurst <christoph@winzerhof-wurst.at>
- * @author Daniel Calviño Sánchez <danxuliu@gmail.com>
- * @author Daniel Kesselberg <mail@danielkesselberg.de>
- * @author Joas Schilling <coding@schilljs.com>
- * @author Julius Härtl <jus@bitgrid.net>
- * @author Lukas Reschke <lukas@statuscode.ch>
- * @author Matthew Setter <matthew@matthewsetter.com>
- * @author Morris Jobke <hey@morrisjobke.de>
- * @author Roeland Jago Douma <roeland@famdouma.nl>
- *
- * @license GNU AGPL version 3 or any later version
- *
- * 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/>.
- *
+ * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
// FIXME: disabled for now to be able to inject IGroupManager and also use
// getSubAdmin()
@@ -37,49 +12,45 @@ namespace OCA\Settings\Controller;
use OC\Group\Manager as GroupManager;
use OC\User\Session;
+use OCA\Encryption\KeyManager;
+use OCA\Encryption\Recovery;
use OCP\App\IAppManager;
use OCP\AppFramework\Controller;
+use OCP\AppFramework\Http\Attribute\BruteForceProtection;
+use OCP\AppFramework\Http\Attribute\NoAdminRequired;
+use OCP\AppFramework\Http\Attribute\PasswordConfirmationRequired;
use OCP\AppFramework\Http\JSONResponse;
use OCP\HintException;
-use OCP\IGroupManager;
use OCP\IL10N;
use OCP\IRequest;
use OCP\IUser;
use OCP\IUserManager;
use OCP\IUserSession;
+use OCP\Server;
class ChangePasswordController extends Controller {
- private ?string $userId;
- private IUserManager $userManager;
- private IL10N $l;
- private GroupManager $groupManager;
private Session $userSession;
- private IAppManager $appManager;
- public function __construct(string $appName,
- IRequest $request,
- ?string $userId,
- IUserManager $userManager,
- IUserSession $userSession,
- IGroupManager $groupManager,
- IAppManager $appManager,
- IL10N $l) {
+ public function __construct(
+ string $appName,
+ IRequest $request,
+ private ?string $userId,
+ private IUserManager $userManager,
+ IUserSession $userSession,
+ private GroupManager $groupManager,
+ private IAppManager $appManager,
+ private IL10N $l,
+ ) {
parent::__construct($appName, $request);
-
- $this->userId = $userId;
- $this->userManager = $userManager;
$this->userSession = $userSession;
- $this->groupManager = $groupManager;
- $this->appManager = $appManager;
- $this->l = $l;
}
/**
- * @NoAdminRequired
* @NoSubAdminRequired
- * @BruteForceProtection(action=changePersonalPassword)
*/
- public function changePersonalPassword(string $oldpassword = '', string $newpassword = null): JSONResponse {
+ #[NoAdminRequired]
+ #[BruteForceProtection(action: 'changePersonalPassword')]
+ public function changePersonalPassword(string $oldpassword = '', ?string $newpassword = null): JSONResponse {
$loginName = $this->userSession->getLoginName();
/** @var IUser $user */
$user = $this->userManager->checkPassword($loginName, $oldpassword);
@@ -123,16 +94,14 @@ class ChangePasswordController extends Controller {
]);
}
- /**
- * @NoAdminRequired
- * @PasswordConfirmationRequired
- */
- public function changeUserPassword(string $username = null, string $password = null, string $recoveryPassword = null): JSONResponse {
+ #[NoAdminRequired]
+ #[PasswordConfirmationRequired]
+ public function changeUserPassword(?string $username = null, ?string $password = null, ?string $recoveryPassword = null): JSONResponse {
if ($username === null) {
return new JSONResponse([
'status' => 'error',
'data' => [
- 'message' => $this->l->t('No user supplied'),
+ 'message' => $this->l->t('No Login supplied'),
],
]);
}
@@ -157,9 +126,9 @@ class ChangePasswordController extends Controller {
$currentUser = $this->userSession->getUser();
$targetUser = $this->userManager->get($username);
- if ($currentUser === null || $targetUser === null ||
- !($this->groupManager->isAdmin($this->userId) ||
- $this->groupManager->getSubAdmin()->isUserAccessible($currentUser, $targetUser))
+ if ($currentUser === null || $targetUser === null
+ || !($this->groupManager->isAdmin($this->userId)
+ || $this->groupManager->getSubAdmin()->isUserAccessible($currentUser, $targetUser))
) {
return new JSONResponse([
'status' => 'error',
@@ -171,36 +140,8 @@ class ChangePasswordController extends Controller {
if ($this->appManager->isEnabledForUser('encryption')) {
//handle the recovery case
- $crypt = new \OCA\Encryption\Crypto\Crypt(
- \OC::$server->getLogger(),
- \OC::$server->getUserSession(),
- \OC::$server->getConfig(),
- \OC::$server->getL10N('encryption'));
- $keyStorage = \OC::$server->getEncryptionKeyStorage();
- $util = new \OCA\Encryption\Util(
- new \OC\Files\View(),
- $crypt,
- \OC::$server->getLogger(),
- \OC::$server->getUserSession(),
- \OC::$server->getConfig(),
- \OC::$server->getUserManager());
- $keyManager = new \OCA\Encryption\KeyManager(
- $keyStorage,
- $crypt,
- \OC::$server->getConfig(),
- \OC::$server->getUserSession(),
- new \OCA\Encryption\Session(\OC::$server->getSession()),
- \OC::$server->getLogger(),
- $util,
- \OC::$server->getLockingProvider()
- );
- $recovery = new \OCA\Encryption\Recovery(
- \OC::$server->getUserSession(),
- $crypt,
- $keyManager,
- \OC::$server->getConfig(),
- \OC::$server->getEncryptionFilesHelper(),
- new \OC\Files\View());
+ $keyManager = Server::get(KeyManager::class);
+ $recovery = Server::get(Recovery::class);
$recoveryAdminEnabled = $recovery->isRecoveryKeyEnabled();
$validRecoveryPassword = false;
@@ -214,7 +155,7 @@ class ChangePasswordController extends Controller {
return new JSONResponse([
'status' => 'error',
'data' => [
- 'message' => $this->l->t('Please provide an admin recovery password; otherwise, all user data will be lost.'),
+ 'message' => $this->l->t('Please provide an admin recovery password; otherwise, all account data will be lost.'),
]
]);
} elseif ($recoveryEnabledForUser && ! $validRecoveryPassword) {
@@ -240,7 +181,7 @@ class ChangePasswordController extends Controller {
return new JSONResponse([
'status' => 'error',
'data' => [
- 'message' => $this->l->t('Backend does not support password change, but the user\'s encryption key was updated.'),
+ 'message' => $this->l->t('Backend does not support password change, but the encryption of the account key was updated.'),
]
]);
} elseif (!$result && !$recoveryEnabledForUser) {
diff --git a/apps/settings/lib/Controller/CheckSetupController.php b/apps/settings/lib/Controller/CheckSetupController.php
index a5c158d2602..2a189a37ce6 100644
--- a/apps/settings/lib/Controller/CheckSetupController.php
+++ b/apps/settings/lib/Controller/CheckSetupController.php
@@ -1,408 +1,63 @@
<?php
+
/**
- * @copyright Copyright (c) 2016, ownCloud, Inc.
- *
- * @author Bjoern Schiessle <bjoern@schiessle.org>
- * @author Christoph Wurst <christoph@winzerhof-wurst.at>
- * @author Cthulhux <git@tuxproject.de>
- * @author Daniel Kesselberg <mail@danielkesselberg.de>
- * @author Derek <derek.kelly27@gmail.com>
- * @author Georg Ehrke <oc.list@georgehrke.com>
- * @author J0WI <J0WI@users.noreply.github.com>
- * @author Joas Schilling <coding@schilljs.com>
- * @author Julius Härtl <jus@bitgrid.net>
- * @author Ko- <k.stoffelen@cs.ru.nl>
- * @author Lauris Binde <laurisb@users.noreply.github.com>
- * @author Lukas Reschke <lukas@statuscode.ch>
- * @author Michael Weimann <mail@michael-weimann.eu>
- * @author Morris Jobke <hey@morrisjobke.de>
- * @author nhirokinet <nhirokinet@nhiroki.net>
- * @author Robin Appelman <robin@icewind.nl>
- * @author Robin McCorkell <robin@mccorkell.me.uk>
- * @author Roeland Jago Douma <roeland@famdouma.nl>
- * @author Sven Strickroth <email@cs-ware.de>
- * @author Sylvia van Os <sylvia@hackerchick.me>
- * @author timm2k <timm2k@gmx.de>
- * @author Timo Förster <tfoerster@webfoersterei.de>
- * @author Valdnet <47037905+Valdnet@users.noreply.github.com>
- * @author MichaIng <micha@dietpi.com>
- *
- * @license AGPL-3.0
- *
- * This code is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * 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, version 3,
- * along with this program. If not, see <http://www.gnu.org/licenses/>
- *
+ * SPDX-FileCopyrightText: 2019-2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
+ * SPDX-License-Identifier: AGPL-3.0-only
*/
namespace OCA\Settings\Controller;
-use bantu\IniGetWrapper\IniGetWrapper;
-use DirectoryIterator;
-use Doctrine\DBAL\Exception;
-use Doctrine\DBAL\Platforms\SqlitePlatform;
-use Doctrine\DBAL\TransactionIsolationLevel;
-use GuzzleHttp\Exception\ClientException;
-use OC;
use OC\AppFramework\Http;
-use OC\DB\Connection;
-use OC\DB\MissingColumnInformation;
-use OC\DB\MissingIndexInformation;
-use OC\DB\MissingPrimaryKeyInformation;
-use OC\DB\SchemaWrapper;
use OC\IntegrityCheck\Checker;
-use OC\Lock\NoopLockingProvider;
-use OC\MemoryInfo;
-use OCA\Settings\SetupChecks\CheckUserCertificates;
-use OCA\Settings\SetupChecks\LdapInvalidUuids;
-use OCA\Settings\SetupChecks\LegacySSEKeyFormat;
-use OCA\Settings\SetupChecks\PhpDefaultCharset;
-use OCA\Settings\SetupChecks\PhpOutputBuffering;
-use OCA\Settings\SetupChecks\SupportedDatabase;
-use OCP\App\IAppManager;
+use OCA\Settings\Settings\Admin\Overview;
use OCP\AppFramework\Controller;
+use OCP\AppFramework\Http\Attribute\AuthorizedAdminSetting;
+use OCP\AppFramework\Http\Attribute\NoAdminRequired;
+use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
+use OCP\AppFramework\Http\Attribute\OpenAPI;
use OCP\AppFramework\Http\DataDisplayResponse;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\Http\RedirectResponse;
-use OCP\DB\Types;
-use OCP\Http\Client\IClientService;
use OCP\IConfig;
-use OCP\IDateTimeFormatter;
-use OCP\IDBConnection;
use OCP\IL10N;
use OCP\IRequest;
-use OCP\IServerContainer;
-use OCP\ITempManager;
use OCP\IURLGenerator;
-use OCP\Lock\ILockingProvider;
-use OCP\Notification\IManager;
-use OCP\Security\ISecureRandom;
+use OCP\SetupCheck\ISetupCheckManager;
use Psr\Log\LoggerInterface;
-use Symfony\Component\EventDispatcher\EventDispatcherInterface;
-use Symfony\Component\EventDispatcher\GenericEvent;
+#[OpenAPI(scope: OpenAPI::SCOPE_IGNORE)]
class CheckSetupController extends Controller {
- /** @var IConfig */
- private $config;
- /** @var IClientService */
- private $clientService;
- /** @var IURLGenerator */
- private $urlGenerator;
- /** @var IL10N */
- private $l10n;
/** @var Checker */
private $checker;
- /** @var LoggerInterface */
- private $logger;
- /** @var EventDispatcherInterface */
- private $dispatcher;
- /** @var Connection */
- private $db;
- /** @var ILockingProvider */
- private $lockingProvider;
- /** @var IDateTimeFormatter */
- private $dateTimeFormatter;
- /** @var MemoryInfo */
- private $memoryInfo;
- /** @var ISecureRandom */
- private $secureRandom;
- /** @var IniGetWrapper */
- private $iniGetWrapper;
- /** @var IDBConnection */
- private $connection;
- /** @var ITempManager */
- private $tempManager;
- /** @var IManager */
- private $manager;
- /** @var IAppManager */
- private $appManager;
- /** @var IServerContainer */
- private $serverContainer;
- public function __construct($AppName,
- IRequest $request,
- IConfig $config,
- IClientService $clientService,
- IURLGenerator $urlGenerator,
- IL10N $l10n,
- Checker $checker,
- LoggerInterface $logger,
- EventDispatcherInterface $dispatcher,
- Connection $db,
- ILockingProvider $lockingProvider,
- IDateTimeFormatter $dateTimeFormatter,
- MemoryInfo $memoryInfo,
- ISecureRandom $secureRandom,
- IniGetWrapper $iniGetWrapper,
- IDBConnection $connection,
- ITempManager $tempManager,
- IManager $manager,
- IAppManager $appManager,
- IServerContainer $serverContainer
+ public function __construct(
+ $AppName,
+ IRequest $request,
+ private IConfig $config,
+ private IURLGenerator $urlGenerator,
+ private IL10N $l10n,
+ Checker $checker,
+ private LoggerInterface $logger,
+ private ISetupCheckManager $setupCheckManager,
) {
parent::__construct($AppName, $request);
- $this->config = $config;
- $this->clientService = $clientService;
- $this->urlGenerator = $urlGenerator;
- $this->l10n = $l10n;
$this->checker = $checker;
- $this->logger = $logger;
- $this->dispatcher = $dispatcher;
- $this->db = $db;
- $this->lockingProvider = $lockingProvider;
- $this->dateTimeFormatter = $dateTimeFormatter;
- $this->memoryInfo = $memoryInfo;
- $this->secureRandom = $secureRandom;
- $this->iniGetWrapper = $iniGetWrapper;
- $this->connection = $connection;
- $this->tempManager = $tempManager;
- $this->manager = $manager;
- $this->appManager = $appManager;
- $this->serverContainer = $serverContainer;
- }
-
- /**
- * Check if is fair use of free push service
- * @return bool
- */
- private function isFairUseOfFreePushService(): bool {
- return $this->manager->isFairUseOfFreePushService();
- }
-
- /**
- * Checks if the server can connect to the internet using HTTPS and HTTP
- * @return bool
- */
- private function hasInternetConnectivityProblems(): bool {
- if ($this->config->getSystemValue('has_internet_connection', true) === false) {
- return false;
- }
-
- $siteArray = $this->config->getSystemValue('connectivity_check_domains', [
- 'www.nextcloud.com', 'www.startpage.com', 'www.eff.org', 'www.edri.org'
- ]);
-
- foreach ($siteArray as $site) {
- if ($this->isSiteReachable($site)) {
- return false;
- }
- }
- return true;
- }
-
- /**
- * Checks if the Nextcloud server can connect to a specific URL
- * @param string $site site domain or full URL with http/https protocol
- * @return bool
- */
- private function isSiteReachable(string $site): bool {
- try {
- $client = $this->clientService->newClient();
- // if there is no protocol, test http:// AND https://
- if (preg_match('/^https?:\/\//', $site) !== 1) {
- $httpSite = 'http://' . $site . '/';
- $client->get($httpSite);
- $httpsSite = 'https://' . $site . '/';
- $client->get($httpsSite);
- } else {
- $client->get($site);
- }
- } catch (\Exception $e) {
- $this->logger->error('Cannot connect to: ' . $site, [
- 'app' => 'internet_connection_check',
- 'exception' => $e,
- ]);
- return false;
- }
- return true;
- }
-
- /**
- * Checks whether a local memcache is installed or not
- * @return bool
- */
- private function isMemcacheConfigured() {
- return $this->config->getSystemValue('memcache.local', null) !== null;
- }
-
- /**
- * Whether PHP can generate "secure" pseudorandom integers
- *
- * @return bool
- */
- private function isRandomnessSecure() {
- try {
- $this->secureRandom->generate(1);
- } catch (\Exception $ex) {
- return false;
- }
- return true;
- }
-
- /**
- * Public for the sake of unit-testing
- *
- * @return array
- */
- protected function getCurlVersion() {
- return curl_version();
- }
-
- /**
- * Check if the used SSL lib is outdated. Older OpenSSL and NSS versions do
- * have multiple bugs which likely lead to problems in combination with
- * functionality required by ownCloud such as SNI.
- *
- * @link https://github.com/owncloud/core/issues/17446#issuecomment-122877546
- * @link https://bugzilla.redhat.com/show_bug.cgi?id=1241172
- * @return string
- */
- private function isUsedTlsLibOutdated() {
- // Don't run check when:
- // 1. Server has `has_internet_connection` set to false
- // 2. AppStore AND S2S is disabled
- if (!$this->config->getSystemValue('has_internet_connection', true)) {
- return '';
- }
- if (!$this->config->getSystemValue('appstoreenabled', true)
- && $this->config->getAppValue('files_sharing', 'outgoing_server2server_share_enabled', 'yes') === 'no'
- && $this->config->getAppValue('files_sharing', 'incoming_server2server_share_enabled', 'yes') === 'no') {
- return '';
- }
-
- $versionString = $this->getCurlVersion();
- if (isset($versionString['ssl_version'])) {
- $versionString = $versionString['ssl_version'];
- } else {
- return '';
- }
-
- $features = $this->l10n->t('installing and updating apps via the App Store or Federated Cloud Sharing');
- if (!$this->config->getSystemValue('appstoreenabled', true)) {
- $features = $this->l10n->t('Federated Cloud Sharing');
- }
-
- // Check if at least OpenSSL after 1.01d or 1.0.2b
- if (strpos($versionString, 'OpenSSL/') === 0) {
- $majorVersion = substr($versionString, 8, 5);
- $patchRelease = substr($versionString, 13, 6);
-
- if (($majorVersion === '1.0.1' && ord($patchRelease) < ord('d')) ||
- ($majorVersion === '1.0.2' && ord($patchRelease) < ord('b'))) {
- return $this->l10n->t('cURL is using an outdated %1$s version (%2$s). Please update your operating system or features such as %3$s will not work reliably.', ['OpenSSL', $versionString, $features]);
- }
- }
-
- // Check if NSS and perform heuristic check
- if (strpos($versionString, 'NSS/') === 0) {
- try {
- $firstClient = $this->clientService->newClient();
- $firstClient->get('https://nextcloud.com/');
-
- $secondClient = $this->clientService->newClient();
- $secondClient->get('https://nextcloud.com/');
- } catch (ClientException $e) {
- if ($e->getResponse()->getStatusCode() === 400) {
- return $this->l10n->t('cURL is using an outdated %1$s version (%2$s). Please update your operating system or features such as %3$s will not work reliably.', ['NSS', $versionString, $features]);
- }
- } catch (\Exception $e) {
- $this->logger->warning('error checking curl', [
- 'app' => 'settings',
- 'exception' => $e,
- ]);
- return $this->l10n->t('Could not determine if TLS version of cURL is outdated or not because an error happened during the HTTPS request against https://nextcloud.com. Please check the Nextcloud log file for more details.');
- }
- }
-
- return '';
- }
-
- /**
- * Whether the version is outdated
- *
- * @return bool
- */
- protected function isPhpOutdated(): bool {
- return PHP_VERSION_ID < 80000;
}
/**
- * Whether the php version is still supported (at time of release)
- * according to: https://www.php.net/supported-versions.php
- *
- * @return array
- */
- private function isPhpSupported(): array {
- return ['eol' => $this->isPhpOutdated(), 'version' => PHP_VERSION];
- }
-
- /**
- * Check if the reverse proxy configuration is working as expected
- *
- * @return bool
- */
- private function forwardedForHeadersWorking(): bool {
- $trustedProxies = $this->config->getSystemValue('trusted_proxies', []);
- $remoteAddress = $this->request->getHeader('REMOTE_ADDR');
-
- if (empty($trustedProxies) && $this->request->getHeader('X-Forwarded-Host') !== '') {
- return false;
- }
-
- if (\is_array($trustedProxies)) {
- if (\in_array($remoteAddress, $trustedProxies, true) && $remoteAddress !== '127.0.0.1') {
- return $remoteAddress !== $this->request->getRemoteAddress();
- }
- } else {
- return false;
- }
-
- // either not enabled or working correctly
- return true;
- }
-
- /**
- * Checks if the correct memcache module for PHP is installed. Only
- * fails if memcached is configured and the working module is not installed.
- *
- * @return bool
- */
- private function isCorrectMemcachedPHPModuleInstalled() {
- if ($this->config->getSystemValue('memcache.distributed', null) !== '\OC\Memcache\Memcached') {
- return true;
- }
-
- // there are two different memcache modules for PHP
- // we only support memcached and not memcache
- // https://code.google.com/p/memcached/wiki/PHPClientComparison
- return !(!extension_loaded('memcached') && extension_loaded('memcache'));
- }
-
- /**
- * Checks if set_time_limit is not disabled.
- *
- * @return bool
+ * @return DataResponse
*/
- private function isSettimelimitAvailable() {
- if (function_exists('set_time_limit')
- && strpos(ini_get('disable_functions'), 'set_time_limit') === false) {
- return true;
- }
-
- return false;
+ #[NoCSRFRequired]
+ #[NoAdminRequired]
+ public function setupCheckManager(): DataResponse {
+ return new DataResponse($this->setupCheckManager->runAll());
}
/**
* @return RedirectResponse
- * @AuthorizedAdminSetting(settings=OCA\Settings\Settings\Admin\Overview)
*/
+ #[NoCSRFRequired]
+ #[AuthorizedAdminSetting(settings: Overview::class)]
public function rescanFailedIntegrityCheck(): RedirectResponse {
$this->checker->runInstanceVerification();
return new RedirectResponse(
@@ -410,10 +65,8 @@ class CheckSetupController extends Controller {
);
}
- /**
- * @NoCSRFRequired
- * @AuthorizedAdminSetting(settings=OCA\Settings\Settings\Admin\Overview)
- */
+ #[NoCSRFRequired]
+ #[AuthorizedAdminSetting(settings: Overview::class)]
public function getFailedIntegrityCheckFiles(): DataDisplayResponse {
if (!$this->checker->isCodeCheckEnforced()) {
return new DataDisplayResponse('Integrity checker has been disabled. Integrity cannot be verified.');
@@ -421,6 +74,10 @@ class CheckSetupController extends Controller {
$completeResults = $this->checker->getResults();
+ if ($completeResults === null) {
+ return new DataDisplayResponse('Integrity checker has not been run. Integrity information not available.');
+ }
+
if (!empty($completeResults)) {
$formattedTextResponse = 'Technical information
=====================
@@ -468,448 +125,13 @@ Raw output
}
/**
- * Checks whether a PHP OPcache is properly set up
- * @return string[] The list of OPcache setup recommendations
- */
- protected function getOpcacheSetupRecommendations(): array {
- // If the module is not loaded, return directly to skip inapplicable checks
- if (!extension_loaded('Zend OPcache')) {
- return [$this->l10n->t('The PHP OPcache module is not loaded. For better performance it is recommended to load it into your PHP installation.')];
- }
-
- $recommendations = [];
-
- // Check whether Nextcloud is allowed to use the OPcache API
- $isPermitted = true;
- $permittedPath = $this->iniGetWrapper->getString('opcache.restrict_api');
- if (isset($permittedPath) && $permittedPath !== '' && !str_starts_with(\OC::$SERVERROOT, rtrim($permittedPath, '/'))) {
- $isPermitted = false;
- }
-
- if (!$this->iniGetWrapper->getBool('opcache.enable')) {
- $recommendations[] = $this->l10n->t('OPcache is disabled. For better performance, it is recommended to apply <code>opcache.enable=1</code> to your PHP configuration.');
-
- // Check for saved comments only when OPcache is currently disabled. If it was enabled, opcache.save_comments=0 would break Nextcloud in the first place.
- if (!$this->iniGetWrapper->getBool('opcache.save_comments')) {
- $recommendations[] = $this->l10n->t('OPcache is configured to remove code comments. With OPcache enabled, <code>opcache.save_comments=1</code> must be set for Nextcloud to function.');
- }
-
- if (!$isPermitted) {
- $recommendations[] = $this->l10n->t('Nextcloud is not allowed to use the OPcache API. With OPcache enabled, it is highly recommended to include all Nextcloud directories with <code>opcache.restrict_api</code> or unset this setting to disable OPcache API restrictions, to prevent errors during Nextcloud core or app upgrades.');
- }
- } elseif (!$isPermitted) {
- $recommendations[] = $this->l10n->t('Nextcloud is not allowed to use the OPcache API. It is highly recommended to include all Nextcloud directories with <code>opcache.restrict_api</code> or unset this setting to disable OPcache API restrictions, to prevent errors during Nextcloud core or app upgrades.');
- } else {
- // Check whether opcache_get_status has been explicitly disabled an in case skip usage based checks
- $disabledFunctions = $this->iniGetWrapper->getString('disable_functions');
- if (isset($disabledFunctions) && str_contains($disabledFunctions, 'opcache_get_status')) {
- return [];
- }
-
- $status = opcache_get_status(false);
-
- // Recommend to raise value, if more than 90% of max value is reached
- if (
- empty($status['opcache_statistics']['max_cached_keys']) ||
- ($status['opcache_statistics']['num_cached_keys'] / $status['opcache_statistics']['max_cached_keys'] > 0.9)
- ) {
- $recommendations[] = $this->l10n->t('The maximum number of OPcache keys is nearly exceeded. To assure that all scripts can be kept in the cache, it is recommended to apply <code>opcache.max_accelerated_files</code> to your PHP configuration with a value higher than <code>%s</code>.', [($this->iniGetWrapper->getNumeric('opcache.max_accelerated_files') ?: 'currently')]);
- }
-
- if (
- empty($status['memory_usage']['free_memory']) ||
- ($status['memory_usage']['used_memory'] / $status['memory_usage']['free_memory'] > 9)
- ) {
- $recommendations[] = $this->l10n->t('The OPcache buffer is nearly full. To assure that all scripts can be hold in cache, it is recommended to apply <code>opcache.memory_consumption</code> to your PHP configuration with a value higher than <code>%s</code>.', [($this->iniGetWrapper->getNumeric('opcache.memory_consumption') ?: 'currently')]);
- }
-
- if (
- // Do not recommend to raise the interned strings buffer size above a quarter of the total OPcache size
- ($this->iniGetWrapper->getNumeric('opcache.interned_strings_buffer') < $this->iniGetWrapper->getNumeric('opcache.memory_consumption') / 4) &&
- (
- empty($status['interned_strings_usage']['free_memory']) ||
- ($status['interned_strings_usage']['used_memory'] / $status['interned_strings_usage']['free_memory'] > 9)
- )
- ) {
- $recommendations[] = $this->l10n->t('The OPcache interned strings buffer is nearly full. To assure that repeating strings can be effectively cached, it is recommended to apply <code>opcache.interned_strings_buffer</code> to your PHP configuration with a value higher than <code>%s</code>.', [($this->iniGetWrapper->getNumeric('opcache.interned_strings_buffer') ?: 'currently')]);
- }
- }
-
- return $recommendations;
- }
-
- /**
- * Check if the required FreeType functions are present
- * @return bool
- */
- protected function hasFreeTypeSupport() {
- return function_exists('imagettfbbox') && function_exists('imagettftext');
- }
-
- protected function hasMissingIndexes(): array {
- $indexInfo = new MissingIndexInformation();
- // Dispatch event so apps can also hint for pending index updates if needed
- $event = new GenericEvent($indexInfo);
- $this->dispatcher->dispatch(IDBConnection::CHECK_MISSING_INDEXES_EVENT, $event);
-
- return $indexInfo->getListOfMissingIndexes();
- }
-
- protected function hasMissingPrimaryKeys(): array {
- $info = new MissingPrimaryKeyInformation();
- // Dispatch event so apps can also hint for pending index updates if needed
- $event = new GenericEvent($info);
- $this->dispatcher->dispatch(IDBConnection::CHECK_MISSING_PRIMARY_KEYS_EVENT, $event);
-
- return $info->getListOfMissingPrimaryKeys();
- }
-
- protected function hasMissingColumns(): array {
- $indexInfo = new MissingColumnInformation();
- // Dispatch event so apps can also hint for pending index updates if needed
- $event = new GenericEvent($indexInfo);
- $this->dispatcher->dispatch(IDBConnection::CHECK_MISSING_COLUMNS_EVENT, $event);
-
- return $indexInfo->getListOfMissingColumns();
- }
-
- protected function isSqliteUsed() {
- return strpos($this->config->getSystemValue('dbtype'), 'sqlite') !== false;
- }
-
- protected function isReadOnlyConfig(): bool {
- return \OC_Helper::isReadOnlyConfigEnabled();
- }
-
- protected function wasEmailTestSuccessful(): bool {
- // Handle the case that the configuration was set before the check was introduced or it was only set via command line and not from the UI
- if ($this->config->getAppValue('core', 'emailTestSuccessful', '') === '' && $this->config->getSystemValue('mail_domain', '') === '') {
- return false;
- }
-
- // The mail test was unsuccessful or the config was changed using the UI without verifying with a testmail, hence return false
- if ($this->config->getAppValue('core', 'emailTestSuccessful', '') === '0') {
- return false;
- }
-
- return true;
- }
-
- protected function hasValidTransactionIsolationLevel(): bool {
- try {
- if ($this->db->getDatabasePlatform() instanceof SqlitePlatform) {
- return true;
- }
-
- return $this->db->getTransactionIsolation() === TransactionIsolationLevel::READ_COMMITTED;
- } catch (Exception $e) {
- // ignore
- }
-
- return true;
- }
-
- protected function hasFileinfoInstalled(): bool {
- return \OC_Util::fileInfoLoaded();
- }
-
- protected function hasWorkingFileLocking(): bool {
- return !($this->lockingProvider instanceof NoopLockingProvider);
- }
-
- protected function getSuggestedOverwriteCliURL(): string {
- $currentOverwriteCliUrl = $this->config->getSystemValue('overwrite.cli.url', '');
- $suggestedOverwriteCliUrl = $this->request->getServerProtocol() . '://' . $this->request->getInsecureServerHost() . \OC::$WEBROOT;
-
- // Check correctness by checking if it is a valid URL
- if (filter_var($currentOverwriteCliUrl, FILTER_VALIDATE_URL)) {
- $suggestedOverwriteCliUrl = '';
- }
-
- return $suggestedOverwriteCliUrl;
- }
-
- protected function getLastCronInfo(): array {
- $lastCronRun = $this->config->getAppValue('core', 'lastcron', 0);
- return [
- 'diffInSeconds' => time() - $lastCronRun,
- 'relativeTime' => $this->dateTimeFormatter->formatTimeSpan($lastCronRun),
- 'backgroundJobsUrl' => $this->urlGenerator->linkToRoute('settings.AdminSettings.index', ['section' => 'server']) . '#backgroundjobs',
- ];
- }
-
- protected function getCronErrors() {
- $errors = json_decode($this->config->getAppValue('core', 'cronErrors', ''), true);
-
- if (is_array($errors)) {
- return $errors;
- }
-
- return [];
- }
-
- private function isTemporaryDirectoryWritable(): bool {
- try {
- if (!empty($this->tempManager->getTempBaseDir())) {
- return true;
- }
- } catch (\Exception $e) {
- }
- return false;
- }
-
- /**
- * Iterates through the configured app roots and
- * tests if the subdirectories are owned by the same user than the current user.
- *
- * @return array
- */
- protected function getAppDirsWithDifferentOwner(): array {
- $currentUser = posix_getuid();
- $appDirsWithDifferentOwner = [[]];
-
- foreach (OC::$APPSROOTS as $appRoot) {
- if ($appRoot['writable'] === true) {
- $appDirsWithDifferentOwner[] = $this->getAppDirsWithDifferentOwnerForAppRoot($currentUser, $appRoot);
- }
- }
-
- $appDirsWithDifferentOwner = array_merge(...$appDirsWithDifferentOwner);
- sort($appDirsWithDifferentOwner);
-
- return $appDirsWithDifferentOwner;
- }
-
- /**
- * Tests if the directories for one apps directory are writable by the current user.
- *
- * @param int $currentUser The current user
- * @param array $appRoot The app root config
- * @return string[] The none writable directory paths inside the app root
- */
- private function getAppDirsWithDifferentOwnerForAppRoot(int $currentUser, array $appRoot): array {
- $appDirsWithDifferentOwner = [];
- $appsPath = $appRoot['path'];
- $appsDir = new DirectoryIterator($appRoot['path']);
-
- foreach ($appsDir as $fileInfo) {
- if ($fileInfo->isDir() && !$fileInfo->isDot()) {
- $absAppPath = $appsPath . DIRECTORY_SEPARATOR . $fileInfo->getFilename();
- $appDirUser = fileowner($absAppPath);
- if ($appDirUser !== $currentUser) {
- $appDirsWithDifferentOwner[] = $absAppPath;
- }
- }
- }
-
- return $appDirsWithDifferentOwner;
- }
-
- /**
- * Checks for potential PHP modules that would improve the instance
- *
- * @return string[] A list of PHP modules that is recommended
- */
- protected function hasRecommendedPHPModules(): array {
- $recommendedPHPModules = [];
-
- if (!extension_loaded('intl')) {
- $recommendedPHPModules[] = 'intl';
- }
-
- if (!extension_loaded('sysvsem')) {
- // used to limit the usage of resources by preview generator
- $recommendedPHPModules[] = 'sysvsem';
- }
-
- if (!defined('PASSWORD_ARGON2I')) {
- // Installing php-sodium on >=php7.4 will provide PASSWORD_ARGON2I
- // on previous version argon2 wasn't part of the "standard" extension
- // and RedHat disabled it so even installing php-sodium won't provide argon2i
- // support in password_hash/password_verify.
- $recommendedPHPModules[] = 'sodium';
- }
-
- return $recommendedPHPModules;
- }
-
- protected function isImagickEnabled(): bool {
- if ($this->config->getAppValue('theming', 'enabled', 'no') === 'yes') {
- if (!extension_loaded('imagick')) {
- return false;
- }
- }
- return true;
- }
-
- protected function areWebauthnExtensionsEnabled(): bool {
- if (!extension_loaded('bcmath')) {
- return false;
- }
- if (!extension_loaded('gmp')) {
- return false;
- }
- return true;
- }
-
- protected function is64bit(): bool {
- if (PHP_INT_SIZE < 8) {
- return false;
- } else {
- return true;
- }
- }
-
- protected function isMysqlUsedWithoutUTF8MB4(): bool {
- return ($this->config->getSystemValue('dbtype', 'sqlite') === 'mysql') && ($this->config->getSystemValue('mysql.utf8mb4', false) === false);
- }
-
- protected function hasBigIntConversionPendingColumns(): array {
- // copy of ConvertFilecacheBigInt::getColumnsByTable()
- $tables = [
- 'activity' => ['activity_id', 'object_id'],
- 'activity_mq' => ['mail_id'],
- 'authtoken' => ['id'],
- 'bruteforce_attempts' => ['id'],
- 'federated_reshares' => ['share_id'],
- 'filecache' => ['fileid', 'storage', 'parent', 'mimetype', 'mimepart', 'mtime', 'storage_mtime'],
- 'filecache_extended' => ['fileid'],
- 'files_trash' => ['auto_id'],
- 'file_locks' => ['id'],
- 'file_metadata' => ['id'],
- 'jobs' => ['id'],
- 'mimetypes' => ['id'],
- 'mounts' => ['id', 'storage_id', 'root_id', 'mount_id'],
- 'share_external' => ['id', 'parent'],
- 'storages' => ['numeric_id'],
- ];
-
- $schema = new SchemaWrapper($this->db);
- $isSqlite = $this->db->getDatabasePlatform() instanceof SqlitePlatform;
- $pendingColumns = [];
-
- foreach ($tables as $tableName => $columns) {
- if (!$schema->hasTable($tableName)) {
- continue;
- }
-
- $table = $schema->getTable($tableName);
- foreach ($columns as $columnName) {
- $column = $table->getColumn($columnName);
- $isAutoIncrement = $column->getAutoincrement();
- $isAutoIncrementOnSqlite = $isSqlite && $isAutoIncrement;
- if ($column->getType()->getName() !== Types::BIGINT && !$isAutoIncrementOnSqlite) {
- $pendingColumns[] = $tableName . '.' . $columnName;
- }
- }
- }
-
- return $pendingColumns;
- }
-
- protected function isEnoughTempSpaceAvailableIfS3PrimaryStorageIsUsed(): bool {
- $objectStore = $this->config->getSystemValue('objectstore', null);
- $objectStoreMultibucket = $this->config->getSystemValue('objectstore_multibucket', null);
-
- if (!isset($objectStoreMultibucket) && !isset($objectStore)) {
- return true;
- }
-
- if (isset($objectStoreMultibucket['class']) && $objectStoreMultibucket['class'] !== 'OC\\Files\\ObjectStore\\S3') {
- return true;
- }
-
- if (isset($objectStore['class']) && $objectStore['class'] !== 'OC\\Files\\ObjectStore\\S3') {
- return true;
- }
-
- $tempPath = sys_get_temp_dir();
- if (!is_dir($tempPath)) {
- $this->logger->error('Error while checking the temporary PHP path - it was not properly set to a directory. Returned value: ' . $tempPath);
- return false;
- }
- $freeSpaceInTemp = function_exists('disk_free_space') ? disk_free_space($tempPath) : false;
- if ($freeSpaceInTemp === false) {
- $this->logger->error('Error while checking the available disk space of temporary PHP path or no free disk space returned. Temporary path: ' . $tempPath);
- return false;
- }
-
- $freeSpaceInTempInGB = $freeSpaceInTemp / 1024 / 1024 / 1024;
- if ($freeSpaceInTempInGB > 50) {
- return true;
- }
-
- $this->logger->warning('Checking the available space in the temporary path resulted in ' . round($freeSpaceInTempInGB, 1) . ' GB instead of the recommended 50GB. Path: ' . $tempPath);
- return false;
- }
-
- protected function imageMagickLacksSVGSupport(): bool {
- return extension_loaded('imagick') && count(\Imagick::queryFormats('SVG')) === 0;
- }
-
- /**
* @return DataResponse
- * @AuthorizedAdminSetting(settings=OCA\Settings\Settings\Admin\Overview)
*/
+ #[AuthorizedAdminSetting(settings: Overview::class)]
public function check() {
- $phpDefaultCharset = new PhpDefaultCharset();
- $phpOutputBuffering = new PhpOutputBuffering();
- $legacySSEKeyFormat = new LegacySSEKeyFormat($this->l10n, $this->config, $this->urlGenerator);
- $checkUserCertificates = new CheckUserCertificates($this->l10n, $this->config, $this->urlGenerator);
- $supportedDatabases = new SupportedDatabase($this->l10n, $this->connection);
- $ldapInvalidUuids = new LdapInvalidUuids($this->appManager, $this->l10n, $this->serverContainer);
-
return new DataResponse(
[
- 'isGetenvServerWorking' => !empty(getenv('PATH')),
- 'isReadOnlyConfig' => $this->isReadOnlyConfig(),
- 'hasValidTransactionIsolationLevel' => $this->hasValidTransactionIsolationLevel(),
- 'wasEmailTestSuccessful' => $this->wasEmailTestSuccessful(),
- 'hasFileinfoInstalled' => $this->hasFileinfoInstalled(),
- 'hasWorkingFileLocking' => $this->hasWorkingFileLocking(),
- 'suggestedOverwriteCliURL' => $this->getSuggestedOverwriteCliURL(),
- 'cronInfo' => $this->getLastCronInfo(),
- 'cronErrors' => $this->getCronErrors(),
- 'isFairUseOfFreePushService' => $this->isFairUseOfFreePushService(),
- 'serverHasInternetConnectionProblems' => $this->hasInternetConnectivityProblems(),
- 'isMemcacheConfigured' => $this->isMemcacheConfigured(),
- 'memcacheDocs' => $this->urlGenerator->linkToDocs('admin-performance'),
- 'isRandomnessSecure' => $this->isRandomnessSecure(),
- 'securityDocs' => $this->urlGenerator->linkToDocs('admin-security'),
- 'isUsedTlsLibOutdated' => $this->isUsedTlsLibOutdated(),
- 'phpSupported' => $this->isPhpSupported(),
- 'forwardedForHeadersWorking' => $this->forwardedForHeadersWorking(),
- 'reverseProxyDocs' => $this->urlGenerator->linkToDocs('admin-reverse-proxy'),
- 'isCorrectMemcachedPHPModuleInstalled' => $this->isCorrectMemcachedPHPModuleInstalled(),
- 'hasPassedCodeIntegrityCheck' => $this->checker->hasPassedCheck(),
- 'codeIntegrityCheckerDocumentation' => $this->urlGenerator->linkToDocs('admin-code-integrity'),
- 'OpcacheSetupRecommendations' => $this->getOpcacheSetupRecommendations(),
- 'isSettimelimitAvailable' => $this->isSettimelimitAvailable(),
- 'hasFreeTypeSupport' => $this->hasFreeTypeSupport(),
- 'missingPrimaryKeys' => $this->hasMissingPrimaryKeys(),
- 'missingIndexes' => $this->hasMissingIndexes(),
- 'missingColumns' => $this->hasMissingColumns(),
- 'isSqliteUsed' => $this->isSqliteUsed(),
- 'databaseConversionDocumentation' => $this->urlGenerator->linkToDocs('admin-db-conversion'),
- 'isMemoryLimitSufficient' => $this->memoryInfo->isMemoryLimitSufficient(),
- 'appDirsWithDifferentOwner' => $this->getAppDirsWithDifferentOwner(),
- 'isImagickEnabled' => $this->isImagickEnabled(),
- 'areWebauthnExtensionsEnabled' => $this->areWebauthnExtensionsEnabled(),
- 'is64bit' => $this->is64bit(),
- 'recommendedPHPModules' => $this->hasRecommendedPHPModules(),
- 'pendingBigIntConversionColumns' => $this->hasBigIntConversionPendingColumns(),
- 'isMysqlUsedWithoutUTF8MB4' => $this->isMysqlUsedWithoutUTF8MB4(),
- 'isEnoughTempSpaceAvailableIfS3PrimaryStorageIsUsed' => $this->isEnoughTempSpaceAvailableIfS3PrimaryStorageIsUsed(),
- 'reverseProxyGeneratedURL' => $this->urlGenerator->getAbsoluteURL('index.php'),
- 'imageMagickLacksSVGSupport' => $this->imageMagickLacksSVGSupport(),
- PhpDefaultCharset::class => ['pass' => $phpDefaultCharset->run(), 'description' => $phpDefaultCharset->description(), 'severity' => $phpDefaultCharset->severity()],
- PhpOutputBuffering::class => ['pass' => $phpOutputBuffering->run(), 'description' => $phpOutputBuffering->description(), 'severity' => $phpOutputBuffering->severity()],
- LegacySSEKeyFormat::class => ['pass' => $legacySSEKeyFormat->run(), 'description' => $legacySSEKeyFormat->description(), 'severity' => $legacySSEKeyFormat->severity(), 'linkToDocumentation' => $legacySSEKeyFormat->linkToDocumentation()],
- CheckUserCertificates::class => ['pass' => $checkUserCertificates->run(), 'description' => $checkUserCertificates->description(), 'severity' => $checkUserCertificates->severity(), 'elements' => $checkUserCertificates->elements()],
- 'isDefaultPhoneRegionSet' => $this->config->getSystemValueString('default_phone_region', '') !== '',
- SupportedDatabase::class => ['pass' => $supportedDatabases->run(), 'description' => $supportedDatabases->description(), 'severity' => $supportedDatabases->severity()],
- 'temporaryDirectoryWritable' => $this->isTemporaryDirectoryWritable(),
- LdapInvalidUuids::class => ['pass' => $ldapInvalidUuids->run(), 'description' => $ldapInvalidUuids->description(), 'severity' => $ldapInvalidUuids->severity()],
+ 'generic' => $this->setupCheckManager->runAll(),
]
);
}
diff --git a/apps/settings/lib/Controller/CommonSettingsTrait.php b/apps/settings/lib/Controller/CommonSettingsTrait.php
index 8ca62b9d1b3..75d2b1f2f9e 100644
--- a/apps/settings/lib/Controller/CommonSettingsTrait.php
+++ b/apps/settings/lib/Controller/CommonSettingsTrait.php
@@ -1,42 +1,32 @@
<?php
+
/**
- * @copyright Copyright (c) 2017 Arthur Schiwon <blizzz@arthur-schiwon.de>
- *
- * @author Arthur Schiwon <blizzz@arthur-schiwon.de>
- * @author Christoph Wurst <christoph@winzerhof-wurst.at>
- * @author Joas Schilling <coding@schilljs.com>
- * @author Julius Härtl <jus@bitgrid.net>
- * @author Morris Jobke <hey@morrisjobke.de>
- * @author Robin Appelman <robin@icewind.nl>
- * @author Roeland Jago Douma <roeland@famdouma.nl>
- *
- * @license GNU AGPL version 3 or any later version
- *
- * 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/>.
- *
+ * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
+
namespace OCA\Settings\Controller;
+use InvalidArgumentException;
+use OC\AppFramework\Middleware\Security\Exceptions\NotAdminException;
+use OCA\Settings\AppInfo\Application;
use OCP\AppFramework\Http\TemplateResponse;
+use OCP\AppFramework\Services\IInitialState;
use OCP\Group\ISubAdmin;
use OCP\IGroupManager;
use OCP\INavigationManager;
use OCP\IUserSession;
+use OCP\Settings\IDeclarativeManager;
+use OCP\Settings\IDeclarativeSettingsForm;
use OCP\Settings\IIconSection;
use OCP\Settings\IManager as ISettingsManager;
use OCP\Settings\ISettings;
+use OCP\Util;
+/**
+ * @psalm-import-type DeclarativeSettingsFormSchemaWithValues from IDeclarativeSettingsForm
+ * @psalm-import-type DeclarativeSettingsFormSchemaWithoutValues from IDeclarativeSettingsForm
+ */
trait CommonSettingsTrait {
/** @var ISettingsManager */
@@ -54,28 +44,26 @@ trait CommonSettingsTrait {
/** @var ISubAdmin */
private $subAdmin;
+ private IDeclarativeManager $declarativeSettingsManager;
+
+ /** @var IInitialState */
+ private $initialState;
+
/**
* @return array{forms: array{personal: array, admin: array}}
*/
private function getNavigationParameters(string $currentType, string $currentSection): array {
- $templateParameters = [
- 'personal' => $this->formatPersonalSections($currentType, $currentSection),
- 'admin' => []
- ];
-
- $templateParameters['admin'] = $this->formatAdminSections(
- $currentType,
- $currentSection
- );
-
return [
- 'forms' => $templateParameters
+ 'forms' => [
+ 'personal' => $this->formatPersonalSections($currentType, $currentSection),
+ 'admin' => $this->formatAdminSections($currentType, $currentSection),
+ ],
];
}
/**
* @param IIconSection[][] $sections
- * @psam-param 'admin'|'personal' $type
+ * @psalm-param 'admin'|'personal' $type
* @return list<array{anchor: string, section-name: string, active: bool, icon: string}>
*/
protected function formatSections(array $sections, string $currentSection, string $type, string $currentType): array {
@@ -87,7 +75,11 @@ trait CommonSettingsTrait {
} elseif ($type === 'personal') {
$settings = $this->settingsManager->getPersonalSettings($section->getID());
}
- if (empty($settings) && !($section->getID() === 'additional' && count(\OC_App::getForms('admin')) > 0)) {
+
+ /** @psalm-suppress PossiblyNullArgument */
+ $declarativeFormIDs = $this->declarativeSettingsManager->getFormIDs($this->userSession->getUser(), $type, $section->getID());
+
+ if (empty($settings) && empty($declarativeFormIDs)) {
continue;
}
@@ -107,54 +99,93 @@ trait CommonSettingsTrait {
return $templateParameters;
}
- protected function formatPersonalSections(string $currentType, string $currentSections): array {
+ protected function formatPersonalSections(string $currentType, string $currentSection): array {
$sections = $this->settingsManager->getPersonalSections();
- return $this->formatSections($sections, $currentSections, 'personal', $currentType);
+ return $this->formatSections($sections, $currentSection, 'personal', $currentType);
}
- protected function formatAdminSections(string $currentType, string $currentSections): array {
+ protected function formatAdminSections(string $currentType, string $currentSection): array {
$sections = $this->settingsManager->getAdminSections();
- return $this->formatSections($sections, $currentSections, 'admin', $currentType);
+ return $this->formatSections($sections, $currentSection, 'admin', $currentType);
}
/**
- * @param array<int, list<\OCP\Settings\ISettings>> $settings
+ * @param list<ISettings> $settings
+ * @param list<DeclarativeSettingsFormSchemaWithValues> $declarativeSettings
* @return array{content: string}
*/
- private function formatSettings(array $settings): array {
+ private function formatSettings(array $settings, array $declarativeSettings): array {
+ $settings = array_merge($settings, $declarativeSettings);
+
+ usort($settings, function ($first, $second) {
+ $priorityOne = $first instanceof ISettings ? $first->getPriority() : $first['priority'];
+ $priorityTwo = $second instanceof ISettings ? $second->getPriority() : $second['priority'];
+ return $priorityOne - $priorityTwo;
+ });
+
$html = '';
- foreach ($settings as $prioritizedSettings) {
- foreach ($prioritizedSettings as $setting) {
- /** @var ISettings $setting */
+ foreach ($settings as $setting) {
+ if ($setting instanceof ISettings) {
$form = $setting->getForm();
$html .= $form->renderAs('')->render();
+ } else {
+ $html .= '<div id="' . $setting['app'] . '_' . $setting['id'] . '"></div>';
}
}
return ['content' => $html];
}
+ /**
+ * @psalm-param 'admin'|'personal' $type
+ */
private function getIndexResponse(string $type, string $section): TemplateResponse {
+ $user = $this->userSession->getUser();
+ assert($user !== null, 'No user logged in for settings');
+
+ $this->declarativeSettingsManager->loadSchemas();
+ $declarativeSettings = $this->declarativeSettingsManager->getFormsWithValues($user, $type, $section);
+
+ foreach ($declarativeSettings as &$form) {
+ foreach ($form['fields'] as &$field) {
+ if (isset($field['sensitive']) && $field['sensitive'] === true && !empty($field['value'])) {
+ $field['value'] = 'dummySecret';
+ }
+ }
+ }
+
if ($type === 'personal') {
+ $settings = array_values($this->settingsManager->getPersonalSettings($section));
if ($section === 'theming') {
$this->navigationManager->setActiveEntry('accessibility_settings');
} else {
$this->navigationManager->setActiveEntry('settings');
}
} elseif ($type === 'admin') {
+ $settings = array_values($this->settingsManager->getAllowedAdminSettings($section, $user));
+ if (empty($settings) && empty($declarativeSettings)) {
+ throw new NotAdminException('Logged in user does not have permission to access these settings.');
+ }
$this->navigationManager->setActiveEntry('admin_settings');
+ } else {
+ throw new InvalidArgumentException('$type must be either "admin" or "personal"');
+ }
+
+ if (!empty($declarativeSettings)) {
+ Util::addScript(Application::APP_ID, 'declarative-settings-forms');
+ $this->initialState->provideInitialState('declarative-settings-forms', $declarativeSettings);
}
- $templateParams = [];
+ $settings = array_merge(...$settings);
+ $templateParams = $this->formatSettings($settings, $declarativeSettings);
$templateParams = array_merge($templateParams, $this->getNavigationParameters($type, $section));
- $templateParams = array_merge($templateParams, $this->getSettings($section));
+
$activeSection = $this->settingsManager->getSection($type, $section);
if ($activeSection) {
$templateParams['pageTitle'] = $activeSection->getName();
$templateParams['activeSectionId'] = $activeSection->getID();
+ $templateParams['activeSectionType'] = $type;
}
return new TemplateResponse('settings', 'settings/frame', $templateParams);
}
-
- abstract protected function getSettings($section);
}
diff --git a/apps/settings/lib/Controller/DeclarativeSettingsController.php b/apps/settings/lib/Controller/DeclarativeSettingsController.php
new file mode 100644
index 00000000000..4e4bee4043c
--- /dev/null
+++ b/apps/settings/lib/Controller/DeclarativeSettingsController.php
@@ -0,0 +1,131 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCA\Settings\Controller;
+
+use Exception;
+use OC\AppFramework\Middleware\Security\Exceptions\NotAdminException;
+use OC\AppFramework\Middleware\Security\Exceptions\NotLoggedInException;
+use OCA\Settings\ResponseDefinitions;
+use OCP\AppFramework\Http;
+use OCP\AppFramework\Http\Attribute\NoAdminRequired;
+use OCP\AppFramework\Http\Attribute\PasswordConfirmationRequired;
+use OCP\AppFramework\Http\DataResponse;
+use OCP\AppFramework\OCS\OCSBadRequestException;
+use OCP\AppFramework\OCSController;
+use OCP\IRequest;
+use OCP\IUserSession;
+use OCP\Settings\IDeclarativeManager;
+use Psr\Log\LoggerInterface;
+
+/**
+ * @psalm-import-type SettingsDeclarativeForm from ResponseDefinitions
+ */
+class DeclarativeSettingsController extends OCSController {
+ public function __construct(
+ string $appName,
+ IRequest $request,
+ private IUserSession $userSession,
+ private IDeclarativeManager $declarativeManager,
+ private LoggerInterface $logger,
+ ) {
+ parent::__construct($appName, $request);
+ }
+
+ /**
+ * Sets a declarative settings value
+ *
+ * @param string $app ID of the app
+ * @param string $formId ID of the form
+ * @param string $fieldId ID of the field
+ * @param mixed $value Value to be saved
+ * @return DataResponse<Http::STATUS_OK, null, array{}>
+ * @throws NotLoggedInException Not logged in or not an admin user
+ * @throws NotAdminException Not logged in or not an admin user
+ * @throws OCSBadRequestException Invalid arguments to save value
+ *
+ * 200: Value set successfully
+ */
+ #[NoAdminRequired]
+ public function setValue(string $app, string $formId, string $fieldId, mixed $value): DataResponse {
+ return $this->saveValue($app, $formId, $fieldId, $value);
+ }
+
+ /**
+ * Sets a declarative settings value.
+ * Password confirmation is required for sensitive values.
+ *
+ * @param string $app ID of the app
+ * @param string $formId ID of the form
+ * @param string $fieldId ID of the field
+ * @param mixed $value Value to be saved
+ * @return DataResponse<Http::STATUS_OK, null, array{}>
+ * @throws NotLoggedInException Not logged in or not an admin user
+ * @throws NotAdminException Not logged in or not an admin user
+ * @throws OCSBadRequestException Invalid arguments to save value
+ *
+ * 200: Value set successfully
+ */
+ #[NoAdminRequired]
+ #[PasswordConfirmationRequired]
+ public function setSensitiveValue(string $app, string $formId, string $fieldId, mixed $value): DataResponse {
+ return $this->saveValue($app, $formId, $fieldId, $value);
+ }
+
+ /**
+ * Sets a declarative settings value.
+ *
+ * @param string $app ID of the app
+ * @param string $formId ID of the form
+ * @param string $fieldId ID of the field
+ * @param mixed $value Value to be saved
+ * @return DataResponse<Http::STATUS_OK, null, array{}>
+ * @throws NotLoggedInException Not logged in or not an admin user
+ * @throws NotAdminException Not logged in or not an admin user
+ * @throws OCSBadRequestException Invalid arguments to save value
+ *
+ * 200: Value set successfully
+ */
+ private function saveValue(string $app, string $formId, string $fieldId, mixed $value): DataResponse {
+ $user = $this->userSession->getUser();
+ if ($user === null) {
+ throw new NotLoggedInException();
+ }
+
+ try {
+ $this->declarativeManager->loadSchemas();
+ $this->declarativeManager->setValue($user, $app, $formId, $fieldId, $value);
+ return new DataResponse(null);
+ } catch (NotAdminException $e) {
+ throw $e;
+ } catch (Exception $e) {
+ $this->logger->error('Failed to set declarative settings value: ' . $e->getMessage());
+ throw new OCSBadRequestException();
+ }
+ }
+
+ /**
+ * Gets all declarative forms with the values prefilled.
+ *
+ * @return DataResponse<Http::STATUS_OK, list<SettingsDeclarativeForm>, array{}>
+ * @throws NotLoggedInException
+ * @NoSubAdminRequired
+ *
+ * 200: Forms returned
+ */
+ #[NoAdminRequired]
+ public function getForms(): DataResponse {
+ $user = $this->userSession->getUser();
+ if ($user === null) {
+ throw new NotLoggedInException();
+ }
+ $this->declarativeManager->loadSchemas();
+ return new DataResponse($this->declarativeManager->getFormsWithValues($user, null, null));
+ }
+}
diff --git a/apps/settings/lib/Controller/HelpController.php b/apps/settings/lib/Controller/HelpController.php
index 38ce84843ed..05bff158ee6 100644
--- a/apps/settings/lib/Controller/HelpController.php
+++ b/apps/settings/lib/Controller/HelpController.php
@@ -3,79 +3,50 @@
declare(strict_types=1);
/**
- * @copyright Copyright (c) 2019 Julius Härtl <jus@bitgrid.net>
- *
- * @author Arthur Schiwon <blizzz@arthur-schiwon.de>
- * @author Daniel Kesselberg <mail@danielkesselberg.de>
- * @author Joas Schilling <coding@schilljs.com>
- * @author Julius Härtl <jus@bitgrid.net>
- * @author Roeland Jago Douma <roeland@famdouma.nl>
- *
- * @license GNU AGPL version 3 or any later version
- *
- * 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/>.
- *
+ * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\Settings\Controller;
use OCP\AppFramework\Controller;
+use OCP\AppFramework\Http\Attribute\NoAdminRequired;
+use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
+use OCP\AppFramework\Http\Attribute\OpenAPI;
use OCP\AppFramework\Http\ContentSecurityPolicy;
use OCP\AppFramework\Http\TemplateResponse;
+use OCP\IAppConfig;
+use OCP\IConfig;
use OCP\IGroupManager;
use OCP\IL10N;
use OCP\INavigationManager;
use OCP\IRequest;
use OCP\IURLGenerator;
+#[OpenAPI(scope: OpenAPI::SCOPE_IGNORE)]
class HelpController extends Controller {
- /** @var INavigationManager */
- private $navigationManager;
- /** @var IURLGenerator */
- private $urlGenerator;
- /** @var IGroupManager */
- private $groupManager;
- /** @var IL10N */
- private $l10n;
-
- /** @var string */
- private $userId;
-
public function __construct(
string $appName,
IRequest $request,
- INavigationManager $navigationManager,
- IURLGenerator $urlGenerator,
- ?string $userId,
- IGroupManager $groupManager,
- IL10N $l10n
+ private INavigationManager $navigationManager,
+ private IURLGenerator $urlGenerator,
+ /** @var string */
+ private ?string $userId,
+ private IGroupManager $groupManager,
+ private IL10N $l10n,
+ private IConfig $config,
+ private IAppConfig $appConfig,
) {
parent::__construct($appName, $request);
- $this->navigationManager = $navigationManager;
- $this->urlGenerator = $urlGenerator;
- $this->userId = $userId;
- $this->groupManager = $groupManager;
- $this->l10n = $l10n;
}
/**
* @return TemplateResponse
*
- * @NoCSRFRequired
- * @NoAdminRequired
* @NoSubAdminRequired
*/
+ #[NoCSRFRequired]
+ #[NoAdminRequired]
public function help(string $mode = 'user'): TemplateResponse {
$this->navigationManager->setActiveEntry('help');
$pageTitle = $this->l10n->t('Administrator documentation');
@@ -91,6 +62,16 @@ class HelpController extends Controller {
$urlUserDocs = $this->urlGenerator->linkToRoute('settings.Help.help', ['mode' => 'user']);
$urlAdminDocs = $this->urlGenerator->linkToRoute('settings.Help.help', ['mode' => 'admin']);
+ $knowledgebaseEmbedded = $this->config->getSystemValueBool('knowledgebase.embedded', false);
+ if (!$knowledgebaseEmbedded) {
+ $pageTitle = $this->l10n->t('Nextcloud help overview');
+ $urlUserDocs = $this->urlGenerator->linkToDocs('user');
+ $urlAdminDocs = $this->urlGenerator->linkToDocs('admin');
+ }
+
+ $legalNoticeUrl = $this->appConfig->getValueString('theming', 'imprintUrl');
+ $privacyUrl = $this->appConfig->getValueString('theming', 'privacyUrl');
+
$response = new TemplateResponse('settings', 'help', [
'admin' => $this->groupManager->isAdmin($this->userId),
'url' => $documentationUrl,
@@ -98,6 +79,9 @@ class HelpController extends Controller {
'urlAdminDocs' => $urlAdminDocs,
'mode' => $mode,
'pageTitle' => $pageTitle,
+ 'knowledgebaseEmbedded' => $knowledgebaseEmbedded,
+ 'legalNoticeUrl' => $legalNoticeUrl,
+ 'privacyUrl' => $privacyUrl,
]);
$policy = new ContentSecurityPolicy();
$policy->addAllowedFrameDomain('\'self\'');
diff --git a/apps/settings/lib/Controller/LogSettingsController.php b/apps/settings/lib/Controller/LogSettingsController.php
index 08c18189d03..90cf4549d2f 100644
--- a/apps/settings/lib/Controller/LogSettingsController.php
+++ b/apps/settings/lib/Controller/LogSettingsController.php
@@ -1,33 +1,17 @@
<?php
+
/**
- * @copyright Copyright (c) 2016, ownCloud, Inc.
- *
- * @author Arthur Schiwon <blizzz@arthur-schiwon.de>
- * @author Christoph Wurst <christoph@winzerhof-wurst.at>
- * @author Georg Ehrke <oc.list@georgehrke.com>
- * @author Lukas Reschke <lukas@statuscode.ch>
- * @author Roeland Jago Douma <roeland@famdouma.nl>
- * @author Thomas Müller <thomas.mueller@tmit.eu>
- *
- * @license AGPL-3.0
- *
- * This code is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * 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, version 3,
- * along with this program. If not, see <http://www.gnu.org/licenses/>
- *
+ * SPDX-FileCopyrightText: 2019-2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
+ * SPDX-License-Identifier: AGPL-3.0-only
*/
namespace OCA\Settings\Controller;
use OC\Log;
use OCP\AppFramework\Controller;
+use OCP\AppFramework\Http;
+use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
+use OCP\AppFramework\Http\Attribute\OpenAPI;
use OCP\AppFramework\Http\StreamResponse;
use OCP\IRequest;
@@ -44,17 +28,23 @@ class LogSettingsController extends Controller {
/**
* download logfile
*
- * @NoCSRFRequired
+ * @return StreamResponse<Http::STATUS_OK, array{Content-Type: 'application/octet-stream', 'Content-Disposition': 'attachment; filename="nextcloud.log"'}>
*
- * @return StreamResponse
+ * 200: Logfile returned
*/
+ #[NoCSRFRequired]
+ #[OpenAPI(scope: OpenAPI::SCOPE_ADMINISTRATION)]
public function download() {
if (!$this->log instanceof Log) {
throw new \UnexpectedValueException('Log file not available');
}
- $resp = new StreamResponse($this->log->getLogPath());
- $resp->addHeader('Content-Type', 'application/octet-stream');
- $resp->addHeader('Content-Disposition', 'attachment; filename="nextcloud.log"');
- return $resp;
+ return new StreamResponse(
+ $this->log->getLogPath(),
+ Http::STATUS_OK,
+ [
+ 'Content-Type' => 'application/octet-stream',
+ 'Content-Disposition' => 'attachment; filename="nextcloud.log"',
+ ],
+ );
}
}
diff --git a/apps/settings/lib/Controller/MailSettingsController.php b/apps/settings/lib/Controller/MailSettingsController.php
index 2df79b67731..f1e3b8032dc 100644
--- a/apps/settings/lib/Controller/MailSettingsController.php
+++ b/apps/settings/lib/Controller/MailSettingsController.php
@@ -1,34 +1,17 @@
<?php
+
/**
- * @copyright Copyright (c) 2017 Joas Schilling <coding@schilljs.com>
- * @copyright Copyright (c) 2016, ownCloud, Inc.
- *
- * @author Christoph Wurst <christoph@winzerhof-wurst.at>
- * @author Daniel Kesselberg <mail@danielkesselberg.de>
- * @author Joas Schilling <coding@schilljs.com>
- * @author Lukas Reschke <lukas@statuscode.ch>
- * @author Morris Jobke <hey@morrisjobke.de>
- * @author Roeland Jago Douma <roeland@famdouma.nl>
- *
- * @license AGPL-3.0
- *
- * This code is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * 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, version 3,
- * along with this program. If not, see <http://www.gnu.org/licenses/>
- *
+ * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
+ * SPDX-License-Identifier: AGPL-3.0-only
*/
namespace OCA\Settings\Controller;
+use OCA\Settings\Settings\Admin\Overview;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http;
+use OCP\AppFramework\Http\Attribute\AuthorizedAdminSetting;
+use OCP\AppFramework\Http\Attribute\PasswordConfirmationRequired;
use OCP\AppFramework\Http\DataResponse;
use OCP\IConfig;
use OCP\IL10N;
@@ -39,17 +22,6 @@ use OCP\Mail\IMailer;
class MailSettingsController extends Controller {
- /** @var IL10N */
- private $l10n;
- /** @var IConfig */
- private $config;
- /** @var IUserSession */
- private $userSession;
- /** @var IMailer */
- private $mailer;
- /** @var IURLGenerator */
- private $urlGenerator;
-
/**
* @param string $appName
* @param IRequest $request
@@ -59,52 +31,51 @@ class MailSettingsController extends Controller {
* @param IURLGenerator $urlGenerator,
* @param IMailer $mailer
*/
- public function __construct($appName,
- IRequest $request,
- IL10N $l10n,
- IConfig $config,
- IUserSession $userSession,
- IURLGenerator $urlGenerator,
- IMailer $mailer) {
+ public function __construct(
+ $appName,
+ IRequest $request,
+ private IL10N $l10n,
+ private IConfig $config,
+ private IUserSession $userSession,
+ private IURLGenerator $urlGenerator,
+ private IMailer $mailer,
+ ) {
parent::__construct($appName, $request);
- $this->l10n = $l10n;
- $this->config = $config;
- $this->userSession = $userSession;
- $this->urlGenerator = $urlGenerator;
- $this->mailer = $mailer;
}
/**
* Sets the email settings
- *
- * @PasswordConfirmationRequired
- * @AuthorizedAdminSetting(settings=OCA\Settings\Settings\Admin\Overview)
- *
- * @param string $mail_domain
- * @param string $mail_from_address
- * @param string $mail_smtpmode
- * @param string $mail_smtpsecure
- * @param string $mail_smtphost
- * @param int $mail_smtpauth
- * @param string $mail_smtpport
- * @return DataResponse
*/
- public function setMailSettings($mail_domain,
- $mail_from_address,
- $mail_smtpmode,
- $mail_smtpsecure,
- $mail_smtphost,
- $mail_smtpauth,
- $mail_smtpport,
- $mail_sendmailmode) {
- $params = get_defined_vars();
- $configs = [];
- foreach ($params as $key => $value) {
+ #[AuthorizedAdminSetting(settings: Overview::class)]
+ #[PasswordConfirmationRequired]
+ public function setMailSettings(
+ string $mail_domain,
+ string $mail_from_address,
+ string $mail_smtpmode,
+ string $mail_smtpsecure,
+ string $mail_smtphost,
+ ?string $mail_smtpauth,
+ string $mail_smtpport,
+ string $mail_sendmailmode,
+ ): DataResponse {
+ $mail_smtpauth = $mail_smtpauth == '1';
+
+ $configs = [
+ 'mail_domain' => $mail_domain,
+ 'mail_from_address' => $mail_from_address,
+ 'mail_smtpmode' => $mail_smtpmode,
+ 'mail_smtpsecure' => $mail_smtpsecure,
+ 'mail_smtphost' => $mail_smtphost,
+ 'mail_smtpauth' => $mail_smtpauth,
+ 'mail_smtpport' => $mail_smtpport,
+ 'mail_sendmailmode' => $mail_sendmailmode,
+ ];
+ foreach ($configs as $key => $value) {
$configs[$key] = empty($value) ? null : $value;
}
// Delete passwords from config in case no auth is specified
- if ($params['mail_smtpauth'] !== 1) {
+ if (!$mail_smtpauth) {
$configs['mail_smtpname'] = null;
$configs['mail_smtppassword'] = null;
}
@@ -119,13 +90,12 @@ class MailSettingsController extends Controller {
/**
* Store the credentials used for SMTP in the config
*
- * @PasswordConfirmationRequired
- * @AuthorizedAdminSetting(settings=OCA\Settings\Settings\Admin\Overview)
- *
* @param string $mail_smtpname
* @param string $mail_smtppassword
* @return DataResponse
*/
+ #[AuthorizedAdminSetting(settings: Overview::class)]
+ #[PasswordConfirmationRequired]
public function storeCredentials($mail_smtpname, $mail_smtppassword) {
if ($mail_smtppassword === '********') {
return new DataResponse($this->l10n->t('Invalid SMTP password.'), Http::STATUS_BAD_REQUEST);
@@ -143,9 +113,9 @@ class MailSettingsController extends Controller {
/**
* Send a mail to test the settings
- * @AuthorizedAdminSetting(settings=OCA\Settings\Settings\Admin\Overview)
* @return DataResponse
*/
+ #[AuthorizedAdminSetting(settings: Overview::class)]
public function sendTestMail() {
$email = $this->config->getUserValue($this->userSession->getUser()->getUID(), $this->appName, 'email', '');
if (!empty($email)) {
@@ -180,6 +150,6 @@ class MailSettingsController extends Controller {
}
$this->config->setAppValue('core', 'emailTestSuccessful', '0');
- return new DataResponse($this->l10n->t('You need to set your user email before being able to send test emails. Go to %s for that.', [$this->urlGenerator->linkToRouteAbsolute('settings.PersonalSettings.index')]), Http::STATUS_BAD_REQUEST);
+ return new DataResponse($this->l10n->t('You need to set your account email before being able to send test emails. Go to %s for that.', [$this->urlGenerator->linkToRouteAbsolute('settings.PersonalSettings.index')]), Http::STATUS_BAD_REQUEST);
}
}
diff --git a/apps/settings/lib/Controller/PersonalSettingsController.php b/apps/settings/lib/Controller/PersonalSettingsController.php
index 8781ecd214e..340ca3f93eb 100644
--- a/apps/settings/lib/Controller/PersonalSettingsController.php
+++ b/apps/settings/lib/Controller/PersonalSettingsController.php
@@ -1,40 +1,26 @@
<?php
+
/**
- * @copyright Copyright (c) 2017 Arthur Schiwon <blizzz@arthur-schiwon.de>
- *
- * @author Arthur Schiwon <blizzz@arthur-schiwon.de>
- * @author Christoph Wurst <christoph@winzerhof-wurst.at>
- * @author Joas Schilling <coding@schilljs.com>
- * @author Robin Appelman <robin@icewind.nl>
- *
- * @license GNU AGPL version 3 or any later version
- *
- * 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/>.
- *
+ * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\Settings\Controller;
use OCP\AppFramework\Controller;
+use OCP\AppFramework\Http\Attribute\NoAdminRequired;
+use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
+use OCP\AppFramework\Http\Attribute\OpenAPI;
use OCP\AppFramework\Http\TemplateResponse;
+use OCP\AppFramework\Services\IInitialState;
use OCP\Group\ISubAdmin;
use OCP\IGroupManager;
use OCP\INavigationManager;
use OCP\IRequest;
use OCP\IUserSession;
+use OCP\Settings\IDeclarativeManager;
use OCP\Settings\IManager as ISettingsManager;
-use OCP\Template;
+#[OpenAPI(scope: OpenAPI::SCOPE_IGNORE)]
class PersonalSettingsController extends Controller {
use CommonSettingsTrait;
@@ -45,7 +31,9 @@ class PersonalSettingsController extends Controller {
ISettingsManager $settingsManager,
IUserSession $userSession,
IGroupManager $groupManager,
- ISubAdmin $subAdmin
+ ISubAdmin $subAdmin,
+ IDeclarativeManager $declarativeSettingsManager,
+ IInitialState $initialState,
) {
parent::__construct($appName, $request);
$this->navigationManager = $navigationManager;
@@ -53,57 +41,19 @@ class PersonalSettingsController extends Controller {
$this->userSession = $userSession;
$this->subAdmin = $subAdmin;
$this->groupManager = $groupManager;
+ $this->declarativeSettingsManager = $declarativeSettingsManager;
+ $this->initialState = $initialState;
}
/**
- * @NoCSRFRequired
- * @NoAdminRequired
* @NoSubAdminRequired
*/
+ #[NoAdminRequired]
+ #[NoCSRFRequired]
public function index(string $section): TemplateResponse {
- return $this->getIndexResponse('personal', $section);
- }
-
- /**
- * @param string $section
- * @return array
- */
- protected function getSettings($section) {
- $settings = $this->settingsManager->getPersonalSettings($section);
- $formatted = $this->formatSettings($settings);
- if ($section === 'additional') {
- $formatted['content'] .= $this->getLegacyForms();
- }
- return $formatted;
- }
-
- /**
- * @return bool|string
- */
- private function getLegacyForms() {
- $forms = \OC_App::getForms('personal');
-
- $forms = array_map(function ($form) {
- if (preg_match('%(<h2(?P<class>[^>]*)>.*?</h2>)%i', $form, $regs)) {
- $sectionName = str_replace('<h2' . $regs['class'] . '>', '', $regs[0]);
- $sectionName = str_replace('</h2>', '', $sectionName);
- $anchor = strtolower($sectionName);
- $anchor = str_replace(' ', '-', $anchor);
-
- return [
- 'anchor' => $anchor,
- 'section-name' => $sectionName,
- 'form' => $form
- ];
- }
- return [
- 'form' => $form
- ];
- }, $forms);
-
- $out = new Template('settings', 'settings/additional');
- $out->assign('forms', $forms);
-
- return $out->fetchPage();
+ return $this->getIndexResponse(
+ 'personal',
+ $section,
+ );
}
}
diff --git a/apps/settings/lib/Controller/ReasonsController.php b/apps/settings/lib/Controller/ReasonsController.php
index 2ceb7005407..91d0a8640d1 100644
--- a/apps/settings/lib/Controller/ReasonsController.php
+++ b/apps/settings/lib/Controller/ReasonsController.php
@@ -3,38 +3,25 @@
declare(strict_types=1);
/**
- * @copyright Copyright (c) 2020, Roeland Jago Douma <roeland@famdouma.nl>
- *
- * @author Jan C. Borchardt <hey@jancborchardt.net>
- *
- * @license GNU AGPL version 3 or any later version
- *
- * 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/>.
- *
+ * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\Settings\Controller;
use OCP\AppFramework\Controller;
+use OCP\AppFramework\Http\Attribute\NoAdminRequired;
+use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
+use OCP\AppFramework\Http\Attribute\OpenAPI;
use OCP\AppFramework\Http\DataDisplayResponse;
+#[OpenAPI(scope: OpenAPI::SCOPE_IGNORE)]
class ReasonsController extends Controller {
/**
- * @NoCSRFRequired
- * @NoAdminRequired
* @NoSubAdminRequired
*/
+ #[NoAdminRequired]
+ #[NoCSRFRequired]
public function getPdf() {
$data = file_get_contents(__DIR__ . '/../../data/Reasons to use Nextcloud.pdf');
diff --git a/apps/settings/lib/Controller/TwoFactorSettingsController.php b/apps/settings/lib/Controller/TwoFactorSettingsController.php
index 45722767cda..e08fca8ec6c 100644
--- a/apps/settings/lib/Controller/TwoFactorSettingsController.php
+++ b/apps/settings/lib/Controller/TwoFactorSettingsController.php
@@ -3,25 +3,8 @@
declare(strict_types=1);
/**
- * @copyright 2018 Christoph Wurst <christoph@winzerhof-wurst.at>
- *
- * @author Christoph Wurst <christoph@winzerhof-wurst.at>
- *
- * @license GNU AGPL version 3 or any later version
- *
- * 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/>.
- *
+ * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\Settings\Controller;
@@ -37,8 +20,8 @@ class TwoFactorSettingsController extends Controller {
private $mandatoryTwoFactor;
public function __construct(string $appName,
- IRequest $request,
- MandatoryTwoFactor $mandatoryTwoFactor) {
+ IRequest $request,
+ MandatoryTwoFactor $mandatoryTwoFactor) {
parent::__construct($appName, $request);
$this->mandatoryTwoFactor = $mandatoryTwoFactor;
diff --git a/apps/settings/lib/Controller/UsersController.php b/apps/settings/lib/Controller/UsersController.php
index 217abf0e156..8efd3eeb8ca 100644
--- a/apps/settings/lib/Controller/UsersController.php
+++ b/apps/settings/lib/Controller/UsersController.php
@@ -3,37 +3,10 @@
declare(strict_types=1);
/**
- * @copyright Copyright (c) 2016, ownCloud, Inc.
- *
- * @author Arthur Schiwon <blizzz@arthur-schiwon.de>
- * @author Bjoern Schiessle <bjoern@schiessle.org>
- * @author Christoph Wurst <christoph@winzerhof-wurst.at>
- * @author Daniel Calviño Sánchez <danxuliu@gmail.com>
- * @author Daniel Kesselberg <mail@danielkesselberg.de>
- * @author GretaD <gretadoci@gmail.com>
- * @author Joas Schilling <coding@schilljs.com>
- * @author John Molakvoæ <skjnldsv@protonmail.com>
- * @author Morris Jobke <hey@morrisjobke.de>
- * @author Roeland Jago Douma <roeland@famdouma.nl>
- * @author Vincent Petry <vincent@nextcloud.com>
- *
- * @license AGPL-3.0
- *
- * This code is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * 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, version 3,
- * along with this program. If not, see <http://www.gnu.org/licenses/>
- *
+ * SPDX-FileCopyrightText: 2019-2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
+ * SPDX-License-Identifier: AGPL-3.0-only
*/
-// FIXME: disabled for now to be able to inject IGroupManager and also use
-// getSubAdmin()
namespace OCA\Settings\Controller;
@@ -41,145 +14,112 @@ use InvalidArgumentException;
use OC\AppFramework\Http;
use OC\Encryption\Exceptions\ModuleDoesNotExistsException;
use OC\ForbiddenException;
-use OC\Group\Manager as GroupManager;
+use OC\Group\MetaData;
use OC\KnownUser\KnownUserService;
-use OC\L10N\Factory;
use OC\Security\IdentityProof\Manager;
use OC\User\Manager as UserManager;
use OCA\Settings\BackgroundJobs\VerifyUserData;
use OCA\Settings\Events\BeforeTemplateRenderedEvent;
+use OCA\Settings\Settings\Admin\Users;
use OCA\User_LDAP\User_Proxy;
use OCP\Accounts\IAccount;
use OCP\Accounts\IAccountManager;
use OCP\Accounts\PropertyDoesNotExistException;
use OCP\App\IAppManager;
use OCP\AppFramework\Controller;
+use OCP\AppFramework\Http\Attribute\AuthorizedAdminSetting;
+use OCP\AppFramework\Http\Attribute\NoAdminRequired;
+use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
+use OCP\AppFramework\Http\Attribute\OpenAPI;
+use OCP\AppFramework\Http\Attribute\PasswordConfirmationRequired;
+use OCP\AppFramework\Http\Attribute\UserRateLimit;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\Http\JSONResponse;
use OCP\AppFramework\Http\TemplateResponse;
+use OCP\AppFramework\Services\IInitialState;
use OCP\BackgroundJob\IJobList;
use OCP\Encryption\IManager;
use OCP\EventDispatcher\IEventDispatcher;
+use OCP\Group\ISubAdmin;
use OCP\IConfig;
+use OCP\IGroup;
use OCP\IGroupManager;
use OCP\IL10N;
+use OCP\INavigationManager;
use OCP\IRequest;
use OCP\IUser;
-use OCP\IUserManager;
use OCP\IUserSession;
use OCP\L10N\IFactory;
use OCP\Mail\IMailer;
+use OCP\Util;
use function in_array;
+#[OpenAPI(scope: OpenAPI::SCOPE_IGNORE)]
class UsersController extends Controller {
- /** @var UserManager */
- private $userManager;
- /** @var GroupManager */
- private $groupManager;
- /** @var IUserSession */
- private $userSession;
- /** @var IConfig */
- private $config;
- /** @var bool */
- private $isAdmin;
- /** @var IL10N */
- private $l10n;
- /** @var IMailer */
- private $mailer;
- /** @var Factory */
- private $l10nFactory;
- /** @var IAppManager */
- private $appManager;
- /** @var IAccountManager */
- private $accountManager;
- /** @var Manager */
- private $keyManager;
- /** @var IJobList */
- private $jobList;
- /** @var IManager */
- private $encryptionManager;
- /** @var KnownUserService */
- private $knownUserService;
- /** @var IEventDispatcher */
- private $dispatcher;
-
+ /** Limit for counting users for subadmins, to avoid spending too much time */
+ private const COUNT_LIMIT_FOR_SUBADMINS = 999;
public function __construct(
string $appName,
IRequest $request,
- IUserManager $userManager,
- IGroupManager $groupManager,
- IUserSession $userSession,
- IConfig $config,
- bool $isAdmin,
- IL10N $l10n,
- IMailer $mailer,
- IFactory $l10nFactory,
- IAppManager $appManager,
- IAccountManager $accountManager,
- Manager $keyManager,
- IJobList $jobList,
- IManager $encryptionManager,
- KnownUserService $knownUserService,
- IEventDispatcher $dispatcher
+ private UserManager $userManager,
+ private IGroupManager $groupManager,
+ private IUserSession $userSession,
+ private IConfig $config,
+ private IL10N $l10n,
+ private IMailer $mailer,
+ private IFactory $l10nFactory,
+ private IAppManager $appManager,
+ private IAccountManager $accountManager,
+ private Manager $keyManager,
+ private IJobList $jobList,
+ private IManager $encryptionManager,
+ private KnownUserService $knownUserService,
+ private IEventDispatcher $dispatcher,
+ private IInitialState $initialState,
) {
parent::__construct($appName, $request);
- $this->userManager = $userManager;
- $this->groupManager = $groupManager;
- $this->userSession = $userSession;
- $this->config = $config;
- $this->isAdmin = $isAdmin;
- $this->l10n = $l10n;
- $this->mailer = $mailer;
- $this->l10nFactory = $l10nFactory;
- $this->appManager = $appManager;
- $this->accountManager = $accountManager;
- $this->keyManager = $keyManager;
- $this->jobList = $jobList;
- $this->encryptionManager = $encryptionManager;
- $this->knownUserService = $knownUserService;
- $this->dispatcher = $dispatcher;
}
/**
- * @NoCSRFRequired
- * @NoAdminRequired
- *
* Display users list template
*
* @return TemplateResponse
*/
- public function usersListByGroup(): TemplateResponse {
- return $this->usersList();
+ #[NoAdminRequired]
+ #[NoCSRFRequired]
+ public function usersListByGroup(INavigationManager $navigationManager, ISubAdmin $subAdmin): TemplateResponse {
+ return $this->usersList($navigationManager, $subAdmin);
}
/**
- * @NoCSRFRequired
- * @NoAdminRequired
- *
* Display users list template
*
* @return TemplateResponse
*/
- public function usersList(): TemplateResponse {
+ #[NoAdminRequired]
+ #[NoCSRFRequired]
+ public function usersList(INavigationManager $navigationManager, ISubAdmin $subAdmin): TemplateResponse {
$user = $this->userSession->getUser();
$uid = $user->getUID();
+ $isAdmin = $this->groupManager->isAdmin($uid);
+ $isDelegatedAdmin = $this->groupManager->isDelegatedAdmin($uid);
- \OC::$server->getNavigationManager()->setActiveEntry('core_users');
+ $navigationManager->setActiveEntry('core_users');
/* SORT OPTION: SORT_USERCOUNT or SORT_GROUPNAME */
- $sortGroupsBy = \OC\Group\MetaData::SORT_USERCOUNT;
+ $sortGroupsBy = MetaData::SORT_USERCOUNT;
$isLDAPUsed = false;
- if ($this->config->getSystemValue('sort_groups_by_name', false)) {
- $sortGroupsBy = \OC\Group\MetaData::SORT_GROUPNAME;
+ if ($this->config->getSystemValueBool('sort_groups_by_name', false)) {
+ $sortGroupsBy = MetaData::SORT_GROUPNAME;
} else {
if ($this->appManager->isEnabledForUser('user_ldap')) {
- $isLDAPUsed =
- $this->groupManager->isBackendUsed('\OCA\User_LDAP\Group_Proxy');
+ $isLDAPUsed
+ = $this->groupManager->isBackendUsed('\OCA\User_LDAP\Group_Proxy');
if ($isLDAPUsed) {
// LDAP user count can be slow, so we sort by group name here
- $sortGroupsBy = \OC\Group\MetaData::SORT_GROUPNAME;
+ $sortGroupsBy = MetaData::SORT_GROUPNAME;
}
}
}
@@ -187,15 +127,23 @@ class UsersController extends Controller {
$canChangePassword = $this->canAdminChangeUserPasswords();
/* GROUPS */
- $groupsInfo = new \OC\Group\MetaData(
+ $groupsInfo = new MetaData(
$uid,
- $this->isAdmin,
+ $isAdmin,
+ $isDelegatedAdmin,
$this->groupManager,
$this->userSession
);
- $groupsInfo->setSorting($sortGroupsBy);
- [$adminGroup, $groups] = $groupsInfo->get();
+ $adminGroup = $this->groupManager->get('admin');
+ $adminGroupData = [
+ 'id' => $adminGroup->getGID(),
+ 'name' => $adminGroup->getDisplayName(),
+ 'usercount' => $sortGroupsBy === MetaData::SORT_USERCOUNT ? $adminGroup->count() : 0,
+ 'disabled' => $adminGroup->countDisabled(),
+ 'canAdd' => $adminGroup->canAddUser(),
+ 'canRemove' => $adminGroup->canRemoveUser(),
+ ];
if (!$isLDAPUsed && $this->appManager->isEnabledForUser('user_ldap')) {
$isLDAPUsed = (bool)array_reduce($this->userManager->getBackends(), function ($ldapFound, $backend) {
@@ -207,40 +155,41 @@ class UsersController extends Controller {
$userCount = 0;
if (!$isLDAPUsed) {
- if ($this->isAdmin) {
+ if ($isAdmin || $isDelegatedAdmin) {
$disabledUsers = $this->userManager->countDisabledUsers();
$userCount = array_reduce($this->userManager->countUsers(), function ($v, $w) {
return $v + (int)$w;
}, 0);
} else {
// User is subadmin !
- // Map group list to names to retrieve the countDisabledUsersOfGroups
- $userGroups = $this->groupManager->getUserGroups($user);
- $groupsNames = [];
-
- foreach ($groups as $key => $group) {
- // $userCount += (int)$group['usercount'];
- array_push($groupsNames, $group['name']);
- // we prevent subadmins from looking up themselves
- // so we lower the count of the groups he belongs to
- if (array_key_exists($group['id'], $userGroups)) {
- $groups[$key]['usercount']--;
- $userCount -= 1; // we also lower from one the total count
- }
- }
- $userCount += $this->userManager->countUsersOfGroups($groupsInfo->getGroups());
- $disabledUsers = $this->userManager->countDisabledUsersOfGroups($groupsNames);
+ [$userCount,$disabledUsers] = $this->userManager->countUsersAndDisabledUsersOfGroups($groupsInfo->getGroups(), self::COUNT_LIMIT_FOR_SUBADMINS);
}
- $userCount -= $disabledUsers;
+ if ($disabledUsers > 0) {
+ $userCount -= $disabledUsers;
+ }
}
+ $recentUsersGroup = [
+ 'id' => '__nc_internal_recent',
+ 'name' => $this->l10n->t('Recently active'),
+ 'usercount' => $this->userManager->countSeenUsers(),
+ ];
+
$disabledUsersGroup = [
'id' => 'disabled',
- 'name' => 'Disabled users',
+ 'name' => $this->l10n->t('Disabled accounts'),
'usercount' => $disabledUsers
];
+ if (!$isAdmin && !$isDelegatedAdmin) {
+ $subAdminGroups = array_map(
+ fn (IGroup $group) => ['id' => $group->getGID(), 'name' => $group->getDisplayName()],
+ $subAdmin->getSubAdminsGroups($user),
+ );
+ $subAdminGroups = array_values($subAdminGroups);
+ }
+
/* QUOTAS PRESETS */
$quotaPreset = $this->parseQuotaPreset($this->config->getAppValue('files', 'quota_preset', '1 GB, 5 GB, 10 GB'));
$allowUnlimitedQuota = $this->config->getAppValue('files', 'allow_unlimited_quota', '1') === '1';
@@ -257,13 +206,21 @@ class UsersController extends Controller {
/* LANGUAGES */
$languages = $this->l10nFactory->getLanguages();
+ /** Using LDAP or admins (system config) can enfore sorting by group name, in this case the frontend setting is overwritten */
+ $forceSortGroupByName = $sortGroupsBy === MetaData::SORT_GROUPNAME;
+
/* FINAL DATA */
$serverData = [];
// groups
- $serverData['groups'] = array_merge_recursive($adminGroup, [$disabledUsersGroup], $groups);
+ $serverData['systemGroups'] = [$adminGroupData, $recentUsersGroup, $disabledUsersGroup];
+ $serverData['subAdminGroups'] = $subAdminGroups ?? [];
// Various data
- $serverData['isAdmin'] = $this->isAdmin;
- $serverData['sortGroups'] = $sortGroupsBy;
+ $serverData['isAdmin'] = $isAdmin;
+ $serverData['isDelegatedAdmin'] = $isDelegatedAdmin;
+ $serverData['sortGroups'] = $forceSortGroupByName
+ ? MetaData::SORT_GROUPNAME
+ : (int)$this->config->getAppValue('core', 'group.sortBy', (string)MetaData::SORT_USERCOUNT);
+ $serverData['forceSortGroupByName'] = $forceSortGroupByName;
$serverData['quotaPreset'] = $quotaPreset;
$serverData['allowUnlimitedQuota'] = $allowUnlimitedQuota;
$serverData['userCount'] = $userCount;
@@ -277,7 +234,12 @@ class UsersController extends Controller {
$serverData['newUserRequireEmail'] = $this->config->getAppValue('core', 'newUser.requireEmail', 'no') === 'yes';
$serverData['newUserSendEmail'] = $this->config->getAppValue('core', 'newUser.sendEmail', 'yes') === 'yes';
- return new TemplateResponse('settings', 'settings-vue', ['serverData' => $serverData]);
+ $this->initialState->provideInitialState('usersSettings', $serverData);
+
+ Util::addStyle('settings', 'settings');
+ Util::addScript('settings', 'vue-settings-apps-users-management');
+
+ return new TemplateResponse('settings', 'settings/empty', ['pageTitle' => $this->l10n->t('Settings')]);
}
/**
@@ -286,8 +248,9 @@ class UsersController extends Controller {
*
* @return JSONResponse
*/
+ #[AuthorizedAdminSetting(settings:Users::class)]
public function setPreference(string $key, string $value): JSONResponse {
- $allowed = ['newUser.sendEmail'];
+ $allowed = ['newUser.sendEmail', 'group.sortBy'];
if (!in_array($key, $allowed, true)) {
return new JSONResponse([], Http::STATUS_FORBIDDEN);
}
@@ -341,9 +304,7 @@ class UsersController extends Controller {
}
/**
- * @NoAdminRequired
* @NoSubAdminRequired
- * @PasswordConfirmationRequired
*
* @param string|null $avatarScope
* @param string|null $displayname
@@ -358,26 +319,39 @@ class UsersController extends Controller {
* @param string|null $addressScope
* @param string|null $twitter
* @param string|null $twitterScope
+ * @param string|null $bluesky
+ * @param string|null $blueskyScope
* @param string|null $fediverse
* @param string|null $fediverseScope
+ * @param string|null $birthdate
+ * @param string|null $birthdateScope
*
* @return DataResponse
*/
+ #[NoAdminRequired]
+ #[PasswordConfirmationRequired]
+ #[UserRateLimit(limit: 5, period: 60)]
public function setUserSettings(?string $avatarScope = null,
- ?string $displayname = null,
- ?string $displaynameScope = null,
- ?string $phone = null,
- ?string $phoneScope = null,
- ?string $email = null,
- ?string $emailScope = null,
- ?string $website = null,
- ?string $websiteScope = null,
- ?string $address = null,
- ?string $addressScope = null,
- ?string $twitter = null,
- ?string $twitterScope = null,
- ?string $fediverse = null,
- ?string $fediverseScope = null
+ ?string $displayname = null,
+ ?string $displaynameScope = null,
+ ?string $phone = null,
+ ?string $phoneScope = null,
+ ?string $email = null,
+ ?string $emailScope = null,
+ ?string $website = null,
+ ?string $websiteScope = null,
+ ?string $address = null,
+ ?string $addressScope = null,
+ ?string $twitter = null,
+ ?string $twitterScope = null,
+ ?string $bluesky = null,
+ ?string $blueskyScope = null,
+ ?string $fediverse = null,
+ ?string $fediverseScope = null,
+ ?string $birthdate = null,
+ ?string $birthdateScope = null,
+ ?string $pronouns = null,
+ ?string $pronounsScope = null,
) {
$user = $this->userSession->getUser();
if (!$user instanceof IUser) {
@@ -385,7 +359,7 @@ class UsersController extends Controller {
[
'status' => 'error',
'data' => [
- 'message' => $this->l10n->t('Invalid user')
+ 'message' => $this->l10n->t('Invalid account')
]
],
Http::STATUS_UNAUTHORIZED
@@ -416,7 +390,10 @@ class UsersController extends Controller {
IAccountManager::PROPERTY_ADDRESS => ['value' => $address, 'scope' => $addressScope],
IAccountManager::PROPERTY_PHONE => ['value' => $phone, 'scope' => $phoneScope],
IAccountManager::PROPERTY_TWITTER => ['value' => $twitter, 'scope' => $twitterScope],
+ IAccountManager::PROPERTY_BLUESKY => ['value' => $bluesky, 'scope' => $blueskyScope],
IAccountManager::PROPERTY_FEDIVERSE => ['value' => $fediverse, 'scope' => $fediverseScope],
+ IAccountManager::PROPERTY_BIRTHDATE => ['value' => $birthdate, 'scope' => $birthdateScope],
+ IAccountManager::PROPERTY_PRONOUNS => ['value' => $pronouns, 'scope' => $pronounsScope],
];
$allowUserToChangeDisplayName = $this->config->getSystemValueBool('allow_user_to_change_display_name', true);
foreach ($updatable as $property => $data) {
@@ -425,10 +402,10 @@ class UsersController extends Controller {
continue;
}
$property = $userAccount->getProperty($property);
- if (null !== $data['value']) {
+ if ($data['value'] !== null) {
$property->setValue($data['value']);
}
- if (null !== $data['scope']) {
+ if ($data['scope'] !== null) {
$property->setScope($data['scope']);
}
}
@@ -456,14 +433,20 @@ class UsersController extends Controller {
'addressScope' => $userAccount->getProperty(IAccountManager::PROPERTY_ADDRESS)->getScope(),
'twitter' => $userAccount->getProperty(IAccountManager::PROPERTY_TWITTER)->getValue(),
'twitterScope' => $userAccount->getProperty(IAccountManager::PROPERTY_TWITTER)->getScope(),
+ 'bluesky' => $userAccount->getProperty(IAccountManager::PROPERTY_BLUESKY)->getValue(),
+ 'blueskyScope' => $userAccount->getProperty(IAccountManager::PROPERTY_BLUESKY)->getScope(),
'fediverse' => $userAccount->getProperty(IAccountManager::PROPERTY_FEDIVERSE)->getValue(),
'fediverseScope' => $userAccount->getProperty(IAccountManager::PROPERTY_FEDIVERSE)->getScope(),
+ 'birthdate' => $userAccount->getProperty(IAccountManager::PROPERTY_BIRTHDATE)->getValue(),
+ 'birthdateScope' => $userAccount->getProperty(IAccountManager::PROPERTY_BIRTHDATE)->getScope(),
+ 'pronouns' => $userAccount->getProperty(IAccountManager::PROPERTY_PRONOUNS)->getValue(),
+ 'pronounsScope' => $userAccount->getProperty(IAccountManager::PROPERTY_PRONOUNS)->getScope(),
'message' => $this->l10n->t('Settings saved'),
],
],
Http::STATUS_OK
);
- } catch (ForbiddenException | InvalidArgumentException | PropertyDoesNotExistException $e) {
+ } catch (ForbiddenException|InvalidArgumentException|PropertyDoesNotExistException $e) {
return new DataResponse([
'status' => 'error',
'data' => [
@@ -516,14 +499,14 @@ class UsersController extends Controller {
/**
* Set the mail address of a user
*
- * @NoAdminRequired
* @NoSubAdminRequired
- * @PasswordConfirmationRequired
*
* @param string $account
* @param bool $onlyVerificationCode only return verification code without updating the data
* @return DataResponse
*/
+ #[NoAdminRequired]
+ #[PasswordConfirmationRequired]
public function getVerificationCode(string $account, bool $onlyVerificationCode): DataResponse {
$user = $this->userSession->getUser();
diff --git a/apps/settings/lib/Controller/WebAuthnController.php b/apps/settings/lib/Controller/WebAuthnController.php
index 2692882301d..495b58e6a4b 100644
--- a/apps/settings/lib/Controller/WebAuthnController.php
+++ b/apps/settings/lib/Controller/WebAuthnController.php
@@ -3,26 +3,8 @@
declare(strict_types=1);
/**
- * @copyright Copyright (c) 2020, Roeland Jago Douma <roeland@famdouma.nl>
- *
- * @author Joas Schilling <coding@schilljs.com>
- * @author Roeland Jago Douma <roeland@famdouma.nl>
- *
- * @license GNU AGPL version 3 or any later version
- *
- * 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/>.
- *
+ * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\Settings\Controller;
@@ -30,46 +12,39 @@ use OC\Authentication\WebAuthn\Manager;
use OCA\Settings\AppInfo\Application;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http;
+use OCP\AppFramework\Http\Attribute\NoAdminRequired;
+use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
+use OCP\AppFramework\Http\Attribute\OpenAPI;
+use OCP\AppFramework\Http\Attribute\PasswordConfirmationRequired;
+use OCP\AppFramework\Http\Attribute\UseSession;
use OCP\AppFramework\Http\JSONResponse;
-use OCP\ILogger;
use OCP\IRequest;
use OCP\ISession;
use OCP\IUserSession;
+use Psr\Log\LoggerInterface;
use Webauthn\PublicKeyCredentialCreationOptions;
+#[OpenAPI(scope: OpenAPI::SCOPE_IGNORE)]
class WebAuthnController extends Controller {
private const WEBAUTHN_REGISTRATION = 'webauthn_registration';
- /** @var Manager */
- private $manager;
-
- /** @var IUserSession */
- private $userSession;
- /**
- * @var ISession
- */
- private $session;
- /**
- * @var ILogger
- */
- private $logger;
-
- public function __construct(IRequest $request, ILogger $logger, Manager $webAuthnManager, IUserSession $userSession, ISession $session) {
+ public function __construct(
+ IRequest $request,
+ private LoggerInterface $logger,
+ private Manager $manager,
+ private IUserSession $userSession,
+ private ISession $session,
+ ) {
parent::__construct(Application::APP_ID, $request);
-
- $this->manager = $webAuthnManager;
- $this->userSession = $userSession;
- $this->session = $session;
- $this->logger = $logger;
}
/**
- * @NoAdminRequired
* @NoSubAdminRequired
- * @PasswordConfirmationRequired
- * @UseSession
- * @NoCSRFRequired
*/
+ #[NoAdminRequired]
+ #[PasswordConfirmationRequired]
+ #[UseSession]
+ #[NoCSRFRequired]
public function startRegistration(): JSONResponse {
$this->logger->debug('Starting WebAuthn registration');
@@ -82,11 +57,11 @@ class WebAuthnController extends Controller {
}
/**
- * @NoAdminRequired
* @NoSubAdminRequired
- * @PasswordConfirmationRequired
- * @UseSession
*/
+ #[NoAdminRequired]
+ #[PasswordConfirmationRequired]
+ #[UseSession]
public function finishRegistration(string $name, string $data): JSONResponse {
$this->logger->debug('Finishing WebAuthn registration');
@@ -104,10 +79,10 @@ class WebAuthnController extends Controller {
}
/**
- * @NoAdminRequired
* @NoSubAdminRequired
- * @PasswordConfirmationRequired
*/
+ #[NoAdminRequired]
+ #[PasswordConfirmationRequired]
public function deleteRegistration(int $id): JSONResponse {
$this->logger->debug('Finishing WebAuthn registration');
diff --git a/apps/settings/lib/Events/BeforeTemplateRenderedEvent.php b/apps/settings/lib/Events/BeforeTemplateRenderedEvent.php
index 7d283cca1ab..b5feeffe30a 100644
--- a/apps/settings/lib/Events/BeforeTemplateRenderedEvent.php
+++ b/apps/settings/lib/Events/BeforeTemplateRenderedEvent.php
@@ -3,26 +3,8 @@
declare(strict_types=1);
/**
- * @copyright Copyright (c) 2020 Arthur Schiwon <blizzz@arthur-schiwon.de>
- *
- * @author Arthur Schiwon <blizzz@arthur-schiwon.de>
- * @author Morris Jobke <hey@morrisjobke.de>
- *
- * @license GNU AGPL version 3 or any later version
- *
- * 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/>.
- *
+ * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\Settings\Events;
diff --git a/apps/settings/lib/Hooks.php b/apps/settings/lib/Hooks.php
index a5be30be53f..f59013ca5e1 100644
--- a/apps/settings/lib/Hooks.php
+++ b/apps/settings/lib/Hooks.php
@@ -1,28 +1,8 @@
<?php
+
/**
- * @copyright Copyright (c) 2017 Joas Schilling <coding@schilljs.com>
- *
- * @author Arthur Schiwon <blizzz@arthur-schiwon.de>
- * @author Christoph Wurst <christoph@winzerhof-wurst.at>
- * @author Joas Schilling <coding@schilljs.com>
- * @author Morris Jobke <hey@morrisjobke.de>
- * @author Thomas Citharel <nextcloud@tcit.fr>
- *
- * @license GNU AGPL version 3 or any later version
- *
- * 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/>.
- *
+ * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\Settings;
@@ -40,43 +20,17 @@ use OCP\Mail\IMailer;
class Hooks {
- /** @var IActivityManager */
- protected $activityManager;
- /** @var IGroupManager|\OC\Group\Manager */
- protected $groupManager;
- /** @var IUserManager */
- protected $userManager;
- /** @var IUserSession */
- protected $userSession;
- /** @var IURLGenerator */
- protected $urlGenerator;
- /** @var IMailer */
- protected $mailer;
- /** @var IConfig */
- protected $config;
- /** @var IFactory */
- protected $languageFactory;
- /** @var Defaults */
- protected $defaults;
-
- public function __construct(IActivityManager $activityManager,
- IGroupManager $groupManager,
- IUserManager $userManager,
- IUserSession $userSession,
- IURLGenerator $urlGenerator,
- IMailer $mailer,
- IConfig $config,
- IFactory $languageFactory,
- Defaults $defaults) {
- $this->activityManager = $activityManager;
- $this->groupManager = $groupManager;
- $this->userManager = $userManager;
- $this->userSession = $userSession;
- $this->urlGenerator = $urlGenerator;
- $this->mailer = $mailer;
- $this->config = $config;
- $this->languageFactory = $languageFactory;
- $this->defaults = $defaults;
+ public function __construct(
+ protected IActivityManager $activityManager,
+ protected IGroupManager $groupManager,
+ protected IUserManager $userManager,
+ protected IUserSession $userSession,
+ protected IURLGenerator $urlGenerator,
+ protected IMailer $mailer,
+ protected IConfig $config,
+ protected IFactory $languageFactory,
+ protected Defaults $defaults,
+ ) {
}
/**
@@ -158,8 +112,8 @@ class Hooks {
* @throws \BadMethodCallException
*/
public function onChangeEmail(IUser $user, $oldMailAddress) {
- if ($oldMailAddress === $user->getEMailAddress() ||
- $user->getLastLogin() === 0) {
+ if ($oldMailAddress === $user->getEMailAddress()
+ || $user->getLastLogin() === 0) {
// Email didn't really change or user didn't login,
// so don't create activities and emails.
return;
@@ -170,6 +124,7 @@ class Hooks {
->setType('personal_settings')
->setAffectedUser($user->getUID());
+ $instanceName = $this->defaults->getName();
$instanceUrl = $this->urlGenerator->getAbsoluteURL('/');
$language = $this->languageFactory->getUserLanguage($user);
$l = $this->languageFactory->get('settings', $language);
@@ -206,7 +161,7 @@ class Hooks {
'instanceUrl' => $instanceUrl,
]);
- $template->setSubject($l->t('Email address for %1$s changed on %2$s', [$user->getDisplayName(), $instanceUrl]));
+ $template->setSubject($l->t('Email address for %1$s changed on %2$s', [$user->getDisplayName(), $instanceName]));
$template->addHeader();
$template->addHeading($l->t('Email address changed for %s', [$user->getDisplayName()]), false);
$template->addBodyText($text . ' ' . $l->t('If you did not request this, please contact an administrator.'));
diff --git a/apps/settings/lib/Listener/AppPasswordCreatedActivityListener.php b/apps/settings/lib/Listener/AppPasswordCreatedActivityListener.php
index 587d626ef97..a51eee1a799 100644
--- a/apps/settings/lib/Listener/AppPasswordCreatedActivityListener.php
+++ b/apps/settings/lib/Listener/AppPasswordCreatedActivityListener.php
@@ -3,25 +3,8 @@
declare(strict_types=1);
/**
- * @copyright 2021 Christoph Wurst <christoph@winzerhof-wurst.at>
- *
- * @author Christoph Wurst <christoph@winzerhof-wurst.at>
- *
- * @license GNU AGPL version 3 or any later version
- *
- * 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/>.
- *
+ * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\Settings\Listener;
@@ -38,21 +21,11 @@ use Psr\Log\LoggerInterface;
* @template-implements IEventListener<\OC\Authentication\Events\AppPasswordCreatedEvent>
*/
class AppPasswordCreatedActivityListener implements IEventListener {
- /** @var IActivityManager */
- private $activityManager;
-
- /** @var IUserSession */
- private $userSession;
-
- /** @var LoggerInterface */
- private $logger;
-
- public function __construct(IActivityManager $activityManager,
- IUserSession $userSession,
- LoggerInterface $logger) {
- $this->activityManager = $activityManager;
- $this->userSession = $userSession;
- $this->logger = $logger;
+ public function __construct(
+ private IActivityManager $activityManager,
+ private IUserSession $userSession,
+ private LoggerInterface $logger,
+ ) {
}
public function handle(Event $event): void {
diff --git a/apps/settings/lib/Listener/GroupRemovedListener.php b/apps/settings/lib/Listener/GroupRemovedListener.php
index c42cfd23ade..c1b3f888ea8 100644
--- a/apps/settings/lib/Listener/GroupRemovedListener.php
+++ b/apps/settings/lib/Listener/GroupRemovedListener.php
@@ -1,26 +1,9 @@
<?php
+
/**
- * @copyright Copyright (c) 2021 Carl Schwan <carl@carlschwan.eu>
- *
- * @author Carl Schwan <carl@carlschwan.eu>
- *
- * @license GNU AGPL version 3 or any later version
- *
- * 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 <https://www.gnu.org/licenses/>.
- *
+ * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
-
namespace OCA\Settings\Listener;
use OCA\Settings\Service\AuthorizedGroupService;
@@ -28,13 +11,12 @@ use OCP\EventDispatcher\Event;
use OCP\EventDispatcher\IEventListener;
use OCP\Group\Events\GroupDeletedEvent;
+/** @template-implements IEventListener<GroupDeletedEvent> */
class GroupRemovedListener implements IEventListener {
- /** @var AuthorizedGroupService $authorizedGroupService */
- private $authorizedGroupService;
-
- public function __construct(AuthorizedGroupService $authorizedGroupService) {
- $this->authorizedGroupService = $authorizedGroupService;
+ public function __construct(
+ private AuthorizedGroupService $authorizedGroupService,
+ ) {
}
/**
diff --git a/apps/settings/lib/Listener/MailProviderListener.php b/apps/settings/lib/Listener/MailProviderListener.php
new file mode 100644
index 00000000000..61446f1e6cb
--- /dev/null
+++ b/apps/settings/lib/Listener/MailProviderListener.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\Listener;
+
+use OCA\Settings\AppInfo\Application;
+use OCP\EventDispatcher\Event;
+use OCP\EventDispatcher\IEventListener;
+use OCP\IAppConfig;
+use OCP\Settings\Events\DeclarativeSettingsGetValueEvent;
+use OCP\Settings\Events\DeclarativeSettingsSetValueEvent;
+
+/** @template-implements IEventListener<DeclarativeSettingsGetValueEvent|DeclarativeSettingsSetValueEvent> */
+class MailProviderListener implements IEventListener {
+
+ public function __construct(
+ private IAppConfig $config,
+ ) {
+ }
+
+ public function handle(Event $event): void {
+
+ /** @var DeclarativeSettingsGetValueEvent|DeclarativeSettingsSetValueEvent $event */
+ if ($event->getApp() !== Application::APP_ID) {
+ return;
+ }
+
+ if ($event instanceof DeclarativeSettingsGetValueEvent) {
+ $this->handleGetValue($event);
+ return;
+ }
+
+ if ($event instanceof DeclarativeSettingsSetValueEvent) {
+ $this->handleSetValue($event);
+ return;
+ }
+
+ }
+
+ private function handleGetValue(DeclarativeSettingsGetValueEvent $event): void {
+
+ if ($event->getFieldId() === 'mail_providers_enabled') {
+ $event->setValue((int)$this->config->getValueBool('core', 'mail_providers_enabled', true));
+ }
+
+ }
+
+ private function handleSetValue(DeclarativeSettingsSetValueEvent $event): void {
+
+ if ($event->getFieldId() === 'mail_providers_enabled') {
+ $this->config->setValueBool('core', 'mail_providers_enabled', (bool)$event->getValue());
+ $event->stopPropagation();
+ }
+
+ }
+
+}
diff --git a/apps/settings/lib/Listener/UserAddedToGroupActivityListener.php b/apps/settings/lib/Listener/UserAddedToGroupActivityListener.php
index a09ba38396c..87cd66efdbc 100644
--- a/apps/settings/lib/Listener/UserAddedToGroupActivityListener.php
+++ b/apps/settings/lib/Listener/UserAddedToGroupActivityListener.php
@@ -3,25 +3,8 @@
declare(strict_types=1);
/**
- * @copyright Copyright (c) 2021 Roeland Jago Douma <roeland@famdouma.nl>
- *
- * @author Roeland Jago Douma <roeland@famdouma.nl>
- *
- * @license GNU AGPL version 3 or any later version
- *
- * 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/>.
- *
+ * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\Settings\Listener;
@@ -34,25 +17,14 @@ use OCP\Group\Events\UserAddedEvent;
use OCP\IUser;
use OCP\IUserSession;
+/** @template-implements IEventListener<UserAddedEvent> */
class UserAddedToGroupActivityListener implements IEventListener {
- /** @var Manager */
- private $groupManager;
-
- /** @var IManager */
- private $activityManager;
-
- /** @var IUserSession */
- private $userSession;
-
public function __construct(
- Manager $groupManager,
- IManager $activityManager,
- IUserSession $userSession
+ private Manager $groupManager,
+ private IManager $activityManager,
+ private IUserSession $userSession,
) {
- $this->groupManager = $groupManager;
- $this->activityManager = $activityManager;
- $this->userSession = $userSession;
}
public function handle(Event $event): void {
diff --git a/apps/settings/lib/Listener/UserRemovedFromGroupActivityListener.php b/apps/settings/lib/Listener/UserRemovedFromGroupActivityListener.php
index 5f3139cbbba..803d86d555d 100644
--- a/apps/settings/lib/Listener/UserRemovedFromGroupActivityListener.php
+++ b/apps/settings/lib/Listener/UserRemovedFromGroupActivityListener.php
@@ -3,25 +3,8 @@
declare(strict_types=1);
/**
- * @copyright Copyright (c) 2021 Roeland Jago Douma <roeland@famdouma.nl>
- *
- * @author Roeland Jago Douma <roeland@famdouma.nl>
- *
- * @license GNU AGPL version 3 or any later version
- *
- * 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/>.
- *
+ * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\Settings\Listener;
@@ -34,25 +17,14 @@ use OCP\Group\Events\UserRemovedEvent;
use OCP\IUser;
use OCP\IUserSession;
+/** @template-implements IEventListener<UserRemovedEvent> */
class UserRemovedFromGroupActivityListener implements IEventListener {
- /** @var Manager */
- private $groupManager;
-
- /** @var IManager */
- private $activityManager;
-
- /** @var IUserSession */
- private $userSession;
-
public function __construct(
- Manager $groupManager,
- IManager $activityManager,
- IUserSession $userSession
+ private Manager $groupManager,
+ private IManager $activityManager,
+ private IUserSession $userSession,
) {
- $this->groupManager = $groupManager;
- $this->activityManager = $activityManager;
- $this->userSession = $userSession;
}
public function handle(Event $event): void {
diff --git a/apps/settings/lib/Mailer/NewUserMailHelper.php b/apps/settings/lib/Mailer/NewUserMailHelper.php
index b91aea4391b..202495a020e 100644
--- a/apps/settings/lib/Mailer/NewUserMailHelper.php
+++ b/apps/settings/lib/Mailer/NewUserMailHelper.php
@@ -1,31 +1,8 @@
<?php
+
/**
- * @copyright Copyright (c) 2017 Lukas Reschke <lukas@statuscode.ch>
- *
- * @author Arthur Schiwon <blizzz@arthur-schiwon.de>
- * @author Christoph Wurst <christoph@winzerhof-wurst.at>
- * @author Joas Schilling <coding@schilljs.com>
- * @author Julius Härtl <jus@bitgrid.net>
- * @author Lukas Reschke <lukas@statuscode.ch>
- * @author Morris Jobke <hey@morrisjobke.de>
- * @author Roeland Jago Douma <roeland@famdouma.nl>
- * @author zulan <git@zulan.net>
- *
- * @license GNU AGPL version 3 or any later version
- *
- * 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/>.
- *
+ * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\Settings\Mailer;
@@ -42,25 +19,6 @@ use OCP\Security\ICrypto;
use OCP\Security\ISecureRandom;
class NewUserMailHelper {
- /** @var Defaults */
- private $themingDefaults;
- /** @var IURLGenerator */
- private $urlGenerator;
- /** @var IFactory */
- private $l10nFactory;
- /** @var IMailer */
- private $mailer;
- /** @var ISecureRandom */
- private $secureRandom;
- /** @var ITimeFactory */
- private $timeFactory;
- /** @var IConfig */
- private $config;
- /** @var ICrypto */
- private $crypto;
- /** @var string */
- private $fromAddress;
-
/**
* @param Defaults $themingDefaults
* @param IURLGenerator $urlGenerator
@@ -72,24 +30,17 @@ class NewUserMailHelper {
* @param ICrypto $crypto
* @param string $fromAddress
*/
- public function __construct(Defaults $themingDefaults,
- IURLGenerator $urlGenerator,
- IFactory $l10nFactory,
- IMailer $mailer,
- ISecureRandom $secureRandom,
- ITimeFactory $timeFactory,
- IConfig $config,
- ICrypto $crypto,
- $fromAddress) {
- $this->themingDefaults = $themingDefaults;
- $this->urlGenerator = $urlGenerator;
- $this->l10nFactory = $l10nFactory;
- $this->mailer = $mailer;
- $this->secureRandom = $secureRandom;
- $this->timeFactory = $timeFactory;
- $this->config = $config;
- $this->crypto = $crypto;
- $this->fromAddress = $fromAddress;
+ public function __construct(
+ private Defaults $themingDefaults,
+ private IURLGenerator $urlGenerator,
+ private IFactory $l10nFactory,
+ private IMailer $mailer,
+ private ISecureRandom $secureRandom,
+ private ITimeFactory $timeFactory,
+ private IConfig $config,
+ private ICrypto $crypto,
+ private $fromAddress,
+ ) {
}
/**
@@ -108,7 +59,7 @@ class NewUserMailHelper {
ISecureRandom::CHAR_ALPHANUMERIC
);
$tokenValue = $this->timeFactory->getTime() . ':' . $token;
- $mailAddress = (null !== $user->getEMailAddress()) ? $user->getEMailAddress() : '';
+ $mailAddress = ($user->getEMailAddress() !== null) ? $user->getEMailAddress() : '';
$encryptedValue = $this->crypto->encrypt($tokenValue, $mailAddress . $this->config->getSystemValue('secret'));
$this->config->setUserValue($user->getUID(), 'core', 'lostpassword', $encryptedValue);
$link = $this->urlGenerator->linkToRouteAbsolute('core.lost.resetform', ['userId' => $user->getUID(), 'token' => $token]);
@@ -134,7 +85,7 @@ class NewUserMailHelper {
}
$emailTemplate->addBodyText($l10n->t('Welcome to your %s account, you can add, protect, and share your data.', [$this->themingDefaults->getName()]));
if ($user->getBackendClassName() !== 'LDAP') {
- $emailTemplate->addBodyText($l10n->t('Your username is: %s', [$userId]));
+ $emailTemplate->addBodyText($l10n->t('Your Login is: %s', [$userId]));
}
if ($generatePasswordResetToken) {
$leftButtonText = $l10n->t('Set your password');
@@ -170,7 +121,7 @@ class NewUserMailHelper {
* @throws \Exception If mail could not be sent
*/
public function sendMail(IUser $user,
- IEMailTemplate $emailTemplate): void {
+ IEMailTemplate $emailTemplate): void {
// Be sure to never try to send to an empty e-mail
$email = $user->getEMailAddress();
diff --git a/apps/settings/lib/Middleware/SubadminMiddleware.php b/apps/settings/lib/Middleware/SubadminMiddleware.php
index c4f3ae3c7c2..02d68e138da 100644
--- a/apps/settings/lib/Middleware/SubadminMiddleware.php
+++ b/apps/settings/lib/Middleware/SubadminMiddleware.php
@@ -1,28 +1,13 @@
<?php
+
+declare(strict_types=1);
+
/**
- * @copyright Copyright (c) 2016, ownCloud, Inc.
- *
- * @author Christoph Wurst <christoph@winzerhof-wurst.at>
- * @author Joas Schilling <coding@schilljs.com>
- * @author Lukas Reschke <lukas@statuscode.ch>
- * @author Morris Jobke <hey@morrisjobke.de>
- * @author Roeland Jago Douma <roeland@famdouma.nl>
- *
- * @license AGPL-3.0
- *
- * This code is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * 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, version 3,
- * along with this program. If not, see <http://www.gnu.org/licenses/>
- *
+ * SPDX-FileCopyrightText: 2019-2024 Nextcloud GmbH and Nextcloud contributors/**
+ * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
+ * SPDX-License-Identifier: AGPL-3.0-only
*/
+
namespace OCA\Settings\Middleware;
use OC\AppFramework\Http;
@@ -31,31 +16,29 @@ use OC\AppFramework\Utility\ControllerMethodReflector;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http\TemplateResponse;
use OCP\AppFramework\Middleware;
+use OCP\Group\ISubAdmin;
use OCP\IL10N;
+use OCP\IUserSession;
/**
* Verifies whether an user has at least subadmin rights.
* To bypass use the `@NoSubAdminRequired` annotation
*/
class SubadminMiddleware extends Middleware {
- /** @var bool */
- protected $isSubAdmin;
- /** @var ControllerMethodReflector */
- protected $reflector;
- /** @var IL10N */
- private $l10n;
+ public function __construct(
+ protected ControllerMethodReflector $reflector,
+ protected IUserSession $userSession,
+ protected ISubAdmin $subAdminManager,
+ private IL10N $l10n,
+ ) {
+ }
- /**
- * @param ControllerMethodReflector $reflector
- * @param bool $isSubAdmin
- * @param IL10N $l10n
- */
- public function __construct(ControllerMethodReflector $reflector,
- $isSubAdmin,
- IL10N $l10n) {
- $this->reflector = $reflector;
- $this->isSubAdmin = $isSubAdmin;
- $this->l10n = $l10n;
+ private function isSubAdmin(): bool {
+ $userObject = $this->userSession->getUser();
+ if ($userObject === null) {
+ return false;
+ }
+ return $this->subAdminManager->isSubAdmin($userObject);
}
/**
@@ -66,8 +49,8 @@ class SubadminMiddleware extends Middleware {
*/
public function beforeController($controller, $methodName) {
if (!$this->reflector->hasAnnotation('NoSubAdminRequired') && !$this->reflector->hasAnnotation('AuthorizedAdminSetting')) {
- if (!$this->isSubAdmin) {
- throw new NotAdminException($this->l10n->t('Logged in user must be a subadmin'));
+ if (!$this->isSubAdmin()) {
+ throw new NotAdminException($this->l10n->t('Logged in account must be a sub admin'));
}
}
}
diff --git a/apps/settings/lib/ResponseDefinitions.php b/apps/settings/lib/ResponseDefinitions.php
new file mode 100644
index 00000000000..12adefda91f
--- /dev/null
+++ b/apps/settings/lib/ResponseDefinitions.php
@@ -0,0 +1,40 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCA\Settings;
+
+/**
+ * @psalm-type SettingsDeclarativeFormField = array{
+ * id: string,
+ * title: string,
+ * description?: string,
+ * type: 'text'|'password'|'email'|'tel'|'url'|'number'|'checkbox'|'multi-checkbox'|'radio'|'select'|'multi-select',
+ * placeholder?: string,
+ * label?: string,
+ * default: mixed,
+ * options?: list<string|array{name: string, value: mixed}>,
+ * value: string|int|float|bool|list<string>,
+ * sensitive?: boolean,
+ * }
+ *
+ * @psalm-type SettingsDeclarativeForm = array{
+ * id: string,
+ * priority: int,
+ * section_type: 'admin'|'personal',
+ * section_id: string,
+ * storage_type: 'internal'|'external',
+ * title: string,
+ * description?: string,
+ * doc_url?: string,
+ * app: string,
+ * fields: list<SettingsDeclarativeFormField>,
+ * }
+ */
+class ResponseDefinitions {
+}
diff --git a/apps/settings/lib/Search/AppSearch.php b/apps/settings/lib/Search/AppSearch.php
index fa3d884e80d..19c2bce5451 100644
--- a/apps/settings/lib/Search/AppSearch.php
+++ b/apps/settings/lib/Search/AppSearch.php
@@ -3,25 +3,8 @@
declare(strict_types=1);
/**
- * @copyright Copyright (c) 2020 Joas Schilling <coding@schilljs.com>
- *
- * @author Joas Schilling <coding@schilljs.com>
- *
- * @license GNU AGPL version 3 or any later version
- *
- * 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/>.
- *
+ * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\Settings\Search;
@@ -34,56 +17,43 @@ use OCP\Search\SearchResult;
use OCP\Search\SearchResultEntry;
class AppSearch implements IProvider {
-
- /** @var INavigationManager */
- protected $navigationManager;
-
- /** @var IL10N */
- protected $l;
-
- public function __construct(INavigationManager $navigationManager,
- IL10N $l) {
- $this->navigationManager = $navigationManager;
- $this->l = $l;
+ public function __construct(
+ protected INavigationManager $navigationManager,
+ protected IL10N $l,
+ ) {
}
- /**
- * @inheritDoc
- */
public function getId(): string {
return 'settings_apps';
}
- /**
- * @inheritDoc
- */
public function getName(): string {
return $this->l->t('Apps');
}
- /**
- * @inheritDoc
- */
public function getOrder(string $route, array $routeParameters): int {
- return -50;
+ return $route === 'settings.AppSettings.viewApps' ? -50 : 100;
}
- /**
- * @inheritDoc
- */
public function search(IUser $user, ISearchQuery $query): SearchResult {
$entries = $this->navigationManager->getAll('all');
+ $searchTitle = $this->l->t('Apps');
+ $term = $query->getFilter('term')?->get();
+ if (empty($term)) {
+ return SearchResult::complete($searchTitle, []);
+ }
+
$result = [];
foreach ($entries as $entry) {
if (
- stripos($entry['name'], $query->getTerm()) === false &&
- stripos($entry['id'], $query->getTerm()) === false
+ stripos($entry['name'], $term) === false
+ && stripos($entry['id'], $term) === false
) {
continue;
}
- if (strpos($query->getRoute(), $entry['id'] . '.') === 0) {
+ if (str_starts_with($query->getRoute(), $entry['id'] . '.')) {
// Skip the current app, unlikely this is intended
continue;
}
@@ -102,9 +72,6 @@ class AppSearch implements IProvider {
);
}
- return SearchResult::complete(
- $this->l->t('Apps'),
- $result
- );
+ return SearchResult::complete($searchTitle, $result);
}
}
diff --git a/apps/settings/lib/Search/SectionSearch.php b/apps/settings/lib/Search/SectionSearch.php
index 77b9bc025d6..52f0c9b08db 100644
--- a/apps/settings/lib/Search/SectionSearch.php
+++ b/apps/settings/lib/Search/SectionSearch.php
@@ -3,27 +3,8 @@
declare(strict_types=1);
/**
- * @copyright Copyright (c) 2020 Joas Schilling <coding@schilljs.com>
- *
- * @author Joas Schilling <coding@schilljs.com>
- * @author John Molakvoæ <skjnldsv@protonmail.com>
- * @author Morris Jobke <hey@morrisjobke.de>
- *
- * @license GNU AGPL version 3 or any later version
- *
- * 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/>.
- *
+ * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\Settings\Search;
@@ -40,26 +21,12 @@ use OCP\Settings\IManager;
class SectionSearch implements IProvider {
- /** @var IManager */
- protected $settingsManager;
-
- /** @var IGroupManager */
- protected $groupManager;
-
- /** @var IURLGenerator */
- protected $urlGenerator;
-
- /** @var IL10N */
- protected $l;
-
- public function __construct(IManager $settingsManager,
- IGroupManager $groupManager,
- IURLGenerator $urlGenerator,
- IL10N $l) {
- $this->settingsManager = $settingsManager;
- $this->groupManager = $groupManager;
- $this->urlGenerator = $urlGenerator;
- $this->l = $l;
+ public function __construct(
+ protected IManager $settingsManager,
+ protected IGroupManager $groupManager,
+ protected IURLGenerator $urlGenerator,
+ protected IL10N $l,
+ ) {
}
/**
@@ -93,9 +60,25 @@ class SectionSearch implements IProvider {
public function search(IUser $user, ISearchQuery $query): SearchResult {
$isAdmin = $this->groupManager->isAdmin($user->getUID());
+ $personalSections = $this->settingsManager->getPersonalSections();
+ foreach ($personalSections as $priority => $sections) {
+ $personalSections[$priority] = array_values(array_filter(
+ $sections,
+ fn (IIconSection $section) => !empty($this->settingsManager->getPersonalSettings($section->getID())),
+ ));
+ }
+
+ $adminSections = $this->settingsManager->getAdminSections();
+ foreach ($adminSections as $priority => $sections) {
+ $adminSections[$priority] = array_values(array_filter(
+ $sections,
+ fn (IIconSection $section) => !empty($this->settingsManager->getAllowedAdminSettings($section->getID(), $user)),
+ ));
+ }
+
$result = $this->searchSections(
$query,
- $this->settingsManager->getPersonalSections(),
+ $personalSections,
$isAdmin ? $this->l->t('Personal') : '',
'settings.PersonalSettings.index'
);
@@ -103,7 +86,7 @@ class SectionSearch implements IProvider {
if ($this->groupManager->isAdmin($user->getUID())) {
$result = array_merge($result, $this->searchSections(
$query,
- $this->settingsManager->getAdminSections(),
+ $adminSections,
$this->l->t('Administration'),
'settings.AdminSettings.index'
));
@@ -127,8 +110,8 @@ class SectionSearch implements IProvider {
foreach ($sections as $priority => $sectionsByPriority) {
foreach ($sectionsByPriority as $section) {
if (
- stripos($section->getName(), $query->getTerm()) === false &&
- stripos($section->getID(), $query->getTerm()) === false
+ stripos($section->getName(), $query->getTerm()) === false
+ && stripos($section->getID(), $query->getTerm()) === false
) {
continue;
}
diff --git a/apps/settings/lib/Search/UserSearch.php b/apps/settings/lib/Search/UserSearch.php
new file mode 100644
index 00000000000..5326f4cefff
--- /dev/null
+++ b/apps/settings/lib/Search/UserSearch.php
@@ -0,0 +1,40 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\Settings\Search;
+
+use OCP\IL10N;
+use OCP\IUser;
+use OCP\Search\IProvider;
+use OCP\Search\ISearchQuery;
+use OCP\Search\SearchResult;
+
+class UserSearch implements IProvider {
+ public function __construct(
+ private IL10N $l,
+ ) {
+ }
+
+ public function getId(): string {
+ return 'users';
+ }
+
+ public function getName(): string {
+ return $this->l->t('Users');
+ }
+
+ public function getOrder(string $route, array $routeParameters): ?int {
+ return str_starts_with($route, 'settings.Users.usersList')
+ ? 300
+ : null;
+ }
+
+ public function search(IUser $user, ISearchQuery $query): SearchResult {
+ return SearchResult::complete($this->l->t('Users'), []);
+ }
+}
diff --git a/apps/settings/lib/Sections/Admin/Additional.php b/apps/settings/lib/Sections/Admin/Additional.php
index d98312fa35d..0d83a98bbe5 100644
--- a/apps/settings/lib/Sections/Admin/Additional.php
+++ b/apps/settings/lib/Sections/Admin/Additional.php
@@ -3,25 +3,8 @@
declare(strict_types=1);
/**
- * @copyright Copyright (c) 2020, Roeland Jago Douma <roeland@famdouma.nl>
- *
- * @author Roeland Jago Douma <roeland@famdouma.nl>
- *
- * @license GNU AGPL version 3 or any later version
- *
- * 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/>.
- *
+ * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\Settings\Sections\Admin;
@@ -31,15 +14,10 @@ use OCP\Settings\IIconSection;
class Additional implements IIconSection {
- /** @var IL10N */
- private $l;
-
- /** @var IURLGenerator */
- private $urlGenerator;
-
- public function __construct(IL10N $l, IURLGenerator $urlGenerator) {
- $this->l = $l;
- $this->urlGenerator = $urlGenerator;
+ public function __construct(
+ private IL10N $l,
+ private IURLGenerator $urlGenerator,
+ ) {
}
public function getIcon(): string {
diff --git a/apps/settings/lib/Sections/Admin/ArtificialIntelligence.php b/apps/settings/lib/Sections/Admin/ArtificialIntelligence.php
new file mode 100644
index 00000000000..2a300c260c0
--- /dev/null
+++ b/apps/settings/lib/Sections/Admin/ArtificialIntelligence.php
@@ -0,0 +1,38 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\Settings\Sections\Admin;
+
+use OCP\IL10N;
+use OCP\IURLGenerator;
+use OCP\Settings\IIconSection;
+
+class ArtificialIntelligence implements IIconSection {
+
+ public function __construct(
+ private IL10N $l,
+ private IURLGenerator $urlGenerator,
+ ) {
+ }
+
+ public function getIcon(): string {
+ return $this->urlGenerator->imagePath('settings', 'ai.svg');
+ }
+
+ public function getID(): string {
+ return 'ai';
+ }
+
+ public function getName(): string {
+ return $this->l->t('Assistant');
+ }
+
+ public function getPriority(): int {
+ return 40;
+ }
+}
diff --git a/apps/settings/lib/Sections/Admin/Delegation.php b/apps/settings/lib/Sections/Admin/Delegation.php
index b88a359c8e8..0dd3b48c20b 100644
--- a/apps/settings/lib/Sections/Admin/Delegation.php
+++ b/apps/settings/lib/Sections/Admin/Delegation.php
@@ -1,24 +1,8 @@
<?php
+
/**
- * @copyright Copyright (c) 2021 Nextcloud GmbH
- *
- * @author Carl Schwan <carl@carlschwan.eu>
- *
- * @license GNU AGPL version 3 or any later version
- *
- * 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/>.
- *
+ * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\Settings\Sections\Admin;
@@ -28,18 +12,14 @@ use OCP\IURLGenerator;
use OCP\Settings\IIconSection;
class Delegation implements IIconSection {
- /** @var IL10N */
- private $l;
- /** @var IURLGenerator */
- private $url;
-
/**
* @param IURLGenerator $url
* @param IL10N $l
*/
- public function __construct(IURLGenerator $url, IL10N $l) {
- $this->url = $url;
- $this->l = $l;
+ public function __construct(
+ private IURLGenerator $url,
+ private IL10N $l,
+ ) {
}
/**
diff --git a/apps/settings/lib/Sections/Admin/Groupware.php b/apps/settings/lib/Sections/Admin/Groupware.php
index 5855c156bf2..57d92b9cc72 100644
--- a/apps/settings/lib/Sections/Admin/Groupware.php
+++ b/apps/settings/lib/Sections/Admin/Groupware.php
@@ -3,25 +3,8 @@
declare(strict_types=1);
/**
- * @copyright Copyright (c) 2020, Roeland Jago Douma <roeland@famdouma.nl>
- *
- * @author Roeland Jago Douma <roeland@famdouma.nl>
- *
- * @license GNU AGPL version 3 or any later version
- *
- * 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/>.
- *
+ * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\Settings\Sections\Admin;
@@ -31,15 +14,10 @@ use OCP\Settings\IIconSection;
class Groupware implements IIconSection {
- /** @var IL10N */
- private $l;
-
- /** @var IURLGenerator */
- private $urlGenerator;
-
- public function __construct(IL10N $l, IURLGenerator $urlGenerator) {
- $this->l = $l;
- $this->urlGenerator = $urlGenerator;
+ public function __construct(
+ private IL10N $l,
+ private IURLGenerator $urlGenerator,
+ ) {
}
public function getIcon(): string {
diff --git a/apps/settings/lib/Sections/Admin/Overview.php b/apps/settings/lib/Sections/Admin/Overview.php
index e6a6e4b4033..0145a2eca93 100644
--- a/apps/settings/lib/Sections/Admin/Overview.php
+++ b/apps/settings/lib/Sections/Admin/Overview.php
@@ -3,25 +3,8 @@
declare(strict_types=1);
/**
- * @copyright Copyright (c) 2020, Roeland Jago Douma <roeland@famdouma.nl>
- *
- * @author Roeland Jago Douma <roeland@famdouma.nl>
- *
- * @license GNU AGPL version 3 or any later version
- *
- * 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/>.
- *
+ * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\Settings\Sections\Admin;
@@ -31,15 +14,10 @@ use OCP\Settings\IIconSection;
class Overview implements IIconSection {
- /** @var IL10N */
- private $l;
-
- /** @var IURLGenerator */
- private $urlGenerator;
-
- public function __construct(IL10N $l, IURLGenerator $urlGenerator) {
- $this->l = $l;
- $this->urlGenerator = $urlGenerator;
+ public function __construct(
+ private IL10N $l,
+ private IURLGenerator $urlGenerator,
+ ) {
}
public function getIcon(): string {
diff --git a/apps/settings/lib/Sections/Admin/Security.php b/apps/settings/lib/Sections/Admin/Security.php
index 10ddc2658f2..10027be32fb 100644
--- a/apps/settings/lib/Sections/Admin/Security.php
+++ b/apps/settings/lib/Sections/Admin/Security.php
@@ -3,25 +3,8 @@
declare(strict_types=1);
/**
- * @copyright Copyright (c) 2020, Roeland Jago Douma <roeland@famdouma.nl>
- *
- * @author Roeland Jago Douma <roeland@famdouma.nl>
- *
- * @license GNU AGPL version 3 or any later version
- *
- * 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/>.
- *
+ * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\Settings\Sections\Admin;
@@ -31,15 +14,10 @@ use OCP\Settings\IIconSection;
class Security implements IIconSection {
- /** @var IL10N */
- private $l;
-
- /** @var IURLGenerator */
- private $urlGenerator;
-
- public function __construct(IL10N $l, IURLGenerator $urlGenerator) {
- $this->l = $l;
- $this->urlGenerator = $urlGenerator;
+ public function __construct(
+ private IL10N $l,
+ private IURLGenerator $urlGenerator,
+ ) {
}
public function getIcon(): string {
diff --git a/apps/settings/lib/Sections/Admin/Server.php b/apps/settings/lib/Sections/Admin/Server.php
index 1a573dfbf06..c6a02efa4e3 100644
--- a/apps/settings/lib/Sections/Admin/Server.php
+++ b/apps/settings/lib/Sections/Admin/Server.php
@@ -3,25 +3,8 @@
declare(strict_types=1);
/**
- * @copyright Copyright (c) 2020, Roeland Jago Douma <roeland@famdouma.nl>
- *
- * @author Roeland Jago Douma <roeland@famdouma.nl>
- *
- * @license GNU AGPL version 3 or any later version
- *
- * 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/>.
- *
+ * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\Settings\Sections\Admin;
@@ -31,15 +14,10 @@ use OCP\Settings\IIconSection;
class Server implements IIconSection {
- /** @var IL10N */
- private $l;
-
- /** @var IURLGenerator */
- private $urlGenerator;
-
- public function __construct(IL10N $l, IURLGenerator $urlGenerator) {
- $this->l = $l;
- $this->urlGenerator = $urlGenerator;
+ public function __construct(
+ private IL10N $l,
+ private IURLGenerator $urlGenerator,
+ ) {
}
public function getIcon(): string {
diff --git a/apps/settings/lib/Sections/Admin/Sharing.php b/apps/settings/lib/Sections/Admin/Sharing.php
index d4ee762aba1..c7598bb1157 100644
--- a/apps/settings/lib/Sections/Admin/Sharing.php
+++ b/apps/settings/lib/Sections/Admin/Sharing.php
@@ -3,25 +3,8 @@
declare(strict_types=1);
/**
- * @copyright Copyright (c) 2020, Roeland Jago Douma <roeland@famdouma.nl>
- *
- * @author Roeland Jago Douma <roeland@famdouma.nl>
- *
- * @license GNU AGPL version 3 or any later version
- *
- * 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/>.
- *
+ * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\Settings\Sections\Admin;
@@ -31,15 +14,10 @@ use OCP\Settings\IIconSection;
class Sharing implements IIconSection {
- /** @var IL10N */
- private $l;
-
- /** @var IURLGenerator */
- private $urlGenerator;
-
- public function __construct(IL10N $l, IURLGenerator $urlGenerator) {
- $this->l = $l;
- $this->urlGenerator = $urlGenerator;
+ public function __construct(
+ private IL10N $l,
+ private IURLGenerator $urlGenerator,
+ ) {
}
public function getIcon(): string {
diff --git a/apps/settings/lib/Sections/Personal/Availability.php b/apps/settings/lib/Sections/Personal/Availability.php
index c23b2c0a822..e12e41ea800 100644
--- a/apps/settings/lib/Sections/Personal/Availability.php
+++ b/apps/settings/lib/Sections/Personal/Availability.php
@@ -3,22 +3,8 @@
declare(strict_types=1);
/**
- * @author Christoph Wurst <christoph@winzerhof-wurst.at>
- *
- * Mail
- *
- * This code is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * 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, version 3,
- * along with this program. If not, see <http://www.gnu.org/licenses/>
- *
+ * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\Settings\Sections\Personal;
@@ -29,15 +15,10 @@ use OCP\Settings\IIconSection;
class Availability implements IIconSection {
- /** @var IL10N */
- private $l;
-
- /** @var IURLGenerator */
- private $urlGenerator;
-
- public function __construct(IL10N $l, IURLGenerator $urlGenerator) {
- $this->l = $l;
- $this->urlGenerator = $urlGenerator;
+ public function __construct(
+ private IL10N $l,
+ private IURLGenerator $urlGenerator,
+ ) {
}
public function getIcon(): string {
diff --git a/apps/settings/lib/Sections/Personal/Calendar.php b/apps/settings/lib/Sections/Personal/Calendar.php
index 3f4f100e121..602b7598f15 100644
--- a/apps/settings/lib/Sections/Personal/Calendar.php
+++ b/apps/settings/lib/Sections/Personal/Calendar.php
@@ -3,22 +3,8 @@
declare(strict_types=1);
/**
- * @author Christoph Wurst <christoph@winzerhof-wurst.at>
- *
- * Mail
- *
- * This code is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * 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, version 3,
- * along with this program. If not, see <http://www.gnu.org/licenses/>
- *
+ * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\Settings\Sections\Personal;
@@ -29,16 +15,14 @@ use OCP\Settings\IIconSection;
class Calendar implements IIconSection {
- private IL10N $l;
- private IURLGenerator $urlGenerator;
-
- public function __construct(IL10N $l, IURLGenerator $urlGenerator) {
- $this->l = $l;
- $this->urlGenerator = $urlGenerator;
+ public function __construct(
+ private IL10N $l,
+ private IURLGenerator $urlGenerator,
+ ) {
}
public function getIcon(): string {
- return $this->urlGenerator->imagePath('core', 'caldav/time.svg');
+ return $this->urlGenerator->imagePath('dav', 'calendar.svg');
}
public function getID(): string {
diff --git a/apps/settings/lib/Sections/Personal/PersonalInfo.php b/apps/settings/lib/Sections/Personal/PersonalInfo.php
index e92262e91b3..35c3e6d2926 100644
--- a/apps/settings/lib/Sections/Personal/PersonalInfo.php
+++ b/apps/settings/lib/Sections/Personal/PersonalInfo.php
@@ -3,25 +3,8 @@
declare(strict_types=1);
/**
- * @copyright Copyright (c) 2020, Roeland Jago Douma <roeland@famdouma.nl>
- *
- * @author Roeland Jago Douma <roeland@famdouma.nl>
- *
- * @license GNU AGPL version 3 or any later version
- *
- * 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/>.
- *
+ * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\Settings\Sections\Personal;
@@ -31,15 +14,10 @@ use OCP\Settings\IIconSection;
class PersonalInfo implements IIconSection {
- /** @var IL10N */
- private $l;
-
- /** @var IURLGenerator */
- private $urlGenerator;
-
- public function __construct(IL10N $l, IURLGenerator $urlGenerator) {
- $this->l = $l;
- $this->urlGenerator = $urlGenerator;
+ public function __construct(
+ private IL10N $l,
+ private IURLGenerator $urlGenerator,
+ ) {
}
public function getIcon() {
diff --git a/apps/settings/lib/Sections/Personal/Security.php b/apps/settings/lib/Sections/Personal/Security.php
index 72c99765266..d7eb65724ec 100644
--- a/apps/settings/lib/Sections/Personal/Security.php
+++ b/apps/settings/lib/Sections/Personal/Security.php
@@ -3,25 +3,8 @@
declare(strict_types=1);
/**
- * @copyright Copyright (c) 2020, Roeland Jago Douma <roeland@famdouma.nl>
- *
- * @author Roeland Jago Douma <roeland@famdouma.nl>
- *
- * @license GNU AGPL version 3 or any later version
- *
- * 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/>.
- *
+ * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\Settings\Sections\Personal;
@@ -31,15 +14,10 @@ use OCP\Settings\IIconSection;
class Security implements IIconSection {
- /** @var IL10N */
- private $l;
-
- /** @var IURLGenerator */
- private $urlGenerator;
-
- public function __construct(IL10N $l, IURLGenerator $urlGenerator) {
- $this->l = $l;
- $this->urlGenerator = $urlGenerator;
+ public function __construct(
+ private IL10N $l,
+ private IURLGenerator $urlGenerator,
+ ) {
}
public function getIcon() {
diff --git a/apps/settings/lib/Sections/Personal/SyncClients.php b/apps/settings/lib/Sections/Personal/SyncClients.php
index 4730f5e1ead..3f221956f0c 100644
--- a/apps/settings/lib/Sections/Personal/SyncClients.php
+++ b/apps/settings/lib/Sections/Personal/SyncClients.php
@@ -3,25 +3,8 @@
declare(strict_types=1);
/**
- * @copyright Copyright (c) 2020, Roeland Jago Douma <roeland@famdouma.nl>
- *
- * @author Roeland Jago Douma <roeland@famdouma.nl>
- *
- * @license GNU AGPL version 3 or any later version
- *
- * 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/>.
- *
+ * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\Settings\Sections\Personal;
@@ -31,15 +14,10 @@ use OCP\Settings\IIconSection;
class SyncClients implements IIconSection {
- /** @var IL10N */
- private $l;
-
- /** @var IURLGenerator */
- private $urlGenerator;
-
- public function __construct(IL10N $l, IURLGenerator $urlGenerator) {
- $this->l = $l;
- $this->urlGenerator = $urlGenerator;
+ public function __construct(
+ private IL10N $l,
+ private IURLGenerator $urlGenerator,
+ ) {
}
public function getIcon() {
diff --git a/apps/settings/lib/Service/AuthorizedGroupService.php b/apps/settings/lib/Service/AuthorizedGroupService.php
index f85c74b0480..15aca94198a 100644
--- a/apps/settings/lib/Service/AuthorizedGroupService.php
+++ b/apps/settings/lib/Service/AuthorizedGroupService.php
@@ -1,44 +1,24 @@
<?php
/**
- * @copyright Copyright (c) 2021 Nextcloud GmbH
- *
- * @author Carl Schwan <carl@carlschwan.eu>
- *
- * @license GNU AGPL version 3 or any later version
- *
- * 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 <https://www.gnu.org/licenses/>.
- *
+ * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
-
namespace OCA\Settings\Service;
-use OCP\AppFramework\Db\DoesNotExistException;
-use OCP\AppFramework\Db\MultipleObjectsReturnedException;
-
use OC\Settings\AuthorizedGroup;
use OC\Settings\AuthorizedGroupMapper;
+
+use OCP\AppFramework\Db\DoesNotExistException;
+use OCP\AppFramework\Db\MultipleObjectsReturnedException;
use OCP\DB\Exception;
use OCP\IGroup;
class AuthorizedGroupService {
- /** @var AuthorizedGroupMapper $mapper */
- private $mapper;
-
- public function __construct(AuthorizedGroupMapper $mapper) {
- $this->mapper = $mapper;
+ public function __construct(
+ private AuthorizedGroupMapper $mapper,
+ ) {
}
/**
@@ -62,9 +42,9 @@ class AuthorizedGroupService {
* @throws NotFoundException
*/
private function handleException(\Exception $e): void {
- if ($e instanceof DoesNotExistException ||
- $e instanceof MultipleObjectsReturnedException) {
- throw new NotFoundException("AuthorizedGroup not found");
+ if ($e instanceof DoesNotExistException
+ || $e instanceof MultipleObjectsReturnedException) {
+ throw new NotFoundException('AuthorizedGroup not found');
} else {
throw $e;
}
diff --git a/apps/settings/lib/Service/NotFoundException.php b/apps/settings/lib/Service/NotFoundException.php
index 8b0fb40fa31..3abdcf72cd4 100644
--- a/apps/settings/lib/Service/NotFoundException.php
+++ b/apps/settings/lib/Service/NotFoundException.php
@@ -1,25 +1,8 @@
<?php
/**
- * @copyright Copyright (c) 2021 Carl Schwan <carl@carlschwan.eu>
- *
- * @author Carl Schwan <carl@carlschwan.eu>
- *
- * @license GNU AGPL version 3 or any later version
- *
- * 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 <https://www.gnu.org/licenses/>.
- *
+ * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\Settings\Service;
diff --git a/apps/settings/lib/Service/ServiceException.php b/apps/settings/lib/Service/ServiceException.php
index 5b29dc072cb..79c848779ca 100644
--- a/apps/settings/lib/Service/ServiceException.php
+++ b/apps/settings/lib/Service/ServiceException.php
@@ -1,25 +1,8 @@
<?php
/**
- * @copyright Copyright (c) 2021 Nextcloud GmbH
- *
- * @author Carl Schwan <carl@carlschwan.eu>
- *
- * @license GNU AGPL version 3 or any later version
- *
- * 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 <https://www.gnu.org/licenses/>.
- *
+ * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\Settings\Service;
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
index 274b71150ed..59a26d1ac04 100644
--- a/apps/settings/lib/Settings/Admin/Delegation.php
+++ b/apps/settings/lib/Settings/Admin/Delegation.php
@@ -1,26 +1,9 @@
<?php
+
/**
- * @copyright Copyright (c) 2021 Carl Schwan <carl@carlschwan.eu>
- *
- * @author Carl Schwan <carl@carlschwan.eu>
- *
- * @license GNU AGPL version 3 or any later version
- *
- * 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 <https://www.gnu.org/licenses/>.
- *
+ * 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;
@@ -28,30 +11,19 @@ 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;
-use OCP\IURLGenerator;
class Delegation implements ISettings {
- private IManager $settingManager;
- private IInitialState $initialStateService;
- private IGroupManager $groupManager;
- private AuthorizedGroupService $authorizedGroupService;
- private IURLGenerator $urlGenerator;
-
public function __construct(
- IManager $settingManager,
- IInitialState $initialStateService,
- IGroupManager $groupManager,
- AuthorizedGroupService $authorizedGroupService,
- IURLGenerator $urlGenerator
+ private IManager $settingManager,
+ private IInitialState $initialStateService,
+ private IGroupManager $groupManager,
+ private AuthorizedGroupService $authorizedGroupService,
+ private IURLGenerator $urlGenerator,
) {
- $this->settingManager = $settingManager;
- $this->initialStateService = $initialStateService;
- $this->groupManager = $groupManager;
- $this->authorizedGroupService = $authorizedGroupService;
- $this->urlGenerator = $urlGenerator;
}
/**
@@ -82,9 +54,11 @@ class Delegation implements ISettings {
$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' => $section->getName() . ($setting->getName() !== null ? ' - ' . $setting->getName() : ''),
+ 'sectionName' => $sectionName,
+ 'id' => mb_strtolower(str_replace(' ', '-', $sectionName)),
'priority' => $section->getPriority(),
];
}, $sectionSettings)
diff --git a/apps/settings/lib/Settings/Admin/Mail.php b/apps/settings/lib/Settings/Admin/Mail.php
index 1cdb7315713..8bf2342a59c 100644
--- a/apps/settings/lib/Settings/Admin/Mail.php
+++ b/apps/settings/lib/Settings/Admin/Mail.php
@@ -1,60 +1,38 @@
<?php
+
/**
- * @copyright Copyright (c) 2016 Arthur Schiwon <blizzz@arthur-schiwon.de>
- *
- * @author Arthur Schiwon <blizzz@arthur-schiwon.de>
- * @author Christoph Wurst <christoph@winzerhof-wurst.at>
- * @author Joas Schilling <coding@schilljs.com>
- * @author Julius Härtl <jus@bitgrid.net>
- * @author Lukas Reschke <lukas@statuscode.ch>
- * @author Roeland Jago Douma <roeland@famdouma.nl>
- *
- * @license GNU AGPL version 3 or any later version
- *
- * 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/>.
- *
+ * 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 {
- /** @var IConfig */
- private $config;
-
- /** @var IL10N $l */
- private $l;
-
/**
* @param IConfig $config
* @param IL10N $l
*/
- public function __construct(IConfig $config, IL10N $l) {
- $this->config = $config;
- $this->l = $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' => (bool) \OC_Helper::findBinaryPath('sendmail'),
+ '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', ''),
@@ -87,8 +65,8 @@ class Mail implements IDelegatedSettings {
/**
* @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.
+ * 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
*/
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
index 1519ca923c2..355200372f1 100644
--- a/apps/settings/lib/Settings/Admin/Overview.php
+++ b/apps/settings/lib/Settings/Admin/Overview.php
@@ -1,45 +1,23 @@
<?php
+
/**
- * @copyright Copyright (c) 2018 Julius Härtl <jus@bitgrid.net>
- *
- * @author Christoph Wurst <christoph@winzerhof-wurst.at>
- * @author Julius Härtl <jus@bitgrid.net>
- * @author Morris Jobke <hey@morrisjobke.de>
- * @author Roeland Jago Douma <roeland@famdouma.nl>
- *
- * @license GNU AGPL version 3 or any later version
- *
- * 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/>.
- *
+ * 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 {
- /** @var IConfig */
- private $config;
-
- /** @var IL10N $l*/
- private $l;
-
- public function __construct(IConfig $config, IL10N $l) {
- $this->config = $config;
- $this->l = $l;
+ public function __construct(
+ private ServerVersion $serverVersion,
+ private IConfig $config,
+ private IL10N $l,
+ ) {
}
/**
@@ -48,6 +26,7 @@ class Overview implements IDelegatedSettings {
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, '');
@@ -62,8 +41,8 @@ class Overview implements IDelegatedSettings {
/**
* @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.
+ * 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
*/
@@ -72,7 +51,7 @@ class Overview implements IDelegatedSettings {
}
public function getName(): ?string {
- return $this->l->t('Security & setup warnings');
+ return $this->l->t('Security & setup checks');
}
public function getAuthorizedAppConfig(): array {
diff --git a/apps/settings/lib/Settings/Admin/Security.php b/apps/settings/lib/Settings/Admin/Security.php
index 63d3137a45c..c4efdb478c7 100644
--- a/apps/settings/lib/Settings/Admin/Security.php
+++ b/apps/settings/lib/Settings/Admin/Security.php
@@ -1,28 +1,8 @@
<?php
+
/**
- * @copyright Copyright (c) 2016 Arthur Schiwon <blizzz@arthur-schiwon.de>
- *
- * @author Arthur Schiwon <blizzz@arthur-schiwon.de>
- * @author Christoph Wurst <christoph@winzerhof-wurst.at>
- * @author Julius Härtl <jus@bitgrid.net>
- * @author Robin Appelman <robin@icewind.nl>
- * @author Roeland Jago Douma <roeland@famdouma.nl>
- *
- * @license GNU AGPL version 3 or any later version
- *
- * 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/>.
- *
+ * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\Settings\Settings\Admin;
@@ -30,27 +10,21 @@ use OC\Authentication\TwoFactorAuth\MandatoryTwoFactor;
use OCP\AppFramework\Http\TemplateResponse;
use OCP\AppFramework\Services\IInitialState;
use OCP\Encryption\IManager;
-use OCP\IUserManager;
use OCP\IURLGenerator;
+use OCP\IUserManager;
use OCP\Settings\ISettings;
class Security implements ISettings {
- private IManager $manager;
- private IUserManager $userManager;
private MandatoryTwoFactor $mandatoryTwoFactor;
- private IInitialState $initialState;
- private IURLGenerator $urlGenerator;
- public function __construct(IManager $manager,
- IUserManager $userManager,
- MandatoryTwoFactor $mandatoryTwoFactor,
- IInitialState $initialState,
- IURLGenerator $urlGenerator) {
- $this->manager = $manager;
- $this->userManager = $userManager;
+ public function __construct(
+ private IManager $manager,
+ private IUserManager $userManager,
+ MandatoryTwoFactor $mandatoryTwoFactor,
+ private IInitialState $initialState,
+ private IURLGenerator $urlGenerator,
+ ) {
$this->mandatoryTwoFactor = $mandatoryTwoFactor;
- $this->initialState = $initialState;
- $this->urlGenerator = $urlGenerator;
}
/**
@@ -88,8 +62,8 @@ class Security implements ISettings {
/**
* @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.
+ * 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
*/
diff --git a/apps/settings/lib/Settings/Admin/Server.php b/apps/settings/lib/Settings/Admin/Server.php
index 0d8ef00a1fd..c0f29ce8f34 100644
--- a/apps/settings/lib/Settings/Admin/Server.php
+++ b/apps/settings/lib/Settings/Admin/Server.php
@@ -1,27 +1,8 @@
<?php
+
/**
- * @copyright Copyright (c) 2016 Arthur Schiwon <blizzz@arthur-schiwon.de>
- *
- * @author Arthur Schiwon <blizzz@arthur-schiwon.de>
- * @author Christoph Wurst <christoph@winzerhof-wurst.at>
- * @author Joas Schilling <coding@schilljs.com>
- * @author Roeland Jago Douma <roeland@famdouma.nl>
- *
- * @license GNU AGPL version 3 or any later version
- *
- * 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/>.
- *
+ * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\Settings\Settings\Admin;
@@ -30,6 +11,7 @@ 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;
@@ -39,47 +21,42 @@ use OCP\Settings\IDelegatedSettings;
class Server implements IDelegatedSettings {
use TProfileHelper;
- private IDBConnection $connection;
- private IInitialState $initialStateService;
- private ProfileManager $profileManager;
- private ITimeFactory $timeFactory;
- private IConfig $config;
- private IL10N $l;
- private IURLGenerator $urlGenerator;
-
- public function __construct(IDBConnection $connection,
- IInitialState $initialStateService,
- ProfileManager $profileManager,
- ITimeFactory $timeFactory,
- IURLGenerator $urlGenerator,
- IConfig $config,
- IL10N $l) {
- $this->connection = $connection;
- $this->initialStateService = $initialStateService;
- $this->profileManager = $profileManager;
- $this->timeFactory = $timeFactory;
- $this->config = $config;
- $this->l = $l;
- $this->urlGenerator = $urlGenerator;
+ 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->config->getAppValue('core', 'backgroundjobs_mode', 'ajax'));
- $this->initialStateService->provideInitialState('lastCron', (int)$this->config->getAppValue('core', 'lastcron', '0'));
+ $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', function_exists('posix_getpwuid'));
- $this->initialStateService->provideInitialState('cliBasedCronUser', function_exists('posix_getpwuid') ? posix_getpwuid(fileowner(\OC::$configDir . 'config.php'))['name'] : '');
+ $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(),
], '');
@@ -94,7 +71,7 @@ class Server implements IDelegatedSettings {
$result = $query->execute();
if ($row = $result->fetch()) {
- $maxAge = (int) $row['last_checked'];
+ $maxAge = (int)$row['last_checked'];
} else {
$maxAge = $this->timeFactory->getTime();
}
@@ -112,8 +89,8 @@ class Server implements IDelegatedSettings {
/**
* @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.
+ * 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
*/
diff --git a/apps/settings/lib/Settings/Admin/Sharing.php b/apps/settings/lib/Settings/Admin/Sharing.php
index d3c6839b8f7..ec5dcdf624d 100644
--- a/apps/settings/lib/Settings/Admin/Sharing.php
+++ b/apps/settings/lib/Settings/Admin/Sharing.php
@@ -1,64 +1,35 @@
<?php
+
/**
- * @copyright Copyright (c) 2016 Arthur Schiwon <blizzz@arthur-schiwon.de>
- *
- * @author Arthur Schiwon <blizzz@arthur-schiwon.de>
- * @author Bjoern Schiessle <bjoern@schiessle.org>
- * @author Christoph Wurst <christoph@winzerhof-wurst.at>
- * @author Joas Schilling <coding@schilljs.com>
- * @author Lukas Reschke <lukas@statuscode.ch>
- * @author Morris Jobke <hey@morrisjobke.de>
- * @author Robin Appelman <robin@icewind.nl>
- * @author Roeland Jago Douma <roeland@famdouma.nl>
- * @author Sascha Wiswedel <sascha.wiswedel@nextcloud.com>
- * @author Vincent Petry <vincent@nextcloud.com>
- * @author Thomas Citharel <nextcloud@tcit.fr>
- *
- * @license GNU AGPL version 3 or any later version
- *
- * 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/>.
- *
+ * 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 {
- /** @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 IAppConfig $appConfig,
+ private IL10N $l,
+ private IManager $shareManager,
+ private IAppManager $appManager,
+ private IURLGenerator $urlGenerator,
+ private IInitialState $initialState,
+ private string $appName,
+ ) {
}
/**
@@ -66,85 +37,62 @@ 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)) : '';
-
+ $onlyShareWithGroupMembersExcludeGroupList = $this->config->getAppValue('core', 'shareapi_only_share_with_group_members_exclude_group_list', '');
$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,
- 'passwordExcludedGroupsFeatureEnabled' => $this->config->getSystemValueBool('sharing.allow_disabled_password_enforcement_groups', false),
+ '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(),
- '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,
- 'publicShareDisclaimerText' => $this->config->getAppValue('core', 'shareapi_public_link_disclaimertext', null),
- 'enableLinkPasswordByDefault' => $this->config->getAppValue('core', 'shareapi_enable_link_password_by_default', 'no'),
- 'shareApiDefaultPermissions' => $this->config->getAppValue('core', 'shareapi_default_permissions', 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'),
+ '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(),
];
- 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);
+
+ 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';
}
/**
@@ -156,8 +104,8 @@ class Sharing implements IDelegatedSettings {
/**
* @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.
+ * 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
*/
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 [];
+ }
+}
diff --git a/apps/settings/lib/Settings/Personal/Additional.php b/apps/settings/lib/Settings/Personal/Additional.php
index e1696e2424e..58fe08a63b7 100644
--- a/apps/settings/lib/Settings/Personal/Additional.php
+++ b/apps/settings/lib/Settings/Personal/Additional.php
@@ -1,25 +1,8 @@
<?php
+
/**
- * @copyright Copyright (c) 2017 Arthur Schiwon <blizzz@arthur-schiwon.de>
- *
- * @author Arthur Schiwon <blizzz@arthur-schiwon.de>
- * @author Roeland Jago Douma <roeland@famdouma.nl>
- *
- * @license GNU AGPL version 3 or any later version
- *
- * 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/>.
- *
+ * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\Settings\Settings\Personal;
@@ -46,8 +29,8 @@ class Additional implements ISettings {
/**
* @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.
+ * 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
* @since 9.1
diff --git a/apps/settings/lib/Settings/Personal/PersonalInfo.php b/apps/settings/lib/Settings/Personal/PersonalInfo.php
index 535c1606527..9a12b18bb5e 100644
--- a/apps/settings/lib/Settings/Personal/PersonalInfo.php
+++ b/apps/settings/lib/Settings/Personal/PersonalInfo.php
@@ -3,40 +3,15 @@
declare(strict_types=1);
/**
- * @copyright Copyright (c) 2017 Arthur Schiwon <blizzz@arthur-schiwon.de>
- *
- * @author Arthur Schiwon <blizzz@arthur-schiwon.de>
- * @author Christoph Wurst <christoph@winzerhof-wurst.at>
- * @author Christopher Ng <chrng8@gmail.com>
- * @author Georg Ehrke <oc.list@georgehrke.com>
- * @author Joas Schilling <coding@schilljs.com>
- * @author John Molakvoæ <skjnldsv@protonmail.com>
- * @author Morris Jobke <hey@morrisjobke.de>
- * @author Robin Appelman <robin@icewind.nl>
- * @author Roeland Jago Douma <roeland@famdouma.nl>
- * @author Thomas Citharel <nextcloud@tcit.fr>
- * @author Vincent Petry <vincent@nextcloud.com>
- *
- * @license GNU AGPL version 3 or any later version
- *
- * 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/>.
- *
+ * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\Settings\Settings\Personal;
+use OC\Profile\ProfileManager;
use OCA\FederatedFileSharing\FederatedShareProvider;
+use OCA\Provisioning_API\Controller\AUserDataOCSController;
use OCP\Accounts\IAccount;
use OCP\Accounts\IAccountManager;
use OCP\Accounts\IAccountProperty;
@@ -51,64 +26,29 @@ use OCP\IL10N;
use OCP\IUser;
use OCP\IUserManager;
use OCP\L10N\IFactory;
-use OC\Profile\ProfileManager;
use OCP\Notification\IManager;
+use OCP\Server;
use OCP\Settings\ISettings;
+use OCP\Util;
class PersonalInfo implements ISettings {
- /** @var IConfig */
- private $config;
-
- /** @var IUserManager */
- private $userManager;
-
- /** @var IAccountManager */
- private $accountManager;
-
/** @var ProfileManager */
private $profileManager;
- /** @var IGroupManager */
- private $groupManager;
-
- /** @var IAppManager */
- private $appManager;
-
- /** @var IFactory */
- private $l10nFactory;
-
- /** @var IL10N */
- private $l;
-
- /** @var IInitialState */
- private $initialStateService;
-
- /** @var IManager */
- private $manager;
-
public function __construct(
- IConfig $config,
- IUserManager $userManager,
- IGroupManager $groupManager,
- IAccountManager $accountManager,
+ private IConfig $config,
+ private IUserManager $userManager,
+ private IGroupManager $groupManager,
+ private IAccountManager $accountManager,
ProfileManager $profileManager,
- IAppManager $appManager,
- IFactory $l10nFactory,
- IL10N $l,
- IInitialState $initialStateService,
- IManager $manager
+ private IAppManager $appManager,
+ private IFactory $l10nFactory,
+ private IL10N $l,
+ private IInitialState $initialStateService,
+ private IManager $manager,
) {
- $this->config = $config;
- $this->userManager = $userManager;
- $this->accountManager = $accountManager;
$this->profileManager = $profileManager;
- $this->groupManager = $groupManager;
- $this->appManager = $appManager;
- $this->l10nFactory = $l10nFactory;
- $this->l = $l;
- $this->initialStateService = $initialStateService;
- $this->manager = $manager;
}
public function getForm(): TemplateResponse {
@@ -117,7 +57,7 @@ class PersonalInfo implements ISettings {
$lookupServerUploadEnabled = false;
if ($federatedFileSharingEnabled) {
/** @var FederatedShareProvider $shareProvider */
- $shareProvider = \OC::$server->query(FederatedShareProvider::class);
+ $shareProvider = Server::get(FederatedShareProvider::class);
$lookupServerUploadEnabled = $shareProvider->isLookupServerUploadEnabled();
}
@@ -132,7 +72,7 @@ class PersonalInfo implements ISettings {
if ($storageInfo['quota'] === FileInfo::SPACE_UNLIMITED) {
$totalSpace = $this->l->t('Unlimited');
} else {
- $totalSpace = \OC_Helper::humanFileSize($storageInfo['total']);
+ $totalSpace = Util::humanFileSize($storageInfo['total']);
}
$messageParameters = $this->getMessageParameters($account);
@@ -149,7 +89,7 @@ class PersonalInfo implements ISettings {
'groups' => $this->getGroups($user),
'quota' => $storageInfo['quota'],
'totalSpace' => $totalSpace,
- 'usage' => \OC_Helper::humanFileSize($storageInfo['used']),
+ 'usage' => Util::humanFileSize($storageInfo['used']),
'usageRelative' => round($storageInfo['relative']),
'displayName' => $this->getProperty($account, IAccountManager::PROPERTY_DISPLAYNAME),
'emailMap' => $this->getEmailMap($account),
@@ -158,6 +98,7 @@ class PersonalInfo implements ISettings {
'location' => $this->getProperty($account, IAccountManager::PROPERTY_ADDRESS),
'website' => $this->getProperty($account, IAccountManager::PROPERTY_WEBSITE),
'twitter' => $this->getProperty($account, IAccountManager::PROPERTY_TWITTER),
+ 'bluesky' => $this->getProperty($account, IAccountManager::PROPERTY_BLUESKY),
'fediverse' => $this->getProperty($account, IAccountManager::PROPERTY_FEDIVERSE),
'languageMap' => $this->getLanguageMap($user),
'localeMap' => $this->getLocaleMap($user),
@@ -167,11 +108,15 @@ class PersonalInfo implements ISettings {
'role' => $this->getProperty($account, IAccountManager::PROPERTY_ROLE),
'headline' => $this->getProperty($account, IAccountManager::PROPERTY_HEADLINE),
'biography' => $this->getProperty($account, IAccountManager::PROPERTY_BIOGRAPHY),
+ 'birthdate' => $this->getProperty($account, IAccountManager::PROPERTY_BIRTHDATE),
+ 'firstDayOfWeek' => $this->config->getUserValue($uid, 'core', AUserDataOCSController::USER_FIELD_FIRST_DAY_OF_WEEK),
+ 'pronouns' => $this->getProperty($account, IAccountManager::PROPERTY_PRONOUNS),
];
$accountParameters = [
'avatarChangeSupported' => $user->canChangeAvatar(),
'displayNameChangeSupported' => $user->canChangeDisplayName(),
+ 'emailChangeSupported' => $user->canChangeEmail(),
'federationEnabled' => $federationEnabled,
'lookupServerUploadEnabled' => $lookupServerUploadEnabled,
];
@@ -221,8 +166,8 @@ class PersonalInfo implements ISettings {
/**
* @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.
+ * 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
* @since 9.1
@@ -324,8 +269,8 @@ class PersonalInfo implements ISettings {
}
$uid = $user->getUID();
- $userLocaleString = $this->config->getUserValue($uid, 'core', 'locale', $this->l10nFactory->findLocale());
$userLang = $this->config->getUserValue($uid, 'core', 'lang', $this->l10nFactory->findLanguage());
+ $userLocaleString = $this->config->getUserValue($uid, 'core', 'locale', $this->l10nFactory->findLocale($userLang));
$localeCodes = $this->l10nFactory->findAvailableLocales();
$userLocale = array_filter($localeCodes, fn ($value) => $userLocaleString === $value['code']);
@@ -333,8 +278,8 @@ class PersonalInfo implements ISettings {
$userLocale = reset($userLocale);
}
- $localesForLanguage = array_values(array_filter($localeCodes, fn ($localeCode) => strpos($localeCode['code'], $userLang) === 0));
- $otherLocales = array_values(array_filter($localeCodes, fn ($localeCode) => strpos($localeCode['code'], $userLang) !== 0));
+ $localesForLanguage = array_values(array_filter($localeCodes, fn ($localeCode) => str_starts_with($localeCode['code'], $userLang)));
+ $otherLocales = array_values(array_filter($localeCodes, fn ($localeCode) => !str_starts_with($localeCode['code'], $userLang)));
if (!$userLocale) {
$userLocale = [
diff --git a/apps/settings/lib/Settings/Personal/Security/Authtokens.php b/apps/settings/lib/Settings/Personal/Security/Authtokens.php
index 4963f40347d..e0509b22a9c 100644
--- a/apps/settings/lib/Settings/Personal/Security/Authtokens.php
+++ b/apps/settings/lib/Settings/Personal/Security/Authtokens.php
@@ -3,67 +3,32 @@
declare(strict_types=1);
/**
- * @copyright Copyright (c) 2019, Roeland Jago Douma <roeland@famdouma.nl>
- *
- * @author Roeland Jago Douma <roeland@famdouma.nl>
- *
- * @license GNU AGPL version 3 or any later version
- *
- * 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/>.
- *
+ * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\Settings\Settings\Personal\Security;
-use OCP\AppFramework\Services\IInitialState;
-use OCP\IUserSession;
-use function array_map;
-use OC\Authentication\Exceptions\InvalidTokenException;
use OC\Authentication\Token\INamedToken;
use OC\Authentication\Token\IProvider as IAuthTokenProvider;
use OC\Authentication\Token\IToken;
use OCP\AppFramework\Http\TemplateResponse;
+use OCP\AppFramework\Services\IInitialState;
+use OCP\Authentication\Exceptions\InvalidTokenException;
use OCP\ISession;
+use OCP\IUserSession;
use OCP\Session\Exceptions\SessionNotAvailableException;
use OCP\Settings\ISettings;
+use function array_map;
class Authtokens implements ISettings {
- /** @var IAuthTokenProvider */
- private $tokenProvider;
-
- /** @var ISession */
- private $session;
-
- /** @var IInitialState */
- private $initialState;
-
- /** @var string|null */
- private $uid;
-
- /** @var IUserSession */
- private $userSession;
-
- public function __construct(IAuthTokenProvider $tokenProvider,
- ISession $session,
- IUserSession $userSession,
- IInitialState $initialState,
- ?string $UserId) {
- $this->tokenProvider = $tokenProvider;
- $this->session = $session;
- $this->initialState = $initialState;
- $this->uid = $UserId;
- $this->userSession = $userSession;
+ public function __construct(
+ private IAuthTokenProvider $tokenProvider,
+ private ISession $session,
+ private IUserSession $userSession,
+ private IInitialState $initialState,
+ private ?string $userId,
+ ) {
}
public function getForm(): TemplateResponse {
@@ -89,7 +54,7 @@ class Authtokens implements ISettings {
}
private function getAppTokens(): array {
- $tokens = $this->tokenProvider->getTokenByUser($this->uid);
+ $tokens = $this->tokenProvider->getTokenByUser($this->userId);
try {
$sessionId = $this->session->getId();
@@ -105,7 +70,7 @@ class Authtokens implements ISettings {
return array_map(function (IToken $token) use ($sessionToken) {
$data = $token->jsonSerialize();
$data['canDelete'] = true;
- $data['canRename'] = $token instanceof INamedToken;
+ $data['canRename'] = $token instanceof INamedToken && $data['type'] !== IToken::WIPE_TOKEN;
if ($sessionToken->getId() === $token->getId()) {
$data['canDelete'] = false;
$data['canRename'] = false;
diff --git a/apps/settings/lib/Settings/Personal/Security/Password.php b/apps/settings/lib/Settings/Personal/Security/Password.php
index 252311b87ab..8184dae9560 100644
--- a/apps/settings/lib/Settings/Personal/Security/Password.php
+++ b/apps/settings/lib/Settings/Personal/Security/Password.php
@@ -3,28 +3,8 @@
declare(strict_types=1);
/**
- * @copyright Copyright (c) 2017 Arthur Schiwon <blizzz@arthur-schiwon.de>
- *
- * @author Arthur Schiwon <blizzz@arthur-schiwon.de>
- * @author Christoph Wurst <christoph@winzerhof-wurst.at>
- * @author Julius Härtl <jus@bitgrid.net>
- * @author Roeland Jago Douma <roeland@famdouma.nl>
- *
- * @license GNU AGPL version 3 or any later version
- *
- * 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/>.
- *
+ * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\Settings\Settings\Personal\Security;
@@ -34,20 +14,14 @@ use OCP\Settings\ISettings;
class Password implements ISettings {
- /** @var IUserManager */
- private $userManager;
-
- /** @var string|null */
- private $uid;
-
- public function __construct(IUserManager $userManager,
- ?string $UserId) {
- $this->userManager = $userManager;
- $this->uid = $UserId;
+ public function __construct(
+ private IUserManager $userManager,
+ private ?string $userId,
+ ) {
}
public function getForm(): TemplateResponse {
- $user = $this->userManager->get($this->uid);
+ $user = $this->userManager->get($this->userId);
$passwordChangeSupported = false;
if ($user !== null) {
$passwordChangeSupported = $user->canChangePassword();
diff --git a/apps/settings/lib/Settings/Personal/Security/TwoFactor.php b/apps/settings/lib/Settings/Personal/Security/TwoFactor.php
index c356442a628..0c419cb6fa7 100644
--- a/apps/settings/lib/Settings/Personal/Security/TwoFactor.php
+++ b/apps/settings/lib/Settings/Personal/Security/TwoFactor.php
@@ -3,42 +3,24 @@
declare(strict_types=1);
/**
- * @copyright Copyright (c) 2019, Roeland Jago Douma <roeland@famdouma.nl>
- *
- * @author Christoph Wurst <christoph@winzerhof-wurst.at>
- * @author Roeland Jago Douma <roeland@famdouma.nl>
- *
- * @license GNU AGPL version 3 or any later version
- *
- * 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/>.
- *
+ * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\Settings\Settings\Personal\Security;
use Exception;
use OC\Authentication\TwoFactorAuth\MandatoryTwoFactor;
-use OCA\TwoFactorBackupCodes\Provider\BackupCodesProvider;
-use function array_filter;
-use function array_map;
-use function is_null;
use OC\Authentication\TwoFactorAuth\ProviderLoader;
+use OCA\TwoFactorBackupCodes\Provider\BackupCodesProvider;
use OCP\AppFramework\Http\TemplateResponse;
use OCP\Authentication\TwoFactorAuth\IProvider;
use OCP\Authentication\TwoFactorAuth\IProvidesPersonalSettings;
use OCP\IConfig;
use OCP\IUserSession;
use OCP\Settings\ISettings;
+use function array_filter;
+use function array_map;
+use function is_null;
class TwoFactor implements ISettings {
@@ -48,25 +30,15 @@ class TwoFactor implements ISettings {
/** @var MandatoryTwoFactor */
private $mandatoryTwoFactor;
- /** @var IUserSession */
- private $userSession;
-
- /** @var string|null */
- private $uid;
-
- /** @var IConfig */
- private $config;
-
- public function __construct(ProviderLoader $providerLoader,
- MandatoryTwoFactor $mandatoryTwoFactor,
- IUserSession $userSession,
- IConfig $config,
- ?string $UserId) {
+ public function __construct(
+ ProviderLoader $providerLoader,
+ MandatoryTwoFactor $mandatoryTwoFactor,
+ private IUserSession $userSession,
+ private IConfig $config,
+ private ?string $userId,
+ ) {
$this->providerLoader = $providerLoader;
$this->mandatoryTwoFactor = $mandatoryTwoFactor;
- $this->userSession = $userSession;
- $this->uid = $UserId;
- $this->config = $config;
}
public function getForm(): TemplateResponse {
diff --git a/apps/settings/lib/Settings/Personal/Security/WebAuthn.php b/apps/settings/lib/Settings/Personal/Security/WebAuthn.php
index 38241c9a41c..a6ba4e9522a 100644
--- a/apps/settings/lib/Settings/Personal/Security/WebAuthn.php
+++ b/apps/settings/lib/Settings/Personal/Security/WebAuthn.php
@@ -3,25 +3,8 @@
declare(strict_types=1);
/**
- * @copyright Copyright (c) 2020, Roeland Jago Douma <roeland@famdouma.nl>
- *
- * @author Roeland Jago Douma <roeland@famdouma.nl>
- *
- * @license GNU AGPL version 3 or any later version
- *
- * 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/>.
- *
+ * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\Settings\Settings\Personal\Security;
@@ -37,22 +20,16 @@ class WebAuthn implements ISettings {
/** @var PublicKeyCredentialMapper */
private $mapper;
- /** @var string */
- private $uid;
-
- /** @var IInitialStateService */
- private $initialStateService;
-
/** @var Manager */
private $manager;
- public function __construct(PublicKeyCredentialMapper $mapper,
- string $UserId,
- IInitialStateService $initialStateService,
- Manager $manager) {
+ public function __construct(
+ PublicKeyCredentialMapper $mapper,
+ private string $userId,
+ private IInitialStateService $initialStateService,
+ Manager $manager,
+ ) {
$this->mapper = $mapper;
- $this->uid = $UserId;
- $this->initialStateService = $initialStateService;
$this->manager = $manager;
}
@@ -60,11 +37,10 @@ class WebAuthn implements ISettings {
$this->initialStateService->provideInitialState(
Application::APP_ID,
'webauthn-devices',
- $this->mapper->findAllForUid($this->uid)
+ $this->mapper->findAllForUid($this->userId)
);
- return new TemplateResponse('settings', 'settings/personal/security/webauthn', [
- ]);
+ return new TemplateResponse('settings', 'settings/personal/security/webauthn');
}
public function getSection(): ?string {
diff --git a/apps/settings/lib/Settings/Personal/ServerDevNotice.php b/apps/settings/lib/Settings/Personal/ServerDevNotice.php
index 3cb9a3a972f..c9993484abd 100644
--- a/apps/settings/lib/Settings/Personal/ServerDevNotice.php
+++ b/apps/settings/lib/Settings/Personal/ServerDevNotice.php
@@ -1,27 +1,8 @@
<?php
+
/**
- * @copyright 2016, Roeland Jago Douma <roeland@famdouma.nl>
- *
- * @author Jan C. Borchardt <hey@jancborchardt.net>
- * @author Julius Härtl <jus@bitgrid.net>
- * @author Morris Jobke <hey@morrisjobke.de>
- * @author Roeland Jago Douma <roeland@famdouma.nl>
- *
- * @license GNU AGPL version 3 or any later version
- *
- * 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/>.
- *
+ * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\Settings\Settings\Personal;
@@ -38,36 +19,14 @@ use OCP\Util;
class ServerDevNotice implements ISettings {
- /** @var IRegistry */
- private $registry;
-
- /** @var IEventDispatcher */
- private $eventDispatcher;
-
- /** @var IRootFolder */
- private $rootFolder;
-
- /** @var IUserSession */
- private $userSession;
-
- /** @var IInitialState */
- private $initialState;
-
- /** @var IURLGenerator */
- private $urlGenerator;
-
- public function __construct(IRegistry $registry,
- IEventDispatcher $eventDispatcher,
- IRootFolder $rootFolder,
- IUserSession $userSession,
- IInitialState $initialState,
- IURLGenerator $urlGenerator) {
- $this->registry = $registry;
- $this->eventDispatcher = $eventDispatcher;
- $this->rootFolder = $rootFolder;
- $this->userSession = $userSession;
- $this->initialState = $initialState;
- $this->urlGenerator = $urlGenerator;
+ public function __construct(
+ private IRegistry $registry,
+ private IEventDispatcher $eventDispatcher,
+ private IRootFolder $rootFolder,
+ private IUserSession $userSession,
+ private IInitialState $initialState,
+ private IURLGenerator $urlGenerator,
+ ) {
}
/**
@@ -78,11 +37,11 @@ class ServerDevNotice implements ISettings {
$hasInitialState = false;
- // viewer is default enabled and this makes a zero-cost assertion for Psalm
- assert(class_exists(LoadViewer::class));
-
- // If the Reasons to use Nextcloud.pdf file is here, let's init Viewer
- if ($userFolder->nodeExists('Reasons to use Nextcloud.pdf')) {
+ // If the Reasons to use Nextcloud.pdf file is here, let's init Viewer, also check that Viewer is there
+ if (class_exists(LoadViewer::class) && $userFolder->nodeExists('Reasons to use Nextcloud.pdf')) {
+ /**
+ * @psalm-suppress UndefinedClass, InvalidArgument
+ */
$this->eventDispatcher->dispatch(LoadViewer::class, new LoadViewer());
$hasInitialState = true;
}
@@ -109,8 +68,8 @@ class ServerDevNotice implements ISettings {
/**
* @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.
+ * 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
*/
diff --git a/apps/settings/lib/SetupChecks/AllowedAdminRanges.php b/apps/settings/lib/SetupChecks/AllowedAdminRanges.php
new file mode 100644
index 00000000000..5116676dd43
--- /dev/null
+++ b/apps/settings/lib/SetupChecks/AllowedAdminRanges.php
@@ -0,0 +1,63 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\Settings\SetupChecks;
+
+use OC\Security\Ip\Range;
+use OC\Security\Ip\RemoteAddress;
+use OCP\IConfig;
+use OCP\IL10N;
+use OCP\SetupCheck\ISetupCheck;
+use OCP\SetupCheck\SetupResult;
+
+class AllowedAdminRanges implements ISetupCheck {
+ public function __construct(
+ private IConfig $config,
+ private IL10N $l10n,
+ ) {
+ }
+
+ public function getCategory(): string {
+ return 'system';
+ }
+
+ public function getName(): string {
+ return $this->l10n->t('Allowed admin IP ranges');
+ }
+
+ public function run(): SetupResult {
+ $allowedAdminRanges = $this->config->getSystemValue(RemoteAddress::SETTING_NAME, false);
+ if (
+ $allowedAdminRanges === false
+ || (is_array($allowedAdminRanges) && empty($allowedAdminRanges))
+ ) {
+ return SetupResult::success($this->l10n->t('Admin IP filtering isn\'t applied.'));
+ }
+
+ if (!is_array($allowedAdminRanges)) {
+ return SetupResult::error(
+ $this->l10n->t(
+ 'Configuration key "%1$s" expects an array (%2$s found). Admin IP range validation will not be applied.',
+ [RemoteAddress::SETTING_NAME, gettype($allowedAdminRanges)],
+ )
+ );
+ }
+
+ $invalidRanges = array_filter($allowedAdminRanges, static fn (mixed $range): bool => !is_string($range) || !Range::isValid($range));
+ if (!empty($invalidRanges)) {
+ return SetupResult::warning(
+ $this->l10n->t(
+ 'Configuration key "%1$s" contains invalid IP range(s): "%2$s"',
+ [RemoteAddress::SETTING_NAME, implode('", "', $invalidRanges)],
+ ),
+ );
+ }
+
+ return SetupResult::success($this->l10n->t('Admin IP filtering is correctly configured.'));
+ }
+}
diff --git a/apps/settings/lib/SetupChecks/AppDirsWithDifferentOwner.php b/apps/settings/lib/SetupChecks/AppDirsWithDifferentOwner.php
new file mode 100644
index 00000000000..0d18037c3b5
--- /dev/null
+++ b/apps/settings/lib/SetupChecks/AppDirsWithDifferentOwner.php
@@ -0,0 +1,87 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\Settings\SetupChecks;
+
+use OCP\IL10N;
+use OCP\SetupCheck\ISetupCheck;
+use OCP\SetupCheck\SetupResult;
+
+class AppDirsWithDifferentOwner implements ISetupCheck {
+ public function __construct(
+ private IL10N $l10n,
+ ) {
+ }
+
+ public function getName(): string {
+ return $this->l10n->t('App directories owner');
+ }
+
+ public function getCategory(): string {
+ return 'security';
+ }
+
+ /**
+ * Iterates through the configured app roots and
+ * tests if the subdirectories are owned by the same user than the current user.
+ *
+ * @return string[]
+ */
+ private function getAppDirsWithDifferentOwner(int $currentUser): array {
+ $appDirsWithDifferentOwner = [[]];
+
+ foreach (\OC::$APPSROOTS as $appRoot) {
+ if ($appRoot['writable'] === true) {
+ $appDirsWithDifferentOwner[] = $this->getAppDirsWithDifferentOwnerForAppRoot($currentUser, $appRoot);
+ }
+ }
+
+ $appDirsWithDifferentOwner = array_merge(...$appDirsWithDifferentOwner);
+ sort($appDirsWithDifferentOwner);
+
+ return $appDirsWithDifferentOwner;
+ }
+
+ /**
+ * Tests if the directories for one apps directory are writable by the current user.
+ *
+ * @param int $currentUser The current user
+ * @param array $appRoot The app root config
+ * @return string[] The none writable directory paths inside the app root
+ */
+ private function getAppDirsWithDifferentOwnerForAppRoot(int $currentUser, array $appRoot): array {
+ $appDirsWithDifferentOwner = [];
+ $appsPath = $appRoot['path'];
+ $appsDir = new \DirectoryIterator($appRoot['path']);
+
+ foreach ($appsDir as $fileInfo) {
+ if ($fileInfo->isDir() && !$fileInfo->isDot()) {
+ $absAppPath = $appsPath . DIRECTORY_SEPARATOR . $fileInfo->getFilename();
+ $appDirUser = fileowner($absAppPath);
+ if ($appDirUser !== $currentUser) {
+ $appDirsWithDifferentOwner[] = $absAppPath;
+ }
+ }
+ }
+
+ return $appDirsWithDifferentOwner;
+ }
+
+ public function run(): SetupResult {
+ $currentUser = posix_getuid();
+ $currentUserInfos = posix_getpwuid($currentUser) ?: [];
+ $appDirsWithDifferentOwner = $this->getAppDirsWithDifferentOwner($currentUser);
+ if (count($appDirsWithDifferentOwner) > 0) {
+ return SetupResult::warning(
+ $this->l10n->t("Some app directories are owned by a different user than the web server one. This may be the case if apps have been installed manually. Check the permissions of the following app directories:\n%s", implode("\n", $appDirsWithDifferentOwner))
+ );
+ } else {
+ return SetupResult::success($this->l10n->t('App directories have the correct owner "%s"', [$currentUserInfos['name'] ?? '']));
+ }
+ }
+}
diff --git a/apps/settings/lib/SetupChecks/BruteForceThrottler.php b/apps/settings/lib/SetupChecks/BruteForceThrottler.php
new file mode 100644
index 00000000000..e97e363944f
--- /dev/null
+++ b/apps/settings/lib/SetupChecks/BruteForceThrottler.php
@@ -0,0 +1,57 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCA\Settings\SetupChecks;
+
+use OCP\IL10N;
+use OCP\IRequest;
+use OCP\IURLGenerator;
+use OCP\Security\Bruteforce\IThrottler;
+use OCP\SetupCheck\ISetupCheck;
+use OCP\SetupCheck\SetupResult;
+
+class BruteForceThrottler implements ISetupCheck {
+ public function __construct(
+ private IL10N $l10n,
+ private IURLGenerator $urlGenerator,
+ private IRequest $request,
+ private IThrottler $throttler,
+ ) {
+ }
+
+ public function getCategory(): string {
+ return 'system';
+ }
+
+ public function getName(): string {
+ return $this->l10n->t('Brute-force Throttle');
+ }
+
+ public function run(): SetupResult {
+ $address = $this->request->getRemoteAddress();
+ if ($address === '') {
+ if (\OC::$CLI) {
+ /* We were called from CLI */
+ return SetupResult::info($this->l10n->t('Your remote address could not be determined.'));
+ } else {
+ /* Should never happen */
+ return SetupResult::error($this->l10n->t('Your remote address could not be determined.'));
+ }
+ } elseif ($this->throttler->showBruteforceWarning($address)) {
+ return SetupResult::error(
+ $this->l10n->t('Your remote address was identified as "%s" and is brute-force throttled at the moment slowing down the performance of various requests. If the remote address is not your address this can be an indication that a proxy is not configured correctly.', [$address]),
+ $this->urlGenerator->linkToDocs('admin-reverse-proxy')
+ );
+ } else {
+ return SetupResult::success(
+ $this->l10n->t('Your remote address "%s" is not brute-force throttled.', [$address])
+ );
+ }
+ }
+}
diff --git a/apps/settings/lib/SetupChecks/CheckUserCertificates.php b/apps/settings/lib/SetupChecks/CheckUserCertificates.php
index 52fea7b6551..d1e3551c085 100644
--- a/apps/settings/lib/SetupChecks/CheckUserCertificates.php
+++ b/apps/settings/lib/SetupChecks/CheckUserCertificates.php
@@ -3,74 +3,42 @@
declare(strict_types=1);
/**
- * @copyright Copyright (c) 2020 Morris Jobke <hey@morrisjobke.de>
- *
- * @author Morris Jobke <hey@morrisjobke.de>
- *
- * @license GNU AGPL version 3 or any later version
- *
- * 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/>.
- *
+ * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\Settings\SetupChecks;
use OCP\IConfig;
use OCP\IL10N;
-use OCP\IURLGenerator;
+use OCP\SetupCheck\ISetupCheck;
+use OCP\SetupCheck\SetupResult;
-class CheckUserCertificates {
- /** @var IL10N */
- private $l10n;
- /** @var string */
- private $configValue;
- /** @var IURLGenerator */
- private $urlGenerator;
+class CheckUserCertificates implements ISetupCheck {
+ private string $configValue;
- public function __construct(IL10N $l10n, IConfig $config, IURLGenerator $urlGenerator) {
- $this->l10n = $l10n;
- $configValue = $config->getAppValue('files_external', 'user_certificate_scan', '');
- $this->configValue = $configValue;
- $this->urlGenerator = $urlGenerator;
+ public function __construct(
+ private IL10N $l10n,
+ IConfig $config,
+ ) {
+ $this->configValue = $config->getAppValue('files_external', 'user_certificate_scan', '');
}
- public function description(): string {
- if ($this->configValue === '') {
- return '';
- }
- if ($this->configValue === 'not-run-yet') {
- return $this->l10n->t('A background job is pending that checks for user imported SSL certificates. Please check back later.');
- }
- return $this->l10n->t('There are some user imported SSL certificates present, that are not used anymore with Nextcloud 21. They can be imported on the command line via "occ security:certificates:import" command. Their paths inside the data directory are shown below.');
+ public function getCategory(): string {
+ return 'security';
}
- public function severity(): string {
- return 'warning';
+ public function getName(): string {
+ return $this->l10n->t('Old administration imported certificates');
}
- public function run(): bool {
+ public function run(): SetupResult {
// all fine if neither "not-run-yet" nor a result
- return $this->configValue === '';
- }
-
- public function elements(): array {
- if ($this->configValue === '' || $this->configValue === 'not-run-yet') {
- return [];
+ if ($this->configValue === '') {
+ return SetupResult::success();
}
- $data = json_decode($this->configValue);
- if (!is_array($data)) {
- return [];
+ if ($this->configValue === 'not-run-yet') {
+ return SetupResult::info($this->l10n->t('A background job is pending that checks for administration imported SSL certificates. Please check back later.'));
}
- return $data;
+ return SetupResult::error($this->l10n->t('There are some administration imported SSL certificates present, that are not used anymore with Nextcloud 21. They can be imported on the command line via "occ security:certificates:import" command. Their paths inside the data directory are shown below.'));
}
}
diff --git a/apps/settings/lib/SetupChecks/CodeIntegrity.php b/apps/settings/lib/SetupChecks/CodeIntegrity.php
new file mode 100644
index 00000000000..2b4271fae9c
--- /dev/null
+++ b/apps/settings/lib/SetupChecks/CodeIntegrity.php
@@ -0,0 +1,66 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\Settings\SetupChecks;
+
+use OC\IntegrityCheck\Checker;
+use OCP\IL10N;
+use OCP\IURLGenerator;
+use OCP\SetupCheck\ISetupCheck;
+use OCP\SetupCheck\SetupResult;
+
+class CodeIntegrity implements ISetupCheck {
+ public function __construct(
+ private IL10N $l10n,
+ private IURLGenerator $urlGenerator,
+ private Checker $checker,
+ ) {
+ }
+
+ public function getName(): string {
+ return $this->l10n->t('Code integrity');
+ }
+
+ public function getCategory(): string {
+ return 'security';
+ }
+
+ public function run(): SetupResult {
+ if (!$this->checker->isCodeCheckEnforced()) {
+ return SetupResult::info($this->l10n->t('Integrity checker has been disabled. Integrity cannot be verified.'));
+ }
+
+ // If there are no results we need to run the verification
+ if ($this->checker->getResults() === null) {
+ $this->checker->runInstanceVerification();
+ }
+
+ if ($this->checker->hasPassedCheck()) {
+ return SetupResult::success($this->l10n->t('No altered files'));
+ } else {
+ return SetupResult::error(
+ $this->l10n->t('Some files have not passed the integrity check. {link1} {link2}'),
+ $this->urlGenerator->linkToDocs('admin-code-integrity'),
+ [
+ 'link1' => [
+ 'type' => 'highlight',
+ 'id' => 'getFailedIntegrityCheckFiles',
+ 'name' => 'List of invalid files…',
+ 'link' => $this->urlGenerator->linkToRoute('settings.CheckSetup.getFailedIntegrityCheckFiles'),
+ ],
+ 'link2' => [
+ 'type' => 'highlight',
+ 'id' => 'rescanFailedIntegrityCheck',
+ 'name' => 'Rescan…',
+ 'link' => $this->urlGenerator->linkToRoute('settings.CheckSetup.rescanFailedIntegrityCheck'),
+ ],
+ ],
+ );
+ }
+ }
+}
diff --git a/apps/settings/lib/SetupChecks/CronErrors.php b/apps/settings/lib/SetupChecks/CronErrors.php
new file mode 100644
index 00000000000..dc625b04477
--- /dev/null
+++ b/apps/settings/lib/SetupChecks/CronErrors.php
@@ -0,0 +1,45 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCA\Settings\SetupChecks;
+
+use OCP\IConfig;
+use OCP\IL10N;
+use OCP\SetupCheck\ISetupCheck;
+use OCP\SetupCheck\SetupResult;
+
+class CronErrors implements ISetupCheck {
+ public function __construct(
+ private IL10N $l10n,
+ private IConfig $config,
+ ) {
+ }
+
+ public function getCategory(): string {
+ return 'system';
+ }
+
+ public function getName(): string {
+ return $this->l10n->t('Cron errors');
+ }
+
+ public function run(): SetupResult {
+ $errors = json_decode($this->config->getAppValue('core', 'cronErrors', ''), true);
+ if (is_array($errors) && count($errors) > 0) {
+ return SetupResult::error(
+ $this->l10n->t(
+ "It was not possible to execute the cron job via CLI. The following technical errors have appeared:\n%s",
+ implode("\n", array_map(fn (array $error) => '- ' . $error['error'] . ' ' . $error['hint'], $errors))
+ )
+ );
+ } else {
+ return SetupResult::success($this->l10n->t('The last cron job ran without errors.'));
+ }
+ }
+}
diff --git a/apps/settings/lib/SetupChecks/CronInfo.php b/apps/settings/lib/SetupChecks/CronInfo.php
new file mode 100644
index 00000000000..f18148c9d14
--- /dev/null
+++ b/apps/settings/lib/SetupChecks/CronInfo.php
@@ -0,0 +1,66 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCA\Settings\SetupChecks;
+
+use OCP\IAppConfig;
+use OCP\IConfig;
+use OCP\IDateTimeFormatter;
+use OCP\IL10N;
+use OCP\IURLGenerator;
+use OCP\SetupCheck\ISetupCheck;
+use OCP\SetupCheck\SetupResult;
+
+class CronInfo implements ISetupCheck {
+ public function __construct(
+ private IL10N $l10n,
+ private IConfig $config,
+ private IAppConfig $appConfig,
+ private IURLGenerator $urlGenerator,
+ private IDateTimeFormatter $dateTimeFormatter,
+ ) {
+ }
+
+ public function getCategory(): string {
+ return 'system';
+ }
+
+ public function getName(): string {
+ return $this->l10n->t('Cron last run');
+ }
+
+ public function run(): SetupResult {
+ $lastCronRun = $this->appConfig->getValueInt('core', 'lastcron', 0);
+ $relativeTime = $this->dateTimeFormatter->formatTimeSpan($lastCronRun);
+
+ if ((time() - $lastCronRun) > 3600) {
+ return SetupResult::error(
+ $this->l10n->t(
+ 'Last background job execution ran %s. Something seems wrong. {link}.',
+ [$relativeTime]
+ ),
+ descriptionParameters:[
+ 'link' => [
+ 'type' => 'highlight',
+ 'id' => 'backgroundjobs',
+ 'name' => 'Check the background job settings',
+ 'link' => $this->urlGenerator->linkToRoute('settings.AdminSettings.index', ['section' => 'server']) . '#backgroundjobs',
+ ],
+ ],
+ );
+ } else {
+ return SetupResult::success(
+ $this->l10n->t(
+ 'Last background job execution ran %s.',
+ [$relativeTime]
+ )
+ );
+ }
+ }
+}
diff --git a/apps/settings/lib/SetupChecks/DataDirectoryProtected.php b/apps/settings/lib/SetupChecks/DataDirectoryProtected.php
new file mode 100644
index 00000000000..e572c345079
--- /dev/null
+++ b/apps/settings/lib/SetupChecks/DataDirectoryProtected.php
@@ -0,0 +1,71 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\Settings\SetupChecks;
+
+use OCP\Http\Client\IClientService;
+use OCP\IConfig;
+use OCP\IL10N;
+use OCP\IURLGenerator;
+use OCP\SetupCheck\CheckServerResponseTrait;
+use OCP\SetupCheck\ISetupCheck;
+use OCP\SetupCheck\SetupResult;
+use Psr\Log\LoggerInterface;
+
+/**
+ * Checks if the data directory can not be accessed from outside
+ */
+class DataDirectoryProtected implements ISetupCheck {
+ use CheckServerResponseTrait;
+
+ public function __construct(
+ protected IL10N $l10n,
+ protected IConfig $config,
+ protected IURLGenerator $urlGenerator,
+ protected IClientService $clientService,
+ protected LoggerInterface $logger,
+ ) {
+ }
+
+ public function getCategory(): string {
+ return 'network';
+ }
+
+ public function getName(): string {
+ return $this->l10n->t('Data directory protected');
+ }
+
+ public function run(): SetupResult {
+ $dataDir = str_replace(\OC::$SERVERROOT . '/', '', $this->config->getSystemValueString('datadirectory', ''));
+ $dataUrl = $this->urlGenerator->linkTo('', $dataDir . '/.ncdata');
+
+ $noResponse = true;
+ foreach ($this->runRequest('GET', $dataUrl, [ 'httpErrors' => false ]) as $response) {
+ $noResponse = false;
+ if ($response->getStatusCode() < 400) {
+ // Read the response body
+ $body = $response->getBody();
+ if (is_resource($body)) {
+ $body = stream_get_contents($body, 64);
+ }
+
+ if (str_contains($body, '# Nextcloud data directory')) {
+ return SetupResult::error($this->l10n->t('Your data directory and files are probably accessible from the internet. The .htaccess file is not working. It is strongly recommended that you configure your web server so that the data directory is no longer accessible, or move the data directory outside the web server document root.'));
+ }
+ } else {
+ $this->logger->debug('[expected] Could not access data directory from outside.', ['url' => $dataUrl]);
+ }
+ }
+
+ if ($noResponse) {
+ return SetupResult::warning($this->l10n->t('Could not check that the data directory is protected. Please check manually that your server does not allow access to the data directory.') . "\n" . $this->serverConfigHelp());
+ }
+ return SetupResult::success();
+
+ }
+}
diff --git a/apps/settings/lib/SetupChecks/DatabaseHasMissingColumns.php b/apps/settings/lib/SetupChecks/DatabaseHasMissingColumns.php
new file mode 100644
index 00000000000..ec004f73021
--- /dev/null
+++ b/apps/settings/lib/SetupChecks/DatabaseHasMissingColumns.php
@@ -0,0 +1,72 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\Settings\SetupChecks;
+
+use OC\DB\Connection;
+use OC\DB\MissingColumnInformation;
+use OC\DB\SchemaWrapper;
+use OCP\DB\Events\AddMissingColumnsEvent;
+use OCP\EventDispatcher\IEventDispatcher;
+use OCP\IL10N;
+use OCP\SetupCheck\ISetupCheck;
+use OCP\SetupCheck\SetupResult;
+
+class DatabaseHasMissingColumns implements ISetupCheck {
+ public function __construct(
+ private IL10N $l10n,
+ private Connection $connection,
+ private IEventDispatcher $dispatcher,
+ ) {
+ }
+
+ public function getCategory(): string {
+ return 'database';
+ }
+
+ public function getName(): string {
+ return $this->l10n->t('Database missing columns');
+ }
+
+ private function getMissingColumns(): array {
+ $columnInfo = new MissingColumnInformation();
+ // Dispatch event so apps can also hint for pending column updates if needed
+ $event = new AddMissingColumnsEvent();
+ $this->dispatcher->dispatchTyped($event);
+ $missingColumns = $event->getMissingColumns();
+
+ if (!empty($missingColumns)) {
+ $schema = new SchemaWrapper($this->connection);
+ foreach ($missingColumns as $missingColumn) {
+ if ($schema->hasTable($missingColumn['tableName'])) {
+ $table = $schema->getTable($missingColumn['tableName']);
+ if (!$table->hasColumn($missingColumn['columnName'])) {
+ $columnInfo->addHintForMissingColumn($missingColumn['tableName'], $missingColumn['columnName']);
+ }
+ }
+ }
+ }
+
+ return $columnInfo->getListOfMissingColumns();
+ }
+
+ public function run(): SetupResult {
+ $missingColumns = $this->getMissingColumns();
+ if (empty($missingColumns)) {
+ return SetupResult::success('None');
+ } else {
+ $list = '';
+ foreach ($missingColumns as $missingColumn) {
+ $list .= "\n" . $this->l10n->t('Missing optional column "%s" in table "%s".', [$missingColumn['columnName'], $missingColumn['tableName']]);
+ }
+ return SetupResult::warning(
+ $this->l10n->t('The database is missing some optional columns. Due to the fact that adding columns on big tables could take some time they were not added automatically when they can be optional. By running "occ db:add-missing-columns" those missing columns could be added manually while the instance keeps running. Once the columns are added some features might improve responsiveness or usability.') . $list
+ );
+ }
+ }
+}
diff --git a/apps/settings/lib/SetupChecks/DatabaseHasMissingIndices.php b/apps/settings/lib/SetupChecks/DatabaseHasMissingIndices.php
new file mode 100644
index 00000000000..97e80c2aaa9
--- /dev/null
+++ b/apps/settings/lib/SetupChecks/DatabaseHasMissingIndices.php
@@ -0,0 +1,93 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\Settings\SetupChecks;
+
+use OC\DB\Connection;
+use OC\DB\MissingIndexInformation;
+use OC\DB\SchemaWrapper;
+use OCP\DB\Events\AddMissingIndicesEvent;
+use OCP\EventDispatcher\IEventDispatcher;
+use OCP\IL10N;
+use OCP\IURLGenerator;
+use OCP\SetupCheck\ISetupCheck;
+use OCP\SetupCheck\SetupResult;
+
+class DatabaseHasMissingIndices implements ISetupCheck {
+ public function __construct(
+ private IL10N $l10n,
+ private Connection $connection,
+ private IEventDispatcher $dispatcher,
+ private IURLGenerator $urlGenerator,
+ ) {
+ }
+
+ public function getCategory(): string {
+ return 'database';
+ }
+
+ public function getName(): string {
+ return $this->l10n->t('Database missing indices');
+ }
+
+ private function getMissingIndices(): array {
+ $indexInfo = new MissingIndexInformation();
+ // Dispatch event so apps can also hint for pending index updates if needed
+ $event = new AddMissingIndicesEvent();
+ $this->dispatcher->dispatchTyped($event);
+ $missingIndices = $event->getMissingIndices();
+ $indicesToReplace = $event->getIndicesToReplace();
+
+ if (!empty($missingIndices)) {
+ $schema = new SchemaWrapper($this->connection);
+ foreach ($missingIndices as $missingIndex) {
+ if ($schema->hasTable($missingIndex['tableName'])) {
+ $table = $schema->getTable($missingIndex['tableName']);
+ if (!$table->hasIndex($missingIndex['indexName'])) {
+ $indexInfo->addHintForMissingIndex($missingIndex['tableName'], $missingIndex['indexName']);
+ }
+ }
+ }
+ }
+
+ if (!empty($indicesToReplace)) {
+ $schema = new SchemaWrapper($this->connection);
+ foreach ($indicesToReplace as $indexToReplace) {
+ if ($schema->hasTable($indexToReplace['tableName'])) {
+ $table = $schema->getTable($indexToReplace['tableName']);
+ if (!$table->hasIndex($indexToReplace['newIndexName'])) {
+ $indexInfo->addHintForMissingIndex($indexToReplace['tableName'], $indexToReplace['newIndexName']);
+ }
+ }
+ }
+ }
+
+ return $indexInfo->getListOfMissingIndices();
+ }
+
+ public function run(): SetupResult {
+ $missingIndices = $this->getMissingIndices();
+ if (empty($missingIndices)) {
+ return SetupResult::success('None');
+ } else {
+ $processed = 0;
+ $list = $this->l10n->t('Missing indices:');
+ foreach ($missingIndices as $missingIndex) {
+ $processed++;
+ $list .= "\n " . $this->l10n->t('"%s" in table "%s"', [$missingIndex['indexName'], $missingIndex['tableName']]);
+ if (count($missingIndices) > $processed) {
+ $list .= ', ';
+ }
+ }
+ return SetupResult::warning(
+ $this->l10n->t('Detected some missing optional indices. Occasionally new indices are added (by Nextcloud or installed applications) to improve database performance. Adding indices can sometimes take awhile and temporarily hurt performance so this is not done automatically during upgrades. Once the indices are added, queries to those tables should be faster. Use the command `occ db:add-missing-indices` to add them.') . "\n" . $list,
+ $this->urlGenerator->linkToDocs('admin-long-running-migration-steps')
+ );
+ }
+ }
+}
diff --git a/apps/settings/lib/SetupChecks/DatabaseHasMissingPrimaryKeys.php b/apps/settings/lib/SetupChecks/DatabaseHasMissingPrimaryKeys.php
new file mode 100644
index 00000000000..03810ca8faf
--- /dev/null
+++ b/apps/settings/lib/SetupChecks/DatabaseHasMissingPrimaryKeys.php
@@ -0,0 +1,72 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\Settings\SetupChecks;
+
+use OC\DB\Connection;
+use OC\DB\MissingPrimaryKeyInformation;
+use OC\DB\SchemaWrapper;
+use OCP\DB\Events\AddMissingPrimaryKeyEvent;
+use OCP\EventDispatcher\IEventDispatcher;
+use OCP\IL10N;
+use OCP\SetupCheck\ISetupCheck;
+use OCP\SetupCheck\SetupResult;
+
+class DatabaseHasMissingPrimaryKeys implements ISetupCheck {
+ public function __construct(
+ private IL10N $l10n,
+ private Connection $connection,
+ private IEventDispatcher $dispatcher,
+ ) {
+ }
+
+ public function getCategory(): string {
+ return 'database';
+ }
+
+ public function getName(): string {
+ return $this->l10n->t('Database missing primary keys');
+ }
+
+ private function getMissingPrimaryKeys(): array {
+ $primaryKeyInfo = new MissingPrimaryKeyInformation();
+ // Dispatch event so apps can also hint for pending primary key updates if needed
+ $event = new AddMissingPrimaryKeyEvent();
+ $this->dispatcher->dispatchTyped($event);
+ $missingPrimaryKeys = $event->getMissingPrimaryKeys();
+
+ if (!empty($missingPrimaryKeys)) {
+ $schema = new SchemaWrapper($this->connection);
+ foreach ($missingPrimaryKeys as $missingPrimaryKey) {
+ if ($schema->hasTable($missingPrimaryKey['tableName'])) {
+ $table = $schema->getTable($missingPrimaryKey['tableName']);
+ if ($table->getPrimaryKey() === null) {
+ $primaryKeyInfo->addHintForMissingPrimaryKey($missingPrimaryKey['tableName']);
+ }
+ }
+ }
+ }
+
+ return $primaryKeyInfo->getListOfMissingPrimaryKeys();
+ }
+
+ public function run(): SetupResult {
+ $missingPrimaryKeys = $this->getMissingPrimaryKeys();
+ if (empty($missingPrimaryKeys)) {
+ return SetupResult::success('None');
+ } else {
+ $list = '';
+ foreach ($missingPrimaryKeys as $missingPrimaryKey) {
+ $list .= "\n" . $this->l10n->t('Missing primary key on table "%s".', [$missingPrimaryKey['tableName']]);
+ }
+ return SetupResult::warning(
+ $this->l10n->t('The database is missing some primary keys. Due to the fact that adding primary keys on big tables could take some time they were not added automatically. By running "occ db:add-missing-primary-keys" those missing primary keys could be added manually while the instance keeps running.') . $list
+ );
+ }
+ }
+}
diff --git a/apps/settings/lib/SetupChecks/DatabasePendingBigIntConversions.php b/apps/settings/lib/SetupChecks/DatabasePendingBigIntConversions.php
new file mode 100644
index 00000000000..bb9794c1e03
--- /dev/null
+++ b/apps/settings/lib/SetupChecks/DatabasePendingBigIntConversions.php
@@ -0,0 +1,82 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\Settings\SetupChecks;
+
+use Doctrine\DBAL\Types\BigIntType;
+use OC\Core\Command\Db\ConvertFilecacheBigInt;
+use OC\DB\Connection;
+use OC\DB\SchemaWrapper;
+use OCP\EventDispatcher\IEventDispatcher;
+use OCP\IDBConnection;
+use OCP\IL10N;
+use OCP\IURLGenerator;
+use OCP\SetupCheck\ISetupCheck;
+use OCP\SetupCheck\SetupResult;
+
+class DatabasePendingBigIntConversions implements ISetupCheck {
+ public function __construct(
+ private IL10N $l10n,
+ private IURLGenerator $urlGenerator,
+ private Connection $db,
+ private IEventDispatcher $dispatcher,
+ private IDBConnection $connection,
+ ) {
+ }
+
+ public function getCategory(): string {
+ return 'database';
+ }
+
+ public function getName(): string {
+ return $this->l10n->t('Database pending bigint migrations');
+ }
+
+ protected function getBigIntConversionPendingColumns(): array {
+ $tables = ConvertFilecacheBigInt::getColumnsByTable();
+
+ $schema = new SchemaWrapper($this->db);
+ $isSqlite = $this->connection->getDatabaseProvider() === IDBConnection::PLATFORM_SQLITE;
+ $pendingColumns = [];
+
+ foreach ($tables as $tableName => $columns) {
+ if (!$schema->hasTable($tableName)) {
+ continue;
+ }
+
+ $table = $schema->getTable($tableName);
+ foreach ($columns as $columnName) {
+ $column = $table->getColumn($columnName);
+ $isAutoIncrement = $column->getAutoincrement();
+ $isAutoIncrementOnSqlite = $isSqlite && $isAutoIncrement;
+ if (!($column->getType() instanceof BigIntType) && !$isAutoIncrementOnSqlite) {
+ $pendingColumns[] = $tableName . '.' . $columnName;
+ }
+ }
+ }
+
+ return $pendingColumns;
+ }
+
+ public function run(): SetupResult {
+ $pendingColumns = $this->getBigIntConversionPendingColumns();
+ if (empty($pendingColumns)) {
+ return SetupResult::success('None');
+ } else {
+ $list = '';
+ foreach ($pendingColumns as $pendingColumn) {
+ $list .= "\n$pendingColumn";
+ }
+ $list .= "\n";
+ return SetupResult::info(
+ $this->l10n->t('Some columns in the database are missing a conversion to big int. Due to the fact that changing column types on big tables could take some time they were not changed automatically. By running "occ db:convert-filecache-bigint" those pending changes could be applied manually. This operation needs to be made while the instance is offline.') . $list,
+ $this->urlGenerator->linkToDocs('admin-bigint-conversion')
+ );
+ }
+ }
+}
diff --git a/apps/settings/lib/SetupChecks/DebugMode.php b/apps/settings/lib/SetupChecks/DebugMode.php
new file mode 100644
index 00000000000..8841ecc607d
--- /dev/null
+++ b/apps/settings/lib/SetupChecks/DebugMode.php
@@ -0,0 +1,38 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\Settings\SetupChecks;
+
+use OCP\IConfig;
+use OCP\IL10N;
+use OCP\SetupCheck\ISetupCheck;
+use OCP\SetupCheck\SetupResult;
+
+class DebugMode implements ISetupCheck {
+ public function __construct(
+ private IL10N $l10n,
+ private IConfig $config,
+ ) {
+ }
+
+ public function getName(): string {
+ return $this->l10n->t('Debug mode');
+ }
+
+ public function getCategory(): string {
+ return 'system';
+ }
+
+ public function run(): SetupResult {
+ if ($this->config->getSystemValueBool('debug', false)) {
+ return SetupResult::warning($this->l10n->t('This instance is running in debug mode. Only enable this for local development and not in production environments.'));
+ } else {
+ return SetupResult::success($this->l10n->t('Debug mode is disabled.'));
+ }
+ }
+}
diff --git a/apps/settings/lib/SetupChecks/DefaultPhoneRegionSet.php b/apps/settings/lib/SetupChecks/DefaultPhoneRegionSet.php
new file mode 100644
index 00000000000..fa94cd9d059
--- /dev/null
+++ b/apps/settings/lib/SetupChecks/DefaultPhoneRegionSet.php
@@ -0,0 +1,41 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\Settings\SetupChecks;
+
+use OCP\IConfig;
+use OCP\IL10N;
+use OCP\SetupCheck\ISetupCheck;
+use OCP\SetupCheck\SetupResult;
+
+class DefaultPhoneRegionSet implements ISetupCheck {
+ public function __construct(
+ private IL10N $l10n,
+ private IConfig $config,
+ ) {
+ }
+
+ public function getName(): string {
+ return $this->l10n->t('Default phone region');
+ }
+
+ public function getCategory(): string {
+ return 'config';
+ }
+
+ public function run(): SetupResult {
+ if ($this->config->getSystemValueString('default_phone_region', '') !== '') {
+ return SetupResult::success($this->config->getSystemValueString('default_phone_region', ''));
+ } else {
+ return SetupResult::info(
+ $this->l10n->t('Your installation has no default phone region set. This is required to validate phone numbers in the profile settings without a country code. To allow numbers without a country code, please add "default_phone_region" with the respective ISO 3166-1 code of the region to your config file.'),
+ 'https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2#Officially_assigned_code_elements'
+ );
+ }
+ }
+}
diff --git a/apps/settings/lib/SetupChecks/EmailTestSuccessful.php b/apps/settings/lib/SetupChecks/EmailTestSuccessful.php
new file mode 100644
index 00000000000..8cad8e82156
--- /dev/null
+++ b/apps/settings/lib/SetupChecks/EmailTestSuccessful.php
@@ -0,0 +1,61 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCA\Settings\SetupChecks;
+
+use OCP\IConfig;
+use OCP\IL10N;
+use OCP\IURLGenerator;
+use OCP\SetupCheck\ISetupCheck;
+use OCP\SetupCheck\SetupResult;
+
+class EmailTestSuccessful implements ISetupCheck {
+ public function __construct(
+ private IL10N $l10n,
+ private IConfig $config,
+ private IURLGenerator $urlGenerator,
+ ) {
+ }
+
+ public function getName(): string {
+ return $this->l10n->t('Email test');
+ }
+
+ public function getCategory(): string {
+ return 'config';
+ }
+
+ protected function wasEmailTestSuccessful(): bool {
+ // Handle the case that the configuration was set before the check was introduced or it was only set via command line and not from the UI
+ if ($this->config->getAppValue('core', 'emailTestSuccessful', '') === '' && $this->config->getSystemValue('mail_domain', '') === '') {
+ return false;
+ }
+
+ // The mail test was unsuccessful or the config was changed using the UI without verifying with a testmail, hence return false
+ if ($this->config->getAppValue('core', 'emailTestSuccessful', '') === '0') {
+ return false;
+ }
+
+ return true;
+ }
+
+ public function run(): SetupResult {
+ if ($this->config->getSystemValueString('mail_smtpmode', 'smtp') === 'null') {
+ return SetupResult::success($this->l10n->t('Mail delivery is disabled by instance config "%s".', ['mail_smtpmode']));
+ } elseif ($this->wasEmailTestSuccessful()) {
+ return SetupResult::success($this->l10n->t('Email test was successfully sent'));
+ } else {
+ // If setup check could link to settings pages, this one should link to OC.generateUrl('/settings/admin')
+ return SetupResult::info(
+ $this->l10n->t('You have not set or verified your email server configuration, yet. Please head over to the "Basic settings" in order to set them. Afterwards, use the "Send email" button below the form to verify your settings.'),
+ $this->urlGenerator->linkToDocs('admin-email')
+ );
+ }
+ }
+}
diff --git a/apps/settings/lib/SetupChecks/FileLocking.php b/apps/settings/lib/SetupChecks/FileLocking.php
new file mode 100644
index 00000000000..f683ee05f03
--- /dev/null
+++ b/apps/settings/lib/SetupChecks/FileLocking.php
@@ -0,0 +1,61 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCA\Settings\SetupChecks;
+
+use OC\Lock\DBLockingProvider;
+use OC\Lock\NoopLockingProvider;
+use OCP\IL10N;
+use OCP\IURLGenerator;
+use OCP\Lock\ILockingProvider;
+use OCP\SetupCheck\ISetupCheck;
+use OCP\SetupCheck\SetupResult;
+
+class FileLocking implements ISetupCheck {
+ public function __construct(
+ private IL10N $l10n,
+ private IURLGenerator $urlGenerator,
+ private ILockingProvider $lockingProvider,
+ ) {
+ }
+
+ public function getName(): string {
+ return $this->l10n->t('Transactional File Locking');
+ }
+
+ public function getCategory(): string {
+ return 'system';
+ }
+
+ protected function hasWorkingFileLocking(): bool {
+ return !($this->lockingProvider instanceof NoopLockingProvider);
+ }
+
+ protected function hasDBFileLocking(): bool {
+ return ($this->lockingProvider instanceof DBLockingProvider);
+ }
+
+ public function run(): SetupResult {
+ if (!$this->hasWorkingFileLocking()) {
+ return SetupResult::error(
+ $this->l10n->t('Transactional File Locking is disabled. This is not a a supported configuraton. It may lead to difficult to isolate problems including file corruption. Please remove the `\'filelocking.enabled\' => false` configuration entry from your `config.php` to avoid these problems.'),
+ $this->urlGenerator->linkToDocs('admin-transactional-locking')
+ );
+ }
+
+ if ($this->hasDBFileLocking()) {
+ return SetupResult::info(
+ $this->l10n->t('The database is used for transactional file locking. To enhance performance, please configure memcache, if available.'),
+ $this->urlGenerator->linkToDocs('admin-transactional-locking')
+ );
+ }
+
+ return SetupResult::success();
+ }
+}
diff --git a/apps/settings/lib/SetupChecks/ForwardedForHeaders.php b/apps/settings/lib/SetupChecks/ForwardedForHeaders.php
new file mode 100644
index 00000000000..8238ce07554
--- /dev/null
+++ b/apps/settings/lib/SetupChecks/ForwardedForHeaders.php
@@ -0,0 +1,87 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCA\Settings\SetupChecks;
+
+use OCP\IConfig;
+use OCP\IL10N;
+use OCP\IRequest;
+use OCP\IURLGenerator;
+use OCP\SetupCheck\ISetupCheck;
+use OCP\SetupCheck\SetupResult;
+
+class ForwardedForHeaders implements ISetupCheck {
+ public function __construct(
+ private IL10N $l10n,
+ private IConfig $config,
+ private IURLGenerator $urlGenerator,
+ private IRequest $request,
+ ) {
+ }
+
+ public function getCategory(): string {
+ return 'security';
+ }
+
+ public function getName(): string {
+ return $this->l10n->t('Forwarded for headers');
+ }
+
+ public function run(): SetupResult {
+ $trustedProxies = $this->config->getSystemValue('trusted_proxies', []);
+ $remoteAddress = $this->request->getHeader('REMOTE_ADDR');
+ $detectedRemoteAddress = $this->request->getRemoteAddress();
+
+ if (!\is_array($trustedProxies)) {
+ return SetupResult::error($this->l10n->t('Your "trusted_proxies" setting is not correctly set, it should be an array.'));
+ }
+
+ foreach ($trustedProxies as $proxy) {
+ $addressParts = explode('/', $proxy, 2);
+ if (filter_var($addressParts[0], FILTER_VALIDATE_IP) === false || !ctype_digit($addressParts[1] ?? '24')) {
+ return SetupResult::error(
+ $this->l10n->t('Your "trusted_proxies" setting is not correctly set, it should be an array of IP addresses - optionally with range in CIDR notation.'),
+ $this->urlGenerator->linkToDocs('admin-reverse-proxy'),
+ );
+ }
+ }
+
+ if (($remoteAddress === '') && ($detectedRemoteAddress === '')) {
+ if (\OC::$CLI) {
+ /* We were called from CLI */
+ return SetupResult::info($this->l10n->t('Your remote address could not be determined.'));
+ } else {
+ /* Should never happen */
+ return SetupResult::error($this->l10n->t('Your remote address could not be determined.'));
+ }
+ }
+
+ if (empty($trustedProxies) && $this->request->getHeader('X-Forwarded-Host') !== '') {
+ return SetupResult::error(
+ $this->l10n->t('The reverse proxy header configuration is incorrect. This is a security issue and can allow an attacker to spoof their IP address as visible to the Nextcloud.'),
+ $this->urlGenerator->linkToDocs('admin-reverse-proxy')
+ );
+ }
+
+ if (\in_array($remoteAddress, $trustedProxies, true) && ($remoteAddress !== '127.0.0.1')) {
+ if ($remoteAddress !== $detectedRemoteAddress) {
+ /* Remote address was successfuly fixed */
+ return SetupResult::success($this->l10n->t('Your IP address was resolved as %s', [$detectedRemoteAddress]));
+ } else {
+ return SetupResult::warning(
+ $this->l10n->t('The reverse proxy header configuration is incorrect, or you are accessing Nextcloud from a trusted proxy. If not, this is a security issue and can allow an attacker to spoof their IP address as visible to the Nextcloud.'),
+ $this->urlGenerator->linkToDocs('admin-reverse-proxy')
+ );
+ }
+ }
+
+ /* Either not enabled or working correctly */
+ return SetupResult::success();
+ }
+}
diff --git a/apps/settings/lib/SetupChecks/HttpsUrlGeneration.php b/apps/settings/lib/SetupChecks/HttpsUrlGeneration.php
new file mode 100644
index 00000000000..7f76297748b
--- /dev/null
+++ b/apps/settings/lib/SetupChecks/HttpsUrlGeneration.php
@@ -0,0 +1,65 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCA\Settings\SetupChecks;
+
+use OCP\IL10N;
+use OCP\IRequest;
+use OCP\IURLGenerator;
+use OCP\SetupCheck\ISetupCheck;
+use OCP\SetupCheck\SetupResult;
+
+class HttpsUrlGeneration implements ISetupCheck {
+ public function __construct(
+ private IL10N $l10n,
+ private IURLGenerator $urlGenerator,
+ private IRequest $request,
+ ) {
+ }
+
+ public function getCategory(): string {
+ return 'security';
+ }
+
+ public function getName(): string {
+ return $this->l10n->t('HTTPS access and URLs');
+ }
+
+ public function run(): SetupResult {
+ if (!\OC::$CLI && $this->request->getServerProtocol() !== 'https') {
+ if (!preg_match('/(?:^(?:localhost|127\.0\.0\.1|::1)|\.onion)$/', $this->request->getInsecureServerHost())) {
+ return SetupResult::error(
+ $this->l10n->t('Accessing site insecurely via HTTP. You are strongly advised to set up your server to require HTTPS instead. Without it some important web functionality like "copy to clipboard" or "service workers" will not work!'),
+ $this->urlGenerator->linkToDocs('admin-security')
+ );
+ } else {
+ return SetupResult::info(
+ $this->l10n->t('Accessing site insecurely via HTTP.'),
+ $this->urlGenerator->linkToDocs('admin-security')
+ );
+ }
+ }
+ $generatedUrl = $this->urlGenerator->getAbsoluteURL('index.php');
+ if (!str_starts_with($generatedUrl, 'https://')) {
+ if (!\OC::$CLI) {
+ return SetupResult::warning(
+ $this->l10n->t('You are accessing your instance over a secure connection, however your instance is generating insecure URLs. This likely means that your instance is behind a reverse proxy and the Nextcloud `overwrite*` config values are not set correctly.'),
+ $this->urlGenerator->linkToDocs('admin-reverse-proxy')
+ );
+ /* We were called from CLI so we can't be 100% sure which scenario is applicable */
+ } else {
+ return SetupResult::info(
+ $this->l10n->t('Your instance is generating insecure URLs. If you access your instance over HTTPS, this likely means that your instance is behind a reverse proxy and the Nextcloud `overwrite*` config values are not set correctly.'),
+ $this->urlGenerator->linkToDocs('admin-reverse-proxy')
+ );
+ }
+ }
+ return SetupResult::success($this->l10n->t('You are accessing your instance over a secure connection, and your instance is generating secure URLs.'));
+ }
+}
diff --git a/apps/settings/lib/SetupChecks/InternetConnectivity.php b/apps/settings/lib/SetupChecks/InternetConnectivity.php
new file mode 100644
index 00000000000..18f2af63b8d
--- /dev/null
+++ b/apps/settings/lib/SetupChecks/InternetConnectivity.php
@@ -0,0 +1,80 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\Settings\SetupChecks;
+
+use OCP\Http\Client\IClientService;
+use OCP\IConfig;
+use OCP\IL10N;
+use OCP\SetupCheck\ISetupCheck;
+use OCP\SetupCheck\SetupResult;
+use Psr\Log\LoggerInterface;
+
+/**
+ * Checks if the server can connect to the internet using HTTPS and HTTP
+ */
+class InternetConnectivity implements ISetupCheck {
+ public function __construct(
+ private IL10N $l10n,
+ private IConfig $config,
+ private IClientService $clientService,
+ private LoggerInterface $logger,
+ ) {
+ }
+
+ public function getCategory(): string {
+ return 'network';
+ }
+
+ public function getName(): string {
+ return $this->l10n->t('Internet connectivity');
+ }
+
+ public function run(): SetupResult {
+ if ($this->config->getSystemValue('has_internet_connection', true) === false) {
+ return SetupResult::success($this->l10n->t('Internet connectivity is disabled in configuration file.'));
+ }
+
+ $siteArray = $this->config->getSystemValue('connectivity_check_domains', [
+ 'https://www.nextcloud.com', 'https://www.startpage.com', 'https://www.eff.org', 'https://www.edri.org'
+ ]);
+
+ foreach ($siteArray as $site) {
+ if ($this->isSiteReachable($site)) {
+ // successful as soon as one connection succeeds
+ return SetupResult::success();
+ }
+ }
+ return SetupResult::warning($this->l10n->t('This server has no working internet connection: Multiple endpoints could not be reached. This means that some of the features like mounting external storage, notifications about updates or installation of third-party apps will not work. Accessing files remotely and sending of notification emails might not work, either. Establish a connection from this server to the internet to enjoy all features.'));
+ }
+
+ /**
+ * Checks if the Nextcloud server can connect to a specific URL
+ * @param string $site site domain or full URL with http/https protocol
+ * @return bool success/failure
+ */
+ private function isSiteReachable(string $site): bool {
+ // if there is no protocol specified, test http:// first then, if necessary, https://
+ if (preg_match('/^https?:\/\//', $site) !== 1) {
+ $httpSite = 'http://' . $site . '/';
+ $httpsSite = 'https://' . $site . '/';
+ return $this->isSiteReachable($httpSite) || $this->isSiteReachable($httpsSite);
+ }
+ try {
+ $client = $this->clientService->newClient();
+ $client->get($site);
+ } catch (\Exception $e) {
+ $this->logger->error('Cannot connect to: ' . $site, [
+ 'app' => 'internet_connection_check',
+ 'exception' => $e,
+ ]);
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/apps/settings/lib/SetupChecks/JavaScriptModules.php b/apps/settings/lib/SetupChecks/JavaScriptModules.php
new file mode 100644
index 00000000000..72f58405811
--- /dev/null
+++ b/apps/settings/lib/SetupChecks/JavaScriptModules.php
@@ -0,0 +1,60 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\Settings\SetupChecks;
+
+use OCP\Http\Client\IClientService;
+use OCP\IConfig;
+use OCP\IL10N;
+use OCP\IURLGenerator;
+use OCP\SetupCheck\CheckServerResponseTrait;
+use OCP\SetupCheck\ISetupCheck;
+use OCP\SetupCheck\SetupResult;
+use Psr\Log\LoggerInterface;
+
+/**
+ * Checks if the webserver serves '.mjs' files using the correct MIME type
+ */
+class JavaScriptModules implements ISetupCheck {
+ use CheckServerResponseTrait;
+
+ public function __construct(
+ protected IL10N $l10n,
+ protected IConfig $config,
+ protected IURLGenerator $urlGenerator,
+ protected IClientService $clientService,
+ protected LoggerInterface $logger,
+ ) {
+ }
+
+ public function getCategory(): string {
+ return 'network';
+ }
+
+ public function getName(): string {
+ return $this->l10n->t('JavaScript modules support');
+ }
+
+ public function run(): SetupResult {
+ $testFile = $this->urlGenerator->linkTo('settings', 'js/esm-test.mjs');
+
+ $noResponse = true;
+ foreach ($this->runRequest('HEAD', $testFile) as $response) {
+ $noResponse = false;
+ if (preg_match('/(text|application)\/javascript/i', $response->getHeader('Content-Type'))) {
+ return SetupResult::success();
+ }
+ }
+
+ if ($noResponse) {
+ return SetupResult::warning($this->l10n->t('Unable to run check for JavaScript support. Please remedy or confirm manually if your webserver serves `.mjs` files using the JavaScript MIME type.') . "\n" . $this->serverConfigHelp());
+ }
+ return SetupResult::error($this->l10n->t('Your webserver does not serve `.mjs` files using the JavaScript MIME type. This will break some apps by preventing browsers from executing the JavaScript files. You should configure your webserver to serve `.mjs` files with either the `text/javascript` or `application/javascript` MIME type.'));
+
+ }
+}
diff --git a/apps/settings/lib/SetupChecks/JavaScriptSourceMaps.php b/apps/settings/lib/SetupChecks/JavaScriptSourceMaps.php
new file mode 100644
index 00000000000..dcfc40192b9
--- /dev/null
+++ b/apps/settings/lib/SetupChecks/JavaScriptSourceMaps.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\SetupChecks;
+
+use OCP\Http\Client\IClientService;
+use OCP\IConfig;
+use OCP\IL10N;
+use OCP\IURLGenerator;
+use OCP\SetupCheck\CheckServerResponseTrait;
+use OCP\SetupCheck\ISetupCheck;
+use OCP\SetupCheck\SetupResult;
+use Psr\Log\LoggerInterface;
+
+/**
+ * Checks if the webserver serves '.map' files using the correct MIME type
+ */
+class JavaScriptSourceMaps implements ISetupCheck {
+ use CheckServerResponseTrait;
+
+ public function __construct(
+ protected IL10N $l10n,
+ protected IConfig $config,
+ protected IURLGenerator $urlGenerator,
+ protected IClientService $clientService,
+ protected LoggerInterface $logger,
+ ) {
+ }
+
+ public function getCategory(): string {
+ return 'network';
+ }
+
+ public function getName(): string {
+ return $this->l10n->t('JavaScript source map support');
+ }
+
+ public function run(): SetupResult {
+ $testFile = $this->urlGenerator->linkTo('settings', 'js/map-test.js.map');
+
+ foreach ($this->runRequest('HEAD', $testFile) as $response) {
+ return SetupResult::success();
+ }
+
+ return SetupResult::warning($this->l10n->t('Your webserver is not set up to serve `.js.map` files. Without these files, JavaScript Source Maps won\'t function properly, making it more challenging to troubleshoot and debug any issues that may arise.'));
+ }
+}
diff --git a/apps/settings/lib/SetupChecks/LdapInvalidUuids.php b/apps/settings/lib/SetupChecks/LdapInvalidUuids.php
deleted file mode 100644
index 11b0105cada..00000000000
--- a/apps/settings/lib/SetupChecks/LdapInvalidUuids.php
+++ /dev/null
@@ -1,69 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-/**
- * @copyright Copyright (c) 2022 Arthur Schiwon <blizzz@arthur-schiwon.de>
- *
- * @author Arthur Schiwon <blizzz@arthur-schiwon.de>
- *
- * @license GNU AGPL version 3 or any later version
- *
- * 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 <https://www.gnu.org/licenses/>.
- *
- */
-
-namespace OCA\Settings\SetupChecks;
-
-use OCA\User_LDAP\Mapping\GroupMapping;
-use OCA\User_LDAP\Mapping\UserMapping;
-use OCP\App\IAppManager;
-use OCP\IL10N;
-use OCP\IServerContainer;
-
-class LdapInvalidUuids {
-
- /** @var IAppManager */
- private $appManager;
- /** @var IL10N */
- private $l10n;
- /** @var IServerContainer */
- private $server;
-
- public function __construct(IAppManager $appManager, IL10N $l10n, IServerContainer $server) {
- $this->appManager = $appManager;
- $this->l10n = $l10n;
- $this->server = $server;
- }
-
- public function description(): string {
- return $this->l10n->t('Invalid UUIDs of LDAP users or groups have been found. Please review your "Override UUID detection" settings in the Expert part of the LDAP configuration and use "occ ldap:update-uuid" to update them.');
- }
-
- public function severity(): string {
- return 'warning';
- }
-
- public function run(): bool {
- if (!$this->appManager->isEnabledForUser('user_ldap')) {
- return true;
- }
- /** @var UserMapping $userMapping */
- $userMapping = $this->server->get(UserMapping::class);
- /** @var GroupMapping $groupMapping */
- $groupMapping = $this->server->get(GroupMapping::class);
- return count($userMapping->getList(0, 1, true)) === 0
- && count($groupMapping->getList(0, 1, true)) === 0;
- }
-}
diff --git a/apps/settings/lib/SetupChecks/LegacySSEKeyFormat.php b/apps/settings/lib/SetupChecks/LegacySSEKeyFormat.php
index 4814d3fba7c..47594e201cb 100644
--- a/apps/settings/lib/SetupChecks/LegacySSEKeyFormat.php
+++ b/apps/settings/lib/SetupChecks/LegacySSEKeyFormat.php
@@ -3,59 +3,37 @@
declare(strict_types=1);
/**
- * @copyright Copyright (c) 2020 Morris Jobke <hey@morrisjobke.de>
- *
- * @author Morris Jobke <hey@morrisjobke.de>
- *
- * @license GNU AGPL version 3 or any later version
- *
- * 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/>.
- *
+ * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\Settings\SetupChecks;
use OCP\IConfig;
use OCP\IL10N;
use OCP\IURLGenerator;
-
-class LegacySSEKeyFormat {
- /** @var IL10N */
- private $l10n;
- /** @var IConfig */
- private $config;
- /** @var IURLGenerator */
- private $urlGenerator;
-
- public function __construct(IL10N $l10n, IConfig $config, IURLGenerator $urlGenerator) {
- $this->l10n = $l10n;
- $this->config = $config;
- $this->urlGenerator = $urlGenerator;
- }
-
- public function description(): string {
- return $this->l10n->t('The old server-side-encryption format is enabled. We recommend disabling this.');
+use OCP\SetupCheck\ISetupCheck;
+use OCP\SetupCheck\SetupResult;
+
+class LegacySSEKeyFormat implements ISetupCheck {
+ public function __construct(
+ private IL10N $l10n,
+ private IConfig $config,
+ private IURLGenerator $urlGenerator,
+ ) {
}
- public function severity(): string {
- return 'warning';
+ public function getCategory(): string {
+ return 'security';
}
- public function run(): bool {
- return $this->config->getSystemValueBool('encryption.legacy_format_support', false) === false;
+ public function getName(): string {
+ return $this->l10n->t('Old server-side-encryption');
}
- public function linkToDocumentation(): string {
- return $this->urlGenerator->linkToDocs('admin-sse-legacy-format');
+ public function run(): SetupResult {
+ if ($this->config->getSystemValueBool('encryption.legacy_format_support', false) === false) {
+ return SetupResult::success($this->l10n->t('Disabled'));
+ }
+ return SetupResult::warning($this->l10n->t('The old server-side-encryption format is enabled. We recommend disabling this.'), $this->urlGenerator->linkToDocs('admin-sse-legacy-format'));
}
}
diff --git a/apps/settings/lib/SetupChecks/LoggingLevel.php b/apps/settings/lib/SetupChecks/LoggingLevel.php
new file mode 100644
index 00000000000..b9e1dbe700d
--- /dev/null
+++ b/apps/settings/lib/SetupChecks/LoggingLevel.php
@@ -0,0 +1,55 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\Settings\SetupChecks;
+
+use OCP\IConfig;
+use OCP\IL10N;
+use OCP\ILogger;
+use OCP\IURLGenerator;
+use OCP\SetupCheck\ISetupCheck;
+use OCP\SetupCheck\SetupResult;
+
+class LoggingLevel implements ISetupCheck {
+ public function __construct(
+ private IL10N $l10n,
+ private IConfig $config,
+ private IURLGenerator $urlGenerator,
+ ) {
+ }
+
+ public function getName(): string {
+ return $this->l10n->t('Logging level');
+ }
+
+ public function getCategory(): string {
+ return 'system';
+ }
+
+ public function run(): SetupResult {
+ $configLogLevel = $this->config->getSystemValue('loglevel', ILogger::WARN);
+ if (!is_int($configLogLevel)
+ || $configLogLevel < ILogger::DEBUG
+ || $configLogLevel > ILogger::FATAL
+ ) {
+ return SetupResult::error(
+ $this->l10n->t('The %1$s configuration option must be a valid integer value.', ['`loglevel`']),
+ $this->urlGenerator->linkToDocs('admin-logging'),
+ );
+ }
+
+ if ($configLogLevel === ILogger::DEBUG) {
+ return SetupResult::warning(
+ $this->l10n->t('The logging level is set to debug level. Use debug level only when you have a problem to diagnose, and then reset your log level to a less-verbose level as it outputs a lot of information, and can affect your server performance.'),
+ $this->urlGenerator->linkToDocs('admin-logging'),
+ );
+ }
+
+ return SetupResult::success($this->l10n->t('Logging level configured correctly.'));
+ }
+}
diff --git a/apps/settings/lib/SetupChecks/MaintenanceWindowStart.php b/apps/settings/lib/SetupChecks/MaintenanceWindowStart.php
new file mode 100644
index 00000000000..ca8df039b1e
--- /dev/null
+++ b/apps/settings/lib/SetupChecks/MaintenanceWindowStart.php
@@ -0,0 +1,53 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCA\Settings\SetupChecks;
+
+use OCP\IConfig;
+use OCP\IL10N;
+use OCP\IURLGenerator;
+use OCP\SetupCheck\ISetupCheck;
+use OCP\SetupCheck\SetupResult;
+
+class MaintenanceWindowStart implements ISetupCheck {
+ public function __construct(
+ private IL10N $l10n,
+ private IURLGenerator $urlGenerator,
+ private IConfig $config,
+ ) {
+ }
+
+ public function getCategory(): string {
+ return 'system';
+ }
+
+ public function getName(): string {
+ return $this->l10n->t('Maintenance window start');
+ }
+
+ public function run(): SetupResult {
+ $configValue = $this->config->getSystemValue('maintenance_window_start', null);
+ if ($configValue === null) {
+ return SetupResult::warning(
+ $this->l10n->t('Server has no maintenance window start time configured. This means resource intensive daily background jobs will also be executed during your main usage time. We recommend to set it to a time of low usage, so users are less impacted by the load caused from these heavy tasks.'),
+ $this->urlGenerator->linkToDocs('admin-background-jobs')
+ );
+ }
+
+ $startValue = (int)$configValue;
+ $endValue = ($startValue + 6) % 24;
+ return SetupResult::success(
+ str_replace(
+ ['{start}', '{end}'],
+ [$startValue, $endValue],
+ $this->l10n->t('Maintenance window to execute heavy background jobs is between {start}:00 UTC and {end}:00 UTC')
+ )
+ );
+ }
+}
diff --git a/apps/settings/lib/SetupChecks/MemcacheConfigured.php b/apps/settings/lib/SetupChecks/MemcacheConfigured.php
new file mode 100644
index 00000000000..e3601d428bb
--- /dev/null
+++ b/apps/settings/lib/SetupChecks/MemcacheConfigured.php
@@ -0,0 +1,98 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\Settings\SetupChecks;
+
+use OC\Memcache\Memcached;
+use OCP\ICacheFactory;
+use OCP\IConfig;
+use OCP\IL10N;
+use OCP\IURLGenerator;
+use OCP\SetupCheck\ISetupCheck;
+use OCP\SetupCheck\SetupResult;
+
+class MemcacheConfigured implements ISetupCheck {
+ public function __construct(
+ private IL10N $l10n,
+ private IConfig $config,
+ private IURLGenerator $urlGenerator,
+ private ICacheFactory $cacheFactory,
+ ) {
+ }
+
+ public function getName(): string {
+ return $this->l10n->t('Memcache');
+ }
+
+ public function getCategory(): string {
+ return 'system';
+ }
+
+ public function run(): SetupResult {
+ $memcacheDistributedClass = $this->config->getSystemValue('memcache.distributed', null);
+ $memcacheLockingClass = $this->config->getSystemValue('memcache.locking', null);
+ $memcacheLocalClass = $this->config->getSystemValue('memcache.local', null);
+ $caches = array_filter([$memcacheDistributedClass,$memcacheLockingClass,$memcacheLocalClass]);
+ if (in_array(Memcached::class, array_map(fn (string $class) => ltrim($class, '\\'), $caches))) {
+ // wrong PHP module is installed
+ if (extension_loaded('memcache') && !extension_loaded('memcached')) {
+ return SetupResult::warning(
+ $this->l10n->t('Memcached is configured as distributed cache, but the wrong PHP module ("memcache") is installed. Please install the PHP module "memcached".')
+ );
+ }
+ // required PHP module is missing
+ if (!extension_loaded('memcached')) {
+ return SetupResult::warning(
+ $this->l10n->t('Memcached is configured as distributed cache, but the PHP module "memcached" is not installed. Please install the PHP module "memcached".')
+ );
+ }
+ }
+ if ($memcacheLocalClass === null) {
+ return SetupResult::info(
+ $this->l10n->t('No memory cache has been configured. To enhance performance, please configure a memcache, if available.'),
+ $this->urlGenerator->linkToDocs('admin-performance')
+ );
+ }
+
+ if ($this->cacheFactory->isLocalCacheAvailable()) {
+ $random = bin2hex(random_bytes(64));
+ $local = $this->cacheFactory->createLocal('setupcheck.local');
+ try {
+ $local->set('test', $random);
+ $local2 = $this->cacheFactory->createLocal('setupcheck.local');
+ $actual = $local2->get('test');
+ $local->remove('test');
+ } catch (\Throwable) {
+ $actual = null;
+ }
+
+ if ($actual !== $random) {
+ return SetupResult::error($this->l10n->t('Failed to write and read a value from local cache.'));
+ }
+ }
+
+ if ($this->cacheFactory->isAvailable()) {
+ $random = bin2hex(random_bytes(64));
+ $distributed = $this->cacheFactory->createDistributed('setupcheck');
+ try {
+ $distributed->set('test', $random);
+ $distributed2 = $this->cacheFactory->createDistributed('setupcheck');
+ $actual = $distributed2->get('test');
+ $distributed->remove('test');
+ } catch (\Throwable) {
+ $actual = null;
+ }
+
+ if ($actual !== $random) {
+ return SetupResult::error($this->l10n->t('Failed to write and read a value from distributed cache.'));
+ }
+ }
+
+ return SetupResult::success($this->l10n->t('Configured'));
+ }
+}
diff --git a/apps/settings/lib/SetupChecks/MimeTypeMigrationAvailable.php b/apps/settings/lib/SetupChecks/MimeTypeMigrationAvailable.php
new file mode 100644
index 00000000000..cf237f68670
--- /dev/null
+++ b/apps/settings/lib/SetupChecks/MimeTypeMigrationAvailable.php
@@ -0,0 +1,41 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\Settings\SetupChecks;
+
+use OC\Repair\RepairMimeTypes;
+use OCP\IL10N;
+use OCP\SetupCheck\ISetupCheck;
+use OCP\SetupCheck\SetupResult;
+
+class MimeTypeMigrationAvailable implements ISetupCheck {
+
+ public function __construct(
+ private RepairMimeTypes $repairMimeTypes,
+ private IL10N $l10n,
+ ) {
+ }
+
+ public function getCategory(): string {
+ return 'system';
+ }
+
+ public function getName(): string {
+ return $this->l10n->t('Mimetype migrations available');
+ }
+
+ public function run(): SetupResult {
+ if ($this->repairMimeTypes->migrationsAvailable()) {
+ return SetupResult::warning(
+ $this->l10n->t('One or more mimetype migrations are available. Occasionally new mimetypes are added to better handle certain file types. Migrating the mimetypes take a long time on larger instances so this is not done automatically during upgrades. Use the command `occ maintenance:repair --include-expensive` to perform the migrations.'),
+ );
+ } else {
+ return SetupResult::success('None');
+ }
+ }
+}
diff --git a/apps/settings/lib/SetupChecks/MysqlRowFormat.php b/apps/settings/lib/SetupChecks/MysqlRowFormat.php
new file mode 100644
index 00000000000..3c27b73db89
--- /dev/null
+++ b/apps/settings/lib/SetupChecks/MysqlRowFormat.php
@@ -0,0 +1,70 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\Settings\SetupChecks;
+
+use Doctrine\DBAL\Platforms\MySQLPlatform;
+use OC\DB\Connection;
+use OCP\IConfig;
+use OCP\IL10N;
+use OCP\IURLGenerator;
+use OCP\SetupCheck\ISetupCheck;
+use OCP\SetupCheck\SetupResult;
+
+class MysqlRowFormat implements ISetupCheck {
+ public function __construct(
+ private IL10N $l10n,
+ private IConfig $config,
+ private Connection $connection,
+ private IURLGenerator $urlGenerator,
+ ) {
+ }
+
+ public function getName(): string {
+ return $this->l10n->t('MySQL row format');
+ }
+
+ public function getCategory(): string {
+ return 'database';
+ }
+
+ public function run(): SetupResult {
+ if (!$this->connection->getDatabasePlatform() instanceof MySQLPlatform) {
+ return SetupResult::success($this->l10n->t('You are not using MySQL'));
+ }
+
+ $wrongRowFormatTables = $this->getRowNotDynamicTables();
+ if (empty($wrongRowFormatTables)) {
+ return SetupResult::success($this->l10n->t('None of your tables use ROW_FORMAT=Compressed'));
+ }
+
+ return SetupResult::warning(
+ $this->l10n->t(
+ 'Incorrect row format found in your database. ROW_FORMAT=Dynamic offers the best database performances for Nextcloud. Please update row format on the following list: %s.',
+ [implode(', ', $wrongRowFormatTables)],
+ ),
+ 'https://dev.mysql.com/doc/refman/en/innodb-row-format.html',
+ );
+ }
+
+ /**
+ * @return string[]
+ */
+ private function getRowNotDynamicTables(): array {
+ $sql = "SELECT table_name
+ FROM information_schema.tables
+ WHERE table_schema = ?
+ AND table_name LIKE '*PREFIX*%'
+ AND row_format != 'Dynamic';";
+
+ return $this->connection->executeQuery(
+ $sql,
+ [$this->config->getSystemValueString('dbname')],
+ )->fetchFirstColumn();
+ }
+}
diff --git a/apps/settings/lib/SetupChecks/MysqlUnicodeSupport.php b/apps/settings/lib/SetupChecks/MysqlUnicodeSupport.php
new file mode 100644
index 00000000000..ba2fa93e094
--- /dev/null
+++ b/apps/settings/lib/SetupChecks/MysqlUnicodeSupport.php
@@ -0,0 +1,46 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\Settings\SetupChecks;
+
+use OCP\IConfig;
+use OCP\IL10N;
+use OCP\IURLGenerator;
+use OCP\SetupCheck\ISetupCheck;
+use OCP\SetupCheck\SetupResult;
+
+class MysqlUnicodeSupport implements ISetupCheck {
+ public function __construct(
+ private IL10N $l10n,
+ private IConfig $config,
+ private IURLGenerator $urlGenerator,
+ ) {
+ }
+
+ public function getName(): string {
+ return $this->l10n->t('MySQL Unicode support');
+ }
+
+ public function getCategory(): string {
+ return 'database';
+ }
+
+ public function run(): SetupResult {
+ if ($this->config->getSystemValueString('dbtype') !== 'mysql') {
+ return SetupResult::success($this->l10n->t('You are not using MySQL'));
+ }
+ if ($this->config->getSystemValueBool('mysql.utf8mb4', false)) {
+ return SetupResult::success($this->l10n->t('MySQL is used as database and does support 4-byte characters'));
+ } else {
+ return SetupResult::warning(
+ $this->l10n->t('MySQL is used as database but does not support 4-byte characters. To be able to handle 4-byte characters (like emojis) without issues in filenames or comments for example it is recommended to enable the 4-byte support in MySQL.'),
+ $this->urlGenerator->linkToDocs('admin-mysql-utf8mb4'),
+ );
+ }
+ }
+}
diff --git a/apps/settings/lib/SetupChecks/OcxProviders.php b/apps/settings/lib/SetupChecks/OcxProviders.php
new file mode 100644
index 00000000000..c53e8087bd9
--- /dev/null
+++ b/apps/settings/lib/SetupChecks/OcxProviders.php
@@ -0,0 +1,83 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\Settings\SetupChecks;
+
+use OCP\Http\Client\IClientService;
+use OCP\IConfig;
+use OCP\IL10N;
+use OCP\IURLGenerator;
+use OCP\SetupCheck\CheckServerResponseTrait;
+use OCP\SetupCheck\ISetupCheck;
+use OCP\SetupCheck\SetupResult;
+use Psr\Log\LoggerInterface;
+
+/**
+ * Checks if the webserver serves the OCM and OCS providers
+ */
+class OcxProviders implements ISetupCheck {
+ use CheckServerResponseTrait;
+
+ public function __construct(
+ protected IL10N $l10n,
+ protected IConfig $config,
+ protected IURLGenerator $urlGenerator,
+ protected IClientService $clientService,
+ protected LoggerInterface $logger,
+ ) {
+ }
+
+ public function getCategory(): string {
+ return 'network';
+ }
+
+ public function getName(): string {
+ return $this->l10n->t('OCS provider resolving');
+ }
+
+ public function run(): SetupResult {
+ // List of providers that work
+ $workingProviders = [];
+ // List of providers we tested (in case one or multiple do not yield any response)
+ $testedProviders = [];
+ // All providers that we need to test
+ $providers = [
+ '/ocm-provider/',
+ '/ocs-provider/',
+ ];
+
+ foreach ($providers as $provider) {
+ foreach ($this->runRequest('HEAD', $provider, ['httpErrors' => false]) as $response) {
+ $testedProviders[$provider] = true;
+ if ($response->getStatusCode() === 200) {
+ $workingProviders[] = $provider;
+ break;
+ }
+ }
+ }
+
+ if (count($testedProviders) < count($providers)) {
+ return SetupResult::warning(
+ $this->l10n->t('Could not check if your web server properly resolves the OCM and OCS provider URLs.', ) . "\n" . $this->serverConfigHelp(),
+ );
+ }
+
+ $missingProviders = array_diff($providers, $workingProviders);
+ if (empty($missingProviders)) {
+ return SetupResult::success();
+ }
+
+ return SetupResult::warning(
+ $this->l10n->t('Your web server is not properly set up to resolve %1$s.
+This is most likely related to a web server configuration that was not updated to deliver this folder directly.
+Please compare your configuration against the shipped rewrite rules in ".htaccess" for Apache or the provided one in the documentation for Nginx.
+On Nginx those are typically the lines starting with "location ~" that need an update.', [join(', ', array_map(fn ($s) => '"' . $s . '"', $missingProviders))]),
+ $this->urlGenerator->linkToDocs('admin-nginx'),
+ );
+ }
+}
diff --git a/apps/settings/lib/SetupChecks/OverwriteCliUrl.php b/apps/settings/lib/SetupChecks/OverwriteCliUrl.php
new file mode 100644
index 00000000000..6fe0a1260cc
--- /dev/null
+++ b/apps/settings/lib/SetupChecks/OverwriteCliUrl.php
@@ -0,0 +1,64 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCA\Settings\SetupChecks;
+
+use OCP\IConfig;
+use OCP\IL10N;
+use OCP\IRequest;
+use OCP\SetupCheck\ISetupCheck;
+use OCP\SetupCheck\SetupResult;
+
+class OverwriteCliUrl implements ISetupCheck {
+ public function __construct(
+ private IL10N $l10n,
+ private IConfig $config,
+ private IRequest $request,
+ ) {
+ }
+
+ public function getCategory(): string {
+ return 'config';
+ }
+
+ public function getName(): string {
+ return $this->l10n->t('Overwrite CLI URL');
+ }
+
+ public function run(): SetupResult {
+ $currentOverwriteCliUrl = $this->config->getSystemValue('overwrite.cli.url', '');
+ $suggestedOverwriteCliUrl = $this->request->getServerProtocol() . '://' . $this->request->getInsecureServerHost() . \OC::$WEBROOT;
+
+ // Check correctness by checking if it is a valid URL
+ if (filter_var($currentOverwriteCliUrl, FILTER_VALIDATE_URL)) {
+ if ($currentOverwriteCliUrl == $suggestedOverwriteCliUrl) {
+ return SetupResult::success(
+ $this->l10n->t(
+ 'The "overwrite.cli.url" option in your config.php is correctly set to "%s".',
+ [$currentOverwriteCliUrl]
+ )
+ );
+ } else {
+ return SetupResult::success(
+ $this->l10n->t(
+ 'The "overwrite.cli.url" option in your config.php is set to "%s" which is a correct URL. Suggested URL is "%s".',
+ [$currentOverwriteCliUrl, $suggestedOverwriteCliUrl]
+ )
+ );
+ }
+ } else {
+ return SetupResult::warning(
+ $this->l10n->t(
+ 'Please make sure to set the "overwrite.cli.url" option in your config.php file to the URL that your users mainly use to access this Nextcloud. Suggestion: "%s". Otherwise there might be problems with the URL generation via cron. (It is possible though that the suggested URL is not the URL that your users mainly use to access this Nextcloud. Best is to double check this in any case.)',
+ [$suggestedOverwriteCliUrl]
+ )
+ );
+ }
+ }
+}
diff --git a/apps/settings/lib/SetupChecks/PhpApcuConfig.php b/apps/settings/lib/SetupChecks/PhpApcuConfig.php
new file mode 100644
index 00000000000..c91a8cefec1
--- /dev/null
+++ b/apps/settings/lib/SetupChecks/PhpApcuConfig.php
@@ -0,0 +1,70 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCA\Settings\SetupChecks;
+
+use OC\Memcache\APCu;
+use OCP\IConfig;
+use OCP\IL10N;
+use OCP\SetupCheck\ISetupCheck;
+use OCP\SetupCheck\SetupResult;
+
+class PhpApcuConfig implements ISetupCheck {
+ public const USAGE_RATE_WARNING = 90;
+ public const AGE_WARNING = 3600 * 8;
+
+ public function __construct(
+ private IL10N $l10n,
+ private IConfig $config,
+ ) {
+ }
+
+ public function getCategory(): string {
+ return 'php';
+ }
+
+ public function getName(): string {
+ return $this->l10n->t('PHP APCu configuration');
+ }
+
+ public function run(): SetupResult {
+ $localIsApcu = ltrim($this->config->getSystemValueString('memcache.local'), '\\') === APCu::class;
+ $distributedIsApcu = ltrim($this->config->getSystemValueString('memcache.distributed'), '\\') === APCu::class;
+ if (!$localIsApcu && !$distributedIsApcu) {
+ return SetupResult::success();
+ }
+
+ if (!APCu::isAvailable()) {
+ return SetupResult::success();
+ }
+
+ $cache = apcu_cache_info(true);
+ $mem = apcu_sma_info(true);
+ if ($cache === false || $mem === false) {
+ return SetupResult::success();
+ }
+
+ $expunges = $cache['expunges'];
+ $memSize = $mem['num_seg'] * $mem['seg_size'];
+ $memAvailable = $mem['avail_mem'];
+ $memUsed = $memSize - $memAvailable;
+ $usageRate = round($memUsed / $memSize * 100, 0);
+ $elapsed = max(time() - $cache['start_time'], 1);
+
+ if ($expunges > 0 && $elapsed < self::AGE_WARNING) {
+ return SetupResult::warning($this->l10n->t('Your APCu cache has been running full, consider increasing the apc.shm_size php setting.'));
+ }
+
+ if ($usageRate > self::USAGE_RATE_WARNING) {
+ return SetupResult::warning($this->l10n->t('Your APCu cache is almost full at %s%%, consider increasing the apc.shm_size php setting.', [$usageRate]));
+ }
+
+ return SetupResult::success();
+ }
+}
diff --git a/apps/settings/lib/SetupChecks/PhpDefaultCharset.php b/apps/settings/lib/SetupChecks/PhpDefaultCharset.php
index 0ad5e2f56ea..580db8cbd02 100644
--- a/apps/settings/lib/SetupChecks/PhpDefaultCharset.php
+++ b/apps/settings/lib/SetupChecks/PhpDefaultCharset.php
@@ -3,38 +3,34 @@
declare(strict_types=1);
/**
- * @copyright Copyright (c) 2020 Daniel Kesselberg <mail@danielkesselberg.de>
- *
- * @author Daniel Kesselberg <mail@danielkesselberg.de>
- *
- * @license GNU AGPL version 3 or any later version
- *
- * 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/>.
- *
+ * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\Settings\SetupChecks;
-class PhpDefaultCharset {
- public function description(): string {
- return 'PHP configuration option default_charset should be UTF-8';
+use OCP\IL10N;
+use OCP\SetupCheck\ISetupCheck;
+use OCP\SetupCheck\SetupResult;
+
+class PhpDefaultCharset implements ISetupCheck {
+ public function __construct(
+ private IL10N $l10n,
+ ) {
+ }
+
+ public function getName(): string {
+ return $this->l10n->t('PHP default charset');
}
- public function severity(): string {
- return 'warning';
+ public function getCategory(): string {
+ return 'php';
}
- public function run(): bool {
- return strtoupper(trim(ini_get('default_charset'))) === 'UTF-8';
+ public function run(): SetupResult {
+ if (strtoupper(trim(ini_get('default_charset'))) === 'UTF-8') {
+ return SetupResult::success('UTF-8');
+ } else {
+ return SetupResult::warning($this->l10n->t('PHP configuration option "default_charset" should be UTF-8'));
+ }
}
}
diff --git a/apps/settings/lib/SetupChecks/PhpDisabledFunctions.php b/apps/settings/lib/SetupChecks/PhpDisabledFunctions.php
new file mode 100644
index 00000000000..b82b5921989
--- /dev/null
+++ b/apps/settings/lib/SetupChecks/PhpDisabledFunctions.php
@@ -0,0 +1,39 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\Settings\SetupChecks;
+
+use OCP\IL10N;
+use OCP\SetupCheck\ISetupCheck;
+use OCP\SetupCheck\SetupResult;
+
+class PhpDisabledFunctions implements ISetupCheck {
+
+ public function __construct(
+ private IL10N $l10n,
+ ) {
+ }
+
+ public function getName(): string {
+ return $this->l10n->t('PHP set_time_limit');
+ }
+
+ public function getCategory(): string {
+ return 'php';
+ }
+
+ public function run(): SetupResult {
+ if (function_exists('set_time_limit') && !str_contains(ini_get('disable_functions'), 'set_time_limit')) {
+ return SetupResult::success($this->l10n->t('The function is available.'));
+ } else {
+ return SetupResult::warning(
+ $this->l10n->t('The PHP function "set_time_limit" is not available. This could result in scripts being halted mid-execution, breaking your installation. Enabling this function is strongly recommended.'),
+ );
+ }
+ }
+}
diff --git a/apps/settings/lib/SetupChecks/PhpFreetypeSupport.php b/apps/settings/lib/SetupChecks/PhpFreetypeSupport.php
new file mode 100644
index 00000000000..ec5d6c7e146
--- /dev/null
+++ b/apps/settings/lib/SetupChecks/PhpFreetypeSupport.php
@@ -0,0 +1,46 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCA\Settings\SetupChecks;
+
+use OCP\IL10N;
+use OCP\SetupCheck\ISetupCheck;
+use OCP\SetupCheck\SetupResult;
+
+class PhpFreetypeSupport implements ISetupCheck {
+ public function __construct(
+ private IL10N $l10n,
+ ) {
+ }
+
+ public function getName(): string {
+ return $this->l10n->t('Freetype');
+ }
+
+ public function getCategory(): string {
+ return 'php';
+ }
+
+ /**
+ * Check if the required FreeType functions are present
+ */
+ protected function hasFreeTypeSupport(): bool {
+ return function_exists('imagettfbbox') && function_exists('imagettftext');
+ }
+
+ public function run(): SetupResult {
+ if ($this->hasFreeTypeSupport()) {
+ return SetupResult::success($this->l10n->t('Supported'));
+ } else {
+ return SetupResult::info(
+ $this->l10n->t('Your PHP does not have FreeType support, resulting in breakage of profile pictures and the settings interface.'),
+ );
+ }
+ }
+}
diff --git a/apps/settings/lib/SetupChecks/PhpGetEnv.php b/apps/settings/lib/SetupChecks/PhpGetEnv.php
new file mode 100644
index 00000000000..279a23a9691
--- /dev/null
+++ b/apps/settings/lib/SetupChecks/PhpGetEnv.php
@@ -0,0 +1,38 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\Settings\SetupChecks;
+
+use OCP\IL10N;
+use OCP\IURLGenerator;
+use OCP\SetupCheck\ISetupCheck;
+use OCP\SetupCheck\SetupResult;
+
+class PhpGetEnv implements ISetupCheck {
+ public function __construct(
+ private IL10N $l10n,
+ private IURLGenerator $urlGenerator,
+ ) {
+ }
+
+ public function getName(): string {
+ return $this->l10n->t('PHP getenv');
+ }
+
+ public function getCategory(): string {
+ return 'php';
+ }
+
+ public function run(): SetupResult {
+ if (!empty(getenv('PATH'))) {
+ return SetupResult::success();
+ } else {
+ return SetupResult::warning($this->l10n->t('PHP does not seem to be setup properly to query system environment variables. The test with getenv("PATH") only returns an empty response.'), $this->urlGenerator->linkToDocs('admin-php-fpm'));
+ }
+ }
+}
diff --git a/apps/settings/lib/SetupChecks/PhpMaxFileSize.php b/apps/settings/lib/SetupChecks/PhpMaxFileSize.php
new file mode 100644
index 00000000000..d81cbe6d45c
--- /dev/null
+++ b/apps/settings/lib/SetupChecks/PhpMaxFileSize.php
@@ -0,0 +1,80 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCA\Settings\SetupChecks;
+
+use bantu\IniGetWrapper\IniGetWrapper;
+use OCP\IL10N;
+use OCP\IURLGenerator;
+use OCP\SetupCheck\ISetupCheck;
+use OCP\SetupCheck\SetupResult;
+use OCP\Util;
+
+class PhpMaxFileSize implements ISetupCheck {
+ public function __construct(
+ private IL10N $l10n,
+ private IURLGenerator $urlGenerator,
+ private IniGetWrapper $iniGetWrapper,
+ ) {
+ }
+
+ public function getCategory(): string {
+ return 'php';
+ }
+
+ public function getName(): string {
+ return $this->l10n->t('PHP file size upload limit');
+ }
+
+ public function run(): SetupResult {
+ $upload_max_filesize = (string)$this->iniGetWrapper->getString('upload_max_filesize');
+ $post_max_size = (string)$this->iniGetWrapper->getString('post_max_size');
+ $max_input_time = (int)$this->iniGetWrapper->getString('max_input_time');
+ $max_execution_time = (int)$this->iniGetWrapper->getString('max_execution_time');
+
+ $warnings = [];
+ $recommendedSize = 16 * 1024 * 1024 * 1024;
+ $recommendedTime = 3600;
+
+ // Check if the PHP upload limit is too low
+ if (Util::computerFileSize($upload_max_filesize) < $recommendedSize) {
+ $warnings[] = $this->l10n->t('The PHP upload_max_filesize is too low. A size of at least %1$s is recommended. Current value: %2$s.', [
+ Util::humanFileSize($recommendedSize),
+ $upload_max_filesize,
+ ]);
+ }
+ if (Util::computerFileSize($post_max_size) < $recommendedSize) {
+ $warnings[] = $this->l10n->t('The PHP post_max_size is too low. A size of at least %1$s is recommended. Current value: %2$s.', [
+ Util::humanFileSize($recommendedSize),
+ $post_max_size,
+ ]);
+ }
+
+ // Check if the PHP execution time is too low
+ if ($max_input_time < $recommendedTime && $max_input_time !== -1) {
+ $warnings[] = $this->l10n->t('The PHP max_input_time is too low. A time of at least %1$s is recommended. Current value: %2$s.', [
+ $recommendedTime,
+ $max_input_time,
+ ]);
+ }
+
+ if ($max_execution_time < $recommendedTime && $max_execution_time !== -1) {
+ $warnings[] = $this->l10n->t('The PHP max_execution_time is too low. A time of at least %1$s is recommended. Current value: %2$s.', [
+ $recommendedTime,
+ $max_execution_time,
+ ]);
+ }
+
+ if (!empty($warnings)) {
+ return SetupResult::warning(join(' ', $warnings), $this->urlGenerator->linkToDocs('admin-big-file-upload'));
+ }
+
+ return SetupResult::success();
+ }
+}
diff --git a/apps/settings/lib/SetupChecks/PhpMemoryLimit.php b/apps/settings/lib/SetupChecks/PhpMemoryLimit.php
new file mode 100644
index 00000000000..7b693169f10
--- /dev/null
+++ b/apps/settings/lib/SetupChecks/PhpMemoryLimit.php
@@ -0,0 +1,40 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCA\Settings\SetupChecks;
+
+use OC\MemoryInfo;
+use OCP\IL10N;
+use OCP\SetupCheck\ISetupCheck;
+use OCP\SetupCheck\SetupResult;
+use OCP\Util;
+
+class PhpMemoryLimit implements ISetupCheck {
+ public function __construct(
+ private IL10N $l10n,
+ private MemoryInfo $memoryInfo,
+ ) {
+ }
+
+ public function getCategory(): string {
+ return 'php';
+ }
+
+ public function getName(): string {
+ return $this->l10n->t('PHP memory limit');
+ }
+
+ public function run(): SetupResult {
+ if ($this->memoryInfo->isMemoryLimitSufficient()) {
+ return SetupResult::success(Util::humanFileSize($this->memoryInfo->getMemoryLimit()));
+ } else {
+ return SetupResult::error($this->l10n->t('The PHP memory limit is below the recommended value of %s. Some features or apps - including the Updater - may not function properly.', Util::humanFileSize(MemoryInfo::RECOMMENDED_MEMORY_LIMIT)));
+ }
+ }
+}
diff --git a/apps/settings/lib/SetupChecks/PhpModules.php b/apps/settings/lib/SetupChecks/PhpModules.php
new file mode 100644
index 00000000000..b0b4f106f4a
--- /dev/null
+++ b/apps/settings/lib/SetupChecks/PhpModules.php
@@ -0,0 +1,103 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\Settings\SetupChecks;
+
+use OCP\IL10N;
+use OCP\IURLGenerator;
+use OCP\SetupCheck\ISetupCheck;
+use OCP\SetupCheck\SetupResult;
+
+class PhpModules implements ISetupCheck {
+ protected const REQUIRED_MODULES = [
+ 'ctype',
+ 'curl',
+ 'dom',
+ 'fileinfo',
+ 'gd',
+ 'json',
+ 'mbstring',
+ 'openssl',
+ 'posix',
+ 'session',
+ 'xml',
+ 'xmlreader',
+ 'xmlwriter',
+ 'zip',
+ 'zlib',
+ ];
+ protected const RECOMMENDED_MODULES = [
+ 'exif',
+ 'gmp',
+ 'intl',
+ 'sodium',
+ 'sysvsem',
+ ];
+
+ public function __construct(
+ private IL10N $l10n,
+ private IURLGenerator $urlGenerator,
+ ) {
+ }
+
+ public function getName(): string {
+ return $this->l10n->t('PHP modules');
+ }
+
+ public function getCategory(): string {
+ return 'php';
+ }
+
+ protected function getRecommendedModuleDescription(string $module): string {
+ return match($module) {
+ 'intl' => $this->l10n->t('increases language translation performance and fixes sorting of non-ASCII characters'),
+ 'sodium' => $this->l10n->t('for Argon2 for password hashing'),
+ 'gmp' => $this->l10n->t('required for SFTP storage and recommended for WebAuthn performance'),
+ 'exif' => $this->l10n->t('for picture rotation in server and metadata extraction in the Photos app'),
+ default => '',
+ };
+ }
+
+ public function run(): SetupResult {
+ $missingRecommendedModules = $this->getMissingModules(self::RECOMMENDED_MODULES);
+ $missingRequiredModules = $this->getMissingModules(self::REQUIRED_MODULES);
+ if (!empty($missingRequiredModules)) {
+ return SetupResult::error(
+ $this->l10n->t('This instance is missing some required PHP modules. It is required to install them: %s.', implode(', ', $missingRequiredModules)),
+ $this->urlGenerator->linkToDocs('admin-php-modules')
+ );
+ } elseif (!empty($missingRecommendedModules)) {
+ $moduleList = implode(
+ "\n",
+ array_map(
+ fn (string $module) => '- ' . $module . ' ' . $this->getRecommendedModuleDescription($module),
+ $missingRecommendedModules
+ )
+ );
+ return SetupResult::info(
+ $this->l10n->t("This instance is missing some recommended PHP modules. For improved performance and better compatibility it is highly recommended to install them:\n%s", $moduleList),
+ $this->urlGenerator->linkToDocs('admin-php-modules')
+ );
+ } else {
+ return SetupResult::success();
+ }
+ }
+
+ /**
+ * Checks for potential PHP modules that would improve the instance
+ *
+ * @param string[] $modules modules to test
+ * @return string[] A list of PHP modules which are missing
+ */
+ protected function getMissingModules(array $modules): array {
+ return array_values(array_filter(
+ $modules,
+ fn (string $module) => !extension_loaded($module),
+ ));
+ }
+}
diff --git a/apps/settings/lib/SetupChecks/PhpOpcacheSetup.php b/apps/settings/lib/SetupChecks/PhpOpcacheSetup.php
new file mode 100644
index 00000000000..83b7be1c390
--- /dev/null
+++ b/apps/settings/lib/SetupChecks/PhpOpcacheSetup.php
@@ -0,0 +1,136 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\Settings\SetupChecks;
+
+use bantu\IniGetWrapper\IniGetWrapper;
+use OCP\IL10N;
+use OCP\IURLGenerator;
+use OCP\SetupCheck\ISetupCheck;
+use OCP\SetupCheck\SetupResult;
+
+class PhpOpcacheSetup implements ISetupCheck {
+ public function __construct(
+ private IL10N $l10n,
+ private IURLGenerator $urlGenerator,
+ private IniGetWrapper $iniGetWrapper,
+ ) {
+ }
+
+ public function getName(): string {
+ return $this->l10n->t('PHP opcache');
+ }
+
+ public function getCategory(): string {
+ return 'php';
+ }
+
+ /**
+ * Checks whether a PHP OPcache is properly set up
+ * @return array{'warning'|'error',list<string>} The level and the list of OPcache setup recommendations
+ */
+ protected function getOpcacheSetupRecommendations(): array {
+ $level = 'warning';
+
+ // If the module is not loaded, return directly to skip inapplicable checks
+ if (!extension_loaded('Zend OPcache')) {
+ return ['error',[$this->l10n->t('The PHP OPcache module is not loaded. For better performance it is recommended to load it into your PHP installation.')]];
+ }
+
+ $recommendations = [];
+
+ // Check whether Nextcloud is allowed to use the OPcache API
+ $isPermitted = true;
+ $permittedPath = (string)$this->iniGetWrapper->getString('opcache.restrict_api');
+ if ($permittedPath !== '' && !str_starts_with(\OC::$SERVERROOT, rtrim($permittedPath, '/'))) {
+ $isPermitted = false;
+ }
+
+ if (!$this->iniGetWrapper->getBool('opcache.enable')) {
+ $recommendations[] = $this->l10n->t('OPcache is disabled. For better performance, it is recommended to apply "opcache.enable=1" to your PHP configuration.');
+ $level = 'error';
+ } elseif ($this->iniGetWrapper->getBool('opcache.file_cache_only')) {
+ $recommendations[] = $this->l10n->t('The shared memory based OPcache is disabled. For better performance, it is recommended to apply "opcache.file_cache_only=0" to your PHP configuration and use the file cache as second level cache only.');
+ } else {
+ // Check whether opcache_get_status has been explicitly disabled and in case skip usage based checks
+ $disabledFunctions = $this->iniGetWrapper->getString('disable_functions');
+ if (isset($disabledFunctions) && str_contains($disabledFunctions, 'opcache_get_status')) {
+ return [$level, $recommendations];
+ }
+
+ $status = opcache_get_status(false);
+
+ if ($status === false) {
+ $recommendations[] = $this->l10n->t('OPcache is not working as it should, opcache_get_status() returns false, please check configuration.');
+ $level = 'error';
+ }
+
+ // Check whether OPcache is full, which can be either the overall OPcache size or limit of cached keys reached.
+ // If the limit of cached keys has been reached, num_cached_keys equals max_cached_keys. The recommendation contains this value instead of opcache.max_accelerated_files, since the effective limit is a next higher prime number: https://www.php.net/manual/en/opcache.configuration.php#ini.opcache.max-accelerated-files
+ // Else, the remaining $status['memory_usage']['free_memory'] was too low to store another script. Aside of used_memory, this can be also due to wasted_memory, remaining cache keys from scripts changed on disk.
+ // Wasted memory is cleared only via opcache_reset(), or if $status['memory_usage']['current_wasted_percentage'] reached opcache.max_wasted_percentage, which triggers an engine restart and hence OPcache reset. Due to this complexity, we check for $status['cache_full'] only.
+ if ($status['cache_full'] === true) {
+ if ($status['opcache_statistics']['num_cached_keys'] === $status['opcache_statistics']['max_cached_keys']) {
+ $recommendations[] = $this->l10n->t('The maximum number of OPcache keys is nearly exceeded. To assure that all scripts can be kept in the cache, it is recommended to apply "opcache.max_accelerated_files" to your PHP configuration with a value higher than "%s".', [($status['opcache_statistics']['max_cached_keys'] ?: 'currently')]);
+ } else {
+ $recommendations[] = $this->l10n->t('The OPcache buffer is nearly full. To assure that all scripts can be hold in cache, it is recommended to apply "opcache.memory_consumption" to your PHP configuration with a value higher than "%s".', [($this->iniGetWrapper->getNumeric('opcache.memory_consumption') ?: 'currently')]);
+ }
+ }
+
+ // Interned strings buffer: recommend to raise size if more than 90% is used
+ $interned_strings_buffer = $this->iniGetWrapper->getNumeric('opcache.interned_strings_buffer') ?? 0;
+ $memory_consumption = $this->iniGetWrapper->getNumeric('opcache.memory_consumption') ?? 0;
+ if (
+ // Do not recommend to raise the interned strings buffer size above a quarter of the total OPcache size
+ ($interned_strings_buffer < ($memory_consumption / 4))
+ && (
+ empty($status['interned_strings_usage']['free_memory'])
+ || ($status['interned_strings_usage']['used_memory'] / $status['interned_strings_usage']['free_memory'] > 9)
+ )
+ ) {
+ $recommendations[] = $this->l10n->t('The OPcache interned strings buffer is nearly full. To assure that repeating strings can be effectively cached, it is recommended to apply "opcache.interned_strings_buffer" to your PHP configuration with a value higher than "%s".', [($this->iniGetWrapper->getNumeric('opcache.interned_strings_buffer') ?: 'currently')]);
+ }
+ }
+
+ // Check for saved comments only when OPcache is currently disabled. If it was enabled, opcache.save_comments=0 would break Nextcloud in the first place.
+ if (!$this->iniGetWrapper->getBool('opcache.save_comments')) {
+ $recommendations[] = $this->l10n->t('OPcache is configured to remove code comments. With OPcache enabled, "opcache.save_comments=1" must be set for Nextcloud to function.');
+ $level = 'error';
+ }
+
+ if (!$isPermitted) {
+ $recommendations[] = $this->l10n->t('Nextcloud is not allowed to use the OPcache API. With OPcache enabled, it is highly recommended to include all Nextcloud directories with "opcache.restrict_api" or unset this setting to disable OPcache API restrictions, to prevent errors during Nextcloud core or app upgrades.');
+ $level = 'error';
+ }
+
+ return [$level, $recommendations];
+ }
+
+ public function run(): SetupResult {
+ // Skip OPcache checks if running from CLI
+ if (\OC::$CLI && !$this->iniGetWrapper->getBool('opcache.enable_cli')) {
+ return SetupResult::success($this->l10n->t('Checking from CLI, OPcache checks have been skipped.'));
+ }
+
+ [$level,$recommendations] = $this->getOpcacheSetupRecommendations();
+ if (!empty($recommendations)) {
+ return match($level) {
+ 'error' => SetupResult::error(
+ $this->l10n->t('The PHP OPcache module is not properly configured. %s.', implode("\n", $recommendations)),
+ $this->urlGenerator->linkToDocs('admin-php-opcache')
+ ),
+ default => SetupResult::warning(
+ $this->l10n->t('The PHP OPcache module is not properly configured. %s.', implode("\n", $recommendations)),
+ $this->urlGenerator->linkToDocs('admin-php-opcache')
+ ),
+ };
+ } else {
+ return SetupResult::success($this->l10n->t('Correctly configured'));
+ }
+ }
+}
diff --git a/apps/settings/lib/SetupChecks/PhpOutdated.php b/apps/settings/lib/SetupChecks/PhpOutdated.php
new file mode 100644
index 00000000000..d0d8e03c705
--- /dev/null
+++ b/apps/settings/lib/SetupChecks/PhpOutdated.php
@@ -0,0 +1,47 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCA\Settings\SetupChecks;
+
+use OCP\IL10N;
+use OCP\SetupCheck\ISetupCheck;
+use OCP\SetupCheck\SetupResult;
+
+class PhpOutdated implements ISetupCheck {
+ public const DEPRECATED_PHP_VERSION = '8.1';
+ public const DEPRECATED_SINCE = '30';
+ public const FUTURE_REQUIRED_PHP_VERSION = '8.2';
+ public const FUTURE_REQUIRED_STARTING = '32';
+
+ public function __construct(
+ private IL10N $l10n,
+ ) {
+ }
+
+ public function getCategory(): string {
+ return 'security';
+ }
+
+ public function getName(): string {
+ return $this->l10n->t('PHP version');
+ }
+
+ public function run(): SetupResult {
+ if (PHP_VERSION_ID < 80200) {
+ return SetupResult::warning($this->l10n->t('You are currently running PHP %1$s. PHP %2$s is deprecated since Nextcloud %3$s. Nextcloud %4$s may require at least PHP %5$s. Please upgrade to one of the officially supported PHP versions provided by the PHP Group as soon as possible.', [
+ PHP_VERSION,
+ self::DEPRECATED_PHP_VERSION,
+ self::DEPRECATED_SINCE,
+ self::FUTURE_REQUIRED_STARTING,
+ self::FUTURE_REQUIRED_PHP_VERSION,
+ ]), 'https://secure.php.net/supported-versions.php');
+ }
+ return SetupResult::success($this->l10n->t('You are currently running PHP %s.', [PHP_VERSION]));
+ }
+}
diff --git a/apps/settings/lib/SetupChecks/PhpOutputBuffering.php b/apps/settings/lib/SetupChecks/PhpOutputBuffering.php
index 3bf52695301..be8154fbb1b 100644
--- a/apps/settings/lib/SetupChecks/PhpOutputBuffering.php
+++ b/apps/settings/lib/SetupChecks/PhpOutputBuffering.php
@@ -3,39 +3,35 @@
declare(strict_types=1);
/**
- * @copyright Copyright (c) 2020 Daniel Kesselberg <mail@danielkesselberg.de>
- *
- * @author Daniel Kesselberg <mail@danielkesselberg.de>
- *
- * @license GNU AGPL version 3 or any later version
- *
- * 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/>.
- *
+ * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\Settings\SetupChecks;
-class PhpOutputBuffering {
- public function description(): string {
- return 'PHP configuration option output_buffering must be disabled';
+use OCP\IL10N;
+use OCP\SetupCheck\ISetupCheck;
+use OCP\SetupCheck\SetupResult;
+
+class PhpOutputBuffering implements ISetupCheck {
+ public function __construct(
+ private IL10N $l10n,
+ ) {
+ }
+
+ public function getCategory(): string {
+ return 'php';
}
- public function severity(): string {
- return 'error';
+ public function getName(): string {
+ return $this->l10n->t('PHP "output_buffering" option');
}
- public function run(): bool {
+ public function run(): SetupResult {
$value = trim(ini_get('output_buffering'));
- return $value === '' || $value === '0';
+ if ($value === '' || $value === '0') {
+ return SetupResult::success($this->l10n->t('Disabled'));
+ } else {
+ return SetupResult::error($this->l10n->t('PHP configuration option "output_buffering" must be disabled'));
+ }
}
}
diff --git a/apps/settings/lib/SetupChecks/PushService.php b/apps/settings/lib/SetupChecks/PushService.php
new file mode 100644
index 00000000000..1f03404d80e
--- /dev/null
+++ b/apps/settings/lib/SetupChecks/PushService.php
@@ -0,0 +1,70 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\Settings\SetupChecks;
+
+use OCP\AppFramework\Utility\ITimeFactory;
+use OCP\IConfig;
+use OCP\IL10N;
+use OCP\Notification\IManager;
+use OCP\SetupCheck\ISetupCheck;
+use OCP\SetupCheck\SetupResult;
+use OCP\Support\Subscription\IRegistry;
+
+class PushService implements ISetupCheck {
+ public function __construct(
+ private IL10N $l10n,
+ private IConfig $config,
+ private IManager $notificationsManager,
+ private IRegistry $subscriptionRegistry,
+ private ITimeFactory $timeFactory,
+ ) {
+ }
+
+ public function getName(): string {
+ return $this->l10n->t('Push service');
+ }
+
+ public function getCategory(): string {
+ return 'system';
+ }
+
+ /**
+ * Check if is fair use of free push service
+ */
+ private function isFairUseOfFreePushService(): bool {
+ $rateLimitReached = (int)$this->config->getAppValue('notifications', 'rate_limit_reached', '0');
+ if ($rateLimitReached >= ($this->timeFactory->now()->getTimestamp() - 7 * 24 * 3600)) {
+ // Notifications app is showing a message already
+ return true;
+ }
+ return $this->notificationsManager->isFairUseOfFreePushService();
+ }
+
+ public function run(): SetupResult {
+ if ($this->subscriptionRegistry->delegateHasValidSubscription()) {
+ return SetupResult::success($this->l10n->t('Valid enterprise license'));
+ }
+
+ if ($this->isFairUseOfFreePushService()) {
+ return SetupResult::success($this->l10n->t('Free push service'));
+ }
+
+ return SetupResult::error(
+ $this->l10n->t('This is the unsupported community build of Nextcloud. Given the size of this instance, performance, reliability and scalability cannot be guaranteed. Push notifications are limited to avoid overloading our free service. Learn more about the benefits of Nextcloud Enterprise at {link}.'),
+ descriptionParameters:[
+ 'link' => [
+ 'type' => 'highlight',
+ 'id' => 'link',
+ 'name' => 'https://nextcloud.com/enterprise',
+ 'link' => 'https://nextcloud.com/enterprise',
+ ],
+ ],
+ );
+ }
+}
diff --git a/apps/settings/lib/SetupChecks/RandomnessSecure.php b/apps/settings/lib/SetupChecks/RandomnessSecure.php
new file mode 100644
index 00000000000..045ddde0b9d
--- /dev/null
+++ b/apps/settings/lib/SetupChecks/RandomnessSecure.php
@@ -0,0 +1,46 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\Settings\SetupChecks;
+
+use OCP\IConfig;
+use OCP\IL10N;
+use OCP\IURLGenerator;
+use OCP\Security\ISecureRandom;
+use OCP\SetupCheck\ISetupCheck;
+use OCP\SetupCheck\SetupResult;
+
+class RandomnessSecure implements ISetupCheck {
+ public function __construct(
+ private IL10N $l10n,
+ private IConfig $config,
+ private IURLGenerator $urlGenerator,
+ private ISecureRandom $secureRandom,
+ ) {
+ }
+
+ public function getName(): string {
+ return $this->l10n->t('Random generator');
+ }
+
+ public function getCategory(): string {
+ return 'security';
+ }
+
+ public function run(): SetupResult {
+ try {
+ $this->secureRandom->generate(1);
+ } catch (\Exception $ex) {
+ return SetupResult::error(
+ $this->l10n->t('No suitable source for randomness found by PHP which is highly discouraged for security reasons.'),
+ $this->urlGenerator->linkToDocs('admin-security')
+ );
+ }
+ return SetupResult::success($this->l10n->t('Secure'));
+ }
+}
diff --git a/apps/settings/lib/SetupChecks/ReadOnlyConfig.php b/apps/settings/lib/SetupChecks/ReadOnlyConfig.php
new file mode 100644
index 00000000000..b616f8a7155
--- /dev/null
+++ b/apps/settings/lib/SetupChecks/ReadOnlyConfig.php
@@ -0,0 +1,38 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\Settings\SetupChecks;
+
+use OCP\IConfig;
+use OCP\IL10N;
+use OCP\SetupCheck\ISetupCheck;
+use OCP\SetupCheck\SetupResult;
+
+class ReadOnlyConfig implements ISetupCheck {
+ public function __construct(
+ private IL10N $l10n,
+ private IConfig $config,
+ ) {
+ }
+
+ public function getName(): string {
+ return $this->l10n->t('Configuration file access rights');
+ }
+
+ public function getCategory(): string {
+ return 'config';
+ }
+
+ public function run(): SetupResult {
+ if ($this->config->getSystemValueBool('config_is_read_only', false)) {
+ return SetupResult::info($this->l10n->t('The read-only config has been enabled. This prevents setting some configurations via the web-interface. Furthermore, the file needs to be made writable manually for every update.'));
+ } else {
+ return SetupResult::success($this->l10n->t('Nextcloud configuration file is writable'));
+ }
+ }
+}
diff --git a/apps/settings/lib/SetupChecks/SchedulingTableSize.php b/apps/settings/lib/SetupChecks/SchedulingTableSize.php
new file mode 100644
index 00000000000..b23972ca7dc
--- /dev/null
+++ b/apps/settings/lib/SetupChecks/SchedulingTableSize.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\SetupChecks;
+
+use OCP\IDBConnection;
+use OCP\IL10N;
+use OCP\SetupCheck\ISetupCheck;
+use OCP\SetupCheck\SetupResult;
+
+class SchedulingTableSize implements ISetupCheck {
+ public const MAX_SCHEDULING_ENTRIES = 50000;
+
+ public function __construct(
+ private IL10N $l10n,
+ private IDBConnection $connection,
+ ) {
+ }
+
+ public function getName(): string {
+ return $this->l10n->t('Scheduling objects table size');
+ }
+
+ public function getCategory(): string {
+ return 'database';
+ }
+
+ public function run(): SetupResult {
+ $qb = $this->connection->getQueryBuilder();
+ $qb->select($qb->func()->count('id'))
+ ->from('schedulingobjects');
+ $query = $qb->executeQuery();
+ $count = $query->fetchOne();
+ $query->closeCursor();
+
+ if ($count > self::MAX_SCHEDULING_ENTRIES) {
+ return SetupResult::warning(
+ $this->l10n->t('You have more than %s rows in the scheduling objects table. Please run the expensive repair jobs via occ maintenance:repair --include-expensive.', [
+ self::MAX_SCHEDULING_ENTRIES,
+ ])
+ );
+ }
+ return SetupResult::success(
+ $this->l10n->t('Scheduling objects table size is within acceptable range.')
+ );
+ }
+}
diff --git a/apps/settings/lib/SetupChecks/SecurityHeaders.php b/apps/settings/lib/SetupChecks/SecurityHeaders.php
new file mode 100644
index 00000000000..9cc6856a170
--- /dev/null
+++ b/apps/settings/lib/SetupChecks/SecurityHeaders.php
@@ -0,0 +1,139 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCA\Settings\SetupChecks;
+
+use OCP\Http\Client\IClientService;
+use OCP\IConfig;
+use OCP\IL10N;
+use OCP\IURLGenerator;
+use OCP\SetupCheck\CheckServerResponseTrait;
+use OCP\SetupCheck\ISetupCheck;
+use OCP\SetupCheck\SetupResult;
+use Psr\Log\LoggerInterface;
+
+class SecurityHeaders implements ISetupCheck {
+
+ use CheckServerResponseTrait;
+
+ public function __construct(
+ protected IL10N $l10n,
+ protected IConfig $config,
+ protected IURLGenerator $urlGenerator,
+ protected IClientService $clientService,
+ protected LoggerInterface $logger,
+ ) {
+ }
+
+ public function getCategory(): string {
+ return 'security';
+ }
+
+ public function getName(): string {
+ return $this->l10n->t('HTTP headers');
+ }
+
+ public function run(): SetupResult {
+ $urls = [
+ ['get', $this->urlGenerator->linkToRoute('heartbeat'), [200]],
+ ];
+ $securityHeaders = [
+ 'X-Content-Type-Options' => ['nosniff', null],
+ 'X-Robots-Tag' => ['noindex,nofollow', null],
+ 'X-Frame-Options' => ['sameorigin', 'deny'],
+ 'X-Permitted-Cross-Domain-Policies' => ['none', null],
+ ];
+
+ foreach ($urls as [$verb,$url,$validStatuses]) {
+ $works = null;
+ foreach ($this->runRequest($verb, $url, ['httpErrors' => false]) as $response) {
+ // Check that the response status matches
+ if (!in_array($response->getStatusCode(), $validStatuses)) {
+ $works = false;
+ continue;
+ }
+ $msg = '';
+ $msgParameters = [];
+ foreach ($securityHeaders as $header => [$expected, $accepted]) {
+ /* Convert to lowercase and remove spaces after comas */
+ $value = preg_replace('/,\s+/', ',', strtolower($response->getHeader($header)));
+ if ($value !== $expected) {
+ if ($accepted !== null && $value === $accepted) {
+ $msg .= $this->l10n->t('- The `%1$s` HTTP header is not set to `%2$s`. Some features might not work correctly, as it is recommended to adjust this setting accordingly.', [$header, $expected]) . "\n";
+ } else {
+ $msg .= $this->l10n->t('- The `%1$s` HTTP header is not set to `%2$s`. This is a potential security or privacy risk, as it is recommended to adjust this setting accordingly.', [$header, $expected]) . "\n";
+ }
+ }
+ }
+
+ $referrerPolicy = $response->getHeader('Referrer-Policy');
+ if (!preg_match('/(no-referrer(-when-downgrade)?|strict-origin(-when-cross-origin)?|same-origin)(,|$)/', $referrerPolicy)) {
+ $msg .= $this->l10n->t(
+ '- The `%1$s` HTTP header is not set to `%2$s`, `%3$s`, `%4$s`, `%5$s` or `%6$s`. This can leak referer information. See the {w3c-recommendation}.',
+ [
+ 'Referrer-Policy',
+ 'no-referrer',
+ 'no-referrer-when-downgrade',
+ 'strict-origin',
+ 'strict-origin-when-cross-origin',
+ 'same-origin',
+ ]
+ ) . "\n";
+ $msgParameters['w3c-recommendation'] = [
+ 'type' => 'highlight',
+ 'id' => 'w3c-recommendation',
+ 'name' => 'W3C Recommendation',
+ 'link' => 'https://www.w3.org/TR/referrer-policy/',
+ ];
+ }
+
+ $transportSecurityValidity = $response->getHeader('Strict-Transport-Security');
+ $minimumSeconds = 15552000;
+ if (preg_match('/^max-age=(\d+)(;.*)?$/', $transportSecurityValidity, $m)) {
+ $transportSecurityValidity = (int)$m[1];
+ if ($transportSecurityValidity < $minimumSeconds) {
+ $msg .= $this->l10n->t('- The `Strict-Transport-Security` HTTP header is not set to at least `%d` seconds (current value: `%d`). For enhanced security, it is recommended to use a long HSTS policy.', [$minimumSeconds, $transportSecurityValidity]) . "\n";
+ }
+ } elseif (!empty($transportSecurityValidity)) {
+ $msg .= $this->l10n->t('- The `Strict-Transport-Security` HTTP header is malformed: `%s`. For enhanced security, it is recommended to enable HSTS.', [$transportSecurityValidity]) . "\n";
+ } else {
+ $msg .= $this->l10n->t('- The `Strict-Transport-Security` HTTP header is not set (should be at least `%d` seconds). For enhanced security, it is recommended to enable HSTS.', [$minimumSeconds]) . "\n";
+ }
+
+ if (!empty($msg)) {
+ return SetupResult::warning(
+ $this->l10n->t('Some headers are not set correctly on your instance') . "\n" . $msg,
+ $this->urlGenerator->linkToDocs('admin-security'),
+ $msgParameters,
+ );
+ }
+ // Skip the other requests if one works
+ $works = true;
+ break;
+ }
+ // If 'works' is null then we could not connect to the server
+ if ($works === null) {
+ return SetupResult::info(
+ $this->l10n->t('Could not check that your web server serves security headers correctly. Please check manually.'),
+ $this->urlGenerator->linkToDocs('admin-security'),
+ );
+ }
+ // Otherwise if we fail we can abort here
+ if ($works === false) {
+ return SetupResult::warning(
+ $this->l10n->t('Could not check that your web server serves security headers correctly, unable to query `%s`', [$url]),
+ $this->urlGenerator->linkToDocs('admin-security'),
+ );
+ }
+ }
+ return SetupResult::success(
+ $this->l10n->t('Your server is correctly configured to send security headers.')
+ );
+ }
+}
diff --git a/apps/settings/lib/SetupChecks/SupportedDatabase.php b/apps/settings/lib/SetupChecks/SupportedDatabase.php
index 302797485d3..d083958d16e 100644
--- a/apps/settings/lib/SetupChecks/SupportedDatabase.php
+++ b/apps/settings/lib/SetupChecks/SupportedDatabase.php
@@ -3,111 +3,124 @@
declare(strict_types=1);
/**
- * @copyright Copyright (c) 2021 Morris Jobke <hey@morrisjobke.de>
- *
- * @author Claas Augner <github@caugner.de>
- * @author Morris Jobke <hey@morrisjobke.de>
- * @author Vincent Petry <vincent@nextcloud.com>
- *
- * @license GNU AGPL version 3 or any later version
- *
- * 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/>.
- *
+ * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\Settings\SetupChecks;
-use Doctrine\DBAL\Platforms\MariaDb1027Platform;
-use Doctrine\DBAL\Platforms\MySQL57Platform;
-use Doctrine\DBAL\Platforms\MySQL80Platform;
use Doctrine\DBAL\Platforms\MySQLPlatform;
use Doctrine\DBAL\Platforms\OraclePlatform;
-use Doctrine\DBAL\Platforms\PostgreSQL100Platform;
-use Doctrine\DBAL\Platforms\PostgreSQL94Platform;
+use Doctrine\DBAL\Platforms\PostgreSQLPlatform;
use Doctrine\DBAL\Platforms\SqlitePlatform;
use OCP\IDBConnection;
use OCP\IL10N;
+use OCP\IURLGenerator;
+use OCP\SetupCheck\ISetupCheck;
+use OCP\SetupCheck\SetupResult;
-class SupportedDatabase {
- /** @var IL10N */
- private $l10n;
- /** @var IDBConnection */
- private $connection;
+class SupportedDatabase implements ISetupCheck {
- private $checked = false;
- private $description = '';
+ private const MIN_MARIADB = '10.6';
+ private const MAX_MARIADB = '11.8';
+ private const MIN_MYSQL = '8.0';
+ private const MAX_MYSQL = '8.4';
+ private const MIN_POSTGRES = '13';
+ private const MAX_POSTGRES = '17';
- public function __construct(IL10N $l10n, IDBConnection $connection) {
- $this->l10n = $l10n;
- $this->connection = $connection;
+ public function __construct(
+ private IL10N $l10n,
+ private IURLGenerator $urlGenerator,
+ private IDBConnection $connection,
+ ) {
}
- public function check() {
- if ($this->checked === true) {
- return;
- }
- $this->checked = true;
+ public function getCategory(): string {
+ return 'database';
+ }
- switch (get_class($this->connection->getDatabasePlatform())) {
- case MySQL80Platform::class: # extends MySQL57Platform
- case MySQL57Platform::class: # extends MySQLPlatform
- case MariaDb1027Platform::class: # extends MySQLPlatform
- case MySQLPlatform::class:
- $result = $this->connection->prepare("SHOW VARIABLES LIKE 'version';");
- $result->execute();
- $row = $result->fetch();
- $version = strtolower($row['Value']);
+ public function getName(): string {
+ return $this->l10n->t('Database version');
+ }
- if (strpos($version, 'mariadb') !== false) {
- if (version_compare($version, '10.2', '<')) {
- $this->description = $this->l10n->t('MariaDB version "%s" is used. Nextcloud 21 and higher do not support this version and require MariaDB 10.2 or higher.', $row['Value']);
- return;
- }
- } else {
- if (version_compare($version, '8', '<')) {
- $this->description = $this->l10n->t('MySQL version "%s" is used. Nextcloud 21 and higher do not support this version and require MySQL 8.0 or MariaDB 10.2 or higher.', $row['Value']);
- return;
- }
+ public function run(): SetupResult {
+ $version = null;
+ $databasePlatform = $this->connection->getDatabasePlatform();
+ if ($databasePlatform instanceof MySQLPlatform) {
+ $statement = $this->connection->prepare("SHOW VARIABLES LIKE 'version';");
+ $result = $statement->execute();
+ $row = $result->fetch();
+ $version = $row['Value'];
+ $versionlc = strtolower($version);
+ // we only care about X.Y not X.Y.Z differences
+ [$major, $minor, ] = explode('.', $versionlc);
+ $versionConcern = $major . '.' . $minor;
+ if (str_contains($versionlc, 'mariadb')) {
+ if (version_compare($versionConcern, '10.3', '=')) {
+ return SetupResult::info(
+ $this->l10n->t(
+ 'MariaDB version 10.3 detected, this version is end-of-life and only supported as part of Ubuntu 20.04. MariaDB >=%1$s and <=%2$s is suggested for best performance, stability and functionality with this version of Nextcloud.',
+ [
+ self::MIN_MARIADB,
+ self::MAX_MARIADB,
+ ]
+ ),
+ );
+ } elseif (version_compare($versionConcern, self::MIN_MARIADB, '<') || version_compare($versionConcern, self::MAX_MARIADB, '>')) {
+ return SetupResult::warning(
+ $this->l10n->t(
+ 'MariaDB version "%1$s" detected. MariaDB >=%2$s and <=%3$s is suggested for best performance, stability and functionality with this version of Nextcloud.',
+ [
+ $version,
+ self::MIN_MARIADB,
+ self::MAX_MARIADB,
+ ],
+ ),
+ );
}
- break;
- case SqlitePlatform::class:
- break;
- case PostgreSQL100Platform::class: # extends PostgreSQL94Platform
- case PostgreSQL94Platform::class:
- $result = $this->connection->prepare('SHOW server_version;');
- $result->execute();
- $row = $result->fetch();
- if (version_compare($row['server_version'], '9.6', '<')) {
- $this->description = $this->l10n->t('PostgreSQL version "%s" is used. Nextcloud 21 and higher do not support this version and require PostgreSQL 9.6 or higher.', $row['server_version']);
- return;
+ } else {
+ if (version_compare($versionConcern, self::MIN_MYSQL, '<') || version_compare($versionConcern, self::MAX_MYSQL, '>')) {
+ return SetupResult::warning(
+ $this->l10n->t(
+ 'MySQL version "%1$s" detected. MySQL >=%2$s and <=%3$s is suggested for best performance, stability and functionality with this version of Nextcloud.',
+ [
+ $version,
+ self::MIN_MYSQL,
+ self::MAX_MYSQL,
+ ],
+ ),
+ );
}
- break;
- case OraclePlatform::class:
- break;
+ }
+ } elseif ($databasePlatform instanceof PostgreSQLPlatform) {
+ $statement = $this->connection->prepare('SHOW server_version;');
+ $result = $statement->execute();
+ $row = $result->fetch();
+ $version = $row['server_version'];
+ $versionlc = strtolower($version);
+ // we only care about X not X.Y or X.Y.Z differences
+ [$major, ] = explode('.', $versionlc);
+ $versionConcern = $major;
+ if (version_compare($versionConcern, self::MIN_POSTGRES, '<') || version_compare($versionConcern, self::MAX_POSTGRES, '>')) {
+ return SetupResult::warning(
+ $this->l10n->t(
+ 'PostgreSQL version "%1$s" detected. PostgreSQL >=%2$s and <=%3$s is suggested for best performance, stability and functionality with this version of Nextcloud.',
+ [
+ $version,
+ self::MIN_POSTGRES,
+ self::MAX_POSTGRES,
+ ])
+ );
+ }
+ } elseif ($databasePlatform instanceof OraclePlatform) {
+ $version = 'Oracle';
+ } elseif ($databasePlatform instanceof SqlitePlatform) {
+ return SetupResult::warning(
+ $this->l10n->t('SQLite is currently being used as the backend database. For larger installations we recommend that you switch to a different database backend. This is particularly recommended when using the desktop client for file synchronisation. To migrate to another database use the command line tool: "occ db:convert-type".'),
+ $this->urlGenerator->linkToDocs('admin-db-conversion')
+ );
+ } else {
+ return SetupResult::error($this->l10n->t('Unknown database platform'));
}
- }
-
- public function description(): string {
- $this->check();
- return $this->description;
- }
-
- public function severity(): string {
- return 'info';
- }
-
- public function run(): bool {
- $this->check();
- return $this->description === '';
+ return SetupResult::success($version);
}
}
diff --git a/apps/settings/lib/SetupChecks/SystemIs64bit.php b/apps/settings/lib/SetupChecks/SystemIs64bit.php
new file mode 100644
index 00000000000..308011c218e
--- /dev/null
+++ b/apps/settings/lib/SetupChecks/SystemIs64bit.php
@@ -0,0 +1,50 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCA\Settings\SetupChecks;
+
+use OCP\IL10N;
+use OCP\IURLGenerator;
+use OCP\SetupCheck\ISetupCheck;
+use OCP\SetupCheck\SetupResult;
+
+class SystemIs64bit implements ISetupCheck {
+ public function __construct(
+ private IL10N $l10n,
+ private IURLGenerator $urlGenerator,
+ ) {
+ }
+
+ public function getName(): string {
+ return $this->l10n->t('Architecture');
+ }
+
+ public function getCategory(): string {
+ return 'system';
+ }
+
+ protected function is64bit(): bool {
+ if (PHP_INT_SIZE < 8) {
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ public function run(): SetupResult {
+ if ($this->is64bit()) {
+ return SetupResult::success($this->l10n->t('64-bit'));
+ } else {
+ return SetupResult::warning(
+ $this->l10n->t('It seems like you are running a 32-bit PHP version. Nextcloud needs 64-bit to run well. Please upgrade your OS and PHP to 64-bit!'),
+ $this->urlGenerator->linkToDocs('admin-system-requirements')
+ );
+ }
+ }
+}
diff --git a/apps/settings/lib/SetupChecks/TaskProcessingPickupSpeed.php b/apps/settings/lib/SetupChecks/TaskProcessingPickupSpeed.php
new file mode 100644
index 00000000000..83168ac0f3e
--- /dev/null
+++ b/apps/settings/lib/SetupChecks/TaskProcessingPickupSpeed.php
@@ -0,0 +1,63 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCA\Settings\SetupChecks;
+
+use OCP\AppFramework\Utility\ITimeFactory;
+use OCP\IL10N;
+use OCP\SetupCheck\ISetupCheck;
+use OCP\SetupCheck\SetupResult;
+use OCP\TaskProcessing\IManager;
+
+class TaskProcessingPickupSpeed implements ISetupCheck {
+ public const MAX_SLOW_PERCENTAGE = 0.2;
+ public const TIME_SPAN = 24;
+
+ public function __construct(
+ private IL10N $l10n,
+ private IManager $taskProcessingManager,
+ private ITimeFactory $timeFactory,
+ ) {
+ }
+
+ public function getCategory(): string {
+ return 'ai';
+ }
+
+ public function getName(): string {
+ return $this->l10n->t('Task Processing pickup speed');
+ }
+
+ public function run(): SetupResult {
+ $tasks = $this->taskProcessingManager->getTasks(userId: '', scheduleAfter: $this->timeFactory->now()->getTimestamp() - 60 * 60 * self::TIME_SPAN); // userId: '' means no filter, whereas null would mean guest
+ $taskCount = count($tasks);
+ if ($taskCount === 0) {
+ return SetupResult::success($this->l10n->n('No scheduled tasks in the last %n hour.', 'No scheduled tasks in the last %n hours.', self::TIME_SPAN));
+ }
+ $slowCount = 0;
+ foreach ($tasks as $task) {
+ if ($task->getStartedAt() === null) {
+ continue; // task was not picked up yet
+ }
+ if ($task->getScheduledAt() === null) {
+ continue; // task was not scheduled yet -- should not happen, but the API specifies null as return value
+ }
+ $pickupDelay = $task->getScheduledAt() - $task->getStartedAt();
+ if ($pickupDelay > 60 * 4) {
+ $slowCount++; // task pickup took longer than 4 minutes
+ }
+ }
+
+ if ($slowCount / $taskCount < self::MAX_SLOW_PERCENTAGE) {
+ return SetupResult::success($this->l10n->n('The task pickup speed has been ok in the last %n hour.', 'The task pickup speed has been ok in the last %n hours.', self::TIME_SPAN));
+ } else {
+ return SetupResult::warning($this->l10n->n('The task pickup speed has been slow in the last %n hour. Many tasks took longer than 4 minutes to be picked up. Consider setting up a worker to process tasks in the background.', 'The task pickup speed has been slow in the last %n hours. Many tasks took longer than 4 minutes to be picked up. Consider setting up a worker to process tasks in the background.', self::TIME_SPAN), 'https://docs.nextcloud.com/server/latest/admin_manual/ai/overview.html#improve-ai-task-pickup-speed');
+ }
+ }
+}
diff --git a/apps/settings/lib/SetupChecks/TempSpaceAvailable.php b/apps/settings/lib/SetupChecks/TempSpaceAvailable.php
new file mode 100644
index 00000000000..49dc0d377e7
--- /dev/null
+++ b/apps/settings/lib/SetupChecks/TempSpaceAvailable.php
@@ -0,0 +1,113 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\Settings\SetupChecks;
+
+use OCP\IConfig;
+use OCP\IL10N;
+use OCP\ITempManager;
+use OCP\IURLGenerator;
+use OCP\SetupCheck\ISetupCheck;
+use OCP\SetupCheck\SetupResult;
+
+class TempSpaceAvailable implements ISetupCheck {
+ public function __construct(
+ private IL10N $l10n,
+ private IConfig $config,
+ private IURLGenerator $urlGenerator,
+ private ITempManager $tempManager,
+ ) {
+ }
+
+ public function getName(): string {
+ return $this->l10n->t('Temporary space available');
+ }
+
+ public function getCategory(): string {
+ return 'system';
+ }
+
+ private function isPrimaryStorageS3(): bool {
+ $objectStore = $this->config->getSystemValue('objectstore', null);
+ $objectStoreMultibucket = $this->config->getSystemValue('objectstore_multibucket', null);
+
+ if (!isset($objectStoreMultibucket) && !isset($objectStore)) {
+ return false;
+ }
+
+ if (isset($objectStoreMultibucket['class']) && $objectStoreMultibucket['class'] !== 'OC\\Files\\ObjectStore\\S3') {
+ return false;
+ }
+
+ if (isset($objectStore['class']) && $objectStore['class'] !== 'OC\\Files\\ObjectStore\\S3') {
+ return false;
+ }
+
+ return true;
+ }
+
+ public function run(): SetupResult {
+ $phpTempPath = sys_get_temp_dir();
+ $nextcloudTempPath = '';
+ try {
+ $nextcloudTempPath = $this->tempManager->getTempBaseDir();
+ } catch (\Exception $e) {
+ }
+
+ if (empty($nextcloudTempPath)) {
+ return SetupResult::error('The temporary directory of this instance points to an either non-existing or non-writable directory.');
+ }
+
+ if (!is_dir($phpTempPath)) {
+ return SetupResult::error($this->l10n->t('Error while checking the temporary PHP path - it was not properly set to a directory. Returned value: %s', [$phpTempPath]));
+ }
+
+ if (!function_exists('disk_free_space')) {
+ return SetupResult::info($this->l10n->t('The PHP function "disk_free_space" is disabled, which prevents the check for enough space in the temporary directories.'));
+ }
+
+ $freeSpaceInTemp = disk_free_space($phpTempPath);
+ if ($freeSpaceInTemp === false) {
+ return SetupResult::error($this->l10n->t('Error while checking the available disk space of temporary PHP path or no free disk space returned. Temporary path: %s', [$phpTempPath]));
+ }
+
+ /** Build details data about temporary directory, either one or two of them */
+ $freeSpaceInTempInGB = $freeSpaceInTemp / 1024 / 1024 / 1024;
+ $spaceDetail = $this->l10n->t('- %.1f GiB available in %s (PHP temporary directory)', [round($freeSpaceInTempInGB, 1),$phpTempPath]);
+ if ($nextcloudTempPath !== $phpTempPath) {
+ $freeSpaceInNextcloudTemp = disk_free_space($nextcloudTempPath);
+ if ($freeSpaceInNextcloudTemp === false) {
+ return SetupResult::error($this->l10n->t('Error while checking the available disk space of temporary PHP path or no free disk space returned. Temporary path: %s', [$nextcloudTempPath]));
+ }
+ $freeSpaceInNextcloudTempInGB = $freeSpaceInNextcloudTemp / 1024 / 1024 / 1024;
+ $spaceDetail .= "\n" . $this->l10n->t('- %.1f GiB available in %s (Nextcloud temporary directory)', [round($freeSpaceInNextcloudTempInGB, 1),$nextcloudTempPath]);
+ }
+
+ if (!$this->isPrimaryStorageS3()) {
+ return SetupResult::success(
+ $this->l10n->t("Temporary directory is correctly configured:\n%s", [$spaceDetail])
+ );
+ }
+
+ if ($freeSpaceInTempInGB > 50) {
+ return SetupResult::success(
+ $this->l10n->t(
+ "This instance uses an S3 based object store as primary storage, and has enough space in the temporary directory.\n%s",
+ [$spaceDetail]
+ )
+ );
+ }
+
+ return SetupResult::warning(
+ $this->l10n->t(
+ "This instance uses an S3 based object store as primary storage. The uploaded files are stored temporarily on the server and thus it is recommended to have 50 GiB of free space available in the temp directory of PHP. To improve this please change the temporary directory in the php.ini or make more space available in that path. \nChecking the available space in the temporary path resulted in %.1f GiB instead of the recommended 50 GiB. Path: %s",
+ [round($freeSpaceInTempInGB, 1),$phpTempPath]
+ )
+ );
+ }
+}
diff --git a/apps/settings/lib/SetupChecks/TransactionIsolation.php b/apps/settings/lib/SetupChecks/TransactionIsolation.php
new file mode 100644
index 00000000000..892c0ecbda6
--- /dev/null
+++ b/apps/settings/lib/SetupChecks/TransactionIsolation.php
@@ -0,0 +1,58 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCA\Settings\SetupChecks;
+
+use Doctrine\DBAL\Exception;
+use Doctrine\DBAL\TransactionIsolationLevel;
+use OC\DB\Connection;
+use OCP\IDBConnection;
+use OCP\IL10N;
+use OCP\IURLGenerator;
+use OCP\SetupCheck\ISetupCheck;
+use OCP\SetupCheck\SetupResult;
+
+class TransactionIsolation implements ISetupCheck {
+ public function __construct(
+ private IL10N $l10n,
+ private IURLGenerator $urlGenerator,
+ private IDBConnection $connection,
+ private Connection $db,
+ ) {
+ }
+
+ public function getName(): string {
+ return $this->l10n->t('Database transaction isolation level');
+ }
+
+ public function getCategory(): string {
+ return 'database';
+ }
+
+ public function run(): SetupResult {
+ try {
+ if ($this->connection->getDatabaseProvider() === IDBConnection::PLATFORM_SQLITE) {
+ return SetupResult::success();
+ }
+
+ if ($this->db->getTransactionIsolation() === TransactionIsolationLevel::READ_COMMITTED) {
+ return SetupResult::success('Read committed');
+ } else {
+ return SetupResult::error(
+ $this->l10n->t('Your database does not run with "READ COMMITTED" transaction isolation level. This can cause problems when multiple actions are executed in parallel.'),
+ $this->urlGenerator->linkToDocs('admin-db-transaction')
+ );
+ }
+ } catch (Exception $e) {
+ return SetupResult::warning(
+ $this->l10n->t('Was not able to get transaction isolation level: %s', $e->getMessage())
+ );
+ }
+ }
+}
diff --git a/apps/settings/lib/SetupChecks/WellKnownUrls.php b/apps/settings/lib/SetupChecks/WellKnownUrls.php
new file mode 100644
index 00000000000..4eeaff8f3c4
--- /dev/null
+++ b/apps/settings/lib/SetupChecks/WellKnownUrls.php
@@ -0,0 +1,95 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCA\Settings\SetupChecks;
+
+use OCP\Http\Client\IClientService;
+use OCP\IConfig;
+use OCP\IL10N;
+use OCP\IURLGenerator;
+use OCP\SetupCheck\CheckServerResponseTrait;
+use OCP\SetupCheck\ISetupCheck;
+use OCP\SetupCheck\SetupResult;
+use Psr\Log\LoggerInterface;
+
+class WellKnownUrls implements ISetupCheck {
+
+ use CheckServerResponseTrait;
+
+ public function __construct(
+ protected IL10N $l10n,
+ protected IConfig $config,
+ protected IURLGenerator $urlGenerator,
+ protected IClientService $clientService,
+ protected LoggerInterface $logger,
+ ) {
+ }
+
+ public function getCategory(): string {
+ return 'network';
+ }
+
+ public function getName(): string {
+ return $this->l10n->t('.well-known URLs');
+ }
+
+ public function run(): SetupResult {
+ if (!$this->config->getSystemValueBool('check_for_working_wellknown_setup', true)) {
+ return SetupResult::info($this->l10n->t('`check_for_working_wellknown_setup` is set to false in your configuration, so this check was skipped.'));
+ }
+
+ $urls = [
+ ['get', '/.well-known/webfinger', [200, 400, 404], true], // 400 indicates a handler is installed but (correctly) failed because we didn't specify a resource
+ ['get', '/.well-known/nodeinfo', [200, 404], true],
+ ['propfind', '/.well-known/caldav', [207], false],
+ ['propfind', '/.well-known/carddav', [207], false],
+ ];
+
+ $requestOptions = ['httpErrors' => false, 'options' => ['allow_redirects' => ['track_redirects' => true]]];
+ foreach ($urls as [$verb,$url,$validStatuses,$checkCustomHeader]) {
+ $works = null;
+ foreach ($this->runRequest($verb, $url, $requestOptions, isRootRequest: true) as $response) {
+ // Check that the response status matches
+ $works = in_array($response->getStatusCode(), $validStatuses);
+ // and (if needed) the custom Nextcloud header is set
+ if ($checkCustomHeader) {
+ $works = $works && !empty($response->getHeader('X-NEXTCLOUD-WELL-KNOWN'));
+ } else {
+ // For default DAV endpoints we lack authorization, but we still can check that the redirect works as expected
+ if (!$works && $response->getStatusCode() === 401) {
+ $redirectHops = explode(',', $response->getHeader('X-Guzzle-Redirect-History'));
+ $effectiveUri = end($redirectHops);
+ $works = str_ends_with(rtrim($effectiveUri, '/'), '/remote.php/dav');
+ }
+ }
+ // Skip the other requests if one works
+ if ($works === true) {
+ break;
+ }
+ }
+ // If 'works' is null then we could not connect to the server
+ if ($works === null) {
+ return SetupResult::info(
+ $this->l10n->t('Could not check that your web server serves `.well-known` correctly. Please check manually.') . "\n" . $this->serverConfigHelp(),
+ $this->urlGenerator->linkToDocs('admin-setup-well-known-URL'),
+ );
+ }
+ // Otherwise if we fail we can abort here
+ if ($works === false) {
+ return SetupResult::warning(
+ $this->l10n->t("Your web server is not properly set up to resolve `.well-known` URLs, failed on:\n`%s`", [$url]),
+ $this->urlGenerator->linkToDocs('admin-setup-well-known-URL'),
+ );
+ }
+ }
+ return SetupResult::success(
+ $this->l10n->t('Your server is correctly configured to serve `.well-known` URLs.')
+ );
+ }
+}
diff --git a/apps/settings/lib/SetupChecks/Woff2Loading.php b/apps/settings/lib/SetupChecks/Woff2Loading.php
new file mode 100644
index 00000000000..27aff4ea999
--- /dev/null
+++ b/apps/settings/lib/SetupChecks/Woff2Loading.php
@@ -0,0 +1,81 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\Settings\SetupChecks;
+
+use OCP\Http\Client\IClientService;
+use OCP\IConfig;
+use OCP\IL10N;
+use OCP\IURLGenerator;
+use OCP\SetupCheck\CheckServerResponseTrait;
+use OCP\SetupCheck\ISetupCheck;
+use OCP\SetupCheck\SetupResult;
+use Psr\Log\LoggerInterface;
+
+/**
+ * Check whether the OTF and WOFF2 URLs works
+ */
+class Woff2Loading implements ISetupCheck {
+ use CheckServerResponseTrait;
+
+ public function __construct(
+ protected IL10N $l10n,
+ protected IConfig $config,
+ protected IURLGenerator $urlGenerator,
+ protected IClientService $clientService,
+ protected LoggerInterface $logger,
+ ) {
+ }
+
+ public function getCategory(): string {
+ return 'network';
+ }
+
+ public function getName(): string {
+ return $this->l10n->t('Font file loading');
+ }
+
+ public function run(): SetupResult {
+ $result = $this->checkFont('otf', $this->urlGenerator->linkTo('theming', 'fonts/OpenDyslexic-Regular.otf'));
+ if ($result->getSeverity() !== SetupResult::SUCCESS) {
+ return $result;
+ }
+ return $this->checkFont('woff2', $this->urlGenerator->linkTo('', 'core/fonts/NotoSans-Regular-latin.woff2'));
+ }
+
+ protected function checkFont(string $fileExtension, string $url): SetupResult {
+ $noResponse = true;
+ $responses = $this->runRequest('HEAD', $url);
+ foreach ($responses as $response) {
+ $noResponse = false;
+ if ($response->getStatusCode() === 200) {
+ return SetupResult::success();
+ }
+ }
+
+ if ($noResponse) {
+ return SetupResult::info(
+ str_replace(
+ '{extension}',
+ $fileExtension,
+ $this->l10n->t('Could not check for {extension} loading support. Please check manually if your webserver serves `.{extension}` files.') . "\n" . $this->serverConfigHelp(),
+ ),
+ $this->urlGenerator->linkToDocs('admin-nginx'),
+ );
+ }
+ return SetupResult::warning(
+ str_replace(
+ '{extension}',
+ $fileExtension,
+ $this->l10n->t('Your web server is not properly set up to deliver .{extension} files. This is typically an issue with the Nginx configuration. For Nextcloud 15 it needs an adjustment to also deliver .{extension} files. Compare your Nginx configuration to the recommended configuration in our documentation.'),
+ ),
+ $this->urlGenerator->linkToDocs('admin-nginx'),
+ );
+
+ }
+}
diff --git a/apps/settings/lib/UserMigration/AccountMigrator.php b/apps/settings/lib/UserMigration/AccountMigrator.php
index e8c70624224..1c51aec5104 100644
--- a/apps/settings/lib/UserMigration/AccountMigrator.php
+++ b/apps/settings/lib/UserMigration/AccountMigrator.php
@@ -3,25 +3,8 @@
declare(strict_types=1);
/**
- * @copyright 2022 Christopher Ng <chrng8@gmail.com>
- *
- * @author Christopher Ng <chrng8@gmail.com>
- *
- * @license GNU AGPL version 3 or any later version
- *
- * 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/>.
- *
+ * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\Settings\UserMigration;
@@ -35,6 +18,7 @@ use OCA\Settings\AppInfo\Application;
use OCP\Accounts\IAccountManager;
use OCP\IAvatarManager;
use OCP\IL10N;
+use OCP\Image;
use OCP\IUser;
use OCP\UserMigration\IExportDestination;
use OCP\UserMigration\IImportSource;
@@ -49,16 +33,8 @@ class AccountMigrator implements IMigrator, ISizeEstimationMigrator {
use TAccountsHelper;
- private IAccountManager $accountManager;
-
- private IAvatarManager $avatarManager;
-
private ProfileManager $profileManager;
- private ProfileConfigMapper $configMapper;
-
- private IL10N $l10n;
-
private const PATH_ROOT = Application::APP_ID . '/';
private const PATH_ACCOUNT_FILE = AccountMigrator::PATH_ROOT . 'account.json';
@@ -68,23 +44,19 @@ class AccountMigrator implements IMigrator, ISizeEstimationMigrator {
private const PATH_CONFIG_FILE = AccountMigrator::PATH_ROOT . 'config.json';
public function __construct(
- IAccountManager $accountManager,
- IAvatarManager $avatarManager,
+ private IAccountManager $accountManager,
+ private IAvatarManager $avatarManager,
ProfileManager $profileManager,
- ProfileConfigMapper $configMapper,
- IL10N $l10n
+ private ProfileConfigMapper $configMapper,
+ private IL10N $l10n,
) {
- $this->accountManager = $accountManager;
- $this->avatarManager = $avatarManager;
$this->profileManager = $profileManager;
- $this->configMapper = $configMapper;
- $this->l10n = $l10n;
}
/**
* {@inheritDoc}
*/
- public function getEstimatedExportSize(IUser $user): int {
+ public function getEstimatedExportSize(IUser $user): int|float {
$size = 100; // 100KiB for account JSON
try {
@@ -97,7 +69,7 @@ class AccountMigrator implements IMigrator, ISizeEstimationMigrator {
// Skip avatar in size estimate on failure
}
- return (int)ceil($size);
+ return ceil($size);
}
/**
@@ -173,7 +145,7 @@ class AccountMigrator implements IMigrator, ISizeEstimationMigrator {
$output->writeln('Importing avatar from ' . $importPath . '…');
$stream = $importSource->getFileAsStream($importPath);
- $image = new \OCP\Image();
+ $image = new Image();
$image->loadFromFileHandle($stream);
try {
diff --git a/apps/settings/lib/UserMigration/AccountMigratorException.php b/apps/settings/lib/UserMigration/AccountMigratorException.php
index 91c63ba9d80..279daec45b3 100644
--- a/apps/settings/lib/UserMigration/AccountMigratorException.php
+++ b/apps/settings/lib/UserMigration/AccountMigratorException.php
@@ -3,25 +3,8 @@
declare(strict_types=1);
/**
- * @copyright 2022 Christopher Ng <chrng8@gmail.com>
- *
- * @author Christopher Ng <chrng8@gmail.com>
- *
- * @license GNU AGPL version 3 or any later version
- *
- * 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/>.
- *
+ * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\Settings\UserMigration;
diff --git a/apps/settings/lib/WellKnown/ChangePasswordHandler.php b/apps/settings/lib/WellKnown/ChangePasswordHandler.php
index 11f733474e3..9609579ef0a 100644
--- a/apps/settings/lib/WellKnown/ChangePasswordHandler.php
+++ b/apps/settings/lib/WellKnown/ChangePasswordHandler.php
@@ -3,24 +3,8 @@
declare(strict_types=1);
/**
- * @copyright 2022 Roeland Jago Douma <roeland@famdouma.nl>
- *
- * @author 2022 Roeland Jago Douma <roeland@famdouma.nl>
- *
- * @license GNU AGPL version 3 or any later version
- *
- * 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/>.
+ * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\Settings\WellKnown;
@@ -34,10 +18,9 @@ use OCP\IURLGenerator;
class ChangePasswordHandler implements IHandler {
- private IURLGenerator $urlGenerator;
-
- public function __construct(IURLGenerator $urlGenerator) {
- $this->urlGenerator = $urlGenerator;
+ public function __construct(
+ private IURLGenerator $urlGenerator,
+ ) {
}
public function handle(string $service, IRequestContext $context, ?IResponse $previousResponse): ?IResponse {
diff --git a/apps/settings/lib/WellKnown/SecurityTxtHandler.php b/apps/settings/lib/WellKnown/SecurityTxtHandler.php
index 665dfba0710..60ab2d57a38 100644
--- a/apps/settings/lib/WellKnown/SecurityTxtHandler.php
+++ b/apps/settings/lib/WellKnown/SecurityTxtHandler.php
@@ -3,24 +3,8 @@
declare(strict_types=1);
/**
- * @copyright 2021 Lukas Reschke <lukas@statuscode.ch>
- *
- * @author 2021 Lukas Reschke <lukas@statuscode.ch>
- *
- * @license GNU AGPL version 3 or any later version
- *
- * 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/>.
+ * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\Settings\WellKnown;
@@ -37,12 +21,13 @@ class SecurityTxtHandler implements IHandler {
return $previousResponse;
}
- $response = "Contact: https://hackerone.com/nextcloud
-Expires: 2023-04-30T23:00:00.000Z
+ $response = 'Contact: https://hackerone.com/nextcloud
+Expires: 2025-08-31T23:00:00.000Z
Acknowledgments: https://hackerone.com/nextcloud/thanks
Acknowledgments: https://github.com/nextcloud/security-advisories/security/advisories
Policy: https://hackerone.com/nextcloud
-Preferred-Languages: en";
+Preferred-Languages: en
+';
return new GenericResponse(new TextPlainResponse($response, 200));
}