You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

app.php 32KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182
  1. <?php
  2. /**
  3. * ownCloud
  4. *
  5. * @author Frank Karlitschek
  6. * @copyright 2012 Frank Karlitschek <frank@owncloud.org>
  7. *
  8. * @author Jakob Sack
  9. * @copyright 2012 Jakob Sack <mail@jakobsack.de>
  10. *
  11. * @author Georg Ehrke
  12. * @copyright 2014 Georg Ehrke <georg@ownCloud.com>
  13. *
  14. * This library is free software; you can redistribute it and/or
  15. * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
  16. * License as published by the Free Software Foundation; either
  17. * version 3 of the License, or any later version.
  18. *
  19. * This library is distributed in the hope that it will be useful,
  20. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  21. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  22. * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
  23. *
  24. * You should have received a copy of the GNU Affero General Public
  25. * License along with this library. If not, see <http://www.gnu.org/licenses/>.
  26. *
  27. */
  28. /**
  29. * This class manages the apps. It allows them to register and integrate in the
  30. * ownCloud ecosystem. Furthermore, this class is responsible for installing,
  31. * upgrading and removing apps.
  32. */
  33. class OC_App {
  34. static private $settingsForms = array();
  35. static private $adminForms = array();
  36. static private $personalForms = array();
  37. static private $appInfo = array();
  38. static private $appTypes = array();
  39. static private $loadedApps = array();
  40. static private $checkedApps = array();
  41. static private $altLogin = array();
  42. /**
  43. * clean the appId
  44. * @param string|boolean $app AppId that needs to be cleaned
  45. * @return string
  46. */
  47. public static function cleanAppId($app) {
  48. return str_replace(array('\0', '/', '\\', '..'), '', $app);
  49. }
  50. /**
  51. * loads all apps
  52. * @param array $types
  53. * @return bool
  54. *
  55. * This function walks through the ownCloud directory and loads all apps
  56. * it can find. A directory contains an app if the file /appinfo/app.php
  57. * exists.
  58. *
  59. * if $types is set, only apps of those types will be loaded
  60. */
  61. public static function loadApps($types = null) {
  62. if (OC_Config::getValue('maintenance', false)) {
  63. return false;
  64. }
  65. // Load the enabled apps here
  66. $apps = self::getEnabledApps();
  67. // prevent app.php from printing output
  68. ob_start();
  69. foreach ($apps as $app) {
  70. if ((is_null($types) or self::isType($app, $types)) && !in_array($app, self::$loadedApps)) {
  71. self::$loadedApps[] = $app;
  72. self::loadApp($app);
  73. }
  74. }
  75. ob_end_clean();
  76. return true;
  77. }
  78. /**
  79. * load a single app
  80. *
  81. * @param string $app
  82. * @param bool $checkUpgrade whether an upgrade check should be done
  83. * @throws \OC\NeedsUpdateException
  84. */
  85. public static function loadApp($app, $checkUpgrade = true) {
  86. if (is_file(self::getAppPath($app) . '/appinfo/app.php')) {
  87. \OC::$server->getEventLogger()->start('load_app_' . $app, 'Load app: ' . $app);
  88. if ($checkUpgrade and self::shouldUpgrade($app)) {
  89. throw new \OC\NeedsUpdateException();
  90. }
  91. self::requireAppFile($app);
  92. if (self::isType($app, array('authentication'))) {
  93. // since authentication apps affect the "is app enabled for group" check,
  94. // the enabled apps cache needs to be cleared to make sure that the
  95. // next time getEnableApps() is called it will also include apps that were
  96. // enabled for groups
  97. self::$enabledAppsCache = array();
  98. }
  99. \OC::$server->getEventLogger()->end('load_app_' . $app);
  100. }
  101. }
  102. /**
  103. * Load app.php from the given app
  104. *
  105. * @param string $app app name
  106. */
  107. private static function requireAppFile($app) {
  108. // encapsulated here to avoid variable scope conflicts
  109. require_once $app . '/appinfo/app.php';
  110. }
  111. /**
  112. * check if an app is of a specific type
  113. *
  114. * @param string $app
  115. * @param string|array $types
  116. * @return bool
  117. */
  118. public static function isType($app, $types) {
  119. if (is_string($types)) {
  120. $types = array($types);
  121. }
  122. $appTypes = self::getAppTypes($app);
  123. foreach ($types as $type) {
  124. if (array_search($type, $appTypes) !== false) {
  125. return true;
  126. }
  127. }
  128. return false;
  129. }
  130. /**
  131. * get the types of an app
  132. *
  133. * @param string $app
  134. * @return array
  135. */
  136. private static function getAppTypes($app) {
  137. //load the cache
  138. if (count(self::$appTypes) == 0) {
  139. self::$appTypes = OC_Appconfig::getValues(false, 'types');
  140. }
  141. if (isset(self::$appTypes[$app])) {
  142. return explode(',', self::$appTypes[$app]);
  143. } else {
  144. return array();
  145. }
  146. }
  147. /**
  148. * read app types from info.xml and cache them in the database
  149. */
  150. public static function setAppTypes($app) {
  151. $appData = self::getAppInfo($app);
  152. if (isset($appData['types'])) {
  153. $appTypes = implode(',', $appData['types']);
  154. } else {
  155. $appTypes = '';
  156. }
  157. OC_Appconfig::setValue($app, 'types', $appTypes);
  158. }
  159. /**
  160. * check if app is shipped
  161. *
  162. * @param string $appId the id of the app to check
  163. * @return bool
  164. *
  165. * Check if an app that is installed is a shipped app or installed from the appstore.
  166. */
  167. public static function isShipped($appId) {
  168. $info = self::getAppInfo($appId);
  169. if (isset($info['shipped']) && $info['shipped'] == 'true') {
  170. return true;
  171. } else {
  172. return false;
  173. }
  174. }
  175. /**
  176. * get all enabled apps
  177. */
  178. protected static $enabledAppsCache = array();
  179. /**
  180. * Returns apps enabled for the current user.
  181. *
  182. * @param bool $forceRefresh whether to refresh the cache
  183. * @param bool $all whether to return apps for all users, not only the
  184. * currently logged in one
  185. */
  186. public static function getEnabledApps($forceRefresh = false, $all = false) {
  187. if (!OC_Config::getValue('installed', false)) {
  188. return array();
  189. }
  190. // in incognito mode or when logged out, $user will be false,
  191. // which is also the case during an upgrade
  192. $user = null;
  193. if (!$all) {
  194. $user = \OC_User::getUser();
  195. }
  196. if (is_string($user) && !$forceRefresh && !empty(self::$enabledAppsCache)) {
  197. return self::$enabledAppsCache;
  198. }
  199. $apps = array();
  200. $appConfig = \OC::$server->getAppConfig();
  201. $appStatus = $appConfig->getValues(false, 'enabled');
  202. foreach ($appStatus as $app => $enabled) {
  203. if ($app === 'files') {
  204. continue;
  205. }
  206. if ($enabled === 'yes') {
  207. $apps[] = $app;
  208. } else if ($enabled !== 'no') {
  209. $groups = json_decode($enabled);
  210. if (is_array($groups)) {
  211. if (is_string($user)) {
  212. foreach ($groups as $group) {
  213. if (\OC_Group::inGroup($user, $group)) {
  214. $apps[] = $app;
  215. break;
  216. }
  217. }
  218. } else {
  219. // global, consider app as enabled
  220. $apps[] = $app;
  221. }
  222. }
  223. }
  224. }
  225. sort($apps);
  226. array_unshift($apps, 'files');
  227. // Only cache the app list, when the user is logged in.
  228. // Otherwise we cache the list with disabled apps, although
  229. // the apps are enabled for the user after he logged in.
  230. if ($user) {
  231. self::$enabledAppsCache = $apps;
  232. }
  233. return $apps;
  234. }
  235. /**
  236. * checks whether or not an app is enabled
  237. * @param string $app app
  238. * @return bool
  239. *
  240. * This function checks whether or not an app is enabled.
  241. */
  242. public static function isEnabled($app) {
  243. if ('files' == $app) {
  244. return true;
  245. }
  246. $enabledApps = self::getEnabledApps();
  247. return in_array($app, $enabledApps);
  248. }
  249. /**
  250. * enables an app
  251. * @param mixed $app app
  252. * @param array $groups (optional) when set, only these groups will have access to the app
  253. * @throws \Exception
  254. * @return void
  255. *
  256. * This function set an app as enabled in appconfig.
  257. */
  258. public static function enable($app, $groups = null) {
  259. self::$enabledAppsCache = array(); // flush
  260. if (!OC_Installer::isInstalled($app)) {
  261. $app = self::installApp($app);
  262. }
  263. if (!is_null($groups)) {
  264. OC_Appconfig::setValue($app, 'enabled', json_encode($groups));
  265. }else{
  266. OC_Appconfig::setValue($app, 'enabled', 'yes');
  267. }
  268. }
  269. /**
  270. * @param string $app
  271. * @return int
  272. */
  273. public static function downloadApp($app) {
  274. $appData=OC_OCSClient::getApplication($app);
  275. $download=OC_OCSClient::getApplicationDownload($app, 1);
  276. if(isset($download['downloadlink']) and $download['downloadlink']!='') {
  277. // Replace spaces in download link without encoding entire URL
  278. $download['downloadlink'] = str_replace(' ', '%20', $download['downloadlink']);
  279. $info = array('source'=>'http', 'href'=>$download['downloadlink'], 'appdata'=>$appData);
  280. $app=OC_Installer::installApp($info);
  281. }
  282. return $app;
  283. }
  284. /**
  285. * @param string $app
  286. * @return bool
  287. */
  288. public static function removeApp($app) {
  289. if (self::isShipped($app)) {
  290. return false;
  291. }
  292. return OC_Installer::removeApp($app);
  293. }
  294. /**
  295. * This function set an app as disabled in appconfig.
  296. * @param string $app app
  297. */
  298. public static function disable($app) {
  299. self::$enabledAppsCache = array(); // flush
  300. // check if app is a shipped app or not. if not delete
  301. \OC_Hook::emit('OC_App', 'pre_disable', array('app' => $app));
  302. OC_Appconfig::setValue($app, 'enabled', 'no' );
  303. }
  304. /**
  305. * adds an entry to the navigation
  306. * @param array $data array containing the data
  307. * @return bool
  308. *
  309. * This function adds a new entry to the navigation visible to users. $data
  310. * is an associative array.
  311. * The following keys are required:
  312. * - id: unique id for this entry ('addressbook_index')
  313. * - href: link to the page
  314. * - name: Human readable name ('Addressbook')
  315. *
  316. * The following keys are optional:
  317. * - icon: path to the icon of the app
  318. * - order: integer, that influences the position of your application in
  319. * the navigation. Lower values come first.
  320. */
  321. public static function addNavigationEntry($data) {
  322. OC::$server->getNavigationManager()->add($data);
  323. return true;
  324. }
  325. /**
  326. * marks a navigation entry as active
  327. * @param string $id id of the entry
  328. * @return bool
  329. *
  330. * This function sets a navigation entry as active and removes the 'active'
  331. * property from all other entries. The templates can use this for
  332. * highlighting the current position of the user.
  333. */
  334. public static function setActiveNavigationEntry($id) {
  335. OC::$server->getNavigationManager()->setActiveEntry($id);
  336. return true;
  337. }
  338. /**
  339. * Get the navigation entries for the $app
  340. * @param string $app app
  341. * @return array an array of the $data added with addNavigationEntry
  342. *
  343. * Warning: destroys the existing entries
  344. */
  345. public static function getAppNavigationEntries($app) {
  346. if (is_file(self::getAppPath($app) . '/appinfo/app.php')) {
  347. OC::$server->getNavigationManager()->clear();
  348. require $app . '/appinfo/app.php';
  349. return OC::$server->getNavigationManager()->getAll();
  350. }
  351. return array();
  352. }
  353. /**
  354. * gets the active Menu entry
  355. * @return string id or empty string
  356. *
  357. * This function returns the id of the active navigation entry (set by
  358. * setActiveNavigationEntry
  359. */
  360. public static function getActiveNavigationEntry() {
  361. return OC::$server->getNavigationManager()->getActiveEntry();
  362. }
  363. /**
  364. * Returns the Settings Navigation
  365. * @return string
  366. *
  367. * This function returns an array containing all settings pages added. The
  368. * entries are sorted by the key 'order' ascending.
  369. */
  370. public static function getSettingsNavigation() {
  371. $l = \OC::$server->getL10N('lib');
  372. $settings = array();
  373. // by default, settings only contain the help menu
  374. if (OC_Util::getEditionString() === '' &&
  375. OC_Config::getValue('knowledgebaseenabled', true) == true
  376. ) {
  377. $settings = array(
  378. array(
  379. "id" => "help",
  380. "order" => 1000,
  381. "href" => OC_Helper::linkToRoute("settings_help"),
  382. "name" => $l->t("Help"),
  383. "icon" => OC_Helper::imagePath("settings", "help.svg")
  384. )
  385. );
  386. }
  387. // if the user is logged-in
  388. if (OC_User::isLoggedIn()) {
  389. // personal menu
  390. $settings[] = array(
  391. "id" => "personal",
  392. "order" => 1,
  393. "href" => OC_Helper::linkToRoute("settings_personal"),
  394. "name" => $l->t("Personal"),
  395. "icon" => OC_Helper::imagePath("settings", "personal.svg")
  396. );
  397. // if there are some settings forms
  398. if (!empty(self::$settingsForms)) {
  399. // settings menu
  400. $settings[] = array(
  401. "id" => "settings",
  402. "order" => 1000,
  403. "href" => OC_Helper::linkToRoute("settings_settings"),
  404. "name" => $l->t("Settings"),
  405. "icon" => OC_Helper::imagePath("settings", "settings.svg")
  406. );
  407. }
  408. //SubAdmins are also allowed to access user management
  409. if (OC_SubAdmin::isSubAdmin(OC_User::getUser())) {
  410. // admin users menu
  411. $settings[] = array(
  412. "id" => "core_users",
  413. "order" => 2,
  414. "href" => OC_Helper::linkToRoute("settings_users"),
  415. "name" => $l->t("Users"),
  416. "icon" => OC_Helper::imagePath("settings", "users.svg")
  417. );
  418. }
  419. // if the user is an admin
  420. if (OC_User::isAdminUser(OC_User::getUser())) {
  421. // admin settings
  422. $settings[] = array(
  423. "id" => "admin",
  424. "order" => 1000,
  425. "href" => OC_Helper::linkToRoute("settings_admin"),
  426. "name" => $l->t("Admin"),
  427. "icon" => OC_Helper::imagePath("settings", "admin.svg")
  428. );
  429. }
  430. }
  431. $navigation = self::proceedNavigation($settings);
  432. return $navigation;
  433. }
  434. // This is private as well. It simply works, so don't ask for more details
  435. private static function proceedNavigation($list) {
  436. $activeApp = OC::$server->getNavigationManager()->getActiveEntry();
  437. foreach ($list as &$navEntry) {
  438. if ($navEntry['id'] == $activeApp) {
  439. $navEntry['active'] = true;
  440. } else {
  441. $navEntry['active'] = false;
  442. }
  443. }
  444. unset($navEntry);
  445. usort($list, create_function('$a, $b', 'if( $a["order"] == $b["order"] ) {return 0;}elseif( $a["order"] < $b["order"] ) {return -1;}else{return 1;}'));
  446. return $list;
  447. }
  448. /**
  449. * Get the path where to install apps
  450. *
  451. * @return string
  452. */
  453. public static function getInstallPath() {
  454. if (OC_Config::getValue('appstoreenabled', true) == false) {
  455. return false;
  456. }
  457. foreach (OC::$APPSROOTS as $dir) {
  458. if (isset($dir['writable']) && $dir['writable'] === true) {
  459. return $dir['path'];
  460. }
  461. }
  462. OC_Log::write('core', 'No application directories are marked as writable.', OC_Log::ERROR);
  463. return null;
  464. }
  465. /**
  466. * search for an app in all app-directories
  467. * @param $appId
  468. * @return mixed (bool|string)
  469. */
  470. protected static function findAppInDirectories($appId) {
  471. static $app_dir = array();
  472. if (isset($app_dir[$appId])) {
  473. return $app_dir[$appId];
  474. }
  475. $possibleApps = array();
  476. foreach(OC::$APPSROOTS as $dir) {
  477. if(file_exists($dir['path'] . '/' . $appId)) {
  478. $possibleApps[] = $dir;
  479. }
  480. }
  481. if (empty($possibleApps)) {
  482. return false;
  483. } elseif(count($possibleApps) === 1) {
  484. $dir = array_shift($possibleApps);
  485. $app_dir[$appId] = $dir;
  486. return $dir;
  487. } else {
  488. $versionToLoad = array();
  489. foreach($possibleApps as $possibleApp) {
  490. $version = self::getAppVersionByPath($possibleApp['path']);
  491. if (empty($versionToLoad) || version_compare($version, $versionToLoad['version'], '>')) {
  492. $versionToLoad = array(
  493. 'dir' => $possibleApp,
  494. 'version' => $version,
  495. );
  496. }
  497. }
  498. $app_dir[$appId] = $versionToLoad['dir'];
  499. return $versionToLoad['dir'];
  500. //TODO - write test
  501. }
  502. }
  503. /**
  504. * Get the directory for the given app.
  505. * If the app is defined in multiple directories, the first one is taken. (false if not found)
  506. *
  507. * @param string $appId
  508. * @return string|false
  509. */
  510. public static function getAppPath($appId) {
  511. if ($appId === null || trim($appId) === '') {
  512. return false;
  513. }
  514. if (($dir = self::findAppInDirectories($appId)) != false) {
  515. return $dir['path'] . '/' . $appId;
  516. }
  517. return false;
  518. }
  519. /**
  520. * check if an app's directory is writable
  521. *
  522. * @param string $appId
  523. * @return bool
  524. */
  525. public static function isAppDirWritable($appId) {
  526. $path = self::getAppPath($appId);
  527. return ($path !== false) ? is_writable($path) : false;
  528. }
  529. /**
  530. * Get the path for the given app on the access
  531. * If the app is defined in multiple directories, the first one is taken. (false if not found)
  532. *
  533. * @param string $appId
  534. * @return string|false
  535. */
  536. public static function getAppWebPath($appId) {
  537. if (($dir = self::findAppInDirectories($appId)) != false) {
  538. return OC::$WEBROOT . $dir['url'] . '/' . $appId;
  539. }
  540. return false;
  541. }
  542. /**
  543. * get the last version of the app, either from appinfo/version or from appinfo/info.xml
  544. *
  545. * @param string $appId
  546. * @return string
  547. */
  548. public static function getAppVersion($appId) {
  549. $file = self::getAppPath($appId);
  550. return ($file !== false) ? self::getAppVersionByPath($file) : '0';
  551. }
  552. /**
  553. * get app's version based on it's path
  554. * @param string $path
  555. * @return string
  556. */
  557. public static function getAppVersionByPath($path) {
  558. $versionFile = $path . '/appinfo/version';
  559. $infoFile = $path . '/appinfo/info.xml';
  560. if(is_file($versionFile)) {
  561. return trim(file_get_contents($versionFile));
  562. }else{
  563. $appData = self::getAppInfo($infoFile, true);
  564. return isset($appData['version']) ? $appData['version'] : '';
  565. }
  566. }
  567. /**
  568. * Read all app metadata from the info.xml file
  569. *
  570. * @param string $appId id of the app or the path of the info.xml file
  571. * @param boolean $path (optional)
  572. * @return array|null
  573. * @note all data is read from info.xml, not just pre-defined fields
  574. */
  575. public static function getAppInfo($appId, $path = false) {
  576. if ($path) {
  577. $file = $appId;
  578. } else {
  579. if (isset(self::$appInfo[$appId])) {
  580. return self::$appInfo[$appId];
  581. }
  582. $file = self::getAppPath($appId) . '/appinfo/info.xml';
  583. }
  584. $parser = new \OC\App\InfoParser(\OC::$server->getHTTPHelper(), \OC::$server->getURLGenerator());
  585. $data = $parser->parse($file);
  586. self::$appInfo[$appId] = $data;
  587. return $data;
  588. }
  589. /**
  590. * Returns the navigation
  591. * @return array
  592. *
  593. * This function returns an array containing all entries added. The
  594. * entries are sorted by the key 'order' ascending. Additional to the keys
  595. * given for each app the following keys exist:
  596. * - active: boolean, signals if the user is on this navigation entry
  597. */
  598. public static function getNavigation() {
  599. $entries = OC::$server->getNavigationManager()->getAll();
  600. $navigation = self::proceedNavigation($entries);
  601. return $navigation;
  602. }
  603. /**
  604. * get the id of loaded app
  605. *
  606. * @return string
  607. */
  608. public static function getCurrentApp() {
  609. $script = substr(OC_Request::scriptName(), strlen(OC::$WEBROOT) + 1);
  610. $topFolder = substr($script, 0, strpos($script, '/'));
  611. if (empty($topFolder)) {
  612. $path_info = OC_Request::getPathInfo();
  613. if ($path_info) {
  614. $topFolder = substr($path_info, 1, strpos($path_info, '/', 1) - 1);
  615. }
  616. }
  617. if ($topFolder == 'apps') {
  618. $length = strlen($topFolder);
  619. return substr($script, $length + 1, strpos($script, '/', $length + 1) - $length - 1);
  620. } else {
  621. return $topFolder;
  622. }
  623. }
  624. /**
  625. * get the forms for either settings, admin or personal
  626. */
  627. public static function getForms($type) {
  628. $forms = array();
  629. switch ($type) {
  630. case 'settings':
  631. $source = self::$settingsForms;
  632. break;
  633. case 'admin':
  634. $source = self::$adminForms;
  635. break;
  636. case 'personal':
  637. $source = self::$personalForms;
  638. break;
  639. default:
  640. return array();
  641. }
  642. foreach ($source as $form) {
  643. $forms[] = include $form;
  644. }
  645. return $forms;
  646. }
  647. /**
  648. * register a settings form to be shown
  649. */
  650. public static function registerSettings($app, $page) {
  651. self::$settingsForms[] = $app . '/' . $page . '.php';
  652. }
  653. /**
  654. * register an admin form to be shown
  655. *
  656. * @param string $app
  657. * @param string $page
  658. */
  659. public static function registerAdmin($app, $page) {
  660. self::$adminForms[] = $app . '/' . $page . '.php';
  661. }
  662. /**
  663. * register a personal form to be shown
  664. */
  665. public static function registerPersonal($app, $page) {
  666. self::$personalForms[] = $app . '/' . $page . '.php';
  667. }
  668. public static function registerLogIn($entry) {
  669. self::$altLogin[] = $entry;
  670. }
  671. public static function getAlternativeLogIns() {
  672. return self::$altLogin;
  673. }
  674. /**
  675. * get a list of all apps in the apps folder
  676. * @return array an array of app names (string IDs)
  677. * @todo: change the name of this method to getInstalledApps, which is more accurate
  678. */
  679. public static function getAllApps() {
  680. $apps = array();
  681. foreach (OC::$APPSROOTS as $apps_dir) {
  682. if (!is_readable($apps_dir['path'])) {
  683. OC_Log::write('core', 'unable to read app folder : ' . $apps_dir['path'], OC_Log::WARN);
  684. continue;
  685. }
  686. $dh = opendir($apps_dir['path']);
  687. if (is_resource($dh)) {
  688. while (($file = readdir($dh)) !== false) {
  689. if ($file[0] != '.' and is_file($apps_dir['path'] . '/' . $file . '/appinfo/app.php')) {
  690. $apps[] = $file;
  691. }
  692. }
  693. }
  694. }
  695. return $apps;
  696. }
  697. /**
  698. * Lists all apps, this is used in apps.php
  699. * @return array
  700. */
  701. public static function listAllApps($onlyLocal = false) {
  702. $installedApps = OC_App::getAllApps();
  703. //TODO which apps do we want to blacklist and how do we integrate
  704. // blacklisting with the multi apps folder feature?
  705. $blacklist = array('files'); //we don't want to show configuration for these
  706. $appList = array();
  707. $l = \OC::$server->getL10N('core');
  708. foreach ($installedApps as $app) {
  709. if (array_search($app, $blacklist) === false) {
  710. $info = OC_App::getAppInfo($app);
  711. if (!isset($info['name'])) {
  712. OC_Log::write('core', 'App id "' . $app . '" has no name in appinfo', OC_Log::ERROR);
  713. continue;
  714. }
  715. $enabled = OC_Appconfig::getValue($app, 'enabled', 'no');
  716. $info['groups'] = null;
  717. if ($enabled === 'yes') {
  718. $active = true;
  719. } else if($enabled === 'no') {
  720. $active = false;
  721. } else {
  722. $active = true;
  723. $info['groups'] = $enabled;
  724. }
  725. $info['active'] = $active;
  726. if(isset($info['shipped']) and ($info['shipped'] == 'true')) {
  727. $info['internal'] = true;
  728. $info['internallabel'] = (string)$l->t('Recommended');
  729. $info['internalclass'] = 'recommendedapp';
  730. $info['removable'] = false;
  731. } else {
  732. $info['internal'] = false;
  733. $info['removable'] = true;
  734. }
  735. $info['update'] = OC_Installer::isUpdateAvailable($app);
  736. $appIcon = self::getAppPath($app) . '/img/' . $app.'.svg';
  737. if (file_exists($appIcon)) {
  738. $info['preview'] = OC_Helper::imagePath($app, $app.'.svg');
  739. $info['previewAsIcon'] = true;
  740. } else {
  741. $appIcon = self::getAppPath($app) . '/img/app.svg';
  742. if (file_exists($appIcon)) {
  743. $info['preview'] = OC_Helper::imagePath($app, 'app.svg');
  744. $info['previewAsIcon'] = true;
  745. }
  746. }
  747. $info['version'] = OC_App::getAppVersion($app);
  748. $appList[] = $info;
  749. }
  750. }
  751. if ($onlyLocal) {
  752. $remoteApps = array();
  753. } else {
  754. $remoteApps = OC_App::getAppstoreApps();
  755. }
  756. if ($remoteApps) {
  757. // Remove duplicates
  758. foreach ($appList as $app) {
  759. foreach ($remoteApps AS $key => $remote) {
  760. if ($app['name'] === $remote['name'] ||
  761. (isset($app['ocsid']) &&
  762. $app['ocsid'] === $remote['id'])) {
  763. unset($remoteApps[$key]);
  764. }
  765. }
  766. }
  767. $combinedApps = array_merge($appList, $remoteApps);
  768. } else {
  769. $combinedApps = $appList;
  770. }
  771. // bring the apps into the right order with a custom sort function
  772. usort($combinedApps, function ($a, $b) {
  773. // priority 1: active
  774. if ($a['active'] != $b['active']) {
  775. return $b['active'] - $a['active'];
  776. }
  777. // priority 2: shipped
  778. $aShipped = (array_key_exists('shipped', $a) && $a['shipped'] === 'true') ? 1 : 0;
  779. $bShipped = (array_key_exists('shipped', $b) && $b['shipped'] === 'true') ? 1 : 0;
  780. if ($aShipped !== $bShipped) {
  781. return ($bShipped - $aShipped);
  782. }
  783. // priority 3: recommended
  784. $internalClassA = isset($a['internalclass']) ? $a['internalclass'] : '';
  785. $internalClassB = isset($b['internalclass']) ? $b['internalclass'] : '';
  786. if ($internalClassA != $internalClassB) {
  787. $aTemp = ($internalClassA == 'recommendedapp' ? 1 : 0);
  788. $bTemp = ($internalClassB == 'recommendedapp' ? 1 : 0);
  789. return ($bTemp - $aTemp);
  790. }
  791. // priority 4: alphabetical
  792. return strcasecmp($a['name'], $b['name']);
  793. });
  794. return $combinedApps;
  795. }
  796. /**
  797. * get a list of all apps on apps.owncloud.com
  798. * @return array, multi-dimensional array of apps.
  799. * Keys: id, name, type, typename, personid, license, detailpage, preview, changed, description
  800. */
  801. public static function getAppstoreApps($filter = 'approved', $category = null) {
  802. $categories = array($category);
  803. if (is_null($category)) {
  804. $categoryNames = OC_OCSClient::getCategories();
  805. if (is_array($categoryNames)) {
  806. // Check that categories of apps were retrieved correctly
  807. if (!$categories = array_keys($categoryNames)) {
  808. return false;
  809. }
  810. } else {
  811. return false;
  812. }
  813. }
  814. $page = 0;
  815. $remoteApps = OC_OCSClient::getApplications($categories, $page, $filter);
  816. $app1 = array();
  817. $i = 0;
  818. $l = \OC::$server->getL10N('core');
  819. foreach ($remoteApps as $app) {
  820. $app1[$i] = $app;
  821. $app1[$i]['author'] = $app['personid'];
  822. $app1[$i]['ocs_id'] = $app['id'];
  823. $app1[$i]['internal'] = $app1[$i]['active'] = 0;
  824. $app1[$i]['update'] = false;
  825. $app1[$i]['groups'] = false;
  826. $app1[$i]['score'] = $app['score'];
  827. $app1[$i]['removable'] = false;
  828. if ($app['label'] == 'recommended') {
  829. $app1[$i]['internallabel'] = (string)$l->t('Recommended');
  830. $app1[$i]['internalclass'] = 'recommendedapp';
  831. }
  832. $i++;
  833. }
  834. if (empty($app1)) {
  835. return false;
  836. } else {
  837. return $app1;
  838. }
  839. }
  840. public static function shouldUpgrade($app) {
  841. $versions = self::getAppVersions();
  842. $currentVersion = OC_App::getAppVersion($app);
  843. if ($currentVersion && isset($versions[$app])) {
  844. $installedVersion = $versions[$app];
  845. if (version_compare($currentVersion, $installedVersion, '>')) {
  846. return true;
  847. }
  848. }
  849. return false;
  850. }
  851. /**
  852. * check if the current enabled apps are compatible with the current
  853. * ownCloud version. disable them if not.
  854. * This is important if you upgrade ownCloud and have non ported 3rd
  855. * party apps installed.
  856. *
  857. * @param array $apps optional app id list to check, uses all enabled apps
  858. * when not specified
  859. *
  860. * @return array containing the list of ids of the disabled apps
  861. */
  862. public static function checkAppsRequirements($apps = array()) {
  863. $disabledApps = array();
  864. if (empty($apps)) {
  865. $apps = OC_App::getEnabledApps();
  866. }
  867. $version = OC_Util::getVersion();
  868. foreach ($apps as $app) {
  869. // check if the app is compatible with this version of ownCloud
  870. $info = OC_App::getAppInfo($app);
  871. if(!self::isAppCompatible($version, $info)) {
  872. OC_Log::write('core',
  873. 'App "' . $info['name'] . '" (' . $app . ') can\'t be used because it is'
  874. . ' not compatible with this version of ownCloud',
  875. OC_Log::ERROR);
  876. OC_App::disable($app);
  877. OC_Hook::emit('update', 'success', 'Disabled ' . $info['name'] . ' app because it is not compatible');
  878. $disabledApps[] = $app;
  879. }
  880. }
  881. return $disabledApps;
  882. }
  883. /**
  884. * Adjust the number of version parts of $version1 to match
  885. * the number of version parts of $version2.
  886. *
  887. * @param string $version1 version to adjust
  888. * @param string $version2 version to take the number of parts from
  889. * @return string shortened $version1
  890. */
  891. private static function adjustVersionParts($version1, $version2) {
  892. $version1 = explode('.', $version1);
  893. $version2 = explode('.', $version2);
  894. // reduce $version1 to match the number of parts in $version2
  895. while (count($version1) > count($version2)) {
  896. array_pop($version1);
  897. }
  898. // if $version1 does not have enough parts, add some
  899. while (count($version1) < count($version2)) {
  900. $version1[] = '0';
  901. }
  902. return implode('.', $version1);
  903. }
  904. /**
  905. * Check whether the current ownCloud version matches the given
  906. * application's version requirements.
  907. *
  908. * The comparison is made based on the number of parts that the
  909. * app info version has. For example for ownCloud 6.0.3 if the
  910. * app info version is expecting version 6.0, the comparison is
  911. * made on the first two parts of the ownCloud version.
  912. * This means that it's possible to specify "requiremin" => 6
  913. * and "requiremax" => 6 and it will still match ownCloud 6.0.3.
  914. *
  915. * @param string $ocVersion ownCloud version to check against
  916. * @param array $appInfo app info (from xml)
  917. *
  918. * @return boolean true if compatible, otherwise false
  919. */
  920. public static function isAppCompatible($ocVersion, $appInfo){
  921. $requireMin = '';
  922. $requireMax = '';
  923. if (isset($appInfo['requiremin'])) {
  924. $requireMin = $appInfo['requiremin'];
  925. } else if (isset($appInfo['require'])) {
  926. $requireMin = $appInfo['require'];
  927. }
  928. if (isset($appInfo['requiremax'])) {
  929. $requireMax = $appInfo['requiremax'];
  930. }
  931. if (is_array($ocVersion)) {
  932. $ocVersion = implode('.', $ocVersion);
  933. }
  934. if (!empty($requireMin)
  935. && version_compare(self::adjustVersionParts($ocVersion, $requireMin), $requireMin, '<')
  936. ) {
  937. return false;
  938. }
  939. if (!empty($requireMax)
  940. && version_compare(self::adjustVersionParts($ocVersion, $requireMax), $requireMax, '>')
  941. ) {
  942. return false;
  943. }
  944. return true;
  945. }
  946. /**
  947. * get the installed version of all apps
  948. */
  949. public static function getAppVersions() {
  950. static $versions;
  951. if (isset($versions)) { // simple cache, needs to be fixed
  952. return $versions; // when function is used besides in checkUpgrade
  953. }
  954. $versions = array();
  955. try {
  956. $query = OC_DB::prepare('SELECT `appid`, `configvalue` FROM `*PREFIX*appconfig`'
  957. . ' WHERE `configkey` = \'installed_version\'');
  958. $result = $query->execute();
  959. while ($row = $result->fetchRow()) {
  960. $versions[$row['appid']] = $row['configvalue'];
  961. }
  962. return $versions;
  963. } catch (\Exception $e) {
  964. return array();
  965. }
  966. }
  967. /**
  968. * @param mixed $app
  969. * @return bool
  970. * @throws Exception if app is not compatible with this version of ownCloud
  971. * @throws Exception if no app-name was specified
  972. */
  973. public static function installApp($app) {
  974. $l = \OC::$server->getL10N('core');
  975. $appData=OC_OCSClient::getApplication($app);
  976. // check if app is a shipped app or not. OCS apps have an integer as id, shipped apps use a string
  977. if(!is_numeric($app)) {
  978. $shippedVersion=self::getAppVersion($app);
  979. if($appData && version_compare($shippedVersion, $appData['version'], '<')) {
  980. $app = self::downloadApp($app);
  981. } else {
  982. $app = OC_Installer::installShippedApp($app);
  983. }
  984. }else{
  985. $app = self::downloadApp($app);
  986. }
  987. if($app!==false) {
  988. // check if the app is compatible with this version of ownCloud
  989. $info = self::getAppInfo($app);
  990. $version=OC_Util::getVersion();
  991. if(!self::isAppCompatible($version, $info)) {
  992. throw new \Exception(
  993. $l->t('App \"%s\" can\'t be installed because it is not compatible with this version of ownCloud.',
  994. array($info['name'])
  995. )
  996. );
  997. }else{
  998. OC_Appconfig::setValue( $app, 'enabled', 'yes' );
  999. if(isset($appData['id'])) {
  1000. OC_Appconfig::setValue( $app, 'ocsid', $appData['id'] );
  1001. }
  1002. \OC_Hook::emit('OC_App', 'post_enable', array('app' => $app));
  1003. }
  1004. }else{
  1005. throw new \Exception($l->t("No app name specified"));
  1006. }
  1007. return $app;
  1008. }
  1009. /**
  1010. * update the database for the app and call the update script
  1011. *
  1012. * @param string $appId
  1013. * @return bool
  1014. */
  1015. public static function updateApp($appId) {
  1016. if (file_exists(self::getAppPath($appId) . '/appinfo/database.xml')) {
  1017. OC_DB::updateDbFromStructure(self::getAppPath($appId) . '/appinfo/database.xml');
  1018. }
  1019. if (!self::isEnabled($appId)) {
  1020. return false;
  1021. }
  1022. if (file_exists(self::getAppPath($appId) . '/appinfo/update.php')) {
  1023. self::loadApp($appId, false);
  1024. include self::getAppPath($appId) . '/appinfo/update.php';
  1025. }
  1026. //set remote/public handlers
  1027. $appData = self::getAppInfo($appId);
  1028. if (array_key_exists('ocsid', $appData)) {
  1029. OC_Appconfig::setValue($appId, 'ocsid', $appData['ocsid']);
  1030. }
  1031. foreach ($appData['remote'] as $name => $path) {
  1032. OCP\CONFIG::setAppValue('core', 'remote_' . $name, $appId . '/' . $path);
  1033. }
  1034. foreach ($appData['public'] as $name => $path) {
  1035. OCP\CONFIG::setAppValue('core', 'public_' . $name, $appId . '/' . $path);
  1036. }
  1037. self::setAppTypes($appId);
  1038. $version = \OC_App::getAppVersion($appId);
  1039. \OC_Appconfig::setValue($appId, 'installed_version', $version);
  1040. return true;
  1041. }
  1042. /**
  1043. * @param string $appId
  1044. * @return \OC\Files\View
  1045. */
  1046. public static function getStorage($appId) {
  1047. if (OC_App::isEnabled($appId)) { //sanity check
  1048. if (OC_User::isLoggedIn()) {
  1049. $view = new \OC\Files\View('/' . OC_User::getUser());
  1050. if (!$view->file_exists($appId)) {
  1051. $view->mkdir($appId);
  1052. }
  1053. return new \OC\Files\View('/' . OC_User::getUser() . '/' . $appId);
  1054. } else {
  1055. OC_Log::write('core', 'Can\'t get app storage, app ' . $appId . ', user not logged in', OC_Log::ERROR);
  1056. return false;
  1057. }
  1058. } else {
  1059. OC_Log::write('core', 'Can\'t get app storage, app ' . $appId . ' not enabled', OC_Log::ERROR);
  1060. return false;
  1061. }
  1062. }
  1063. }