@@ -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,30 +207,41 @@ 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 ());
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+ }
219+
220+ // Resolve dynamically added navigation entries via event listeners
221+ if (!$ this ->additionalEntriesLoaded ) {
222+ $ this ->eventDispatcher ->dispatchTyped (new LoadAdditionalEntriesEvent ());
223+ $ this ->additionalEntriesLoaded = true ;
224+ }
212225 }
226+ }
213227
214- // Resolve classic info.xml based navigation entries
228+ /**
229+ * Resolve classic info.xml based navigation entires
230+ */
231+ private function resolveAppInfoEntries (): bool {
215232 if ($ this ->userSession ->isLoggedIn ()) {
216233 $ user = $ this ->userSession ->getUser ();
217234 $ apps = $ this ->appManager ->getEnabledAppsForUser ($ user );
218235 } else {
219236 $ apps = $ this ->appManager ->getEnabledApps ();
220237 }
221238
222- if ( count ( $ apps ) === count ($ this ->loadedAppInfo )) {
223- // All apps that are loaded for the user have already been resolved
224- return ;
239+ $ appsToLoad = array_diff ($ this ->loadedAppInfo , $ apps );
240+ if ( $ appsToLoad === []) {
241+ return false ;
225242 }
226243
227- foreach ($ apps as $ app ) {
228- if (in_array ($ app , $ this ->loadedAppInfo )) {
229- // the apps navigations were already resolved
230- continue ;
231- }
232-
244+ foreach ($ appsToLoad as $ app ) {
233245 // load plugins and collections from info.xml
234246 $ info = $ this ->appManager ->getAppInfo ($ app );
235247 if (!isset ($ info ['navigations ' ]['navigation ' ])) {
@@ -302,9 +314,7 @@ private function resolveAppNavigationEntries(): void {
302314 ));
303315 }
304316 }
305-
306- // Resolve dynamically added navigation entries via event listeners
307- $ this ->eventDispatcher ->dispatchTyped (new LoadAdditionalEntriesEvent ());
317+ return true ;
308318 }
309319
310320 private function isAdmin (): bool {
0 commit comments