ccefefa6f7
The app store category cache (cache-categories) held the name and description of every category in every language, about 170 KB, stored as a regular app config value. Nextcloud loads all regular values of all apps on every request, so this one value was read on every page load. It was only refreshed with the "remove cache" button. - Keep only the category ids and names, store the cache as a lazy value so it is only loaded when categories are needed, and refresh it after a week. - Only load the categories where they are used: the menus with categories, and the app list when custom categories are set. - Save all settings in one request to a new admin endpoint, which accepts only known settings, then clears the category cache and changes the stylesheet cache key. The settings page sent one request per setting and reloaded right after; with Nextcloud's short-lived local config cache, the reloaded page could show a half-saved configuration, so a change sometimes had to be made twice.
272 lines
9.1 KiB
PHP
272 lines
9.1 KiB
PHP
<?php
|
|
|
|
/**
|
|
* @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/>.
|
|
*/
|
|
|
|
namespace OCA\SideMenu\Controller;
|
|
|
|
use OCA\SideMenu\AppInfo\Application;
|
|
use OCA\SideMenu\Service\CategoryRepository;
|
|
use OCA\SideMenu\Service\Color;
|
|
use OCA\SideMenu\Service\ConfigProxy;
|
|
use OCA\SideMenu\Service\LangRepository;
|
|
use OCP\AppFramework\Controller;
|
|
use OCP\AppFramework\Http\Attribute\FrontpageRoute;
|
|
use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
|
|
use OCP\AppFramework\Http\Attribute\PasswordConfirmationRequired;
|
|
use OCP\AppFramework\Http\DataDownloadResponse;
|
|
use OCP\AppFramework\Http\JSONResponse;
|
|
use OCP\AppFramework\Http\RedirectResponse;
|
|
use OCP\IConfig;
|
|
use OCP\IRequest;
|
|
use OCP\IURLGenerator;
|
|
|
|
class AdminSettingController extends Controller
|
|
{
|
|
/**
|
|
* Keys that are managed by the app or by Nextcloud itself ("enabled")
|
|
* and must not be written from the settings form.
|
|
*/
|
|
protected const UNSAVED_KEYS = ['cache', 'cache-categories', 'enabled'];
|
|
|
|
public function __construct(
|
|
$appName,
|
|
IRequest $request,
|
|
protected IConfig $config,
|
|
protected ConfigProxy $configProxy,
|
|
protected IURLGenerator $urlGenerator,
|
|
protected Color $color,
|
|
protected LangRepository $langRepository,
|
|
protected CategoryRepository $categoryRepository,
|
|
) {
|
|
parent::__construct($appName, $request);
|
|
}
|
|
|
|
#[NoCSRFRequired]
|
|
#[FrontpageRoute(verb: 'GET', url: '/admin/cache/remove')]
|
|
public function removeCache(): RedirectResponse
|
|
{
|
|
$this->categoryRepository->clearCache();
|
|
|
|
return new RedirectResponse($this->urlGenerator->linkToRoute('settings.AdminSettings.index', [
|
|
'section' => Application::APP_ID,
|
|
]).'#more');
|
|
}
|
|
|
|
#[NoCSRFRequired]
|
|
#[FrontpageRoute(verb: 'GET', url: '/admin/config/export')]
|
|
public function exportConfiguration(): DataDownloadResponse
|
|
{
|
|
$keys = $this->config->getAppKeys(Application::APP_ID);
|
|
$appConfig = [];
|
|
$excludedKeys = [
|
|
'cache',
|
|
'cache-categories',
|
|
'langs',
|
|
];
|
|
|
|
foreach ($keys as $key) {
|
|
if (in_array($key, $excludedKeys)) {
|
|
continue;
|
|
}
|
|
|
|
$appConfig[$key] = $this->config->getAppValue(Application::APP_ID, $key);
|
|
}
|
|
|
|
return new DataDownloadResponse(
|
|
json_encode($appConfig, JSON_PRETTY_PRINT),
|
|
'config.json',
|
|
'text/json'
|
|
);
|
|
}
|
|
|
|
#[NoCSRFRequired]
|
|
#[FrontpageRoute(verb: 'GET', url: '/admin/config')]
|
|
public function configuration(): JSONResponse
|
|
{
|
|
$keys = $this->config->getAppKeys(Application::APP_ID);
|
|
$booleans = [
|
|
'opener-only',
|
|
'opener-hover',
|
|
'display-logo',
|
|
'use-avatar',
|
|
'add-logo-link',
|
|
'show-settings',
|
|
'show-search-form',
|
|
'loader-enabled',
|
|
'always-displayed',
|
|
'enabled',
|
|
'force',
|
|
'big-menu',
|
|
'external-sites-in-top-menu',
|
|
'force-light-icon',
|
|
'side-with-categories',
|
|
'default-enabled',
|
|
];
|
|
|
|
$arrays = [
|
|
'apps-categories-custom',
|
|
'big-menu-hidden-apps',
|
|
'apps-order',
|
|
'categories-custom',
|
|
'categories-order',
|
|
'target-blank-apps',
|
|
'top-menu-apps',
|
|
'top-side-menu-apps',
|
|
];
|
|
|
|
$integers = [
|
|
'background-color-opacity',
|
|
'dark-mode-background-color-opacity',
|
|
'dark-mode-icon-invert-filter',
|
|
'dark-mode-icon-opacity',
|
|
'icon-invert-filter',
|
|
'icon-opacity',
|
|
'target-blank-mode',
|
|
'top-menu-mouse-over-hidden-label',
|
|
];
|
|
|
|
$defaults = $this->getDefaults();
|
|
|
|
$config = [];
|
|
|
|
foreach ($keys as $key) {
|
|
if (!isset($defaults[$key])) {
|
|
continue;
|
|
}
|
|
|
|
if (in_array($key, $booleans)) {
|
|
$config[$key] = $this->configProxy->getAppValueBool($key, $defaults[$key]);
|
|
} elseif (in_array($key, $arrays)) {
|
|
$config[$key] = $this->configProxy->getAppValueArray($key, $defaults[$key]);
|
|
} elseif (in_array($key, $integers)) {
|
|
$config[$key] = $this->configProxy->getAppValueInt($key, $defaults[$key]);
|
|
} else {
|
|
$config[$key] = $this->configProxy->getAppValue($key, $defaults[$key]);
|
|
}
|
|
}
|
|
|
|
foreach ($defaults as $key => $default) {
|
|
if (array_key_exists($key, $config)) {
|
|
continue;
|
|
}
|
|
|
|
if (in_array($key, $booleans)) {
|
|
$config[$key] = $default === '1';
|
|
} elseif (in_array($key, $arrays)) {
|
|
$config[$key] = json_decode($default);
|
|
} elseif (in_array($key, $integers)) {
|
|
$config[$key] = (int) $default;
|
|
} else {
|
|
$config[$key] = $default;
|
|
}
|
|
}
|
|
|
|
$config['langs'] = $this->langRepository->getUsedLangs();
|
|
|
|
return new JSONResponse($config);
|
|
}
|
|
|
|
#[PasswordConfirmationRequired]
|
|
#[FrontpageRoute(verb: 'POST', url: '/admin/config')]
|
|
public function saveConfiguration(array $config): JSONResponse
|
|
{
|
|
$allowed = array_diff(array_keys($this->getDefaults()), self::UNSAVED_KEYS);
|
|
|
|
foreach ($config as $key => $value) {
|
|
if (!in_array($key, $allowed, true)) {
|
|
continue;
|
|
}
|
|
|
|
if (is_array($value)) {
|
|
$value = json_encode($value);
|
|
} elseif (is_bool($value)) {
|
|
$value = $value ? '1' : '0';
|
|
}
|
|
|
|
$this->config->setAppValue(Application::APP_ID, $key, (string) $value);
|
|
}
|
|
|
|
// New stylesheet URL, and category names that match the new settings
|
|
$this->config->setAppValue(Application::APP_ID, 'cache', (string) time());
|
|
$this->categoryRepository->clearCache();
|
|
|
|
return new JSONResponse(['status' => 'ok']);
|
|
}
|
|
|
|
protected function getDefaults(): array
|
|
{
|
|
return [
|
|
'opener-only' => '0',
|
|
'opener-hover' => '0',
|
|
'display-logo' => '1',
|
|
'use-avatar' => '0',
|
|
'add-logo-link' => '1',
|
|
'show-settings' => '0',
|
|
'show-search-form' => '1',
|
|
'loader-enabled' => '1',
|
|
'always-displayed' => '0',
|
|
'enabled' => '1',
|
|
'force' => '0',
|
|
'big-menu' => '0',
|
|
'external-sites-in-top-menu' => '0',
|
|
'force-light-icon' => '0',
|
|
'side-with-categories' => '0',
|
|
'cache' => '1',
|
|
'default-enabled' => '1',
|
|
|
|
'apps-categories-custom' => '[]',
|
|
'big-menu-hidden-apps' => '[]',
|
|
'apps-order' => '[]',
|
|
'categories-custom' => '[]',
|
|
'categories-order' => '[]',
|
|
'target-blank-apps' => '[]',
|
|
'top-menu-apps' => '[]',
|
|
'top-side-menu-apps' => '[]',
|
|
'cache-categories' => '[]',
|
|
|
|
'background-color-opacity' => '100',
|
|
'dark-mode-background-color-opacity' => '100',
|
|
'dark-mode-icon-invert-filter' => '0',
|
|
'dark-mode-icon-opacity' => '100',
|
|
'icon-invert-filter' => '0',
|
|
'icon-opacity' => '100',
|
|
'top-menu-mouse-over-hidden-label' => '0',
|
|
|
|
'opener' => 'side-menu-opener',
|
|
'dark-mode-opener' => 'side-menu-opener',
|
|
'size-icon' => 'normal',
|
|
'size-text' => 'normal',
|
|
'opener-position' => 'before',
|
|
|
|
'background-color' => $this->color->getPrimaryColor(),
|
|
'background-color-to' => $this->color->getLightenPrimaryColor(),
|
|
'current-app-background-color' => $this->color->getDarkenPrimaryColor(),
|
|
'text-color' => $this->color->getTextColorPrimary(),
|
|
'loader-color' => $this->color->getLightenPrimaryColor(),
|
|
|
|
'dark-mode-background-color' => $this->color->getDarkenPrimaryColor(),
|
|
'dark-mode-background-color-to' => $this->color->getDarkenPrimaryColor(),
|
|
'dark-mode-current-app-background-color' => $this->color->getDarkenPrimaryColor2(),
|
|
'dark-mode-text-color' => $this->color->getTextColorPrimary(),
|
|
'dark-mode-loader-color' => $this->color->getLightenPrimaryColor(),
|
|
|
|
'categories-order-type' => 'default',
|
|
];
|
|
}
|
|
}
|