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