File manager - Edit - /home/opticamezl/www/newok/Console.tar
Back
RemoveOldFilesCommand.php 0000644 00000011374 15172704325 0011447 0 ustar 00 <?php /** * Joomla! Content Management System * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Console; use Joomla\Console\Command\AbstractCommand; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Console command for removing files which should have been cleared during an update * * @since 4.0.0 */ class RemoveOldFilesCommand extends AbstractCommand { /** * The default command name * * @var string * @since 4.0.0 */ protected static $defaultName = 'update:joomla:remove-old-files'; /** * Internal function to execute the command. * * @param InputInterface $input The input to inject into the command. * @param OutputInterface $output The output to inject into the command. * * @return integer The command exit code * * @since 4.0.0 */ protected function doExecute(InputInterface $input, OutputInterface $output): int { $symfonyStyle = new SymfonyStyle($input, $output); $dryRun = $input->getOption('dry-run'); $symfonyStyle->title('Removing Unneeded Files & Folders' . ($dryRun ? ' - Dry Run' : '')); // We need the update script \JLoader::register('JoomlaInstallerScript', JPATH_ADMINISTRATOR . '/components/com_admin/script.php'); $status = (new \JoomlaInstallerScript())->deleteUnexistingFiles($dryRun, true); if ($output->isVeryVerbose() || $output->isDebug()) { foreach ($status['files_checked'] as $file) { $exists = in_array($file, array_values($status['files_exist'])); if ($exists) { $symfonyStyle->writeln('<error>File Checked & Exists</error> - ' . $file, OutputInterface::VERBOSITY_VERY_VERBOSE); } else { $symfonyStyle->writeln('<info>File Checked & Doesn\'t Exist</info> - ' . $file, OutputInterface::VERBOSITY_DEBUG); } } foreach ($status['folders_checked'] as $folder) { $exists = in_array($folder, array_values($status['folders_exist'])); if ($exists) { $symfonyStyle->writeln('<error>Folder Checked & Exists</error> - ' . $folder, OutputInterface::VERBOSITY_VERY_VERBOSE); } else { $symfonyStyle->writeln('<info>Folder Checked & Doesn\'t Exist</info> - ' . $folder, OutputInterface::VERBOSITY_DEBUG); } } } if ($dryRun === false) { foreach ($status['files_deleted'] as $file) { $symfonyStyle->writeln('<comment>File Deleted = ' . $file . '</comment>', OutputInterface::VERBOSITY_VERBOSE); } foreach ($status['files_errors'] as $error) { $symfonyStyle->error($error); } foreach ($status['folders_deleted'] as $folder) { $symfonyStyle->writeln('<comment>Folder Deleted = ' . $folder . '</comment>', OutputInterface::VERBOSITY_VERBOSE); } foreach ($status['folders_errors'] as $error) { $symfonyStyle->error($error); } } $symfonyStyle->success( sprintf( $dryRun ? '%s Files checked and %s would be deleted' : '%s Files checked and %s deleted', \count($status['files_checked']), ($dryRun ? \count($status['files_exist']) : \count($status['files_deleted'])) ) ); $symfonyStyle->success( sprintf( $dryRun ? '%s Folders checked and %s would be deleted' : '%s Folders checked and %s deleted', \count($status['folders_checked']), ($dryRun ? \count($status['folders_exist']) : \count($status['folders_deleted'])) ) ); return Command::SUCCESS; } /** * Configure the command. * * @return void * * @since 4.0.0 */ protected function configure(): void { $help = "<info>%command.name%</info> removes old files which should have been deleted during a Joomla update \nUsage: <info>php %command.full_name%</info>"; $this->setDescription('Remove old system files'); $this->addOption('dry-run', null, InputOption::VALUE_NONE, 'Executes a dry run without deleting anything'); $this->setHelp($help); } } SessionGcCommand.php 0000644 00000007204 15172704325 0010462 0 ustar 00 <?php /** * Joomla! Content Management System * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Console; use Joomla\Console\Command\AbstractCommand; use Joomla\DI\ContainerAwareInterface; use Joomla\DI\ContainerAwareTrait; use Joomla\Session\SessionInterface; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Console command for performing session garbage collection * * @since 4.0.0 */ class SessionGcCommand extends AbstractCommand implements ContainerAwareInterface { use ContainerAwareTrait; /** * The default command name * * @var string * @since 4.0.0 */ protected static $defaultName = 'session:gc'; /** * Internal function to execute the command. * * @param InputInterface $input The input to inject into the command. * @param OutputInterface $output The output to inject into the command. * * @return integer The command exit code * * @since 4.0.0 */ protected function doExecute(InputInterface $input, OutputInterface $output): int { $symfonyStyle = new SymfonyStyle($input, $output); $symfonyStyle->title('Running Session Garbage Collection'); $session = $this->getSessionService($input->getOption('application')); $gcResult = $session->gc(); // Destroy the session started for this process $session->destroy(); if ($gcResult === false) { $symfonyStyle->error('Garbage collection was not completed. Either the operation failed or it is not supported on your platform.'); return Command::FAILURE; } $symfonyStyle->success('Garbage collection completed.'); return Command::SUCCESS; } /** * Configure the command. * * @return void * * @since 4.0.0 */ protected function configure(): void { $help = "<info>%command.name%</info> runs PHP's garbage collection operation for session data \nUsage: <info>php %command.full_name%</info> \nThis command defaults to performing garbage collection for the frontend (site) application. \nTo run garbage collection for another application, you can specify it with the <info>--application</info> option. \nUsage: <info>php %command.full_name% --application=[APPLICATION]</info>"; $this->setDescription('Perform session garbage collection'); $this->addOption('application', 'app', InputOption::VALUE_OPTIONAL, 'The application to perform garbage collection for.', 'site'); $this->setHelp($help); } /** * Get the session service for the requested application. * * @param string $application The application session service to retrieve * * @return SessionInterface * * @since 4.0.0 */ private function getSessionService(string $application): SessionInterface { if (!$this->getContainer()->has("session.web.$application")) { throw new \InvalidArgumentException( sprintf( 'The `%s` application is not a valid option.', $application ) ); } return $this->getContainer()->get("session.web.$application"); } } SetConfigurationCommand.php 0000644 00000027720 15172704325 0012055 0 ustar 00 <?php /** * Joomla! Content Management System * * @copyright (C) 2020 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Console; use Joomla\CMS\Factory; use Joomla\CMS\Language\Text; use Joomla\Console\Command\AbstractCommand; use Joomla\Database\DatabaseDriver; use Joomla\Registry\Registry; use Symfony\Component\Console\Input\Input; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Console command Setting Configuration options * * @since 4.0.0 */ class SetConfigurationCommand extends AbstractCommand { /** * The default command name * * @var string * @since 4.0.0 */ protected static $defaultName = 'config:set'; /** * Stores the Input Object * @var Input * @since 4.0.0 */ private $cliInput; /** * SymfonyStyle Object * @var SymfonyStyle * @since 4.0.0 */ private $ioStyle; /** * Options Array * @var array * @since 4.0.0 */ private $options; /** * Return code if configuration is set successfully * @since 4.0.0 */ public const CONFIG_SET_SUCCESSFUL = 0; /** * Return code if configuration set failed * @since 4.0.0 */ public const CONFIG_SET_FAILED = 1; /** * Return code if config validation failed * @since 4.0.0 */ public const CONFIG_VALIDATION_FAILED = 2; /** * Return code if options are wrong * @since 4.0.0 */ public const CONFIG_OPTIONS_WRONG = 3; /** * Return code if database validation failed * @since 4.0.0 */ public const DB_VALIDATION_FAILED = 4; /** * Configures the IO * * @param InputInterface $input Console Input * @param OutputInterface $output Console Output * * @return void * * @since 4.0.0 * */ private function configureIO(InputInterface $input, OutputInterface $output) { $language = Factory::getLanguage(); $language->load('', JPATH_INSTALLATION, null, false, false) || $language->load('', JPATH_INSTALLATION, null, true); $language->load('com_config', JPATH_ADMINISTRATOR, null, false, false) || $language->load('com_config', JPATH_ADMINISTRATOR, null, true); $this->cliInput = $input; $this->ioStyle = new SymfonyStyle($input, $output); } /** * Collects options from user input * * @param array $options Options input by users * * @return boolean * * @since 4.0.0 */ private function retrieveOptionsFromInput(array $options): bool { $collected = []; foreach ($options as $option) { if (strpos($option, '=') === false) { $this->ioStyle->error('Options and values should be separated by "="'); return false; } list($option, $value) = explode('=', $option); $collected[$option] = $value; } $this->options = $collected; return true; } /** * Validates the options provided * * @return boolean * * @since 4.0.0 */ private function validateOptions(): bool { $config = $this->getInitialConfigurationOptions(); $configs = $config->toArray(); $valid = true; array_walk( $this->options, function ($value, $key) use ($configs, &$valid) { if (!array_key_exists($key, $configs)) { $this->ioStyle->error("Can't find option *$key* in configuration list"); $valid = false; } } ); return $valid; } /** * Sets the options array * * @param string $options Options string * * @since 4.0.0 * * @return void */ public function setOptions($options) { $this->options = explode(' ', $options); } /** * Collects the options array * * @return array|mixed * * @since 4.0.0 */ public function getOptions() { return $this->cliInput->getArgument('options'); } /** * Returns Default configuration Object * * @return Registry * * @since 4.0.0 */ public function getInitialConfigurationOptions(): Registry { return (new Registry(new \JConfig())); } /** * Save the configuration file * * @param array $options Options array * * @return boolean * * @since 4.0.0 */ public function saveConfiguration($options): bool { $app = $this->getApplication(); // Check db connection encryption properties $model = $app->bootComponent('com_config')->getMVCFactory($app)->createModel('Application', 'Administrator'); if (!$model->save($options)) { $this->ioStyle->error(Text::_('Failed to save properties')); return false; } return true; } /** * Initialise the command. * * @return void * * @since 4.0.0 */ protected function configure(): void { $this->addArgument( 'options', InputArgument::REQUIRED | InputArgument::IS_ARRAY, 'All the options you want to set' ); $help = "<info>%command.name%</info> sets the value for a configuration option \nUsage: <info>php %command.full_name%</info> <option>=<value>"; $this->setDescription('Set a value for a configuration option'); $this->setHelp($help); } /** * Verifies database connection * * @param array $options Options array * * @return boolean|\Joomla\Database\DatabaseInterface * * @since 4.0.0 * @throws \Exception */ public function checkDb($options): bool { // Ensure a database type was selected. if (empty($options['dbtype'])) { $this->ioStyle->error(Text::_('INSTL_DATABASE_INVALID_TYPE')); return false; } // Ensure that a hostname and user name were input. if (empty($options['host']) || empty($options['user'])) { $this->ioStyle->error(Text::_('INSTL_DATABASE_INVALID_DB_DETAILS')); return false; } // Validate database table prefix. if (isset($options['dbprefix']) && !preg_match('#^[a-zA-Z]+[a-zA-Z0-9_]*$#', $options['dbprefix'])) { $this->ioStyle->error(Text::_('INSTL_DATABASE_PREFIX_MSG')); return false; } // Validate length of database table prefix. if (isset($options['dbprefix']) && strlen($options['dbprefix']) > 15) { $this->ioStyle->error(Text::_('INSTL_DATABASE_FIX_TOO_LONG'), 'warning'); return false; } // Validate length of database name. if (strlen($options['db']) > 64) { $this->ioStyle->error(Text::_('INSTL_DATABASE_NAME_TOO_LONG')); return false; } // Validate database name. if (in_array($options['dbtype'], ['pgsql', 'postgresql'], true) && !preg_match('#^[a-zA-Z_][0-9a-zA-Z_$]*$#', $options['db'])) { $this->ioStyle->error(Text::_('INSTL_DATABASE_NAME_MSG_POSTGRES')); return false; } if (in_array($options['dbtype'], ['mysql', 'mysqli']) && preg_match('#[\\\\\/]#', $options['db'])) { $this->ioStyle->error(Text::_('INSTL_DATABASE_NAME_MSG_MYSQL')); return false; } // Workaround for UPPERCASE table prefix for PostgreSQL if (in_array($options['dbtype'], ['pgsql', 'postgresql'])) { if (isset($options['dbprefix']) && strtolower($options['dbprefix']) !== $options['dbprefix']) { $this->ioStyle->error(Text::_('INSTL_DATABASE_FIX_LOWERCASE')); return false; } } $app = $this->getApplication(); // Check db connection encryption properties $model = $app->bootComponent('com_config')->getMVCFactory($app)->createModel('Application', 'Administrator'); if (!$model->validateDbConnection($options)) { $this->ioStyle->error(Text::_('Failed to validate the db connection encryption properties')); return false; } // Build the connection options array. $settings = [ 'driver' => $options['dbtype'], 'host' => $options['host'], 'user' => $options['user'], 'password' => $options['password'], 'database' => $options['db'], 'prefix' => $options['dbprefix'], ]; if ((int) $options['dbencryption'] !== 0) { $settings['ssl'] = [ 'enable' => true, 'verify_server_cert' => (bool) $options['dbsslverifyservercert'], ]; foreach (['cipher', 'ca', 'key', 'cert'] as $value) { $confVal = trim($options['dbssl' . $value]); if ($confVal !== '') { $settings['ssl'][$value] = $confVal; } } } // Get a database object. try { $db = DatabaseDriver::getInstance($settings); $db->getVersion(); } catch (\Exception $e) { $this->ioStyle->error( Text::sprintf( 'Cannot connect to database, verify that you specified the correct database details %s', $e->getMessage() ) ); return false; } if ((int) $options['dbencryption'] !== 0 && empty($db->getConnectionEncryption())) { if ($db->isConnectionEncryptionSupported()) { $this->ioStyle->error(Text::_('COM_CONFIG_ERROR_DATABASE_ENCRYPTION_CONN_NOT_ENCRYPT')); } else { $this->ioStyle->error(Text::_('COM_CONFIG_ERROR_DATABASE_ENCRYPTION_SRV_NOT_SUPPORTS')); } return false; } return true; } /** * Internal function to execute the command. * * @param InputInterface $input The input to inject into the command. * @param OutputInterface $output The output to inject into the command. * * @return integer The command exit code * * @since 4.0.0 * @throws \Exception */ protected function doExecute(InputInterface $input, OutputInterface $output): int { $this->configureIO($input, $output); $options = $this->getOptions(); if (!$this->retrieveOptionsFromInput($options)) { return self::CONFIG_OPTIONS_WRONG; } if (!$this->validateOptions()) { return self::CONFIG_VALIDATION_FAILED; } $initialOptions = $this->getInitialConfigurationOptions()->toArray(); $combinedOptions = $this->sanitizeOptions(array_merge($initialOptions, $this->options)); if (!$this->checkDb($combinedOptions)) { return self::DB_VALIDATION_FAILED; } if ($this->saveConfiguration($combinedOptions)) { $this->ioStyle->success('Configuration set'); return self::CONFIG_SET_SUCCESSFUL; } return self::CONFIG_SET_FAILED; } /** * Sanitize the options array for boolean * * @param array $options Options array * * @return array * * @since 4.0.0 */ public function sanitizeOptions(array $options): array { foreach ($options as $key => $value) { $value = $value === 'false' ? false : $value; $value = $value === 'true' ? true : $value; $options[$key] = $value; } return $options; } } CleanCacheCommand.php 0000644 00000005303 15172704325 0010531 0 ustar 00 <?php /** * Joomla! Content Management System * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Console; use Joomla\Console\Command\AbstractCommand; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Console command for cleaning the system cache * * @since 4.0.0 */ class CleanCacheCommand extends AbstractCommand { /** * The default command name * * @var string * @since 4.0.0 */ protected static $defaultName = 'cache:clean'; /** * Internal function to execute the command. * * @param InputInterface $input The input to inject into the command. * @param OutputInterface $output The output to inject into the command. * * @return integer The command exit code * * @since 4.0.0 */ protected function doExecute(InputInterface $input, OutputInterface $output): int { $symfonyStyle = new SymfonyStyle($input, $output); $symfonyStyle->title('Cleaning System Cache'); $cache = $this->getApplication()->bootComponent('com_cache')->getMVCFactory(); /** @var \Joomla\Component\Cache\Administrator\Model\CacheModel $model */ $model = $cache->createModel('Cache', 'Administrator', ['ignore_request' => true]); if ($input->getArgument('expired')) { if (!$model->purge()) { $symfonyStyle->error('Expired Cache not cleaned'); return Command::FAILURE; } $symfonyStyle->success('Expired Cache cleaned'); return Command::SUCCESS; } if (!$model->clean()) { $symfonyStyle->error('Cache not cleaned'); return Command::FAILURE; } $symfonyStyle->success('Cache cleaned'); return Command::SUCCESS; } /** * Configure the command. * * @return void * * @since 4.0.0 */ protected function configure(): void { $help = "<info>%command.name%</info> will clear entries from the system cache \nUsage: <info>php %command.full_name%</info>"; $this->addArgument('expired', InputArgument::OPTIONAL, 'will clear expired entries from the system cache'); $this->setDescription('Clean cache entries'); $this->setHelp($help); } } ExtensionDiscoverListCommand.php 0000644 00000005627 15172704325 0013103 0 ustar 00 <?php /** * Joomla! Content Management System * * @copyright (C) 2021 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Console; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Console command for list discovered extensions * * @since 4.0.0 */ class ExtensionDiscoverListCommand extends ExtensionsListCommand { /** * The default command name * * @var string * * @since 4.0.0 */ protected static $defaultName = 'extension:discover:list'; /** * Initialise the command. * * @return void * * @since 4.0.0 */ protected function configure(): void { $help = "<info>%command.name%</info> is used to list all extensions that could be installed via discoverinstall \nUsage: \n <info>php %command.full_name%</info>"; $this->setDescription('List discovered extensions'); $this->setHelp($help); } /** * Filters the extension state * * @param array $extensions The Extensions * @param string $state The Extension state * * @return array * * @since 4.0.0 */ public function filterExtensionsBasedOnState($extensions, $state): array { $filteredExtensions = []; foreach ($extensions as $key => $extension) { if ($extension['state'] === $state) { $filteredExtensions[] = $extension; } } return $filteredExtensions; } /** * Internal function to execute the command. * * @param InputInterface $input The input to inject into the command. * @param OutputInterface $output The output to inject into the command. * * @return integer The command exit code * * @since 4.0.0 */ protected function doExecute(InputInterface $input, OutputInterface $output): int { $this->configureIO($input, $output); $this->ioStyle->title('Discovered Extensions'); $extensions = $this->getExtensions(); $state = -1; $discovered_extensions = $this->filterExtensionsBasedOnState($extensions, $state); if (empty($discovered_extensions)) { $this->ioStyle->note("There are no pending discovered extensions to install. Perhaps you need to run extension:discover first?"); return Command::SUCCESS; } $discovered_extensions = $this->getExtensionsNameAndId($discovered_extensions); $this->ioStyle->table(['Name', 'Extension ID', 'Version', 'Type', 'Enabled'], $discovered_extensions); return Command::SUCCESS; } } TasksStateCommand.php 0000644 00000013337 15172704325 0010657 0 ustar 00 <?php /** * Joomla! Content Management System. * * @copyright (C) 2021 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Console; use Joomla\CMS\Application\ConsoleApplication; use Joomla\CMS\Factory; use Joomla\Component\Scheduler\Administrator\Model\TaskModel; use Joomla\Component\Scheduler\Administrator\Table\TaskTable; use Joomla\Component\Scheduler\Administrator\Task\Task; use Joomla\Console\Application; use Joomla\Console\Command\AbstractCommand; use Joomla\Utilities\ArrayHelper; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Console command to change the state of tasks. * * @since 4.1.0 */ class TasksStateCommand extends AbstractCommand { /** * The default command name * * @var string * @since 4.1.0 */ protected static $defaultName = 'scheduler:state'; /** * The console application object * * @var Application * * @since 4.1.0 */ protected $application; /** * @var SymfonyStyle * * @since 4.1.0 */ private $ioStyle; /** * Internal function to execute the command. * * @param InputInterface $input The input to inject into the command. * @param OutputInterface $output The output to inject into the command. * * @return integer The command exit code * * @since 4.1.0 * @throws \Exception */ protected function doExecute(InputInterface $input, OutputInterface $output): int { Factory::getApplication()->getLanguage()->load('joomla', JPATH_ADMINISTRATOR); $this->configureIO($input, $output); $this->ioStyle->title('Change Task State'); $id = (string) $input->getOption('id'); $state = (string) $input->getOption('state'); // Try to validate and process ID, if passed if (\strlen($id)) { if (!Task::isValidId($id)) { $this->ioStyle->error('Invalid id passed!'); return 2; } $id = (is_numeric($id)) ? ($id + 0) : $id; } // Try to validate and process state, if passed if (\strlen($state)) { // If we get the logical state, we try to get the enumeration (but as a string) if (!is_numeric($state)) { $state = (string) ArrayHelper::arraySearch($state, Task::STATE_MAP); } if (!\strlen($state) || !Task::isValidState($state)) { $this->ioStyle->error('Invalid state passed!'); return 2; } } // If we didn't get ID as a flag, ask for it interactively while (!Task::isValidId($id)) { $id = $this->ioStyle->ask('Please specify the ID of the task'); } // If we didn't get state as a flag, ask for it interactively while ($state === false || !Task::isValidState($state)) { $state = (string) $this->ioStyle->ask('Should the state be "enable" (1), "disable" (0) or "trash" (-2)'); // Ensure we have the enumerated value (still as a string) $state = (Task::isValidState($state)) ? $state : ArrayHelper::arraySearch($state, Task::STATE_MAP); } // Finally, the enumerated state and id in their pure form $state = (int) $state; $id = (int) $id; /** @var ConsoleApplication $app */ $app = $this->getApplication(); /** @var TaskModel $taskModel */ $taskModel = $app->bootComponent('com_scheduler')->getMVCFactory()->createModel('Task', 'Administrator'); $task = $taskModel->getItem($id); // We couldn't fetch that task :( if (empty($task->id)) { $this->ioStyle->error("Task ID '{$id}' does not exist!"); return 1; } // If the item is checked-out we need a check in (currently not possible through the CLI) if ($taskModel->isCheckedOut($task)) { $this->ioStyle->error("Task ID '{$id}' is checked out!"); return 1; } /** @var TaskTable $table */ $table = $taskModel->getTable(); $action = Task::STATE_MAP[$state]; if (!$table->publish($id, $state)) { $this->ioStyle->error("Can't {$action} Task ID '{$id}'"); return 3; } $this->ioStyle->success("Task ID {$id} {$action}."); return 0; } /** * Configure the IO. * * @param InputInterface $input The input to inject into the command. * @param OutputInterface $output The output to inject into the command. * * @return void * * @since 4.1.0 */ private function configureIO(InputInterface $input, OutputInterface $output): void { $this->ioStyle = new SymfonyStyle($input, $output); } /** * Configure the command. * * @return void * * @since 4.1.0 */ protected function configure(): void { $this->addOption('id', 'i', InputOption::VALUE_REQUIRED, 'The id of the task to change state.'); $this->addOption('state', 's', InputOption::VALUE_REQUIRED, 'The new state of the task, can be 1/enable, 0/disable, or -2/trash.'); $help = "<info>%command.name%</info> changes the state of a task. \nUsage: <info>php %command.full_name%</info>"; $this->setDescription('Enable, disable or trash a scheduled task'); $this->setHelp($help); } } ExtensionDiscoverInstallCommand.php 0000644 00000015015 15172704325 0013566 0 ustar 00 <?php /** * Joomla! Content Management System * * @copyright (C) 2020 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Console; use Joomla\CMS\Installer\Installer; use Joomla\Console\Command\AbstractCommand; use Joomla\Database\DatabaseAwareTrait; use Joomla\Database\DatabaseInterface; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Console command for discovering extensions * * @since 4.0.0 */ class ExtensionDiscoverInstallCommand extends AbstractCommand { use DatabaseAwareTrait; /** * The default command name * * @var string * @since 4.0.0 */ protected static $defaultName = 'extension:discover:install'; /** * Stores the Input Object * * @var InputInterface * @since 4.0.0 */ private $cliInput; /** * SymfonyStyle Object * * @var SymfonyStyle * @since 4.0.0 */ private $ioStyle; /** * Instantiate the command. * * @param DatabaseInterface $db Database connector * * @since 4.0.0 */ public function __construct(DatabaseInterface $db) { parent::__construct(); $this->setDatabase($db); } /** * Configures the IO * * @param InputInterface $input Console Input * @param OutputInterface $output Console Output * * @return void * * @since 4.0.0 * */ private function configureIO(InputInterface $input, OutputInterface $output): void { $this->cliInput = $input; $this->ioStyle = new SymfonyStyle($input, $output); } /** * Initialise the command. * * @return void * * @since 4.0.0 */ protected function configure(): void { $this->addOption('eid', null, InputOption::VALUE_REQUIRED, 'The ID of the extension to discover'); $help = "<info>%command.name%</info> is used to discover extensions \nYou can provide the following option to the command: \n --eid: The ID of the extension \n If you do not provide a ID all discovered extensions are installed. \nUsage: \n <info>php %command.full_name% --eid=<id_of_the_extension></info>"; $this->setDescription('Install discovered extensions'); $this->setHelp($help); } /** * Used for discovering extensions * * @param string $eid Id of the extension * * @return integer The count of installed extensions * * @throws \Exception * @since 4.0.0 */ public function processDiscover($eid): int { $jInstaller = new Installer(); $jInstaller->setDatabase($this->getDatabase()); $count = 0; if ($eid === -1) { $db = $this->getDatabase(); $query = $db->getQuery(true) ->select($db->quoteName(['extension_id'])) ->from($db->quoteName('#__extensions')) ->where($db->quoteName('state') . ' = -1'); $db->setQuery($query); $eidsToDiscover = $db->loadObjectList(); foreach ($eidsToDiscover as $eidToDiscover) { if (!$jInstaller->discover_install($eidToDiscover->extension_id)) { return -1; } $count++; } if (empty($eidsToDiscover)) { return 0; } } else { if ($jInstaller->discover_install($eid)) { return 1; } else { return -1; } } return $count; } /** * Used for finding the text for the note * * @param int $count Number of extensions to install * @param int $eid ID of the extension or -1 if no special * * @return string The text for the note * * @since 4.0.0 */ public function getNote(int $count, int $eid): string { if ($count < 0 && $eid >= 0) { return 'Unable to install the extension with ID ' . $eid; } elseif ($count < 0 && $eid < 0) { return 'Unable to install discovered extensions.'; } elseif ($count === 0) { return 'There are no pending discovered extensions for install. Perhaps you need to run extension:discover first?'; } elseif ($count === 1 && $eid > 0) { return 'Extension with ID ' . $eid . ' installed successfully.'; } elseif ($count === 1 && $eid < 0) { return $count . ' discovered extension has been installed.'; } elseif ($count > 1 && $eid < 0) { return $count . ' discovered extensions have been installed.'; } else { return 'The return value is not possible and has to be checked.'; } } /** * Internal function to execute the command. * * @param InputInterface $input The input to inject into the command. * @param OutputInterface $output The output to inject into the command. * * @return integer The command exit code * * @since 4.0.0 */ protected function doExecute(InputInterface $input, OutputInterface $output): int { $this->configureIO($input, $output); $this->ioStyle->title('Install Discovered Extensions'); if ($eid = $this->cliInput->getOption('eid')) { $result = $this->processDiscover($eid); if ($result === -1) { $this->ioStyle->error($this->getNote($result, $eid)); return Command::FAILURE; } else { $this->ioStyle->success($this->getNote($result, $eid)); return Command::SUCCESS; } } else { $result = $this->processDiscover(-1); if ($result < 0) { $this->ioStyle->error($this->getNote($result, -1)); return Command::FAILURE; } elseif ($result === 0) { $this->ioStyle->note($this->getNote($result, -1)); return Command::SUCCESS; } else { $this->ioStyle->note($this->getNote($result, -1)); return Command::SUCCESS; } } } } ExtensionDiscoverCommand.php 0000644 00000007042 15172704325 0012240 0 ustar 00 <?php /** * Joomla! Content Management System * * @copyright (C) 2021 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Console; use Joomla\Console\Command\AbstractCommand; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Console command for discovering extensions * * @since 4.0.0 */ class ExtensionDiscoverCommand extends AbstractCommand { /** * The default command name * * @var string * * @since 4.0.0 */ protected static $defaultName = 'extension:discover'; /** * Stores the Input Object * * @var InputInterface * * @since 4.0.0 */ private $cliInput; /** * SymfonyStyle Object * * @var SymfonyStyle * * @since 4.0.0 */ private $ioStyle; /** * Configures the IO * * @param InputInterface $input Console Input * @param OutputInterface $output Console Output * * @return void * * @since 4.0.0 * */ private function configureIO(InputInterface $input, OutputInterface $output): void { $this->cliInput = $input; $this->ioStyle = new SymfonyStyle($input, $output); } /** * Initialise the command. * * @return void * * @since 4.0.0 */ protected function configure(): void { $help = "<info>%command.name%</info> is used to discover extensions \nUsage: \n <info>php %command.full_name%</info>"; $this->setDescription('Discover extensions'); $this->setHelp($help); } /** * Used for discovering extensions * * @return integer The count of discovered extensions * * @throws \Exception * * @since 4.0.0 */ public function processDiscover(): int { $app = $this->getApplication(); $mvcFactory = $app->bootComponent('com_installer')->getMVCFactory(); $model = $mvcFactory->createModel('Discover', 'Administrator'); return $model->discover(); } /** * Used for finding the text for the note * * @param int $count The count of installed Extensions * * @return string The text for the note * * @since 4.0.0 */ public function getNote(int $count): string { if ($count < 1) { return 'No extensions were discovered.'; } elseif ($count === 1) { return $count . ' extension has been discovered.'; } else { return $count . ' extensions have been discovered.'; } } /** * Internal function to execute the command. * * @param InputInterface $input The input to inject into the command. * @param OutputInterface $output The output to inject into the command. * * @return integer The command exit code * * @since 4.0.0 */ protected function doExecute(InputInterface $input, OutputInterface $output): int { $this->configureIO($input, $output); $count = $this->processDiscover(); $this->ioStyle->title('Discover Extensions'); $this->ioStyle->note($this->getNote($count)); return Command::SUCCESS; } } UpdateCoreCommand.php 0000644 00000027412 15172704325 0010623 0 ustar 00 <?php /** * Joomla! Content Management System * * @copyright (C) 2020 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Console; use Joomla\Application\Cli\CliInput; use Joomla\CMS\Extension\ExtensionHelper; use Joomla\CMS\Factory; use Joomla\CMS\Filesystem\File; use Joomla\CMS\Filesystem\Folder; use Joomla\CMS\Installer\InstallerHelper; use Joomla\CMS\Language\Text; use Joomla\CMS\Log\Log; use Joomla\Console\Command\AbstractCommand; use Joomla\Database\DatabaseInterface; use Symfony\Component\Console\Helper\ProgressBar; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Console command for updating Joomla! core * * @since 4.0.0 */ class UpdateCoreCommand extends AbstractCommand { /** * The default command name * * @var string * @since 4.0.0 */ protected static $defaultName = 'core:update'; /** * Stores the Input Object * @var CliInput * @since 4.0.0 */ private $cliInput; /** * SymfonyStyle Object * @var SymfonyStyle * @since 4.0.0 */ private $ioStyle; /** * Update Information * @var array * @since 4.0.0 */ public $updateInfo; /** * Update Model * @var array * @since 4.0.0 */ public $updateModel; /** * Progress Bar object * @var ProgressBar * @since 4.0.0 */ public $progressBar; /** * Return code for successful update * @since 4.0.0 */ public const UPDATE_SUCCESSFUL = 0; /** * Return code for failed update * @since 4.0.0 */ public const ERR_UPDATE_FAILED = 2; /** * Return code for failed checks * @since 4.0.0 */ public const ERR_CHECKS_FAILED = 1; /** * @var DatabaseInterface * @since 4.0.0 */ private $db; /** * UpdateCoreCommand constructor. * * @param DatabaseInterface $db Database Instance * * @since 4.0.0 */ public function __construct(DatabaseInterface $db) { $this->db = $db; parent::__construct(); } /** * Configures the IO * * @param InputInterface $input Console Input * @param OutputInterface $output Console Output * * @return void * * @since 4.0.0 * */ private function configureIO(InputInterface $input, OutputInterface $output) { ProgressBar::setFormatDefinition('custom', ' %current%/%max% -- %message%'); $this->progressBar = new ProgressBar($output, 8); $this->progressBar->setFormat('custom'); $this->cliInput = $input; $this->ioStyle = new SymfonyStyle($input, $output); $language = Factory::getLanguage(); $language->load('lib_joomla', JPATH_ADMINISTRATOR); $language->load('', JPATH_ADMINISTRATOR); $language->load('com_joomlaupdate', JPATH_ADMINISTRATOR); } /** * Internal function to execute the command. * * @param InputInterface $input The input to inject into the command. * @param OutputInterface $output The output to inject into the command. * * @return integer The command exit code * * @since 4.0.0 * @throws \Exception */ public function doExecute(InputInterface $input, OutputInterface $output): int { $this->configureIO($input, $output); $this->ioStyle->title('Updating Joomla'); $this->progressBar->setMessage("Starting up ..."); $this->progressBar->start(); $model = $this->getUpdateModel(); // Make sure logging is working before continue try { Log::add('Test logging', Log::INFO, 'Update'); } catch (\Throwable $e) { $message = Text::sprintf('COM_JOOMLAUPDATE_UPDATE_LOGGING_TEST_FAIL', $e->getMessage()); $this->ioStyle->error($message); return self::ERR_UPDATE_FAILED; } Log::add(Text::sprintf('COM_JOOMLAUPDATE_UPDATE_LOG_START', 0, 'CLI', \JVERSION), Log::INFO, 'Update'); $this->setUpdateInfo($model->getUpdateInformation()); $this->progressBar->advance(); $this->progressBar->setMessage('Running checks ...'); if (!$this->updateInfo['hasUpdate']) { $this->progressBar->finish(); $this->ioStyle->note('You already have the latest Joomla! version. ' . $this->updateInfo['latest']); return self::ERR_CHECKS_FAILED; } $this->progressBar->advance(); $this->progressBar->setMessage('Check Database Table Structure...'); $errors = $this->checkSchema(); if ($errors > 0) { $this->ioStyle->error('Database Table Structure not Up to Date'); $this->progressBar->finish(); $this->ioStyle->info('There were ' . $errors . ' errors'); return self::ERR_CHECKS_FAILED; } $this->progressBar->advance(); $this->progressBar->setMessage('Starting Joomla! update ...'); if ($this->updateJoomlaCore($model)) { $this->progressBar->finish(); if ($model->getErrors()) { $this->ioStyle->error('Update finished with errors. Please check logs for details.'); return self::ERR_UPDATE_FAILED; } $this->ioStyle->success('Joomla core updated successfully!'); return self::UPDATE_SUCCESSFUL; } $this->progressBar->finish(); $this->ioStyle->error('Update cannot be performed.'); return self::ERR_UPDATE_FAILED; } /** * Initialise the command. * * @return void * * @since 4.0.0 */ protected function configure(): void { $help = "<info>%command.name%</info> is used to update Joomla \nUsage: <info>php %command.full_name%</info>"; $this->setDescription('Update Joomla'); $this->setHelp($help); } /** * Update Core Joomla * * @param mixed $updatemodel Update Model * * @return boolean success * * @since 4.0.0 */ private function updateJoomlaCore($updatemodel): bool { $updateInformation = $this->updateInfo; if (!empty($updateInformation['hasUpdate'])) { $this->progressBar->advance(); $this->progressBar->setMessage("Processing update package ..."); $package = $this->processUpdatePackage($updateInformation); $this->progressBar->advance(); $this->progressBar->setMessage("Finalizing update ..."); $result = $updatemodel->finaliseUpgrade(); if ($result) { $this->progressBar->advance(); $this->progressBar->setMessage("Cleaning up ..."); // Remove the administrator/cache/autoload_psr4.php file $autoloadFile = JPATH_CACHE . '/autoload_psr4.php'; if (file_exists($autoloadFile)) { File::delete($autoloadFile); } // Remove the xml if (file_exists(JPATH_BASE . '/joomla.xml')) { File::delete(JPATH_BASE . '/joomla.xml'); } InstallerHelper::cleanupInstall($package['file'], $package['extractdir']); $updatemodel->purge(); return true; } } return false; } /** * Sets the update Information * * @param array $data Stores the update information * * @since 4.0.0 * * @return void */ public function setUpdateInfo($data): void { $this->updateInfo = $data; } /** * Retrieves the Update model from com_joomlaupdate * * @return mixed * * @since 4.0.0 * * @throws \Exception */ public function getUpdateModel() { if (!isset($this->updateModel)) { $this->setUpdateModel(); } return $this->updateModel; } /** * Sets the Update Model * * @return void * * @since 4.0.0 */ public function setUpdateModel(): void { $app = $this->getApplication(); $updatemodel = $app->bootComponent('com_joomlaupdate')->getMVCFactory($app)->createModel('Update', 'Administrator'); if (is_bool($updatemodel)) { $this->updateModel = $updatemodel; return; } $updatemodel->purge(); $updatemodel->refreshUpdates(true); $this->updateModel = $updatemodel; } /** * Downloads and extracts the update Package * * @param array $updateInformation Stores the update information * * @return array | boolean * * @since 4.0.0 */ public function processUpdatePackage($updateInformation) { if (!$updateInformation['object']) { return false; } $this->progressBar->advance(); $this->progressBar->setMessage("Downloading update package ..."); $file = $this->downloadFile($updateInformation['object']->downloadurl->_data); $tmpPath = $this->getApplication()->get('tmp_path'); $updatePackage = $tmpPath . '/' . $file; $this->progressBar->advance(); $this->progressBar->setMessage("Extracting update package ..."); $package = $this->extractFile($updatePackage); $this->progressBar->advance(); $this->progressBar->setMessage("Copying files ..."); $this->copyFileTo($package['extractdir'], JPATH_BASE); return ['file' => $updatePackage, 'extractdir' => $package['extractdir']]; } /** * Downloads the Update file * * @param string $url URL to update file * * @return boolean | string * * @since 4.0.0 */ public function downloadFile($url) { $file = InstallerHelper::downloadPackage($url); if (!$file) { return false; } return $file; } /** * Extracts Update file * * @param string $file Full path to file location * * @return array | boolean * * @since 4.0.0 */ public function extractFile($file) { $package = InstallerHelper::unpack($file, true); return $package; } /** * Copy a file to a destination directory * * @param string $file Full path to file * @param string $dir Destination directory * * @return void * * @since 4.0.0 */ public function copyFileTo($file, $dir): void { Folder::copy($file, $dir, '', true); } /** * Check Database Table Structure * * @return integer the number of errors * * @since 4.4.0 */ public function checkSchema(): int { $app = $this->getApplication(); $app->getLanguage()->load('com_installer', JPATH_ADMINISTRATOR); $coreExtensionInfo = ExtensionHelper::getExtensionRecord('joomla', 'file'); $dbmodel = $app->bootComponent('com_installer')->getMVCFactory($app)->createModel('Database', 'Administrator'); // Ensure we only get information for core $dbmodel->setState('filter.extension_id', $coreExtensionInfo->extension_id); // We're filtering by a single extension which must always exist - so can safely access this through element 0 of the array $changeInformation = $dbmodel->getItems()[0]; foreach ($changeInformation['errorsMessage'] as $msg) { $this->ioStyle->info($msg); } return $changeInformation['errorsCount']; } } RemoveUserFromGroupCommand.php 0000644 00000021262 15172704325 0012522 0 ustar 00 <?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Console; use Joomla\CMS\Access\Access; use Joomla\CMS\User\User; use Joomla\CMS\User\UserHelper; use Joomla\Console\Command\AbstractCommand; use Joomla\Database\DatabaseAwareTrait; use Joomla\Database\DatabaseInterface; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Exception\InvalidOptionException; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Question\ChoiceQuestion; use Symfony\Component\Console\Style\SymfonyStyle; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Console command to remove a user from a group * * @since 4.0.0 */ class RemoveUserFromGroupCommand extends AbstractCommand { use DatabaseAwareTrait; /** * The default command name * * @var string * @since 4.0.0 */ protected static $defaultName = 'user:removefromgroup'; /** * SymfonyStyle Object * @var object * @since 4.0.0 */ private $ioStyle; /** * Stores the Input Object * @var object * @since 4.0.0 */ private $cliInput; /** * The username * * @var string * * @since 4.0.0 */ private $username; /** * The usergroups * * @var array * * @since 4.0.0 */ private $userGroups = []; /** * Command constructor. * * @param DatabaseInterface $db The database * * @since 4.2.0 */ public function __construct(DatabaseInterface $db) { parent::__construct(); $this->setDatabase($db); } /** * Internal function to execute the command. * * @param InputInterface $input The input to inject into the command. * @param OutputInterface $output The output to inject into the command. * * @return integer The command exit code * * @since 4.0.0 */ protected function doExecute(InputInterface $input, OutputInterface $output): int { $this->configureIO($input, $output); $this->ioStyle->title('Remove User From Group'); $this->username = $this->getStringFromOption('username', 'Please enter a username'); $userId = UserHelper::getUserId($this->username); if (empty($userId)) { $this->ioStyle->error("The user " . $this->username . " does not exist!"); return 1; } $user = User::getInstance($userId); $this->userGroups = $this->getGroups($user); $db = $this->getDatabase(); $query = $db->getQuery(true) ->select($db->quoteName('title')) ->from($db->quoteName('#__usergroups')) ->where($db->quoteName('id') . ' = :userGroup'); foreach ($this->userGroups as $userGroup) { $query->bind(':userGroup', $userGroup); $db->setQuery($query); $result = $db->loadResult(); if (Access::checkGroup($userGroup, 'core.admin')) { $queryUser = $db->getQuery(true); $queryUser->select('COUNT(*)') ->from($db->quoteName('#__users', 'u')) ->leftJoin( $db->quoteName('#__user_usergroup_map', 'g'), '(' . $db->quoteName('u.id') . ' = ' . $db->quoteName('g.user_id') . ')' ) ->where($db->quoteName('g.group_id') . " = :groupId") ->where($db->quoteName('u.block') . " = 0") ->bind(':groupId', $userGroup); $db->setQuery($queryUser); $activeSuperUser = $db->loadResult(); if ($activeSuperUser < 2) { $this->ioStyle->error("Can't remove user '" . $user->username . "' from group '" . $result . "'! " . $result . " needs at least one active user!"); return Command::FAILURE; } } if (\count(Access::getGroupsByUser($user->id, false)) < 2) { $this->ioStyle->error("Can't remove '" . $user->username . "' from group '" . $result . "'! Every user needs to be a member of at least one group"); return Command::FAILURE; } if (!UserHelper::removeUserFromGroup($user->id, $userGroup)) { $this->ioStyle->error("Can't remove '" . $user->username . "' from group '" . $result . "'!"); return Command::FAILURE; } $this->ioStyle->success("Removed '" . $user->username . "' from group '" . $result . "'!"); } return Command::SUCCESS; } /** * Method to get a value from option * * @param object $user user object * * @return array * * @since 4.0.0 */ protected function getGroups($user): array { $option = $this->getApplication()->getConsoleInput()->getOption('group'); $db = $this->getDatabase(); $userGroups = Access::getGroupsByUser($user->id, false); if (!$option) { $query = $db->getQuery(true) ->select($db->quoteName('title')) ->from($db->quoteName('#__usergroups')) ->whereIn($db->quoteName('id'), $userGroups); $db->setQuery($query); $result = $db->loadColumn(); $choice = new ChoiceQuestion( 'Please select a usergroup (separate multiple groups with a comma)', $result ); $choice->setMultiselect(true); $answer = (array) $this->ioStyle->askQuestion($choice); $groupList = []; foreach ($answer as $group) { $groupList[] = $this->getGroupId($group); } return $groupList; } $groupList = []; $option = explode(',', $option); foreach ($option as $group) { $groupId = $this->getGroupId($group); if (empty($groupId)) { $this->ioStyle->error("Invalid group name '" . $group . "'"); throw new InvalidOptionException("Invalid group name " . $group); } $groupList[] = $this->getGroupId($group); } return $groupList; } /** * Method to get groupId by groupName * * @param string $groupName name of group * * @return integer * * @since 4.0.0 */ protected function getGroupId($groupName) { $db = $this->getDatabase(); $query = $db->getQuery(true) ->select($db->quoteName('id')) ->from($db->quoteName('#__usergroups')) ->where($db->quoteName('title') . '= :groupName') ->bind(':groupName', $groupName); $db->setQuery($query); return $db->loadResult(); } /** * Method to get a value from option * * @param string $option set the option name * * @param string $question set the question if user enters no value to option * * @return string * * @since 4.0.0 */ protected function getStringFromOption($option, $question): string { $answer = (string) $this->getApplication()->getConsoleInput()->getOption($option); while (!$answer) { $answer = (string) $this->ioStyle->ask($question); } return $answer; } /** * Configure the IO. * * @param InputInterface $input The input to inject into the command. * @param OutputInterface $output The output to inject into the command. * * @return void * * @since 4.0.0 */ private function configureIO(InputInterface $input, OutputInterface $output) { $this->cliInput = $input; $this->ioStyle = new SymfonyStyle($input, $output); } /** * Configure the command. * * @return void * * @since 4.0.0 */ protected function configure(): void { $help = "<info>%command.name%</info> removes a user from a group \nUsage: <info>php %command.full_name%</info>"; $this->setDescription('Remove a user from a group'); $this->addOption('username', null, InputOption::VALUE_OPTIONAL, 'username'); $this->addOption('group', null, InputOption::VALUE_OPTIONAL, 'group'); $this->setHelp($help); } } DeleteUserCommand.php 0000644 00000013353 15172704325 0010630 0 ustar 00 <?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Console; use Joomla\CMS\Access\Access; use Joomla\CMS\User\User; use Joomla\CMS\User\UserHelper; use Joomla\Console\Command\AbstractCommand; use Joomla\Database\DatabaseAwareTrait; use Joomla\Database\DatabaseInterface; use Joomla\Database\ParameterType; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Console command for deleting a user * * @since 4.0.0 */ class DeleteUserCommand extends AbstractCommand { use DatabaseAwareTrait; /** * The default command name * * @var string * @since 4.0.0 */ protected static $defaultName = 'user:delete'; /** * SymfonyStyle Object * @var object * @since 4.0.0 */ private $ioStyle; /** * Stores the Input Object * @var object * @since 4.0.0 */ private $cliInput; /** * The username * * @var string * * @since 4.0.0 */ private $username; /** * Command constructor. * * @param DatabaseInterface $db The database * * @since 4.2.0 */ public function __construct(DatabaseInterface $db) { parent::__construct(); $this->setDatabase($db); } /** * Internal function to execute the command. * * @param InputInterface $input The input to inject into the command. * @param OutputInterface $output The output to inject into the command. * * @return integer The command exit code * * @since 4.0.0 */ protected function doExecute(InputInterface $input, OutputInterface $output): int { $this->configureIO($input, $output); $this->ioStyle->title('Delete User'); $this->username = $this->getStringFromOption('username', 'Please enter a username'); $userId = UserHelper::getUserId($this->username); $db = $this->getDatabase(); if (empty($userId)) { $this->ioStyle->error($this->username . ' does not exist!'); return Command::FAILURE; } if ($input->isInteractive() && !$this->ioStyle->confirm('Are you sure you want to delete this user?', false)) { $this->ioStyle->note('User not deleted'); return Command::SUCCESS; } $groups = UserHelper::getUserGroups($userId); $user = User::getInstance($userId); if ($user->block == 0) { foreach ($groups as $groupId) { if (Access::checkGroup($groupId, 'core.admin')) { $queryUser = $db->getQuery(true); $queryUser->select('COUNT(*)') ->from($db->quoteName('#__users', 'u')) ->leftJoin( $db->quoteName('#__user_usergroup_map', 'g'), '(' . $db->quoteName('u.id') . ' = ' . $db->quoteName('g.user_id') . ')' ) ->where($db->quoteName('g.group_id') . " = :groupId") ->where($db->quoteName('u.block') . " = 0") ->bind(':groupId', $groupId, ParameterType::INTEGER); $db->setQuery($queryUser); $activeSuperUser = $db->loadResult(); if ($activeSuperUser < 2) { $this->ioStyle->error("You can't delete the last active Super User"); return Command::FAILURE; } } } } // Trigger delete of user $result = $user->delete(); if (!$result) { $this->ioStyle->error("Can't remove " . $this->username . ' from usertable'); return Command::FAILURE; } $this->ioStyle->success('User ' . $this->username . ' deleted!'); return Command::SUCCESS; } /** * Method to get a value from option * * @param string $option set the option name * * @param string $question set the question if user enters no value to option * * @return string * * @since 4.0.0 */ protected function getStringFromOption($option, $question): string { $answer = (string) $this->getApplication()->getConsoleInput()->getOption($option); while (!$answer) { $answer = (string) $this->ioStyle->ask($question); } return $answer; } /** * Configure the IO. * * @param InputInterface $input The input to inject into the command. * @param OutputInterface $output The output to inject into the command. * * @return void * * @since 4.0.0 */ private function configureIO(InputInterface $input, OutputInterface $output) { $this->cliInput = $input; $this->ioStyle = new SymfonyStyle($input, $output); } /** * Configure the command. * * @return void * * @since 4.0.0 */ protected function configure(): void { $help = "<info>%command.name%</info> deletes a user \nUsage: <info>php %command.full_name%</info>"; $this->setDescription('Delete a user'); $this->addOption('username', null, InputOption::VALUE_OPTIONAL, 'username'); $this->setHelp($help); } } TasksRunCommand.php 0000644 00000012123 15172704325 0010333 0 ustar 00 <?php /** * Joomla! Content Management System. * * @copyright (C) 2021 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Console; use Joomla\Component\Scheduler\Administrator\Scheduler\Scheduler; use Joomla\Component\Scheduler\Administrator\Task\Status; use Joomla\Console\Command\AbstractCommand; use Symfony\Component\Console\Exception\InvalidArgumentException; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Console command to run scheduled tasks. * * @since 4.1.0 */ class TasksRunCommand extends AbstractCommand { /** * The default command name * * @var string * @since 4.1.0 */ protected static $defaultName = 'scheduler:run'; /** * @var SymfonyStyle * @since 4.1.0 */ private $ioStyle; /** * @param InputInterface $input The input to inject into the command. * @param OutputInterface $output The output to inject into the command. * * @return integer The command exit code. * * @since 4.1.0 * @throws \RunTimeException * @throws InvalidArgumentException */ protected function doExecute(InputInterface $input, OutputInterface $output): int { /** * Not as a class constant because of some the autoload order doesn't let us * load the namespace when it's time to do that (why?) */ static $outTextMap = [ Status::OK => 'Task#%1$02d \'%2$s\' processed in %3$.2f seconds.', Status::WILL_RESUME => '<notice>Task#%1$02d \'%2$s\' ran for %3$.2f seconds, will resume next time.</notice>', Status::NO_RUN => '<warning>Task#%1$02d \'%2$s\' failed to run. Is it already running?</warning>', Status::NO_ROUTINE => '<error>Task#%1$02d \'%2$s\' is orphaned! Visit the backend to resolve.</error>', 'N/A' => '<error>Task#%1$02d \'%2$s\' exited with code %4$d in %3$.2f seconds.</error>', ]; $this->configureIo($input, $output); $this->ioStyle->title('Run Tasks'); $scheduler = new Scheduler(); $id = $input->getOption('id'); $all = $input->getOption('all'); if ($id) { $records[] = $scheduler->fetchTaskRecord($id); } else { $filters = $scheduler::TASK_QUEUE_FILTERS; $listConfig = $scheduler::TASK_QUEUE_LIST_CONFIG; $listConfig['limit'] = ($all ? null : 1); $records = $scheduler->fetchTaskRecords($filters, $listConfig); } if ($id && !$records[0]) { $this->ioStyle->writeln('<error>No matching task found!</error>'); return Status::NO_TASK; } elseif (!$records) { $this->ioStyle->writeln('<error>No tasks due!</error>'); return Status::NO_TASK; } $status = ['startTime' => microtime(true)]; $taskCount = \count($records); $exit = Status::OK; foreach ($records as $record) { $cStart = microtime(true); $task = $scheduler->runTask(['id' => $record->id, 'allowDisabled' => true, 'allowConcurrent' => true]); $exit = empty($task) ? Status::NO_RUN : $task->getContent()['status']; $duration = microtime(true) - $cStart; $key = (\array_key_exists($exit, $outTextMap)) ? $exit : 'N/A'; $this->ioStyle->writeln(sprintf($outTextMap[$key], $record->id, $record->title, $duration, $exit)); } $netTime = round(microtime(true) - $status['startTime'], 2); $this->ioStyle->newLine(); $this->ioStyle->writeln("<info>Finished running $taskCount tasks in $netTime seconds.</info>"); return $taskCount === 1 ? $exit : Status::OK; } /** * Configure the IO. * * @param InputInterface $input The input to inject into the command. * @param OutputInterface $output The output to inject into the command. * * @return void * * @since 4.1.0 */ private function configureIO(InputInterface $input, OutputInterface $output) { $this->ioStyle = new SymfonyStyle($input, $output); } /** * Configure the command. * * @return void * * @since 4.1.0 */ protected function configure(): void { $this->addOption('id', 'i', InputOption::VALUE_REQUIRED, 'The id of the task to run.'); $this->addOption('all', '', InputOption::VALUE_NONE, 'Run all due tasks. Note that this is overridden if --id is used.'); $help = "<info>%command.name%</info> run scheduled tasks. \nUsage: <info>php %command.full_name% [flags]</info>"; $this->setDescription('Run one or more scheduled tasks'); $this->setHelp($help); } } AddUserToGroupCommand.php 0000644 00000017757 15172704325 0011452 0 ustar 00 <?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Console; use Joomla\CMS\Access\Access; use Joomla\CMS\User\User; use Joomla\CMS\User\UserHelper; use Joomla\Console\Command\AbstractCommand; use Joomla\Database\DatabaseAwareTrait; use Joomla\Database\DatabaseInterface; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Exception\InvalidOptionException; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Question\ChoiceQuestion; use Symfony\Component\Console\Style\SymfonyStyle; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Console command to add a user to group * * @since 4.0.0 */ class AddUserToGroupCommand extends AbstractCommand { use DatabaseAwareTrait; /** * The default command name * * @var string * @since 4.0.0 */ protected static $defaultName = 'user:addtogroup'; /** * SymfonyStyle Object * @var object * @since 4.0.0 */ private $ioStyle; /** * Stores the Input Object * @var object * @since 4.0.0 */ private $cliInput; /** * The username * * @var string * * @since 4.0.0 */ private $username; /** * The usergroups * * @var array * * @since 4.0.0 */ private $userGroups = []; /** * Command constructor. * * @param DatabaseInterface $db The database * * @since 4.2.0 */ public function __construct(DatabaseInterface $db) { parent::__construct(); $this->setDatabase($db); } /** * Internal function to execute the command. * * @param InputInterface $input The input to inject into the command. * @param OutputInterface $output The output to inject into the command. * * @return integer The command exit code * * @since 4.0.0 */ protected function doExecute(InputInterface $input, OutputInterface $output): int { $this->configureIO($input, $output); $this->ioStyle->title('Add User To Group'); $this->username = $this->getStringFromOption('username', 'Please enter a username'); $userId = $this->getUserId($this->username); if (empty($userId)) { $this->ioStyle->error("The user " . $this->username . " does not exist!"); return Command::FAILURE; } // Fetch user $user = User::getInstance($userId); $this->userGroups = $this->getGroups($user); $db = $this->getDatabase(); $query = $db->getQuery(true) ->select($db->quoteName('title')) ->from($db->quoteName('#__usergroups')) ->where($db->quoteName('id') . ' = :userGroup'); foreach ($this->userGroups as $userGroup) { $query->bind(':userGroup', $userGroup); $db->setQuery($query); $result = $db->loadResult(); if (UserHelper::addUserToGroup($user->id, $userGroup)) { $this->ioStyle->success("Added '" . $user->username . "' to group '" . $result . "'!"); } else { $this->ioStyle->error("Can't add '" . $user->username . "' to group '" . $result . "'!"); return Command::FAILURE; } } return Command::SUCCESS; } /** * Method to get a value from option * * @param User $user a UserInstance * * @return array * * @since 4.0.0 */ protected function getGroups($user): array { $groups = $this->getApplication()->getConsoleInput()->getOption('group'); $db = $this->getDatabase(); $groupList = []; // Group names have been supplied as input arguments if ($groups) { $groups = explode(',', $groups); foreach ($groups as $group) { $groupId = $this->getGroupId($group); if (empty($groupId)) { $this->ioStyle->error("Invalid group name '" . $group . "'"); throw new InvalidOptionException("Invalid group name " . $group); } $groupList[] = $this->getGroupId($group); } return $groupList; } $userGroups = Access::getGroupsByUser($user->id, false); // Generate select list for user $query = $db->getQuery(true) ->select($db->quoteName('title')) ->from($db->quoteName('#__usergroups')) ->whereNotIn($db->quoteName('id'), $userGroups) ->order($db->quoteName('id') . ' ASC'); $db->setQuery($query); $list = $db->loadColumn(); $choice = new ChoiceQuestion( 'Please select a usergroup (separate multiple groups with a comma)', $list ); $choice->setMultiselect(true); $answer = (array) $this->ioStyle->askQuestion($choice); foreach ($answer as $group) { $groupList[] = $this->getGroupId($group); } return $groupList; } /** * Method to get groupId by groupName * * @param string $groupName name of group * * @return integer * * @since 4.0.0 */ protected function getGroupId($groupName) { $db = $this->getDatabase(); $query = $db->getQuery(true) ->select($db->quoteName('id')) ->from($db->quoteName('#__usergroups')) ->where($db->quoteName('title') . '= :groupName') ->bind(':groupName', $groupName); $db->setQuery($query); return $db->loadResult(); } /** * Method to get a user object * * @param string $username username * * @return object * * @since 4.0.0 */ protected function getUserId($username) { $db = $this->getDatabase(); $query = $db->getQuery(true) ->select($db->quoteName('id')) ->from($db->quoteName('#__users')) ->where($db->quoteName('username') . '= :username') ->bind(':username', $username); $db->setQuery($query); return $db->loadResult(); } /** * Method to get a value from option * * @param string $option set the option name * * @param string $question set the question if user enters no value to option * * @return string * * @since 4.0.0 */ protected function getStringFromOption($option, $question): string { $answer = (string) $this->getApplication()->getConsoleInput()->getOption($option); while (!$answer) { $answer = (string) $this->ioStyle->ask($question); } return $answer; } /** * Configure the IO. * * @param InputInterface $input The input to inject into the command. * @param OutputInterface $output The output to inject into the command. * * @return void * * @since 4.0.0 */ private function configureIO(InputInterface $input, OutputInterface $output) { $this->cliInput = $input; $this->ioStyle = new SymfonyStyle($input, $output); } /** * Configure the command. * * @return void * * @since 4.0.0 */ protected function configure(): void { $help = "<info>%command.name%</info> adds a user to a group \nUsage: <info>php %command.full_name%</info>"; $this->setDescription('Add a user to a group'); $this->addOption('username', null, InputOption::VALUE_OPTIONAL, 'username'); $this->addOption('group', null, InputOption::VALUE_OPTIONAL, 'group'); $this->setHelp($help); } } ChangeUserPasswordCommand.php 0000644 00000010515 15172704325 0012333 0 ustar 00 <?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Console; use Joomla\CMS\User\User; use Joomla\CMS\User\UserHelper; use Joomla\Console\Command\AbstractCommand; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Console command to change a user's password * * @since 4.0.0 */ class ChangeUserPasswordCommand extends AbstractCommand { /** * The default command name * * @var string * @since 4.0.0 */ protected static $defaultName = 'user:reset-password'; /** * SymfonyStyle Object * @var object * @since 4.0.0 */ private $ioStyle; /** * Stores the Input Object * @var object * @since 4.0.0 */ private $cliInput; /** * The username * * @var string * * @since 4.0.0 */ private $username; /** * The password * * @var string * * @since 4.0.0 */ private $password; /** * Internal function to execute the command. * * @param InputInterface $input The input to inject into the command. * @param OutputInterface $output The output to inject into the command. * * @return integer The command exit code * * @since 4.0.0 */ protected function doExecute(InputInterface $input, OutputInterface $output): int { $this->configureIO($input, $output); $this->ioStyle->title('Change Password'); $this->username = $this->getStringFromOption('username', 'Please enter a username'); $userId = UserHelper::getUserId($this->username); if (empty($userId)) { $this->ioStyle->error("The user " . $this->username . " does not exist!"); return Command::FAILURE; } $user = User::getInstance($userId); $this->password = $this->getStringFromOption('password', 'Please enter a new password'); $user->password = UserHelper::hashPassword($this->password); if (!$user->save(true)) { $this->ioStyle->error($user->getError()); return Command::FAILURE; } $this->ioStyle->success("Password changed!"); return Command::SUCCESS; } /** * Method to get a value from option * * @param string $option set the option name * * @param string $question set the question if user enters no value to option * * @return string * * @since 4.0.0 */ protected function getStringFromOption($option, $question): string { $answer = (string) $this->cliInput->getOption($option); while (!$answer) { if ($option === 'password') { $answer = (string) $this->ioStyle->askHidden($question); } else { $answer = (string) $this->ioStyle->ask($question); } } return $answer; } /** * Configure the IO. * * @param InputInterface $input The input to inject into the command. * @param OutputInterface $output The output to inject into the command. * * @return void * * @since 4.0.0 */ private function configureIO(InputInterface $input, OutputInterface $output) { $this->cliInput = $input; $this->ioStyle = new SymfonyStyle($input, $output); } /** * Configure the command. * * @return void * * @since 4.0.0 */ protected function configure(): void { $help = "<info>%command.name%</info> will change a user's password \nUsage: <info>php %command.full_name%</info>"; $this->addOption('username', null, InputOption::VALUE_OPTIONAL, 'username'); $this->addOption('password', null, InputOption::VALUE_OPTIONAL, 'password'); $this->setDescription("Change a user's password"); $this->setHelp($help); } } AddUserCommand.php 0000644 00000020772 15172704325 0010121 0 ustar 00 <?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Console; use Joomla\CMS\User\User; use Joomla\Console\Command\AbstractCommand; use Joomla\Database\DatabaseAwareTrait; use Joomla\Database\DatabaseInterface; use Joomla\Filter\InputFilter; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Exception\InvalidOptionException; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Question\ChoiceQuestion; use Symfony\Component\Console\Style\SymfonyStyle; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Console command for adding a user * * @since 4.0.0 */ class AddUserCommand extends AbstractCommand { use DatabaseAwareTrait; /** * The default command name * * @var string * @since 4.0.0 */ protected static $defaultName = 'user:add'; /** * SymfonyStyle Object * @var object * @since 4.0.0 */ private $ioStyle; /** * Stores the Input Object * @var object * @since 4.0.0 */ private $cliInput; /** * The username * * @var string * * @since 4.0.0 */ private $user; /** * The password * * @var string * * @since 4.0.0 */ private $password; /** * The name * * @var string * * @since 4.0.0 */ private $name; /** * The email address * * @var string * * @since 4.0.0 */ private $email; /** * The usergroups * * @var array * * @since 4.0.0 */ private $userGroups = []; /** * Command constructor. * * @param DatabaseInterface $db The database * * @since 4.2.0 */ public function __construct(DatabaseInterface $db) { parent::__construct(); $this->setDatabase($db); } /** * Internal function to execute the command. * * @param InputInterface $input The input to inject into the command. * @param OutputInterface $output The output to inject into the command. * * @return integer The command exit code * * @since 4.0.0 */ protected function doExecute(InputInterface $input, OutputInterface $output): int { $this->configureIO($input, $output); $this->ioStyle->title('Add User'); $this->user = $this->getStringFromOption('username', 'Please enter a username'); $this->name = $this->getStringFromOption('name', 'Please enter a name (full name of user)'); $this->email = $this->getStringFromOption('email', 'Please enter an email address'); $this->password = $this->getStringFromOption('password', 'Please enter a password'); $this->userGroups = $this->getUserGroups(); if (\in_array("error", $this->userGroups)) { $this->ioStyle->error("'" . $this->userGroups[1] . "' user group doesn't exist!"); return Command::FAILURE; } // Get filter to remove invalid characters $filter = new InputFilter(); $user = [ 'username' => $filter->clean($this->user, 'USERNAME'), 'password' => $this->password, 'name' => $filter->clean($this->name, 'STRING'), 'email' => $this->email, 'groups' => $this->userGroups, ]; $userObj = User::getInstance(); $userObj->bind($user); if (!$userObj->save()) { switch ($userObj->getError()) { case "JLIB_DATABASE_ERROR_USERNAME_INUSE": $this->ioStyle->error("The username already exists!"); break; case "JLIB_DATABASE_ERROR_EMAIL_INUSE": $this->ioStyle->error("The email address already exists!"); break; case "JLIB_DATABASE_ERROR_VALID_MAIL": $this->ioStyle->error("The email address is invalid!"); break; } return 1; } $this->ioStyle->success("User created!"); return Command::SUCCESS; } /** * Method to get groupId by groupName * * @param string $groupName name of group * * @return integer * * @since 4.0.0 */ protected function getGroupId($groupName) { $db = $this->getDatabase(); $query = $db->getQuery(true) ->select($db->quoteName('id')) ->from($db->quoteName('#__usergroups')) ->where($db->quoteName('title') . ' = :groupName') ->bind(':groupName', $groupName); $db->setQuery($query); return $db->loadResult(); } /** * Method to get a value from option * * @param string $option set the option name * @param string $question set the question if user enters no value to option * * @return string * * @since 4.0.0 */ public function getStringFromOption($option, $question): string { $answer = (string) $this->cliInput->getOption($option); while (!$answer) { if ($option === 'password') { $answer = (string) $this->ioStyle->askHidden($question); } else { $answer = (string) $this->ioStyle->ask($question); } } return $answer; } /** * Method to get a value from option * * @return array * * @since 4.0.0 */ protected function getUserGroups(): array { $groups = $this->getApplication()->getConsoleInput()->getOption('usergroup'); $db = $this->getDatabase(); $groupList = []; // Group names have been supplied as input arguments if (!\is_null($groups) && $groups[0]) { $groups = explode(',', $groups); foreach ($groups as $group) { $groupId = $this->getGroupId($group); if (empty($groupId)) { $this->ioStyle->error("Invalid group name '" . $group . "'"); throw new InvalidOptionException("Invalid group name " . $group); } $groupList[] = $this->getGroupId($group); } return $groupList; } // Generate select list for user $query = $db->getQuery(true) ->select($db->quoteName('title')) ->from($db->quoteName('#__usergroups')) ->order($db->quoteName('id') . 'ASC'); $db->setQuery($query); $list = $db->loadColumn(); $choice = new ChoiceQuestion( 'Please select a usergroup (separate multiple groups with a comma)', $list ); $choice->setMultiselect(true); $answer = (array) $this->ioStyle->askQuestion($choice); foreach ($answer as $group) { $groupList[] = $this->getGroupId($group); } return $groupList; } /** * Configure the IO. * * @param InputInterface $input The input to inject into the command. * @param OutputInterface $output The output to inject into the command. * * @return void * * @since 4.0.0 */ private function configureIO(InputInterface $input, OutputInterface $output) { $this->cliInput = $input; $this->ioStyle = new SymfonyStyle($input, $output); } /** * Configure the command. * * @return void * * @since 4.0.0 */ protected function configure(): void { $help = "<info>%command.name%</info> will add a user \nUsage: <info>php %command.full_name%</info>"; $this->addOption('username', null, InputOption::VALUE_OPTIONAL, 'username'); $this->addOption('name', null, InputOption::VALUE_OPTIONAL, 'full name of user'); $this->addOption('password', null, InputOption::VALUE_OPTIONAL, 'password'); $this->addOption('email', null, InputOption::VALUE_OPTIONAL, 'email address'); $this->addOption('usergroup', null, InputOption::VALUE_OPTIONAL, 'usergroup (separate multiple groups with comma ",")'); $this->setDescription('Add a user'); $this->setHelp($help); } } ListUserCommand.php 0000644 00000007561 15172704325 0010345 0 ustar 00 <?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Console; use Joomla\Console\Command\AbstractCommand; use Joomla\Database\DatabaseAwareTrait; use Joomla\Database\DatabaseInterface; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Console command to list existing users * * @since 4.0.0 */ class ListUserCommand extends AbstractCommand { use DatabaseAwareTrait; /** * The default command name * * @var string * @since 4.0.0 */ protected static $defaultName = 'user:list'; /** * SymfonyStyle Object * @var object * @since 4.0.0 */ private $ioStyle; /** * Command constructor. * * @param DatabaseInterface $db The database * * @since 4.2.0 */ public function __construct(DatabaseInterface $db) { parent::__construct(); $this->setDatabase($db); } /** * Internal function to execute the command. * * @param InputInterface $input The input to inject into the command. * @param OutputInterface $output The output to inject into the command. * * @return integer The command exit code * * @since 4.0.0 */ protected function doExecute(InputInterface $input, OutputInterface $output): int { $db = $this->getDatabase(); $this->configureIO($input, $output); $this->ioStyle->title('List Users'); $groupsQuery = $db->getQuery(true) ->select($db->quoteName(['title', 'id'])) ->from($db->quoteName('#__usergroups')); $groups = $db->setQuery($groupsQuery)->loadAssocList('id', 'title'); $query = $db->getQuery(true); $query->select($db->quoteName(['u.id', 'u.username', 'u.name', 'u.email', 'u.block'])) ->select($query->groupConcat($query->castAs('CHAR', $db->quoteName('g.group_id'))) . ' AS ' . $db->quoteName('groups')) ->innerJoin($db->quoteName('#__user_usergroup_map', 'g'), $db->quoteName('g.user_id') . ' = ' . $db->quoteName('u.id')) ->from($db->quoteName('#__users', 'u')) ->group($db->quoteName('u.id')); $db->setQuery($query); $users = []; foreach ($db->loadAssocList() as $user) { $user["groups"] = array_map( function ($groupId) use ($groups) { return $groups[$groupId]; }, explode(",", $user["groups"]) ); $user["groups"] = implode(", ", $user["groups"]); $users[] = $user; } $this->ioStyle->table(['ID', 'Username', 'Name', 'Email', 'Blocked', 'Groups'], $users); return Command::SUCCESS; } /** * Configure the IO. * * @param InputInterface $input The input to inject into the command. * @param OutputInterface $output The output to inject into the command. * * @return void * * @since 4.0.0 */ private function configureIO(InputInterface $input, OutputInterface $output) { $this->ioStyle = new SymfonyStyle($input, $output); } /** * Configure the command. * * @return void * * @since 4.0.0 */ protected function configure(): void { $help = "<info>%command.name%</info> will list all users \nUsage: <info>php %command.full_name%</info>"; $this->setDescription('List all users'); $this->setHelp($help); } } TasksListCommand.php 0000644 00000010133 15172704325 0010501 0 ustar 00 <?php /** * Joomla! Content Management System. * * @copyright (C) 2021 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Console; use Joomla\CMS\Factory; use Joomla\Component\Scheduler\Administrator\Scheduler\Scheduler; use Joomla\Console\Application; use Joomla\Console\Command\AbstractCommand; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Console command to list scheduled tasks. * * @since 4.1.0 */ class TasksListCommand extends AbstractCommand { /** * The default command name * * @var string * @since 4.1.0 */ protected static $defaultName = 'scheduler:list'; /** * The console application object * * @var Application * @since 4.1.0 */ protected $application; /** * @var SymfonyStyle * @since 4.1.0 */ private $ioStyle; /** * Internal function to execute the command. * * @param InputInterface $input The input to inject into the command. * @param OutputInterface $output The output to inject into the command. * * @return integer The command exit code * * @since 4.1.0 * @throws \Exception */ protected function doExecute(InputInterface $input, OutputInterface $output): int { Factory::getApplication()->getLanguage()->load('joomla', JPATH_ADMINISTRATOR); $this->configureIO($input, $output); $this->ioStyle->title('List Scheduled Tasks'); $tasks = array_map( function (\stdClass $task): array { $enabled = $task->state === 1; $rule = json_decode($task->execution_rules); if ($rule->{'rule-type'} === 'manual') { $nextRun = 'Manual'; } else { $nextExec = Factory::getDate($task->next_execution, 'UTC'); $due = $enabled && $task->taskOption && Factory::getDate('now', 'UTC') > $nextExec; $nextRun = $due ? 'DUE!' : $nextExec->toRFC822(); } return [ 'id' => $task->id, 'title' => $task->title, 'type' => $task->safeTypeTitle, 'state' => $task->state === 1 ? 'Enabled' : ($task->state === 0 ? 'Disabled' : 'Trashed'), 'next_execution' => $nextRun, ]; }, $this->getTasks() ); $this->ioStyle->table(['ID', 'Title', 'Type', 'State', 'Next Run'], $tasks); return 0; } /** * Returns a stdClass object array of scheduled tasks. * * @return array * * @since 4.1.0 * @throws \RunTimeException */ private function getTasks(): array { $scheduler = new Scheduler(); return $scheduler->fetchTaskRecords( ['state' => '*'], ['ordering' => 'a.title', 'select' => 'a.id, a.title, a.type, a.state, a.next_execution, a.execution_rules'] ); } /** * Configure the IO. * * @param InputInterface $input The input to inject into the command. * @param OutputInterface $output The output to inject into the command. * * @return void * * @since 4.1.0 */ private function configureIO(InputInterface $input, OutputInterface $output) { $this->ioStyle = new SymfonyStyle($input, $output); } /** * Configure the command. * * @return void * * @since 4.1.0 */ protected function configure(): void { $help = "<info>%command.name%</info> lists all scheduled tasks. \nUsage: <info>php %command.full_name%</info>"; $this->setDescription('List all scheduled tasks'); $this->setHelp($help); } } ExtensionRemoveCommand.php 0000644 00000013537 15172704325 0011725 0 ustar 00 <?php /** * Joomla! Content Management System * * @copyright (C) 2020 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Console; use Joomla\CMS\Factory; use Joomla\CMS\Installer\Installer; use Joomla\CMS\Language\Text; use Joomla\CMS\Table\Extension; use Joomla\Console\Command\AbstractCommand; use Joomla\Database\DatabaseAwareTrait; use Joomla\Database\DatabaseInterface; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Console command for removing extensions * * @since 4.0.0 */ class ExtensionRemoveCommand extends AbstractCommand { use DatabaseAwareTrait; /** * The default command name * * @var string * @since 4.0.0 */ protected static $defaultName = 'extension:remove'; /** * @var InputInterface * @since 4.0.0 */ private $cliInput; /** * @var SymfonyStyle * @since 4.0.0 */ private $ioStyle; /** * Exit Code for extensions remove abort * @since 4.0.0 */ public const REMOVE_ABORT = 3; /** * Exit Code for extensions remove failure * @since 4.0.0 */ public const REMOVE_FAILED = 1; /** * Exit Code for invalid response * @since 4.0.0 */ public const REMOVE_INVALID_RESPONSE = 5; /** * Exit Code for invalid type * @since 4.0.0 */ public const REMOVE_INVALID_TYPE = 6; /** * Exit Code for extensions locked remove failure * @since 4.0.0 */ public const REMOVE_LOCKED = 4; /** * Exit Code for extensions not found * @since 4.0.0 */ public const REMOVE_NOT_FOUND = 2; /** * Exit Code for extensions remove success * @since 4.0.0 */ public const REMOVE_SUCCESSFUL = 0; /** * Command constructor. * * @param DatabaseInterface $db The database * * @since 4.2.0 */ public function __construct(DatabaseInterface $db) { parent::__construct(); $this->setDatabase($db); } /** * Configures the IO * * @param InputInterface $input Console Input * @param OutputInterface $output Console Output * * @return void * * @since 4.0.0 * */ private function configureIO(InputInterface $input, OutputInterface $output): void { $this->cliInput = $input; $this->ioStyle = new SymfonyStyle($input, $output); $language = Factory::getLanguage(); $language->load('', JPATH_ADMINISTRATOR, null, false, false) || $language->load('', JPATH_ADMINISTRATOR, null, true); $language->load('com_installer', JPATH_ADMINISTRATOR, null, false, false) || $language->load('com_installer', JPATH_ADMINISTRATOR, null, true); } /** * Initialise the command. * * @return void * * @since 4.0.0 */ protected function configure(): void { $this->addArgument( 'extensionId', InputArgument::REQUIRED, 'ID of extension to be removed (run extension:list command to check)' ); $help = "<info>%command.name%</info> is used to uninstall extensions. \nThe command requires one argument, the ID of the extension to uninstall. \nYou may find this ID by running the <info>extension:list</info> command. \nUsage: <info>php %command.full_name% <extension_id></info>"; $this->setDescription('Remove an extension'); $this->setHelp($help); } /** * Internal function to execute the command. * * @param InputInterface $input The input to inject into the command. * @param OutputInterface $output The output to inject into the command. * * @return integer The command exit code * * @since 4.0.0 */ protected function doExecute(InputInterface $input, OutputInterface $output): int { $this->configureIO($input, $output); $this->ioStyle->title('Remove Extension'); $extensionId = $this->cliInput->getArgument('extensionId'); $response = $this->ioStyle->ask('Are you sure you want to remove this extension?', 'yes/no'); if (strtolower($response) === 'yes') { // Get an installer object for the extension type $installer = Installer::getInstance(); $row = new Extension($this->getDatabase()); if ((int) $extensionId === 0 || !$row->load($extensionId)) { $this->ioStyle->error("Extension with ID of $extensionId not found."); return self::REMOVE_NOT_FOUND; } // Do not allow to uninstall locked extensions. if ((int) $row->locked === 1) { $this->ioStyle->error(Text::sprintf('COM_INSTALLER_UNINSTALL_ERROR_LOCKED_EXTENSION', $row->name, $extensionId)); return self::REMOVE_LOCKED; } if ($row->type) { if (!$installer->uninstall($row->type, $extensionId)) { $this->ioStyle->error('Extension not removed.'); return self::REMOVE_FAILED; } $this->ioStyle->success('Extension removed!'); return self::REMOVE_SUCCESSFUL; } return self::REMOVE_INVALID_TYPE; } elseif (strtolower($response) === 'no') { $this->ioStyle->note('Extension not removed.'); return self::REMOVE_ABORT; } $this->ioStyle->warning('Invalid response'); return self::REMOVE_INVALID_RESPONSE; } } ExtensionsListCommand.php 0000644 00000014045 15172704325 0011561 0 ustar 00 <?php /** * Joomla! Content Management System * * @copyright (C) 2020 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Console; use Joomla\Console\Command\AbstractCommand; use Joomla\Database\DatabaseAwareTrait; use Joomla\Database\DatabaseInterface; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Console command for listing installed extensions * * @since 4.0.0 */ class ExtensionsListCommand extends AbstractCommand { use DatabaseAwareTrait; /** * The default command name * * @var string * @since 4.0.0 */ protected static $defaultName = 'extension:list'; /** * Stores the installed Extensions * @var array * @since 4.0.0 */ protected $extensions; /** * Stores the Input Object * @var InputInterface * @since 4.0.0 */ protected $cliInput; /** * SymfonyStyle Object * @var SymfonyStyle * @since 4.0.0 */ protected $ioStyle; /** * Instantiate the command. * * @param DatabaseInterface $db Database connector * * @since 4.0.0 */ public function __construct(DatabaseInterface $db) { parent::__construct(); $this->setDatabase($db); } /** * Configures the IO * * @param InputInterface $input Console Input * @param OutputInterface $output Console Output * * @return void * * @since 4.0.0 * */ protected function configureIO(InputInterface $input, OutputInterface $output): void { $this->cliInput = $input; $this->ioStyle = new SymfonyStyle($input, $output); } /** * Initialise the command. * * @return void * * @since 4.0.0 */ protected function configure(): void { $this->addOption('type', null, InputOption::VALUE_REQUIRED, 'Type of the extension'); $help = "<info>%command.name%</info> lists all installed extensions \nUsage: <info>php %command.full_name% <extension_id></info> \nYou may filter on the type of extension (component, module, plugin, etc.) using the <info>--type</info> option: \n <info>php %command.full_name% --type=<type></info>"; $this->setDescription('List installed extensions'); $this->setHelp($help); } /** * Retrieves all extensions * * @return mixed * * @since 4.0.0 */ public function getExtensions() { if (!$this->extensions) { $this->setExtensions(); } return $this->extensions; } /** * Retrieves the extension from the model and sets the class variable * * @param null $extensions Array of extensions * * @return void * * @since 4.0.0 */ public function setExtensions($extensions = null): void { if (!$extensions) { $this->extensions = $this->getAllExtensionsFromDB(); } else { $this->extensions = $extensions; } } /** * Retrieves extension list from DB * * @return array * * @since 4.0.0 */ private function getAllExtensionsFromDB(): array { $db = $this->getDatabase(); $query = $db->getQuery(true); $query->select('*') ->from('#__extensions'); $db->setQuery($query); $extensions = $db->loadAssocList('extension_id'); return $extensions; } /** * Transforms extension arrays into required form * * @param array $extensions Array of extensions * * @return array * * @since 4.0.0 */ protected function getExtensionsNameAndId($extensions): array { $extInfo = []; foreach ($extensions as $key => $extension) { $manifest = json_decode($extension['manifest_cache']); $extInfo[] = [ $extension['name'], $extension['extension_id'], $manifest ? $manifest->version : '--', $extension['type'], $extension['enabled'] == 1 ? 'Yes' : 'No', ]; } return $extInfo; } /** * Filters the extension type * * @param string $type Extension type * * @return array * * @since 4.0.0 */ private function filterExtensionsBasedOn($type): array { $extensions = []; foreach ($this->extensions as $key => $extension) { if ($extension['type'] == $type) { $extensions[] = $extension; } } return $extensions; } /** * Internal function to execute the command. * * @param InputInterface $input The input to inject into the command. * @param OutputInterface $output The output to inject into the command. * * @return integer The command exit code * * @since 4.0.0 */ protected function doExecute(InputInterface $input, OutputInterface $output): int { $this->configureIO($input, $output); $extensions = $this->getExtensions(); $type = $this->cliInput->getOption('type'); if ($type) { $extensions = $this->filterExtensionsBasedOn($type); } if (empty($extensions)) { $this->ioStyle->error("Cannot find extensions of the type '$type' specified."); return Command::SUCCESS; } $extensions = $this->getExtensionsNameAndId($extensions); $this->ioStyle->title('Installed Extensions'); $this->ioStyle->table(['Name', 'Extension ID', 'Version', 'Type', 'Enabled'], $extensions); return Command::SUCCESS; } } CheckUpdatesCommand.php 0000644 00000004413 15172704325 0011127 0 ustar 00 <?php /** * Joomla! Content Management System * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Console; use Joomla\CMS\Updater\Updater; use Joomla\Console\Command\AbstractCommand; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Console command for checking if there are pending extension updates * * @since 4.0.0 */ class CheckUpdatesCommand extends AbstractCommand { /** * The default command name * * @var string * @since 4.0.0 */ protected static $defaultName = 'update:extensions:check'; /** * Internal function to execute the command. * * @param InputInterface $input The input to inject into the command. * @param OutputInterface $output The output to inject into the command. * * @return integer The command exit code * * @since 4.0.0 */ protected function doExecute(InputInterface $input, OutputInterface $output): int { $symfonyStyle = new SymfonyStyle($input, $output); $symfonyStyle->title('Fetching Extension Updates'); $this->getApplication()->getLanguage()->load('lib_joomla'); // Find all updates $ret = Updater::getInstance()->findUpdates(); if ($ret) { $symfonyStyle->note('There are available updates to apply'); $symfonyStyle->success('Check complete.'); } else { $symfonyStyle->success('There are no available updates'); } return Command::SUCCESS; } /** * Configure the command. * * @return void * * @since 4.0.0 */ protected function configure(): void { $help = "<info>%command.name%</info> command checks for pending extension updates \nUsage: <info>php %command.full_name%</info>"; $this->setDescription('Check for pending extension updates'); $this->setHelp($help); } } CheckJoomlaUpdatesCommand.php 0000644 00000007125 15172704325 0012274 0 ustar 00 <?php /** * Joomla! Content Management System * * @copyright (C) 2020 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Console; use Joomla\Component\Joomlaupdate\Administrator\Model\UpdateModel; use Joomla\Console\Command\AbstractCommand; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Console command for checking if there are pending extension updates * * @since 4.0.0 */ class CheckJoomlaUpdatesCommand extends AbstractCommand { /** * The default command name * * @var string * @since 4.0.0 */ protected static $defaultName = 'core:check-updates'; /** * Stores the Update Information * * @var UpdateModel * @since 4.0.0 */ private $updateInfo; /** * Initialise the command. * * @return void * * @since 4.0.0 */ protected function configure(): void { $help = "<info>%command.name%</info> will check for Joomla updates \nUsage: <info>php %command.full_name%</info>"; $this->setDescription('Check for Joomla updates'); $this->setHelp($help); } /** * Retrieves Update Information * * @return mixed * * @since 4.0.0 */ private function getUpdateInformationFromModel() { $app = $this->getApplication(); $updatemodel = $app->bootComponent('com_joomlaupdate')->getMVCFactory($app)->createModel('Update', 'Administrator'); $updatemodel->purge(); $updatemodel->refreshUpdates(true); return $updatemodel; } /** * Gets the Update Information * * @return mixed * * @since 4.0.0 */ public function getUpdateInfo() { if (!$this->updateInfo) { $this->setUpdateInfo(); } return $this->updateInfo; } /** * Sets the Update Information * * @param null $info stores update Information * * @return void * * @since 4.0.0 */ public function setUpdateInfo($info = null): void { if (!$info) { $this->updateInfo = $this->getUpdateInformationFromModel(); } else { $this->updateInfo = $info; } } /** * Internal function to execute the command. * * @param InputInterface $input The input to inject into the command. * @param OutputInterface $output The output to inject into the command. * * @return integer The command exit code * * @since 4.0.0 */ protected function doExecute(InputInterface $input, OutputInterface $output): int { $symfonyStyle = new SymfonyStyle($input, $output); $model = $this->getUpdateInfo(); $data = $model->getUpdateInformation(); $symfonyStyle->title('Joomla! Updates'); if (!$data['hasUpdate']) { $symfonyStyle->success('You already have the latest Joomla version ' . $data['latest']); return Command::SUCCESS; } $symfonyStyle->note('New Joomla Version ' . $data['latest'] . ' is available.'); if (!isset($data['object']->downloadurl->_data)) { $symfonyStyle->warning('We cannot find an update URL'); } return Command::SUCCESS; } } GetConfigurationCommand.php 0000644 00000021507 15172704325 0012036 0 ustar 00 <?php /** * Joomla! Content Management System * * @copyright (C) 2020 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Console; use Joomla\Console\Command\AbstractCommand; use Symfony\Component\Console\Input\Input; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Console command for displaying configuration options * * @since 4.0.0 */ class GetConfigurationCommand extends AbstractCommand { /** * The default command name * * @var string * @since 4.0.0 */ protected static $defaultName = 'config:get'; /** * Stores the Input Object * @var Input * @since 4.0.0 */ private $cliInput; /** * SymfonyStyle Object * @var SymfonyStyle * @since 4.0.0 */ private $ioStyle; /** * Constant defining the Database option group * @var array * @since 4.0.0 */ public const DB_GROUP = [ 'name' => 'db', 'options' => [ 'dbtype', 'host', 'user', 'password', 'dbprefix', 'db', 'dbencryption', 'dbsslverifyservercert', 'dbsslkey', 'dbsslcert', 'dbsslca', 'dbsslcipher', ], ]; /** * Constant defining the Session option group * @var array * @since 4.0.0 */ public const SESSION_GROUP = [ 'name' => 'session', 'options' => [ 'session_handler', 'shared_session', 'session_metadata', ], ]; /** * Constant defining the Mail option group * @var array * @since 4.0.0 */ public const MAIL_GROUP = [ 'name' => 'mail', 'options' => [ 'mailonline', 'mailer', 'mailfrom', 'fromname', 'sendmail', 'smtpauth', 'smtpuser', 'smtppass', 'smtphost', 'smtpsecure', 'smtpport', ], ]; /** * Return code if configuration is get successfully * @since 4.0.0 */ public const CONFIG_GET_SUCCESSFUL = 0; /** * Return code if configuration group option is not found * @since 4.0.0 */ public const CONFIG_GET_GROUP_NOT_FOUND = 1; /** * Return code if configuration option is not found * @since 4.0.0 */ public const CONFIG_GET_OPTION_NOT_FOUND = 2; /** * Return code if the command has been invoked with wrong options * @since 4.0.0 */ public const CONFIG_GET_OPTION_FAILED = 3; /** * Configures the IO * * @param InputInterface $input Console Input * @param OutputInterface $output Console Output * * @return void * * @since 4.0.0 * */ private function configureIO(InputInterface $input, OutputInterface $output) { $this->cliInput = $input; $this->ioStyle = new SymfonyStyle($input, $output); } /** * Displays logically grouped options * * @param string $group The group to be processed * * @return integer * * @since 4.0.0 */ public function processGroupOptions($group): int { $configs = $this->getApplication()->getConfig()->toArray(); $configs = $this->formatConfig($configs); $groups = $this->getGroups(); $foundGroup = false; foreach ($groups as $key => $value) { if ($value['name'] === $group) { $foundGroup = true; $options = []; foreach ($value['options'] as $option) { $options[] = [$option, $configs[$option]]; } $this->ioStyle->table(['Option', 'Value'], $options); } } if (!$foundGroup) { $this->ioStyle->error("Group *$group* not found"); return self::CONFIG_GET_GROUP_NOT_FOUND; } return self::CONFIG_GET_SUCCESSFUL; } /** * Gets the defined option groups * * @return array * * @since 4.0.0 */ public function getGroups() { return [ self::DB_GROUP, self::MAIL_GROUP, self::SESSION_GROUP, ]; } /** * Formats the configuration array into desired format * * @param array $configs Array of the configurations * * @return array * * @since 4.0.0 */ public function formatConfig(array $configs): array { $newConfig = []; foreach ($configs as $key => $config) { $config = $config === false ? "false" : $config; $config = $config === true ? "true" : $config; if (!in_array($key, ['cwd', 'execution'])) { $newConfig[$key] = $config; } } return $newConfig; } /** * Handles the command when a single option is requested * * @param string $option The option we want to get its value * * @return integer * * @since 4.0.0 */ public function processSingleOption($option): int { $configs = $this->getApplication()->getConfig()->toArray(); if (!array_key_exists($option, $configs)) { $this->ioStyle->error("Can't find option *$option* in configuration list"); return self::CONFIG_GET_OPTION_NOT_FOUND; } $value = $this->formatConfigValue($this->getApplication()->get($option)); $this->ioStyle->table(['Option', 'Value'], [[$option, $value]]); return self::CONFIG_GET_SUCCESSFUL; } /** * Formats the Configuration value * * @param mixed $value Value to be formatted * * @return string * * @since 4.0.0 */ protected function formatConfigValue($value): string { if ($value === false) { return 'false'; } elseif ($value === true) { return 'true'; } elseif ($value === null) { return 'Not Set'; } elseif (\is_array($value)) { return \json_encode($value); } elseif (\is_object($value)) { return \json_encode(\get_object_vars($value)); } else { return $value; } } /** * Initialise the command. * * @return void * * @since 4.0.0 */ protected function configure(): void { $groups = $this->getGroups(); foreach ($groups as $key => $group) { $groupNames[] = $group['name']; } $groupNames = implode(', ', $groupNames); $this->addArgument('option', null, 'Name of the option'); $this->addOption('group', 'g', InputOption::VALUE_REQUIRED, 'Name of the option'); $help = "<info>%command.name%</info> displays the current value of a configuration option \nUsage: <info>php %command.full_name%</info> <option> \nGroup usage: <info>php %command.full_name%</info> --group <groupname> \nAvailable group names: $groupNames"; $this->setDescription('Display the current value of a configuration option'); $this->setHelp($help); } /** * Internal function to execute the command. * * @param InputInterface $input The input to inject into the command. * @param OutputInterface $output The output to inject into the command. * * @return integer The command exit code * * @since 4.0.0 */ protected function doExecute(InputInterface $input, OutputInterface $output): int { $this->configureIO($input, $output); $configs = $this->formatConfig($this->getApplication()->getConfig()->toArray()); $option = $this->cliInput->getArgument('option'); $group = $this->cliInput->getOption('group'); if ($group) { return $this->processGroupOptions($group); } if ($option) { return $this->processSingleOption($option); } if (!$option && !$group) { $options = []; array_walk( $configs, function ($value, $key) use (&$options) { $options[] = [$key, $this->formatConfigValue($value)]; } ); $this->ioStyle->title("Current options in Configuration"); $this->ioStyle->table(['Option', 'Value'], $options); return self::CONFIG_GET_SUCCESSFUL; } return self::CONFIG_GET_OPTION_NOT_FOUND; } } FinderIndexCommand.php 0000644 00000036570 15172704325 0010774 0 ustar 00 <?php /** * Joomla! CLI * * @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Console; use Joomla\CMS\Factory; use Joomla\CMS\Language\LanguageAwareInterface; use Joomla\CMS\Language\LanguageAwareTrait; use Joomla\CMS\Language\Text; use Joomla\CMS\Plugin\PluginHelper; use Joomla\Component\Finder\Administrator\Indexer\Indexer; use Joomla\Console\Command\AbstractCommand; use Joomla\Database\DatabaseInterface; use Symfony\Component\Console\Command\Command; 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; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Console command Purges and rebuilds the index (search filters are preserved) * * @since 4.0.0 */ class FinderIndexCommand extends AbstractCommand implements LanguageAwareInterface { use LanguageAwareTrait; /** * The default command name * * @var string * @since 4.0.0 */ protected static $defaultName = 'finder:index'; /** * Stores the Input Object * * @var InputInterface * @since 4.0.0 */ private $cliInput; /** * SymfonyStyle Object * * @var SymfonyStyle * @since 4.0.0 */ private $ioStyle; /** * Database connector * * @var DatabaseInterface * @since 4.0.0 */ private $db; /** * Start time for the index process * * @var string * @since 2.5 */ private $time; /** * Start time for each batch * * @var string * @since 2.5 */ private $qtime; /** * Static filters information. * * @var array * @since 3.3 */ private $filters = []; /** * Pausing type or defined pause time in seconds. * One pausing type is implemented: 'division' for dynamic calculation of pauses * * Defaults to 'division' * * @var string|integer * @since 3.9.12 */ private $pause = 'division'; /** * The divisor of the division: batch-processing time / divisor. * This is used together with --pause=division in order to pause dynamically * in relation to the processing time * Defaults to 5 * * @var integer * @since 3.9.12 */ private $divisor = 5; /** * Minimum processing time in seconds, in order to apply a pause * Defaults to 1 * * @var integer * @since 3.9.12 */ private $minimumBatchProcessingTime = 1; /** * Instantiate the command. * * @param DatabaseInterface $db Database connector * * @since 4.0.0 */ public function __construct(DatabaseInterface $db) { $this->db = $db; parent::__construct(); } /** * Initialise the command. * * @return void * * @since 4.0.0 */ protected function configure(): void { $this->addArgument('purge', InputArgument::OPTIONAL, 'Purge the index and rebuilds'); $this->addOption('minproctime', null, InputOption::VALUE_REQUIRED, 'Minimum processing time in seconds, in order to apply a pause', 1); $this->addOption('pause', null, InputOption::VALUE_REQUIRED, 'Pausing type or defined pause time in seconds', 'division'); $this->addOption('divisor', null, InputOption::VALUE_REQUIRED, 'The divisor of the division: batch-processing time / divisor', 5); $help = <<<'EOF' The <info>%command.name%</info> Purges and rebuilds the index (search filters are preserved). <info>php %command.full_name%</info> EOF; $this->setDescription('Purges and rebuild the index'); $this->setHelp($help); } /** * Internal function to execute the command. * * @param InputInterface $input The input to inject into the command. * @param OutputInterface $output The output to inject into the command. * * @return integer The command exit code * * @since 4.0.0 */ protected function doExecute(InputInterface $input, OutputInterface $output): int { // Initialize the time value. $this->time = microtime(true); $this->configureIO($input, $output); $this->ioStyle->writeln( [ '<info>Finder Indexer</>', '<info>==========================</>', '', ] ); if ($this->cliInput->getOption('minproctime')) { $this->minimumBatchProcessingTime = $this->cliInput->getOption('minproctime'); } if ($this->cliInput->getOption('pause')) { $this->pause = $this->cliInput->getOption('pause'); } if ($this->cliInput->getOption('divisor')) { $this->divisor = $this->cliInput->getOption('divisor'); } if ($this->cliInput->getArgument('purge')) { // Taxonomy ids will change following a purge/index, so save filter information first. $this->getFilters(); // Purge the index. $this->purge(); // Run the indexer. $this->index(); // Restore the filters again. $this->putFilters(); } else { $this->index(); } $this->ioStyle->newLine(1); // Total reporting. $this->ioStyle->writeln( [ '<info>' . Text::sprintf('FINDER_CLI_PROCESS_COMPLETE', round(microtime(true) - $this->time, 3)) . '</>', '<info>' . Text::sprintf('FINDER_CLI_PEAK_MEMORY_USAGE', number_format(memory_get_peak_usage(true))) . '</>', ] ); $this->ioStyle->newLine(1); return Command::SUCCESS; } /** * Configures the IO * * @param InputInterface $input Console Input * @param OutputInterface $output Console Output * * @return void * * @since 4.0.0 * */ private function configureIO(InputInterface $input, OutputInterface $output): void { $this->cliInput = $input; $this->ioStyle = new SymfonyStyle($input, $output); try { $language = $this->getLanguage(); } catch (\UnexpectedValueException $e) { @trigger_error(sprintf('Language must be set in 6.0 in %s', __METHOD__), E_USER_DEPRECATED); $language = Factory::getLanguage(); } $language->load('', JPATH_ADMINISTRATOR, null, false, false) || $language->load('', JPATH_ADMINISTRATOR, null, true); $language->load('finder_cli', JPATH_SITE, null, false, false) || $language->load('finder_cli', JPATH_SITE, null, true); } /** * Save static filters. * * Since a purge/index cycle will cause all the taxonomy ids to change, * the static filters need to be updated with the new taxonomy ids. * The static filter information is saved prior to the purge/index * so that it can later be used to update the filters with new ids. * * @return void * * @since 4.0.0 */ private function getFilters(): void { $this->ioStyle->text(Text::_('FINDER_CLI_SAVE_FILTERS')); // Get the taxonomy ids used by the filters. $db = $this->db; $query = $db->getQuery(true); $query ->select('filter_id, title, data') ->from($db->quoteName('#__finder_filters')); $filters = $db->setQuery($query)->loadObjectList(); // Get the name of each taxonomy and the name of its parent. foreach ($filters as $filter) { // Skip empty filters. if ($filter->data === '') { continue; } // Get taxonomy records. $query = $db->getQuery(true); $query ->select('t.title, p.title AS parent') ->from($db->quoteName('#__finder_taxonomy') . ' AS t') ->leftJoin($db->quoteName('#__finder_taxonomy') . ' AS p ON p.id = t.parent_id') ->where($db->quoteName('t.id') . ' IN (' . $filter->data . ')'); $taxonomies = $db->setQuery($query)->loadObjectList(); // Construct a temporary data structure to hold the filter information. foreach ($taxonomies as $taxonomy) { $this->filters[$filter->filter_id][] = [ 'filter' => $filter->title, 'title' => $taxonomy->title, 'parent' => $taxonomy->parent, ]; } } $this->ioStyle->text(Text::sprintf('FINDER_CLI_SAVE_FILTER_COMPLETED', count($filters))); } /** * Purge the index. * * @return void * * @since 3.3 */ private function purge() { $this->ioStyle->text(Text::_('FINDER_CLI_INDEX_PURGE')); // Load the model. $app = $this->getApplication(); $model = $app->bootComponent('com_finder')->getMVCFactory($app)->createModel('Index', 'Administrator'); // Attempt to purge the index. $return = $model->purge(); // If unsuccessful then stop. if (!$return) { $message = Text::_('FINDER_CLI_INDEX_PURGE_FAILED', $model->getError()); $this->ioStyle->error($message); exit(); } $this->ioStyle->text(Text::_('FINDER_CLI_INDEX_PURGE_SUCCESS')); } /** * Run the indexer. * * @return void * * @since 2.5 */ private function index() { // Disable caching. $app = $this->getApplication(); $app->set('caching', 0); $app->set('cache_handler', 'file'); // Reset the indexer state. Indexer::resetState(); // Import the plugins. PluginHelper::importPlugin('system'); PluginHelper::importPlugin('finder'); // Starting Indexer. $this->ioStyle->text(Text::_('FINDER_CLI_STARTING_INDEXER')); // Trigger the onStartIndex event. $app->triggerEvent('onStartIndex'); // Remove the script time limit. if (\function_exists('set_time_limit')) { set_time_limit(0); } // Get the indexer state. $state = Indexer::getState(); // Setting up plugins. $this->ioStyle->text(Text::_('FINDER_CLI_SETTING_UP_PLUGINS')); // Trigger the onBeforeIndex event. $app->triggerEvent('onBeforeIndex'); // Startup reporting. $this->ioStyle->text(Text::sprintf('FINDER_CLI_SETUP_ITEMS', $state->totalItems, round(microtime(true) - $this->time, 3))); // Get the number of batches. $t = (int) $state->totalItems; $c = (int) ceil($t / $state->batchSize); $c = $c === 0 ? 1 : $c; try { // Process the batches. for ($i = 0; $i < $c; $i++) { // Set the batch start time. $this->qtime = microtime(true); // Reset the batch offset. $state->batchOffset = 0; // Trigger the onBuildIndex event. Factory::getApplication()->triggerEvent('onBuildIndex'); // Batch reporting. $text = Text::sprintf('FINDER_CLI_BATCH_COMPLETE', $i + 1, $processingTime = round(microtime(true) - $this->qtime, 3)); $this->ioStyle->text($text); if ($this->pause !== 0) { // Pausing Section $skip = !($processingTime >= $this->minimumBatchProcessingTime); $pause = 0; if ($this->pause === 'division' && $this->divisor > 0) { if (!$skip) { $pause = round($processingTime / $this->divisor); } else { $pause = 1; } } elseif ($this->pause > 0) { $pause = $this->pause; } if ($pause > 0 && !$skip) { $this->ioStyle->text(Text::sprintf('FINDER_CLI_BATCH_PAUSING', $pause)); sleep($pause); $this->ioStyle->text(Text::_('FINDER_CLI_BATCH_CONTINUING')); } if ($skip) { $this->ioStyle->text( Text::sprintf( 'FINDER_CLI_SKIPPING_PAUSE_LOW_BATCH_PROCESSING_TIME', $processingTime, $this->minimumBatchProcessingTime ) ); } // End of Pausing Section } } } catch (\Exception $e) { // Display the error $this->ioStyle->error($e->getMessage()); // Reset the indexer state. Indexer::resetState(); // Close the app $app->close($e->getCode()); } // Reset the indexer state. Indexer::resetState(); } /** * Restore static filters. * * Using the saved filter information, update the filter records * with the new taxonomy ids. * * @return void * * @since 3.3 */ private function putFilters() { $this->ioStyle->text(Text::_('FINDER_CLI_RESTORE_FILTERS')); $db = $this->db; // Use the temporary filter information to update the filter taxonomy ids. foreach ($this->filters as $filter_id => $filter) { $tids = []; foreach ($filter as $element) { // Look for the old taxonomy in the new taxonomy table. $query = $db->getQuery(true); $query ->select('t.id') ->from($db->quoteName('#__finder_taxonomy') . ' AS t') ->leftJoin($db->quoteName('#__finder_taxonomy') . ' AS p ON p.id = t.parent_id') ->where($db->quoteName('t.title') . ' = ' . $db->quote($element['title'])) ->where($db->quoteName('p.title') . ' = ' . $db->quote($element['parent'])); $taxonomy = $db->setQuery($query)->loadResult(); // If we found it then add it to the list. if ($taxonomy) { $tids[] = $taxonomy; } else { $text = Text::sprintf('FINDER_CLI_FILTER_RESTORE_WARNING', $element['parent'], $element['title'], $element['filter']); $this->ioStyle->text($text); } } // Construct a comma-separated string from the taxonomy ids. $taxonomyIds = empty($tids) ? '' : implode(',', $tids); // Update the filter with the new taxonomy ids. $query = $db->getQuery(true); $query ->update($db->quoteName('#__finder_filters')) ->set($db->quoteName('data') . ' = ' . $db->quote($taxonomyIds)) ->where($db->quoteName('filter_id') . ' = ' . (int) $filter_id); $db->setQuery($query)->execute(); } $this->ioStyle->text(Text::sprintf('FINDER_CLI_RESTORE_FILTER_COMPLETED', count($this->filters))); } } SiteUpCommand.php 0000644 00000005517 15172704325 0010003 0 ustar 00 <?php /** * Joomla! Content Management System * * @copyright (C) 2020 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Console; use Joomla\Console\Command\AbstractCommand; use Symfony\Component\Console\Input\ArrayInput; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Console command wrapper for getting the site into offline mode * * @since 4.0.0 */ class SiteUpCommand extends AbstractCommand { /** * The default command name * * @var string * @since 4.0.0 */ protected static $defaultName = 'site:up'; /** * SymfonyStyle Object * @var SymfonyStyle * @since 4.0.0 */ private $ioStyle; /** * Return code if site:up failed * @since 4.0.0 */ public const SITE_UP_FAILED = 1; /** * Return code if site:up was successful * @since 4.0.0 */ public const SITE_UP_SUCCESSFUL = 0; /** * Configures the IO * * @param InputInterface $input Console Input * @param OutputInterface $output Console Output * * @return void * * @since 4.0.0 * */ private function configureIO(InputInterface $input, OutputInterface $output) { $this->ioStyle = new SymfonyStyle($input, $output); } /** * Initialise the command. * * @return void * * @since 4.0.0 */ protected function configure(): void { $help = "<info>%command.name%</info> puts the site into online mode \nUsage: <info>php %command.full_name%</info>"; $this->setDescription('Put the site into online mode'); $this->setHelp($help); } /** * Internal function to execute the command. * * @param InputInterface $input The input to inject into the command. * @param OutputInterface $output The output to inject into the command. * * @return integer The command exit code * * @since 4.0.0 */ protected function doExecute(InputInterface $input, OutputInterface $output): int { $this->configureIO($input, $output); $this->ioStyle->title('Site Online'); $returnCode = $this->getApplication()->getCommand(SetConfigurationCommand::getDefaultName())->execute( new ArrayInput(['options' => ['offline=false']]), $output ); if ($returnCode === 0) { $this->ioStyle->success("Website is now online"); return self::SITE_UP_SUCCESSFUL; } return self::SITE_UP_FAILED; } } SiteDownCommand.php 0000644 00000005540 15172704325 0010322 0 ustar 00 <?php /** * Joomla! Content Management System * * @copyright (C) 2020 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Console; use Joomla\Console\Command\AbstractCommand; use Symfony\Component\Console\Input\ArrayInput; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Console command wrapper for getting the site into offline mode * * @since 4.0.0 */ class SiteDownCommand extends AbstractCommand { /** * The default command name * * @var string * @since 4.0.0 */ protected static $defaultName = 'site:down'; /** * SymfonyStyle Object * @var SymfonyStyle * @since 4.0.0 */ private $ioStyle; /** * Return code if site:down failed * @since 4.0.0 */ public const SITE_DOWN_FAILED = 1; /** * Return code if site:down was successful * @since 4.0.0 */ public const SITE_DOWN_SUCCESSFUL = 0; /** * Configures the IO * * @param InputInterface $input Console Input * @param OutputInterface $output Console Output * * @return void * * @since 4.0.0 * */ private function configureIO(InputInterface $input, OutputInterface $output) { $this->ioStyle = new SymfonyStyle($input, $output); } /** * Initialise the command. * * @return void * * @since 4.0.0 */ protected function configure(): void { $help = "<info>%command.name%</info> puts the site into offline mode \nUsage: <info>php %command.full_name%</info>"; $this->setDescription('Put the site into offline mode'); $this->setHelp($help); } /** * Internal function to execute the command. * * @param InputInterface $input The input to inject into the command. * @param OutputInterface $output The output to inject into the command. * * @return integer The command exit code * * @since 4.0.0 */ protected function doExecute(InputInterface $input, OutputInterface $output): int { $this->configureIO($input, $output); $this->ioStyle->title('Site Offline'); $returnCode = $this->getApplication()->getCommand(SetConfigurationCommand::getDefaultName())->execute( new ArrayInput(['options' => ['offline=true']]), $output ); if ($returnCode === 0) { $this->ioStyle->success("Website is now offline"); return self::SITE_DOWN_SUCCESSFUL; } return self::SITE_DOWN_FAILED; } } ExtensionInstallCommand.php 0000644 00000013521 15172704325 0012067 0 ustar 00 <?php /** * Joomla! Content Management System * * @copyright (C) 2020 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Console; use Joomla\CMS\Installer\Installer; use Joomla\CMS\Installer\InstallerHelper; use Joomla\Console\Command\AbstractCommand; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Console command for installing extensions * * @since 4.0.0 */ class ExtensionInstallCommand extends AbstractCommand { /** * The default command name * * @var string * @since 4.0.0 */ protected static $defaultName = 'extension:install'; /** * Stores the Input Object * @var InputInterface * @since 4.0.0 */ private $cliInput; /** * SymfonyStyle Object * @var SymfonyStyle * @since 4.0.0 */ private $ioStyle; /** * Exit Code For installation failure * @since 4.0.0 */ public const INSTALLATION_FAILED = 1; /** * Exit Code For installation Success * @since 4.0.0 */ public const INSTALLATION_SUCCESSFUL = 0; /** * Configures the IO * * @param InputInterface $input Console Input * @param OutputInterface $output Console Output * * @return void * * @since 4.0.0 * */ private function configureIO(InputInterface $input, OutputInterface $output): void { $this->cliInput = $input; $this->ioStyle = new SymfonyStyle($input, $output); } /** * Initialise the command. * * @return void * * @since 4.0.0 */ protected function configure(): void { $this->addOption('path', null, InputOption::VALUE_REQUIRED, 'The path to the extension'); $this->addOption('url', null, InputOption::VALUE_REQUIRED, 'The url to the extension'); $help = "<info>%command.name%</info> is used to install extensions \nYou must provide one of the following options to the command: \n --path: The path on your local filesystem to the install package \n --url: The URL from where the install package should be downloaded \nUsage: \n <info>php %command.full_name% --path=<path_to_file></info> \n <info>php %command.full_name% --url=<url_to_file></info>"; $this->setDescription('Install an extension from a URL or from a path'); $this->setHelp($help); } /** * Used for installing extension from a path * * @param string $path Path to the extension zip file * * @return boolean * * @since 4.0.0 * * @throws \Exception */ public function processPathInstallation($path): bool { if (!file_exists($path)) { $this->ioStyle->warning('The file path specified does not exist.'); return false; } $tmpPath = $this->getApplication()->get('tmp_path'); $tmpPath = $tmpPath . '/' . basename($path); $package = InstallerHelper::unpack($path, true); if ($package['type'] === false) { return false; } $jInstaller = Installer::getInstance(); $result = $jInstaller->install($package['extractdir']); InstallerHelper::cleanupInstall($tmpPath, $package['extractdir']); return $result; } /** * Used for installing extension from a URL * * @param string $url URL to the extension zip file * * @return boolean * * @since 4.0.0 * * @throws \Exception */ public function processUrlInstallation($url): bool { $filename = InstallerHelper::downloadPackage($url); $tmpPath = $this->getApplication()->get('tmp_path'); $path = $tmpPath . '/' . basename($filename); $package = InstallerHelper::unpack($path, true); if ($package['type'] === false) { return false; } $jInstaller = new Installer(); $result = $jInstaller->install($package['extractdir']); InstallerHelper::cleanupInstall($path, $package['extractdir']); return $result; } /** * Internal function to execute the command. * * @param InputInterface $input The input to inject into the command. * @param OutputInterface $output The output to inject into the command. * * @return integer The command exit code * * @throws \Exception * @since 4.0.0 */ protected function doExecute(InputInterface $input, OutputInterface $output): int { $this->configureIO($input, $output); $this->ioStyle->title('Install Extension'); if ($path = $this->cliInput->getOption('path')) { $result = $this->processPathInstallation($path); if (!$result) { $this->ioStyle->error('Unable to install extension'); return self::INSTALLATION_FAILED; } $this->ioStyle->success('Extension installed successfully.'); return self::INSTALLATION_SUCCESSFUL; } elseif ($url = $this->cliInput->getOption('url')) { $result = $this->processUrlInstallation($url); if (!$result) { $this->ioStyle->error('Unable to install extension'); return self::INSTALLATION_FAILED; } $this->ioStyle->success('Extension installed successfully.'); return self::INSTALLATION_SUCCESSFUL; } $this->ioStyle->error('Invalid argument supplied for command.'); return self::INSTALLATION_FAILED; } } Loader/WritableLoaderInterface.php 0000644 00000001563 15172704325 0013217 0 ustar 00 <?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Console\Loader; use Joomla\Console\Loader\LoaderInterface; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Interface defining a writable command loader. * * @since 4.0.0 */ interface WritableLoaderInterface extends LoaderInterface { /** * Adds a command to the loader. * * @param string $commandName The name of the command to load. * @param string $className The fully qualified class name of the command. * * @return void * * @since 4.0.0 */ public function add(string $commandName, string $className); } Loader/WritableContainerLoader.php 0000644 00000005377 15172704325 0013250 0 ustar 00 <?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Console\Loader; use Joomla\Console\Command\AbstractCommand; use Psr\Container\ContainerInterface; use Symfony\Component\Console\Exception\CommandNotFoundException; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * PSR-11 compatible writable command loader. * * @since 4.0.0 */ final class WritableContainerLoader implements WritableLoaderInterface { /** * The service container. * * @var ContainerInterface * @since 4.0.0 */ private $container; /** * The command name to service ID map. * * @var string[] * @since 4.0.0 */ private $commandMap; /** * Constructor. * * @param ContainerInterface $container A container from which to load command services. * @param array $commandMap An array with command names as keys and service IDs as values. * * @since 4.0.0 */ public function __construct(ContainerInterface $container, array $commandMap) { $this->container = $container; $this->commandMap = $commandMap; } /** * Adds a command to the loader. * * @param string $commandName The name of the command to load. * @param string $className The fully qualified class name of the command. * * @return void * * @since 4.0.0 */ public function add(string $commandName, string $className) { $this->commandMap[$commandName] = $className; } /** * Loads a command. * * @param string $name The command to load. * * @return AbstractCommand * * @since 4.0.0 * @throws CommandNotFoundException */ public function get(string $name): AbstractCommand { if (!$this->has($name)) { throw new CommandNotFoundException(sprintf('Command "%s" does not exist.', $name)); } return $this->container->get($this->commandMap[$name]); } /** * Get the names of the registered commands. * * @return string[] * * @since 4.0.0 */ public function getNames(): array { return array_keys($this->commandMap); } /** * Checks if a command exists. * * @param string $name The command to check. * * @return boolean * * @since 4.0.0 */ public function has($name): bool { return isset($this->commandMap[$name]) && $this->container->has($this->commandMap[$name]); } } SessionMetadataGcCommand.php 0000644 00000005606 15172704325 0012127 0 ustar 00 <?php /** * Joomla! Content Management System * * @copyright (C) 2005 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Console; use Joomla\CMS\Session\MetadataManager; use Joomla\Console\Command\AbstractCommand; use Joomla\Session\SessionInterface; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Console command for performing session metadata garbage collection * * @since 4.0.0 */ class SessionMetadataGcCommand extends AbstractCommand { /** * The default command name * * @var string * @since 4.0.0 */ protected static $defaultName = 'session:metadata:gc'; /** * The session metadata manager. * * @var MetadataManager * @since 4.0.0 */ private $metadataManager; /** * The session object. * * @var SessionInterface * @since 4.0.0 */ private $session; /** * Instantiate the command. * * @param SessionInterface $session The session object. * @param MetadataManager $metadataManager The session metadata manager. * * @since 4.0.0 */ public function __construct(SessionInterface $session, MetadataManager $metadataManager) { $this->session = $session; $this->metadataManager = $metadataManager; parent::__construct(); } /** * Internal function to execute the command. * * @param InputInterface $input The input to inject into the command. * @param OutputInterface $output The output to inject into the command. * * @return integer The command exit code * * @since 4.0.0 */ protected function doExecute(InputInterface $input, OutputInterface $output): int { $symfonyStyle = new SymfonyStyle($input, $output); $symfonyStyle->title('Running Session Metadata Garbage Collection'); $sessionExpire = $this->session->getExpire(); $this->metadataManager->deletePriorTo(time() - $sessionExpire); $symfonyStyle->success('Metadata garbage collection completed.'); return Command::FAILURE; } /** * Configure the command. * * @return void * * @since 4.0.0 */ protected function configure(): void { $help = "<info>%command.name%</info> runs the garbage collection operation for Joomla session metadata \nUsage: <info>php %command.full_name%</info>"; $this->setDescription('Perform session metadata garbage collection'); $this->setHelp($help); } }
| ver. 1.4 |
Github
|
.
| PHP 8.3.23 | Generation time: 0 |
proxy
|
phpinfo
|
Settings