* @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. * @license GNU General Public License version 3 or later; see LICENSE */ namespace Joomla\Plugin\Console\MokoSuiteBackup\Command; defined('_JEXEC') or die; use Joomla\CMS\Factory; use Joomla\Component\MokoSuiteBackup\Administrator\Engine\RestoreEngine; use Joomla\Console\Command\AbstractCommand; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; class RestoreCommand extends AbstractCommand { protected static $defaultName = 'mokosuitebackup:restore'; protected function configure(): void { $this->setDescription('Restore a backup by record ID'); $this->addArgument('id', InputArgument::REQUIRED, 'Backup record ID to restore'); $this->addOption('files-only', null, InputOption::VALUE_NONE, 'Restore files only (skip database)'); $this->addOption('db-only', null, InputOption::VALUE_NONE, 'Restore database only (skip files)'); $this->addOption('no-preserve-config', null, InputOption::VALUE_NONE, 'Do not preserve current configuration.php'); $this->addOption('password', 'p', InputOption::VALUE_REQUIRED, 'Decryption password for encrypted archives', ''); } protected function doExecute(InputInterface $input, OutputInterface $output): int { $io = new SymfonyStyle($input, $output); $recordId = (int) $input->getArgument('id'); $io->title('MokoSuiteBackup — Restore Backup'); $db = Factory::getDbo(); $query = $db->getQuery(true) ->select('*') ->from($db->quoteName('#__mokosuitebackup_records')) ->where($db->quoteName('id') . ' = ' . $recordId); $db->setQuery($query); $record = $db->loadObject(); if (!$record) { $io->error('Backup record not found: ' . $recordId); return 1; } if ($record->status !== 'complete') { $io->error('Cannot restore — backup status is: ' . $record->status); return 1; } if (empty($record->absolute_path) || !is_file($record->absolute_path)) { $io->error('Backup archive not found: ' . ($record->absolute_path ?: 'no path')); return 1; } $io->warning('This will overwrite the current site files and/or database.'); $io->text('Archive: ' . $record->absolute_path); $io->text('Type: ' . $record->backup_type); if (!$io->confirm('Are you sure you want to continue?', false)) { $io->info('Restore cancelled.'); return 0; } $engineFile = JPATH_ADMINISTRATOR . '/components/com_mokosuitebackup/src/Engine/RestoreEngine.php'; if (!file_exists($engineFile)) { $io->error('RestoreEngine not found. Is the component fully installed?'); return 1; } if (!class_exists(RestoreEngine::class)) { require_once $engineFile; } $filesOnly = $input->getOption('files-only'); $dbOnly = $input->getOption('db-only'); $preserveConfig = !$input->getOption('no-preserve-config'); $password = $input->getOption('password') ?: ''; $restoreFiles = !$dbOnly; $restoreDb = !$filesOnly; if ($filesOnly) { $io->note('Restoring files only (database will not be touched)'); } elseif ($dbOnly) { $io->note('Restoring database only (files will not be touched)'); } $engine = new RestoreEngine(); $result = $engine->restore($recordId, $restoreFiles, $restoreDb, $preserveConfig, $password); if ($result['success']) { $io->success($result['message']); return 0; } $io->error($result['message']); return 1; } }