diff options
author | Roeland Jago Douma <rullzer@owncloud.com> | 2016-04-30 10:45:19 +0200 |
---|---|---|
committer | Roeland Jago Douma <rullzer@owncloud.com> | 2016-04-30 11:32:22 +0200 |
commit | 368be8894cebd8e730f2ef7994d0b56bdc0f47e2 (patch) | |
tree | e4153727428d58cdbf82173cc3132bc30dd7e4e2 /lib/private/app.php | |
parent | 023c8b0eacc243b2dd4763e6086fc87ce8b76126 (diff) | |
download | nextcloud-server-368be8894cebd8e730f2ef7994d0b56bdc0f47e2.tar.gz nextcloud-server-368be8894cebd8e730f2ef7994d0b56bdc0f47e2.zip |
Move non PSR-4 files from lib/private root to legacy
As discussed we move all old style classes (OC_FOO_BAR) to legacy.
Then from there we can evaluate the need to convert them back or if they
can be fully deprecated/deleted.
Diffstat (limited to 'lib/private/app.php')
-rw-r--r-- | lib/private/app.php | 1284 |
1 files changed, 0 insertions, 1284 deletions
diff --git a/lib/private/app.php b/lib/private/app.php deleted file mode 100644 index 246bf97ee91..00000000000 --- a/lib/private/app.php +++ /dev/null @@ -1,1284 +0,0 @@ -<?php -/** - * @author Arthur Schiwon <blizzz@owncloud.com> - * @author Bart Visscher <bartv@thisnet.nl> - * @author Bernhard Posselt <dev@bernhard-posselt.com> - * @author Björn Schießle <schiessle@owncloud.com> - * @author Borjan Tchakaloff <borjan@tchakaloff.fr> - * @author Brice Maron <brice@bmaron.net> - * @author Christopher Schäpers <kondou@ts.unde.re> - * @author Felix Moeller <mail@felixmoeller.de> - * @author Frank Karlitschek <frank@owncloud.org> - * @author Georg Ehrke <georg@owncloud.com> - * @author Jakob Sack <mail@jakobsack.de> - * @author Jan-Christoph Borchardt <hey@jancborchardt.net> - * @author Joas Schilling <nickvergessen@owncloud.com> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author Kamil Domanski <kdomanski@kdemail.net> - * @author Lukas Reschke <lukas@owncloud.com> - * @author Markus Goetz <markus@woboq.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @author RealRancor <Fisch.666@gmx.de> - * @author Robin Appelman <icewind@owncloud.com> - * @author Robin McCorkell <robin@mccorkell.me.uk> - * @author Roeland Jago Douma <rullzer@owncloud.com> - * @author Sam Tuke <mail@samtuke.com> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Thomas Tanghus <thomas@tanghus.net> - * @author Tom Needham <tom@owncloud.com> - * @author Vincent Petry <pvince81@owncloud.com> - * - * @copyright Copyright (c) 2016, ownCloud, Inc. - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program 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, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * - */ -use OC\App\DependencyAnalyzer; -use OC\App\Platform; -use OC\OCSClient; -use OC\Repair; - -/** - * This class manages the apps. It allows them to register and integrate in the - * ownCloud ecosystem. Furthermore, this class is responsible for installing, - * upgrading and removing apps. - */ -class OC_App { - static private $appVersion = []; - static private $adminForms = array(); - static private $personalForms = array(); - static private $appInfo = array(); - static private $appTypes = array(); - static private $loadedApps = array(); - static private $altLogin = array(); - const officialApp = 200; - - /** - * clean the appId - * - * @param string|boolean $app AppId that needs to be cleaned - * @return string - */ - public static function cleanAppId($app) { - return str_replace(array('\0', '/', '\\', '..'), '', $app); - } - - /** - * Check if an app is loaded - * - * @param string $app - * @return bool - */ - public static function isAppLoaded($app) { - return in_array($app, self::$loadedApps, true); - } - - /** - * loads all apps - * - * @param string[] | string | null $types - * @return bool - * - * This function walks through the ownCloud directory and loads all apps - * it can find. A directory contains an app if the file /appinfo/info.xml - * exists. - * - * if $types is set, only apps of those types will be loaded - */ - public static function loadApps($types = null) { - if (\OC::$server->getSystemConfig()->getValue('maintenance', false)) { - return false; - } - // Load the enabled apps here - $apps = self::getEnabledApps(); - - // Add each apps' folder as allowed class path - foreach($apps as $app) { - $path = self::getAppPath($app); - if($path !== false) { - \OC::$loader->addValidRoot($path); - } - } - - // prevent app.php from printing output - ob_start(); - foreach ($apps as $app) { - if ((is_null($types) or self::isType($app, $types)) && !in_array($app, self::$loadedApps)) { - self::loadApp($app); - } - } - ob_end_clean(); - - return true; - } - - /** - * load a single app - * - * @param string $app - * @param bool $checkUpgrade whether an upgrade check should be done - * @throws \OC\NeedsUpdateException - */ - public static function loadApp($app, $checkUpgrade = true) { - self::$loadedApps[] = $app; - $appPath = self::getAppPath($app); - if($appPath === false) { - return; - } - \OC::$loader->addValidRoot($appPath); // in case someone calls loadApp() directly - if (is_file($appPath . '/appinfo/app.php')) { - \OC::$server->getEventLogger()->start('load_app_' . $app, 'Load app: ' . $app); - if ($checkUpgrade and self::shouldUpgrade($app)) { - throw new \OC\NeedsUpdateException(); - } - self::requireAppFile($app); - if (self::isType($app, array('authentication'))) { - // since authentication apps affect the "is app enabled for group" check, - // the enabled apps cache needs to be cleared to make sure that the - // next time getEnableApps() is called it will also include apps that were - // enabled for groups - self::$enabledAppsCache = array(); - } - \OC::$server->getEventLogger()->end('load_app_' . $app); - } - } - - /** - * Load app.php from the given app - * - * @param string $app app name - */ - private static function requireAppFile($app) { - try { - // encapsulated here to avoid variable scope conflicts - require_once $app . '/appinfo/app.php'; - } catch (Error $ex) { - \OC::$server->getLogger()->logException($ex); - $blacklist = \OC::$server->getAppManager()->getAlwaysEnabledApps(); - if (!in_array($app, $blacklist)) { - self::disable($app); - } - } - } - - /** - * check if an app is of a specific type - * - * @param string $app - * @param string|array $types - * @return bool - */ - public static function isType($app, $types) { - if (is_string($types)) { - $types = array($types); - } - $appTypes = self::getAppTypes($app); - foreach ($types as $type) { - if (array_search($type, $appTypes) !== false) { - return true; - } - } - return false; - } - - /** - * get the types of an app - * - * @param string $app - * @return array - */ - private static function getAppTypes($app) { - //load the cache - if (count(self::$appTypes) == 0) { - self::$appTypes = \OC::$server->getAppConfig()->getValues(false, 'types'); - } - - if (isset(self::$appTypes[$app])) { - return explode(',', self::$appTypes[$app]); - } else { - return array(); - } - } - - /** - * read app types from info.xml and cache them in the database - */ - public static function setAppTypes($app) { - $appData = self::getAppInfo($app); - if(!is_array($appData)) { - return; - } - - if (isset($appData['types'])) { - $appTypes = implode(',', $appData['types']); - } else { - $appTypes = ''; - } - - \OC::$server->getAppConfig()->setValue($app, 'types', $appTypes); - } - - /** - * check if app is shipped - * - * @param string $appId the id of the app to check - * @return bool - * - * Check if an app that is installed is a shipped app or installed from the appstore. - */ - public static function isShipped($appId) { - return \OC::$server->getAppManager()->isShipped($appId); - } - - /** - * get all enabled apps - */ - protected static $enabledAppsCache = array(); - - /** - * Returns apps enabled for the current user. - * - * @param bool $forceRefresh whether to refresh the cache - * @param bool $all whether to return apps for all users, not only the - * currently logged in one - * @return string[] - */ - public static function getEnabledApps($forceRefresh = false, $all = false) { - if (!\OC::$server->getSystemConfig()->getValue('installed', false)) { - return array(); - } - // in incognito mode or when logged out, $user will be false, - // which is also the case during an upgrade - $appManager = \OC::$server->getAppManager(); - if ($all) { - $user = null; - } else { - $user = \OC::$server->getUserSession()->getUser(); - } - - if (is_null($user)) { - $apps = $appManager->getInstalledApps(); - } else { - $apps = $appManager->getEnabledAppsForUser($user); - } - $apps = array_filter($apps, function ($app) { - return $app !== 'files';//we add this manually - }); - sort($apps); - array_unshift($apps, 'files'); - return $apps; - } - - /** - * checks whether or not an app is enabled - * - * @param string $app app - * @return bool - * - * This function checks whether or not an app is enabled. - */ - public static function isEnabled($app) { - return \OC::$server->getAppManager()->isEnabledForUser($app); - } - - /** - * enables an app - * - * @param mixed $app app - * @param array $groups (optional) when set, only these groups will have access to the app - * @throws \Exception - * @return void - * - * This function set an app as enabled in appconfig. - */ - public static function enable($app, $groups = null) { - self::$enabledAppsCache = array(); // flush - if (!OC_Installer::isInstalled($app)) { - $app = self::installApp($app); - } - - $appManager = \OC::$server->getAppManager(); - if (!is_null($groups)) { - $groupManager = \OC::$server->getGroupManager(); - $groupsList = []; - foreach ($groups as $group) { - $groupItem = $groupManager->get($group); - if ($groupItem instanceof \OCP\IGroup) { - $groupsList[] = $groupManager->get($group); - } - } - $appManager->enableAppForGroups($app, $groupsList); - } else { - $appManager->enableApp($app); - } - } - - /** - * @param string $app - * @return int - */ - private static function downloadApp($app) { - $ocsClient = new OCSClient( - \OC::$server->getHTTPClientService(), - \OC::$server->getConfig(), - \OC::$server->getLogger() - ); - $appData = $ocsClient->getApplication($app, \OCP\Util::getVersion()); - $download = $ocsClient->getApplicationDownload($app, \OCP\Util::getVersion()); - 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 - * @return bool - */ - public static function removeApp($app) { - if (self::isShipped($app)) { - return false; - } - - return OC_Installer::removeApp($app); - } - - /** - * This function set an app as disabled in appconfig. - * - * @param string $app app - * @throws Exception - */ - public static function disable($app) { - // Convert OCS ID to regular application identifier - if(self::getInternalAppIdByOcs($app) !== false) { - $app = self::getInternalAppIdByOcs($app); - } - - self::$enabledAppsCache = array(); // flush - // check if app is a shipped app or not. if not delete - \OC_Hook::emit('OC_App', 'pre_disable', array('app' => $app)); - $appManager = \OC::$server->getAppManager(); - $appManager->disableApp($app); - } - - /** - * Returns the Settings Navigation - * - * @return string[] - * - * This function returns an array containing all settings pages added. The - * entries are sorted by the key 'order' ascending. - */ - public static function getSettingsNavigation() { - $l = \OC::$server->getL10N('lib'); - $urlGenerator = \OC::$server->getURLGenerator(); - - $settings = array(); - // by default, settings only contain the help menu - if (OC_Util::getEditionString() === '' && - \OC::$server->getSystemConfig()->getValue('knowledgebaseenabled', true) == true - ) { - $settings = array( - array( - "id" => "help", - "order" => 1000, - "href" => $urlGenerator->linkToRoute('settings_help'), - "name" => $l->t("Help"), - "icon" => $urlGenerator->imagePath("settings", "help.svg") - ) - ); - } - - // if the user is logged-in - if (OC_User::isLoggedIn()) { - // personal menu - $settings[] = array( - "id" => "personal", - "order" => 1, - "href" => $urlGenerator->linkToRoute('settings_personal'), - "name" => $l->t("Personal"), - "icon" => $urlGenerator->imagePath("settings", "personal.svg") - ); - - //SubAdmins are also allowed to access user management - $userObject = \OC::$server->getUserSession()->getUser(); - $isSubAdmin = false; - if($userObject !== null) { - $isSubAdmin = \OC::$server->getGroupManager()->getSubAdmin()->isSubAdmin($userObject); - } - if ($isSubAdmin) { - // admin users menu - $settings[] = array( - "id" => "core_users", - "order" => 2, - "href" => $urlGenerator->linkToRoute('settings_users'), - "name" => $l->t("Users"), - "icon" => $urlGenerator->imagePath("settings", "users.svg") - ); - } - - // if the user is an admin - if (OC_User::isAdminUser(OC_User::getUser())) { - // admin settings - $settings[] = array( - "id" => "admin", - "order" => 1000, - "href" => $urlGenerator->linkToRoute('settings_admin'), - "name" => $l->t("Admin"), - "icon" => $urlGenerator->imagePath("settings", "admin.svg") - ); - } - } - - $navigation = self::proceedNavigation($settings); - return $navigation; - } - - // This is private as well. It simply works, so don't ask for more details - 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; - } - } - unset($navEntry); - - usort($list, create_function('$a, $b', 'if( $a["order"] == $b["order"] ) {return 0;}elseif( $a["order"] < $b["order"] ) {return -1;}else{return 1;}')); - - return $list; - } - - /** - * Get the path where to install apps - * - * @return string|false - */ - public static function getInstallPath() { - if (\OC::$server->getSystemConfig()->getValue('appstoreenabled', true) == false) { - return false; - } - - foreach (OC::$APPSROOTS as $dir) { - if (isset($dir['writable']) && $dir['writable'] === true) { - return $dir['path']; - } - } - - \OCP\Util::writeLog('core', 'No application directories are marked as writable.', \OCP\Util::ERROR); - return null; - } - - - /** - * search for an app in all app-directories - * - * @param string $appId - * @return false|string - */ - protected static function findAppInDirectories($appId) { - $sanitizedAppId = self::cleanAppId($appId); - if($sanitizedAppId !== $appId) { - return false; - } - static $app_dir = array(); - - if (isset($app_dir[$appId])) { - return $app_dir[$appId]; - } - - $possibleApps = array(); - foreach (OC::$APPSROOTS as $dir) { - if (file_exists($dir['path'] . '/' . $appId)) { - $possibleApps[] = $dir; - } - } - - 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 - } - } - - /** - * 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; - } - return false; - } - - - /** - * 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; - } - - /** - * 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; - } - return false; - } - - /** - * get the last version of the app from appinfo/info.xml - * - * @param string $appId - * @return string - */ - public static function getAppVersion($appId) { - if (!isset(self::$appVersion[$appId])) { - $file = self::getAppPath($appId); - self::$appVersion[$appId] = ($file !== false) ? self::getAppVersionByPath($file) : '0'; - } - return self::$appVersion[$appId]; - } - - /** - * get app's version based on it's path - * - * @param string $path - * @return string - */ - public static function getAppVersionByPath($path) { - $infoFile = $path . '/appinfo/info.xml'; - $appData = self::getAppInfo($infoFile, true); - return isset($appData['version']) ? $appData['version'] : ''; - } - - - /** - * Read all app metadata from the info.xml file - * - * @param string $appId id of the app or the path of the info.xml file - * @param boolean $path (optional) - * @return array|null - * @note all data is read from info.xml, not just pre-defined fields - */ - public static function getAppInfo($appId, $path = false) { - if ($path) { - $file = $appId; - } else { - if (isset(self::$appInfo[$appId])) { - return self::$appInfo[$appId]; - } - $appPath = self::getAppPath($appId); - if($appPath === false) { - return null; - } - $file = $appPath . '/appinfo/info.xml'; - } - - $parser = new \OC\App\InfoParser(\OC::$server->getURLGenerator()); - $data = $parser->parse($file); - - if (is_array($data)) { - $data = OC_App::parseAppInfo($data); - } - if(isset($data['ocsid'])) { - $storedId = \OC::$server->getConfig()->getAppValue($appId, 'ocsid'); - if($storedId !== '' && $storedId !== $data['ocsid']) { - $data['ocsid'] = $storedId; - } - } - - self::$appInfo[$appId] = $data; - - return $data; - } - - /** - * Returns the navigation - * - * @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() { - $entries = OC::$server->getNavigationManager()->getAll(); - $navigation = self::proceedNavigation($entries); - return $navigation; - } - - /** - * get the id of loaded app - * - * @return string - */ - public static function getCurrentApp() { - $request = \OC::$server->getRequest(); - $script = substr($request->getScriptName(), strlen(OC::$WEBROOT) + 1); - $topFolder = substr($script, 0, strpos($script, '/')); - if (empty($topFolder)) { - $path_info = $request->getPathInfo(); - if ($path_info) { - $topFolder = substr($path_info, 1, strpos($path_info, '/', 1) - 1); - } - } - if ($topFolder == 'apps') { - $length = strlen($topFolder); - return substr($script, $length + 1, strpos($script, '/', $length + 1) - $length - 1); - } else { - return $topFolder; - } - } - - /** - * @param string $type - * @return array - */ - public static function getForms($type) { - $forms = array(); - switch ($type) { - case 'admin': - $source = self::$adminForms; - break; - case 'personal': - $source = self::$personalForms; - break; - default: - return array(); - } - foreach ($source as $form) { - $forms[] = include $form; - } - return $forms; - } - - /** - * register an admin form to be shown - * - * @param string $app - * @param string $page - */ - public static function registerAdmin($app, $page) { - self::$adminForms[] = $app . '/' . $page . '.php'; - } - - /** - * register a personal form to be shown - * @param string $app - * @param string $page - */ - public static function registerPersonal($app, $page) { - self::$personalForms[] = $app . '/' . $page . '.php'; - } - - /** - * @param array $entry - */ - public static function registerLogIn(array $entry) { - self::$altLogin[] = $entry; - } - - /** - * @return array - */ - public static function getAlternativeLogIns() { - return self::$altLogin; - } - - /** - * get a list of all apps in the apps folder - * - * @return array an array of app names (string IDs) - * @todo: change the name of this method to getInstalledApps, which is more accurate - */ - public static function getAllApps() { - - $apps = array(); - - foreach (OC::$APPSROOTS as $apps_dir) { - if (!is_readable($apps_dir['path'])) { - \OCP\Util::writeLog('core', 'unable to read app folder : ' . $apps_dir['path'], \OCP\Util::WARN); - continue; - } - $dh = opendir($apps_dir['path']); - - if (is_resource($dh)) { - while (($file = readdir($dh)) !== false) { - - if ($file[0] != '.' and is_dir($apps_dir['path'] . '/' . $file) and is_file($apps_dir['path'] . '/' . $file . '/appinfo/info.xml')) { - - $apps[] = $file; - } - } - } - } - - return $apps; - } - - /** - * List all apps, this is used in apps.php - * - * @param bool $onlyLocal - * @param bool $includeUpdateInfo Should we check whether there is an update - * in the app store? - * @param OCSClient $ocsClient - * @return array - */ - public static function listAllApps($onlyLocal = false, - $includeUpdateInfo = true, - OCSClient $ocsClient) { - $installedApps = OC_App::getAllApps(); - - //TODO which apps do we want to blacklist and how do we integrate - // blacklisting with the multi apps folder feature? - - //we don't want to show configuration for these - $blacklist = \OC::$server->getAppManager()->getAlwaysEnabledApps(); - $appList = array(); - - foreach ($installedApps as $app) { - if (array_search($app, $blacklist) === false) { - - $info = OC_App::getAppInfo($app); - if (!is_array($info)) { - \OCP\Util::writeLog('core', 'Could not read app info file for app "' . $app . '"', \OCP\Util::ERROR); - continue; - } - - if (!isset($info['name'])) { - \OCP\Util::writeLog('core', 'App id "' . $app . '" has no name in appinfo', \OCP\Util::ERROR); - continue; - } - - $enabled = \OC::$server->getAppConfig()->getValue($app, 'enabled', 'no'); - $info['groups'] = null; - if ($enabled === 'yes') { - $active = true; - } else if ($enabled === 'no') { - $active = false; - } else { - $active = true; - $info['groups'] = $enabled; - } - - $info['active'] = $active; - - if (self::isShipped($app)) { - $info['internal'] = true; - $info['level'] = self::officialApp; - $info['removable'] = false; - } else { - $info['internal'] = false; - $info['removable'] = true; - } - - $info['update'] = ($includeUpdateInfo) ? OC_Installer::isUpdateAvailable($app) : null; - - $appPath = self::getAppPath($app); - if($appPath !== false) { - $appIcon = $appPath . '/img/' . $app . '.svg'; - if (file_exists($appIcon)) { - $info['preview'] = \OC::$server->getURLGenerator()->imagePath($app, $app . '.svg'); - $info['previewAsIcon'] = true; - } else { - $appIcon = $appPath . '/img/app.svg'; - if (file_exists($appIcon)) { - $info['preview'] = \OC::$server->getURLGenerator()->imagePath($app, 'app.svg'); - $info['previewAsIcon'] = true; - } - } - } - $info['version'] = OC_App::getAppVersion($app); - $appList[] = $info; - } - } - if ($onlyLocal) { - $remoteApps = []; - } else { - $remoteApps = OC_App::getAppstoreApps('approved', null, $ocsClient); - } - if ($remoteApps) { - // Remove duplicates - foreach ($appList as $app) { - foreach ($remoteApps AS $key => $remote) { - if ($app['name'] === $remote['name'] || - (isset($app['ocsid']) && - $app['ocsid'] === $remote['id']) - ) { - unset($remoteApps[$key]); - } - } - } - $combinedApps = array_merge($appList, $remoteApps); - } else { - $combinedApps = $appList; - } - - return $combinedApps; - } - - /** - * Returns the internal app ID or false - * @param string $ocsID - * @return string|false - */ - public static function getInternalAppIdByOcs($ocsID) { - if(is_numeric($ocsID)) { - $idArray = \OC::$server->getAppConfig()->getValues(false, 'ocsid'); - if(array_search($ocsID, $idArray)) { - return array_search($ocsID, $idArray); - } - } - return false; - } - - /** - * Get a list of all apps on the appstore - * @param string $filter - * @param string|null $category - * @param OCSClient $ocsClient - * @return array|bool multi-dimensional array of apps. - * Keys: id, name, type, typename, personid, license, detailpage, preview, changed, description - */ - public static function getAppstoreApps($filter = 'approved', - $category = null, - OCSClient $ocsClient) { - $categories = [$category]; - - if (is_null($category)) { - $categoryNames = $ocsClient->getCategories(\OCP\Util::getVersion()); - if (is_array($categoryNames)) { - // Check that categories of apps were retrieved correctly - if (!$categories = array_keys($categoryNames)) { - return false; - } - } else { - return false; - } - } - - $page = 0; - $remoteApps = $ocsClient->getApplications($categories, $page, $filter, \OCP\Util::getVersion()); - $apps = []; - $i = 0; - $l = \OC::$server->getL10N('core'); - foreach ($remoteApps as $app) { - $potentialCleanId = self::getInternalAppIdByOcs($app['id']); - // enhance app info (for example the description) - $apps[$i] = OC_App::parseAppInfo($app); - $apps[$i]['author'] = $app['personid']; - $apps[$i]['ocs_id'] = $app['id']; - $apps[$i]['internal'] = 0; - $apps[$i]['active'] = ($potentialCleanId !== false) ? self::isEnabled($potentialCleanId) : false; - $apps[$i]['update'] = false; - $apps[$i]['groups'] = false; - $apps[$i]['score'] = $app['score']; - $apps[$i]['removable'] = false; - if ($app['label'] == 'recommended') { - $apps[$i]['internallabel'] = (string)$l->t('Recommended'); - $apps[$i]['internalclass'] = 'recommendedapp'; - } - - // Apps from the appstore are always assumed to be compatible with the - // the current release as the initial filtering is done on the appstore - $apps[$i]['dependencies']['owncloud']['@attributes']['min-version'] = implode('.', \OCP\Util::getVersion()); - $apps[$i]['dependencies']['owncloud']['@attributes']['max-version'] = implode('.', \OCP\Util::getVersion()); - - $i++; - } - - - - if (empty($apps)) { - return false; - } else { - return $apps; - } - } - - public static function shouldUpgrade($app) { - $versions = self::getAppVersions(); - $currentVersion = OC_App::getAppVersion($app); - if ($currentVersion && isset($versions[$app])) { - $installedVersion = $versions[$app]; - if (version_compare($currentVersion, $installedVersion, '>')) { - return true; - } - } - return false; - } - - /** - * 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); - } - - /** - * 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) - * - * @return boolean true if compatible, otherwise false - */ - public static function isAppCompatible($ocVersion, $appInfo) { - $requireMin = ''; - $requireMax = ''; - if (isset($appInfo['dependencies']['owncloud']['@attributes']['min-version'])) { - $requireMin = $appInfo['dependencies']['owncloud']['@attributes']['min-version']; - } else if (isset($appInfo['requiremin'])) { - $requireMin = $appInfo['requiremin']; - } else if (isset($appInfo['require'])) { - $requireMin = $appInfo['require']; - } - - if (isset($appInfo['dependencies']['owncloud']['@attributes']['max-version'])) { - $requireMax = $appInfo['dependencies']['owncloud']['@attributes']['max-version']; - } else if (isset($appInfo['requiremax'])) { - $requireMax = $appInfo['requiremax']; - } - - if (is_array($ocVersion)) { - $ocVersion = implode('.', $ocVersion); - } - - if (!empty($requireMin) - && version_compare(self::adjustVersionParts($ocVersion, $requireMin), $requireMin, '<') - ) { - - return false; - } - - if (!empty($requireMax) - && version_compare(self::adjustVersionParts($ocVersion, $requireMax), $requireMax, '>') - ) { - return false; - } - - return true; - } - - /** - * get the installed version of all apps - */ - public static function getAppVersions() { - static $versions; - - if(!$versions) { - $appConfig = \OC::$server->getAppConfig(); - $versions = $appConfig->getValues(false, 'installed_version'); - } - return $versions; - } - - /** - * @param string $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::$server->getL10N('core'); - $config = \OC::$server->getConfig(); - $ocsClient = new OCSClient( - \OC::$server->getHTTPClientService(), - $config, - \OC::$server->getLogger() - ); - $appData = $ocsClient->getApplication($app, \OCP\Util::getVersion()); - - // 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 { - // Maybe the app is already installed - compare the version in this - // case and use the local already installed one. - // FIXME: This is a horrible hack. I feel sad. The god of code cleanness may forgive me. - $internalAppId = self::getInternalAppIdByOcs($app); - if($internalAppId !== false) { - if($appData && version_compare(\OC_App::getAppVersion($internalAppId), $appData['version'], '<')) { - $app = self::downloadApp($app); - } else { - self::enable($internalAppId); - $app = $internalAppId; - } - } else { - $app = self::downloadApp($app); - } - } - - if ($app !== false) { - // check if the app is compatible with this version of ownCloud - $info = self::getAppInfo($app); - if(!is_array($info)) { - throw new \Exception( - $l->t('App "%s" cannot be installed because appinfo file cannot be read.', - [$info['name']] - ) - ); - } - - $version = \OCP\Util::getVersion(); - if (!self::isAppCompatible($version, $info)) { - throw new \Exception( - $l->t('App "%s" cannot be installed because it is not compatible with this version of ownCloud.', - array($info['name']) - ) - ); - } - - // check for required dependencies - $dependencyAnalyzer = new DependencyAnalyzer(new Platform($config), $l); - $missing = $dependencyAnalyzer->analyze($info); - if (!empty($missing)) { - $missingMsg = join(PHP_EOL, $missing); - throw new \Exception( - $l->t('App "%s" cannot be installed because the following dependencies are not fulfilled: %s', - array($info['name'], $missingMsg) - ) - ); - } - - $config->setAppValue($app, 'enabled', 'yes'); - if (isset($appData['id'])) { - $config->setAppValue($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; - } - - /** - * update the database for the app and call the update script - * - * @param string $appId - * @return bool - */ - public static function updateApp($appId) { - $appPath = self::getAppPath($appId); - if($appPath === false) { - return false; - } - $appData = self::getAppInfo($appId); - self::executeRepairSteps($appId, $appData['repair-steps']['pre-migration']); - if (file_exists($appPath . '/appinfo/database.xml')) { - OC_DB::updateDbFromStructure($appPath . '/appinfo/database.xml'); - } - self::executeRepairSteps($appId, $appData['repair-steps']['post-migration']); - self::setupLiveMigrations($appId, $appData['repair-steps']['live-migration']); - unset(self::$appVersion[$appId]); - // run upgrade code - if (file_exists($appPath . '/appinfo/update.php')) { - self::loadApp($appId, false); - include $appPath . '/appinfo/update.php'; - } - - //set remote/public handlers - if (array_key_exists('ocsid', $appData)) { - \OC::$server->getConfig()->setAppValue($appId, 'ocsid', $appData['ocsid']); - } elseif(\OC::$server->getConfig()->getAppValue($appId, 'ocsid', null) !== null) { - \OC::$server->getConfig()->deleteAppValue($appId, 'ocsid'); - } - foreach ($appData['remote'] as $name => $path) { - \OC::$server->getConfig()->setAppValue('core', 'remote_' . $name, $appId . '/' . $path); - } - foreach ($appData['public'] as $name => $path) { - \OC::$server->getConfig()->setAppValue('core', 'public_' . $name, $appId . '/' . $path); - } - - self::setAppTypes($appId); - - $version = \OC_App::getAppVersion($appId); - \OC::$server->getAppConfig()->setValue($appId, 'installed_version', $version); - - return true; - } - - /** - * @param string $appId - * @param string[] $steps - * @throws \OC\NeedsUpdateException - */ - private static function executeRepairSteps($appId, array $steps) { - if (empty($steps)) { - return; - } - // load the app - self::loadApp($appId, false); - - $dispatcher = OC::$server->getEventDispatcher(); - - // load the steps - $r = new Repair([], $dispatcher); - foreach ($steps as $step) { - try { - $r->addStep($step); - } catch (Exception $ex) { - $r->emit('\OC\Repair', 'error', [$ex->getMessage()]); - \OC::$server->getLogger()->logException($ex); - } - } - // run the steps - $r->run(); - } - - /** - * @param string $appId - * @param string[] $steps - */ - private static function setupLiveMigrations($appId, array $steps) { - $queue = \OC::$server->getJobList(); - foreach ($steps as $step) { - $queue->add('OC\Migration\BackgroundRepair', [ - 'app' => $appId, - 'step' => $step]); - } - } - - /** - * @param string $appId - * @return \OC\Files\View|false - */ - 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); - } - return new \OC\Files\View('/' . OC_User::getUser() . '/' . $appId); - } else { - \OCP\Util::writeLog('core', 'Can\'t get app storage, app ' . $appId . ', user not logged in', \OCP\Util::ERROR); - return false; - } - } else { - \OCP\Util::writeLog('core', 'Can\'t get app storage, app ' . $appId . ' not enabled', \OCP\Util::ERROR); - return false; - } - } - - /** - * parses the app data array and enhanced the 'description' value - * - * @param array $data the app data - * @return array improved app data - */ - public static function parseAppInfo(array $data) { - - // just modify the description if it is available - // otherwise this will create a $data element with an empty 'description' - if (isset($data['description'])) { - if (is_string($data['description'])) { - // sometimes the description contains line breaks and they are then also - // shown in this way in the app management which isn't wanted as HTML - // manages line breaks itself - - // first of all we split on empty lines - $paragraphs = preg_split("!\n[[:space:]]*\n!mu", $data['description']); - - $result = []; - foreach ($paragraphs as $value) { - // replace multiple whitespace (tabs, space, newlines) inside a paragraph - // with a single space - also trims whitespace - $result[] = trim(preg_replace('![[:space:]]+!mu', ' ', $value)); - } - - // join the single paragraphs with a empty line in between - $data['description'] = implode("\n\n", $result); - - } else { - $data['description'] = ''; - } - } - - return $data; - } -} |