|
|
|
@@ -28,13 +28,17 @@ namespace Froxlor\Cli;
|
|
|
|
use Exception;
|
|
|
|
use Exception;
|
|
|
|
use Froxlor\Froxlor;
|
|
|
|
use Froxlor\Froxlor;
|
|
|
|
use Froxlor\Install\AutoUpdate;
|
|
|
|
use Froxlor\Install\AutoUpdate;
|
|
|
|
|
|
|
|
use Froxlor\Install\Preconfig;
|
|
|
|
use Froxlor\Install\Update;
|
|
|
|
use Froxlor\Install\Update;
|
|
|
|
use Froxlor\Settings;
|
|
|
|
use Froxlor\Settings;
|
|
|
|
use Froxlor\System\Mailer;
|
|
|
|
use Froxlor\System\Mailer;
|
|
|
|
use Symfony\Component\Console\Input\InputInterface;
|
|
|
|
use Symfony\Component\Console\Input\InputInterface;
|
|
|
|
use Symfony\Component\Console\Input\InputOption;
|
|
|
|
use Symfony\Component\Console\Input\InputOption;
|
|
|
|
use Symfony\Component\Console\Output\OutputInterface;
|
|
|
|
use Symfony\Component\Console\Output\OutputInterface;
|
|
|
|
|
|
|
|
use Symfony\Component\Console\Question\ChoiceQuestion;
|
|
|
|
use Symfony\Component\Console\Question\ConfirmationQuestion;
|
|
|
|
use Symfony\Component\Console\Question\ConfirmationQuestion;
|
|
|
|
|
|
|
|
use Symfony\Component\Console\Question\Question;
|
|
|
|
|
|
|
|
use Symfony\Component\Console\Style\SymfonyStyle;
|
|
|
|
|
|
|
|
|
|
|
|
final class UpdateCommand extends CliCommand
|
|
|
|
final class UpdateCommand extends CliCommand
|
|
|
|
{
|
|
|
|
{
|
|
|
|
@@ -44,6 +48,8 @@ final class UpdateCommand extends CliCommand
|
|
|
|
$this->setName('froxlor:update');
|
|
|
|
$this->setName('froxlor:update');
|
|
|
|
$this->setDescription('Check for newer version and update froxlor');
|
|
|
|
$this->setDescription('Check for newer version and update froxlor');
|
|
|
|
$this->addOption('check-only', 'c', InputOption::VALUE_NONE, 'Only check for newer version and exit')
|
|
|
|
$this->addOption('check-only', 'c', InputOption::VALUE_NONE, 'Only check for newer version and exit')
|
|
|
|
|
|
|
|
->addOption('show-update-options', 'o', InputOption::VALUE_NONE, 'Show possible update option parameter for the update if any. Only usable in combination with "check-only".')
|
|
|
|
|
|
|
|
->addOption('update-options', 'O', InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, 'Parameter list of update options.')
|
|
|
|
->addOption('database', 'd', InputOption::VALUE_NONE, 'Only run database updates in case updates are done via apt or manually.')
|
|
|
|
->addOption('database', 'd', InputOption::VALUE_NONE, 'Only run database updates in case updates are done via apt or manually.')
|
|
|
|
->addOption('mail-notify', 'm', InputOption::VALUE_NONE, 'Additionally inform administrator via email if a newer version was found')
|
|
|
|
->addOption('mail-notify', 'm', InputOption::VALUE_NONE, 'Additionally inform administrator via email if a newer version was found')
|
|
|
|
->addOption('yes-to-all', 'A', InputOption::VALUE_NONE, 'Do not ask for download, extract and database-update, just do it (if not --check-only is set)')
|
|
|
|
->addOption('yes-to-all', 'A', InputOption::VALUE_NONE, 'Do not ask for download, extract and database-update, just do it (if not --check-only is set)')
|
|
|
|
@@ -63,9 +69,13 @@ final class UpdateCommand extends CliCommand
|
|
|
|
$output->writeln('<info>' . lng('update.dbupdate_required') . '</>');
|
|
|
|
$output->writeln('<info>' . lng('update.dbupdate_required') . '</>');
|
|
|
|
if ($input->getOption('check-only')) {
|
|
|
|
if ($input->getOption('check-only')) {
|
|
|
|
$output->writeln('<comment>Doing nothing because of "check-only" flag.</>');
|
|
|
|
$output->writeln('<comment>Doing nothing because of "check-only" flag.</>');
|
|
|
|
|
|
|
|
$this->askUpdateOptions($input, $output, null, false);
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
$yestoall = $input->getOption('yes-to-all') !== false;
|
|
|
|
$yestoall = $input->getOption('yes-to-all') !== false;
|
|
|
|
$helper = $this->getHelper('question');
|
|
|
|
$helper = $this->getHelper('question');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
$this->askUpdateOptions($input, $output, $helper, $yestoall);
|
|
|
|
|
|
|
|
|
|
|
|
$question = new ConfirmationQuestion('Update database? [no] ', false, '/^(y|j)/i');
|
|
|
|
$question = new ConfirmationQuestion('Update database? [no] ', false, '/^(y|j)/i');
|
|
|
|
if ($yestoall || $helper->ask($input, $output, $question)) {
|
|
|
|
if ($yestoall || $helper->ask($input, $output, $question)) {
|
|
|
|
$result = $this->runUpdate($output, true);
|
|
|
|
$result = $this->runUpdate($output, true);
|
|
|
|
@@ -101,7 +111,7 @@ final class UpdateCommand extends CliCommand
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// there is a new version
|
|
|
|
// there is a new version
|
|
|
|
if ($input->getOption('check-only')) {
|
|
|
|
if ($input->getOption('check-only')) {
|
|
|
|
$text = lng('update.uc_newinfo', [(Settings::Get('system.update_channel') != 'stable' ? Settings::Get('system.update_channel').' ' : ''), AutoUpdate::getFromResult('version'), Froxlor::VERSION]);
|
|
|
|
$text = lng('update.uc_newinfo', [(Settings::Get('system.update_channel') != 'stable' ? Settings::Get('system.update_channel') . ' ' : ''), AutoUpdate::getFromResult('version'), Froxlor::VERSION]);
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
$text = lng('admin.newerversionavailable') . ' ' . lng('admin.newerversiondetails', [AutoUpdate::getFromResult('version'), Froxlor::VERSION]);
|
|
|
|
$text = lng('admin.newerversionavailable') . ' ' . lng('admin.newerversiondetails', [AutoUpdate::getFromResult('version'), Froxlor::VERSION]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@@ -152,6 +162,7 @@ final class UpdateCommand extends CliCommand
|
|
|
|
// check whether we only wanted to check
|
|
|
|
// check whether we only wanted to check
|
|
|
|
if ($input->getOption('check-only')) {
|
|
|
|
if ($input->getOption('check-only')) {
|
|
|
|
//$output->writeln('<comment>Not proceeding as "check-only" is specified</>');
|
|
|
|
//$output->writeln('<comment>Not proceeding as "check-only" is specified</>');
|
|
|
|
|
|
|
|
$this->askUpdateOptions($input, $output, null, false);
|
|
|
|
return $result;
|
|
|
|
return $result;
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
$yestoall = $input->getOption('yes-to-all') !== false;
|
|
|
|
$yestoall = $input->getOption('yes-to-all') !== false;
|
|
|
|
@@ -174,6 +185,9 @@ final class UpdateCommand extends CliCommand
|
|
|
|
if ($auex == 0) {
|
|
|
|
if ($auex == 0) {
|
|
|
|
$output->writeln("<info>Froxlor files updated successfully.</>");
|
|
|
|
$output->writeln("<info>Froxlor files updated successfully.</>");
|
|
|
|
$result = self::SUCCESS;
|
|
|
|
$result = self::SUCCESS;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
$this->askUpdateOptions($input, $output, $helper, $yestoall);
|
|
|
|
|
|
|
|
|
|
|
|
$question = new ConfirmationQuestion('Update database? [no] ', false, '/^(y|j)/i');
|
|
|
|
$question = new ConfirmationQuestion('Update database? [no] ', false, '/^(y|j)/i');
|
|
|
|
if ($yestoall || $helper->ask($input, $output, $question)) {
|
|
|
|
if ($yestoall || $helper->ask($input, $output, $question)) {
|
|
|
|
$result = $this->runUpdate($output, true);
|
|
|
|
$result = $this->runUpdate($output, true);
|
|
|
|
@@ -195,12 +209,141 @@ final class UpdateCommand extends CliCommand
|
|
|
|
return $result;
|
|
|
|
return $result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* @param InputInterface $input
|
|
|
|
|
|
|
|
* @param OutputInterface $output
|
|
|
|
|
|
|
|
* @param $helper
|
|
|
|
|
|
|
|
* @param bool $yestoall
|
|
|
|
|
|
|
|
* @return void
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
private function askUpdateOptions(InputInterface $input, OutputInterface $output, $helper, bool $yestoall = false)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
// check for preconfigs
|
|
|
|
|
|
|
|
$preconfig = Preconfig::getPreConfig(true);
|
|
|
|
|
|
|
|
$show_options_only = $input->getOption('show-update-options') !== false;
|
|
|
|
|
|
|
|
if (!is_null($helper) && $show_options_only) {
|
|
|
|
|
|
|
|
$output->writeln('<comment>Unsetting "show-update-options" due to not being called with "check-only".</>');
|
|
|
|
|
|
|
|
$show_options_only = false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
$update_options = [];
|
|
|
|
|
|
|
|
// set parameters
|
|
|
|
|
|
|
|
$uOptions = $input->getOption('update-options');
|
|
|
|
|
|
|
|
if (!empty($uOptions)) {
|
|
|
|
|
|
|
|
$options_value = [];
|
|
|
|
|
|
|
|
foreach ($uOptions as $givenOption) {
|
|
|
|
|
|
|
|
$optVal = explode("=", $givenOption);
|
|
|
|
|
|
|
|
if (count($optVal) == 2) {
|
|
|
|
|
|
|
|
$options_value[$optVal[0]] = $optVal[1];
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!empty($preconfig)) {
|
|
|
|
|
|
|
|
krsort($preconfig);
|
|
|
|
|
|
|
|
foreach ($preconfig as $section) {
|
|
|
|
|
|
|
|
if (!$show_options_only) {
|
|
|
|
|
|
|
|
$output->writeln("<info>Updater questions for " . $section['title'] . "</>");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
foreach ($section['fields'] as $update_field => $metainfo) {
|
|
|
|
|
|
|
|
if (isset($options_value[$update_field])) {
|
|
|
|
|
|
|
|
$output->writeln('Setting given parameter "' . $update_field . '" to "' . $options_value[$update_field] . '"');
|
|
|
|
|
|
|
|
$_POST[$update_field] = $options_value[$update_field];
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
$default = null;
|
|
|
|
|
|
|
|
$question_text = html_entity_decode(strip_tags($metainfo['label']), ENT_QUOTES | ENT_IGNORE, "UTF-8");
|
|
|
|
|
|
|
|
if ($metainfo['type'] == 'checkbox') {
|
|
|
|
|
|
|
|
$default = (int)$metainfo['checked'];
|
|
|
|
|
|
|
|
if ($show_options_only) {
|
|
|
|
|
|
|
|
$update_options[] = [
|
|
|
|
|
|
|
|
'name' => $update_field,
|
|
|
|
|
|
|
|
'question' => $question_text,
|
|
|
|
|
|
|
|
'default' => $default,
|
|
|
|
|
|
|
|
'choices' => '0: No' . PHP_EOL . '1: Yes' . PHP_EOL
|
|
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
$question = new ConfirmationQuestion($question_text . ' [' . ($metainfo['checked'] ? 'yes' : 'no') . '] ', (bool)$metainfo['checked'], '/^(y|j)/i');
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
} elseif ($metainfo['type'] == 'select') {
|
|
|
|
|
|
|
|
$default = $metainfo['selected'];
|
|
|
|
|
|
|
|
$choices = "";
|
|
|
|
|
|
|
|
foreach (array_values($metainfo['select_var'] ?? []) as $index => $choice) {
|
|
|
|
|
|
|
|
$choices .= $index . ': ' . $choice . PHP_EOL;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($show_options_only) {
|
|
|
|
|
|
|
|
$update_options[] = [
|
|
|
|
|
|
|
|
'name' => $update_field,
|
|
|
|
|
|
|
|
'question' => $question_text,
|
|
|
|
|
|
|
|
'default' => !empty($default) ? $default : '-',
|
|
|
|
|
|
|
|
'choices' => $choices
|
|
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
$question = new ChoiceQuestion(
|
|
|
|
|
|
|
|
$question_text,
|
|
|
|
|
|
|
|
array_values($metainfo['select_var'] ?? []),
|
|
|
|
|
|
|
|
$metainfo['selected']
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
$question->setValidator(function ($answer) use ($metainfo): string {
|
|
|
|
|
|
|
|
$key = array_keys($metainfo['select_var'])[(int)$answer] ?? false; // Find the key based on the selected value
|
|
|
|
|
|
|
|
if ($key === false) {
|
|
|
|
|
|
|
|
throw new \RuntimeException('Invalid selection.');
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return $key;
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
} elseif ($metainfo['type'] == 'text') {
|
|
|
|
|
|
|
|
$default = $metainfo['value'] ?? '';
|
|
|
|
|
|
|
|
if ($show_options_only) {
|
|
|
|
|
|
|
|
$update_options[] = [
|
|
|
|
|
|
|
|
'name' => $update_field,
|
|
|
|
|
|
|
|
'question' => $question_text,
|
|
|
|
|
|
|
|
'default' => $default,
|
|
|
|
|
|
|
|
'choices' => PHP_EOL
|
|
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
$question = new Question($question_text . (!empty($metainfo['value']) ? ' [' . $metainfo['value'] . ']' : ''), $default);
|
|
|
|
|
|
|
|
$question->setValidator(function (string $answer) use ($metainfo): string {
|
|
|
|
|
|
|
|
if (($metainfo['mandatory'] ?? false) && empty($answer)) {
|
|
|
|
|
|
|
|
throw new \RuntimeException(
|
|
|
|
|
|
|
|
'Answer cannot be empty'
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!empty($metainfo['pattern'] ?? "") && !preg_match("/" . $metainfo['pattern'] . "/", $answer)) {
|
|
|
|
|
|
|
|
throw new \RuntimeException('Answer does not seem to be in valid format');
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return $answer;
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
$output->writeln("<error>Unknown type " . $metainfo['type'] . "</error>");
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!$show_options_only) {
|
|
|
|
|
|
|
|
if ($yestoall) {
|
|
|
|
|
|
|
|
$_POST[$update_field] = $default;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
$_POST[$update_field] = $helper->ask($input, $output, $question);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if ($show_options_only) {
|
|
|
|
|
|
|
|
$io = new SymfonyStyle($input, $output);
|
|
|
|
|
|
|
|
$io->table(
|
|
|
|
|
|
|
|
['Parameter', 'Description', 'Default', 'Choices'],
|
|
|
|
|
|
|
|
$update_options
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private function mailNotify(InputInterface $input, OutputInterface $output)
|
|
|
|
private function mailNotify(InputInterface $input, OutputInterface $output)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
if ($input->getOption('mail-notify')) {
|
|
|
|
if ($input->getOption('mail-notify')) {
|
|
|
|
$last_check_version = Settings::Get('system.update_notify_last');
|
|
|
|
$last_check_version = Settings::Get('system.update_notify_last');
|
|
|
|
if (Update::versionInUpdate($last_check_version, AutoUpdate::getFromResult('version'))) {
|
|
|
|
if (Update::versionInUpdate($last_check_version, AutoUpdate::getFromResult('version'))) {
|
|
|
|
$text = lng('update.uc_newinfo', [(Settings::Get('system.update_channel') != 'stable' ? Settings::Get('system.update_channel').' ' : ''), AutoUpdate::getFromResult('version'), Froxlor::VERSION]);
|
|
|
|
$text = lng('update.uc_newinfo', [(Settings::Get('system.update_channel') != 'stable' ? Settings::Get('system.update_channel') . ' ' : ''), AutoUpdate::getFromResult('version'), Froxlor::VERSION]);
|
|
|
|
$mail = new Mailer(true);
|
|
|
|
$mail = new Mailer(true);
|
|
|
|
$mail->Body = $text;
|
|
|
|
$mail->Body = $text;
|
|
|
|
$mail->Subject = "[froxlor] " . lng('update.notify_subject');
|
|
|
|
$mail->Subject = "[froxlor] " . lng('update.notify_subject');
|
|
|
|
|