Skip to content

Commit d505473

Browse files
committed
(fix): performance improvement - load ExApps menu items only when an event is triggered
Signed-off-by: Oleksander Piskun <oleksandr2088@icloud.com>
1 parent cfd5acf commit d505473

File tree

4 files changed

+102
-63
lines changed

4 files changed

+102
-63
lines changed

lib/AppInfo/Application.php

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,14 @@
1616
use OCA\AppAPI\Listener\DeclarativeSettings\SetValueListener;
1717
use OCA\AppAPI\Listener\FileEventsListener;
1818
use OCA\AppAPI\Listener\LoadFilesPluginListener;
19+
use OCA\AppAPI\Listener\LoadMenuEntriesListener;
1920
use OCA\AppAPI\Listener\SabrePluginAuthInitListener;
2021
use OCA\AppAPI\Middleware\AppAPIAuthMiddleware;
2122
use OCA\AppAPI\Middleware\ExAppUIL10NMiddleware;
2223
use OCA\AppAPI\Middleware\ExAppUiMiddleware;
2324
use OCA\AppAPI\Notifications\ExAppNotifier;
2425
use OCA\AppAPI\PublicCapabilities;
2526
use OCA\AppAPI\Service\ProvidersAI\TaskProcessingService;
26-
use OCA\AppAPI\Service\UI\TopMenuService;
2727
use OCA\AppAPI\SetupChecks\DaemonCheck;
2828
use OCA\DAV\Events\SabrePluginAuthInitEvent;
2929
use OCA\Files\Event\LoadAdditionalScriptsEvent;
@@ -38,13 +38,13 @@
3838
use OCP\Files\Events\Node\NodeRenamedEvent;
3939
use OCP\Files\Events\Node\NodeTouchedEvent;
4040
use OCP\Files\Events\Node\NodeWrittenEvent;
41+
use OCP\Navigation\Events\LoadAdditionalEntriesEvent;
4142
use OCP\SabrePluginEvent;
4243
use OCP\Settings\Events\DeclarativeSettingsGetValueEvent;
4344
use OCP\Settings\Events\DeclarativeSettingsRegisterFormEvent;
4445
use OCP\Settings\Events\DeclarativeSettingsSetValueEvent;
4546
use Psr\Container\ContainerExceptionInterface;
4647
use Psr\Container\NotFoundExceptionInterface;
47-
use Throwable;
4848

4949
class Application extends App implements IBootstrap {
5050
public const APP_ID = 'app_api';
@@ -61,6 +61,7 @@ public function __construct(array $urlParams = []) {
6161
* @psalm-suppress UndefinedClass
6262
*/
6363
public function register(IRegistrationContext $context): void {
64+
$context->registerEventListener(LoadAdditionalEntriesEvent::class, LoadMenuEntriesListener::class);
6465
$context->registerEventListener(LoadAdditionalScriptsEvent::class, LoadFilesPluginListener::class);
6566
$context->registerCapability(Capabilities::class);
6667
$context->registerCapability(PublicCapabilities::class);
@@ -93,10 +94,6 @@ public function register(IRegistrationContext $context): void {
9394
}
9495

9596
public function boot(IBootContext $context): void {
96-
try {
97-
$context->injectFn($this->registerExAppsMenuEntries(...));
98-
} catch (NotFoundExceptionInterface|ContainerExceptionInterface|Throwable) {
99-
}
10097
}
10198

10299
public function registerDavAuth(): void {
@@ -107,10 +104,4 @@ public function registerDavAuth(): void {
107104
$event->getServer()->addPlugin($container->query(DavPlugin::class));
108105
});
109106
}
110-
111-
private function registerExAppsMenuEntries(): void {
112-
$container = $this->getContainer();
113-
$menuEntryService = $container->get(TopMenuService::class);
114-
$menuEntryService->registerMenuEntries($container);
115-
}
116107
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
7+
* SPDX-License-Identifier: AGPL-3.0-or-later
8+
*/
9+
10+
namespace OCA\AppAPI\Listener;
11+
12+
use OCA\AppAPI\AppInfo\Application;
13+
use OCA\AppAPI\Service\UI\TopMenuService;
14+
use OCP\EventDispatcher\Event;
15+
use OCP\EventDispatcher\IEventListener;
16+
use OCP\IGroupManager;
17+
use OCP\INavigationManager;
18+
use OCP\IURLGenerator;
19+
use OCP\IUserSession;
20+
21+
use OCP\L10N\IFactory;
22+
use OCP\Navigation\Events\LoadAdditionalEntriesEvent;
23+
use OCP\Server;
24+
25+
/**
26+
* @template-extends IEventListener<LoadMenuEntriesListener>
27+
*/
28+
class LoadMenuEntriesListener implements IEventListener {
29+
30+
public function __construct(
31+
private readonly TopMenuService $topMenuService,
32+
) {
33+
}
34+
35+
public function handle(Event $event): void {
36+
if (!$event instanceof LoadAdditionalEntriesEvent) {
37+
return;
38+
}
39+
40+
$menuEntries = $this->topMenuService->getExAppMenuEntries();
41+
if (empty($menuEntries)) {
42+
return;
43+
}
44+
45+
$user = Server::get(IUserSession::class)->getUser();
46+
if (!$user) {
47+
return;
48+
}
49+
$isUserAdmin = Server::get(IGroupManager::class)->isAdmin($user->getUID());
50+
51+
/** @var INavigationManager $navigationManager */
52+
$navigationManager = Server::get(INavigationManager::class);
53+
54+
foreach ($menuEntries as $menuEntry) {
55+
if ($menuEntry->getAdminRequired() === 1 && !$isUserAdmin) {
56+
continue; // Skip this entry if the user is not an admin and the entry requires admin privileges
57+
}
58+
$navigationManager->add(static function () use ($menuEntry) {
59+
$appId = $menuEntry->getAppid();
60+
$entryName = $menuEntry->getName();
61+
$icon = $menuEntry->getIcon();
62+
$urlGenerator = Server::get(IURLGenerator::class);
63+
return [
64+
'id' => Application::APP_ID . '_' . $appId . '_' . $entryName,
65+
'type' => 'link',
66+
'app' => Application::APP_ID,
67+
'href' => $urlGenerator->linkToRoute(
68+
'app_api.TopMenu.viewExAppPage', ['appId' => $appId, 'name' => $entryName]
69+
),
70+
'icon' => $icon === '' ?
71+
$urlGenerator->imagePath('app_api', 'app.svg') :
72+
$urlGenerator->linkToRoute(
73+
'app_api.ExAppProxy.ExAppGet', ['appId' => $appId, 'other' => $icon]
74+
),
75+
'name' => Server::get(IFactory::class)->get($appId)->t($menuEntry->getDisplayName()),
76+
];
77+
});
78+
}
79+
}
80+
}

lib/Service/UI/TopMenuService.php

Lines changed: 0 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,6 @@
1717
use OCP\DB\Exception;
1818
use OCP\ICache;
1919
use OCP\ICacheFactory;
20-
use OCP\IGroupManager;
21-
use OCP\INavigationManager;
22-
use OCP\IURLGenerator;
23-
use OCP\IUser;
24-
use OCP\IUserSession;
25-
use OCP\L10N\IFactory;
26-
use Psr\Container\ContainerExceptionInterface;
27-
use Psr\Container\ContainerInterface;
28-
use Psr\Container\NotFoundExceptionInterface;
2920
use Psr\Log\LoggerInterface;
3021

3122
class TopMenuService {
@@ -44,47 +35,6 @@ public function __construct(
4435
}
4536
}
4637

47-
/**
48-
* @throws NotFoundExceptionInterface
49-
* @throws ContainerExceptionInterface
50-
* @throws Exception
51-
*/
52-
public function registerMenuEntries(ContainerInterface $container): void {
53-
/** @var TopMenu $menuEntry */
54-
foreach ($this->getExAppMenuEntries() as $menuEntry) {
55-
$userSession = $container->get(IUserSession::class);
56-
/** @var IGroupManager $groupManager */
57-
$groupManager = $container->get(IGroupManager::class);
58-
/** @var IUser $user */
59-
$user = $userSession->getUser();
60-
if ($menuEntry->getAdminRequired() === 1 && !$groupManager->isAdmin($user->getUID())) {
61-
continue; // Skip this entry if user is not admin and entry requires admin privileges
62-
}
63-
$container->get(INavigationManager::class)->add(function () use ($container, $menuEntry) {
64-
$urlGenerator = $container->get(IURLGenerator::class);
65-
/** @var IFactory $l10nFactory */
66-
$l10nFactory = $container->get(IFactory::class);
67-
$appId = $menuEntry->getAppid();
68-
$entryName = $menuEntry->getName();
69-
$icon = $menuEntry->getIcon();
70-
return [
71-
'id' => Application::APP_ID . '_' . $appId . '_' . $entryName,
72-
'type' => 'link',
73-
'app' => Application::APP_ID,
74-
'href' => $urlGenerator->linkToRoute(
75-
'app_api.TopMenu.viewExAppPage', ['appId' => $appId, 'name' => $entryName]
76-
),
77-
'icon' => $icon === '' ?
78-
$urlGenerator->imagePath('app_api', 'app.svg') :
79-
$urlGenerator->linkToRoute(
80-
'app_api.ExAppProxy.ExAppGet', ['appId' => $appId, 'other' => $icon]
81-
),
82-
'name' => $l10nFactory->get($appId)->t($menuEntry->getDisplayName()),
83-
];
84-
});
85-
}
86-
}
87-
8838
public function registerExAppMenuEntry(string $appId, string $name, string $displayName,
8939
string $icon, int $adminRequired): ?TopMenu {
9040
$menuEntry = $this->getExAppMenuEntry($appId, $name);

tests/psalm-baseline.xml

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
<file src="lib/AppInfo/Application.php">
88
<InvalidArgument>
99
<code><![CDATA[LoadFilesPluginListener::class]]></code>
10+
<code><![CDATA[LoadMenuEntriesListener::class]]></code>
1011
<code><![CDATA[SabrePluginAuthInitListener::class]]></code>
1112
</InvalidArgument>
1213
<MissingDependency>
@@ -97,6 +98,23 @@
9798
<code><![CDATA[LoadAdditionalScriptsEvent]]></code>
9899
</UndefinedClass>
99100
</file>
101+
<file src="lib/Listener/LoadMenuEntriesListener.php">
102+
<ImplementedParamTypeMismatch>
103+
<code><![CDATA[$event]]></code>
104+
</ImplementedParamTypeMismatch>
105+
<InvalidDocblock>
106+
<code><![CDATA[class LoadMenuEntriesListener implements IEventListener {]]></code>
107+
</InvalidDocblock>
108+
<InvalidTemplateParam>
109+
<code><![CDATA[IEventListener]]></code>
110+
</InvalidTemplateParam>
111+
<MissingTemplateParam>
112+
<code><![CDATA[IEventListener]]></code>
113+
</MissingTemplateParam>
114+
<UndefinedClass>
115+
<code><![CDATA[LoadAdditionalEntriesEvent]]></code>
116+
</UndefinedClass>
117+
</file>
100118
<file src="lib/Listener/SabrePluginAuthInitListener.php">
101119
<ImplementedParamTypeMismatch>
102120
<code><![CDATA[$event]]></code>
@@ -123,9 +141,9 @@
123141
</file>
124142
<file src="lib/Service/HarpService.php">
125143
<UndefinedClass>
126-
<code><![CDATA[ClientException]]></code>
127144
<code><![CDATA[$e]]></code>
128145
<code><![CDATA[$e]]></code>
146+
<code><![CDATA[ClientException]]></code>
129147
</UndefinedClass>
130148
</file>
131149
</files>

0 commit comments

Comments
 (0)