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.'
);
}
}
}