Skip to content

Commit f1f007f

Browse files
authored
Merge pull request #543 from nextcloud/backport/542/stable31
[stable31] (fix): perfomance - load ExApps menu items only when event is triggered
2 parents fbda250 + 6d3432d commit f1f007f

File tree

4 files changed

+101
-62
lines changed

4 files changed

+101
-62
lines changed

lib/AppInfo/Application.php

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
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;
@@ -26,7 +27,6 @@
2627
use OCA\AppAPI\Service\ProvidersAI\TaskProcessingService;
2728
use OCA\AppAPI\Service\ProvidersAI\TextProcessingService;
2829
use OCA\AppAPI\Service\ProvidersAI\TranslationService;
29-
use OCA\AppAPI\Service\UI\TopMenuService;
3030
use OCA\DAV\Events\SabrePluginAuthInitEvent;
3131
use OCA\Files\Event\LoadAdditionalScriptsEvent;
3232
use OCP\AppFramework\App;
@@ -41,13 +41,13 @@
4141
use OCP\Files\Events\Node\NodeTouchedEvent;
4242
use OCP\Files\Events\Node\NodeWrittenEvent;
4343
use OCP\IConfig;
44+
use OCP\Navigation\Events\LoadAdditionalEntriesEvent;
4445
use OCP\SabrePluginEvent;
4546
use OCP\Settings\Events\DeclarativeSettingsGetValueEvent;
4647
use OCP\Settings\Events\DeclarativeSettingsRegisterFormEvent;
4748
use OCP\Settings\Events\DeclarativeSettingsSetValueEvent;
4849
use Psr\Container\ContainerExceptionInterface;
4950
use Psr\Container\NotFoundExceptionInterface;
50-
use Throwable;
5151

5252
class Application extends App implements IBootstrap {
5353
public const APP_ID = 'app_api';
@@ -64,6 +64,7 @@ public function __construct(array $urlParams = []) {
6464
* @psalm-suppress UndefinedClass
6565
*/
6666
public function register(IRegistrationContext $context): void {
67+
$context->registerEventListener(LoadAdditionalEntriesEvent::class, LoadMenuEntriesListener::class);
6768
$context->registerEventListener(LoadAdditionalScriptsEvent::class, LoadFilesPluginListener::class);
6869
$context->registerCapability(Capabilities::class);
6970
$context->registerCapability(PublicCapabilities::class);
@@ -109,10 +110,6 @@ public function register(IRegistrationContext $context): void {
109110
}
110111

111112
public function boot(IBootContext $context): void {
112-
try {
113-
$context->injectFn($this->registerExAppsMenuEntries(...));
114-
} catch (NotFoundExceptionInterface|ContainerExceptionInterface|Throwable) {
115-
}
116113
}
117114

118115
public function registerDavAuth(): void {
@@ -123,10 +120,4 @@ public function registerDavAuth(): void {
123120
$event->getServer()->addPlugin($container->query(DavPlugin::class));
124121
});
125122
}
126-
127-
private function registerExAppsMenuEntries(): void {
128-
$container = $this->getContainer();
129-
$menuEntryService = $container->get(TopMenuService::class);
130-
$menuEntryService->registerMenuEntries($container);
131-
}
132123
}
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: 18 additions & 0 deletions
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>

0 commit comments

Comments
 (0)