Skip to content

Commit

Permalink
Merge pull request #48424 from nextcloud/fix/declarative-settings-pri…
Browse files Browse the repository at this point in the history
…ority

fix(settings): Sort all settings - incl declarative settings - by priority
  • Loading branch information
susnux committed Sep 28, 2024
2 parents 31ad1c5 + b525618 commit a0b2297
Show file tree
Hide file tree
Showing 5 changed files with 73 additions and 80 deletions.
25 changes: 5 additions & 20 deletions apps/settings/lib/Controller/AdminSettingsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
*/
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;
Expand All @@ -16,7 +15,6 @@
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;
Expand Down Expand Up @@ -49,27 +47,14 @@ public function __construct(
/**
* @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();
$settings = $this->settingsManager->getAllowedAdminSettings($section, $user);
$declarativeFormIDs = $this->declarativeSettingsManager->getFormIDs($user, 'admin', $section);
if (empty($settings) && empty($declarativeFormIDs)) {
throw new NotAdminException("Logged in user doesn't have permission to access these settings.");
}
$formatted = $this->formatSettings($settings);
return $formatted;
return $this->getIndexResponse(
'admin',
$section,
);
}
}
61 changes: 38 additions & 23 deletions apps/settings/lib/Controller/CommonSettingsTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

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;
Expand All @@ -21,7 +23,8 @@
use OCP\Util;

/**
* @psalm-import-type DeclarativeSettingsFormField from IDeclarativeSettingsForm
* @psalm-import-type DeclarativeSettingsFormSchemaWithValues from IDeclarativeSettingsForm
* @psalm-import-type DeclarativeSettingsFormSchemaWithoutValues from IDeclarativeSettingsForm
*/
trait CommonSettingsTrait {

Expand Down Expand Up @@ -106,16 +109,26 @@ protected function formatAdminSections(string $currentType, string $currentSecti
}

/**
* @param array<int, list<\OCP\Settings\ISettings>> $settings
* @param list<\OCP\Settings\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];
Expand All @@ -125,34 +138,38 @@ private function formatSettings(array $settings): array {
* @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);

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"');
}

$this->declarativeSettingsManager->loadSchemas();

$templateParams = [];
$templateParams = array_merge($templateParams, $this->getNavigationParameters($type, $section));
$templateParams = array_merge($templateParams, $this->getSettings($section));

/** @psalm-suppress PossiblyNullArgument */
$declarativeFormIDs = $this->declarativeSettingsManager->getFormIDs($this->userSession->getUser(), $type, $section);
if (!empty($declarativeFormIDs)) {
foreach ($declarativeFormIDs as $app => $ids) {
/** @psalm-suppress PossiblyUndefinedArrayOffset */
$templateParams['content'] .= join(array_map(fn (string $id) => '<div id="' . $app . '_' . $id . '"></div>', $ids));
}
if (!empty($declarativeSettings)) {
Util::addScript(Application::APP_ID, 'declarative-settings-forms');
/** @psalm-suppress PossiblyNullArgument */
$this->initialState->provideInitialState('declarative-settings-forms', $this->declarativeSettingsManager->getFormsWithValues($this->userSession->getUser(), $type, $section));
$this->initialState->provideInitialState('declarative-settings-forms', $declarativeSettings);
}

$settings = array_merge(...$settings);
$templateParams = $this->formatSettings($settings, $declarativeSettings);
$templateParams = array_merge($templateParams, $this->getNavigationParameters($type, $section));

$activeSection = $this->settingsManager->getSection($type, $section);
if ($activeSection) {
$templateParams['pageTitle'] = $activeSection->getName();
Expand All @@ -162,6 +179,4 @@ private function getIndexResponse(string $type, string $section): TemplateRespon

return new TemplateResponse('settings', 'settings/frame', $templateParams);
}

abstract protected function getSettings($section);
}
15 changes: 4 additions & 11 deletions apps/settings/lib/Controller/PersonalSettingsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,16 +50,9 @@ public function __construct(
#[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);
return $formatted;
return $this->getIndexResponse(
'personal',
$section,
);
}
}
50 changes: 25 additions & 25 deletions apps/settings/tests/Controller/AdminSettingsControllerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
use OCP\INavigationManager;
use OCP\IRequest;
use OCP\IUser;
use OCP\IUserManager;
use OCP\IUserSession;
use OCP\Settings\IDeclarativeManager;
use OCP\Settings\IManager;
Expand All @@ -29,26 +30,17 @@
*/
class AdminSettingsControllerTest extends TestCase {

/** @var AdminSettingsController */
private $adminSettingsController;
/** @var IRequest|MockObject */
private $request;
/** @var INavigationManager|MockObject */
private $navigationManager;
/** @var IManager|MockObject */
private $settingsManager;
/** @var IUserSession|MockObject */
private $userSession;
/** @var IGroupManager|MockObject */
private $groupManager;
/** @var ISubAdmin|MockObject */
private $subAdmin;
/** @var IDeclarativeManager|MockObject */
private $declarativeSettingsManager;
/** @var IInitialState|MockObject */
private $initialState;
/** @var string */
private $adminUid = 'lololo';
private IRequest&MockObject $request;
private INavigationManager&MockObject $navigationManager;
private IManager&MockObject $settingsManager;
private IUserSession&MockObject $userSession;
private IGroupManager&MockObject $groupManager;
private ISubAdmin&MockObject $subAdmin;
private IDeclarativeManager&MockObject $declarativeSettingsManager;
private IInitialState&MockObject $initialState;

private string $adminUid = 'lololo';
private AdminSettingsController $adminSettingsController;

protected function setUp(): void {
parent::setUp();
Expand All @@ -74,15 +66,17 @@ protected function setUp(): void {
$this->initialState,
);

$user = \OC::$server->getUserManager()->createUser($this->adminUid, 'mylongrandompassword');
$user = \OCP\Server::get(IUserManager::class)->createUser($this->adminUid, 'mylongrandompassword');
\OC_User::setUserId($user->getUID());
\OC::$server->getGroupManager()->createGroup('admin')->addUser($user);
\OCP\Server::get(IGroupManager::class)->createGroup('admin')->addUser($user);
}

protected function tearDown(): void {
\OC::$server->getUserManager()->get($this->adminUid)->delete();
\OCP\Server::get(IUserManager::class)
->get($this->adminUid)
->delete();
\OC_User::setUserId(null);
\OC::$server->getUserSession()->setUser(null);
\OCP\Server::get(IUserSession::class)->setUser(null);

parent::tearDown();
}
Expand All @@ -101,6 +95,12 @@ public function testIndex(): void {
->method('isSubAdmin')
->with($user)
->willReturn(false);

$form = new TemplateResponse('settings', 'settings/empty');
$setting = $this->createMock(ServerDevNotice::class);
$setting->expects(self::any())
->method('getForm')
->willReturn($form);
$this->settingsManager
->expects($this->once())
->method('getAdminSections')
Expand All @@ -113,7 +113,7 @@ public function testIndex(): void {
->expects($this->once())
->method('getAllowedAdminSettings')
->with('test')
->willReturn([5 => $this->createMock(ServerDevNotice::class)]);
->willReturn([5 => [$setting]]);
$this->declarativeSettingsManager
->expects($this->any())
->method('getFormIDs')
Expand Down
2 changes: 1 addition & 1 deletion lib/public/Settings/IManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ public function getAdminSettings(string $section, bool $subAdminOnly = false): a
/**
* Returns a list of admin settings that the given user can use for the give section
*
* @return array<int, list<ISettings>> The array of admin settings there admin delegation is allowed.
* @return array<int, list<ISettings>> List of admin-settings the user has access to, with priority as key.
* @since 23.0.0
*/
public function getAllowedAdminSettings(string $section, IUser $user): array;
Expand Down

0 comments on commit a0b2297

Please sign in to comment.