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.
159 lines
4.6 KiB
PHP
159 lines
4.6 KiB
PHP
<?php
|
|
|
|
namespace OCA\SideMenu\AppInfo;
|
|
|
|
use OC\AllConfig;
|
|
use OC\App\AppStore\Fetcher\CategoryFetcher;
|
|
use OC\AppFramework\Http\Request;
|
|
use OC\Security\CSP\ContentSecurityPolicyNonceManager;
|
|
use OC\Security\CSRF\CsrfTokenManager;
|
|
use OC\User\User;
|
|
use OCA\SideMenu\Service\AppRepository;
|
|
use OCA\SideMenu\Service\CategoryRepository;
|
|
use OCA\SideMenu\Service\Color;
|
|
use OCA\SideMenu\Service\ConfigProxy;
|
|
use OCA\Theming\ThemingDefaults;
|
|
use OCP\AppFramework\App;
|
|
use OCP\AppFramework\Bootstrap\IBootContext;
|
|
use OCP\AppFramework\Bootstrap\IBootstrap;
|
|
use OCP\AppFramework\Bootstrap\IRegistrationContext;
|
|
use OCP\AppFramework\Utility\ITimeFactory;
|
|
use OCP\EventDispatcher\IEventDispatcher;
|
|
use OCP\IAppConfig;
|
|
use OCP\IConfig;
|
|
use OCP\INavigationManager;
|
|
use OCP\IRequest;
|
|
use OCP\IURLGenerator;
|
|
use OCP\IUserSession;
|
|
use OCP\L10N\IFactory;
|
|
use OCP\Server;
|
|
use OCP\Util;
|
|
use Psr\Container\ContainerInterface;
|
|
|
|
/**
|
|
* class Application.
|
|
*
|
|
* @author Simon Vieille <simon@deblan.fr>
|
|
*/
|
|
class Application extends App implements IBootstrap
|
|
{
|
|
public const APP_ID = 'side_menu';
|
|
public const APP_NAME = 'Custom menu';
|
|
|
|
protected IConfig $config;
|
|
protected ContentSecurityPolicyNonceManager $cspnm;
|
|
protected IRequest $request;
|
|
protected ?User $user = null;
|
|
|
|
public function __construct(array $urlParams = [])
|
|
{
|
|
parent::__construct(self::APP_ID, $urlParams);
|
|
}
|
|
|
|
public function register(IRegistrationContext $context): void
|
|
{
|
|
$context->registerService(CategoryRepository::class, function (ContainerInterface $c) {
|
|
return new CategoryRepository(
|
|
$c->get(CategoryFetcher::class),
|
|
$c->get(ConfigProxy::class),
|
|
$c->get(IConfig::class),
|
|
$c->get(IFactory::class),
|
|
$c->get(IUserSession::class),
|
|
$c->get(IAppConfig::class),
|
|
$c->get(ITimeFactory::class)
|
|
);
|
|
});
|
|
|
|
$context->registerService(AppRepository::class, function (ContainerInterface $c) {
|
|
return new AppRepository(
|
|
$c->get(\OC_App::class),
|
|
$c->get(INavigationManager::class),
|
|
$c->get(IFactory::class),
|
|
$c->get(ConfigProxy::class),
|
|
$c->get(CategoryRepository::class),
|
|
$c->get(IEventDispatcher::class),
|
|
$c->get(IUserSession::class)
|
|
);
|
|
});
|
|
|
|
$context->registerService(ConfigProxy::class, function (ContainerInterface $c) {
|
|
return new ConfigProxy(
|
|
$c->get(IConfig::class),
|
|
);
|
|
});
|
|
|
|
$context->registerService(Color::class, function (ContainerInterface $c) {
|
|
return new Color(
|
|
$c->get(ThemingDefaults::class),
|
|
);
|
|
});
|
|
}
|
|
|
|
public function boot(IBootContext $context): void
|
|
{
|
|
$this->config = Server::get(IConfig::class);
|
|
$this->user = Server::get(IUserSession::class)->getUser();
|
|
$this->request = Server::get(IRequest::class);
|
|
|
|
if (!$this->isEnabled()) {
|
|
return;
|
|
}
|
|
|
|
$this->addAssets();
|
|
}
|
|
|
|
protected function isEnabled(): bool
|
|
{
|
|
if (isset($this->request->server['HTTP_USER_AGENT']) && preg_match('/MemoriesNative/', $this->request->server['HTTP_USER_AGENT'])) {
|
|
return false;
|
|
}
|
|
|
|
$enabled = true;
|
|
$isForced = (bool) $this->config->getAppValue(self::APP_ID, 'force', '0');
|
|
|
|
if (null !== $this->user && !$isForced) {
|
|
$enabled = (bool) $this->config->getUserValue(
|
|
$this->user->getUid(),
|
|
self::APP_ID,
|
|
'enabled',
|
|
$this->config->getAppValue(
|
|
self::APP_ID,
|
|
'default-enabled',
|
|
'1'
|
|
)
|
|
);
|
|
}
|
|
|
|
return $enabled;
|
|
}
|
|
|
|
protected function addAssets()
|
|
{
|
|
Util::addScript(self::APP_ID, 'side_menu-menu');
|
|
|
|
$assets = [
|
|
'stylesheet' => [
|
|
'route' => 'side_menu.Css.stylesheet',
|
|
'type' => 'link',
|
|
'route_attr' => 'href',
|
|
'attr' => [
|
|
'rel' => 'stylesheet',
|
|
],
|
|
],
|
|
];
|
|
|
|
$cache = $this->config->getAppValue(self::APP_ID, 'cache', '0');
|
|
|
|
foreach ($assets as $value) {
|
|
$route = Server::get(IURLGenerator::class)->linkToRoute(
|
|
$value['route'],
|
|
['v' => $cache]
|
|
);
|
|
|
|
$value['attr'][$value['route_attr']] = $route;
|
|
|
|
Util::addHeader($value['type'], $value['attr'], '');
|
|
}
|
|
}
|
|
}
|