Passed
Push — master ( da4c5a...5e7e3e )
by Julius
17:18 queued 13s
created
lib/public/App/IAppManager.php 1 patch
Indentation   +209 added lines, -209 removed lines patch added patch discarded remove patch
@@ -42,213 +42,213 @@
 block discarded – undo
42 42
  * @since 8.0.0
43 43
  */
44 44
 interface IAppManager {
45
-	/**
46
-	 * Returns the app information from "appinfo/info.xml".
47
-	 *
48
-	 * @param string $appId
49
-	 * @return mixed
50
-	 * @since 14.0.0
51
-	 */
52
-	public function getAppInfo(string $appId, bool $path = false, $lang = null);
53
-
54
-	/**
55
-	 * Returns the app information from "appinfo/info.xml".
56
-	 *
57
-	 * @param string $appId
58
-	 * @param bool $useCache
59
-	 * @return string
60
-	 * @since 14.0.0
61
-	 */
62
-	public function getAppVersion(string $appId, bool $useCache = true): string;
63
-
64
-	/**
65
-	 * Check if an app is enabled for user
66
-	 *
67
-	 * @param string $appId
68
-	 * @param \OCP\IUser $user (optional) if not defined, the currently loggedin user will be used
69
-	 * @return bool
70
-	 * @since 8.0.0
71
-	 */
72
-	public function isEnabledForUser($appId, $user = null);
73
-
74
-	/**
75
-	 * Check if an app is enabled in the instance
76
-	 *
77
-	 * Notice: This actually checks if the app is enabled and not only if it is installed.
78
-	 *
79
-	 * @param string $appId
80
-	 * @return bool
81
-	 * @since 8.0.0
82
-	 */
83
-	public function isInstalled($appId);
84
-
85
-	/**
86
-	 * Check if an app should be enabled by default
87
-	 *
88
-	 * Notice: This actually checks if the app should be enabled by default
89
-	 * and not if currently installed/enabled
90
-	 *
91
-	 * @param string $appId ID of the app
92
-	 * @since 25.0.0
93
-	 */
94
-	public function isDefaultEnabled(string $appId):bool;
95
-
96
-	/**
97
-	 * Load an app, if not already loaded
98
-	 * @param string $app app id
99
-	 * @since 27.0.0
100
-	 */
101
-	public function loadApp(string $app): void;
102
-
103
-	/**
104
-	 * Check if an app is loaded
105
-	 * @param string $app app id
106
-	 * @since 27.0.0
107
-	 */
108
-	public function isAppLoaded(string $app): bool;
109
-
110
-	/**
111
-	 * Enable an app for every user
112
-	 *
113
-	 * @param string $appId
114
-	 * @param bool $forceEnable
115
-	 * @throws AppPathNotFoundException
116
-	 * @since 8.0.0
117
-	 */
118
-	public function enableApp(string $appId, bool $forceEnable = false): void;
119
-
120
-	/**
121
-	 * Whether a list of types contains a protected app type
122
-	 *
123
-	 * @param string[] $types
124
-	 * @return bool
125
-	 * @since 12.0.0
126
-	 */
127
-	public function hasProtectedAppType($types);
128
-
129
-	/**
130
-	 * Enable an app only for specific groups
131
-	 *
132
-	 * @param string $appId
133
-	 * @param \OCP\IGroup[] $groups
134
-	 * @param bool $forceEnable
135
-	 * @throws \Exception
136
-	 * @since 8.0.0
137
-	 */
138
-	public function enableAppForGroups(string $appId, array $groups, bool $forceEnable = false): void;
139
-
140
-	/**
141
-	 * Disable an app for every user
142
-	 *
143
-	 * @param string $appId
144
-	 * @param bool $automaticDisabled
145
-	 * @since 8.0.0
146
-	 */
147
-	public function disableApp($appId, $automaticDisabled = false);
148
-
149
-	/**
150
-	 * Get the directory for the given app.
151
-	 *
152
-	 * @param string $appId
153
-	 * @return string
154
-	 * @since 11.0.0
155
-	 * @throws AppPathNotFoundException
156
-	 */
157
-	public function getAppPath($appId);
158
-
159
-	/**
160
-	 * Get the web path for the given app.
161
-	 *
162
-	 * @param string $appId
163
-	 * @return string
164
-	 * @since 18.0.0
165
-	 * @throws AppPathNotFoundException
166
-	 */
167
-	public function getAppWebPath(string $appId): string;
168
-
169
-	/**
170
-	 * List all apps enabled for a user
171
-	 *
172
-	 * @param \OCP\IUser $user
173
-	 * @return string[]
174
-	 * @since 8.1.0
175
-	 */
176
-	public function getEnabledAppsForUser(IUser $user);
177
-
178
-	/**
179
-	 * List all installed apps
180
-	 *
181
-	 * @return string[]
182
-	 * @since 8.1.0
183
-	 */
184
-	public function getInstalledApps();
185
-
186
-	/**
187
-	 * Clear the cached list of apps when enabling/disabling an app
188
-	 * @since 8.1.0
189
-	 */
190
-	public function clearAppsCache();
191
-
192
-	/**
193
-	 * @param string $appId
194
-	 * @return boolean
195
-	 * @since 9.0.0
196
-	 */
197
-	public function isShipped($appId);
198
-
199
-	/**
200
-	 * Loads all apps
201
-	 *
202
-	 * @param string[] $types
203
-	 * @return bool
204
-	 *
205
-	 * This function walks through the Nextcloud directory and loads all apps
206
-	 * it can find. A directory contains an app if the file /appinfo/info.xml
207
-	 * exists.
208
-	 *
209
-	 * if $types is set to non-empty array, only apps of those types will be loaded
210
-	 * @since 27.0.0
211
-	 */
212
-	public function loadApps(array $types = []): bool;
213
-
214
-	/**
215
-	 * Check if an app is of a specific type
216
-	 * @since 27.0.0
217
-	 */
218
-	public function isType(string $app, array $types): bool;
219
-
220
-	/**
221
-	 * @return string[]
222
-	 * @since 9.0.0
223
-	 */
224
-	public function getAlwaysEnabledApps();
225
-
226
-	/**
227
-	 * @return string[] app IDs
228
-	 * @since 25.0.0
229
-	 */
230
-	public function getDefaultEnabledApps(): array;
231
-
232
-	/**
233
-	 * @param \OCP\IGroup $group
234
-	 * @return String[]
235
-	 * @since 17.0.0
236
-	 */
237
-	public function getEnabledAppsForGroup(IGroup $group): array;
238
-
239
-	/**
240
-	 * @param String $appId
241
-	 * @return string[]
242
-	 * @since 17.0.0
243
-	 */
244
-	public function getAppRestriction(string $appId): array;
245
-
246
-	/**
247
-	 * Returns the id of the user's default app
248
-	 *
249
-	 * If `user` is not passed, the currently logged in user will be used
250
-	 *
251
-	 * @since 25.0.6
252
-	 */
253
-	public function getDefaultAppForUser(?IUser $user = null): string;
45
+    /**
46
+     * Returns the app information from "appinfo/info.xml".
47
+     *
48
+     * @param string $appId
49
+     * @return mixed
50
+     * @since 14.0.0
51
+     */
52
+    public function getAppInfo(string $appId, bool $path = false, $lang = null);
53
+
54
+    /**
55
+     * Returns the app information from "appinfo/info.xml".
56
+     *
57
+     * @param string $appId
58
+     * @param bool $useCache
59
+     * @return string
60
+     * @since 14.0.0
61
+     */
62
+    public function getAppVersion(string $appId, bool $useCache = true): string;
63
+
64
+    /**
65
+     * Check if an app is enabled for user
66
+     *
67
+     * @param string $appId
68
+     * @param \OCP\IUser $user (optional) if not defined, the currently loggedin user will be used
69
+     * @return bool
70
+     * @since 8.0.0
71
+     */
72
+    public function isEnabledForUser($appId, $user = null);
73
+
74
+    /**
75
+     * Check if an app is enabled in the instance
76
+     *
77
+     * Notice: This actually checks if the app is enabled and not only if it is installed.
78
+     *
79
+     * @param string $appId
80
+     * @return bool
81
+     * @since 8.0.0
82
+     */
83
+    public function isInstalled($appId);
84
+
85
+    /**
86
+     * Check if an app should be enabled by default
87
+     *
88
+     * Notice: This actually checks if the app should be enabled by default
89
+     * and not if currently installed/enabled
90
+     *
91
+     * @param string $appId ID of the app
92
+     * @since 25.0.0
93
+     */
94
+    public function isDefaultEnabled(string $appId):bool;
95
+
96
+    /**
97
+     * Load an app, if not already loaded
98
+     * @param string $app app id
99
+     * @since 27.0.0
100
+     */
101
+    public function loadApp(string $app): void;
102
+
103
+    /**
104
+     * Check if an app is loaded
105
+     * @param string $app app id
106
+     * @since 27.0.0
107
+     */
108
+    public function isAppLoaded(string $app): bool;
109
+
110
+    /**
111
+     * Enable an app for every user
112
+     *
113
+     * @param string $appId
114
+     * @param bool $forceEnable
115
+     * @throws AppPathNotFoundException
116
+     * @since 8.0.0
117
+     */
118
+    public function enableApp(string $appId, bool $forceEnable = false): void;
119
+
120
+    /**
121
+     * Whether a list of types contains a protected app type
122
+     *
123
+     * @param string[] $types
124
+     * @return bool
125
+     * @since 12.0.0
126
+     */
127
+    public function hasProtectedAppType($types);
128
+
129
+    /**
130
+     * Enable an app only for specific groups
131
+     *
132
+     * @param string $appId
133
+     * @param \OCP\IGroup[] $groups
134
+     * @param bool $forceEnable
135
+     * @throws \Exception
136
+     * @since 8.0.0
137
+     */
138
+    public function enableAppForGroups(string $appId, array $groups, bool $forceEnable = false): void;
139
+
140
+    /**
141
+     * Disable an app for every user
142
+     *
143
+     * @param string $appId
144
+     * @param bool $automaticDisabled
145
+     * @since 8.0.0
146
+     */
147
+    public function disableApp($appId, $automaticDisabled = false);
148
+
149
+    /**
150
+     * Get the directory for the given app.
151
+     *
152
+     * @param string $appId
153
+     * @return string
154
+     * @since 11.0.0
155
+     * @throws AppPathNotFoundException
156
+     */
157
+    public function getAppPath($appId);
158
+
159
+    /**
160
+     * Get the web path for the given app.
161
+     *
162
+     * @param string $appId
163
+     * @return string
164
+     * @since 18.0.0
165
+     * @throws AppPathNotFoundException
166
+     */
167
+    public function getAppWebPath(string $appId): string;
168
+
169
+    /**
170
+     * List all apps enabled for a user
171
+     *
172
+     * @param \OCP\IUser $user
173
+     * @return string[]
174
+     * @since 8.1.0
175
+     */
176
+    public function getEnabledAppsForUser(IUser $user);
177
+
178
+    /**
179
+     * List all installed apps
180
+     *
181
+     * @return string[]
182
+     * @since 8.1.0
183
+     */
184
+    public function getInstalledApps();
185
+
186
+    /**
187
+     * Clear the cached list of apps when enabling/disabling an app
188
+     * @since 8.1.0
189
+     */
190
+    public function clearAppsCache();
191
+
192
+    /**
193
+     * @param string $appId
194
+     * @return boolean
195
+     * @since 9.0.0
196
+     */
197
+    public function isShipped($appId);
198
+
199
+    /**
200
+     * Loads all apps
201
+     *
202
+     * @param string[] $types
203
+     * @return bool
204
+     *
205
+     * This function walks through the Nextcloud directory and loads all apps
206
+     * it can find. A directory contains an app if the file /appinfo/info.xml
207
+     * exists.
208
+     *
209
+     * if $types is set to non-empty array, only apps of those types will be loaded
210
+     * @since 27.0.0
211
+     */
212
+    public function loadApps(array $types = []): bool;
213
+
214
+    /**
215
+     * Check if an app is of a specific type
216
+     * @since 27.0.0
217
+     */
218
+    public function isType(string $app, array $types): bool;
219
+
220
+    /**
221
+     * @return string[]
222
+     * @since 9.0.0
223
+     */
224
+    public function getAlwaysEnabledApps();
225
+
226
+    /**
227
+     * @return string[] app IDs
228
+     * @since 25.0.0
229
+     */
230
+    public function getDefaultEnabledApps(): array;
231
+
232
+    /**
233
+     * @param \OCP\IGroup $group
234
+     * @return String[]
235
+     * @since 17.0.0
236
+     */
237
+    public function getEnabledAppsForGroup(IGroup $group): array;
238
+
239
+    /**
240
+     * @param String $appId
241
+     * @return string[]
242
+     * @since 17.0.0
243
+     */
244
+    public function getAppRestriction(string $appId): array;
245
+
246
+    /**
247
+     * Returns the id of the user's default app
248
+     *
249
+     * If `user` is not passed, the currently logged in user will be used
250
+     *
251
+     * @since 25.0.6
252
+     */
253
+    public function getDefaultAppForUser(?IUser $user = null): string;
254 254
 }
Please login to merge, or discard this patch.
lib/private/legacy/OC_App.php 1 patch
Indentation   +965 added lines, -965 removed lines patch added patch discarded remove patch
@@ -73,969 +73,969 @@
 block discarded – undo
73 73
  * upgrading and removing apps.
74 74
  */
75 75
 class OC_App {
76
-	private static $adminForms = [];
77
-	private static $personalForms = [];
78
-	private static $altLogin = [];
79
-	private static $alreadyRegistered = [];
80
-	public const supportedApp = 300;
81
-	public const officialApp = 200;
82
-
83
-	/**
84
-	 * clean the appId
85
-	 *
86
-	 * @psalm-taint-escape file
87
-	 * @psalm-taint-escape include
88
-	 *
89
-	 * @param string $app AppId that needs to be cleaned
90
-	 * @return string
91
-	 */
92
-	public static function cleanAppId(string $app): string {
93
-		return str_replace(['\0', '/', '\\', '..'], '', $app);
94
-	}
95
-
96
-	/**
97
-	 * Check if an app is loaded
98
-	 *
99
-	 * @param string $app
100
-	 * @return bool
101
-	 * @deprecated 27.0.0 use IAppManager::isAppLoaded
102
-	 */
103
-	public static function isAppLoaded(string $app): bool {
104
-		return \OC::$server->get(IAppManager::class)->isAppLoaded($app);
105
-	}
106
-
107
-	/**
108
-	 * loads all apps
109
-	 *
110
-	 * @param string[] $types
111
-	 * @return bool
112
-	 *
113
-	 * This function walks through the ownCloud directory and loads all apps
114
-	 * it can find. A directory contains an app if the file /appinfo/info.xml
115
-	 * exists.
116
-	 *
117
-	 * if $types is set to non-empty array, only apps of those types will be loaded
118
-	 */
119
-	public static function loadApps(array $types = []): bool {
120
-		if (!\OC::$server->getSystemConfig()->getValue('installed', false)) {
121
-			// This should be done before calling this method so that appmanager can be used
122
-			return false;
123
-		}
124
-		return \OC::$server->get(IAppManager::class)->loadApps($types);
125
-	}
126
-
127
-	/**
128
-	 * load a single app
129
-	 *
130
-	 * @param string $app
131
-	 * @throws Exception
132
-	 * @deprecated 27.0.0 use IAppManager::loadApp
133
-	 */
134
-	public static function loadApp(string $app): void {
135
-		\OC::$server->get(IAppManager::class)->loadApp($app);
136
-	}
137
-
138
-	/**
139
-	 * @internal
140
-	 * @param string $app
141
-	 * @param string $path
142
-	 * @param bool $force
143
-	 */
144
-	public static function registerAutoloading(string $app, string $path, bool $force = false) {
145
-		$key = $app . '-' . $path;
146
-		if (!$force && isset(self::$alreadyRegistered[$key])) {
147
-			return;
148
-		}
149
-
150
-		self::$alreadyRegistered[$key] = true;
151
-
152
-		// Register on PSR-4 composer autoloader
153
-		$appNamespace = \OC\AppFramework\App::buildAppNamespace($app);
154
-		\OC::$server->registerNamespace($app, $appNamespace);
155
-
156
-		if (file_exists($path . '/composer/autoload.php')) {
157
-			require_once $path . '/composer/autoload.php';
158
-		} else {
159
-			\OC::$composerAutoloader->addPsr4($appNamespace . '\\', $path . '/lib/', true);
160
-		}
161
-
162
-		// Register Test namespace only when testing
163
-		if (defined('PHPUNIT_RUN') || defined('CLI_TEST_RUN')) {
164
-			\OC::$composerAutoloader->addPsr4($appNamespace . '\\Tests\\', $path . '/tests/', true);
165
-		}
166
-	}
167
-
168
-	/**
169
-	 * check if an app is of a specific type
170
-	 *
171
-	 * @param string $app
172
-	 * @param array $types
173
-	 * @return bool
174
-	 * @deprecated 27.0.0 use IAppManager::isType
175
-	 */
176
-	public static function isType(string $app, array $types): bool {
177
-		return \OC::$server->get(IAppManager::class)->isType($app, $types);
178
-	}
179
-
180
-	/**
181
-	 * read app types from info.xml and cache them in the database
182
-	 */
183
-	public static function setAppTypes(string $app) {
184
-		$appManager = \OC::$server->getAppManager();
185
-		$appData = $appManager->getAppInfo($app);
186
-		if (!is_array($appData)) {
187
-			return;
188
-		}
189
-
190
-		if (isset($appData['types'])) {
191
-			$appTypes = implode(',', $appData['types']);
192
-		} else {
193
-			$appTypes = '';
194
-			$appData['types'] = [];
195
-		}
196
-
197
-		$config = \OC::$server->getConfig();
198
-		$config->setAppValue($app, 'types', $appTypes);
199
-
200
-		if ($appManager->hasProtectedAppType($appData['types'])) {
201
-			$enabled = $config->getAppValue($app, 'enabled', 'yes');
202
-			if ($enabled !== 'yes' && $enabled !== 'no') {
203
-				$config->setAppValue($app, 'enabled', 'yes');
204
-			}
205
-		}
206
-	}
207
-
208
-	/**
209
-	 * Returns apps enabled for the current user.
210
-	 *
211
-	 * @param bool $forceRefresh whether to refresh the cache
212
-	 * @param bool $all whether to return apps for all users, not only the
213
-	 * currently logged in one
214
-	 * @return string[]
215
-	 */
216
-	public static function getEnabledApps(bool $forceRefresh = false, bool $all = false): array {
217
-		if (!\OC::$server->getSystemConfig()->getValue('installed', false)) {
218
-			return [];
219
-		}
220
-		// in incognito mode or when logged out, $user will be false,
221
-		// which is also the case during an upgrade
222
-		$appManager = \OC::$server->getAppManager();
223
-		if ($all) {
224
-			$user = null;
225
-		} else {
226
-			$user = \OC::$server->getUserSession()->getUser();
227
-		}
228
-
229
-		if (is_null($user)) {
230
-			$apps = $appManager->getInstalledApps();
231
-		} else {
232
-			$apps = $appManager->getEnabledAppsForUser($user);
233
-		}
234
-		$apps = array_filter($apps, function ($app) {
235
-			return $app !== 'files';//we add this manually
236
-		});
237
-		sort($apps);
238
-		array_unshift($apps, 'files');
239
-		return $apps;
240
-	}
241
-
242
-	/**
243
-	 * checks whether or not an app is enabled
244
-	 *
245
-	 * @param string $app app
246
-	 * @return bool
247
-	 * @deprecated 13.0.0 use \OC::$server->getAppManager()->isEnabledForUser($appId)
248
-	 *
249
-	 * This function checks whether or not an app is enabled.
250
-	 */
251
-	public static function isEnabled(string $app): bool {
252
-		return \OC::$server->getAppManager()->isEnabledForUser($app);
253
-	}
254
-
255
-	/**
256
-	 * enables an app
257
-	 *
258
-	 * @param string $appId
259
-	 * @param array $groups (optional) when set, only these groups will have access to the app
260
-	 * @throws \Exception
261
-	 * @return void
262
-	 *
263
-	 * This function set an app as enabled in appconfig.
264
-	 */
265
-	public function enable(string $appId,
266
-						   array $groups = []) {
267
-		// Check if app is already downloaded
268
-		/** @var Installer $installer */
269
-		$installer = \OC::$server->query(Installer::class);
270
-		$isDownloaded = $installer->isDownloaded($appId);
271
-
272
-		if (!$isDownloaded) {
273
-			$installer->downloadApp($appId);
274
-		}
275
-
276
-		$installer->installApp($appId);
277
-
278
-		$appManager = \OC::$server->getAppManager();
279
-		if ($groups !== []) {
280
-			$groupManager = \OC::$server->getGroupManager();
281
-			$groupsList = [];
282
-			foreach ($groups as $group) {
283
-				$groupItem = $groupManager->get($group);
284
-				if ($groupItem instanceof \OCP\IGroup) {
285
-					$groupsList[] = $groupManager->get($group);
286
-				}
287
-			}
288
-			$appManager->enableAppForGroups($appId, $groupsList);
289
-		} else {
290
-			$appManager->enableApp($appId);
291
-		}
292
-	}
293
-
294
-	/**
295
-	 * Get the path where to install apps
296
-	 *
297
-	 * @return string|false
298
-	 */
299
-	public static function getInstallPath() {
300
-		foreach (OC::$APPSROOTS as $dir) {
301
-			if (isset($dir['writable']) && $dir['writable'] === true) {
302
-				return $dir['path'];
303
-			}
304
-		}
305
-
306
-		\OCP\Util::writeLog('core', 'No application directories are marked as writable.', ILogger::ERROR);
307
-		return null;
308
-	}
309
-
310
-
311
-	/**
312
-	 * search for an app in all app-directories
313
-	 *
314
-	 * @param string $appId
315
-	 * @param bool $ignoreCache ignore cache and rebuild it
316
-	 * @return false|string
317
-	 */
318
-	public static function findAppInDirectories(string $appId, bool $ignoreCache = false) {
319
-		$sanitizedAppId = self::cleanAppId($appId);
320
-		if ($sanitizedAppId !== $appId) {
321
-			return false;
322
-		}
323
-		static $app_dir = [];
324
-
325
-		if (isset($app_dir[$appId]) && !$ignoreCache) {
326
-			return $app_dir[$appId];
327
-		}
328
-
329
-		$possibleApps = [];
330
-		foreach (OC::$APPSROOTS as $dir) {
331
-			if (file_exists($dir['path'] . '/' . $appId)) {
332
-				$possibleApps[] = $dir;
333
-			}
334
-		}
335
-
336
-		if (empty($possibleApps)) {
337
-			return false;
338
-		} elseif (count($possibleApps) === 1) {
339
-			$dir = array_shift($possibleApps);
340
-			$app_dir[$appId] = $dir;
341
-			return $dir;
342
-		} else {
343
-			$versionToLoad = [];
344
-			foreach ($possibleApps as $possibleApp) {
345
-				$version = self::getAppVersionByPath($possibleApp['path'] . '/' . $appId);
346
-				if (empty($versionToLoad) || version_compare($version, $versionToLoad['version'], '>')) {
347
-					$versionToLoad = [
348
-						'dir' => $possibleApp,
349
-						'version' => $version,
350
-					];
351
-				}
352
-			}
353
-			$app_dir[$appId] = $versionToLoad['dir'];
354
-			return $versionToLoad['dir'];
355
-			//TODO - write test
356
-		}
357
-	}
358
-
359
-	/**
360
-	 * Get the directory for the given app.
361
-	 * If the app is defined in multiple directories, the first one is taken. (false if not found)
362
-	 *
363
-	 * @psalm-taint-specialize
364
-	 *
365
-	 * @param string $appId
366
-	 * @param bool $refreshAppPath should be set to true only during install/upgrade
367
-	 * @return string|false
368
-	 * @deprecated 11.0.0 use \OC::$server->getAppManager()->getAppPath()
369
-	 */
370
-	public static function getAppPath(string $appId, bool $refreshAppPath = false) {
371
-		if ($appId === null || trim($appId) === '') {
372
-			return false;
373
-		}
374
-
375
-		if (($dir = self::findAppInDirectories($appId, $refreshAppPath)) != false) {
376
-			return $dir['path'] . '/' . $appId;
377
-		}
378
-		return false;
379
-	}
380
-
381
-	/**
382
-	 * Get the path for the given app on the access
383
-	 * If the app is defined in multiple directories, the first one is taken. (false if not found)
384
-	 *
385
-	 * @param string $appId
386
-	 * @return string|false
387
-	 * @deprecated 18.0.0 use \OC::$server->getAppManager()->getAppWebPath()
388
-	 */
389
-	public static function getAppWebPath(string $appId) {
390
-		if (($dir = self::findAppInDirectories($appId)) != false) {
391
-			return OC::$WEBROOT . $dir['url'] . '/' . $appId;
392
-		}
393
-		return false;
394
-	}
395
-
396
-	/**
397
-	 * get the last version of the app from appinfo/info.xml
398
-	 *
399
-	 * @param string $appId
400
-	 * @param bool $useCache
401
-	 * @return string
402
-	 * @deprecated 14.0.0 use \OC::$server->getAppManager()->getAppVersion()
403
-	 */
404
-	public static function getAppVersion(string $appId, bool $useCache = true): string {
405
-		return \OC::$server->getAppManager()->getAppVersion($appId, $useCache);
406
-	}
407
-
408
-	/**
409
-	 * get app's version based on it's path
410
-	 *
411
-	 * @param string $path
412
-	 * @return string
413
-	 */
414
-	public static function getAppVersionByPath(string $path): string {
415
-		$infoFile = $path . '/appinfo/info.xml';
416
-		$appData = \OC::$server->getAppManager()->getAppInfo($infoFile, true);
417
-		return isset($appData['version']) ? $appData['version'] : '';
418
-	}
419
-
420
-
421
-	/**
422
-	 * Read all app metadata from the info.xml file
423
-	 *
424
-	 * @param string $appId id of the app or the path of the info.xml file
425
-	 * @param bool $path
426
-	 * @param string $lang
427
-	 * @return array|null
428
-	 * @note all data is read from info.xml, not just pre-defined fields
429
-	 * @deprecated 14.0.0 use \OC::$server->getAppManager()->getAppInfo()
430
-	 */
431
-	public static function getAppInfo(string $appId, bool $path = false, string $lang = null) {
432
-		return \OC::$server->getAppManager()->getAppInfo($appId, $path, $lang);
433
-	}
434
-
435
-	/**
436
-	 * Returns the navigation
437
-	 *
438
-	 * @return array
439
-	 * @deprecated 14.0.0 use \OC::$server->getNavigationManager()->getAll()
440
-	 *
441
-	 * This function returns an array containing all entries added. The
442
-	 * entries are sorted by the key 'order' ascending. Additional to the keys
443
-	 * given for each app the following keys exist:
444
-	 *   - active: boolean, signals if the user is on this navigation entry
445
-	 */
446
-	public static function getNavigation(): array {
447
-		return OC::$server->getNavigationManager()->getAll();
448
-	}
449
-
450
-	/**
451
-	 * Returns the Settings Navigation
452
-	 *
453
-	 * @return string[]
454
-	 * @deprecated 14.0.0 use \OC::$server->getNavigationManager()->getAll('settings')
455
-	 *
456
-	 * This function returns an array containing all settings pages added. The
457
-	 * entries are sorted by the key 'order' ascending.
458
-	 */
459
-	public static function getSettingsNavigation(): array {
460
-		return OC::$server->getNavigationManager()->getAll('settings');
461
-	}
462
-
463
-	/**
464
-	 * get the id of loaded app
465
-	 *
466
-	 * @return string
467
-	 */
468
-	public static function getCurrentApp(): string {
469
-		if (\OC::$CLI) {
470
-			return '';
471
-		}
472
-
473
-		$request = \OC::$server->getRequest();
474
-		$script = substr($request->getScriptName(), strlen(OC::$WEBROOT) + 1);
475
-		$topFolder = substr($script, 0, strpos($script, '/') ?: 0);
476
-		if (empty($topFolder)) {
477
-			try {
478
-				$path_info = $request->getPathInfo();
479
-			} catch (Exception $e) {
480
-				// Can happen from unit tests because the script name is `./vendor/bin/phpunit` or something a like then.
481
-				\OC::$server->get(LoggerInterface::class)->error('Failed to detect current app from script path', ['exception' => $e]);
482
-				return '';
483
-			}
484
-			if ($path_info) {
485
-				$topFolder = substr($path_info, 1, strpos($path_info, '/', 1) - 1);
486
-			}
487
-		}
488
-		if ($topFolder == 'apps') {
489
-			$length = strlen($topFolder);
490
-			return substr($script, $length + 1, strpos($script, '/', $length + 1) - $length - 1) ?: '';
491
-		} else {
492
-			return $topFolder;
493
-		}
494
-	}
495
-
496
-	/**
497
-	 * @param string $type
498
-	 * @return array
499
-	 */
500
-	public static function getForms(string $type): array {
501
-		$forms = [];
502
-		switch ($type) {
503
-			case 'admin':
504
-				$source = self::$adminForms;
505
-				break;
506
-			case 'personal':
507
-				$source = self::$personalForms;
508
-				break;
509
-			default:
510
-				return [];
511
-		}
512
-		foreach ($source as $form) {
513
-			$forms[] = include $form;
514
-		}
515
-		return $forms;
516
-	}
517
-
518
-	/**
519
-	 * @param array $entry
520
-	 * @deprecated 20.0.0 Please register your alternative login option using the registerAlternativeLogin() on the RegistrationContext in your Application class implementing the OCP\Authentication\IAlternativeLogin interface
521
-	 */
522
-	public static function registerLogIn(array $entry) {
523
-		\OC::$server->getLogger()->debug('OC_App::registerLogIn() is deprecated, please register your alternative login option using the registerAlternativeLogin() on the RegistrationContext in your Application class implementing the OCP\Authentication\IAlternativeLogin interface');
524
-		self::$altLogin[] = $entry;
525
-	}
526
-
527
-	/**
528
-	 * @return array
529
-	 */
530
-	public static function getAlternativeLogIns(): array {
531
-		/** @var Coordinator $bootstrapCoordinator */
532
-		$bootstrapCoordinator = \OC::$server->query(Coordinator::class);
533
-
534
-		foreach ($bootstrapCoordinator->getRegistrationContext()->getAlternativeLogins() as $registration) {
535
-			if (!in_array(IAlternativeLogin::class, class_implements($registration->getService()), true)) {
536
-				\OC::$server->getLogger()->error('Alternative login option {option} does not implement {interface} and is therefore ignored.', [
537
-					'option' => $registration->getService(),
538
-					'interface' => IAlternativeLogin::class,
539
-					'app' => $registration->getAppId(),
540
-				]);
541
-				continue;
542
-			}
543
-
544
-			try {
545
-				/** @var IAlternativeLogin $provider */
546
-				$provider = \OC::$server->query($registration->getService());
547
-			} catch (QueryException $e) {
548
-				\OC::$server->getLogger()->logException($e, [
549
-					'message' => 'Alternative login option {option} can not be initialised.',
550
-					'option' => $registration->getService(),
551
-					'app' => $registration->getAppId(),
552
-				]);
553
-			}
554
-
555
-			try {
556
-				$provider->load();
557
-
558
-				self::$altLogin[] = [
559
-					'name' => $provider->getLabel(),
560
-					'href' => $provider->getLink(),
561
-					'class' => $provider->getClass(),
562
-				];
563
-			} catch (Throwable $e) {
564
-				\OC::$server->getLogger()->logException($e, [
565
-					'message' => 'Alternative login option {option} had an error while loading.',
566
-					'option' => $registration->getService(),
567
-					'app' => $registration->getAppId(),
568
-				]);
569
-			}
570
-		}
571
-
572
-		return self::$altLogin;
573
-	}
574
-
575
-	/**
576
-	 * get a list of all apps in the apps folder
577
-	 *
578
-	 * @return string[] an array of app names (string IDs)
579
-	 * @todo: change the name of this method to getInstalledApps, which is more accurate
580
-	 */
581
-	public static function getAllApps(): array {
582
-		$apps = [];
583
-
584
-		foreach (OC::$APPSROOTS as $apps_dir) {
585
-			if (!is_readable($apps_dir['path'])) {
586
-				\OCP\Util::writeLog('core', 'unable to read app folder : ' . $apps_dir['path'], ILogger::WARN);
587
-				continue;
588
-			}
589
-			$dh = opendir($apps_dir['path']);
590
-
591
-			if (is_resource($dh)) {
592
-				while (($file = readdir($dh)) !== false) {
593
-					if ($file[0] != '.' and is_dir($apps_dir['path'] . '/' . $file) and is_file($apps_dir['path'] . '/' . $file . '/appinfo/info.xml')) {
594
-						$apps[] = $file;
595
-					}
596
-				}
597
-			}
598
-		}
599
-
600
-		$apps = array_unique($apps);
601
-
602
-		return $apps;
603
-	}
604
-
605
-	/**
606
-	 * List all supported apps
607
-	 *
608
-	 * @return array
609
-	 */
610
-	public function getSupportedApps(): array {
611
-		/** @var \OCP\Support\Subscription\IRegistry $subscriptionRegistry */
612
-		$subscriptionRegistry = \OC::$server->query(\OCP\Support\Subscription\IRegistry::class);
613
-		$supportedApps = $subscriptionRegistry->delegateGetSupportedApps();
614
-		return $supportedApps;
615
-	}
616
-
617
-	/**
618
-	 * List all apps, this is used in apps.php
619
-	 *
620
-	 * @return array
621
-	 */
622
-	public function listAllApps(): array {
623
-		$installedApps = OC_App::getAllApps();
624
-
625
-		$appManager = \OC::$server->getAppManager();
626
-		//we don't want to show configuration for these
627
-		$blacklist = $appManager->getAlwaysEnabledApps();
628
-		$appList = [];
629
-		$langCode = \OC::$server->getL10N('core')->getLanguageCode();
630
-		$urlGenerator = \OC::$server->getURLGenerator();
631
-		$supportedApps = $this->getSupportedApps();
632
-
633
-		foreach ($installedApps as $app) {
634
-			if (array_search($app, $blacklist) === false) {
635
-				$info = OC_App::getAppInfo($app, false, $langCode);
636
-				if (!is_array($info)) {
637
-					\OCP\Util::writeLog('core', 'Could not read app info file for app "' . $app . '"', ILogger::ERROR);
638
-					continue;
639
-				}
640
-
641
-				if (!isset($info['name'])) {
642
-					\OCP\Util::writeLog('core', 'App id "' . $app . '" has no name in appinfo', ILogger::ERROR);
643
-					continue;
644
-				}
645
-
646
-				$enabled = \OC::$server->getConfig()->getAppValue($app, 'enabled', 'no');
647
-				$info['groups'] = null;
648
-				if ($enabled === 'yes') {
649
-					$active = true;
650
-				} elseif ($enabled === 'no') {
651
-					$active = false;
652
-				} else {
653
-					$active = true;
654
-					$info['groups'] = $enabled;
655
-				}
656
-
657
-				$info['active'] = $active;
658
-
659
-				if ($appManager->isShipped($app)) {
660
-					$info['internal'] = true;
661
-					$info['level'] = self::officialApp;
662
-					$info['removable'] = false;
663
-				} else {
664
-					$info['internal'] = false;
665
-					$info['removable'] = true;
666
-				}
667
-
668
-				if (in_array($app, $supportedApps)) {
669
-					$info['level'] = self::supportedApp;
670
-				}
671
-
672
-				$appPath = self::getAppPath($app);
673
-				if ($appPath !== false) {
674
-					$appIcon = $appPath . '/img/' . $app . '.svg';
675
-					if (file_exists($appIcon)) {
676
-						$info['preview'] = $urlGenerator->imagePath($app, $app . '.svg');
677
-						$info['previewAsIcon'] = true;
678
-					} else {
679
-						$appIcon = $appPath . '/img/app.svg';
680
-						if (file_exists($appIcon)) {
681
-							$info['preview'] = $urlGenerator->imagePath($app, 'app.svg');
682
-							$info['previewAsIcon'] = true;
683
-						}
684
-					}
685
-				}
686
-				// fix documentation
687
-				if (isset($info['documentation']) && is_array($info['documentation'])) {
688
-					foreach ($info['documentation'] as $key => $url) {
689
-						// If it is not an absolute URL we assume it is a key
690
-						// i.e. admin-ldap will get converted to go.php?to=admin-ldap
691
-						if (stripos($url, 'https://') !== 0 && stripos($url, 'http://') !== 0) {
692
-							$url = $urlGenerator->linkToDocs($url);
693
-						}
694
-
695
-						$info['documentation'][$key] = $url;
696
-					}
697
-				}
698
-
699
-				$info['version'] = OC_App::getAppVersion($app);
700
-				$appList[] = $info;
701
-			}
702
-		}
703
-
704
-		return $appList;
705
-	}
706
-
707
-	public static function shouldUpgrade(string $app): bool {
708
-		$versions = self::getAppVersions();
709
-		$currentVersion = OC_App::getAppVersion($app);
710
-		if ($currentVersion && isset($versions[$app])) {
711
-			$installedVersion = $versions[$app];
712
-			if (!version_compare($currentVersion, $installedVersion, '=')) {
713
-				return true;
714
-			}
715
-		}
716
-		return false;
717
-	}
718
-
719
-	/**
720
-	 * Adjust the number of version parts of $version1 to match
721
-	 * the number of version parts of $version2.
722
-	 *
723
-	 * @param string $version1 version to adjust
724
-	 * @param string $version2 version to take the number of parts from
725
-	 * @return string shortened $version1
726
-	 */
727
-	private static function adjustVersionParts(string $version1, string $version2): string {
728
-		$version1 = explode('.', $version1);
729
-		$version2 = explode('.', $version2);
730
-		// reduce $version1 to match the number of parts in $version2
731
-		while (count($version1) > count($version2)) {
732
-			array_pop($version1);
733
-		}
734
-		// if $version1 does not have enough parts, add some
735
-		while (count($version1) < count($version2)) {
736
-			$version1[] = '0';
737
-		}
738
-		return implode('.', $version1);
739
-	}
740
-
741
-	/**
742
-	 * Check whether the current ownCloud version matches the given
743
-	 * application's version requirements.
744
-	 *
745
-	 * The comparison is made based on the number of parts that the
746
-	 * app info version has. For example for ownCloud 6.0.3 if the
747
-	 * app info version is expecting version 6.0, the comparison is
748
-	 * made on the first two parts of the ownCloud version.
749
-	 * This means that it's possible to specify "requiremin" => 6
750
-	 * and "requiremax" => 6 and it will still match ownCloud 6.0.3.
751
-	 *
752
-	 * @param string $ocVersion ownCloud version to check against
753
-	 * @param array $appInfo app info (from xml)
754
-	 *
755
-	 * @return boolean true if compatible, otherwise false
756
-	 */
757
-	public static function isAppCompatible(string $ocVersion, array $appInfo, bool $ignoreMax = false): bool {
758
-		$requireMin = '';
759
-		$requireMax = '';
760
-		if (isset($appInfo['dependencies']['nextcloud']['@attributes']['min-version'])) {
761
-			$requireMin = $appInfo['dependencies']['nextcloud']['@attributes']['min-version'];
762
-		} elseif (isset($appInfo['dependencies']['owncloud']['@attributes']['min-version'])) {
763
-			$requireMin = $appInfo['dependencies']['owncloud']['@attributes']['min-version'];
764
-		} elseif (isset($appInfo['requiremin'])) {
765
-			$requireMin = $appInfo['requiremin'];
766
-		} elseif (isset($appInfo['require'])) {
767
-			$requireMin = $appInfo['require'];
768
-		}
769
-
770
-		if (isset($appInfo['dependencies']['nextcloud']['@attributes']['max-version'])) {
771
-			$requireMax = $appInfo['dependencies']['nextcloud']['@attributes']['max-version'];
772
-		} elseif (isset($appInfo['dependencies']['owncloud']['@attributes']['max-version'])) {
773
-			$requireMax = $appInfo['dependencies']['owncloud']['@attributes']['max-version'];
774
-		} elseif (isset($appInfo['requiremax'])) {
775
-			$requireMax = $appInfo['requiremax'];
776
-		}
777
-
778
-		if (!empty($requireMin)
779
-			&& version_compare(self::adjustVersionParts($ocVersion, $requireMin), $requireMin, '<')
780
-		) {
781
-			return false;
782
-		}
783
-
784
-		if (!$ignoreMax && !empty($requireMax)
785
-			&& version_compare(self::adjustVersionParts($ocVersion, $requireMax), $requireMax, '>')
786
-		) {
787
-			return false;
788
-		}
789
-
790
-		return true;
791
-	}
792
-
793
-	/**
794
-	 * get the installed version of all apps
795
-	 */
796
-	public static function getAppVersions() {
797
-		static $versions;
798
-
799
-		if (!$versions) {
800
-			$appConfig = \OC::$server->getAppConfig();
801
-			$versions = $appConfig->getValues(false, 'installed_version');
802
-		}
803
-		return $versions;
804
-	}
805
-
806
-	/**
807
-	 * update the database for the app and call the update script
808
-	 *
809
-	 * @param string $appId
810
-	 * @return bool
811
-	 */
812
-	public static function updateApp(string $appId): bool {
813
-		// for apps distributed with core, we refresh app path in case the downloaded version
814
-		// have been installed in custom apps and not in the default path
815
-		$appPath = self::getAppPath($appId, true);
816
-		if ($appPath === false) {
817
-			return false;
818
-		}
819
-
820
-		if (is_file($appPath . '/appinfo/database.xml')) {
821
-			\OC::$server->getLogger()->error('The appinfo/database.xml file is not longer supported. Used in ' . $appId);
822
-			return false;
823
-		}
824
-
825
-		\OC::$server->getAppManager()->clearAppsCache();
826
-		$l = \OC::$server->getL10N('core');
827
-		$appData = self::getAppInfo($appId, false, $l->getLanguageCode());
828
-
829
-		$ignoreMaxApps = \OC::$server->getConfig()->getSystemValue('app_install_overwrite', []);
830
-		$ignoreMax = in_array($appId, $ignoreMaxApps, true);
831
-		\OC_App::checkAppDependencies(
832
-			\OC::$server->getConfig(),
833
-			$l,
834
-			$appData,
835
-			$ignoreMax
836
-		);
837
-
838
-		self::registerAutoloading($appId, $appPath, true);
839
-		self::executeRepairSteps($appId, $appData['repair-steps']['pre-migration']);
840
-
841
-		$ms = new MigrationService($appId, \OC::$server->get(\OC\DB\Connection::class));
842
-		$ms->migrate();
843
-
844
-		self::executeRepairSteps($appId, $appData['repair-steps']['post-migration']);
845
-		self::setupLiveMigrations($appId, $appData['repair-steps']['live-migration']);
846
-		// update appversion in app manager
847
-		\OC::$server->getAppManager()->clearAppsCache();
848
-		\OC::$server->getAppManager()->getAppVersion($appId, false);
849
-
850
-		self::setupBackgroundJobs($appData['background-jobs']);
851
-
852
-		//set remote/public handlers
853
-		if (array_key_exists('ocsid', $appData)) {
854
-			\OC::$server->getConfig()->setAppValue($appId, 'ocsid', $appData['ocsid']);
855
-		} elseif (\OC::$server->getConfig()->getAppValue($appId, 'ocsid', null) !== null) {
856
-			\OC::$server->getConfig()->deleteAppValue($appId, 'ocsid');
857
-		}
858
-		foreach ($appData['remote'] as $name => $path) {
859
-			\OC::$server->getConfig()->setAppValue('core', 'remote_' . $name, $appId . '/' . $path);
860
-		}
861
-		foreach ($appData['public'] as $name => $path) {
862
-			\OC::$server->getConfig()->setAppValue('core', 'public_' . $name, $appId . '/' . $path);
863
-		}
864
-
865
-		self::setAppTypes($appId);
866
-
867
-		$version = \OC_App::getAppVersion($appId);
868
-		\OC::$server->getConfig()->setAppValue($appId, 'installed_version', $version);
869
-
870
-		\OC::$server->get(IEventDispatcher::class)->dispatchTyped(new AppUpdateEvent($appId));
871
-		\OC::$server->getEventDispatcher()->dispatch(ManagerEvent::EVENT_APP_UPDATE, new ManagerEvent(
872
-			ManagerEvent::EVENT_APP_UPDATE, $appId
873
-		));
874
-
875
-		return true;
876
-	}
877
-
878
-	/**
879
-	 * @param string $appId
880
-	 * @param string[] $steps
881
-	 * @throws \OC\NeedsUpdateException
882
-	 */
883
-	public static function executeRepairSteps(string $appId, array $steps) {
884
-		if (empty($steps)) {
885
-			return;
886
-		}
887
-		// load the app
888
-		self::loadApp($appId);
889
-
890
-		$dispatcher = \OC::$server->get(IEventDispatcher::class);
891
-
892
-		// load the steps
893
-		$r = new Repair([], $dispatcher, \OC::$server->get(LoggerInterface::class));
894
-		foreach ($steps as $step) {
895
-			try {
896
-				$r->addStep($step);
897
-			} catch (Exception $ex) {
898
-				$dispatcher->dispatchTyped(new RepairErrorEvent($ex->getMessage()));
899
-				\OC::$server->getLogger()->logException($ex);
900
-			}
901
-		}
902
-		// run the steps
903
-		$r->run();
904
-	}
905
-
906
-	public static function setupBackgroundJobs(array $jobs) {
907
-		$queue = \OC::$server->getJobList();
908
-		foreach ($jobs as $job) {
909
-			$queue->add($job);
910
-		}
911
-	}
912
-
913
-	/**
914
-	 * @param string $appId
915
-	 * @param string[] $steps
916
-	 */
917
-	private static function setupLiveMigrations(string $appId, array $steps) {
918
-		$queue = \OC::$server->getJobList();
919
-		foreach ($steps as $step) {
920
-			$queue->add('OC\Migration\BackgroundRepair', [
921
-				'app' => $appId,
922
-				'step' => $step]);
923
-		}
924
-	}
925
-
926
-	/**
927
-	 * @param string $appId
928
-	 * @return \OC\Files\View|false
929
-	 */
930
-	public static function getStorage(string $appId) {
931
-		if (\OC::$server->getAppManager()->isEnabledForUser($appId)) { //sanity check
932
-			if (\OC::$server->getUserSession()->isLoggedIn()) {
933
-				$view = new \OC\Files\View('/' . OC_User::getUser());
934
-				if (!$view->file_exists($appId)) {
935
-					$view->mkdir($appId);
936
-				}
937
-				return new \OC\Files\View('/' . OC_User::getUser() . '/' . $appId);
938
-			} else {
939
-				\OCP\Util::writeLog('core', 'Can\'t get app storage, app ' . $appId . ', user not logged in', ILogger::ERROR);
940
-				return false;
941
-			}
942
-		} else {
943
-			\OCP\Util::writeLog('core', 'Can\'t get app storage, app ' . $appId . ' not enabled', ILogger::ERROR);
944
-			return false;
945
-		}
946
-	}
947
-
948
-	protected static function findBestL10NOption(array $options, string $lang): string {
949
-		// only a single option
950
-		if (isset($options['@value'])) {
951
-			return $options['@value'];
952
-		}
953
-
954
-		$fallback = $similarLangFallback = $englishFallback = false;
955
-
956
-		$lang = strtolower($lang);
957
-		$similarLang = $lang;
958
-		if (strpos($similarLang, '_')) {
959
-			// For "de_DE" we want to find "de" and the other way around
960
-			$similarLang = substr($lang, 0, strpos($lang, '_'));
961
-		}
962
-
963
-		foreach ($options as $option) {
964
-			if (is_array($option)) {
965
-				if ($fallback === false) {
966
-					$fallback = $option['@value'];
967
-				}
968
-
969
-				if (!isset($option['@attributes']['lang'])) {
970
-					continue;
971
-				}
972
-
973
-				$attributeLang = strtolower($option['@attributes']['lang']);
974
-				if ($attributeLang === $lang) {
975
-					return $option['@value'];
976
-				}
977
-
978
-				if ($attributeLang === $similarLang) {
979
-					$similarLangFallback = $option['@value'];
980
-				} elseif (strpos($attributeLang, $similarLang . '_') === 0) {
981
-					if ($similarLangFallback === false) {
982
-						$similarLangFallback = $option['@value'];
983
-					}
984
-				}
985
-			} else {
986
-				$englishFallback = $option;
987
-			}
988
-		}
989
-
990
-		if ($similarLangFallback !== false) {
991
-			return $similarLangFallback;
992
-		} elseif ($englishFallback !== false) {
993
-			return $englishFallback;
994
-		}
995
-		return (string) $fallback;
996
-	}
997
-
998
-	/**
999
-	 * parses the app data array and enhanced the 'description' value
1000
-	 *
1001
-	 * @param array $data the app data
1002
-	 * @param string $lang
1003
-	 * @return array improved app data
1004
-	 */
1005
-	public static function parseAppInfo(array $data, $lang = null): array {
1006
-		if ($lang && isset($data['name']) && is_array($data['name'])) {
1007
-			$data['name'] = self::findBestL10NOption($data['name'], $lang);
1008
-		}
1009
-		if ($lang && isset($data['summary']) && is_array($data['summary'])) {
1010
-			$data['summary'] = self::findBestL10NOption($data['summary'], $lang);
1011
-		}
1012
-		if ($lang && isset($data['description']) && is_array($data['description'])) {
1013
-			$data['description'] = trim(self::findBestL10NOption($data['description'], $lang));
1014
-		} elseif (isset($data['description']) && is_string($data['description'])) {
1015
-			$data['description'] = trim($data['description']);
1016
-		} else {
1017
-			$data['description'] = '';
1018
-		}
1019
-
1020
-		return $data;
1021
-	}
1022
-
1023
-	/**
1024
-	 * @param \OCP\IConfig $config
1025
-	 * @param \OCP\IL10N $l
1026
-	 * @param array $info
1027
-	 * @throws \Exception
1028
-	 */
1029
-	public static function checkAppDependencies(\OCP\IConfig $config, \OCP\IL10N $l, array $info, bool $ignoreMax) {
1030
-		$dependencyAnalyzer = new DependencyAnalyzer(new Platform($config), $l);
1031
-		$missing = $dependencyAnalyzer->analyze($info, $ignoreMax);
1032
-		if (!empty($missing)) {
1033
-			$missingMsg = implode(PHP_EOL, $missing);
1034
-			throw new \Exception(
1035
-				$l->t('App "%1$s" cannot be installed because the following dependencies are not fulfilled: %2$s',
1036
-					[$info['name'], $missingMsg]
1037
-				)
1038
-			);
1039
-		}
1040
-	}
76
+    private static $adminForms = [];
77
+    private static $personalForms = [];
78
+    private static $altLogin = [];
79
+    private static $alreadyRegistered = [];
80
+    public const supportedApp = 300;
81
+    public const officialApp = 200;
82
+
83
+    /**
84
+     * clean the appId
85
+     *
86
+     * @psalm-taint-escape file
87
+     * @psalm-taint-escape include
88
+     *
89
+     * @param string $app AppId that needs to be cleaned
90
+     * @return string
91
+     */
92
+    public static function cleanAppId(string $app): string {
93
+        return str_replace(['\0', '/', '\\', '..'], '', $app);
94
+    }
95
+
96
+    /**
97
+     * Check if an app is loaded
98
+     *
99
+     * @param string $app
100
+     * @return bool
101
+     * @deprecated 27.0.0 use IAppManager::isAppLoaded
102
+     */
103
+    public static function isAppLoaded(string $app): bool {
104
+        return \OC::$server->get(IAppManager::class)->isAppLoaded($app);
105
+    }
106
+
107
+    /**
108
+     * loads all apps
109
+     *
110
+     * @param string[] $types
111
+     * @return bool
112
+     *
113
+     * This function walks through the ownCloud directory and loads all apps
114
+     * it can find. A directory contains an app if the file /appinfo/info.xml
115
+     * exists.
116
+     *
117
+     * if $types is set to non-empty array, only apps of those types will be loaded
118
+     */
119
+    public static function loadApps(array $types = []): bool {
120
+        if (!\OC::$server->getSystemConfig()->getValue('installed', false)) {
121
+            // This should be done before calling this method so that appmanager can be used
122
+            return false;
123
+        }
124
+        return \OC::$server->get(IAppManager::class)->loadApps($types);
125
+    }
126
+
127
+    /**
128
+     * load a single app
129
+     *
130
+     * @param string $app
131
+     * @throws Exception
132
+     * @deprecated 27.0.0 use IAppManager::loadApp
133
+     */
134
+    public static function loadApp(string $app): void {
135
+        \OC::$server->get(IAppManager::class)->loadApp($app);
136
+    }
137
+
138
+    /**
139
+     * @internal
140
+     * @param string $app
141
+     * @param string $path
142
+     * @param bool $force
143
+     */
144
+    public static function registerAutoloading(string $app, string $path, bool $force = false) {
145
+        $key = $app . '-' . $path;
146
+        if (!$force && isset(self::$alreadyRegistered[$key])) {
147
+            return;
148
+        }
149
+
150
+        self::$alreadyRegistered[$key] = true;
151
+
152
+        // Register on PSR-4 composer autoloader
153
+        $appNamespace = \OC\AppFramework\App::buildAppNamespace($app);
154
+        \OC::$server->registerNamespace($app, $appNamespace);
155
+
156
+        if (file_exists($path . '/composer/autoload.php')) {
157
+            require_once $path . '/composer/autoload.php';
158
+        } else {
159
+            \OC::$composerAutoloader->addPsr4($appNamespace . '\\', $path . '/lib/', true);
160
+        }
161
+
162
+        // Register Test namespace only when testing
163
+        if (defined('PHPUNIT_RUN') || defined('CLI_TEST_RUN')) {
164
+            \OC::$composerAutoloader->addPsr4($appNamespace . '\\Tests\\', $path . '/tests/', true);
165
+        }
166
+    }
167
+
168
+    /**
169
+     * check if an app is of a specific type
170
+     *
171
+     * @param string $app
172
+     * @param array $types
173
+     * @return bool
174
+     * @deprecated 27.0.0 use IAppManager::isType
175
+     */
176
+    public static function isType(string $app, array $types): bool {
177
+        return \OC::$server->get(IAppManager::class)->isType($app, $types);
178
+    }
179
+
180
+    /**
181
+     * read app types from info.xml and cache them in the database
182
+     */
183
+    public static function setAppTypes(string $app) {
184
+        $appManager = \OC::$server->getAppManager();
185
+        $appData = $appManager->getAppInfo($app);
186
+        if (!is_array($appData)) {
187
+            return;
188
+        }
189
+
190
+        if (isset($appData['types'])) {
191
+            $appTypes = implode(',', $appData['types']);
192
+        } else {
193
+            $appTypes = '';
194
+            $appData['types'] = [];
195
+        }
196
+
197
+        $config = \OC::$server->getConfig();
198
+        $config->setAppValue($app, 'types', $appTypes);
199
+
200
+        if ($appManager->hasProtectedAppType($appData['types'])) {
201
+            $enabled = $config->getAppValue($app, 'enabled', 'yes');
202
+            if ($enabled !== 'yes' && $enabled !== 'no') {
203
+                $config->setAppValue($app, 'enabled', 'yes');
204
+            }
205
+        }
206
+    }
207
+
208
+    /**
209
+     * Returns apps enabled for the current user.
210
+     *
211
+     * @param bool $forceRefresh whether to refresh the cache
212
+     * @param bool $all whether to return apps for all users, not only the
213
+     * currently logged in one
214
+     * @return string[]
215
+     */
216
+    public static function getEnabledApps(bool $forceRefresh = false, bool $all = false): array {
217
+        if (!\OC::$server->getSystemConfig()->getValue('installed', false)) {
218
+            return [];
219
+        }
220
+        // in incognito mode or when logged out, $user will be false,
221
+        // which is also the case during an upgrade
222
+        $appManager = \OC::$server->getAppManager();
223
+        if ($all) {
224
+            $user = null;
225
+        } else {
226
+            $user = \OC::$server->getUserSession()->getUser();
227
+        }
228
+
229
+        if (is_null($user)) {
230
+            $apps = $appManager->getInstalledApps();
231
+        } else {
232
+            $apps = $appManager->getEnabledAppsForUser($user);
233
+        }
234
+        $apps = array_filter($apps, function ($app) {
235
+            return $app !== 'files';//we add this manually
236
+        });
237
+        sort($apps);
238
+        array_unshift($apps, 'files');
239
+        return $apps;
240
+    }
241
+
242
+    /**
243
+     * checks whether or not an app is enabled
244
+     *
245
+     * @param string $app app
246
+     * @return bool
247
+     * @deprecated 13.0.0 use \OC::$server->getAppManager()->isEnabledForUser($appId)
248
+     *
249
+     * This function checks whether or not an app is enabled.
250
+     */
251
+    public static function isEnabled(string $app): bool {
252
+        return \OC::$server->getAppManager()->isEnabledForUser($app);
253
+    }
254
+
255
+    /**
256
+     * enables an app
257
+     *
258
+     * @param string $appId
259
+     * @param array $groups (optional) when set, only these groups will have access to the app
260
+     * @throws \Exception
261
+     * @return void
262
+     *
263
+     * This function set an app as enabled in appconfig.
264
+     */
265
+    public function enable(string $appId,
266
+                            array $groups = []) {
267
+        // Check if app is already downloaded
268
+        /** @var Installer $installer */
269
+        $installer = \OC::$server->query(Installer::class);
270
+        $isDownloaded = $installer->isDownloaded($appId);
271
+
272
+        if (!$isDownloaded) {
273
+            $installer->downloadApp($appId);
274
+        }
275
+
276
+        $installer->installApp($appId);
277
+
278
+        $appManager = \OC::$server->getAppManager();
279
+        if ($groups !== []) {
280
+            $groupManager = \OC::$server->getGroupManager();
281
+            $groupsList = [];
282
+            foreach ($groups as $group) {
283
+                $groupItem = $groupManager->get($group);
284
+                if ($groupItem instanceof \OCP\IGroup) {
285
+                    $groupsList[] = $groupManager->get($group);
286
+                }
287
+            }
288
+            $appManager->enableAppForGroups($appId, $groupsList);
289
+        } else {
290
+            $appManager->enableApp($appId);
291
+        }
292
+    }
293
+
294
+    /**
295
+     * Get the path where to install apps
296
+     *
297
+     * @return string|false
298
+     */
299
+    public static function getInstallPath() {
300
+        foreach (OC::$APPSROOTS as $dir) {
301
+            if (isset($dir['writable']) && $dir['writable'] === true) {
302
+                return $dir['path'];
303
+            }
304
+        }
305
+
306
+        \OCP\Util::writeLog('core', 'No application directories are marked as writable.', ILogger::ERROR);
307
+        return null;
308
+    }
309
+
310
+
311
+    /**
312
+     * search for an app in all app-directories
313
+     *
314
+     * @param string $appId
315
+     * @param bool $ignoreCache ignore cache and rebuild it
316
+     * @return false|string
317
+     */
318
+    public static function findAppInDirectories(string $appId, bool $ignoreCache = false) {
319
+        $sanitizedAppId = self::cleanAppId($appId);
320
+        if ($sanitizedAppId !== $appId) {
321
+            return false;
322
+        }
323
+        static $app_dir = [];
324
+
325
+        if (isset($app_dir[$appId]) && !$ignoreCache) {
326
+            return $app_dir[$appId];
327
+        }
328
+
329
+        $possibleApps = [];
330
+        foreach (OC::$APPSROOTS as $dir) {
331
+            if (file_exists($dir['path'] . '/' . $appId)) {
332
+                $possibleApps[] = $dir;
333
+            }
334
+        }
335
+
336
+        if (empty($possibleApps)) {
337
+            return false;
338
+        } elseif (count($possibleApps) === 1) {
339
+            $dir = array_shift($possibleApps);
340
+            $app_dir[$appId] = $dir;
341
+            return $dir;
342
+        } else {
343
+            $versionToLoad = [];
344
+            foreach ($possibleApps as $possibleApp) {
345
+                $version = self::getAppVersionByPath($possibleApp['path'] . '/' . $appId);
346
+                if (empty($versionToLoad) || version_compare($version, $versionToLoad['version'], '>')) {
347
+                    $versionToLoad = [
348
+                        'dir' => $possibleApp,
349
+                        'version' => $version,
350
+                    ];
351
+                }
352
+            }
353
+            $app_dir[$appId] = $versionToLoad['dir'];
354
+            return $versionToLoad['dir'];
355
+            //TODO - write test
356
+        }
357
+    }
358
+
359
+    /**
360
+     * Get the directory for the given app.
361
+     * If the app is defined in multiple directories, the first one is taken. (false if not found)
362
+     *
363
+     * @psalm-taint-specialize
364
+     *
365
+     * @param string $appId
366
+     * @param bool $refreshAppPath should be set to true only during install/upgrade
367
+     * @return string|false
368
+     * @deprecated 11.0.0 use \OC::$server->getAppManager()->getAppPath()
369
+     */
370
+    public static function getAppPath(string $appId, bool $refreshAppPath = false) {
371
+        if ($appId === null || trim($appId) === '') {
372
+            return false;
373
+        }
374
+
375
+        if (($dir = self::findAppInDirectories($appId, $refreshAppPath)) != false) {
376
+            return $dir['path'] . '/' . $appId;
377
+        }
378
+        return false;
379
+    }
380
+
381
+    /**
382
+     * Get the path for the given app on the access
383
+     * If the app is defined in multiple directories, the first one is taken. (false if not found)
384
+     *
385
+     * @param string $appId
386
+     * @return string|false
387
+     * @deprecated 18.0.0 use \OC::$server->getAppManager()->getAppWebPath()
388
+     */
389
+    public static function getAppWebPath(string $appId) {
390
+        if (($dir = self::findAppInDirectories($appId)) != false) {
391
+            return OC::$WEBROOT . $dir['url'] . '/' . $appId;
392
+        }
393
+        return false;
394
+    }
395
+
396
+    /**
397
+     * get the last version of the app from appinfo/info.xml
398
+     *
399
+     * @param string $appId
400
+     * @param bool $useCache
401
+     * @return string
402
+     * @deprecated 14.0.0 use \OC::$server->getAppManager()->getAppVersion()
403
+     */
404
+    public static function getAppVersion(string $appId, bool $useCache = true): string {
405
+        return \OC::$server->getAppManager()->getAppVersion($appId, $useCache);
406
+    }
407
+
408
+    /**
409
+     * get app's version based on it's path
410
+     *
411
+     * @param string $path
412
+     * @return string
413
+     */
414
+    public static function getAppVersionByPath(string $path): string {
415
+        $infoFile = $path . '/appinfo/info.xml';
416
+        $appData = \OC::$server->getAppManager()->getAppInfo($infoFile, true);
417
+        return isset($appData['version']) ? $appData['version'] : '';
418
+    }
419
+
420
+
421
+    /**
422
+     * Read all app metadata from the info.xml file
423
+     *
424
+     * @param string $appId id of the app or the path of the info.xml file
425
+     * @param bool $path
426
+     * @param string $lang
427
+     * @return array|null
428
+     * @note all data is read from info.xml, not just pre-defined fields
429
+     * @deprecated 14.0.0 use \OC::$server->getAppManager()->getAppInfo()
430
+     */
431
+    public static function getAppInfo(string $appId, bool $path = false, string $lang = null) {
432
+        return \OC::$server->getAppManager()->getAppInfo($appId, $path, $lang);
433
+    }
434
+
435
+    /**
436
+     * Returns the navigation
437
+     *
438
+     * @return array
439
+     * @deprecated 14.0.0 use \OC::$server->getNavigationManager()->getAll()
440
+     *
441
+     * This function returns an array containing all entries added. The
442
+     * entries are sorted by the key 'order' ascending. Additional to the keys
443
+     * given for each app the following keys exist:
444
+     *   - active: boolean, signals if the user is on this navigation entry
445
+     */
446
+    public static function getNavigation(): array {
447
+        return OC::$server->getNavigationManager()->getAll();
448
+    }
449
+
450
+    /**
451
+     * Returns the Settings Navigation
452
+     *
453
+     * @return string[]
454
+     * @deprecated 14.0.0 use \OC::$server->getNavigationManager()->getAll('settings')
455
+     *
456
+     * This function returns an array containing all settings pages added. The
457
+     * entries are sorted by the key 'order' ascending.
458
+     */
459
+    public static function getSettingsNavigation(): array {
460
+        return OC::$server->getNavigationManager()->getAll('settings');
461
+    }
462
+
463
+    /**
464
+     * get the id of loaded app
465
+     *
466
+     * @return string
467
+     */
468
+    public static function getCurrentApp(): string {
469
+        if (\OC::$CLI) {
470
+            return '';
471
+        }
472
+
473
+        $request = \OC::$server->getRequest();
474
+        $script = substr($request->getScriptName(), strlen(OC::$WEBROOT) + 1);
475
+        $topFolder = substr($script, 0, strpos($script, '/') ?: 0);
476
+        if (empty($topFolder)) {
477
+            try {
478
+                $path_info = $request->getPathInfo();
479
+            } catch (Exception $e) {
480
+                // Can happen from unit tests because the script name is `./vendor/bin/phpunit` or something a like then.
481
+                \OC::$server->get(LoggerInterface::class)->error('Failed to detect current app from script path', ['exception' => $e]);
482
+                return '';
483
+            }
484
+            if ($path_info) {
485
+                $topFolder = substr($path_info, 1, strpos($path_info, '/', 1) - 1);
486
+            }
487
+        }
488
+        if ($topFolder == 'apps') {
489
+            $length = strlen($topFolder);
490
+            return substr($script, $length + 1, strpos($script, '/', $length + 1) - $length - 1) ?: '';
491
+        } else {
492
+            return $topFolder;
493
+        }
494
+    }
495
+
496
+    /**
497
+     * @param string $type
498
+     * @return array
499
+     */
500
+    public static function getForms(string $type): array {
501
+        $forms = [];
502
+        switch ($type) {
503
+            case 'admin':
504
+                $source = self::$adminForms;
505
+                break;
506
+            case 'personal':
507
+                $source = self::$personalForms;
508
+                break;
509
+            default:
510
+                return [];
511
+        }
512
+        foreach ($source as $form) {
513
+            $forms[] = include $form;
514
+        }
515
+        return $forms;
516
+    }
517
+
518
+    /**
519
+     * @param array $entry
520
+     * @deprecated 20.0.0 Please register your alternative login option using the registerAlternativeLogin() on the RegistrationContext in your Application class implementing the OCP\Authentication\IAlternativeLogin interface
521
+     */
522
+    public static function registerLogIn(array $entry) {
523
+        \OC::$server->getLogger()->debug('OC_App::registerLogIn() is deprecated, please register your alternative login option using the registerAlternativeLogin() on the RegistrationContext in your Application class implementing the OCP\Authentication\IAlternativeLogin interface');
524
+        self::$altLogin[] = $entry;
525
+    }
526
+
527
+    /**
528
+     * @return array
529
+     */
530
+    public static function getAlternativeLogIns(): array {
531
+        /** @var Coordinator $bootstrapCoordinator */
532
+        $bootstrapCoordinator = \OC::$server->query(Coordinator::class);
533
+
534
+        foreach ($bootstrapCoordinator->getRegistrationContext()->getAlternativeLogins() as $registration) {
535
+            if (!in_array(IAlternativeLogin::class, class_implements($registration->getService()), true)) {
536
+                \OC::$server->getLogger()->error('Alternative login option {option} does not implement {interface} and is therefore ignored.', [
537
+                    'option' => $registration->getService(),
538
+                    'interface' => IAlternativeLogin::class,
539
+                    'app' => $registration->getAppId(),
540
+                ]);
541
+                continue;
542
+            }
543
+
544
+            try {
545
+                /** @var IAlternativeLogin $provider */
546
+                $provider = \OC::$server->query($registration->getService());
547
+            } catch (QueryException $e) {
548
+                \OC::$server->getLogger()->logException($e, [
549
+                    'message' => 'Alternative login option {option} can not be initialised.',
550
+                    'option' => $registration->getService(),
551
+                    'app' => $registration->getAppId(),
552
+                ]);
553
+            }
554
+
555
+            try {
556
+                $provider->load();
557
+
558
+                self::$altLogin[] = [
559
+                    'name' => $provider->getLabel(),
560
+                    'href' => $provider->getLink(),
561
+                    'class' => $provider->getClass(),
562
+                ];
563
+            } catch (Throwable $e) {
564
+                \OC::$server->getLogger()->logException($e, [
565
+                    'message' => 'Alternative login option {option} had an error while loading.',
566
+                    'option' => $registration->getService(),
567
+                    'app' => $registration->getAppId(),
568
+                ]);
569
+            }
570
+        }
571
+
572
+        return self::$altLogin;
573
+    }
574
+
575
+    /**
576
+     * get a list of all apps in the apps folder
577
+     *
578
+     * @return string[] an array of app names (string IDs)
579
+     * @todo: change the name of this method to getInstalledApps, which is more accurate
580
+     */
581
+    public static function getAllApps(): array {
582
+        $apps = [];
583
+
584
+        foreach (OC::$APPSROOTS as $apps_dir) {
585
+            if (!is_readable($apps_dir['path'])) {
586
+                \OCP\Util::writeLog('core', 'unable to read app folder : ' . $apps_dir['path'], ILogger::WARN);
587
+                continue;
588
+            }
589
+            $dh = opendir($apps_dir['path']);
590
+
591
+            if (is_resource($dh)) {
592
+                while (($file = readdir($dh)) !== false) {
593
+                    if ($file[0] != '.' and is_dir($apps_dir['path'] . '/' . $file) and is_file($apps_dir['path'] . '/' . $file . '/appinfo/info.xml')) {
594
+                        $apps[] = $file;
595
+                    }
596
+                }
597
+            }
598
+        }
599
+
600
+        $apps = array_unique($apps);
601
+
602
+        return $apps;
603
+    }
604
+
605
+    /**
606
+     * List all supported apps
607
+     *
608
+     * @return array
609
+     */
610
+    public function getSupportedApps(): array {
611
+        /** @var \OCP\Support\Subscription\IRegistry $subscriptionRegistry */
612
+        $subscriptionRegistry = \OC::$server->query(\OCP\Support\Subscription\IRegistry::class);
613
+        $supportedApps = $subscriptionRegistry->delegateGetSupportedApps();
614
+        return $supportedApps;
615
+    }
616
+
617
+    /**
618
+     * List all apps, this is used in apps.php
619
+     *
620
+     * @return array
621
+     */
622
+    public function listAllApps(): array {
623
+        $installedApps = OC_App::getAllApps();
624
+
625
+        $appManager = \OC::$server->getAppManager();
626
+        //we don't want to show configuration for these
627
+        $blacklist = $appManager->getAlwaysEnabledApps();
628
+        $appList = [];
629
+        $langCode = \OC::$server->getL10N('core')->getLanguageCode();
630
+        $urlGenerator = \OC::$server->getURLGenerator();
631
+        $supportedApps = $this->getSupportedApps();
632
+
633
+        foreach ($installedApps as $app) {
634
+            if (array_search($app, $blacklist) === false) {
635
+                $info = OC_App::getAppInfo($app, false, $langCode);
636
+                if (!is_array($info)) {
637
+                    \OCP\Util::writeLog('core', 'Could not read app info file for app "' . $app . '"', ILogger::ERROR);
638
+                    continue;
639
+                }
640
+
641
+                if (!isset($info['name'])) {
642
+                    \OCP\Util::writeLog('core', 'App id "' . $app . '" has no name in appinfo', ILogger::ERROR);
643
+                    continue;
644
+                }
645
+
646
+                $enabled = \OC::$server->getConfig()->getAppValue($app, 'enabled', 'no');
647
+                $info['groups'] = null;
648
+                if ($enabled === 'yes') {
649
+                    $active = true;
650
+                } elseif ($enabled === 'no') {
651
+                    $active = false;
652
+                } else {
653
+                    $active = true;
654
+                    $info['groups'] = $enabled;
655
+                }
656
+
657
+                $info['active'] = $active;
658
+
659
+                if ($appManager->isShipped($app)) {
660
+                    $info['internal'] = true;
661
+                    $info['level'] = self::officialApp;
662
+                    $info['removable'] = false;
663
+                } else {
664
+                    $info['internal'] = false;
665
+                    $info['removable'] = true;
666
+                }
667
+
668
+                if (in_array($app, $supportedApps)) {
669
+                    $info['level'] = self::supportedApp;
670
+                }
671
+
672
+                $appPath = self::getAppPath($app);
673
+                if ($appPath !== false) {
674
+                    $appIcon = $appPath . '/img/' . $app . '.svg';
675
+                    if (file_exists($appIcon)) {
676
+                        $info['preview'] = $urlGenerator->imagePath($app, $app . '.svg');
677
+                        $info['previewAsIcon'] = true;
678
+                    } else {
679
+                        $appIcon = $appPath . '/img/app.svg';
680
+                        if (file_exists($appIcon)) {
681
+                            $info['preview'] = $urlGenerator->imagePath($app, 'app.svg');
682
+                            $info['previewAsIcon'] = true;
683
+                        }
684
+                    }
685
+                }
686
+                // fix documentation
687
+                if (isset($info['documentation']) && is_array($info['documentation'])) {
688
+                    foreach ($info['documentation'] as $key => $url) {
689
+                        // If it is not an absolute URL we assume it is a key
690
+                        // i.e. admin-ldap will get converted to go.php?to=admin-ldap
691
+                        if (stripos($url, 'https://') !== 0 && stripos($url, 'http://') !== 0) {
692
+                            $url = $urlGenerator->linkToDocs($url);
693
+                        }
694
+
695
+                        $info['documentation'][$key] = $url;
696
+                    }
697
+                }
698
+
699
+                $info['version'] = OC_App::getAppVersion($app);
700
+                $appList[] = $info;
701
+            }
702
+        }
703
+
704
+        return $appList;
705
+    }
706
+
707
+    public static function shouldUpgrade(string $app): bool {
708
+        $versions = self::getAppVersions();
709
+        $currentVersion = OC_App::getAppVersion($app);
710
+        if ($currentVersion && isset($versions[$app])) {
711
+            $installedVersion = $versions[$app];
712
+            if (!version_compare($currentVersion, $installedVersion, '=')) {
713
+                return true;
714
+            }
715
+        }
716
+        return false;
717
+    }
718
+
719
+    /**
720
+     * Adjust the number of version parts of $version1 to match
721
+     * the number of version parts of $version2.
722
+     *
723
+     * @param string $version1 version to adjust
724
+     * @param string $version2 version to take the number of parts from
725
+     * @return string shortened $version1
726
+     */
727
+    private static function adjustVersionParts(string $version1, string $version2): string {
728
+        $version1 = explode('.', $version1);
729
+        $version2 = explode('.', $version2);
730
+        // reduce $version1 to match the number of parts in $version2
731
+        while (count($version1) > count($version2)) {
732
+            array_pop($version1);
733
+        }
734
+        // if $version1 does not have enough parts, add some
735
+        while (count($version1) < count($version2)) {
736
+            $version1[] = '0';
737
+        }
738
+        return implode('.', $version1);
739
+    }
740
+
741
+    /**
742
+     * Check whether the current ownCloud version matches the given
743
+     * application's version requirements.
744
+     *
745
+     * The comparison is made based on the number of parts that the
746
+     * app info version has. For example for ownCloud 6.0.3 if the
747
+     * app info version is expecting version 6.0, the comparison is
748
+     * made on the first two parts of the ownCloud version.
749
+     * This means that it's possible to specify "requiremin" => 6
750
+     * and "requiremax" => 6 and it will still match ownCloud 6.0.3.
751
+     *
752
+     * @param string $ocVersion ownCloud version to check against
753
+     * @param array $appInfo app info (from xml)
754
+     *
755
+     * @return boolean true if compatible, otherwise false
756
+     */
757
+    public static function isAppCompatible(string $ocVersion, array $appInfo, bool $ignoreMax = false): bool {
758
+        $requireMin = '';
759
+        $requireMax = '';
760
+        if (isset($appInfo['dependencies']['nextcloud']['@attributes']['min-version'])) {
761
+            $requireMin = $appInfo['dependencies']['nextcloud']['@attributes']['min-version'];
762
+        } elseif (isset($appInfo['dependencies']['owncloud']['@attributes']['min-version'])) {
763
+            $requireMin = $appInfo['dependencies']['owncloud']['@attributes']['min-version'];
764
+        } elseif (isset($appInfo['requiremin'])) {
765
+            $requireMin = $appInfo['requiremin'];
766
+        } elseif (isset($appInfo['require'])) {
767
+            $requireMin = $appInfo['require'];
768
+        }
769
+
770
+        if (isset($appInfo['dependencies']['nextcloud']['@attributes']['max-version'])) {
771
+            $requireMax = $appInfo['dependencies']['nextcloud']['@attributes']['max-version'];
772
+        } elseif (isset($appInfo['dependencies']['owncloud']['@attributes']['max-version'])) {
773
+            $requireMax = $appInfo['dependencies']['owncloud']['@attributes']['max-version'];
774
+        } elseif (isset($appInfo['requiremax'])) {
775
+            $requireMax = $appInfo['requiremax'];
776
+        }
777
+
778
+        if (!empty($requireMin)
779
+            && version_compare(self::adjustVersionParts($ocVersion, $requireMin), $requireMin, '<')
780
+        ) {
781
+            return false;
782
+        }
783
+
784
+        if (!$ignoreMax && !empty($requireMax)
785
+            && version_compare(self::adjustVersionParts($ocVersion, $requireMax), $requireMax, '>')
786
+        ) {
787
+            return false;
788
+        }
789
+
790
+        return true;
791
+    }
792
+
793
+    /**
794
+     * get the installed version of all apps
795
+     */
796
+    public static function getAppVersions() {
797
+        static $versions;
798
+
799
+        if (!$versions) {
800
+            $appConfig = \OC::$server->getAppConfig();
801
+            $versions = $appConfig->getValues(false, 'installed_version');
802
+        }
803
+        return $versions;
804
+    }
805
+
806
+    /**
807
+     * update the database for the app and call the update script
808
+     *
809
+     * @param string $appId
810
+     * @return bool
811
+     */
812
+    public static function updateApp(string $appId): bool {
813
+        // for apps distributed with core, we refresh app path in case the downloaded version
814
+        // have been installed in custom apps and not in the default path
815
+        $appPath = self::getAppPath($appId, true);
816
+        if ($appPath === false) {
817
+            return false;
818
+        }
819
+
820
+        if (is_file($appPath . '/appinfo/database.xml')) {
821
+            \OC::$server->getLogger()->error('The appinfo/database.xml file is not longer supported. Used in ' . $appId);
822
+            return false;
823
+        }
824
+
825
+        \OC::$server->getAppManager()->clearAppsCache();
826
+        $l = \OC::$server->getL10N('core');
827
+        $appData = self::getAppInfo($appId, false, $l->getLanguageCode());
828
+
829
+        $ignoreMaxApps = \OC::$server->getConfig()->getSystemValue('app_install_overwrite', []);
830
+        $ignoreMax = in_array($appId, $ignoreMaxApps, true);
831
+        \OC_App::checkAppDependencies(
832
+            \OC::$server->getConfig(),
833
+            $l,
834
+            $appData,
835
+            $ignoreMax
836
+        );
837
+
838
+        self::registerAutoloading($appId, $appPath, true);
839
+        self::executeRepairSteps($appId, $appData['repair-steps']['pre-migration']);
840
+
841
+        $ms = new MigrationService($appId, \OC::$server->get(\OC\DB\Connection::class));
842
+        $ms->migrate();
843
+
844
+        self::executeRepairSteps($appId, $appData['repair-steps']['post-migration']);
845
+        self::setupLiveMigrations($appId, $appData['repair-steps']['live-migration']);
846
+        // update appversion in app manager
847
+        \OC::$server->getAppManager()->clearAppsCache();
848
+        \OC::$server->getAppManager()->getAppVersion($appId, false);
849
+
850
+        self::setupBackgroundJobs($appData['background-jobs']);
851
+
852
+        //set remote/public handlers
853
+        if (array_key_exists('ocsid', $appData)) {
854
+            \OC::$server->getConfig()->setAppValue($appId, 'ocsid', $appData['ocsid']);
855
+        } elseif (\OC::$server->getConfig()->getAppValue($appId, 'ocsid', null) !== null) {
856
+            \OC::$server->getConfig()->deleteAppValue($appId, 'ocsid');
857
+        }
858
+        foreach ($appData['remote'] as $name => $path) {
859
+            \OC::$server->getConfig()->setAppValue('core', 'remote_' . $name, $appId . '/' . $path);
860
+        }
861
+        foreach ($appData['public'] as $name => $path) {
862
+            \OC::$server->getConfig()->setAppValue('core', 'public_' . $name, $appId . '/' . $path);
863
+        }
864
+
865
+        self::setAppTypes($appId);
866
+
867
+        $version = \OC_App::getAppVersion($appId);
868
+        \OC::$server->getConfig()->setAppValue($appId, 'installed_version', $version);
869
+
870
+        \OC::$server->get(IEventDispatcher::class)->dispatchTyped(new AppUpdateEvent($appId));
871
+        \OC::$server->getEventDispatcher()->dispatch(ManagerEvent::EVENT_APP_UPDATE, new ManagerEvent(
872
+            ManagerEvent::EVENT_APP_UPDATE, $appId
873
+        ));
874
+
875
+        return true;
876
+    }
877
+
878
+    /**
879
+     * @param string $appId
880
+     * @param string[] $steps
881
+     * @throws \OC\NeedsUpdateException
882
+     */
883
+    public static function executeRepairSteps(string $appId, array $steps) {
884
+        if (empty($steps)) {
885
+            return;
886
+        }
887
+        // load the app
888
+        self::loadApp($appId);
889
+
890
+        $dispatcher = \OC::$server->get(IEventDispatcher::class);
891
+
892
+        // load the steps
893
+        $r = new Repair([], $dispatcher, \OC::$server->get(LoggerInterface::class));
894
+        foreach ($steps as $step) {
895
+            try {
896
+                $r->addStep($step);
897
+            } catch (Exception $ex) {
898
+                $dispatcher->dispatchTyped(new RepairErrorEvent($ex->getMessage()));
899
+                \OC::$server->getLogger()->logException($ex);
900
+            }
901
+        }
902
+        // run the steps
903
+        $r->run();
904
+    }
905
+
906
+    public static function setupBackgroundJobs(array $jobs) {
907
+        $queue = \OC::$server->getJobList();
908
+        foreach ($jobs as $job) {
909
+            $queue->add($job);
910
+        }
911
+    }
912
+
913
+    /**
914
+     * @param string $appId
915
+     * @param string[] $steps
916
+     */
917
+    private static function setupLiveMigrations(string $appId, array $steps) {
918
+        $queue = \OC::$server->getJobList();
919
+        foreach ($steps as $step) {
920
+            $queue->add('OC\Migration\BackgroundRepair', [
921
+                'app' => $appId,
922
+                'step' => $step]);
923
+        }
924
+    }
925
+
926
+    /**
927
+     * @param string $appId
928
+     * @return \OC\Files\View|false
929
+     */
930
+    public static function getStorage(string $appId) {
931
+        if (\OC::$server->getAppManager()->isEnabledForUser($appId)) { //sanity check
932
+            if (\OC::$server->getUserSession()->isLoggedIn()) {
933
+                $view = new \OC\Files\View('/' . OC_User::getUser());
934
+                if (!$view->file_exists($appId)) {
935
+                    $view->mkdir($appId);
936
+                }
937
+                return new \OC\Files\View('/' . OC_User::getUser() . '/' . $appId);
938
+            } else {
939
+                \OCP\Util::writeLog('core', 'Can\'t get app storage, app ' . $appId . ', user not logged in', ILogger::ERROR);
940
+                return false;
941
+            }
942
+        } else {
943
+            \OCP\Util::writeLog('core', 'Can\'t get app storage, app ' . $appId . ' not enabled', ILogger::ERROR);
944
+            return false;
945
+        }
946
+    }
947
+
948
+    protected static function findBestL10NOption(array $options, string $lang): string {
949
+        // only a single option
950
+        if (isset($options['@value'])) {
951
+            return $options['@value'];
952
+        }
953
+
954
+        $fallback = $similarLangFallback = $englishFallback = false;
955
+
956
+        $lang = strtolower($lang);
957
+        $similarLang = $lang;
958
+        if (strpos($similarLang, '_')) {
959
+            // For "de_DE" we want to find "de" and the other way around
960
+            $similarLang = substr($lang, 0, strpos($lang, '_'));
961
+        }
962
+
963
+        foreach ($options as $option) {
964
+            if (is_array($option)) {
965
+                if ($fallback === false) {
966
+                    $fallback = $option['@value'];
967
+                }
968
+
969
+                if (!isset($option['@attributes']['lang'])) {
970
+                    continue;
971
+                }
972
+
973
+                $attributeLang = strtolower($option['@attributes']['lang']);
974
+                if ($attributeLang === $lang) {
975
+                    return $option['@value'];
976
+                }
977
+
978
+                if ($attributeLang === $similarLang) {
979
+                    $similarLangFallback = $option['@value'];
980
+                } elseif (strpos($attributeLang, $similarLang . '_') === 0) {
981
+                    if ($similarLangFallback === false) {
982
+                        $similarLangFallback = $option['@value'];
983
+                    }
984
+                }
985
+            } else {
986
+                $englishFallback = $option;
987
+            }
988
+        }
989
+
990
+        if ($similarLangFallback !== false) {
991
+            return $similarLangFallback;
992
+        } elseif ($englishFallback !== false) {
993
+            return $englishFallback;
994
+        }
995
+        return (string) $fallback;
996
+    }
997
+
998
+    /**
999
+     * parses the app data array and enhanced the 'description' value
1000
+     *
1001
+     * @param array $data the app data
1002
+     * @param string $lang
1003
+     * @return array improved app data
1004
+     */
1005
+    public static function parseAppInfo(array $data, $lang = null): array {
1006
+        if ($lang && isset($data['name']) && is_array($data['name'])) {
1007
+            $data['name'] = self::findBestL10NOption($data['name'], $lang);
1008
+        }
1009
+        if ($lang && isset($data['summary']) && is_array($data['summary'])) {
1010
+            $data['summary'] = self::findBestL10NOption($data['summary'], $lang);
1011
+        }
1012
+        if ($lang && isset($data['description']) && is_array($data['description'])) {
1013
+            $data['description'] = trim(self::findBestL10NOption($data['description'], $lang));
1014
+        } elseif (isset($data['description']) && is_string($data['description'])) {
1015
+            $data['description'] = trim($data['description']);
1016
+        } else {
1017
+            $data['description'] = '';
1018
+        }
1019
+
1020
+        return $data;
1021
+    }
1022
+
1023
+    /**
1024
+     * @param \OCP\IConfig $config
1025
+     * @param \OCP\IL10N $l
1026
+     * @param array $info
1027
+     * @throws \Exception
1028
+     */
1029
+    public static function checkAppDependencies(\OCP\IConfig $config, \OCP\IL10N $l, array $info, bool $ignoreMax) {
1030
+        $dependencyAnalyzer = new DependencyAnalyzer(new Platform($config), $l);
1031
+        $missing = $dependencyAnalyzer->analyze($info, $ignoreMax);
1032
+        if (!empty($missing)) {
1033
+            $missingMsg = implode(PHP_EOL, $missing);
1034
+            throw new \Exception(
1035
+                $l->t('App "%1$s" cannot be installed because the following dependencies are not fulfilled: %2$s',
1036
+                    [$info['name'], $missingMsg]
1037
+                )
1038
+            );
1039
+        }
1040
+    }
1041 1041
 }
Please login to merge, or discard this patch.