From 8acb54aa0b32a8b750f8ab3aba9f63aa931be7d1 Mon Sep 17 00:00:00 2001 From: Lukas Reschke Date: Mon, 31 Oct 2016 11:07:54 +0100 Subject: [PATCH] Add update support Signed-off-by: Lukas Reschke --- .../lib/Notification/BackgroundJob.php | 3 +- lib/private/App/DependencyAnalyzer.php | 9 +- lib/private/Installer.php | 317 ++++-------------- lib/private/Server.php | 32 ++ lib/private/Updater.php | 15 +- lib/private/legacy/app.php | 35 +- lib/private/legacy/util.php | 1 + settings/Controller/AppSettingsController.php | 28 +- settings/ajax/updateapp.php | 19 +- tests/lib/App/DependencyAnalyzerTest.php | 227 ++++++++++++- tests/lib/AppTest.php | 34 ++ tests/lib/InstallerTest.php | 89 ++--- tests/lib/ServerTest.php | 4 + 13 files changed, 445 insertions(+), 368 deletions(-) diff --git a/apps/updatenotification/lib/Notification/BackgroundJob.php b/apps/updatenotification/lib/Notification/BackgroundJob.php index 3a1aa5e0f1..7bcc0e8690 100644 --- a/apps/updatenotification/lib/Notification/BackgroundJob.php +++ b/apps/updatenotification/lib/Notification/BackgroundJob.php @@ -22,7 +22,6 @@ namespace OCA\UpdateNotification\Notification; - use OC\BackgroundJob\TimedJob; use OC\Installer; use OC\Updater\VersionCheck; @@ -215,6 +214,6 @@ class BackgroundJob extends TimedJob { * @return string|false */ protected function isUpdateAvailable($app) { - return Installer::isUpdateAvailable($app); + return Installer::isUpdateAvailable($app, \OC::$server->getAppFetcher()); } } diff --git a/lib/private/App/DependencyAnalyzer.php b/lib/private/App/DependencyAnalyzer.php index 67268981e9..c24b25ff14 100644 --- a/lib/private/App/DependencyAnalyzer.php +++ b/lib/private/App/DependencyAnalyzer.php @@ -1,6 +1,7 @@ * * @author Bernhard Posselt * @author Joas Schilling @@ -294,7 +295,9 @@ class DependencyAnalyzer { private function analyzeOC(array $dependencies, array $appInfo) { $missing = []; $minVersion = null; - if (isset($dependencies['owncloud']['@attributes']['min-version'])) { + if (isset($dependencies['nextcloud']['@attributes']['min-version'])) { + $minVersion = $dependencies['nextcloud']['@attributes']['min-version']; + } elseif (isset($dependencies['owncloud']['@attributes']['min-version'])) { $minVersion = $dependencies['owncloud']['@attributes']['min-version']; } elseif (isset($appInfo['requiremin'])) { $minVersion = $appInfo['requiremin']; @@ -302,7 +305,9 @@ class DependencyAnalyzer { $minVersion = $appInfo['require']; } $maxVersion = null; - if (isset($dependencies['owncloud']['@attributes']['max-version'])) { + if (isset($dependencies['nextcloud']['@attributes']['max-version'])) { + $maxVersion = $dependencies['nextcloud']['@attributes']['max-version']; + } elseif (isset($dependencies['owncloud']['@attributes']['max-version'])) { $maxVersion = $dependencies['owncloud']['@attributes']['max-version']; } elseif (isset($appInfo['requiremax'])) { $maxVersion = $appInfo['requiremax']; diff --git a/lib/private/Installer.php b/lib/private/Installer.php index 58a91bb797..6aaa6fe6ac 100644 --- a/lib/private/Installer.php +++ b/lib/private/Installer.php @@ -1,6 +1,7 @@ * * @author Arthur Schiwon * @author Bart Visscher @@ -54,39 +55,37 @@ use OCP\ITempManager; use phpseclib\File\X509; /** - * This class provides the functionality needed to install, update and remove plugins/apps + * This class provides the functionality needed to install, update and remove apps */ class Installer { + /** @var AppFetcher */ + private $appFetcher; + /** @var IClientService */ + private $clientService; + /** @var ITempManager */ + private $tempManager; + /** @var ILogger */ + private $logger; /** + * @param AppFetcher $appFetcher + * @param IClientService $clientService + * @param ITempManager $tempManager + * @param ILogger $logger + */ + public function __construct(AppFetcher $appFetcher, + IClientService $clientService, + ITempManager $tempManager, + ILogger $logger) { + $this->appFetcher = $appFetcher; + $this->clientService = $clientService; + $this->tempManager = $tempManager; + $this->logger = $logger; + } + + /** + * Installs an app that is located in one of the app folders already * - * This function installs an app. All information needed are passed in the - * associative array $data. - * The following keys are required: - * - source: string, can be "path" or "http" - * - * One of the following keys is required: - * - path: path to the file containing the app - * - href: link to the downloadable file containing the app - * - * The following keys are optional: - * - pretend: boolean, if set true the system won't do anything - * - noinstall: boolean, if true appinfo/install.php won't be loaded - * - inactive: boolean, if set true the appconfig/app.sample.php won't be - * renamed - * - * This function works as follows - * -# fetching the file - * -# unzipping it - * -# check the code - * -# installing the database at appinfo/database.xml - * -# including appinfo/install.php - * -# setting the installed version - * - * It is the task of oc_app_install to create the tables and do whatever is - * needed to get the app working. - * - * Installs an app * @param string $appId App to install * @throws \Exception * @return integer @@ -143,114 +142,36 @@ class Installer { } /** - * @brief Update an application - * @param array $info - * @param bool $isShipped - * @throws \Exception + * Updates the specified app from the appstore + * + * @param string $appId * @return bool - * - * This function could work like described below, but currently it disables and then - * enables the app again. This does result in an updated app. - * - * - * This function installs an app. All information needed are passed in the - * associative array $info. - * The following keys are required: - * - source: string, can be "path" or "http" - * - * One of the following keys is required: - * - path: path to the file containing the app - * - href: link to the downloadable file containing the app - * - * The following keys are optional: - * - pretend: boolean, if set true the system won't do anything - * - noupgrade: boolean, if true appinfo/upgrade.php won't be loaded - * - * This function works as follows - * -# fetching the file - * -# removing the old files - * -# unzipping new file - * -# including appinfo/upgrade.php - * -# setting the installed version - * - * upgrade.php can determine the current installed version of the app using - * "\OC::$server->getAppConfig()->getValue($appid, 'installed_version')" */ - public static function updateApp($info=array(), $isShipped=false) { - list($extractDir, $path) = self::downloadApp($info); - $info = self::checkAppsIntegrity($info, $extractDir, $path, $isShipped); - - $currentDir = OC_App::getAppPath($info['id']); - $basedir = OC_App::getInstallPath(); - $basedir .= '/'; - $basedir .= $info['id']; - - if($currentDir !== false && is_writable($currentDir)) { - $basedir = $currentDir; - } - if(is_dir($basedir)) { - OC_Helper::rmdirr($basedir); + public function updateAppstoreApp($appId) { + if(self::isUpdateAvailable($appId, $this->appFetcher)) { + try { + $this->downloadApp($appId); + } catch (\Exception $e) { + $this->logger->error($e->getMessage(), ['app' => 'core']); + return false; + } + return OC_App::updateApp($appId); } - $appInExtractDir = $extractDir; - if (substr($extractDir, -1) !== '/') { - $appInExtractDir .= '/'; - } - - $appInExtractDir .= $info['id']; - OC_Helper::copyr($appInExtractDir, $basedir); - OC_Helper::rmdirr($extractDir); - - return OC_App::updateApp($info['id']); - } - - /** - * update an app by it's id - * - * @param integer $ocsId - * @return bool - * @throws \Exception - */ - public static function updateAppByOCSId($ocsId) { - $ocsClient = new OCSClient( - \OC::$server->getHTTPClientService(), - \OC::$server->getConfig(), - \OC::$server->getLogger() - ); - $appData = $ocsClient->getApplication($ocsId, \OCP\Util::getVersion()); - $download = $ocsClient->getApplicationDownload($ocsId, \OCP\Util::getVersion()); - - if (isset($download['downloadlink']) && trim($download['downloadlink']) !== '') { - $download['downloadlink'] = str_replace(' ', '%20', $download['downloadlink']); - $info = array( - 'source' => 'http', - 'href' => $download['downloadlink'], - 'appdata' => $appData - ); - } else { - throw new \Exception('Could not fetch app info!'); - } - - return self::updateApp($info); + return false; } /** * Downloads an app and puts it into the app directory * * @param string $appId - * @param AppFetcher $appFetcher - * @param IClientService $clientService - * @param ITempManager $tempManager * * @throws \Exception If the installation was not successful */ - public function downloadApp($appId, - AppFetcher $appFetcher, - IClientService $clientService, - ITempManager $tempManager) { + public function downloadApp($appId) { $appId = strtolower($appId); - $apps = $appFetcher->get(); + $apps = $this->appFetcher->get(); foreach($apps as $app) { if($app['id'] === $appId) { // Load the certificate @@ -316,8 +237,8 @@ class Installer { } // Download the release - $tempFile = $tempManager->getTemporaryFile('.tar.gz'); - $client = $clientService->newClient(); + $tempFile = $this->tempManager->getTemporaryFile('.tar.gz'); + $client = $this->clientService->newClient(); $client->get($app['releases'][0]['download'], ['save_to' => $tempFile]); // Check if the signature actually matches the downloaded content @@ -327,7 +248,7 @@ class Installer { if($verified === true) { // Seems to match, let's proceed - $extractDir = $tempManager->getTemporaryFolder(); + $extractDir = $this->tempManager->getTemporaryFolder(); $archive = Archive::open($tempFile); if($archive) { @@ -350,9 +271,10 @@ class Installer { ); } + $baseDir = OC_App::getInstallPath() . '/' . $appId; + // Remove old app with the ID if existent + OC_Helper::rmdirr($baseDir); // Move to app folder - $baseDir = OC_App::getInstallPath().'/'.$appId; - //copy the app to the correct place if(@mkdir($baseDir)) { $extractDir .= '/' . $appId; OC_Helper::copyr($extractDir, $baseDir); @@ -387,108 +309,15 @@ class Installer { } } - /** - * check an app's integrity - * @param array $data - * @param string $extractDir - * @param string $path - * @param bool $isShipped - * @return array - * @throws \Exception - */ - public static function checkAppsIntegrity($data, $extractDir, $path, $isShipped = false) { - $l = \OC::$server->getL10N('lib'); - //load the info.xml file of the app - if(!is_file($extractDir.'/appinfo/info.xml')) { - //try to find it in a subdir - $dh=opendir($extractDir); - if(is_resource($dh)) { - while (($folder = readdir($dh)) !== false) { - if($folder[0]!='.' and is_dir($extractDir.'/'.$folder)) { - if(is_file($extractDir.'/'.$folder.'/appinfo/info.xml')) { - $extractDir.='/'.$folder; - } - } - } - } - } - if(!is_file($extractDir.'/appinfo/info.xml')) { - OC_Helper::rmdirr($extractDir); - if($data['source'] === 'http') { - unlink($path); - } - throw new \Exception($l->t("App does not provide an info.xml file")); - } - - $info = OC_App::getAppInfo($extractDir.'/appinfo/info.xml', true); - if(!is_array($info)) { - throw new \Exception($l->t('App cannot be installed because appinfo file cannot be read.')); - } - - // We can't trust the parsed info.xml file as it may have been tampered - // with by an attacker and thus we need to use the local data to check - // whether the application needs to be signed. - $appId = OC_App::cleanAppId($data['appdata']['id']); - $appBelongingToId = OC_App::getInternalAppIdByOcs($appId); - if(is_string($appBelongingToId)) { - $previouslySigned = \OC::$server->getConfig()->getAppValue($appBelongingToId, 'signed', 'false'); - } else { - $appBelongingToId = $info['id']; - $previouslySigned = 'false'; - } - if($data['appdata']['level'] === OC_App::officialApp || $previouslySigned === 'true') { - \OC::$server->getConfig()->setAppValue($appBelongingToId, 'signed', 'true'); - $integrityResult = \OC::$server->getIntegrityCodeChecker()->verifyAppSignature( - $appBelongingToId, - $extractDir - ); - if($integrityResult !== []) { - $e = new \Exception( - $l->t( - 'Signature could not get checked. Please contact the app developer and check your admin screen.' - ) - ); - throw $e; - } - } - - // check the code for not allowed calls - if(!$isShipped && !Installer::checkCode($extractDir)) { - OC_Helper::rmdirr($extractDir); - throw new \Exception($l->t("App can't be installed because of not allowed code in the App")); - } - - // check if the app is compatible with this version of ownCloud - if(!OC_App::isAppCompatible(\OCP\Util::getVersion(), $info)) { - OC_Helper::rmdirr($extractDir); - throw new \Exception($l->t("App can't be installed because it is not compatible with this version of the server")); - } - - // check if shipped tag is set which is only allowed for apps that are shipped with ownCloud - if(!$isShipped && isset($info['shipped']) && ($info['shipped']=='true')) { - OC_Helper::rmdirr($extractDir); - throw new \Exception($l->t("App can't be installed because it contains the true tag which is not allowed for non shipped apps")); - } - - // check if the ocs version is the same as the version in info.xml/version - $version = trim($info['version']); - - if(isset($data['appdata']['version']) && $version<>trim($data['appdata']['version'])) { - OC_Helper::rmdirr($extractDir); - throw new \Exception($l->t("App can't be installed because the version in info.xml is not the same as the version reported from the app store")); - } - - return $info; - } - /** * Check if an update for the app is available - * @param string $app - * @return string|false false or the version number of the update * - * The function will check if an update for a version is available + * @param string $appId + * @param AppFetcher $appFetcher + * @return string|false false or the version number of the update */ - public static function isUpdateAvailable( $app ) { + public static function isUpdateAvailable($appId, + AppFetcher $appFetcher) { static $isInstanceReadyForUpdates = null; if ($isInstanceReadyForUpdates === null) { @@ -504,27 +333,20 @@ class Installer { return false; } - $ocsid=\OC::$server->getAppConfig()->getValue( $app, 'ocsid', ''); - - if($ocsid<>'') { - $ocsClient = new OCSClient( - \OC::$server->getHTTPClientService(), - \OC::$server->getConfig(), - \OC::$server->getLogger() - ); - $ocsdata = $ocsClient->getApplication($ocsid, \OCP\Util::getVersion()); - $ocsversion= (string) $ocsdata['version']; - $currentversion=OC_App::getAppVersion($app); - if (version_compare($ocsversion, $currentversion, '>')) { - return($ocsversion); - }else{ - return false; + $apps = $appFetcher->get(); + foreach($apps as $app) { + if($app['id'] === $appId) { + $currentVersion = OC_App::getAppVersion($appId); + $newestVersion = $app['releases'][0]['version']; + if (version_compare($newestVersion, $currentVersion, '>')) { + return $newestVersion; + } else { + return false; + } } - - }else{ - return false; } + return false; } /** @@ -562,13 +384,10 @@ class Installer { * The function will not delete preferences, tables and the configuration, * this has to be done by the function oc_app_uninstall(). */ - public static function removeApp($appId) { - $installer = new Installer(); - - if($installer->isDownloaded( $appId )) { - $appDir=OC_App::getInstallPath() . '/' . $appId; + public function removeApp($appId) { + if($this->isDownloaded( $appId )) { + $appDir = OC_App::getInstallPath() . '/' . $appId; OC_Helper::rmdirr($appDir); - return true; }else{ \OCP\Util::writeLog('core', 'can\'t remove app '.$appId.'. It is not installed.', \OCP\Util::ERROR); @@ -689,7 +508,7 @@ class Installer { } /** - * @param $basedir + * @param string $script */ private static function includeAppScript($script) { if ( file_exists($script) ){ diff --git a/lib/private/Server.php b/lib/private/Server.php index 39905dcf7c..6e481e1d3b 100644 --- a/lib/private/Server.php +++ b/lib/private/Server.php @@ -1,6 +1,7 @@ * * @author Arthur Schiwon * @author Bart Visscher @@ -41,6 +42,8 @@ namespace OC; use bantu\IniGetWrapper\IniGetWrapper; +use OC\App\AppStore\Fetcher\AppFetcher; +use OC\App\AppStore\Fetcher\CategoryFetcher; use OC\AppFramework\Http\Request; use OC\AppFramework\Db\Db; use OC\AppFramework\Utility\TimeFactory; @@ -320,6 +323,21 @@ class Server extends ServerContainer implements IServerContainer { $this->registerService('AppHelper', function ($c) { return new \OC\AppHelper(); }); + $this->registerService('AppFetcher', function ($c) { + return new AppFetcher( + $this->getAppDataDir('appstore'), + $this->getHTTPClientService(), + $this->query(TimeFactory::class), + $this->getConfig() + ); + }); + $this->registerService('CategoryFetcher', function ($c) { + return new CategoryFetcher( + $this->getAppDataDir('appstore'), + $this->getHTTPClientService(), + $this->query(TimeFactory::class) + ); + }); $this->registerService('UserCache', function ($c) { return new Cache\File(); }); @@ -1000,6 +1018,20 @@ class Server extends ServerContainer implements IServerContainer { return $this->query('AppHelper'); } + /** + * @return AppFetcher + */ + public function getAppFetcher() { + return $this->query('AppFetcher'); + } + + /** + * @return CategoryFetcher + */ + public function getCategoryFetcher() { + return $this->query('CategoryFetcher'); + } + /** * Returns an ICache instance. Since 8.1.0 it returns a fake cache. Use * getMemCacheFactory() instead. diff --git a/lib/private/Updater.php b/lib/private/Updater.php index 646fc031a8..cd2934f719 100644 --- a/lib/private/Updater.php +++ b/lib/private/Updater.php @@ -1,6 +1,7 @@ * * @author Arthur Schiwon * @author Frank Karlitschek @@ -426,11 +427,15 @@ class Updater extends BasicEmitter { private function upgradeAppStoreApps(array $disabledApps) { foreach($disabledApps as $app) { try { - if (Installer::isUpdateAvailable($app)) { - $ocsId = \OC::$server->getConfig()->getAppValue($app, 'ocsid', ''); - - $this->emit('\OC\Updater', 'upgradeAppStoreApp', array($app)); - Installer::updateAppByOCSId($ocsId); + $installer = new Installer( + \OC::$server->getAppFetcher(), + \OC::$server->getHTTPClientService(), + \OC::$server->getTempManager(), + $this->log + ); + if (Installer::isUpdateAvailable($app, \OC::$server->getAppFetcher())) { + $this->emit('\OC\Updater', 'upgradeAppStoreApp', [$app]); + $installer->updateAppstoreApp($app); } } catch (\Exception $ex) { $this->log->logException($ex, ['app' => 'core']); diff --git a/lib/private/legacy/app.php b/lib/private/legacy/app.php index 43830b5fd1..6b5c4f3978 100644 --- a/lib/private/legacy/app.php +++ b/lib/private/legacy/app.php @@ -341,21 +341,16 @@ class OC_App { $config = \OC::$server->getConfig(); // Check if app is already downloaded - $installer = new Installer(); + $installer = new Installer( + \OC::$server->getAppFetcher(), + \OC::$server->getHTTPClientService(), + \OC::$server->getTempManager(), + \OC::$server->getLogger() + ); $isDownloaded = $installer->isDownloaded($appId); if(!$isDownloaded) { - $installer->downloadApp( - $appId, - new \OC\App\AppStore\Fetcher\AppFetcher( - \OC::$server->getAppDataDir('appstore'), - \OC::$server->getHTTPClientService(), - \OC::$server->query(\OC\AppFramework\Utility\TimeFactory::class), - $config - ), - \OC::$server->getHTTPClientService(), - \OC::$server->getTempManager() - ); + $installer->downloadApp($appId); } if (!Installer::isInstalled($appId)) { @@ -404,7 +399,13 @@ class OC_App { return false; } - return Installer::removeApp($app); + $installer = new Installer( + \OC::$server->getAppFetcher(), + \OC::$server->getHTTPClientService(), + \OC::$server->getTempManager(), + \OC::$server->getLogger() + ); + return $installer->removeApp($app); } /** @@ -975,7 +976,9 @@ class OC_App { public static function isAppCompatible($ocVersion, $appInfo) { $requireMin = ''; $requireMax = ''; - if (isset($appInfo['dependencies']['owncloud']['@attributes']['min-version'])) { + if (isset($appInfo['dependencies']['nextcloud']['@attributes']['min-version'])) { + $requireMin = $appInfo['dependencies']['nextcloud']['@attributes']['min-version']; + } elseif (isset($appInfo['dependencies']['owncloud']['@attributes']['min-version'])) { $requireMin = $appInfo['dependencies']['owncloud']['@attributes']['min-version']; } else if (isset($appInfo['requiremin'])) { $requireMin = $appInfo['requiremin']; @@ -983,7 +986,9 @@ class OC_App { $requireMin = $appInfo['require']; } - if (isset($appInfo['dependencies']['owncloud']['@attributes']['max-version'])) { + if (isset($appInfo['dependencies']['nextcloud']['@attributes']['max-version'])) { + $requireMax = $appInfo['dependencies']['nextcloud']['@attributes']['max-version']; + } elseif (isset($appInfo['dependencies']['owncloud']['@attributes']['max-version'])) { $requireMax = $appInfo['dependencies']['owncloud']['@attributes']['max-version']; } else if (isset($appInfo['requiremax'])) { $requireMax = $appInfo['requiremax']; diff --git a/lib/private/legacy/util.php b/lib/private/legacy/util.php index e4c2caeafd..5cd92eaa41 100644 --- a/lib/private/legacy/util.php +++ b/lib/private/legacy/util.php @@ -757,6 +757,7 @@ class OC_Util { 'simplexml_load_string' => 'SimpleXML', 'hash' => 'HASH Message Digest Framework', 'curl_init' => 'cURL', + 'openssl_verify' => 'OpenSSL', ], 'defined' => array( 'PDO::ATTR_DRIVER_NAME' => 'PDO' diff --git a/settings/Controller/AppSettingsController.php b/settings/Controller/AppSettingsController.php index 16d4780c5f..1222b6bc86 100644 --- a/settings/Controller/AppSettingsController.php +++ b/settings/Controller/AppSettingsController.php @@ -37,7 +37,6 @@ use \OCP\AppFramework\Controller; use OCP\AppFramework\Http\ContentSecurityPolicy; use OCP\AppFramework\Http\JSONResponse; use OCP\AppFramework\Http\TemplateResponse; -use OCP\ICacheFactory; use OCP\INavigationManager; use OCP\IRequest; use OCP\IL10N; @@ -55,8 +54,6 @@ class AppSettingsController extends Controller { private $l10n; /** @var IConfig */ private $config; - /** @var \OCP\ICache */ - private $cache; /** @var INavigationManager */ private $navigationManager; /** @var IAppManager */ @@ -73,7 +70,6 @@ class AppSettingsController extends Controller { * @param IRequest $request * @param IL10N $l10n * @param IConfig $config - * @param ICacheFactory $cache * @param INavigationManager $navigationManager * @param IAppManager $appManager * @param CategoryFetcher $categoryFetcher @@ -84,7 +80,6 @@ class AppSettingsController extends Controller { IRequest $request, IL10N $l10n, IConfig $config, - ICacheFactory $cache, INavigationManager $navigationManager, IAppManager $appManager, CategoryFetcher $categoryFetcher, @@ -93,7 +88,6 @@ class AppSettingsController extends Controller { parent::__construct($appName, $request); $this->l10n = $l10n; $this->config = $config; - $this->cache = $cache->create($appName); $this->navigationManager = $navigationManager; $this->appManager = $appManager; $this->categoryFetcher = $categoryFetcher; @@ -201,6 +195,18 @@ class AppSettingsController extends Controller { } $currentLanguage = substr(\OC::$server->getL10NFactory()->findLanguage(), 0, 2); + $enabledValue = $this->config->getAppValue($app['id'], 'enabled', 'no'); + $groups = null; + if($enabledValue !== 'no' && $enabledValue !== 'yes') { + $groups = $enabledValue; + } + + $currentVersion = ''; + if($this->appManager->isInstalled($app['id'])) { + $currentVersion = \OC_App::getAppVersion($app['id']); + } else { + $currentLanguage = $app['releases'][0]['version']; + } $formattedApps[] = [ 'id' => $app['id'], @@ -209,7 +215,7 @@ class AppSettingsController extends Controller { 'license' => $app['releases'][0]['licenses'], 'author' => $authors, 'shipped' => false, - 'version' => $app['releases'][0]['version'], + 'version' => $currentVersion, 'default_enable' => '', 'types' => [], 'documentation' => [ @@ -233,7 +239,15 @@ class AppSettingsController extends Controller { 'removable' => $existsLocally, 'active' => $this->appManager->isEnabledForUser($app['id']), 'needsDownload' => !$existsLocally, + 'groups' => $groups, ]; + + + $appFetcher = \OC::$server->getAppFetcher(); + $newVersion = \OC\Installer::isUpdateAvailable($app['id'], $appFetcher); + if($newVersion) { + $formattedApps[count($formattedApps)-1]['update'] = $newVersion; + } } return $formattedApps; diff --git a/settings/ajax/updateapp.php b/settings/ajax/updateapp.php index 47ecac26cf..3020f82857 100644 --- a/settings/ajax/updateapp.php +++ b/settings/ajax/updateapp.php @@ -35,23 +35,18 @@ if (!array_key_exists('appid', $_POST)) { } $appId = (string)$_POST['appid']; - -if (!is_numeric($appId)) { - $appId = \OC::$server->getAppConfig()->getValue($appId, 'ocsid', null); - if ($appId === null) { - OCP\JSON::error(array( - 'message' => 'No OCS-ID found for app!' - )); - exit; - } -} - $appId = OC_App::cleanAppId($appId); $config = \OC::$server->getConfig(); $config->setSystemValue('maintenance', true); try { - $result = \OC\Installer::updateAppByOCSId($appId); + $installer = new \OC\Installer( + \OC::$server->getAppFetcher(), + \OC::$server->getHTTPClientService(), + \OC::$server->getTempManager(), + \OC::$server->getLogger() + ); + $result = $installer->updateAppstoreApp($appId); $config->setSystemValue('maintenance', false); } catch(Exception $ex) { $config->setSystemValue('maintenance', false); diff --git a/tests/lib/App/DependencyAnalyzerTest.php b/tests/lib/App/DependencyAnalyzerTest.php index c41829b796..fd44954eaf 100644 --- a/tests/lib/App/DependencyAnalyzerTest.php +++ b/tests/lib/App/DependencyAnalyzerTest.php @@ -1,9 +1,10 @@ + * * See the COPYING-README file. */ @@ -187,7 +188,7 @@ class DependencyAnalyzerTest extends TestCase { 'dependencies' => array() ); if (!is_null($oc)) { - $app['dependencies']['owncloud'] = $oc; + $app['dependencies'] = $oc; } $missing = $this->analyser->analyze($app); @@ -200,18 +201,216 @@ class DependencyAnalyzerTest extends TestCase { * @return array */ function providesOC() { - return array( + return [ // no version -> no missing dependency - array(array(), null), - array(array(), array('@attributes' => array('min-version' => '8', 'max-version' => '8'))), - array(array(), array('@attributes' => array('min-version' => '8.0', 'max-version' => '8.0'))), - array(array(), array('@attributes' => array('min-version' => '8.0.2', 'max-version' => '8.0.2'))), - array(array('Server version 8.0.3 or higher is required.'), array('@attributes' => array('min-version' => '8.0.3'))), - array(array('Server version 9 or higher is required.'), array('@attributes' => array('min-version' => '9'))), - array(array('Server version 10 or higher is required.'), array('@attributes' => array('min-version' => '9.1'))), - array(array('Server version 11 or higher is required.'), array('@attributes' => array('min-version' => '9.2'))), - [['Server version 8.0.1 or lower is required.'], ['@attributes' => ['max-version' => '8.0.1']]], - ); + [ + [], + null, + ], + [ + [], + [ + 'nextcloud' => [ + '@attributes' => [ + 'min-version' => '8', + 'max-version' => '8', + ], + ], + ], + ], + [ + [], + [ + 'nextcloud' => [ + '@attributes' => [ + 'min-version' => '8.0', + 'max-version' => '8.0', + ], + ], + ], + ], + [ + [], + [ + 'nextcloud' => [ + '@attributes' => [ + 'min-version' => '8.0.2', + 'max-version' => '8.0.2' + ], + ], + ], + ], + [ + [ + 'Server version 8.0.3 or higher is required.', + ], + [ + 'nextcloud' => [ + '@attributes' => [ + 'min-version' => '8.0.3' + ], + ], + ], + ], + [ + [ + 'Server version 9 or higher is required.', + ], + [ + 'nextcloud' => [ + '@attributes' => [ + 'min-version' => '9' + ], + ], + ], + ], + [ + [ + 'Server version 10 or higher is required.', + ], + [ + 'nextcloud' => [ + '@attributes' => [ + 'min-version' => '10' + ], + ], + 'owncloud' => [ + '@attributes' => [ + 'min-version' => '9' + ], + ], + ], + ], + [ + [ + 'Server version 10 or higher is required.', + ], + [ + 'nextcloud' => [ + '@attributes' => [ + 'min-version' => '9.1', + ], + ], + ], + ], + [ + [ + 'Server version 11 or higher is required.', + ], + [ + 'nextcloud' => [ + '@attributes' => [ + 'min-version' => '9.2', + ], + ], + ], + ], + [ + [ + 'Server version 8.0.1 or lower is required.', + ], + [ + 'nextcloud' => [ + '@attributes' => [ + 'max-version' => '8.0.1', + ], + ], + ], + ], + [ + [], + [ + 'owncloud' => [ + '@attributes' => [ + 'min-version' => '8', + 'max-version' => '8', + ], + ], + ], + ], + [ + [], + [ + 'owncloud' => [ + '@attributes' => [ + 'min-version' => '8.0', + 'max-version' => '8.0', + ], + ], + ], + ], + [ + [], + [ + 'owncloud' => [ + '@attributes' => [ + 'min-version' => '8.0.2', + 'max-version' => '8.0.2' + ], + ], + ], + ], + [ + [ + 'Server version 8.0.3 or higher is required.', + ], + [ + 'owncloud' => [ + '@attributes' => [ + 'min-version' => '8.0.3' + ], + ], + ], + ], + [ + [ + 'Server version 9 or higher is required.', + ], + [ + 'owncloud' => [ + '@attributes' => [ + 'min-version' => '9' + ], + ], + ], + ], + [ + [ + 'Server version 10 or higher is required.', + ], + [ + 'owncloud' => [ + '@attributes' => [ + 'min-version' => '9.1', + ], + ], + ], + ], + [ + [ + 'Server version 11 or higher is required.', + ], + [ + 'owncloud' => [ + '@attributes' => [ + 'min-version' => '9.2', + ], + ], + ], + ], + [ + [ + 'Server version 8.0.1 or lower is required.', + ], + [ + 'owncloud' => [ + '@attributes' => [ + 'max-version' => '8.0.1', + ], + ], + ], + ], + ]; } /** diff --git a/tests/lib/AppTest.php b/tests/lib/AppTest.php index b7263adb78..971d86cf6a 100644 --- a/tests/lib/AppTest.php +++ b/tests/lib/AppTest.php @@ -264,6 +264,40 @@ class AppTest extends \Test\TestCase { ), true ), + [ + '9.2.0.0', + [ + 'dependencies' => [ + 'owncloud' => [ + '@attributes' => [ + 'min-version' => '9.0', + 'max-version' => '9.1', + ], + ], + 'nextcloud' => [ + '@attributes' => [ + 'min-version' => '9.1', + 'max-version' => '9.2', + ], + ], + ], + ], + true + ], + [ + '9.2.0.0', + [ + 'dependencies' => [ + 'nextcloud' => [ + '@attributes' => [ + 'min-version' => '9.1', + 'max-version' => '9.2', + ], + ], + ], + ], + true + ], ); } diff --git a/tests/lib/InstallerTest.php b/tests/lib/InstallerTest.php index 11c0e2675b..fb19ee94aa 100644 --- a/tests/lib/InstallerTest.php +++ b/tests/lib/InstallerTest.php @@ -9,6 +9,7 @@ namespace Test; +use OC\Archive\ZIP; use OC\Installer; class InstallerTest extends TestCase { @@ -22,80 +23,44 @@ class InstallerTest extends TestCase { $config = \OC::$server->getConfig(); $this->appstore = $config->setSystemValue('appstoreenabled', true); $config->setSystemValue('appstoreenabled', true); - Installer::removeApp(self::$appid); + $installer = new Installer( + \OC::$server->getAppFetcher(), + \OC::$server->getHTTPClientService(), + \OC::$server->getTempManager(), + \OC::$server->getLogger() + ); + $installer->removeApp(self::$appid); } protected function tearDown() { - Installer::removeApp(self::$appid); + $installer = new Installer( + \OC::$server->getAppFetcher(), + \OC::$server->getHTTPClientService(), + \OC::$server->getTempManager(), + \OC::$server->getLogger() + ); + $installer->removeApp(self::$appid); \OC::$server->getConfig()->setSystemValue('appstoreenabled', $this->appstore); parent::tearDown(); } public function testInstallApp() { - $pathOfTestApp = __DIR__; - $pathOfTestApp .= '/../data/'; - $pathOfTestApp .= 'testapp.zip'; + // Extract app + $pathOfTestApp = __DIR__ . '/../data/testapp.zip'; + $tar = new ZIP($pathOfTestApp); + $tar->extract(\OC_App::getInstallPath()); - $tmp = \OC::$server->getTempManager()->getTemporaryFile('.zip'); - \OC_Helper::copyr($pathOfTestApp, $tmp); - - $data = array( - 'path' => $tmp, - 'source' => 'path', - 'appdata' => [ - 'id' => 'Bar', - 'level' => 100, - ] + // Install app + $installer = new Installer( + \OC::$server->getAppFetcher(), + \OC::$server->getHTTPClientService(), + \OC::$server->getTempManager(), + \OC::$server->getLogger() ); - - $installer = new Installer(); - $installer->installApp($data); + $installer->installApp(self::$appid); $isInstalled = Installer::isInstalled(self::$appid); - $this->assertTrue($isInstalled); - } - - public function testUpdateApp() { - $pathOfOldTestApp = __DIR__; - $pathOfOldTestApp .= '/../data/'; - $pathOfOldTestApp .= 'testapp.zip'; - - $oldTmp = \OC::$server->getTempManager()->getTemporaryFile('.zip'); - \OC_Helper::copyr($pathOfOldTestApp, $oldTmp); - - $oldData = array( - 'path' => $oldTmp, - 'source' => 'path', - 'appdata' => [ - 'id' => 'Bar', - 'level' => 100, - ] - ); - - $pathOfNewTestApp = __DIR__; - $pathOfNewTestApp .= '/../data/'; - $pathOfNewTestApp .= 'testapp2.zip'; - - $newTmp = \OC::$server->getTempManager()->getTemporaryFile('.zip'); - \OC_Helper::copyr($pathOfNewTestApp, $newTmp); - - $newData = array( - 'path' => $newTmp, - 'source' => 'path', - 'appdata' => [ - 'id' => 'Bar', - 'level' => 100, - ] - ); - - $installer = new Installer(); - $installer->installApp($oldData); - $oldVersionNumber = \OC_App::getAppVersion(self::$appid); - - Installer::updateApp($newData); - $newVersionNumber = \OC_App::getAppVersion(self::$appid); - - $this->assertNotEquals($oldVersionNumber, $newVersionNumber); + $installer->removeApp(self::$appid); } } diff --git a/tests/lib/ServerTest.php b/tests/lib/ServerTest.php index 3ff8787b91..02fccee628 100644 --- a/tests/lib/ServerTest.php +++ b/tests/lib/ServerTest.php @@ -23,6 +23,8 @@ */ namespace Test; +use OC\App\AppStore\Fetcher\AppFetcher; +use OC\App\AppStore\Fetcher\CategoryFetcher; /** * Class Server @@ -50,6 +52,7 @@ class ServerTest extends \Test\TestCase { ['AllConfig', '\OCP\IConfig'], ['AppConfig', '\OC\AppConfig'], ['AppConfig', '\OCP\IAppConfig'], + ['AppFetcher', AppFetcher::class], ['AppHelper', '\OC\AppHelper'], ['AppHelper', '\OCP\IHelper'], ['AppManager', '\OC\App\AppManager'], @@ -59,6 +62,7 @@ class ServerTest extends \Test\TestCase { ['AvatarManager', '\OC\AvatarManager'], ['AvatarManager', '\OCP\IAvatarManager'], + ['CategoryFetcher', CategoryFetcher::class], ['CapabilitiesManager', '\OC\CapabilitiesManager'], ['ContactsManager', '\OC\ContactsManager'], ['ContactsManager', '\OCP\Contacts\IManager'],