Passed
Push — master ( 039ecb...847ee4 )
by Roeland
16:06 queued 11s
created
lib/private/AppFramework/Bootstrap/RegistrationContext.php 1 patch
Indentation   +409 added lines, -409 removed lines patch added patch discarded remove patch
@@ -49,413 +49,413 @@
 block discarded – undo
49 49
 
50 50
 class RegistrationContext {
51 51
 
52
-	/** @var ServiceRegistration<ICapability>[] */
53
-	private $capabilities = [];
54
-
55
-	/** @var ServiceRegistration<IReporter>[] */
56
-	private $crashReporters = [];
57
-
58
-	/** @var ServiceRegistration<IWidget>[] */
59
-	private $dashboardPanels = [];
60
-
61
-	/** @var ServiceFactoryRegistration[] */
62
-	private $services = [];
63
-
64
-	/** @var ServiceAliasRegistration[] */
65
-	private $aliases = [];
66
-
67
-	/** @var ParameterRegistration[] */
68
-	private $parameters = [];
69
-
70
-	/** @var EventListenerRegistration[] */
71
-	private $eventListeners = [];
72
-
73
-	/** @var ServiceRegistration<Middleware>[] */
74
-	private $middlewares = [];
75
-
76
-	/** @var ServiceRegistration<IProvider>[] */
77
-	private $searchProviders = [];
78
-
79
-	/** @var ServiceRegistration<IAlternativeLogin>[] */
80
-	private $alternativeLogins = [];
81
-
82
-	/** @var array[] */
83
-	private $initialStates = [];
84
-
85
-	/** @var ServiceRegistration<IHandler>[] */
86
-	private $wellKnownHandlers = [];
87
-
88
-	/** @var ServiceRegistration<ICustomTemplateProvider>[] */
89
-	private $templateProviders = [];
90
-
91
-	/** @var ILogger */
92
-	private $logger;
93
-
94
-	public function __construct(ILogger $logger) {
95
-		$this->logger = $logger;
96
-	}
97
-
98
-	public function for(string $appId): IRegistrationContext {
99
-		return new class($appId, $this) implements IRegistrationContext {
100
-			/** @var string */
101
-			private $appId;
102
-
103
-			/** @var RegistrationContext */
104
-			private $context;
105
-
106
-			public function __construct(string $appId, RegistrationContext $context) {
107
-				$this->appId = $appId;
108
-				$this->context = $context;
109
-			}
110
-
111
-			public function registerCapability(string $capability): void {
112
-				$this->context->registerCapability(
113
-					$this->appId,
114
-					$capability
115
-				);
116
-			}
117
-
118
-			public function registerCrashReporter(string $reporterClass): void {
119
-				$this->context->registerCrashReporter(
120
-					$this->appId,
121
-					$reporterClass
122
-				);
123
-			}
124
-
125
-			public function registerDashboardWidget(string $widgetClass): void {
126
-				$this->context->registerDashboardPanel(
127
-					$this->appId,
128
-					$widgetClass
129
-				);
130
-			}
131
-
132
-			public function registerService(string $name, callable $factory, bool $shared = true): void {
133
-				$this->context->registerService(
134
-					$this->appId,
135
-					$name,
136
-					$factory,
137
-					$shared
138
-				);
139
-			}
140
-
141
-			public function registerServiceAlias(string $alias, string $target): void {
142
-				$this->context->registerServiceAlias(
143
-					$this->appId,
144
-					$alias,
145
-					$target
146
-				);
147
-			}
148
-
149
-			public function registerParameter(string $name, $value): void {
150
-				$this->context->registerParameter(
151
-					$this->appId,
152
-					$name,
153
-					$value
154
-				);
155
-			}
156
-
157
-			public function registerEventListener(string $event, string $listener, int $priority = 0): void {
158
-				$this->context->registerEventListener(
159
-					$this->appId,
160
-					$event,
161
-					$listener,
162
-					$priority
163
-				);
164
-			}
165
-
166
-			public function registerMiddleware(string $class): void {
167
-				$this->context->registerMiddleware(
168
-					$this->appId,
169
-					$class
170
-				);
171
-			}
172
-
173
-			public function registerSearchProvider(string $class): void {
174
-				$this->context->registerSearchProvider(
175
-					$this->appId,
176
-					$class
177
-				);
178
-			}
179
-
180
-			public function registerAlternativeLogin(string $class): void {
181
-				$this->context->registerAlternativeLogin(
182
-					$this->appId,
183
-					$class
184
-				);
185
-			}
186
-
187
-			public function registerInitialStateProvider(string $class): void {
188
-				$this->context->registerInitialState(
189
-					$this->appId,
190
-					$class
191
-				);
192
-			}
193
-
194
-			public function registerWellKnownHandler(string $class): void {
195
-				$this->context->registerWellKnown(
196
-					$this->appId,
197
-					$class
198
-				);
199
-			}
200
-
201
-			public function registerTemplateProvider(string $providerClass): void {
202
-				$this->context->registerTemplateProvider(
203
-					$this->appId,
204
-					$providerClass
205
-				);
206
-			}
207
-		};
208
-	}
209
-
210
-	/**
211
-	 * @psalm-param class-string<ICapability> $capability
212
-	 */
213
-	public function registerCapability(string $appId, string $capability): void {
214
-		$this->capabilities[] = new ServiceRegistration($appId, $capability);
215
-	}
216
-
217
-	/**
218
-	 * @psalm-param class-string<IReporter> $capability
219
-	 */
220
-	public function registerCrashReporter(string $appId, string $reporterClass): void {
221
-		$this->crashReporters[] = new ServiceRegistration($appId, $reporterClass);
222
-	}
223
-
224
-	/**
225
-	 * @psalm-param class-string<IWidget> $capability
226
-	 */
227
-	public function registerDashboardPanel(string $appId, string $panelClass): void {
228
-		$this->dashboardPanels[] = new ServiceRegistration($appId, $panelClass);
229
-	}
230
-
231
-	public function registerService(string $appId, string $name, callable $factory, bool $shared = true): void {
232
-		$this->services[] = new ServiceFactoryRegistration($appId, $name, $factory, $shared);
233
-	}
234
-
235
-	public function registerServiceAlias(string $appId, string $alias, string $target): void {
236
-		$this->aliases[] = new ServiceAliasRegistration($appId, $alias, $target);
237
-	}
238
-
239
-	public function registerParameter(string $appId, string $name, $value): void {
240
-		$this->parameters[] = new ParameterRegistration($appId, $name, $value);
241
-	}
242
-
243
-	public function registerEventListener(string $appId, string $event, string $listener, int $priority = 0): void {
244
-		$this->eventListeners[] = new EventListenerRegistration($appId, $event, $listener, $priority);
245
-	}
246
-
247
-	/**
248
-	 * @psalm-param class-string<Middleware> $class
249
-	 */
250
-	public function registerMiddleware(string $appId, string $class): void {
251
-		$this->middlewares[] = new ServiceRegistration($appId, $class);
252
-	}
253
-
254
-	public function registerSearchProvider(string $appId, string $class) {
255
-		$this->searchProviders[] = new ServiceRegistration($appId, $class);
256
-	}
257
-
258
-	public function registerAlternativeLogin(string $appId, string $class): void {
259
-		$this->alternativeLogins[] = new ServiceRegistration($appId, $class);
260
-	}
261
-
262
-	public function registerInitialState(string $appId, string $class): void {
263
-		$this->initialStates[] = [
264
-			'appId' => $appId,
265
-			'class' => $class,
266
-		];
267
-	}
268
-
269
-	public function registerWellKnown(string $appId, string $class): void {
270
-		$this->wellKnownHandlers[] = new ServiceRegistration($appId, $class);
271
-	}
272
-
273
-	public function registerTemplateProvider(string $appId, string $class): void {
274
-		$this->templateProviders[] = new ServiceRegistration($appId, $class);
275
-	}
276
-
277
-	/**
278
-	 * @param App[] $apps
279
-	 */
280
-	public function delegateCapabilityRegistrations(array $apps): void {
281
-		while (($registration = array_shift($this->capabilities)) !== null) {
282
-			try {
283
-				$apps[$registration->getAppId()]
284
-					->getContainer()
285
-					->registerCapability($registration->getService());
286
-			} catch (Throwable $e) {
287
-				$appId = $registration->getAppId();
288
-				$this->logger->logException($e, [
289
-					'message' => "Error during capability registration of $appId: " . $e->getMessage(),
290
-					'level' => ILogger::ERROR,
291
-				]);
292
-			}
293
-		}
294
-	}
295
-
296
-	/**
297
-	 * @param App[] $apps
298
-	 */
299
-	public function delegateCrashReporterRegistrations(array $apps, Registry $registry): void {
300
-		while (($registration = array_shift($this->crashReporters)) !== null) {
301
-			try {
302
-				$registry->registerLazy($registration->getService());
303
-			} catch (Throwable $e) {
304
-				$appId = $registration->getAppId();
305
-				$this->logger->logException($e, [
306
-					'message' => "Error during crash reporter registration of $appId: " . $e->getMessage(),
307
-					'level' => ILogger::ERROR,
308
-				]);
309
-			}
310
-		}
311
-	}
312
-
313
-	/**
314
-	 * @param App[] $apps
315
-	 */
316
-	public function delegateDashboardPanelRegistrations(array $apps, IManager $dashboardManager): void {
317
-		while (($panel = array_shift($this->dashboardPanels)) !== null) {
318
-			try {
319
-				$dashboardManager->lazyRegisterWidget($panel->getService());
320
-			} catch (Throwable $e) {
321
-				$appId = $panel->getAppId();
322
-				$this->logger->logException($e, [
323
-					'message' => "Error during dashboard registration of $appId: " . $e->getMessage(),
324
-					'level' => ILogger::ERROR,
325
-				]);
326
-			}
327
-		}
328
-	}
329
-
330
-	public function delegateEventListenerRegistrations(IEventDispatcher $eventDispatcher): void {
331
-		while (($registration = array_shift($this->eventListeners)) !== null) {
332
-			try {
333
-				$eventDispatcher->addServiceListener(
334
-					$registration->getEvent(),
335
-					$registration->getService(),
336
-					$registration->getPriority()
337
-				);
338
-			} catch (Throwable $e) {
339
-				$appId = $registration->getAppId();
340
-				$this->logger->logException($e, [
341
-					'message' => "Error during event listener registration of $appId: " . $e->getMessage(),
342
-					'level' => ILogger::ERROR,
343
-				]);
344
-			}
345
-		}
346
-	}
347
-
348
-	/**
349
-	 * @param App[] $apps
350
-	 */
351
-	public function delegateContainerRegistrations(array $apps): void {
352
-		while (($registration = array_shift($this->services)) !== null) {
353
-			try {
354
-				/**
355
-				 * Register the service and convert the callable into a \Closure if necessary
356
-				 */
357
-				$apps[$registration->getAppId()]
358
-					->getContainer()
359
-					->registerService(
360
-						$registration->getName(),
361
-						Closure::fromCallable($registration->getFactory()),
362
-						$registration->isShared()
363
-					);
364
-			} catch (Throwable $e) {
365
-				$appId = $registration->getAppId();
366
-				$this->logger->logException($e, [
367
-					'message' => "Error during service registration of $appId: " . $e->getMessage(),
368
-					'level' => ILogger::ERROR,
369
-				]);
370
-			}
371
-		}
372
-
373
-		foreach ($this->aliases as $registration) {
374
-			try {
375
-				$apps[$registration->getAppId()]
376
-					->getContainer()
377
-					->registerAlias(
378
-						$registration->getAlias(),
379
-						$registration->getTarget()
380
-					);
381
-			} catch (Throwable $e) {
382
-				$appId = $registration->getAppId();
383
-				$this->logger->logException($e, [
384
-					'message' => "Error during service alias registration of $appId: " . $e->getMessage(),
385
-					'level' => ILogger::ERROR,
386
-				]);
387
-			}
388
-		}
389
-
390
-		foreach ($this->parameters as $registration) {
391
-			try {
392
-				$apps[$registration->getAppId()]
393
-					->getContainer()
394
-					->registerParameter(
395
-						$registration->getName(),
396
-						$registration->getValue()
397
-					);
398
-			} catch (Throwable $e) {
399
-				$appId = $registration->getAppId();
400
-				$this->logger->logException($e, [
401
-					'message' => "Error during service alias registration of $appId: " . $e->getMessage(),
402
-					'level' => ILogger::ERROR,
403
-				]);
404
-			}
405
-		}
406
-	}
407
-
408
-	/**
409
-	 * @param App[] $apps
410
-	 */
411
-	public function delegateMiddlewareRegistrations(array $apps): void {
412
-		while (($middleware = array_shift($this->middlewares)) !== null) {
413
-			try {
414
-				$apps[$middleware->getAppId()]
415
-					->getContainer()
416
-					->registerMiddleWare($middleware->getService());
417
-			} catch (Throwable $e) {
418
-				$appId = $middleware->getAppId();
419
-				$this->logger->logException($e, [
420
-					'message' => "Error during capability registration of $appId: " . $e->getMessage(),
421
-					'level' => ILogger::ERROR,
422
-				]);
423
-			}
424
-		}
425
-	}
426
-
427
-	/**
428
-	 * @return ServiceRegistration<IProvider>[]
429
-	 */
430
-	public function getSearchProviders(): array {
431
-		return $this->searchProviders;
432
-	}
433
-
434
-	/**
435
-	 * @return ServiceRegistration<IAlternativeLogin>[]
436
-	 */
437
-	public function getAlternativeLogins(): array {
438
-		return $this->alternativeLogins;
439
-	}
440
-
441
-	/**
442
-	 * @return array[]
443
-	 */
444
-	public function getInitialStates(): array {
445
-		return $this->initialStates;
446
-	}
447
-
448
-	/**
449
-	 * @return ServiceRegistration<IHandler>[]
450
-	 */
451
-	public function getWellKnownHandlers(): array {
452
-		return $this->wellKnownHandlers;
453
-	}
454
-
455
-	/**
456
-	 * @return ServiceRegistration<ICustomTemplateProvider>[]
457
-	 */
458
-	public function getTemplateProviders(): array {
459
-		return $this->templateProviders;
460
-	}
52
+    /** @var ServiceRegistration<ICapability>[] */
53
+    private $capabilities = [];
54
+
55
+    /** @var ServiceRegistration<IReporter>[] */
56
+    private $crashReporters = [];
57
+
58
+    /** @var ServiceRegistration<IWidget>[] */
59
+    private $dashboardPanels = [];
60
+
61
+    /** @var ServiceFactoryRegistration[] */
62
+    private $services = [];
63
+
64
+    /** @var ServiceAliasRegistration[] */
65
+    private $aliases = [];
66
+
67
+    /** @var ParameterRegistration[] */
68
+    private $parameters = [];
69
+
70
+    /** @var EventListenerRegistration[] */
71
+    private $eventListeners = [];
72
+
73
+    /** @var ServiceRegistration<Middleware>[] */
74
+    private $middlewares = [];
75
+
76
+    /** @var ServiceRegistration<IProvider>[] */
77
+    private $searchProviders = [];
78
+
79
+    /** @var ServiceRegistration<IAlternativeLogin>[] */
80
+    private $alternativeLogins = [];
81
+
82
+    /** @var array[] */
83
+    private $initialStates = [];
84
+
85
+    /** @var ServiceRegistration<IHandler>[] */
86
+    private $wellKnownHandlers = [];
87
+
88
+    /** @var ServiceRegistration<ICustomTemplateProvider>[] */
89
+    private $templateProviders = [];
90
+
91
+    /** @var ILogger */
92
+    private $logger;
93
+
94
+    public function __construct(ILogger $logger) {
95
+        $this->logger = $logger;
96
+    }
97
+
98
+    public function for(string $appId): IRegistrationContext {
99
+        return new class($appId, $this) implements IRegistrationContext {
100
+            /** @var string */
101
+            private $appId;
102
+
103
+            /** @var RegistrationContext */
104
+            private $context;
105
+
106
+            public function __construct(string $appId, RegistrationContext $context) {
107
+                $this->appId = $appId;
108
+                $this->context = $context;
109
+            }
110
+
111
+            public function registerCapability(string $capability): void {
112
+                $this->context->registerCapability(
113
+                    $this->appId,
114
+                    $capability
115
+                );
116
+            }
117
+
118
+            public function registerCrashReporter(string $reporterClass): void {
119
+                $this->context->registerCrashReporter(
120
+                    $this->appId,
121
+                    $reporterClass
122
+                );
123
+            }
124
+
125
+            public function registerDashboardWidget(string $widgetClass): void {
126
+                $this->context->registerDashboardPanel(
127
+                    $this->appId,
128
+                    $widgetClass
129
+                );
130
+            }
131
+
132
+            public function registerService(string $name, callable $factory, bool $shared = true): void {
133
+                $this->context->registerService(
134
+                    $this->appId,
135
+                    $name,
136
+                    $factory,
137
+                    $shared
138
+                );
139
+            }
140
+
141
+            public function registerServiceAlias(string $alias, string $target): void {
142
+                $this->context->registerServiceAlias(
143
+                    $this->appId,
144
+                    $alias,
145
+                    $target
146
+                );
147
+            }
148
+
149
+            public function registerParameter(string $name, $value): void {
150
+                $this->context->registerParameter(
151
+                    $this->appId,
152
+                    $name,
153
+                    $value
154
+                );
155
+            }
156
+
157
+            public function registerEventListener(string $event, string $listener, int $priority = 0): void {
158
+                $this->context->registerEventListener(
159
+                    $this->appId,
160
+                    $event,
161
+                    $listener,
162
+                    $priority
163
+                );
164
+            }
165
+
166
+            public function registerMiddleware(string $class): void {
167
+                $this->context->registerMiddleware(
168
+                    $this->appId,
169
+                    $class
170
+                );
171
+            }
172
+
173
+            public function registerSearchProvider(string $class): void {
174
+                $this->context->registerSearchProvider(
175
+                    $this->appId,
176
+                    $class
177
+                );
178
+            }
179
+
180
+            public function registerAlternativeLogin(string $class): void {
181
+                $this->context->registerAlternativeLogin(
182
+                    $this->appId,
183
+                    $class
184
+                );
185
+            }
186
+
187
+            public function registerInitialStateProvider(string $class): void {
188
+                $this->context->registerInitialState(
189
+                    $this->appId,
190
+                    $class
191
+                );
192
+            }
193
+
194
+            public function registerWellKnownHandler(string $class): void {
195
+                $this->context->registerWellKnown(
196
+                    $this->appId,
197
+                    $class
198
+                );
199
+            }
200
+
201
+            public function registerTemplateProvider(string $providerClass): void {
202
+                $this->context->registerTemplateProvider(
203
+                    $this->appId,
204
+                    $providerClass
205
+                );
206
+            }
207
+        };
208
+    }
209
+
210
+    /**
211
+     * @psalm-param class-string<ICapability> $capability
212
+     */
213
+    public function registerCapability(string $appId, string $capability): void {
214
+        $this->capabilities[] = new ServiceRegistration($appId, $capability);
215
+    }
216
+
217
+    /**
218
+     * @psalm-param class-string<IReporter> $capability
219
+     */
220
+    public function registerCrashReporter(string $appId, string $reporterClass): void {
221
+        $this->crashReporters[] = new ServiceRegistration($appId, $reporterClass);
222
+    }
223
+
224
+    /**
225
+     * @psalm-param class-string<IWidget> $capability
226
+     */
227
+    public function registerDashboardPanel(string $appId, string $panelClass): void {
228
+        $this->dashboardPanels[] = new ServiceRegistration($appId, $panelClass);
229
+    }
230
+
231
+    public function registerService(string $appId, string $name, callable $factory, bool $shared = true): void {
232
+        $this->services[] = new ServiceFactoryRegistration($appId, $name, $factory, $shared);
233
+    }
234
+
235
+    public function registerServiceAlias(string $appId, string $alias, string $target): void {
236
+        $this->aliases[] = new ServiceAliasRegistration($appId, $alias, $target);
237
+    }
238
+
239
+    public function registerParameter(string $appId, string $name, $value): void {
240
+        $this->parameters[] = new ParameterRegistration($appId, $name, $value);
241
+    }
242
+
243
+    public function registerEventListener(string $appId, string $event, string $listener, int $priority = 0): void {
244
+        $this->eventListeners[] = new EventListenerRegistration($appId, $event, $listener, $priority);
245
+    }
246
+
247
+    /**
248
+     * @psalm-param class-string<Middleware> $class
249
+     */
250
+    public function registerMiddleware(string $appId, string $class): void {
251
+        $this->middlewares[] = new ServiceRegistration($appId, $class);
252
+    }
253
+
254
+    public function registerSearchProvider(string $appId, string $class) {
255
+        $this->searchProviders[] = new ServiceRegistration($appId, $class);
256
+    }
257
+
258
+    public function registerAlternativeLogin(string $appId, string $class): void {
259
+        $this->alternativeLogins[] = new ServiceRegistration($appId, $class);
260
+    }
261
+
262
+    public function registerInitialState(string $appId, string $class): void {
263
+        $this->initialStates[] = [
264
+            'appId' => $appId,
265
+            'class' => $class,
266
+        ];
267
+    }
268
+
269
+    public function registerWellKnown(string $appId, string $class): void {
270
+        $this->wellKnownHandlers[] = new ServiceRegistration($appId, $class);
271
+    }
272
+
273
+    public function registerTemplateProvider(string $appId, string $class): void {
274
+        $this->templateProviders[] = new ServiceRegistration($appId, $class);
275
+    }
276
+
277
+    /**
278
+     * @param App[] $apps
279
+     */
280
+    public function delegateCapabilityRegistrations(array $apps): void {
281
+        while (($registration = array_shift($this->capabilities)) !== null) {
282
+            try {
283
+                $apps[$registration->getAppId()]
284
+                    ->getContainer()
285
+                    ->registerCapability($registration->getService());
286
+            } catch (Throwable $e) {
287
+                $appId = $registration->getAppId();
288
+                $this->logger->logException($e, [
289
+                    'message' => "Error during capability registration of $appId: " . $e->getMessage(),
290
+                    'level' => ILogger::ERROR,
291
+                ]);
292
+            }
293
+        }
294
+    }
295
+
296
+    /**
297
+     * @param App[] $apps
298
+     */
299
+    public function delegateCrashReporterRegistrations(array $apps, Registry $registry): void {
300
+        while (($registration = array_shift($this->crashReporters)) !== null) {
301
+            try {
302
+                $registry->registerLazy($registration->getService());
303
+            } catch (Throwable $e) {
304
+                $appId = $registration->getAppId();
305
+                $this->logger->logException($e, [
306
+                    'message' => "Error during crash reporter registration of $appId: " . $e->getMessage(),
307
+                    'level' => ILogger::ERROR,
308
+                ]);
309
+            }
310
+        }
311
+    }
312
+
313
+    /**
314
+     * @param App[] $apps
315
+     */
316
+    public function delegateDashboardPanelRegistrations(array $apps, IManager $dashboardManager): void {
317
+        while (($panel = array_shift($this->dashboardPanels)) !== null) {
318
+            try {
319
+                $dashboardManager->lazyRegisterWidget($panel->getService());
320
+            } catch (Throwable $e) {
321
+                $appId = $panel->getAppId();
322
+                $this->logger->logException($e, [
323
+                    'message' => "Error during dashboard registration of $appId: " . $e->getMessage(),
324
+                    'level' => ILogger::ERROR,
325
+                ]);
326
+            }
327
+        }
328
+    }
329
+
330
+    public function delegateEventListenerRegistrations(IEventDispatcher $eventDispatcher): void {
331
+        while (($registration = array_shift($this->eventListeners)) !== null) {
332
+            try {
333
+                $eventDispatcher->addServiceListener(
334
+                    $registration->getEvent(),
335
+                    $registration->getService(),
336
+                    $registration->getPriority()
337
+                );
338
+            } catch (Throwable $e) {
339
+                $appId = $registration->getAppId();
340
+                $this->logger->logException($e, [
341
+                    'message' => "Error during event listener registration of $appId: " . $e->getMessage(),
342
+                    'level' => ILogger::ERROR,
343
+                ]);
344
+            }
345
+        }
346
+    }
347
+
348
+    /**
349
+     * @param App[] $apps
350
+     */
351
+    public function delegateContainerRegistrations(array $apps): void {
352
+        while (($registration = array_shift($this->services)) !== null) {
353
+            try {
354
+                /**
355
+                 * Register the service and convert the callable into a \Closure if necessary
356
+                 */
357
+                $apps[$registration->getAppId()]
358
+                    ->getContainer()
359
+                    ->registerService(
360
+                        $registration->getName(),
361
+                        Closure::fromCallable($registration->getFactory()),
362
+                        $registration->isShared()
363
+                    );
364
+            } catch (Throwable $e) {
365
+                $appId = $registration->getAppId();
366
+                $this->logger->logException($e, [
367
+                    'message' => "Error during service registration of $appId: " . $e->getMessage(),
368
+                    'level' => ILogger::ERROR,
369
+                ]);
370
+            }
371
+        }
372
+
373
+        foreach ($this->aliases as $registration) {
374
+            try {
375
+                $apps[$registration->getAppId()]
376
+                    ->getContainer()
377
+                    ->registerAlias(
378
+                        $registration->getAlias(),
379
+                        $registration->getTarget()
380
+                    );
381
+            } catch (Throwable $e) {
382
+                $appId = $registration->getAppId();
383
+                $this->logger->logException($e, [
384
+                    'message' => "Error during service alias registration of $appId: " . $e->getMessage(),
385
+                    'level' => ILogger::ERROR,
386
+                ]);
387
+            }
388
+        }
389
+
390
+        foreach ($this->parameters as $registration) {
391
+            try {
392
+                $apps[$registration->getAppId()]
393
+                    ->getContainer()
394
+                    ->registerParameter(
395
+                        $registration->getName(),
396
+                        $registration->getValue()
397
+                    );
398
+            } catch (Throwable $e) {
399
+                $appId = $registration->getAppId();
400
+                $this->logger->logException($e, [
401
+                    'message' => "Error during service alias registration of $appId: " . $e->getMessage(),
402
+                    'level' => ILogger::ERROR,
403
+                ]);
404
+            }
405
+        }
406
+    }
407
+
408
+    /**
409
+     * @param App[] $apps
410
+     */
411
+    public function delegateMiddlewareRegistrations(array $apps): void {
412
+        while (($middleware = array_shift($this->middlewares)) !== null) {
413
+            try {
414
+                $apps[$middleware->getAppId()]
415
+                    ->getContainer()
416
+                    ->registerMiddleWare($middleware->getService());
417
+            } catch (Throwable $e) {
418
+                $appId = $middleware->getAppId();
419
+                $this->logger->logException($e, [
420
+                    'message' => "Error during capability registration of $appId: " . $e->getMessage(),
421
+                    'level' => ILogger::ERROR,
422
+                ]);
423
+            }
424
+        }
425
+    }
426
+
427
+    /**
428
+     * @return ServiceRegistration<IProvider>[]
429
+     */
430
+    public function getSearchProviders(): array {
431
+        return $this->searchProviders;
432
+    }
433
+
434
+    /**
435
+     * @return ServiceRegistration<IAlternativeLogin>[]
436
+     */
437
+    public function getAlternativeLogins(): array {
438
+        return $this->alternativeLogins;
439
+    }
440
+
441
+    /**
442
+     * @return array[]
443
+     */
444
+    public function getInitialStates(): array {
445
+        return $this->initialStates;
446
+    }
447
+
448
+    /**
449
+     * @return ServiceRegistration<IHandler>[]
450
+     */
451
+    public function getWellKnownHandlers(): array {
452
+        return $this->wellKnownHandlers;
453
+    }
454
+
455
+    /**
456
+     * @return ServiceRegistration<ICustomTemplateProvider>[]
457
+     */
458
+    public function getTemplateProviders(): array {
459
+        return $this->templateProviders;
460
+    }
461 461
 }
Please login to merge, or discard this patch.
lib/private/legacy/OC_App.php 1 patch
Indentation   +1121 added lines, -1121 removed lines patch added patch discarded remove patch
@@ -68,1125 +68,1125 @@
 block discarded – undo
68 68
  * upgrading and removing apps.
69 69
  */
70 70
 class OC_App {
71
-	private static $adminForms = [];
72
-	private static $personalForms = [];
73
-	private static $appTypes = [];
74
-	private static $loadedApps = [];
75
-	private static $altLogin = [];
76
-	private static $alreadyRegistered = [];
77
-	public const supportedApp = 300;
78
-	public const officialApp = 200;
79
-
80
-	/**
81
-	 * clean the appId
82
-	 *
83
-	 * @psalm-taint-escape file
84
-	 * @psalm-taint-escape include
85
-	 *
86
-	 * @param string $app AppId that needs to be cleaned
87
-	 * @return string
88
-	 */
89
-	public static function cleanAppId(string $app): string {
90
-		return str_replace(['\0', '/', '\\', '..'], '', $app);
91
-	}
92
-
93
-	/**
94
-	 * Check if an app is loaded
95
-	 *
96
-	 * @param string $app
97
-	 * @return bool
98
-	 */
99
-	public static function isAppLoaded(string $app): bool {
100
-		return isset(self::$loadedApps[$app]);
101
-	}
102
-
103
-	/**
104
-	 * loads all apps
105
-	 *
106
-	 * @param string[] $types
107
-	 * @return bool
108
-	 *
109
-	 * This function walks through the ownCloud directory and loads all apps
110
-	 * it can find. A directory contains an app if the file /appinfo/info.xml
111
-	 * exists.
112
-	 *
113
-	 * if $types is set to non-empty array, only apps of those types will be loaded
114
-	 */
115
-	public static function loadApps(array $types = []): bool {
116
-		if ((bool) \OC::$server->getSystemConfig()->getValue('maintenance', false)) {
117
-			return false;
118
-		}
119
-		// Load the enabled apps here
120
-		$apps = self::getEnabledApps();
121
-
122
-		// Add each apps' folder as allowed class path
123
-		foreach ($apps as $app) {
124
-			// If the app is already loaded then autoloading it makes no sense
125
-			if (!isset(self::$loadedApps[$app])) {
126
-				$path = self::getAppPath($app);
127
-				if ($path !== false) {
128
-					self::registerAutoloading($app, $path);
129
-				}
130
-			}
131
-		}
132
-
133
-		// prevent app.php from printing output
134
-		ob_start();
135
-		foreach ($apps as $app) {
136
-			if (!isset(self::$loadedApps[$app]) && ($types === [] || self::isType($app, $types))) {
137
-				self::loadApp($app);
138
-			}
139
-		}
140
-		ob_end_clean();
141
-
142
-		return true;
143
-	}
144
-
145
-	/**
146
-	 * load a single app
147
-	 *
148
-	 * @param string $app
149
-	 * @throws Exception
150
-	 */
151
-	public static function loadApp(string $app) {
152
-		self::$loadedApps[$app] = true;
153
-		$appPath = self::getAppPath($app);
154
-		if ($appPath === false) {
155
-			return;
156
-		}
157
-
158
-		// in case someone calls loadApp() directly
159
-		self::registerAutoloading($app, $appPath);
160
-
161
-		/** @var Coordinator $coordinator */
162
-		$coordinator = \OC::$server->query(Coordinator::class);
163
-		$isBootable = $coordinator->isBootable($app);
164
-
165
-		$hasAppPhpFile = is_file($appPath . '/appinfo/app.php');
166
-
167
-		if ($isBootable && $hasAppPhpFile) {
168
-			\OC::$server->getLogger()->error('/appinfo/app.php is not loaded when \OCP\AppFramework\Bootstrap\IBootstrap on the application class is used. Migrate everything from app.php to the Application class.', [
169
-				'app' => $app,
170
-			]);
171
-		} elseif ($hasAppPhpFile) {
172
-			\OC::$server->getLogger()->debug('/appinfo/app.php is deprecated, use \OCP\AppFramework\Bootstrap\IBootstrap on the application class instead.', [
173
-				'app' => $app,
174
-			]);
175
-			\OC::$server->getEventLogger()->start('load_app_' . $app, 'Load app: ' . $app);
176
-			try {
177
-				self::requireAppFile($app);
178
-			} catch (Throwable $ex) {
179
-				if ($ex instanceof ServerNotAvailableException) {
180
-					throw $ex;
181
-				}
182
-				if (!\OC::$server->getAppManager()->isShipped($app) && !self::isType($app, ['authentication'])) {
183
-					\OC::$server->getLogger()->logException($ex, [
184
-						'message' => "App $app threw an error during app.php load and will be disabled: " . $ex->getMessage(),
185
-					]);
186
-
187
-					// Only disable apps which are not shipped and that are not authentication apps
188
-					\OC::$server->getAppManager()->disableApp($app, true);
189
-				} else {
190
-					\OC::$server->getLogger()->logException($ex, [
191
-						'message' => "App $app threw an error during app.php load: " . $ex->getMessage(),
192
-					]);
193
-				}
194
-			}
195
-			\OC::$server->getEventLogger()->end('load_app_' . $app);
196
-		}
197
-		$coordinator->bootApp($app);
198
-
199
-		$info = self::getAppInfo($app);
200
-		if (!empty($info['activity']['filters'])) {
201
-			foreach ($info['activity']['filters'] as $filter) {
202
-				\OC::$server->getActivityManager()->registerFilter($filter);
203
-			}
204
-		}
205
-		if (!empty($info['activity']['settings'])) {
206
-			foreach ($info['activity']['settings'] as $setting) {
207
-				\OC::$server->getActivityManager()->registerSetting($setting);
208
-			}
209
-		}
210
-		if (!empty($info['activity']['providers'])) {
211
-			foreach ($info['activity']['providers'] as $provider) {
212
-				\OC::$server->getActivityManager()->registerProvider($provider);
213
-			}
214
-		}
215
-
216
-		if (!empty($info['settings']['admin'])) {
217
-			foreach ($info['settings']['admin'] as $setting) {
218
-				\OC::$server->getSettingsManager()->registerSetting('admin', $setting);
219
-			}
220
-		}
221
-		if (!empty($info['settings']['admin-section'])) {
222
-			foreach ($info['settings']['admin-section'] as $section) {
223
-				\OC::$server->getSettingsManager()->registerSection('admin', $section);
224
-			}
225
-		}
226
-		if (!empty($info['settings']['personal'])) {
227
-			foreach ($info['settings']['personal'] as $setting) {
228
-				\OC::$server->getSettingsManager()->registerSetting('personal', $setting);
229
-			}
230
-		}
231
-		if (!empty($info['settings']['personal-section'])) {
232
-			foreach ($info['settings']['personal-section'] as $section) {
233
-				\OC::$server->getSettingsManager()->registerSection('personal', $section);
234
-			}
235
-		}
236
-
237
-		if (!empty($info['collaboration']['plugins'])) {
238
-			// deal with one or many plugin entries
239
-			$plugins = isset($info['collaboration']['plugins']['plugin']['@value']) ?
240
-				[$info['collaboration']['plugins']['plugin']] : $info['collaboration']['plugins']['plugin'];
241
-			foreach ($plugins as $plugin) {
242
-				if ($plugin['@attributes']['type'] === 'collaborator-search') {
243
-					$pluginInfo = [
244
-						'shareType' => $plugin['@attributes']['share-type'],
245
-						'class' => $plugin['@value'],
246
-					];
247
-					\OC::$server->getCollaboratorSearch()->registerPlugin($pluginInfo);
248
-				} elseif ($plugin['@attributes']['type'] === 'autocomplete-sort') {
249
-					\OC::$server->getAutoCompleteManager()->registerSorter($plugin['@value']);
250
-				}
251
-			}
252
-		}
253
-	}
254
-
255
-	/**
256
-	 * @internal
257
-	 * @param string $app
258
-	 * @param string $path
259
-	 * @param bool $force
260
-	 */
261
-	public static function registerAutoloading(string $app, string $path, bool $force = false) {
262
-		$key = $app . '-' . $path;
263
-		if (!$force && isset(self::$alreadyRegistered[$key])) {
264
-			return;
265
-		}
266
-
267
-		self::$alreadyRegistered[$key] = true;
268
-
269
-		// Register on PSR-4 composer autoloader
270
-		$appNamespace = \OC\AppFramework\App::buildAppNamespace($app);
271
-		\OC::$server->registerNamespace($app, $appNamespace);
272
-
273
-		if (file_exists($path . '/composer/autoload.php')) {
274
-			require_once $path . '/composer/autoload.php';
275
-		} else {
276
-			\OC::$composerAutoloader->addPsr4($appNamespace . '\\', $path . '/lib/', true);
277
-			// Register on legacy autoloader
278
-			\OC::$loader->addValidRoot($path);
279
-		}
280
-
281
-		// Register Test namespace only when testing
282
-		if (defined('PHPUNIT_RUN') || defined('CLI_TEST_RUN')) {
283
-			\OC::$composerAutoloader->addPsr4($appNamespace . '\\Tests\\', $path . '/tests/', true);
284
-		}
285
-	}
286
-
287
-	/**
288
-	 * Load app.php from the given app
289
-	 *
290
-	 * @param string $app app name
291
-	 * @throws Error
292
-	 */
293
-	private static function requireAppFile(string $app) {
294
-		// encapsulated here to avoid variable scope conflicts
295
-		require_once $app . '/appinfo/app.php';
296
-	}
297
-
298
-	/**
299
-	 * check if an app is of a specific type
300
-	 *
301
-	 * @param string $app
302
-	 * @param array $types
303
-	 * @return bool
304
-	 */
305
-	public static function isType(string $app, array $types): bool {
306
-		$appTypes = self::getAppTypes($app);
307
-		foreach ($types as $type) {
308
-			if (array_search($type, $appTypes) !== false) {
309
-				return true;
310
-			}
311
-		}
312
-		return false;
313
-	}
314
-
315
-	/**
316
-	 * get the types of an app
317
-	 *
318
-	 * @param string $app
319
-	 * @return array
320
-	 */
321
-	private static function getAppTypes(string $app): array {
322
-		//load the cache
323
-		if (count(self::$appTypes) == 0) {
324
-			self::$appTypes = \OC::$server->getAppConfig()->getValues(false, 'types');
325
-		}
326
-
327
-		if (isset(self::$appTypes[$app])) {
328
-			return explode(',', self::$appTypes[$app]);
329
-		}
330
-
331
-		return [];
332
-	}
333
-
334
-	/**
335
-	 * read app types from info.xml and cache them in the database
336
-	 */
337
-	public static function setAppTypes(string $app) {
338
-		$appManager = \OC::$server->getAppManager();
339
-		$appData = $appManager->getAppInfo($app);
340
-		if (!is_array($appData)) {
341
-			return;
342
-		}
343
-
344
-		if (isset($appData['types'])) {
345
-			$appTypes = implode(',', $appData['types']);
346
-		} else {
347
-			$appTypes = '';
348
-			$appData['types'] = [];
349
-		}
350
-
351
-		$config = \OC::$server->getConfig();
352
-		$config->setAppValue($app, 'types', $appTypes);
353
-
354
-		if ($appManager->hasProtectedAppType($appData['types'])) {
355
-			$enabled = $config->getAppValue($app, 'enabled', 'yes');
356
-			if ($enabled !== 'yes' && $enabled !== 'no') {
357
-				$config->setAppValue($app, 'enabled', 'yes');
358
-			}
359
-		}
360
-	}
361
-
362
-	/**
363
-	 * Returns apps enabled for the current user.
364
-	 *
365
-	 * @param bool $forceRefresh whether to refresh the cache
366
-	 * @param bool $all whether to return apps for all users, not only the
367
-	 * currently logged in one
368
-	 * @return string[]
369
-	 */
370
-	public static function getEnabledApps(bool $forceRefresh = false, bool $all = false): array {
371
-		if (!\OC::$server->getSystemConfig()->getValue('installed', false)) {
372
-			return [];
373
-		}
374
-		// in incognito mode or when logged out, $user will be false,
375
-		// which is also the case during an upgrade
376
-		$appManager = \OC::$server->getAppManager();
377
-		if ($all) {
378
-			$user = null;
379
-		} else {
380
-			$user = \OC::$server->getUserSession()->getUser();
381
-		}
382
-
383
-		if (is_null($user)) {
384
-			$apps = $appManager->getInstalledApps();
385
-		} else {
386
-			$apps = $appManager->getEnabledAppsForUser($user);
387
-		}
388
-		$apps = array_filter($apps, function ($app) {
389
-			return $app !== 'files';//we add this manually
390
-		});
391
-		sort($apps);
392
-		array_unshift($apps, 'files');
393
-		return $apps;
394
-	}
395
-
396
-	/**
397
-	 * checks whether or not an app is enabled
398
-	 *
399
-	 * @param string $app app
400
-	 * @return bool
401
-	 * @deprecated 13.0.0 use \OC::$server->getAppManager()->isEnabledForUser($appId)
402
-	 *
403
-	 * This function checks whether or not an app is enabled.
404
-	 */
405
-	public static function isEnabled(string $app): bool {
406
-		return \OC::$server->getAppManager()->isEnabledForUser($app);
407
-	}
408
-
409
-	/**
410
-	 * enables an app
411
-	 *
412
-	 * @param string $appId
413
-	 * @param array $groups (optional) when set, only these groups will have access to the app
414
-	 * @throws \Exception
415
-	 * @return void
416
-	 *
417
-	 * This function set an app as enabled in appconfig.
418
-	 */
419
-	public function enable(string $appId,
420
-						   array $groups = []) {
421
-
422
-		// Check if app is already downloaded
423
-		/** @var Installer $installer */
424
-		$installer = \OC::$server->query(Installer::class);
425
-		$isDownloaded = $installer->isDownloaded($appId);
426
-
427
-		if (!$isDownloaded) {
428
-			$installer->downloadApp($appId);
429
-		}
430
-
431
-		$installer->installApp($appId);
432
-
433
-		$appManager = \OC::$server->getAppManager();
434
-		if ($groups !== []) {
435
-			$groupManager = \OC::$server->getGroupManager();
436
-			$groupsList = [];
437
-			foreach ($groups as $group) {
438
-				$groupItem = $groupManager->get($group);
439
-				if ($groupItem instanceof \OCP\IGroup) {
440
-					$groupsList[] = $groupManager->get($group);
441
-				}
442
-			}
443
-			$appManager->enableAppForGroups($appId, $groupsList);
444
-		} else {
445
-			$appManager->enableApp($appId);
446
-		}
447
-	}
448
-
449
-	/**
450
-	 * Get the path where to install apps
451
-	 *
452
-	 * @return string|false
453
-	 */
454
-	public static function getInstallPath() {
455
-		if (\OC::$server->getSystemConfig()->getValue('appstoreenabled', true) == false) {
456
-			return false;
457
-		}
458
-
459
-		foreach (OC::$APPSROOTS as $dir) {
460
-			if (isset($dir['writable']) && $dir['writable'] === true) {
461
-				return $dir['path'];
462
-			}
463
-		}
464
-
465
-		\OCP\Util::writeLog('core', 'No application directories are marked as writable.', ILogger::ERROR);
466
-		return null;
467
-	}
468
-
469
-
470
-	/**
471
-	 * search for an app in all app-directories
472
-	 *
473
-	 * @param string $appId
474
-	 * @return false|string
475
-	 */
476
-	public static function findAppInDirectories(string $appId) {
477
-		$sanitizedAppId = self::cleanAppId($appId);
478
-		if ($sanitizedAppId !== $appId) {
479
-			return false;
480
-		}
481
-		static $app_dir = [];
482
-
483
-		if (isset($app_dir[$appId])) {
484
-			return $app_dir[$appId];
485
-		}
486
-
487
-		$possibleApps = [];
488
-		foreach (OC::$APPSROOTS as $dir) {
489
-			if (file_exists($dir['path'] . '/' . $appId)) {
490
-				$possibleApps[] = $dir;
491
-			}
492
-		}
493
-
494
-		if (empty($possibleApps)) {
495
-			return false;
496
-		} elseif (count($possibleApps) === 1) {
497
-			$dir = array_shift($possibleApps);
498
-			$app_dir[$appId] = $dir;
499
-			return $dir;
500
-		} else {
501
-			$versionToLoad = [];
502
-			foreach ($possibleApps as $possibleApp) {
503
-				$version = self::getAppVersionByPath($possibleApp['path'] . '/' . $appId);
504
-				if (empty($versionToLoad) || version_compare($version, $versionToLoad['version'], '>')) {
505
-					$versionToLoad = [
506
-						'dir' => $possibleApp,
507
-						'version' => $version,
508
-					];
509
-				}
510
-			}
511
-			$app_dir[$appId] = $versionToLoad['dir'];
512
-			return $versionToLoad['dir'];
513
-			//TODO - write test
514
-		}
515
-	}
516
-
517
-	/**
518
-	 * Get the directory for the given app.
519
-	 * If the app is defined in multiple directories, the first one is taken. (false if not found)
520
-	 *
521
-	 * @psalm-taint-specialize
522
-	 *
523
-	 * @param string $appId
524
-	 * @return string|false
525
-	 * @deprecated 11.0.0 use \OC::$server->getAppManager()->getAppPath()
526
-	 */
527
-	public static function getAppPath(string $appId) {
528
-		if ($appId === null || trim($appId) === '') {
529
-			return false;
530
-		}
531
-
532
-		if (($dir = self::findAppInDirectories($appId)) != false) {
533
-			return $dir['path'] . '/' . $appId;
534
-		}
535
-		return false;
536
-	}
537
-
538
-	/**
539
-	 * Get the path for the given app on the access
540
-	 * If the app is defined in multiple directories, the first one is taken. (false if not found)
541
-	 *
542
-	 * @param string $appId
543
-	 * @return string|false
544
-	 * @deprecated 18.0.0 use \OC::$server->getAppManager()->getAppWebPath()
545
-	 */
546
-	public static function getAppWebPath(string $appId) {
547
-		if (($dir = self::findAppInDirectories($appId)) != false) {
548
-			return OC::$WEBROOT . $dir['url'] . '/' . $appId;
549
-		}
550
-		return false;
551
-	}
552
-
553
-	/**
554
-	 * get the last version of the app from appinfo/info.xml
555
-	 *
556
-	 * @param string $appId
557
-	 * @param bool $useCache
558
-	 * @return string
559
-	 * @deprecated 14.0.0 use \OC::$server->getAppManager()->getAppVersion()
560
-	 */
561
-	public static function getAppVersion(string $appId, bool $useCache = true): string {
562
-		return \OC::$server->getAppManager()->getAppVersion($appId, $useCache);
563
-	}
564
-
565
-	/**
566
-	 * get app's version based on it's path
567
-	 *
568
-	 * @param string $path
569
-	 * @return string
570
-	 */
571
-	public static function getAppVersionByPath(string $path): string {
572
-		$infoFile = $path . '/appinfo/info.xml';
573
-		$appData = \OC::$server->getAppManager()->getAppInfo($infoFile, true);
574
-		return isset($appData['version']) ? $appData['version'] : '';
575
-	}
576
-
577
-
578
-	/**
579
-	 * Read all app metadata from the info.xml file
580
-	 *
581
-	 * @param string $appId id of the app or the path of the info.xml file
582
-	 * @param bool $path
583
-	 * @param string $lang
584
-	 * @return array|null
585
-	 * @note all data is read from info.xml, not just pre-defined fields
586
-	 * @deprecated 14.0.0 use \OC::$server->getAppManager()->getAppInfo()
587
-	 */
588
-	public static function getAppInfo(string $appId, bool $path = false, string $lang = null) {
589
-		return \OC::$server->getAppManager()->getAppInfo($appId, $path, $lang);
590
-	}
591
-
592
-	/**
593
-	 * Returns the navigation
594
-	 *
595
-	 * @return array
596
-	 * @deprecated 14.0.0 use \OC::$server->getNavigationManager()->getAll()
597
-	 *
598
-	 * This function returns an array containing all entries added. The
599
-	 * entries are sorted by the key 'order' ascending. Additional to the keys
600
-	 * given for each app the following keys exist:
601
-	 *   - active: boolean, signals if the user is on this navigation entry
602
-	 */
603
-	public static function getNavigation(): array {
604
-		return OC::$server->getNavigationManager()->getAll();
605
-	}
606
-
607
-	/**
608
-	 * Returns the Settings Navigation
609
-	 *
610
-	 * @return string[]
611
-	 * @deprecated 14.0.0 use \OC::$server->getNavigationManager()->getAll('settings')
612
-	 *
613
-	 * This function returns an array containing all settings pages added. The
614
-	 * entries are sorted by the key 'order' ascending.
615
-	 */
616
-	public static function getSettingsNavigation(): array {
617
-		return OC::$server->getNavigationManager()->getAll('settings');
618
-	}
619
-
620
-	/**
621
-	 * get the id of loaded app
622
-	 *
623
-	 * @return string
624
-	 */
625
-	public static function getCurrentApp(): string {
626
-		$request = \OC::$server->getRequest();
627
-		$script = substr($request->getScriptName(), strlen(OC::$WEBROOT) + 1);
628
-		$topFolder = substr($script, 0, strpos($script, '/') ?: 0);
629
-		if (empty($topFolder)) {
630
-			$path_info = $request->getPathInfo();
631
-			if ($path_info) {
632
-				$topFolder = substr($path_info, 1, strpos($path_info, '/', 1) - 1);
633
-			}
634
-		}
635
-		if ($topFolder == 'apps') {
636
-			$length = strlen($topFolder);
637
-			return substr($script, $length + 1, strpos($script, '/', $length + 1) - $length - 1) ?: '';
638
-		} else {
639
-			return $topFolder;
640
-		}
641
-	}
642
-
643
-	/**
644
-	 * @param string $type
645
-	 * @return array
646
-	 */
647
-	public static function getForms(string $type): array {
648
-		$forms = [];
649
-		switch ($type) {
650
-			case 'admin':
651
-				$source = self::$adminForms;
652
-				break;
653
-			case 'personal':
654
-				$source = self::$personalForms;
655
-				break;
656
-			default:
657
-				return [];
658
-		}
659
-		foreach ($source as $form) {
660
-			$forms[] = include $form;
661
-		}
662
-		return $forms;
663
-	}
664
-
665
-	/**
666
-	 * register an admin form to be shown
667
-	 *
668
-	 * @param string $app
669
-	 * @param string $page
670
-	 */
671
-	public static function registerAdmin(string $app, string $page) {
672
-		self::$adminForms[] = $app . '/' . $page . '.php';
673
-	}
674
-
675
-	/**
676
-	 * register a personal form to be shown
677
-	 * @param string $app
678
-	 * @param string $page
679
-	 */
680
-	public static function registerPersonal(string $app, string $page) {
681
-		self::$personalForms[] = $app . '/' . $page . '.php';
682
-	}
683
-
684
-	/**
685
-	 * @param array $entry
686
-	 * @deprecated 20.0.0 Please register your alternative login option using the registerAlternativeLogin() on the RegistrationContext in your Application class implementing the OCP\Authentication\IAlternativeLogin interface
687
-	 */
688
-	public static function registerLogIn(array $entry) {
689
-		\OC::$server->getLogger()->debug('OC_App::registerLogIn() is deprecated, please register your alternative login option using the registerAlternativeLogin() on the RegistrationContext in your Application class implementing the OCP\Authentication\IAlternativeLogin interface');
690
-		self::$altLogin[] = $entry;
691
-	}
692
-
693
-	/**
694
-	 * @return array
695
-	 */
696
-	public static function getAlternativeLogIns(): array {
697
-		/** @var Coordinator $bootstrapCoordinator */
698
-		$bootstrapCoordinator = \OC::$server->query(Coordinator::class);
699
-
700
-		foreach ($bootstrapCoordinator->getRegistrationContext()->getAlternativeLogins() as $registration) {
701
-			if (!in_array(IAlternativeLogin::class, class_implements($registration->getService()), true)) {
702
-				\OC::$server->getLogger()->error('Alternative login option {option} does not implement {interface} and is therefore ignored.', [
703
-					'option' => $registration->getService(),
704
-					'interface' => IAlternativeLogin::class,
705
-					'app' => $registration->getAppId(),
706
-				]);
707
-				continue;
708
-			}
709
-
710
-			try {
711
-				/** @var IAlternativeLogin $provider */
712
-				$provider = \OC::$server->query($registration->getService());
713
-			} catch (QueryException $e) {
714
-				\OC::$server->getLogger()->logException($e, [
715
-					'message' => 'Alternative login option {option} can not be initialised.',
716
-					'option' => $registration->getService(),
717
-					'app' => $registration->getAppId(),
718
-				]);
719
-			}
720
-
721
-			try {
722
-				$provider->load();
723
-
724
-				self::$altLogin[] = [
725
-					'name' => $provider->getLabel(),
726
-					'href' => $provider->getLink(),
727
-					'style' => $provider->getClass(),
728
-				];
729
-			} catch (Throwable $e) {
730
-				\OC::$server->getLogger()->logException($e, [
731
-					'message' => 'Alternative login option {option} had an error while loading.',
732
-					'option' => $registration->getService(),
733
-					'app' => $registration->getAppId(),
734
-				]);
735
-			}
736
-		}
737
-
738
-		return self::$altLogin;
739
-	}
740
-
741
-	/**
742
-	 * get a list of all apps in the apps folder
743
-	 *
744
-	 * @return string[] an array of app names (string IDs)
745
-	 * @todo: change the name of this method to getInstalledApps, which is more accurate
746
-	 */
747
-	public static function getAllApps(): array {
748
-		$apps = [];
749
-
750
-		foreach (OC::$APPSROOTS as $apps_dir) {
751
-			if (!is_readable($apps_dir['path'])) {
752
-				\OCP\Util::writeLog('core', 'unable to read app folder : ' . $apps_dir['path'], ILogger::WARN);
753
-				continue;
754
-			}
755
-			$dh = opendir($apps_dir['path']);
756
-
757
-			if (is_resource($dh)) {
758
-				while (($file = readdir($dh)) !== false) {
759
-					if ($file[0] != '.' and is_dir($apps_dir['path'] . '/' . $file) and is_file($apps_dir['path'] . '/' . $file . '/appinfo/info.xml')) {
760
-						$apps[] = $file;
761
-					}
762
-				}
763
-			}
764
-		}
765
-
766
-		$apps = array_unique($apps);
767
-
768
-		return $apps;
769
-	}
770
-
771
-	/**
772
-	 * List all apps, this is used in apps.php
773
-	 *
774
-	 * @return array
775
-	 */
776
-	public function listAllApps(): array {
777
-		$installedApps = OC_App::getAllApps();
778
-
779
-		$appManager = \OC::$server->getAppManager();
780
-		//we don't want to show configuration for these
781
-		$blacklist = $appManager->getAlwaysEnabledApps();
782
-		$appList = [];
783
-		$langCode = \OC::$server->getL10N('core')->getLanguageCode();
784
-		$urlGenerator = \OC::$server->getURLGenerator();
785
-		/** @var \OCP\Support\Subscription\IRegistry $subscriptionRegistry */
786
-		$subscriptionRegistry = \OC::$server->query(\OCP\Support\Subscription\IRegistry::class);
787
-		$supportedApps = $subscriptionRegistry->delegateGetSupportedApps();
788
-
789
-		foreach ($installedApps as $app) {
790
-			if (array_search($app, $blacklist) === false) {
791
-				$info = OC_App::getAppInfo($app, false, $langCode);
792
-				if (!is_array($info)) {
793
-					\OCP\Util::writeLog('core', 'Could not read app info file for app "' . $app . '"', ILogger::ERROR);
794
-					continue;
795
-				}
796
-
797
-				if (!isset($info['name'])) {
798
-					\OCP\Util::writeLog('core', 'App id "' . $app . '" has no name in appinfo', ILogger::ERROR);
799
-					continue;
800
-				}
801
-
802
-				$enabled = \OC::$server->getConfig()->getAppValue($app, 'enabled', 'no');
803
-				$info['groups'] = null;
804
-				if ($enabled === 'yes') {
805
-					$active = true;
806
-				} elseif ($enabled === 'no') {
807
-					$active = false;
808
-				} else {
809
-					$active = true;
810
-					$info['groups'] = $enabled;
811
-				}
812
-
813
-				$info['active'] = $active;
814
-
815
-				if ($appManager->isShipped($app)) {
816
-					$info['internal'] = true;
817
-					$info['level'] = self::officialApp;
818
-					$info['removable'] = false;
819
-				} else {
820
-					$info['internal'] = false;
821
-					$info['removable'] = true;
822
-				}
823
-
824
-				if (in_array($app, $supportedApps)) {
825
-					$info['level'] = self::supportedApp;
826
-				}
827
-
828
-				$appPath = self::getAppPath($app);
829
-				if ($appPath !== false) {
830
-					$appIcon = $appPath . '/img/' . $app . '.svg';
831
-					if (file_exists($appIcon)) {
832
-						$info['preview'] = $urlGenerator->imagePath($app, $app . '.svg');
833
-						$info['previewAsIcon'] = true;
834
-					} else {
835
-						$appIcon = $appPath . '/img/app.svg';
836
-						if (file_exists($appIcon)) {
837
-							$info['preview'] = $urlGenerator->imagePath($app, 'app.svg');
838
-							$info['previewAsIcon'] = true;
839
-						}
840
-					}
841
-				}
842
-				// fix documentation
843
-				if (isset($info['documentation']) && is_array($info['documentation'])) {
844
-					foreach ($info['documentation'] as $key => $url) {
845
-						// If it is not an absolute URL we assume it is a key
846
-						// i.e. admin-ldap will get converted to go.php?to=admin-ldap
847
-						if (stripos($url, 'https://') !== 0 && stripos($url, 'http://') !== 0) {
848
-							$url = $urlGenerator->linkToDocs($url);
849
-						}
850
-
851
-						$info['documentation'][$key] = $url;
852
-					}
853
-				}
854
-
855
-				$info['version'] = OC_App::getAppVersion($app);
856
-				$appList[] = $info;
857
-			}
858
-		}
859
-
860
-		return $appList;
861
-	}
862
-
863
-	public static function shouldUpgrade(string $app): bool {
864
-		$versions = self::getAppVersions();
865
-		$currentVersion = OC_App::getAppVersion($app);
866
-		if ($currentVersion && isset($versions[$app])) {
867
-			$installedVersion = $versions[$app];
868
-			if (!version_compare($currentVersion, $installedVersion, '=')) {
869
-				return true;
870
-			}
871
-		}
872
-		return false;
873
-	}
874
-
875
-	/**
876
-	 * Adjust the number of version parts of $version1 to match
877
-	 * the number of version parts of $version2.
878
-	 *
879
-	 * @param string $version1 version to adjust
880
-	 * @param string $version2 version to take the number of parts from
881
-	 * @return string shortened $version1
882
-	 */
883
-	private static function adjustVersionParts(string $version1, string $version2): string {
884
-		$version1 = explode('.', $version1);
885
-		$version2 = explode('.', $version2);
886
-		// reduce $version1 to match the number of parts in $version2
887
-		while (count($version1) > count($version2)) {
888
-			array_pop($version1);
889
-		}
890
-		// if $version1 does not have enough parts, add some
891
-		while (count($version1) < count($version2)) {
892
-			$version1[] = '0';
893
-		}
894
-		return implode('.', $version1);
895
-	}
896
-
897
-	/**
898
-	 * Check whether the current ownCloud version matches the given
899
-	 * application's version requirements.
900
-	 *
901
-	 * The comparison is made based on the number of parts that the
902
-	 * app info version has. For example for ownCloud 6.0.3 if the
903
-	 * app info version is expecting version 6.0, the comparison is
904
-	 * made on the first two parts of the ownCloud version.
905
-	 * This means that it's possible to specify "requiremin" => 6
906
-	 * and "requiremax" => 6 and it will still match ownCloud 6.0.3.
907
-	 *
908
-	 * @param string $ocVersion ownCloud version to check against
909
-	 * @param array $appInfo app info (from xml)
910
-	 *
911
-	 * @return boolean true if compatible, otherwise false
912
-	 */
913
-	public static function isAppCompatible(string $ocVersion, array $appInfo, bool $ignoreMax = false): bool {
914
-		$requireMin = '';
915
-		$requireMax = '';
916
-		if (isset($appInfo['dependencies']['nextcloud']['@attributes']['min-version'])) {
917
-			$requireMin = $appInfo['dependencies']['nextcloud']['@attributes']['min-version'];
918
-		} elseif (isset($appInfo['dependencies']['owncloud']['@attributes']['min-version'])) {
919
-			$requireMin = $appInfo['dependencies']['owncloud']['@attributes']['min-version'];
920
-		} elseif (isset($appInfo['requiremin'])) {
921
-			$requireMin = $appInfo['requiremin'];
922
-		} elseif (isset($appInfo['require'])) {
923
-			$requireMin = $appInfo['require'];
924
-		}
925
-
926
-		if (isset($appInfo['dependencies']['nextcloud']['@attributes']['max-version'])) {
927
-			$requireMax = $appInfo['dependencies']['nextcloud']['@attributes']['max-version'];
928
-		} elseif (isset($appInfo['dependencies']['owncloud']['@attributes']['max-version'])) {
929
-			$requireMax = $appInfo['dependencies']['owncloud']['@attributes']['max-version'];
930
-		} elseif (isset($appInfo['requiremax'])) {
931
-			$requireMax = $appInfo['requiremax'];
932
-		}
933
-
934
-		if (!empty($requireMin)
935
-			&& version_compare(self::adjustVersionParts($ocVersion, $requireMin), $requireMin, '<')
936
-		) {
937
-			return false;
938
-		}
939
-
940
-		if (!$ignoreMax && !empty($requireMax)
941
-			&& version_compare(self::adjustVersionParts($ocVersion, $requireMax), $requireMax, '>')
942
-		) {
943
-			return false;
944
-		}
945
-
946
-		return true;
947
-	}
948
-
949
-	/**
950
-	 * get the installed version of all apps
951
-	 */
952
-	public static function getAppVersions() {
953
-		static $versions;
954
-
955
-		if (!$versions) {
956
-			$appConfig = \OC::$server->getAppConfig();
957
-			$versions = $appConfig->getValues(false, 'installed_version');
958
-		}
959
-		return $versions;
960
-	}
961
-
962
-	/**
963
-	 * update the database for the app and call the update script
964
-	 *
965
-	 * @param string $appId
966
-	 * @return bool
967
-	 */
968
-	public static function updateApp(string $appId): bool {
969
-		$appPath = self::getAppPath($appId);
970
-		if ($appPath === false) {
971
-			return false;
972
-		}
973
-
974
-		\OC::$server->getAppManager()->clearAppsCache();
975
-		$appData = self::getAppInfo($appId);
976
-
977
-		$ignoreMaxApps = \OC::$server->getConfig()->getSystemValue('app_install_overwrite', []);
978
-		$ignoreMax = in_array($appId, $ignoreMaxApps, true);
979
-		\OC_App::checkAppDependencies(
980
-			\OC::$server->getConfig(),
981
-			\OC::$server->getL10N('core'),
982
-			$appData,
983
-			$ignoreMax
984
-		);
985
-
986
-		self::registerAutoloading($appId, $appPath, true);
987
-		self::executeRepairSteps($appId, $appData['repair-steps']['pre-migration']);
988
-
989
-		if (file_exists($appPath . '/appinfo/database.xml')) {
990
-			OC_DB::updateDbFromStructure($appPath . '/appinfo/database.xml');
991
-		} else {
992
-			$ms = new MigrationService($appId, \OC::$server->get(\OC\DB\Connection::class));
993
-			$ms->migrate();
994
-		}
995
-
996
-		self::executeRepairSteps($appId, $appData['repair-steps']['post-migration']);
997
-		self::setupLiveMigrations($appId, $appData['repair-steps']['live-migration']);
998
-		// update appversion in app manager
999
-		\OC::$server->getAppManager()->clearAppsCache();
1000
-		\OC::$server->getAppManager()->getAppVersion($appId, false);
1001
-
1002
-		self::setupBackgroundJobs($appData['background-jobs']);
1003
-
1004
-		//set remote/public handlers
1005
-		if (array_key_exists('ocsid', $appData)) {
1006
-			\OC::$server->getConfig()->setAppValue($appId, 'ocsid', $appData['ocsid']);
1007
-		} elseif (\OC::$server->getConfig()->getAppValue($appId, 'ocsid', null) !== null) {
1008
-			\OC::$server->getConfig()->deleteAppValue($appId, 'ocsid');
1009
-		}
1010
-		foreach ($appData['remote'] as $name => $path) {
1011
-			\OC::$server->getConfig()->setAppValue('core', 'remote_' . $name, $appId . '/' . $path);
1012
-		}
1013
-		foreach ($appData['public'] as $name => $path) {
1014
-			\OC::$server->getConfig()->setAppValue('core', 'public_' . $name, $appId . '/' . $path);
1015
-		}
1016
-
1017
-		self::setAppTypes($appId);
1018
-
1019
-		$version = \OC_App::getAppVersion($appId);
1020
-		\OC::$server->getConfig()->setAppValue($appId, 'installed_version', $version);
1021
-
1022
-		\OC::$server->getEventDispatcher()->dispatch(ManagerEvent::EVENT_APP_UPDATE, new ManagerEvent(
1023
-			ManagerEvent::EVENT_APP_UPDATE, $appId
1024
-		));
1025
-
1026
-		return true;
1027
-	}
1028
-
1029
-	/**
1030
-	 * @param string $appId
1031
-	 * @param string[] $steps
1032
-	 * @throws \OC\NeedsUpdateException
1033
-	 */
1034
-	public static function executeRepairSteps(string $appId, array $steps) {
1035
-		if (empty($steps)) {
1036
-			return;
1037
-		}
1038
-		// load the app
1039
-		self::loadApp($appId);
1040
-
1041
-		$dispatcher = OC::$server->getEventDispatcher();
1042
-
1043
-		// load the steps
1044
-		$r = new Repair([], $dispatcher);
1045
-		foreach ($steps as $step) {
1046
-			try {
1047
-				$r->addStep($step);
1048
-			} catch (Exception $ex) {
1049
-				$r->emit('\OC\Repair', 'error', [$ex->getMessage()]);
1050
-				\OC::$server->getLogger()->logException($ex);
1051
-			}
1052
-		}
1053
-		// run the steps
1054
-		$r->run();
1055
-	}
1056
-
1057
-	public static function setupBackgroundJobs(array $jobs) {
1058
-		$queue = \OC::$server->getJobList();
1059
-		foreach ($jobs as $job) {
1060
-			$queue->add($job);
1061
-		}
1062
-	}
1063
-
1064
-	/**
1065
-	 * @param string $appId
1066
-	 * @param string[] $steps
1067
-	 */
1068
-	private static function setupLiveMigrations(string $appId, array $steps) {
1069
-		$queue = \OC::$server->getJobList();
1070
-		foreach ($steps as $step) {
1071
-			$queue->add('OC\Migration\BackgroundRepair', [
1072
-				'app' => $appId,
1073
-				'step' => $step]);
1074
-		}
1075
-	}
1076
-
1077
-	/**
1078
-	 * @param string $appId
1079
-	 * @return \OC\Files\View|false
1080
-	 */
1081
-	public static function getStorage(string $appId) {
1082
-		if (\OC::$server->getAppManager()->isEnabledForUser($appId)) { //sanity check
1083
-			if (\OC::$server->getUserSession()->isLoggedIn()) {
1084
-				$view = new \OC\Files\View('/' . OC_User::getUser());
1085
-				if (!$view->file_exists($appId)) {
1086
-					$view->mkdir($appId);
1087
-				}
1088
-				return new \OC\Files\View('/' . OC_User::getUser() . '/' . $appId);
1089
-			} else {
1090
-				\OCP\Util::writeLog('core', 'Can\'t get app storage, app ' . $appId . ', user not logged in', ILogger::ERROR);
1091
-				return false;
1092
-			}
1093
-		} else {
1094
-			\OCP\Util::writeLog('core', 'Can\'t get app storage, app ' . $appId . ' not enabled', ILogger::ERROR);
1095
-			return false;
1096
-		}
1097
-	}
1098
-
1099
-	protected static function findBestL10NOption(array $options, string $lang): string {
1100
-		// only a single option
1101
-		if (isset($options['@value'])) {
1102
-			return $options['@value'];
1103
-		}
1104
-
1105
-		$fallback = $similarLangFallback = $englishFallback = false;
1106
-
1107
-		$lang = strtolower($lang);
1108
-		$similarLang = $lang;
1109
-		if (strpos($similarLang, '_')) {
1110
-			// For "de_DE" we want to find "de" and the other way around
1111
-			$similarLang = substr($lang, 0, strpos($lang, '_'));
1112
-		}
1113
-
1114
-		foreach ($options as $option) {
1115
-			if (is_array($option)) {
1116
-				if ($fallback === false) {
1117
-					$fallback = $option['@value'];
1118
-				}
1119
-
1120
-				if (!isset($option['@attributes']['lang'])) {
1121
-					continue;
1122
-				}
1123
-
1124
-				$attributeLang = strtolower($option['@attributes']['lang']);
1125
-				if ($attributeLang === $lang) {
1126
-					return $option['@value'];
1127
-				}
1128
-
1129
-				if ($attributeLang === $similarLang) {
1130
-					$similarLangFallback = $option['@value'];
1131
-				} elseif (strpos($attributeLang, $similarLang . '_') === 0) {
1132
-					if ($similarLangFallback === false) {
1133
-						$similarLangFallback = $option['@value'];
1134
-					}
1135
-				}
1136
-			} else {
1137
-				$englishFallback = $option;
1138
-			}
1139
-		}
1140
-
1141
-		if ($similarLangFallback !== false) {
1142
-			return $similarLangFallback;
1143
-		} elseif ($englishFallback !== false) {
1144
-			return $englishFallback;
1145
-		}
1146
-		return (string) $fallback;
1147
-	}
1148
-
1149
-	/**
1150
-	 * parses the app data array and enhanced the 'description' value
1151
-	 *
1152
-	 * @param array $data the app data
1153
-	 * @param string $lang
1154
-	 * @return array improved app data
1155
-	 */
1156
-	public static function parseAppInfo(array $data, $lang = null): array {
1157
-		if ($lang && isset($data['name']) && is_array($data['name'])) {
1158
-			$data['name'] = self::findBestL10NOption($data['name'], $lang);
1159
-		}
1160
-		if ($lang && isset($data['summary']) && is_array($data['summary'])) {
1161
-			$data['summary'] = self::findBestL10NOption($data['summary'], $lang);
1162
-		}
1163
-		if ($lang && isset($data['description']) && is_array($data['description'])) {
1164
-			$data['description'] = trim(self::findBestL10NOption($data['description'], $lang));
1165
-		} elseif (isset($data['description']) && is_string($data['description'])) {
1166
-			$data['description'] = trim($data['description']);
1167
-		} else {
1168
-			$data['description'] = '';
1169
-		}
1170
-
1171
-		return $data;
1172
-	}
1173
-
1174
-	/**
1175
-	 * @param \OCP\IConfig $config
1176
-	 * @param \OCP\IL10N $l
1177
-	 * @param array $info
1178
-	 * @throws \Exception
1179
-	 */
1180
-	public static function checkAppDependencies(\OCP\IConfig $config, \OCP\IL10N $l, array $info, bool $ignoreMax) {
1181
-		$dependencyAnalyzer = new DependencyAnalyzer(new Platform($config), $l);
1182
-		$missing = $dependencyAnalyzer->analyze($info, $ignoreMax);
1183
-		if (!empty($missing)) {
1184
-			$missingMsg = implode(PHP_EOL, $missing);
1185
-			throw new \Exception(
1186
-				$l->t('App "%1$s" cannot be installed because the following dependencies are not fulfilled: %2$s',
1187
-					[$info['name'], $missingMsg]
1188
-				)
1189
-			);
1190
-		}
1191
-	}
71
+    private static $adminForms = [];
72
+    private static $personalForms = [];
73
+    private static $appTypes = [];
74
+    private static $loadedApps = [];
75
+    private static $altLogin = [];
76
+    private static $alreadyRegistered = [];
77
+    public const supportedApp = 300;
78
+    public const officialApp = 200;
79
+
80
+    /**
81
+     * clean the appId
82
+     *
83
+     * @psalm-taint-escape file
84
+     * @psalm-taint-escape include
85
+     *
86
+     * @param string $app AppId that needs to be cleaned
87
+     * @return string
88
+     */
89
+    public static function cleanAppId(string $app): string {
90
+        return str_replace(['\0', '/', '\\', '..'], '', $app);
91
+    }
92
+
93
+    /**
94
+     * Check if an app is loaded
95
+     *
96
+     * @param string $app
97
+     * @return bool
98
+     */
99
+    public static function isAppLoaded(string $app): bool {
100
+        return isset(self::$loadedApps[$app]);
101
+    }
102
+
103
+    /**
104
+     * loads all apps
105
+     *
106
+     * @param string[] $types
107
+     * @return bool
108
+     *
109
+     * This function walks through the ownCloud directory and loads all apps
110
+     * it can find. A directory contains an app if the file /appinfo/info.xml
111
+     * exists.
112
+     *
113
+     * if $types is set to non-empty array, only apps of those types will be loaded
114
+     */
115
+    public static function loadApps(array $types = []): bool {
116
+        if ((bool) \OC::$server->getSystemConfig()->getValue('maintenance', false)) {
117
+            return false;
118
+        }
119
+        // Load the enabled apps here
120
+        $apps = self::getEnabledApps();
121
+
122
+        // Add each apps' folder as allowed class path
123
+        foreach ($apps as $app) {
124
+            // If the app is already loaded then autoloading it makes no sense
125
+            if (!isset(self::$loadedApps[$app])) {
126
+                $path = self::getAppPath($app);
127
+                if ($path !== false) {
128
+                    self::registerAutoloading($app, $path);
129
+                }
130
+            }
131
+        }
132
+
133
+        // prevent app.php from printing output
134
+        ob_start();
135
+        foreach ($apps as $app) {
136
+            if (!isset(self::$loadedApps[$app]) && ($types === [] || self::isType($app, $types))) {
137
+                self::loadApp($app);
138
+            }
139
+        }
140
+        ob_end_clean();
141
+
142
+        return true;
143
+    }
144
+
145
+    /**
146
+     * load a single app
147
+     *
148
+     * @param string $app
149
+     * @throws Exception
150
+     */
151
+    public static function loadApp(string $app) {
152
+        self::$loadedApps[$app] = true;
153
+        $appPath = self::getAppPath($app);
154
+        if ($appPath === false) {
155
+            return;
156
+        }
157
+
158
+        // in case someone calls loadApp() directly
159
+        self::registerAutoloading($app, $appPath);
160
+
161
+        /** @var Coordinator $coordinator */
162
+        $coordinator = \OC::$server->query(Coordinator::class);
163
+        $isBootable = $coordinator->isBootable($app);
164
+
165
+        $hasAppPhpFile = is_file($appPath . '/appinfo/app.php');
166
+
167
+        if ($isBootable && $hasAppPhpFile) {
168
+            \OC::$server->getLogger()->error('/appinfo/app.php is not loaded when \OCP\AppFramework\Bootstrap\IBootstrap on the application class is used. Migrate everything from app.php to the Application class.', [
169
+                'app' => $app,
170
+            ]);
171
+        } elseif ($hasAppPhpFile) {
172
+            \OC::$server->getLogger()->debug('/appinfo/app.php is deprecated, use \OCP\AppFramework\Bootstrap\IBootstrap on the application class instead.', [
173
+                'app' => $app,
174
+            ]);
175
+            \OC::$server->getEventLogger()->start('load_app_' . $app, 'Load app: ' . $app);
176
+            try {
177
+                self::requireAppFile($app);
178
+            } catch (Throwable $ex) {
179
+                if ($ex instanceof ServerNotAvailableException) {
180
+                    throw $ex;
181
+                }
182
+                if (!\OC::$server->getAppManager()->isShipped($app) && !self::isType($app, ['authentication'])) {
183
+                    \OC::$server->getLogger()->logException($ex, [
184
+                        'message' => "App $app threw an error during app.php load and will be disabled: " . $ex->getMessage(),
185
+                    ]);
186
+
187
+                    // Only disable apps which are not shipped and that are not authentication apps
188
+                    \OC::$server->getAppManager()->disableApp($app, true);
189
+                } else {
190
+                    \OC::$server->getLogger()->logException($ex, [
191
+                        'message' => "App $app threw an error during app.php load: " . $ex->getMessage(),
192
+                    ]);
193
+                }
194
+            }
195
+            \OC::$server->getEventLogger()->end('load_app_' . $app);
196
+        }
197
+        $coordinator->bootApp($app);
198
+
199
+        $info = self::getAppInfo($app);
200
+        if (!empty($info['activity']['filters'])) {
201
+            foreach ($info['activity']['filters'] as $filter) {
202
+                \OC::$server->getActivityManager()->registerFilter($filter);
203
+            }
204
+        }
205
+        if (!empty($info['activity']['settings'])) {
206
+            foreach ($info['activity']['settings'] as $setting) {
207
+                \OC::$server->getActivityManager()->registerSetting($setting);
208
+            }
209
+        }
210
+        if (!empty($info['activity']['providers'])) {
211
+            foreach ($info['activity']['providers'] as $provider) {
212
+                \OC::$server->getActivityManager()->registerProvider($provider);
213
+            }
214
+        }
215
+
216
+        if (!empty($info['settings']['admin'])) {
217
+            foreach ($info['settings']['admin'] as $setting) {
218
+                \OC::$server->getSettingsManager()->registerSetting('admin', $setting);
219
+            }
220
+        }
221
+        if (!empty($info['settings']['admin-section'])) {
222
+            foreach ($info['settings']['admin-section'] as $section) {
223
+                \OC::$server->getSettingsManager()->registerSection('admin', $section);
224
+            }
225
+        }
226
+        if (!empty($info['settings']['personal'])) {
227
+            foreach ($info['settings']['personal'] as $setting) {
228
+                \OC::$server->getSettingsManager()->registerSetting('personal', $setting);
229
+            }
230
+        }
231
+        if (!empty($info['settings']['personal-section'])) {
232
+            foreach ($info['settings']['personal-section'] as $section) {
233
+                \OC::$server->getSettingsManager()->registerSection('personal', $section);
234
+            }
235
+        }
236
+
237
+        if (!empty($info['collaboration']['plugins'])) {
238
+            // deal with one or many plugin entries
239
+            $plugins = isset($info['collaboration']['plugins']['plugin']['@value']) ?
240
+                [$info['collaboration']['plugins']['plugin']] : $info['collaboration']['plugins']['plugin'];
241
+            foreach ($plugins as $plugin) {
242
+                if ($plugin['@attributes']['type'] === 'collaborator-search') {
243
+                    $pluginInfo = [
244
+                        'shareType' => $plugin['@attributes']['share-type'],
245
+                        'class' => $plugin['@value'],
246
+                    ];
247
+                    \OC::$server->getCollaboratorSearch()->registerPlugin($pluginInfo);
248
+                } elseif ($plugin['@attributes']['type'] === 'autocomplete-sort') {
249
+                    \OC::$server->getAutoCompleteManager()->registerSorter($plugin['@value']);
250
+                }
251
+            }
252
+        }
253
+    }
254
+
255
+    /**
256
+     * @internal
257
+     * @param string $app
258
+     * @param string $path
259
+     * @param bool $force
260
+     */
261
+    public static function registerAutoloading(string $app, string $path, bool $force = false) {
262
+        $key = $app . '-' . $path;
263
+        if (!$force && isset(self::$alreadyRegistered[$key])) {
264
+            return;
265
+        }
266
+
267
+        self::$alreadyRegistered[$key] = true;
268
+
269
+        // Register on PSR-4 composer autoloader
270
+        $appNamespace = \OC\AppFramework\App::buildAppNamespace($app);
271
+        \OC::$server->registerNamespace($app, $appNamespace);
272
+
273
+        if (file_exists($path . '/composer/autoload.php')) {
274
+            require_once $path . '/composer/autoload.php';
275
+        } else {
276
+            \OC::$composerAutoloader->addPsr4($appNamespace . '\\', $path . '/lib/', true);
277
+            // Register on legacy autoloader
278
+            \OC::$loader->addValidRoot($path);
279
+        }
280
+
281
+        // Register Test namespace only when testing
282
+        if (defined('PHPUNIT_RUN') || defined('CLI_TEST_RUN')) {
283
+            \OC::$composerAutoloader->addPsr4($appNamespace . '\\Tests\\', $path . '/tests/', true);
284
+        }
285
+    }
286
+
287
+    /**
288
+     * Load app.php from the given app
289
+     *
290
+     * @param string $app app name
291
+     * @throws Error
292
+     */
293
+    private static function requireAppFile(string $app) {
294
+        // encapsulated here to avoid variable scope conflicts
295
+        require_once $app . '/appinfo/app.php';
296
+    }
297
+
298
+    /**
299
+     * check if an app is of a specific type
300
+     *
301
+     * @param string $app
302
+     * @param array $types
303
+     * @return bool
304
+     */
305
+    public static function isType(string $app, array $types): bool {
306
+        $appTypes = self::getAppTypes($app);
307
+        foreach ($types as $type) {
308
+            if (array_search($type, $appTypes) !== false) {
309
+                return true;
310
+            }
311
+        }
312
+        return false;
313
+    }
314
+
315
+    /**
316
+     * get the types of an app
317
+     *
318
+     * @param string $app
319
+     * @return array
320
+     */
321
+    private static function getAppTypes(string $app): array {
322
+        //load the cache
323
+        if (count(self::$appTypes) == 0) {
324
+            self::$appTypes = \OC::$server->getAppConfig()->getValues(false, 'types');
325
+        }
326
+
327
+        if (isset(self::$appTypes[$app])) {
328
+            return explode(',', self::$appTypes[$app]);
329
+        }
330
+
331
+        return [];
332
+    }
333
+
334
+    /**
335
+     * read app types from info.xml and cache them in the database
336
+     */
337
+    public static function setAppTypes(string $app) {
338
+        $appManager = \OC::$server->getAppManager();
339
+        $appData = $appManager->getAppInfo($app);
340
+        if (!is_array($appData)) {
341
+            return;
342
+        }
343
+
344
+        if (isset($appData['types'])) {
345
+            $appTypes = implode(',', $appData['types']);
346
+        } else {
347
+            $appTypes = '';
348
+            $appData['types'] = [];
349
+        }
350
+
351
+        $config = \OC::$server->getConfig();
352
+        $config->setAppValue($app, 'types', $appTypes);
353
+
354
+        if ($appManager->hasProtectedAppType($appData['types'])) {
355
+            $enabled = $config->getAppValue($app, 'enabled', 'yes');
356
+            if ($enabled !== 'yes' && $enabled !== 'no') {
357
+                $config->setAppValue($app, 'enabled', 'yes');
358
+            }
359
+        }
360
+    }
361
+
362
+    /**
363
+     * Returns apps enabled for the current user.
364
+     *
365
+     * @param bool $forceRefresh whether to refresh the cache
366
+     * @param bool $all whether to return apps for all users, not only the
367
+     * currently logged in one
368
+     * @return string[]
369
+     */
370
+    public static function getEnabledApps(bool $forceRefresh = false, bool $all = false): array {
371
+        if (!\OC::$server->getSystemConfig()->getValue('installed', false)) {
372
+            return [];
373
+        }
374
+        // in incognito mode or when logged out, $user will be false,
375
+        // which is also the case during an upgrade
376
+        $appManager = \OC::$server->getAppManager();
377
+        if ($all) {
378
+            $user = null;
379
+        } else {
380
+            $user = \OC::$server->getUserSession()->getUser();
381
+        }
382
+
383
+        if (is_null($user)) {
384
+            $apps = $appManager->getInstalledApps();
385
+        } else {
386
+            $apps = $appManager->getEnabledAppsForUser($user);
387
+        }
388
+        $apps = array_filter($apps, function ($app) {
389
+            return $app !== 'files';//we add this manually
390
+        });
391
+        sort($apps);
392
+        array_unshift($apps, 'files');
393
+        return $apps;
394
+    }
395
+
396
+    /**
397
+     * checks whether or not an app is enabled
398
+     *
399
+     * @param string $app app
400
+     * @return bool
401
+     * @deprecated 13.0.0 use \OC::$server->getAppManager()->isEnabledForUser($appId)
402
+     *
403
+     * This function checks whether or not an app is enabled.
404
+     */
405
+    public static function isEnabled(string $app): bool {
406
+        return \OC::$server->getAppManager()->isEnabledForUser($app);
407
+    }
408
+
409
+    /**
410
+     * enables an app
411
+     *
412
+     * @param string $appId
413
+     * @param array $groups (optional) when set, only these groups will have access to the app
414
+     * @throws \Exception
415
+     * @return void
416
+     *
417
+     * This function set an app as enabled in appconfig.
418
+     */
419
+    public function enable(string $appId,
420
+                            array $groups = []) {
421
+
422
+        // Check if app is already downloaded
423
+        /** @var Installer $installer */
424
+        $installer = \OC::$server->query(Installer::class);
425
+        $isDownloaded = $installer->isDownloaded($appId);
426
+
427
+        if (!$isDownloaded) {
428
+            $installer->downloadApp($appId);
429
+        }
430
+
431
+        $installer->installApp($appId);
432
+
433
+        $appManager = \OC::$server->getAppManager();
434
+        if ($groups !== []) {
435
+            $groupManager = \OC::$server->getGroupManager();
436
+            $groupsList = [];
437
+            foreach ($groups as $group) {
438
+                $groupItem = $groupManager->get($group);
439
+                if ($groupItem instanceof \OCP\IGroup) {
440
+                    $groupsList[] = $groupManager->get($group);
441
+                }
442
+            }
443
+            $appManager->enableAppForGroups($appId, $groupsList);
444
+        } else {
445
+            $appManager->enableApp($appId);
446
+        }
447
+    }
448
+
449
+    /**
450
+     * Get the path where to install apps
451
+     *
452
+     * @return string|false
453
+     */
454
+    public static function getInstallPath() {
455
+        if (\OC::$server->getSystemConfig()->getValue('appstoreenabled', true) == false) {
456
+            return false;
457
+        }
458
+
459
+        foreach (OC::$APPSROOTS as $dir) {
460
+            if (isset($dir['writable']) && $dir['writable'] === true) {
461
+                return $dir['path'];
462
+            }
463
+        }
464
+
465
+        \OCP\Util::writeLog('core', 'No application directories are marked as writable.', ILogger::ERROR);
466
+        return null;
467
+    }
468
+
469
+
470
+    /**
471
+     * search for an app in all app-directories
472
+     *
473
+     * @param string $appId
474
+     * @return false|string
475
+     */
476
+    public static function findAppInDirectories(string $appId) {
477
+        $sanitizedAppId = self::cleanAppId($appId);
478
+        if ($sanitizedAppId !== $appId) {
479
+            return false;
480
+        }
481
+        static $app_dir = [];
482
+
483
+        if (isset($app_dir[$appId])) {
484
+            return $app_dir[$appId];
485
+        }
486
+
487
+        $possibleApps = [];
488
+        foreach (OC::$APPSROOTS as $dir) {
489
+            if (file_exists($dir['path'] . '/' . $appId)) {
490
+                $possibleApps[] = $dir;
491
+            }
492
+        }
493
+
494
+        if (empty($possibleApps)) {
495
+            return false;
496
+        } elseif (count($possibleApps) === 1) {
497
+            $dir = array_shift($possibleApps);
498
+            $app_dir[$appId] = $dir;
499
+            return $dir;
500
+        } else {
501
+            $versionToLoad = [];
502
+            foreach ($possibleApps as $possibleApp) {
503
+                $version = self::getAppVersionByPath($possibleApp['path'] . '/' . $appId);
504
+                if (empty($versionToLoad) || version_compare($version, $versionToLoad['version'], '>')) {
505
+                    $versionToLoad = [
506
+                        'dir' => $possibleApp,
507
+                        'version' => $version,
508
+                    ];
509
+                }
510
+            }
511
+            $app_dir[$appId] = $versionToLoad['dir'];
512
+            return $versionToLoad['dir'];
513
+            //TODO - write test
514
+        }
515
+    }
516
+
517
+    /**
518
+     * Get the directory for the given app.
519
+     * If the app is defined in multiple directories, the first one is taken. (false if not found)
520
+     *
521
+     * @psalm-taint-specialize
522
+     *
523
+     * @param string $appId
524
+     * @return string|false
525
+     * @deprecated 11.0.0 use \OC::$server->getAppManager()->getAppPath()
526
+     */
527
+    public static function getAppPath(string $appId) {
528
+        if ($appId === null || trim($appId) === '') {
529
+            return false;
530
+        }
531
+
532
+        if (($dir = self::findAppInDirectories($appId)) != false) {
533
+            return $dir['path'] . '/' . $appId;
534
+        }
535
+        return false;
536
+    }
537
+
538
+    /**
539
+     * Get the path for the given app on the access
540
+     * If the app is defined in multiple directories, the first one is taken. (false if not found)
541
+     *
542
+     * @param string $appId
543
+     * @return string|false
544
+     * @deprecated 18.0.0 use \OC::$server->getAppManager()->getAppWebPath()
545
+     */
546
+    public static function getAppWebPath(string $appId) {
547
+        if (($dir = self::findAppInDirectories($appId)) != false) {
548
+            return OC::$WEBROOT . $dir['url'] . '/' . $appId;
549
+        }
550
+        return false;
551
+    }
552
+
553
+    /**
554
+     * get the last version of the app from appinfo/info.xml
555
+     *
556
+     * @param string $appId
557
+     * @param bool $useCache
558
+     * @return string
559
+     * @deprecated 14.0.0 use \OC::$server->getAppManager()->getAppVersion()
560
+     */
561
+    public static function getAppVersion(string $appId, bool $useCache = true): string {
562
+        return \OC::$server->getAppManager()->getAppVersion($appId, $useCache);
563
+    }
564
+
565
+    /**
566
+     * get app's version based on it's path
567
+     *
568
+     * @param string $path
569
+     * @return string
570
+     */
571
+    public static function getAppVersionByPath(string $path): string {
572
+        $infoFile = $path . '/appinfo/info.xml';
573
+        $appData = \OC::$server->getAppManager()->getAppInfo($infoFile, true);
574
+        return isset($appData['version']) ? $appData['version'] : '';
575
+    }
576
+
577
+
578
+    /**
579
+     * Read all app metadata from the info.xml file
580
+     *
581
+     * @param string $appId id of the app or the path of the info.xml file
582
+     * @param bool $path
583
+     * @param string $lang
584
+     * @return array|null
585
+     * @note all data is read from info.xml, not just pre-defined fields
586
+     * @deprecated 14.0.0 use \OC::$server->getAppManager()->getAppInfo()
587
+     */
588
+    public static function getAppInfo(string $appId, bool $path = false, string $lang = null) {
589
+        return \OC::$server->getAppManager()->getAppInfo($appId, $path, $lang);
590
+    }
591
+
592
+    /**
593
+     * Returns the navigation
594
+     *
595
+     * @return array
596
+     * @deprecated 14.0.0 use \OC::$server->getNavigationManager()->getAll()
597
+     *
598
+     * This function returns an array containing all entries added. The
599
+     * entries are sorted by the key 'order' ascending. Additional to the keys
600
+     * given for each app the following keys exist:
601
+     *   - active: boolean, signals if the user is on this navigation entry
602
+     */
603
+    public static function getNavigation(): array {
604
+        return OC::$server->getNavigationManager()->getAll();
605
+    }
606
+
607
+    /**
608
+     * Returns the Settings Navigation
609
+     *
610
+     * @return string[]
611
+     * @deprecated 14.0.0 use \OC::$server->getNavigationManager()->getAll('settings')
612
+     *
613
+     * This function returns an array containing all settings pages added. The
614
+     * entries are sorted by the key 'order' ascending.
615
+     */
616
+    public static function getSettingsNavigation(): array {
617
+        return OC::$server->getNavigationManager()->getAll('settings');
618
+    }
619
+
620
+    /**
621
+     * get the id of loaded app
622
+     *
623
+     * @return string
624
+     */
625
+    public static function getCurrentApp(): string {
626
+        $request = \OC::$server->getRequest();
627
+        $script = substr($request->getScriptName(), strlen(OC::$WEBROOT) + 1);
628
+        $topFolder = substr($script, 0, strpos($script, '/') ?: 0);
629
+        if (empty($topFolder)) {
630
+            $path_info = $request->getPathInfo();
631
+            if ($path_info) {
632
+                $topFolder = substr($path_info, 1, strpos($path_info, '/', 1) - 1);
633
+            }
634
+        }
635
+        if ($topFolder == 'apps') {
636
+            $length = strlen($topFolder);
637
+            return substr($script, $length + 1, strpos($script, '/', $length + 1) - $length - 1) ?: '';
638
+        } else {
639
+            return $topFolder;
640
+        }
641
+    }
642
+
643
+    /**
644
+     * @param string $type
645
+     * @return array
646
+     */
647
+    public static function getForms(string $type): array {
648
+        $forms = [];
649
+        switch ($type) {
650
+            case 'admin':
651
+                $source = self::$adminForms;
652
+                break;
653
+            case 'personal':
654
+                $source = self::$personalForms;
655
+                break;
656
+            default:
657
+                return [];
658
+        }
659
+        foreach ($source as $form) {
660
+            $forms[] = include $form;
661
+        }
662
+        return $forms;
663
+    }
664
+
665
+    /**
666
+     * register an admin form to be shown
667
+     *
668
+     * @param string $app
669
+     * @param string $page
670
+     */
671
+    public static function registerAdmin(string $app, string $page) {
672
+        self::$adminForms[] = $app . '/' . $page . '.php';
673
+    }
674
+
675
+    /**
676
+     * register a personal form to be shown
677
+     * @param string $app
678
+     * @param string $page
679
+     */
680
+    public static function registerPersonal(string $app, string $page) {
681
+        self::$personalForms[] = $app . '/' . $page . '.php';
682
+    }
683
+
684
+    /**
685
+     * @param array $entry
686
+     * @deprecated 20.0.0 Please register your alternative login option using the registerAlternativeLogin() on the RegistrationContext in your Application class implementing the OCP\Authentication\IAlternativeLogin interface
687
+     */
688
+    public static function registerLogIn(array $entry) {
689
+        \OC::$server->getLogger()->debug('OC_App::registerLogIn() is deprecated, please register your alternative login option using the registerAlternativeLogin() on the RegistrationContext in your Application class implementing the OCP\Authentication\IAlternativeLogin interface');
690
+        self::$altLogin[] = $entry;
691
+    }
692
+
693
+    /**
694
+     * @return array
695
+     */
696
+    public static function getAlternativeLogIns(): array {
697
+        /** @var Coordinator $bootstrapCoordinator */
698
+        $bootstrapCoordinator = \OC::$server->query(Coordinator::class);
699
+
700
+        foreach ($bootstrapCoordinator->getRegistrationContext()->getAlternativeLogins() as $registration) {
701
+            if (!in_array(IAlternativeLogin::class, class_implements($registration->getService()), true)) {
702
+                \OC::$server->getLogger()->error('Alternative login option {option} does not implement {interface} and is therefore ignored.', [
703
+                    'option' => $registration->getService(),
704
+                    'interface' => IAlternativeLogin::class,
705
+                    'app' => $registration->getAppId(),
706
+                ]);
707
+                continue;
708
+            }
709
+
710
+            try {
711
+                /** @var IAlternativeLogin $provider */
712
+                $provider = \OC::$server->query($registration->getService());
713
+            } catch (QueryException $e) {
714
+                \OC::$server->getLogger()->logException($e, [
715
+                    'message' => 'Alternative login option {option} can not be initialised.',
716
+                    'option' => $registration->getService(),
717
+                    'app' => $registration->getAppId(),
718
+                ]);
719
+            }
720
+
721
+            try {
722
+                $provider->load();
723
+
724
+                self::$altLogin[] = [
725
+                    'name' => $provider->getLabel(),
726
+                    'href' => $provider->getLink(),
727
+                    'style' => $provider->getClass(),
728
+                ];
729
+            } catch (Throwable $e) {
730
+                \OC::$server->getLogger()->logException($e, [
731
+                    'message' => 'Alternative login option {option} had an error while loading.',
732
+                    'option' => $registration->getService(),
733
+                    'app' => $registration->getAppId(),
734
+                ]);
735
+            }
736
+        }
737
+
738
+        return self::$altLogin;
739
+    }
740
+
741
+    /**
742
+     * get a list of all apps in the apps folder
743
+     *
744
+     * @return string[] an array of app names (string IDs)
745
+     * @todo: change the name of this method to getInstalledApps, which is more accurate
746
+     */
747
+    public static function getAllApps(): array {
748
+        $apps = [];
749
+
750
+        foreach (OC::$APPSROOTS as $apps_dir) {
751
+            if (!is_readable($apps_dir['path'])) {
752
+                \OCP\Util::writeLog('core', 'unable to read app folder : ' . $apps_dir['path'], ILogger::WARN);
753
+                continue;
754
+            }
755
+            $dh = opendir($apps_dir['path']);
756
+
757
+            if (is_resource($dh)) {
758
+                while (($file = readdir($dh)) !== false) {
759
+                    if ($file[0] != '.' and is_dir($apps_dir['path'] . '/' . $file) and is_file($apps_dir['path'] . '/' . $file . '/appinfo/info.xml')) {
760
+                        $apps[] = $file;
761
+                    }
762
+                }
763
+            }
764
+        }
765
+
766
+        $apps = array_unique($apps);
767
+
768
+        return $apps;
769
+    }
770
+
771
+    /**
772
+     * List all apps, this is used in apps.php
773
+     *
774
+     * @return array
775
+     */
776
+    public function listAllApps(): array {
777
+        $installedApps = OC_App::getAllApps();
778
+
779
+        $appManager = \OC::$server->getAppManager();
780
+        //we don't want to show configuration for these
781
+        $blacklist = $appManager->getAlwaysEnabledApps();
782
+        $appList = [];
783
+        $langCode = \OC::$server->getL10N('core')->getLanguageCode();
784
+        $urlGenerator = \OC::$server->getURLGenerator();
785
+        /** @var \OCP\Support\Subscription\IRegistry $subscriptionRegistry */
786
+        $subscriptionRegistry = \OC::$server->query(\OCP\Support\Subscription\IRegistry::class);
787
+        $supportedApps = $subscriptionRegistry->delegateGetSupportedApps();
788
+
789
+        foreach ($installedApps as $app) {
790
+            if (array_search($app, $blacklist) === false) {
791
+                $info = OC_App::getAppInfo($app, false, $langCode);
792
+                if (!is_array($info)) {
793
+                    \OCP\Util::writeLog('core', 'Could not read app info file for app "' . $app . '"', ILogger::ERROR);
794
+                    continue;
795
+                }
796
+
797
+                if (!isset($info['name'])) {
798
+                    \OCP\Util::writeLog('core', 'App id "' . $app . '" has no name in appinfo', ILogger::ERROR);
799
+                    continue;
800
+                }
801
+
802
+                $enabled = \OC::$server->getConfig()->getAppValue($app, 'enabled', 'no');
803
+                $info['groups'] = null;
804
+                if ($enabled === 'yes') {
805
+                    $active = true;
806
+                } elseif ($enabled === 'no') {
807
+                    $active = false;
808
+                } else {
809
+                    $active = true;
810
+                    $info['groups'] = $enabled;
811
+                }
812
+
813
+                $info['active'] = $active;
814
+
815
+                if ($appManager->isShipped($app)) {
816
+                    $info['internal'] = true;
817
+                    $info['level'] = self::officialApp;
818
+                    $info['removable'] = false;
819
+                } else {
820
+                    $info['internal'] = false;
821
+                    $info['removable'] = true;
822
+                }
823
+
824
+                if (in_array($app, $supportedApps)) {
825
+                    $info['level'] = self::supportedApp;
826
+                }
827
+
828
+                $appPath = self::getAppPath($app);
829
+                if ($appPath !== false) {
830
+                    $appIcon = $appPath . '/img/' . $app . '.svg';
831
+                    if (file_exists($appIcon)) {
832
+                        $info['preview'] = $urlGenerator->imagePath($app, $app . '.svg');
833
+                        $info['previewAsIcon'] = true;
834
+                    } else {
835
+                        $appIcon = $appPath . '/img/app.svg';
836
+                        if (file_exists($appIcon)) {
837
+                            $info['preview'] = $urlGenerator->imagePath($app, 'app.svg');
838
+                            $info['previewAsIcon'] = true;
839
+                        }
840
+                    }
841
+                }
842
+                // fix documentation
843
+                if (isset($info['documentation']) && is_array($info['documentation'])) {
844
+                    foreach ($info['documentation'] as $key => $url) {
845
+                        // If it is not an absolute URL we assume it is a key
846
+                        // i.e. admin-ldap will get converted to go.php?to=admin-ldap
847
+                        if (stripos($url, 'https://') !== 0 && stripos($url, 'http://') !== 0) {
848
+                            $url = $urlGenerator->linkToDocs($url);
849
+                        }
850
+
851
+                        $info['documentation'][$key] = $url;
852
+                    }
853
+                }
854
+
855
+                $info['version'] = OC_App::getAppVersion($app);
856
+                $appList[] = $info;
857
+            }
858
+        }
859
+
860
+        return $appList;
861
+    }
862
+
863
+    public static function shouldUpgrade(string $app): bool {
864
+        $versions = self::getAppVersions();
865
+        $currentVersion = OC_App::getAppVersion($app);
866
+        if ($currentVersion && isset($versions[$app])) {
867
+            $installedVersion = $versions[$app];
868
+            if (!version_compare($currentVersion, $installedVersion, '=')) {
869
+                return true;
870
+            }
871
+        }
872
+        return false;
873
+    }
874
+
875
+    /**
876
+     * Adjust the number of version parts of $version1 to match
877
+     * the number of version parts of $version2.
878
+     *
879
+     * @param string $version1 version to adjust
880
+     * @param string $version2 version to take the number of parts from
881
+     * @return string shortened $version1
882
+     */
883
+    private static function adjustVersionParts(string $version1, string $version2): string {
884
+        $version1 = explode('.', $version1);
885
+        $version2 = explode('.', $version2);
886
+        // reduce $version1 to match the number of parts in $version2
887
+        while (count($version1) > count($version2)) {
888
+            array_pop($version1);
889
+        }
890
+        // if $version1 does not have enough parts, add some
891
+        while (count($version1) < count($version2)) {
892
+            $version1[] = '0';
893
+        }
894
+        return implode('.', $version1);
895
+    }
896
+
897
+    /**
898
+     * Check whether the current ownCloud version matches the given
899
+     * application's version requirements.
900
+     *
901
+     * The comparison is made based on the number of parts that the
902
+     * app info version has. For example for ownCloud 6.0.3 if the
903
+     * app info version is expecting version 6.0, the comparison is
904
+     * made on the first two parts of the ownCloud version.
905
+     * This means that it's possible to specify "requiremin" => 6
906
+     * and "requiremax" => 6 and it will still match ownCloud 6.0.3.
907
+     *
908
+     * @param string $ocVersion ownCloud version to check against
909
+     * @param array $appInfo app info (from xml)
910
+     *
911
+     * @return boolean true if compatible, otherwise false
912
+     */
913
+    public static function isAppCompatible(string $ocVersion, array $appInfo, bool $ignoreMax = false): bool {
914
+        $requireMin = '';
915
+        $requireMax = '';
916
+        if (isset($appInfo['dependencies']['nextcloud']['@attributes']['min-version'])) {
917
+            $requireMin = $appInfo['dependencies']['nextcloud']['@attributes']['min-version'];
918
+        } elseif (isset($appInfo['dependencies']['owncloud']['@attributes']['min-version'])) {
919
+            $requireMin = $appInfo['dependencies']['owncloud']['@attributes']['min-version'];
920
+        } elseif (isset($appInfo['requiremin'])) {
921
+            $requireMin = $appInfo['requiremin'];
922
+        } elseif (isset($appInfo['require'])) {
923
+            $requireMin = $appInfo['require'];
924
+        }
925
+
926
+        if (isset($appInfo['dependencies']['nextcloud']['@attributes']['max-version'])) {
927
+            $requireMax = $appInfo['dependencies']['nextcloud']['@attributes']['max-version'];
928
+        } elseif (isset($appInfo['dependencies']['owncloud']['@attributes']['max-version'])) {
929
+            $requireMax = $appInfo['dependencies']['owncloud']['@attributes']['max-version'];
930
+        } elseif (isset($appInfo['requiremax'])) {
931
+            $requireMax = $appInfo['requiremax'];
932
+        }
933
+
934
+        if (!empty($requireMin)
935
+            && version_compare(self::adjustVersionParts($ocVersion, $requireMin), $requireMin, '<')
936
+        ) {
937
+            return false;
938
+        }
939
+
940
+        if (!$ignoreMax && !empty($requireMax)
941
+            && version_compare(self::adjustVersionParts($ocVersion, $requireMax), $requireMax, '>')
942
+        ) {
943
+            return false;
944
+        }
945
+
946
+        return true;
947
+    }
948
+
949
+    /**
950
+     * get the installed version of all apps
951
+     */
952
+    public static function getAppVersions() {
953
+        static $versions;
954
+
955
+        if (!$versions) {
956
+            $appConfig = \OC::$server->getAppConfig();
957
+            $versions = $appConfig->getValues(false, 'installed_version');
958
+        }
959
+        return $versions;
960
+    }
961
+
962
+    /**
963
+     * update the database for the app and call the update script
964
+     *
965
+     * @param string $appId
966
+     * @return bool
967
+     */
968
+    public static function updateApp(string $appId): bool {
969
+        $appPath = self::getAppPath($appId);
970
+        if ($appPath === false) {
971
+            return false;
972
+        }
973
+
974
+        \OC::$server->getAppManager()->clearAppsCache();
975
+        $appData = self::getAppInfo($appId);
976
+
977
+        $ignoreMaxApps = \OC::$server->getConfig()->getSystemValue('app_install_overwrite', []);
978
+        $ignoreMax = in_array($appId, $ignoreMaxApps, true);
979
+        \OC_App::checkAppDependencies(
980
+            \OC::$server->getConfig(),
981
+            \OC::$server->getL10N('core'),
982
+            $appData,
983
+            $ignoreMax
984
+        );
985
+
986
+        self::registerAutoloading($appId, $appPath, true);
987
+        self::executeRepairSteps($appId, $appData['repair-steps']['pre-migration']);
988
+
989
+        if (file_exists($appPath . '/appinfo/database.xml')) {
990
+            OC_DB::updateDbFromStructure($appPath . '/appinfo/database.xml');
991
+        } else {
992
+            $ms = new MigrationService($appId, \OC::$server->get(\OC\DB\Connection::class));
993
+            $ms->migrate();
994
+        }
995
+
996
+        self::executeRepairSteps($appId, $appData['repair-steps']['post-migration']);
997
+        self::setupLiveMigrations($appId, $appData['repair-steps']['live-migration']);
998
+        // update appversion in app manager
999
+        \OC::$server->getAppManager()->clearAppsCache();
1000
+        \OC::$server->getAppManager()->getAppVersion($appId, false);
1001
+
1002
+        self::setupBackgroundJobs($appData['background-jobs']);
1003
+
1004
+        //set remote/public handlers
1005
+        if (array_key_exists('ocsid', $appData)) {
1006
+            \OC::$server->getConfig()->setAppValue($appId, 'ocsid', $appData['ocsid']);
1007
+        } elseif (\OC::$server->getConfig()->getAppValue($appId, 'ocsid', null) !== null) {
1008
+            \OC::$server->getConfig()->deleteAppValue($appId, 'ocsid');
1009
+        }
1010
+        foreach ($appData['remote'] as $name => $path) {
1011
+            \OC::$server->getConfig()->setAppValue('core', 'remote_' . $name, $appId . '/' . $path);
1012
+        }
1013
+        foreach ($appData['public'] as $name => $path) {
1014
+            \OC::$server->getConfig()->setAppValue('core', 'public_' . $name, $appId . '/' . $path);
1015
+        }
1016
+
1017
+        self::setAppTypes($appId);
1018
+
1019
+        $version = \OC_App::getAppVersion($appId);
1020
+        \OC::$server->getConfig()->setAppValue($appId, 'installed_version', $version);
1021
+
1022
+        \OC::$server->getEventDispatcher()->dispatch(ManagerEvent::EVENT_APP_UPDATE, new ManagerEvent(
1023
+            ManagerEvent::EVENT_APP_UPDATE, $appId
1024
+        ));
1025
+
1026
+        return true;
1027
+    }
1028
+
1029
+    /**
1030
+     * @param string $appId
1031
+     * @param string[] $steps
1032
+     * @throws \OC\NeedsUpdateException
1033
+     */
1034
+    public static function executeRepairSteps(string $appId, array $steps) {
1035
+        if (empty($steps)) {
1036
+            return;
1037
+        }
1038
+        // load the app
1039
+        self::loadApp($appId);
1040
+
1041
+        $dispatcher = OC::$server->getEventDispatcher();
1042
+
1043
+        // load the steps
1044
+        $r = new Repair([], $dispatcher);
1045
+        foreach ($steps as $step) {
1046
+            try {
1047
+                $r->addStep($step);
1048
+            } catch (Exception $ex) {
1049
+                $r->emit('\OC\Repair', 'error', [$ex->getMessage()]);
1050
+                \OC::$server->getLogger()->logException($ex);
1051
+            }
1052
+        }
1053
+        // run the steps
1054
+        $r->run();
1055
+    }
1056
+
1057
+    public static function setupBackgroundJobs(array $jobs) {
1058
+        $queue = \OC::$server->getJobList();
1059
+        foreach ($jobs as $job) {
1060
+            $queue->add($job);
1061
+        }
1062
+    }
1063
+
1064
+    /**
1065
+     * @param string $appId
1066
+     * @param string[] $steps
1067
+     */
1068
+    private static function setupLiveMigrations(string $appId, array $steps) {
1069
+        $queue = \OC::$server->getJobList();
1070
+        foreach ($steps as $step) {
1071
+            $queue->add('OC\Migration\BackgroundRepair', [
1072
+                'app' => $appId,
1073
+                'step' => $step]);
1074
+        }
1075
+    }
1076
+
1077
+    /**
1078
+     * @param string $appId
1079
+     * @return \OC\Files\View|false
1080
+     */
1081
+    public static function getStorage(string $appId) {
1082
+        if (\OC::$server->getAppManager()->isEnabledForUser($appId)) { //sanity check
1083
+            if (\OC::$server->getUserSession()->isLoggedIn()) {
1084
+                $view = new \OC\Files\View('/' . OC_User::getUser());
1085
+                if (!$view->file_exists($appId)) {
1086
+                    $view->mkdir($appId);
1087
+                }
1088
+                return new \OC\Files\View('/' . OC_User::getUser() . '/' . $appId);
1089
+            } else {
1090
+                \OCP\Util::writeLog('core', 'Can\'t get app storage, app ' . $appId . ', user not logged in', ILogger::ERROR);
1091
+                return false;
1092
+            }
1093
+        } else {
1094
+            \OCP\Util::writeLog('core', 'Can\'t get app storage, app ' . $appId . ' not enabled', ILogger::ERROR);
1095
+            return false;
1096
+        }
1097
+    }
1098
+
1099
+    protected static function findBestL10NOption(array $options, string $lang): string {
1100
+        // only a single option
1101
+        if (isset($options['@value'])) {
1102
+            return $options['@value'];
1103
+        }
1104
+
1105
+        $fallback = $similarLangFallback = $englishFallback = false;
1106
+
1107
+        $lang = strtolower($lang);
1108
+        $similarLang = $lang;
1109
+        if (strpos($similarLang, '_')) {
1110
+            // For "de_DE" we want to find "de" and the other way around
1111
+            $similarLang = substr($lang, 0, strpos($lang, '_'));
1112
+        }
1113
+
1114
+        foreach ($options as $option) {
1115
+            if (is_array($option)) {
1116
+                if ($fallback === false) {
1117
+                    $fallback = $option['@value'];
1118
+                }
1119
+
1120
+                if (!isset($option['@attributes']['lang'])) {
1121
+                    continue;
1122
+                }
1123
+
1124
+                $attributeLang = strtolower($option['@attributes']['lang']);
1125
+                if ($attributeLang === $lang) {
1126
+                    return $option['@value'];
1127
+                }
1128
+
1129
+                if ($attributeLang === $similarLang) {
1130
+                    $similarLangFallback = $option['@value'];
1131
+                } elseif (strpos($attributeLang, $similarLang . '_') === 0) {
1132
+                    if ($similarLangFallback === false) {
1133
+                        $similarLangFallback = $option['@value'];
1134
+                    }
1135
+                }
1136
+            } else {
1137
+                $englishFallback = $option;
1138
+            }
1139
+        }
1140
+
1141
+        if ($similarLangFallback !== false) {
1142
+            return $similarLangFallback;
1143
+        } elseif ($englishFallback !== false) {
1144
+            return $englishFallback;
1145
+        }
1146
+        return (string) $fallback;
1147
+    }
1148
+
1149
+    /**
1150
+     * parses the app data array and enhanced the 'description' value
1151
+     *
1152
+     * @param array $data the app data
1153
+     * @param string $lang
1154
+     * @return array improved app data
1155
+     */
1156
+    public static function parseAppInfo(array $data, $lang = null): array {
1157
+        if ($lang && isset($data['name']) && is_array($data['name'])) {
1158
+            $data['name'] = self::findBestL10NOption($data['name'], $lang);
1159
+        }
1160
+        if ($lang && isset($data['summary']) && is_array($data['summary'])) {
1161
+            $data['summary'] = self::findBestL10NOption($data['summary'], $lang);
1162
+        }
1163
+        if ($lang && isset($data['description']) && is_array($data['description'])) {
1164
+            $data['description'] = trim(self::findBestL10NOption($data['description'], $lang));
1165
+        } elseif (isset($data['description']) && is_string($data['description'])) {
1166
+            $data['description'] = trim($data['description']);
1167
+        } else {
1168
+            $data['description'] = '';
1169
+        }
1170
+
1171
+        return $data;
1172
+    }
1173
+
1174
+    /**
1175
+     * @param \OCP\IConfig $config
1176
+     * @param \OCP\IL10N $l
1177
+     * @param array $info
1178
+     * @throws \Exception
1179
+     */
1180
+    public static function checkAppDependencies(\OCP\IConfig $config, \OCP\IL10N $l, array $info, bool $ignoreMax) {
1181
+        $dependencyAnalyzer = new DependencyAnalyzer(new Platform($config), $l);
1182
+        $missing = $dependencyAnalyzer->analyze($info, $ignoreMax);
1183
+        if (!empty($missing)) {
1184
+            $missingMsg = implode(PHP_EOL, $missing);
1185
+            throw new \Exception(
1186
+                $l->t('App "%1$s" cannot be installed because the following dependencies are not fulfilled: %2$s',
1187
+                    [$info['name'], $missingMsg]
1188
+                )
1189
+            );
1190
+        }
1191
+    }
1192 1192
 }
Please login to merge, or discard this patch.