| @@ -45,472 +45,472 @@ | ||
| 45 | 45 | * - failure(string $message) | 
| 46 | 46 | */ | 
| 47 | 47 |  class Updater extends BasicEmitter { | 
| 48 | - private array $logLevelNames = [ | |
| 49 | - 0 => 'Debug', | |
| 50 | - 1 => 'Info', | |
| 51 | - 2 => 'Warning', | |
| 52 | - 3 => 'Error', | |
| 53 | - 4 => 'Fatal', | |
| 54 | - ]; | |
| 55 | - | |
| 56 | - public function __construct( | |
| 57 | - private ServerVersion $serverVersion, | |
| 58 | - private IConfig $config, | |
| 59 | - private IAppConfig $appConfig, | |
| 60 | - private Checker $checker, | |
| 61 | - private ?LoggerInterface $log, | |
| 62 | - private Installer $installer, | |
| 63 | -	) { | |
| 64 | - } | |
| 65 | - | |
| 66 | - /** | |
| 67 | - * runs the update actions in maintenance mode, does not upgrade the source files | |
| 68 | - * except the main .htaccess file | |
| 69 | - * | |
| 70 | - * @return bool true if the operation succeeded, false otherwise | |
| 71 | - */ | |
| 72 | -	public function upgrade(): bool { | |
| 73 | - $this->logAllEvents(); | |
| 74 | - | |
| 75 | -		$logLevel = $this->config->getSystemValue('loglevel', ILogger::WARN); | |
| 76 | -		$this->emit('\OC\Updater', 'setDebugLogLevel', [ $logLevel, $this->logLevelNames[$logLevel] ]); | |
| 77 | -		$this->config->setSystemValue('loglevel', ILogger::DEBUG); | |
| 78 | - | |
| 79 | -		$wasMaintenanceModeEnabled = $this->config->getSystemValueBool('maintenance'); | |
| 80 | - | |
| 81 | -		if (!$wasMaintenanceModeEnabled) { | |
| 82 | -			$this->config->setSystemValue('maintenance', true); | |
| 83 | -			$this->emit('\OC\Updater', 'maintenanceEnabled'); | |
| 84 | - } | |
| 85 | - | |
| 86 | - // Clear CAN_INSTALL file if not on git | |
| 87 | -		if ($this->serverVersion->getChannel() !== 'git' && is_file(\OC::$configDir . '/CAN_INSTALL')) { | |
| 88 | -			if (!unlink(\OC::$configDir . '/CAN_INSTALL')) { | |
| 89 | -				$this->log->error('Could not cleanup CAN_INSTALL from your config folder. Please remove this file manually.'); | |
| 90 | - } | |
| 91 | - } | |
| 92 | - | |
| 93 | -		$installedVersion = $this->config->getSystemValueString('version', '0.0.0'); | |
| 94 | -		$currentVersion = implode('.', $this->serverVersion->getVersion()); | |
| 95 | - | |
| 96 | -		$this->log->debug('starting upgrade from ' . $installedVersion . ' to ' . $currentVersion, ['app' => 'core']); | |
| 97 | - | |
| 98 | - $success = true; | |
| 99 | -		try { | |
| 100 | - $this->doUpgrade($currentVersion, $installedVersion); | |
| 101 | -		} catch (HintException $exception) { | |
| 102 | - $this->log->error($exception->getMessage(), [ | |
| 103 | - 'exception' => $exception, | |
| 104 | - ]); | |
| 105 | -			$this->emit('\OC\Updater', 'failure', [$exception->getMessage() . ': ' . $exception->getHint()]); | |
| 106 | - $success = false; | |
| 107 | -		} catch (\Exception $exception) { | |
| 108 | - $this->log->error($exception->getMessage(), [ | |
| 109 | - 'exception' => $exception, | |
| 110 | - ]); | |
| 111 | -			$this->emit('\OC\Updater', 'failure', [get_class($exception) . ': ' . $exception->getMessage()]); | |
| 112 | - $success = false; | |
| 113 | - } | |
| 114 | - | |
| 115 | -		$this->emit('\OC\Updater', 'updateEnd', [$success]); | |
| 116 | - | |
| 117 | -		if (!$wasMaintenanceModeEnabled && $success) { | |
| 118 | -			$this->config->setSystemValue('maintenance', false); | |
| 119 | -			$this->emit('\OC\Updater', 'maintenanceDisabled'); | |
| 120 | -		} else { | |
| 121 | -			$this->emit('\OC\Updater', 'maintenanceActive'); | |
| 122 | - } | |
| 123 | - | |
| 124 | -		$this->emit('\OC\Updater', 'resetLogLevel', [ $logLevel, $this->logLevelNames[$logLevel] ]); | |
| 125 | -		$this->config->setSystemValue('loglevel', $logLevel); | |
| 126 | -		$this->config->setSystemValue('installed', true); | |
| 127 | - | |
| 128 | - return $success; | |
| 129 | - } | |
| 130 | - | |
| 131 | - /** | |
| 132 | - * Return version from which this version is allowed to upgrade from | |
| 133 | - * | |
| 134 | - * @return array allowed previous versions per vendor | |
| 135 | - */ | |
| 136 | -	private function getAllowedPreviousVersions(): array { | |
| 137 | - // this should really be a JSON file | |
| 138 | - require \OC::$SERVERROOT . '/version.php'; | |
| 139 | - /** @var array $OC_VersionCanBeUpgradedFrom */ | |
| 140 | - return $OC_VersionCanBeUpgradedFrom; | |
| 141 | - } | |
| 142 | - | |
| 143 | - /** | |
| 144 | - * Return vendor from which this version was published | |
| 145 | - * | |
| 146 | - * @return string Get the vendor | |
| 147 | - */ | |
| 148 | -	private function getVendor(): string { | |
| 149 | - // this should really be a JSON file | |
| 150 | - require \OC::$SERVERROOT . '/version.php'; | |
| 151 | - /** @var string $vendor */ | |
| 152 | - return (string)$vendor; | |
| 153 | - } | |
| 154 | - | |
| 155 | - /** | |
| 156 | - * Whether an upgrade to a specified version is possible | |
| 157 | - * @param string $oldVersion | |
| 158 | - * @param string $newVersion | |
| 159 | - * @param array $allowedPreviousVersions | |
| 160 | - * @return bool | |
| 161 | - */ | |
| 162 | -	public function isUpgradePossible(string $oldVersion, string $newVersion, array $allowedPreviousVersions): bool { | |
| 163 | -		$version = explode('.', $oldVersion); | |
| 164 | - $majorMinor = $version[0] . '.' . $version[1]; | |
| 165 | - | |
| 166 | -		$currentVendor = $this->config->getAppValue('core', 'vendor', ''); | |
| 167 | - | |
| 168 | - // Vendor was not set correctly on install, so we have to white-list known versions | |
| 169 | - if ($currentVendor === '' && ( | |
| 170 | - isset($allowedPreviousVersions['owncloud'][$oldVersion]) | |
| 171 | - || isset($allowedPreviousVersions['owncloud'][$majorMinor]) | |
| 172 | -		)) { | |
| 173 | - $currentVendor = 'owncloud'; | |
| 174 | -			$this->config->setAppValue('core', 'vendor', $currentVendor); | |
| 175 | - } | |
| 176 | - | |
| 177 | -		if ($currentVendor === 'nextcloud') { | |
| 178 | - return isset($allowedPreviousVersions[$currentVendor][$majorMinor]) | |
| 179 | - && (version_compare($oldVersion, $newVersion, '<=') | |
| 180 | -					|| $this->config->getSystemValueBool('debug', false)); | |
| 181 | - } | |
| 182 | - | |
| 183 | - // Check if the instance can be migrated | |
| 184 | - return isset($allowedPreviousVersions[$currentVendor][$majorMinor]) | |
| 185 | - || isset($allowedPreviousVersions[$currentVendor][$oldVersion]); | |
| 186 | - } | |
| 187 | - | |
| 188 | - /** | |
| 189 | - * runs the update actions in maintenance mode, does not upgrade the source files | |
| 190 | - * except the main .htaccess file | |
| 191 | - * | |
| 192 | - * @param string $currentVersion current version to upgrade to | |
| 193 | - * @param string $installedVersion previous version from which to upgrade from | |
| 194 | - * | |
| 195 | - * @throws \Exception | |
| 196 | - */ | |
| 197 | -	private function doUpgrade(string $currentVersion, string $installedVersion): void { | |
| 198 | - // Stop update if the update is over several major versions | |
| 199 | - $allowedPreviousVersions = $this->getAllowedPreviousVersions(); | |
| 200 | -		if (!$this->isUpgradePossible($installedVersion, $currentVersion, $allowedPreviousVersions)) { | |
| 201 | -			throw new \Exception('Updates between multiple major versions and downgrades are unsupported.'); | |
| 202 | - } | |
| 203 | - | |
| 204 | - // Update .htaccess files | |
| 205 | -		try { | |
| 206 | - Setup::updateHtaccess(); | |
| 207 | - Setup::protectDataDirectory(); | |
| 208 | -		} catch (\Exception $e) { | |
| 209 | - throw new \Exception($e->getMessage()); | |
| 210 | - } | |
| 211 | - | |
| 212 | - // create empty file in data dir, so we can later find | |
| 213 | - // out that this is indeed a Nextcloud data directory | |
| 214 | - // (in case it didn't exist before) | |
| 215 | - file_put_contents( | |
| 216 | -			$this->config->getSystemValueString('datadirectory', \OC::$SERVERROOT . '/data') . '/.ncdata', | |
| 217 | - "# Nextcloud data directory\n# Do not change this file", | |
| 218 | - ); | |
| 219 | - | |
| 220 | - // pre-upgrade repairs | |
| 221 | - $repair = \OCP\Server::get(Repair::class); | |
| 222 | - $repair->setRepairSteps(Repair::getBeforeUpgradeRepairSteps()); | |
| 223 | - $repair->run(); | |
| 224 | - | |
| 225 | - $this->doCoreUpgrade(); | |
| 226 | - | |
| 227 | -		try { | |
| 228 | - // TODO: replace with the new repair step mechanism https://github.com/owncloud/core/pull/24378 | |
| 229 | - Setup::installBackgroundJobs(); | |
| 230 | -		} catch (\Exception $e) { | |
| 231 | - throw new \Exception($e->getMessage()); | |
| 232 | - } | |
| 233 | - | |
| 234 | - // update all shipped apps | |
| 235 | - $this->checkAppsRequirements(); | |
| 236 | - $this->doAppUpgrade(); | |
| 237 | - | |
| 238 | - // Update the appfetchers version so it downloads the correct list from the appstore | |
| 239 | - \OC::$server->get(AppFetcher::class)->setVersion($currentVersion); | |
| 240 | - | |
| 241 | - /** @var AppManager $appManager */ | |
| 242 | - $appManager = \OC::$server->getAppManager(); | |
| 243 | - | |
| 244 | - // upgrade appstore apps | |
| 245 | - $this->upgradeAppStoreApps($appManager->getEnabledApps()); | |
| 246 | - $autoDisabledApps = $appManager->getAutoDisabledApps(); | |
| 247 | -		if (!empty($autoDisabledApps)) { | |
| 248 | - $this->upgradeAppStoreApps(array_keys($autoDisabledApps), $autoDisabledApps); | |
| 249 | - } | |
| 250 | - | |
| 251 | - // install new shipped apps on upgrade | |
| 252 | - $errors = Installer::installShippedApps(true); | |
| 253 | -		foreach ($errors as $appId => $exception) { | |
| 254 | - /** @var \Exception $exception */ | |
| 255 | - $this->log->error($exception->getMessage(), [ | |
| 256 | - 'exception' => $exception, | |
| 257 | - 'app' => $appId, | |
| 258 | - ]); | |
| 259 | -			$this->emit('\OC\Updater', 'failure', [$appId . ': ' . $exception->getMessage()]); | |
| 260 | - } | |
| 261 | - | |
| 262 | - // post-upgrade repairs | |
| 263 | - $repair = \OCP\Server::get(Repair::class); | |
| 264 | - $repair->setRepairSteps(Repair::getRepairSteps()); | |
| 265 | - $repair->run(); | |
| 266 | - | |
| 267 | - //Invalidate update feed | |
| 268 | -		$this->appConfig->setValueInt('core', 'lastupdatedat', 0); | |
| 269 | - | |
| 270 | - // Check for code integrity if not disabled | |
| 271 | -		if (\OC::$server->getIntegrityCodeChecker()->isCodeCheckEnforced()) { | |
| 272 | -			$this->emit('\OC\Updater', 'startCheckCodeIntegrity'); | |
| 273 | - $this->checker->runInstanceVerification(); | |
| 274 | -			$this->emit('\OC\Updater', 'finishedCheckCodeIntegrity'); | |
| 275 | - } | |
| 276 | - | |
| 277 | - // only set the final version if everything went well | |
| 278 | -		$this->config->setSystemValue('version', implode('.', Util::getVersion())); | |
| 279 | -		$this->config->setAppValue('core', 'vendor', $this->getVendor()); | |
| 280 | - } | |
| 281 | - | |
| 282 | -	protected function doCoreUpgrade(): void { | |
| 283 | -		$this->emit('\OC\Updater', 'dbUpgradeBefore'); | |
| 284 | - | |
| 285 | - // execute core migrations | |
| 286 | -		$ms = new MigrationService('core', \OC::$server->get(Connection::class)); | |
| 287 | - $ms->migrate(); | |
| 288 | - | |
| 289 | -		$this->emit('\OC\Updater', 'dbUpgrade'); | |
| 290 | - } | |
| 291 | - | |
| 292 | - /** | |
| 293 | - * upgrades all apps within a major ownCloud upgrade. Also loads "priority" | |
| 294 | - * (types authentication, filesystem, logging, in that order) afterwards. | |
| 295 | - * | |
| 296 | - * @throws NeedsUpdateException | |
| 297 | - */ | |
| 298 | -	protected function doAppUpgrade(): void { | |
| 299 | - $apps = \OC_App::getEnabledApps(); | |
| 300 | - $priorityTypes = ['authentication', 'extended_authentication', 'filesystem', 'logging']; | |
| 301 | - $pseudoOtherType = 'other'; | |
| 302 | - $stacks = [$pseudoOtherType => []]; | |
| 303 | - | |
| 304 | -		foreach ($apps as $appId) { | |
| 305 | - $priorityType = false; | |
| 306 | -			foreach ($priorityTypes as $type) { | |
| 307 | -				if (!isset($stacks[$type])) { | |
| 308 | - $stacks[$type] = []; | |
| 309 | - } | |
| 310 | -				if (\OC_App::isType($appId, [$type])) { | |
| 311 | - $stacks[$type][] = $appId; | |
| 312 | - $priorityType = true; | |
| 313 | - break; | |
| 314 | - } | |
| 315 | - } | |
| 316 | -			if (!$priorityType) { | |
| 317 | - $stacks[$pseudoOtherType][] = $appId; | |
| 318 | - } | |
| 319 | - } | |
| 320 | -		foreach (array_merge($priorityTypes, [$pseudoOtherType]) as $type) { | |
| 321 | - $stack = $stacks[$type]; | |
| 322 | -			foreach ($stack as $appId) { | |
| 323 | -				if (\OC_App::shouldUpgrade($appId)) { | |
| 324 | -					$this->emit('\OC\Updater', 'appUpgradeStarted', [$appId, \OCP\Server::get(IAppManager::class)->getAppVersion($appId)]); | |
| 325 | - \OC_App::updateApp($appId); | |
| 326 | -					$this->emit('\OC\Updater', 'appUpgrade', [$appId, \OCP\Server::get(IAppManager::class)->getAppVersion($appId)]); | |
| 327 | - } | |
| 328 | -				if ($type !== $pseudoOtherType) { | |
| 329 | - // load authentication, filesystem and logging apps after | |
| 330 | - // upgrading them. Other apps my need to rely on modifying | |
| 331 | - // user and/or filesystem aspects. | |
| 332 | - \OC_App::loadApp($appId); | |
| 333 | - } | |
| 334 | - } | |
| 335 | - } | |
| 336 | - } | |
| 337 | - | |
| 338 | - /** | |
| 339 | - * check if the current enabled apps are compatible with the current | |
| 340 | - * ownCloud version. disable them if not. | |
| 341 | - * This is important if you upgrade ownCloud and have non ported 3rd | |
| 342 | - * party apps installed. | |
| 343 | - * | |
| 344 | - * @throws \Exception | |
| 345 | - */ | |
| 346 | -	private function checkAppsRequirements(): void { | |
| 347 | - $isCoreUpgrade = $this->isCodeUpgrade(); | |
| 348 | - $apps = OC_App::getEnabledApps(); | |
| 349 | -		$version = implode('.', Util::getVersion()); | |
| 350 | - $appManager = \OC::$server->getAppManager(); | |
| 351 | -		foreach ($apps as $app) { | |
| 352 | - // check if the app is compatible with this version of Nextcloud | |
| 353 | - $info = $appManager->getAppInfo($app); | |
| 354 | -			if ($info === null || !OC_App::isAppCompatible($version, $info)) { | |
| 355 | -				if ($appManager->isShipped($app)) { | |
| 356 | -					throw new \UnexpectedValueException('The files of the app "' . $app . '" were not correctly replaced before running the update'); | |
| 357 | - } | |
| 358 | - $appManager->disableApp($app, true); | |
| 359 | -				$this->emit('\OC\Updater', 'incompatibleAppDisabled', [$app]); | |
| 360 | - } | |
| 361 | - } | |
| 362 | - } | |
| 363 | - | |
| 364 | - /** | |
| 365 | - * @return bool | |
| 366 | - */ | |
| 367 | -	private function isCodeUpgrade(): bool { | |
| 368 | -		$installedVersion = $this->config->getSystemValueString('version', '0.0.0'); | |
| 369 | -		$currentVersion = implode('.', Util::getVersion()); | |
| 370 | -		if (version_compare($currentVersion, $installedVersion, '>')) { | |
| 371 | - return true; | |
| 372 | - } | |
| 373 | - return false; | |
| 374 | - } | |
| 375 | - | |
| 376 | - /** | |
| 377 | - * @param array $apps | |
| 378 | - * @param array $previousEnableStates | |
| 379 | - * @throws \Exception | |
| 380 | - */ | |
| 381 | -	private function upgradeAppStoreApps(array $apps, array $previousEnableStates = []): void { | |
| 382 | -		foreach ($apps as $app) { | |
| 383 | -			try { | |
| 384 | -				$this->emit('\OC\Updater', 'checkAppStoreAppBefore', [$app]); | |
| 385 | -				if ($this->installer->isUpdateAvailable($app)) { | |
| 386 | -					$this->emit('\OC\Updater', 'upgradeAppStoreApp', [$app]); | |
| 387 | - $this->installer->updateAppstoreApp($app); | |
| 388 | -				} elseif (!empty($previousEnableStates)) { | |
| 389 | - /** | |
| 390 | - * When updating a local app we still need to run updateApp | |
| 391 | - * so that repair steps and migrations are correctly executed | |
| 392 | - * Ref: https://github.com/nextcloud/server/issues/53985 | |
| 393 | - */ | |
| 394 | - \OC_App::updateApp($app); | |
| 395 | - } | |
| 396 | -				$this->emit('\OC\Updater', 'checkAppStoreApp', [$app]); | |
| 397 | - | |
| 398 | -				if (!empty($previousEnableStates)) { | |
| 399 | - $ocApp = new \OC_App(); | |
| 400 | -					if (!empty($previousEnableStates[$app]) && is_array($previousEnableStates[$app])) { | |
| 401 | - $ocApp->enable($app, $previousEnableStates[$app]); | |
| 402 | -					} else { | |
| 403 | - $ocApp->enable($app); | |
| 404 | - } | |
| 405 | - } | |
| 406 | -			} catch (\Exception $ex) { | |
| 407 | - $this->log->error($ex->getMessage(), [ | |
| 408 | - 'exception' => $ex, | |
| 409 | - ]); | |
| 410 | - } | |
| 411 | - } | |
| 412 | - } | |
| 413 | - | |
| 414 | -	private function logAllEvents(): void { | |
| 415 | - $log = $this->log; | |
| 416 | - | |
| 417 | - /** @var IEventDispatcher $dispatcher */ | |
| 418 | - $dispatcher = \OC::$server->get(IEventDispatcher::class); | |
| 419 | - $dispatcher->addListener( | |
| 420 | - MigratorExecuteSqlEvent::class, | |
| 421 | -			function (MigratorExecuteSqlEvent $event) use ($log): void { | |
| 422 | -				$log->info(get_class($event) . ': ' . $event->getSql() . ' (' . $event->getCurrentStep() . ' of ' . $event->getMaxStep() . ')', ['app' => 'updater']); | |
| 423 | - } | |
| 424 | - ); | |
| 425 | - | |
| 426 | -		$repairListener = function (Event $event) use ($log): void { | |
| 427 | -			if ($event instanceof RepairStartEvent) { | |
| 428 | -				$log->info(get_class($event) . ': Starting ... ' . $event->getMaxStep() . ' (' . $event->getCurrentStepName() . ')', ['app' => 'updater']); | |
| 429 | -			} elseif ($event instanceof RepairAdvanceEvent) { | |
| 430 | - $desc = $event->getDescription(); | |
| 431 | -				if (empty($desc)) { | |
| 432 | - $desc = ''; | |
| 433 | - } | |
| 434 | -				$log->info(get_class($event) . ': ' . $desc . ' (' . $event->getIncrement() . ')', ['app' => 'updater']); | |
| 435 | -			} elseif ($event instanceof RepairFinishEvent) { | |
| 436 | - $log->info(get_class($event), ['app' => 'updater']); | |
| 437 | -			} elseif ($event instanceof RepairStepEvent) { | |
| 438 | - $log->info(get_class($event) . ': Repair step: ' . $event->getStepName(), ['app' => 'updater']); | |
| 439 | -			} elseif ($event instanceof RepairInfoEvent) { | |
| 440 | - $log->info(get_class($event) . ': Repair info: ' . $event->getMessage(), ['app' => 'updater']); | |
| 441 | -			} elseif ($event instanceof RepairWarningEvent) { | |
| 442 | - $log->warning(get_class($event) . ': Repair warning: ' . $event->getMessage(), ['app' => 'updater']); | |
| 443 | -			} elseif ($event instanceof RepairErrorEvent) { | |
| 444 | - $log->error(get_class($event) . ': Repair error: ' . $event->getMessage(), ['app' => 'updater']); | |
| 445 | - } | |
| 446 | - }; | |
| 447 | - | |
| 448 | - $dispatcher->addListener(RepairStartEvent::class, $repairListener); | |
| 449 | - $dispatcher->addListener(RepairAdvanceEvent::class, $repairListener); | |
| 450 | - $dispatcher->addListener(RepairFinishEvent::class, $repairListener); | |
| 451 | - $dispatcher->addListener(RepairStepEvent::class, $repairListener); | |
| 452 | - $dispatcher->addListener(RepairInfoEvent::class, $repairListener); | |
| 453 | - $dispatcher->addListener(RepairWarningEvent::class, $repairListener); | |
| 454 | - $dispatcher->addListener(RepairErrorEvent::class, $repairListener); | |
| 455 | - | |
| 456 | - | |
| 457 | -		$this->listen('\OC\Updater', 'maintenanceEnabled', function () use ($log) { | |
| 458 | -			$log->info('\OC\Updater::maintenanceEnabled: Turned on maintenance mode', ['app' => 'updater']); | |
| 459 | - }); | |
| 460 | -		$this->listen('\OC\Updater', 'maintenanceDisabled', function () use ($log) { | |
| 461 | -			$log->info('\OC\Updater::maintenanceDisabled: Turned off maintenance mode', ['app' => 'updater']); | |
| 462 | - }); | |
| 463 | -		$this->listen('\OC\Updater', 'maintenanceActive', function () use ($log) { | |
| 464 | -			$log->info('\OC\Updater::maintenanceActive: Maintenance mode is kept active', ['app' => 'updater']); | |
| 465 | - }); | |
| 466 | -		$this->listen('\OC\Updater', 'updateEnd', function ($success) use ($log) { | |
| 467 | -			if ($success) { | |
| 468 | -				$log->info('\OC\Updater::updateEnd: Update successful', ['app' => 'updater']); | |
| 469 | -			} else { | |
| 470 | -				$log->error('\OC\Updater::updateEnd: Update failed', ['app' => 'updater']); | |
| 471 | - } | |
| 472 | - }); | |
| 473 | -		$this->listen('\OC\Updater', 'dbUpgradeBefore', function () use ($log) { | |
| 474 | -			$log->info('\OC\Updater::dbUpgradeBefore: Updating database schema', ['app' => 'updater']); | |
| 475 | - }); | |
| 476 | -		$this->listen('\OC\Updater', 'dbUpgrade', function () use ($log) { | |
| 477 | -			$log->info('\OC\Updater::dbUpgrade: Updated database', ['app' => 'updater']); | |
| 478 | - }); | |
| 479 | -		$this->listen('\OC\Updater', 'incompatibleAppDisabled', function ($app) use ($log) { | |
| 480 | -			$log->info('\OC\Updater::incompatibleAppDisabled: Disabled incompatible app: ' . $app, ['app' => 'updater']); | |
| 481 | - }); | |
| 482 | -		$this->listen('\OC\Updater', 'checkAppStoreAppBefore', function ($app) use ($log) { | |
| 483 | -			$log->debug('\OC\Updater::checkAppStoreAppBefore: Checking for update of app "' . $app . '" in appstore', ['app' => 'updater']); | |
| 484 | - }); | |
| 485 | -		$this->listen('\OC\Updater', 'upgradeAppStoreApp', function ($app) use ($log) { | |
| 486 | -			$log->info('\OC\Updater::upgradeAppStoreApp: Update app "' . $app . '" from appstore', ['app' => 'updater']); | |
| 487 | - }); | |
| 488 | -		$this->listen('\OC\Updater', 'checkAppStoreApp', function ($app) use ($log) { | |
| 489 | -			$log->debug('\OC\Updater::checkAppStoreApp: Checked for update of app "' . $app . '" in appstore', ['app' => 'updater']); | |
| 490 | - }); | |
| 491 | -		$this->listen('\OC\Updater', 'appSimulateUpdate', function ($app) use ($log) { | |
| 492 | -			$log->info('\OC\Updater::appSimulateUpdate: Checking whether the database schema for <' . $app . '> can be updated (this can take a long time depending on the database size)', ['app' => 'updater']); | |
| 493 | - }); | |
| 494 | -		$this->listen('\OC\Updater', 'appUpgradeStarted', function ($app) use ($log) { | |
| 495 | -			$log->info('\OC\Updater::appUpgradeStarted: Updating <' . $app . '> ...', ['app' => 'updater']); | |
| 496 | - }); | |
| 497 | -		$this->listen('\OC\Updater', 'appUpgrade', function ($app, $version) use ($log) { | |
| 498 | -			$log->info('\OC\Updater::appUpgrade: Updated <' . $app . '> to ' . $version, ['app' => 'updater']); | |
| 499 | - }); | |
| 500 | -		$this->listen('\OC\Updater', 'failure', function ($message) use ($log) { | |
| 501 | -			$log->error('\OC\Updater::failure: ' . $message, ['app' => 'updater']); | |
| 502 | - }); | |
| 503 | -		$this->listen('\OC\Updater', 'setDebugLogLevel', function () use ($log) { | |
| 504 | -			$log->info('\OC\Updater::setDebugLogLevel: Set log level to debug', ['app' => 'updater']); | |
| 505 | - }); | |
| 506 | -		$this->listen('\OC\Updater', 'resetLogLevel', function ($logLevel, $logLevelName) use ($log) { | |
| 507 | -			$log->info('\OC\Updater::resetLogLevel: Reset log level to ' . $logLevelName . '(' . $logLevel . ')', ['app' => 'updater']); | |
| 508 | - }); | |
| 509 | -		$this->listen('\OC\Updater', 'startCheckCodeIntegrity', function () use ($log) { | |
| 510 | -			$log->info('\OC\Updater::startCheckCodeIntegrity: Starting code integrity check...', ['app' => 'updater']); | |
| 511 | - }); | |
| 512 | -		$this->listen('\OC\Updater', 'finishedCheckCodeIntegrity', function () use ($log) { | |
| 513 | -			$log->info('\OC\Updater::finishedCheckCodeIntegrity: Finished code integrity check', ['app' => 'updater']); | |
| 514 | - }); | |
| 515 | - } | |
| 48 | + private array $logLevelNames = [ | |
| 49 | + 0 => 'Debug', | |
| 50 | + 1 => 'Info', | |
| 51 | + 2 => 'Warning', | |
| 52 | + 3 => 'Error', | |
| 53 | + 4 => 'Fatal', | |
| 54 | + ]; | |
| 55 | + | |
| 56 | + public function __construct( | |
| 57 | + private ServerVersion $serverVersion, | |
| 58 | + private IConfig $config, | |
| 59 | + private IAppConfig $appConfig, | |
| 60 | + private Checker $checker, | |
| 61 | + private ?LoggerInterface $log, | |
| 62 | + private Installer $installer, | |
| 63 | +    ) { | |
| 64 | + } | |
| 65 | + | |
| 66 | + /** | |
| 67 | + * runs the update actions in maintenance mode, does not upgrade the source files | |
| 68 | + * except the main .htaccess file | |
| 69 | + * | |
| 70 | + * @return bool true if the operation succeeded, false otherwise | |
| 71 | + */ | |
| 72 | +    public function upgrade(): bool { | |
| 73 | + $this->logAllEvents(); | |
| 74 | + | |
| 75 | +        $logLevel = $this->config->getSystemValue('loglevel', ILogger::WARN); | |
| 76 | +        $this->emit('\OC\Updater', 'setDebugLogLevel', [ $logLevel, $this->logLevelNames[$logLevel] ]); | |
| 77 | +        $this->config->setSystemValue('loglevel', ILogger::DEBUG); | |
| 78 | + | |
| 79 | +        $wasMaintenanceModeEnabled = $this->config->getSystemValueBool('maintenance'); | |
| 80 | + | |
| 81 | +        if (!$wasMaintenanceModeEnabled) { | |
| 82 | +            $this->config->setSystemValue('maintenance', true); | |
| 83 | +            $this->emit('\OC\Updater', 'maintenanceEnabled'); | |
| 84 | + } | |
| 85 | + | |
| 86 | + // Clear CAN_INSTALL file if not on git | |
| 87 | +        if ($this->serverVersion->getChannel() !== 'git' && is_file(\OC::$configDir . '/CAN_INSTALL')) { | |
| 88 | +            if (!unlink(\OC::$configDir . '/CAN_INSTALL')) { | |
| 89 | +                $this->log->error('Could not cleanup CAN_INSTALL from your config folder. Please remove this file manually.'); | |
| 90 | + } | |
| 91 | + } | |
| 92 | + | |
| 93 | +        $installedVersion = $this->config->getSystemValueString('version', '0.0.0'); | |
| 94 | +        $currentVersion = implode('.', $this->serverVersion->getVersion()); | |
| 95 | + | |
| 96 | +        $this->log->debug('starting upgrade from ' . $installedVersion . ' to ' . $currentVersion, ['app' => 'core']); | |
| 97 | + | |
| 98 | + $success = true; | |
| 99 | +        try { | |
| 100 | + $this->doUpgrade($currentVersion, $installedVersion); | |
| 101 | +        } catch (HintException $exception) { | |
| 102 | + $this->log->error($exception->getMessage(), [ | |
| 103 | + 'exception' => $exception, | |
| 104 | + ]); | |
| 105 | +            $this->emit('\OC\Updater', 'failure', [$exception->getMessage() . ': ' . $exception->getHint()]); | |
| 106 | + $success = false; | |
| 107 | +        } catch (\Exception $exception) { | |
| 108 | + $this->log->error($exception->getMessage(), [ | |
| 109 | + 'exception' => $exception, | |
| 110 | + ]); | |
| 111 | +            $this->emit('\OC\Updater', 'failure', [get_class($exception) . ': ' . $exception->getMessage()]); | |
| 112 | + $success = false; | |
| 113 | + } | |
| 114 | + | |
| 115 | +        $this->emit('\OC\Updater', 'updateEnd', [$success]); | |
| 116 | + | |
| 117 | +        if (!$wasMaintenanceModeEnabled && $success) { | |
| 118 | +            $this->config->setSystemValue('maintenance', false); | |
| 119 | +            $this->emit('\OC\Updater', 'maintenanceDisabled'); | |
| 120 | +        } else { | |
| 121 | +            $this->emit('\OC\Updater', 'maintenanceActive'); | |
| 122 | + } | |
| 123 | + | |
| 124 | +        $this->emit('\OC\Updater', 'resetLogLevel', [ $logLevel, $this->logLevelNames[$logLevel] ]); | |
| 125 | +        $this->config->setSystemValue('loglevel', $logLevel); | |
| 126 | +        $this->config->setSystemValue('installed', true); | |
| 127 | + | |
| 128 | + return $success; | |
| 129 | + } | |
| 130 | + | |
| 131 | + /** | |
| 132 | + * Return version from which this version is allowed to upgrade from | |
| 133 | + * | |
| 134 | + * @return array allowed previous versions per vendor | |
| 135 | + */ | |
| 136 | +    private function getAllowedPreviousVersions(): array { | |
| 137 | + // this should really be a JSON file | |
| 138 | + require \OC::$SERVERROOT . '/version.php'; | |
| 139 | + /** @var array $OC_VersionCanBeUpgradedFrom */ | |
| 140 | + return $OC_VersionCanBeUpgradedFrom; | |
| 141 | + } | |
| 142 | + | |
| 143 | + /** | |
| 144 | + * Return vendor from which this version was published | |
| 145 | + * | |
| 146 | + * @return string Get the vendor | |
| 147 | + */ | |
| 148 | +    private function getVendor(): string { | |
| 149 | + // this should really be a JSON file | |
| 150 | + require \OC::$SERVERROOT . '/version.php'; | |
| 151 | + /** @var string $vendor */ | |
| 152 | + return (string)$vendor; | |
| 153 | + } | |
| 154 | + | |
| 155 | + /** | |
| 156 | + * Whether an upgrade to a specified version is possible | |
| 157 | + * @param string $oldVersion | |
| 158 | + * @param string $newVersion | |
| 159 | + * @param array $allowedPreviousVersions | |
| 160 | + * @return bool | |
| 161 | + */ | |
| 162 | +    public function isUpgradePossible(string $oldVersion, string $newVersion, array $allowedPreviousVersions): bool { | |
| 163 | +        $version = explode('.', $oldVersion); | |
| 164 | + $majorMinor = $version[0] . '.' . $version[1]; | |
| 165 | + | |
| 166 | +        $currentVendor = $this->config->getAppValue('core', 'vendor', ''); | |
| 167 | + | |
| 168 | + // Vendor was not set correctly on install, so we have to white-list known versions | |
| 169 | + if ($currentVendor === '' && ( | |
| 170 | + isset($allowedPreviousVersions['owncloud'][$oldVersion]) | |
| 171 | + || isset($allowedPreviousVersions['owncloud'][$majorMinor]) | |
| 172 | +        )) { | |
| 173 | + $currentVendor = 'owncloud'; | |
| 174 | +            $this->config->setAppValue('core', 'vendor', $currentVendor); | |
| 175 | + } | |
| 176 | + | |
| 177 | +        if ($currentVendor === 'nextcloud') { | |
| 178 | + return isset($allowedPreviousVersions[$currentVendor][$majorMinor]) | |
| 179 | + && (version_compare($oldVersion, $newVersion, '<=') | |
| 180 | +                    || $this->config->getSystemValueBool('debug', false)); | |
| 181 | + } | |
| 182 | + | |
| 183 | + // Check if the instance can be migrated | |
| 184 | + return isset($allowedPreviousVersions[$currentVendor][$majorMinor]) | |
| 185 | + || isset($allowedPreviousVersions[$currentVendor][$oldVersion]); | |
| 186 | + } | |
| 187 | + | |
| 188 | + /** | |
| 189 | + * runs the update actions in maintenance mode, does not upgrade the source files | |
| 190 | + * except the main .htaccess file | |
| 191 | + * | |
| 192 | + * @param string $currentVersion current version to upgrade to | |
| 193 | + * @param string $installedVersion previous version from which to upgrade from | |
| 194 | + * | |
| 195 | + * @throws \Exception | |
| 196 | + */ | |
| 197 | +    private function doUpgrade(string $currentVersion, string $installedVersion): void { | |
| 198 | + // Stop update if the update is over several major versions | |
| 199 | + $allowedPreviousVersions = $this->getAllowedPreviousVersions(); | |
| 200 | +        if (!$this->isUpgradePossible($installedVersion, $currentVersion, $allowedPreviousVersions)) { | |
| 201 | +            throw new \Exception('Updates between multiple major versions and downgrades are unsupported.'); | |
| 202 | + } | |
| 203 | + | |
| 204 | + // Update .htaccess files | |
| 205 | +        try { | |
| 206 | + Setup::updateHtaccess(); | |
| 207 | + Setup::protectDataDirectory(); | |
| 208 | +        } catch (\Exception $e) { | |
| 209 | + throw new \Exception($e->getMessage()); | |
| 210 | + } | |
| 211 | + | |
| 212 | + // create empty file in data dir, so we can later find | |
| 213 | + // out that this is indeed a Nextcloud data directory | |
| 214 | + // (in case it didn't exist before) | |
| 215 | + file_put_contents( | |
| 216 | +            $this->config->getSystemValueString('datadirectory', \OC::$SERVERROOT . '/data') . '/.ncdata', | |
| 217 | + "# Nextcloud data directory\n# Do not change this file", | |
| 218 | + ); | |
| 219 | + | |
| 220 | + // pre-upgrade repairs | |
| 221 | + $repair = \OCP\Server::get(Repair::class); | |
| 222 | + $repair->setRepairSteps(Repair::getBeforeUpgradeRepairSteps()); | |
| 223 | + $repair->run(); | |
| 224 | + | |
| 225 | + $this->doCoreUpgrade(); | |
| 226 | + | |
| 227 | +        try { | |
| 228 | + // TODO: replace with the new repair step mechanism https://github.com/owncloud/core/pull/24378 | |
| 229 | + Setup::installBackgroundJobs(); | |
| 230 | +        } catch (\Exception $e) { | |
| 231 | + throw new \Exception($e->getMessage()); | |
| 232 | + } | |
| 233 | + | |
| 234 | + // update all shipped apps | |
| 235 | + $this->checkAppsRequirements(); | |
| 236 | + $this->doAppUpgrade(); | |
| 237 | + | |
| 238 | + // Update the appfetchers version so it downloads the correct list from the appstore | |
| 239 | + \OC::$server->get(AppFetcher::class)->setVersion($currentVersion); | |
| 240 | + | |
| 241 | + /** @var AppManager $appManager */ | |
| 242 | + $appManager = \OC::$server->getAppManager(); | |
| 243 | + | |
| 244 | + // upgrade appstore apps | |
| 245 | + $this->upgradeAppStoreApps($appManager->getEnabledApps()); | |
| 246 | + $autoDisabledApps = $appManager->getAutoDisabledApps(); | |
| 247 | +        if (!empty($autoDisabledApps)) { | |
| 248 | + $this->upgradeAppStoreApps(array_keys($autoDisabledApps), $autoDisabledApps); | |
| 249 | + } | |
| 250 | + | |
| 251 | + // install new shipped apps on upgrade | |
| 252 | + $errors = Installer::installShippedApps(true); | |
| 253 | +        foreach ($errors as $appId => $exception) { | |
| 254 | + /** @var \Exception $exception */ | |
| 255 | + $this->log->error($exception->getMessage(), [ | |
| 256 | + 'exception' => $exception, | |
| 257 | + 'app' => $appId, | |
| 258 | + ]); | |
| 259 | +            $this->emit('\OC\Updater', 'failure', [$appId . ': ' . $exception->getMessage()]); | |
| 260 | + } | |
| 261 | + | |
| 262 | + // post-upgrade repairs | |
| 263 | + $repair = \OCP\Server::get(Repair::class); | |
| 264 | + $repair->setRepairSteps(Repair::getRepairSteps()); | |
| 265 | + $repair->run(); | |
| 266 | + | |
| 267 | + //Invalidate update feed | |
| 268 | +        $this->appConfig->setValueInt('core', 'lastupdatedat', 0); | |
| 269 | + | |
| 270 | + // Check for code integrity if not disabled | |
| 271 | +        if (\OC::$server->getIntegrityCodeChecker()->isCodeCheckEnforced()) { | |
| 272 | +            $this->emit('\OC\Updater', 'startCheckCodeIntegrity'); | |
| 273 | + $this->checker->runInstanceVerification(); | |
| 274 | +            $this->emit('\OC\Updater', 'finishedCheckCodeIntegrity'); | |
| 275 | + } | |
| 276 | + | |
| 277 | + // only set the final version if everything went well | |
| 278 | +        $this->config->setSystemValue('version', implode('.', Util::getVersion())); | |
| 279 | +        $this->config->setAppValue('core', 'vendor', $this->getVendor()); | |
| 280 | + } | |
| 281 | + | |
| 282 | +    protected function doCoreUpgrade(): void { | |
| 283 | +        $this->emit('\OC\Updater', 'dbUpgradeBefore'); | |
| 284 | + | |
| 285 | + // execute core migrations | |
| 286 | +        $ms = new MigrationService('core', \OC::$server->get(Connection::class)); | |
| 287 | + $ms->migrate(); | |
| 288 | + | |
| 289 | +        $this->emit('\OC\Updater', 'dbUpgrade'); | |
| 290 | + } | |
| 291 | + | |
| 292 | + /** | |
| 293 | + * upgrades all apps within a major ownCloud upgrade. Also loads "priority" | |
| 294 | + * (types authentication, filesystem, logging, in that order) afterwards. | |
| 295 | + * | |
| 296 | + * @throws NeedsUpdateException | |
| 297 | + */ | |
| 298 | +    protected function doAppUpgrade(): void { | |
| 299 | + $apps = \OC_App::getEnabledApps(); | |
| 300 | + $priorityTypes = ['authentication', 'extended_authentication', 'filesystem', 'logging']; | |
| 301 | + $pseudoOtherType = 'other'; | |
| 302 | + $stacks = [$pseudoOtherType => []]; | |
| 303 | + | |
| 304 | +        foreach ($apps as $appId) { | |
| 305 | + $priorityType = false; | |
| 306 | +            foreach ($priorityTypes as $type) { | |
| 307 | +                if (!isset($stacks[$type])) { | |
| 308 | + $stacks[$type] = []; | |
| 309 | + } | |
| 310 | +                if (\OC_App::isType($appId, [$type])) { | |
| 311 | + $stacks[$type][] = $appId; | |
| 312 | + $priorityType = true; | |
| 313 | + break; | |
| 314 | + } | |
| 315 | + } | |
| 316 | +            if (!$priorityType) { | |
| 317 | + $stacks[$pseudoOtherType][] = $appId; | |
| 318 | + } | |
| 319 | + } | |
| 320 | +        foreach (array_merge($priorityTypes, [$pseudoOtherType]) as $type) { | |
| 321 | + $stack = $stacks[$type]; | |
| 322 | +            foreach ($stack as $appId) { | |
| 323 | +                if (\OC_App::shouldUpgrade($appId)) { | |
| 324 | +                    $this->emit('\OC\Updater', 'appUpgradeStarted', [$appId, \OCP\Server::get(IAppManager::class)->getAppVersion($appId)]); | |
| 325 | + \OC_App::updateApp($appId); | |
| 326 | +                    $this->emit('\OC\Updater', 'appUpgrade', [$appId, \OCP\Server::get(IAppManager::class)->getAppVersion($appId)]); | |
| 327 | + } | |
| 328 | +                if ($type !== $pseudoOtherType) { | |
| 329 | + // load authentication, filesystem and logging apps after | |
| 330 | + // upgrading them. Other apps my need to rely on modifying | |
| 331 | + // user and/or filesystem aspects. | |
| 332 | + \OC_App::loadApp($appId); | |
| 333 | + } | |
| 334 | + } | |
| 335 | + } | |
| 336 | + } | |
| 337 | + | |
| 338 | + /** | |
| 339 | + * check if the current enabled apps are compatible with the current | |
| 340 | + * ownCloud version. disable them if not. | |
| 341 | + * This is important if you upgrade ownCloud and have non ported 3rd | |
| 342 | + * party apps installed. | |
| 343 | + * | |
| 344 | + * @throws \Exception | |
| 345 | + */ | |
| 346 | +    private function checkAppsRequirements(): void { | |
| 347 | + $isCoreUpgrade = $this->isCodeUpgrade(); | |
| 348 | + $apps = OC_App::getEnabledApps(); | |
| 349 | +        $version = implode('.', Util::getVersion()); | |
| 350 | + $appManager = \OC::$server->getAppManager(); | |
| 351 | +        foreach ($apps as $app) { | |
| 352 | + // check if the app is compatible with this version of Nextcloud | |
| 353 | + $info = $appManager->getAppInfo($app); | |
| 354 | +            if ($info === null || !OC_App::isAppCompatible($version, $info)) { | |
| 355 | +                if ($appManager->isShipped($app)) { | |
| 356 | +                    throw new \UnexpectedValueException('The files of the app "' . $app . '" were not correctly replaced before running the update'); | |
| 357 | + } | |
| 358 | + $appManager->disableApp($app, true); | |
| 359 | +                $this->emit('\OC\Updater', 'incompatibleAppDisabled', [$app]); | |
| 360 | + } | |
| 361 | + } | |
| 362 | + } | |
| 363 | + | |
| 364 | + /** | |
| 365 | + * @return bool | |
| 366 | + */ | |
| 367 | +    private function isCodeUpgrade(): bool { | |
| 368 | +        $installedVersion = $this->config->getSystemValueString('version', '0.0.0'); | |
| 369 | +        $currentVersion = implode('.', Util::getVersion()); | |
| 370 | +        if (version_compare($currentVersion, $installedVersion, '>')) { | |
| 371 | + return true; | |
| 372 | + } | |
| 373 | + return false; | |
| 374 | + } | |
| 375 | + | |
| 376 | + /** | |
| 377 | + * @param array $apps | |
| 378 | + * @param array $previousEnableStates | |
| 379 | + * @throws \Exception | |
| 380 | + */ | |
| 381 | +    private function upgradeAppStoreApps(array $apps, array $previousEnableStates = []): void { | |
| 382 | +        foreach ($apps as $app) { | |
| 383 | +            try { | |
| 384 | +                $this->emit('\OC\Updater', 'checkAppStoreAppBefore', [$app]); | |
| 385 | +                if ($this->installer->isUpdateAvailable($app)) { | |
| 386 | +                    $this->emit('\OC\Updater', 'upgradeAppStoreApp', [$app]); | |
| 387 | + $this->installer->updateAppstoreApp($app); | |
| 388 | +                } elseif (!empty($previousEnableStates)) { | |
| 389 | + /** | |
| 390 | + * When updating a local app we still need to run updateApp | |
| 391 | + * so that repair steps and migrations are correctly executed | |
| 392 | + * Ref: https://github.com/nextcloud/server/issues/53985 | |
| 393 | + */ | |
| 394 | + \OC_App::updateApp($app); | |
| 395 | + } | |
| 396 | +                $this->emit('\OC\Updater', 'checkAppStoreApp', [$app]); | |
| 397 | + | |
| 398 | +                if (!empty($previousEnableStates)) { | |
| 399 | + $ocApp = new \OC_App(); | |
| 400 | +                    if (!empty($previousEnableStates[$app]) && is_array($previousEnableStates[$app])) { | |
| 401 | + $ocApp->enable($app, $previousEnableStates[$app]); | |
| 402 | +                    } else { | |
| 403 | + $ocApp->enable($app); | |
| 404 | + } | |
| 405 | + } | |
| 406 | +            } catch (\Exception $ex) { | |
| 407 | + $this->log->error($ex->getMessage(), [ | |
| 408 | + 'exception' => $ex, | |
| 409 | + ]); | |
| 410 | + } | |
| 411 | + } | |
| 412 | + } | |
| 413 | + | |
| 414 | +    private function logAllEvents(): void { | |
| 415 | + $log = $this->log; | |
| 416 | + | |
| 417 | + /** @var IEventDispatcher $dispatcher */ | |
| 418 | + $dispatcher = \OC::$server->get(IEventDispatcher::class); | |
| 419 | + $dispatcher->addListener( | |
| 420 | + MigratorExecuteSqlEvent::class, | |
| 421 | +            function (MigratorExecuteSqlEvent $event) use ($log): void { | |
| 422 | +                $log->info(get_class($event) . ': ' . $event->getSql() . ' (' . $event->getCurrentStep() . ' of ' . $event->getMaxStep() . ')', ['app' => 'updater']); | |
| 423 | + } | |
| 424 | + ); | |
| 425 | + | |
| 426 | +        $repairListener = function (Event $event) use ($log): void { | |
| 427 | +            if ($event instanceof RepairStartEvent) { | |
| 428 | +                $log->info(get_class($event) . ': Starting ... ' . $event->getMaxStep() . ' (' . $event->getCurrentStepName() . ')', ['app' => 'updater']); | |
| 429 | +            } elseif ($event instanceof RepairAdvanceEvent) { | |
| 430 | + $desc = $event->getDescription(); | |
| 431 | +                if (empty($desc)) { | |
| 432 | + $desc = ''; | |
| 433 | + } | |
| 434 | +                $log->info(get_class($event) . ': ' . $desc . ' (' . $event->getIncrement() . ')', ['app' => 'updater']); | |
| 435 | +            } elseif ($event instanceof RepairFinishEvent) { | |
| 436 | + $log->info(get_class($event), ['app' => 'updater']); | |
| 437 | +            } elseif ($event instanceof RepairStepEvent) { | |
| 438 | + $log->info(get_class($event) . ': Repair step: ' . $event->getStepName(), ['app' => 'updater']); | |
| 439 | +            } elseif ($event instanceof RepairInfoEvent) { | |
| 440 | + $log->info(get_class($event) . ': Repair info: ' . $event->getMessage(), ['app' => 'updater']); | |
| 441 | +            } elseif ($event instanceof RepairWarningEvent) { | |
| 442 | + $log->warning(get_class($event) . ': Repair warning: ' . $event->getMessage(), ['app' => 'updater']); | |
| 443 | +            } elseif ($event instanceof RepairErrorEvent) { | |
| 444 | + $log->error(get_class($event) . ': Repair error: ' . $event->getMessage(), ['app' => 'updater']); | |
| 445 | + } | |
| 446 | + }; | |
| 447 | + | |
| 448 | + $dispatcher->addListener(RepairStartEvent::class, $repairListener); | |
| 449 | + $dispatcher->addListener(RepairAdvanceEvent::class, $repairListener); | |
| 450 | + $dispatcher->addListener(RepairFinishEvent::class, $repairListener); | |
| 451 | + $dispatcher->addListener(RepairStepEvent::class, $repairListener); | |
| 452 | + $dispatcher->addListener(RepairInfoEvent::class, $repairListener); | |
| 453 | + $dispatcher->addListener(RepairWarningEvent::class, $repairListener); | |
| 454 | + $dispatcher->addListener(RepairErrorEvent::class, $repairListener); | |
| 455 | + | |
| 456 | + | |
| 457 | +        $this->listen('\OC\Updater', 'maintenanceEnabled', function () use ($log) { | |
| 458 | +            $log->info('\OC\Updater::maintenanceEnabled: Turned on maintenance mode', ['app' => 'updater']); | |
| 459 | + }); | |
| 460 | +        $this->listen('\OC\Updater', 'maintenanceDisabled', function () use ($log) { | |
| 461 | +            $log->info('\OC\Updater::maintenanceDisabled: Turned off maintenance mode', ['app' => 'updater']); | |
| 462 | + }); | |
| 463 | +        $this->listen('\OC\Updater', 'maintenanceActive', function () use ($log) { | |
| 464 | +            $log->info('\OC\Updater::maintenanceActive: Maintenance mode is kept active', ['app' => 'updater']); | |
| 465 | + }); | |
| 466 | +        $this->listen('\OC\Updater', 'updateEnd', function ($success) use ($log) { | |
| 467 | +            if ($success) { | |
| 468 | +                $log->info('\OC\Updater::updateEnd: Update successful', ['app' => 'updater']); | |
| 469 | +            } else { | |
| 470 | +                $log->error('\OC\Updater::updateEnd: Update failed', ['app' => 'updater']); | |
| 471 | + } | |
| 472 | + }); | |
| 473 | +        $this->listen('\OC\Updater', 'dbUpgradeBefore', function () use ($log) { | |
| 474 | +            $log->info('\OC\Updater::dbUpgradeBefore: Updating database schema', ['app' => 'updater']); | |
| 475 | + }); | |
| 476 | +        $this->listen('\OC\Updater', 'dbUpgrade', function () use ($log) { | |
| 477 | +            $log->info('\OC\Updater::dbUpgrade: Updated database', ['app' => 'updater']); | |
| 478 | + }); | |
| 479 | +        $this->listen('\OC\Updater', 'incompatibleAppDisabled', function ($app) use ($log) { | |
| 480 | +            $log->info('\OC\Updater::incompatibleAppDisabled: Disabled incompatible app: ' . $app, ['app' => 'updater']); | |
| 481 | + }); | |
| 482 | +        $this->listen('\OC\Updater', 'checkAppStoreAppBefore', function ($app) use ($log) { | |
| 483 | +            $log->debug('\OC\Updater::checkAppStoreAppBefore: Checking for update of app "' . $app . '" in appstore', ['app' => 'updater']); | |
| 484 | + }); | |
| 485 | +        $this->listen('\OC\Updater', 'upgradeAppStoreApp', function ($app) use ($log) { | |
| 486 | +            $log->info('\OC\Updater::upgradeAppStoreApp: Update app "' . $app . '" from appstore', ['app' => 'updater']); | |
| 487 | + }); | |
| 488 | +        $this->listen('\OC\Updater', 'checkAppStoreApp', function ($app) use ($log) { | |
| 489 | +            $log->debug('\OC\Updater::checkAppStoreApp: Checked for update of app "' . $app . '" in appstore', ['app' => 'updater']); | |
| 490 | + }); | |
| 491 | +        $this->listen('\OC\Updater', 'appSimulateUpdate', function ($app) use ($log) { | |
| 492 | +            $log->info('\OC\Updater::appSimulateUpdate: Checking whether the database schema for <' . $app . '> can be updated (this can take a long time depending on the database size)', ['app' => 'updater']); | |
| 493 | + }); | |
| 494 | +        $this->listen('\OC\Updater', 'appUpgradeStarted', function ($app) use ($log) { | |
| 495 | +            $log->info('\OC\Updater::appUpgradeStarted: Updating <' . $app . '> ...', ['app' => 'updater']); | |
| 496 | + }); | |
| 497 | +        $this->listen('\OC\Updater', 'appUpgrade', function ($app, $version) use ($log) { | |
| 498 | +            $log->info('\OC\Updater::appUpgrade: Updated <' . $app . '> to ' . $version, ['app' => 'updater']); | |
| 499 | + }); | |
| 500 | +        $this->listen('\OC\Updater', 'failure', function ($message) use ($log) { | |
| 501 | +            $log->error('\OC\Updater::failure: ' . $message, ['app' => 'updater']); | |
| 502 | + }); | |
| 503 | +        $this->listen('\OC\Updater', 'setDebugLogLevel', function () use ($log) { | |
| 504 | +            $log->info('\OC\Updater::setDebugLogLevel: Set log level to debug', ['app' => 'updater']); | |
| 505 | + }); | |
| 506 | +        $this->listen('\OC\Updater', 'resetLogLevel', function ($logLevel, $logLevelName) use ($log) { | |
| 507 | +            $log->info('\OC\Updater::resetLogLevel: Reset log level to ' . $logLevelName . '(' . $logLevel . ')', ['app' => 'updater']); | |
| 508 | + }); | |
| 509 | +        $this->listen('\OC\Updater', 'startCheckCodeIntegrity', function () use ($log) { | |
| 510 | +            $log->info('\OC\Updater::startCheckCodeIntegrity: Starting code integrity check...', ['app' => 'updater']); | |
| 511 | + }); | |
| 512 | +        $this->listen('\OC\Updater', 'finishedCheckCodeIntegrity', function () use ($log) { | |
| 513 | +            $log->info('\OC\Updater::finishedCheckCodeIntegrity: Finished code integrity check', ['app' => 'updater']); | |
| 514 | + }); | |
| 515 | + } | |
| 516 | 516 | } |