@@ -39,6 +39,7 @@ class NavigationManager implements INavigationManager {
3939 private ?array $ customAppOrder = null ;
4040 /** List of loaded app info */
4141 private array $ loadedAppInfo = [];
42+ private bool $ additionalEntriesLoaded = false ;
4243
4344 public function __construct (
4445 protected IAppManager $ appManager ,
@@ -206,35 +207,50 @@ private function init(): void {
206207 * Resolve the app navigation entries from closures and info.xml files.
207208 */
208209 private function resolveAppNavigationEntries (): void {
209- // Resolve app navigation closures
210- while ($ c = array_pop ($ this ->closureEntries )) {
211- $ this ->add ($ c ());
212- }
210+ $ this ->resolveAppInfoEntries ();
211+
212+ // we do not really know the current bootstrapping state
213+ // but we know that the files app is always enabled and loaded when "filesystem" is loaded thus the server is ready or close-to-ready.
214+ if ($ this ->appManager ->isAppLoaded ('files ' )) {
215+ // Resolve app navigation closures
216+ while ($ c = array_pop ($ this ->closureEntries )) {
217+ $ this ->add ($ c ());
218+ }
213219
214- // Resolve dynamically added navigation entries via event listeners
215- if ($ this ->loadedAppInfo === []) {
216- $ this ->eventDispatcher ->dispatchTyped (new LoadAdditionalEntriesEvent ());
220+ // Resolve dynamically added navigation entries via event listeners
221+ if (!$ this ->additionalEntriesLoaded ) {
222+ $ this ->additionalEntriesLoaded = true ;
223+ $ this ->eventDispatcher ->dispatchTyped (new LoadAdditionalEntriesEvent ());
224+ }
217225 }
226+ }
218227
219- // Resolve classic info.xml based navigation entries
228+ /**
229+ * Resolve classic info.xml based navigation entires
230+ */
231+ private function resolveAppInfoEntries (): void {
220232 if ($ this ->userSession ->isLoggedIn ()) {
221233 $ user = $ this ->userSession ->getUser ();
222234 $ apps = $ this ->appManager ->getEnabledAppsForUser ($ user );
223235 } else {
224236 $ apps = $ this ->appManager ->getEnabledApps ();
225237 }
226238
227- foreach ($ apps as $ app ) {
228- // skip already loaded apps
229- if (in_array ( $ app , $ this -> loadedAppInfo ) ) {
230- continue ;
231- }
239+ $ appsToLoad = array_diff ($ apps, $ this -> loadedAppInfo );
240+ $ appsToLoad = array_filter ( $ appsToLoad , $ this -> appManager -> isAppLoaded (...));
241+ if ($ appsToLoad === [] ) {
242+ return ;
243+ }
232244
245+ foreach ($ appsToLoad as $ app ) {
233246 // load plugins and collections from info.xml
234247 $ info = $ this ->appManager ->getAppInfo ($ app );
235248 if (!isset ($ info ['navigations ' ]['navigation ' ])) {
249+ // this app does not have any navigation entries, skip it
250+ $ this ->loadedAppInfo [] = $ app ;
236251 continue ;
237252 }
253+
238254 foreach ($ info ['navigations ' ]['navigation ' ] as $ key => $ nav ) {
239255 $ nav ['type ' ] = $ nav ['type ' ] ?? 'link ' ;
240256 if (!isset ($ nav ['name ' ])) {
@@ -250,8 +266,11 @@ private function resolveAppNavigationEntries(): void {
250266 }
251267 $ id = $ nav ['id ' ] ?? $ app . ($ key === 0 ? '' : $ key );
252268 $ order = $ nav ['order ' ] ?? 100 ;
253- $ type = $ nav ['type ' ];
254- $ route = !empty ($ nav ['route ' ]) ? $ this ->urlGenerator ->linkToRoute ($ nav ['route ' ]) : '' ;
269+ $ type = $ nav ['type ' ] ?? 'link ' ;
270+ $ route = $ nav ['route ' ] ?? '' ;
271+ if ($ route !== '' ) {
272+ $ route = $ this ->urlGenerator ->linkToRoute ($ route );
273+ }
255274 $ icon = $ nav ['icon ' ] ?? null ;
256275 if ($ icon !== null ) {
257276 try {
0 commit comments