Completed
Pull Request — master (#8380)
by Joas
33:04 queued 15:49
created
lib/private/Updater.php 2 patches
Indentation   +557 added lines, -557 removed lines patch added patch discarded remove patch
@@ -54,563 +54,563 @@
 block discarded – undo
54 54
  */
55 55
 class Updater extends BasicEmitter {
56 56
 
57
-	/** @var ILogger $log */
58
-	private $log;
59
-
60
-	/** @var IConfig */
61
-	private $config;
62
-
63
-	/** @var Checker */
64
-	private $checker;
65
-
66
-	/** @var Installer */
67
-	private $installer;
68
-
69
-	private $logLevelNames = [
70
-		0 => 'Debug',
71
-		1 => 'Info',
72
-		2 => 'Warning',
73
-		3 => 'Error',
74
-		4 => 'Fatal',
75
-	];
76
-
77
-	/**
78
-	 * @param IConfig $config
79
-	 * @param Checker $checker
80
-	 * @param ILogger $log
81
-	 * @param Installer $installer
82
-	 */
83
-	public function __construct(IConfig $config,
84
-								Checker $checker,
85
-								ILogger $log = null,
86
-								Installer $installer) {
87
-		$this->log = $log;
88
-		$this->config = $config;
89
-		$this->checker = $checker;
90
-		$this->installer = $installer;
91
-	}
92
-
93
-	/**
94
-	 * runs the update actions in maintenance mode, does not upgrade the source files
95
-	 * except the main .htaccess file
96
-	 *
97
-	 * @return bool true if the operation succeeded, false otherwise
98
-	 */
99
-	public function upgrade() {
100
-		$this->emitRepairEvents();
101
-		$this->logAllEvents();
102
-
103
-		$logLevel = $this->config->getSystemValue('loglevel', Util::WARN);
104
-		$this->emit('\OC\Updater', 'setDebugLogLevel', [ $logLevel, $this->logLevelNames[$logLevel] ]);
105
-		$this->config->setSystemValue('loglevel', Util::DEBUG);
106
-
107
-		$wasMaintenanceModeEnabled = $this->config->getSystemValue('maintenance', false);
108
-
109
-		if(!$wasMaintenanceModeEnabled) {
110
-			$this->config->setSystemValue('maintenance', true);
111
-			$this->emit('\OC\Updater', 'maintenanceEnabled');
112
-		}
113
-
114
-		$installedVersion = $this->config->getSystemValue('version', '0.0.0');
115
-		$currentVersion = implode('.', \OCP\Util::getVersion());
116
-		$this->log->debug('starting upgrade from ' . $installedVersion . ' to ' . $currentVersion, array('app' => 'core'));
117
-
118
-		$success = true;
119
-		try {
120
-			$this->doUpgrade($currentVersion, $installedVersion);
121
-		} catch (HintException $exception) {
122
-			$this->log->logException($exception, ['app' => 'core']);
123
-			$this->emit('\OC\Updater', 'failure', array($exception->getMessage() . ': ' .$exception->getHint()));
124
-			$success = false;
125
-		} catch (\Exception $exception) {
126
-			$this->log->logException($exception, ['app' => 'core']);
127
-			$this->emit('\OC\Updater', 'failure', array(get_class($exception) . ': ' .$exception->getMessage()));
128
-			$success = false;
129
-		}
130
-
131
-		$this->emit('\OC\Updater', 'updateEnd', array($success));
132
-
133
-		if(!$wasMaintenanceModeEnabled && $success) {
134
-			$this->config->setSystemValue('maintenance', false);
135
-			$this->emit('\OC\Updater', 'maintenanceDisabled');
136
-		} else {
137
-			$this->emit('\OC\Updater', 'maintenanceActive');
138
-		}
139
-
140
-		$this->emit('\OC\Updater', 'resetLogLevel', [ $logLevel, $this->logLevelNames[$logLevel] ]);
141
-		$this->config->setSystemValue('loglevel', $logLevel);
142
-		$this->config->setSystemValue('installed', true);
143
-
144
-		return $success;
145
-	}
146
-
147
-	/**
148
-	 * Return version from which this version is allowed to upgrade from
149
-	 *
150
-	 * @return array allowed previous versions per vendor
151
-	 */
152
-	private function getAllowedPreviousVersions() {
153
-		// this should really be a JSON file
154
-		require \OC::$SERVERROOT . '/version.php';
155
-		/** @var array $OC_VersionCanBeUpgradedFrom */
156
-		return $OC_VersionCanBeUpgradedFrom;
157
-	}
158
-
159
-	/**
160
-	 * Return vendor from which this version was published
161
-	 *
162
-	 * @return string Get the vendor
163
-	 */
164
-	private function getVendor() {
165
-		// this should really be a JSON file
166
-		require \OC::$SERVERROOT . '/version.php';
167
-		/** @var string $vendor */
168
-		return (string) $vendor;
169
-	}
170
-
171
-	/**
172
-	 * Whether an upgrade to a specified version is possible
173
-	 * @param string $oldVersion
174
-	 * @param string $newVersion
175
-	 * @param array $allowedPreviousVersions
176
-	 * @return bool
177
-	 */
178
-	public function isUpgradePossible($oldVersion, $newVersion, array $allowedPreviousVersions) {
179
-		$version = explode('.', $oldVersion);
180
-		$majorMinor = $version[0] . '.' . $version[1];
181
-
182
-		$currentVendor = $this->config->getAppValue('core', 'vendor', '');
183
-
184
-		// Vendor was not set correctly on install, so we have to white-list known versions
185
-		if ($currentVendor === '' && isset($allowedPreviousVersions['owncloud'][$oldVersion])) {
186
-			$currentVendor = 'owncloud';
187
-		}
188
-
189
-		if ($currentVendor === 'nextcloud') {
190
-			return isset($allowedPreviousVersions[$currentVendor][$majorMinor])
191
-				&& (version_compare($oldVersion, $newVersion, '<=') ||
192
-					$this->config->getSystemValue('debug', false));
193
-		}
194
-
195
-		// Check if the instance can be migrated
196
-		return isset($allowedPreviousVersions[$currentVendor][$majorMinor]) ||
197
-			isset($allowedPreviousVersions[$currentVendor][$oldVersion]);
198
-	}
199
-
200
-	/**
201
-	 * runs the update actions in maintenance mode, does not upgrade the source files
202
-	 * except the main .htaccess file
203
-	 *
204
-	 * @param string $currentVersion current version to upgrade to
205
-	 * @param string $installedVersion previous version from which to upgrade from
206
-	 *
207
-	 * @throws \Exception
208
-	 */
209
-	private function doUpgrade($currentVersion, $installedVersion) {
210
-		// Stop update if the update is over several major versions
211
-		$allowedPreviousVersions = $this->getAllowedPreviousVersions();
212
-		if (!$this->isUpgradePossible($installedVersion, $currentVersion, $allowedPreviousVersions)) {
213
-			throw new \Exception('Updates between multiple major versions and downgrades are unsupported.');
214
-		}
215
-
216
-		// Update .htaccess files
217
-		try {
218
-			Setup::updateHtaccess();
219
-			Setup::protectDataDirectory();
220
-		} catch (\Exception $e) {
221
-			throw new \Exception($e->getMessage());
222
-		}
223
-
224
-		// create empty file in data dir, so we can later find
225
-		// out that this is indeed an ownCloud data directory
226
-		// (in case it didn't exist before)
227
-		file_put_contents($this->config->getSystemValue('datadirectory', \OC::$SERVERROOT . '/data') . '/.ocdata', '');
228
-
229
-		// pre-upgrade repairs
230
-		$repair = new Repair(Repair::getBeforeUpgradeRepairSteps(), \OC::$server->getEventDispatcher());
231
-		$repair->run();
232
-
233
-		$this->doCoreUpgrade();
234
-
235
-		try {
236
-			// TODO: replace with the new repair step mechanism https://github.com/owncloud/core/pull/24378
237
-			Setup::installBackgroundJobs();
238
-		} catch (\Exception $e) {
239
-			throw new \Exception($e->getMessage());
240
-		}
241
-
242
-		// update all shipped apps
243
-		$this->checkAppsRequirements();
244
-		$this->doAppUpgrade();
245
-
246
-		// Update the appfetchers version so it downloads the correct list from the appstore
247
-		\OC::$server->getAppFetcher()->setVersion($currentVersion);
248
-
249
-		// upgrade appstore apps
250
-		$this->upgradeAppStoreApps(\OC::$server->getAppManager()->getInstalledApps());
251
-		$this->upgradeAppStoreApps(\OC_App::$autoDisabledApps, true);
252
-
253
-		// install new shipped apps on upgrade
254
-		OC_App::loadApps('authentication');
255
-		$errors = Installer::installShippedApps(true);
256
-		foreach ($errors as $appId => $exception) {
257
-			/** @var \Exception $exception */
258
-			$this->log->logException($exception, ['app' => $appId]);
259
-			$this->emit('\OC\Updater', 'failure', [$appId . ': ' . $exception->getMessage()]);
260
-		}
261
-
262
-		// post-upgrade repairs
263
-		$repair = new Repair(Repair::getRepairSteps(), \OC::$server->getEventDispatcher());
264
-		$repair->run();
265
-
266
-		//Invalidate update feed
267
-		$this->config->setAppValue('core', 'lastupdatedat', 0);
268
-
269
-		// Check for code integrity if not disabled
270
-		if(\OC::$server->getIntegrityCodeChecker()->isCodeCheckEnforced()) {
271
-			$this->emit('\OC\Updater', 'startCheckCodeIntegrity');
272
-			$this->checker->runInstanceVerification();
273
-			$this->emit('\OC\Updater', 'finishedCheckCodeIntegrity');
274
-		}
275
-
276
-		// only set the final version if everything went well
277
-		$this->config->setSystemValue('version', implode('.', Util::getVersion()));
278
-		$this->config->setAppValue('core', 'vendor', $this->getVendor());
279
-	}
280
-
281
-	protected function doCoreUpgrade() {
282
-		$this->emit('\OC\Updater', 'dbUpgradeBefore');
283
-
284
-		// execute core migrations
285
-		$ms = new MigrationService('core', \OC::$server->getDatabaseConnection());
286
-		$ms->migrate();
287
-
288
-		$this->emit('\OC\Updater', 'dbUpgrade');
289
-	}
290
-
291
-	/**
292
-	 * @param string $version the oc version to check app compatibility with
293
-	 */
294
-	protected function checkAppUpgrade($version) {
295
-		$apps = \OC_App::getEnabledApps();
296
-		$this->emit('\OC\Updater', 'appUpgradeCheckBefore');
297
-
298
-		$appManager = \OC::$server->getAppManager();
299
-		foreach ($apps as $appId) {
300
-			$info = \OC_App::getAppInfo($appId);
301
-			$compatible = \OC_App::isAppCompatible($version, $info);
302
-			$isShipped = $appManager->isShipped($appId);
303
-
304
-			if ($compatible && $isShipped && \OC_App::shouldUpgrade($appId)) {
305
-				/**
306
-				 * FIXME: The preupdate check is performed before the database migration, otherwise database changes
307
-				 * are not possible anymore within it. - Consider this when touching the code.
308
-				 * @link https://github.com/owncloud/core/issues/10980
309
-				 * @see \OC_App::updateApp
310
-				 */
311
-				if (file_exists(\OC_App::getAppPath($appId) . '/appinfo/preupdate.php')) {
312
-					$this->includePreUpdate($appId);
313
-				}
314
-				if (file_exists(\OC_App::getAppPath($appId) . '/appinfo/database.xml')) {
315
-					$this->emit('\OC\Updater', 'appSimulateUpdate', array($appId));
316
-					\OC_DB::simulateUpdateDbFromStructure(\OC_App::getAppPath($appId) . '/appinfo/database.xml');
317
-				}
318
-			}
319
-		}
320
-
321
-		$this->emit('\OC\Updater', 'appUpgradeCheck');
322
-	}
323
-
324
-	/**
325
-	 * Includes the pre-update file. Done here to prevent namespace mixups.
326
-	 * @param string $appId
327
-	 */
328
-	private function includePreUpdate($appId) {
329
-		include \OC_App::getAppPath($appId) . '/appinfo/preupdate.php';
330
-	}
331
-
332
-	/**
333
-	 * upgrades all apps within a major ownCloud upgrade. Also loads "priority"
334
-	 * (types authentication, filesystem, logging, in that order) afterwards.
335
-	 *
336
-	 * @throws NeedsUpdateException
337
-	 */
338
-	protected function doAppUpgrade() {
339
-		$apps = \OC_App::getEnabledApps();
340
-		$priorityTypes = array('authentication', 'filesystem', 'logging');
341
-		$pseudoOtherType = 'other';
342
-		$stacks = array($pseudoOtherType => array());
343
-
344
-		foreach ($apps as $appId) {
345
-			$priorityType = false;
346
-			foreach ($priorityTypes as $type) {
347
-				if(!isset($stacks[$type])) {
348
-					$stacks[$type] = array();
349
-				}
350
-				if (\OC_App::isType($appId, $type)) {
351
-					$stacks[$type][] = $appId;
352
-					$priorityType = true;
353
-					break;
354
-				}
355
-			}
356
-			if (!$priorityType) {
357
-				$stacks[$pseudoOtherType][] = $appId;
358
-			}
359
-		}
360
-		foreach ($stacks as $type => $stack) {
361
-			foreach ($stack as $appId) {
362
-				if (\OC_App::shouldUpgrade($appId)) {
363
-					$this->emit('\OC\Updater', 'appUpgradeStarted', [$appId, \OC_App::getAppVersion($appId)]);
364
-					\OC_App::updateApp($appId);
365
-					$this->emit('\OC\Updater', 'appUpgrade', [$appId, \OC_App::getAppVersion($appId)]);
366
-				}
367
-				if($type !== $pseudoOtherType) {
368
-					// load authentication, filesystem and logging apps after
369
-					// upgrading them. Other apps my need to rely on modifying
370
-					// user and/or filesystem aspects.
371
-					\OC_App::loadApp($appId);
372
-				}
373
-			}
374
-		}
375
-	}
376
-
377
-	/**
378
-	 * check if the current enabled apps are compatible with the current
379
-	 * ownCloud version. disable them if not.
380
-	 * This is important if you upgrade ownCloud and have non ported 3rd
381
-	 * party apps installed.
382
-	 *
383
-	 * @return array
384
-	 * @throws \Exception
385
-	 */
386
-	private function checkAppsRequirements() {
387
-		$isCoreUpgrade = $this->isCodeUpgrade();
388
-		$apps = OC_App::getEnabledApps();
389
-		$version = Util::getVersion();
390
-		$disabledApps = [];
391
-		$appManager = \OC::$server->getAppManager();
392
-		foreach ($apps as $app) {
393
-			// check if the app is compatible with this version of ownCloud
394
-			$info = OC_App::getAppInfo($app);
395
-			if(!OC_App::isAppCompatible($version, $info)) {
396
-				if ($appManager->isShipped($app)) {
397
-					throw new \UnexpectedValueException('The files of the app "' . $app . '" were not correctly replaced before running the update');
398
-				}
399
-				OC_App::disable($app);
400
-				$this->emit('\OC\Updater', 'incompatibleAppDisabled', array($app));
401
-			}
402
-			// no need to disable any app in case this is a non-core upgrade
403
-			if (!$isCoreUpgrade) {
404
-				continue;
405
-			}
406
-			// shipped apps will remain enabled
407
-			if ($appManager->isShipped($app)) {
408
-				continue;
409
-			}
410
-			// authentication and session apps will remain enabled as well
411
-			if (OC_App::isType($app, ['session', 'authentication'])) {
412
-				continue;
413
-			}
414
-		}
415
-		return $disabledApps;
416
-	}
417
-
418
-	/**
419
-	 * @return bool
420
-	 */
421
-	private function isCodeUpgrade() {
422
-		$installedVersion = $this->config->getSystemValue('version', '0.0.0');
423
-		$currentVersion = implode('.', Util::getVersion());
424
-		if (version_compare($currentVersion, $installedVersion, '>')) {
425
-			return true;
426
-		}
427
-		return false;
428
-	}
429
-
430
-	/**
431
-	 * @param array $disabledApps
432
-	 * @param bool $reenable
433
-	 * @throws \Exception
434
-	 */
435
-	private function upgradeAppStoreApps(array $disabledApps, $reenable = false) {
436
-		foreach($disabledApps as $app) {
437
-			try {
438
-				$this->emit('\OC\Updater', 'checkAppStoreAppBefore', [$app]);
439
-				if ($this->installer->isUpdateAvailable($app)) {
440
-					$this->emit('\OC\Updater', 'upgradeAppStoreApp', [$app]);
441
-					$this->installer->updateAppstoreApp($app);
442
-				}
443
-				$this->emit('\OC\Updater', 'checkAppStoreApp', [$app]);
444
-
445
-				if ($reenable) {
446
-					$ocApp = new \OC_App();
447
-					$ocApp->enable($app);
448
-				}
449
-			} catch (\Exception $ex) {
450
-				$this->log->logException($ex, ['app' => 'core']);
451
-			}
452
-		}
453
-	}
454
-
455
-	/**
456
-	 * Forward messages emitted by the repair routine
457
-	 */
458
-	private function emitRepairEvents() {
459
-		$dispatcher = \OC::$server->getEventDispatcher();
460
-		$dispatcher->addListener('\OC\Repair::warning', function ($event) {
461
-			if ($event instanceof GenericEvent) {
462
-				$this->emit('\OC\Updater', 'repairWarning', $event->getArguments());
463
-			}
464
-		});
465
-		$dispatcher->addListener('\OC\Repair::error', function ($event) {
466
-			if ($event instanceof GenericEvent) {
467
-				$this->emit('\OC\Updater', 'repairError', $event->getArguments());
468
-			}
469
-		});
470
-		$dispatcher->addListener('\OC\Repair::info', function ($event) {
471
-			if ($event instanceof GenericEvent) {
472
-				$this->emit('\OC\Updater', 'repairInfo', $event->getArguments());
473
-			}
474
-		});
475
-		$dispatcher->addListener('\OC\Repair::step', function ($event) {
476
-			if ($event instanceof GenericEvent) {
477
-				$this->emit('\OC\Updater', 'repairStep', $event->getArguments());
478
-			}
479
-		});
480
-	}
481
-
482
-	private function logAllEvents() {
483
-		$log = $this->log;
484
-
485
-		$dispatcher = \OC::$server->getEventDispatcher();
486
-		$dispatcher->addListener('\OC\DB\Migrator::executeSql', function($event) use ($log) {
487
-			if (!$event instanceof GenericEvent) {
488
-				return;
489
-			}
490
-			$log->info('\OC\DB\Migrator::executeSql: ' . $event->getSubject() . ' (' . $event->getArgument(0) . ' of ' . $event->getArgument(1) . ')', ['app' => 'updater']);
491
-		});
492
-		$dispatcher->addListener('\OC\DB\Migrator::checkTable', function($event) use ($log) {
493
-			if (!$event instanceof GenericEvent) {
494
-				return;
495
-			}
496
-			$log->info('\OC\DB\Migrator::checkTable: ' . $event->getSubject() . ' (' . $event->getArgument(0) . ' of ' . $event->getArgument(1) . ')', ['app' => 'updater']);
497
-		});
498
-
499
-		$repairListener = function($event) use ($log) {
500
-			if (!$event instanceof GenericEvent) {
501
-				return;
502
-			}
503
-			switch ($event->getSubject()) {
504
-				case '\OC\Repair::startProgress':
505
-					$log->info('\OC\Repair::startProgress: Starting ... ' . $event->getArgument(1) .  ' (' . $event->getArgument(0) . ')', ['app' => 'updater']);
506
-					break;
507
-				case '\OC\Repair::advance':
508
-					$desc = $event->getArgument(1);
509
-					if (empty($desc)) {
510
-						$desc = '';
511
-					}
512
-					$log->info('\OC\Repair::advance: ' . $desc . ' (' . $event->getArgument(0) . ')', ['app' => 'updater']);
513
-
514
-					break;
515
-				case '\OC\Repair::finishProgress':
516
-					$log->info('\OC\Repair::finishProgress', ['app' => 'updater']);
517
-					break;
518
-				case '\OC\Repair::step':
519
-					$log->info('\OC\Repair::step: Repair step: ' . $event->getArgument(0), ['app' => 'updater']);
520
-					break;
521
-				case '\OC\Repair::info':
522
-					$log->info('\OC\Repair::info: Repair info: ' . $event->getArgument(0), ['app' => 'updater']);
523
-					break;
524
-				case '\OC\Repair::warning':
525
-					$log->warning('\OC\Repair::warning: Repair warning: ' . $event->getArgument(0), ['app' => 'updater']);
526
-					break;
527
-				case '\OC\Repair::error':
528
-					$log->error('\OC\Repair::error: Repair error: ' . $event->getArgument(0), ['app' => 'updater']);
529
-					break;
530
-			}
531
-		};
532
-
533
-		$dispatcher->addListener('\OC\Repair::startProgress', $repairListener);
534
-		$dispatcher->addListener('\OC\Repair::advance', $repairListener);
535
-		$dispatcher->addListener('\OC\Repair::finishProgress', $repairListener);
536
-		$dispatcher->addListener('\OC\Repair::step', $repairListener);
537
-		$dispatcher->addListener('\OC\Repair::info', $repairListener);
538
-		$dispatcher->addListener('\OC\Repair::warning', $repairListener);
539
-		$dispatcher->addListener('\OC\Repair::error', $repairListener);
540
-
541
-
542
-		$this->listen('\OC\Updater', 'maintenanceEnabled', function () use($log) {
543
-			$log->info('\OC\Updater::maintenanceEnabled: Turned on maintenance mode', ['app' => 'updater']);
544
-		});
545
-		$this->listen('\OC\Updater', 'maintenanceDisabled', function () use($log) {
546
-			$log->info('\OC\Updater::maintenanceDisabled: Turned off maintenance mode', ['app' => 'updater']);
547
-		});
548
-		$this->listen('\OC\Updater', 'maintenanceActive', function () use($log) {
549
-			$log->info('\OC\Updater::maintenanceActive: Maintenance mode is kept active', ['app' => 'updater']);
550
-		});
551
-		$this->listen('\OC\Updater', 'updateEnd', function ($success) use($log) {
552
-			if ($success) {
553
-				$log->info('\OC\Updater::updateEnd: Update successful', ['app' => 'updater']);
554
-			} else {
555
-				$log->error('\OC\Updater::updateEnd: Update failed', ['app' => 'updater']);
556
-			}
557
-		});
558
-		$this->listen('\OC\Updater', 'dbUpgradeBefore', function () use($log) {
559
-			$log->info('\OC\Updater::dbUpgradeBefore: Updating database schema', ['app' => 'updater']);
560
-		});
561
-		$this->listen('\OC\Updater', 'dbUpgrade', function () use($log) {
562
-			$log->info('\OC\Updater::dbUpgrade: Updated database', ['app' => 'updater']);
563
-		});
564
-		$this->listen('\OC\Updater', 'dbSimulateUpgradeBefore', function () use($log) {
565
-			$log->info('\OC\Updater::dbSimulateUpgradeBefore: Checking whether the database schema can be updated (this can take a long time depending on the database size)', ['app' => 'updater']);
566
-		});
567
-		$this->listen('\OC\Updater', 'dbSimulateUpgrade', function () use($log) {
568
-			$log->info('\OC\Updater::dbSimulateUpgrade: Checked database schema update', ['app' => 'updater']);
569
-		});
570
-		$this->listen('\OC\Updater', 'incompatibleAppDisabled', function ($app) use($log) {
571
-			$log->info('\OC\Updater::incompatibleAppDisabled: Disabled incompatible app: ' . $app, ['app' => 'updater']);
572
-		});
573
-		$this->listen('\OC\Updater', 'checkAppStoreAppBefore', function ($app) use($log) {
574
-			$log->info('\OC\Updater::checkAppStoreAppBefore: Checking for update of app "' . $app . '" in appstore', ['app' => 'updater']);
575
-		});
576
-		$this->listen('\OC\Updater', 'upgradeAppStoreApp', function ($app) use($log) {
577
-			$log->info('\OC\Updater::upgradeAppStoreApp: Update app "' . $app . '" from appstore', ['app' => 'updater']);
578
-		});
579
-		$this->listen('\OC\Updater', 'checkAppStoreApp', function ($app) use($log) {
580
-			$log->info('\OC\Updater::checkAppStoreApp: Checked for update of app "' . $app . '" in appstore', ['app' => 'updater']);
581
-		});
582
-		$this->listen('\OC\Updater', 'appUpgradeCheckBefore', function () use ($log) {
583
-			$log->info('\OC\Updater::appUpgradeCheckBefore: Checking updates of apps', ['app' => 'updater']);
584
-		});
585
-		$this->listen('\OC\Updater', 'appSimulateUpdate', function ($app) use ($log) {
586
-			$log->info('\OC\Updater::appSimulateUpdate: Checking whether the database schema for <' . $app . '> can be updated (this can take a long time depending on the database size)', ['app' => 'updater']);
587
-		});
588
-		$this->listen('\OC\Updater', 'appUpgradeCheck', function () use ($log) {
589
-			$log->info('\OC\Updater::appUpgradeCheck: Checked database schema update for apps', ['app' => 'updater']);
590
-		});
591
-		$this->listen('\OC\Updater', 'appUpgradeStarted', function ($app) use ($log) {
592
-			$log->info('\OC\Updater::appUpgradeStarted: Updating <' . $app . '> ...', ['app' => 'updater']);
593
-		});
594
-		$this->listen('\OC\Updater', 'appUpgrade', function ($app, $version) use ($log) {
595
-			$log->info('\OC\Updater::appUpgrade: Updated <' . $app . '> to ' . $version, ['app' => 'updater']);
596
-		});
597
-		$this->listen('\OC\Updater', 'failure', function ($message) use($log) {
598
-			$log->error('\OC\Updater::failure: ' . $message, ['app' => 'updater']);
599
-		});
600
-		$this->listen('\OC\Updater', 'setDebugLogLevel', function () use($log) {
601
-			$log->info('\OC\Updater::setDebugLogLevel: Set log level to debug', ['app' => 'updater']);
602
-		});
603
-		$this->listen('\OC\Updater', 'resetLogLevel', function ($logLevel, $logLevelName) use($log) {
604
-			$log->info('\OC\Updater::resetLogLevel: Reset log level to ' . $logLevelName . '(' . $logLevel . ')', ['app' => 'updater']);
605
-		});
606
-		$this->listen('\OC\Updater', 'startCheckCodeIntegrity', function () use($log) {
607
-			$log->info('\OC\Updater::startCheckCodeIntegrity: Starting code integrity check...', ['app' => 'updater']);
608
-		});
609
-		$this->listen('\OC\Updater', 'finishedCheckCodeIntegrity', function () use($log) {
610
-			$log->info('\OC\Updater::finishedCheckCodeIntegrity: Finished code integrity check', ['app' => 'updater']);
611
-		});
612
-
613
-	}
57
+    /** @var ILogger $log */
58
+    private $log;
59
+
60
+    /** @var IConfig */
61
+    private $config;
62
+
63
+    /** @var Checker */
64
+    private $checker;
65
+
66
+    /** @var Installer */
67
+    private $installer;
68
+
69
+    private $logLevelNames = [
70
+        0 => 'Debug',
71
+        1 => 'Info',
72
+        2 => 'Warning',
73
+        3 => 'Error',
74
+        4 => 'Fatal',
75
+    ];
76
+
77
+    /**
78
+     * @param IConfig $config
79
+     * @param Checker $checker
80
+     * @param ILogger $log
81
+     * @param Installer $installer
82
+     */
83
+    public function __construct(IConfig $config,
84
+                                Checker $checker,
85
+                                ILogger $log = null,
86
+                                Installer $installer) {
87
+        $this->log = $log;
88
+        $this->config = $config;
89
+        $this->checker = $checker;
90
+        $this->installer = $installer;
91
+    }
92
+
93
+    /**
94
+     * runs the update actions in maintenance mode, does not upgrade the source files
95
+     * except the main .htaccess file
96
+     *
97
+     * @return bool true if the operation succeeded, false otherwise
98
+     */
99
+    public function upgrade() {
100
+        $this->emitRepairEvents();
101
+        $this->logAllEvents();
102
+
103
+        $logLevel = $this->config->getSystemValue('loglevel', Util::WARN);
104
+        $this->emit('\OC\Updater', 'setDebugLogLevel', [ $logLevel, $this->logLevelNames[$logLevel] ]);
105
+        $this->config->setSystemValue('loglevel', Util::DEBUG);
106
+
107
+        $wasMaintenanceModeEnabled = $this->config->getSystemValue('maintenance', false);
108
+
109
+        if(!$wasMaintenanceModeEnabled) {
110
+            $this->config->setSystemValue('maintenance', true);
111
+            $this->emit('\OC\Updater', 'maintenanceEnabled');
112
+        }
113
+
114
+        $installedVersion = $this->config->getSystemValue('version', '0.0.0');
115
+        $currentVersion = implode('.', \OCP\Util::getVersion());
116
+        $this->log->debug('starting upgrade from ' . $installedVersion . ' to ' . $currentVersion, array('app' => 'core'));
117
+
118
+        $success = true;
119
+        try {
120
+            $this->doUpgrade($currentVersion, $installedVersion);
121
+        } catch (HintException $exception) {
122
+            $this->log->logException($exception, ['app' => 'core']);
123
+            $this->emit('\OC\Updater', 'failure', array($exception->getMessage() . ': ' .$exception->getHint()));
124
+            $success = false;
125
+        } catch (\Exception $exception) {
126
+            $this->log->logException($exception, ['app' => 'core']);
127
+            $this->emit('\OC\Updater', 'failure', array(get_class($exception) . ': ' .$exception->getMessage()));
128
+            $success = false;
129
+        }
130
+
131
+        $this->emit('\OC\Updater', 'updateEnd', array($success));
132
+
133
+        if(!$wasMaintenanceModeEnabled && $success) {
134
+            $this->config->setSystemValue('maintenance', false);
135
+            $this->emit('\OC\Updater', 'maintenanceDisabled');
136
+        } else {
137
+            $this->emit('\OC\Updater', 'maintenanceActive');
138
+        }
139
+
140
+        $this->emit('\OC\Updater', 'resetLogLevel', [ $logLevel, $this->logLevelNames[$logLevel] ]);
141
+        $this->config->setSystemValue('loglevel', $logLevel);
142
+        $this->config->setSystemValue('installed', true);
143
+
144
+        return $success;
145
+    }
146
+
147
+    /**
148
+     * Return version from which this version is allowed to upgrade from
149
+     *
150
+     * @return array allowed previous versions per vendor
151
+     */
152
+    private function getAllowedPreviousVersions() {
153
+        // this should really be a JSON file
154
+        require \OC::$SERVERROOT . '/version.php';
155
+        /** @var array $OC_VersionCanBeUpgradedFrom */
156
+        return $OC_VersionCanBeUpgradedFrom;
157
+    }
158
+
159
+    /**
160
+     * Return vendor from which this version was published
161
+     *
162
+     * @return string Get the vendor
163
+     */
164
+    private function getVendor() {
165
+        // this should really be a JSON file
166
+        require \OC::$SERVERROOT . '/version.php';
167
+        /** @var string $vendor */
168
+        return (string) $vendor;
169
+    }
170
+
171
+    /**
172
+     * Whether an upgrade to a specified version is possible
173
+     * @param string $oldVersion
174
+     * @param string $newVersion
175
+     * @param array $allowedPreviousVersions
176
+     * @return bool
177
+     */
178
+    public function isUpgradePossible($oldVersion, $newVersion, array $allowedPreviousVersions) {
179
+        $version = explode('.', $oldVersion);
180
+        $majorMinor = $version[0] . '.' . $version[1];
181
+
182
+        $currentVendor = $this->config->getAppValue('core', 'vendor', '');
183
+
184
+        // Vendor was not set correctly on install, so we have to white-list known versions
185
+        if ($currentVendor === '' && isset($allowedPreviousVersions['owncloud'][$oldVersion])) {
186
+            $currentVendor = 'owncloud';
187
+        }
188
+
189
+        if ($currentVendor === 'nextcloud') {
190
+            return isset($allowedPreviousVersions[$currentVendor][$majorMinor])
191
+                && (version_compare($oldVersion, $newVersion, '<=') ||
192
+                    $this->config->getSystemValue('debug', false));
193
+        }
194
+
195
+        // Check if the instance can be migrated
196
+        return isset($allowedPreviousVersions[$currentVendor][$majorMinor]) ||
197
+            isset($allowedPreviousVersions[$currentVendor][$oldVersion]);
198
+    }
199
+
200
+    /**
201
+     * runs the update actions in maintenance mode, does not upgrade the source files
202
+     * except the main .htaccess file
203
+     *
204
+     * @param string $currentVersion current version to upgrade to
205
+     * @param string $installedVersion previous version from which to upgrade from
206
+     *
207
+     * @throws \Exception
208
+     */
209
+    private function doUpgrade($currentVersion, $installedVersion) {
210
+        // Stop update if the update is over several major versions
211
+        $allowedPreviousVersions = $this->getAllowedPreviousVersions();
212
+        if (!$this->isUpgradePossible($installedVersion, $currentVersion, $allowedPreviousVersions)) {
213
+            throw new \Exception('Updates between multiple major versions and downgrades are unsupported.');
214
+        }
215
+
216
+        // Update .htaccess files
217
+        try {
218
+            Setup::updateHtaccess();
219
+            Setup::protectDataDirectory();
220
+        } catch (\Exception $e) {
221
+            throw new \Exception($e->getMessage());
222
+        }
223
+
224
+        // create empty file in data dir, so we can later find
225
+        // out that this is indeed an ownCloud data directory
226
+        // (in case it didn't exist before)
227
+        file_put_contents($this->config->getSystemValue('datadirectory', \OC::$SERVERROOT . '/data') . '/.ocdata', '');
228
+
229
+        // pre-upgrade repairs
230
+        $repair = new Repair(Repair::getBeforeUpgradeRepairSteps(), \OC::$server->getEventDispatcher());
231
+        $repair->run();
232
+
233
+        $this->doCoreUpgrade();
234
+
235
+        try {
236
+            // TODO: replace with the new repair step mechanism https://github.com/owncloud/core/pull/24378
237
+            Setup::installBackgroundJobs();
238
+        } catch (\Exception $e) {
239
+            throw new \Exception($e->getMessage());
240
+        }
241
+
242
+        // update all shipped apps
243
+        $this->checkAppsRequirements();
244
+        $this->doAppUpgrade();
245
+
246
+        // Update the appfetchers version so it downloads the correct list from the appstore
247
+        \OC::$server->getAppFetcher()->setVersion($currentVersion);
248
+
249
+        // upgrade appstore apps
250
+        $this->upgradeAppStoreApps(\OC::$server->getAppManager()->getInstalledApps());
251
+        $this->upgradeAppStoreApps(\OC_App::$autoDisabledApps, true);
252
+
253
+        // install new shipped apps on upgrade
254
+        OC_App::loadApps('authentication');
255
+        $errors = Installer::installShippedApps(true);
256
+        foreach ($errors as $appId => $exception) {
257
+            /** @var \Exception $exception */
258
+            $this->log->logException($exception, ['app' => $appId]);
259
+            $this->emit('\OC\Updater', 'failure', [$appId . ': ' . $exception->getMessage()]);
260
+        }
261
+
262
+        // post-upgrade repairs
263
+        $repair = new Repair(Repair::getRepairSteps(), \OC::$server->getEventDispatcher());
264
+        $repair->run();
265
+
266
+        //Invalidate update feed
267
+        $this->config->setAppValue('core', 'lastupdatedat', 0);
268
+
269
+        // Check for code integrity if not disabled
270
+        if(\OC::$server->getIntegrityCodeChecker()->isCodeCheckEnforced()) {
271
+            $this->emit('\OC\Updater', 'startCheckCodeIntegrity');
272
+            $this->checker->runInstanceVerification();
273
+            $this->emit('\OC\Updater', 'finishedCheckCodeIntegrity');
274
+        }
275
+
276
+        // only set the final version if everything went well
277
+        $this->config->setSystemValue('version', implode('.', Util::getVersion()));
278
+        $this->config->setAppValue('core', 'vendor', $this->getVendor());
279
+    }
280
+
281
+    protected function doCoreUpgrade() {
282
+        $this->emit('\OC\Updater', 'dbUpgradeBefore');
283
+
284
+        // execute core migrations
285
+        $ms = new MigrationService('core', \OC::$server->getDatabaseConnection());
286
+        $ms->migrate();
287
+
288
+        $this->emit('\OC\Updater', 'dbUpgrade');
289
+    }
290
+
291
+    /**
292
+     * @param string $version the oc version to check app compatibility with
293
+     */
294
+    protected function checkAppUpgrade($version) {
295
+        $apps = \OC_App::getEnabledApps();
296
+        $this->emit('\OC\Updater', 'appUpgradeCheckBefore');
297
+
298
+        $appManager = \OC::$server->getAppManager();
299
+        foreach ($apps as $appId) {
300
+            $info = \OC_App::getAppInfo($appId);
301
+            $compatible = \OC_App::isAppCompatible($version, $info);
302
+            $isShipped = $appManager->isShipped($appId);
303
+
304
+            if ($compatible && $isShipped && \OC_App::shouldUpgrade($appId)) {
305
+                /**
306
+                 * FIXME: The preupdate check is performed before the database migration, otherwise database changes
307
+                 * are not possible anymore within it. - Consider this when touching the code.
308
+                 * @link https://github.com/owncloud/core/issues/10980
309
+                 * @see \OC_App::updateApp
310
+                 */
311
+                if (file_exists(\OC_App::getAppPath($appId) . '/appinfo/preupdate.php')) {
312
+                    $this->includePreUpdate($appId);
313
+                }
314
+                if (file_exists(\OC_App::getAppPath($appId) . '/appinfo/database.xml')) {
315
+                    $this->emit('\OC\Updater', 'appSimulateUpdate', array($appId));
316
+                    \OC_DB::simulateUpdateDbFromStructure(\OC_App::getAppPath($appId) . '/appinfo/database.xml');
317
+                }
318
+            }
319
+        }
320
+
321
+        $this->emit('\OC\Updater', 'appUpgradeCheck');
322
+    }
323
+
324
+    /**
325
+     * Includes the pre-update file. Done here to prevent namespace mixups.
326
+     * @param string $appId
327
+     */
328
+    private function includePreUpdate($appId) {
329
+        include \OC_App::getAppPath($appId) . '/appinfo/preupdate.php';
330
+    }
331
+
332
+    /**
333
+     * upgrades all apps within a major ownCloud upgrade. Also loads "priority"
334
+     * (types authentication, filesystem, logging, in that order) afterwards.
335
+     *
336
+     * @throws NeedsUpdateException
337
+     */
338
+    protected function doAppUpgrade() {
339
+        $apps = \OC_App::getEnabledApps();
340
+        $priorityTypes = array('authentication', 'filesystem', 'logging');
341
+        $pseudoOtherType = 'other';
342
+        $stacks = array($pseudoOtherType => array());
343
+
344
+        foreach ($apps as $appId) {
345
+            $priorityType = false;
346
+            foreach ($priorityTypes as $type) {
347
+                if(!isset($stacks[$type])) {
348
+                    $stacks[$type] = array();
349
+                }
350
+                if (\OC_App::isType($appId, $type)) {
351
+                    $stacks[$type][] = $appId;
352
+                    $priorityType = true;
353
+                    break;
354
+                }
355
+            }
356
+            if (!$priorityType) {
357
+                $stacks[$pseudoOtherType][] = $appId;
358
+            }
359
+        }
360
+        foreach ($stacks as $type => $stack) {
361
+            foreach ($stack as $appId) {
362
+                if (\OC_App::shouldUpgrade($appId)) {
363
+                    $this->emit('\OC\Updater', 'appUpgradeStarted', [$appId, \OC_App::getAppVersion($appId)]);
364
+                    \OC_App::updateApp($appId);
365
+                    $this->emit('\OC\Updater', 'appUpgrade', [$appId, \OC_App::getAppVersion($appId)]);
366
+                }
367
+                if($type !== $pseudoOtherType) {
368
+                    // load authentication, filesystem and logging apps after
369
+                    // upgrading them. Other apps my need to rely on modifying
370
+                    // user and/or filesystem aspects.
371
+                    \OC_App::loadApp($appId);
372
+                }
373
+            }
374
+        }
375
+    }
376
+
377
+    /**
378
+     * check if the current enabled apps are compatible with the current
379
+     * ownCloud version. disable them if not.
380
+     * This is important if you upgrade ownCloud and have non ported 3rd
381
+     * party apps installed.
382
+     *
383
+     * @return array
384
+     * @throws \Exception
385
+     */
386
+    private function checkAppsRequirements() {
387
+        $isCoreUpgrade = $this->isCodeUpgrade();
388
+        $apps = OC_App::getEnabledApps();
389
+        $version = Util::getVersion();
390
+        $disabledApps = [];
391
+        $appManager = \OC::$server->getAppManager();
392
+        foreach ($apps as $app) {
393
+            // check if the app is compatible with this version of ownCloud
394
+            $info = OC_App::getAppInfo($app);
395
+            if(!OC_App::isAppCompatible($version, $info)) {
396
+                if ($appManager->isShipped($app)) {
397
+                    throw new \UnexpectedValueException('The files of the app "' . $app . '" were not correctly replaced before running the update');
398
+                }
399
+                OC_App::disable($app);
400
+                $this->emit('\OC\Updater', 'incompatibleAppDisabled', array($app));
401
+            }
402
+            // no need to disable any app in case this is a non-core upgrade
403
+            if (!$isCoreUpgrade) {
404
+                continue;
405
+            }
406
+            // shipped apps will remain enabled
407
+            if ($appManager->isShipped($app)) {
408
+                continue;
409
+            }
410
+            // authentication and session apps will remain enabled as well
411
+            if (OC_App::isType($app, ['session', 'authentication'])) {
412
+                continue;
413
+            }
414
+        }
415
+        return $disabledApps;
416
+    }
417
+
418
+    /**
419
+     * @return bool
420
+     */
421
+    private function isCodeUpgrade() {
422
+        $installedVersion = $this->config->getSystemValue('version', '0.0.0');
423
+        $currentVersion = implode('.', Util::getVersion());
424
+        if (version_compare($currentVersion, $installedVersion, '>')) {
425
+            return true;
426
+        }
427
+        return false;
428
+    }
429
+
430
+    /**
431
+     * @param array $disabledApps
432
+     * @param bool $reenable
433
+     * @throws \Exception
434
+     */
435
+    private function upgradeAppStoreApps(array $disabledApps, $reenable = false) {
436
+        foreach($disabledApps as $app) {
437
+            try {
438
+                $this->emit('\OC\Updater', 'checkAppStoreAppBefore', [$app]);
439
+                if ($this->installer->isUpdateAvailable($app)) {
440
+                    $this->emit('\OC\Updater', 'upgradeAppStoreApp', [$app]);
441
+                    $this->installer->updateAppstoreApp($app);
442
+                }
443
+                $this->emit('\OC\Updater', 'checkAppStoreApp', [$app]);
444
+
445
+                if ($reenable) {
446
+                    $ocApp = new \OC_App();
447
+                    $ocApp->enable($app);
448
+                }
449
+            } catch (\Exception $ex) {
450
+                $this->log->logException($ex, ['app' => 'core']);
451
+            }
452
+        }
453
+    }
454
+
455
+    /**
456
+     * Forward messages emitted by the repair routine
457
+     */
458
+    private function emitRepairEvents() {
459
+        $dispatcher = \OC::$server->getEventDispatcher();
460
+        $dispatcher->addListener('\OC\Repair::warning', function ($event) {
461
+            if ($event instanceof GenericEvent) {
462
+                $this->emit('\OC\Updater', 'repairWarning', $event->getArguments());
463
+            }
464
+        });
465
+        $dispatcher->addListener('\OC\Repair::error', function ($event) {
466
+            if ($event instanceof GenericEvent) {
467
+                $this->emit('\OC\Updater', 'repairError', $event->getArguments());
468
+            }
469
+        });
470
+        $dispatcher->addListener('\OC\Repair::info', function ($event) {
471
+            if ($event instanceof GenericEvent) {
472
+                $this->emit('\OC\Updater', 'repairInfo', $event->getArguments());
473
+            }
474
+        });
475
+        $dispatcher->addListener('\OC\Repair::step', function ($event) {
476
+            if ($event instanceof GenericEvent) {
477
+                $this->emit('\OC\Updater', 'repairStep', $event->getArguments());
478
+            }
479
+        });
480
+    }
481
+
482
+    private function logAllEvents() {
483
+        $log = $this->log;
484
+
485
+        $dispatcher = \OC::$server->getEventDispatcher();
486
+        $dispatcher->addListener('\OC\DB\Migrator::executeSql', function($event) use ($log) {
487
+            if (!$event instanceof GenericEvent) {
488
+                return;
489
+            }
490
+            $log->info('\OC\DB\Migrator::executeSql: ' . $event->getSubject() . ' (' . $event->getArgument(0) . ' of ' . $event->getArgument(1) . ')', ['app' => 'updater']);
491
+        });
492
+        $dispatcher->addListener('\OC\DB\Migrator::checkTable', function($event) use ($log) {
493
+            if (!$event instanceof GenericEvent) {
494
+                return;
495
+            }
496
+            $log->info('\OC\DB\Migrator::checkTable: ' . $event->getSubject() . ' (' . $event->getArgument(0) . ' of ' . $event->getArgument(1) . ')', ['app' => 'updater']);
497
+        });
498
+
499
+        $repairListener = function($event) use ($log) {
500
+            if (!$event instanceof GenericEvent) {
501
+                return;
502
+            }
503
+            switch ($event->getSubject()) {
504
+                case '\OC\Repair::startProgress':
505
+                    $log->info('\OC\Repair::startProgress: Starting ... ' . $event->getArgument(1) .  ' (' . $event->getArgument(0) . ')', ['app' => 'updater']);
506
+                    break;
507
+                case '\OC\Repair::advance':
508
+                    $desc = $event->getArgument(1);
509
+                    if (empty($desc)) {
510
+                        $desc = '';
511
+                    }
512
+                    $log->info('\OC\Repair::advance: ' . $desc . ' (' . $event->getArgument(0) . ')', ['app' => 'updater']);
513
+
514
+                    break;
515
+                case '\OC\Repair::finishProgress':
516
+                    $log->info('\OC\Repair::finishProgress', ['app' => 'updater']);
517
+                    break;
518
+                case '\OC\Repair::step':
519
+                    $log->info('\OC\Repair::step: Repair step: ' . $event->getArgument(0), ['app' => 'updater']);
520
+                    break;
521
+                case '\OC\Repair::info':
522
+                    $log->info('\OC\Repair::info: Repair info: ' . $event->getArgument(0), ['app' => 'updater']);
523
+                    break;
524
+                case '\OC\Repair::warning':
525
+                    $log->warning('\OC\Repair::warning: Repair warning: ' . $event->getArgument(0), ['app' => 'updater']);
526
+                    break;
527
+                case '\OC\Repair::error':
528
+                    $log->error('\OC\Repair::error: Repair error: ' . $event->getArgument(0), ['app' => 'updater']);
529
+                    break;
530
+            }
531
+        };
532
+
533
+        $dispatcher->addListener('\OC\Repair::startProgress', $repairListener);
534
+        $dispatcher->addListener('\OC\Repair::advance', $repairListener);
535
+        $dispatcher->addListener('\OC\Repair::finishProgress', $repairListener);
536
+        $dispatcher->addListener('\OC\Repair::step', $repairListener);
537
+        $dispatcher->addListener('\OC\Repair::info', $repairListener);
538
+        $dispatcher->addListener('\OC\Repair::warning', $repairListener);
539
+        $dispatcher->addListener('\OC\Repair::error', $repairListener);
540
+
541
+
542
+        $this->listen('\OC\Updater', 'maintenanceEnabled', function () use($log) {
543
+            $log->info('\OC\Updater::maintenanceEnabled: Turned on maintenance mode', ['app' => 'updater']);
544
+        });
545
+        $this->listen('\OC\Updater', 'maintenanceDisabled', function () use($log) {
546
+            $log->info('\OC\Updater::maintenanceDisabled: Turned off maintenance mode', ['app' => 'updater']);
547
+        });
548
+        $this->listen('\OC\Updater', 'maintenanceActive', function () use($log) {
549
+            $log->info('\OC\Updater::maintenanceActive: Maintenance mode is kept active', ['app' => 'updater']);
550
+        });
551
+        $this->listen('\OC\Updater', 'updateEnd', function ($success) use($log) {
552
+            if ($success) {
553
+                $log->info('\OC\Updater::updateEnd: Update successful', ['app' => 'updater']);
554
+            } else {
555
+                $log->error('\OC\Updater::updateEnd: Update failed', ['app' => 'updater']);
556
+            }
557
+        });
558
+        $this->listen('\OC\Updater', 'dbUpgradeBefore', function () use($log) {
559
+            $log->info('\OC\Updater::dbUpgradeBefore: Updating database schema', ['app' => 'updater']);
560
+        });
561
+        $this->listen('\OC\Updater', 'dbUpgrade', function () use($log) {
562
+            $log->info('\OC\Updater::dbUpgrade: Updated database', ['app' => 'updater']);
563
+        });
564
+        $this->listen('\OC\Updater', 'dbSimulateUpgradeBefore', function () use($log) {
565
+            $log->info('\OC\Updater::dbSimulateUpgradeBefore: Checking whether the database schema can be updated (this can take a long time depending on the database size)', ['app' => 'updater']);
566
+        });
567
+        $this->listen('\OC\Updater', 'dbSimulateUpgrade', function () use($log) {
568
+            $log->info('\OC\Updater::dbSimulateUpgrade: Checked database schema update', ['app' => 'updater']);
569
+        });
570
+        $this->listen('\OC\Updater', 'incompatibleAppDisabled', function ($app) use($log) {
571
+            $log->info('\OC\Updater::incompatibleAppDisabled: Disabled incompatible app: ' . $app, ['app' => 'updater']);
572
+        });
573
+        $this->listen('\OC\Updater', 'checkAppStoreAppBefore', function ($app) use($log) {
574
+            $log->info('\OC\Updater::checkAppStoreAppBefore: Checking for update of app "' . $app . '" in appstore', ['app' => 'updater']);
575
+        });
576
+        $this->listen('\OC\Updater', 'upgradeAppStoreApp', function ($app) use($log) {
577
+            $log->info('\OC\Updater::upgradeAppStoreApp: Update app "' . $app . '" from appstore', ['app' => 'updater']);
578
+        });
579
+        $this->listen('\OC\Updater', 'checkAppStoreApp', function ($app) use($log) {
580
+            $log->info('\OC\Updater::checkAppStoreApp: Checked for update of app "' . $app . '" in appstore', ['app' => 'updater']);
581
+        });
582
+        $this->listen('\OC\Updater', 'appUpgradeCheckBefore', function () use ($log) {
583
+            $log->info('\OC\Updater::appUpgradeCheckBefore: Checking updates of apps', ['app' => 'updater']);
584
+        });
585
+        $this->listen('\OC\Updater', 'appSimulateUpdate', function ($app) use ($log) {
586
+            $log->info('\OC\Updater::appSimulateUpdate: Checking whether the database schema for <' . $app . '> can be updated (this can take a long time depending on the database size)', ['app' => 'updater']);
587
+        });
588
+        $this->listen('\OC\Updater', 'appUpgradeCheck', function () use ($log) {
589
+            $log->info('\OC\Updater::appUpgradeCheck: Checked database schema update for apps', ['app' => 'updater']);
590
+        });
591
+        $this->listen('\OC\Updater', 'appUpgradeStarted', function ($app) use ($log) {
592
+            $log->info('\OC\Updater::appUpgradeStarted: Updating <' . $app . '> ...', ['app' => 'updater']);
593
+        });
594
+        $this->listen('\OC\Updater', 'appUpgrade', function ($app, $version) use ($log) {
595
+            $log->info('\OC\Updater::appUpgrade: Updated <' . $app . '> to ' . $version, ['app' => 'updater']);
596
+        });
597
+        $this->listen('\OC\Updater', 'failure', function ($message) use($log) {
598
+            $log->error('\OC\Updater::failure: ' . $message, ['app' => 'updater']);
599
+        });
600
+        $this->listen('\OC\Updater', 'setDebugLogLevel', function () use($log) {
601
+            $log->info('\OC\Updater::setDebugLogLevel: Set log level to debug', ['app' => 'updater']);
602
+        });
603
+        $this->listen('\OC\Updater', 'resetLogLevel', function ($logLevel, $logLevelName) use($log) {
604
+            $log->info('\OC\Updater::resetLogLevel: Reset log level to ' . $logLevelName . '(' . $logLevel . ')', ['app' => 'updater']);
605
+        });
606
+        $this->listen('\OC\Updater', 'startCheckCodeIntegrity', function () use($log) {
607
+            $log->info('\OC\Updater::startCheckCodeIntegrity: Starting code integrity check...', ['app' => 'updater']);
608
+        });
609
+        $this->listen('\OC\Updater', 'finishedCheckCodeIntegrity', function () use($log) {
610
+            $log->info('\OC\Updater::finishedCheckCodeIntegrity: Finished code integrity check', ['app' => 'updater']);
611
+        });
612
+
613
+    }
614 614
 
615 615
 }
616 616
 
Please login to merge, or discard this patch.
Spacing   +65 added lines, -65 removed lines patch added patch discarded remove patch
@@ -101,43 +101,43 @@  discard block
 block discarded – undo
101 101
 		$this->logAllEvents();
102 102
 
103 103
 		$logLevel = $this->config->getSystemValue('loglevel', Util::WARN);
104
-		$this->emit('\OC\Updater', 'setDebugLogLevel', [ $logLevel, $this->logLevelNames[$logLevel] ]);
104
+		$this->emit('\OC\Updater', 'setDebugLogLevel', [$logLevel, $this->logLevelNames[$logLevel]]);
105 105
 		$this->config->setSystemValue('loglevel', Util::DEBUG);
106 106
 
107 107
 		$wasMaintenanceModeEnabled = $this->config->getSystemValue('maintenance', false);
108 108
 
109
-		if(!$wasMaintenanceModeEnabled) {
109
+		if (!$wasMaintenanceModeEnabled) {
110 110
 			$this->config->setSystemValue('maintenance', true);
111 111
 			$this->emit('\OC\Updater', 'maintenanceEnabled');
112 112
 		}
113 113
 
114 114
 		$installedVersion = $this->config->getSystemValue('version', '0.0.0');
115 115
 		$currentVersion = implode('.', \OCP\Util::getVersion());
116
-		$this->log->debug('starting upgrade from ' . $installedVersion . ' to ' . $currentVersion, array('app' => 'core'));
116
+		$this->log->debug('starting upgrade from '.$installedVersion.' to '.$currentVersion, array('app' => 'core'));
117 117
 
118 118
 		$success = true;
119 119
 		try {
120 120
 			$this->doUpgrade($currentVersion, $installedVersion);
121 121
 		} catch (HintException $exception) {
122 122
 			$this->log->logException($exception, ['app' => 'core']);
123
-			$this->emit('\OC\Updater', 'failure', array($exception->getMessage() . ': ' .$exception->getHint()));
123
+			$this->emit('\OC\Updater', 'failure', array($exception->getMessage().': '.$exception->getHint()));
124 124
 			$success = false;
125 125
 		} catch (\Exception $exception) {
126 126
 			$this->log->logException($exception, ['app' => 'core']);
127
-			$this->emit('\OC\Updater', 'failure', array(get_class($exception) . ': ' .$exception->getMessage()));
127
+			$this->emit('\OC\Updater', 'failure', array(get_class($exception).': '.$exception->getMessage()));
128 128
 			$success = false;
129 129
 		}
130 130
 
131 131
 		$this->emit('\OC\Updater', 'updateEnd', array($success));
132 132
 
133
-		if(!$wasMaintenanceModeEnabled && $success) {
133
+		if (!$wasMaintenanceModeEnabled && $success) {
134 134
 			$this->config->setSystemValue('maintenance', false);
135 135
 			$this->emit('\OC\Updater', 'maintenanceDisabled');
136 136
 		} else {
137 137
 			$this->emit('\OC\Updater', 'maintenanceActive');
138 138
 		}
139 139
 
140
-		$this->emit('\OC\Updater', 'resetLogLevel', [ $logLevel, $this->logLevelNames[$logLevel] ]);
140
+		$this->emit('\OC\Updater', 'resetLogLevel', [$logLevel, $this->logLevelNames[$logLevel]]);
141 141
 		$this->config->setSystemValue('loglevel', $logLevel);
142 142
 		$this->config->setSystemValue('installed', true);
143 143
 
@@ -151,7 +151,7 @@  discard block
 block discarded – undo
151 151
 	 */
152 152
 	private function getAllowedPreviousVersions() {
153 153
 		// this should really be a JSON file
154
-		require \OC::$SERVERROOT . '/version.php';
154
+		require \OC::$SERVERROOT.'/version.php';
155 155
 		/** @var array $OC_VersionCanBeUpgradedFrom */
156 156
 		return $OC_VersionCanBeUpgradedFrom;
157 157
 	}
@@ -163,7 +163,7 @@  discard block
 block discarded – undo
163 163
 	 */
164 164
 	private function getVendor() {
165 165
 		// this should really be a JSON file
166
-		require \OC::$SERVERROOT . '/version.php';
166
+		require \OC::$SERVERROOT.'/version.php';
167 167
 		/** @var string $vendor */
168 168
 		return (string) $vendor;
169 169
 	}
@@ -177,7 +177,7 @@  discard block
 block discarded – undo
177 177
 	 */
178 178
 	public function isUpgradePossible($oldVersion, $newVersion, array $allowedPreviousVersions) {
179 179
 		$version = explode('.', $oldVersion);
180
-		$majorMinor = $version[0] . '.' . $version[1];
180
+		$majorMinor = $version[0].'.'.$version[1];
181 181
 
182 182
 		$currentVendor = $this->config->getAppValue('core', 'vendor', '');
183 183
 
@@ -224,7 +224,7 @@  discard block
 block discarded – undo
224 224
 		// create empty file in data dir, so we can later find
225 225
 		// out that this is indeed an ownCloud data directory
226 226
 		// (in case it didn't exist before)
227
-		file_put_contents($this->config->getSystemValue('datadirectory', \OC::$SERVERROOT . '/data') . '/.ocdata', '');
227
+		file_put_contents($this->config->getSystemValue('datadirectory', \OC::$SERVERROOT.'/data').'/.ocdata', '');
228 228
 
229 229
 		// pre-upgrade repairs
230 230
 		$repair = new Repair(Repair::getBeforeUpgradeRepairSteps(), \OC::$server->getEventDispatcher());
@@ -256,7 +256,7 @@  discard block
 block discarded – undo
256 256
 		foreach ($errors as $appId => $exception) {
257 257
 			/** @var \Exception $exception */
258 258
 			$this->log->logException($exception, ['app' => $appId]);
259
-			$this->emit('\OC\Updater', 'failure', [$appId . ': ' . $exception->getMessage()]);
259
+			$this->emit('\OC\Updater', 'failure', [$appId.': '.$exception->getMessage()]);
260 260
 		}
261 261
 
262 262
 		// post-upgrade repairs
@@ -267,7 +267,7 @@  discard block
 block discarded – undo
267 267
 		$this->config->setAppValue('core', 'lastupdatedat', 0);
268 268
 
269 269
 		// Check for code integrity if not disabled
270
-		if(\OC::$server->getIntegrityCodeChecker()->isCodeCheckEnforced()) {
270
+		if (\OC::$server->getIntegrityCodeChecker()->isCodeCheckEnforced()) {
271 271
 			$this->emit('\OC\Updater', 'startCheckCodeIntegrity');
272 272
 			$this->checker->runInstanceVerification();
273 273
 			$this->emit('\OC\Updater', 'finishedCheckCodeIntegrity');
@@ -308,12 +308,12 @@  discard block
 block discarded – undo
308 308
 				 * @link https://github.com/owncloud/core/issues/10980
309 309
 				 * @see \OC_App::updateApp
310 310
 				 */
311
-				if (file_exists(\OC_App::getAppPath($appId) . '/appinfo/preupdate.php')) {
311
+				if (file_exists(\OC_App::getAppPath($appId).'/appinfo/preupdate.php')) {
312 312
 					$this->includePreUpdate($appId);
313 313
 				}
314
-				if (file_exists(\OC_App::getAppPath($appId) . '/appinfo/database.xml')) {
314
+				if (file_exists(\OC_App::getAppPath($appId).'/appinfo/database.xml')) {
315 315
 					$this->emit('\OC\Updater', 'appSimulateUpdate', array($appId));
316
-					\OC_DB::simulateUpdateDbFromStructure(\OC_App::getAppPath($appId) . '/appinfo/database.xml');
316
+					\OC_DB::simulateUpdateDbFromStructure(\OC_App::getAppPath($appId).'/appinfo/database.xml');
317 317
 				}
318 318
 			}
319 319
 		}
@@ -326,7 +326,7 @@  discard block
 block discarded – undo
326 326
 	 * @param string $appId
327 327
 	 */
328 328
 	private function includePreUpdate($appId) {
329
-		include \OC_App::getAppPath($appId) . '/appinfo/preupdate.php';
329
+		include \OC_App::getAppPath($appId).'/appinfo/preupdate.php';
330 330
 	}
331 331
 
332 332
 	/**
@@ -344,7 +344,7 @@  discard block
 block discarded – undo
344 344
 		foreach ($apps as $appId) {
345 345
 			$priorityType = false;
346 346
 			foreach ($priorityTypes as $type) {
347
-				if(!isset($stacks[$type])) {
347
+				if (!isset($stacks[$type])) {
348 348
 					$stacks[$type] = array();
349 349
 				}
350 350
 				if (\OC_App::isType($appId, $type)) {
@@ -364,7 +364,7 @@  discard block
 block discarded – undo
364 364
 					\OC_App::updateApp($appId);
365 365
 					$this->emit('\OC\Updater', 'appUpgrade', [$appId, \OC_App::getAppVersion($appId)]);
366 366
 				}
367
-				if($type !== $pseudoOtherType) {
367
+				if ($type !== $pseudoOtherType) {
368 368
 					// load authentication, filesystem and logging apps after
369 369
 					// upgrading them. Other apps my need to rely on modifying
370 370
 					// user and/or filesystem aspects.
@@ -392,9 +392,9 @@  discard block
 block discarded – undo
392 392
 		foreach ($apps as $app) {
393 393
 			// check if the app is compatible with this version of ownCloud
394 394
 			$info = OC_App::getAppInfo($app);
395
-			if(!OC_App::isAppCompatible($version, $info)) {
395
+			if (!OC_App::isAppCompatible($version, $info)) {
396 396
 				if ($appManager->isShipped($app)) {
397
-					throw new \UnexpectedValueException('The files of the app "' . $app . '" were not correctly replaced before running the update');
397
+					throw new \UnexpectedValueException('The files of the app "'.$app.'" were not correctly replaced before running the update');
398 398
 				}
399 399
 				OC_App::disable($app);
400 400
 				$this->emit('\OC\Updater', 'incompatibleAppDisabled', array($app));
@@ -433,7 +433,7 @@  discard block
 block discarded – undo
433 433
 	 * @throws \Exception
434 434
 	 */
435 435
 	private function upgradeAppStoreApps(array $disabledApps, $reenable = false) {
436
-		foreach($disabledApps as $app) {
436
+		foreach ($disabledApps as $app) {
437 437
 			try {
438 438
 				$this->emit('\OC\Updater', 'checkAppStoreAppBefore', [$app]);
439 439
 				if ($this->installer->isUpdateAvailable($app)) {
@@ -457,22 +457,22 @@  discard block
 block discarded – undo
457 457
 	 */
458 458
 	private function emitRepairEvents() {
459 459
 		$dispatcher = \OC::$server->getEventDispatcher();
460
-		$dispatcher->addListener('\OC\Repair::warning', function ($event) {
460
+		$dispatcher->addListener('\OC\Repair::warning', function($event) {
461 461
 			if ($event instanceof GenericEvent) {
462 462
 				$this->emit('\OC\Updater', 'repairWarning', $event->getArguments());
463 463
 			}
464 464
 		});
465
-		$dispatcher->addListener('\OC\Repair::error', function ($event) {
465
+		$dispatcher->addListener('\OC\Repair::error', function($event) {
466 466
 			if ($event instanceof GenericEvent) {
467 467
 				$this->emit('\OC\Updater', 'repairError', $event->getArguments());
468 468
 			}
469 469
 		});
470
-		$dispatcher->addListener('\OC\Repair::info', function ($event) {
470
+		$dispatcher->addListener('\OC\Repair::info', function($event) {
471 471
 			if ($event instanceof GenericEvent) {
472 472
 				$this->emit('\OC\Updater', 'repairInfo', $event->getArguments());
473 473
 			}
474 474
 		});
475
-		$dispatcher->addListener('\OC\Repair::step', function ($event) {
475
+		$dispatcher->addListener('\OC\Repair::step', function($event) {
476 476
 			if ($event instanceof GenericEvent) {
477 477
 				$this->emit('\OC\Updater', 'repairStep', $event->getArguments());
478 478
 			}
@@ -487,13 +487,13 @@  discard block
 block discarded – undo
487 487
 			if (!$event instanceof GenericEvent) {
488 488
 				return;
489 489
 			}
490
-			$log->info('\OC\DB\Migrator::executeSql: ' . $event->getSubject() . ' (' . $event->getArgument(0) . ' of ' . $event->getArgument(1) . ')', ['app' => 'updater']);
490
+			$log->info('\OC\DB\Migrator::executeSql: '.$event->getSubject().' ('.$event->getArgument(0).' of '.$event->getArgument(1).')', ['app' => 'updater']);
491 491
 		});
492 492
 		$dispatcher->addListener('\OC\DB\Migrator::checkTable', function($event) use ($log) {
493 493
 			if (!$event instanceof GenericEvent) {
494 494
 				return;
495 495
 			}
496
-			$log->info('\OC\DB\Migrator::checkTable: ' . $event->getSubject() . ' (' . $event->getArgument(0) . ' of ' . $event->getArgument(1) . ')', ['app' => 'updater']);
496
+			$log->info('\OC\DB\Migrator::checkTable: '.$event->getSubject().' ('.$event->getArgument(0).' of '.$event->getArgument(1).')', ['app' => 'updater']);
497 497
 		});
498 498
 
499 499
 		$repairListener = function($event) use ($log) {
@@ -502,30 +502,30 @@  discard block
 block discarded – undo
502 502
 			}
503 503
 			switch ($event->getSubject()) {
504 504
 				case '\OC\Repair::startProgress':
505
-					$log->info('\OC\Repair::startProgress: Starting ... ' . $event->getArgument(1) .  ' (' . $event->getArgument(0) . ')', ['app' => 'updater']);
505
+					$log->info('\OC\Repair::startProgress: Starting ... '.$event->getArgument(1).' ('.$event->getArgument(0).')', ['app' => 'updater']);
506 506
 					break;
507 507
 				case '\OC\Repair::advance':
508 508
 					$desc = $event->getArgument(1);
509 509
 					if (empty($desc)) {
510 510
 						$desc = '';
511 511
 					}
512
-					$log->info('\OC\Repair::advance: ' . $desc . ' (' . $event->getArgument(0) . ')', ['app' => 'updater']);
512
+					$log->info('\OC\Repair::advance: '.$desc.' ('.$event->getArgument(0).')', ['app' => 'updater']);
513 513
 
514 514
 					break;
515 515
 				case '\OC\Repair::finishProgress':
516 516
 					$log->info('\OC\Repair::finishProgress', ['app' => 'updater']);
517 517
 					break;
518 518
 				case '\OC\Repair::step':
519
-					$log->info('\OC\Repair::step: Repair step: ' . $event->getArgument(0), ['app' => 'updater']);
519
+					$log->info('\OC\Repair::step: Repair step: '.$event->getArgument(0), ['app' => 'updater']);
520 520
 					break;
521 521
 				case '\OC\Repair::info':
522
-					$log->info('\OC\Repair::info: Repair info: ' . $event->getArgument(0), ['app' => 'updater']);
522
+					$log->info('\OC\Repair::info: Repair info: '.$event->getArgument(0), ['app' => 'updater']);
523 523
 					break;
524 524
 				case '\OC\Repair::warning':
525
-					$log->warning('\OC\Repair::warning: Repair warning: ' . $event->getArgument(0), ['app' => 'updater']);
525
+					$log->warning('\OC\Repair::warning: Repair warning: '.$event->getArgument(0), ['app' => 'updater']);
526 526
 					break;
527 527
 				case '\OC\Repair::error':
528
-					$log->error('\OC\Repair::error: Repair error: ' . $event->getArgument(0), ['app' => 'updater']);
528
+					$log->error('\OC\Repair::error: Repair error: '.$event->getArgument(0), ['app' => 'updater']);
529 529
 					break;
530 530
 			}
531 531
 		};
@@ -539,74 +539,74 @@  discard block
 block discarded – undo
539 539
 		$dispatcher->addListener('\OC\Repair::error', $repairListener);
540 540
 
541 541
 
542
-		$this->listen('\OC\Updater', 'maintenanceEnabled', function () use($log) {
542
+		$this->listen('\OC\Updater', 'maintenanceEnabled', function() use($log) {
543 543
 			$log->info('\OC\Updater::maintenanceEnabled: Turned on maintenance mode', ['app' => 'updater']);
544 544
 		});
545
-		$this->listen('\OC\Updater', 'maintenanceDisabled', function () use($log) {
545
+		$this->listen('\OC\Updater', 'maintenanceDisabled', function() use($log) {
546 546
 			$log->info('\OC\Updater::maintenanceDisabled: Turned off maintenance mode', ['app' => 'updater']);
547 547
 		});
548
-		$this->listen('\OC\Updater', 'maintenanceActive', function () use($log) {
548
+		$this->listen('\OC\Updater', 'maintenanceActive', function() use($log) {
549 549
 			$log->info('\OC\Updater::maintenanceActive: Maintenance mode is kept active', ['app' => 'updater']);
550 550
 		});
551
-		$this->listen('\OC\Updater', 'updateEnd', function ($success) use($log) {
551
+		$this->listen('\OC\Updater', 'updateEnd', function($success) use($log) {
552 552
 			if ($success) {
553 553
 				$log->info('\OC\Updater::updateEnd: Update successful', ['app' => 'updater']);
554 554
 			} else {
555 555
 				$log->error('\OC\Updater::updateEnd: Update failed', ['app' => 'updater']);
556 556
 			}
557 557
 		});
558
-		$this->listen('\OC\Updater', 'dbUpgradeBefore', function () use($log) {
558
+		$this->listen('\OC\Updater', 'dbUpgradeBefore', function() use($log) {
559 559
 			$log->info('\OC\Updater::dbUpgradeBefore: Updating database schema', ['app' => 'updater']);
560 560
 		});
561
-		$this->listen('\OC\Updater', 'dbUpgrade', function () use($log) {
561
+		$this->listen('\OC\Updater', 'dbUpgrade', function() use($log) {
562 562
 			$log->info('\OC\Updater::dbUpgrade: Updated database', ['app' => 'updater']);
563 563
 		});
564
-		$this->listen('\OC\Updater', 'dbSimulateUpgradeBefore', function () use($log) {
564
+		$this->listen('\OC\Updater', 'dbSimulateUpgradeBefore', function() use($log) {
565 565
 			$log->info('\OC\Updater::dbSimulateUpgradeBefore: Checking whether the database schema can be updated (this can take a long time depending on the database size)', ['app' => 'updater']);
566 566
 		});
567
-		$this->listen('\OC\Updater', 'dbSimulateUpgrade', function () use($log) {
567
+		$this->listen('\OC\Updater', 'dbSimulateUpgrade', function() use($log) {
568 568
 			$log->info('\OC\Updater::dbSimulateUpgrade: Checked database schema update', ['app' => 'updater']);
569 569
 		});
570
-		$this->listen('\OC\Updater', 'incompatibleAppDisabled', function ($app) use($log) {
571
-			$log->info('\OC\Updater::incompatibleAppDisabled: Disabled incompatible app: ' . $app, ['app' => 'updater']);
570
+		$this->listen('\OC\Updater', 'incompatibleAppDisabled', function($app) use($log) {
571
+			$log->info('\OC\Updater::incompatibleAppDisabled: Disabled incompatible app: '.$app, ['app' => 'updater']);
572 572
 		});
573
-		$this->listen('\OC\Updater', 'checkAppStoreAppBefore', function ($app) use($log) {
574
-			$log->info('\OC\Updater::checkAppStoreAppBefore: Checking for update of app "' . $app . '" in appstore', ['app' => 'updater']);
573
+		$this->listen('\OC\Updater', 'checkAppStoreAppBefore', function($app) use($log) {
574
+			$log->info('\OC\Updater::checkAppStoreAppBefore: Checking for update of app "'.$app.'" in appstore', ['app' => 'updater']);
575 575
 		});
576
-		$this->listen('\OC\Updater', 'upgradeAppStoreApp', function ($app) use($log) {
577
-			$log->info('\OC\Updater::upgradeAppStoreApp: Update app "' . $app . '" from appstore', ['app' => 'updater']);
576
+		$this->listen('\OC\Updater', 'upgradeAppStoreApp', function($app) use($log) {
577
+			$log->info('\OC\Updater::upgradeAppStoreApp: Update app "'.$app.'" from appstore', ['app' => 'updater']);
578 578
 		});
579
-		$this->listen('\OC\Updater', 'checkAppStoreApp', function ($app) use($log) {
580
-			$log->info('\OC\Updater::checkAppStoreApp: Checked for update of app "' . $app . '" in appstore', ['app' => 'updater']);
579
+		$this->listen('\OC\Updater', 'checkAppStoreApp', function($app) use($log) {
580
+			$log->info('\OC\Updater::checkAppStoreApp: Checked for update of app "'.$app.'" in appstore', ['app' => 'updater']);
581 581
 		});
582
-		$this->listen('\OC\Updater', 'appUpgradeCheckBefore', function () use ($log) {
582
+		$this->listen('\OC\Updater', 'appUpgradeCheckBefore', function() use ($log) {
583 583
 			$log->info('\OC\Updater::appUpgradeCheckBefore: Checking updates of apps', ['app' => 'updater']);
584 584
 		});
585
-		$this->listen('\OC\Updater', 'appSimulateUpdate', function ($app) use ($log) {
586
-			$log->info('\OC\Updater::appSimulateUpdate: Checking whether the database schema for <' . $app . '> can be updated (this can take a long time depending on the database size)', ['app' => 'updater']);
585
+		$this->listen('\OC\Updater', 'appSimulateUpdate', function($app) use ($log) {
586
+			$log->info('\OC\Updater::appSimulateUpdate: Checking whether the database schema for <'.$app.'> can be updated (this can take a long time depending on the database size)', ['app' => 'updater']);
587 587
 		});
588
-		$this->listen('\OC\Updater', 'appUpgradeCheck', function () use ($log) {
588
+		$this->listen('\OC\Updater', 'appUpgradeCheck', function() use ($log) {
589 589
 			$log->info('\OC\Updater::appUpgradeCheck: Checked database schema update for apps', ['app' => 'updater']);
590 590
 		});
591
-		$this->listen('\OC\Updater', 'appUpgradeStarted', function ($app) use ($log) {
592
-			$log->info('\OC\Updater::appUpgradeStarted: Updating <' . $app . '> ...', ['app' => 'updater']);
591
+		$this->listen('\OC\Updater', 'appUpgradeStarted', function($app) use ($log) {
592
+			$log->info('\OC\Updater::appUpgradeStarted: Updating <'.$app.'> ...', ['app' => 'updater']);
593 593
 		});
594
-		$this->listen('\OC\Updater', 'appUpgrade', function ($app, $version) use ($log) {
595
-			$log->info('\OC\Updater::appUpgrade: Updated <' . $app . '> to ' . $version, ['app' => 'updater']);
594
+		$this->listen('\OC\Updater', 'appUpgrade', function($app, $version) use ($log) {
595
+			$log->info('\OC\Updater::appUpgrade: Updated <'.$app.'> to '.$version, ['app' => 'updater']);
596 596
 		});
597
-		$this->listen('\OC\Updater', 'failure', function ($message) use($log) {
598
-			$log->error('\OC\Updater::failure: ' . $message, ['app' => 'updater']);
597
+		$this->listen('\OC\Updater', 'failure', function($message) use($log) {
598
+			$log->error('\OC\Updater::failure: '.$message, ['app' => 'updater']);
599 599
 		});
600
-		$this->listen('\OC\Updater', 'setDebugLogLevel', function () use($log) {
600
+		$this->listen('\OC\Updater', 'setDebugLogLevel', function() use($log) {
601 601
 			$log->info('\OC\Updater::setDebugLogLevel: Set log level to debug', ['app' => 'updater']);
602 602
 		});
603
-		$this->listen('\OC\Updater', 'resetLogLevel', function ($logLevel, $logLevelName) use($log) {
604
-			$log->info('\OC\Updater::resetLogLevel: Reset log level to ' . $logLevelName . '(' . $logLevel . ')', ['app' => 'updater']);
603
+		$this->listen('\OC\Updater', 'resetLogLevel', function($logLevel, $logLevelName) use($log) {
604
+			$log->info('\OC\Updater::resetLogLevel: Reset log level to '.$logLevelName.'('.$logLevel.')', ['app' => 'updater']);
605 605
 		});
606
-		$this->listen('\OC\Updater', 'startCheckCodeIntegrity', function () use($log) {
606
+		$this->listen('\OC\Updater', 'startCheckCodeIntegrity', function() use($log) {
607 607
 			$log->info('\OC\Updater::startCheckCodeIntegrity: Starting code integrity check...', ['app' => 'updater']);
608 608
 		});
609
-		$this->listen('\OC\Updater', 'finishedCheckCodeIntegrity', function () use($log) {
609
+		$this->listen('\OC\Updater', 'finishedCheckCodeIntegrity', function() use($log) {
610 610
 			$log->info('\OC\Updater::finishedCheckCodeIntegrity: Finished code integrity check', ['app' => 'updater']);
611 611
 		});
612 612
 
Please login to merge, or discard this patch.
lib/private/legacy/app.php 1 patch
Indentation   +1097 added lines, -1097 removed lines patch added patch discarded remove patch
@@ -62,1101 +62,1101 @@
 block discarded – undo
62 62
  * upgrading and removing apps.
63 63
  */
64 64
 class OC_App {
65
-	static private $adminForms = array();
66
-	static private $personalForms = array();
67
-	static private $appTypes = array();
68
-	static private $loadedApps = array();
69
-	static private $altLogin = array();
70
-	static private $alreadyRegistered = [];
71
-	static public $autoDisabledApps = [];
72
-	const officialApp = 200;
73
-
74
-	/**
75
-	 * clean the appId
76
-	 *
77
-	 * @param string|boolean $app AppId that needs to be cleaned
78
-	 * @return string
79
-	 */
80
-	public static function cleanAppId($app) {
81
-		return str_replace(array('\0', '/', '\\', '..'), '', $app);
82
-	}
83
-
84
-	/**
85
-	 * Check if an app is loaded
86
-	 *
87
-	 * @param string $app
88
-	 * @return bool
89
-	 */
90
-	public static function isAppLoaded($app) {
91
-		return in_array($app, self::$loadedApps, true);
92
-	}
93
-
94
-	/**
95
-	 * loads all apps
96
-	 *
97
-	 * @param string[] | string | null $types
98
-	 * @return bool
99
-	 *
100
-	 * This function walks through the ownCloud directory and loads all apps
101
-	 * it can find. A directory contains an app if the file /appinfo/info.xml
102
-	 * exists.
103
-	 *
104
-	 * if $types is set, only apps of those types will be loaded
105
-	 */
106
-	public static function loadApps($types = null) {
107
-		if (\OC::$server->getSystemConfig()->getValue('maintenance', false)) {
108
-			return false;
109
-		}
110
-		// Load the enabled apps here
111
-		$apps = self::getEnabledApps();
112
-
113
-		// Add each apps' folder as allowed class path
114
-		foreach($apps as $app) {
115
-			$path = self::getAppPath($app);
116
-			if($path !== false) {
117
-				self::registerAutoloading($app, $path);
118
-			}
119
-		}
120
-
121
-		// prevent app.php from printing output
122
-		ob_start();
123
-		foreach ($apps as $app) {
124
-			if ((is_null($types) or self::isType($app, $types)) && !in_array($app, self::$loadedApps)) {
125
-				self::loadApp($app);
126
-			}
127
-		}
128
-		ob_end_clean();
129
-
130
-		return true;
131
-	}
132
-
133
-	/**
134
-	 * load a single app
135
-	 *
136
-	 * @param string $app
137
-	 * @throws Exception
138
-	 */
139
-	public static function loadApp($app) {
140
-		self::$loadedApps[] = $app;
141
-		$appPath = self::getAppPath($app);
142
-		if($appPath === false) {
143
-			return;
144
-		}
145
-
146
-		// in case someone calls loadApp() directly
147
-		self::registerAutoloading($app, $appPath);
148
-
149
-		if (is_file($appPath . '/appinfo/app.php')) {
150
-			\OC::$server->getEventLogger()->start('load_app_' . $app, 'Load app: ' . $app);
151
-			try {
152
-				self::requireAppFile($app);
153
-			} catch (Error $ex) {
154
-				\OC::$server->getLogger()->logException($ex);
155
-				if (!\OC::$server->getAppManager()->isShipped($app)) {
156
-					// Only disable apps which are not shipped
157
-					self::disable($app);
158
-					self::$autoDisabledApps[] = $app;
159
-				}
160
-			}
161
-			if (self::isType($app, array('authentication'))) {
162
-				// since authentication apps affect the "is app enabled for group" check,
163
-				// the enabled apps cache needs to be cleared to make sure that the
164
-				// next time getEnableApps() is called it will also include apps that were
165
-				// enabled for groups
166
-				self::$enabledAppsCache = array();
167
-			}
168
-			\OC::$server->getEventLogger()->end('load_app_' . $app);
169
-		}
170
-
171
-		$info = self::getAppInfo($app);
172
-		if (!empty($info['activity']['filters'])) {
173
-			foreach ($info['activity']['filters'] as $filter) {
174
-				\OC::$server->getActivityManager()->registerFilter($filter);
175
-			}
176
-		}
177
-		if (!empty($info['activity']['settings'])) {
178
-			foreach ($info['activity']['settings'] as $setting) {
179
-				\OC::$server->getActivityManager()->registerSetting($setting);
180
-			}
181
-		}
182
-		if (!empty($info['activity']['providers'])) {
183
-			foreach ($info['activity']['providers'] as $provider) {
184
-				\OC::$server->getActivityManager()->registerProvider($provider);
185
-			}
186
-		}
187
-
188
-		if (!empty($info['settings']['admin'])) {
189
-			foreach ($info['settings']['admin'] as $setting) {
190
-				\OC::$server->getSettingsManager()->registerSetting('admin', $setting);
191
-			}
192
-		}
193
-		if (!empty($info['settings']['admin-section'])) {
194
-			foreach ($info['settings']['admin-section'] as $section) {
195
-				\OC::$server->getSettingsManager()->registerSection('admin', $section);
196
-			}
197
-		}
198
-		if (!empty($info['settings']['personal'])) {
199
-			foreach ($info['settings']['personal'] as $setting) {
200
-				\OC::$server->getSettingsManager()->registerSetting('personal', $setting);
201
-			}
202
-		}
203
-		if (!empty($info['settings']['personal-section'])) {
204
-			foreach ($info['settings']['personal-section'] as $section) {
205
-				\OC::$server->getSettingsManager()->registerSection('personal', $section);
206
-			}
207
-		}
208
-
209
-		if (!empty($info['collaboration']['plugins'])) {
210
-			// deal with one or many plugin entries
211
-			$plugins = isset($info['collaboration']['plugins']['plugin']['@value']) ?
212
-				[$info['collaboration']['plugins']['plugin']] : $info['collaboration']['plugins']['plugin'];
213
-			foreach ($plugins as $plugin) {
214
-				if($plugin['@attributes']['type'] === 'collaborator-search') {
215
-					$pluginInfo = [
216
-						'shareType' => $plugin['@attributes']['share-type'],
217
-						'class' => $plugin['@value'],
218
-					];
219
-					\OC::$server->getCollaboratorSearch()->registerPlugin($pluginInfo);
220
-				} else if ($plugin['@attributes']['type'] === 'autocomplete-sort') {
221
-					\OC::$server->getAutoCompleteManager()->registerSorter($plugin['@value']);
222
-				}
223
-			}
224
-		}
225
-	}
226
-
227
-	/**
228
-	 * @internal
229
-	 * @param string $app
230
-	 * @param string $path
231
-	 */
232
-	public static function registerAutoloading($app, $path) {
233
-		$key = $app . '-' . $path;
234
-		if(isset(self::$alreadyRegistered[$key])) {
235
-			return;
236
-		}
237
-
238
-		self::$alreadyRegistered[$key] = true;
239
-
240
-		// Register on PSR-4 composer autoloader
241
-		$appNamespace = \OC\AppFramework\App::buildAppNamespace($app);
242
-		\OC::$server->registerNamespace($app, $appNamespace);
243
-
244
-		if (file_exists($path . '/composer/autoload.php')) {
245
-			require_once $path . '/composer/autoload.php';
246
-		} else {
247
-			\OC::$composerAutoloader->addPsr4($appNamespace . '\\', $path . '/lib/', true);
248
-			// Register on legacy autoloader
249
-			\OC::$loader->addValidRoot($path);
250
-		}
251
-
252
-		// Register Test namespace only when testing
253
-		if (defined('PHPUNIT_RUN') || defined('CLI_TEST_RUN')) {
254
-			\OC::$composerAutoloader->addPsr4($appNamespace . '\\Tests\\', $path . '/tests/', true);
255
-		}
256
-	}
257
-
258
-	/**
259
-	 * Load app.php from the given app
260
-	 *
261
-	 * @param string $app app name
262
-	 * @throws Error
263
-	 */
264
-	private static function requireAppFile($app) {
265
-		// encapsulated here to avoid variable scope conflicts
266
-		require_once $app . '/appinfo/app.php';
267
-	}
268
-
269
-	/**
270
-	 * check if an app is of a specific type
271
-	 *
272
-	 * @param string $app
273
-	 * @param string|array $types
274
-	 * @return bool
275
-	 */
276
-	public static function isType($app, $types) {
277
-		if (is_string($types)) {
278
-			$types = array($types);
279
-		}
280
-		$appTypes = self::getAppTypes($app);
281
-		foreach ($types as $type) {
282
-			if (array_search($type, $appTypes) !== false) {
283
-				return true;
284
-			}
285
-		}
286
-		return false;
287
-	}
288
-
289
-	/**
290
-	 * get the types of an app
291
-	 *
292
-	 * @param string $app
293
-	 * @return array
294
-	 */
295
-	private static function getAppTypes($app) {
296
-		//load the cache
297
-		if (count(self::$appTypes) == 0) {
298
-			self::$appTypes = \OC::$server->getAppConfig()->getValues(false, 'types');
299
-		}
300
-
301
-		if (isset(self::$appTypes[$app])) {
302
-			return explode(',', self::$appTypes[$app]);
303
-		} else {
304
-			return array();
305
-		}
306
-	}
307
-
308
-	/**
309
-	 * read app types from info.xml and cache them in the database
310
-	 */
311
-	public static function setAppTypes($app) {
312
-		$appData = self::getAppInfo($app);
313
-		if(!is_array($appData)) {
314
-			return;
315
-		}
316
-
317
-		if (isset($appData['types'])) {
318
-			$appTypes = implode(',', $appData['types']);
319
-		} else {
320
-			$appTypes = '';
321
-			$appData['types'] = [];
322
-		}
323
-
324
-		\OC::$server->getConfig()->setAppValue($app, 'types', $appTypes);
325
-
326
-		if (\OC::$server->getAppManager()->hasProtectedAppType($appData['types'])) {
327
-			$enabled = \OC::$server->getConfig()->getAppValue($app, 'enabled', 'yes');
328
-			if ($enabled !== 'yes' && $enabled !== 'no') {
329
-				\OC::$server->getConfig()->setAppValue($app, 'enabled', 'yes');
330
-			}
331
-		}
332
-	}
333
-
334
-	/**
335
-	 * get all enabled apps
336
-	 */
337
-	protected static $enabledAppsCache = array();
338
-
339
-	/**
340
-	 * Returns apps enabled for the current user.
341
-	 *
342
-	 * @param bool $forceRefresh whether to refresh the cache
343
-	 * @param bool $all whether to return apps for all users, not only the
344
-	 * currently logged in one
345
-	 * @return string[]
346
-	 */
347
-	public static function getEnabledApps($forceRefresh = false, $all = false) {
348
-		if (!\OC::$server->getSystemConfig()->getValue('installed', false)) {
349
-			return array();
350
-		}
351
-		// in incognito mode or when logged out, $user will be false,
352
-		// which is also the case during an upgrade
353
-		$appManager = \OC::$server->getAppManager();
354
-		if ($all) {
355
-			$user = null;
356
-		} else {
357
-			$user = \OC::$server->getUserSession()->getUser();
358
-		}
359
-
360
-		if (is_null($user)) {
361
-			$apps = $appManager->getInstalledApps();
362
-		} else {
363
-			$apps = $appManager->getEnabledAppsForUser($user);
364
-		}
365
-		$apps = array_filter($apps, function ($app) {
366
-			return $app !== 'files';//we add this manually
367
-		});
368
-		sort($apps);
369
-		array_unshift($apps, 'files');
370
-		return $apps;
371
-	}
372
-
373
-	/**
374
-	 * checks whether or not an app is enabled
375
-	 *
376
-	 * @param string $app app
377
-	 * @return bool
378
-	 * @deprecated 13.0.0 use \OC::$server->getAppManager()->isEnabledForUser($appId)
379
-	 *
380
-	 * This function checks whether or not an app is enabled.
381
-	 */
382
-	public static function isEnabled($app) {
383
-		return \OC::$server->getAppManager()->isEnabledForUser($app);
384
-	}
385
-
386
-	/**
387
-	 * enables an app
388
-	 *
389
-	 * @param string $appId
390
-	 * @param array $groups (optional) when set, only these groups will have access to the app
391
-	 * @throws \Exception
392
-	 * @return void
393
-	 *
394
-	 * This function set an app as enabled in appconfig.
395
-	 */
396
-	public function enable($appId,
397
-						   $groups = null) {
398
-		self::$enabledAppsCache = []; // flush
399
-
400
-		// Check if app is already downloaded
401
-		$installer = \OC::$server->query(Installer::class);
402
-		$isDownloaded = $installer->isDownloaded($appId);
403
-
404
-		if(!$isDownloaded) {
405
-			$installer->downloadApp($appId);
406
-		}
407
-
408
-		$installer->installApp($appId);
409
-
410
-		$appManager = \OC::$server->getAppManager();
411
-		if (!is_null($groups)) {
412
-			$groupManager = \OC::$server->getGroupManager();
413
-			$groupsList = [];
414
-			foreach ($groups as $group) {
415
-				$groupItem = $groupManager->get($group);
416
-				if ($groupItem instanceof \OCP\IGroup) {
417
-					$groupsList[] = $groupManager->get($group);
418
-				}
419
-			}
420
-			$appManager->enableAppForGroups($appId, $groupsList);
421
-		} else {
422
-			$appManager->enableApp($appId);
423
-		}
424
-	}
425
-
426
-	/**
427
-	 * This function set an app as disabled in appconfig.
428
-	 *
429
-	 * @param string $app app
430
-	 * @throws Exception
431
-	 */
432
-	public static function disable($app) {
433
-		// flush
434
-		self::$enabledAppsCache = array();
435
-
436
-		// run uninstall steps
437
-		$appData = OC_App::getAppInfo($app);
438
-		if (!is_null($appData)) {
439
-			OC_App::executeRepairSteps($app, $appData['repair-steps']['uninstall']);
440
-		}
441
-
442
-		// emit disable hook - needed anymore ?
443
-		\OC_Hook::emit('OC_App', 'pre_disable', array('app' => $app));
444
-
445
-		// finally disable it
446
-		$appManager = \OC::$server->getAppManager();
447
-		$appManager->disableApp($app);
448
-	}
449
-
450
-	// This is private as well. It simply works, so don't ask for more details
451
-	private static function proceedNavigation($list) {
452
-		usort($list, function($a, $b) {
453
-			if (isset($a['order']) && isset($b['order'])) {
454
-				return ($a['order'] < $b['order']) ? -1 : 1;
455
-			} else if (isset($a['order']) || isset($b['order'])) {
456
-				return isset($a['order']) ? -1 : 1;
457
-			} else {
458
-				return ($a['name'] < $b['name']) ? -1 : 1;
459
-			}
460
-		});
461
-
462
-		$activeApp = OC::$server->getNavigationManager()->getActiveEntry();
463
-		foreach ($list as $index => &$navEntry) {
464
-			if ($navEntry['id'] == $activeApp) {
465
-				$navEntry['active'] = true;
466
-			} else {
467
-				$navEntry['active'] = false;
468
-			}
469
-		}
470
-		unset($navEntry);
471
-
472
-		return $list;
473
-	}
474
-
475
-	/**
476
-	 * Get the path where to install apps
477
-	 *
478
-	 * @return string|false
479
-	 */
480
-	public static function getInstallPath() {
481
-		if (\OC::$server->getSystemConfig()->getValue('appstoreenabled', true) == false) {
482
-			return false;
483
-		}
484
-
485
-		foreach (OC::$APPSROOTS as $dir) {
486
-			if (isset($dir['writable']) && $dir['writable'] === true) {
487
-				return $dir['path'];
488
-			}
489
-		}
490
-
491
-		\OCP\Util::writeLog('core', 'No application directories are marked as writable.', \OCP\Util::ERROR);
492
-		return null;
493
-	}
494
-
495
-
496
-	/**
497
-	 * search for an app in all app-directories
498
-	 *
499
-	 * @param string $appId
500
-	 * @return false|string
501
-	 */
502
-	public static function findAppInDirectories($appId) {
503
-		$sanitizedAppId = self::cleanAppId($appId);
504
-		if($sanitizedAppId !== $appId) {
505
-			return false;
506
-		}
507
-		static $app_dir = array();
508
-
509
-		if (isset($app_dir[$appId])) {
510
-			return $app_dir[$appId];
511
-		}
512
-
513
-		$possibleApps = array();
514
-		foreach (OC::$APPSROOTS as $dir) {
515
-			if (file_exists($dir['path'] . '/' . $appId)) {
516
-				$possibleApps[] = $dir;
517
-			}
518
-		}
519
-
520
-		if (empty($possibleApps)) {
521
-			return false;
522
-		} elseif (count($possibleApps) === 1) {
523
-			$dir = array_shift($possibleApps);
524
-			$app_dir[$appId] = $dir;
525
-			return $dir;
526
-		} else {
527
-			$versionToLoad = array();
528
-			foreach ($possibleApps as $possibleApp) {
529
-				$version = self::getAppVersionByPath($possibleApp['path']);
530
-				if (empty($versionToLoad) || version_compare($version, $versionToLoad['version'], '>')) {
531
-					$versionToLoad = array(
532
-						'dir' => $possibleApp,
533
-						'version' => $version,
534
-					);
535
-				}
536
-			}
537
-			$app_dir[$appId] = $versionToLoad['dir'];
538
-			return $versionToLoad['dir'];
539
-			//TODO - write test
540
-		}
541
-	}
542
-
543
-	/**
544
-	 * Get the directory for the given app.
545
-	 * If the app is defined in multiple directories, the first one is taken. (false if not found)
546
-	 *
547
-	 * @param string $appId
548
-	 * @return string|false
549
-	 */
550
-	public static function getAppPath($appId) {
551
-		if ($appId === null || trim($appId) === '') {
552
-			return false;
553
-		}
554
-
555
-		if (($dir = self::findAppInDirectories($appId)) != false) {
556
-			return $dir['path'] . '/' . $appId;
557
-		}
558
-		return false;
559
-	}
560
-
561
-	/**
562
-	 * Get the path for the given app on the access
563
-	 * If the app is defined in multiple directories, the first one is taken. (false if not found)
564
-	 *
565
-	 * @param string $appId
566
-	 * @return string|false
567
-	 */
568
-	public static function getAppWebPath($appId) {
569
-		if (($dir = self::findAppInDirectories($appId)) != false) {
570
-			return OC::$WEBROOT . $dir['url'] . '/' . $appId;
571
-		}
572
-		return false;
573
-	}
574
-
575
-	/**
576
-	 * get the last version of the app from appinfo/info.xml
577
-	 *
578
-	 * @param string $appId
579
-	 * @param bool $useCache
580
-	 * @return string
581
-	 * @deprecated 14.0.0 use \OC::$server->getAppManager()->getAppVersion()
582
-	 */
583
-	public static function getAppVersion($appId, $useCache = true) {
584
-		return \OC::$server->getAppManager()->getAppVersion($appId, $useCache);
585
-	}
586
-
587
-	/**
588
-	 * get app's version based on it's path
589
-	 *
590
-	 * @param string $path
591
-	 * @return string
592
-	 */
593
-	public static function getAppVersionByPath($path) {
594
-		$infoFile = $path . '/appinfo/info.xml';
595
-		$appData = \OC::$server->getAppManager()->getAppInfo($infoFile, true);
596
-		return isset($appData['version']) ? $appData['version'] : '';
597
-	}
598
-
599
-
600
-	/**
601
-	 * Read all app metadata from the info.xml file
602
-	 *
603
-	 * @param string $appId id of the app or the path of the info.xml file
604
-	 * @param bool $path
605
-	 * @param string $lang
606
-	 * @return array|null
607
-	 * @note all data is read from info.xml, not just pre-defined fields
608
-	 * @deprecated 14.0.0 use \OC::$server->getAppManager()->getAppInfo()
609
-	 */
610
-	public static function getAppInfo($appId, $path = false, $lang = null) {
611
-		return \OC::$server->getAppManager()->getAppInfo($appId, $path, $lang);
612
-	}
613
-
614
-	/**
615
-	 * Returns the navigation
616
-	 *
617
-	 * @return array
618
-	 *
619
-	 * This function returns an array containing all entries added. The
620
-	 * entries are sorted by the key 'order' ascending. Additional to the keys
621
-	 * given for each app the following keys exist:
622
-	 *   - active: boolean, signals if the user is on this navigation entry
623
-	 */
624
-	public static function getNavigation() {
625
-		$entries = OC::$server->getNavigationManager()->getAll();
626
-		return self::proceedNavigation($entries);
627
-	}
628
-
629
-	/**
630
-	 * Returns the Settings Navigation
631
-	 *
632
-	 * @return string[]
633
-	 *
634
-	 * This function returns an array containing all settings pages added. The
635
-	 * entries are sorted by the key 'order' ascending.
636
-	 */
637
-	public static function getSettingsNavigation() {
638
-		$entries = OC::$server->getNavigationManager()->getAll('settings');
639
-		return self::proceedNavigation($entries);
640
-	}
641
-
642
-	/**
643
-	 * get the id of loaded app
644
-	 *
645
-	 * @return string
646
-	 */
647
-	public static function getCurrentApp() {
648
-		$request = \OC::$server->getRequest();
649
-		$script = substr($request->getScriptName(), strlen(OC::$WEBROOT) + 1);
650
-		$topFolder = substr($script, 0, strpos($script, '/') ?: 0);
651
-		if (empty($topFolder)) {
652
-			$path_info = $request->getPathInfo();
653
-			if ($path_info) {
654
-				$topFolder = substr($path_info, 1, strpos($path_info, '/', 1) - 1);
655
-			}
656
-		}
657
-		if ($topFolder == 'apps') {
658
-			$length = strlen($topFolder);
659
-			return substr($script, $length + 1, strpos($script, '/', $length + 1) - $length - 1);
660
-		} else {
661
-			return $topFolder;
662
-		}
663
-	}
664
-
665
-	/**
666
-	 * @param string $type
667
-	 * @return array
668
-	 */
669
-	public static function getForms($type) {
670
-		$forms = array();
671
-		switch ($type) {
672
-			case 'admin':
673
-				$source = self::$adminForms;
674
-				break;
675
-			case 'personal':
676
-				$source = self::$personalForms;
677
-				break;
678
-			default:
679
-				return array();
680
-		}
681
-		foreach ($source as $form) {
682
-			$forms[] = include $form;
683
-		}
684
-		return $forms;
685
-	}
686
-
687
-	/**
688
-	 * register an admin form to be shown
689
-	 *
690
-	 * @param string $app
691
-	 * @param string $page
692
-	 */
693
-	public static function registerAdmin($app, $page) {
694
-		self::$adminForms[] = $app . '/' . $page . '.php';
695
-	}
696
-
697
-	/**
698
-	 * register a personal form to be shown
699
-	 * @param string $app
700
-	 * @param string $page
701
-	 */
702
-	public static function registerPersonal($app, $page) {
703
-		self::$personalForms[] = $app . '/' . $page . '.php';
704
-	}
705
-
706
-	/**
707
-	 * @param array $entry
708
-	 */
709
-	public static function registerLogIn(array $entry) {
710
-		self::$altLogin[] = $entry;
711
-	}
712
-
713
-	/**
714
-	 * @return array
715
-	 */
716
-	public static function getAlternativeLogIns() {
717
-		return self::$altLogin;
718
-	}
719
-
720
-	/**
721
-	 * get a list of all apps in the apps folder
722
-	 *
723
-	 * @return array an array of app names (string IDs)
724
-	 * @todo: change the name of this method to getInstalledApps, which is more accurate
725
-	 */
726
-	public static function getAllApps() {
727
-
728
-		$apps = array();
729
-
730
-		foreach (OC::$APPSROOTS as $apps_dir) {
731
-			if (!is_readable($apps_dir['path'])) {
732
-				\OCP\Util::writeLog('core', 'unable to read app folder : ' . $apps_dir['path'], \OCP\Util::WARN);
733
-				continue;
734
-			}
735
-			$dh = opendir($apps_dir['path']);
736
-
737
-			if (is_resource($dh)) {
738
-				while (($file = readdir($dh)) !== false) {
739
-
740
-					if ($file[0] != '.' and is_dir($apps_dir['path'] . '/' . $file) and is_file($apps_dir['path'] . '/' . $file . '/appinfo/info.xml')) {
741
-
742
-						$apps[] = $file;
743
-					}
744
-				}
745
-			}
746
-		}
747
-
748
-		$apps = array_unique($apps);
749
-
750
-		return $apps;
751
-	}
752
-
753
-	/**
754
-	 * List all apps, this is used in apps.php
755
-	 *
756
-	 * @return array
757
-	 */
758
-	public function listAllApps() {
759
-		$installedApps = OC_App::getAllApps();
760
-
761
-		$appManager = \OC::$server->getAppManager();
762
-		//we don't want to show configuration for these
763
-		$blacklist = $appManager->getAlwaysEnabledApps();
764
-		$appList = array();
765
-		$langCode = \OC::$server->getL10N('core')->getLanguageCode();
766
-		$urlGenerator = \OC::$server->getURLGenerator();
767
-
768
-		foreach ($installedApps as $app) {
769
-			if (array_search($app, $blacklist) === false) {
770
-
771
-				$info = OC_App::getAppInfo($app, false, $langCode);
772
-				if (!is_array($info)) {
773
-					\OCP\Util::writeLog('core', 'Could not read app info file for app "' . $app . '"', \OCP\Util::ERROR);
774
-					continue;
775
-				}
776
-
777
-				if (!isset($info['name'])) {
778
-					\OCP\Util::writeLog('core', 'App id "' . $app . '" has no name in appinfo', \OCP\Util::ERROR);
779
-					continue;
780
-				}
781
-
782
-				$enabled = \OC::$server->getConfig()->getAppValue($app, 'enabled', 'no');
783
-				$info['groups'] = null;
784
-				if ($enabled === 'yes') {
785
-					$active = true;
786
-				} else if ($enabled === 'no') {
787
-					$active = false;
788
-				} else {
789
-					$active = true;
790
-					$info['groups'] = $enabled;
791
-				}
792
-
793
-				$info['active'] = $active;
794
-
795
-				if ($appManager->isShipped($app)) {
796
-					$info['internal'] = true;
797
-					$info['level'] = self::officialApp;
798
-					$info['removable'] = false;
799
-				} else {
800
-					$info['internal'] = false;
801
-					$info['removable'] = true;
802
-				}
803
-
804
-				$appPath = self::getAppPath($app);
805
-				if($appPath !== false) {
806
-					$appIcon = $appPath . '/img/' . $app . '.svg';
807
-					if (file_exists($appIcon)) {
808
-						$info['preview'] = $urlGenerator->imagePath($app, $app . '.svg');
809
-						$info['previewAsIcon'] = true;
810
-					} else {
811
-						$appIcon = $appPath . '/img/app.svg';
812
-						if (file_exists($appIcon)) {
813
-							$info['preview'] = $urlGenerator->imagePath($app, 'app.svg');
814
-							$info['previewAsIcon'] = true;
815
-						}
816
-					}
817
-				}
818
-				// fix documentation
819
-				if (isset($info['documentation']) && is_array($info['documentation'])) {
820
-					foreach ($info['documentation'] as $key => $url) {
821
-						// If it is not an absolute URL we assume it is a key
822
-						// i.e. admin-ldap will get converted to go.php?to=admin-ldap
823
-						if (stripos($url, 'https://') !== 0 && stripos($url, 'http://') !== 0) {
824
-							$url = $urlGenerator->linkToDocs($url);
825
-						}
826
-
827
-						$info['documentation'][$key] = $url;
828
-					}
829
-				}
830
-
831
-				$info['version'] = OC_App::getAppVersion($app);
832
-				$appList[] = $info;
833
-			}
834
-		}
835
-
836
-		return $appList;
837
-	}
838
-
839
-	public static function shouldUpgrade($app) {
840
-		$versions = self::getAppVersions();
841
-		$currentVersion = OC_App::getAppVersion($app);
842
-		if ($currentVersion && isset($versions[$app])) {
843
-			$installedVersion = $versions[$app];
844
-			if (!version_compare($currentVersion, $installedVersion, '=')) {
845
-				return true;
846
-			}
847
-		}
848
-		return false;
849
-	}
850
-
851
-	/**
852
-	 * Adjust the number of version parts of $version1 to match
853
-	 * the number of version parts of $version2.
854
-	 *
855
-	 * @param string $version1 version to adjust
856
-	 * @param string $version2 version to take the number of parts from
857
-	 * @return string shortened $version1
858
-	 */
859
-	private static function adjustVersionParts($version1, $version2) {
860
-		$version1 = explode('.', $version1);
861
-		$version2 = explode('.', $version2);
862
-		// reduce $version1 to match the number of parts in $version2
863
-		while (count($version1) > count($version2)) {
864
-			array_pop($version1);
865
-		}
866
-		// if $version1 does not have enough parts, add some
867
-		while (count($version1) < count($version2)) {
868
-			$version1[] = '0';
869
-		}
870
-		return implode('.', $version1);
871
-	}
872
-
873
-	/**
874
-	 * Check whether the current ownCloud version matches the given
875
-	 * application's version requirements.
876
-	 *
877
-	 * The comparison is made based on the number of parts that the
878
-	 * app info version has. For example for ownCloud 6.0.3 if the
879
-	 * app info version is expecting version 6.0, the comparison is
880
-	 * made on the first two parts of the ownCloud version.
881
-	 * This means that it's possible to specify "requiremin" => 6
882
-	 * and "requiremax" => 6 and it will still match ownCloud 6.0.3.
883
-	 *
884
-	 * @param string $ocVersion ownCloud version to check against
885
-	 * @param array $appInfo app info (from xml)
886
-	 *
887
-	 * @return boolean true if compatible, otherwise false
888
-	 */
889
-	public static function isAppCompatible($ocVersion, $appInfo) {
890
-		$requireMin = '';
891
-		$requireMax = '';
892
-		if (isset($appInfo['dependencies']['nextcloud']['@attributes']['min-version'])) {
893
-			$requireMin = $appInfo['dependencies']['nextcloud']['@attributes']['min-version'];
894
-		} elseif (isset($appInfo['dependencies']['owncloud']['@attributes']['min-version'])) {
895
-			$requireMin = $appInfo['dependencies']['owncloud']['@attributes']['min-version'];
896
-		} else if (isset($appInfo['requiremin'])) {
897
-			$requireMin = $appInfo['requiremin'];
898
-		} else if (isset($appInfo['require'])) {
899
-			$requireMin = $appInfo['require'];
900
-		}
901
-
902
-		if (isset($appInfo['dependencies']['nextcloud']['@attributes']['max-version'])) {
903
-			$requireMax = $appInfo['dependencies']['nextcloud']['@attributes']['max-version'];
904
-		} elseif (isset($appInfo['dependencies']['owncloud']['@attributes']['max-version'])) {
905
-			$requireMax = $appInfo['dependencies']['owncloud']['@attributes']['max-version'];
906
-		} else if (isset($appInfo['requiremax'])) {
907
-			$requireMax = $appInfo['requiremax'];
908
-		}
909
-
910
-		if (is_array($ocVersion)) {
911
-			$ocVersion = implode('.', $ocVersion);
912
-		}
913
-
914
-		if (!empty($requireMin)
915
-			&& version_compare(self::adjustVersionParts($ocVersion, $requireMin), $requireMin, '<')
916
-		) {
917
-
918
-			return false;
919
-		}
920
-
921
-		if (!empty($requireMax)
922
-			&& version_compare(self::adjustVersionParts($ocVersion, $requireMax), $requireMax, '>')
923
-		) {
924
-			return false;
925
-		}
926
-
927
-		return true;
928
-	}
929
-
930
-	/**
931
-	 * get the installed version of all apps
932
-	 */
933
-	public static function getAppVersions() {
934
-		static $versions;
935
-
936
-		if(!$versions) {
937
-			$appConfig = \OC::$server->getAppConfig();
938
-			$versions = $appConfig->getValues(false, 'installed_version');
939
-		}
940
-		return $versions;
941
-	}
942
-
943
-	/**
944
-	 * update the database for the app and call the update script
945
-	 *
946
-	 * @param string $appId
947
-	 * @return bool
948
-	 */
949
-	public static function updateApp($appId) {
950
-		$appPath = self::getAppPath($appId);
951
-		if($appPath === false) {
952
-			return false;
953
-		}
954
-		self::registerAutoloading($appId, $appPath);
955
-
956
-		$appData = self::getAppInfo($appId);
957
-		self::executeRepairSteps($appId, $appData['repair-steps']['pre-migration']);
958
-
959
-		if (file_exists($appPath . '/appinfo/database.xml')) {
960
-			OC_DB::updateDbFromStructure($appPath . '/appinfo/database.xml');
961
-		} else {
962
-			$ms = new MigrationService($appId, \OC::$server->getDatabaseConnection());
963
-			$ms->migrate();
964
-		}
965
-
966
-		self::executeRepairSteps($appId, $appData['repair-steps']['post-migration']);
967
-		self::setupLiveMigrations($appId, $appData['repair-steps']['live-migration']);
968
-		// update appversion in app manager
969
-		\OC::$server->getAppManager()->getAppVersion($appId, false);
970
-
971
-		// run upgrade code
972
-		if (file_exists($appPath . '/appinfo/update.php')) {
973
-			self::loadApp($appId);
974
-			include $appPath . '/appinfo/update.php';
975
-		}
976
-		self::setupBackgroundJobs($appData['background-jobs']);
977
-
978
-		//set remote/public handlers
979
-		if (array_key_exists('ocsid', $appData)) {
980
-			\OC::$server->getConfig()->setAppValue($appId, 'ocsid', $appData['ocsid']);
981
-		} elseif(\OC::$server->getConfig()->getAppValue($appId, 'ocsid', null) !== null) {
982
-			\OC::$server->getConfig()->deleteAppValue($appId, 'ocsid');
983
-		}
984
-		foreach ($appData['remote'] as $name => $path) {
985
-			\OC::$server->getConfig()->setAppValue('core', 'remote_' . $name, $appId . '/' . $path);
986
-		}
987
-		foreach ($appData['public'] as $name => $path) {
988
-			\OC::$server->getConfig()->setAppValue('core', 'public_' . $name, $appId . '/' . $path);
989
-		}
990
-
991
-		self::setAppTypes($appId);
992
-
993
-		$version = \OC_App::getAppVersion($appId);
994
-		\OC::$server->getConfig()->setAppValue($appId, 'installed_version', $version);
995
-
996
-		\OC::$server->getEventDispatcher()->dispatch(ManagerEvent::EVENT_APP_UPDATE, new ManagerEvent(
997
-			ManagerEvent::EVENT_APP_UPDATE, $appId
998
-		));
999
-
1000
-		return true;
1001
-	}
1002
-
1003
-	/**
1004
-	 * @param string $appId
1005
-	 * @param string[] $steps
1006
-	 * @throws \OC\NeedsUpdateException
1007
-	 */
1008
-	public static function executeRepairSteps($appId, array $steps) {
1009
-		if (empty($steps)) {
1010
-			return;
1011
-		}
1012
-		// load the app
1013
-		self::loadApp($appId);
1014
-
1015
-		$dispatcher = OC::$server->getEventDispatcher();
1016
-
1017
-		// load the steps
1018
-		$r = new Repair([], $dispatcher);
1019
-		foreach ($steps as $step) {
1020
-			try {
1021
-				$r->addStep($step);
1022
-			} catch (Exception $ex) {
1023
-				$r->emit('\OC\Repair', 'error', [$ex->getMessage()]);
1024
-				\OC::$server->getLogger()->logException($ex);
1025
-			}
1026
-		}
1027
-		// run the steps
1028
-		$r->run();
1029
-	}
1030
-
1031
-	public static function setupBackgroundJobs(array $jobs) {
1032
-		$queue = \OC::$server->getJobList();
1033
-		foreach ($jobs as $job) {
1034
-			$queue->add($job);
1035
-		}
1036
-	}
1037
-
1038
-	/**
1039
-	 * @param string $appId
1040
-	 * @param string[] $steps
1041
-	 */
1042
-	private static function setupLiveMigrations($appId, array $steps) {
1043
-		$queue = \OC::$server->getJobList();
1044
-		foreach ($steps as $step) {
1045
-			$queue->add('OC\Migration\BackgroundRepair', [
1046
-				'app' => $appId,
1047
-				'step' => $step]);
1048
-		}
1049
-	}
1050
-
1051
-	/**
1052
-	 * @param string $appId
1053
-	 * @return \OC\Files\View|false
1054
-	 */
1055
-	public static function getStorage($appId) {
1056
-		if (\OC::$server->getAppManager()->isEnabledForUser($appId)) { //sanity check
1057
-			if (\OC::$server->getUserSession()->isLoggedIn()) {
1058
-				$view = new \OC\Files\View('/' . OC_User::getUser());
1059
-				if (!$view->file_exists($appId)) {
1060
-					$view->mkdir($appId);
1061
-				}
1062
-				return new \OC\Files\View('/' . OC_User::getUser() . '/' . $appId);
1063
-			} else {
1064
-				\OCP\Util::writeLog('core', 'Can\'t get app storage, app ' . $appId . ', user not logged in', \OCP\Util::ERROR);
1065
-				return false;
1066
-			}
1067
-		} else {
1068
-			\OCP\Util::writeLog('core', 'Can\'t get app storage, app ' . $appId . ' not enabled', \OCP\Util::ERROR);
1069
-			return false;
1070
-		}
1071
-	}
1072
-
1073
-	protected static function findBestL10NOption($options, $lang) {
1074
-		$fallback = $similarLangFallback = $englishFallback = false;
1075
-
1076
-		$lang = strtolower($lang);
1077
-		$similarLang = $lang;
1078
-		if (strpos($similarLang, '_')) {
1079
-			// For "de_DE" we want to find "de" and the other way around
1080
-			$similarLang = substr($lang, 0, strpos($lang, '_'));
1081
-		}
1082
-
1083
-		foreach ($options as $option) {
1084
-			if (is_array($option)) {
1085
-				if ($fallback === false) {
1086
-					$fallback = $option['@value'];
1087
-				}
1088
-
1089
-				if (!isset($option['@attributes']['lang'])) {
1090
-					continue;
1091
-				}
1092
-
1093
-				$attributeLang = strtolower($option['@attributes']['lang']);
1094
-				if ($attributeLang === $lang) {
1095
-					return $option['@value'];
1096
-				}
1097
-
1098
-				if ($attributeLang === $similarLang) {
1099
-					$similarLangFallback = $option['@value'];
1100
-				} else if (strpos($attributeLang, $similarLang . '_') === 0) {
1101
-					if ($similarLangFallback === false) {
1102
-						$similarLangFallback =  $option['@value'];
1103
-					}
1104
-				}
1105
-			} else {
1106
-				$englishFallback = $option;
1107
-			}
1108
-		}
1109
-
1110
-		if ($similarLangFallback !== false) {
1111
-			return $similarLangFallback;
1112
-		} else if ($englishFallback !== false) {
1113
-			return $englishFallback;
1114
-		}
1115
-		return (string) $fallback;
1116
-	}
1117
-
1118
-	/**
1119
-	 * parses the app data array and enhanced the 'description' value
1120
-	 *
1121
-	 * @param array $data the app data
1122
-	 * @param string $lang
1123
-	 * @return array improved app data
1124
-	 */
1125
-	public static function parseAppInfo(array $data, $lang = null) {
1126
-
1127
-		if ($lang && isset($data['name']) && is_array($data['name'])) {
1128
-			$data['name'] = self::findBestL10NOption($data['name'], $lang);
1129
-		}
1130
-		if ($lang && isset($data['summary']) && is_array($data['summary'])) {
1131
-			$data['summary'] = self::findBestL10NOption($data['summary'], $lang);
1132
-		}
1133
-		if ($lang && isset($data['description']) && is_array($data['description'])) {
1134
-			$data['description'] = trim(self::findBestL10NOption($data['description'], $lang));
1135
-		} else if (isset($data['description']) && is_string($data['description'])) {
1136
-			$data['description'] = trim($data['description']);
1137
-		} else  {
1138
-			$data['description'] = '';
1139
-		}
1140
-
1141
-		return $data;
1142
-	}
1143
-
1144
-	/**
1145
-	 * @param \OCP\IConfig $config
1146
-	 * @param \OCP\IL10N $l
1147
-	 * @param array $info
1148
-	 * @throws \Exception
1149
-	 */
1150
-	public static function checkAppDependencies($config, $l, $info) {
1151
-		$dependencyAnalyzer = new DependencyAnalyzer(new Platform($config), $l);
1152
-		$missing = $dependencyAnalyzer->analyze($info);
1153
-		if (!empty($missing)) {
1154
-			$missingMsg = implode(PHP_EOL, $missing);
1155
-			throw new \Exception(
1156
-				$l->t('App "%s" cannot be installed because the following dependencies are not fulfilled: %s',
1157
-					[$info['name'], $missingMsg]
1158
-				)
1159
-			);
1160
-		}
1161
-	}
65
+    static private $adminForms = array();
66
+    static private $personalForms = array();
67
+    static private $appTypes = array();
68
+    static private $loadedApps = array();
69
+    static private $altLogin = array();
70
+    static private $alreadyRegistered = [];
71
+    static public $autoDisabledApps = [];
72
+    const officialApp = 200;
73
+
74
+    /**
75
+     * clean the appId
76
+     *
77
+     * @param string|boolean $app AppId that needs to be cleaned
78
+     * @return string
79
+     */
80
+    public static function cleanAppId($app) {
81
+        return str_replace(array('\0', '/', '\\', '..'), '', $app);
82
+    }
83
+
84
+    /**
85
+     * Check if an app is loaded
86
+     *
87
+     * @param string $app
88
+     * @return bool
89
+     */
90
+    public static function isAppLoaded($app) {
91
+        return in_array($app, self::$loadedApps, true);
92
+    }
93
+
94
+    /**
95
+     * loads all apps
96
+     *
97
+     * @param string[] | string | null $types
98
+     * @return bool
99
+     *
100
+     * This function walks through the ownCloud directory and loads all apps
101
+     * it can find. A directory contains an app if the file /appinfo/info.xml
102
+     * exists.
103
+     *
104
+     * if $types is set, only apps of those types will be loaded
105
+     */
106
+    public static function loadApps($types = null) {
107
+        if (\OC::$server->getSystemConfig()->getValue('maintenance', false)) {
108
+            return false;
109
+        }
110
+        // Load the enabled apps here
111
+        $apps = self::getEnabledApps();
112
+
113
+        // Add each apps' folder as allowed class path
114
+        foreach($apps as $app) {
115
+            $path = self::getAppPath($app);
116
+            if($path !== false) {
117
+                self::registerAutoloading($app, $path);
118
+            }
119
+        }
120
+
121
+        // prevent app.php from printing output
122
+        ob_start();
123
+        foreach ($apps as $app) {
124
+            if ((is_null($types) or self::isType($app, $types)) && !in_array($app, self::$loadedApps)) {
125
+                self::loadApp($app);
126
+            }
127
+        }
128
+        ob_end_clean();
129
+
130
+        return true;
131
+    }
132
+
133
+    /**
134
+     * load a single app
135
+     *
136
+     * @param string $app
137
+     * @throws Exception
138
+     */
139
+    public static function loadApp($app) {
140
+        self::$loadedApps[] = $app;
141
+        $appPath = self::getAppPath($app);
142
+        if($appPath === false) {
143
+            return;
144
+        }
145
+
146
+        // in case someone calls loadApp() directly
147
+        self::registerAutoloading($app, $appPath);
148
+
149
+        if (is_file($appPath . '/appinfo/app.php')) {
150
+            \OC::$server->getEventLogger()->start('load_app_' . $app, 'Load app: ' . $app);
151
+            try {
152
+                self::requireAppFile($app);
153
+            } catch (Error $ex) {
154
+                \OC::$server->getLogger()->logException($ex);
155
+                if (!\OC::$server->getAppManager()->isShipped($app)) {
156
+                    // Only disable apps which are not shipped
157
+                    self::disable($app);
158
+                    self::$autoDisabledApps[] = $app;
159
+                }
160
+            }
161
+            if (self::isType($app, array('authentication'))) {
162
+                // since authentication apps affect the "is app enabled for group" check,
163
+                // the enabled apps cache needs to be cleared to make sure that the
164
+                // next time getEnableApps() is called it will also include apps that were
165
+                // enabled for groups
166
+                self::$enabledAppsCache = array();
167
+            }
168
+            \OC::$server->getEventLogger()->end('load_app_' . $app);
169
+        }
170
+
171
+        $info = self::getAppInfo($app);
172
+        if (!empty($info['activity']['filters'])) {
173
+            foreach ($info['activity']['filters'] as $filter) {
174
+                \OC::$server->getActivityManager()->registerFilter($filter);
175
+            }
176
+        }
177
+        if (!empty($info['activity']['settings'])) {
178
+            foreach ($info['activity']['settings'] as $setting) {
179
+                \OC::$server->getActivityManager()->registerSetting($setting);
180
+            }
181
+        }
182
+        if (!empty($info['activity']['providers'])) {
183
+            foreach ($info['activity']['providers'] as $provider) {
184
+                \OC::$server->getActivityManager()->registerProvider($provider);
185
+            }
186
+        }
187
+
188
+        if (!empty($info['settings']['admin'])) {
189
+            foreach ($info['settings']['admin'] as $setting) {
190
+                \OC::$server->getSettingsManager()->registerSetting('admin', $setting);
191
+            }
192
+        }
193
+        if (!empty($info['settings']['admin-section'])) {
194
+            foreach ($info['settings']['admin-section'] as $section) {
195
+                \OC::$server->getSettingsManager()->registerSection('admin', $section);
196
+            }
197
+        }
198
+        if (!empty($info['settings']['personal'])) {
199
+            foreach ($info['settings']['personal'] as $setting) {
200
+                \OC::$server->getSettingsManager()->registerSetting('personal', $setting);
201
+            }
202
+        }
203
+        if (!empty($info['settings']['personal-section'])) {
204
+            foreach ($info['settings']['personal-section'] as $section) {
205
+                \OC::$server->getSettingsManager()->registerSection('personal', $section);
206
+            }
207
+        }
208
+
209
+        if (!empty($info['collaboration']['plugins'])) {
210
+            // deal with one or many plugin entries
211
+            $plugins = isset($info['collaboration']['plugins']['plugin']['@value']) ?
212
+                [$info['collaboration']['plugins']['plugin']] : $info['collaboration']['plugins']['plugin'];
213
+            foreach ($plugins as $plugin) {
214
+                if($plugin['@attributes']['type'] === 'collaborator-search') {
215
+                    $pluginInfo = [
216
+                        'shareType' => $plugin['@attributes']['share-type'],
217
+                        'class' => $plugin['@value'],
218
+                    ];
219
+                    \OC::$server->getCollaboratorSearch()->registerPlugin($pluginInfo);
220
+                } else if ($plugin['@attributes']['type'] === 'autocomplete-sort') {
221
+                    \OC::$server->getAutoCompleteManager()->registerSorter($plugin['@value']);
222
+                }
223
+            }
224
+        }
225
+    }
226
+
227
+    /**
228
+     * @internal
229
+     * @param string $app
230
+     * @param string $path
231
+     */
232
+    public static function registerAutoloading($app, $path) {
233
+        $key = $app . '-' . $path;
234
+        if(isset(self::$alreadyRegistered[$key])) {
235
+            return;
236
+        }
237
+
238
+        self::$alreadyRegistered[$key] = true;
239
+
240
+        // Register on PSR-4 composer autoloader
241
+        $appNamespace = \OC\AppFramework\App::buildAppNamespace($app);
242
+        \OC::$server->registerNamespace($app, $appNamespace);
243
+
244
+        if (file_exists($path . '/composer/autoload.php')) {
245
+            require_once $path . '/composer/autoload.php';
246
+        } else {
247
+            \OC::$composerAutoloader->addPsr4($appNamespace . '\\', $path . '/lib/', true);
248
+            // Register on legacy autoloader
249
+            \OC::$loader->addValidRoot($path);
250
+        }
251
+
252
+        // Register Test namespace only when testing
253
+        if (defined('PHPUNIT_RUN') || defined('CLI_TEST_RUN')) {
254
+            \OC::$composerAutoloader->addPsr4($appNamespace . '\\Tests\\', $path . '/tests/', true);
255
+        }
256
+    }
257
+
258
+    /**
259
+     * Load app.php from the given app
260
+     *
261
+     * @param string $app app name
262
+     * @throws Error
263
+     */
264
+    private static function requireAppFile($app) {
265
+        // encapsulated here to avoid variable scope conflicts
266
+        require_once $app . '/appinfo/app.php';
267
+    }
268
+
269
+    /**
270
+     * check if an app is of a specific type
271
+     *
272
+     * @param string $app
273
+     * @param string|array $types
274
+     * @return bool
275
+     */
276
+    public static function isType($app, $types) {
277
+        if (is_string($types)) {
278
+            $types = array($types);
279
+        }
280
+        $appTypes = self::getAppTypes($app);
281
+        foreach ($types as $type) {
282
+            if (array_search($type, $appTypes) !== false) {
283
+                return true;
284
+            }
285
+        }
286
+        return false;
287
+    }
288
+
289
+    /**
290
+     * get the types of an app
291
+     *
292
+     * @param string $app
293
+     * @return array
294
+     */
295
+    private static function getAppTypes($app) {
296
+        //load the cache
297
+        if (count(self::$appTypes) == 0) {
298
+            self::$appTypes = \OC::$server->getAppConfig()->getValues(false, 'types');
299
+        }
300
+
301
+        if (isset(self::$appTypes[$app])) {
302
+            return explode(',', self::$appTypes[$app]);
303
+        } else {
304
+            return array();
305
+        }
306
+    }
307
+
308
+    /**
309
+     * read app types from info.xml and cache them in the database
310
+     */
311
+    public static function setAppTypes($app) {
312
+        $appData = self::getAppInfo($app);
313
+        if(!is_array($appData)) {
314
+            return;
315
+        }
316
+
317
+        if (isset($appData['types'])) {
318
+            $appTypes = implode(',', $appData['types']);
319
+        } else {
320
+            $appTypes = '';
321
+            $appData['types'] = [];
322
+        }
323
+
324
+        \OC::$server->getConfig()->setAppValue($app, 'types', $appTypes);
325
+
326
+        if (\OC::$server->getAppManager()->hasProtectedAppType($appData['types'])) {
327
+            $enabled = \OC::$server->getConfig()->getAppValue($app, 'enabled', 'yes');
328
+            if ($enabled !== 'yes' && $enabled !== 'no') {
329
+                \OC::$server->getConfig()->setAppValue($app, 'enabled', 'yes');
330
+            }
331
+        }
332
+    }
333
+
334
+    /**
335
+     * get all enabled apps
336
+     */
337
+    protected static $enabledAppsCache = array();
338
+
339
+    /**
340
+     * Returns apps enabled for the current user.
341
+     *
342
+     * @param bool $forceRefresh whether to refresh the cache
343
+     * @param bool $all whether to return apps for all users, not only the
344
+     * currently logged in one
345
+     * @return string[]
346
+     */
347
+    public static function getEnabledApps($forceRefresh = false, $all = false) {
348
+        if (!\OC::$server->getSystemConfig()->getValue('installed', false)) {
349
+            return array();
350
+        }
351
+        // in incognito mode or when logged out, $user will be false,
352
+        // which is also the case during an upgrade
353
+        $appManager = \OC::$server->getAppManager();
354
+        if ($all) {
355
+            $user = null;
356
+        } else {
357
+            $user = \OC::$server->getUserSession()->getUser();
358
+        }
359
+
360
+        if (is_null($user)) {
361
+            $apps = $appManager->getInstalledApps();
362
+        } else {
363
+            $apps = $appManager->getEnabledAppsForUser($user);
364
+        }
365
+        $apps = array_filter($apps, function ($app) {
366
+            return $app !== 'files';//we add this manually
367
+        });
368
+        sort($apps);
369
+        array_unshift($apps, 'files');
370
+        return $apps;
371
+    }
372
+
373
+    /**
374
+     * checks whether or not an app is enabled
375
+     *
376
+     * @param string $app app
377
+     * @return bool
378
+     * @deprecated 13.0.0 use \OC::$server->getAppManager()->isEnabledForUser($appId)
379
+     *
380
+     * This function checks whether or not an app is enabled.
381
+     */
382
+    public static function isEnabled($app) {
383
+        return \OC::$server->getAppManager()->isEnabledForUser($app);
384
+    }
385
+
386
+    /**
387
+     * enables an app
388
+     *
389
+     * @param string $appId
390
+     * @param array $groups (optional) when set, only these groups will have access to the app
391
+     * @throws \Exception
392
+     * @return void
393
+     *
394
+     * This function set an app as enabled in appconfig.
395
+     */
396
+    public function enable($appId,
397
+                            $groups = null) {
398
+        self::$enabledAppsCache = []; // flush
399
+
400
+        // Check if app is already downloaded
401
+        $installer = \OC::$server->query(Installer::class);
402
+        $isDownloaded = $installer->isDownloaded($appId);
403
+
404
+        if(!$isDownloaded) {
405
+            $installer->downloadApp($appId);
406
+        }
407
+
408
+        $installer->installApp($appId);
409
+
410
+        $appManager = \OC::$server->getAppManager();
411
+        if (!is_null($groups)) {
412
+            $groupManager = \OC::$server->getGroupManager();
413
+            $groupsList = [];
414
+            foreach ($groups as $group) {
415
+                $groupItem = $groupManager->get($group);
416
+                if ($groupItem instanceof \OCP\IGroup) {
417
+                    $groupsList[] = $groupManager->get($group);
418
+                }
419
+            }
420
+            $appManager->enableAppForGroups($appId, $groupsList);
421
+        } else {
422
+            $appManager->enableApp($appId);
423
+        }
424
+    }
425
+
426
+    /**
427
+     * This function set an app as disabled in appconfig.
428
+     *
429
+     * @param string $app app
430
+     * @throws Exception
431
+     */
432
+    public static function disable($app) {
433
+        // flush
434
+        self::$enabledAppsCache = array();
435
+
436
+        // run uninstall steps
437
+        $appData = OC_App::getAppInfo($app);
438
+        if (!is_null($appData)) {
439
+            OC_App::executeRepairSteps($app, $appData['repair-steps']['uninstall']);
440
+        }
441
+
442
+        // emit disable hook - needed anymore ?
443
+        \OC_Hook::emit('OC_App', 'pre_disable', array('app' => $app));
444
+
445
+        // finally disable it
446
+        $appManager = \OC::$server->getAppManager();
447
+        $appManager->disableApp($app);
448
+    }
449
+
450
+    // This is private as well. It simply works, so don't ask for more details
451
+    private static function proceedNavigation($list) {
452
+        usort($list, function($a, $b) {
453
+            if (isset($a['order']) && isset($b['order'])) {
454
+                return ($a['order'] < $b['order']) ? -1 : 1;
455
+            } else if (isset($a['order']) || isset($b['order'])) {
456
+                return isset($a['order']) ? -1 : 1;
457
+            } else {
458
+                return ($a['name'] < $b['name']) ? -1 : 1;
459
+            }
460
+        });
461
+
462
+        $activeApp = OC::$server->getNavigationManager()->getActiveEntry();
463
+        foreach ($list as $index => &$navEntry) {
464
+            if ($navEntry['id'] == $activeApp) {
465
+                $navEntry['active'] = true;
466
+            } else {
467
+                $navEntry['active'] = false;
468
+            }
469
+        }
470
+        unset($navEntry);
471
+
472
+        return $list;
473
+    }
474
+
475
+    /**
476
+     * Get the path where to install apps
477
+     *
478
+     * @return string|false
479
+     */
480
+    public static function getInstallPath() {
481
+        if (\OC::$server->getSystemConfig()->getValue('appstoreenabled', true) == false) {
482
+            return false;
483
+        }
484
+
485
+        foreach (OC::$APPSROOTS as $dir) {
486
+            if (isset($dir['writable']) && $dir['writable'] === true) {
487
+                return $dir['path'];
488
+            }
489
+        }
490
+
491
+        \OCP\Util::writeLog('core', 'No application directories are marked as writable.', \OCP\Util::ERROR);
492
+        return null;
493
+    }
494
+
495
+
496
+    /**
497
+     * search for an app in all app-directories
498
+     *
499
+     * @param string $appId
500
+     * @return false|string
501
+     */
502
+    public static function findAppInDirectories($appId) {
503
+        $sanitizedAppId = self::cleanAppId($appId);
504
+        if($sanitizedAppId !== $appId) {
505
+            return false;
506
+        }
507
+        static $app_dir = array();
508
+
509
+        if (isset($app_dir[$appId])) {
510
+            return $app_dir[$appId];
511
+        }
512
+
513
+        $possibleApps = array();
514
+        foreach (OC::$APPSROOTS as $dir) {
515
+            if (file_exists($dir['path'] . '/' . $appId)) {
516
+                $possibleApps[] = $dir;
517
+            }
518
+        }
519
+
520
+        if (empty($possibleApps)) {
521
+            return false;
522
+        } elseif (count($possibleApps) === 1) {
523
+            $dir = array_shift($possibleApps);
524
+            $app_dir[$appId] = $dir;
525
+            return $dir;
526
+        } else {
527
+            $versionToLoad = array();
528
+            foreach ($possibleApps as $possibleApp) {
529
+                $version = self::getAppVersionByPath($possibleApp['path']);
530
+                if (empty($versionToLoad) || version_compare($version, $versionToLoad['version'], '>')) {
531
+                    $versionToLoad = array(
532
+                        'dir' => $possibleApp,
533
+                        'version' => $version,
534
+                    );
535
+                }
536
+            }
537
+            $app_dir[$appId] = $versionToLoad['dir'];
538
+            return $versionToLoad['dir'];
539
+            //TODO - write test
540
+        }
541
+    }
542
+
543
+    /**
544
+     * Get the directory for the given app.
545
+     * If the app is defined in multiple directories, the first one is taken. (false if not found)
546
+     *
547
+     * @param string $appId
548
+     * @return string|false
549
+     */
550
+    public static function getAppPath($appId) {
551
+        if ($appId === null || trim($appId) === '') {
552
+            return false;
553
+        }
554
+
555
+        if (($dir = self::findAppInDirectories($appId)) != false) {
556
+            return $dir['path'] . '/' . $appId;
557
+        }
558
+        return false;
559
+    }
560
+
561
+    /**
562
+     * Get the path for the given app on the access
563
+     * If the app is defined in multiple directories, the first one is taken. (false if not found)
564
+     *
565
+     * @param string $appId
566
+     * @return string|false
567
+     */
568
+    public static function getAppWebPath($appId) {
569
+        if (($dir = self::findAppInDirectories($appId)) != false) {
570
+            return OC::$WEBROOT . $dir['url'] . '/' . $appId;
571
+        }
572
+        return false;
573
+    }
574
+
575
+    /**
576
+     * get the last version of the app from appinfo/info.xml
577
+     *
578
+     * @param string $appId
579
+     * @param bool $useCache
580
+     * @return string
581
+     * @deprecated 14.0.0 use \OC::$server->getAppManager()->getAppVersion()
582
+     */
583
+    public static function getAppVersion($appId, $useCache = true) {
584
+        return \OC::$server->getAppManager()->getAppVersion($appId, $useCache);
585
+    }
586
+
587
+    /**
588
+     * get app's version based on it's path
589
+     *
590
+     * @param string $path
591
+     * @return string
592
+     */
593
+    public static function getAppVersionByPath($path) {
594
+        $infoFile = $path . '/appinfo/info.xml';
595
+        $appData = \OC::$server->getAppManager()->getAppInfo($infoFile, true);
596
+        return isset($appData['version']) ? $appData['version'] : '';
597
+    }
598
+
599
+
600
+    /**
601
+     * Read all app metadata from the info.xml file
602
+     *
603
+     * @param string $appId id of the app or the path of the info.xml file
604
+     * @param bool $path
605
+     * @param string $lang
606
+     * @return array|null
607
+     * @note all data is read from info.xml, not just pre-defined fields
608
+     * @deprecated 14.0.0 use \OC::$server->getAppManager()->getAppInfo()
609
+     */
610
+    public static function getAppInfo($appId, $path = false, $lang = null) {
611
+        return \OC::$server->getAppManager()->getAppInfo($appId, $path, $lang);
612
+    }
613
+
614
+    /**
615
+     * Returns the navigation
616
+     *
617
+     * @return array
618
+     *
619
+     * This function returns an array containing all entries added. The
620
+     * entries are sorted by the key 'order' ascending. Additional to the keys
621
+     * given for each app the following keys exist:
622
+     *   - active: boolean, signals if the user is on this navigation entry
623
+     */
624
+    public static function getNavigation() {
625
+        $entries = OC::$server->getNavigationManager()->getAll();
626
+        return self::proceedNavigation($entries);
627
+    }
628
+
629
+    /**
630
+     * Returns the Settings Navigation
631
+     *
632
+     * @return string[]
633
+     *
634
+     * This function returns an array containing all settings pages added. The
635
+     * entries are sorted by the key 'order' ascending.
636
+     */
637
+    public static function getSettingsNavigation() {
638
+        $entries = OC::$server->getNavigationManager()->getAll('settings');
639
+        return self::proceedNavigation($entries);
640
+    }
641
+
642
+    /**
643
+     * get the id of loaded app
644
+     *
645
+     * @return string
646
+     */
647
+    public static function getCurrentApp() {
648
+        $request = \OC::$server->getRequest();
649
+        $script = substr($request->getScriptName(), strlen(OC::$WEBROOT) + 1);
650
+        $topFolder = substr($script, 0, strpos($script, '/') ?: 0);
651
+        if (empty($topFolder)) {
652
+            $path_info = $request->getPathInfo();
653
+            if ($path_info) {
654
+                $topFolder = substr($path_info, 1, strpos($path_info, '/', 1) - 1);
655
+            }
656
+        }
657
+        if ($topFolder == 'apps') {
658
+            $length = strlen($topFolder);
659
+            return substr($script, $length + 1, strpos($script, '/', $length + 1) - $length - 1);
660
+        } else {
661
+            return $topFolder;
662
+        }
663
+    }
664
+
665
+    /**
666
+     * @param string $type
667
+     * @return array
668
+     */
669
+    public static function getForms($type) {
670
+        $forms = array();
671
+        switch ($type) {
672
+            case 'admin':
673
+                $source = self::$adminForms;
674
+                break;
675
+            case 'personal':
676
+                $source = self::$personalForms;
677
+                break;
678
+            default:
679
+                return array();
680
+        }
681
+        foreach ($source as $form) {
682
+            $forms[] = include $form;
683
+        }
684
+        return $forms;
685
+    }
686
+
687
+    /**
688
+     * register an admin form to be shown
689
+     *
690
+     * @param string $app
691
+     * @param string $page
692
+     */
693
+    public static function registerAdmin($app, $page) {
694
+        self::$adminForms[] = $app . '/' . $page . '.php';
695
+    }
696
+
697
+    /**
698
+     * register a personal form to be shown
699
+     * @param string $app
700
+     * @param string $page
701
+     */
702
+    public static function registerPersonal($app, $page) {
703
+        self::$personalForms[] = $app . '/' . $page . '.php';
704
+    }
705
+
706
+    /**
707
+     * @param array $entry
708
+     */
709
+    public static function registerLogIn(array $entry) {
710
+        self::$altLogin[] = $entry;
711
+    }
712
+
713
+    /**
714
+     * @return array
715
+     */
716
+    public static function getAlternativeLogIns() {
717
+        return self::$altLogin;
718
+    }
719
+
720
+    /**
721
+     * get a list of all apps in the apps folder
722
+     *
723
+     * @return array an array of app names (string IDs)
724
+     * @todo: change the name of this method to getInstalledApps, which is more accurate
725
+     */
726
+    public static function getAllApps() {
727
+
728
+        $apps = array();
729
+
730
+        foreach (OC::$APPSROOTS as $apps_dir) {
731
+            if (!is_readable($apps_dir['path'])) {
732
+                \OCP\Util::writeLog('core', 'unable to read app folder : ' . $apps_dir['path'], \OCP\Util::WARN);
733
+                continue;
734
+            }
735
+            $dh = opendir($apps_dir['path']);
736
+
737
+            if (is_resource($dh)) {
738
+                while (($file = readdir($dh)) !== false) {
739
+
740
+                    if ($file[0] != '.' and is_dir($apps_dir['path'] . '/' . $file) and is_file($apps_dir['path'] . '/' . $file . '/appinfo/info.xml')) {
741
+
742
+                        $apps[] = $file;
743
+                    }
744
+                }
745
+            }
746
+        }
747
+
748
+        $apps = array_unique($apps);
749
+
750
+        return $apps;
751
+    }
752
+
753
+    /**
754
+     * List all apps, this is used in apps.php
755
+     *
756
+     * @return array
757
+     */
758
+    public function listAllApps() {
759
+        $installedApps = OC_App::getAllApps();
760
+
761
+        $appManager = \OC::$server->getAppManager();
762
+        //we don't want to show configuration for these
763
+        $blacklist = $appManager->getAlwaysEnabledApps();
764
+        $appList = array();
765
+        $langCode = \OC::$server->getL10N('core')->getLanguageCode();
766
+        $urlGenerator = \OC::$server->getURLGenerator();
767
+
768
+        foreach ($installedApps as $app) {
769
+            if (array_search($app, $blacklist) === false) {
770
+
771
+                $info = OC_App::getAppInfo($app, false, $langCode);
772
+                if (!is_array($info)) {
773
+                    \OCP\Util::writeLog('core', 'Could not read app info file for app "' . $app . '"', \OCP\Util::ERROR);
774
+                    continue;
775
+                }
776
+
777
+                if (!isset($info['name'])) {
778
+                    \OCP\Util::writeLog('core', 'App id "' . $app . '" has no name in appinfo', \OCP\Util::ERROR);
779
+                    continue;
780
+                }
781
+
782
+                $enabled = \OC::$server->getConfig()->getAppValue($app, 'enabled', 'no');
783
+                $info['groups'] = null;
784
+                if ($enabled === 'yes') {
785
+                    $active = true;
786
+                } else if ($enabled === 'no') {
787
+                    $active = false;
788
+                } else {
789
+                    $active = true;
790
+                    $info['groups'] = $enabled;
791
+                }
792
+
793
+                $info['active'] = $active;
794
+
795
+                if ($appManager->isShipped($app)) {
796
+                    $info['internal'] = true;
797
+                    $info['level'] = self::officialApp;
798
+                    $info['removable'] = false;
799
+                } else {
800
+                    $info['internal'] = false;
801
+                    $info['removable'] = true;
802
+                }
803
+
804
+                $appPath = self::getAppPath($app);
805
+                if($appPath !== false) {
806
+                    $appIcon = $appPath . '/img/' . $app . '.svg';
807
+                    if (file_exists($appIcon)) {
808
+                        $info['preview'] = $urlGenerator->imagePath($app, $app . '.svg');
809
+                        $info['previewAsIcon'] = true;
810
+                    } else {
811
+                        $appIcon = $appPath . '/img/app.svg';
812
+                        if (file_exists($appIcon)) {
813
+                            $info['preview'] = $urlGenerator->imagePath($app, 'app.svg');
814
+                            $info['previewAsIcon'] = true;
815
+                        }
816
+                    }
817
+                }
818
+                // fix documentation
819
+                if (isset($info['documentation']) && is_array($info['documentation'])) {
820
+                    foreach ($info['documentation'] as $key => $url) {
821
+                        // If it is not an absolute URL we assume it is a key
822
+                        // i.e. admin-ldap will get converted to go.php?to=admin-ldap
823
+                        if (stripos($url, 'https://') !== 0 && stripos($url, 'http://') !== 0) {
824
+                            $url = $urlGenerator->linkToDocs($url);
825
+                        }
826
+
827
+                        $info['documentation'][$key] = $url;
828
+                    }
829
+                }
830
+
831
+                $info['version'] = OC_App::getAppVersion($app);
832
+                $appList[] = $info;
833
+            }
834
+        }
835
+
836
+        return $appList;
837
+    }
838
+
839
+    public static function shouldUpgrade($app) {
840
+        $versions = self::getAppVersions();
841
+        $currentVersion = OC_App::getAppVersion($app);
842
+        if ($currentVersion && isset($versions[$app])) {
843
+            $installedVersion = $versions[$app];
844
+            if (!version_compare($currentVersion, $installedVersion, '=')) {
845
+                return true;
846
+            }
847
+        }
848
+        return false;
849
+    }
850
+
851
+    /**
852
+     * Adjust the number of version parts of $version1 to match
853
+     * the number of version parts of $version2.
854
+     *
855
+     * @param string $version1 version to adjust
856
+     * @param string $version2 version to take the number of parts from
857
+     * @return string shortened $version1
858
+     */
859
+    private static function adjustVersionParts($version1, $version2) {
860
+        $version1 = explode('.', $version1);
861
+        $version2 = explode('.', $version2);
862
+        // reduce $version1 to match the number of parts in $version2
863
+        while (count($version1) > count($version2)) {
864
+            array_pop($version1);
865
+        }
866
+        // if $version1 does not have enough parts, add some
867
+        while (count($version1) < count($version2)) {
868
+            $version1[] = '0';
869
+        }
870
+        return implode('.', $version1);
871
+    }
872
+
873
+    /**
874
+     * Check whether the current ownCloud version matches the given
875
+     * application's version requirements.
876
+     *
877
+     * The comparison is made based on the number of parts that the
878
+     * app info version has. For example for ownCloud 6.0.3 if the
879
+     * app info version is expecting version 6.0, the comparison is
880
+     * made on the first two parts of the ownCloud version.
881
+     * This means that it's possible to specify "requiremin" => 6
882
+     * and "requiremax" => 6 and it will still match ownCloud 6.0.3.
883
+     *
884
+     * @param string $ocVersion ownCloud version to check against
885
+     * @param array $appInfo app info (from xml)
886
+     *
887
+     * @return boolean true if compatible, otherwise false
888
+     */
889
+    public static function isAppCompatible($ocVersion, $appInfo) {
890
+        $requireMin = '';
891
+        $requireMax = '';
892
+        if (isset($appInfo['dependencies']['nextcloud']['@attributes']['min-version'])) {
893
+            $requireMin = $appInfo['dependencies']['nextcloud']['@attributes']['min-version'];
894
+        } elseif (isset($appInfo['dependencies']['owncloud']['@attributes']['min-version'])) {
895
+            $requireMin = $appInfo['dependencies']['owncloud']['@attributes']['min-version'];
896
+        } else if (isset($appInfo['requiremin'])) {
897
+            $requireMin = $appInfo['requiremin'];
898
+        } else if (isset($appInfo['require'])) {
899
+            $requireMin = $appInfo['require'];
900
+        }
901
+
902
+        if (isset($appInfo['dependencies']['nextcloud']['@attributes']['max-version'])) {
903
+            $requireMax = $appInfo['dependencies']['nextcloud']['@attributes']['max-version'];
904
+        } elseif (isset($appInfo['dependencies']['owncloud']['@attributes']['max-version'])) {
905
+            $requireMax = $appInfo['dependencies']['owncloud']['@attributes']['max-version'];
906
+        } else if (isset($appInfo['requiremax'])) {
907
+            $requireMax = $appInfo['requiremax'];
908
+        }
909
+
910
+        if (is_array($ocVersion)) {
911
+            $ocVersion = implode('.', $ocVersion);
912
+        }
913
+
914
+        if (!empty($requireMin)
915
+            && version_compare(self::adjustVersionParts($ocVersion, $requireMin), $requireMin, '<')
916
+        ) {
917
+
918
+            return false;
919
+        }
920
+
921
+        if (!empty($requireMax)
922
+            && version_compare(self::adjustVersionParts($ocVersion, $requireMax), $requireMax, '>')
923
+        ) {
924
+            return false;
925
+        }
926
+
927
+        return true;
928
+    }
929
+
930
+    /**
931
+     * get the installed version of all apps
932
+     */
933
+    public static function getAppVersions() {
934
+        static $versions;
935
+
936
+        if(!$versions) {
937
+            $appConfig = \OC::$server->getAppConfig();
938
+            $versions = $appConfig->getValues(false, 'installed_version');
939
+        }
940
+        return $versions;
941
+    }
942
+
943
+    /**
944
+     * update the database for the app and call the update script
945
+     *
946
+     * @param string $appId
947
+     * @return bool
948
+     */
949
+    public static function updateApp($appId) {
950
+        $appPath = self::getAppPath($appId);
951
+        if($appPath === false) {
952
+            return false;
953
+        }
954
+        self::registerAutoloading($appId, $appPath);
955
+
956
+        $appData = self::getAppInfo($appId);
957
+        self::executeRepairSteps($appId, $appData['repair-steps']['pre-migration']);
958
+
959
+        if (file_exists($appPath . '/appinfo/database.xml')) {
960
+            OC_DB::updateDbFromStructure($appPath . '/appinfo/database.xml');
961
+        } else {
962
+            $ms = new MigrationService($appId, \OC::$server->getDatabaseConnection());
963
+            $ms->migrate();
964
+        }
965
+
966
+        self::executeRepairSteps($appId, $appData['repair-steps']['post-migration']);
967
+        self::setupLiveMigrations($appId, $appData['repair-steps']['live-migration']);
968
+        // update appversion in app manager
969
+        \OC::$server->getAppManager()->getAppVersion($appId, false);
970
+
971
+        // run upgrade code
972
+        if (file_exists($appPath . '/appinfo/update.php')) {
973
+            self::loadApp($appId);
974
+            include $appPath . '/appinfo/update.php';
975
+        }
976
+        self::setupBackgroundJobs($appData['background-jobs']);
977
+
978
+        //set remote/public handlers
979
+        if (array_key_exists('ocsid', $appData)) {
980
+            \OC::$server->getConfig()->setAppValue($appId, 'ocsid', $appData['ocsid']);
981
+        } elseif(\OC::$server->getConfig()->getAppValue($appId, 'ocsid', null) !== null) {
982
+            \OC::$server->getConfig()->deleteAppValue($appId, 'ocsid');
983
+        }
984
+        foreach ($appData['remote'] as $name => $path) {
985
+            \OC::$server->getConfig()->setAppValue('core', 'remote_' . $name, $appId . '/' . $path);
986
+        }
987
+        foreach ($appData['public'] as $name => $path) {
988
+            \OC::$server->getConfig()->setAppValue('core', 'public_' . $name, $appId . '/' . $path);
989
+        }
990
+
991
+        self::setAppTypes($appId);
992
+
993
+        $version = \OC_App::getAppVersion($appId);
994
+        \OC::$server->getConfig()->setAppValue($appId, 'installed_version', $version);
995
+
996
+        \OC::$server->getEventDispatcher()->dispatch(ManagerEvent::EVENT_APP_UPDATE, new ManagerEvent(
997
+            ManagerEvent::EVENT_APP_UPDATE, $appId
998
+        ));
999
+
1000
+        return true;
1001
+    }
1002
+
1003
+    /**
1004
+     * @param string $appId
1005
+     * @param string[] $steps
1006
+     * @throws \OC\NeedsUpdateException
1007
+     */
1008
+    public static function executeRepairSteps($appId, array $steps) {
1009
+        if (empty($steps)) {
1010
+            return;
1011
+        }
1012
+        // load the app
1013
+        self::loadApp($appId);
1014
+
1015
+        $dispatcher = OC::$server->getEventDispatcher();
1016
+
1017
+        // load the steps
1018
+        $r = new Repair([], $dispatcher);
1019
+        foreach ($steps as $step) {
1020
+            try {
1021
+                $r->addStep($step);
1022
+            } catch (Exception $ex) {
1023
+                $r->emit('\OC\Repair', 'error', [$ex->getMessage()]);
1024
+                \OC::$server->getLogger()->logException($ex);
1025
+            }
1026
+        }
1027
+        // run the steps
1028
+        $r->run();
1029
+    }
1030
+
1031
+    public static function setupBackgroundJobs(array $jobs) {
1032
+        $queue = \OC::$server->getJobList();
1033
+        foreach ($jobs as $job) {
1034
+            $queue->add($job);
1035
+        }
1036
+    }
1037
+
1038
+    /**
1039
+     * @param string $appId
1040
+     * @param string[] $steps
1041
+     */
1042
+    private static function setupLiveMigrations($appId, array $steps) {
1043
+        $queue = \OC::$server->getJobList();
1044
+        foreach ($steps as $step) {
1045
+            $queue->add('OC\Migration\BackgroundRepair', [
1046
+                'app' => $appId,
1047
+                'step' => $step]);
1048
+        }
1049
+    }
1050
+
1051
+    /**
1052
+     * @param string $appId
1053
+     * @return \OC\Files\View|false
1054
+     */
1055
+    public static function getStorage($appId) {
1056
+        if (\OC::$server->getAppManager()->isEnabledForUser($appId)) { //sanity check
1057
+            if (\OC::$server->getUserSession()->isLoggedIn()) {
1058
+                $view = new \OC\Files\View('/' . OC_User::getUser());
1059
+                if (!$view->file_exists($appId)) {
1060
+                    $view->mkdir($appId);
1061
+                }
1062
+                return new \OC\Files\View('/' . OC_User::getUser() . '/' . $appId);
1063
+            } else {
1064
+                \OCP\Util::writeLog('core', 'Can\'t get app storage, app ' . $appId . ', user not logged in', \OCP\Util::ERROR);
1065
+                return false;
1066
+            }
1067
+        } else {
1068
+            \OCP\Util::writeLog('core', 'Can\'t get app storage, app ' . $appId . ' not enabled', \OCP\Util::ERROR);
1069
+            return false;
1070
+        }
1071
+    }
1072
+
1073
+    protected static function findBestL10NOption($options, $lang) {
1074
+        $fallback = $similarLangFallback = $englishFallback = false;
1075
+
1076
+        $lang = strtolower($lang);
1077
+        $similarLang = $lang;
1078
+        if (strpos($similarLang, '_')) {
1079
+            // For "de_DE" we want to find "de" and the other way around
1080
+            $similarLang = substr($lang, 0, strpos($lang, '_'));
1081
+        }
1082
+
1083
+        foreach ($options as $option) {
1084
+            if (is_array($option)) {
1085
+                if ($fallback === false) {
1086
+                    $fallback = $option['@value'];
1087
+                }
1088
+
1089
+                if (!isset($option['@attributes']['lang'])) {
1090
+                    continue;
1091
+                }
1092
+
1093
+                $attributeLang = strtolower($option['@attributes']['lang']);
1094
+                if ($attributeLang === $lang) {
1095
+                    return $option['@value'];
1096
+                }
1097
+
1098
+                if ($attributeLang === $similarLang) {
1099
+                    $similarLangFallback = $option['@value'];
1100
+                } else if (strpos($attributeLang, $similarLang . '_') === 0) {
1101
+                    if ($similarLangFallback === false) {
1102
+                        $similarLangFallback =  $option['@value'];
1103
+                    }
1104
+                }
1105
+            } else {
1106
+                $englishFallback = $option;
1107
+            }
1108
+        }
1109
+
1110
+        if ($similarLangFallback !== false) {
1111
+            return $similarLangFallback;
1112
+        } else if ($englishFallback !== false) {
1113
+            return $englishFallback;
1114
+        }
1115
+        return (string) $fallback;
1116
+    }
1117
+
1118
+    /**
1119
+     * parses the app data array and enhanced the 'description' value
1120
+     *
1121
+     * @param array $data the app data
1122
+     * @param string $lang
1123
+     * @return array improved app data
1124
+     */
1125
+    public static function parseAppInfo(array $data, $lang = null) {
1126
+
1127
+        if ($lang && isset($data['name']) && is_array($data['name'])) {
1128
+            $data['name'] = self::findBestL10NOption($data['name'], $lang);
1129
+        }
1130
+        if ($lang && isset($data['summary']) && is_array($data['summary'])) {
1131
+            $data['summary'] = self::findBestL10NOption($data['summary'], $lang);
1132
+        }
1133
+        if ($lang && isset($data['description']) && is_array($data['description'])) {
1134
+            $data['description'] = trim(self::findBestL10NOption($data['description'], $lang));
1135
+        } else if (isset($data['description']) && is_string($data['description'])) {
1136
+            $data['description'] = trim($data['description']);
1137
+        } else  {
1138
+            $data['description'] = '';
1139
+        }
1140
+
1141
+        return $data;
1142
+    }
1143
+
1144
+    /**
1145
+     * @param \OCP\IConfig $config
1146
+     * @param \OCP\IL10N $l
1147
+     * @param array $info
1148
+     * @throws \Exception
1149
+     */
1150
+    public static function checkAppDependencies($config, $l, $info) {
1151
+        $dependencyAnalyzer = new DependencyAnalyzer(new Platform($config), $l);
1152
+        $missing = $dependencyAnalyzer->analyze($info);
1153
+        if (!empty($missing)) {
1154
+            $missingMsg = implode(PHP_EOL, $missing);
1155
+            throw new \Exception(
1156
+                $l->t('App "%s" cannot be installed because the following dependencies are not fulfilled: %s',
1157
+                    [$info['name'], $missingMsg]
1158
+                )
1159
+            );
1160
+        }
1161
+    }
1162 1162
 }
Please login to merge, or discard this patch.