cache = $cache; $this->cleanupFiles = $cleanupFiles; $this->composer = $composer; $this->dependencyChecker = $dependencyChecker; $this->themeCollection = $themeCollection; $this->backupRollbackFactory = $backupRollbackFactory; $this->themeValidator = $themeValidator; $this->themePackageInfo = $themePackageInfo; $this->themeUninstaller = $themeUninstaller; $this->themeDependencyChecker = $themeDependencyChecker; $this->maintenanceModeEnabler = $maintenanceModeEnabler ?: ObjectManager::getInstance()->get(MaintenanceModeEnabler::class); parent::__construct(); } /** * {@inheritdoc} */ protected function configure() { $this->setName('theme:uninstall'); $this->setDescription('Uninstalls theme'); $this->addOption( self::INPUT_KEY_BACKUP_CODE, null, InputOption::VALUE_NONE, 'Take code backup (excluding temporary files)' ); $this->addArgument( self::INPUT_KEY_THEMES, InputArgument::IS_ARRAY | InputArgument::REQUIRED, 'Path of the theme. Theme path should be specified as full path which is area/vendor/name.' . ' For example, frontend/Magento/blank' ); $this->addOption( self::INPUT_KEY_CLEAR_STATIC_CONTENT, 'c', InputOption::VALUE_NONE, 'Clear generated static view files.' ); parent::configure(); } /** * {@inheritdoc} */ protected function execute(InputInterface $input, OutputInterface $output) { $messages = []; $themePaths = $input->getArgument(self::INPUT_KEY_THEMES); $messages = array_merge($messages, $this->validate($themePaths)); if (!empty($messages)) { $output->writeln($messages); // we must have an exit code higher than zero to indicate something was wrong return \Magento\Framework\Console\Cli::RETURN_FAILURE; } $messages = array_merge( $messages, $this->themeValidator->validateIsThemeInUse($themePaths), $this->themeDependencyChecker->checkChildTheme($themePaths), $this->checkDependencies($themePaths) ); if (!empty($messages)) { $output->writeln( 'Unable to uninstall. Please resolve the following issues:' . PHP_EOL . implode(PHP_EOL, $messages) ); // we must have an exit code higher than zero to indicate something was wrong return \Magento\Framework\Console\Cli::RETURN_FAILURE; } $result = $this->maintenanceModeEnabler->executeInMaintenanceMode( function () use ($input, $output, $themePaths) { try { if ($input->getOption(self::INPUT_KEY_BACKUP_CODE)) { $time = time(); $codeBackup = $this->backupRollbackFactory->create($output); $codeBackup->codeBackup($time); } $this->themeUninstaller->uninstallRegistry($output, $themePaths); $this->themeUninstaller->uninstallCode($output, $themePaths); $this->cleanup($input, $output); return \Magento\Framework\Console\Cli::RETURN_SUCCESS; } catch (\Exception $e) { $output->writeln('' . $e->getMessage() . ''); $output->writeln('Please disable maintenance mode after you resolved above issues'); // we must have an exit code higher than zero to indicate something was wrong return \Magento\Framework\Console\Cli::RETURN_FAILURE; } }, $output, true ); return $result; } /** * Validate given full theme paths * * @param string[] $themePaths * @return string[] */ private function validate($themePaths) { $messages = []; $incorrectThemes = $this->getIncorrectThemes($themePaths); if (!empty($incorrectThemes)) { $text = 'Theme path should be specified as full path which is area/vendor/name.'; $messages[] = 'Incorrect theme(s) format: ' . implode(', ', $incorrectThemes) . '. ' . $text . ''; return $messages; } $unknownPackages = $this->getUnknownPackages($themePaths); $unknownThemes = $this->getUnknownThemes($themePaths); $unknownPackages = array_diff($unknownPackages, $unknownThemes); if (!empty($unknownPackages)) { $text = count($unknownPackages) > 1 ? ' are not installed Composer packages' : ' is not an installed Composer package'; $messages[] = '' . implode(', ', $unknownPackages) . $text . ''; } if (!empty($unknownThemes)) { $messages[] = 'Unknown theme(s): ' . implode(', ', $unknownThemes) . ''; } return $messages; } /** * Retrieve list of themes with wrong name format * * @param string[] $themePaths * @return string[] */ protected function getIncorrectThemes($themePaths) { $result = []; foreach ($themePaths as $themePath) { if (!preg_match('/^[^\/]+\/[^\/]+\/[^\/]+$/', $themePath)) { $result[] = $themePath; continue; } } return $result; } /** * Retrieve list of unknown packages * * @param string[] $themePaths * @return string[] */ protected function getUnknownPackages($themePaths) { $installedPackages = $this->composer->getRootRequiredPackages(); $result = []; foreach ($themePaths as $themePath) { if (array_search($this->themePackageInfo->getPackageName($themePath), $installedPackages) === false) { $result[] = $themePath; } } return $result; } /** * Retrieve list of unknown themes * * @param string[] $themePaths * @return string[] */ protected function getUnknownThemes($themePaths) { $result = []; foreach ($themePaths as $themePath) { if (!$this->themeCollection->hasTheme($this->themeCollection->getThemeByFullPath($themePath))) { $result[] = $themePath; } } return $result; } /** * Check dependencies to given full theme paths * * @param string[] $themePaths * @return string[] */ private function checkDependencies($themePaths) { $messages = []; $packageToPath = []; foreach ($themePaths as $themePath) { $packageToPath[$this->themePackageInfo->getPackageName($themePath)] = $themePath; } $dependencies = $this->dependencyChecker->checkDependencies(array_keys($packageToPath), true); foreach ($dependencies as $package => $dependingPackages) { if (!empty($dependingPackages)) { $messages[] = '' . $packageToPath[$package] . " has the following dependent package(s):" . PHP_EOL . "\t" . implode('' . PHP_EOL . "\t", $dependingPackages) . ""; } } return $messages; } /** * Cleanup after updated modules status * * @param InputInterface $input * @param OutputInterface $output * @return void */ private function cleanup(InputInterface $input, OutputInterface $output) { $this->cache->clean(); $output->writeln('Cache cleared successfully.'); if ($input->getOption(self::INPUT_KEY_CLEAR_STATIC_CONTENT)) { $this->cleanupFiles->clearMaterializedViewFiles(); $output->writeln('Generated static view files cleared successfully.'); } else { $output->writeln( 'Alert: Generated static view files were not cleared.' . ' You can clear them using the --' . self::INPUT_KEY_CLEAR_STATIC_CONTENT . ' option.' . ' Failure to clear static view files might cause display issues in the Admin and storefront.' ); } } }