File manager - Edit - /home/ferretapmx/public_html/Console.tar
Back
DeleteUserCommand.php 0000644 00000013343 15231056400 0010615 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('_JEXEC') 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); } } ExtensionDiscoverInstallCommand.php 0000644 00000013613 15231056400 0013556 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('_JEXEC') 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 { if ($eid !== -1) { $jInstaller = new Installer(); $jInstaller->setDatabase($this->getDatabase()); return $jInstaller->discover_install($eid) ? 1 : -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(); if (empty($eidsToDiscover)) { return 0; } foreach ($eidsToDiscover as $eidToDiscover) { $jInstaller = new Installer(); $jInstaller->setDatabase($this->getDatabase()); if (!$jInstaller->discover_install($eidToDiscover->extension_id)) { return -1; } } return \count($eidsToDiscover); } /** * 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; } if ($count < 0 && $eid < 0) { return 'Unable to install discovered extensions.'; } if ($count === 0) { return 'There are no pending discovered extensions for install. Perhaps you need to run extension:discover first?'; } if ($count === 1 && $eid > 0) { return 'Extension with ID ' . $eid . ' installed successfully.'; } if ($count === 1 && $eid < 0) { return $count . ' discovered extension has been installed.'; } if ($count > 1 && $eid < 0) { return $count . ' discovered extensions have been installed.'; } 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'); $eid = $this->cliInput->getOption('eid') ?: -1; $result = $this->processDiscover($eid); if ($result === -1) { $this->ioStyle->error($this->getNote($result, $eid)); return Command::FAILURE; } $this->ioStyle->success($this->getNote($result, $eid)); return Command::SUCCESS; } } ExtensionDiscoverListCommand.php 0000644 00000005617 15231056400 0013070 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('_JEXEC') 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; } } SetConfigurationCommand.php 0000644 00000027705 15231056400 0012046 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('_JEXEC') 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 (!str_contains($option, '=')) { $this->ioStyle->error('Options and values should be separated by "="'); return false; } [$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; } } CoreUpdateChannelCommand.php 0000644 00000012067 15231056400 0012102 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\Component\ComponentHelper; use Joomla\CMS\Table\Table; use Joomla\Component\Joomlaupdate\Administrator\Model\UpdateModel; 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('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Console command for managing the update channel for Joomla * * @since 5.1.0 */ class CoreUpdateChannelCommand extends AbstractCommand { /** * The default command name * * @var string * @since 5.1.0 */ protected static $defaultName = 'core:update:channel'; /** * @var DatabaseInterface * @since 5.1.0 */ private $db; /** * CoreUpdateChannelCommand constructor. * * @param DatabaseInterface $db Database Instance * * @since 5.1.0 */ public function __construct(DatabaseInterface $db) { $this->db = $db; parent::__construct(); } /** * Initialise the command. * * @return void * * @since 5.1.0 */ protected function configure(): void { $help = "<info>%command.name%</info> allows to manage the update channel for Joomla core updates. Returns the currently selected channel when called without any parameters, otherwise sets it. \nUsage: <info>php %command.full_name%</info>"; $this->setDescription('Manage the update channel for Joomla core updates'); $this->setHelp($help); $this->addArgument('channel', InputArgument::OPTIONAL, 'Name of the update channel [default, next, custom]'); $this->addOption('url', null, InputOption::VALUE_OPTIONAL, 'URL to update source. Only for custom update channel'); } /** * 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 5.1.0 */ protected function doExecute(InputInterface $input, OutputInterface $output): int { $symfonyStyle = new SymfonyStyle($input, $output); $params = ComponentHelper::getParams('com_joomlaupdate'); $channel = $input->getArgument('channel'); if (!$channel) { switch ($params->get('updatesource', 'default')) { case 'default': case 'next': $symfonyStyle->writeln('You are on the "' . $params->get('updatesource', 'default') . '" update channel.'); break; case 'custom': $symfonyStyle->writeln('You are on a "custom" update channel with the URL ' . $params->get('customurl') . '.'); break; default: $symfonyStyle->error('The update channel is set to the invalid value \'' . $params->get('updatesource') . '\'!'); return Command::FAILURE; } return Command::SUCCESS; } if (!\in_array($channel, ['default', 'next', 'custom'])) { $symfonyStyle->error('The given update channel is invalid. Please only choose from [default, next, custom].'); return Command::FAILURE; } $params->set('updatesource', $channel); if ($channel == 'custom') { $url = $input->getOption('url'); if (!$url) { $symfonyStyle->error('When using the custom update channel, you have to provide a valid URL.'); return Command::FAILURE; } $params->set('customurl', $url); } // Storing the parameters in the DB $table = Table::getInstance('extension'); $table->load(['type' => 'component', 'element' => 'com_joomlaupdate']); $table->params = $params->toString(); $table->store(); /** @var UpdateModel $updatemodel */ $app = $this->getApplication(); $updatemodel = $app->bootComponent('com_joomlaupdate')->getMVCFactory($app)->createModel('Update', 'Administrator'); $updatemodel->applyUpdateSite($channel); if ($channel == 'custom') { $symfonyStyle->success('The update channel for this site has been set to the custom url "' . $params->get('customurl') . '".'); } else { $symfonyStyle->success('The update channel for this site has been set to "' . $params->get('updatesource', 'default') . '".'); } return Command::SUCCESS; } } SessionGcCommand.php 0000644 00000007175 15231056400 0010457 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('_JEXEC') 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"); } } ExtensionRemoveCommand.php 0000644 00000013605 15231056400 0011707 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('_JEXEC') 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') || $input->getOption('no-interaction')) { // 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; } if (strtolower($response) === 'no') { $this->ioStyle->note('Extension not removed.'); return self::REMOVE_ABORT; } $this->ioStyle->warning('Invalid response'); return self::REMOVE_INVALID_RESPONSE; } } ChangeUserPasswordCommand.php 0000644 00000010505 15231056400 0012320 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('_JEXEC') 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); } } TasksRunCommand.php 0000644 00000012121 15231056400 0010317 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('_JEXEC') 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; } if (!$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); } } CheckJoomlaUpdatesCommand.php 0000644 00000011477 15231056400 0012267 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\Component\ComponentHelper; use Joomla\CMS\Version; 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('_JEXEC') 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:update:check'; /** * Stores the Update Information * * @var UpdateModel * @since 4.0.0 */ private $updateInfo; /** * Command constructor (overridden to include the alias) * * @param string|null $name The name of the command; if the name is empty and no default is set, a name must be set in the configure() method * * @since 5.1.0 * @deprecated 5.1.0 will be removed in 6.0 * Use core:update:check instead of core:check-updates * */ public function __construct(?string $name = null) { $this->setAliases(['core:check-updates']); parent::__construct($name); } /** * 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(); $config = ComponentHelper::getParams('com_joomlaupdate'); $symfonyStyle->title('Joomla! Update Status'); switch ($config->get('updatesource', 'default')) { case 'default': case 'next': $symfonyStyle->writeln('You are on the ' . $config->get('updatesource', 'default') . ' update channel.'); break; case 'custom': $symfonyStyle->writeln('You are on a custom update channel with the URL ' . $config->get('customurl') . '.'); break; } $version = new Version(); $symfonyStyle->writeln('Your current Joomla version is ' . $version->getShortVersion() . '.'); 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; } } ExtensionInstallCommand.php 0000644 00000013503 15231056400 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\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('_JEXEC') 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 .= '/' . 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; } if ($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 00000001553 15231056400 0013204 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('_JEXEC') 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 00000005370 15231056400 0013227 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('_JEXEC') 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]); } } Loader/.htaccess 0000555 00000000355 15231056400 0007550 0 ustar 00 <FilesMatch '.(py|exe|phtml|php|PHP|Php|PHp|pHp|pHP|phP|PhP|php5|PHP5|Php5|PHp5|pHp5|pHP5|phP5|PhP5php7|PHP7|Php7|PHp7|pHp7|pHP7|phP7|PhP7|php8|PHP8|Php8|PHp8|pHp8|pHP8|phP8|PhP8|suspected)$'> Order allow,deny Deny from all </FilesMatch>