Files
nextcloud-side_menu/lib/Service/AppRepository.php
T
dillard ccefefa6f7 fix(config): keep the category cache small and save settings at once
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.
2026-09-30 12:01:50 +02:00

137 lines
4.4 KiB
PHP

<?php
namespace OCA\SideMenu\Service;
use OC\User\User;
use OCA\SideMenu\AppInfo\Application;
use OCP\AppFramework\Http\Events\BeforeTemplateRenderedEvent;
use OCP\AppFramework\Http\TemplateResponse;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\INavigationManager;
use OCP\IUserSession;
use OCP\L10N\IFactory;
/**
* class AppRepository.
*
* @author Simon Vieille <simon@deblan.fr>
*/
class AppRepository
{
public function __construct(
protected \OC_App $ocApp,
protected INavigationManager $navigationManager,
protected IFactory $l10nFactory,
protected ConfigProxy $config,
protected CategoryRepository $categoryRepository,
protected IEventDispatcher $dispatcher,
protected IUserSession $userSession,
) {
$this->dispatcher->dispatchTyped(new BeforeTemplateRenderedEvent(
$this->userSession->isLoggedIn(),
new TemplateResponse(Application::APP_NAME, '')
));
}
/**
* Retrieves visibles apps.
*/
public function getVisibleApps(): array
{
$navigation = $this->navigationManager->getAll();
$appCategoriesCustom = $this->config->getAppValueArray('apps-categories-custom', '[]');
// Only needed to check custom categories, skip loading them otherwise
$categories = empty(array_filter($appCategoriesCustom)) ? [] : $this->categoryRepository->getOrderedCategories();
$apps = $this->ocApp->listAllApps();
$visibleApps = [];
foreach ($apps as $app) {
$id = $app['id'];
foreach ([$app['id'], $app['id'].'_index'] as $id) {
if (isset($navigation[$id])) {
$app['name'] = $this->getAppName($app);
$app['href'] = $navigation[$id]['href'];
$app['icon'] = $navigation[$id]['icon'];
$visibleApps[$id] = $app;
}
}
}
foreach ($navigation as $app) {
if ('external_index' === substr($app['id'], 0, 14)) {
$visibleApps[$app['id']] = [
'id' => $app['id'],
'name' => $this->getAppName($app),
'href' => $app['href'],
'icon' => $app['icon'],
'category' => [
'external_links',
],
];
} elseif ('tables_application' === substr($app['id'], 0, 18)) {
$visibleApps[$app['id']] = [
'id' => $app['id'],
'name' => $this->getAppName($app),
'href' => $app['href'],
'icon' => $app['icon'],
'category' => [],
];
} elseif ('files' === $app['id']) {
$visibleApps[$app['id']] = [
'id' => $app['id'],
'name' => $this->getAppName($app),
'href' => $app['href'],
'icon' => $app['icon'],
'category' => [],
];
}
}
foreach ($visibleApps as $id => $app) {
if (isset($appCategoriesCustom[$id], $categories[$appCategoriesCustom[$id]])) {
$visibleApps[$id]['category'] = [$appCategoriesCustom[$id]];
}
}
return $visibleApps;
}
public function getAppName($app): string
{
return $this->config->getAppValue(
'app.navigation.name',
$this->l10nFactory->get($app['id'])->t($app['name']),
$app['id']
);
}
public function getOrderedApps(?User $user = null): array
{
$apps = $this->getVisibleApps();
$orders = $this->config->getAppValueArray('apps-order', '[]');
if (null !== $user && !$this->config->getAppValueBool('force', '0')) {
$userOrders = $this->config->getUserValueArray($user, 'apps-order', '[]');
if (!empty($userOrders)) {
$orders = $userOrders;
}
}
usort($apps, function ($a, $b) use ($orders) {
$ak = array_keys($orders, $a['id'])[0] ?? null;
$bk = array_keys($orders, $b['id'])[0] ?? null;
if (null === $ak || null === $bk) {
return ($a['name'] < $b['name']) ? -1 : 1;
}
return $ak < $bk ? -1 : 1;
});
return $apps;
}
}