Blame view

sources/lib/private/app.php 33.2 KB
03e52840d   Kload   Init
1
2
3
4
5
  <?php
  /**
   * ownCloud
   *
   * @author Frank Karlitschek
6d9380f96   Cédric Dupont   Update sources OC...
6
7
   * @copyright 2012 Frank Karlitschek <frank@owncloud.org>
   *
03e52840d   Kload   Init
8
   * @author Jakob Sack
6d9380f96   Cédric Dupont   Update sources OC...
9
10
11
12
   * @copyright 2012 Jakob Sack <mail@jakobsack.de>
   *
   * @author Georg Ehrke
   * @copyright 2014 Georg Ehrke <georg@ownCloud.com>
03e52840d   Kload   Init
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   *
   * This library is free software; you can redistribute it and/or
   * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
   * License as published by the Free Software Foundation; either
   * version 3 of the License, or any later version.
   *
   * This library is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
   *
   * You should have received a copy of the GNU Affero General Public
   * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
   *
   */
  
  /**
   * This class manages the apps. It allows them to register and integrate in the
6d9380f96   Cédric Dupont   Update sources OC...
31
   * ownCloud ecosystem. Furthermore, this class is responsible for installing,
03e52840d   Kload   Init
32
33
   * upgrading and removing apps.
   */
6d9380f96   Cédric Dupont   Update sources OC...
34
  class OC_App {
03e52840d   Kload   Init
35
36
37
38
39
40
41
42
43
44
  	static private $settingsForms = array();
  	static private $adminForms = array();
  	static private $personalForms = array();
  	static private $appInfo = array();
  	static private $appTypes = array();
  	static private $loadedApps = array();
  	static private $checkedApps = array();
  	static private $altLogin = array();
  
  	/**
6d9380f96   Cédric Dupont   Update sources OC...
45
46
  	 * clean the appId
  	 * @param string|boolean $app AppId that needs to be cleaned
03e52840d   Kload   Init
47
48
49
50
51
52
53
  	 * @return string
  	 */
  	public static function cleanAppId($app) {
  		return str_replace(array('\0', '/', '\\', '..'), '', $app);
  	}
  
  	/**
6d9380f96   Cédric Dupont   Update sources OC...
54
  	 * loads all apps
03e52840d   Kload   Init
55
56
57
  	 * @param array $types
  	 * @return bool
  	 *
6d9380f96   Cédric Dupont   Update sources OC...
58
  	 * This function walks through the ownCloud directory and loads all apps
03e52840d   Kload   Init
59
60
61
62
63
  	 * it can find. A directory contains an app if the file /appinfo/app.php
  	 * exists.
  	 *
  	 * if $types is set, only apps of those types will be loaded
  	 */
6d9380f96   Cédric Dupont   Update sources OC...
64
  	public static function loadApps($types = null) {
03e52840d   Kload   Init
65
66
67
68
  		// Load the enabled apps here
  		$apps = self::getEnabledApps();
  		// prevent app.php from printing output
  		ob_start();
6d9380f96   Cédric Dupont   Update sources OC...
69
70
  		foreach ($apps as $app) {
  			if ((is_null($types) or self::isType($app, $types)) && !in_array($app, self::$loadedApps)) {
03e52840d   Kload   Init
71
  				self::$loadedApps[] = $app;
6d9380f96   Cédric Dupont   Update sources OC...
72
  				self::loadApp($app);
03e52840d   Kload   Init
73
74
75
  			}
  		}
  		ob_end_clean();
03e52840d   Kload   Init
76
77
78
79
80
  		return true;
  	}
  
  	/**
  	 * load a single app
6d9380f96   Cédric Dupont   Update sources OC...
81
  	 *
03e52840d   Kload   Init
82
83
84
  	 * @param string $app
  	 */
  	public static function loadApp($app) {
6d9380f96   Cédric Dupont   Update sources OC...
85
  		if (is_file(self::getAppPath($app) . '/appinfo/app.php')) {
03e52840d   Kload   Init
86
  			self::checkUpgrade($app);
6d9380f96   Cédric Dupont   Update sources OC...
87
  			require_once $app . '/appinfo/app.php';
03e52840d   Kload   Init
88
89
90
91
92
  		}
  	}
  
  	/**
  	 * check if an app is of a specific type
6d9380f96   Cédric Dupont   Update sources OC...
93
  	 *
03e52840d   Kload   Init
94
  	 * @param string $app
6d9380f96   Cédric Dupont   Update sources OC...
95
  	 * @param string|array $types
03e52840d   Kload   Init
96
97
98
  	 * @return bool
  	 */
  	public static function isType($app, $types) {
6d9380f96   Cédric Dupont   Update sources OC...
99
100
  		if (is_string($types)) {
  			$types = array($types);
03e52840d   Kload   Init
101
  		}
6d9380f96   Cédric Dupont   Update sources OC...
102
103
104
  		$appTypes = self::getAppTypes($app);
  		foreach ($types as $type) {
  			if (array_search($type, $appTypes) !== false) {
03e52840d   Kload   Init
105
106
107
108
109
110
111
112
  				return true;
  			}
  		}
  		return false;
  	}
  
  	/**
  	 * get the types of an app
6d9380f96   Cédric Dupont   Update sources OC...
113
  	 *
03e52840d   Kload   Init
114
115
116
117
118
  	 * @param string $app
  	 * @return array
  	 */
  	private static function getAppTypes($app) {
  		//load the cache
6d9380f96   Cédric Dupont   Update sources OC...
119
120
  		if (count(self::$appTypes) == 0) {
  			self::$appTypes = OC_Appconfig::getValues(false, 'types');
03e52840d   Kload   Init
121
  		}
6d9380f96   Cédric Dupont   Update sources OC...
122
  		if (isset(self::$appTypes[$app])) {
03e52840d   Kload   Init
123
  			return explode(',', self::$appTypes[$app]);
6d9380f96   Cédric Dupont   Update sources OC...
124
  		} else {
03e52840d   Kload   Init
125
126
127
128
129
130
131
132
  			return array();
  		}
  	}
  
  	/**
  	 * read app types from info.xml and cache them in the database
  	 */
  	public static function setAppTypes($app) {
6d9380f96   Cédric Dupont   Update sources OC...
133
  		$appData = self::getAppInfo($app);
03e52840d   Kload   Init
134

6d9380f96   Cédric Dupont   Update sources OC...
135
136
137
138
  		if (isset($appData['types'])) {
  			$appTypes = implode(',', $appData['types']);
  		} else {
  			$appTypes = '';
03e52840d   Kload   Init
139
140
141
142
143
144
145
  		}
  
  		OC_Appconfig::setValue($app, 'types', $appTypes);
  	}
  
  	/**
  	 * check if app is shipped
6d9380f96   Cédric Dupont   Update sources OC...
146
147
  	 *
  	 * @param string $appId the id of the app to check
03e52840d   Kload   Init
148
149
150
151
  	 * @return bool
  	 *
  	 * Check if an app that is installed is a shipped app or installed from the appstore.
  	 */
6d9380f96   Cédric Dupont   Update sources OC...
152
153
154
  	public static function isShipped($appId) {
  		$info = self::getAppInfo($appId);
  		if (isset($info['shipped']) && $info['shipped'] == 'true') {
03e52840d   Kload   Init
155
156
157
158
159
160
161
162
163
  			return true;
  		} else {
  			return false;
  		}
  	}
  
  	/**
  	 * get all enabled apps
  	 */
6d9380f96   Cédric Dupont   Update sources OC...
164
165
166
167
  	protected static $enabledAppsCache = array();
  
  	public static function getEnabledApps($forceRefresh = false) {
  		if (!OC_Config::getValue('installed', false)) {
03e52840d   Kload   Init
168
169
  			return array();
  		}
6d9380f96   Cédric Dupont   Update sources OC...
170
  		if (!$forceRefresh && !empty(self::$enabledAppsCache)) {
31b7f2792   Kload   Upgrade to ownclo...
171
172
  			return self::$enabledAppsCache;
  		}
6d9380f96   Cédric Dupont   Update sources OC...
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
  		$appConfig = \OC::$server->getAppConfig();
  		$appStatus = $appConfig->getValues(false, 'enabled');
  		$user = \OC_User::getUser();
  		foreach ($appStatus as $app => $enabled) {
  			if ($app === 'files') {
  				continue;
  			}
  			if ($enabled === 'yes') {
  				$apps[] = $app;
  			} else if ($enabled !== 'no') {
  				$groups = json_decode($enabled);
  				if (is_array($groups)) {
  					foreach ($groups as $group) {
  						if (\OC_Group::inGroup($user, $group)) {
  							$apps[] = $app;
  							break;
  						}
  					}
  				}
03e52840d   Kload   Init
192
193
  			}
  		}
6d9380f96   Cédric Dupont   Update sources OC...
194
195
196
197
198
199
200
201
  		sort($apps);
  		array_unshift($apps, 'files');
  		// Only cache the app list, when the user is logged in.
  		// Otherwise we cache the list with disabled apps, although
  		// the apps are enabled for the user after he logged in.
  		if ($user) {
  			self::$enabledAppsCache = $apps;
  		}
03e52840d   Kload   Init
202
203
204
205
  		return $apps;
  	}
  
  	/**
6d9380f96   Cédric Dupont   Update sources OC...
206
  	 * checks whether or not an app is enabled
03e52840d   Kload   Init
207
208
209
210
211
  	 * @param string $app app
  	 * @return bool
  	 *
  	 * This function checks whether or not an app is enabled.
  	 */
6d9380f96   Cédric Dupont   Update sources OC...
212
213
  	public static function isEnabled($app) {
  		if ('files' == $app) {
03e52840d   Kload   Init
214
215
  			return true;
  		}
31b7f2792   Kload   Upgrade to ownclo...
216
217
  		$enabledApps = self::getEnabledApps();
  		return in_array($app, $enabledApps);
03e52840d   Kload   Init
218
219
220
  	}
  
  	/**
6d9380f96   Cédric Dupont   Update sources OC...
221
  	 * enables an app
03e52840d   Kload   Init
222
  	 * @param mixed $app app
6d9380f96   Cédric Dupont   Update sources OC...
223
  	 * @param array $groups (optional) when set, only these groups will have access to the app
31b7f2792   Kload   Upgrade to ownclo...
224
225
  	 * @throws \Exception
  	 * @return void
03e52840d   Kload   Init
226
227
228
  	 *
  	 * This function set an app as enabled in appconfig.
  	 */
6d9380f96   Cédric Dupont   Update sources OC...
229
  	public static function enable($app, $groups = null) {
31b7f2792   Kload   Upgrade to ownclo...
230
  		self::$enabledAppsCache = array(); // flush
6d9380f96   Cédric Dupont   Update sources OC...
231
232
  		if (!OC_Installer::isInstalled($app)) {
  			$app = self::installApp($app);
03e52840d   Kload   Init
233
  		}
6d9380f96   Cédric Dupont   Update sources OC...
234
235
236
  
  		if (!is_null($groups)) {
  			OC_Appconfig::setValue($app, 'enabled', json_encode($groups));
03e52840d   Kload   Init
237
  		}else{
6d9380f96   Cédric Dupont   Update sources OC...
238
  			OC_Appconfig::setValue($app, 'enabled', 'yes');
03e52840d   Kload   Init
239
240
241
242
  		}
  	}
  
  	/**
6d9380f96   Cédric Dupont   Update sources OC...
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
  	 * @param string $app
  	 * @return int
  	 */
  	public static function downloadApp($app) {
  		$appData=OC_OCSClient::getApplication($app);
  		$download=OC_OCSClient::getApplicationDownload($app, 1);
  		if(isset($download['downloadlink']) and $download['downloadlink']!='') {
  			// Replace spaces in download link without encoding entire URL
  			$download['downloadlink'] = str_replace(' ', '%20', $download['downloadlink']);
  			$info = array('source'=>'http', 'href'=>$download['downloadlink'], 'appdata'=>$appData);
  			$app=OC_Installer::installApp($info);
  		}
  		return $app;
  	}
  
  	/**
  	 * @param string $app
03e52840d   Kload   Init
260
  	 * @return bool
6d9380f96   Cédric Dupont   Update sources OC...
261
262
263
264
265
266
267
268
269
270
  	 */
  	public static function removeApp($app) {
  		if (self::isShipped($app)) {
  			return false;
  		}
  
  		return OC_Installer::removeApp($app);
  	}
  
  	/**
03e52840d   Kload   Init
271
  	 * This function set an app as disabled in appconfig.
6d9380f96   Cédric Dupont   Update sources OC...
272
  	 * @param string $app app
03e52840d   Kload   Init
273
  	 */
6d9380f96   Cédric Dupont   Update sources OC...
274
  	public static function disable($app) {
31b7f2792   Kload   Upgrade to ownclo...
275
  		self::$enabledAppsCache = array(); // flush
03e52840d   Kload   Init
276
277
  		// check if app is a shipped app or not. if not delete
  		\OC_Hook::emit('OC_App', 'pre_disable', array('app' => $app));
6d9380f96   Cédric Dupont   Update sources OC...
278
  		OC_Appconfig::setValue($app, 'enabled', 'no' );
03e52840d   Kload   Init
279
280
281
  	}
  
  	/**
6d9380f96   Cédric Dupont   Update sources OC...
282
  	 * adds an entry to the navigation
31b7f2792   Kload   Upgrade to ownclo...
283
  	 * @param array $data array containing the data
03e52840d   Kload   Init
284
285
286
287
288
289
290
291
292
293
294
295
296
297
  	 * @return bool
  	 *
  	 * This function adds a new entry to the navigation visible to users. $data
  	 * is an associative array.
  	 * The following keys are required:
  	 *   - id: unique id for this entry ('addressbook_index')
  	 *   - href: link to the page
  	 *   - name: Human readable name ('Addressbook')
  	 *
  	 * The following keys are optional:
  	 *   - icon: path to the icon of the app
  	 *   - order: integer, that influences the position of your application in
  	 *     the navigation. Lower values come first.
  	 */
6d9380f96   Cédric Dupont   Update sources OC...
298
  	public static function addNavigationEntry($data) {
31b7f2792   Kload   Upgrade to ownclo...
299
  		OC::$server->getNavigationManager()->add($data);
03e52840d   Kload   Init
300
301
302
303
  		return true;
  	}
  
  	/**
6d9380f96   Cédric Dupont   Update sources OC...
304
  	 * marks a navigation entry as active
03e52840d   Kload   Init
305
306
307
308
309
310
311
  	 * @param string $id id of the entry
  	 * @return bool
  	 *
  	 * This function sets a navigation entry as active and removes the 'active'
  	 * property from all other entries. The templates can use this for
  	 * highlighting the current position of the user.
  	 */
6d9380f96   Cédric Dupont   Update sources OC...
312
  	public static function setActiveNavigationEntry($id) {
31b7f2792   Kload   Upgrade to ownclo...
313
  		OC::$server->getNavigationManager()->setActiveEntry($id);
03e52840d   Kload   Init
314
315
316
317
  		return true;
  	}
  
  	/**
6d9380f96   Cédric Dupont   Update sources OC...
318
  	 * Get the navigation entries for the $app
03e52840d   Kload   Init
319
  	 * @param string $app app
6d9380f96   Cédric Dupont   Update sources OC...
320
  	 * @return array an array of the $data added with addNavigationEntry
31b7f2792   Kload   Upgrade to ownclo...
321
322
  	 *
  	 * Warning: destroys the existing entries
03e52840d   Kload   Init
323
324
  	 */
  	public static function getAppNavigationEntries($app) {
6d9380f96   Cédric Dupont   Update sources OC...
325
  		if (is_file(self::getAppPath($app) . '/appinfo/app.php')) {
31b7f2792   Kload   Upgrade to ownclo...
326
  			OC::$server->getNavigationManager()->clear();
6d9380f96   Cédric Dupont   Update sources OC...
327
  			require $app . '/appinfo/app.php';
31b7f2792   Kload   Upgrade to ownclo...
328
  			return OC::$server->getNavigationManager()->getAll();
03e52840d   Kload   Init
329
330
331
332
333
  		}
  		return array();
  	}
  
  	/**
6d9380f96   Cédric Dupont   Update sources OC...
334
  	 * gets the active Menu entry
03e52840d   Kload   Init
335
336
337
338
339
340
  	 * @return string id or empty string
  	 *
  	 * This function returns the id of the active navigation entry (set by
  	 * setActiveNavigationEntry
  	 */
  	public static function getActiveNavigationEntry() {
31b7f2792   Kload   Upgrade to ownclo...
341
  		return OC::$server->getNavigationManager()->getActiveEntry();
03e52840d   Kload   Init
342
343
344
  	}
  
  	/**
6d9380f96   Cédric Dupont   Update sources OC...
345
346
  	 * Returns the Settings Navigation
  	 * @return string
03e52840d   Kload   Init
347
348
349
350
351
  	 *
  	 * This function returns an array containing all settings pages added. The
  	 * entries are sorted by the key 'order' ascending.
  	 */
  	public static function getSettingsNavigation() {
6d9380f96   Cédric Dupont   Update sources OC...
352
  		$l = OC_L10N::get('lib');
03e52840d   Kload   Init
353
354
355
  
  		$settings = array();
  		// by default, settings only contain the help menu
6d9380f96   Cédric Dupont   Update sources OC...
356
357
358
  		if (OC_Util::getEditionString() === '' &&
  			OC_Config::getValue('knowledgebaseenabled', true) == true
  		) {
03e52840d   Kload   Init
359
360
361
362
  			$settings = array(
  				array(
  					"id" => "help",
  					"order" => 1000,
6d9380f96   Cédric Dupont   Update sources OC...
363
  					"href" => OC_Helper::linkToRoute("settings_help"),
03e52840d   Kload   Init
364
  					"name" => $l->t("Help"),
6d9380f96   Cédric Dupont   Update sources OC...
365
  					"icon" => OC_Helper::imagePath("settings", "help.svg")
03e52840d   Kload   Init
366
367
368
369
370
371
372
373
374
375
  				)
  			);
  		}
  
  		// if the user is logged-in
  		if (OC_User::isLoggedIn()) {
  			// personal menu
  			$settings[] = array(
  				"id" => "personal",
  				"order" => 1,
6d9380f96   Cédric Dupont   Update sources OC...
376
  				"href" => OC_Helper::linkToRoute("settings_personal"),
03e52840d   Kload   Init
377
  				"name" => $l->t("Personal"),
6d9380f96   Cédric Dupont   Update sources OC...
378
  				"icon" => OC_Helper::imagePath("settings", "personal.svg")
03e52840d   Kload   Init
379
380
381
  			);
  
  			// if there are some settings forms
6d9380f96   Cédric Dupont   Update sources OC...
382
  			if (!empty(self::$settingsForms)) {
03e52840d   Kload   Init
383
  				// settings menu
6d9380f96   Cédric Dupont   Update sources OC...
384
  				$settings[] = array(
03e52840d   Kload   Init
385
386
  					"id" => "settings",
  					"order" => 1000,
6d9380f96   Cédric Dupont   Update sources OC...
387
  					"href" => OC_Helper::linkToRoute("settings_settings"),
03e52840d   Kload   Init
388
  					"name" => $l->t("Settings"),
6d9380f96   Cédric Dupont   Update sources OC...
389
  					"icon" => OC_Helper::imagePath("settings", "settings.svg")
03e52840d   Kload   Init
390
391
392
393
  				);
  			}
  
  			//SubAdmins are also allowed to access user management
6d9380f96   Cédric Dupont   Update sources OC...
394
  			if (OC_SubAdmin::isSubAdmin(OC_User::getUser())) {
03e52840d   Kload   Init
395
396
397
398
  				// admin users menu
  				$settings[] = array(
  					"id" => "core_users",
  					"order" => 2,
6d9380f96   Cédric Dupont   Update sources OC...
399
  					"href" => OC_Helper::linkToRoute("settings_users"),
03e52840d   Kload   Init
400
  					"name" => $l->t("Users"),
6d9380f96   Cédric Dupont   Update sources OC...
401
  					"icon" => OC_Helper::imagePath("settings", "users.svg")
03e52840d   Kload   Init
402
403
404
405
406
  				);
  			}
  
  
  			// if the user is an admin
6d9380f96   Cédric Dupont   Update sources OC...
407
  			if (OC_User::isAdminUser(OC_User::getUser())) {
31b7f2792   Kload   Upgrade to ownclo...
408
  				// admin settings
6d9380f96   Cédric Dupont   Update sources OC...
409
  				$settings[] = array(
03e52840d   Kload   Init
410
411
  					"id" => "admin",
  					"order" => 1000,
6d9380f96   Cédric Dupont   Update sources OC...
412
  					"href" => OC_Helper::linkToRoute("settings_admin"),
03e52840d   Kload   Init
413
  					"name" => $l->t("Admin"),
6d9380f96   Cédric Dupont   Update sources OC...
414
  					"icon" => OC_Helper::imagePath("settings", "admin.svg")
03e52840d   Kload   Init
415
416
417
418
419
420
421
  				);
  			}
  		}
  
  		$navigation = self::proceedNavigation($settings);
  		return $navigation;
  	}
31b7f2792   Kload   Upgrade to ownclo...
422
  	// This is private as well. It simply works, so don't ask for more details
6d9380f96   Cédric Dupont   Update sources OC...
423
424
425
426
427
428
429
  	private static function proceedNavigation($list) {
  		$activeApp = OC::$server->getNavigationManager()->getActiveEntry();
  		foreach ($list as &$navEntry) {
  			if ($navEntry['id'] == $activeApp) {
  				$navEntry['active'] = true;
  			} else {
  				$navEntry['active'] = false;
03e52840d   Kload   Init
430
  			}
6d9380f96   Cédric Dupont   Update sources OC...
431
432
  		}
  		unset($navEntry);
03e52840d   Kload   Init
433

6d9380f96   Cédric Dupont   Update sources OC...
434
  		usort($list, create_function('$a, $b', 'if( $a["order"] == $b["order"] ) {return 0;}elseif( $a["order"] < $b["order"] ) {return -1;}else{return 1;}'));
03e52840d   Kload   Init
435
436
437
438
439
440
  
  		return $list;
  	}
  
  	/**
  	 * Get the path where to install apps
6d9380f96   Cédric Dupont   Update sources OC...
441
442
  	 *
  	 * @return string
03e52840d   Kload   Init
443
444
  	 */
  	public static function getInstallPath() {
6d9380f96   Cédric Dupont   Update sources OC...
445
  		if (OC_Config::getValue('appstoreenabled', true) == false) {
03e52840d   Kload   Init
446
447
  			return false;
  		}
6d9380f96   Cédric Dupont   Update sources OC...
448
449
  		foreach (OC::$APPSROOTS as $dir) {
  			if (isset($dir['writable']) && $dir['writable'] === true) {
03e52840d   Kload   Init
450
451
452
453
454
455
456
  				return $dir['path'];
  			}
  		}
  
  		OC_Log::write('core', 'No application directories are marked as writable.', OC_Log::ERROR);
  		return null;
  	}
6d9380f96   Cédric Dupont   Update sources OC...
457
458
459
460
461
462
  	/**
  	 * search for an app in all app-directories
  	 * @param $appId
  	 * @return mixed (bool|string)
  	 */
  	protected static function findAppInDirectories($appId) {
03e52840d   Kload   Init
463
  		static $app_dir = array();
6d9380f96   Cédric Dupont   Update sources OC...
464
465
466
  
  		if (isset($app_dir[$appId])) {
  			return $app_dir[$appId];
03e52840d   Kload   Init
467
  		}
6d9380f96   Cédric Dupont   Update sources OC...
468
469
  
  		$possibleApps = array();
03e52840d   Kload   Init
470
  		foreach(OC::$APPSROOTS as $dir) {
6d9380f96   Cédric Dupont   Update sources OC...
471
472
  			if(file_exists($dir['path'] . '/' . $appId)) {
  				$possibleApps[] = $dir;
03e52840d   Kload   Init
473
474
  			}
  		}
6d9380f96   Cédric Dupont   Update sources OC...
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
  
  		if (empty($possibleApps)) {
  			return false;
  		} elseif(count($possibleApps) === 1) {
  			$dir = array_shift($possibleApps);
  			$app_dir[$appId] = $dir;
  			return $dir;
  		} else {
  			$versionToLoad = array();
  			foreach($possibleApps as $possibleApp) {
  				$version = self::getAppVersionByPath($possibleApp['path']);
  				if (empty($versionToLoad) || version_compare($version, $versionToLoad['version'], '>')) {
  					$versionToLoad = array(
  						'dir' => $possibleApp,
  						'version' => $version,
  					);
  				}
  			}
  			$app_dir[$appId] = $versionToLoad['dir'];
  			return $versionToLoad['dir'];
  			//TODO - write test
  		}
03e52840d   Kload   Init
497
  	}
6d9380f96   Cédric Dupont   Update sources OC...
498

03e52840d   Kload   Init
499
  	/**
6d9380f96   Cédric Dupont   Update sources OC...
500
501
502
503
504
505
506
507
508
509
510
511
512
  	 * Get the directory for the given app.
  	 * If the app is defined in multiple directories, the first one is taken. (false if not found)
  	 *
  	 * @param string $appId
  	 * @return string|false
  	 */
  	public static function getAppPath($appId) {
  		if ($appId === null || trim($appId) === '') {
  			return false;
  		}
  
  		if (($dir = self::findAppInDirectories($appId)) != false) {
  			return $dir['path'] . '/' . $appId;
03e52840d   Kload   Init
513
514
515
  		}
  		return false;
  	}
6d9380f96   Cédric Dupont   Update sources OC...
516
517
518
519
520
521
522
523
524
525
526
  
  	/**
  	 * check if an app's directory is writable
  	 *
  	 * @param string $appId
  	 * @return bool
  	 */
  	public static function isAppDirWritable($appId) {
  		$path = self::getAppPath($appId);
  		return ($path !== false) ? is_writable($path) : false;
  	}
03e52840d   Kload   Init
527
  	/**
6d9380f96   Cédric Dupont   Update sources OC...
528
529
530
531
532
533
534
535
536
  	 * Get the path for the given app on the access
  	 * If the app is defined in multiple directories, the first one is taken. (false if not found)
  	 *
  	 * @param string $appId
  	 * @return string|false
  	 */
  	public static function getAppWebPath($appId) {
  		if (($dir = self::findAppInDirectories($appId)) != false) {
  			return OC::$WEBROOT . $dir['url'] . '/' . $appId;
03e52840d   Kload   Init
537
538
539
540
541
542
  		}
  		return false;
  	}
  
  	/**
  	 * get the last version of the app, either from appinfo/version or from appinfo/info.xml
6d9380f96   Cédric Dupont   Update sources OC...
543
544
545
546
547
548
549
550
551
552
553
554
555
  	 *
  	 * @param string $appId
  	 * @return string
  	 */
  	public static function getAppVersion($appId) {
  		$file = self::getAppPath($appId);
  		return ($file !== false) ? self::getAppVersionByPath($file) : '0';
  	}
  
  	/**
  	 * get app's version based on it's path
  	 * @param string $path
  	 * @return string
03e52840d   Kload   Init
556
  	 */
6d9380f96   Cédric Dupont   Update sources OC...
557
558
559
560
561
  	public static function getAppVersionByPath($path) {
  		$versionFile = $path . '/appinfo/version';
  		$infoFile = $path . '/appinfo/info.xml';
  		if(is_file($versionFile)) {
  			return trim(file_get_contents($versionFile));
03e52840d   Kload   Init
562
  		}else{
6d9380f96   Cédric Dupont   Update sources OC...
563
564
  			$appData = self::getAppInfo($infoFile, true);
  			return isset($appData['version']) ? $appData['version'] : '';
03e52840d   Kload   Init
565
566
  		}
  	}
6d9380f96   Cédric Dupont   Update sources OC...
567

03e52840d   Kload   Init
568
  	/**
6d9380f96   Cédric Dupont   Update sources OC...
569
570
571
  	 * Read all app metadata from the info.xml file
  	 *
  	 * @param string $appId id of the app or the path of the info.xml file
03e52840d   Kload   Init
572
  	 * @param boolean $path (optional)
6d9380f96   Cédric Dupont   Update sources OC...
573
  	 * @return array|null
03e52840d   Kload   Init
574
  	 * @note all data is read from info.xml, not just pre-defined fields
6d9380f96   Cédric Dupont   Update sources OC...
575
576
577
578
579
580
581
  	 */
  	public static function getAppInfo($appId, $path = false) {
  		if ($path) {
  			$file = $appId;
  		} else {
  			if (isset(self::$appInfo[$appId])) {
  				return self::$appInfo[$appId];
03e52840d   Kload   Init
582
  			}
6d9380f96   Cédric Dupont   Update sources OC...
583
584
585
586
587
  			$file = self::getAppPath($appId) . '/appinfo/info.xml';
  		}
  		$data = array();
  		if (!file_exists($file)) {
  			return null;
03e52840d   Kload   Init
588
  		}
6d9380f96   Cédric Dupont   Update sources OC...
589
590
  		$content = @file_get_contents($file);
  		if (!$content) {
03e52840d   Kload   Init
591
592
593
  			return null;
  		}
  		$xml = new SimpleXMLElement($content);
6d9380f96   Cédric Dupont   Update sources OC...
594
595
596
597
  		$data['info'] = array();
  		$data['remote'] = array();
  		$data['public'] = array();
  		foreach ($xml->children() as $child) {
03e52840d   Kload   Init
598
599
600
  			/**
  			 * @var $child SimpleXMLElement
  			 */
6d9380f96   Cédric Dupont   Update sources OC...
601
602
  			if ($child->getName() == 'remote') {
  				foreach ($child->children() as $remote) {
03e52840d   Kload   Init
603
604
605
  					/**
  					 * @var $remote SimpleXMLElement
  					 */
6d9380f96   Cédric Dupont   Update sources OC...
606
  					$data['remote'][$remote->getName()] = (string)$remote;
03e52840d   Kload   Init
607
  				}
6d9380f96   Cédric Dupont   Update sources OC...
608
609
  			} elseif ($child->getName() == 'public') {
  				foreach ($child->children() as $public) {
03e52840d   Kload   Init
610
611
612
  					/**
  					 * @var $public SimpleXMLElement
  					 */
6d9380f96   Cédric Dupont   Update sources OC...
613
  					$data['public'][$public->getName()] = (string)$public;
03e52840d   Kload   Init
614
  				}
6d9380f96   Cédric Dupont   Update sources OC...
615
616
617
  			} elseif ($child->getName() == 'types') {
  				$data['types'] = array();
  				foreach ($child->children() as $type) {
03e52840d   Kload   Init
618
619
620
  					/**
  					 * @var $type SimpleXMLElement
  					 */
6d9380f96   Cédric Dupont   Update sources OC...
621
  					$data['types'][] = $type->getName();
03e52840d   Kload   Init
622
  				}
6d9380f96   Cédric Dupont   Update sources OC...
623
624
625
626
627
628
  			} elseif ($child->getName() == 'description') {
  				$xml = (string)$child->asXML();
  				$data[$child->getName()] = substr($xml, 13, -14); //script <description> tags
  			} elseif ($child->getName() == 'documentation') {
  				foreach ($child as $subChild) {
  					$data["documentation"][$subChild->getName()] = (string)$subChild;
a293d369c   Kload   Update sources to...
629
  				}
6d9380f96   Cédric Dupont   Update sources OC...
630
631
  			} else {
  				$data[$child->getName()] = (string)$child;
03e52840d   Kload   Init
632
633
  			}
  		}
6d9380f96   Cédric Dupont   Update sources OC...
634
  		self::$appInfo[$appId] = $data;
03e52840d   Kload   Init
635
636
637
638
639
  
  		return $data;
  	}
  
  	/**
6d9380f96   Cédric Dupont   Update sources OC...
640
  	 * Returns the navigation
03e52840d   Kload   Init
641
642
643
644
645
646
647
648
  	 * @return array
  	 *
  	 * This function returns an array containing all entries added. The
  	 * entries are sorted by the key 'order' ascending. Additional to the keys
  	 * given for each app the following keys exist:
  	 *   - active: boolean, signals if the user is on this navigation entry
  	 */
  	public static function getNavigation() {
31b7f2792   Kload   Upgrade to ownclo...
649
  		$entries = OC::$server->getNavigationManager()->getAll();
6d9380f96   Cédric Dupont   Update sources OC...
650
  		$navigation = self::proceedNavigation($entries);
03e52840d   Kload   Init
651
652
653
654
655
  		return $navigation;
  	}
  
  	/**
  	 * get the id of loaded app
6d9380f96   Cédric Dupont   Update sources OC...
656
  	 *
03e52840d   Kload   Init
657
658
659
  	 * @return string
  	 */
  	public static function getCurrentApp() {
6d9380f96   Cédric Dupont   Update sources OC...
660
661
  		$script = substr(OC_Request::scriptName(), strlen(OC::$WEBROOT) + 1);
  		$topFolder = substr($script, 0, strpos($script, '/'));
03e52840d   Kload   Init
662
663
664
  		if (empty($topFolder)) {
  			$path_info = OC_Request::getPathInfo();
  			if ($path_info) {
6d9380f96   Cédric Dupont   Update sources OC...
665
  				$topFolder = substr($path_info, 1, strpos($path_info, '/', 1) - 1);
03e52840d   Kload   Init
666
667
  			}
  		}
6d9380f96   Cédric Dupont   Update sources OC...
668
669
670
671
  		if ($topFolder == 'apps') {
  			$length = strlen($topFolder);
  			return substr($script, $length + 1, strpos($script, '/', $length + 1) - $length - 1);
  		} else {
03e52840d   Kload   Init
672
673
674
  			return $topFolder;
  		}
  	}
03e52840d   Kload   Init
675
676
677
678
  	/**
  	 * get the forms for either settings, admin or personal
  	 */
  	public static function getForms($type) {
6d9380f96   Cédric Dupont   Update sources OC...
679
680
  		$forms = array();
  		switch ($type) {
03e52840d   Kload   Init
681
  			case 'settings':
6d9380f96   Cédric Dupont   Update sources OC...
682
  				$source = self::$settingsForms;
03e52840d   Kload   Init
683
684
  				break;
  			case 'admin':
6d9380f96   Cédric Dupont   Update sources OC...
685
  				$source = self::$adminForms;
03e52840d   Kload   Init
686
687
  				break;
  			case 'personal':
6d9380f96   Cédric Dupont   Update sources OC...
688
  				$source = self::$personalForms;
03e52840d   Kload   Init
689
690
691
692
  				break;
  			default:
  				return array();
  		}
6d9380f96   Cédric Dupont   Update sources OC...
693
694
  		foreach ($source as $form) {
  			$forms[] = include $form;
03e52840d   Kload   Init
695
696
697
698
699
700
701
702
  		}
  		return $forms;
  	}
  
  	/**
  	 * register a settings form to be shown
  	 */
  	public static function registerSettings($app, $page) {
6d9380f96   Cédric Dupont   Update sources OC...
703
  		self::$settingsForms[] = $app . '/' . $page . '.php';
03e52840d   Kload   Init
704
705
706
707
  	}
  
  	/**
  	 * register an admin form to be shown
6d9380f96   Cédric Dupont   Update sources OC...
708
709
710
  	 *
  	 * @param string $app
  	 * @param string $page
03e52840d   Kload   Init
711
712
  	 */
  	public static function registerAdmin($app, $page) {
6d9380f96   Cédric Dupont   Update sources OC...
713
  		self::$adminForms[] = $app . '/' . $page . '.php';
03e52840d   Kload   Init
714
715
716
717
718
719
  	}
  
  	/**
  	 * register a personal form to be shown
  	 */
  	public static function registerPersonal($app, $page) {
6d9380f96   Cédric Dupont   Update sources OC...
720
  		self::$personalForms[] = $app . '/' . $page . '.php';
03e52840d   Kload   Init
721
722
723
724
725
726
727
728
729
730
731
  	}
  
  	public static function registerLogIn($entry) {
  		self::$altLogin[] = $entry;
  	}
  
  	public static function getAlternativeLogIns() {
  		return self::$altLogin;
  	}
  
  	/**
6d9380f96   Cédric Dupont   Update sources OC...
732
733
  	 * get a list of all apps in the apps folder
  	 * @return array an array of app names (string IDs)
03e52840d   Kload   Init
734
735
736
  	 * @todo: change the name of this method to getInstalledApps, which is more accurate
  	 */
  	public static function getAllApps() {
6d9380f96   Cédric Dupont   Update sources OC...
737
  		$apps = array();
03e52840d   Kload   Init
738

6d9380f96   Cédric Dupont   Update sources OC...
739
740
741
  		foreach (OC::$APPSROOTS as $apps_dir) {
  			if (!is_readable($apps_dir['path'])) {
  				OC_Log::write('core', 'unable to read app folder : ' . $apps_dir['path'], OC_Log::WARN);
03e52840d   Kload   Init
742
743
  				continue;
  			}
6d9380f96   Cédric Dupont   Update sources OC...
744
  			$dh = opendir($apps_dir['path']);
03e52840d   Kload   Init
745

6d9380f96   Cédric Dupont   Update sources OC...
746
  			if (is_resource($dh)) {
03e52840d   Kload   Init
747
  				while (($file = readdir($dh)) !== false) {
6d9380f96   Cédric Dupont   Update sources OC...
748
  					if ($file[0] != '.' and is_file($apps_dir['path'] . '/' . $file . '/appinfo/app.php')) {
03e52840d   Kload   Init
749
750
751
752
753
754
755
756
757
758
759
760
761
762
  
  						$apps[] = $file;
  
  					}
  
  				}
  			}
  
  		}
  
  		return $apps;
  	}
  
  	/**
6d9380f96   Cédric Dupont   Update sources OC...
763
  	 * Lists all apps, this is used in apps.php
03e52840d   Kload   Init
764
765
766
767
768
769
770
  	 * @return array
  	 */
  	public static function listAllApps() {
  		$installedApps = OC_App::getAllApps();
  
  		//TODO which apps do we want to blacklist and how do we integrate
  		// blacklisting with the multi apps folder feature?
6d9380f96   Cédric Dupont   Update sources OC...
771
  		$blacklist = array('files'); //we don't want to show configuration for these
03e52840d   Kload   Init
772
  		$appList = array();
6d9380f96   Cédric Dupont   Update sources OC...
773
774
  		foreach ($installedApps as $app) {
  			if (array_search($app, $blacklist) === false) {
03e52840d   Kload   Init
775

6d9380f96   Cédric Dupont   Update sources OC...
776
  				$info = OC_App::getAppInfo($app);
03e52840d   Kload   Init
777
778
  
  				if (!isset($info['name'])) {
6d9380f96   Cédric Dupont   Update sources OC...
779
  					OC_Log::write('core', 'App id "' . $app . '" has no name in appinfo', OC_Log::ERROR);
03e52840d   Kload   Init
780
781
  					continue;
  				}
6d9380f96   Cédric Dupont   Update sources OC...
782
783
784
  				$enabled = OC_Appconfig::getValue($app, 'enabled', 'no');
  				$info['groups'] = null;
  				if ($enabled === 'yes') {
03e52840d   Kload   Init
785
  					$active = true;
6d9380f96   Cédric Dupont   Update sources OC...
786
  				} else if($enabled === 'no') {
03e52840d   Kload   Init
787
  					$active = false;
6d9380f96   Cédric Dupont   Update sources OC...
788
789
790
  				} else {
  					$active = true;
  					$info['groups'] = $enabled;
03e52840d   Kload   Init
791
792
793
  				}
  
  				$info['active'] = $active;
6d9380f96   Cédric Dupont   Update sources OC...
794
795
796
797
798
  				if(isset($info['shipped']) and ($info['shipped'] == 'true')) {
  					$info['internal'] = true;
  					$info['internallabel'] = 'Internal App';
  					$info['internalclass'] = '';
  					$info['removable'] = false;
03e52840d   Kload   Init
799
  				} else {
6d9380f96   Cédric Dupont   Update sources OC...
800
801
802
803
  					$info['internal'] = false;
  					$info['internallabel'] = '3rd Party';
  					$info['internalclass'] = 'externalapp';
  					$info['removable'] = true;
03e52840d   Kload   Init
804
  				}
6d9380f96   Cédric Dupont   Update sources OC...
805
  				$info['update'] = OC_Installer::isUpdateAvailable($app);
03e52840d   Kload   Init
806
807
808
809
810
811
  				$info['preview'] = OC_Helper::imagePath('settings', 'trans.png');
  				$info['version'] = OC_App::getAppVersion($app);
  				$appList[] = $info;
  			}
  		}
  		$remoteApps = OC_App::getAppstoreApps();
6d9380f96   Cédric Dupont   Update sources OC...
812
  		if ($remoteApps) {
03e52840d   Kload   Init
813
  			// Remove duplicates
6d9380f96   Cédric Dupont   Update sources OC...
814
815
816
817
818
  			foreach ($appList as $app) {
  				foreach ($remoteApps AS $key => $remote) {
  					if ($app['name'] === $remote['name'] ||
  						$app['ocsid'] ===  $remote['id']) {
  						unset($remoteApps[$key]);
03e52840d   Kload   Init
819
820
821
  					}
  				}
  			}
6d9380f96   Cédric Dupont   Update sources OC...
822
  			$combinedApps = array_merge($appList, $remoteApps);
03e52840d   Kload   Init
823
824
825
  		} else {
  			$combinedApps = $appList;
  		}
6d9380f96   Cédric Dupont   Update sources OC...
826
827
  		// bring the apps into the right order with a custom sort function
  		usort($combinedApps, function ($a, $b) {
31b7f2792   Kload   Upgrade to ownclo...
828

6d9380f96   Cédric Dupont   Update sources OC...
829
830
831
832
  			// priority 1: active
  			if ($a['active'] != $b['active']) {
  				return $b['active'] - $a['active'];
  			}
31b7f2792   Kload   Upgrade to ownclo...
833

6d9380f96   Cédric Dupont   Update sources OC...
834
835
836
837
838
839
  			// priority 2: shipped
  			$aShipped = (array_key_exists('shipped', $a) && $a['shipped'] === 'true') ? 1 : 0;
  			$bShipped = (array_key_exists('shipped', $b) && $b['shipped'] === 'true') ? 1 : 0;
  			if ($aShipped !== $bShipped) {
  				return ($bShipped - $aShipped);
  			}
31b7f2792   Kload   Upgrade to ownclo...
840

6d9380f96   Cédric Dupont   Update sources OC...
841
842
843
844
845
846
  			// priority 3: recommended
  			if ($a['internalclass'] != $b['internalclass']) {
  				$aTemp = ($a['internalclass'] == 'recommendedapp' ? 1 : 0);
  				$bTemp = ($b['internalclass'] == 'recommendedapp' ? 1 : 0);
  				return ($bTemp - $aTemp);
  			}
31b7f2792   Kload   Upgrade to ownclo...
847

6d9380f96   Cédric Dupont   Update sources OC...
848
849
  			// priority 4: alphabetical
  			return strcasecmp($a['name'], $b['name']);
31b7f2792   Kload   Upgrade to ownclo...
850

6d9380f96   Cédric Dupont   Update sources OC...
851
  		});
31b7f2792   Kload   Upgrade to ownclo...
852

6d9380f96   Cédric Dupont   Update sources OC...
853
  		return $combinedApps;
31b7f2792   Kload   Upgrade to ownclo...
854
855
856
  	}
  
  	/**
6d9380f96   Cédric Dupont   Update sources OC...
857
  	 * get a list of all apps on apps.owncloud.com
03e52840d   Kload   Init
858
859
860
  	 * @return array, multi-dimensional array of apps.
  	 *     Keys: id, name, type, typename, personid, license, detailpage, preview, changed, description
  	 */
6d9380f96   Cédric Dupont   Update sources OC...
861
  	public static function getAppstoreApps($filter = 'approved') {
03e52840d   Kload   Init
862
  		$categoryNames = OC_OCSClient::getCategories();
6d9380f96   Cédric Dupont   Update sources OC...
863
  		if (is_array($categoryNames)) {
03e52840d   Kload   Init
864
  			// Check that categories of apps were retrieved correctly
6d9380f96   Cédric Dupont   Update sources OC...
865
  			if (!$categories = array_keys($categoryNames)) {
03e52840d   Kload   Init
866
867
868
869
  				return false;
  			}
  
  			$page = 0;
6d9380f96   Cédric Dupont   Update sources OC...
870
  			$remoteApps = OC_OCSClient::getApplications($categories, $page, $filter);
03e52840d   Kload   Init
871
872
  			$app1 = array();
  			$i = 0;
6d9380f96   Cédric Dupont   Update sources OC...
873
  			foreach ($remoteApps as $app) {
03e52840d   Kload   Init
874
875
876
877
878
  				$app1[$i] = $app;
  				$app1[$i]['author'] = $app['personid'];
  				$app1[$i]['ocs_id'] = $app['id'];
  				$app1[$i]['internal'] = $app1[$i]['active'] = 0;
  				$app1[$i]['update'] = false;
6d9380f96   Cédric Dupont   Update sources OC...
879
880
881
  				$app1[$i]['groups'] = false;
  				$app1[$i]['removable'] = false;
  				if ($app['label'] == 'recommended') {
03e52840d   Kload   Init
882
883
  					$app1[$i]['internallabel'] = 'Recommended';
  					$app1[$i]['internalclass'] = 'recommendedapp';
6d9380f96   Cédric Dupont   Update sources OC...
884
  				} else {
03e52840d   Kload   Init
885
886
887
888
889
890
  					$app1[$i]['internallabel'] = '3rd Party';
  					$app1[$i]['internalclass'] = 'externalapp';
  				}
  
  
  				// rating img
6d9380f96   Cédric Dupont   Update sources OC...
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
  				if ($app['score'] < 5) {
  					$img = OC_Helper::imagePath( "core", "rating/s1.png" );
  				} elseif ($app['score'] < 15) {
  					$img = OC_Helper::imagePath( "core", "rating/s2.png" );
  				} elseif($app['score'] < 25) {
  					$img = OC_Helper::imagePath( "core", "rating/s3.png" );
  				} elseif($app['score'] < 35) {
  					$img = OC_Helper::imagePath( "core", "rating/s4.png" );
  				} elseif($app['score'] < 45) {
  					$img = OC_Helper::imagePath( "core", "rating/s5.png" );
  				} elseif($app['score'] < 55) {
  					$img = OC_Helper::imagePath( "core", "rating/s6.png" );
  				} elseif($app['score'] < 65) {
  					$img = OC_Helper::imagePath( "core", "rating/s7.png" );
  				} elseif($app['score'] < 75) {
  					$img = OC_Helper::imagePath( "core", "rating/s8.png" );
  				} elseif($app['score'] < 85) {
  					$img = OC_Helper::imagePath( "core", "rating/s9.png" );
  				} elseif($app['score'] < 95) {
  					$img = OC_Helper::imagePath( "core", "rating/s10.png" );
  				} elseif($app['score'] < 100) {
  					$img = OC_Helper::imagePath( "core", "rating/s11.png" );
  				}
  
  				$app1[$i]['score'] = '<img src="' . $img . '"> Score: ' . $app['score'] . '%';
03e52840d   Kload   Init
916
917
918
  				$i++;
  			}
  		}
6d9380f96   Cédric Dupont   Update sources OC...
919
  		if (empty($app1)) {
03e52840d   Kload   Init
920
921
922
923
924
  			return false;
  		} else {
  			return $app1;
  		}
  	}
6d9380f96   Cédric Dupont   Update sources OC...
925
926
927
928
929
930
931
932
933
934
935
  	public static function shouldUpgrade($app) {
  		$versions = self::getAppVersions();
  		$currentVersion = OC_App::getAppVersion($app);
  		if ($currentVersion) {
  			$installedVersion = $versions[$app];
  			if (version_compare($currentVersion, $installedVersion, '>')) {
  				return true;
  			}
  		}
  		return false;
  	}
03e52840d   Kload   Init
936
  	/**
31b7f2792   Kload   Upgrade to ownclo...
937
  	 * check if the app needs updating and update when needed
6d9380f96   Cédric Dupont   Update sources OC...
938
939
  	 *
  	 * @param string $app
03e52840d   Kload   Init
940
941
942
943
944
945
  	 */
  	public static function checkUpgrade($app) {
  		if (in_array($app, self::$checkedApps)) {
  			return;
  		}
  		self::$checkedApps[] = $app;
6d9380f96   Cédric Dupont   Update sources OC...
946
947
948
  		if (!self::shouldUpgrade($app)) {
  			return;
  		}
03e52840d   Kload   Init
949
  		$versions = self::getAppVersions();
6d9380f96   Cédric Dupont   Update sources OC...
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
  		$installedVersion = $versions[$app];
  		$currentVersion = OC_App::getAppVersion($app);
  		OC_Log::write(
  			$app,
  			'starting app upgrade from ' . $installedVersion . ' to ' . $currentVersion,
  			OC_Log::DEBUG
  		);
  		$info = self::getAppInfo($app);
  		try {
  			OC_App::updateApp($app);
  			OC_Hook::emit('update', 'success', 'Updated ' . $info['name'] . ' app');
  		} catch (Exception $e) {
  			OC_Hook::emit('update', 'failure', 'Failed to update ' . $info['name'] . ' app: ' . $e->getMessage());
  			$l = OC_L10N::get('lib');
  			throw new RuntimeException($l->t('Failed to upgrade "%s".', array($app)), 0, $e);
03e52840d   Kload   Init
965
  		}
6d9380f96   Cédric Dupont   Update sources OC...
966
  		OC_Appconfig::setValue($app, 'installed_version', OC_App::getAppVersion($app));
03e52840d   Kload   Init
967
968
969
970
971
972
973
  	}
  
  	/**
  	 * check if the current enabled apps are compatible with the current
  	 * ownCloud version. disable them if not.
  	 * This is important if you upgrade ownCloud and have non ported 3rd
  	 * party apps installed.
6d9380f96   Cédric Dupont   Update sources OC...
974
975
976
977
978
  	 *
  	 * @param array $apps optional app id list to check, uses all enabled apps
  	 * when not specified
  	 *
  	 * @return array containing the list of ids of the disabled apps
03e52840d   Kload   Init
979
980
  	 */
  	public static function checkAppsRequirements($apps = array()) {
6d9380f96   Cédric Dupont   Update sources OC...
981
  		$disabledApps = array();
03e52840d   Kload   Init
982
983
984
985
  		if (empty($apps)) {
  			$apps = OC_App::getEnabledApps();
  		}
  		$version = OC_Util::getVersion();
6d9380f96   Cédric Dupont   Update sources OC...
986
  		foreach ($apps as $app) {
03e52840d   Kload   Init
987
988
  			// check if the app is compatible with this version of ownCloud
  			$info = OC_App::getAppInfo($app);
6d9380f96   Cédric Dupont   Update sources OC...
989
  			if(!self::isAppCompatible($version, $info)) {
03e52840d   Kload   Init
990
  				OC_Log::write('core',
6d9380f96   Cédric Dupont   Update sources OC...
991
992
  					'App "' . $info['name'] . '" (' . $app . ') can\'t be used because it is'
  					. ' not compatible with this version of ownCloud',
03e52840d   Kload   Init
993
  					OC_Log::ERROR);
6d9380f96   Cédric Dupont   Update sources OC...
994
995
996
  				OC_App::disable($app);
  				OC_Hook::emit('update', 'success', 'Disabled ' . $info['name'] . ' app because it is not compatible');
  				$disabledApps[] = $app;
03e52840d   Kload   Init
997
998
  			}
  		}
6d9380f96   Cédric Dupont   Update sources OC...
999
  		return $disabledApps;
03e52840d   Kload   Init
1000
  	}
6d9380f96   Cédric Dupont   Update sources OC...
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
  	/**
  	 * Adjust the number of version parts of $version1 to match
  	 * the number of version parts of $version2.
  	 *
  	 * @param string $version1 version to adjust
  	 * @param string $version2 version to take the number of parts from
  	 * @return string shortened $version1
  	 */
  	private static function adjustVersionParts($version1, $version2) {
  		$version1 = explode('.', $version1);
  		$version2 = explode('.', $version2);
  		// reduce $version1 to match the number of parts in $version2
  		while (count($version1) > count($version2)) {
  			array_pop($version1);
  		}
  		// if $version1 does not have enough parts, add some
  		while (count($version1) < count($version2)) {
  			$version1[] = '0';
  		}
  		return implode('.', $version1);
  	}
03e52840d   Kload   Init
1022
1023
  
  	/**
6d9380f96   Cédric Dupont   Update sources OC...
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
  	 * Check whether the current ownCloud version matches the given
  	 * application's version requirements.
  	 *
  	 * The comparison is made based on the number of parts that the
  	 * app info version has. For example for ownCloud 6.0.3 if the
  	 * app info version is expecting version 6.0, the comparison is
  	 * made on the first two parts of the ownCloud version.
  	 * This means that it's possible to specify "requiremin" => 6
  	 * and "requiremax" => 6 and it will still match ownCloud 6.0.3.
  	 *
  	 * @param string $ocVersion ownCloud version to check against
  	 * @param array  $appInfo app info (from xml)
  	 *
03e52840d   Kload   Init
1037
1038
  	 * @return boolean true if compatible, otherwise false
  	 */
6d9380f96   Cédric Dupont   Update sources OC...
1039
1040
1041
1042
1043
1044
1045
1046
  	public static function isAppCompatible($ocVersion, $appInfo){
  		$requireMin = '';
  		$requireMax = '';
  		if (isset($appInfo['requiremin'])) {
  			$requireMin = $appInfo['requiremin'];
  		} else if (isset($appInfo['require'])) {
  			$requireMin = $appInfo['require'];
  		}
03e52840d   Kload   Init
1047

6d9380f96   Cédric Dupont   Update sources OC...
1048
1049
1050
  		if (isset($appInfo['requiremax'])) {
  			$requireMax = $appInfo['requiremax'];
  		}
03e52840d   Kload   Init
1051

6d9380f96   Cédric Dupont   Update sources OC...
1052
1053
1054
  		if (is_array($ocVersion)) {
  			$ocVersion = implode('.', $ocVersion);
  		}
03e52840d   Kload   Init
1055

6d9380f96   Cédric Dupont   Update sources OC...
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
  		if (!empty($requireMin)
  			&& version_compare(self::adjustVersionParts($ocVersion, $requireMin), $requireMin, '<')
  		) {
  
  			return false;
  		}
  
  		if (!empty($requireMax)
  			&& version_compare(self::adjustVersionParts($ocVersion, $requireMax), $requireMax, '>')
  		) {
  
  			return false;
03e52840d   Kload   Init
1068
1069
1070
1071
  		}
  
  		return true;
  	}
03e52840d   Kload   Init
1072
1073
1074
1075
1076
  	/**
  	 * get the installed version of all apps
  	 */
  	public static function getAppVersions() {
  		static $versions;
6d9380f96   Cédric Dupont   Update sources OC...
1077
  		if (isset($versions)) { // simple cache, needs to be fixed
03e52840d   Kload   Init
1078
1079
  			return $versions; // when function is used besides in checkUpgrade
  		}
6d9380f96   Cédric Dupont   Update sources OC...
1080
1081
1082
  		$versions = array();
  		$query = OC_DB::prepare('SELECT `appid`, `configvalue` FROM `*PREFIX*appconfig`'
  			. ' WHERE `configkey` = \'installed_version\'');
03e52840d   Kload   Init
1083
  		$result = $query->execute();
6d9380f96   Cédric Dupont   Update sources OC...
1084
1085
  		while ($row = $result->fetchRow()) {
  			$versions[$row['appid']] = $row['configvalue'];
03e52840d   Kload   Init
1086
1087
1088
  		}
  		return $versions;
  	}
6d9380f96   Cédric Dupont   Update sources OC...
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
  
  	/**
  	 * @param mixed $app
  	 * @return bool
  	 * @throws Exception if app is not compatible with this version of ownCloud
  	 * @throws Exception if no app-name was specified
  	 */
  	public static function installApp($app) {
  		$l = OC_L10N::get('core');
  		$appData=OC_OCSClient::getApplication($app);
  
  		// check if app is a shipped app or not. OCS apps have an integer as id, shipped apps use a string
  		if(!is_numeric($app)) {
  			$shippedVersion=self::getAppVersion($app);
  			if($appData && version_compare($shippedVersion, $appData['version'], '<')) {
  				$app = self::downloadApp($app);
  			} else {
  				$app = OC_Installer::installShippedApp($app);
  			}
  		}else{
  			$app = self::downloadApp($app);
  		}
  
  		if($app!==false) {
  			// check if the app is compatible with this version of ownCloud
  			$info = self::getAppInfo($app);
  			$version=OC_Util::getVersion();
  			if(!self::isAppCompatible($version, $info)) {
  				throw new \Exception(
  					$l->t('App \"%s\" can\'t be installed because it is not compatible with this version of ownCloud.',
  						array($info['name'])
  					)
  				);
  			}else{
  				OC_Appconfig::setValue( $app, 'enabled', 'yes' );
  				if(isset($appData['id'])) {
  					OC_Appconfig::setValue( $app, 'ocsid', $appData['id'] );
  				}
  				\OC_Hook::emit('OC_App', 'post_enable', array('app' => $app));
  			}
  		}else{
  			throw new \Exception($l->t("No app name specified"));
  		}
  
  		return $app;
  	}
03e52840d   Kload   Init
1135
1136
  	/**
  	 * update the database for the app and call the update script
6d9380f96   Cédric Dupont   Update sources OC...
1137
1138
1139
  	 *
  	 * @param string $appId
  	 * @return bool
03e52840d   Kload   Init
1140
  	 */
6d9380f96   Cédric Dupont   Update sources OC...
1141
1142
1143
1144
  	public static function updateApp($appId) {
  		if (file_exists(self::getAppPath($appId) . '/appinfo/preupdate.php')) {
  			self::loadApp($appId);
  			include self::getAppPath($appId) . '/appinfo/preupdate.php';
03e52840d   Kload   Init
1145
  		}
6d9380f96   Cédric Dupont   Update sources OC...
1146
1147
  		if (file_exists(self::getAppPath($appId) . '/appinfo/database.xml')) {
  			OC_DB::updateDbFromStructure(self::getAppPath($appId) . '/appinfo/database.xml');
03e52840d   Kload   Init
1148
  		}
6d9380f96   Cédric Dupont   Update sources OC...
1149
1150
  		if (!self::isEnabled($appId)) {
  			return false;
03e52840d   Kload   Init
1151
  		}
6d9380f96   Cédric Dupont   Update sources OC...
1152
1153
1154
  		if (file_exists(self::getAppPath($appId) . '/appinfo/update.php')) {
  			self::loadApp($appId);
  			include self::getAppPath($appId) . '/appinfo/update.php';
03e52840d   Kload   Init
1155
1156
1157
  		}
  
  		//set remote/public handlers
6d9380f96   Cédric Dupont   Update sources OC...
1158
1159
1160
  		$appData = self::getAppInfo($appId);
  		if (array_key_exists('ocsid', $appData)) {
  			OC_Appconfig::setValue($appId, 'ocsid', $appData['ocsid']);
03e52840d   Kload   Init
1161
  		}
6d9380f96   Cédric Dupont   Update sources OC...
1162
1163
  		foreach ($appData['remote'] as $name => $path) {
  			OCP\CONFIG::setAppValue('core', 'remote_' . $name, $appId . '/' . $path);
03e52840d   Kload   Init
1164
  		}
6d9380f96   Cédric Dupont   Update sources OC...
1165
1166
1167
1168
1169
  		foreach ($appData['public'] as $name => $path) {
  			OCP\CONFIG::setAppValue('core', 'public_' . $name, $appId . '/' . $path);
  		}
  
  		self::setAppTypes($appId);
03e52840d   Kload   Init
1170

6d9380f96   Cédric Dupont   Update sources OC...
1171
  		return true;
03e52840d   Kload   Init
1172
1173
1174
  	}
  
  	/**
6d9380f96   Cédric Dupont   Update sources OC...
1175
  	 * @param string $appId
03e52840d   Kload   Init
1176
1177
  	 * @return \OC\Files\View
  	 */
6d9380f96   Cédric Dupont   Update sources OC...
1178
1179
1180
1181
1182
1183
  	public static function getStorage($appId) {
  		if (OC_App::isEnabled($appId)) { //sanity check
  			if (OC_User::isLoggedIn()) {
  				$view = new \OC\Files\View('/' . OC_User::getUser());
  				if (!$view->file_exists($appId)) {
  					$view->mkdir($appId);
03e52840d   Kload   Init
1184
  				}
6d9380f96   Cédric Dupont   Update sources OC...
1185
1186
1187
  				return new \OC\Files\View('/' . OC_User::getUser() . '/' . $appId);
  			} else {
  				OC_Log::write('core', 'Can\'t get app storage, app ' . $appId . ', user not logged in', OC_Log::ERROR);
03e52840d   Kload   Init
1188
1189
  				return false;
  			}
6d9380f96   Cédric Dupont   Update sources OC...
1190
1191
  		} else {
  			OC_Log::write('core', 'Can\'t get app storage, app ' . $appId . ' not enabled', OC_Log::ERROR);
03e52840d   Kload   Init
1192
1193
1194
1195
  			return false;
  		}
  	}
  }