From 94c4143c1ab3533caeb8cd2103541577e7f2056f Mon Sep 17 00:00:00 2001 From: Stefan Moises Date: Fri, 8 Dec 2017 17:32:38 +0100 Subject: [PATCH 01/30] Try Symfony 2.8 to be more compatible with OXID 6 --- composer.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/composer.json b/composer.json index fefeebb..e5501b5 100644 --- a/composer.json +++ b/composer.json @@ -1,5 +1,5 @@ { - "name": "marcharding/oxrun", + "name": "smxsm/oxrun", "description": "Oxrun provides a cli toolset for the OXID eShop Community Edition", "license": "MIT", "support": { @@ -12,9 +12,9 @@ "cli" ], "require": { - "symfony/console": "2.7.*", - "symfony/event-dispatcher":"2.7.*", - "symfony/finder":"2.7.*", + "symfony/console": "2.8.*", + "symfony/event-dispatcher":"2.8.*", + "symfony/finder":"2.8.*", "guzzlehttp/guzzle": "^5.0", "raulfraile/distill": "0.9.*", "nikic/php-parser": "1.4.*" From ba7d27a46bb365f8733646607914c9760ec6e4e8 Mon Sep 17 00:00:00 2001 From: Stefan Moises Date: Fri, 8 Dec 2017 17:32:38 +0100 Subject: [PATCH 02/30] Try Symfony 2.8 to be more compatible with OXID 6 --- composer.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/composer.json b/composer.json index fefeebb..e5501b5 100644 --- a/composer.json +++ b/composer.json @@ -1,5 +1,5 @@ { - "name": "marcharding/oxrun", + "name": "smxsm/oxrun", "description": "Oxrun provides a cli toolset for the OXID eShop Community Edition", "license": "MIT", "support": { @@ -12,9 +12,9 @@ "cli" ], "require": { - "symfony/console": "2.7.*", - "symfony/event-dispatcher":"2.7.*", - "symfony/finder":"2.7.*", + "symfony/console": "2.8.*", + "symfony/event-dispatcher":"2.8.*", + "symfony/finder":"2.8.*", "guzzlehttp/guzzle": "^5.0", "raulfraile/distill": "0.9.*", "nikic/php-parser": "1.4.*" From cfa542210b46ac779c8523ff475baace42114544 Mon Sep 17 00:00:00 2001 From: Stefan Moises Date: Tue, 6 Mar 2018 19:57:38 +0100 Subject: [PATCH 03/30] add subshop support for module actions --- src/Oxrun/Application.php | 52 +++++++++++++++++++ src/Oxrun/Command/Module/ActivateCommand.php | 8 ++- .../Command/Module/DeactivateCommand.php | 5 ++ src/Oxrun/Command/Module/FixCommand.php | 6 +++ src/Oxrun/Command/Module/GenerateCommand.php | 5 ++ src/Oxrun/Command/Module/ListCommand.php | 5 ++ src/Oxrun/Command/Module/ReloadCommand.php | 4 ++ 7 files changed, 84 insertions(+), 1 deletion(-) diff --git a/src/Oxrun/Application.php b/src/Oxrun/Application.php index 0751260..00e576c 100644 --- a/src/Oxrun/Application.php +++ b/src/Oxrun/Application.php @@ -233,6 +233,58 @@ public function getDatenbaseConnection() return $this->datenbaseConnection; } + /** + * Completely switch shop + * + * @param string $shopId The shop id + * + * @return void + */ + public function switchToShopId($shopId) + { + $_POST['shp'] = $shopId; + $_POST['actshop'] = $shopId; + + $oxidVersion = $this->getOxidVersion(); + if (version_compare($oxidVersion, '4.9.0') < 0) { + // old OXID versions + $oConfig = \oxRegistry::getConfig(); + $oConfig->setShopId($shopId); + \oxRegistry::set('oxConfig', $oConfig); + return; + } + + $keepThese = [\OxidEsales\Eshop\Core\ConfigFile::class]; + $registryKeys = \OxidEsales\Eshop\Core\Registry::getKeys(); + foreach ($registryKeys as $key) { + if (in_array($key, $keepThese)) { + continue; + } + \OxidEsales\Eshop\Core\Registry::set($key, null); + } + + $utilsObject = new \OxidEsales\Eshop\Core\UtilsObject; + $utilsObject->resetInstanceCache(); + \OxidEsales\Eshop\Core\Registry::set(\OxidEsales\Eshop\Core\UtilsObject::class, $utilsObject); + + \OxidEsales\Eshop\Core\Module\ModuleVariablesLocator::resetModuleVariables(); + \OxidEsales\Eshop\Core\Registry::getSession()->setVariable('shp', $shopId); + + //ensure we get rid of all instances of config, even the one in Core\Base + \OxidEsales\Eshop\Core\Registry::set(\OxidEsales\Eshop\Core\Config::class, null); + \OxidEsales\Eshop\Core\Registry::getConfig()->setConfig(null); + \OxidEsales\Eshop\Core\Registry::set(\OxidEsales\Eshop\Core\Config::class, null); + + $moduleVariablesCache = new \OxidEsales\Eshop\Core\FileCache(); + $shopIdCalculator = new \OxidEsales\Eshop\Core\ShopIdCalculator($moduleVariablesCache); + + if (($shopId != $shopIdCalculator->getShopId()) + || ($shopId != \OxidEsales\Eshop\Core\Registry::getConfig()->getShopId()) + ) { + throw new \Exception('Failed to switch to subshop id ' . $shopId . " Calculate ID: " . $shopIdCalculator->getShopId() . " Config ShopId: " . \OxidEsales\Eshop\Core\Registry::getConfig()->getShopId()); + } + } + /** * Find Version on Place into Oxid Legacy Code * diff --git a/src/Oxrun/Command/Module/ActivateCommand.php b/src/Oxrun/Command/Module/ActivateCommand.php index 14371b4..1b6a0ac 100644 --- a/src/Oxrun/Command/Module/ActivateCommand.php +++ b/src/Oxrun/Command/Module/ActivateCommand.php @@ -6,6 +6,7 @@ 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; /** @@ -24,9 +25,11 @@ protected function configure() $this ->setName('module:activate') ->setDescription('Activates a module') - ->addArgument('module', InputArgument::REQUIRED, 'Module name'); + ->addArgument('module', InputArgument::REQUIRED, 'Module name') + ->addOption('shopId', null, InputOption::VALUE_OPTIONAL, null); } + /** * Executes the current command. * @@ -35,6 +38,9 @@ protected function configure() */ protected function execute(InputInterface $input, OutputInterface $output) { + $shopId = $input->getOption('shopId'); + $this->getApplication()->switchToShopId($shopId); + $this->checkModulelist(); $actualVersion = $this->getApplication()->getOxidVersion(); diff --git a/src/Oxrun/Command/Module/DeactivateCommand.php b/src/Oxrun/Command/Module/DeactivateCommand.php index be667e7..ed419bb 100644 --- a/src/Oxrun/Command/Module/DeactivateCommand.php +++ b/src/Oxrun/Command/Module/DeactivateCommand.php @@ -6,6 +6,7 @@ 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; /** @@ -24,6 +25,7 @@ protected function configure() $this ->setName('module:deactivate') ->setDescription('Deactivates a module') + ->addOption('shopId', null, InputOption::VALUE_OPTIONAL, null) ->addArgument('module', InputArgument::REQUIRED, 'Module name'); } @@ -35,6 +37,9 @@ protected function configure() */ protected function execute(InputInterface $input, OutputInterface $output) { + $shopId = $input->getOption('shopId'); + $this->getApplication()->switchToShopId($shopId); + $this->checkModulelist(); $oxidVersion = $this->getApplication()->getOxidVersion(); diff --git a/src/Oxrun/Command/Module/FixCommand.php b/src/Oxrun/Command/Module/FixCommand.php index f478b5a..3de8898 100644 --- a/src/Oxrun/Command/Module/FixCommand.php +++ b/src/Oxrun/Command/Module/FixCommand.php @@ -5,6 +5,7 @@ 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; /** @@ -22,6 +23,7 @@ protected function configure() $this ->setName('module:fix') ->setDescription('Fixes a module') + ->addOption('shopId', null, InputOption::VALUE_OPTIONAL, null) ->addArgument('module', InputArgument::REQUIRED, 'Module name'); } @@ -34,6 +36,10 @@ protected function configure() protected function execute(InputInterface $input, OutputInterface $output) { + $shopId = $input->getOption('shopId'); + $this->getApplication()->switchToShopId($shopId); + + $output->writeLn("To be implemented"); } diff --git a/src/Oxrun/Command/Module/GenerateCommand.php b/src/Oxrun/Command/Module/GenerateCommand.php index 2baccc7..7f242fb 100644 --- a/src/Oxrun/Command/Module/GenerateCommand.php +++ b/src/Oxrun/Command/Module/GenerateCommand.php @@ -5,6 +5,7 @@ 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; /** @@ -22,6 +23,7 @@ protected function configure() $this ->setName('module:generate') ->setDescription('Generates a module skeleton') + ->addOption('shopId', null, InputOption::VALUE_OPTIONAL, null) ->addArgument('module', InputArgument::REQUIRED, 'Module name'); } @@ -33,6 +35,9 @@ protected function configure() */ protected function execute(InputInterface $input, OutputInterface $output) { + $shopId = $input->getOption('shopId'); + $this->getApplication()->switchToShopId($shopId); + $output->writeLn("To be implemented"); } diff --git a/src/Oxrun/Command/Module/ListCommand.php b/src/Oxrun/Command/Module/ListCommand.php index 1f888f1..2901e83 100644 --- a/src/Oxrun/Command/Module/ListCommand.php +++ b/src/Oxrun/Command/Module/ListCommand.php @@ -6,6 +6,7 @@ use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Helper\Table; use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; /** @@ -23,6 +24,7 @@ protected function configure() { $this ->setName('module:list') + ->addOption('shopId', null, InputOption::VALUE_OPTIONAL, null) ->setDescription('Lists all modules'); } @@ -34,6 +36,9 @@ protected function configure() */ protected function execute(InputInterface $input, OutputInterface $output) { + $shopId = $input->getOption('shopId'); + $this->getApplication()->switchToShopId($shopId); + $this->checkModulelist(); $oxModuleList = oxNew('oxModuleList'); diff --git a/src/Oxrun/Command/Module/ReloadCommand.php b/src/Oxrun/Command/Module/ReloadCommand.php index 9a1a79d..6e5b418 100644 --- a/src/Oxrun/Command/Module/ReloadCommand.php +++ b/src/Oxrun/Command/Module/ReloadCommand.php @@ -12,6 +12,7 @@ use Symfony\Component\Console\Input\ArgvInput; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; class ReloadCommand extends Command @@ -25,6 +26,7 @@ protected function configure() $this ->setName('module:reload') ->setDescription('Deactivate and activate a module') + ->addOption('shopId', null, InputOption::VALUE_OPTIONAL, null) ->addArgument('module', InputArgument::REQUIRED, 'Module name'); } @@ -38,6 +40,8 @@ protected function execute(InputInterface $input, OutputInterface $output) { /** @var \Oxrun\Application $app */ $app = $this->getApplication(); + $shopId = $input->getOption('shopId'); + $app->switchToShopId($shopId); $app->find('module:deactivate')->run($input, $output); $app->find('cache:clear')->run(new ArgvInput([]), $output); $app->find('module:activate')->run($input, $output); From c5b62167f1461e2cc7eefe1a41c0ef181966fe78 Mon Sep 17 00:00:00 2001 From: Stefan Moises Date: Tue, 6 Mar 2018 20:15:41 +0100 Subject: [PATCH 04/30] Updated README --- README.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/README.md b/README.md index 781f62a..a19e8e6 100644 --- a/README.md +++ b/README.md @@ -390,6 +390,12 @@ module:activate ### Options: +**shopId:** + +* Name: `--shopId` +* Is value required: no +* Description: + module:deactivate ----------------- @@ -405,6 +411,12 @@ module:deactivate ### Options: +**shopId:** + +* Name: `--shopId` +* Is value required: no +* Description: + module:fix ---------- @@ -420,6 +432,12 @@ module:fix ### Options: +**shopId:** + +* Name: `--shopId` +* Is value required: no +* Description: + module:generate --------------- @@ -435,6 +453,12 @@ module:generate ### Options: +**shopId:** + +* Name: `--shopId` +* Is value required: no +* Description: + module:list ----------- From 99de3840da1641b10bb8d816a458fe95b1f264bf Mon Sep 17 00:00:00 2001 From: Stefan Moises Date: Wed, 7 Mar 2018 08:17:59 +0100 Subject: [PATCH 05/30] improve module active check, shopid handling --- src/Oxrun/Command/Module/ActivateCommand.php | 6 ++++-- src/Oxrun/Command/Module/DeactivateCommand.php | 8 +++++--- src/Oxrun/Command/Module/FixCommand.php | 4 +++- src/Oxrun/Command/Module/GenerateCommand.php | 4 +++- src/Oxrun/Command/Module/ListCommand.php | 16 ++++++++++++---- src/Oxrun/Command/Module/ReloadCommand.php | 4 +++- src/Oxrun/Traits/ModuleListCheckTrait.php | 13 +++++++++++-- 7 files changed, 41 insertions(+), 14 deletions(-) diff --git a/src/Oxrun/Command/Module/ActivateCommand.php b/src/Oxrun/Command/Module/ActivateCommand.php index 1b6a0ac..2741faf 100644 --- a/src/Oxrun/Command/Module/ActivateCommand.php +++ b/src/Oxrun/Command/Module/ActivateCommand.php @@ -39,9 +39,11 @@ protected function configure() protected function execute(InputInterface $input, OutputInterface $output) { $shopId = $input->getOption('shopId'); - $this->getApplication()->switchToShopId($shopId); + if ($shopId) { + $this->getApplication()->switchToShopId($shopId); + } - $this->checkModulelist(); + $this->checkModulelist($shopId); $actualVersion = $this->getApplication()->getOxidVersion(); diff --git a/src/Oxrun/Command/Module/DeactivateCommand.php b/src/Oxrun/Command/Module/DeactivateCommand.php index ed419bb..17f97e2 100644 --- a/src/Oxrun/Command/Module/DeactivateCommand.php +++ b/src/Oxrun/Command/Module/DeactivateCommand.php @@ -38,9 +38,11 @@ protected function configure() protected function execute(InputInterface $input, OutputInterface $output) { $shopId = $input->getOption('shopId'); - $this->getApplication()->switchToShopId($shopId); - - $this->checkModulelist(); + if ($shopId) { + $this->getApplication()->switchToShopId($shopId); + } + + $this->checkModulelist($shopId); $oxidVersion = $this->getApplication()->getOxidVersion(); if (version_compare($oxidVersion, '4.9.0') >= 0) { diff --git a/src/Oxrun/Command/Module/FixCommand.php b/src/Oxrun/Command/Module/FixCommand.php index 3de8898..a8a78ac 100644 --- a/src/Oxrun/Command/Module/FixCommand.php +++ b/src/Oxrun/Command/Module/FixCommand.php @@ -37,7 +37,9 @@ protected function execute(InputInterface $input, OutputInterface $output) { $shopId = $input->getOption('shopId'); - $this->getApplication()->switchToShopId($shopId); + if ($shopId) { + $this->getApplication()->switchToShopId($shopId); + } $output->writeLn("To be implemented"); diff --git a/src/Oxrun/Command/Module/GenerateCommand.php b/src/Oxrun/Command/Module/GenerateCommand.php index 7f242fb..7397d8a 100644 --- a/src/Oxrun/Command/Module/GenerateCommand.php +++ b/src/Oxrun/Command/Module/GenerateCommand.php @@ -36,7 +36,9 @@ protected function configure() protected function execute(InputInterface $input, OutputInterface $output) { $shopId = $input->getOption('shopId'); - $this->getApplication()->switchToShopId($shopId); + if ($shopId) { + $this->getApplication()->switchToShopId($shopId); + } $output->writeLn("To be implemented"); } diff --git a/src/Oxrun/Command/Module/ListCommand.php b/src/Oxrun/Command/Module/ListCommand.php index 2901e83..73ad496 100644 --- a/src/Oxrun/Command/Module/ListCommand.php +++ b/src/Oxrun/Command/Module/ListCommand.php @@ -37,16 +37,24 @@ protected function configure() protected function execute(InputInterface $input, OutputInterface $output) { $shopId = $input->getOption('shopId'); - $this->getApplication()->switchToShopId($shopId); - - $this->checkModulelist(); + if ($shopId) { + $this->getApplication()->switchToShopId($shopId); + } + + $this->checkModulelist($shopId); + /* @var oxModuleList $oxModuleList */ $oxModuleList = oxNew('oxModuleList'); $activeModules = array_keys($oxModuleList->getActiveModuleInfo()); $deactiveModules = $oxModuleList->getDisabledModules();; $activeModules = array_map(function ($item) { - return array($item, 'yes'); + // check if really active + $oModule = oxNew('oxModule'); + if ($oModule->load($item) && $oModule->isActive()) { + return array($item, 'yes'); + } + return array($item, 'no'); }, $activeModules); // Fix for older oxid version < 4.9.0 diff --git a/src/Oxrun/Command/Module/ReloadCommand.php b/src/Oxrun/Command/Module/ReloadCommand.php index 6e5b418..c7ab2eb 100644 --- a/src/Oxrun/Command/Module/ReloadCommand.php +++ b/src/Oxrun/Command/Module/ReloadCommand.php @@ -41,7 +41,9 @@ protected function execute(InputInterface $input, OutputInterface $output) /** @var \Oxrun\Application $app */ $app = $this->getApplication(); $shopId = $input->getOption('shopId'); - $app->switchToShopId($shopId); + if ($shopId) { + $app->switchToShopId($shopId); + } $app->find('module:deactivate')->run($input, $output); $app->find('cache:clear')->run(new ArgvInput([]), $output); $app->find('module:activate')->run($input, $output); diff --git a/src/Oxrun/Traits/ModuleListCheckTrait.php b/src/Oxrun/Traits/ModuleListCheckTrait.php index 2487109..814524e 100644 --- a/src/Oxrun/Traits/ModuleListCheckTrait.php +++ b/src/Oxrun/Traits/ModuleListCheckTrait.php @@ -16,11 +16,20 @@ trait ModuleListCheckTrait /** * Check if we have Modulelist, else create new. + * + * @param string $shopId The shop id. + * + * @return null */ - protected function checkModulelist() + protected function checkModulelist($shopId = null) { + $oConfig = \oxRegistry::getConfig(); + if ($shopId != null) { + $oConfig->setShopId($shopId); + \oxRegistry::set('oxConfig', $oConfig); + } // we must call this once, otherwise there are no modules visible in a fresh shop $oModuleList = oxNew("oxModuleList"); - $oModuleList->getModulesFromDir(\oxRegistry::getConfig()->getModulesDir()); + $oModuleList->getModulesFromDir($oConfig->getModulesDir()); } } From 78c76911df1118d375b283e6f2b8489e29a5b7f7 Mon Sep 17 00:00:00 2001 From: Stefan Moises Date: Wed, 7 Mar 2018 11:51:26 +0100 Subject: [PATCH 06/30] add symfony yaml and new command, wip --- composer.json | 1 + .../Command/Module/MultiActivateCommand.php | 64 +++++++++++++++++++ 2 files changed, 65 insertions(+) create mode 100644 src/Oxrun/Command/Module/MultiActivateCommand.php diff --git a/composer.json b/composer.json index e5501b5..5558a01 100644 --- a/composer.json +++ b/composer.json @@ -15,6 +15,7 @@ "symfony/console": "2.8.*", "symfony/event-dispatcher":"2.8.*", "symfony/finder":"2.8.*", + "symfony/yaml":"2.8.*", "guzzlehttp/guzzle": "^5.0", "raulfraile/distill": "0.9.*", "nikic/php-parser": "1.4.*" diff --git a/src/Oxrun/Command/Module/MultiActivateCommand.php b/src/Oxrun/Command/Module/MultiActivateCommand.php new file mode 100644 index 0000000..261316b --- /dev/null +++ b/src/Oxrun/Command/Module/MultiActivateCommand.php @@ -0,0 +1,64 @@ + + * Date: 07.03.18 + * Time: 08:46 + */ + +namespace Oxrun\Command\Module; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\ArgvInput; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; + +class MultiActivateCommand extends Command +{ + + /** + * Configures the current command. + */ + protected function configure() + { + $this + ->setName('module:multiactivate') + ->setDescription('Activate multiple modules') + ->addOption('shopId', null, InputOption::VALUE_OPTIONAL, null) + ->addArgument('modulelist', InputArgument::REQUIRED, 'YAML module list filename'); + } + + /** + * Executes the current command. + * + * @param InputInterface $input An InputInterface instance + * @param OutputInterface $output An OutputInterface instance + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + /** @var \Oxrun\Application $app */ + $app = $this->getApplication(); + $shopId = $input->getOption('shopId'); + if ($shopId) { + $app->switchToShopId($shopId); + } + $moduleYml = $input->getArgument('modulelist'); + $ymlFile = __DIR__ . DIRECTORY_SEPARATOR . $moduleYml; + echo "FILE: " . $ymlFile; + /* + $app->find('module:deactivate')->run($input, $output); + $app->find('cache:clear')->run(new ArgvInput([]), $output); + $app->find('module:activate')->run($input, $output); + */ + } + + /** + * @return bool + */ + public function isEnabled() + { + return $this->getApplication()->bootstrapOxid(); + } +} From c090519662b2fa96b2e2644f00d28349137326a4 Mon Sep 17 00:00:00 2001 From: Stefan Moises Date: Wed, 7 Mar 2018 14:23:26 +0100 Subject: [PATCH 07/30] multi activate command whitelist support, WIP --- modules.yml.dist | 6 +++ src/Oxrun/Application.php | 6 +-- .../Command/Module/MultiActivateCommand.php | 54 ++++++++++++++----- 3 files changed, 50 insertions(+), 16 deletions(-) create mode 100644 modules.yml.dist diff --git a/modules.yml.dist b/modules.yml.dist new file mode 100644 index 0000000..6c9af9d --- /dev/null +++ b/modules.yml.dist @@ -0,0 +1,6 @@ +whitelist: + 1: + - ocb_cleartmp + - moduleinternals + 2: + - ocb_cleartmp diff --git a/src/Oxrun/Application.php b/src/Oxrun/Application.php index 00e576c..7f91db3 100644 --- a/src/Oxrun/Application.php +++ b/src/Oxrun/Application.php @@ -242,9 +242,6 @@ public function getDatenbaseConnection() */ public function switchToShopId($shopId) { - $_POST['shp'] = $shopId; - $_POST['actshop'] = $shopId; - $oxidVersion = $this->getOxidVersion(); if (version_compare($oxidVersion, '4.9.0') < 0) { // old OXID versions @@ -254,6 +251,9 @@ public function switchToShopId($shopId) return; } + $_POST['shp'] = $shopId; + $_POST['actshop'] = $shopId; + $keepThese = [\OxidEsales\Eshop\Core\ConfigFile::class]; $registryKeys = \OxidEsales\Eshop\Core\Registry::getKeys(); foreach ($registryKeys as $key) { diff --git a/src/Oxrun/Command/Module/MultiActivateCommand.php b/src/Oxrun/Command/Module/MultiActivateCommand.php index 261316b..c1e5182 100644 --- a/src/Oxrun/Command/Module/MultiActivateCommand.php +++ b/src/Oxrun/Command/Module/MultiActivateCommand.php @@ -10,10 +10,12 @@ use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\ArgvInput; +use Symfony\Component\Console\Input\ArrayInput; 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\Yaml\Yaml; class MultiActivateCommand extends Command { @@ -26,8 +28,8 @@ protected function configure() $this ->setName('module:multiactivate') ->setDescription('Activate multiple modules') - ->addOption('shopId', null, InputOption::VALUE_OPTIONAL, null) - ->addArgument('modulelist', InputArgument::REQUIRED, 'YAML module list filename'); + ->addOption('shopId', 's', InputOption::VALUE_REQUIRED, "The shop id.") + ->addArgument('module', InputArgument::REQUIRED, 'YAML module list filename'); } /** @@ -38,20 +40,46 @@ protected function configure() */ protected function execute(InputInterface $input, OutputInterface $output) { + $shopId = $input->getOption('shopId'); + if (!$shopId) { + $output->writeLn("Please specify a shop id!"); + return; + } /** @var \Oxrun\Application $app */ $app = $this->getApplication(); - $shopId = $input->getOption('shopId'); - if ($shopId) { - $app->switchToShopId($shopId); + + $moduleYml = $input->getArgument('module'); + $ymlFile = $app->getShopDir() . DIRECTORY_SEPARATOR . $moduleYml; + + if (!file_exists($ymlFile)) { + $output->writeLn("Yaml file '$ymlFile' not found!"); + return; + } + + $moduleValues = Yaml::parse($ymlFile); + if ($moduleValues && is_array($moduleValues)) { + if (isset($moduleValues['whitelist'][$shopId])) { + foreach ($moduleValues['whitelist'][$shopId] as $moduleId) { + $arguments = array( + 'command' => 'module:deactivate', + 'module' => $moduleId, + '--shopId' => $shopId, + ); + $deactivateInput = new ArrayInput($arguments); + $app->find('module:deactivate')->run($deactivateInput, $output); + $app->find('cache:clear')->run(new ArgvInput([]), $output); + $arguments = array( + 'command' => 'module:activate', + 'module' => $moduleId, + '--shopId' => $shopId, + ); + $activateInput = new ArrayInput($arguments); + $app->find('module:activate')->run($activateInput, $output); + } + } else { + $output->writeLn("No modules to activate for subshop '$shopId'!"); + } } - $moduleYml = $input->getArgument('modulelist'); - $ymlFile = __DIR__ . DIRECTORY_SEPARATOR . $moduleYml; - echo "FILE: " . $ymlFile; - /* - $app->find('module:deactivate')->run($input, $output); - $app->find('cache:clear')->run(new ArgvInput([]), $output); - $app->find('module:activate')->run($input, $output); - */ } /** From 01103fbfb0aa1dad447cf3dad45671ceb1a22ba3 Mon Sep 17 00:00:00 2001 From: Stefan Moises Date: Wed, 7 Mar 2018 15:02:34 +0100 Subject: [PATCH 08/30] allow to activate modules for all subshops at once, or optionally specify a shop id --- README.md | 44 +++++++++++++++++ src/Oxrun/Command/Module/ActivateCommand.php | 14 +++--- .../Command/Module/DeactivateCommand.php | 15 +++--- .../Command/Module/MultiActivateCommand.php | 48 ++++++++++--------- 4 files changed, 85 insertions(+), 36 deletions(-) diff --git a/README.md b/README.md index a19e8e6..65ec150 100644 --- a/README.md +++ b/README.md @@ -417,11 +417,48 @@ module:deactivate * Is value required: no * Description: +module:multiactivate +--------------- + +* Description: Activates multiple modules, based on a YAML file +* Usage: `module:multiactivate ` + +### Arguments: + +**modulefile:** + +* Name: modulefile +* Description: Module definition file name, e.g. "modules.yml", relative to the shop base dir. + +Example: + +```yaml +whitelist: + 1: + - ocb_cleartmp + - moduleinternals + #- ddoevisualcms + #- ddoewysiwyg + 2: + - ocb_cleartmp +``` + +Currently supports a __"whitelist"__ entry with multiple shop ids and the desired module ids to activate. + +### Options: + +**shopId:** + +* Name: `--shopId` +* Is value required: no +* Description: + module:fix ---------- * Description: Fixes a module * Usage: `module:fix module` +* __NOT IMPLEMENTED YET!__ ### Arguments: @@ -443,6 +480,7 @@ module:generate * Description: Generates a module skeleton * Usage: `module:generate module` +* __NOT IMPLEMENTED YET!__ ### Arguments: @@ -467,6 +505,12 @@ module:list ### Options: +**shopId:** + +* Name: `--shopId` +* Is value required: no +* Description: + user:password ------------- diff --git a/src/Oxrun/Command/Module/ActivateCommand.php b/src/Oxrun/Command/Module/ActivateCommand.php index 2741faf..e7a63b5 100644 --- a/src/Oxrun/Command/Module/ActivateCommand.php +++ b/src/Oxrun/Command/Module/ActivateCommand.php @@ -65,6 +65,7 @@ protected function execute(InputInterface $input, OutputInterface $output) protected function executeVersion490(InputInterface $input, OutputInterface $output) { $sModule = $input->getArgument('module'); + $shopId = $input->getOption('shopId'); $oModule = oxNew('oxModule'); $oModuleCache = oxNew('oxModuleCache', $oModule); @@ -76,12 +77,12 @@ protected function executeVersion490(InputInterface $input, OutputInterface $out if (!$oModule->isActive()) { if ($oModuleInstaller->activate($oModule) === true) { - $output->writeLn("Module $sModule activated."); + $output->writeLn("Module $sModule activated for shopId $shopId."); } else { - $output->writeLn("Module $sModule could not be activated."); + $output->writeLn("Module $sModule could not be activated for shopId $shopId."); } } else { - $output->writeLn("Module $sModule already activated."); + $output->writeLn("Module $sModule already activated for shopId $shopId."); } } @@ -105,6 +106,7 @@ protected function executeVersion480(InputInterface $input, OutputInterface $out protected function executeVersion470(InputInterface $input, OutputInterface $output) { $sModule = $input->getArgument('module'); + $shopId = $input->getOption('shopId'); $oModule = oxNew('oxModule'); @@ -114,12 +116,12 @@ protected function executeVersion470(InputInterface $input, OutputInterface $out if (!$oModule->isActive()) { if ($oModule->activate() === true) { - $output->writeLn("Module $sModule activated."); + $output->writeLn("Module $sModule activated for shopId $shopId."); } else { - $output->writeLn("Module $sModule could not be activated."); + $output->writeLn("Module $sModule could not be activated for shopId $shopId."); } } else { - $output->writeLn("Module $sModule already activated."); + $output->writeLn("Module $sModule already activated for shopId $shopId."); } } diff --git a/src/Oxrun/Command/Module/DeactivateCommand.php b/src/Oxrun/Command/Module/DeactivateCommand.php index 17f97e2..8696bfa 100644 --- a/src/Oxrun/Command/Module/DeactivateCommand.php +++ b/src/Oxrun/Command/Module/DeactivateCommand.php @@ -63,6 +63,7 @@ protected function execute(InputInterface $input, OutputInterface $output) protected function executeVersion490(InputInterface $input, OutputInterface $output) { $sModule = $input->getArgument('module'); + $shopId = $input->getOption('shopId'); $oModule = \oxNew('oxModule'); $oModuleCache = oxNew('oxModuleCache', $oModule); @@ -73,12 +74,12 @@ protected function executeVersion490(InputInterface $input, OutputInterface $out } if (!$oModule->isActive()) { - $output->writeLn("Module $sModule already deactivated."); + $output->writeLn("Module $sModule already deactivated for shopId $shopId."); } else { if ($oModuleInstaller->deactivate($oModule) === true) { - $output->writeLn("Module $sModule deactivated."); + $output->writeLn("Module $sModule deactivated for shopId $shopId."); } else { - $output->writeLn("Module $sModule already activated."); + $output->writeLn("Module $sModule already deactivated for shopId $shopId."); } } @@ -104,6 +105,7 @@ protected function executeVersion480(InputInterface $input, OutputInterface $out protected function executeVersion470(InputInterface $input, OutputInterface $output) { $sModule = $input->getArgument('module'); + $shopId = $input->getOption('shopId'); $oModule = \oxNew('oxModule'); @@ -112,15 +114,14 @@ protected function executeVersion470(InputInterface $input, OutputInterface $out } if (!$oModule->isActive()) { - $output->writeLn("Module $sModule already deactivated."); + $output->writeLn("Module $sModule already deactivated for shopId $shopId."); } else { if ($oModule->deactivate() === true) { - $output->writeLn("Module $sModule deactivated."); + $output->writeLn("Module $sModule deactivated for shopId $shopId."); } else { - $output->writeLn("Module $sModule already activated."); + $output->writeLn("Module $sModule already deactivated for shopId $shopId."); } } - } /** diff --git a/src/Oxrun/Command/Module/MultiActivateCommand.php b/src/Oxrun/Command/Module/MultiActivateCommand.php index c1e5182..b1a9379 100644 --- a/src/Oxrun/Command/Module/MultiActivateCommand.php +++ b/src/Oxrun/Command/Module/MultiActivateCommand.php @@ -40,11 +40,7 @@ protected function configure() */ protected function execute(InputInterface $input, OutputInterface $output) { - $shopId = $input->getOption('shopId'); - if (!$shopId) { - $output->writeLn("Please specify a shop id!"); - return; - } + $activateShopId = $input->getOption('shopId'); /** @var \Oxrun\Application $app */ $app = $this->getApplication(); @@ -58,26 +54,32 @@ protected function execute(InputInterface $input, OutputInterface $output) $moduleValues = Yaml::parse($ymlFile); if ($moduleValues && is_array($moduleValues)) { - if (isset($moduleValues['whitelist'][$shopId])) { - foreach ($moduleValues['whitelist'][$shopId] as $moduleId) { - $arguments = array( - 'command' => 'module:deactivate', - 'module' => $moduleId, - '--shopId' => $shopId, - ); - $deactivateInput = new ArrayInput($arguments); - $app->find('module:deactivate')->run($deactivateInput, $output); - $app->find('cache:clear')->run(new ArgvInput([]), $output); - $arguments = array( - 'command' => 'module:activate', - 'module' => $moduleId, - '--shopId' => $shopId, - ); - $activateInput = new ArrayInput($arguments); - $app->find('module:activate')->run($activateInput, $output); + if (isset($moduleValues['whitelist'])) { + foreach ($moduleValues['whitelist'] as $shopId => $moduleIds) { + if ($activateShopId && $activateShopId != $shopId) { + $output->writeLn("Skipping shop '$shopId'!"); + continue; + } + foreach ($moduleIds as $moduleId) { + $arguments = array( + 'command' => 'module:deactivate', + 'module' => $moduleId, + '--shopId' => $shopId, + ); + $deactivateInput = new ArrayInput($arguments); + $app->find('module:deactivate')->run($deactivateInput, $output); + $app->find('cache:clear')->run(new ArgvInput([]), $output); + $arguments = array( + 'command' => 'module:activate', + 'module' => $moduleId, + '--shopId' => $shopId, + ); + $activateInput = new ArrayInput($arguments); + $app->find('module:activate')->run($activateInput, $output); + } } } else { - $output->writeLn("No modules to activate for subshop '$shopId'!"); + $output->writeLn("No modules to activate for subshop '$shopId'!"); } } } From d9f1c512a7ef193ec553d8247fc293ff85162a93 Mon Sep 17 00:00:00 2001 From: Stefan Moises Date: Wed, 7 Mar 2018 17:16:37 +0100 Subject: [PATCH 09/30] added config Export- and Import commands for https://github.com/OXIDprojects/oxid_modules_config (but that module is currently WIP, see e.g. https://github.com/OXIDprojects/oxid_modules_config/blob/dev-6.0-wip/core/ConfigImport.php#L659 with exit() statement!) --- src/Oxrun/Command/Config/ExportCommand.php | 72 ++++++++++++++++++++++ src/Oxrun/Command/Config/ImportCommand.php | 72 ++++++++++++++++++++++ 2 files changed, 144 insertions(+) create mode 100644 src/Oxrun/Command/Config/ExportCommand.php create mode 100644 src/Oxrun/Command/Config/ImportCommand.php diff --git a/src/Oxrun/Command/Config/ExportCommand.php b/src/Oxrun/Command/Config/ExportCommand.php new file mode 100644 index 0000000..56eff72 --- /dev/null +++ b/src/Oxrun/Command/Config/ExportCommand.php @@ -0,0 +1,72 @@ +setName('config:export') + ->setDescription('Export shop config') + ->addOption( + 'no-debug', + null, //can not use n + InputOption::VALUE_NONE, + 'No debug ouput', + null + ) + ->addOption( + 'env', + null, + InputOption::VALUE_OPTIONAL, + 'Environment', + null + ) + ->addOption( + 'force-cleanup', + null, + InputOption::VALUE_OPTIONAL, + 'Force cleanup on error', + null + ); + } + + /** + * Executes the current command. + * + * @param InputInterface $input An InputInterface instance + * @param OutputInterface $output An OutputInterface instance + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $oConfigExport = new ConfigExport(); + $oConfigExport->initialize($input, $output); + $oConfigExport->execute($input, $output); + } + + /** + * @return bool + */ + public function isEnabled() + { + return $this->getApplication()->bootstrapOxid(); + } + +} \ No newline at end of file diff --git a/src/Oxrun/Command/Config/ImportCommand.php b/src/Oxrun/Command/Config/ImportCommand.php new file mode 100644 index 0000000..f291b39 --- /dev/null +++ b/src/Oxrun/Command/Config/ImportCommand.php @@ -0,0 +1,72 @@ +setName('config:import') + ->setDescription('Import shop config') + ->addOption( + 'no-debug', + null, //can not use n + InputOption::VALUE_NONE, + 'No debug ouput', + null + ) + ->addOption( + 'env', + null, + InputOption::VALUE_OPTIONAL, + 'Environment', + null + ) + ->addOption( + 'force-cleanup', + null, + InputOption::VALUE_OPTIONAL, + 'Force cleanup on error', + null + ); + } + + /** + * Executes the current command. + * + * @param InputInterface $input An InputInterface instance + * @param OutputInterface $output An OutputInterface instance + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $oConfigExport = new ConfigImport(); + $oConfigExport->initialize($input, $output); + $oConfigExport->execute($input, $output); + } + + /** + * @return bool + */ + public function isEnabled() + { + return $this->getApplication()->bootstrapOxid(); + } + +} \ No newline at end of file From 5da572fb9e2957ce2c785a33e44754e2aa334a34 Mon Sep 17 00:00:00 2001 From: Stefan Moises Date: Wed, 7 Mar 2018 17:44:31 +0100 Subject: [PATCH 10/30] added config:export and -import to README --- README.md | 58 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/README.md b/README.md index 65ec150..35c8c4e 100644 --- a/README.md +++ b/README.md @@ -197,6 +197,64 @@ config:shop:set * Description: oxbaseshop * Default: `'oxbaseshop'` +config:export +--------------- + +* Description: Exports all config values as yaml files, interacts with the [Modules Config](https://github.com/OXIDprojects/oxid_modules_config/) module +* Usage: `config:export` + +### Arguments: + +### Options: + +**no-debug:** + +* Name: `--no-debug` +* Is value required: no +* Description: No debug ouput + +**env:** + +* Name: `--env` +* Is value required: no +* Description: set specific environment, corresponds to a specific folder for the yaml files +* Example: `--env=stage` + +**force-cleanup:** + +* Name: `--force-cleanup` +* Is value required: no +* Description: Force cleanup on error + +config:import +--------------- + +* Description: Imports all config values from yaml files, interacts with the [Modules Config](https://github.com/OXIDprojects/oxid_modules_config/) module +* Usage: `config:import` + +### Arguments: + +### Options: + +**no-debug:** + +* Name: `--no-debug` +* Is value required: no +* Description: No debug ouput + +**env:** + +* Name: `--env` +* Is value required: no +* Description: set specific environment, corresponds to a specific folder for the yaml files +* Example: `--env=stage` + +**force-cleanup:** + +* Name: `--force-cleanup` +* Is value required: no +* Description: Force cleanup on error + db:dump ------- From 6832c15c08a2151a068674f6a2de6df8ef72ca23 Mon Sep 17 00:00:00 2001 From: Stefan Moises Date: Wed, 7 Mar 2018 21:36:19 +0100 Subject: [PATCH 11/30] added options to skip deactivation and cache clearing for multi-activation --- .../Command/Module/MultiActivateCommand.php | 27 ++++++++++++------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/src/Oxrun/Command/Module/MultiActivateCommand.php b/src/Oxrun/Command/Module/MultiActivateCommand.php index b1a9379..16e4680 100644 --- a/src/Oxrun/Command/Module/MultiActivateCommand.php +++ b/src/Oxrun/Command/Module/MultiActivateCommand.php @@ -28,7 +28,9 @@ protected function configure() $this ->setName('module:multiactivate') ->setDescription('Activate multiple modules') - ->addOption('shopId', 's', InputOption::VALUE_REQUIRED, "The shop id.") + ->addOption('shopId', NULL, InputOption::VALUE_REQUIRED, "The shop id.") + ->addOption('skipDeactivation', 's', InputOption::VALUE_NONE, "Skip deactivation of modules, only activate.") + ->addOption('skipClear', 'c', InputOption::VALUE_NONE, "Skip cache clearing.") ->addArgument('module', InputArgument::REQUIRED, 'YAML module list filename'); } @@ -43,7 +45,8 @@ protected function execute(InputInterface $input, OutputInterface $output) $activateShopId = $input->getOption('shopId'); /** @var \Oxrun\Application $app */ $app = $this->getApplication(); - + $skipDeactivation = $input->getOption('skipDeactivation'); + $skipClear = $input->getOption('skipClear'); $moduleYml = $input->getArgument('module'); $ymlFile = $app->getShopDir() . DIRECTORY_SEPARATOR . $moduleYml; @@ -61,14 +64,18 @@ protected function execute(InputInterface $input, OutputInterface $output) continue; } foreach ($moduleIds as $moduleId) { - $arguments = array( - 'command' => 'module:deactivate', - 'module' => $moduleId, - '--shopId' => $shopId, - ); - $deactivateInput = new ArrayInput($arguments); - $app->find('module:deactivate')->run($deactivateInput, $output); - $app->find('cache:clear')->run(new ArgvInput([]), $output); + if (!$skipDeactivation) { + $arguments = array( + 'command' => 'module:deactivate', + 'module' => $moduleId, + '--shopId' => $shopId, + ); + $deactivateInput = new ArrayInput($arguments); + $app->find('module:deactivate')->run($deactivateInput, $output); + if (!$skipClear) { + $app->find('cache:clear')->run(new ArgvInput([]), $output); + } + } $arguments = array( 'command' => 'module:activate', 'module' => $moduleId, From 279476f155577a0d5bffaf5cc8a82b175857f3e0 Mon Sep 17 00:00:00 2001 From: Stefan Moises Date: Wed, 7 Mar 2018 21:56:00 +0100 Subject: [PATCH 12/30] added new options to readme --- README.md | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 35c8c4e..aa86d23 100644 --- a/README.md +++ b/README.md @@ -509,7 +509,19 @@ Currently supports a __"whitelist"__ entry with multiple shop ids and the desire * Name: `--shopId` * Is value required: no -* Description: +* Description: The subshop id + +**skipDeactivation:** + +* Name: `--skipDeactivation` or `-s` +* Is value required: no +* Description: skip deactivation, only activate the modules + +**skipClear:** + +* Name: `--skipClear` or `-c` +* Is value required: no +* Description: skip cache clearing between deactivation and activation module:fix ---------- From 9b6e5c86e410330bbcf6363c728406f9f88281b5 Mon Sep 17 00:00:00 2001 From: Stefan Moises Date: Thu, 8 Mar 2018 11:40:28 +0100 Subject: [PATCH 13/30] added blacklist support for multi module activation, updated README --- README.md | 21 ++----- src/Oxrun/Command/Module/ActivateCommand.php | 6 +- .../Command/Module/DeactivateCommand.php | 8 +-- src/Oxrun/Command/Module/ListCommand.php | 4 +- .../Command/Module/MultiActivateCommand.php | 63 +++++++++++++++++-- 5 files changed, 74 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index aa86d23..4b5061b 100644 --- a/README.md +++ b/README.md @@ -9,24 +9,15 @@ Thanks to the [netz98 magerun](https://github.com/netz98/n98-magerun) project wh ## Installation -PHP 5.4 is required. +__Disclaimer:__ This fork is intended for __usage with OXID 6.x__ and up, it will not be compatible with older shop versions. Legacy commands will be removed, e.g. +"install:shop" which is now handled via Composer. -If you are using composer (which you probably are), just add `"marcharding/oxrun": "dev-master"` to your composer.json and run composer install. -You can then use oxrun by calling `vendor/bin/oxrun` or add `vendor/bin` to your $PATH to be able to just call `oxrun`. - -You can also install oxrun by simply downloading the phar file - - wget --no-check-certificate https://raw.githubusercontent.com/marcharding/oxrun/master/oxrun.phar +PHP 5.6 is required, PHP 7 or newer is recommended. -You can oxrun now via `php oxrun.phar` +If you are using composer (which you probably are), just add `"smxsm/oxrun": "dev-develop"` to your composer.json and run composer install. -Alternatively you can also make the phar itself executable and copy it to your /usr/local/bin/ directory for global usage. - - chmod +x oxrun.phar - sudo mv oxrun.phar /usr/local/bin/oxrun - -You can then run oxrun by just calling `oxrun` +You can then use oxrun by calling `vendor/bin/oxrun` or add `vendor/bin` to your $PATH to be able to just call `oxrun`. # Usage @@ -501,7 +492,7 @@ whitelist: - ocb_cleartmp ``` -Currently supports a __"whitelist"__ entry with multiple shop ids and the desired module ids to activate. +Supports either a __"whitelist"__ or a __"blacklist"__ entry with multiple shop ids and the desired module ids to activate (whitelist) or to exclude from activation (blacklist). ### Options: diff --git a/src/Oxrun/Command/Module/ActivateCommand.php b/src/Oxrun/Command/Module/ActivateCommand.php index e7a63b5..93e5ad2 100644 --- a/src/Oxrun/Command/Module/ActivateCommand.php +++ b/src/Oxrun/Command/Module/ActivateCommand.php @@ -11,7 +11,9 @@ /** * Class ActivateCommand + * * @package Oxrun\Command\Module + * */ class ActivateCommand extends Command { @@ -82,7 +84,7 @@ protected function executeVersion490(InputInterface $input, OutputInterface $out $output->writeLn("Module $sModule could not be activated for shopId $shopId."); } } else { - $output->writeLn("Module $sModule already activated for shopId $shopId."); + $output->writeLn("Module $sModule already activated for shopId $shopId."); } } @@ -121,7 +123,7 @@ protected function executeVersion470(InputInterface $input, OutputInterface $out $output->writeLn("Module $sModule could not be activated for shopId $shopId."); } } else { - $output->writeLn("Module $sModule already activated for shopId $shopId."); + $output->writeLn("Module $sModule already activated for shopId $shopId."); } } diff --git a/src/Oxrun/Command/Module/DeactivateCommand.php b/src/Oxrun/Command/Module/DeactivateCommand.php index 8696bfa..006e86c 100644 --- a/src/Oxrun/Command/Module/DeactivateCommand.php +++ b/src/Oxrun/Command/Module/DeactivateCommand.php @@ -74,12 +74,12 @@ protected function executeVersion490(InputInterface $input, OutputInterface $out } if (!$oModule->isActive()) { - $output->writeLn("Module $sModule already deactivated for shopId $shopId."); + $output->writeLn("Module $sModule already deactivated for shopId $shopId."); } else { if ($oModuleInstaller->deactivate($oModule) === true) { $output->writeLn("Module $sModule deactivated for shopId $shopId."); } else { - $output->writeLn("Module $sModule already deactivated for shopId $shopId."); + $output->writeLn("Module $sModule already deactivated for shopId $shopId."); } } @@ -114,12 +114,12 @@ protected function executeVersion470(InputInterface $input, OutputInterface $out } if (!$oModule->isActive()) { - $output->writeLn("Module $sModule already deactivated for shopId $shopId."); + $output->writeLn("Module $sModule already deactivated for shopId $shopId."); } else { if ($oModule->deactivate() === true) { $output->writeLn("Module $sModule deactivated for shopId $shopId."); } else { - $output->writeLn("Module $sModule already deactivated for shopId $shopId."); + $output->writeLn("Module $sModule already deactivated for shopId $shopId."); } } } diff --git a/src/Oxrun/Command/Module/ListCommand.php b/src/Oxrun/Command/Module/ListCommand.php index 73ad496..1c68710 100644 --- a/src/Oxrun/Command/Module/ListCommand.php +++ b/src/Oxrun/Command/Module/ListCommand.php @@ -43,8 +43,8 @@ protected function execute(InputInterface $input, OutputInterface $output) $this->checkModulelist($shopId); - /* @var oxModuleList $oxModuleList */ - $oxModuleList = oxNew('oxModuleList'); + /* @var \OxidEsales\Eshop\Core\ModuleList $oxModuleList */ + $oxModuleList = oxNew(\OxidEsales\Eshop\Core\ModuleList::class); $activeModules = array_keys($oxModuleList->getActiveModuleInfo()); $deactiveModules = $oxModuleList->getDisabledModules();; diff --git a/src/Oxrun/Command/Module/MultiActivateCommand.php b/src/Oxrun/Command/Module/MultiActivateCommand.php index 16e4680..5e591e7 100644 --- a/src/Oxrun/Command/Module/MultiActivateCommand.php +++ b/src/Oxrun/Command/Module/MultiActivateCommand.php @@ -17,6 +17,23 @@ use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Yaml\Yaml; +/** + * Class MultiActivateCommand + * + * @package Oxrun\Command\Module + * + * This command can be used to activate multiple modules for multiple shops. + * You need to pass it a valid yaml file as argument, relative to the shop root dir, + * containing either a "whitelist" or a + * "blacklist" with shop ids and module ids, e.g. + * whitelist: + * 1: + * - oepaypal + * - oxpspaymorrow + * 2: + * - oepaypal + * - oxpspaymorrow + */ class MultiActivateCommand extends Command { @@ -28,7 +45,7 @@ protected function configure() $this ->setName('module:multiactivate') ->setDescription('Activate multiple modules') - ->addOption('shopId', NULL, InputOption::VALUE_REQUIRED, "The shop id.") + ->addOption('shopId', null, InputOption::VALUE_REQUIRED, "The shop id.") ->addOption('skipDeactivation', 's', InputOption::VALUE_NONE, "Skip deactivation of modules, only activate.") ->addOption('skipClear', 'c', InputOption::VALUE_NONE, "Skip cache clearing.") ->addArgument('module', InputArgument::REQUIRED, 'YAML module list filename'); @@ -37,26 +54,28 @@ protected function configure() /** * Executes the current command. * - * @param InputInterface $input An InputInterface instance + * @param InputInterface $input An InputInterface instance * @param OutputInterface $output An OutputInterface instance */ protected function execute(InputInterface $input, OutputInterface $output) { $activateShopId = $input->getOption('shopId'); - /** @var \Oxrun\Application $app */ + /* @var \Oxrun\Application $app */ $app = $this->getApplication(); $skipDeactivation = $input->getOption('skipDeactivation'); $skipClear = $input->getOption('skipClear'); + + // now try to ready specified YAML file $moduleYml = $input->getArgument('module'); $ymlFile = $app->getShopDir() . DIRECTORY_SEPARATOR . $moduleYml; - if (!file_exists($ymlFile)) { $output->writeLn("Yaml file '$ymlFile' not found!"); return; } - $moduleValues = Yaml::parse($ymlFile); + if ($moduleValues && is_array($moduleValues)) { + // use whitelist if (isset($moduleValues['whitelist'])) { foreach ($moduleValues['whitelist'] as $shopId => $moduleIds) { if ($activateShopId && $activateShopId != $shopId) { @@ -85,6 +104,40 @@ protected function execute(InputInterface $input, OutputInterface $output) $app->find('module:activate')->run($activateInput, $output); } } + } elseif (isset($moduleValues['blacklist'])) { + // use blacklist + /* @var \OxidEsales\Eshop\Core\Module\ModuleList $oxModuleList */ + $oxModuleList = oxNew(\OxidEsales\Eshop\Core\Module\ModuleList::class); + $oConfig = \oxRegistry::getConfig(); + $aModules = $oxModuleList->getModulesFromDir($oConfig->getModulesDir()); + foreach ($aModules as $moduleId => $aModuleData) { + foreach ($moduleValues['blacklist'] as $shopId => $moduleIds) { + if (in_array($moduleId, $moduleIds)) { + $output->writeLn("Module blacklisted: '$moduleId' - skipping!"); + continue 2; + } + // activate + if (!$skipDeactivation) { + $arguments = array( + 'command' => 'module:deactivate', + 'module' => $moduleId, + '--shopId' => $shopId, + ); + $deactivateInput = new ArrayInput($arguments); + $app->find('module:deactivate')->run($deactivateInput, $output); + if (!$skipClear) { + $app->find('cache:clear')->run(new ArgvInput([]), $output); + } + } + $arguments = array( + 'command' => 'module:activate', + 'module' => $moduleId, + '--shopId' => $shopId, + ); + $activateInput = new ArrayInput($arguments); + $app->find('module:activate')->run($activateInput, $output); + } + } } else { $output->writeLn("No modules to activate for subshop '$shopId'!"); } From ce70d56788ac4c70e21aa827af70c31cdd31880b Mon Sep 17 00:00:00 2001 From: Stefan Moises Date: Thu, 8 Mar 2018 12:33:43 +0100 Subject: [PATCH 14/30] catch Exceptions for De-/Activation of modules --- src/Oxrun/Command/Module/ActivateCommand.php | 24 ++++++++++++------- .../Command/Module/DeactivateCommand.php | 24 ++++++++++++------- 2 files changed, 32 insertions(+), 16 deletions(-) diff --git a/src/Oxrun/Command/Module/ActivateCommand.php b/src/Oxrun/Command/Module/ActivateCommand.php index 93e5ad2..95b86a6 100644 --- a/src/Oxrun/Command/Module/ActivateCommand.php +++ b/src/Oxrun/Command/Module/ActivateCommand.php @@ -78,10 +78,14 @@ protected function executeVersion490(InputInterface $input, OutputInterface $out } if (!$oModule->isActive()) { - if ($oModuleInstaller->activate($oModule) === true) { - $output->writeLn("Module $sModule activated for shopId $shopId."); - } else { - $output->writeLn("Module $sModule could not be activated for shopId $shopId."); + try { + if ($oModuleInstaller->activate($oModule) === true) { + $output->writeLn("Module $sModule activated for shopId $shopId."); + } else { + $output->writeLn("Module $sModule could not be activated for shopId $shopId."); + } + } catch (\Exception $ex) { + $output->writeLn("Exception actiating module: $sModule for shop $shopId: {$ex->getMessage()}"); } } else { $output->writeLn("Module $sModule already activated for shopId $shopId."); @@ -117,10 +121,14 @@ protected function executeVersion470(InputInterface $input, OutputInterface $out } if (!$oModule->isActive()) { - if ($oModule->activate() === true) { - $output->writeLn("Module $sModule activated for shopId $shopId."); - } else { - $output->writeLn("Module $sModule could not be activated for shopId $shopId."); + try { + if ($oModule->activate() === true) { + $output->writeLn("Module $sModule activated for shopId $shopId."); + } else { + $output->writeLn("Module $sModule could not be activated for shopId $shopId."); + } + } catch (\Exception $ex) { + $output->writeLn("Exception actiating module: $sModule for shop $shopId: {$ex->getMessage()}"); } } else { $output->writeLn("Module $sModule already activated for shopId $shopId."); diff --git a/src/Oxrun/Command/Module/DeactivateCommand.php b/src/Oxrun/Command/Module/DeactivateCommand.php index 006e86c..1dc65c5 100644 --- a/src/Oxrun/Command/Module/DeactivateCommand.php +++ b/src/Oxrun/Command/Module/DeactivateCommand.php @@ -76,10 +76,14 @@ protected function executeVersion490(InputInterface $input, OutputInterface $out if (!$oModule->isActive()) { $output->writeLn("Module $sModule already deactivated for shopId $shopId."); } else { - if ($oModuleInstaller->deactivate($oModule) === true) { - $output->writeLn("Module $sModule deactivated for shopId $shopId."); - } else { - $output->writeLn("Module $sModule already deactivated for shopId $shopId."); + try { + if ($oModuleInstaller->deactivate($oModule) === true) { + $output->writeLn("Module $sModule deactivated for shopId $shopId."); + } else { + $output->writeLn("Module $sModule already deactivated for shopId $shopId."); + } + } catch (\Exception $ex) { + $output->writeLn("Exception deactiating module: $sModule for shop $shopId: {$ex->getMessage()}"); } } @@ -116,10 +120,14 @@ protected function executeVersion470(InputInterface $input, OutputInterface $out if (!$oModule->isActive()) { $output->writeLn("Module $sModule already deactivated for shopId $shopId."); } else { - if ($oModule->deactivate() === true) { - $output->writeLn("Module $sModule deactivated for shopId $shopId."); - } else { - $output->writeLn("Module $sModule already deactivated for shopId $shopId."); + try { + if ($oModule->deactivate() === true) { + $output->writeLn("Module $sModule deactivated for shopId $shopId."); + } else { + $output->writeLn("Module $sModule already deactivated for shopId $shopId."); + } + } catch (\Exception $ex) { + $output->writeLn("Exception deactiating module: $sModule for shop $shopId: {$ex->getMessage()}"); } } } From 5c6e602cb5101b216af32ff056ca5ed3f82ada13 Mon Sep 17 00:00:00 2001 From: Stefan Moises Date: Fri, 9 Mar 2018 10:17:53 +0100 Subject: [PATCH 15/30] added config:multiset command for saving multiple config values from a yaml file --- README.md | 33 ++++++ malls.yml.dist | 28 +++++ src/Oxrun/Command/Config/MultiSetCommand.php | 107 +++++++++++++++++++ 3 files changed, 168 insertions(+) create mode 100644 malls.yml.dist create mode 100644 src/Oxrun/Command/Config/MultiSetCommand.php diff --git a/README.md b/README.md index 4b5061b..cca6ff2 100644 --- a/README.md +++ b/README.md @@ -139,6 +139,39 @@ config:set * Is value required: no * Description: +config:multiset +---------- + +* Description: Sets multiple config values for multiple subshops, defined in a yaml file +* Usage: `config:multiset configfile` + +### Arguments: + +**configfile:** + +The file containing the config values, see malls.yml.dist. The file path is relative to the shop root. + +```yaml +config: + 1: + blReverseProxyActive: + variableType: bool + variableValue: false + # simple string type + sMallShopURL: http://myshop.dev.local + sMallSSLShopURL: http://myshop.dev.local + myMultiVal: + variableType: aarr + variableValue: + - /foo/bar/ + - /bar/foo/ + # optional module id + moduleId: my_module + 2: + blReverseProxyActive: +... +``` + config:shop:get --------------- diff --git a/malls.yml.dist b/malls.yml.dist new file mode 100644 index 0000000..20651b4 --- /dev/null +++ b/malls.yml.dist @@ -0,0 +1,28 @@ +config: + 1: + blReverseProxyActive: + variableType: bool + variableValue: false + # simple string type + sMallShopURL: http://myshop.dev.local + sMallSSLShopURL: http://myshop.dev.local + myMultiVal: + variableType: aarr + variableValue: + - /foo/bar/ + - /bar/foo/ + # optional module id + moduleId: my_module + 2: + blReverseProxyActive: + variableType: bool + variableValue: false + sMallShopURL: http://myshop2.dev.local + sMallSSLShopURL: http://myshop2.dev.local + myMultiVal: + variableType: aarr + variableValue: + - /foo/bar/ + - /bar/foo/ + # optional module id + moduleId: my_module diff --git a/src/Oxrun/Command/Config/MultiSetCommand.php b/src/Oxrun/Command/Config/MultiSetCommand.php new file mode 100644 index 0000000..c4bf35f --- /dev/null +++ b/src/Oxrun/Command/Config/MultiSetCommand.php @@ -0,0 +1,107 @@ +setName('config:multiset') + ->setDescription('Sets multiple config values from yaml file') + ->addArgument('configfile', InputArgument::REQUIRED, 'The yaml file name, relative to shop base.'); + } + + /** + * Executes the current command. + * + * @param InputInterface $input An InputInterface instance + * @param OutputInterface $output An OutputInterface instance + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + // do not use the registry pattern (\oxRegistry::getConfig()) here, so we do not have any caches (breaks unit tests) + $oxConfig = oxNew('oxConfig'); + + /* @var \Oxrun\Application $app */ + $app = $this->getApplication(); + + // now try to ready specified YAML file + $mallYml = $input->getArgument('configfile'); + $ymlFile = $app->getShopDir() . DIRECTORY_SEPARATOR . $mallYml; + if (!file_exists($ymlFile)) { + $output->writeLn("Yaml file '$ymlFile' not found!"); + return; + } + $mallValues = Yaml::parse($ymlFile); + if ($mallValues && is_array($mallValues['config'])) { + $mallSettings = $mallValues['config']; + foreach ($mallSettings as $shopId => $configData) { + foreach ($configData as $configKey => $configValue) { + $moduleId = ''; + if (!is_array($configValue)) { + // assume simple string + $variableType = 'str'; + $variableValue = $configValue; + } else { + $variableType = $configValue['variableType']; + $variableValue = $configValue['variableValue']; + if (isset($configValue['moduleId'])) { + $moduleId = $configValue['moduleId']; + } + } + $oxConfig->saveShopConfVar( + $variableType, + $configKey, + $variableValue, + $shopId, + $moduleId + ); + $output->writeln("Config {$configKey} for shop {$shopId} set to " . print_r($variableValue, true) . ""); + } + } + } + } + + /** + * @return bool + */ + public function isEnabled() + { + return $this->getApplication()->bootstrapOxid(); + } + +} \ No newline at end of file From b1882a05ca6d89115c08203da9051a74b34078e3 Mon Sep 17 00:00:00 2001 From: Stefan Moises Date: Fri, 9 Mar 2018 12:03:57 +0100 Subject: [PATCH 16/30] refactored Datenbase to Database :) --- src/Oxrun/Application.php | 42 ++++++++++--------- src/Oxrun/Command/Custom/ListCommand.php | 2 +- src/Oxrun/Command/Module/ListCommand.php | 28 ++++++++----- ...eConnection.php => DatabaseConnection.php} | 14 +++---- 4 files changed, 47 insertions(+), 39 deletions(-) rename src/Oxrun/Helper/{DatenbaseConnection.php => DatabaseConnection.php} (93%) diff --git a/src/Oxrun/Application.php b/src/Oxrun/Application.php index 7f91db3..9dacd63 100644 --- a/src/Oxrun/Application.php +++ b/src/Oxrun/Application.php @@ -4,7 +4,7 @@ use Composer\Autoload\ClassLoader; use Oxrun\Command\Custom; -use Oxrun\Helper\DatenbaseConnection; +use Oxrun\Helper\DatabaseConnection; use Symfony\Component\Console\Application as BaseApplication; use Symfony\Component\Console\Command\HelpCommand; use Symfony\Component\Console\Input\ArgvInput; @@ -44,9 +44,9 @@ class Application extends BaseApplication protected $oxidConfigContent; /** - * @var DatenbaseConnection + * @var databaseConnection */ - protected $datenbaseConnection = null; + protected $databaseConnection = null; /** * @var string @@ -54,9 +54,9 @@ class Application extends BaseApplication protected $oxid_version = "0.0.0"; /** - * @param ClassLoader $autoloader - * @param string $name - * @param string $version + * @param ClassLoader $autoloader The composer autoloader + * @param string $name + * @param string $version */ public function __construct($autoloader = null, $name = 'UNKNOWN', $version = 'UNKNOWN') { @@ -116,11 +116,12 @@ public function bootstrapOxid($blNeedDBConnection = true) * * @return bool */ - protected function findBootstrapFile() { + protected function findBootstrapFile() + { $input = new ArgvInput(); - if($input->getParameterOption('--shopDir')) { + if ($input->getParameterOption('--shopDir')) { $oxBootstrap = $input->getParameterOption('--shopDir'). '/bootstrap.php'; - if( $this->checkBootstrapOxidInclude( $oxBootstrap ) === true ) { + if ($this->checkBootstrapOxidInclude($oxBootstrap) === true ) { return true; } return false; @@ -130,7 +131,7 @@ protected function findBootstrapFile() { $currentWorkingDirectory = getcwd(); do { $oxBootstrap = $currentWorkingDirectory . '/bootstrap.php'; - if( $this->checkBootstrapOxidInclude( $oxBootstrap ) === true ) { + if ($this->checkBootstrapOxidInclude($oxBootstrap) === true ) { return true; break; } @@ -143,7 +144,8 @@ protected function findBootstrapFile() { * Check if bootstrap file exists * * @param String $oxBootstrap Path to oxid bootstrap.php - * @param bool $skipViews Add 'blSkipViewUsage' to OXIDs config. + * @param bool $skipViews Add 'blSkipViewUsage' to OXIDs config. + * * @return bool */ public function checkBootstrapOxidInclude($oxBootstrap) @@ -153,7 +155,7 @@ public function checkBootstrapOxidInclude($oxBootstrap) if (strpos(file_get_contents($oxBootstrap), 'OX_BASE_PATH') !== false) { $this->shopDir = dirname($oxBootstrap); - require_once $oxBootstrap; + include_once $oxBootstrap; // If we've an autoloader we must re-register it to avoid conflicts with a composer autoloader from shop if (null !== $this->autoloader) { @@ -208,29 +210,29 @@ public function canConnectToDB() if ($this->shopDir && file_exists($configfile)) { $oxConfigFile = new \OxConfigFile($configfile); - $datenbaseConnection = $this->getDatenbaseConnection(); - $datenbaseConnection + $databaseConnection = $this->getDatabaseConnection(); + $databaseConnection ->setHost($oxConfigFile->getVar('dbHost')) ->setUser($oxConfigFile->getVar('dbUser')) ->setPass($oxConfigFile->getVar('dbPwd')) ->setDatabase($oxConfigFile->getVar('dbName')); - return $this->hasDBConnection = $datenbaseConnection->canConnectToMysql(); + return $this->hasDBConnection = $databaseConnection->canConnectToMysql(); } return $this->hasDBConnection = false; } /** - * @return DatenbaseConnection + * @return DatabaseConnection */ - public function getDatenbaseConnection() + public function getDatabaseConnection() { - if ($this->datenbaseConnection === null) { - $this->datenbaseConnection = new DatenbaseConnection(); + if ($this->databaseConnection === null) { + $this->databaseConnection = new DatabaseConnection(); } - return $this->datenbaseConnection; + return $this->databaseConnection; } /** diff --git a/src/Oxrun/Command/Custom/ListCommand.php b/src/Oxrun/Command/Custom/ListCommand.php index 003443d..6ddb6b9 100644 --- a/src/Oxrun/Command/Custom/ListCommand.php +++ b/src/Oxrun/Command/Custom/ListCommand.php @@ -28,7 +28,7 @@ protected function checkDatabase(Application $application, OutputInterface $outp if ($application->bootstrapOxid(false) && $application->canConnectToDB() == false) { $output->writeln(''); $output->writeln(' Can\'t connect to Database. Most of the Commands are disabled '); - $output->writeln(' Error Message: ' . $application->getDatenbaseConnection()->getLastErrorMsg() . ''); + $output->writeln(' Error Message: ' . $application->getDatabaseConnection()->getLastErrorMsg() . ''); } } } \ No newline at end of file diff --git a/src/Oxrun/Command/Module/ListCommand.php b/src/Oxrun/Command/Module/ListCommand.php index 1c68710..399b82f 100644 --- a/src/Oxrun/Command/Module/ListCommand.php +++ b/src/Oxrun/Command/Module/ListCommand.php @@ -48,23 +48,29 @@ protected function execute(InputInterface $input, OutputInterface $output) $activeModules = array_keys($oxModuleList->getActiveModuleInfo()); $deactiveModules = $oxModuleList->getDisabledModules();; - $activeModules = array_map(function ($item) { - // check if really active - $oModule = oxNew('oxModule'); - if ($oModule->load($item) && $oModule->isActive()) { - return array($item, 'yes'); - } - return array($item, 'no'); - }, $activeModules); + $activeModules = array_map( + function ($item) { + // check if really active + $oModule = oxNew('oxModule'); + if ($oModule->load($item) && $oModule->isActive()) { + return array($item, 'yes'); + } + return array($item, 'no'); + }, + $activeModules + ); // Fix for older oxid version < 4.9.0 if (!is_array($deactiveModules)) { $deactiveModules = array(); } - $deactiveModules = array_map(function ($item) { - return array($item, 'no'); - }, $deactiveModules); + $deactiveModules = array_map( + function ($item) { + return array($item, 'no'); + }, + $deactiveModules + ); $table = new Table($output); $table diff --git a/src/Oxrun/Helper/DatenbaseConnection.php b/src/Oxrun/Helper/DatabaseConnection.php similarity index 93% rename from src/Oxrun/Helper/DatenbaseConnection.php rename to src/Oxrun/Helper/DatabaseConnection.php index 49b9810..4cd1a42 100644 --- a/src/Oxrun/Helper/DatenbaseConnection.php +++ b/src/Oxrun/Helper/DatabaseConnection.php @@ -8,13 +8,13 @@ namespace Oxrun\Helper; /** - * Class DatenbaseConnection + * Class DatabaseConnection * * Connect direct to Database * * @package Oxrun\Helper */ -class DatenbaseConnection +class DatabaseConnection { /** @@ -54,7 +54,7 @@ class DatenbaseConnection /** * @param string $host - * @return DatenbaseConnection + * @return DatabaseConnection */ public function setHost($host) { @@ -78,7 +78,7 @@ public function getHost() /** * @param int $port - * @return DatenbaseConnection + * @return DatabaseConnection */ public function setPort($port) { @@ -96,7 +96,7 @@ public function getPort() /** * @param string $user - * @return DatenbaseConnection + * @return DatabaseConnection */ public function setUser($user) { @@ -106,7 +106,7 @@ public function setUser($user) /** * @param string $pass - * @return DatenbaseConnection + * @return DatabaseConnection */ public function setPass($pass) { @@ -116,7 +116,7 @@ public function setPass($pass) /** * @param string $database - * @return DatenbaseConnection + * @return DatabaseConnection */ public function setDatabase($database) { From 0680094dfe84e65777a12f625fa8048028329634 Mon Sep 17 00:00:00 2001 From: Stefan Moises Date: Fri, 9 Mar 2018 12:10:08 +0100 Subject: [PATCH 17/30] refactored Datenbase to Database :) --- ...seConnectionTest.php => DatabaseConnectionTest.php} | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) rename tests/Oxrun/Helper/{DatenbaseConnectionTest.php => DatabaseConnectionTest.php} (89%) diff --git a/tests/Oxrun/Helper/DatenbaseConnectionTest.php b/tests/Oxrun/Helper/DatabaseConnectionTest.php similarity index 89% rename from tests/Oxrun/Helper/DatenbaseConnectionTest.php rename to tests/Oxrun/Helper/DatabaseConnectionTest.php index 7fc7fff..9e7a03c 100644 --- a/tests/Oxrun/Helper/DatenbaseConnectionTest.php +++ b/tests/Oxrun/Helper/DatabaseConnectionTest.php @@ -8,11 +8,11 @@ namespace Oxrun\Helper; -class DatenbaseConnectionTest extends \PHPUnit_Framework_TestCase +class DatabaseConnectionTest extends \PHPUnit_Framework_TestCase { /** - * @var DatenbaseConnection + * @var DatabaseConnection */ protected $testSubject; @@ -21,8 +21,8 @@ class DatenbaseConnectionTest extends \PHPUnit_Framework_TestCase */ protected function setUp() { - $datenbaseConnection = new DatenbaseConnection(); - $datenbaseConnection + $databaseConnection = new DatabaseConnection(); + $databaseConnection // Must be right to work correct ->setHost('127.0.0.1') ->setPort('3306') @@ -30,7 +30,7 @@ protected function setUp() ->setPass('') ->setDatabase('oxid'); - $this->testSubject = $datenbaseConnection; + $this->testSubject = $databaseConnection; } public function testCanParseHostPort() From 14d94f89c87eebffe78f6a0bd39f086b65d777fd Mon Sep 17 00:00:00 2001 From: Stefan Moises Date: Fri, 9 Mar 2018 22:03:54 +0100 Subject: [PATCH 18/30] Generated new phar, README updated with usage instructions. --- README.md | 25 ++++++++++++++++++++++++- oxrun.phar | Bin 1883165 -> 1913233 bytes 2 files changed, 24 insertions(+), 1 deletion(-) mode change 100755 => 100644 oxrun.phar diff --git a/README.md b/README.md index cca6ff2..76761fc 100644 --- a/README.md +++ b/README.md @@ -21,10 +21,33 @@ You can then use oxrun by calling `vendor/bin/oxrun` or add `vendor/bin` to your # Usage -To use oxrun just execute `php oxrun.phar` or `oxrun` (see above). +To use oxrun just execute `php oxrun.phar` or `./vendor/bin/oxrun` (see above). Execute oxrun inside your OXID eShop base directory (or subdirectory) if you want to interact with an existing shop. It will automatically try to find the oxid boostrap.php and load it. +If you want to __run it from a different directory__, you have to add the option `"--shopDir=/path/to/your/shop"`. + +You can use it e.g. to help you with an OXID 6 installation or deployment, e.g.: + +```json + "scripts": { + "post-update-cmd": [ + "Incenteev\\ParameterHandler\\ScriptHandler::buildParameters", + "@oe:ide-helper:generate", + "@oxrun:activate-modules", + "@oxrun:set-config" + ], + "oxrun:activate-modules": [ + "./vendor/bin/oxrun module:multiactivate configs/modules.yml -c --shopDir=./source" + ], + "oxrun:set-config": [ + "./vendor/bin/oxrun config:multiset configs/malls.yml --shopDir=./source" + ], + +``` + +This could activate some modules in different subshops and set a bunch of config variables e.g. + # Available commands cache:clear diff --git a/oxrun.phar b/oxrun.phar old mode 100755 new mode 100644 index 12bd586672f20d77c67aee90f6671fa338ccd3bb..0a8be9bb4cf8260a978b199d6dff6e3c684a56ed GIT binary patch delta 61414 zcma%kXFwE5(=fAyUDz-SyX1_bf*?TzF@Xvuf}o;;l0+0F3K+ngBZ{=zW>N3V0X=oh zqTZPU>YX`Y&iTygtL~m%@ZKNK_x!lVuIkEFUEMRY&(co?bM6(D6&a7o;0zkgDfUty zG&C?6|6yeU84Gtzx{f}X)Kid&X$A%c(M2|sQOf-|BQk+@-w#5w@5@k!`znfY7%-}Q z&Wu2Z?uXNfQi*}V=zg8wq0{$*=)_MW1B2h%I84NI+&K-he_@TM8go_@WGOW;SX?n{ z1X}wtgibhe1_lj0zauo~ttT4yz?M#CY7Gp2k4ey=6|a2Dzu8z4ISv8akE?tAwi6bR z|205cU%ERs$64x%jB<%eif%$6z3jqV4#j2JQ>~5Xi%TmPH4<48S;9?th60; z>_0Xw8{HYKK{Y?DQT_)hATXd8sEH5$*aYInFb}z zh2CL-gAiJh9F#b27E5pT`d`e|=NQPxK}U0VrieS16aPdqpTeE0236Nb{{ zYbe?yq7Wi^?{k^}`R3N$@^k*nV7l?`eFTPbzcQK^V8H~}r+#!nyApr0;Jhdi8Q;$h*7TDXd3o!G9UeofpMmy@N^!EJ376#@%ZbGpG=wKggL1r5_^LA2&XP zBCoehG@{F%!x2i#q8npzS0qEmE7yv(6c(1=kFX$KCx#?r*y+>{)R4X(L_@au5NaJh zJpI3Nj;^17mlA28N{O5i8IbYR;A3v+{Kw`t-7^?>;yl8EjNd*P?WYUdW#;~uXz|(+ z6~Mh6_1Y;wPWU%qGjjdZo=yHSgzy#?lLNT`~1v2!lJJ5Z^q>v0$`XvBy;93{7HBs2QiR{KmmQM z;vmia(D^qk=~lrc-=zIHSn7uwz0okAg}jNQbl!s~XVz9$!;(%6^5RvvRd4G4Be0TK zj*Pd{IzIX(drrTWp(d|c1tSQ3=9;WPk{$@NuW#izpth4q(XvHdAInhj z!v+*61K>VcfCH4p2B_?r3SEEBipO^lDB1hYda*{dq}bswIjZy7&M4JGM@o+7KVpSr z0{};!54(=qHq!wc(^aHU{Q1g04()r?hnBpNkm-}F`yAI*kgibB>Va66$mFRLUGf(! zdH=Y|5%qnNNS6@Ro*sz1pj*;IzvS5Cddy%iANQkc4+83r!8<%r+f1Dtay0O%C&QZe zbTGvtKI&Wg)ej8ri7Fo}9m9vGrRNuA=FxyW4!RJNlX0Q~A4P5LHR!;@wsh%pSn8YC zC_uEC51rfsn3q>(Ekj$|Xi&`a2s%lm{xZjWJ394j0G*f#nsYLnu4qNR23HwDv95e< zhJHSiqNr!)6yr3|DN4yP7oF^cUvivEOJcAs>^?*$GCsayAwh#_rVeCE+}*GV=+$ zK_WXcj{7!y4}s4+#WliRB^;|U&HyRCYYnBIR&y$08FKbiL;RC<_h`!C2u%2ClLm_n0_fyd&^o5)p(WyE1GM2M%b_H|4nOZI)7jAp zNSmR=A8NX6K0uQX*Q_JPKrO#FKtFyeX<{N6WA+5Fo)oMvG-BQB*;Z zg(+@p#Azsm+nUfoa^E5UU34Wz_hvz>pUy=5(#DZi9;W`A^?TM4by=nSm>^v3`el46__)%xLo{ zi*7E_r=n@(orB3V)2Pq7$csc;EhLnUK@#FyGWH0sUn7d@X`5P#q}3)$!Pod$7Z@W$&YxTRmYjHCjubrLudmchbTt= z!n$K#Q65!~n4MUFj7J@~d25aDFp zHQiRw2j`O0ETINMZ0$bSv{_yDXvz`hbP_5R7uqk=uNr+=YDwvi6X}xinW^G8Jx0WF zwVVia4J^7Ytqs~5tc8N&fd(C9*%bf_PCf0ar5kAWQMRk*fe@))TULpN^+eT&*!GC- z9124Lf0?s*?)+C6GCRqRulYcyL+s`8dd5@#R5Pq4f3iw75s2>mC!|2+rxxe~vZ{&&EH80@vcwX(;tV@S?gc>1z76m7;q&;Y1>^bSXc)?>VzES` zWZV25&xk9~jU!Cl5}4fkIMZ4eI#3*pQN(Fe8Uoz_xV}SbOr5G}jGpagKDZ8QxZlg0 z0*bamH7jLk-#S)OieT04yGPAM&v_u#HnzeW0EzRFT2#yfIqhI+8VGTkD$_ETGJdx3Z8ShpQ)-#w?``d(3hVbbbeG z$tD3{t-Nyt#cm@oY)>*-)XQ7c91?T86yY>v-%R~G<%KT`bcky>J7{i=}-v5Eu3f3 zG$od(T5pV+Y-Fo40QlBDb_En9Vhv+dvVq0!KmZH~8L_xpf0I)8u zE^4M1ose+ZAO^JGhR$|v3kp(;X&Mn+k}YK1F}eFPh#3gE?Iw14g+*cMbc^-?-3SU9 zAB-L~QcrfqPG%850Q_dDazw2cw?bu`*sf3VvN&>B5yehfqd=fmVc5_Xs%fKfr!;bPtQK!mEpVl%6*w5uN?t|w#3+k9tMewTzWwWF6rp~|JTgr;QJ zmFYNGM-9pTuLM7&XVqZl?$lNVKyut)e`f!}*>stfk#=1cg`5{NI}zpH7JC@~qB+Qj zrY&c@2}1kdoio8^%n$i6Q6+!*fbP-PEBw)umCT5R0I0vwu#L#n9}W49MKn?HSSQDK zdO)9b3_xgmJb2$APVS@&aE-F?Up$(qlj35P#{=j zjIPXFpl6|6QOo#!6P61_MC-d3z|zUL8*jIUFV~ zlwN9rwyf<#CrV)AX9xdrqBu>FYK4h)+g#cb)D+s$RkB5Q)5n zqmIb*lAc>rD{1f6@1Bc>3r5iuwYr<8!R8@f9ggx=uu?!I4cXatG)%@(yBER4h{Fxz z#pShr+;wnne|?EgSth4VRo%O-q350N=xP_r{DX-}D%R&~N?pt7fgbE+Vv=&5;Dr0r z6%`AO?R%sb<`$@BI(3f zn7C43*+HzSa5=P|=|gJj?C73T_1r@@%dB%*V@O0L`j9c{yZcVbRoYpXrmHRYXkL@DHYd-)OEPQiZ$Etj*3EI7t zg)C{4yY&1#N?*&Dh%LYX0K5)vA%UwA4RYA!OIc(Bq57vMY!}at{&cbw7N)_J?gxcY+mzP}pu(?d<@w^?MI9no%^OCIsI5An*d!BF(DbJ9}MgK3tso4 zg3R23bi=&^=SPA^#u>*@JIK-?pkmz71` zUEz)ZE-Pb}6FJQ7eQHlETL4bzlkv61!bEy`+VVg%G;$x?3$_EGS%)^ZkaU!>txsfT zF1;Zqq{&h;&h2$_e_a+xc|hzkTn_YR5{*320lnYDc7<@z`qGq*Kd4vB_AAihJuJn; ziC0g44A$FIU1dZc$`H>IW@L;`6pVC7P=7RTQ4W}2Z-Lv1 zWmjG+!K40g_-B{6ks!fl=c5zJQ645`$f=wKg&Z9B;`w)WVU~TATo=gD*tskkufeM5 zxY7XKsyau%4AD_!+<#ekDRPsck%$@c7922cS)RKjMmZ(y5L1wspI%TzE6EV?B9e?R z(-S*K>qqv*z*wkUr-3ydcq zY}iFVU(|IDyAzuNYrFn5*^TFz!2L;SnHv@AF<2p^<>YPX+q57$u?j?Aq^a%&QaT}Z znF5*3a;AV3Sl`jw*9n~-NdXQWav{6YQ)!9v5s&L+T(+3E;HCw&P@oQD<9^!|Am-S%#fV?TE>^)9+5;d z8OvJFa%Q(feexOUIXix=`c>pVrZbUegSx*d`iVDlqi1+iOG!YH?f=g0&+hAzO4)1*@LY0g zg8MHLmd{`UZiiTJeK_(Pz2($Ne{Qx39%iLtcln(}MadW#y=x0NtO2qqGpDDZmoWKy z_-h<$&1sQtdFFIR`#5B6u(iDfdXdePJygmvzyemeCA!_xySmLx=IYrX>WZa8Gk9oY zi>hZZOAG-2k2grVfV?KKNI47M3{1Fn0j^%1Q1WDJbYn8JN*}=dZS$(nkS7|ncZxO2 zg2zbpCyPh<;K}Tv%M^ljbgM1po;=wKHJ!q?2y(?*P};mP1i2G(hKuDHQ<=OC!LDuY zPFkS{Or6F6xv(PlMH4CHAv`4RVuJFgIiVKQSUSG|;NZaD=hXq)ph;7#SuL%Y5{#}- zb)bGDcSMoClLMe!MA3-|(C}Mo&r+BO#arenjnRuK>~u_S7#_dcA4G*GGG3X+Zqhyj z4GZVBOvdEN3Uq6t6uq9n$%atwY*Iei9AslJ8JLoQ z{haQIZj2_LRG@Q{SjBTTH!$cuerYZM+);HlGbni~@Ug5aOP7`&%#sP?S=Sf^8Iw0> z!fU7#EvOYcO@d1arZg7P`A{KN6jcCf#Api`qv(caO_`{asz%$YAax-_u@ zD1!uof&Mr>phI1Vsk#H)!;$Xs3SWncO|R1=W}U)FoETa-Jou?YwIPH)^Fp;W~C!X!%Gr zt^Yqjb)p;@mBSt{Lw8F2&>|15dOj#f0A$?R5Upe-XY>p?iYd{c-6Pmu@XZX$gXOV9 z`V=5`AR?p`i)k>TI2i3LVS>sa`*&SdnbBIbc!U!gID$FA4a~72(ylS=yG*_1BiWPR zS+Jr}!eww~NoQ)J3=k)B(DZ?5{rCVnF%NF_ugJE9Kn%2|6X8(sR{g9_uPd+|CFhu+ zwK-y;*q_6^&=|tI_xaTiAqYKe-JPtb3xA;fyvZ>E%OqT@;OI zUOwCuB@JWc9v%xBYadBc zvWOmp%%S0|*!>2G7q+i2L!7%_r0AjUlr`DoI(x(&*0GijW4=EOHXjxpI}S|igCdKV z->(6ouKzYWCF*U3A`4j)K@`~Ud36h5vyx~Hn6JE)8)@iN4VI#B1&jo#2``g(?gV}j zRDdqvjm4jrk(=v|bV30WkE`x=p`3>fX3KiRvR+eCoj|onie&&Xe!TOrb9zyp7co&% z0dwcRfVFh_5>fD{K(qd%F6W@pa)LS`M<9}YZvXJ1>A5skmcSxnY%(^waA~U;x>l&s zV7B4Z2Ysw29eQ0G@kFUD1uWuE!m?|H^~b}m$;jsqHbE}=X87|$xdN5uvmMkOXnc;m zQ6_dg4q!bqw6Tz#4h{l|3#THCbfw+_sSBj&%wV>(0?L`~+J`Qnz7P7hkagExAPbAe z{{1JkCyZ-y4(r9oTN@boTx@t5Z5q>(K6@*Iit+Ui=?&e%ry&|FbwH=2?3wpTkn6-1 zzO4=l_l8n<{3~Ocp8;YuBkcY~Po#d-^5g}-%R-~`EA&0-2^;>{*&HYadMHT<#Pze_Sy-SS;_D9!lu{TN_VN<_y zHCTay{~km!wt)_j^WWS?+_k=F?QK>z_d^+IxJ)?>ZTUNw)hpM33fUhE{z*fxFBzem z*O`S+0L!sEH=agwE+?aXw^*;A0tG(4Y2P^X@E&)Fq0Pc?V(LNmV#h?s2Dj{SYZ@Ej7{q* zKcO+#hEWd;1dfN_HL}!s0N$*-($gU(wIDS|WMV8bA>(>s=omfC6F0>}_l=uFDAxp_ z^|ho+1eJdHC2@-?y)>LcNqL-CoU$G)SU@L|VIsE>7gN31LphPi5L8wyOHY;JykQj!;)j= zcL>Ol;wUQse`@dyo@fT@^&-W8I;b|%^n+6~Ltl|o( zjC08Q3EHb8bTanBa`)Eo;ta*lrgY$nFZ z7F;i^)^aRnA44J>JF_GOzc7dCWg3pBoSY##a7uHPj#CNa6l~5lrG|V8%cd;&HRmrn#oT|ctyE#{QmNIH`SGmSJSBk%^Th?5Sq?%S-H zObn61#8WD`460fwI{GrRms?U^466^v5q)TPF~@LM z=6s>)B0?ZXc?MldF1)>E!%9%1=|INqxM>{PZR?<3A?70)cSe0SMAfED(Gf6_G`!?5 zouWQy^j#OExXa4pU?6C^XnrH~{VofFk1$a((9x6Lx0T)VrEIr@(>i(Wdx4tVWjVJ1 zu;yA0JSJkPk#NU_Vv>U4_3&XEwvTkZ$J(yxF#Bcng+w&uZVM*%A9veOd?SdtDxc!b zsOueey>kb|ox97a5Lw^xq8Q{__t4{>Gq7`4&K~DC0tc7`ajb#31(~Mpsr*6qS1ndH zhqP8V;t2_Z6ir5-=SbgOE{X(^v) zkW2(j#)~QAhTt*nIVYx>Z!^$;Q-GN0<&Y$PvGO0JhjzmNV~=Ib!DQDSZ6Wu08ob4u z;|WNK|86E`C2>bnm&b-Ur9&OMN<6KZSaV{5@ude>dy>YeF5{E{@wK-QK(Jcr`1h}7 z@@GD>O)i(z)LX#nZM~M>NI8HbHHz?6R z<~Mix=3S_0WD9)NPgIgPD)?*8V4ae7c#@x(m+v4fzWxZU{I7H^{Y2?j@gE!wAf>*}+i+&7)wXz%Sulz?>t(U0l1km-uH>*9h zx;i%$b=?e=@6N>O@5xsOcyVJ0gVPPft#T|-E&Z5v42}{SJUW<@6Ry^{StCwMZ`-Ot zQ>%qJctt}l5Wn=`SOZF4QI>ys@az{}GCaMJ=&rHA(9bt;BA(iq>rOR4O{}@*p?tmv zxYeS8D435OoL8ssSCQK@yrs2{ds<^L2uA|YV( zE;kfKB-fzXfq_GTVGNZZ1?ic&i8O&AAQ4rHf(dkX55L z&~2TXE7Fr=ho*->`t)Srkl=N+7e3I0i)Yf=`*3~OUnM?VZ@M#)Tfn{cc&V;nN8(s_ zXli17ICFgbv6U4q5&J;^5{G0=>>j+1>eI)avm)Lw*z zM@4rleSY)!lt0IAw7S4%6t@287}|R|cV>Qr3gR!mfjk#Db{>1?FB)(gytr1B;!b!4 zwMn<5hko%kkF&hQo~R9UF}7Dmm((daS%DmTKN!ypW})?1RBd#}rLO303lZ$)uzNBRjgdKkPNh`+@^u{ak2Zs;1#8B;fG2WrF( zWc;n(_H143$Qa&)2r;&-p%*|S*N0>CUufV>QDVS2L%{S=`tR4VcEW)<#v00F8W@G} zAY=5c>FaeUw)kB%$9CO)V3!?xj(d}8B#O8uTJ)z0`18(<>J9j8B$r5i#R0FXn|Tgc z6~VDvtPoh===!*ux?H2;v)l1+a8i6y+^vPKh)|F<-Vn)I(`Y>aOMQYp^q(h0;FJii z9qN9R_=s--Am64Sv-NqJ2zPjy;>8r<+p`CKEHWOE>b8o#EakXUU^8e5xzQ z8ZHtR+dT8Pk$sn`Np2qE8p}tL-J0ddIS7V?qVDjfy>64Q=>~hRPj`^|@9yG0NS*}6 zeS7_qgta}^A&6le(_M^0IbhjtbNGz=b{4VhI%MYdXU~1@Tt(x+7Gm)Skj_J!8 z;{|;=c8{fD_dyI{>>U*h`aXU*g2aV0$*Q_@4|usWa9Cv zZk!b@`GL^6M4vi}&znHML^~G>-v!$st3sD!xh^zFf`H{TyPe%ho>0x$*~SLa%=`4T zfpv)Y0^)Q@Hz3}}b?^b)Hdb_HAPDzix$JSRD=QK>H8zhItHL+1vF651bLyRl1kMIm zCWxuN89+v7rW@$IY=i&k!?8O9a@N>BaA%*|kfc;Imby%i*0*79h;S@i5yu z$s?LP3C2r$aqL1j7-rkKl+*X?FqI&ok0=3oWjCR4)7#pdrfjzN=GX^BYrxLaHn+wk z*c@?XJg35@eZ{yq1hD55W^^Q`aKkC_oB~bv<<&XBjU)mYe>^wXP+P|+>BQb*K3{$QmNC!?zK@$F-`l3C9NFbdq0{KumFvT<4kd4 zPxz00PqFZjdx1_#o%m)^ybdPjmyN>;Fbdo2XTKWcMPQrD>6ylmJ^?J)%%h!am}UiOpb<6>HY~!y;duCdTR$ARCe8-Gema_86u$_(nf5KbJzN z#O|2lMaa5izMshLJ8-*gZ<<7M(;mN0hrAt~&Z%hKoEr!aAvzUQ>UMc&yds^mwhu2% zONHnEo)i)#MCIe7FMrw zZnpTA#`*?~E*YDtS7LSpK0pgaM%*)sJ-5yG$a^vz7sLu)y*pk>(^Y zSIB~bm%3U8^?|^X6UD6W1@MLyKiBF~q&cP704P>Qb=j?}75;RVv=yfsE*?y4@SapC z&oN1mv%8YSKHUWDHFw$96TiUC@U}#;R*?(5Qw@DwNdnmuIb_%}QFQx9&`(o;^BZ=) zr9L{9D)#s1Ak9W0%dO<{1Os1d9G%Lsi}CZ3aI`B9d_ww!dU$nn(aL*))6hG<14x$H zW0z3i)Fnuywl&tkV8H>mbmD<}_<6Y4D@aV?q$FmZ=zV*}4+zjv8OmasP31H!y~m*vztDiVII4+J?$ zLQaEm0N?l{QgDHuGw=;tV1Lb6mbs1Mh=rN0l=9RlR|bz2bcYzMJBJqe-@ z?s2L+6Hf<|;x-+`cxw+ae6aI~W8}P04{z=$ZnUp}+m)8?Pnn-!T1f?sgK9_wC1d;Y zQT9a85E^UuK)v`r{=G?Qtw;Dvu}4R7BO>=yJL6NW>zJA%NIHpGm;!+)caKkCi=(@6 zCiqZi(fzIvOwpSggplm5hXKYKdLACvwkkA1xTdoxK@@Pg5T4+}3K4a7cqg$0bO6KV z#!eR*JnAGSfFVq$7ykRT)_S3xpr=~eNj&Ht2P;f{_Xg`WJvBbqnscI-*el-Ok+H9x zS%9v;79BwF@R-)3@5nb4E?pPd(M0Rh1`-kAw)k`_5grTh&)4<@>wGK1N465P<_84X z1Gg$W-4TpM_ffE#bU%7syJ{A>2`FzNy0()8JoG^OQ)^o^Y7~m}zPae=qtNxe+SBAN zt3|a4cUy?5xdIHmb$wEd&IWckb$wWOv|14$AjWK8fXHu{fzFM~z- zqM;jc`FnSlTIx5%q-iy_nsz@n=69eG{B-xVz@+@mXi{@d>A`~_%l(VFVfUzK^y!BSR zB8*%skY(rEiPk11c4KHo9%15%%|b*~n?n$F=`EKNx!H-a8cIq(qtU%}?HcSFgpf-c z(ezD0+U~_qv0gSMhBw#Q?>6A?A>tvUD};0=chyB7APSt=RutuoExaEvXV)T9T$)q4 zH-I|_OfbYwT{#Er+fKYJHEh5FiHs``Et^t@mO0R3H(fw0@4{K^+DWHzK^w92FoHPP znsD%(P7z1!)Jl|~7qOwoSMnkPd~V*EBxFmtMs3=XV_!(%h?X3?>R$nv{ld=&>WHwb zbn0ZAl!H5k@g;F^OHOLbqsBw0nqt&t7kdHE|XR>)vK?`I{Z?fWoSzbQ15>M1Op=xa7XL;Z#7dU(I){rRK;|(p;LObEt%odF(V ztJ|VUdhSX25tf9KT7;9*SE9^KUTCh&Tva{Qh{AjTmX)9WTe4E`}m2E}iV6Qzi zla!(3c8ONq9^@ws$>sc4!fkh0yFz3&NllG#SJzhMGhh{H~H;xopiR? zWt5?%S}O`q#>cO3z&qAVcw0QY5`rlStVMJqV}mHGP`a_y%Hw4(wlQQ+J!xiph!~q~ zI^@D5%z9oLk?p|*P%pf@G`p#XvBz(nV99Og zQz>|@y?7^h6wLEBrsoryjG!Pt-w7TfZP_W2$^HV%5*y&Y4qOvEc;;Tn4rl#=Hkl`5 zW6M`&ILDDor(DTT8)%*e&7!vtgc#1BXb4Y;1c`%e3b3Zs-a2r36#EXSZEX0-g9zhB z*(U9jn5rLwLKV+SX$0iNz4?iJ5rQ#?J-0=@!<-dPk_P3aYZyqS6R3rH2z z;u=@Zi=H+kL8#%0T?gyVdhM~R8x;5_Zep&@1Hq^u!hF$=Ll8UiMy$5S--3 zMN&#pfO*--a-EJ+1H9E9IQiGpT}^bYCq59_3Ov&nSbxNQojCT?4=-|pCj>rwBy#m` z*dK_3WPEE?k0aGYi+?ylN}h8PpI5gB0`;}=xjI%28LO_&x+{SkXEAuj!~Xy7)J0pc zhb!u+VD4`Nw%a+{!x0WauIRO#y_9v%4PsTfioBR)i+t5G>8t8nzkyEw_D$Zh;c!iMIF5P#@;Po{xRGp zQsD-*4Bl(YwnH9SjPnoRJZqy{96ai_9q1)G@E&aSuMJFi* zu4~bV+MV1jCC^w%K4qz0`CeT48q9q3!p=+-ovd5QqsuMXTOMrzIOV`4Z}?8FDJ9vU z4T}gbAo=}zmwNEHA&K(%0GRe+jrRP)qkRh|-37>U#n`bQ;f2~-9wS#?| zu$z4H;#|M6fhE)|M31p;*wd*`K);~6>3cNNR!2gH_P1uwMK%JkWwYO3>c#BSnu)0f zvnM(M9t`rqK4+nq+A!W@*wqS{k^0fSXS*loAQw+(oigt0B~va`+DL z>B$0ZL_`c#pZsFnqny%8J-v)5X2C!R0K@0y>uHPE#dhTa&tV!Y{H^wPVi92SF#zn9)M?EbF}$uwq4( z+<}^is71)N4`UU9yc0OyY{_tV`d2G?N;@fP+@3vSlR*7$r#=4LFRLfEXRFE6&6noe z|M|~q=1?h^^WkzAU%fdi+A?z<0x8ExmK@RRRvgN7O9FC#WWRfie5e`SP+(_vN%#Vs z+2e5Y;pBZ9_+7XJC`p~zIpzo0sYOyGk77Erw|kbugrVo~OvqxQkR|HXiABnL;M%A7 z@~WFZs2Bf&Ctr)Ec7d-T#6T+z7T+6QAIvb80F8Mc!(Bw;Qmn8x zm7#YXnO-d*3Y@d02Wb^_X={&_)}~UlsG|j?z75vC_#SAYkMEc+OzUOPAkGW+JfdeC z(?wR^g$YdJc;jP=I9VT6ir5zcyVa9Sds-#0hkK*T9hm*% zAOtNgJ)zXH zoCX^e3zw&294!FMZOE`2ErnylSo_igwq%RE1_x;DsGG>q=_c%k96fE)htlf>CL3UP zD@4T5qTx-L;^Tl($){qOC@~avSboe~dk5l9lRZFi#o?GG4lQ-jKS7 zA+StI2bN{4fB#91R~f*JcNuhRxh~}cjWu<1w)MNh>muU@$45fyc!OXbRN{4j>GD+3 zif#+)=1yp4b7sVe!0^EDLGww6r9siw?8&1c=&)>#XCLZAXL~6+XUDRcJd*ru+hQR^ zC^Ij4RD65ym}l@dvKMl;XQurQSb3Epm^K&{)^_NO6$|h(kj>-k>EYrYZHHX!O^}Tp zQ<1z47GRl_L=8un7TB>DfF6SI>DhLdXbB;JEw*N8wvG6a#Cn@>cx8;$(P=>9^nn*A zP)!DrhGJr~WKJt91_&`QDzXhnuWVfCv5ugRa%+mBlD&1<=ud0LbP~Ya25*MXj$lXE zF>P$aL>&pm=FiIXUDO*yK__cw&W|8!d7I&gCaN#|cGGBIQ*_gZCI1IVfLX$Wr(o3x z>Xog)BkN+w9DM{6>|4Gpj&1;{{P3P*0J|BQ;>#>cjz(^$W)#s1L9qS(m_(#VPi=8_ zIXPJ%2bC>l+{~9rwHg>}3h#wdC#fBcQJ5zKbO9~r*^DiNUqOPF3jkG(eW_$yK(f={ zFZiKUPZsjifPPK(>y{8zI!ranZOnQy@+tJ4MFxv#QD$V+$fPloY%l;Lzb?06?H428 z2X>5)sy}MDD}mCp^h*sOKw_zs6Cf!v z+a5frCquCRZm6cf0RTL6WawPIafx7K8ZlRl@P?c8L{ z$oO#P)2ZmJiyuYUQje`(0}cA~f*n54+%jW~X~d(}9#Xpcp$l70#zg}r)(2^W(XfV0 zzq_D<>e6)bML%0f_~zEPqVGq3gWZtEsCWGgs=^vbu0m%6vtO`(x+~Gp2F!otmtUH; z)*OW(Wp46;a@rzbE<9?5{i#+jsSSM8oJAhP-GopeJjl5432cfh>i3|$$j=A1eHU9y zQ-_#}Swu@EBN<2~JK#QmVo!zm7NQfKS=mZ;PmwkmEzR%jqjxH(k2fnp z|H6@^=kT76Wa}$;azrPcm@)gnnJL)8qkbJfvA(@G^gjFg{wOEjEq8K7#ZK%o(=`yL zC9hOernD1nY{&Hl=M0U=h_ZP3N z!`TECIWS-~*u&&;jt#S?un(}9t%5EmOfp)kkM>bstmcr1%vlq!N~oQE)$sa}pRo#6sMslv zdd)ElQK8jYe0PY5z zQ^(64pqSAH%oY)n3JufH7I=llTKhnU%OVAXExgj=B(!$asN`rlFP?Ju^5V}W^Xd@v zOvQ*>!}{hOKMc`x4d)r47{oc)IOUjl^tVL=Em$06!-@fYO}Ei)l!#(x!8-qu0O-B$ z$W(EQa7KG9SpQBQ)l__SgBP~pKH5${DRdsV>Ax{$nJ;2|e z0mgz@jnnTup&F7ExBZyv$gq^(%%3uoaDgl-by4?t0&mRgIgnVtoL@EAw;xSp6z@aTWPVrCo(XK9J`M&~uN2 zs|tT+u?mo&)$~OzsPW6XnW0uGEF#JK(>>ayx1nw?>t>2Nc4eZCf)#DE$b#j z2htSiNUGS}+)7PmeQDc%oyt?$ifv$Xo97$%K;8vV>{tCPP<|R~;{}NPvQw4eaC4AG zdz|+m>yzUrQRrl7eWUGR0ZXO|5J>UowCF$@jjjtv15#NI|LX!Lzf13X(J&w?zwXL%LIw(Oub*W>QEG)+0^mlh zsP4yDcBt3^7|Y_Dy@mS-FI3h|jrJt5tAU#!b;An}N}xYr$E}56z7JtjOsN``vGq{> zWR}aNfZ1x^(@nH#BqZJ?ih0H-g`w%mEGowV)~Lee&7o8=M$s`W#?s+o_5|&pFQH*> zg}Ns(?15m8Hm-eN=xNGhS)`C3?fvkvbSk)n9Un%)gsb!A(^SO@@M=wWhDk1?cK0qH zrUxiQKnG}77U7jcL5Y3xS=5~GGl~{q(kI-=S@I6PQ_`U8aV^oA_y+X;le}G2y(>k3|CxZ! z$Atq_ijF6+40!>X7#cQvf?D^CrT~vb*2~AkrFZ>1A-wLu-469gV4drFAlkLv;Oja_ z9F>7QzVyiK{Q)KP&7~8o0WJF7gJYQEA&Xk}VjWB?(9C;GMLDYMnN8950GeCBZJy*4 z8<4#%Y8>Z8WhbwRP8?`EOLw7~46;X{hrLU?0%VJ--=GF22U0D_kDf@jt=fp%Q1`-j=80;A5?N5}08=`?e9;DiqA$gk z0+Uwt){KQ`+l_XP;E`~4d*vG>^Z7&rm2)LR8^%KOp39SGW=%CZ|AV%&L8 zB6JGLbfO0M|FI@(x|p0Y;LqZ(3;=D;zC8&!6M!6}t#Ij7Q})U%xdkQc$(V8c8T@nu z9I8mM11$6@nw74fz}e!M^&QAkyxSA>yt{dO@qi9x=sP!Uw1e zk{-_4;jUJ~`{=~JWb{8%(il~mw2CDYwh(sC62pPISoQ#k3k%YGx(+WGlIzo>AT_h7 z&8>nU>6G5TK*wSuU~k z3rj6Z?U!1Z4!^|&Lcs@n@B`FDqB8MhM#hVqB3IR2gjPvx)bpH}$YgxDXzN6go!CFt zgSTa786TYUG(hbnQX=E?lGe}WJauZ&EpJFsx)1yxDGV7{hi89i?THo0*mLTQf6!nq zh+1J4*ml~Kr`kF~IJkxn&(ABUD>5nHWH}jyzc$YStF)wPgkWObkNycj&mCRm*mutN z!5Urxt9JiFvvbaWXj+Rj$vAMtx&3;h&gP``;AawxGP|eOmEkQ=B{GmPBEs}HkO6*| zQNrrD1c*c}O5Dka7`xd{fDk0uWSmi1s{bh}KjdQU(1?<6n^u%LG_@$bj+xerM;tO< z8Q6RR7!*|*Td7?{-elZTSfvoz@Myh}Mx7|ymW&g+H;P4Pj9S>{iIZe(HN4S2y)6iT z*KXS;lgAtMu(T7!uhQ-W^8 zN@o7G7Ik6sedJm}lfhl)uwu7^EpBHiY2g*@AJDvia9Xow z0e-3J&HJYYrlkk>^Xr!$&@a6y%r#uEShTO-3SrfSFI{4A}B5IkSI*aT-cGBd-`XEl!r zj!KBn)MRP#ZFj9DPMWB(!cJQ4WC0*kCHPgM#!NVpsF^Cq$y?!1mu;Fcw&c%4ICCp3 zy}ed5Rz|5{prR4}8KpS_9{|13mX4~d=wl_wY@l@(Px0|7_Uv_@X#!+79aeeNyK*XTJ8J? z8hfmjYyZL3&oqgG|0B(LPFQRNh(a4f^50L!+5}E`^G>rzBBa$24i!H%^Ed%G7;%F0 zTg?SdIQ*K-8$QvT;e^|qHklLtc|c~mNwudq;r>_hcl(!`u^g^>rim8LKh^M}h$miY zJ`48u2!i)>jRi%B5+;4o0F4bFG{ZOnbY8__mlj%?(Bi7b*BJl!TN8tqyK9Yc_lp`k zVdh26VGbLYXw7l*MKGf8WlaxG0Hl2!F=b<%lcZ4yHA}QM9De9vVoux%ACJxgqrNQF z$T)msjpikeSq477UTUnsn@Tmo_*$8!Gjpu>Dos4DUZm+Ln9tK}1F;y;!rG6ZX&7SQ!a@mT4*t@%1vzG2!Jb%?l38 zS85{lv~%$CJgpR0Bx$XMu+^F}4x6onK;xflY%o7x>nYq=rRgHU&MP&Wv5PNYmL+IS zaZ*3+GGT9$c9kKH$<`PP%M!GXM#8s#+E0dpbDXxsm|$3A_%lracT0`1dZEU2eh$pt zbkbVk4gIuH7~H7E4t=x*!uNsN5<}rmp7w)@u%=jR!V^{|!UnBYB8AVuwGD*tL$w;okVS$%+lOdfIV0N)uuR?v{r@i)zM5u_>CL3ZP zks!?W(3%?JbMD&iLZcSi`i8$q=OB#p)owHt+!|=*fL}N5eDIG^8har|p+!=>WsX*k zZOXK+!ULq;!s-8Qk6)H+6}a*pIF#h80QuTjD(sn~4dC!?MAEiQ3*LgI6m^eKGg$ST zlTa^Jbyg)zSZwjd9LL)74v-HjGn`y%5i9t*s-@t0^6w-U^#$Sf1dAe5tPbUk@h!Pp zDx8$7r$b8RSa{*GF}%_Ihbnlr>4?f0kA1FErMPh;Lvi%)-2 zMGBRr7G+AjH%+AyE~zb^^d&4Jg%RKRWrmDf zjVWBX`GHU81e@=wHZlxC$^@6ssvm|zp)GG>fvb1%_W0Ffl?s0+LVkbE$4Riu3SK6R zKFK%aaLNr3j)JaU;@cSFu?Kh)!SEr!jT81f;VU@ovtFga%bxHK!j4D$CCI@;@aOpD z1XN3Q(MJBUsc`;2@6QPzZ}KC7?sFAmeoh@0C(rZe4XNZitN2!&VELZ!P1(R(?`Ofvl-JeM zd^-*&?%}1vr(Hahxmq!{odR`*pu4;$Cycla--6eno$-S2EfIR`C*s565p`i+*Z5P0 zP&r_QV1Je$NUdjzdwTO0nE#Vk3td<6P{+U)8e#TP{*|GSdXLZNgjR?6Q1Cr5=dHak zLG@A%Di|2?~!Zy*}0(F#706M(>2PLMw5{Qyn$l`SqC3yj03@T(*^ zKY}-buk&~f`|DURaH;9UXK_ORblw%%M(_&ZOFr+x;n`z&V+c!2Ha9#TKK8{Fe_~hO zS;Tyj!r$ivNfDpT34M!+v9fb{A5Q3y!`pH=raMGawIApdR=_V5mrO3<>u~~rrb2~@ z;-xq_im#8KM)7CxgeZO>7NYr8bpVc0@Jg}}$+yH6O?X$~WG09vd>zWWbGp0{1w0ReK}#$0A2vr$-Ginn8$~5SiMFVXdpirPioHR;nhL#p9jtO0=&2bcoYZlgYeSUyn_8TBY zW7_ckcwabBIS|ShVwZM6WobL0QrHO?g?8ct@RW`K7!(WyMuhNbczhdv2*DeQqe6jE zSU5i%f9}YaU_~34yA=W~DHV(MK=fUEAoV2x);=X`K{OSf-5&nh&=#lwODS&N7RY;b zfMuy+@Sp4u5Lew13Qt7`ATYl*5dBD4R)_ILctb0Y3QX%JXq)n_IcDW|gNT*AhC{ju zxkLC;PFR<~yVP}PVg{eX2|Gvdk({7TBLA%lhA8@vPtNoM57Y^RQOP7wMn>>zk&$mV zLY6wmAsf|Cw%TjJO>f2CBbY4Z$$D@G)3^VC48VBpcdl1 z`3@rDm{EMSkT-@`vQWm~%`Id?*U`L&!37{R(gatRgT-_;*MybenKPl_nNQ~@NJuoQ z@sm>CR(Lv}p9LkfoY&&yIlLDRo6GMM0?K%a1lwL#@z``8-xRm7wou|u^Z17NTp6z< zh~7d_IiJJ{U*_`1IN@q3?S zK@>kwH5LFKDbZ!Ct8n0`>XBGUw%<}+fR`P$#X&)TAip#~`h`zNYDB$Pc}gkcdcY(J|?VS?;@sM;nG z_UBl*Sz-7ud=JdFIGUS)Toor{o7V+1^>el>dCyUWHY1DGxbGdZ zH=g3n1kHQZYC}AuOf^<`lc##j;Rp9+miX~jLk-TjW~|2QdzPlc*?*OjBzUL4YJkw( zPURuPWkIUeLUDk~+ED1wOVv>#AYQfBM1biVQo*6A%7qh7H&Tt}gn@3VEE6F+L{+X9 z>^aqJ*_xJCDhc+rQ^|xbo~jZKR|csZg-50;Y)t;N7MjJV%%Q&uQn}#KyvkB2jaSW= z;@kh2nqVg@l_O(a_E9;*v<}kM#;WTk*ky;pOxR$fngm-M{E5SDR4uVj15-=kK%(-h zA^v$+X^LmxRmyOyyUI9xvlLDhxp$OjF}|zpfur9k6~c~JN_z?UGZ!a4S6bjFOBD4n zeybFOXLpsaAhzEqR|)ygl_9VrJyDL9;C=6u=kb#na(le|j?x&HZBfp`ua7FELaPkr zcTc>zTB*eKg5?$hTC9Y|=J!;klL=mWR4&Dyl}dM9{ahgzGLh0qhBt0e+SP%$Ooc_& z8D z9a(6PZ!89qprIWGWgLabB??aq9J5nyD*XIQUg{=P8Yy1!nEwbO0UfpADN#(82u>g6 z-^>U*8)58D`4|yl=41J1;l(q#n_O6~RZQe?*PU`docAYqeCHFnH&lQcg*h|lk!H#f z_QFVid4FRJB<*nM9Ys@NOILZ4T#sdfeLg8ng!ouxi~<$d@}C z{-R0){KL#NLh#L!cjNHzK5`r3T%mjhUuVtiZ27le*D&=3z?Omv-xT&()dy@zb56)Qr>K_U(A9DaJantP8Ls|a-T?bvHZ#I$ zmGa*B!|(FGc;WBzMC=)3;)ZX8K;t`VwcHoCxvR9nH$&jO2mfq@tJ|BHQGPT&BBc_U z(0YwL0S;}ygKhR!%A4Z&qbm9QX2vGC=6AXJ*9F3dt@0d>NTJ1>R?CM8m22h8r8sX9 ztlh9s&I`pA@>3lCxk%m*uW~lFUNd}xoc2PKk+(AN9k}A(i?1WXb z3|CqU&W9y%jncPD(pxaIk~CkZM`-Nwzb&oT_e?#Cn!{}WY6Jthg{{wEsFKOwQir;ke}{!df^P`&;)MPc7b z$%p@qhA*6!p#RAezc?eY!As9beE+{tw{d;&9cv>SA$y}?tc9?7hEX1*p0$y!u>6W) z7h~bC1BTaZarK{uGT}h3(KOf+{xmcbGFBTJnFv;)Mo*1#>jp+<_|_1KTsZ1yWCvR$ z!F_Vm@O*ddm?}{UpM#8CWIH<;-EgEJyuYoL5)Y3uQsd8m8kPx7GmRz~3pdh?RvO`D zpQKVDdZ!%jR!e*6biwxInv5+&|liMv0@VM-}g%5WA2&n(By)OZesyh4s?rga;A$w*| z;1U8efh25-8X$yCMS(;>6akYV8Av3_3^NIffB~)6rLw5-x}hl6y04Yv(iW^$zxGoB zweBL;+A386m0EH8|K4-&EJ;9Z`#t~X`Tqa!@iE+c&-R}8y!(02ncsbq|0JR~{Mh)9 z{4%KPJ^423`T~CfU*w-n2M^^B7r%cq|5f!D{XfmO>hBFN=AUTSEA9I*U!_y_=l?{$ zimiXnf5So-f04ge%y}+!1-++^*dCUA^ukG9E4a9KSo^6r7qe%->Hg*3C=k#!rx+g zW|;GJ@ydMXpDbLIEq0vkbX&#E!=05@v1+XI9g}!|l=FU@m|NrgTA_zJaX(4fFz0e{ z;9%h+4yrLbEh5wGJS~IPe}ItKogWkiH=A*|bK!>waA{_zCf@w2@G~22?{H4jfvfpT z;e(LH?-p9cIS!|-5B=`_!Z@Aue&JPO`n!csn#E6t6g-|IDzCE4vWi1DS)lywx$ZpL zNtXV!jx1-1h8rv!5pBD{k}KXP%WN}En^a(@ug4eUlC8;^Pxse*@+mOBU@V<;YQbUg z`jmnj9W<&IzTNamh_gk<7yOK_sVS(ZGo}<=DvpdVXt0Tg#}qtkrbpLWa|mRZE$T)W zoTF?;RG)Uo5INjg<9;KLE}4ew9$N$MjpD;eF3?MR!0jOSG*^dMR^mQv5ibmPe{P~z zhr5f#g2C?23@Sa*olDzBs6|v4hwb|Laqf5c+l91ixLXx39q&FvR<98!2i;#F;xOGc z`4__uxlHujw=UQ?jjr=?dbZHrS6upc*VAU&7jsP)Q$KSdH2G+b`=|VV5|?{uT$5{r zsQ%U!%@fm(xZ2HhUyj=$-um2ik0So^fvXu#Nu%pH8upp1NL0pMzk^@I?^=^<5#8I* z?xF|2brsR_t6g3Zt#y?a>NWf4yZ^4puBhZG*K(@+r3-lOa0Nk7E{8z%+f?z%1{cCM zRX4eIE8@NFu2m-C*y_4N6{~i*u16&64%Z0kdc!qRoYm=i*(+ZAwd*znR)6Qp5tlyU zsx#@`joIS*mg*lvpN+qCt|)@JF1KuZ0*{|3j@;pzok_JH zxrp|C?8+d|&s+uc&Brb$?KtQfMK65p@{<3c>kWaYT?);vS`xwqqZ9b~hcUb)&gR6I4#S7;HR)cSlL+S*{Ii+l*O ze_L4SpbMV#Rf(b-d>;_ilM>AuOUGH;o^gT#5r~3<2HO3JD{PbGi zc?w-|6?RQdCttIit((fuD##SOulAYEba0Q)BL2S4f4hn9*aCiR$o0cmw)qE}sjM?6 z5BECxal*~!&wu0sf1OQK5BB@|i|Q-Emmh4jNeQ zFA!ZT{MdduGShEESf|bpqGn(Sz#LB>g;1ewiT??hs4M+8@#1X%FRY?-rT;-_mRWv> zn9<^I!#F70ey0C9MeGXsFEZ-J+x-{9r}^0D5Le{;M_R=~zyFOa@f)}Q4l~u1`M;&D zL;MKZWceSUcOLb+#O_@G!-y5};1HDksAgS3?&d`<3w0gmx6yy)_z#HPS^g^(%2=RE znsJI^hyODZ(}YbQi>ivS)pF`BSjXKRuDn!LfvU|a zle$}E4k*Gd0{Fxx+LskwKS~UlSY)$pzprSHO`JTg=p8fR^YOZ*VQ#{eXGNK!?-@l` z3=-G;vdC+pbsJp8lF_)xL8GS>?WE`ye>C+?d^V-1#3spIRF5e#K^$(ul7E|DG}A%j zZUt@+Z1GQr4gD-0e}0eu60iRF*d6}An5nk7XfS;=(dH4S-|lZlDC}1Mb7oq<&EufH zn?OlBHu<~sV1Ok1gFgp}a;X@z*&js2dlL`{c$Vo+@Nty5_B#J-0D>h?#U}r3`n;sb zEgs(JcbEl6`?;0wP_Yq)o+eWH-S)opz%v;*p}xfrRk_WRNzDaCxpdDn8Ls5F@w8!^ zN8UP14Q&5K)r@T}49 zO^QuK6yGD+v}TbvgL0R6Z9+NMdkHp7`0<_7yd?#~wZ!|fl|O)eH+n~9iv3Hy)gHY@ zpDkW2DH?CEi{N_iVw33iws&yp=FJ#$_AkB9i4pgB5oqCUS5f_>_kEK99U|!Td<*vD zcHZl~7GaJ@y(iFD9-l_JPkKwmmPfo3%woXryils8xxS#4hUEJCl6Jd$2-Q5|J&Cq% z@!)2m*>0N%dVMQ=VzSdW4Mx-+Z=N{qKfROf;;fgw|EbdJ_ZH!TJ*zjDYG3xMV*MWP zqbBaaPRl2F+!S5lv5NO||L9&Q)+nA+ zi^QN;-1Vvm_4VB9t_MYh;#pRWft^|niAXaL;w-5Cl@4X*( zj;AkU&@T?J^AuZjh-DkV-j{y0$D?k)*Hc?6t_yq4Q0d?rB~NU+-gBwCIm0wM**RwV zh}Wy**z}P{^~zV!WClMj>7YE#8q1he{U8q?h7S@ZL4`tG@yPfdp zA9rFG<5!0oj+;Q}mv)%loNm|t`@TSe;*=LaU?{hf1v7U3CT#5bLjb)6#s`2}W*jCReU z%F(XzqT+ZLSS5w{1Miu$#s1N*=fG)L2=U3EoX?oV7auq;Ql;%6vHa%`oCV^S?>n>1 ztbL3n`|7YcCl#&cC7U3=Ipl23-~Ohh+9ZZQYI&uECckYd6yt5y)4Wt+vlh~vw=J2X z{?C>_^%r>DC$)=<;CyD$T|)|j;)(k#OYO98yXv6>bFJ9Wm;ni#b-$Vo?ojRGu0_`M zCQ&lqdV`sR(;k6JBe7dJ!@ATiJ_=iZp@^~PSz!+xoQqf%KKB>5HCj)Fd4Gm=6O*+| zywqU56Xszh;yehVH*VUQogoek&YT62H_SGK4(!dai9fhApU9_m?o2ROL6K$itB%36 z;lYet+FEO$NNZ~C8g1Qc%@p65GjA-Q-#neskB}tbpw`OF8z0HfRN(&|QL$UL7=FmX zm#w3zZkIY;)U8ynG0~kjJM$1}%ScwSi!Ci`p-Jppqn=};pPhmY?@w2#&lBFG;_IKP z$@F=)*fhz|uSO0+>$xGDR6}snnhmYQh zsyU+ZA{7=CT6o1D7ON%;`&=$e)}A4h3)KHIQB$?OjDAyV%@a>dwokJ$b9p3m0wu*6 zqW`&ecRnMpSX?;QezQq@w_SY!9QC^6c<2pV7VUdZtr8pWS06Tu8#kyq#q{2K^)c$V z5<&yG3X=AU-68IITRjo1_KF?x)%(>vG2(f3hKcaiLF*q@H;C4~>OkzEEz-wUHw$$b zZK$<-WNX@2=ExN5cd2h>iRT_yU&+(K8S_gO?q8}F_a>D4R0SCk0}-p;)z|*94OctZ zvuMpE2ewr{wwF^~nH^z&9=E)&eH;C1pnZdQ-e-T!#A2>k44i7O$)VRqByccfzDgNl zliPlxnO3i~*v0-rJJLb+2kkdz(1Dc}Rm>V>-)s_dN80y0ByI(8`<97r{HyBQo@*a( zp^tLxg*0@n%|jbF7`^VxKFG=WtBSz$J4XP<>2L>x`F$1EFCZuq_r9Z^Xw`Y3UQzSK z<=P38NcSUJvWM?>#P%XBkr4Z1>@wa_|qlUJvM5wxDXJ2N6io~ zthIir(3^dLot+j;ahX+p!9-u&ghfewe2}s-)iI)fmbz6DczjKfRq5Gm^&5Sbb5->Y z6YZR6vxot%KF3{5>tAx@^ShOD9>Vs(*LQ<&r!EJ-J@){9+9Ug+j9h_l_aWio{tQTx z+wRRkR>hh+TOkXdLi+VYV)a z3w=sAKc1nAU0ZFlu@k(@hP&5%wgH>(gYz2=+loZ>XSOl8!s;3O@uL1Z8%$d{7P?hw zRkb~j851c|yKD&KGx#t`@#h))ak4d`HIi(?Hiy8b!p3rL+@EgV2CMt_D{Y;2-sXYp zdC-Pk1fGI~7M;17aed;t*|u5P)O8R*)y;9h9)z%?HBTevT^F`V8E2*Ewqa|b?JJuq z#_zU`)CikM4#MWo0N5SCWb{ttVVu+f$fnOhk6mQD47zm}DDU7A;3)c;t(3Mtm|>;+ zK0_BZVcS_Q+Ht(Yz}enQZ4W8(T>ytr*>_)VyU#>7ZL?*Ne=S7IhRbb!@z6@!QbY~; zF?NYwf3!Ypp+OI3l+myq za5m&N+OuPA*GnREQr&dyjJy-H+QgKbt?wb%F={OmP&-?*NV~ZkIbaBo-x;>y;s|&W zQItBM>Suo`%n}W+Wt@fWi4|a+PkxTwme@qw`LyyB+p;uCy)WD~)Yw9&9_Hf%IcvSF7KWay#0Z^t%Q`}2^gd)HYrY4>!X>_{yp zj@FgBt_0qE^wBe*{VmgN$ddq&gQ#||PWQOYY8zgmMJ0u0(gho_7%7#pZ!AKuUq78u znXmKm{5PzXh;1=1N*+qAU7i4>y=8ZnVW}Ohl>NXR%CUdYKC}yoD~Z5luh6bJ*geYHk6x>?K8}iG#ki5y zQ!~Wj)2*#0Zhew?Xo_`;Ni3RfJq?swg3avF)4@N$aUQLD8vA!V8ez0N(K6YZYZ1}$ z*6Yl4Z;3Sv{pmaggJPI?KWK#_8t8)ViuAA zpmHlV*w01g)Fyi$iY|3ji(ARDx=0Ey0wX%aCo#u*Gi^N{8_|28cHCzZ`<`5Vk&nK43EA^Zu5zcpR$`xM6(=Mia9loBW8Zg zr#Y|K3+RCn5M%Stbo8V4S&lO9r(HDMZ8v$uxsx1=a;WR~Ffb(d;p_msf7lI@ti+mU z?4O!M&3pFI7W&Nx=&8Zy7)SuqPCM_m=Tg5n?1;I)Wj|u0oVTzrHTT&2h_^qme}@VE z#a<|OzivOyBv$^_UIj(-2Xwu=%3-5ll{wam{~Y9i$=A)o=!5Is9D~I39>*0(lI!Q_ z5KD_3ADhK{Lma#8)G*2choH>S!z}3|&yh6$)|#kNao9xdclHyN?H}8pggNlGI*>6vLtJBXAf0c`f7$cIII{!k zP&eIb8=Y33{e^uM;zyVxSAuV)IV$Axh;J>9ixI5m5H(XmvJRpFs>76Us}0RPEK6{?_;G_RZhf;qn$?_N$IK`k86y>yEQU z&0CJ2z=}VJZdp8t^`AM;w+R$IhTI`0KiS};cN}o6q>yH{tyBJiZym@Rzv*cQf+m;Q z;pwkRC~&Yq23@uq1X6TErc2y&seM6)s99w{oJln=JI2wLR~+@U{#C~J0Wj>U0eLK~}<46DR{C8S&m**kr)avM#(#yvbkhkqWezQ#S2_HJ+&3R|nkOA^E47w|TClXExg}mv-5cV#Hg@b-t-Z6gm8*VVwM5}U{AfaQ z*8GFH!LpM^YpZ`~m_dWI(%xXwgH6`LKJCV7ky(*R6KHtAR(LejZoItEliz!AdSR}+ zuZ8DG_g~@eOLw;+`gneuQfI=Ko7-`NVQFM}5?*?v9oLo)4ZwjrzFlcPW}QFMQxWBA zYkOmBXA}KAs;nmSTPnEju&Qky=d;jVPvQc~SF2sQ-50L^SS=t|j`nP_795|xs69}` z6Pc|qcWrwu7Jq7ZQK)f6uuP-=QKdjmv|IToi5RQwl6E(`kt_6o%|gwcxJ-4>UQ6!K z#!xyM(NVXRPoqgm9{uSIM~1he$7cqzv@z1Yuw~IgtSpGgpon;LBsz)q+-mlB1@!XD zmi9(I+ZGs|zVfnOOH9LRa|iWd=8AYQt%h(8k=WDW`eS&GzCkUa;7vH z#L<%A4GKM^c$6U3W#zavU^ZD$t_6;(JgIV6piCR14bz*^;y$L*u86* zSw17Y2s9a6QB?&(2nG_X6(~pldebD*B|FsD)y|wXHxLJ@&u5y`0R?dx1);_c{*)-} z;%T6CaX1{E)f|SR8LQ0&ZMQT{j)z*q@p+TEHZ)Q!j6Vq;sH$pg1@X*B!ERU+ z&de4^Ynqw`=$5N8y)Q(kU7^Ni$op=+gyIa(iiAjLX|E5(Vxbj5{)lNOL6yAO7-{cl zY3~g4rww}S{`Hf_^YnipBw9iXu#!_FjiHW6 ztg1@FnkeitN`Dl%Si70wIP3ginIzam=RrwtfKR8kV}q8W16~TueEiW6trJ>u*2Y>L z15ecd2F2Zl?(|l{@2}kpfFD18X`4L3otSf6F0(-~UB>{96-JUA4D3t{wKjIP@>+a9 zxy_U!CRIClQ~-c$SQ9gY36Z2hsrMR4ssO#eAh>4LDzKcsR9yWSurp$PnW+P zcULfQatO0((mEoVE*0fd=K}6fYiZH~T4e$PJe6pP8HBYdldABZEKL+dV%^}=8%bHC z>7(ndbTIRJi6sfAnt+pTdC%snI<~#CQY(!vUNohBVFcJW%mt=v06(cE7A&LPx0$_N z0n~xXurOlS3*D>(qrGk#q&kc8MbGsu?Jf0OJD3;-3zTN3rWwtN3UdR7Asv7`)zv3` zX|iEk^%6Dzj9xPtm9&-mOCg*vS^{ap#*o^&*q+C8GmJB+=)@{c47JY-binRuZ<1)% zj4GAdw9twLVGUOoXtifgnWQzG+1|3S1q;qPJ{}D?L`H_)%tD9K$Y+pekV4NMZ-->menfVKDz&H z_gsCa|JSIcKV5JZyu0WwWgP8%5?ktW)A=7JQ=O@40ZliX`j%;tg<7b!RhuQnKP-=m z>QGZtZIo@IpcaU>hFaQTMg*dC@GZqfx!KN)G0|8{dxyp-)1Za1tZgAJ9tLIO8iJzk{W9SaN z(TtP$V8tK~6(K`M}o5qWHibP-z-&61W|1 z{H|{m95R+DW#ejYU?9r~kSvZJD}^p&hHR!otc7BWma;WV%4LeHDOtwiM^XSf2rrMe z;zwzNfpWGV6AiEmtBp#$Zak8KPzW+C@9a=(OOsAT@XTdjy4I0U4Acophha%4!Ukwx z5-7pAx;rIXQsrGr-)RZwAQo9Rci8;Vs{w@UPY%n(ASrLG*3zcKSyC!HL^b(u2_8~? zQtlIqvr8{wr6SwnUs)BM>4T-_mYNpiy&gyj2ITMo9$G%>?pBy@SBMAa1|;arBePaS z!-4r})xb$JBFo@D^_YOsODxPk)d+W~vm@HsL1+9z@zfj}nY?<-Kw#l1(-}zlICaYD zlkqZ_ZS1B6Ox3ickIBzs1ldP-Do(#{K(MT!}v*n=1P}>`(U(&Tj4IGK<{3- zTI))iQ*uGMb6!Scx{Og+@J(QnNPApPzeFNE4n&V>pQeiDZdyiB(2G z4?rq`UkK95_Kt<%7h_D!In{`<2bP1M3y*;X8J2{$UZ)HklGYdIoc6|W@ubt}fA3|A zyL5bOE3{j(qa@v@*W5Hd+fBLql^UwqrY!BLH2Q5;f?cJdc3e`@7U_)Z=x5T?;B7)y z@si007FEd?1n8juSLp9jn#*eZ1kZVKxT6I#H>#&TrhSNpR`AUAFI~&wcg5o{A{%2Z z3&Kr$x3O?rD7ILCHe|5TLE=pcV7&r35}u7w9*Ked^zZU96zgbdgcFqZiH$V<9r~?o zMKsh}pK66qsV`}e&L2%nmO?N`HY?K=jefmZne6YS6y-YeNQO$3JO^bgzq7F{6;7nR zbw#B%DZDU*tAwQbk>&A9?M$#$57ld^WN06F4GG02HR?bE6$5)K=axn7Am7>c7NC~F zX-O%18b?Z2zh00hiGZtzlGU@PL2=;1C1VECc%R(+wP*aut{GEmYK z>n84ZnQkb^Z@Qt7K#5UX@9bYk1GpqP5jjdcrBMQ`(E&wixo)KPsL1lDhe0w4)&Vt> zQrz{=TO-rK+`2K+6A`*41VaeR*Hir;!zk(c*R7KO7Nev~`vKc4HCsu^8ZK=L$9hjL zvD(Z48ps3GF6+nHvf?HUiH9^nf^S`Vss0gGmr*;xoei4fN5hv~^_1bI>8m?Hs|&{# zawrI*Uz^zh%Tt>fj=@74)0Jj`Vu@0KOCQLv|3Xz-!^)oIx4J<$3V$qoOSKeSvQ33{ zR!c`$vj#90Fuo9p2x}`MofsJgemo2eHsdP?PNi&-(xbgew$-#?L#m{rQwzkf2^7#O znpv!48>eXjUZPrMWu?|oW;8h(q+Mxdc510vj7ou4iNSlrT?e9N*lMLAq`|w0Xd&%n zG?GI&1`pP1nGU2{YiVDUAO$%wc%i!Bf$yPB?}}(`(xcFpHHSL1WiWPP;U%3dF_h|^ z&Cp-tDPtQE{}d|AQ{ThO!;Pr|Y1SuwifJomo-tLMICTm{b)+?(tfo}cUD)B8suSz< z2+K35$x>a+>Vk&f*~o4|tSzkD%&bY-h(m~*MM<(A?{|%+8?RMLGmdWHDWfUtI^;ul zT&Dy~t*uB+R%*J~U2Tac0-{SimX@?OjV>DEBouW1**~joa()+=5Wwf@wEgV~fC`fu4+dy(-FWG^Pjig;2 z*Re`Bmw254d_3d3yz;Ky{2y3OJTW|-)&=cKf;bW4f1 zz?YgIFg;?J0t+LZ z?M)DQflAu5T`APP|3Fz~pkd6=a4Z&yRkLpe=I#iNzyoaFbb2sYL#Hy`XiqqpIw2Tl zd%}G^HvAGbafk?JN({u4NOaf(4?X(Si4}4Q_6)>+Vti0t&vw9jk55TTXlhvqQ$M`C zs~hJt<)(SdN5`t+m-Unm3qx?ObS6xni_xFtUSrM)Im8p`T2bE?iUtE=`HSFjSTe0x zSvGf)fEZ6n@s;q*0$Qq?fqWKP`mLZWqG$0`Fk@p8;6uxeI+ke@^;*HQF8JZ-h`sT2 z=#JhI`$*D7>NyB(M1*hJBNc2^GW{?HN?Fm!dH1ZY!QU#;sx{QWZYO*s_N@|W@yKs%avz2bDHqdQ6QwEB9OkOm|v zg!~w+_O9I%b+Q)>+XM7;%8{hol~PMO8yt(Ek3mH}sxx+s)zIgGmFP}SJsNbOg|RZp z<&1}xhHK$X7#ku9uI-SVV^E?tP77352ec~u10TQFpe697)4P$xA}N7U&=QyXetlQt0`A2 z^qeRaFVU|ATE*j`TNU*BCZBuYh&jxH$0=iNflagH|+$ zmsc!kjll3n>%?rhXkc6?Ssib(HYSmih}#;_Q*?~mMF+1?oK$?batbZ_X?{T$;x7|* z!j=NDbRooIN)qxS@|Q)373ccS5=zm zmIAA{x|cZDdzYOWK}VMUsc_!^##m)^`Tw67YXDL-E#jQ^mWZ(;jtGy6-oZXf-`??6 z*m;P3>Ty!Jtx=hv#I$Vd^INNJa>N^_# zrlWv@6FoV3716opYV(Kio9%V|Nxu#}uoo&(KJ(9E)i*E}jXw zSGx=w@7)H44%9pDY;Re9atIgsw}z1ym61N5zY^ZHm-!MHAMBKtftjqQCsOEzJ<~4}mHxXk zFB9CBNR#V(GL}t7E@ZM~JhhO_N~`J64Zi+aC22D#AzYX&H%rDzwRYSw@y2|b)M1OD zOj9_QnWsvCa+WKT>G@?!j+&qnS)vBw*w^S-5s}fNk z3Nf=NvhzBcW1Yd0wxwF>%2XNbk@UCvNNW?Xfh1e4Vr;Z~@lYL5SiWgJ#@e)s84J5t zKmdZ-wO|{jp>(BeJ~zOIN?`u#2F~A)RdV7J z<6v^_R6Lb%8biFZDZ(aT&lV`H)Xs~vvk^x!zFPv(wTD;$8aqJjsfs(5Hd4_pE9IrJu^(>hxq&P5rN-W5&4v{NNcOUeYUW@QU5$Gj7WV`K*I*u_!|le`*a{bCb|MmVJP#KSimi} z&ShMDytmYQvenLJNSWg{Skf75Euqj)75~VRv%(AF*lg6!oH4ahI~hjUBz+$Xw}Q4_to1NFf^98+S~u-FA$ zY3&i-#5Int}u!WI@O_uwe}7ctS>`7hVCQ z@_XvBqt6+%M#e;@^sROHWo&nVTsNs*<&^Y}`6CN=iRv=xcn@bTWXGGo>+6XGa&194 z%nTZZnujZ&sH4mOGtTqk>X!XV6?OH=Eb5cKew4RLaj8=70-tooCyJ*F4{{OkFA$_B z{NY(*yj*fO5L;h2e%7gs0izajo0GW~feX$ZI^+U2fAGJtPO=`1xhPk}4o72iFmS=p zQm!RyaPUKhK89w)7fWb3bWTrZJA7vx1G6kQ<*M{ZX+iQe509LlPCcU~slyVBM@zoU zghB9X9}7k1*s|&pa$+uLPS4llMtDk5NcRfT=QT#wvt)v@C1$gf2c0in{lV-meh*F^_;o(D*^rJ>7w zef`}lVg-317HKfTLpNDmXE9+(5y#7GK#`tGlK|OR-$e5iJMb4)3rX>*8`66k3yWqX zIxHEZTjo*=lux6n4WzM_7_un(oE`6fhsrJuKH9^t|!s2ZG zSu8+i>Pd=`7}A*n>7&wHoP6 z1(r!g617Oz`E}SmZ zx?>HyC{juxV_A;PA}10J!zxI^5?IDH7A}iH0V3mARs)2xx034a-JxQ%u@&3LiHeA4 zPF<5zBI++zy!hFhwaOOUvvjXwnI}_q>td0Gda{40m3P2f8gUu~)&WfY$ub*ciu}Q> zd5y?1>GqwCp!9m9HJPWZ>8)UUOr`s;R5EOc4I`(74&A1>%Vc&5^E=xE7@=3TD(?oc zJwb2T_|dKe{mS2Dn@OQ7l{2kv{PS_V?$Cf1j=9lFA1U)AWSmRjY!j?_nnfeW$g)l|3uFQ!lhoVPjRm)ODGH}j@Urg6>Y62Qjx@qv_oN?g9(fr~0kHLiB4TvmYZt&O!^68eIia)?`UmpPz+YBkn%ful$P7s@HCsZMuPedX;hX$E zNyi^nuFwV4lqQ+jP6sbi)LcX5>Hx#QVK(r&j($j!5a7b^41tnqlVW;5c_Mx&Upcsu zgN^m_*36@D`*_;=xpI!d?O%Vc6nn=Tzsaa2*yEzwEHDV9I&hyWy1Ng$qdNNuN=%tQq!wp0y1$6*Xo4h;nV(G z081-LdaH(At^+9UL~J^)fneNU=j)>E829EKihE@gxZc3>FXcPx;Oquz)y7p1bV{{*)&D zD1ts2{g^a^*uNK*NH`GQLR5!#E%)|m0NLuHG(GI3_kd7RVkS)3`bSwIi3WVGs9=Dn zAV|gd(VxrZ5u;S|DKxCX?Q(O{mkcDu*))?nk$DG1(oTS}v z=rMgRl8p69uwvy-f&s~9I3dk^h;y>s8=HjC{Yzz6!MXFx(z}yq0g@2WJzMha^Nx;( ze+vtV#ySp8KL!qh270R!)e}=l5>qd9AdJ4kQP|v_bQj=k$>KB`M}TXZWRR(Rz-uf5 zP8{Frb|{dSOg|H-*ECKra|7cnFt3(8w^yo!Xq?4VM01uPx}GqkJeMx49rb`!wz<>*<`1mT&0{~VegxR{e{$QHid1v{7qR5M>pBJj&8Dn&sQfR z$a3#f=NA?yiKDzy6#Ua7rH3pD(P6V`6E(d8sBb@{lz2-c`bZVk{QK7k;bW6l?$J`k zr}!n0zuxc1O}7@@qczrQ(z4}pg!LA2js9RUg}5(2O}3f>I3Cn$H68e8(2rZ2-lx<4 z1~`5g^~>q_bxM_vb&f*(NQ9sEZy_8L?hLgiPH1Bzcqq-v$|=ZZ-H=xB&0%E{?L4eF zv$}s@`}d=Nzvp9+=^gLc3OFkwhuXeS8hTAfEcryqSIh`x(wYOxdHG2v1g?&*>}kV| zxLgR_sndO7Urvmp6vxv1n;o!|R6eqjOyq`qtMs z*;~T2sKT*hq*3ujJjv+nYC8C-GC0FvUh4OmQqnisFbQb-HUzz!=kfEjKkGGP!i+W2 zzEal8Sx@*>ne#tp*2h480x@-K@C!=+gj|x0vE_56^hYp81|9hv8Giqo3wi3l#U7m1 z(2F_vLa)^4Bx_`*e@!w*x352jE$F)=N`LXUFBGeiPIg|({!+=T{C?VFGd_8W*I+Ck zo&2RT>-*@;+IyUSKQK-@vrSDgn{HwMJM1R*e~J5+dUFnt@f~8gxF8e!U=A?YFf8LEsZ)-^J#H<2k z+`Ugm*ONxkTM|EXnu2H?zvU-hm=(X=QRx}E0woCFbEvX<&=WBzHhH@Gjaij zG)m?U=i`Zb3BtH_fEO;TY&3Voe0uknwjiDOAZ~WO{=*DEEq)j`c7L!xql!*^*p_XI zB7tjkk&AZz#+p%tJP2eu$#96?9l!EG_?N~%o-qh%Ieg_HPdTlYfdT$ZN~ayaQ!^&h zGSol-0x`x@T8qRd?%Q~#Cp)YI7T^?wK?1WP@^})~e5JlUt3w3rb-O~;*cjIfTiWAM z(C+v~Y<0#bVOZWkT8$(mAW#d=n;Kcv(nwohRNZ~j*Pg}=$Z*n(3N^ng&KH>`4(I5( z{#8v2c++?6!1!oJJO9g@o%k1_ECcI_=_EfGE2l%n)d5F0NaZy2yXx==9L3ww*qy?9 zvMWX|F5oOIrUzbBGmZlIQ;I#`Z|q%THzp!4!b3)n%gR$sX z=6f*BCN4`#Ps{1O0CL^X5e`$sn}P)6RZDhXQH{J8mTW=p9rHw124`P$rSwJ>)!|A* z?im1Z-R?Ey?)Jn^TugwAyhfAqQlA1E*skQ#j!V>nvyZa6-DyK-3*PkE_uxKAOG zP3#3Vu|%WTyQ0S4zB4m-7nAq>p8URletGgZFlSCLb7t<{W%k{Yr5}naO3aR=b4Jdy zOMRv5O^l4jYa)7*abl0R)9|Sj&Rx1u!e+@BX#9FIuISGNNuz|dWVE{fc^BT6!8zj| znViy!khmxU5_Yb4*>u$aPJ~+z;Or@Em8p@@lu1t<3@{69Gmx{RuzTi4M!rF-_TrTT zI4^u>pnzfwC^3ur?k3Z%=t2ZD>%R@`g%6~09@witXDcnTV1km-CunR0E*uCV>H2e) zcySu%NYO+zc+nm=c+BP8!z;XY}Ws>C%M_jf^hNG^@g8nOrcPIRd(_ zy*GS4{xg;9#K-}wxBJANczP<=j?PTAH8SdvWp2xuhh>1`-_kjmG|7?)N5+T?s&L#O zgA0-N6=un}wlru2F-cQtA7i$bjMfwTwD^IRJE65+fRiybbKeEx5ob(!?MnwkB!g{; zLS&pa$S;i8$Qj#bavDc6T?o^esm*Wg=8WinUO1sY=ZG6*aSCZO!L4NMHA#62ch2IP zQpGG_=4N_!DJEn4t=(G}uxfq*d&fe9K{yqgr#s;h!jclehqZ1YD!Q1;3ozSam zGUPPfg^Hj8)?cDbV)Y_;Ae-qLG<+%xkW&CXdDXTpx-hjdrSTn@ocNON3lr{6Md@id z{smbDV!)fciL(kLquP;-H1=o}DQfb=b zIpdY{O*so>``w!&op&`dI%U18NI;UR(~A6xONR6(s<#mWfQ-9Gduh?DF${U{cdL0W zoD(JR8A4q*PJRfFm2(Zy-tT^N_IU#%qn*23EI@~^$gq_UX9Ey>H2k{*h4xb#8Rd`9 z4#Jf!xp+Eb1**yBo;ZWX{w2dMA)GIrJp(psRC&}EUueU1q%$KR6Ly#uj6{w7W%!l@ zr^FSZoE4tcQb;^w5H+{Jdohv5*8nreokKY(%f3FLTsKOp6o^;(q#nls99t^E8O>Bu zE24P`n3Cq|mR5p*(_zMYS7C;MumsNx;S`ioBE(InD;;~_*(Q2Qp{+O>g<%LX-vPr>I<~%8G9?;$S-9H%HwdNA&%qUQ= z$NHm_^=75&o8!7rLH!(nXY{e$P7KxruV~FNdnbYrD?YpK#@j-;&XkZ67Dgl}0+>3s z#=ur(!m$|M2?E*wdAg?F#uR>lV=i>wEn7|P??KHuZH6QCeI&^P3bt?>nx0oeV#Wk$6J^M_Y_@lAJk}hP1qODw zv`DOyP7q>>jIM=Sg78=?0gh(wx5VPlN-J8XuJ*3(@UK22+S|Km*Q}B7X&Neq|VD;+Hq$ z%y3N$j+O3e*x)0KM~j$4%UeL`Sv3{NKD072T6!+hPhSR<7~m|C7J3>PJwAKnB8g#N zGA+UBO$5i(Kq{L*%5@^r;BZgQ2{&&cfIPt)-OcC37|_gdWgsV~Qufdq86D{JZH_*h zoblE`&QcjOB&Q^scyyc#^C%g2ZQ4GY#1UmpX;@&ZCIX=)5NJd1E&c_Mb^OPIosEUS zGKZ8Iuyj~D#OFTlrP>A~Quq1V0o z;77q+DxKL1Gj02f7$WG~jI+hZgN2-`1ywy1Rx5vi)dUH!$$+QzzVr@%R}eJ`UjJaB z&FKaKz4Tn-MqJf}z?yR&l+`GZvelaS2Slb6gE-tH&y6@CiV+E<(^)`!$NdFSKUj!F z)wZTWnb-y);B+C--XND5PW6L*?o+8uy3R%@GcZ0Y+7&=d(wKtr7hmWyw)%1wEc*BR za%?YM2evA9F%tg}z(CFx&-52c>TKZG_Dw=5%VHYgu)q;t^%Gnk01HBb=H`*yOr|Q3 zj0zp{>M*YB%QQ2?;Q<`mFNx_!9p87}AR23XP6JBQ*0N&;2UD$Vouaez29pT=3%o&U z4jC6dj<(fDau8)$4N>=C`jg4{6)7Tm87*+AuaItcfV|D+sXbX(5_=Mx;d5S`g##g< zA@n|EJbQHLT%70d4??hz_2HF3=xO@pMnaYuDBbiH0(_(&D3Wr)h;6p@qQC+Xe&i#p zdZ05hT9h%~?Vm=qz;xB#072?*psb_a?F0W-i>yMarPesyn=_>b7zc7^x2-z$Fa1ir zI4Omv_!=3Fl}@gxuNy5rgt`$q!N_QyQ+dS?qU`hJq}bF`$i^w4a&1msD?^-`VOZcp z=@tSk`tx#otd;2PMUk^SIY-)L^L&V%zL1f&>y2-TgW;hC=&8{ty>|Erspb1~P5%_-n@cSa8PonhSNxQD$V+*?zn;+5T9a z6bA|MTnJc4icD?sUjbYwc&fe{}{Eq+)~spM3Q%sD03oAT)nsg|L-8K&Q80vZ908Sa9}+knR4OJTE_ zrWDr9U2wrW2!xsZ{U@Z`@x>JOw2{DjEx=^Ocf)_6>O$FF1*u(6Pb*<+QKWnB!X9%2 zUz30UO-@=3q_7`@DdJ}#V7;D~mD^eaI6(5Z_;KzasA3KQr}0#+LrM>b*N zQ-aIe3W;$DcGm2VZVC0gHt2cT0l^4wb0g|`;KlmuRxqm+$0z7#75GhCp_ZqCaC?e} zysHPr>OnpL5|=-9r*T5zvNYu9`y)y)^{ zG?4Qt&d@*EIt%(P1FbIqb-{#Wn;$-)hV#OwQd?DgT2XO&5vfGKL6DF_N5&T0hc00r z0!p@M!w5U+D_}stWSo6=$_qS8&Glq)_Dn78YV84a(WlDG)H+m#T(uBnhXB;^A9dl_ac?{*a%fHS`OH13 zE)dEwS|PfOfQuiuJ(Z+`A6}>x_TO|s@qRI|F^TY|_>GKHGQp(|Tq0#fx{G=5JLnK6 zmtlW%s1q+m91G@)a6+&uFMp4>MSx*zMG!+T&4lt{31}(Pe(uXm1u$FONi29b7Vs|F zEwcOpmTbW}GRAopTzh=LTp-gH^q+L-vY(#27yiji*wcG}LgC>%Y1`8jOD$pRSS1!b zdJ?!h8o9pHqp^#OIv{rJ;_hr>XaVECr4ZRtptf)@ZbG`%zEtr|B$}G7dGW*Qh9Y6L zFVO#bJ+dK*7)pt`(oBV_TjIn#YxU@0Bv)C`!pHxs>jk{eTk*M>6indGm-g zL_v78HN?HnS}?d^ZCwZN9Pf@`e+cV)Fi!|F|{EoY74%*5^v4ui;JjF%iG z%J-nA+7HI_*`a{dwp=)#DHW=^$R0X?<+?z!upQoHBgA_F;7^z~ErhJ@%AEe#R_Oa1 z0pAyPJO5yMwXzY6*94~5rEMEWg105kwG~PZsfs^|m(UXvhFYM)eUyTbU7-Q8&u?^y z)G!LMQGotddhh~}VLLKOWSLUo#M$8U3eKF`Zz_mQ>_8xCnF|XSTG)P=aFVW=rm;>@Pnk&~LVh*v^LY!M4B+FLM&k+N6Zv-DWmG z-(QAMV%ve(^RL0?B>7n~hJc}6azs^RrYY9Gkz3>U_Cm||7FPEzz48E`vlH6LZ$LGq zU$iZ25+EE!48_Ktvr)z849U#SBh~FWY`#QaGIl-EVmNBBMuul4!R36wp${g%mA7Xb7xeNU}=ge?7dhx zm#hUHmvZp2C*OgfjdaoD3IDj`aBf*3U|SEd{(b&5zcy z425YpQCbN_HyA?5e?WJ9D%<^aZWVrIzr4X!as}kSr?DFE)WcWW`Vt; z^vz_KC~hEChFm)B?J;K@OdJ)*#p1wZp;WJc=@_?0wfeMTQTzpTsw*CmZdh!N*C%r_D*O<@Fy6ju zlV12f*e;2S!cP-~vbPU}aqYj|8|xCd!IX6HEQ+xKX5!ivUe{yvVi?|WLaILpjZI9NJti2*cv8HOBIyvaUfDg~;T|zu z0dxGB7_N+xBnQwNxl7*YN%mtTKgSEET?_EoH}{VdBW6-XdWwyV+-`^2qS=lzd_7sX zxZDM^+858;7=luYgSv1|j9Yvc2%KJ#oD01W&4foAZ5s5!FZ*)MaAgvNi+N|xmD2tT z9PV-PzG16Yk_?1E=`8H2GXNes@5yFE=2DmiUeZN~|FN*3tay0~+SNjSsj*3lP_vxD zIH8prhU2t$g3O~~ao2X~ZOBXz)^+5xREUvqE^2W^HCn$ZQ6Ve{+!1?52-LO%HK%8r z_A%Vb_Hai%rK14*TL(Mr<+t5gH%w~?EU?EZ?S%zjz#)scs%!sTz;4erLV@UL_167g z+<|~KKHfp_%1JO`)bKtM(iRS&etZjldVJzkuHKI{8-1`I%?|h5sxrq*BZLyjg9H_) zD?9v8E><|TgHX;^fPJif{qoq5vKBbDFDJ(S{a~A0&__^)T%2E@ysj7N$of%PEP(is z7UwtXWl12F!H3}xKKaM@>afVbg?tQ=+kL}I%p)NtNc&Gow zUhJZiS`;K<+a-ZLy*YCr;ok;(^bty4E=(8Pe}LFsl^#Wm-}Dh0OfRt5Pj3Pnu{iF| zdE@w=!ll(-5WuXrB^B3b;Wbm#iApQ^h6Af-6>mwer6WY==*D=hE0JzaDT2{8u-DBPrgC zX3H~(f>au4w?3#WX(LquFw$6%vCX;Hr-X}b74|X{-17}|nYqa|S=dBOh39R>4shy8 z+G9b^rf}8Go=3cgz4YW1NNae`5lIcd1GJT#{I*Ki7fiwNznOBZ6(?Kel++cjsG`^z z)fKa+MY(|RzM^_CdQ#$rZjEG*Cpti7L8c~3Z0hc_k zz}0&=4y(=Q8$e*An)I{H4w#N%y0t z!%kzqAd`9fUP}emmZ-9fU5D=j*2<0n55fFisC>LRIy=Ea*+Ku{Kp}7><4a%bO>@S3 zqk>6hu!ud6AqSMjAD0!8))dW}%sNxjcz=w(QGp7l1Q2rQ{6r=mIc1*cGVDAw8SGK9 z8?2O1I9z97K1^8I102+SQkpYryh(;?21`) zIf|cRjeI6Db6SB^kE-uRlB;?Ym!-#MLM#E&o)<4~1531}?m7h%e-^v_M6DS=MTE*G zu$5B*@FHdRZswKh@$3;rf0#Y!eQhhb*-@hE@j7HP#hMa=hpt8!Pe+-8-mLkJ1b38_ z|Ng6>9=mlR_vvweH#=?6dB`Nj?g>ck_Av51V^=U3g{5X~+8366#m3#y>80m#Jmy6NapV-3*(hwXgj@mnCd=ypu2=r6KViU&xlez5= z%(p&!Vs0SgI*ySc+=q={HJEI?dhTMR_=!D0B6d!fwp#|)^ag#|F58<3JY>%$I+-_) zO|AxUF7Dh=0g2n1%JU2?xkLMFyZSWfH=JCzGh?iUh%bB*>PC(vEJ+AdJlus=FyuEp zj{Q(RoIOFj1HJ6i8O=NVbA9XxmRJ_Re9sjdF=*F?GS33$(SxRDviKmmOvuNMfVUtt zoLK4#9XxRu88=3&e4uK0(G^=tSwen+$gXodei#TgMq5hBwK0q8`9OHL2l|<&FrjHx z%C3sPfkM{99xY?$*K_8Bpst*=@gK$Vj$;0fRw;9cMB+3fai6__u<*nO(XB z{0qamg&@TR;Fv9h!#y!M3LKI z=DzzwWz1t=hO%c*KZ9+TN4I`06kxVPl)x$N^A6DsC_Y}OcC-X79c{zVwvG-(A!C^J z&wwnWetRZ`V$gvar3q-!K0VBu`iY=T8Dm8O805GlnR3ivt=iG->DF)n`0=B1Ai*Q( z$}b>J;b`9u)Nv)lm>S(cH_kL{UA-EuOW4!I=isocf<|xaArnei>mLJccmC{gb^7ka z4XqreL(4}>D4Tg|IP6$#4Wrho9L3m70f|LIkdosuXC@qqpS%u6r`94NQ4HBh9FNRerV6_5;N4hy&f{4`vBIr6I&LMYyJ3ppt4P;IVWP(7dx;bOaS>j0Z?2xjU?J^s5;JV67lR>j!s;Udz zaQ66VX)6eW`8xuHLE?U*xP=JuycCV_odAKRvcO2LH6&#f;ZtGBHBXplBPSBz%o zv4|sOv}i^PA67Yt3^n=eU8t8}nl-X~E#2v=3fN9J9&~y1@?dje5?+!h7n#xS=Es-A zzNB@VA!y5v!5)Ct=FWtb0-7`ODPW4u2KLc`6N7$aFGAz;S>bvP(8Ay~^C&G{KBJWf zlX=hmMS?mq6kNzW{}BN8F2Ae<&o@FZGg!{A0?vzMHQj}rWRHYSz_YlP3bj2nRNkoJ zAjWJKICZsEf6$lizzSqBm^pMObVRL>M1K)BS9V+f7g*=&yZP-QOQ3WNvO)!eSdAwy zz3lvZ?m%+g0=s3V9Xg-Gt}8M?zMqWxU8I5&l=C@aG(LydOm+t93PIef|Af74 zc10GOy$sYVTF-PrZI8-OtHD|tA`2j!N%|M~PvW5aobSv8&^%%?y*cniRWA|(Y z=dgCg9xOUy*1#a}YjYauFJML8@H#KFv0+O(aSom(I}Wm6i6&<>qZ5xn)T~w$x6^vq z%EJz|YN%k@(yd_^_QR@%z9_ztDc-Wp%33-JbS9FKG48whUUb2u310r#*hbp98H13~ zbL5N$=yqBNw8Uw}#VMpgm4Gt{A{n1Q`?>@Iya}pKvq0JbthPM_NjiSqca9`3G>joG z6zVdNnRF_s_Il@ur6{EdLvtBufkqFoq)M0r&~gZLMTJvjDAk8y=J#jry9s8$>3IG! z$kUlA3x4BZ-S=+64()-X9Z?@10!vLjmxObor?a+_Q($dGMB z6ZD}I^WadhMEf-^+2}>b4yaEDR?LRL_v-3-Th=0%e$7#Nw2h{Hep*g?F*^bO$UJO! z+tJj)M824KmZQY23(JmLfL%NMZ!W$4Y~>MfX(BakAuN`D+!;;|AW)6`+>kEL5mj|& z$y*Fiw@oH($c>#0-Rw=EZWM0>KssJJ-p(NVnb>;S_e?zTyI%8pU!~k_zJ@Nh?ZR3u zqRH{j+Bf>8EfqajCjV%l7h&8BXDr4$H=4iyd&s1l4L+syS z9A5Hii@}<)`6V3+hp_jDOMn>h1sO*k@#u-lqIyyVhr`6h8JGQq5cZ-W(7Q9Mb|iGG zB`@v+JNAMs3HX+3e}98HHpL6g*x~pX6dG9{6|t4s3+jyx&P6x6u-ZqAeat^|LA|jF z)FYa0g2dq8J2n|>;4q>KGdMAp*ZsEx4QWsw&C;Mm2y-&tx^v7%7ayS z7xcIdi^fb?nh}<9kS3w7wHb;JH*YBntqq-4j;o z0kWi$qU^j(icDZc7}PziJ9Qb6>eAYhW~eZ@Iby6#X+a1QEJ4N%Es7RE%CKiNLPDWbF5~$pn-i4jWq!5xUiy<;4=%8@vCs?P0zCTYE5XWB^KC z%MfEdq!pDc5@xd+K6yp>peISpGKBiIWaCwa2y<-59`Pgpb_o>C7gntMQoweymyny% zcIN15SD^@h?i!)#I3#aC`?MSy@8z(X*n^B$-K;wr0)m(#qZK@6D)Pmir3PXpT}tm|cPa@T#lt z$a;kiwK7LOt(olP5z(M%ZYQf$wh>IibZQk5(}X~Kk}Fbuj!Unl%3X}T^e``w8{8ONq0X3 zg)yz`Dd3*_!l|Lgv^I03QC%dUk@0iIqL1jXu?*en5lEep4Y}iXX53EbJDa1wd$Mi( z3QWG+{$w}Q-{xplFQ%~$&@)fE#T&vasu!~@`8Y@Gw_aj_uA5VAS}wihsDM_O*n*6| zSvInQUOE`{Y03O|9Xd+)yZMtqi593P1+F)Hv(z64fT>jxUG$c7L-D;u=yD44R|jBL zcj|ph16o-ML+b!&mnUudrgxPas_ScyM)nadI%f3g!)ny?KI|=)ji6&mQUgx}ukWBU zeLFFzqF+69Zue6M|4D6mUncujz^$`i*jHb%g}&RnPXapGPhfbxUk_^F01#Lz8r;Vq zaHXKTf#EI3&Xt|B+v}44Px9$H9P$z&Ot~}FJ=3* zVphNM076j7I4mY2%z(Pvm!Xp9g7?ns&#kvDA>6wKOW{y}ZlAyFDTo@1x;AGq^$jL} zo-;xNg@cJv5+GbTO$&%XR(^uI&VCUnu9@&lTC)hG4q*7r0sn;e$k8;G2)9R#*~XRw z0KYJ+Q9?X(VDJkgGfwHqcE8T9WkAd9VNBCH_e zii_=+K~RUHT`gFEjDX4gkFpi@XgPw}h~9)S86~~;4l+oT9LzqQg0h3-sWZu?$L>GE z$en*ETGLF}>i2=VSG^e*;_kf_{$~wbI^~xz;XDK`WIU$f3Q&AwMzj-5ymtxgsn-{M z2@;wc;FjjQ6&~ZTZ5m2(pqX=*yqxU3^v+#lqDj3WTRD-PjK>Q4S^Z-zniOcy;(BRd z1ak6cHJk|0_`s(Z|6Cg2!-60Q&RJHov|kNIhz(+H`4a%^2ee7jH!JgpnlbYg0JLMmm7j#j zHCJX8L(YGyveC*i29MGn>xuBGbq+qN1tk%fepKMaI4kd8z!K( z0ZjoaLw^ejp9kClmN6fp!6E_9u0F_Zlyl^urh*(|R!I|KDKRz~H*`#6rBK(D$&d-0 z_Et4*W=Oj80W9grhWdHEyrCYTN-j=^|B3f<0EZAHV~%D-A5_@9Gquuwz==Pw@vR;Q z1vh5IN&yg3I_SQ>*n-5S$S;+(C#0Ci_T^3K#g(s@4<(gPNF5*3h8*4JgqWhQp3I*u zq5jA(w|N9rnf2<+z`<3wp1TNf*bx1e%2IeMT#_xjT4xOr8;nZRSrh*Ocx~0Xtb)7_ zMtj`ZCEi>(ZELivw5+C?s=H+uXP2S%a+=R|eaoY&SZ-Puc z*qvepyxaYD$I|9t#*WmkWYumg9l|cQ;AY9ejqRSF?BH>M`+}FSQih&XxT4|HnH@vG z%XUpp4n^fN22m2*p`DDL|LPWsp3@thpDEPU2Q!OVV)UIg0M$)rX}L%Q+nZ13K;$<+ z1%1Z=Lkdjf z72{I944G60oB0;VPoP1L1V`R@Is&%>%Ki;R+^Jb(HlsFkQ&dUm>0Q$@Nmh`TR7t3i zv3&QYGayJlT|XBHbn}k!BI0V3@`n@+(4#a4@dyeTm81U>QHAzZFopU7+9In9&A`kl zGRVAQfe(2IN03nP+#!@c`S{f55s^_~s1($?f>}NlNcL>nCmGqy$e^pV;NPp)T3UhT zeNf^|Io19k7|mmu^%TAKiJ*M~Dqp}NDhcvp{n<0N^y8fC*9ec>&a7c~UGL#RMLRrf z2rX%WYgsuVZ$f14zV`>p+2~6-kQ2t?N8P6BIe4K9>s2UnBMau+V9L3BoZ($}Xzr%3 zm!c*c*t1097T*UC+RzKI>+97>yPjEvoTn`p&G)1?qu1AaQr#B7b6_7y}4q|)z%M|+)p z4?NdP?}0coEwVOdvXGDXu5P0lj|yKoAS*L_bbzx&CBiFqM}S^+z}b=LtD+GF$hbx< zj9YWf(Qm>k@^a(o{$1XnE@s}S#)NGrE8y|dyFKsIK@|1`CL0{FCcc*pRxvZ*wT1R9 zp>QJG4C7?%+|~hDmUTJT1#$8qwBDXQZXl044_E}zmw9EVQo%Mr@N*0iD)P;!ateb4*=#Q`itFfa% zr-?Pag^MZz336cWCT9Hgr8*g2gBi$d@zIez{HX%SuEJg3t)fD(jkWew=$S3pbgyS+j1dSg1d z021j%%I8IThbU2=sRi;ivqaZTS+MYcc6v_d3+SBA8|9eVA~#c^%Ly^f2_6ljH5!?sFSZ+I_ssuGeerf(**`o@>66Zltw#-trzJNLeE`ApR!ftpS}LqEPdA zX6*{|mS~HVoyprnD!lD_F$(ra8LE|ruocdBWCa{srOXDafQGGq`}3%QMGK^pFva@9 zHX-To4G5@$IeQDU`qlD@K6C%<018u4GctK8s$4|Hv_Q*8pd9*s6t7 z`-1FzLXF&g6LHA+YHU6{{s*NF2}%)fN1RD}N2x{R(hY)eAP8jKlsEGlrPiT;kKPK=tZ?&STtKKw5YXWxTz?n z!v4rRsJA?sl0PWet`c~Dc|Gq}n5?&5{@YxCDq-}9+_Zn|n>&BucdA-UG5v6>o;MjM zYtzCABC0VLu6}CG!%@CO!XzFo30EW*{15X^HM?~Huvkw=O#0=6=|Re(YZ|@9{*lGp zc`@!3);;@w5Q@IDhrxO@r}5hhXc5Dbaqlly)ATkJ!kgSM5il8h{T;Lg>0G^0rNjnV zSy(cGl@{TOUfBi21km2m#bg}U;7Ma47quwdM1=Y@V7ulg=y~2g^?7E*YD{xhmY!+dkyOWSgZcoVS5fjxSlJQ{bY|_re3(o*QoEiCIYY;`z)di@ zGvVY2S^?rU&gi?&0v*z_7XC9-r}%D%m(c#O+*N_%U2K)fc}2`Ssenq%NXB`YsL%ye7;`(TbF#|QR7Z^BM+*M5B-z>VLr7wc1 zY*WAxo~MKQ(c@$9I(r=YP3$NoFVGRck#SqJ$1n7}15jl7itSiO!6jzb*14mymn;j* zfzJC~6(9AH`kEqKoL*b zsVF_PKZWjI$u2X-1N8dQWv}(n3zpiTfvnbN6SAV$+GTf&~jTQSK0Cbs>Jl%{1SQz(g~82 z1Q;2wEU~?gwk{DqvjC^|Z=Xy)12StXT_9p{Y73ixuzJ4Ko{IhmQ zT8kn*(O4krkE$2ip^!za6K?@hZ*J_8$olOi3nMAsO_)6pw$6|OUa0K{2UPw}f}Xq* z4piS?vF+wB;Je*+rWa(L1FC#)39D>z_zSU(^eQkXh9Tp@@w3F}_8VUUF~_4fN^GV5 zKw$zVW1|>(I4xD*UMq3_CW%~n8C*=DWNZ*!{uv#9%j$pw81Yfvjh3kJElW2JWX(%? zeF91diGKC~c^%qLIAcnY@5VhN)Bg44thv?H?Ac*_knRZ20Fiv z@AeobT=C*<5^L!Lm?2BaI3O_c1e9H-RQ@MRX+(AgeS?6Un#dy5{-ZyR-YyYKpFwI- z4ltgXXWtZUJLQ0+pMqJSM}A@#7q`JuW+orJSk+nfkwqNYc4beR&-{Uv7{|UBi=^by zEul)r?0mN(6!noEEe1k9`8d85qlB-*$zaykX2|Lra|rn?wVOlFJap@;A6~jkB9ZQe zT0{tu@wL1mhjhO&ts=g$0+|bvnI@bG0D{hV?+39HTJg=4TJ_Q!1V%G0ZoPDc`$ot zNJ9c8<1LrdsX`X0aPB9ul~fGHl0eBQYcu`55G7(nsNeV`c94?C<75RHulMX~EZ}=n zp?bk-f0T3LT~u(BX}$b2+eo58h2eeU3I%Bc=<2r+VE6shT_NhUX!&P*JbkakP8uUH zAmbHN$Mxvk=Ons(9!yM?dq<+vU)c6ZUYET%XLv8zAQ?CBuPlkS0k`x8iuY*!Ar^fq zm>AmR#ARf?*%jT~B%${O9}SWeE|MJq-6=Wbut}&fUYoqpifs}UzMZ-5G8p9g#HGj4=xu&z(pKT=#^SBb z(BIo+R4WHy@ZwDJEabP(50&q*KwWk*B?46NqSw@eHpqE*Gc<_T(lWOg;&aHVvW@!k z7=~`|_C=F-u-GaGS~K1chRdX#%}~$|#$XFX;_dNM-x?T9+rt=;=Zk|sr=J1_EIr6u zhB?8(uy?~7WR>?MsAi7>-QB^?>NNnAFU&m!v^!J5%Ykm=z~J#{bd4I->{OwLdsu-! z54~?tR%AA17`W3CZQsql>jKs6W?x2t{P@9bN?TNOfbC>$U{_RL9X$edJ`hJ4?1n`3Pds)QoXQ%z z!$A1&#P#jjlN!xl)~ikgs=a)VbqCS?kn?^qe7zM53i2AQ*NP-LXxxhuoDLHKL6KJM zY08p)?B)K0phw@Nmg`aWUcqd~_V#Ahao^X2A`(jb)p#ilL=tzcw=xzYuu$!YA(tT$ zNle=!O(iqk??7~5{Azcai2a^PD6sD~*6WkQYw6$F_*7Mpb)S&{yk{sn|9(XXx4_VAfk0hwL2p)F+~NNMd;ZUwk49! z_{x9Q{thL&6IIU6f80})jcx#!Fs01<)9@xdXQznZ=lqj z2QM8B^j!`L2lDWP(Nyl;K%(YO-(3c|k8Naf-vr+|bhG}3!v1QAhOM!cZcAWH$++N< zYdY*(4rtRYTeSRv1#0z>?SsE-;0ra^G(8MfbVe1mW&mX0qlOMW=tvnhhiZ`!{p(LC z?;05DPYd+%IkN>hAKW|kWDgW^21xvqNwObA9C|x216` z%-axE-*aRI9VTf(hq0G$si{!M*k*Ab$lbKJ%@B-c3s@4AbDx#0SZF46zweC%n>!=v zeJMI{pHYURp$-y zKrl``WZC)*ER){dQ3M&r3S|qRAK619p&^j^#1?IN#NN!}2tVhE8 z3BAuAP0=_TZbD^l=285>+`SS$zxA0~Lpk~a>N%jT}>AS)Vz3S!jZF{>-(@LW>T zdRt3_M_isT@7K7&SE87X?m?r><%zATb9znz`y!qiB{Llv^Dmz3MVG`qWn0==m>e~G z&mz?LQ77srXE0L#^Y;B{x)BY_AF_{bm^5N!$oTW&Ws}f_N6hBrQQU%%77+%SZauX^ zibw21D-_(`@XewvlqmqTN$CY?MQOr=Uy?Y)t7P01z9Jv`nuZ8gDU(V{*?UdapfFiM z#+mmPafUyTQd)DWdyXEvM;7%%sX{vFB#ORFg`kzwoe-r(t! z>*nb7b>aOw$qiO+CV(hG&HP(Zfys*BZ?Ib5#t9xKe~p|6J_)0iBHv-rq2zlR6`HJ^ zeuHg6Z9(Ud_KnR90Nq~<;03`kXOH7#>ej1&vZyo$v6^0=+XWc1Tc7se{HiV^kJD8J zx7ez?fZDTnlO8nfN!cp6<$(KD*ju7LH`zvY3Is3gdOC@6Ex08@M{cp0A>T>6yJwoh zP__W5iW^c=FoXtUGB})An2hb`l&K+d8v=C^dT^5|{u1=O+RFWj!EOX7u4NgL3JD=` z?+J}9JXh!k9}ug$Ek;dmv%=C5RLe`4+RGqx)opibx)VTh@|#zf%35_Bj+M9Bj$#5B z>BWD47D^E-9Yzq_^Jc6WI_En3{ZFWL0WlXl19m7S*8p-jc1xo$YO$NO%wpu^cd6ZnQ(9OfFc?tt% zJ+F`b3m(_0QN>|9^s$QRqk!ubG-^W!=ug#X#SsOvI_gC0{1*i?E*T$}CWf%*c+Zcp zzElOC?svqJe7c5ou@->pPZc@@i~?GEU1S#ri>s7q<>AH@!a;0R|CO&nQ_oQm>OYfA zz9^OWk&LSw?#vNNy#*DtPn9h#qb`6>7NNrGmZ*2N7^lYCvI_hiWFZhTb~SpP3456@ zT2sv)6ip(Jozwlzd6|M_Klj2u$rY0`96yeI%pc z@T)~(zxvTh`Y`?U{fWY6AO?U8z5kV!B+?OW_b%9OP+-MT3938FHcs*q5D}A%Ci|D1 zqgyUpz@Eg;A!*<@=PpEh3*koTD68GGKvvD8wQeZo3><|{SfKC6El|iwRziZ|6!~ts z#UtVHBtmt^g~y%FCnD*WTr>worl@wK{F@p8+M)+1*w&f|QPR6X;UQ`p0@!h!0fGS# zd-~WC2var1SayOXq!W~VzcW)ZA>hOks`-x<#x?S zgLDLV;Wt+3$Y-mPztp{eBP8&tVUvGDhMB!7JYxtg=`j%sKhHj6HVkZJ+HPJB^qlOT ziF`3ht5xe;2-~IwNJUnQ(dYBRhOew)ha7U3TxxoC2b`FQ>}=KY^GpZw8OC^{+%iEi z5!!T4f*PG;T9el=_Py{s1(Dm52Ei{N_a9O_o|bgYNgJ|SnV6oLKAh~4e?p%_98Jcy zm*2n}aPaa%I5cvEDWUu2oM0P}UH(EnaL#oqkg8*L-axkTsTwd6WQ@4i%>~6BXX!;= zSgE_xGZJpK)TrPjyby7k)r^*)Ks()>hY+mn*?3QwXx+~%7A)n8&Y!eISI)4EAP=Xm z=2YiFDPVw`r&;{11KLxzw8k_r3r@1E{Q_iUH>TSeyist{0Zlu_w(e8_uC-adOsIL# zm;k(aXX)7bdwqnJx_tt0w%$ zcF6OaE3S7ndlkO0J9xJ~8V(23SuQj177Xsk4M?eFEyn)y3 z(ef)SNG1RIBWyodo{TJBIB8EChj77#-8&}3sV-ZYP zfUGXE7f+W$2qx^Bluq7tJSFX zk{N1wiM5=!z{FvertpF6i>$>bU$5$YY+VOx$=XY-Rt*H#`G2^1*N}C!mu#Wj!8d07 z&h9w7!h*f$yMLs0odB=?&U~8$e|G=Ij$JQ zX}RY7sX^Qu4*R^5i*fZwjTugmYP@jic6${raFfATDqgm3i6iYa#aLvo3BwaknrZMx zd(CRR_>r9hK6K5x0e&jee6lMZoL$f@JAGKOuC*>B4PKtc)^d#w`^z=MjXiZZSD|U3 z(z)diZiS=q&YZPE$~a!TWdCl1Mjk3ME(XL|Pb=%14$2%lV>mYMFMYu!dyQZyvJuU9Kw znXV|ka7cC$7AZAlxSNlrBd+MI(R#$>4;%s?zKh5%^7Mjb0`VevY&A6f2kJPjqp=LW zmd{>>mrpjf7uR#g*6S1syhR4jYlgHlb{LaCJbNIM2>vaBZY+|~9lKH`Y z|3glk@K)^@|9_`kPuMLleb_%#1eFAZ{wLMVD?|#si zKQhspM5P<~iByzysE(u{>6b#6&m` zw|bGf!e_p)U5%Avh^O07Qgah2EBc8{U0Sh@PSW3K;{Fpcy|{N@5D>@aXw>c`Wc)pd zaD)y;=^faY$nyg`JUU0Cl@MDH#_7fIM}gurYQ?}Ixdl4EEcap^{O>n}jqb&GbeyJA z`fr}tCtkCLSSSb|N!Hlg5Zyhz%5(!DzDm;ZVN_XQ$_upxv$P6$iD>+_y)dz@l~V#7aX5^rD=!xza14gd>ll}`L`MiJmZ@t72kQM z(c;UmHLvF(tpeYFqlw2yOd$>n{?_<%{y6ll#+&>spBJaKz-a_*~LqX6Rs2z@bsR{Z2$1J?V zcTGc1ueeNBuQT0vNt(>7(B>KRtTzwm&s!L4oj42>obmT}nz4AIiB^fVA2d?1;r~hs zCz@*e{;#y~UNh~$`6{i;zYK#%O0?sDu*bmn#`gbLE8>7Fn#DNLLOUIseb6}JXlrc+ zp_h!MV;~k@Mri_Zej>D1`pC>=@%a3a#u9fsV=cuU9&6I^_B$F!TaWzVtiOncJ>0_! zzkC8wIsJ*I90yd{OR&!~jn=6&y_nSaGT2<;<1E=j(}j)%A9|`uGpq~7lOi>Z@Z@Kj zlSD!OhPAfPly`2Rm6+g}n}GBCSR1+#nBzDPtqq^-ru}5hr!>-7nqozrqcxu4ueHLL zbGQ&)P+)N50qKE_Gg@Xe8xYvCf77)78T|(~3l0trYT0;T#sD0c%gOo2N#ct282;^n z&9_r8RtL5b!+3Gy=!N`krnT!!x*k}UksRs7N_iSK_C z;~!T`-u?G7{QZao{U=Yn{ivk#f3H&gD%p;w1v=VckLF?(UiwvR_n%1oxv%0~2mf;! zUbSD+&g;KC7ZT!DP?VmLJscMvkjU`QZN)MibUA%owk;^VvJP6Pd2ssXoiQ)cW~f8{wis0zx%G_j*>t0mn71R z|53o6Ab^l^I_kuXPfZNUs{i`#rH%CIR;YJ zy#IU0WtO6mVjcYFigj;P4!qqQ$NjE+)hx&6#=Nx7@v}MK@SLNCD_^_QF%q^Tozf0} z4^oMEi{`4k7JPL-Rh9)06PCufHcTbKFyVvaS}4u2ONz>jZ`ef@C&H@UDqHfm(HyX} zvkYDtQ@}wZwUbJTn|4<1gTK}WHWEjv#^PCD)Gqw&aMel^KB}W?EysKHR!ud*FA`N^ z9CcUij4xdR_S!5b3IC#-3jTTtu(ab}CaMaJ`Qh=Z3}e1mvMQeA10z%p#yH+dz64** zlsl21MEHfHyaB(&LY{2N$BE>=96!%QzSsmmKBJKH#!C5miXh@)S++6%%1*wH+x1eu z)|j6wlXo@7HLKzK(68}|W>^($>%TH{3^<}H>I(`=e<|@o8Uvklp?&mRB4X`UxJ>*>^OdfN`$31oZRrTQF1x2?XCPp!^4_+ zE(R8MeBRH>IAa5P5H4RSHzPb${E&sp9Txm=!<6?q9GfH$fIn#ff3&PmmXkfMOP9l; zph|AemnSM8z<1BzQ#$Y|y_`B(Q$2am^8km#FDc;*gl{ROICY7<9X2j;a=^>)SgQHz z-<4%1IN*+@2=~0C98vET?EBEt1HZJklVayMurKIfY9yBV2m$ZPK^+flamICZtk6tBOkOvB5sLVUNrs_aIjY=kSvF$+Uu z?xpbHXC7C!HN)jkoHQ8Zcc3=o!DbU|aLRlqTRz8Hxma#6ipBgOr8)n~PdVBQ*L?zu zOZ^m1{3~~*j~PF;sWQwMe{ZT3@t+zhuX6uFa_~{^Hpbygojj>8fe=)Pi%t_Q`2vaZ zIfq|dcXBnj+8m$kK685D zXLT?!nJun3XQ9C_?y9BpPh0B959avBd&up8RuD?kzH)Os##(8KLBr<6;Y2qP29fOW zW|>+-R+wR%r%oRHxV28(;oGAPu}B<(tF4tf9>D!|xH1z$0&J!s5#r7}2P;2<_D`Hb zc!=b3Y9t7mw<0lK@m^(5z0E^_KJwu=o>!Dk;4dXR&Ek05o=%|{UwnjeC{lG{2m1^(2uwuM1G!fk*?^AJGwws zf?w_VTYVLNBK+5Bg)N?RT(Oa#_N(G|;~y};pHci`jBlJ)h$k}Y}EkhcSx0cR;K#dn62{5 zR88Zr7N`zM_&u2_Yh(Ucj;fV0Zg|m2&2Jv1^7=Pfeto{mMj(SHD{48sv>Xaq`>~2P zA|`>ZkK6*|a>Z&KmZz}frxhwTOW2Paauruh_&eE(DieJ2vr@vZTCCW}@#Ci|8rb33 zrHTSvFcmOD$`pM~aB4DC01FcZ7R`^9Th7mt55c_?ZEUeLUuA)(M#-f-*Fzp`%3H?D z9gO*^Jh|EwU+e1RgzGZpY7D=_pFJSy2({0(K3!OulV-``Xmbk!wDOD63{U@}RL{?W ztH!{^P(pqC%D-E%z4Kx$P>I`Z?1;A}%9j(O7P!D(-Ivs;wtW96`50qXSkDh--ZiW3G%DIpt&YfNk`k`vCHLfi8}m~Z$d{Vnu#1q>gdzrt%?x?!##daF_p`(MqHXMO z%~5$859PPO7}pJ!t02GS*f++;g3ta{zSWvgkK(fq$^AKOe^kDi1}G1q-2u$_24r}U ztQcvG%f$*EJ|Cwri(+7et zz)3ORn4cz9ED_<#J_=L5#$9p2nEy?scp^5$gow`?CD)2D{OiRcgQj|&7`lm?Z?^Wh za-|}W?=w{X+zb!uZ%gyln*ZgEysa(uF$N!t_`UPw%Z>R7E; zD4dW=su*KR(ohz|`C*^ce>mW}11cLHm}yP^mo40zRaFRUJN~A<`htQ)M{82y#XM-) z$ry*(tMhPtH?<7MRmr`nM0GOQ{{v+pbG}UN%!8!4#<uVGy3eZRHuz9mwId%MqYg60HeG;h;B2)Y(bf*nih(xBrJeedIX=@` z?SR#L)Zutcj9SiHwNW21;Y;GwAsnu1tA?_GS ziq!p0e`pWLZt_DG!>*vi6?xE>d>#m%oHa~6oR|KeuAVzSs$%PJX7|qByV+Z|C1E$D zKtLc8Lg+07q(dMm50D~7MU(&m>Agx52_iXkB2pBEghx3~K*|$Dg+Nqlf+3+iI*B5n zl<=Lqn-}@Mf9`MY+%q$0&YU@O+U|Vp_gE_S#sD~!<`078*7r(Z%<96iE6ljDO4{uY7CL>xHHe}=pS zadeoOJ>$FM{gWBw#t{w`!(sXiKR4h|Dv`!yV-L?jH*WoTR*VRQ;39 zQtzPl_u#g_riDbgZwh40_H}~hQw;^Xf8*;=$<$3;5FJ~|j_+srmRNu^gP^vL-=6ht zjYfh$7GbI!4FhTn=x2!Ow{G(d)^Yn*-vb8gW|NI@Y9A5BzR|Z`z_zb^3(WnYz>;EW z)TG$|CXy~tm+sEFLR?EKNllEt`~x86iod?8o6$AAxx}%# zMP&V~y+Ul8Tdde&=V@ZYJw^W6aJ1Ne3lcUH>t*fpcgOVa{DXLaTafVCYzdMlyC*U5 zU*DSplJ*f6K=p-{oxj;X-5K13=03-D2mO--Tzca7AhG(i@=l1xcPiB3a$O}rw6Y)l zbcmYf*OpJ)hg#r`sL&)xT@y;05$T#hx@PzbJU+QEos})?g|2a%XqH=5E92x?{4OdK zTyWGWzX>@_Wf?0+g!UjG%7#ic!P=gT9x@AEK(^gWV9X?`-~VDrA6PTOtKn<1?F7eq zLzTBAh^#^)enw5Z4Kn^s8gkJ|TMhG7hpgjD6|Cd3pQg`e6@iO~E4xEr?L!;6<>xBS z3ZOQquUBLp);+X||NEr@M2}EHu-^iOE@~>!6%`MyQl65J)GXect3+9VnuY^8%Own5 zqf&5MB4X2p>p!pD3U$r;4LezaF z7KfB52N*VdqTJ#^&`Xb%?htiZF<{YUrN3#U^n*@Ul-GiG1lcDO1IXjCE6QMIS~qq` zohOqCQN9{&f>X+RUsmwxb)|z0QP*f4P8TbEq0TjhZm%0Fdm-{N)say`zW>u?vqfA{ z;$cFu(g2D~<0gkFX5v6ORxkD+BpYtKrVMAe{!`ms4)J!IjD;^NpX-p*L9ye-JGR+X zV0m-pFDQDM7^UD(A33@^DYZG&w%cs@VGAXe3`1Sf@qUc*)E;0OAf}1Mc~MFldGK_l zL`Ujt4#Qp%woY^L&;^@9VT)ZhFU-)0e;XgPbtLclE?XGR*ko%+>$8b)Sh(M|h2!pB zwp=*?mN`Op21bW(H`+F1=6NO0hBJ@aZj#gR0x=4ai=_CAZ0rl-B2S8lMEd%32o%<^ zdtmVnCBo!d12S5MIWT3rGS~vqCvDX-Puikk`;SV83eq=#CeAPwrG3ghVS>b(pI45*s4J3bHxuiF1r;nt}AY&t@T*O>>bJ)8xqi-5Ey#F_Rink z<7m6>x-*D>R!uu;<6V@={)%@&z7}Gx0TY!}M}<)!V8VqE6_(_M2+SWTe`kT5Q?d<* zAC_YU8uR3FaWL%{xthr|A^80<*-F~0xf>$$}X z`9S{3)VjP_*COvAr%Q_52wU`*my*U_m73RWE-$bJYhUXiua)555ZQ|kkNi1ZsBHoq zYD4pa!r`(5`gJCS;S*hU!@}B}%*1a!baGvxbADT~;)UpH1Wj;cBqj^Ft}WO+zM4Et z;GU}T1cv`^Cr@HX1C)~SD9J-Hqlz5Of`fmq$)t+~u)FP(H_1p+`C|jAqJxYimfLeo z`G=e-XH-gpY(So%4r{XkeQ^hXw zbHxigq&%~B#+D!nE6Pix#x`tp)9UrW^N$Fpwlr54mW~rUP1Q(L!soq3Pol3^RL5Uy zh<@aBsU{pav%4te_$)~@CJn5%2!Xr=Q3Ed3B_~F|MA3roo2GI8u4`6Dj4;2BgFh3* zXy_R)(qU^Idc-xM$qr2vbzf+)AVG|T^xE{*XO@a;3kgQnrB^Dw0;w^=j|)1Ax5_aY z)6}D;)1xWSP$BSD6Y(b0Z$Wi=je-vu7BnCnwzd#sA*DIt(5^A{J-ne91=s9L`qbs*GrwAjTkhFz+Ox(uJB2!laZB#nA6lGf52BbvSN znjI~Mz@#Yg4s44QxlkM>J`2zw*io!5=(Htvi-J1qQNy5eZq+b>M zzEnyq?;!DnMBG;AlJp)ey(xZvye^TEL=`gbd`hDQ$64_oYJpv?g2Zf-0;xH}v`11h zX>MJF8)>)_3!cxGULp&~lv|sNsEGG}m0Huv-jm)b7ia%rCeu`4ghS3r$1TAY)xMV^ zf}fC(ZEJY}YT!-js0G)2Crv5G@X=+demPq0e~`u!s})O)7(RU}zaBx81hrBzkcxKH|gXe`i zO-jPnSNVM>9^T;?LEfZ&4uN-0IJR1$_AW;qoR#l5pd%Gm$&h@@(Fi{>)D{xxcO5k$ z*{=@Dyz59t!PLfzSyva!es`R+;aR(yZ-uHpbs*%~RT+Nvs=b4ywsv)q3HcIE2~|HO z!@_Jd{+8n~dB**eSai^*&V(FUwQV``#35xex|Vh9?~dMqLXNH$Vls^^1#x@0&vDPd z%%2>)C`RK~>v~|?bi;;?^VKhP)FZWb$$*tL(pP=j4;IMp<-XYbk+2Aji_Byl_ z+9>Gj(pH!y1Mj%Cu5^kE?SToi5I4!%8auu*iqWc_A3--N_a4Fi4fBVyJoJrZZ8(gc z%BsVXOlHS7GueEy3zyOLl>7HH8y=j-26+NSprV3TkFamaTUD7(;8Pyn7W`Fo1%GjI znf;$|QZ#h?mllHmtj?!1m|Be=gDKT`N9gb6Tk)GPK82$k%D-YT>=Vi-NG>F0rMNP` zL)yfCCgDLpCpTpsA78{UH-_hsm$3%-K=LP~P}TABcv$m0-L9EijgND+fK703#`?7DrAr35_Wa) ziwy7TJdHM10U@|f=iMMamDA7sb*^DYjrU*>DbYA9&$A6M^%HKx9|TX3kSaeTnQ?>l z#QZy~Kf{!ZOkI3K|9wZ~M3 zfepD8*2fdx123{A&>)}fgZwtM0MW%P0}Af3-w3l5bQGHnPds4M@ZIZX+3_Fq`P~iH zhvED`*>Q%w3t2A`R>{53@3_idDxh?r@JlK?z*SU2fuVO`_*k=|vOB?@fq zBvya+1^0u#hxjG+0$or^>|{gjD;_7CUt;k6U7X@9O?GlRLI`RvY){e2uHMD#VdPex z{WqDHqGvq&R6yB&77N=pkeUBaCx3`TGd(YHi21@Z5q)2KzO`V=DG#YAp*v~eqrUNY zQT@@=P{PEOo@+Y9f8!C@>8R&hE3W#&Gf9WJD?HoGhk;l2ds@-e$oZ-cjlOVHhM_~f z1{}$D#Nmm$baOnL41*Sg)xbaMsG}IXH^@t2v-xT`4mzuTPfqw1YArZ>M*SGxJENw- zp+jmz7~Y;f-#MVB*|BJ&y3-EZ+pBgscUV;+>abb~4vki2?277r0fl?iCb%qH?J8sM zAJh;B%vhk3ldiXF!y|pwF`-Dsm&rN1QOz)$2}l~PM!}x>>IFy}t=chbnHnomU`Wjt zcw~ioiQ}sq)TazG=Bw2qb+u~X@X_kap*Zm)wX%du7O0o4n9)uxb3&^FY6|RsOV#i} zW11y8+N#4C+-XTo@>{Ek9PS-f1wL)5?h1s}3|JrGP~eSM9L^wtE;VpOLf=k~>ZttW zITV659(yRtM$b@;vv|8&adwF(&w{MX09K9F$NRmi%xr7c!LMnc3f#;pQHoVZ!+m^wN1{BE1 zYU-_wvzmJw2vFk6yTleFyx(!$KhdjN;6@vjg4iVJJ?$Kc@Oa%$uDaQp0I7*K!~FCIl=fCBq4J*g8k{K8L(Q*Mp=2h-2LJmS zfy?J;Ej(r*Ux6RX^l%d<0ylNnI_OA{7s!O%sU>26)MOtN{jAl%mKU`5$&{udB&fO# zQ#NX;G_d(@+|m@xTCHue;faeH>HSO2Xl1x|vzEu;?pcbA{kB!hvcdERTB86g4D3c) zKp|>gNb>k~61iF;{bXS)^|JV^wvglS&$THWsBFjm`I^DaraFfGLL=f^KWQ5oF5RiE zvcTn4+8l`fgXn(TqSp?EjP6=U#k-%fTOMkks3sv^$1c;fp``NM(p<3mruG4xzN_^M zzIM2)eP}8I3hW(4nY_(zk+A;ewzep!9l(NI&5cw^a)vr;X427%Q@d%m8BBXfF5|#^ zlP9#VtbyX{0jOn5c~);rfBuZ#n)Jhvm0nJugWu%@@@;x0jI(Juh(z^4^j+$Zq`GX5u zoTE(hf^R+KpX{W)f+>~tqgJ4&fi|Ds)`Cl;w0ycT{kUocO>#wp(b^S)A8d8b>yFX$ z+@~Xep5#QI?!HdiFeQ4oo#4*VAb&K4Xj*!mHeCF%r@&&ma-0zT$VK>n;oJu${oL>1 zSF!E^Hb}k^(GZ@0<kH z`oSTa`xSK7cCYcl`fR6!OBZ>r379;}9T$9Wy1?@)1?_gY6u5TP6^4Btxo(Ew9}iqZ z$Ye_JNXV|a?i+CZs>_G{E$&aP<~%5i!R>j?7M$qdIi6pFwUBy`=1EESPDtMAiZpvA z@osi9d7FC$#npGX{P@}}cV%nvlle~x_@sqr7^P}nbRUCDm)wz%)hxn^bNrt9H1Al? zFc=u_bm62T_cfdO{e8%L&!gcl_uSoL&G%}M`h(kntKRc`LmJ+3wLVg#mBHfQbTZM| zgvn>zx*Tj|{xASHwDBCP1_N%oLonu7_Z9MTRbXyrv?BFfY&XB}GanALZFK)-gVNVs zD$c6xs^Z3nZWm?AL=SW=P@vT961XwJby~u&Te*_L@r!p|!#FhRK-xVmsxR1XT0>W} zkRa@{*If|;)u&EUhYmfZ;q;}hc`O*kEn4eZEuejd>qnft(e;f5B8rGI0rDMK?xGaz zUuRQNVb;g4YdqK>>2sH1h0@b*8$?_tqFy}ZYO7%EW!JyRbSkg4!SAPB9&B*IMRtiN z%~dNFiCYUa7`ek$?e7HvUy++w*v#;ak0zGCGbtkaZ%QJu;dm>~J>hC+L29V`aq}qm zA_?eO-CVl?0a(l(=H5=3jnj=Vpv1#4TrpUG#fp^lMxOG5A-XRdDEFLMyK8rJt}vpFhdznvkMb!J&!zE(_Lct;dJpgao~m@=FpZ z*j{}@DCMW0qtL{(6#Y$H`>I}M#S1Ca1MF2PCC1!j;J+E-1VW<@irxB24gQ;ZTUon-vL;8F}(rn3hd^(4#~ zYkY3OJ?|L#Dm0#Gl;ImkbYBd1yswj)Y2pWjpACm48FaQ0l75(4;<01b=0;_X8+sa) zelnt~AuzXtu|>keb&XmUENWtOq~wa;#t<3n^e~2#dpMa$M5B7~c720Pu|ShY0}S#U z2WYS+*@z=sE7_=QjuC>38yQg?35q;VL^%njT_Z{)-PA)UoiDeRDuF z3j^Dg5G}CKfHy+Iv2zdSZql~s&LK$e?Cb@(J4gsD`A3-eZ>nS!NbW{ilD9iK=fa2K zPD*Pmaw`!15((-*Iy?X3|G)CHZq7?&4QS4EQ#Q%Cx{9->72WNfCoGx0oVB1!X=pfp z*~U4Pk^>S+y3b8^UZrJ9bk;%Yw0|W6YGa0LDYg^ttZ(*S14zD6rf`wliF^AyFLe%d zTmgp!M;i06HUt>=Nw_bN7>k{HIr|287#><-D3rCBVYqPXd}EJ& Date: Thu, 15 Mar 2018 22:13:06 +0100 Subject: [PATCH 19/30] Updated README --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 76761fc..06a9257 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,7 @@ You can use it e.g. to help you with an OXID 6 installation or deployment, e.g.: ``` This could activate some modules in different subshops and set a bunch of config variables e.g. +__Please note:__ since activating modules and updating config values requires a filled database and a valid config.inc.php file, you'd have to do some more voodoo during the OXID setup routine to make the above example work! But it should give you an idea how to use oxrun "multiactivate" and "multiset" :) # Available commands @@ -247,7 +248,7 @@ config:shop:set config:export --------------- -* Description: Exports all config values as yaml files, interacts with the [Modules Config](https://github.com/OXIDprojects/oxid_modules_config/) module +* Description: Exports all config values as yaml files, interacts with the [Modules Config](https://github.com/OXIDprojects/oxid_modules_config/) module [__which currently isn't fully ported to OXID 6 yet!__](https://github.com/OXIDprojects/oxid_modules_config/tree/dev-6.0-wip) * Usage: `config:export` ### Arguments: @@ -276,7 +277,7 @@ config:export config:import --------------- -* Description: Imports all config values from yaml files, interacts with the [Modules Config](https://github.com/OXIDprojects/oxid_modules_config/) module +* Description: Imports all config values from yaml files, interacts with the [Modules Config](https://github.com/OXIDprojects/oxid_modules_config/) module, [__which currently isn't fully ported to OXID 6 yet!__](https://github.com/OXIDprojects/oxid_modules_config/tree/dev-6.0-wip) * Usage: `config:import` ### Arguments: @@ -368,7 +369,7 @@ Requires php exec and MySQL CLI tools installed on your system. * Description: Raw output * Default: `false` -install:shop +install:shop __[DEPRECATED!]__ ------------ * Description: Installs the shop From 2c61d8cea9dc2d3321d50ecdef425bbe45b43ae3 Mon Sep 17 00:00:00 2001 From: Stefan Moises Date: Fri, 16 Mar 2018 12:55:38 +0100 Subject: [PATCH 20/30] Make code more OXID 6 compatible, cleanup --- .gitignore | 3 +- README.md | 21 +++++++ composer.json | 9 +++ src/Oxrun/Application.php | 9 ++- src/Oxrun/Command/Cms/UpdateCommand.php | 10 +-- src/Oxrun/Command/Config/GetCommand.php | 4 +- src/Oxrun/Command/Config/MultiSetCommand.php | 6 +- src/Oxrun/Command/Config/SetCommand.php | 8 +-- src/Oxrun/Command/Config/ShopGetCommand.php | 2 +- src/Oxrun/Command/Config/ShopSetCommand.php | 2 +- src/Oxrun/Command/Database/DumpCommand.php | 40 ++++++------ src/Oxrun/Command/Database/ImportCommand.php | 8 +-- src/Oxrun/Command/Database/ListCommand.php | 10 ++- src/Oxrun/Command/Database/QueryCommand.php | 16 ++--- src/Oxrun/Command/Install/InstallCommand.php | 20 +++--- src/Oxrun/Command/Ioly/IolyCommand.php | 2 +- .../Misc/GenerateDocumentationCommand.php | 12 ++-- .../Command/Misc/PhpstormMetadataCommand.php | 14 ++--- src/Oxrun/Command/Module/ActivateCommand.php | 61 ++----------------- .../Command/Module/DeactivateCommand.php | 61 ++----------------- .../Command/Module/MultiActivateCommand.php | 2 +- src/Oxrun/Command/Route/DebugCommand.php | 9 ++- src/Oxrun/Command/User/PasswordCommand.php | 8 +-- src/Oxrun/Command/Views/UpdateCommand.php | 4 +- src/Oxrun/Traits/ModuleListCheckTrait.php | 6 +- .../Command/Database/DumpCommandTest.php | 7 ++- 26 files changed, 154 insertions(+), 200 deletions(-) diff --git a/.gitignore b/.gitignore index 573c1e2..ba2c229 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ .idea clover.xml -vendor \ No newline at end of file +vendor +.vscode-upload.json diff --git a/README.md b/README.md index 06a9257..e267cb2 100644 --- a/README.md +++ b/README.md @@ -654,3 +654,24 @@ views:update * Description: Updates the views * Usage: `views:update` + +route:debug +----------------- + +* Description: lookup a SQL Url in the database and check which controller is associated etc. +* Usage: `route:debug http://myshop.de/my/url` + +### Arguments: + +**url:** + +* Name: url +* Description: SEO URL + +### Options: + +**shopId:** + +* Name: `--shopId` +* Is value required: no +* Description: diff --git a/composer.json b/composer.json index 5558a01..478466a 100644 --- a/composer.json +++ b/composer.json @@ -2,6 +2,7 @@ "name": "smxsm/oxrun", "description": "Oxrun provides a cli toolset for the OXID eShop Community Edition", "license": "MIT", + "version": "v0.2.2", "support": { "issues": "https://github.com/marcharding/oxrun/issues" }, @@ -40,6 +41,14 @@ { "name":"Stefan Krenz", "email":"info@kyoya.de" + }, + { + "name":"Tobias Matthaiou", + "email":"matthaiou@tobimat.eu" + }, + { + "name":"Stefan Moises", + "email":"moises@shoptimax.de" } ], "bin": ["bin/oxrun"] diff --git a/src/Oxrun/Application.php b/src/Oxrun/Application.php index 9dacd63..5668367 100644 --- a/src/Oxrun/Application.php +++ b/src/Oxrun/Application.php @@ -69,7 +69,10 @@ public function __construct($autoloader = null, $name = 'UNKNOWN', $version = 'U */ protected function getDefaultCommands() { - return array(new HelpCommand(), new Custom\ListCommand()); + return array( + new HelpCommand(), + new Custom\ListCommand(), + ); } @@ -247,9 +250,9 @@ public function switchToShopId($shopId) $oxidVersion = $this->getOxidVersion(); if (version_compare($oxidVersion, '4.9.0') < 0) { // old OXID versions - $oConfig = \oxRegistry::getConfig(); + $oConfig = \OxidEsales\Eshop\Core\Registry::getConfig(); $oConfig->setShopId($shopId); - \oxRegistry::set('oxConfig', $oConfig); + \OxidEsales\Eshop\Core\Registry::set('oxConfig', $oConfig); return; } diff --git a/src/Oxrun/Command/Cms/UpdateCommand.php b/src/Oxrun/Command/Cms/UpdateCommand.php index 563a614..5b2b6a8 100644 --- a/src/Oxrun/Command/Cms/UpdateCommand.php +++ b/src/Oxrun/Command/Cms/UpdateCommand.php @@ -38,7 +38,7 @@ protected function configure() */ protected function execute(InputInterface $input, OutputInterface $output) { - $oxContent = oxNew('oxcontent'); + $oxContent = oxNew(\OxidEsales\Eshop\Application\Model\Content::class); $oxContent->loadByIdent($input->getArgument('ident')); if (!$oxContent) { @@ -49,13 +49,13 @@ protected function execute(InputInterface $input, OutputInterface $output) if ($input->getOption('language') !== false) { $language = $input->getOption('language'); } else { - $language = \oxRegistry::getLang()->getBaseLanguage(); + $language = \OxidEsales\Eshop\Core\Registry::getLang()->getBaseLanguage(); } $oxContent->setLanguage($language); if ($input->getOption('title')) { - $oxContent->oxcontents__oxtitle = new \oxField($input->getOption('title')); + $oxContent->oxcontents__oxtitle = new \OxidEsales\Eshop\Core\Field($input->getOption('title')); } if ($input->getOption('content')) { @@ -64,11 +64,11 @@ protected function execute(InputInterface $input, OutputInterface $output) } else { $content = $input->getOption('content'); } - $oxContent->oxcontents__oxcontent = new \oxField($content); + $oxContent->oxcontents__oxcontent = new \OxidEsales\Eshop\Core\Field($content); } if ($input->getOption('active')) { - $oxContent->oxcontents__oxactive = new \oxField($input->getOption('active')); + $oxContent->oxcontents__oxactive = new \OxidEsales\Eshop\Core\Field($input->getOption('active')); } if ($oxContent->save()) { $output->writeLn("Content with ident {$input->getArgument('ident')} updated."); diff --git a/src/Oxrun/Command/Config/GetCommand.php b/src/Oxrun/Command/Config/GetCommand.php index d4a0015..30479e7 100755 --- a/src/Oxrun/Command/Config/GetCommand.php +++ b/src/Oxrun/Command/Config/GetCommand.php @@ -36,7 +36,7 @@ protected function configure() */ protected function execute(InputInterface $input, OutputInterface $output) { - $oxConfig = oxNew('oxConfig'); + $oxConfig = oxNew(\OxidEsales\Eshop\Core\Config::class); $shopConfVar = $oxConfig->getShopConfVar( $input->getArgument('variableName'), $input->getOption('shopId'), @@ -45,7 +45,7 @@ protected function execute(InputInterface $input, OutputInterface $output) if (is_array($shopConfVar)) { $shopConfVar = json_encode($shopConfVar, true); } - if( empty($shopConfVar)){ + if (empty($shopConfVar)) { $shopConfVar = 0; } $output->writeln("{$input->getArgument('variableName')} has value {$shopConfVar}"); diff --git a/src/Oxrun/Command/Config/MultiSetCommand.php b/src/Oxrun/Command/Config/MultiSetCommand.php index c4bf35f..0c521f7 100644 --- a/src/Oxrun/Command/Config/MultiSetCommand.php +++ b/src/Oxrun/Command/Config/MultiSetCommand.php @@ -53,13 +53,13 @@ protected function configure() */ protected function execute(InputInterface $input, OutputInterface $output) { - // do not use the registry pattern (\oxRegistry::getConfig()) here, so we do not have any caches (breaks unit tests) - $oxConfig = oxNew('oxConfig'); + // do not use the registry pattern (\OxidEsales\Eshop\Core\Registry::getConfig()) here, so we do not have any caches (breaks unit tests) + $oxConfig = oxNew(\OxidEsales\Eshop\Core\Config::class); /* @var \Oxrun\Application $app */ $app = $this->getApplication(); - // now try to ready specified YAML file + // now try to read specified YAML file $mallYml = $input->getArgument('configfile'); $ymlFile = $app->getShopDir() . DIRECTORY_SEPARATOR . $mallYml; if (!file_exists($ymlFile)) { diff --git a/src/Oxrun/Command/Config/SetCommand.php b/src/Oxrun/Command/Config/SetCommand.php index 6e61ffd..3b659f1 100755 --- a/src/Oxrun/Command/Config/SetCommand.php +++ b/src/Oxrun/Command/Config/SetCommand.php @@ -38,8 +38,8 @@ protected function configure() */ protected function execute(InputInterface $input, OutputInterface $output) { - // do not use the registry pattern (\oxRegistry::getConfig()) here, so we do not have any caches (breaks unit tests) - $oxConfig = oxNew('oxConfig'); + // do not use the registry pattern (\OxidEsales\Eshop\Core\Registry::getConfig()) here, so we do not have any caches (breaks unit tests) + $oxConfig = oxNew(\OxidEsales\Eshop\Core\Config::class); // determine variable type if ($input->getOption('variableType')) { @@ -47,9 +47,9 @@ protected function execute(InputInterface $input, OutputInterface $output) } else { $sql = sprintf( "SELECT `oxconfig`.`OXVARTYPE` FROM `oxconfig` WHERE `oxconfig`.`OXVARNAME` = %s", - \oxDb::getDb()->quote($input->getArgument('variableName')) + \OxidEsales\Eshop\Core\DatabaseProvider::getDb()->quote($input->getArgument('variableName')) ); - $variableType = \oxDb::getDb()->getOne($sql); + $variableType = \OxidEsales\Eshop\Core\DatabaseProvider::getDb()->getOne($sql); } if (in_array($variableType, array('aarr', 'arr'))) { diff --git a/src/Oxrun/Command/Config/ShopGetCommand.php b/src/Oxrun/Command/Config/ShopGetCommand.php index 6ce53f6..71509fd 100644 --- a/src/Oxrun/Command/Config/ShopGetCommand.php +++ b/src/Oxrun/Command/Config/ShopGetCommand.php @@ -36,7 +36,7 @@ protected function configure() protected function execute(InputInterface $input, OutputInterface $output) { // Shop config - $oxShop = oxNew('oxShop'); + $oxShop = oxNew(\OxidEsales\Eshop\Application\Model\Shop::class); $oxShop->load($input->getOption('shopId')); $varibaleValue = $oxShop->{'oxshops__' . $input->getArgument('variableName')}->value; $output->writeln("Config {$input->getArgument('variableName')} has value $varibaleValue"); diff --git a/src/Oxrun/Command/Config/ShopSetCommand.php b/src/Oxrun/Command/Config/ShopSetCommand.php index 54c39a7..982af97 100644 --- a/src/Oxrun/Command/Config/ShopSetCommand.php +++ b/src/Oxrun/Command/Config/ShopSetCommand.php @@ -36,7 +36,7 @@ protected function configure() */ protected function execute(InputInterface $input, OutputInterface $output) { - $oxShop = oxNew('oxShop'); + $oxShop = oxNew(\OxidEsales\Eshop\Application\Model\Shop::class); $oxShop->load($input->getOption('shopId')); $oxShop->assign(array('oxshops__' . $input->getArgument('variableName') => $input->getArgument('variableValue'))); $oxShop->save(); diff --git a/src/Oxrun/Command/Database/DumpCommand.php b/src/Oxrun/Command/Database/DumpCommand.php index d5e6935..53cb7c7 100644 --- a/src/Oxrun/Command/Database/DumpCommand.php +++ b/src/Oxrun/Command/Database/DumpCommand.php @@ -71,10 +71,10 @@ protected function configure() 'Do not export table with person related data.' ) ->addOption( - 'withoutTableData', - 'w', - InputOption::VALUE_REQUIRED, - 'Table name to dump without data. Use comma separated list and or pattern e.g. %voucher%' + 'withoutTableData', + 'w', + InputOption::VALUE_REQUIRED, + 'Table name to dump without data. Use comma separated list and or pattern e.g. %voucher%' ); $anonymousTables= implode('`, `', $this->anonymousTables); @@ -118,10 +118,10 @@ protected function execute(InputInterface $input, OutputInterface $output) $canDumpTables = true; $file = $input->getOption('file'); - $dbName = \oxRegistry::getConfig()->getConfigParam('dbName'); + $dbName = \OxidEsales\Eshop\Core\Registry::getConfig()->getConfigParam('dbName'); - if($input->getOption('ignoreViews')) { - $viewsResultArray = \oxDb::getDb()->getAll("SHOW FULL TABLES IN {$dbName} WHERE TABLE_TYPE LIKE 'VIEW'"); + if ($input->getOption('ignoreViews')) { + $viewsResultArray = \OxidEsales\Eshop\Core\DatabaseProvider::getDb()->getAll("SHOW FULL TABLES IN {$dbName} WHERE TABLE_TYPE LIKE 'VIEW'"); if (is_array($viewsResultArray)) { foreach ($viewsResultArray as $sqlRow) { $ignoreTables[] = $sqlRow[0]; @@ -219,17 +219,17 @@ protected function execute(InputInterface $input, OutputInterface $output) */ protected function getMysqlDumpCommand() { - $dbHost = \oxRegistry::getConfig()->getConfigParam('dbHost'); - $dbUser = \oxRegistry::getConfig()->getConfigParam('dbUser'); - $dbName = \oxRegistry::getConfig()->getConfigParam('dbName'); + $dbHost = \OxidEsales\Eshop\Core\Registry::getConfig()->getConfigParam('dbHost'); + $dbUser = \OxidEsales\Eshop\Core\Registry::getConfig()->getConfigParam('dbUser'); + $dbName = \OxidEsales\Eshop\Core\Registry::getConfig()->getConfigParam('dbName'); - $dbPwd = \oxRegistry::getConfig()->getConfigParam('dbPwd'); + $dbPwd = \OxidEsales\Eshop\Core\Registry::getConfig()->getConfigParam('dbPwd'); if (!empty($dbPwd)) { $dbPwd = ' -p' . $dbPwd; } $utfMode = ''; - if (\oxRegistry::getConfig()->getConfigParam('iUtfMode')) { + if (\OxidEsales\Eshop\Core\Registry::getConfig()->getConfigParam('iUtfMode')) { $utfMode = ' --default-character-set=utf8'; } @@ -242,9 +242,9 @@ protected function getMysqlDumpCommand() ' --opt' . ' --hex-blob' . $utfMode . - ' %s ' . # argumment part + ' %s ' . // argumment part $dbName . - ' '; # bash part + ' '; // bash part return $mysqldump; } @@ -260,6 +260,7 @@ public function isEnabled() /** * @param string $flag * @param array $tables + * * @return array */ protected function addCommandFlag($flag, $tables) @@ -281,7 +282,7 @@ protected function filterValidTables($tables) { $whereIN = $whereLIKE = []; - $dbName = \oxRegistry::getConfig()->getConfigParam('dbName'); + $dbName = \OxidEsales\Eshop\Core\Registry::getConfig()->getConfigParam('dbName'); foreach ($tables as $name) { if (preg_match('/[%*]/', $name)) { @@ -305,10 +306,13 @@ protected function filterValidTables($tables) $sqlstament = "SHOW FULL TABLES IN {$dbName} WHERE $conditionsIN $conditionsLIKE"; - $result = \oxDb::getDb()->getAll($sqlstament); - - $existsTable = array_map(function ($row) {return $row[0];}, $result); + $result = \OxidEsales\Eshop\Core\DatabaseProvider::getDb()->getAll($sqlstament); + $existsTable = array_map( + function ($row) { + return $row[0]; + }, $result + ); return $existsTable; } diff --git a/src/Oxrun/Command/Database/ImportCommand.php b/src/Oxrun/Command/Database/ImportCommand.php index 952375a..39436fc 100644 --- a/src/Oxrun/Command/Database/ImportCommand.php +++ b/src/Oxrun/Command/Database/ImportCommand.php @@ -48,17 +48,17 @@ protected function execute(InputInterface $input, OutputInterface $output) } // allow empty password - $dbPwd = \oxRegistry::getConfig()->getConfigParam('dbPwd'); + $dbPwd = \OxidEsales\Eshop\Core\Registry::getConfig()->getConfigParam('dbPwd'); if (!empty($dbPwd)) { $dbPwd = '-p' . $dbPwd; } $exec = sprintf( "mysql -h%s %s -u%s %s < %s 2>&1", - \oxRegistry::getConfig()->getConfigParam('dbHost'), + \OxidEsales\Eshop\Core\Registry::getConfig()->getConfigParam('dbHost'), $dbPwd, - \oxRegistry::getConfig()->getConfigParam('dbUser'), - \oxRegistry::getConfig()->getConfigParam('dbName'), + \OxidEsales\Eshop\Core\Registry::getConfig()->getConfigParam('dbUser'), + \OxidEsales\Eshop\Core\Registry::getConfig()->getConfigParam('dbName'), $file ); diff --git a/src/Oxrun/Command/Database/ListCommand.php b/src/Oxrun/Command/Database/ListCommand.php index dd83cb6..ef6b927 100644 --- a/src/Oxrun/Command/Database/ListCommand.php +++ b/src/Oxrun/Command/Database/ListCommand.php @@ -64,7 +64,11 @@ protected function execute(InputInterface $input, OutputInterface $output) if ($input->getOption('plain')) { $quote = "'"; - $existsTable = array_map(function ($row) {return $row[0];}, $tablenames); + $existsTable = array_map( + function ($row) { + return $row[0]; + }, $tablenames + ); $list = implode("$quote, $quote", $existsTable); $output->writeln($quote . $list . $quote); return; @@ -86,7 +90,7 @@ protected function filterValidTables($tables) { $whereIN = $whereLIKE = []; - $dbName = \oxRegistry::getConfig()->getConfigParam('dbName'); + $dbName = \OxidEsales\Eshop\Core\Registry::getConfig()->getConfigParam('dbName'); foreach ($tables as $name) { if (preg_match('/[%*]/', $name)) { @@ -110,7 +114,7 @@ protected function filterValidTables($tables) $sqlstament = "SHOW FULL TABLES IN {$dbName} WHERE $conditionsIN $conditionsLIKE"; - $existsTable = \oxDb::getDb()->getAll($sqlstament); + $existsTable = \OxidEsales\Eshop\Core\DatabaseProvider::getDb()->getAll($sqlstament); return $existsTable; } diff --git a/src/Oxrun/Command/Database/QueryCommand.php b/src/Oxrun/Command/Database/QueryCommand.php index f415560..1ea7c0a 100644 --- a/src/Oxrun/Command/Database/QueryCommand.php +++ b/src/Oxrun/Command/Database/QueryCommand.php @@ -64,17 +64,17 @@ protected function execute(InputInterface $input, OutputInterface $output) $query = $this->getEscapedSql($input->getArgument('query')); // allow empty password - $dbPwd = \oxRegistry::getConfig()->getConfigParam('dbPwd'); + $dbPwd = \OxidEsales\Eshop\Core\Registry::getConfig()->getConfigParam('dbPwd'); if (!empty($dbPwd)) { $dbPwd = '-p' . $dbPwd; } $exec = sprintf( "mysql -h%s %s -u%s %s -e '%s' 2>&1", - \oxRegistry::getConfig()->getConfigParam('dbHost'), + \OxidEsales\Eshop\Core\Registry::getConfig()->getConfigParam('dbHost'), $dbPwd, - \oxRegistry::getConfig()->getConfigParam('dbUser'), - \oxRegistry::getConfig()->getConfigParam('dbName'), + \OxidEsales\Eshop\Core\Registry::getConfig()->getConfigParam('dbUser'), + \OxidEsales\Eshop\Core\Registry::getConfig()->getConfigParam('dbName'), $query ); @@ -90,9 +90,11 @@ protected function execute(InputInterface $input, OutputInterface $output) return; } - $commandOutput = array_map(function ($row) { - return explode("\t", $row); - }, $commandOutput); + $commandOutput = array_map( + function ($row) { + return explode("\t", $row); + }, $commandOutput + ); $table = new Table($output); $table->setHeaders(array_shift($commandOutput))->setRows($commandOutput); diff --git a/src/Oxrun/Command/Install/InstallCommand.php b/src/Oxrun/Command/Install/InstallCommand.php index 8efc206..1b5b697 100755 --- a/src/Oxrun/Command/Install/InstallCommand.php +++ b/src/Oxrun/Command/Install/InstallCommand.php @@ -123,9 +123,9 @@ protected function execute(InputInterface $input, OutputInterface $output) $oxSetupController->dbCreate(); $oxSetupController->dirsWrite(); $aMessages = $oxSetupController->getView()->getMessages(); - foreach($aMessages as $message) { - $cleanMessage = str_replace(array('ERROR:','FEHLER:'), '', $message); - if($cleanMessage !== $message) { + foreach ($aMessages as $message) { + $cleanMessage = str_replace(array('ERROR:', 'FEHLER:'), '', $message); + if ($cleanMessage !== $message) { $cleanMessage = trim($cleanMessage); $output->writeln("An error occured during the installation: {$cleanMessage}"); } @@ -180,8 +180,8 @@ protected function patchOxSetup($target, $version = null) } // patch version 4.10.2 and above (broken in official installer) - if(isset($version)) { - if(version_compare($version, "v4.10.2") >= 0){ + if (isset($version)) { + if (version_compare($version, "v4.10.2") >= 0) { $code = file_get_contents($target . '/setup/oxrunsetup.php'); $code = str_replace("demodata.sql", "test_demodata.sql", $code); file_put_contents($target . '/setup/oxrunsetup.php', $code); @@ -200,8 +200,8 @@ protected function getOxidVersions() { $client = new Client(); $githubToken = getenv('GITHUB_TOKEN'); - if( $githubToken ) { - $tagsArray = $client->get('https://api.github.com/repos/OXID-eSales/oxideshop_ce/tags?per_page=9999&access_token='.$githubToken)->json(); + if ($githubToken) { + $tagsArray = $client->get('https://api.github.com/repos/OXID-eSales/oxideshop_ce/tags?per_page=9999&access_token=' . $githubToken)->json(); } else { $tagsArray = $client->get('https://api.github.com/repos/OXID-eSales/oxideshop_ce/tags?per_page=9999')->json(); } @@ -222,7 +222,7 @@ function ($result, $item) { ); $tagsArray = array_filter( $tagsArray, - function($tagArray) { + function ($tagArray) { return preg_match('#^v\d+\.\d+\.\d+$#', $tagArray['tag']); } ); @@ -247,8 +247,8 @@ protected function downloadOxid(OutputInterface $output, $version) try { $githubToken = getenv('GITHUB_TOKEN'); - if( $githubToken ) { - $request = $client->createRequest('GET', $version['zip'].'?access_token='.$githubToken, array('save_to' => $file)); + if ($githubToken) { + $request = $client->createRequest('GET', $version['zip'] . '?access_token=' . $githubToken, array('save_to' => $file)); } else { $request = $client->createRequest('GET', $version['zip'], array('save_to' => $file)); } diff --git a/src/Oxrun/Command/Ioly/IolyCommand.php b/src/Oxrun/Command/Ioly/IolyCommand.php index 09f3bc4..812f960 100644 --- a/src/Oxrun/Command/Ioly/IolyCommand.php +++ b/src/Oxrun/Command/Ioly/IolyCommand.php @@ -50,7 +50,7 @@ protected function execute(InputInterface $input, OutputInterface $output) public function isEnabled() { $ioly = getenv('IOLY_PHP'); - if( !empty( $ioly ) && is_file( $ioly ) ) { + if (!empty($ioly) && is_file($ioly) ) { return true; } return false; diff --git a/src/Oxrun/Command/Misc/GenerateDocumentationCommand.php b/src/Oxrun/Command/Misc/GenerateDocumentationCommand.php index b0e8f06..eb86a83 100644 --- a/src/Oxrun/Command/Misc/GenerateDocumentationCommand.php +++ b/src/Oxrun/Command/Misc/GenerateDocumentationCommand.php @@ -56,12 +56,14 @@ function ($item) { ); $availableCommands = array_keys($this->getApplication()->all()); - $availableCommands = array_filter($availableCommands, function ($commandName) { - if (in_array($commandName, $this->skipCommands)) { - return false; + $availableCommands = array_filter( + $availableCommands, function ($commandName) { + if (in_array($commandName, $this->skipCommands)) { + return false; + } + return true; } - return true; - }); + ); $command = $this->getApplication()->find('help'); $commandTester = new CommandTester($command); diff --git a/src/Oxrun/Command/Misc/PhpstormMetadataCommand.php b/src/Oxrun/Command/Misc/PhpstormMetadataCommand.php index 5762d77..fa65bb0 100644 --- a/src/Oxrun/Command/Misc/PhpstormMetadataCommand.php +++ b/src/Oxrun/Command/Misc/PhpstormMetadataCommand.php @@ -42,10 +42,10 @@ protected function configure() protected function execute(InputInterface $input, OutputInterface $output) { $searchDirs = [ - OX_BASE_PATH . '/application/components', - OX_BASE_PATH . '/application/controllers', - OX_BASE_PATH . '/application/models', - OX_BASE_PATH . '/core', + OX_BASE_PATH . '../vendor/oxid-esales/oxideshop-ce/source/Application/Component', + OX_BASE_PATH . '../vendor/oxid-esales/oxideshop-ce/source/Application/Controller', + OX_BASE_PATH . '../vendor/oxid-esales/oxideshop-ce/source/Application/Model', + OX_BASE_PATH . '../vendor/oxid-esales/oxideshop-ce/source/Core', OX_BASE_PATH . '/modules', ]; $finder = new Finder(); @@ -61,9 +61,9 @@ protected function execute(InputInterface $input, OutputInterface $output) ->files(); try { - $extendedOxidClasses = \oxRegistry::get('oxModuleInstaller')->getModulesWithExtendedClass(); + $extendedOxidClasses = \OxidEsales\Eshop\Core\Registry::get('oxModuleInstaller')->getModulesWithExtendedClass(); } catch (\oxSystemComponentException $e) { - $extendedOxidClasses = \oxRegistry::getConfig()->getAllModules(); + $extendedOxidClasses = \OxidEsales\Eshop\Core\Registry::getConfig()->getAllModules(); } $classes = array(); @@ -135,7 +135,7 @@ protected function execute(InputInterface $input, OutputInterface $output) ) ); override( - \oxRegistry::get(0), + \OxidEsales\Eshop\Core\Registry::get(0), map( [ CLASSES_PLACEHOLDER diff --git a/src/Oxrun/Command/Module/ActivateCommand.php b/src/Oxrun/Command/Module/ActivateCommand.php index 95b86a6..5ef448c 100644 --- a/src/Oxrun/Command/Module/ActivateCommand.php +++ b/src/Oxrun/Command/Module/ActivateCommand.php @@ -47,15 +47,7 @@ protected function execute(InputInterface $input, OutputInterface $output) $this->checkModulelist($shopId); - $actualVersion = $this->getApplication()->getOxidVersion(); - - if (version_compare($actualVersion, '4.9.0') >= 0) { - $this->executeVersion490($input, $output); - } elseif (version_compare($actualVersion, '4.8.0') >= 0) { - $this->executeVersion480($input, $output); - } elseif (version_compare($actualVersion, '4.7.0') >= 0) { - $this->executeVersion470($input, $output); - } + $this->executeActivate($input, $output); } /** @@ -64,14 +56,14 @@ protected function execute(InputInterface $input, OutputInterface $output) * @param InputInterface $input An InputInterface instance * @param OutputInterface $output An OutputInterface instance */ - protected function executeVersion490(InputInterface $input, OutputInterface $output) + protected function executeActivate(InputInterface $input, OutputInterface $output) { $sModule = $input->getArgument('module'); $shopId = $input->getOption('shopId'); - $oModule = oxNew('oxModule'); - $oModuleCache = oxNew('oxModuleCache', $oModule); - $oModuleInstaller = oxNew('oxModuleInstaller', $oModuleCache); + $oModule = oxNew(\OxidEsales\Eshop\Core\Module\Module::class); + $oModuleCache = oxNew(\OxidEsales\Eshop\Core\Module\ModuleCache::class, $oModule); + $oModuleInstaller = oxNew(\OxidEsales\Eshop\Core\Module\ModuleInstaller::class, $oModuleCache); if (!$oModule->load($sModule)) { $output->writeLn("Cannot load module $sModule."); @@ -92,49 +84,6 @@ protected function executeVersion490(InputInterface $input, OutputInterface $out } } - /** - * Executes the current command. - * - * @param InputInterface $input An InputInterface instance - * @param OutputInterface $output An OutputInterface instance - */ - protected function executeVersion480(InputInterface $input, OutputInterface $output) - { - $this->executeVersion470($input, $output); - } - - /** - * Executes the current command. - * - * @param InputInterface $input An InputInterface instance - * @param OutputInterface $output An OutputInterface instance - */ - protected function executeVersion470(InputInterface $input, OutputInterface $output) - { - $sModule = $input->getArgument('module'); - $shopId = $input->getOption('shopId'); - - $oModule = oxNew('oxModule'); - - if (!$oModule->load($sModule)) { - $output->writeLn("Cannot load module $sModule."); - } - - if (!$oModule->isActive()) { - try { - if ($oModule->activate() === true) { - $output->writeLn("Module $sModule activated for shopId $shopId."); - } else { - $output->writeLn("Module $sModule could not be activated for shopId $shopId."); - } - } catch (\Exception $ex) { - $output->writeLn("Exception actiating module: $sModule for shop $shopId: {$ex->getMessage()}"); - } - } else { - $output->writeLn("Module $sModule already activated for shopId $shopId."); - } - } - /** * @return bool */ diff --git a/src/Oxrun/Command/Module/DeactivateCommand.php b/src/Oxrun/Command/Module/DeactivateCommand.php index 1dc65c5..022e001 100644 --- a/src/Oxrun/Command/Module/DeactivateCommand.php +++ b/src/Oxrun/Command/Module/DeactivateCommand.php @@ -44,14 +44,7 @@ protected function execute(InputInterface $input, OutputInterface $output) $this->checkModulelist($shopId); - $oxidVersion = $this->getApplication()->getOxidVersion(); - if (version_compare($oxidVersion, '4.9.0') >= 0) { - $this->executeVersion490($input, $output); - } elseif (version_compare($oxidVersion, '4.8.0') >= 0) { - $this->executeVersion480($input, $output); - } elseif (version_compare($oxidVersion, '4.7.0') >= 0) { - $this->executeVersion470($input, $output); - } + $this->executeDeactivate($input, $output); } /** @@ -60,14 +53,14 @@ protected function execute(InputInterface $input, OutputInterface $output) * @param InputInterface $input An InputInterface instance * @param OutputInterface $output An OutputInterface instance */ - protected function executeVersion490(InputInterface $input, OutputInterface $output) + protected function executeDeactivate(InputInterface $input, OutputInterface $output) { $sModule = $input->getArgument('module'); $shopId = $input->getOption('shopId'); - $oModule = \oxNew('oxModule'); - $oModuleCache = oxNew('oxModuleCache', $oModule); - $oModuleInstaller = oxNew('oxModuleInstaller', $oModuleCache); + $oModule = oxNew(\OxidEsales\Eshop\Core\Module\Module::class); + $oModuleCache = oxNew(\OxidEsales\Eshop\Core\Module\ModuleCache::class, $oModule); + $oModuleInstaller = oxNew(\OxidEsales\Eshop\Core\Module\ModuleInstaller::class, $oModuleCache); if (!$oModule->load($sModule)) { $output->writeLn("Cannot load module $sModule."); @@ -88,50 +81,6 @@ protected function executeVersion490(InputInterface $input, OutputInterface $out } } - - /** - * Executes the current command. - * - * @param InputInterface $input An InputInterface instance - * @param OutputInterface $output An OutputInterface instance - */ - protected function executeVersion480(InputInterface $input, OutputInterface $output) - { - $this->executeVersion470($input, $output); - } - - /** - * Executes the current command. - * - * @param InputInterface $input An InputInterface instance - * @param OutputInterface $output An OutputInterface instance - */ - protected function executeVersion470(InputInterface $input, OutputInterface $output) - { - $sModule = $input->getArgument('module'); - $shopId = $input->getOption('shopId'); - - $oModule = \oxNew('oxModule'); - - if (!$oModule->load($sModule)) { - $output->writeLn("Cannot load module $sModule."); - } - - if (!$oModule->isActive()) { - $output->writeLn("Module $sModule already deactivated for shopId $shopId."); - } else { - try { - if ($oModule->deactivate() === true) { - $output->writeLn("Module $sModule deactivated for shopId $shopId."); - } else { - $output->writeLn("Module $sModule already deactivated for shopId $shopId."); - } - } catch (\Exception $ex) { - $output->writeLn("Exception deactiating module: $sModule for shop $shopId: {$ex->getMessage()}"); - } - } - } - /** * @return bool */ diff --git a/src/Oxrun/Command/Module/MultiActivateCommand.php b/src/Oxrun/Command/Module/MultiActivateCommand.php index 5e591e7..6fe8165 100644 --- a/src/Oxrun/Command/Module/MultiActivateCommand.php +++ b/src/Oxrun/Command/Module/MultiActivateCommand.php @@ -108,7 +108,7 @@ protected function execute(InputInterface $input, OutputInterface $output) // use blacklist /* @var \OxidEsales\Eshop\Core\Module\ModuleList $oxModuleList */ $oxModuleList = oxNew(\OxidEsales\Eshop\Core\Module\ModuleList::class); - $oConfig = \oxRegistry::getConfig(); + $oConfig = \OxidEsales\Eshop\Core\Registry::getConfig(); $aModules = $oxModuleList->getModulesFromDir($oConfig->getModulesDir()); foreach ($aModules as $moduleId => $aModuleData) { foreach ($moduleValues['blacklist'] as $shopId => $moduleIds) { diff --git a/src/Oxrun/Command/Route/DebugCommand.php b/src/Oxrun/Command/Route/DebugCommand.php index e985010..73622e8 100644 --- a/src/Oxrun/Command/Route/DebugCommand.php +++ b/src/Oxrun/Command/Route/DebugCommand.php @@ -40,6 +40,7 @@ protected function configure() $this->setName("route:debug") ->setDescription("Returns the route. Which controller and parameters are called.") ->addArgument("url", InputArgument::REQUIRED, "Website URL. Full or Path") + ->addOption('shopId', null, InputOption::VALUE_OPTIONAL, null) ->addOption('copy', 'c', InputOption::VALUE_NONE, 'Copy file path from the class to the clipboard (only MacOS)'); } @@ -50,11 +51,15 @@ protected function configure() protected function execute(InputInterface $input, OutputInterface $output) { $url = $this->cleanUrlPath($input); + $shopId = $input->getOption('shopId'); + if ($shopId) { + $this->getApplication()->switchToShopId($shopId); + } $output->writeln("Results for: $url"); - /** @var \oxSeoDecoder $oxSeoDecoder */ - $oxSeoDecoder = \oxNew('oxSeoDecoder'); + /** @var \OxidEsales\Eshop\Core\SeoDecoder $oxSeoDecoder */ + $oxSeoDecoder = oxNew(\OxidEsales\Eshop\Core\SeoDecoder::class); $aSeoURl = $oxSeoDecoder->decodeUrl($url); if ($aSeoURl == false) { diff --git a/src/Oxrun/Command/User/PasswordCommand.php b/src/Oxrun/Command/User/PasswordCommand.php index 8b22a83..82b8b71 100644 --- a/src/Oxrun/Command/User/PasswordCommand.php +++ b/src/Oxrun/Command/User/PasswordCommand.php @@ -34,14 +34,14 @@ protected function configure() */ protected function execute(InputInterface $input, OutputInterface $output) { - $oxUser = \oxNew('oxUser'); + $oxUser = \oxNew(\OxidEsales\Eshop\Application\Model\User::class); $sql = sprintf( "SELECT `oxuser`.`OXID` FROM `oxuser` WHERE `oxuser`.`OXUSERNAME` = %s", - \oxDb::getDb()->quote($input->getArgument('username')) + \OxidEsales\Eshop\Core\DatabaseProvider::getDb()->quote($input->getArgument('username')) ); - $userOxid = \oxDb::getDb()->getOne($sql); - if(empty($userOxid)){ + $userOxid = \OxidEsales\Eshop\Core\DatabaseProvider::getDb()->getOne($sql); + if (empty($userOxid)) { $output->writeln('User does not exist.'); return; } diff --git a/src/Oxrun/Command/Views/UpdateCommand.php b/src/Oxrun/Command/Views/UpdateCommand.php index f9617d9..c893932 100644 --- a/src/Oxrun/Command/Views/UpdateCommand.php +++ b/src/Oxrun/Command/Views/UpdateCommand.php @@ -31,10 +31,10 @@ protected function configure() */ protected function execute(InputInterface $input, OutputInterface $output) { - $myConfig = \oxRegistry::getConfig(); + $myConfig = \OxidEsales\Eshop\Core\Registry::getConfig(); $myConfig->setConfigParam('blSkipViewUsage', true); - $oMetaData = \oxNew('oxDbMetaDataHandler'); + $oMetaData = \oxNew(\OxidEsales\Eshop\Core\DbMetaDataHandler::class); if ($oMetaData->updateViews()) { $output->writeln('Views updated.'); } else { diff --git a/src/Oxrun/Traits/ModuleListCheckTrait.php b/src/Oxrun/Traits/ModuleListCheckTrait.php index 814524e..efd992d 100644 --- a/src/Oxrun/Traits/ModuleListCheckTrait.php +++ b/src/Oxrun/Traits/ModuleListCheckTrait.php @@ -23,13 +23,13 @@ trait ModuleListCheckTrait */ protected function checkModulelist($shopId = null) { - $oConfig = \oxRegistry::getConfig(); + $oConfig = \OxidEsales\Eshop\Core\Registry::getConfig(); if ($shopId != null) { $oConfig->setShopId($shopId); - \oxRegistry::set('oxConfig', $oConfig); + \OxidEsales\Eshop\Core\Registry::set('oxConfig', $oConfig); } // we must call this once, otherwise there are no modules visible in a fresh shop - $oModuleList = oxNew("oxModuleList"); + $oModuleList = oxNew(\OxidEsales\Eshop\Core\Module\ModuleList::class); $oModuleList->getModulesFromDir($oConfig->getModulesDir()); } } diff --git a/tests/Oxrun/Command/Database/DumpCommandTest.php b/tests/Oxrun/Command/Database/DumpCommandTest.php index 20e4845..161cc01 100644 --- a/tests/Oxrun/Command/Database/DumpCommandTest.php +++ b/tests/Oxrun/Command/Database/DumpCommandTest.php @@ -8,6 +8,11 @@ class DumpCommandTest extends TestCase { + /** + * DB execute test + * + * @return void + */ public function testExecute() { $app = new Application(); @@ -44,7 +49,7 @@ public function testExecute() ) ); - $dump = file_get_contents( $path ); + $dump = file_get_contents($path); $this->assertContains('DROP TABLE IF EXISTS `oxacceptedterms`;', $dump); $this->assertContains('DROP TABLE IF EXISTS `oxaccessoire2article`;', $dump); $this->assertContains('DROP TABLE IF EXISTS `oxactions`;', $dump); From 780e0f249e70170d9993933d8f67114a952e572f Mon Sep 17 00:00:00 2001 From: Stefan Moises Date: Fri, 16 Mar 2018 13:04:35 +0100 Subject: [PATCH 21/30] Fix typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e267cb2..04d0d46 100644 --- a/README.md +++ b/README.md @@ -658,7 +658,7 @@ views:update route:debug ----------------- -* Description: lookup a SQL Url in the database and check which controller is associated etc. +* Description: lookup a SEO Url in the database and check which controller is associated etc. * Usage: `route:debug http://myshop.de/my/url` ### Arguments: From ec7869fd48817cce9b2a6d5ab45bc4b4bdd7e232 Mon Sep 17 00:00:00 2001 From: Stefan Moises Date: Fri, 16 Mar 2018 13:07:16 +0100 Subject: [PATCH 22/30] new phar file --- oxrun.phar | Bin 1913233 -> 1912593 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/oxrun.phar b/oxrun.phar index 0a8be9bb4cf8260a978b199d6dff6e3c684a56ed..f315dd39af23fc36acb1ea7dacfc4c6516d8d279 100644 GIT binary patch delta 16882 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 delta 16829 zcmb_DXH-Nu`-r~0x7PQ*wZ0#B?YZ~tvwJ&tX69cho&Q_uyw%#; zMk>_@Pd-giV9D007w2zn(g^P7UmT(U7Af^WI9tFgQViaE8g)llVAKuacB2Fk!bDuNNp3qpzn&2(pbtil?1E*dpd+4!}JOs&Al=0CYB8*O4oX1>RewbYR8lLS1WA>H)a zUKl)|0aHz2$Kyb_CEKLc;lqx`;Do*><5|l<9lNTHK&QLe_Ci=}n}kR|!p{24t~c18 zvw&n9((o|#BhyvPvqHx#O`KSqod@p9x64EXGuZX5S%}PDIfXEd&J!QL0tM0nep&Z6 zg4^Mi*}4>jw{__V6YaAQZn4ir=;4r!aJoYQ!oM9d5e{)2hVZcCNQ5y?r0N0SGR2DT zwq8RH{oiFFK-8lVmcg zky@p?o|T&?Y6b*>?1Z~7%H+)EZawjSoEuR)?nW*e0yV4i2~RVqAK@&^-42n;-3j@y zfNmaX_{hYZA9bvqk1ezJwnxMk-W1^#-u;oX2~buA8lPs9nzdRGVeHyJuOzRu5R1hXSW#+| z0xR@dPcUdJmfqA6>6AAm2cK=)9qFuuDm84pJRCzgx>+b9l{F*d9BkGH?>&5|a#r|| zvOYx(&u-cDo^cp;sUfbdkn{-*nY?}KcQdILkC`rDN zN|i5<9mLXFMY40vHMn@Y`2bw(13Rtey1ha}rnd;k#icDM1e~O z{VJdDUdBl_WH(QeLq*k<{e=7>Tjjj43n+Vvjy~?=;jiYZ(#u?O_ zkTLgPE^KO%hCY>AV?%DkEDR9iWClw9Dpn$qKHK>AoGDcV_0EKB-@m%X7y2Kh;UmV7{K$L z?L6kNq@Iy1DaM4&ZchQdqCJJm^Y)Zd{iErAb2M?VjG_DN7_$0zG4$Lib`bh{5^ye0 z7*qOIIX|{5mMmHqON}I;14XJoV1DtkXe0Bi)3N0p+M|5+9f(|b#}p)&0Z6VMEBpmg zb5_*FtZ`EQs1n5sMWT=>@T<x(!A%9O_2Q{ks#K)198*>rVK+ z;@(mIeBq`wMEQ6e6>Z0O61g;fC{jBgPc1tpA-%ye>FMW-LG#Ww|7sQX<~^Ky(b7Q$ zhgBz1>V2O`;^=!2g9$z8{%Q|m(6#3~+l0FY1|oyRQTgxN^-a$~DAbT7+PgdHolxeP zpPDg`A#GV@f{omQPwQxp-6tawru@3at3SXW(0- zv|={B)GVSm>Cmk=rOfi)eJvG|eh^{LE?BeRz%b0#h(1&^d-S0KFdNqIS|$(0yu00p z^6o_+O4gozsaTB!q!;rRKWoT)Z3k0!r>_-i`n)e?@GwA{UbXZPON)+Vyq^viH}<2& zN?1JSZQbQv-o*8%7)k3-JOTh?$g5UUQIO~TNf65cl<*s2@lNujVpxn~>jqH4*(nx> z!lG)?!vnZ8Ihl6mB~uy9hQ$Zr5k_o}k&e|T2V;cf!3OUmlebX!HepGrruK>-x(X93 z(0pa<2V&g?V!xymE2E;MVpauyH}5wG=$38SUju1KuuLIkU0^Zlv7ZHYkk3+F(Gq)7 zX!6mfQoYVgCDnd`(Qf8`)`3qlh3vkPN-f)O5M{!wL6jMH2hjkTIGCPS52i8HamWzV zXgx^$(SSqWz0E;y_F%9bJ2-@labXArZV(8jRSzEo-7}2UrJ1tvX;c>HrcqtGl}18_ zrN<-L?XY2K#8W1^R1>y0oxS?3Xsg%fuSVAg`s4R8^E&5vz^DlvI+tuOqLm&kZF$8sxo^byp&1a zt%9ND(Ut?t*e@9otUQaft;$M9;k_JHs(5~QrI@WenBL%oyCV!_j=#ZZU%7dF#!mRO z;>ov-Y_#3lkb4#ATQS=c=!P@jY?3e{n+~=Uv#FTx&W=GopMemIb#()Pk2|X#X2n8t zJdjUvPDg~xb2=luo7ry|kLrq2hJyIt9Leq?oMp+X z$62zkMwx568ISLL_W;ff>bUgbCf-R?e_aGKq0?~=cCTz)Q2c(t_I&ZN3`s_P7 zejiP8yckVm#;5RhXhlNQBNjB>kCl#Tj6%&FLm5yvhK2_JG7@rP8S(tJF_i1sZ9C(p zA!TOFs$7d3J<7=mAA?ZaeAuV#O@$v*5H^)lj_od|da?%8EWQ`%%~$G$ffTGBV=1wU z#`Z+rR~MeWUrln`AwoBVB#d2W-m;lE)wwpO||nsAUDhch4z)i?|>}yV;d_i znAc=S6sz52V)Hp5oZPZ*9-m|d27b%t_~fRD^>i|k>oJ9dT|R~EG!&BiyXNP=f`sv7 zl~YXF(Wz$6oktr+;P~hSG?f;r!2ABz8_;M;Gt_MSR5Dr>Y`>SbZQFt=Fb_{Nf!&l; zcc)U6MSn=_=6*;*s=$v2x{o>j))tP;V;c3`sA+^bdm5F_bJNJ`&8AZ!J_MXDlz4xD zg=6k?DjY|qQ+afsL8s{48Pw`_&!BSofs;xVbjs(f2peBCMO+nCz}Zz{cz>XZh?&f! zOzAQ+5+mh&EgX*QmbPIhs+wv>8fdbcBvST=nQd|N%bD#EM$95fX3ZLn_a?Io5ssN% zg7A;o6$ppV8H4cmIi&~(%^i)*4nmeyZrgB#9iN}ZqUPD*!*rN82DTp5j|F_3#&*u5 za6C4z9CyaeC%A9^SiJAJfGB^rpc3z6SQ)}yjGnjoh$P$e5$$Wfkl>nyB$@ZebieZB ziMY>;)Ba_AJl?x~G92N|PsmXhKcNxdYf&mbuUtfL-)=3+LE*zdqm3;+db5W+?OD-c z%I6)6N!ZXO6L3?_l2HhQmXbNvEG@!&=ViqRXD=Ix&}ccCvvfJbdy5rxKW+uBKU^^q znRW%HXP0U&GSAgr*}TupS?x+^+;DLvX_2~$v^~FSDn8HnG#BBKPl;j7XL)$P;WM&A z;OFF@5(vM#9;uDFX9A3V@k>M4gwNIN_2*8A-FP*LF?)48yg$5}I`UI1m8t_DGYVff z1g{Cl#r!oLQPxQCE~_?dqdD-{8ZxKD7bL)>FUS&qeL-$3_>u&vfD9T^Hg+!#Bp27Z zGPku97cJLDBKe}VRHkazQdjr{B>%`EXFpEWv({=^t95!r?6wSmK-bf-mh1%<%ao{M^?{#H$Thy#*BaIi+H-D>qxRsIN$eUSCn*E&hrci42?qM}_WVMOUE3 zR%@7CtwSrEh7y;R^m$V@p-vIOj@4G1v*KzJV@5TxyCdo7GKk*Gw?Od#SfS52hS1fs;{Yst@xVGcay<>Cto`5WEor9u#j)4 zxyOA&6LKYJIQ!}F{ZRkmUG+6fR=3@W{q~JLn&bI5l!M7zNX`vg=wxfTRU9T*h+S0; zoopV|(DTIY195%rc5F5ac2Fxz-$94}y*o%M@12BJxszT--rPxKdhDWsVg%U1Z)G1x z_SG#i)VW=x=KWn%dZNFjUb*;NI$E@c(}VN531fw2jhV-HZYW6fccT3WSpOXg#zMcR zcl#^9r<|$(o@PB|+V?*@e=Iw(+n)`pr97EfOD~+afYU#3vLzp00J*Yxdu-XWTI#Ky zyD8&}cK1WB2L#mbp#aF=LzVI8Jro%3Kalyx{m>inPy9gdEW&=IQ^L3(Dc|<|Nc5b4 zq7k9+C*t${PyKP9(_YHMl)cormh7bgE}oRJ3hZ>PzXzl|3u@>+pD zN4htG)OBN(dreu;esWfq{cTao%Kc=8Bl}4@w*%DJhaVuDH-?THaAZafWTY#5uwTQj zA8^EtZw}C5Dd8Z^4lltux=XWcaRS(VFanWY9i%x%Dg0YnFk?jr?b*yj6t`a< zA_KlYL{mwx!(_m9hw0Vc7f^|3Y>MIyhAKL!W$lhMMh^XtP>Cr34zKU3)9=}A{a944zf@Hy794Dn4*HKcAs-u`cQb)0F zd4j5X;)y6EIQsA0E&bW+hh^bJBwCI$_SNPFf?9e3CW|IZ00a)HXmBS)yryj^^^w0c4SGX zjge|!K-h77z%x-^%@CpLbYoon5Ecu~3ZX|pyeK=*ou+tsc$z{Y@Mp4f9}760M|X)v zg@VspuvI@3;?AGZAq&n>I(9!p_4yoF)N*)qK2#A`7JS}})t-@2?ki^~zdX)TzsoyI zdAR*7g_Gqus^87PvDJxjI~cV%H4w35Rj2K>GeBe|R}^^IXGyV$U^7Ixb&kTw>O2Ld z;d~V8`4q&_d2f1%Jy8+o#Cb}ro9Ejh!ifZxD!e@O7JLB)9qWP_>wSS5(69^StPK|^ z0H0n6#|;fn@w2SMGEh+7b!Y-UnJ!$E>Se6T%xdug>zd($Cf5wZe^1x zJAH{}o_m)%qjf?r6SuO<;7}j>L*bl|O31F$&^()A4K|Ou-)DKSh9T$vu1urS<31Y^KkHfm;ZmF(tul?2YaO2e7PRi%n7F=a^U%2lHD>MB*I_+N-(BXgC?Q1aJ{ z2El88X^O@?0BTPE^C1^Ws%3@0xmzkPCU?OB6P(Q=f2AtF_}Bipp%*Cj?844)w2SsP zvde3*?(bf^UEVh4c4&)bzmX6A_^ltl7~TgZ^8U_1R%~U@Vy6=<<} z-H;?9M<^1wb&ZUvg^elx<;KDn8r+%xJC&Ip)+&|$^o}@GP5Zl+efv9Aj`P2hCk>F? z8@~KxnkZBx@l2?d-EQfu8%(_7x-0Vh?mDGWTX0aXgr7cOyReysL)AuIjSH(^7UFuC51~s=Ru2XJNXIXQLDs&jk zc5%s;mTbai^1jDyQYi9vPvk!5HjTt_U>46)O-e+4abjk7sJ8S3ALU8s^%(3u@6ZrY zc!wfyJ(weVa-#rD$n@J9R(Zn&N8CouD7_W<>7M1&aJR>8jY~H}ei8N5I%h&rVByJQ zDS~qliZ$;py&@R^A@zyhi+v5=viXC44ly0XisRQ0Hz+pb4^ph+4^r&tA5_b&?@<87 z-y;itbT1lXZwADq@8OUkU~U~tePG8QzqW8t52CWf^!I80E4@!tw%n(wT=|95jcavF z-|k{f9?-xT{eZmK6)aNRYQ{4B9YW-NJGS*f6V&M91Da$!9+Din52+>Ycu14Y>xWc9 z6CY8wEqO$axep$6-B{NMo7(F~tx@3>qbocoj5`29s@srZYYAO4F1$^9|axZKC|eEVa;6p`6-DM z^f$%sSONF`O)nevf$twB^}od)o+l*>pPTa)k1fp9dxer6o<(6X$O5CcaJbL)36ecq z{*2xZ?Er~&x7*HPn-AGD?Q^OE-JcgB?{A+|=YR2>{M+sYMM?P!%AdV2=nK<@5D!(3 z&EtetL8#4R$oJm^TwyHu(~YfoVaJPJS{SoUFUcT3yrckY1>&DvPzG`RBZ=Sq6;*|9 zuc*Duc}2zdjDYU1X?D$cO^4wRpf~+__>?o~4?m%Lqjk*~8BkJ6KWl0a#Fh4`z-<%! z+!jo6{@7SDW12T)+sHTM-nnlEpuV@>pgR{vNT}z+G6@H>g-0dqu)LWh;rW42lEjk> zl7tf?4>pp>peewMhyFQTDa`&{VrJIJq#CQ1a`3}?wUmh;Y|SJ*Uhx7mDGO=E0j;5xp<|UdgRQyN zT*6}u4>OmtamOliiN3?tfj{6T0cuH> zk}-d3Az?k@_LhRuJjj7gleb0l%@IUNO_2+Hi61uEiD0nOQ7G~ZokZuBz3_%;@$|SBe3AoL^G}_G6_#u4 zML-$sB|4NxfNwnV)wRm($m{K;2&61K2xpf$2*cDlNOX!ehtXp2rKjodxggz9IJ3-A zqDt2f{A73LdJ!SbYb@FD<4zKN!&(JeD`}@di){N<@9b&g1QsZC5>B7xBtr0slkie% zV+r3C@`H^fJbZAyvk3koXJP!K&e9-M!QVwZnc*V!!Y7yCerV|`%=MwGpnS(wL_|+n zczZ9{CG6|bODSgcvZP__#@ebMLW@_@Pva)cp}ctxkiJe=!Zv^}a+AD~*KRi{9bLa1 zir<;bCmJaUAHWB?OP;ts-dzZO)LrOLT-&UMSS>ns|r(GOb~sng@AG8e|>oDfK|n=GnsgkQYbF6w&+@ z;%a`(Q^NYn?|KUBb@vj%yw*#~#%;=JiBN4SAbAyU3Q5h_t26Mfg5wF40$OyJ5(^KI`^$ z3e5&)?0LCP{RQ+pB`Q|55WVPm3&HPKu$j%7D*VkzOOo!TzvRfj@|W=jZ)U$X|3UN0wtWd`RYK4ew%O|m`9F! zxQ9O&{1!7NND4r@ML~k@=^zQ4IhTVaI@_ee%pWfMd6Nq(!=PJM2TP8)?O?D-V)vF3 z_Gw<+Qq-!0Ek!0Q1p!B2TLPU>X&12Hk{eoy0$binV4r9uYUT~7nahsF1t>wsV|*ny zp50oqL6iIf?rnO)-}r5nbS58fZY^4OU2FPTKKBffa>?N#BCu|TNa^?_zKsZ)Z`ueP z_fTmllJ5`UzU)}<^$i}?@cp5prq+i_d9=T+sBG)n3WZ;_6`>j*CehJqk*`X1>*x%K*NyoxqkN#r{B=|lFiAv-9@-n&u6*IO?QItk`yWL~z9_L< zd*O%j_F||y&|YM^OSC8g#nECqvjrZVnwzRHCzE1e1bGuJI;U%lF#3oX;p*BLq4}3k zXl@itFM)3{fqM*$TRmbWIz_ht=`1SmG^GGUiYsD;QFg`(LY5uGaF^Uc=sE!?_U&M3 zPqs|&2KDXF4x%k;JBrTo5`t#2RC``YQ-2OfX1uzi@b!8J_yvvC_nTJiMGLV)p|#hiH%PJi)bgDiPQ2PuRDZ8x-d zOc&8QmUR)8*bHJxdT?hr?-ega^6;*r8w~0yy6>K@qSLqN_IA4MCdRP)-9-E8*ZDC6g z^}T1JRE|rB6D9mYktg(!#^aMCJ){!+(4nV<#~{A7rNJ>=k^f^d9#l+4)JpON`?60d|$yh zp`Z9r+fORMrJ(+j0coj$R%DYeDwVGlL-@-9kXA)IT-Ds#ST^Uc`iqKhHb6{+Lk37g zkU;GK(ewP1g>jZ8izgNX1(UG@rCelk6_|YU#qcVns0$|TQbcU^Oc9;_B7}18fjhIQ z(_<9QN`baxo+{ZOOP5sP^RiUop{uDP9O4EEe{2{eBE@#FI9rSzEX2AoSRnTrBGGrX z?vTVrso%O|I#&0RTzR83A(c~_sBX8Q7WTVfG(Z`VIN-?I{5cjnpN*e7#cUn^5^~IZ~5W(!Bl8bgx6IxQ>$vGyq zeBMwg3|(?~s1&8mf^Mh~Rbc<=mwx7(vq6;FOqdd*G9?@BXE5R`>k6DP?Y6zbRIFEK zisJoArWB)P0#Si$UuUH>5HjO7S(3H(n$Sss{vn_3c#lw0mLQayCBflckzl64#KnWRk-pK{AazrNm=L24QWxzy=;(_03Ott+Hd)c$ zog?B-gP0QQ48V1;u!908_wnt;J+q|ZK^~LtC%k`1l;DmG%dynu>%9Tyw2#tsxO8yY!{q>%BV0~!HHOO>E|;350@ssk_@%qH zPCUY1w&$DgIJ@%p#pY_h-d?ulr5CKdYre3Tt1OHP%lPI}*?|x8kUf=~cK&kHn(ZF4 zg;m4SA1&lCu@tHEDlAJZ%o>$%NXRQG_3~?{_b%icZpsmS^leuMu3aVD@WLqBf%uGRb0jQn3w3ktIg`Tr3svv6chqn5Irq^Q!OmHeU5&!y&xWWm>tl{NhP1i2YcNQ5?U`=RVsW0@#-F@B$L7{A|3wr-&DzfeElTZZ30 zD%8EbM;4Cp(^rf%l#UuXLa*GV1`lpG-e2!snlUurFe0PCP*PJhQ0|19gR}i=K)_~3 zIzt(_u%xeoq>s`MDlF?Xv?h13{Nim;_;J89Gs?^6C-NYM_Tr<0CN-i*ZtGXgxun}_NAda3-9V&5^M(x8+&0#O(9$t%qrHMDQxaKi|_A7Ap3oXzt; zk~LCb&4iES{zewQVB(zEks~w8`Q}?jPBl+Hmfa-HKQ?mDPvl6uK%eN0%uGW`Nm{8w zF3KmcrsNa(kUbx_Q9jSlc9O06#F3CUXEw?H4&aL(hB3ZAg=KvVg`EtU5Y!`mYJ$I# zZ&_HtL-5!YUHcup1WlGn$TVHW3`#GCAYB1hX-AdgKMgOln)rczazF< zcH#35$ls88^A5^!^yw>WH|Ls12j$mhR__=<67(=}^Qs9uB_A+tAYB8bV{L@D9BP)_ zlxJA0GcrrzuM+)v>}dxb_m6^jAL`1@c$*82+Qr72}2W2C{?``^ii{TF1z{_kYl z{ugB15?LNP8wP!SvaGgvPmkDFavQ$-iJZ=>AAp}*Kb0LdU}dVK9Wyd>4L-d5sqCX` zc-S5O^N^twJfE7=PvuEQJbbof_pfw8k=S{<2-|;{jRbMzJ)g+V4Kl#Pf04nK-+n63 zwG3zvHq0BHQEKpAc-1Ym=J4Ng8xvkS721Jclx)}F!sh>-tBe&_@ju<=rp^8jo_KH9 z|C^yxY~N)NS=omNKb4*Td#i7GBUc&m<_VhrAFKZ-fqr}=zp)NzpJixp!q%&9TP>Az z?NoOQMHH53$;miu~-f-ODE^0NH=cx5#>@B!4| znrGH-3ydv+U}oWnQs}fkHQ(8}`C=b

yNv!m=)ThWsq5@9Fk#W;F-x-L$xV-@$Fj z`|A!h;~m|488`FQdlE;zcV1zBx!zCTteHNqBrO|$0uO~xJmY%;t#!QU17|INGuYHl znF};EA+B!w-nKjk-c;`v=K!L08k%S*&FGX-n$ZnLNeubrdba^K^2nNmhmF^iuidJS XUH;lG;o)D`ymr6|L)EoIVu$|$iZs0G From 30662e98b31bce515aabb2744fc9c53df82721b9 Mon Sep 17 00:00:00 2001 From: Stefan Moises Date: Fri, 16 Mar 2018 16:20:26 +0100 Subject: [PATCH 23/30] Fixed namespace in module list command --- src/Oxrun/Command/Module/ListCommand.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Oxrun/Command/Module/ListCommand.php b/src/Oxrun/Command/Module/ListCommand.php index 399b82f..bd71529 100644 --- a/src/Oxrun/Command/Module/ListCommand.php +++ b/src/Oxrun/Command/Module/ListCommand.php @@ -43,8 +43,8 @@ protected function execute(InputInterface $input, OutputInterface $output) $this->checkModulelist($shopId); - /* @var \OxidEsales\Eshop\Core\ModuleList $oxModuleList */ - $oxModuleList = oxNew(\OxidEsales\Eshop\Core\ModuleList::class); + /* @var \OxidEsales\Eshop\Core\Module\ModuleList $oxModuleList */ + $oxModuleList = oxNew(\OxidEsales\Eshop\Core\Module\ModuleList::class); $activeModules = array_keys($oxModuleList->getActiveModuleInfo()); $deactiveModules = $oxModuleList->getDisabledModules();; From d81afe74f24cb083e980a629fba2db7c91569397 Mon Sep 17 00:00:00 2001 From: Stefan Moises Date: Mon, 19 Mar 2018 13:31:22 +0100 Subject: [PATCH 24/30] Tweaked existing tests a bit for OXID 6 --- README.md | 8 +++ .../Command/Module/ActivateCommandTest.php | 12 ++--- .../Command/Module/DeactivateCommandTest.php | 4 +- .../Oxrun/Command/Module/ListCommandTest.php | 2 +- .../Command/Module/ReloadCommandTest.php | 2 +- tests/Oxrun/Command/Route/DebugTest.php | 49 +++++++++++-------- tests/Oxrun/Helper/DatabaseConnectionTest.php | 11 +++-- 7 files changed, 52 insertions(+), 36 deletions(-) diff --git a/README.md b/README.md index 04d0d46..6dcd3dd 100644 --- a/README.md +++ b/README.md @@ -675,3 +675,11 @@ route:debug * Name: `--shopId` * Is value required: no * Description: + +# Run the unit tests + +The unit tests require a configured shop and a database. To start the tests, run the following command __in the "source" folder of your OXID 6 installation__ and set the correct path to the "oxrun" vendor directory, e.g.: + +```bash +../vendor/bin/phpunit /var/www/html/oxid6/vendor/smxsm/oxrun/ +``` diff --git a/tests/Oxrun/Command/Module/ActivateCommandTest.php b/tests/Oxrun/Command/Module/ActivateCommandTest.php index e4682a9..e7f558c 100644 --- a/tests/Oxrun/Command/Module/ActivateCommandTest.php +++ b/tests/Oxrun/Command/Module/ActivateCommandTest.php @@ -20,7 +20,7 @@ public function testExecute() $commandTester->execute( array( 'command' => $command->getName(), - 'module' => 'invoicepdf' + 'module' => 'oepaypal' ) ); @@ -30,22 +30,22 @@ public function testExecute() $commandTester->execute( array( 'command' => $command->getName(), - 'module' => 'invoicepdf' + 'module' => 'oepaypal' ) ); - $this->assertContains('Module invoicepdf activated.', $commandTester->getDisplay()); + $this->assertContains('Module oepaypal activated', $commandTester->getDisplay()); $commandTester = new CommandTester($command); $commandTester->execute( array( 'command' => $command->getName(), - 'module' => 'invoicepdf' + 'module' => 'oepaypal' ) ); - $this->assertContains('Module invoicepdf already activated.', $commandTester->getDisplay()); + $this->assertContains('Module oepaypal already activated', $commandTester->getDisplay()); $commandTester = new CommandTester($command); $commandTester->execute( @@ -55,7 +55,7 @@ public function testExecute() ) ); - $this->assertContains('Cannot load module not_and_existing_module.', $commandTester->getDisplay()); + $this->assertContains('Cannot load module not_and_existing_module', $commandTester->getDisplay()); } diff --git a/tests/Oxrun/Command/Module/DeactivateCommandTest.php b/tests/Oxrun/Command/Module/DeactivateCommandTest.php index d1866b6..433d30d 100644 --- a/tests/Oxrun/Command/Module/DeactivateCommandTest.php +++ b/tests/Oxrun/Command/Module/DeactivateCommandTest.php @@ -19,11 +19,11 @@ public function testExecute() $commandTester->execute( array( 'command' => $command->getName(), - 'module' => 'invoicepdf' + 'module' => 'oepaypal' ) ); - $this->assertContains('Module invoicepdf deactivated.', $commandTester->getDisplay()); + $this->assertContains('Module oepaypal deactivated', $commandTester->getDisplay()); } } \ No newline at end of file diff --git a/tests/Oxrun/Command/Module/ListCommandTest.php b/tests/Oxrun/Command/Module/ListCommandTest.php index fa6e65b..201fbce 100644 --- a/tests/Oxrun/Command/Module/ListCommandTest.php +++ b/tests/Oxrun/Command/Module/ListCommandTest.php @@ -22,7 +22,7 @@ public function testExecute() ) ); - $this->assertContains('invoice', $commandTester->getDisplay()); + $this->assertContains('paypal', $commandTester->getDisplay()); } } \ No newline at end of file diff --git a/tests/Oxrun/Command/Module/ReloadCommandTest.php b/tests/Oxrun/Command/Module/ReloadCommandTest.php index c6b34d8..3d8efa6 100644 --- a/tests/Oxrun/Command/Module/ReloadCommandTest.php +++ b/tests/Oxrun/Command/Module/ReloadCommandTest.php @@ -29,7 +29,7 @@ public function testExecute() $commandTester->execute( array( 'command' => $command->getName(), - 'module' => 'invoicepdf' + 'module' => 'oepaypal' ) ); diff --git a/tests/Oxrun/Command/Route/DebugTest.php b/tests/Oxrun/Command/Route/DebugTest.php index 8bd3e7f..7d22248 100644 --- a/tests/Oxrun/Command/Route/DebugTest.php +++ b/tests/Oxrun/Command/Route/DebugTest.php @@ -16,7 +16,9 @@ */ class DebugTest extends TestCase { - const commandName = 'route:debug'; + const COMMANDNAME = 'route:debug'; + + protected $baseUrl = 'http://localhost'; /** * @var Route\DebugCommand|CommandTester */ @@ -27,7 +29,11 @@ protected function setUp() $app = new Application(); $app->add(new Route\DebugCommand()); - $command = $app->find(self::commandName); + $command = $app->find(self::COMMANDNAME); + + $this->baseUrl = \OxidEsales\Eshop\Core\Registry::getConfig()->getConfigParam('sShopURL'); + + // TODO - insert static seo url for "warenkorb"!!? $this->commandTester = new CommandTester($command); } @@ -36,20 +42,21 @@ public function testCompleteUrl() { $this->commandTester->execute( array( - 'url' => 'http://localhost/warenkorb/', - 'command' => self::commandName, + 'url' => $this->baseUrl . '/warenkorb/', + 'command' => self::COMMANDNAME, ) ); + //echo "\nRESULT: " . $this->commandTester->getDisplay(); $this->assertRegExp('~\|\s+Controller\s+\|\s+basket\s+\|~', $this->commandTester->getDisplay()); } - public function testHalfBrockenUrl() + public function testHalfBrokenUrl() { $this->commandTester->execute( array( - 'url' => 'http://localhost/warenkorb', - 'command' => self::commandName, + 'url' => $this->baseUrl . '/warenkorb', + 'command' => self::COMMANDNAME, ) ); @@ -61,7 +68,7 @@ public function testOnlyPath() $this->commandTester->execute( array( 'url' => 'warenkorb/', - 'command' => self::commandName, + 'command' => self::COMMANDNAME, ) ); @@ -73,7 +80,7 @@ public function testHalfOnlyPath() $this->commandTester->execute( array( 'url' => 'warenkorb', - 'command' => self::commandName, + 'command' => self::COMMANDNAME, ) ); @@ -85,7 +92,7 @@ public function testGiveMeClassPath() $this->commandTester->execute( array( 'url' => 'warenkorb', - 'command' => self::commandName, + 'command' => self::COMMANDNAME, ) ); @@ -94,30 +101,30 @@ public function testGiveMeClassPath() public function testGiveFunctionLineNumber() { - /** @var oxSeoEncoder $oxSeoEncoder */ - $oxSeoEncoder = oxNew('oxSeoEncoder'); + /** @var \OxidEsales\Eshop\Core\SeoDecoder $oxSeoEncoder */ + $oxSeoEncoder = oxNew(\OxidEsales\Eshop\Core\SeoEncoder::class); $oxSeoEncoder->getDynamicUrl('index.php?cl=news&fnc=render', 'newspage/', 0); $this->commandTester->execute( array( 'url' => 'NewsPage/', - 'command' => self::commandName, + 'command' => self::COMMANDNAME, ) ); - $this->assertRegExp('~news.php:\d+~', $this->commandTester->getDisplay()); + $this->assertRegExp('~NewsController.php:\d+~', $this->commandTester->getDisplay()); } public function testClassDontExists() { - /** @var oxSeoEncoder $oxSeoEncoder */ - $oxSeoEncoder = oxNew('oxSeoEncoder'); + /** @var \OxidEsales\Eshop\Core\SeoDecoder $oxSeoEncoder */ + $oxSeoEncoder = oxNew(\OxidEsales\Eshop\Core\SeoEncoder::class); $oxSeoEncoder->getDynamicUrl('index.php?cl=classdontexists', 'class/dont/exists/', 0); $this->commandTester->execute( array( 'url' => 'Class/Dont/Exists/', - 'command' => self::commandName, + 'command' => self::COMMANDNAME, ) ); @@ -126,14 +133,14 @@ public function testClassDontExists() public function testMethodInClassDontExists() { - /** @var oxSeoEncoder $oxSeoEncoder */ - $oxSeoEncoder = oxNew('oxSeoEncoder'); + /** @var \OxidEsales\Eshop\Core\SeoEncoder $oxSeoEncoder */ + $oxSeoEncoder = oxNew(\OxidEsales\Eshop\Core\SeoEncoder::class); $oxSeoEncoder->getDynamicUrl('index.php?cl=news&fnc=nameXYX', 'method/in/class/dont/exists/', 0); $this->commandTester->execute( array( 'url' => 'Method/In/Class/Dont/Exists/', - 'command' => self::commandName, + 'command' => self::COMMANDNAME, ) ); @@ -145,7 +152,7 @@ public function testMethodInClassDontExists() */ public static function tearDownAfterClass() { - $db = oxDb::getDb(); + $db = \OxidEsales\Eshop\Core\DatabaseProvider::getDb(); $seoURls[] = $db->quote('newspage/'); $seoURls[] = $db->quote('class/dont/exists/'); diff --git a/tests/Oxrun/Helper/DatabaseConnectionTest.php b/tests/Oxrun/Helper/DatabaseConnectionTest.php index 9e7a03c..0d15618 100644 --- a/tests/Oxrun/Helper/DatabaseConnectionTest.php +++ b/tests/Oxrun/Helper/DatabaseConnectionTest.php @@ -21,14 +21,15 @@ class DatabaseConnectionTest extends \PHPUnit_Framework_TestCase */ protected function setUp() { + $oConfig = \OxidEsales\Eshop\Core\Registry::getConfig(); $databaseConnection = new DatabaseConnection(); $databaseConnection // Must be right to work correct - ->setHost('127.0.0.1') + ->setHost($oConfig->getConfigParam('dbHost')) ->setPort('3306') - ->setUser('root') - ->setPass('') - ->setDatabase('oxid'); + ->setUser($oConfig->getConfigParam('dbUser')) + ->setPass($oConfig->getConfigParam('dbPwd')) + ->setDatabase($oConfig->getConfigParam('dbName')); $this->testSubject = $databaseConnection; } @@ -46,7 +47,7 @@ public function testHasNotConnected() $this->testSubject->setDatabase('oxid_unbekannt'); self::assertFalse($this->testSubject->canConnectToMysql()); - self::assertContains("Unknown database 'oxid_unbekannt'", $this->testSubject->getLastErrorMsg()); + self::assertContains("database 'oxid_unbekannt'", $this->testSubject->getLastErrorMsg()); } /** From bfe11d77b2ac09bad84471c18ee5eedeb767daae Mon Sep 17 00:00:00 2001 From: Stefan Moises Date: Mon, 19 Mar 2018 17:57:43 +0100 Subject: [PATCH 25/30] Fixed some tests for OXID 6, allow to pass YAML as string for multi* commands, added multiactivate command --- README.md | 6 +++ src/Oxrun/Application.php | 27 ++++++++++++ src/Oxrun/Command/Config/MultiSetCommand.php | 14 +++--- .../Command/Module/MultiActivateCommand.php | 14 +++--- .../Module/MultiActivateCommandTest.php | 44 +++++++++++++++++++ tests/Oxrun/Command/Route/DebugTest.php | 41 ++++++++++++----- .../Command/User/PasswordCommandTest.php | 21 +++++++++ 7 files changed, 137 insertions(+), 30 deletions(-) create mode 100644 tests/Oxrun/Command/Module/MultiActivateCommandTest.php diff --git a/README.md b/README.md index 6dcd3dd..ae319ef 100644 --- a/README.md +++ b/README.md @@ -551,6 +551,12 @@ whitelist: Supports either a __"whitelist"__ or a __"blacklist"__ entry with multiple shop ids and the desired module ids to activate (whitelist) or to exclude from activation (blacklist). +If you want, you can also specify __a YAML string on the command line instead of a file__, e.g.: + +```bash +../vendor/bin/oxrun module:multiactivate $'whitelist:\n 1:\n - oepaypal\n' --shopId=1 +``` + ### Options: **shopId:** diff --git a/src/Oxrun/Application.php b/src/Oxrun/Application.php index 5668367..9f529b2 100644 --- a/src/Oxrun/Application.php +++ b/src/Oxrun/Application.php @@ -289,6 +289,33 @@ public function switchToShopId($shopId) throw new \Exception('Failed to switch to subshop id ' . $shopId . " Calculate ID: " . $shopIdCalculator->getShopId() . " Config ShopId: " . \OxidEsales\Eshop\Core\Registry::getConfig()->getShopId()); } } + + /** + * Get YAML string, either from file or from string + * + * @param string $ymlString The relative file path, from shop root OR a YAML string + * @param string $basePath Alternative root dir path, if a file is used + * + * @return string + */ + public function getYaml($ymlString, $basePath = '') + { + // is it a file? + if (strpos(strtolower($ymlString), '.yml') !== false + || strpos(strtolower($ymlString), '.yaml') !== false + ) { + if ($basePath == '') { + $basePath = $this->getShopDir() . DIRECTORY_SEPARATOR; + } + $ymlFile = $basePath . $ymlString; + if (file_exists($ymlFile)) { + $ymlString = file_get_contents($ymlFile); + } + } + + return $ymlString; + } + /** * Find Version on Place into Oxid Legacy Code diff --git a/src/Oxrun/Command/Config/MultiSetCommand.php b/src/Oxrun/Command/Config/MultiSetCommand.php index 0c521f7..6b6d0c1 100644 --- a/src/Oxrun/Command/Config/MultiSetCommand.php +++ b/src/Oxrun/Command/Config/MultiSetCommand.php @@ -44,7 +44,7 @@ protected function configure() ->setDescription('Sets multiple config values from yaml file') ->addArgument('configfile', InputArgument::REQUIRED, 'The yaml file name, relative to shop base.'); } - + /** * Executes the current command. * @@ -59,14 +59,10 @@ protected function execute(InputInterface $input, OutputInterface $output) /* @var \Oxrun\Application $app */ $app = $this->getApplication(); - // now try to read specified YAML file - $mallYml = $input->getArgument('configfile'); - $ymlFile = $app->getShopDir() . DIRECTORY_SEPARATOR . $mallYml; - if (!file_exists($ymlFile)) { - $output->writeLn("Yaml file '$ymlFile' not found!"); - return; - } - $mallValues = Yaml::parse($ymlFile); + // now try to read YAML + $mallYml = $app->getYaml($input->getArgument('configfile')); + $mallValues = Yaml::parse($mallYml); + if ($mallValues && is_array($mallValues['config'])) { $mallSettings = $mallValues['config']; foreach ($mallSettings as $shopId => $configData) { diff --git a/src/Oxrun/Command/Module/MultiActivateCommand.php b/src/Oxrun/Command/Module/MultiActivateCommand.php index 6fe8165..83be07b 100644 --- a/src/Oxrun/Command/Module/MultiActivateCommand.php +++ b/src/Oxrun/Command/Module/MultiActivateCommand.php @@ -65,15 +65,9 @@ protected function execute(InputInterface $input, OutputInterface $output) $skipDeactivation = $input->getOption('skipDeactivation'); $skipClear = $input->getOption('skipClear'); - // now try to ready specified YAML file - $moduleYml = $input->getArgument('module'); - $ymlFile = $app->getShopDir() . DIRECTORY_SEPARATOR . $moduleYml; - if (!file_exists($ymlFile)) { - $output->writeLn("Yaml file '$ymlFile' not found!"); - return; - } - $moduleValues = Yaml::parse($ymlFile); - + // now try to read YAML + $moduleYml = $app->getYaml($input->getArgument('module')); + $moduleValues = Yaml::parse($moduleYml); if ($moduleValues && is_array($moduleValues)) { // use whitelist if (isset($moduleValues['whitelist'])) { @@ -141,6 +135,8 @@ protected function execute(InputInterface $input, OutputInterface $output) } else { $output->writeLn("No modules to activate for subshop '$shopId'!"); } + } else { + $output->writeLn("No valid YAML data found!"); } } diff --git a/tests/Oxrun/Command/Module/MultiActivateCommandTest.php b/tests/Oxrun/Command/Module/MultiActivateCommandTest.php new file mode 100644 index 0000000..a0e737a --- /dev/null +++ b/tests/Oxrun/Command/Module/MultiActivateCommandTest.php @@ -0,0 +1,44 @@ +add(new ActivateCommand()); + $app->add(new ClearCommand()); + $app->add(new DeactivateCommand()); + $app->add(new MultiActivateCommand()); + + $command = $app->find('module:multiactivate'); + + $commandTester = new CommandTester($command); + $commandTester->execute( + array( + 'command' => $command->getName(), + 'module' => "whitelist:\n 1:\n - oepaypal\n" + ) + ); + + $this->assertContains('Module oepaypal activated', $commandTester->getDisplay()); + + $commandTester = new CommandTester($command); + $commandTester->execute( + array( + 'command' => $command->getName(), + 'module' => "whitelist:\n 1:\n - not_and_existing_module\n" + ) + ); + + $this->assertContains('Cannot load module not_and_existing_module', $commandTester->getDisplay()); + + } + +} \ No newline at end of file diff --git a/tests/Oxrun/Command/Route/DebugTest.php b/tests/Oxrun/Command/Route/DebugTest.php index 7d22248..14e660b 100644 --- a/tests/Oxrun/Command/Route/DebugTest.php +++ b/tests/Oxrun/Command/Route/DebugTest.php @@ -18,12 +18,30 @@ class DebugTest extends TestCase { const COMMANDNAME = 'route:debug'; + /** + * Shop URL, will be read from config + * + * @var string + */ protected $baseUrl = 'http://localhost'; + + /** + * Seo path to search for + * + * @var string + */ + protected $seoKey = 'Nach-Hersteller'; + /** * @var Route\DebugCommand|CommandTester */ protected $commandTester; + /** + * Prepare + * + * @return void + */ protected function setUp() { $app = new Application(); @@ -33,8 +51,7 @@ protected function setUp() $this->baseUrl = \OxidEsales\Eshop\Core\Registry::getConfig()->getConfigParam('sShopURL'); - // TODO - insert static seo url for "warenkorb"!!? - + // TODO - insert static seo url for seoKey!? $this->commandTester = new CommandTester($command); } @@ -42,61 +59,61 @@ public function testCompleteUrl() { $this->commandTester->execute( array( - 'url' => $this->baseUrl . '/warenkorb/', + 'url' => $this->baseUrl . "/{$this->seoKey}/", 'command' => self::COMMANDNAME, ) ); //echo "\nRESULT: " . $this->commandTester->getDisplay(); - $this->assertRegExp('~\|\s+Controller\s+\|\s+basket\s+\|~', $this->commandTester->getDisplay()); + $this->assertRegExp('~\|\s+Controller\s+\|\s+manufacturerlist\s+\|~', $this->commandTester->getDisplay()); } public function testHalfBrokenUrl() { $this->commandTester->execute( array( - 'url' => $this->baseUrl . '/warenkorb', + 'url' => $this->baseUrl . '/' . $this->seoKey, 'command' => self::COMMANDNAME, ) ); - $this->assertRegExp('~\|\s+Controller\s+\|\s+basket\s+\|~', $this->commandTester->getDisplay()); + $this->assertRegExp('~\|\s+Controller\s+\|\s+manufacturerlist\s+\|~', $this->commandTester->getDisplay()); } public function testOnlyPath() { $this->commandTester->execute( array( - 'url' => 'warenkorb/', + 'url' => $this->seoKey. '/', 'command' => self::COMMANDNAME, ) ); - $this->assertRegExp('~\|\s+Controller\s+\|\s+basket\s+\|~', $this->commandTester->getDisplay()); + $this->assertRegExp('~\|\s+Controller\s+\|\s+manufacturerlist\s+\|~', $this->commandTester->getDisplay()); } public function testHalfOnlyPath() { $this->commandTester->execute( array( - 'url' => 'warenkorb', + 'url' => $this->seoKey, 'command' => self::COMMANDNAME, ) ); - $this->assertRegExp('~\|\s+Controller\s+\|\s+basket\s+\|~', $this->commandTester->getDisplay()); + $this->assertRegExp('~\|\s+Controller\s+\|\s+manufacturerlist\s+\|~', $this->commandTester->getDisplay()); } public function testGiveMeClassPath() { $this->commandTester->execute( array( - 'url' => 'warenkorb', + 'url' => $this->seoKey, 'command' => self::COMMANDNAME, ) ); - $this->assertContains('basket.php', $this->commandTester->getDisplay()); + $this->assertContains('manufacturerlist', $this->commandTester->getDisplay()); } public function testGiveFunctionLineNumber() diff --git a/tests/Oxrun/Command/User/PasswordCommandTest.php b/tests/Oxrun/Command/User/PasswordCommandTest.php index 068dbf5..4c30f24 100644 --- a/tests/Oxrun/Command/User/PasswordCommandTest.php +++ b/tests/Oxrun/Command/User/PasswordCommandTest.php @@ -8,6 +8,27 @@ class PasswordCommandTest extends TestCase { + /** + * Preparation + * + * @return void + */ + protected function setUp() + { + // TODO - insert user + } + + /** + * Cleanup + */ + public static function tearDownAfterClass() + { + // TODO - delete user + $db = \OxidEsales\Eshop\Core\DatabaseProvider::getDb(); + + //$db->execute("DELETE FROM oxuser WHERE ..."); + } + public function testExecute() { $app = new Application(); From 8e42f3fdabfdd2299eaaef91b904c92419e001ed Mon Sep 17 00:00:00 2001 From: Stefan Moises Date: Wed, 21 Mar 2018 13:56:13 +0100 Subject: [PATCH 26/30] added blacklist test and multiset test --- .../Command/Config/MultiSetCommandTest.php | 29 +++++++++++++++++++ .../Module/MultiActivateCommandTest.php | 10 +++++++ 2 files changed, 39 insertions(+) create mode 100644 tests/Oxrun/Command/Config/MultiSetCommandTest.php diff --git a/tests/Oxrun/Command/Config/MultiSetCommandTest.php b/tests/Oxrun/Command/Config/MultiSetCommandTest.php new file mode 100644 index 0000000..f31b43b --- /dev/null +++ b/tests/Oxrun/Command/Config/MultiSetCommandTest.php @@ -0,0 +1,29 @@ +add(new MultiSetCommand()); + + $command = $app->find('config:multiset'); + + $commandTester = new CommandTester($command); + $commandTester->execute( + array( + 'command' => $command->getName(), + 'configfile' => "config:\n 1:\n foobar: barfoo\n" + ) + ); + + $this->assertContains("Config foobar for shop 1 set", $commandTester->getDisplay()); + } + +} \ No newline at end of file diff --git a/tests/Oxrun/Command/Module/MultiActivateCommandTest.php b/tests/Oxrun/Command/Module/MultiActivateCommandTest.php index a0e737a..af4f72d 100644 --- a/tests/Oxrun/Command/Module/MultiActivateCommandTest.php +++ b/tests/Oxrun/Command/Module/MultiActivateCommandTest.php @@ -29,6 +29,16 @@ public function testExecute() $this->assertContains('Module oepaypal activated', $commandTester->getDisplay()); + $commandTester = new CommandTester($command); + $commandTester->execute( + array( + 'command' => $command->getName(), + 'module' => "blacklist:\n 1:\n - oepaypal\n" + ) + ); + + $this->assertContains("Module blacklisted: 'oepaypal'", $commandTester->getDisplay()); + $commandTester = new CommandTester($command); $commandTester->execute( array( From 56ef33451415f794a3233792405b6d33d2196459 Mon Sep 17 00:00:00 2001 From: Stefan Moises Date: Wed, 21 Mar 2018 14:26:34 +0100 Subject: [PATCH 27/30] fixed password update test by inserting and deleting dummy user --- tests/Oxrun/Command/User/PasswordCommandTest.php | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/tests/Oxrun/Command/User/PasswordCommandTest.php b/tests/Oxrun/Command/User/PasswordCommandTest.php index 4c30f24..85c7749 100644 --- a/tests/Oxrun/Command/User/PasswordCommandTest.php +++ b/tests/Oxrun/Command/User/PasswordCommandTest.php @@ -15,7 +15,12 @@ class PasswordCommandTest extends TestCase */ protected function setUp() { - // TODO - insert user + $db = \OxidEsales\Eshop\Core\DatabaseProvider::getDb(); + // insert user + $sql = "INSERT INTO `oxuser` (`OXID`, `OXACTIVE`, `OXRIGHTS`, `OXSHOPID`, `OXUSERNAME`, `OXPASSWORD`, `OXPASSSALT`, `OXCUSTNR`, `OXUSTID`, `OXCOMPANY`, `OXFNAME`, `OXLNAME`, `OXSTREET`, `OXSTREETNR`, `OXADDINFO`, `OXCITY`, `OXCOUNTRYID`, `OXSTATEID`, `OXZIP`, `OXFON`, `OXFAX`, `OXSAL`, `OXBONI`, `OXCREATE`, `OXREGISTER`, `OXPRIVFON`, `OXMOBFON`, `OXBIRTHDATE`, `OXURL`, `OXUPDATEKEY`, `OXUPDATEEXP`, `OXPOINTS`, `OXTIMESTAMP`) VALUES + ('__dummyuser__', 1, 'malladmin', '1', 'foobar@barfoo.de', '378d25534d551e83433f532f95893d7bd6ebb2727e392da65b6cd8adb06277f577325757a21ddf6b7dd68629c1f4541b1ceeff9945dec3b7c900ba1a0a05097e', '3a4e1e271e36554a6b89ef41f8a3c544', 1, '', '', 'John', 'Doe', 'Teststrasse 10', '', '', 'Any City', 'a7c40f631fc920687.20179984', '', '90410', '217-8918712', '', 'MR', 1000, '2003-01-01 00:00:00', '2003-01-01 00:00:00', '', '', '0000-00-00', '', '', 0, 0, '2016-03-15 08:19:20') + ON DUPLICATE KEY UPDATE oxrights='malladmin'"; + $db->execute($sql); } /** @@ -23,10 +28,9 @@ protected function setUp() */ public static function tearDownAfterClass() { - // TODO - delete user + // delete user $db = \OxidEsales\Eshop\Core\DatabaseProvider::getDb(); - - //$db->execute("DELETE FROM oxuser WHERE ..."); + $db->execute("DELETE FROM oxuser WHERE OXID = '__dummyuser__'"); } public function testExecute() @@ -40,7 +44,7 @@ public function testExecute() $commandTester->execute( array( 'command' => $command->getName(), - 'username' => 'info@oxid-esales.com', + 'username' => 'foobar@barfoo.de', 'password' => 'thenewpassword' ) ); From 16306cb71580183c5e8edf7707fed7d17f57284a Mon Sep 17 00:00:00 2001 From: Stefan Moises Date: Wed, 21 Mar 2018 14:42:52 +0100 Subject: [PATCH 28/30] new phar, for new release, ya know --- oxrun.phar | Bin 1912593 -> 1912736 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/oxrun.phar b/oxrun.phar index f315dd39af23fc36acb1ea7dacfc4c6516d8d279..2e08ec1232a447bbffcb7ccf3ad58f16d3131625 100644 GIT binary patch delta 14816 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 delta 14654 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
Chv6?8p>+DemA(87sqX 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 e_Sfyd8j?S6Ro(OQdb5J9i|<0dD|&W`>heE Date: Sun, 25 Mar 2018 12:59:20 +0200 Subject: [PATCH 29/30] Updated and added help and docs --- README.md | 877 +++++++++++------- src/Oxrun/Command/Config/ExportCommand.php | 9 +- src/Oxrun/Command/Config/ImportCommand.php | 9 +- src/Oxrun/Command/Config/MultiSetCommand.php | 34 +- src/Oxrun/Command/Install/InstallCommand.php | 4 +- src/Oxrun/Command/Module/FixCommand.php | 2 +- src/Oxrun/Command/Module/GenerateCommand.php | 2 +- .../Command/Module/MultiActivateCommand.php | 33 +- 8 files changed, 648 insertions(+), 322 deletions(-) diff --git a/README.md b/README.md index ae319ef..5ef73eb 100644 --- a/README.md +++ b/README.md @@ -51,190 +51,329 @@ __Please note:__ since activating modules and updating config values requires a # Available commands -cache:clear ------------ -* Description: Clears the cache -* Usage: `cache:clear` + +misc:phpstorm:metadata +---------------------- + +* Description: Generate a PhpStorm metadata file for auto-completion. +* Usage: + + * `misc:phpstorm:metadata [-o|--output-dir OUTPUT-DIR]` + +Generate a PhpStorm metadata file for auto-completion. ### Options: -cms:update ----------- +**output-dir:** -* Description: Updates a cms page -* Usage: `cms:update [--title[="..."]] [--content[="..."]] [--language="..."] [--active="..."] ident` +* Name: `--output-dir` +* Shortcut: `-o` +* Is value required: yes +* Description: Writes the metadata for PhpStorm to the specified directory. + +misc:generate:documentation +--------------------------- + +* Description: Generate a raw command documentation of the available commands +* Usage: + + * `misc:generate:documentation` + +Generate a raw command documentation of the available commands ### Arguments: -**ident:** +**command:** -* Name: ident -* Description: Content ident +* Name: command +* Description: The command to execute ### Options: -**title:** +db:query +-------- -* Name: `--title` -* Is value required: no -* Description: Content title +* Description: Executes a query +* Usage: -**content:** + * `db:query [--raw] [--] ` -* Name: `--content` -* Is value required: no -* Description: Content body +Executes an SQL query on the current shop database. Wrap your SQL in quotes. -**language:** +If your query produces a result (e.g. a SELECT statement), the output will be returned via the table component. Add the raw option for raw output. -* Name: `--language` -* Is value required: yes -* Description: Content language +Requires php exec and MySQL CLI tools installed on your system. -**active:** +### Arguments: -* Name: `--active` -* Is value required: yes -* Description: Content active +**query:** -config:get ----------- +* Name: query +* Description: The query which is to be executed -* Description: Gets a config value -* Usage: `config:get [--shopId[="..."]] [--moduleId[="..."]] variableName` +### Options: + +**raw:** + +* Name: `--raw` +* Accept value: no +* Is value required: no +* Description: Raw output +* Default: `false` + +db:import +--------- + +* Description: Import a sql file +* Usage: + + * `db:import ` + +Imports an SQL file on the current shop database. + +Requires php exec and MySQL CLI tools installed on your system. ### Arguments: -**variableName:** +**file:** -* Name: variableName -* Description: Variable name +* Name: file +* Description: The sql file which is to be imported ### Options: -**shopId:** +db:list +------- -* Name: `--shopId` +* Description: List of all Tables +* Usage: + + * `db:list [-p|--plain] [-t|--pattern PATTERN]` + +List Tables + +usage: + oxrun db:list --pattern oxseo%,oxuser + - To dump all Tables, but `oxseo`, `oxvoucher`, and `oxvoucherseries` without data. + possibilities: oxseo%,oxuser,%logs% + + + +### Options: + +**plain:** + +* Name: `--plain` +* Shortcut: `-p` +* Accept value: no * Is value required: no -* Description: +* Description: print list as comma separated. +* Default: `false` -**moduleId:** +**pattern:** -* Name: `--moduleId` +* Name: `--pattern` +* Shortcut: `-t` +* Is value required: yes +* Description: table name pattern test. e.g. oxseo%,oxuser + +db:dump +------- + +* Description: Dumps the the current shop database +* Usage: + + * `db:dump [--file FILE] [-t|--table TABLE] [-i|--ignoreViews] [-a|--anonymous] [-w|--withoutTableData WITHOUTTABLEDATA]` + +Dump the current shop database. + +usage: + oxrun db:dump --withoutTableData oxseo,oxvou% + - To dump all Tables, but `oxseo`, `oxvoucher`, and `oxvoucherseries` without data. + possibilities: oxseo%,oxuser,%logs% + + oxrun db:dump --table %user% + - to dump only those tables `oxuser` `oxuserbasketitems` `oxuserbaskets` `oxuserpayments` + + oxrun db:dump --anonymous # Perfect for Stage Server + - Those table without data: `oxseo`, `oxseologs`, `oxseohistory`, `oxuser`, `oxuserbasketitems`, `oxuserbaskets`, `oxuserpayments`, `oxnewssubscribed`, `oxremark`, `oxvouchers`, `oxvoucherseries`, `oxaddress`, `oxorder`, `oxorderarticles`, `oxorderfiles`, `oepaypal_order`, `oepaypal_orderpayments`. + + oxrun db:dump -v + - With verbose mode you will see the mysqldump command + (`mysqldump -u 'root' -h 'oxid_db' -p ... `) + + oxrun db:dump --file dump.sql + - Put the Output into a File + +** Only existing tables will be exported. No matter what was required. + +Requires php, exec and MySQL CLI tools installed on your system. + +### Options: + +**file:** + +* Name: `--file` +* Is value required: yes +* Description: Dump sql in to this file + +**table:** + +* Name: `--table` +* Shortcut: `-t` +* Is value required: yes +* Description: name of table to dump only. Default all tables. Use comma separated list and or pattern e.g. %voucher% + +**ignoreViews:** + +* Name: `--ignoreViews` +* Shortcut: `-i` +* Accept value: no * Is value required: no -* Description: +* Description: Ignore views +* Default: `false` -config:set ----------- +**anonymous:** -* Description: Sets a config value -* Usage: `config:set [--variableType="..."] [--shopId[="..."]] [--moduleId[="..."]] variableName variableValue` +* Name: `--anonymous` +* Shortcut: `-a` +* Accept value: no +* Is value required: no +* Description: Do not export table with person related data. +* Default: `false` -### Arguments: +**withoutTableData:** -**variableName:** +* Name: `--withoutTableData` +* Shortcut: `-w` +* Is value required: yes +* Description: Table name to dump without data. Use comma separated list and or pattern e.g. %voucher% -* Name: variableName -* Description: Variable name +install:shop __[DEPRECATED]__ +----------------------------- -**variableValue:** +* Description: Installs the shop, for OXID 6 composer is used instead! +* Usage: -* Name: variableValue -* Description: Variable value + * `install:shop __[DEPRECATED]__ [--oxidVersion [OXIDVERSION]] [--installationFolder [INSTALLATIONFOLDER]] [--dbHost DBHOST] [--dbUser DBUSER] [--dbPwd DBPWD] [--dbName DBNAME] [--dbPort [DBPORT]] [--installSampleData [INSTALLSAMPLEDATA]] [--shopURL SHOPURL] [--adminUser ADMINUSER] [--adminPassword ADMINPASSWORD]` + +Installs the shop, for OXID 6 composer is used instead! ### Options: -**variableType:** +**oxidVersion:** -* Name: `--variableType` -* Is value required: yes -* Description: Variable type +* Name: `--oxidVersion` +* Is value required: no +* Description: Oxid version -**shopId:** +**installationFolder:** -* Name: `--shopId` +* Name: `--installationFolder` * Is value required: no -* Description: +* Description: Installation folder +* Default: `'/var/www/html/gerstaecker-oxid6/source'` -**moduleId:** +**dbHost:** -* Name: `--moduleId` +* Name: `--dbHost` +* Is value required: yes +* Description: Database host +* Default: `'localhost'` + +**dbUser:** + +* Name: `--dbUser` +* Is value required: yes +* Description: Database user +* Default: `'oxid'` + +**dbPwd:** + +* Name: `--dbPwd` +* Is value required: yes +* Description: Database password +* Default: `''` + +**dbName:** + +* Name: `--dbName` +* Is value required: yes +* Description: Database name +* Default: `'oxid'` + +**dbPort:** + +* Name: `--dbPort` * Is value required: no -* Description: +* Description: Database port +* Default: `3306` -config:multiset ----------- +**installSampleData:** + +* Name: `--installSampleData` +* Is value required: no +* Description: Install sample data +* Default: `true` -* Description: Sets multiple config values for multiple subshops, defined in a yaml file -* Usage: `config:multiset configfile` +**shopURL:** -### Arguments: +* Name: `--shopURL` +* Is value required: yes +* Description: Installation base url -**configfile:** +**adminUser:** -The file containing the config values, see malls.yml.dist. The file path is relative to the shop root. +* Name: `--adminUser` +* Is value required: yes +* Description: Admin user email/login +* Default: `'admin@example.com'` -```yaml -config: - 1: - blReverseProxyActive: - variableType: bool - variableValue: false - # simple string type - sMallShopURL: http://myshop.dev.local - sMallSSLShopURL: http://myshop.dev.local - myMultiVal: - variableType: aarr - variableValue: - - /foo/bar/ - - /bar/foo/ - # optional module id - moduleId: my_module - 2: - blReverseProxyActive: -... -``` +**adminPassword:** -config:shop:get ---------------- +* Name: `--adminPassword` +* Is value required: yes +* Description: Admin password +* Default: `'oxid-123456'` -* Description: Sets a shop config value -* Usage: `config:shop:get [--shopId[="..."]] variableName` +cache:clear +----------- -### Arguments: +* Description: Clears the cache +* Usage: -**variableName:** + * `cache:clear [-f|--force]` -* Name: variableName -* Description: Variable name +Clears the cache ### Options: -**shopId:** +**force:** -* Name: `--shopId` +* Name: `--force` +* Shortcut: `-f` +* Accept value: no * Is value required: no -* Description: oxbaseshop -* Default: `'oxbaseshop'` +* Description: Try to delete the cache anyway. [danger or permission denied] +* Default: `false` -config:shop:set ---------------- +route:debug +----------- -* Description: Sets a shop config value -* Usage: `config:shop:set [--shopId[="..."]] variableName variableValue` +* Description: Returns the route. Which controller and parameters are called. +* Usage: -### Arguments: + * `route:debug [--shopId [SHOPID]] [-c|--copy] [--] ` -**variableName:** +Returns the route. Which controller and parameters are called. -* Name: variableName -* Description: Variable name +### Arguments: -**variableValue:** +**url:** -* Name: variableValue -* Description: Variable value +* Name: url +* Description: Website URL. Full or Path ### Options: @@ -242,60 +381,76 @@ config:shop:set * Name: `--shopId` * Is value required: no -* Description: oxbaseshop -* Default: `'oxbaseshop'` +* Description: -config:export ---------------- +**copy:** + +* Name: `--copy` +* Shortcut: `-c` +* Accept value: no +* Is value required: no +* Description: Copy file path from the class to the clipboard (only MacOS) +* Default: `false` + +config:get +---------- -* Description: Exports all config values as yaml files, interacts with the [Modules Config](https://github.com/OXIDprojects/oxid_modules_config/) module [__which currently isn't fully ported to OXID 6 yet!__](https://github.com/OXIDprojects/oxid_modules_config/tree/dev-6.0-wip) -* Usage: `config:export` +* Description: Gets a config value +* Usage: + + * `config:get [--shopId [SHOPID]] [--moduleId [MODULEID]] [--] ` + +Gets a config value ### Arguments: -### Options: +**variableName:** -**no-debug:** +* Name: variableName +* Description: Variable name -* Name: `--no-debug` -* Is value required: no -* Description: No debug ouput +### Options: -**env:** +**shopId:** -* Name: `--env` +* Name: `--shopId` * Is value required: no -* Description: set specific environment, corresponds to a specific folder for the yaml files -* Example: `--env=stage` +* Description: -**force-cleanup:** +**moduleId:** -* Name: `--force-cleanup` +* Name: `--moduleId` * Is value required: no -* Description: Force cleanup on error +* Description: -config:import ---------------- +config:export +------------- -* Description: Imports all config values from yaml files, interacts with the [Modules Config](https://github.com/OXIDprojects/oxid_modules_config/) module, [__which currently isn't fully ported to OXID 6 yet!__](https://github.com/OXIDprojects/oxid_modules_config/tree/dev-6.0-wip) -* Usage: `config:import` +* Description: Export shop config +* Usage: -### Arguments: + * `config:export [--no-debug] [--env [ENV]] [--force-cleanup [FORCE-CLEANUP]]` + +Info: +Exports all config values to yaml files, interacts with the +[Modules Config](https://github.com/OXIDprojects/oxid_modules_config/) module, +[__which currently isn't fully ported to OXID 6 yet!__](https://github.com/OXIDprojects/oxid_modules_config/tree/dev-6.0-wip) ### Options: **no-debug:** * Name: `--no-debug` +* Accept value: no * Is value required: no * Description: No debug ouput +* Default: `false` **env:** * Name: `--env` * Is value required: no * Description: set specific environment, corresponds to a specific folder for the yaml files -* Example: `--env=stage` **force-cleanup:** @@ -303,196 +458,178 @@ config:import * Is value required: no * Description: Force cleanup on error -db:dump -------- - -* Description: Dumps the the current shop database -* Usage: `db:dump [--file="..."]` - -Dumps the the current shop database. - -Requires php exec and MySQL CLI tools installed on your system. - -### Options: - -**file:** - -* Name: `--file` -* Is value required: yes -* Description: Dump sql in to this file - -db:import ---------- +config:shop:set +--------------- -* Description: Import a sql file -* Usage: `db:import file` +* Description: Sets a shop config value +* Usage: -Imports an SQL file on the current shop database. + * `config:shop:set [--shopId [SHOPID]] [--] ` -Requires php exec and MySQL CLI tools installed on your system. +Sets a shop config value ### Arguments: -**file:** +**variableName:** -* Name: file -* Description: The sql file which is to be imported +* Name: variableName +* Description: Variable name -### Options: +**variableValue:** -db:query --------- +* Name: variableValue +* Description: Variable value -* Description: Executes a query -* Usage: `db:query [--raw] query` +### Options: -Executes an SQL query on the current shop database. Wrap your SQL in quotes. +**shopId:** -If your query produces a result (e.g. a SELECT statement), the output will be returned via the table component. Add the raw option for raw output. +* Name: `--shopId` +* Is value required: no +* Description: oxbaseshop +* Default: `'oxbaseshop'` -Requires php exec and MySQL CLI tools installed on your system. +config:set +---------- -### Arguments: +* Description: Sets a config value +* Usage: -**query:** + * `config:set [--variableType VARIABLETYPE] [--shopId [SHOPID]] [--moduleId [MODULEID]] [--] ` -* Name: query -* Description: The query which is to be executed +Sets a config value -### Options: +### Arguments: -**raw:** +**variableName:** -* Name: `--raw` -* Accept value: no -* Is value required: no -* Description: Raw output -* Default: `false` +* Name: variableName +* Description: Variable name -install:shop __[DEPRECATED!]__ ------------- +**variableValue:** -* Description: Installs the shop -* Usage: `install:shop [--oxidVersion[="..."]] [--installationFolder[="..."]] [--dbHost="..."] [--dbUser="..."] [--dbPwd="..."] [--dbName="..."] [--dbPort[="..."]] [--installSampleData[="..."]] [--shopURL="..."] [--adminUser="..."] [--adminPassword="..."]` +* Name: variableValue +* Description: Variable value ### Options: -**oxidVersion:** - -* Name: `--oxidVersion` -* Is value required: no -* Description: Oxid version -* Default: `'v4.9.5'` +**variableType:** -**installationFolder:** +* Name: `--variableType` +* Is value required: yes +* Description: Variable type -* Name: `--installationFolder` -* Is value required: no -* Description: Installation folder -* Default: `'/vagrant/web/oxrun'` +**shopId:** -**dbHost:** +* Name: `--shopId` +* Is value required: no +* Description: -* Name: `--dbHost` -* Is value required: yes -* Description: Database host -* Default: `'localhost'` +**moduleId:** -**dbUser:** +* Name: `--moduleId` +* Is value required: no +* Description: -* Name: `--dbUser` -* Is value required: yes -* Description: Database user -* Default: `'oxid'` +config:import +------------- -**dbPwd:** +* Description: Import shop config +* Usage: -* Name: `--dbPwd` -* Is value required: yes -* Description: Database password -* Default: `''` + * `config:import [--no-debug] [--env [ENV]] [--force-cleanup [FORCE-CLEANUP]]` -**dbName:** +Info: +Imports all config values from yaml files, interacts with the +[Modules Config](https://github.com/OXIDprojects/oxid_modules_config/) module, +[__which currently isn't fully ported to OXID 6 yet!__](https://github.com/OXIDprojects/oxid_modules_config/tree/dev-6.0-wip) -* Name: `--dbName` -* Is value required: yes -* Description: Database name -* Default: `'oxid'` +### Options: -**dbPort:** +**no-debug:** -* Name: `--dbPort` +* Name: `--no-debug` +* Accept value: no * Is value required: no -* Description: Database port -* Default: `3306` +* Description: No debug ouput +* Default: `false` -**installSampleData:** +**env:** -* Name: `--installSampleData` +* Name: `--env` * Is value required: no -* Description: Install sample data -* Default: `true` +* Description: set specific environment, corresponds to a specific folder for the yaml files -**shopURL:** +**force-cleanup:** -* Name: `--shopURL` -* Is value required: yes -* Description: Installation base url +* Name: `--force-cleanup` +* Is value required: no +* Description: Force cleanup on error -**adminUser:** +config:multiset +--------------- -* Name: `--adminUser` -* Is value required: yes -* Description: Admin user email/login +* Description: Sets multiple config values from yaml file +* Usage: -**adminPassword:** + * `config:multiset ` -* Name: `--adminPassword` -* Is value required: yes -* Description: Admin password +YAML example: +```yaml +config: + 1: + blReverseProxyActive: + variableType: bool + variableValue: false + # simple string type + sMallShopURL: http://myshop.dev.local + sMallSSLShopURL: http://myshop.dev.local + myMultiVal: + variableType: aarr + variableValue: + - /foo/bar/ + - /bar/foo/ + # optional module id + moduleId: my_module + 2: + blReverseProxyActive: +... +``` -misc:generate:documentation ---------------------------- +If you want, you can also specify __a YAML string on the command line instead of a file__, e.g.: -* Description: Generate a raw command documentation of the available commands -* Usage: `misc:generate:documentation` +```bash +../vendor/bin/oxrun module:multiset $'config: + 1: + foobar: barfoo +' --shopId=1 +``` ### Arguments: -**command:** - -* Name: command -* Description: The command to execute - -### Options: - -misc:phpstorm:metadata ----------------------- +**configfile:** -* Description: Generate a PhpStorm metadata file for autocompletion -* Usage: `misc:phpstorm:metadata` +* Name: configfile +* Description: The file containing the config values, see malls.yml.dist. The file path is relative to the shop root. You can also pass a YAML string on the command line. ### Options: -**output-dir:** +config:shop:get +--------------- -* Name: `--output-dir`, `-o` -* Accept value: yes -* Is value required: yes -* Description: Writes the metadata for PhpStorm to the specified directory. +* Description: Sets a shop config value +* Usage: -module:activate ---------------- + * `config:shop:get [--shopId [SHOPID]] [--] ` -* Description: Activates a module -* Usage: `module:activate module` +Sets a shop config value ### Arguments: -**module:** +**variableName:** -* Name: module -* Description: Module name +* Name: variableName +* Description: Variable name ### Options: @@ -500,13 +637,18 @@ module:activate * Name: `--shopId` * Is value required: no -* Description: +* Description: oxbaseshop +* Default: `'oxbaseshop'` -module:deactivate ------------------ +module:generate +--------------- -* Description: Deactivates a module -* Usage: `module:deactivate module` +* Description: Generates a module skeleton __[NOT IMPLEMENTED YET]__ +* Usage: + + * `module:generate [--shopId [SHOPID]] [--] ` + +Generates a module skeleton __[NOT IMPLEMENTED YET]__ ### Arguments: @@ -524,28 +666,28 @@ module:deactivate * Description: module:multiactivate ---------------- +-------------------- * Description: Activates multiple modules, based on a YAML file -* Usage: `module:multiactivate ` - -### Arguments: +* Usage: -**modulefile:** + * `module:multiactivate [--shopId SHOPID] [-s|--skipDeactivation] [-c|--skipClear] [--] ` -* Name: modulefile -* Description: Module definition file name, e.g. "modules.yml", relative to the shop base dir. +usage: +oxrun module:multiactivate configs/modules.yml +- to activate all modules defined in the file "configs/modules.yml" based +on a white- or blacklist Example: ```yaml whitelist: - 1: +1: - ocb_cleartmp - moduleinternals #- ddoevisualcms #- ddoewysiwyg - 2: +2: - ocb_cleartmp ``` @@ -554,35 +696,72 @@ Supports either a __"whitelist"__ or a __"blacklist"__ entry with multiple shop If you want, you can also specify __a YAML string on the command line instead of a file__, e.g.: ```bash -../vendor/bin/oxrun module:multiactivate $'whitelist:\n 1:\n - oepaypal\n' --shopId=1 +../vendor/bin/oxrun module:multiactivate $'whitelist: + 1: + - oepaypal +' --shopId=1 ``` +### Arguments: + +**module:** + +* Name: module +* Description: YAML module list filename or YAML string + ### Options: **shopId:** * Name: `--shopId` -* Is value required: no -* Description: The subshop id +* Is value required: yes +* Description: The shop id. **skipDeactivation:** -* Name: `--skipDeactivation` or `-s` +* Name: `--skipDeactivation` +* Shortcut: `-s` +* Accept value: no * Is value required: no -* Description: skip deactivation, only activate the modules +* Description: Skip deactivation of modules, only activate. +* Default: `false` **skipClear:** -* Name: `--skipClear` or `-c` +* Name: `--skipClear` +* Shortcut: `-c` +* Accept value: no * Is value required: no -* Description: skip cache clearing between deactivation and activation +* Description: Skip cache clearing. +* Default: `false` -module:fix ----------- +module:list +----------- + +* Description: Lists all modules +* Usage: + + * `module:list [--shopId [SHOPID]]` + +Lists all modules + +### Options: + +**shopId:** + +* Name: `--shopId` +* Is value required: no +* Description: -* Description: Fixes a module -* Usage: `module:fix module` -* __NOT IMPLEMENTED YET!__ +module:activate +--------------- + +* Description: Activates a module +* Usage: + + * `module:activate [--shopId [SHOPID]] [--] ` + +Activates a module ### Arguments: @@ -599,12 +778,15 @@ module:fix * Is value required: no * Description: -module:generate ---------------- +module:deactivate +----------------- + +* Description: Deactivates a module +* Usage: -* Description: Generates a module skeleton -* Usage: `module:generate module` -* __NOT IMPLEMENTED YET!__ + * `module:deactivate [--shopId [SHOPID]] [--] ` + +Deactivates a module ### Arguments: @@ -621,11 +803,47 @@ module:generate * Is value required: no * Description: -module:list ------------ +module:reload +------------- -* Description: Lists all modules -* Usage: `module:list` +* Description: Deactivate and activate a module +* Usage: + + * `module:reload [--shopId [SHOPID]] [--] ` + +Deactivate and activate a module + +### Arguments: + +**module:** + +* Name: module +* Description: Module name + +### Options: + +**shopId:** + +* Name: `--shopId` +* Is value required: no +* Description: + +module:fix +---------- + +* Description: Fixes a module __[NOT IMPLEMENTED YET]__ +* Usage: + + * `module:fix [--shopId [SHOPID]] [--] ` + +Fixes a module __[NOT IMPLEMENTED YET]__ + +### Arguments: + +**module:** + +* Name: module +* Description: Module name ### Options: @@ -639,7 +857,11 @@ user:password ------------- * Description: Sets a new password -* Usage: `user:password username password` +* Usage: + + * `user:password ` + +Sets a new password ### Arguments: @@ -655,32 +877,61 @@ user:password ### Options: -views:update ------------- +cms:update +---------- -* Description: Updates the views -* Usage: `views:update` +* Description: Updates a cms page +* Usage: -route:debug ------------------ + * `cms:update [--title [TITLE]] [--content [CONTENT]] [--language LANGUAGE] [--active ACTIVE] [--] ` -* Description: lookup a SEO Url in the database and check which controller is associated etc. -* Usage: `route:debug http://myshop.de/my/url` +Updates a cms page ### Arguments: -**url:** +**ident:** -* Name: url -* Description: SEO URL +* Name: ident +* Description: Content ident ### Options: -**shopId:** +**title:** -* Name: `--shopId` +* Name: `--title` * Is value required: no -* Description: +* Description: Content title + +**content:** + +* Name: `--content` +* Is value required: no +* Description: Content body + +**language:** + +* Name: `--language` +* Is value required: yes +* Description: Content language + +**active:** + +* Name: `--active` +* Is value required: yes +* Description: Content active + +views:update +------------ + +* Description: Updates the views +* Usage: + + * `views:update` + +Updates the views + +### Options: + # Run the unit tests diff --git a/src/Oxrun/Command/Config/ExportCommand.php b/src/Oxrun/Command/Config/ExportCommand.php index 56eff72..7f6a309 100644 --- a/src/Oxrun/Command/Config/ExportCommand.php +++ b/src/Oxrun/Command/Config/ExportCommand.php @@ -36,7 +36,7 @@ protected function configure() 'env', null, InputOption::VALUE_OPTIONAL, - 'Environment', + 'set specific environment, corresponds to a specific folder for the yaml files', null ) ->addOption( @@ -46,6 +46,13 @@ protected function configure() 'Force cleanup on error', null ); +$help = <<Info: +Exports all config values to yaml files, interacts with the +[Modules Config](https://github.com/OXIDprojects/oxid_modules_config/) module, +[__which currently isn't fully ported to OXID 6 yet!__](https://github.com/OXIDprojects/oxid_modules_config/tree/dev-6.0-wip) +HELP; + $this->setHelp($help); } /** diff --git a/src/Oxrun/Command/Config/ImportCommand.php b/src/Oxrun/Command/Config/ImportCommand.php index f291b39..fd52d6f 100644 --- a/src/Oxrun/Command/Config/ImportCommand.php +++ b/src/Oxrun/Command/Config/ImportCommand.php @@ -36,7 +36,7 @@ protected function configure() 'env', null, InputOption::VALUE_OPTIONAL, - 'Environment', + 'set specific environment, corresponds to a specific folder for the yaml files', null ) ->addOption( @@ -46,6 +46,13 @@ protected function configure() 'Force cleanup on error', null ); +$help = <<Info: +Imports all config values from yaml files, interacts with the +[Modules Config](https://github.com/OXIDprojects/oxid_modules_config/) module, +[__which currently isn't fully ported to OXID 6 yet!__](https://github.com/OXIDprojects/oxid_modules_config/tree/dev-6.0-wip) +HELP; + $this->setHelp($help); } /** diff --git a/src/Oxrun/Command/Config/MultiSetCommand.php b/src/Oxrun/Command/Config/MultiSetCommand.php index 6b6d0c1..96d9955 100644 --- a/src/Oxrun/Command/Config/MultiSetCommand.php +++ b/src/Oxrun/Command/Config/MultiSetCommand.php @@ -42,7 +42,39 @@ protected function configure() $this ->setName('config:multiset') ->setDescription('Sets multiple config values from yaml file') - ->addArgument('configfile', InputArgument::REQUIRED, 'The yaml file name, relative to shop base.'); + ->addArgument('configfile', InputArgument::REQUIRED, 'The file containing the config values, see malls.yml.dist. The file path is relative to the shop root. You can also pass a YAML string on the command line.'); + +$help = <<YAML example: +```yaml +config: + 1: + blReverseProxyActive: + variableType: bool + variableValue: false + # simple string type + sMallShopURL: http://myshop.dev.local + sMallSSLShopURL: http://myshop.dev.local + myMultiVal: + variableType: aarr + variableValue: + - /foo/bar/ + - /bar/foo/ + # optional module id + moduleId: my_module + 2: + blReverseProxyActive: +... +``` + +If you want, you can also specify __a YAML string on the command line instead of a file__, e.g.: + +```bash +../vendor/bin/oxrun module:multiset $'config:\n 1:\n foobar: barfoo\n' --shopId=1 +``` +HELP; + $this->setHelp($help); + } /** diff --git a/src/Oxrun/Command/Install/InstallCommand.php b/src/Oxrun/Command/Install/InstallCommand.php index 1b5b697..5d22a5f 100755 --- a/src/Oxrun/Command/Install/InstallCommand.php +++ b/src/Oxrun/Command/Install/InstallCommand.php @@ -36,7 +36,7 @@ class InstallCommand extends Command protected function configure() { $this - ->setName('install:shop') + ->setName('install:shop __[DEPRECATED]__') ->addOption('oxidVersion', null, InputOption::VALUE_OPTIONAL, 'Oxid version') ->addOption('installationFolder', null, InputOption::VALUE_OPTIONAL, 'Installation folder', getcwd()) ->addOption('dbHost', null, InputOption::VALUE_REQUIRED, 'Database host', 'localhost') @@ -48,7 +48,7 @@ protected function configure() ->addOption('shopURL', null, InputOption::VALUE_REQUIRED, 'Installation base url') ->addOption('adminUser', null, InputOption::VALUE_REQUIRED, 'Admin user email/login', 'admin@example.com') ->addOption('adminPassword', null, InputOption::VALUE_REQUIRED, 'Admin password', 'oxid-123456') - ->setDescription('Installs the shop'); + ->setDescription('Installs the shop, for OXID 6 composer is used instead!'); } /** diff --git a/src/Oxrun/Command/Module/FixCommand.php b/src/Oxrun/Command/Module/FixCommand.php index a8a78ac..fbbf288 100644 --- a/src/Oxrun/Command/Module/FixCommand.php +++ b/src/Oxrun/Command/Module/FixCommand.php @@ -22,7 +22,7 @@ protected function configure() { $this ->setName('module:fix') - ->setDescription('Fixes a module') + ->setDescription('Fixes a module __[NOT IMPLEMENTED YET]__') ->addOption('shopId', null, InputOption::VALUE_OPTIONAL, null) ->addArgument('module', InputArgument::REQUIRED, 'Module name'); } diff --git a/src/Oxrun/Command/Module/GenerateCommand.php b/src/Oxrun/Command/Module/GenerateCommand.php index 7397d8a..2761578 100644 --- a/src/Oxrun/Command/Module/GenerateCommand.php +++ b/src/Oxrun/Command/Module/GenerateCommand.php @@ -22,7 +22,7 @@ protected function configure() { $this ->setName('module:generate') - ->setDescription('Generates a module skeleton') + ->setDescription('Generates a module skeleton __[NOT IMPLEMENTED YET]__') ->addOption('shopId', null, InputOption::VALUE_OPTIONAL, null) ->addArgument('module', InputArgument::REQUIRED, 'Module name'); } diff --git a/src/Oxrun/Command/Module/MultiActivateCommand.php b/src/Oxrun/Command/Module/MultiActivateCommand.php index 83be07b..57e2b14 100644 --- a/src/Oxrun/Command/Module/MultiActivateCommand.php +++ b/src/Oxrun/Command/Module/MultiActivateCommand.php @@ -44,11 +44,40 @@ protected function configure() { $this ->setName('module:multiactivate') - ->setDescription('Activate multiple modules') + ->setDescription('Activates multiple modules, based on a YAML file') ->addOption('shopId', null, InputOption::VALUE_REQUIRED, "The shop id.") ->addOption('skipDeactivation', 's', InputOption::VALUE_NONE, "Skip deactivation of modules, only activate.") ->addOption('skipClear', 'c', InputOption::VALUE_NONE, "Skip cache clearing.") - ->addArgument('module', InputArgument::REQUIRED, 'YAML module list filename'); + ->addArgument('module', InputArgument::REQUIRED, 'YAML module list filename or YAML string'); + +$help = <<usage: +oxrun module:multiactivate configs/modules.yml +- to activate all modules defined in the file "configs/modules.yml" based +on a white- or blacklist + +Example: + +```yaml +whitelist: +1: + - ocb_cleartmp + - moduleinternals + #- ddoevisualcms + #- ddoewysiwyg +2: + - ocb_cleartmp +``` + +Supports either a __"whitelist"__ or a __"blacklist"__ entry with multiple shop ids and the desired module ids to activate (whitelist) or to exclude from activation (blacklist). + +If you want, you can also specify __a YAML string on the command line instead of a file__, e.g.: + +```bash +../vendor/bin/oxrun module:multiactivate $'whitelist:\n 1:\n - oepaypal\n' --shopId=1 +``` +HELP; + $this->setHelp($help); } /** From d05dd21534b4906e79386f1b0125f0fc26cd583d Mon Sep 17 00:00:00 2001 From: Stefan Moises Date: Tue, 27 Mar 2018 12:29:13 +0200 Subject: [PATCH 30/30] Changed composer project name for PR to origin --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 478466a..d6293cb 100644 --- a/composer.json +++ b/composer.json @@ -1,5 +1,5 @@ { - "name": "smxsm/oxrun", + "name": "marcharding/oxrun", "description": "Oxrun provides a cli toolset for the OXID eShop Community Edition", "license": "MIT", "version": "v0.2.2",