diff --git a/lib/private/Repair.php b/lib/private/Repair.php index 2cacc22a1c21..fe9e6ba756a6 100644 --- a/lib/private/Repair.php +++ b/lib/private/Repair.php @@ -179,7 +179,7 @@ public static function getBeforeUpgradeRepairSteps() { new Collation(\OC::$server->getConfig(), $connection), new SqliteAutoincrement($connection), new SearchLuceneTables(), - new Apps(\OC::$server->getAppManager(), \OC::$server->getEventDispatcher(), \OC::$server->getConfig()), + new Apps(\OC::$server->getAppManager(), \OC::$server->getEventDispatcher(), \OC::$server->getConfig(), new \OC_Defaults()), ]; //There is no need to delete all previews on every single update diff --git a/lib/private/Repair/Apps.php b/lib/private/Repair/Apps.php index 838d9a9f0d6b..a512ac0c377c 100644 --- a/lib/private/Repair/Apps.php +++ b/lib/private/Repair/Apps.php @@ -51,15 +51,22 @@ class Apps implements IRepairStep { /** @var IConfig */ private $config; + /** @var \OC_Defaults */ + private $defaults; + /** * Apps constructor. * * @param IAppManager $appManager + * @param EventDispatcher $eventDispatcher + * @param IConfig $config + * @param \OC_Defaults $defaults */ - public function __construct(IAppManager $appManager, EventDispatcher $eventDispatcher, IConfig $config) { + public function __construct(IAppManager $appManager, EventDispatcher $eventDispatcher, IConfig $config, \OC_Defaults $defaults) { $this->appManager = $appManager; $this->eventDispatcher = $eventDispatcher; $this->config = $config; + $this->defaults = $defaults; } /** @@ -75,7 +82,7 @@ public function getName() { */ private function isCoreUpdate() { $installedVersion = $this->config->getSystemValue('version', '0.0.0'); - $currentVersion = implode('.', \OCP\Util::getVersion()); + $currentVersion = implode('.', Util::getVersion()); $versionDiff = version_compare($currentVersion, $installedVersion); if ($versionDiff > 0) { return true; @@ -90,10 +97,11 @@ private function isCoreUpdate() { private function requiresMarketEnable() { $installedVersion = $this->config->getSystemValue('version', '0.0.0'); $versionDiff = version_compare('10.0.0', $installedVersion); - if ($versionDiff >= 0) { - return true; + if ($versionDiff < 0) { + return false; } - return false; + return true; + } /** @@ -101,67 +109,66 @@ private function requiresMarketEnable() { * @throws RepairException */ public function run(IOutput $output) { - $isCoreUpdate = $this->isCoreUpdate(); + + if ($this->config->getSystemValue('has_internet_connection', true) !== true) { + $link = $this->defaults->buildDocLinkToKey('admin-marketplace-apps'); + $output->info('No internet connection available - no app updates will be taken from the marketplace.'); + $output->info("How to update apps in such situation please see $link"); + return; + } $appsToUpgrade = $this->getAppsToUpgrade(); $failedCompatibleApps = []; $failedMissingApps = $appsToUpgrade[self::KEY_MISSING]; $failedIncompatibleApps = $appsToUpgrade[self::KEY_INCOMPATIBLE]; $hasNotUpdatedCompatibleApps = 0; - $requiresMarketEnable = $this->requiresMarketEnable(); - if($isCoreUpdate && $requiresMarketEnable) { - // Then we need to enable the market app to support app updates / downloads during upgrade - $output->info('Enabling market app to assist with update'); - // delete old value that might influence old APIs - if ($this->config->getSystemValue('appstoreenabled', null) !== null) { - $this->config->deleteSystemValue('appstoreenabled'); - } - $this->appManager->enableApp('market'); - } + // fix market app state + $shallContactMarketplace = $this->fixMarketAppState($output); + if ($shallContactMarketplace) { + // Check if we can use the marketplace to update apps as needed? + if ($this->appManager->isEnabledForUser('market')) { + // Use market to fix missing / old apps + $this->loadApp('market'); + $output->info('Using market to update existing apps'); + try { + // Try to update incompatible apps + if (!empty($appsToUpgrade[self::KEY_INCOMPATIBLE])) { + $output->info('Attempting to update the following existing but incompatible app from market: ' . implode(', ', $appsToUpgrade[self::KEY_INCOMPATIBLE])); + $failedIncompatibleApps = $this->getAppsFromMarket( + $output, + $appsToUpgrade[self::KEY_INCOMPATIBLE], + 'upgradeAppStoreApp' + ); + } - // Check if we can use the marketplace to update apps as needed? - if($this->appManager->isEnabledForUser('market')) { - // Use market to fix missing / old apps - $this->loadApp('market'); - $output->info('Using market to update existing apps'); - try { - // Try to update incompatible apps - if(!empty($appsToUpgrade[self::KEY_INCOMPATIBLE])) { - $output->info('Attempting to update the following existing but incompatible app from market: '.implode(', ', $appsToUpgrade[self::KEY_INCOMPATIBLE])); - $failedIncompatibleApps = $this->getAppsFromMarket( - $output, - $appsToUpgrade[self::KEY_INCOMPATIBLE], - 'upgradeAppStoreApp' - ); - } + // Try to download missing apps + if (!empty($appsToUpgrade[self::KEY_MISSING])) { + $output->info('Attempting to update the following missing apps from market: ' . implode(', ', $appsToUpgrade[self::KEY_MISSING])); + $failedMissingApps = $this->getAppsFromMarket( + $output, + $appsToUpgrade[self::KEY_MISSING], + 'reinstallAppStoreApp' + ); + } - // Try to download missing apps - if(!empty($appsToUpgrade[self::KEY_MISSING])) { - $output->info('Attempting to update the following missing apps from market: '.implode(', ', $appsToUpgrade[self::KEY_MISSING])); - $failedMissingApps = $this->getAppsFromMarket( - $output, - $appsToUpgrade[self::KEY_MISSING], - 'reinstallAppStoreApp' - ); - } + // Try to update compatible apps + if (!empty($appsToUpgrade[self::KEY_COMPATIBLE])) { + $output->info('Attempting to update the following existing compatible apps from market: ' . implode(', ', $appsToUpgrade[self::KEY_MISSING])); + $failedCompatibleApps = $this->getAppsFromMarket( + $output, + $appsToUpgrade[self::KEY_COMPATIBLE], + 'upgradeAppStoreApp' + ); + } - // Try to update compatible apps - if(!empty($appsToUpgrade[self::KEY_COMPATIBLE])) { - $output->info('Attempting to update the following existing compatible apps from market: '.implode(', ', $appsToUpgrade[self::KEY_MISSING])); - $failedCompatibleApps = $this->getAppsFromMarket( - $output, - $appsToUpgrade[self::KEY_COMPATIBLE], - 'upgradeAppStoreApp' - ); + $hasNotUpdatedCompatibleApps = count($failedCompatibleApps); + } catch (AppManagerException $e) { + $output->warning('No connection to marketplace: ' . $e->getPrevious()); } - - $hasNotUpdatedCompatibleApps = count($failedCompatibleApps); - } catch (AppManagerException $e) { - $output->warning('No connection to marketplace: ' . $e->getPrevious()); + } else { + // No market available, output error and continue attempt + $output->warning('Market app is unavailable for updating of apps. Enable with: occ app:enable market'); } - } else { - // No market available, output error and continue attempt - $output->warning('Market app is unavailable for updating of apps. Enable with: occ app:enable market'); } $hasBlockingMissingApps = count($failedMissingApps); @@ -190,6 +197,7 @@ public function run(IOutput $output) { * * @param IOutput $output * @param string[] $appList + * @param string $event * @return array * @throws AppManagerException */ @@ -274,4 +282,41 @@ function ($appId) { protected function loadApp($app) { OC_App::loadApp($app, false); } + + /** + * @return bool + */ + private function isAppStoreEnabled() { + // if appstoreenabled was explicitly disabled we shall not use the market app for upgrade + $appStoreEnabled = $this->config->getSystemValue('appstoreenabled', null); + if ($appStoreEnabled === false) { + return false; + } + return true; + } + + private function fixMarketAppState(IOutput $output) { + // no core update -> nothing to do + if (!$this->isCoreUpdate()) { + return false; + } + + // no update from a version before 10.0 -> nothing to do, but allow apps to be updated + if (!$this->requiresMarketEnable()) { + return true; + } + // if the appstore was explicitly disabled -> disable market app as well + if (!$this->isAppStoreEnabled()) { + $this->appManager->disableApp('market'); + $link = $this->defaults->buildDocLinkToKey('admin-marketplace-apps'); + $output->info('Appstore was disabled in past versions and marketplace interactions are disabled for now as well.'); + $output->info('If you would like to get automated app updates on upgrade please enable the market app and remove "appstoreenabled" from your config.'); + $output->info("Please note that the market app is not recommended for clustered setups - see $link"); + return false; + } + // Then we need to enable the market app to support app updates / downloads during upgrade + $output->info('Enabling market app to assist with update'); + $this->appManager->enableApp('market'); + return true; + } } diff --git a/tests/lib/Repair/AppsTest.php b/tests/lib/Repair/AppsTest.php index 457559b0c709..a8679a50f55d 100644 --- a/tests/lib/Repair/AppsTest.php +++ b/tests/lib/Repair/AppsTest.php @@ -19,53 +19,57 @@ * */ namespace Test\Repair; +use OC\Repair\Apps; use OCP\App\IAppManager; use OCP\IConfig; use Symfony\Component\EventDispatcher\EventDispatcher; +use Test\TestCase; /** * Tests to check version comparison * * @see \OC\Repair\AppsTest */ -class AppsTest extends \Test\TestCase { +class AppsTest extends TestCase { - /** @var \OC\Repair\AvatarPermissions */ + /** @var Apps */ protected $repair; - - /** @var IAppManager */ + /** @var IAppManager | \PHPUnit_Framework_MockObject_MockObject */ protected $appManager; - /** @var EventDispatcher */ + /** @var EventDispatcher | \PHPUnit_Framework_MockObject_MockObject */ protected $eventDispatcher; - /** @var IConfig */ + /** @var IConfig | \PHPUnit_Framework_MockObject_MockObject*/ protected $config; + /** @var \OC_Defaults | \PHPUnit_Framework_MockObject_MockObject */ + private $defaults; protected function setUp() { parent::setUp(); - $this->appManager = $this->getMockBuilder(IAppManager::class)->getMock(); - $this->eventDispatcher = $this->getMockBuilder(EventDispatcher::class)->getMock(); - $this->config = $this->getMockBuilder(IConfig::class)->getMock(); - $this->repair = new \OC\Repair\Apps($this->appManager, $this->eventDispatcher, $this->config); + $this->appManager = $this->createMock(IAppManager::class); + $this->defaults = $this->createMock(\OC_Defaults::class); + $this->eventDispatcher = $this->createMock(EventDispatcher::class); + $this->config = $this->createMock(IConfig::class); + $this->repair = new Apps($this->appManager, $this->eventDispatcher, $this->config, $this->defaults); } public function testMarketEnableVersionCompare10() { - $this->config->expects($this->once())->method('getSystemValue')->with('version', '0.0.0')->will($this->returnValue('10.0.0')); + $this->config->expects($this->once())->method('getSystemValue')->with('version', '0.0.0')->willReturn('10.0.0'); $this->assertTrue($this->invokePrivate($this->repair, 'requiresMarketEnable')); } public function testMarketEnableVersionCompare9() { - $this->config->expects($this->once())->method('getSystemValue')->with('version', '0.0.0')->will($this->returnValue('9.1.5')); + $this->config->expects($this->once())->method('getSystemValue')->with('version', '0.0.0')->willReturn('9.1.5'); $this->assertTrue($this->invokePrivate($this->repair, 'requiresMarketEnable')); } public function testMarketEnableVersionCompareFuture() { - $this->config->expects($this->once())->method('getSystemValue')->with('version', '0.0.0')->will($this->returnValue('10.0.2')); + $this->config->expects($this->once())->method('getSystemValue')->with('version', '0.0.0')->willReturn('10.0.2'); $this->assertFalse($this->invokePrivate($this->repair, 'requiresMarketEnable')); } public function testMarketEnableVersionCompareCurrent() { - $this->config->expects($this->once())->method('getSystemValue')->with('version', '0.0.0')->will($this->returnValue('10.0.1')); + $this->config->expects($this->once())->method('getSystemValue')->with('version', '0.0.0')->willReturn('10.0.1'); $this->assertFalse($this->invokePrivate($this->repair, 'requiresMarketEnable')); } -} \ No newline at end of file +}