Inital commit

This commit is contained in:
2020-11-19 15:36:28 +01:00
parent 71f32f83d3
commit 66ce4ee218
18077 changed files with 2166122 additions and 35184 deletions

View File

@@ -16,8 +16,12 @@ 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\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage;
use Thelia\Core\Event\Administrator\AdministratorUpdatePasswordEvent;
use Thelia\Core\Event\TheliaEvents;
use Thelia\Core\HttpFoundation\Request;
use Thelia\Core\HttpFoundation\Session\Session;
use Thelia\Model\AdminQuery;
use Thelia\Tools\Password;
@@ -28,10 +32,21 @@ use Thelia\Tools\Password;
*
* Class AdminUpdatePasswordCommand
* @package Thelia\Command
* @author Manuel Raynaud <mraynaud@openstudio.fr>
* @author Manuel Raynaud <manu@raynaud.io>
*/
class AdminUpdatePasswordCommand extends ContainerAwareCommand
{
protected function init()
{
$container = $this->getContainer();
$request = new Request();
$request->setSession(new Session(new MockArraySessionStorage()));
/** @var RequestStack $requestStack */
$requestStack = $container->get('request_stack');
$requestStack->push($request);
}
/**
* Configures the current command.
@@ -58,6 +73,8 @@ class AdminUpdatePasswordCommand extends ContainerAwareCommand
protected function execute(InputInterface $input, OutputInterface $output)
{
$this->init();
$login = $input->getArgument('login');
if (null === $admin = AdminQuery::create()->filterByLogin($login)->findOne()) {
@@ -69,10 +86,7 @@ class AdminUpdatePasswordCommand extends ContainerAwareCommand
$event = new AdministratorUpdatePasswordEvent($admin);
$event->setPassword($password);
$this->
getContainer()
->get('event_dispatcher')
->dispatch(TheliaEvents::ADMINISTRATOR_UPDATEPASSWORD, $event);
$this->getDispatcher()->dispatch(TheliaEvents::ADMINISTRATOR_UPDATEPASSWORD, $event);
$output->writeln(array(
'',
@@ -80,7 +94,5 @@ class AdminUpdatePasswordCommand extends ContainerAwareCommand
sprintf('<info>new password is : %s</info>', $password),
''
));
}
}

View File

@@ -12,25 +12,23 @@
namespace Thelia\Command;
use Thelia\Model\Module;
/**
* base class for module commands
*
* Class BaseModuleGenerate
* @package Thelia\Command
* @author Manuel Raynaud <mraynaud@openstudio.fr>
* @author Manuel Raynaud <manu@raynaud.io>
*/
abstract class BaseModuleGenerate extends ContainerAwareCommand
{
protected $module;
protected $moduleDirectory;
protected $module;
protected $moduleDirectory;
protected $reservedKeyWords = array(
protected $reservedKeyWords = array(
'thelia'
);
protected $neededDirectories = array(
protected $neededDirectories = array(
'Config',
'Model',
'Loop',
@@ -38,23 +36,37 @@ abstract class BaseModuleGenerate extends ContainerAwareCommand
'Controller',
'EventListeners',
'I18n',
Module::ADMIN_INCLUDES_DIRECTORY_NAME,
'templates',
'Hook',
);
protected function verifyExistingModule()
{
if (file_exists($this->moduleDirectory)) {
throw new \RuntimeException(sprintf("%s module already exists", $this->module));
}
}
protected function verifyExistingModule()
{
if (file_exists($this->moduleDirectory)) {
throw new \RuntimeException(
sprintf(
"%s module already exists. Use --force option to force generation.",
$this->module
)
);
}
}
protected function formatModuleName($name)
{
if (in_array(strtolower($name), $this->reservedKeyWords)) {
throw new \RuntimeException(sprintf("%s module name is a reserved keyword", $name));
}
protected function formatModuleName($name)
{
if (in_array(strtolower($name), $this->reservedKeyWords)) {
throw new \RuntimeException(sprintf("%s module name is a reserved keyword", $name));
}
return ucfirst($name);
}
return ucfirst($name);
}
protected function validModuleName($name)
{
if (!preg_match('#^[A-Z]([A-Za-z\d])+$#', $name)) {
throw new \RuntimeException(
sprintf("%s module name is not a valid name, it must be in CamelCase. (ex: MyModuleName)", $name)
);
}
}
}

View File

@@ -15,18 +15,17 @@ namespace Thelia\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Filesystem\Exception\IOException;
use Thelia\Core\Event\Cache\CacheEvent;
use Thelia\Core\Event\TheliaEvents;
use Thelia\Model\ConfigQuery;
/**
* clear the cache
*
* Class CacheClear
* @package Thelia\Command
* @author Manuel Raynaud <mraynaud@openstudio.fr>
* @author Manuel Raynaud <manu@raynaud.io>
*
*/
class CacheClear extends ContainerAwareCommand
@@ -46,26 +45,46 @@ class CacheClear extends ContainerAwareCommand
'with-images',
null,
InputOption::VALUE_NONE,
'clear images generated in web/cache directory'
'clear images generated in `image_cache_dir_from_web_root` or web/cache/images directory'
)
->addOption(
'with-documents',
null,
InputOption::VALUE_NONE,
'clear documents generated in `document_cache_dir_from_web_root` or web/cache/documents directory'
)
;
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$cacheDir = $this->getContainer()->getParameter("kernel.cache_dir");
$this->clearCache($cacheDir, $output);
if (!$input->getOption("without-assets")) {
$this->clearCache(THELIA_WEB_DIR . "assets", $output);
if (!$input->getOption('without-assets')) {
$this->clearCache(THELIA_WEB_DIR . ConfigQuery::read('asset_dir_from_web_root', 'assets'), $output);
}
if ($input->getOption('with-images')) {
$this->clearCache(THELIA_CACHE_DIR, $output);
$this->clearCache(
THELIA_WEB_DIR . ConfigQuery::read(
'image_cache_dir_from_web_root',
'cache' . DS . 'images'
),
$output
);
}
if ($input->getOption('with-documents')) {
$this->clearCache(
THELIA_WEB_DIR . ConfigQuery::read(
'document_cache_dir_from_web_root',
'cache' . DS . 'documents'
),
$output
);
}
}
protected function clearCache($dir, OutputInterface $output)
@@ -74,10 +93,7 @@ class CacheClear extends ContainerAwareCommand
try {
$cacheEvent = new CacheEvent($dir);
$this->
getContainer()
->get('event_dispatcher')
->dispatch(TheliaEvents::CACHE_CLEAR, $cacheEvent);
$this->getDispatcher()->dispatch(TheliaEvents::CACHE_CLEAR, $cacheEvent);
} catch (\UnexpectedValueException $e) {
// throws same exception code for does not exist and permission denied ...
if (!file_exists($dir)) {
@@ -92,6 +108,5 @@ class CacheClear extends ContainerAwareCommand
}
$output->writeln(sprintf("<info>%s cache directory cleared successfully</info>", $dir));
}
}

View File

@@ -32,8 +32,6 @@ class ClearImageCache extends ContainerAwareCommand
protected function execute(InputInterface $input, OutputInterface $output)
{
$dispatcher = $this->getContainer()->get('event_dispatcher');
$request = new Request();
try {
@@ -41,13 +39,15 @@ class ClearImageCache extends ContainerAwareCommand
$subdir = $input->getArgument('subdir');
if (! is_null($subdir)) $event->setCacheSubdirectory($subdir);
if (! is_null($subdir)) {
$event->setCacheSubdirectory($subdir);
}
$dispatcher->dispatch(TheliaEvents::IMAGE_CLEAR_CACHE, $event);
$this->getDispatcher()->dispatch(TheliaEvents::IMAGE_CLEAR_CACHE, $event);
$output->writeln(sprintf('%s image cache successfully cleared.', is_null($subdir) ? 'Entire' : ucfirst($subdir)));
} catch (\Exception $ex) {
$output->writeln(sprintf("Failed to clear image cache: %s", $ex->getMessage()));
$output->writeln(sprintf("Failed to clear image cache: %s", $ex->getMessage()));
}
}
}

View File

@@ -0,0 +1,225 @@
<?php
/*******************************************************************************/
/* This file is part of the Thelia package. */
/* */
/* Copyright (c) OpenStudio */
/* email : dev@thelia.net */
/* web : http://www.thelia.net */
/* */
/* For the full copyright and license information, please view the LICENSE.txt */
/* file that was distributed with this source code. */
/*************************************************************************************/
namespace Thelia\Command;
use Symfony\Component\Console\Helper\Table;
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 Thelia\Model\Config;
use Thelia\Model\ConfigQuery;
/**
* command line for managing configuration variables
*
* php Thelia thelia:config COMMAND [name] [value] [--secured] [--visible]
*
* Where COMMAND is list, get, set or delete.
*
* For command get and delete, you should also set the name attribute.
*
* For command set, you should set the name and value attributes and optionally add
* --secured and/or --visible arguments.
*
* Class ConfigCommand
* @package Thelia\Command
* @author Julien Chanséaume <jchanseaume@openstudio.fr>
*/
class ConfigCommand extends ContainerAwareCommand
{
protected function configure()
{
$this
->setName("thelia:config")
->setDescription("Manage configuration variables")
->addArgument(
'COMMAND',
InputArgument::REQUIRED,
'Command : list, get, set, delete'
)
->addArgument(
'name',
InputArgument::OPTIONAL,
'The variable name'
)
->addArgument(
'value',
InputArgument::OPTIONAL,
'The variable value'
)
->addOption(
'secured',
null,
InputOption::VALUE_NONE,
'When setting a new variable tell variable is secured.'
)
->addOption(
'visible',
null,
InputOption::VALUE_NONE,
'When setting a new variable tell variable is visible.'
)
;
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$command = $input->getArgument("COMMAND");
switch ($command) {
case "list":
$this->listConfig($input, $output);
break;
case "get":
$this->getConfig($input, $output);
break;
case "set":
$this->setConfig($input, $output);
break;
case "delete":
$this->deleteConfig($input, $output);
break;
default:
$output->writeln(
"<error>Unknown argument 'COMMAND' : list, get, set, delete</error>"
);
}
}
private function listConfig(InputInterface $input, OutputInterface $output)
{
$output->writeln([
"",
"<error>Variables list</error>",
""
]);
$vars = ConfigQuery::create()
->orderByName()
->find()
;
$rows = [];
/** @var Config $var */
foreach ($vars as $var) {
$rows[] = [
$var->getName(),
$var->getValue(),
$var->getSecured() !== 0 ? "yes" : "no",
$var->getHidden() !== 0 ? "yes" : "no"
];
}
$table = new Table($output);
$table
->setHeaders(['Name', 'Value', 'secured', 'hidden'])
->setRows($rows)
;
$table->render();
}
private function getConfig(InputInterface $input, OutputInterface $output)
{
$varName = $input->getArgument("name");
if (null === $varName) {
$output->writeln(
"<error>Need argument 'name' for get command</error>"
);
return;
}
$var = ConfigQuery::create()->findOneByName($varName);
$out = [];
if (null === $var) {
$out[] = sprintf(
"<error>Unknown variable '%s'</error>",
$varName
);
} else {
$out = [
sprintf('%12s: <%3$s>%s</%3$s>', "Name", $var->getName(), "info"),
sprintf('%12s: <%3$s>%s</%3$s>', "Value", $var->getValue(), "info"),
sprintf('%12s: <%3$s>%s</%3$s>', "Secured", $var->getSecured() ? "yes" : "no", "info"),
sprintf('%12s: <%3$s>%s</%3$s>', "Hidden", $var->getHidden() ? "yes" : "no", "info"),
sprintf('%12s: <%3$s>%s</%3$s>', "Title", $var->getTitle(), "info"),
sprintf('%12s: <%3$s>%s</%3$s>', "Description", $var->getDescription(), "info"),
];
}
$output->writeln($out);
}
private function setConfig(InputInterface $input, OutputInterface $output)
{
$varName = $input->getArgument("name");
$varValue = $input->getArgument("value");
if (null === $varName || null === $varValue) {
$output->writeln(
"<error>Need argument 'name' and 'value' for set command</error>"
);
return;
}
ConfigQuery::write(
$varName,
$varValue,
$input->getOption("secured"),
!$input->getOption("visible")
);
$output->writeln("<info>Variable has been set</info>");
}
private function deleteConfig(InputInterface $input, OutputInterface $output)
{
$varName = $input->getArgument("name");
if (null === $varName) {
$output->writeln(
"<error>Need argument 'name' for get command</error>"
);
return;
}
$var = ConfigQuery::create()->findOneByName($varName);
if (null === $var) {
$output->writeln(
sprintf(
"<error>Unknown variable '%s'</error>",
$varName
)
);
} else {
$var->delete();
$output->writeln(
sprintf(
"<info>Variable '%s' has been deleted</info>",
$varName
)
);
}
}
}

View File

@@ -15,11 +15,23 @@ namespace Thelia\Command;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage;
use Symfony\Component\Routing\RequestContext;
use Thelia\Core\Application;
use Thelia\Core\HttpFoundation\Request;
use Thelia\Core\HttpFoundation\Session\Session;
use Thelia\Core\Translation\Translator;
use Thelia\Model\ConfigQuery;
use Thelia\Model\Lang;
use Thelia\Model\LangQuery;
use Thelia\Tools\URL;
/**
* Command.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Gilles Bourgeat <gbourgeat@openstudio.fr>
*/
class ContainerAwareCommand extends Command implements ContainerAwareInterface
{
@@ -34,7 +46,9 @@ class ContainerAwareCommand extends Command implements ContainerAwareInterface
protected function getContainer()
{
if (null === $this->container) {
$this->container = $this->getApplication()->getKernel()->getContainer();
/** @var Application $application */
$application = $this->getApplication();
$this->container = $application->getKernel()->getContainer();
}
return $this->container;
@@ -42,9 +56,78 @@ class ContainerAwareCommand extends Command implements ContainerAwareInterface
/**
* @see ContainerAwareInterface::setContainer()
* @param ContainerInterface $container
*/
public function setContainer(ContainerInterface $container = null)
{
$this->container = $container;
}
/**
* @return \Symfony\Component\EventDispatcher\EventDispatcher
*/
public function getDispatcher()
{
$container = $this->getContainer();
// Initialize Thelia translator, if not already done.
try {
Translator::getInstance();
} catch (\Exception $ex) {
$this->container->get('thelia.translator');
}
return $container->get('event_dispatcher');
}
/**
* For init an Request, if your command has need an Request
* @param Lang|null $lang
* @since 2.3
*/
protected function initRequest(Lang $lang = null)
{
$container = $this->getContainer();
$request = Request::create($this->getBaseUrl($lang));
$request->setSession(new Session(new MockArraySessionStorage()));
$container->set("request_stack", new RequestStack());
$container->get('request_stack')->push($request);
$requestContext = new RequestContext();
$requestContext->fromRequest($request);
$url = $container->get('thelia.url.manager');
$url->setRequestContext($requestContext);
$this->getContainer()->get('router.admin')->setContext($requestContext);
}
/**
* @param Lang|null $lang
* @return string
* @since 2.3
*/
protected function getBaseUrl(Lang $lang = null)
{
$baseUrl = '';
if ((int) ConfigQuery::read('one_domain_foreach_lang') === 1) {
if ($lang === null) {
$lang = LangQuery::create()->findOneByByDefault(true);
}
$baseUrl = $lang->getUrl();
}
$baseUrl = trim($baseUrl);
if (empty($baseUrl)) {
$baseUrl = ConfigQuery::read('url_site');
}
if (empty($baseUrl)) {
$baseUrl = 'http://localhost';
}
return $baseUrl;
}
}

View File

@@ -12,11 +12,13 @@
namespace Thelia\Command;
use Symfony\Component\Console\Helper\QuestionHelper;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\Question;
use Thelia\Model\Admin;
use Thelia\Model\AdminQuery;
class CreateAdminUser extends ContainerAwareCommand
{
@@ -50,6 +52,13 @@ class CreateAdminUser extends ContainerAwareCommand
'User last name',
null
)
->addOption(
"email",
null,
InputOption::VALUE_OPTIONAL,
'Admin email address',
null
)
->addOption(
"locale",
null,
@@ -65,13 +74,13 @@ class CreateAdminUser extends ContainerAwareCommand
null
)
;
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$output->writeln('Please enter the admin user information:');
/** @var Admin $admin */
$admin = $this->getAdminInfo($input, $output);
$admin->save();
@@ -83,22 +92,30 @@ class CreateAdminUser extends ContainerAwareCommand
));
}
protected function enterData($dialog, $output, $label, $error_message, $hidden = false)
{
$command = $hidden ? 'askHiddenResponse' : 'askAndValidate';
protected function enterData(
QuestionHelper $helper,
InputInterface $input,
OutputInterface $output,
$label,
$errorMessage,
$hidden = false
) {
$question = new Question($this->decorateInfo($label));
return $dialog->$command(
$output,
$this->decorateInfo($label),
function ($answer) {
$answer = trim($answer);
if (empty($answer)) {
throw new \RuntimeException("This information is mandatory.");
}
if ($hidden) {
$question->setHidden(true);
$question->setHiddenFallback(false);
}
return $answer;
$question->setValidator(function ($value) use (&$errorMessage) {
if (trim($value) == '') {
throw new \Exception($errorMessage);
}
);
return $value;
});
return $helper->ask($input, $output, $question);
}
/**
@@ -110,21 +127,23 @@ class CreateAdminUser extends ContainerAwareCommand
*/
protected function getAdminInfo(InputInterface $input, OutputInterface $output)
{
$dialog = $this->getHelperSet()->get('dialog');
/** @var QuestionHelper $helper */
$helper = $this->getHelper('question');
$admin = new Admin();
$admin->setLogin($input->getOption("login_name") ?: $this->enterData($dialog, $output, "Admin login name : ", "Please enter a login name."));
$admin->setFirstname($input->getOption("first_name") ?: $this->enterData($dialog, $output, "User first name : ", "Please enter user first name."));
$admin->setLastname($input->getOption("last_name") ?: $this->enterData($dialog, $output, "User last name : ", "Please enter user last name."));
$admin->setLogin($input->getOption("login_name") ?: $this->enterLogin($helper, $input, $output));
$admin->setFirstname($input->getOption("first_name") ?: $this->enterData($helper, $input, $output, "User first name : ", "Please enter user first name."));
$admin->setLastname($input->getOption("last_name") ?: $this->enterData($helper, $input, $output, "User last name : ", "Please enter user last name."));
$admin->setLocale($input->getOption("locale") ?: 'en_US');
$admin->setEmail($input->getOption("email") ?: $this->enterEmail($helper, $input, $output));
do {
$password = $input->getOption("password") ?: $this->enterData($dialog, $output, "Password : ", "Please enter a password.", true);
$password_again = $input->getOption("password") ?: $this->enterData($dialog, $output, "Password (again): ", "Please enter the password again.", true);
$password = $input->getOption("password") ?: $this->enterData($helper, $input, $output, "Password : ", "Please enter a password.", true);
$password_again = $input->getOption("password") ?: $this->enterData($helper, $input, $output, "Password (again): ", "Please enter the password again.", true);
if (! empty($password) && $password == $password_again) {
$admin->setPassword($password);
break;
@@ -143,4 +162,44 @@ class CreateAdminUser extends ContainerAwareCommand
return sprintf("<info>%s</info>", $text);
}
protected function enterLogin(QuestionHelper $helper, InputInterface $input, OutputInterface $output)
{
$question = new Question($this->decorateInfo("Admin login name : "));
$question->setValidator(function ($answer) {
$answer = trim($answer);
if (empty($answer)) {
throw new \RuntimeException("Please enter a login name.");
}
if (AdminQuery::create()->findOneByLogin($answer)) {
throw new \RuntimeException("An administrator with this login already exists.");
}
return $answer;
});
return $helper->ask($input, $output, $question);
}
protected function enterEmail(QuestionHelper $helper, InputInterface $input, OutputInterface $output)
{
$question = new Question($this->decorateInfo("Admin email or empty value : "));
$question->setValidator(function ($answer) {
$answer = trim($answer);
if (!empty($answer) && !filter_var($answer, FILTER_VALIDATE_EMAIL)) {
throw new \RuntimeException("Please enter an email or an empty value.");
}
if (AdminQuery::create()->findOneByEmail($answer)) {
throw new \RuntimeException("An administrator with this email already exists.");
}
return !empty($answer) ? $answer : uniqid('CHANGE_ME_');
});
return $helper->ask($input, $output, $question);
}
}

View File

@@ -0,0 +1,241 @@
<?php
/*************************************************************************************/
/* This file is part of the Thelia package. */
/* */
/* Copyright (c) OpenStudio */
/* email : dev@thelia.net */
/* web : http://www.thelia.net */
/* */
/* For the full copyright and license information, please view the LICENSE.txt */
/* file that was distributed with this source code. */
/*************************************************************************************/
namespace Thelia\Command;
use Symfony\Component\Console\Helper\Table;
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 Thelia\Core\Archiver\ArchiverInterface;
use Thelia\Core\Archiver\ArchiverManager;
use Thelia\Core\DependencyInjection\Compiler\RegisterArchiverPass;
use Thelia\Core\DependencyInjection\Compiler\RegisterSerializerPass;
use Thelia\Core\Serializer\SerializerInterface;
use Thelia\Core\Serializer\SerializerManager;
use Thelia\Model\ExportQuery;
use Thelia\Model\LangQuery;
/**
* Class ExportCommand
* @author Jérôme Billiras <jbilliras@openstudio.fr>
*/
class ExportCommand extends ContainerAwareCommand
{
protected function configure()
{
$this
->setName('export')
->setDescription('Export data')
->setHelp('The <info>export</info> command run selected export')
->addArgument(
'ref',
InputArgument::OPTIONAL,
'Export reference.'
)
->addArgument(
'serializer',
InputArgument::OPTIONAL,
'Serializer identifier.'
)
->addArgument(
'archiver',
InputArgument::OPTIONAL,
'Archiver identifier.'
)
->addOption(
'locale',
null,
InputOption::VALUE_REQUIRED,
'Locale for export',
'en_US'
)
->addOption(
'list-export',
null,
InputOption::VALUE_NONE,
'List available exports and exit.'
)
->addOption(
'list-serializer',
null,
InputOption::VALUE_NONE,
'List available serializers and exit.'
)
->addOption(
'list-archiver',
null,
InputOption::VALUE_NONE,
'List available archivers and exit.'
)
;
}
protected function execute(InputInterface $input, OutputInterface $output)
{
if ($input->getOption('list-export')) {
$this->listExport($output);
return;
}
if ($input->getOption('list-serializer')) {
$this->listSerializer($output);
return;
}
if ($input->getOption('list-archiver')) {
$this->listArchiver($output);
return;
}
$exportRef = $input->getArgument('ref');
$serializer = $input->getArgument('serializer');
if ($exportRef === null || $serializer === null) {
throw new \RuntimeException(
'Not enough arguments.' . PHP_EOL . 'If no options are provided, ref and serializer arguments are required.'
);
}
/** @var \Thelia\Handler\ExportHandler $exportHandler */
$exportHandler = $this->getContainer()->get('thelia.export.handler');
$export = $exportHandler->getExportByRef($exportRef);
if ($export === null) {
throw new \RuntimeException(
$exportRef . ' export doesn\'t exist.'
);
}
$serializerManager = $this->getContainer()->get(RegisterSerializerPass::MANAGER_SERVICE_ID);
$serializer = $serializerManager->get($serializer);
$archiver = null;
if ($input->getArgument('archiver')) {
/** @var \Thelia\Core\Archiver\ArchiverManager $archiverManager */
$archiverManager = $this->getContainer()->get(RegisterArchiverPass::MANAGER_SERVICE_ID);
$archiver = $archiverManager->get($input->getArgument('archiver'));
}
$exportEvent = $exportHandler->export(
$export,
$serializer,
$archiver,
(new LangQuery)->findOneByLocale($input->getOption('locale'))
);
$formattedLine = $this->getHelper('formatter')->formatBlock(
'Export finish',
'fg=black;bg=green',
true
);
$output->writeln($formattedLine);
$output->writeln('<info>Export available at path:</info>');
$output->writeln('<comment>' . $exportEvent->getFilePath() . '</comment>');
}
/**
* Output available exports
*
* @param \Symfony\Component\Console\Output\OutputInterface $output An output interface
*/
protected function listExport(OutputInterface $output)
{
$table = new Table($output);
foreach ((new ExportQuery)->find() as $export) {
$table->addRow([
$export->getRef(),
$export->getTitle(),
$export->getDescription()
]);
}
$table
->setHeaders([
'Reference',
'Title',
'Description'
])
->render()
;
}
/**
* Output available serializers
*
* @param \Symfony\Component\Console\Output\OutputInterface $output An output interface
*/
protected function listSerializer(OutputInterface $output)
{
$table = new Table($output);
/** @var SerializerManager $serializerManager */
$serializerManager = $this->getContainer()->get(RegisterSerializerPass::MANAGER_SERVICE_ID);
/** @var SerializerInterface $serializer */
foreach ($serializerManager->getSerializers() as $serializer) {
$table->addRow([
$serializer->getId(),
$serializer->getName(),
$serializer->getExtension(),
$serializer->getMimeType()
]);
}
$table
->setHeaders([
'Id',
'Name',
'Extension',
'MIME type'
])
->render()
;
}
/**
* Output available archivers
*
* @param \Symfony\Component\Console\Output\OutputInterface $output An output interface
*/
protected function listArchiver(OutputInterface $output)
{
$table = new Table($output);
/** @var ArchiverManager $archiverManager */
$archiverManager = $this->getContainer()->get(RegisterArchiverPass::MANAGER_SERVICE_ID);
/** @var ArchiverInterface $archiver */
foreach ($archiverManager->getArchivers(true) as $archiver) {
$table->addRow([
$archiver->getId(),
$archiver->getName(),
$archiver->getExtension(),
$archiver->getMimeType()
]);
}
$table
->setHeaders([
'Id',
'Name',
'Extension',
'MIME type'
])
->render()
;
}
}

View File

@@ -15,9 +15,7 @@ namespace Thelia\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Thelia\Core\Security\Resource\AdminResources;
use Thelia\Model\Map\ResourceI18nTableMap;
use Thelia\Model\Map\ResourceTableMap;
@@ -40,7 +38,6 @@ class GenerateResources extends ContainerAwareCommand
null
)
;
}
protected function execute(InputInterface $input, OutputInterface $output)
@@ -65,7 +62,7 @@ class GenerateResources extends ContainerAwareCommand
}
$compteur++;
$output->writeln(
"($compteur, '$value', NOW(), NOW())" . ($constant === key( array_slice( $constants, -1, 1, true ) ) ? ';' : ',')
"($compteur, '$value', NOW(), NOW())" . ($constant === key(array_slice($constants, -1, 1, true)) ? ';' : ',')
);
}
break;
@@ -81,17 +78,17 @@ class GenerateResources extends ContainerAwareCommand
$compteur++;
$title = ucwords( str_replace('.', ' / ', str_replace('admin.', '', $value) ) );
$title = ucwords(str_replace('.', ' / ', str_replace('admin.', '', $value)));
$output->writeln(
"($compteur, 'en_US', '$title'),"
);
$output->writeln(
"($compteur, 'fr_FR', '$title')" . ($constant === key( array_slice( $constants, -1, 1, true ) ) ? ';' : ',')
"($compteur, 'fr_FR', '$title')" . ($constant === key(array_slice($constants, -1, 1, true)) ? ';' : ',')
);
}
break;
default :
default:
foreach ($constants as $constant => $value) {
if ($constant == AdminResources::SUPERADMINISTRATOR) {
continue;
@@ -101,5 +98,4 @@ class GenerateResources extends ContainerAwareCommand
break;
}
}
}

View File

@@ -0,0 +1,221 @@
<?php
/*************************************************************************************/
/* This file is part of the Thelia package. */
/* */
/* Copyright (c) OpenStudio */
/* email : dev@thelia.net */
/* web : http://www.thelia.net */
/* */
/* For the full copyright and license information, please view the LICENSE.txt */
/* file that was distributed with this source code. */
/*************************************************************************************/
namespace Thelia\Command;
use Imagine\Exception\RuntimeException;
use Propel\Runtime\Propel;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Finder\Finder;
use Thelia\Core\Translation\Translator;
use Thelia\Model\CustomerQuery;
use Thelia\Model\Map\ProductTableMap;
use Thelia\Tools\URL;
use Thelia\Tools\Version\Version;
use TheliaSmarty\Template\SmartyParser;
/**
* Class GenerateSQLCommand
* @package Thelia\Command
* @author Julien Chanséaume <jchanseaume@openstudio.fr>
*/
class GenerateSQLCommand extends ContainerAwareCommand
{
/** @var Translator $translator */
protected $translator = null;
/** @var SmartyParser $parser */
protected $parser = null;
/** @var \PDO */
protected $con;
/** @var array */
protected $locales;
protected function configure()
{
$this
->setName("generate:sql")
->setDescription("Generate SQL files (insert.sql, update*.sql)")
->addOption(
"locales",
null,
InputOption::VALUE_OPTIONAL,
"generate only for only specific locales (separated by a ,) : fr_FR,es_ES or es_ES"
);
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$this->init($input);
// Main insert.sql file
$content = file_get_contents(THELIA_SETUP_DIRECTORY . 'insert.sql.tpl');
$version = Version::parse();
$content = $this->parser->renderString($content, $version, false);
if (false === file_put_contents(THELIA_SETUP_DIRECTORY . 'insert.sql', $content)) {
$output->writeln("Can't write file " . THELIA_SETUP_DIRECTORY . 'insert.sql');
} else {
$output->writeln("File " . THELIA_SETUP_DIRECTORY . 'insert.sql generated successfully.');
}
// sql update files
$finder = Finder::create()
->name('*.tpl')
->depth(0)
->in(THELIA_SETUP_DIRECTORY . 'update' . DS . 'tpl');
/** @var \SplFileInfo $file */
foreach ($finder as $file) {
$content = file_get_contents($file->getRealPath());
$content = $this->parser->renderString($content, [], false);
$destination = THELIA_SETUP_DIRECTORY . 'update' . DS . 'sql' . DS . $file->getBasename('.tpl');
if (false === file_put_contents($destination, $content)) {
$output->writeln("Can't write file " . $destination);
} else {
$output->writeln("File " . $destination . ' generated successfully.');
}
}
}
protected function init(InputInterface $input)
{
$this->initRequest();
$container = $this->getContainer();
$this->translator = $container->get('thelia.translator');
$this->parser = $container->get('thelia.parser');
$this->con = Propel::getConnection(ProductTableMap::DATABASE_NAME);
$this->initLocales($input);
$this->initParser();
}
/**
* @param InputInterface $input
* @return array
*/
protected function initLocales(InputInterface $input)
{
$this->locales = [];
$availableLocales = [];
$finder = Finder::create()
->name('*.php')
->depth(0)
->sortByName()
->in(THELIA_SETUP_DIRECTORY . 'I18n');
// limit to only some locale(s)
$localesToKeep = $input->getOption("locales");
if (!empty($localesToKeep)) {
$localesToKeep = explode(',', $localesToKeep);
} else {
$localesToKeep = null;
}
/** @var \SplFileInfo $file */
foreach ($finder as $file) {
$locale = $file->getBasename('.php');
$availableLocales[] = $locale;
if (empty($localesToKeep) || in_array($locale, $localesToKeep)) {
$this->locales[] = $locale;
$this->translator->addResource(
'php',
$file->getRealPath(),
$locale,
'install'
);
}
}
if (empty($this->locales)) {
throw new \RuntimeException(
sprintf(
"You should at least generate sql for one locale. Available locales : %s",
implode(', ', $availableLocales)
)
);
}
}
/**
* Initialize the smarty parser.
*
* The intl function is replaced, and locales are assigned.
*
* @throws \SmartyException
*/
protected function initParser()
{
$this->parser->unregisterPlugin('function', 'intl');
$this->parser->registerPlugin('function', 'intl', [$this, 'translate']);
$this->parser->assign("locales", $this->locales);
}
/**
* Smarty function that replace the classic `intl` function.
*
* The attributes of the function are:
* - `l`: the key
* - `locale`: the locale. eg.: fr_FR
* - `in_string`: set to 1 not add simple quote around the string. (default = 0)
* - `use_default`: set to 1 to use the `l` string as a fallback. (default = 0)
*
* @param $params
* @param $smarty
* @return string
*/
public function translate($params, $smarty)
{
$translation = '';
if (empty($params["l"])) {
throw new RuntimeException('Translation Error. Key is empty.');
} elseif (empty($params["locale"])) {
throw new RuntimeException('Translation Error. Locale is empty.');
} else {
$inString = (0 !== intval($params["in_string"]));
$useDefault = (0 !== intval($params["use_default"]));
$translation = $this->translator->trans(
$params["l"],
[],
'install',
$params["locale"],
$useDefault
);
if (empty($translation)) {
$translation = ($inString) ? '' : "NULL";
} else {
$translation = $this->con->quote($translation);
// remove quote
if ($inString) {
$translation = substr($translation, 1, -1);
}
}
}
return $translation;
}
}

View File

@@ -0,0 +1,157 @@
<?php
/*************************************************************************************/
/* This file is part of the Thelia package. */
/* */
/* Copyright (c) OpenStudio */
/* email : dev@thelia.net */
/* web : http://www.thelia.net */
/* */
/* For the full copyright and license information, please view the LICENSE.txt */
/* file that was distributed with this source code. */
/*************************************************************************************/
namespace Thelia\Command;
use Symfony\Component\Console\Helper\QuestionHelper;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ConfirmationQuestion;
use Thelia\Core\Event\Cache\CacheEvent;
use Thelia\Core\Event\TheliaEvents;
use Thelia\Model\IgnoredModuleHookQuery;
use Thelia\Model\Module;
use Thelia\Model\ModuleHookQuery;
use Thelia\Model\ModuleQuery;
/**
* Clean hook
*
* Class HookCleanCommand
* @package Thelia\Command
*
* @author Julien Chanséaume <julien@thelia.net>
*
*/
class HookCleanCommand extends ContainerAwareCommand
{
protected function configure()
{
$this
->setName("hook:clean")
->setDescription("Clean hooks. It will delete all hooks, then recreate it.")
->addOption(
"assume-yes",
'y',
InputOption::VALUE_NONE,
'Assume to answer yes to all questions'
)
->addArgument(
"module",
InputArgument::OPTIONAL,
"The module code to clean up"
);
}
protected function execute(InputInterface $input, OutputInterface $output)
{
try {
$module = $this->getModule($input);
if (!$this->askConfirmation($input, $output)) {
return;
}
$this->deleteHooks($module);
$output->writeln("<info>Hooks have been successfully deleted</info>");
$this->clearCache($output);
} catch (\Exception $ex) {
$output->writeln(sprintf("<error>%s</error>", $ex->getMessage()));
}
}
private function getModule(InputInterface $input)
{
$module = null;
$moduleCode = $input->getArgument("module");
if (!empty($moduleCode)) {
if (null === $module = ModuleQuery::create()->findOneByCode($moduleCode)) {
throw new \RuntimeException(sprintf("Module %s does not exist.", $moduleCode));
}
}
return $module;
}
private function askConfirmation(InputInterface $input, OutputInterface $output)
{
$assumeYes = $input->getOption("assume-yes");
$moduleCode = $input->getArgument("module");
if (!$assumeYes) {
/** @var QuestionHelper $helper */
$helper = $this->getHelper('question');
$questionText = "Would you like to delete all hooks ";
$questionText .= (empty($moduleCode))
? "of all modules"
: "of module " . $moduleCode;
$questionText .= " ? (yes, or no) ";
$question = new ConfirmationQuestion($questionText, false);
if (!$helper->ask($input, $output, $question)) {
$output->writeln("<info>No hooks deleted</info>");
return false;
}
}
return true;
}
/**
* Delete module hooks
*
* @param Module|null $module if specified it will only delete hooks related to this module.
* @throws \Exception
* @throws \Propel\Runtime\Exception\PropelException
*/
protected function deleteHooks($module)
{
$query = ModuleHookQuery::create();
if (null !== $module) {
$query
->filterByModule($module)
->delete();
} else {
$query->deleteAll();
}
$query = IgnoredModuleHookQuery::create();
if (null !== $module) {
$query
->filterByModule($module)
->delete();
} else {
$query->deleteAll();
}
}
/**
* @param OutputInterface $output
* @throws \Exception
*/
protected function clearCache(OutputInterface $output)
{
try {
$cacheDir = $this->getContainer()->getParameter("kernel.cache_dir");
$cacheEvent = new CacheEvent($cacheDir);
$this->getDispatcher()->dispatch(TheliaEvents::CACHE_CLEAR, $cacheEvent);
} catch (\Exception $ex) {
throw new \Exception(sprintf("Error during clearing of cache : %s", $ex->getMessage()));
}
}
}

View File

@@ -0,0 +1,141 @@
<?php
/*************************************************************************************/
/* This file is part of the Thelia package. */
/* */
/* Copyright (c) OpenStudio */
/* email : dev@thelia.net */
/* web : http://www.thelia.net */
/* */
/* For the full copyright and license information, please view the LICENSE.txt */
/* file that was distributed with this source code. */
/*************************************************************************************/
namespace Thelia\Command;
use Symfony\Component\Console\Helper\Table;
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\HttpFoundation\File\File;
use Thelia\Model\ImportQuery;
use Thelia\Model\LangQuery;
/**
* Class ImportCommand
* @author Jérôme Billiras <jbilliras@openstudio.fr>
*/
class ImportCommand extends ContainerAwareCommand
{
protected function configure()
{
$this
->setName('import')
->setDescription('Import data')
->setHelp('The <info>import</info> command run selected import')
->addArgument(
'ref',
InputArgument::OPTIONAL,
'Import reference.'
)
->addArgument(
'filePath',
InputArgument::OPTIONAL,
'File path to import'
)
->addOption(
'locale',
null,
InputOption::VALUE_REQUIRED,
'Locale for export',
'en_US'
)
->addOption(
'list',
null,
InputOption::VALUE_NONE,
'List available imports and exit.'
)
;
}
protected function execute(InputInterface $input, OutputInterface $output)
{
if ($input->getOption('list')) {
$this->listImport($output);
return;
}
$importRef = $input->getArgument('ref');
$path = $input->getArgument('filePath');
if ($importRef === null || $path === null) {
throw new \RuntimeException(
'Not enough arguments.' . PHP_EOL . 'If no options are provided, ref and filePath arguments are required.'
);
}
/** @var \Thelia\Handler\ImportHandler $importHandler */
$importHandler = $this->getContainer()->get('thelia.import.handler');
$import = $importHandler->getImportByRef($importRef);
if ($import === null) {
throw new \RuntimeException(
$importRef . ' import doesn\'t exist.'
);
}
$importEvent = $importHandler->import(
$import,
new File($input->getArgument('filePath')),
(new LangQuery)->findOneByLocale($input->getOption('locale'))
);
$formattedLine = $this->getHelper('formatter')->formatBlock(
'Successfully import ' . $importEvent->getImport()->getImportedRows() . ' row(s)',
'fg=black;bg=green',
true
);
$output->writeln($formattedLine);
if (count($importEvent->getErrors()) > 0) {
$formattedLine = $this->getHelper('formatter')->formatBlock(
'With error',
'fg=black;bg=yellow',
true
);
$output->writeln($formattedLine);
foreach ($importEvent->getErrors() as $error) {
$output->writeln('<comment>' . $error . '</comment>');
}
}
}
/**
* Output available imports
*
* @param \Symfony\Component\Console\Output\OutputInterface $output An output interface
*/
protected function listImport(OutputInterface $output)
{
$table = new Table($output);
foreach ((new ImportQuery)->find() as $import) {
$table->addRow([
$import->getRef(),
$import->getTitle(),
$import->getDescription()
]);
}
$table
->setHeaders([
'Reference',
'Title',
'Description'
])
->render()
;
}
}

View File

@@ -12,20 +12,23 @@
namespace Thelia\Command;
use Symfony\Component\Console\Helper\QuestionHelper;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\Question;
use Symfony\Component\Filesystem\Filesystem;
use Thelia\Core\Translation\Translator;
use Thelia\Install\CheckPermission;
use Thelia\Install\Database;
use Thelia\Tools\TokenProvider;
/**
* try to install a new instance of Thelia
*
* Class Install
* @package Thelia\Command
* @author Manuel Raynaud <mraynaud@openstudio.fr>
* @author Manuel Raynaud <manu@raynaud.io>
*/
class Install extends ContainerAwareCommand
{
@@ -42,7 +45,8 @@ class Install extends ContainerAwareCommand
"db_host",
null,
InputOption::VALUE_OPTIONAL,
"host for your database"
"host for your database",
"localhost"
)
->addOption(
"db_username",
@@ -62,8 +66,14 @@ class Install extends ContainerAwareCommand
InputOption::VALUE_OPTIONAL,
"database name"
)
->addOption(
"db_port",
null,
InputOption::VALUE_OPTIONAL,
"database port",
"3306"
)
;
}
protected function execute(InputInterface $input, OutputInterface $output)
@@ -84,11 +94,12 @@ class Install extends ContainerAwareCommand
"host" => $input->getOption("db_host"),
"dbName" => $input->getOption("db_name"),
"username" => $input->getOption("db_username"),
"password" => $input->getOption("db_password")
"password" => $input->getOption("db_password"),
"port" => $input->getOption("db_port")
);
while (false === $connection = $this->tryConnection($connectionInfo, $output)) {
$connectionInfo = $this->getConnectionInfo($input, $output);
$connectionInfo = $this->getConnectionInfo($input, $output);
}
$database = new Database($connection);
@@ -101,6 +112,7 @@ class Install extends ContainerAwareCommand
""
));
$database->insertSql($connectionInfo["dbName"]);
$this->manageSecret($database);
$output->writeln(array(
"",
@@ -118,6 +130,13 @@ class Install extends ContainerAwareCommand
));
}
protected function manageSecret(Database $database)
{
$secret = TokenProvider::generateToken();
$sql = "UPDATE `config` SET `value`=? WHERE `name`='form.secret'";
$database->execute($sql, [$secret]);
}
/**
* Test if needed directories have write permission
*
@@ -129,26 +148,32 @@ class Install extends ContainerAwareCommand
"Checking some permissions"
));
$permissions = new CheckPermission(false, $this->getContainer()->get('thelia.translator'));
/** @var Translator $translator */
$translator = $this->getContainer()->get('thelia.translator');
$permissions = new CheckPermission(false, $translator);
$isValid = $permissions->exec();
foreach ($permissions->getValidationMessages() as $item => $data) {
if ($data['status']) {
$output->writeln(array(
sprintf("<info>%s ...</info> %s",
$data['text'],
"<info>Ok</info>")
$output->writeln(
array(
sprintf(
"<info>%s ...</info> %s",
$data['text'],
"<info>Ok</info>"
)
)
);
} else {
$output->writeln(array(
sprintf("<error>%s </error>%s",
sprintf(
"<error>%s </error>%s",
$data['text'],
sprintf("<error>%s</error>", $data["hint"])
)
));
}
}
if (false === $isValid) {
@@ -177,14 +202,13 @@ class Install extends ContainerAwareCommand
$configContent = str_replace("%PASSWORD%", $connectionInfo["password"], $configContent);
$configContent = str_replace(
"%DSN%",
sprintf("mysql:host=%s;dbname=%s", $connectionInfo["host"], $connectionInfo["dbName"]),
sprintf("mysql:host=%s;dbname=%s;port=%s", $connectionInfo["host"], $connectionInfo["dbName"], $connectionInfo['port']),
$configContent
);
file_put_contents($configFile, $configContent);
$fs->remove($this->getContainer()->getParameter("kernel.cache_dir"));
}
/**
@@ -196,16 +220,15 @@ class Install extends ContainerAwareCommand
*/
protected function tryConnection($connectionInfo, OutputInterface $output)
{
if (is_null($connectionInfo["dbName"])) {
return false;
}
$dsn = "mysql:host=%s";
$dsn = "mysql:host=%s;port=%s";
try {
$connection = new \PDO(
sprintf($dsn, $connectionInfo["host"]),
sprintf($dsn, $connectionInfo["host"], $connectionInfo["port"]),
$connectionInfo["username"],
$connectionInfo["password"]
);
@@ -230,62 +253,93 @@ class Install extends ContainerAwareCommand
*/
protected function getConnectionInfo(InputInterface $input, OutputInterface $output)
{
$dialog = $this->getHelperSet()->get('dialog');
/** @var QuestionHelper $helper */
$helper = $this->getHelper('question');
$connectionInfo = array();
$connectionInfo["host"] = $dialog->askAndValidate(
$connectionInfo['host'] = $this->enterData(
$helper,
$input,
$output,
$this->decorateInfo("Database host : "),
function ($answer) {
$answer = trim($answer);
if (is_null($answer)) {
throw new \RuntimeException("You must specify a database host");
}
return $answer;
}
"Database host [default: localhost] : ",
"You must specify a database host",
false,
"localhost"
);
$connectionInfo["dbName"] = $dialog->askAndValidate(
$connectionInfo['port'] = $this->enterData(
$helper,
$input,
$output,
$this->decorateInfo("Database name (if database does not exist, Thelia will try to create it) : "),
function ($answer) {
$answer = trim($answer);
if (is_null($answer)) {
throw new \RuntimeException("You must specify a database name");
}
return $answer;
}
"Database port [default: 3306] : ",
"You must specify a database port",
false,
"3306"
);
$connectionInfo["username"] = $dialog->askAndValidate(
$connectionInfo['dbName'] = $this->enterData(
$helper,
$input,
$output,
$this->decorateInfo("Database username : "),
function ($answer) {
$answer = trim($answer);
if (is_null($answer)) {
throw new \RuntimeException("You must specify a database username");
}
return $answer;
}
"Database name (if database does not exist, Thelia will try to create it) : ",
"You must specify a database name"
);
$connectionInfo["password"] = $dialog->askHiddenResponse(
$connectionInfo['username'] = $this->enterData(
$helper,
$input,
$output,
$this->decorateInfo("Database password : ")
"Database username : ",
"You must specify a database username"
);
$connectionInfo['password'] = $this->enterData(
$helper,
$input,
$output,
"Database password : ",
"You must specify a database username",
true,
null,
true
);
return $connectionInfo;
}
protected function enterData(
QuestionHelper $helper,
InputInterface $input,
OutputInterface $output,
$label,
$errorMessage,
$hidden = false,
$defaultValue = null,
$beEmpty = false
) {
$question = new Question($label, $defaultValue);
if ($hidden) {
$question->setHidden(true);
$question->setHiddenFallback(false);
}
$question->setValidator(function ($value) use (&$errorMessage, &$beEmpty) {
if (trim($value) == '') {
if (is_null($value) && !$beEmpty) {
throw new \Exception($errorMessage);
}
}
return $value;
});
return $helper->ask($input, $output, $question);
}
protected function decorateInfo($text)
{
return sprintf("<info>%s</info>", $text);
}
}

View File

@@ -15,8 +15,11 @@ namespace Thelia\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Input\InputOption;
use Thelia\Core\Event\Module\ModuleToggleActivationEvent;
use Thelia\Core\Event\TheliaEvents;
use Thelia\Model\ModuleQuery;
use Thelia\Module\BaseModule;
/**
* activates a module
@@ -33,12 +36,17 @@ class ModuleActivateCommand extends BaseModuleGenerate
$this
->setName("module:activate")
->setDescription("Activates a module")
->addOption(
"with-dependencies",
null,
InputOption::VALUE_NONE,
'activate module recursively'
)
->addArgument(
"module" ,
"module",
InputArgument::REQUIRED,
"module to activate"
)
;
);
}
protected function execute(InputInterface $input, OutputInterface $output)
@@ -51,12 +59,26 @@ class ModuleActivateCommand extends BaseModuleGenerate
throw new \RuntimeException(sprintf("module %s not found", $moduleCode));
}
try {
$moduleInstance = $module->createInstance();
if ($module->getActivate() == BaseModule::IS_ACTIVATED) {
throw new \RuntimeException(sprintf("module %s is already actived", $moduleCode));
}
$moduleInstance->activate();
try {
$event = new ModuleToggleActivationEvent($module->getId());
if ($input->getOption("with-dependencies")) {
$event->setRecursive(true);
}
$this->getDispatcher()->dispatch(TheliaEvents::MODULE_TOGGLE_ACTIVATION, $event);
} catch (\Exception $e) {
throw new \RuntimeException(sprintf("Activation fail with Exception : [%d] %s", $e->getCode(), $e->getMessage()));
throw new \RuntimeException(
sprintf(
"Activation fail with Exception : [%d] %s",
$e->getCode(),
$e->getMessage()
)
);
}
//impossible to change output class in CommandTester...

View File

@@ -12,11 +12,17 @@
namespace Thelia\Command;
use Symfony\Component\Console\Helper\QuestionHelper;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Question\ConfirmationQuestion;
use Thelia\Action\Module;
use Thelia\Core\Event\Module\ModuleToggleActivationEvent;
use Thelia\Core\Event\TheliaEvents;
use Thelia\Model\ModuleQuery;
use Thelia\Module\BaseModule;
/**
* Deactivates a module
@@ -33,11 +39,23 @@ class ModuleDeactivateCommand extends BaseModuleGenerate
$this
->setName("module:deactivate")
->setDescription("Deactivate a module")
->addOption(
"with-dependencies",
null,
InputOption::VALUE_NONE,
'activate module recursively'
)
->addArgument(
"module" ,
"module",
InputArgument::REQUIRED,
"module to deactivate"
)
->addOption(
"assume-yes",
'y',
InputOption::VALUE_NONE,
'Assume to deactivate a mandatory module'
)
;
}
@@ -51,10 +69,26 @@ class ModuleDeactivateCommand extends BaseModuleGenerate
throw new \RuntimeException(sprintf("module %s not found", $moduleCode));
}
try {
$moduleInstance = $module->createInstance();
if ($module->getActivate() == BaseModule::IS_NOT_ACTIVATED) {
throw new \RuntimeException(sprintf("module %s is already deactivated", $moduleCode));
}
$moduleInstance->deActivate();
try {
$event = new ModuleToggleActivationEvent($module->getId());
$module = ModuleQuery::create()->findPk($module->getId());
if ($module->getMandatory() == BaseModule::IS_MANDATORY) {
if (!$this->askConfirmation($input, $output)) {
return;
}
$event->setAssumeDeactivate(true);
}
if ($input->getOption("with-dependencies")) {
$event->setRecursive(true);
}
$this->getDispatcher()->dispatch(TheliaEvents::MODULE_TOGGLE_ACTIVATION, $event);
} catch (\Exception $e) {
throw new \RuntimeException(sprintf("Deactivation fail with Exception : [%d] %s", $e->getCode(), $e->getMessage()));
}
@@ -68,4 +102,33 @@ class ModuleDeactivateCommand extends BaseModuleGenerate
), "bg=green;fg=black");
}
}
private function askConfirmation(InputInterface $input, OutputInterface $output)
{
$assumeYes = $input->getOption("assume-yes");
$moduleCode = $input->getArgument("module");
if (!$assumeYes) {
/** @var QuestionHelper $helper */
$helper = $this->getHelper('question');
$questionText = "Module ";
$questionText .= (empty($moduleCode))
? ""
: $moduleCode;
$questionText .= " is mandatory.\n";
$questionText .= "Would you like to deactivate the module ";
$questionText .= (empty($moduleCode))
? ""
: $moduleCode;
$questionText .= " ? (yes, or no) ";
$question = new ConfirmationQuestion($questionText, false);
if (!$helper->ask($input, $output, $question)) {
return false;
}
}
return true;
}
}

View File

@@ -14,6 +14,7 @@ namespace Thelia\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Filesystem\Filesystem;
@@ -22,7 +23,7 @@ use Symfony\Component\Filesystem\Filesystem;
*
* Class ModuleGenerateCommand
* @package Thelia\Command
* @author Manuel Raynaud <mraynaud@openstudio.fr>
* @author Manuel Raynaud <manu@raynaud.io>
*/
class ModuleGenerateCommand extends BaseModuleGenerate
{
@@ -32,23 +33,38 @@ class ModuleGenerateCommand extends BaseModuleGenerate
->setName("module:generate")
->setDescription("generate all needed files for creating a new Module")
->addArgument(
"name" ,
"name",
InputArgument::REQUIRED,
"name wanted for your Module"
)
->addOption(
'force',
null,
InputOption::VALUE_NONE,
'If defined, it will update the module with missing directories and files (no overrides).'
)
;
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$this->module = $this->formatModuleName($input->getArgument("name"));
$this->moduleDirectory = THELIA_MODULE_DIR . DIRECTORY_SEPARATOR . $this->module;
$this->verifyExistingModule();
$this->moduleDirectory = THELIA_MODULE_DIR . $this->module;
$this->validModuleName($this->module);
try {
$this->verifyExistingModule();
} catch (\RuntimeException $ex) {
if (false === $input->getOption('force')) {
throw $ex;
}
}
$this->createDirectories();
$this->createFiles();
if (method_exists($this, "renderBlock")) {
//impossible to change output class in CommandTester...
if (method_exists($output, "renderBlock")) {
// impossible to change output class in CommandTester...
$output->renderBlock(array(
'',
sprintf("module %s create with success", $this->module),
@@ -56,19 +72,38 @@ class ModuleGenerateCommand extends BaseModuleGenerate
''
), "bg=green;fg=black");
}
}
private function createDirectories()
{
$fs = new Filesystem();
$fs->mkdir($this->moduleDirectory);
foreach ($this->neededDirectories as $directory) {
$fs->mkdir($this->moduleDirectory . DIRECTORY_SEPARATOR . $directory);
if (!$fs->exists($this->moduleDirectory)) {
$fs->mkdir($this->moduleDirectory);
}
foreach ($this->neededDirectories as $directory) {
if (!$fs->exists($this->moduleDirectory . DIRECTORY_SEPARATOR . $directory)) {
$fs->mkdir($this->moduleDirectory . DIRECTORY_SEPARATOR . $directory);
}
}
}
protected function copyConfigFile($filename, $skeletonDir, Filesystem $fs)
{
$filename = $this->moduleDirectory . DIRECTORY_SEPARATOR . "Config" . DIRECTORY_SEPARATOR . $filename;
if (!$fs->exists($filename)) {
$configContent = file_get_contents($skeletonDir . "config.xml");
$configContent = str_replace("%%CLASSNAME%%", $this->module, $configContent);
$configContent = str_replace("%%NAMESPACE%%", $this->module, $configContent);
$configContent = str_replace("%%NAMESPACE_LOWER%%", strtolower($this->module), $configContent);
file_put_contents(
$filename,
$configContent
);
}
}
private function createFiles()
@@ -76,56 +111,116 @@ class ModuleGenerateCommand extends BaseModuleGenerate
$fs = new Filesystem();
try {
$skeletonDir = str_replace("/", DIRECTORY_SEPARATOR, THELIA_ROOT . "/core/lib/Thelia/Command/Skeleton/Module/");
$skeletonDir = str_replace("/", DIRECTORY_SEPARATOR, __DIR__ . "/Skeleton/Module/");
// config.xml file
$fs->copy($skeletonDir . "config.xml", $this->moduleDirectory . DIRECTORY_SEPARATOR . "Config" . DIRECTORY_SEPARATOR . "config.xml");
$this->copyConfigFile("config.xml", $skeletonDir, $fs);
$this->copyConfigFile("config_prod.xml", $skeletonDir, $fs);
$this->copyConfigFile("config_dev.xml", $skeletonDir, $fs);
$this->copyConfigFile("config_test.xml", $skeletonDir, $fs);
$moduleContent = file_get_contents($skeletonDir . "module.xml");
// Readme.md file
$filename = $this->moduleDirectory . DIRECTORY_SEPARATOR . "Readme.md";
if (!$fs->exists($filename)) {
$readmeContent = file_get_contents($skeletonDir . "Readme.md");
$moduleContent = str_replace("%%CLASSNAME%%", $this->module, $moduleContent);
$moduleContent = str_replace("%%NAMESPACE%%", $this->module, $moduleContent);
// generate title for readme
preg_match_all('!([A-Z][A-Z0-9]*(?=$|[A-Z][a-z0-9])|[A-Za-z][a-z0-9]+)!', $this->module, $readmeTitle);
$composerFinalName = strtolower(implode("-", $readmeTitle[0]));
file_put_contents($this->moduleDirectory . DIRECTORY_SEPARATOR . "Config". DIRECTORY_SEPARATOR . "module.xml", $moduleContent);
$readmeContent = str_replace("%%MODULENAME%%", $this->module, $readmeContent);
$readmeContent = str_replace("%%MODULENAMETITLE%%", implode(" ", $readmeTitle[0]), $readmeContent);
$readmeContent = str_replace("%%COMPOSERNAME%%", $composerFinalName, $readmeContent);
file_put_contents($filename, $readmeContent);
}
// composer.json file
$filename = $this->moduleDirectory . DIRECTORY_SEPARATOR . "composer.json";
if (!$fs->exists($filename)) {
$composerContent = file_get_contents($skeletonDir . "composer.json");
// generate composer module name
preg_match_all('!([A-Z][A-Z0-9]*(?=$|[A-Z][a-z0-9])|[A-Za-z][a-z0-9]+)!', $this->module, $composerName);
$composerContent = str_replace("%%MODULENAME%%", $this->module, $composerContent);
$composerContent = str_replace("%%COMPOSERNAME%%", strtolower(implode("-", $composerName[0])), $composerContent);
file_put_contents($filename, $composerContent);
}
// module.xml file
$filename = $this->moduleDirectory . DIRECTORY_SEPARATOR . "Config". DIRECTORY_SEPARATOR . "module.xml";
if (!$fs->exists($filename)) {
$moduleContent = file_get_contents($skeletonDir . "module.xml");
$moduleContent = str_replace("%%CLASSNAME%%", $this->module, $moduleContent);
$moduleContent = str_replace("%%NAMESPACE%%", $this->module, $moduleContent);
file_put_contents($filename, $moduleContent);
}
// PHP Class template
$classContent = file_get_contents($skeletonDir . "Class.php.template");
$filename = $this->moduleDirectory . DIRECTORY_SEPARATOR . $this->module . ".php";
if (!$fs->exists($filename)) {
$classContent = file_get_contents($skeletonDir . "Class.php.template");
$classContent = str_replace("%%CLASSNAME%%", $this->module, $classContent);
$classContent = str_replace("%%NAMESPACE%%", $this->module, $classContent);
$classContent = str_replace("%%CLASSNAME%%", $this->module, $classContent);
$classContent = str_replace("%%NAMESPACE%%", $this->module, $classContent);
$classContent = str_replace("%%DOMAINNAME%%", strtolower($this->module), $classContent);
file_put_contents($this->moduleDirectory . DIRECTORY_SEPARATOR . $this->module.".php", $classContent);
file_put_contents($filename, $classContent);
}
// schema.xml file
$schemaContent = file_get_contents($skeletonDir . "schema.xml");
$filename = $this->moduleDirectory . DIRECTORY_SEPARATOR . "Config" . DIRECTORY_SEPARATOR . "schema.xml";
if (!$fs->exists($filename)) {
$schemaContent = file_get_contents($skeletonDir . "schema.xml");
$schemaContent = str_replace("%%NAMESPACE%%", $this->module, $schemaContent);
$schemaContent = str_replace("%%NAMESPACE%%", $this->module, $schemaContent);
$schemaContent = str_replace(
'%%XSD_LOCATION%%',
$fs->makePathRelative(
THELIA_VENDOR . 'propel/propel/resources/xsd/',
$this->moduleDirectory
) . 'database.xsd',
$schemaContent
);
file_put_contents($this->moduleDirectory . DIRECTORY_SEPARATOR . "Config". DIRECTORY_SEPARATOR . "schema.xml", $schemaContent);
file_put_contents($filename, $schemaContent);
}
// routing.xml file
$routingContent = file_get_contents($skeletonDir . "routing.xml");
$filename = $this->moduleDirectory . DIRECTORY_SEPARATOR . "Config" . DIRECTORY_SEPARATOR . "routing.xml";
if (!$fs->exists($filename)) {
$routingContent = file_get_contents($skeletonDir . "routing.xml");
$routingContent = str_replace("%%NAMESPACE%%", $this->module, $routingContent);
$routingContent = str_replace("%%CLASSNAME_LOWER%%", strtolower($this->module), $routingContent);
$routingContent = str_replace("%%NAMESPACE%%", $this->module, $routingContent);
$routingContent = str_replace("%%CLASSNAME_LOWER%%", strtolower($this->module), $routingContent);
file_put_contents($this->moduleDirectory . DIRECTORY_SEPARATOR . "Config". DIRECTORY_SEPARATOR . "routing.xml", $routingContent);
file_put_contents($filename, $routingContent);
}
// I18n sample files
$fs->copy(
$skeletonDir . DIRECTORY_SEPARATOR . "I18n" . DIRECTORY_SEPARATOR . "fr_FR.php",
$this->moduleDirectory . DIRECTORY_SEPARATOR . "I18n" . DIRECTORY_SEPARATOR . "fr_FR.php"
);
$filename = $this->moduleDirectory . DIRECTORY_SEPARATOR . "I18n" . DIRECTORY_SEPARATOR . "fr_FR.php";
if (!$fs->exists($filename)) {
$fs->copy(
$skeletonDir . DIRECTORY_SEPARATOR . "I18n" . DIRECTORY_SEPARATOR . "fr_FR.php",
$filename
);
}
$fs->copy(
$skeletonDir . DIRECTORY_SEPARATOR . "I18n" . DIRECTORY_SEPARATOR . "en_US.php",
$this->moduleDirectory . DIRECTORY_SEPARATOR . "I18n" . DIRECTORY_SEPARATOR . "en_US.php"
);
$filename = $this->moduleDirectory . DIRECTORY_SEPARATOR . "I18n" . DIRECTORY_SEPARATOR . "en_US.php";
if (!$fs->exists($filename)) {
$fs->copy(
$skeletonDir . DIRECTORY_SEPARATOR . "I18n" . DIRECTORY_SEPARATOR . "en_US.php",
$this->moduleDirectory . DIRECTORY_SEPARATOR . "I18n" . DIRECTORY_SEPARATOR . "en_US.php"
);
}
} catch (\Exception $ex) {
$fs->remove($this->moduleDirectory);
throw $ex;
}
}
}

View File

@@ -13,6 +13,7 @@
namespace Thelia\Command;
use Propel\Generator\Command\ModelBuildCommand;
use Symfony\Component\Console\Helper\FormatterHelper;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
@@ -25,7 +26,7 @@ use Symfony\Component\Filesystem\Filesystem;
*
* Class ModuleGenerateModelCommand
* @package Thelia\Command
* @author Manuel Raynaud <mraynaud@openstudio.fr>
* @author Manuel Raynaud <manu@raynaud.io>
*/
class ModuleGenerateModelCommand extends BaseModuleGenerate
{
@@ -46,13 +47,12 @@ class ModuleGenerateModelCommand extends BaseModuleGenerate
"with this option generate sql file at the same time"
)
;
}
public function execute(InputInterface $input, OutputInterface $output)
{
$this->module = $this->formatModuleName($input->getArgument("name"));
$this->moduleDirectory = THELIA_MODULE_DIR . DS . $this->module;
$this->moduleDirectory = THELIA_MODULE_DIR . $this->module;
$fs = new Filesystem();
@@ -66,11 +66,13 @@ class ModuleGenerateModelCommand extends BaseModuleGenerate
$this->generateModel($output);
$output->renderBlock(array(
'',
'Model generated successfuly',
''
), 'bg=green;fg=black');
/** @var FormatterHelper $formatter */
$formatter = $this->getHelper('formatter');
$formattedBlock = $formatter->formatBlock(
'Model generated successfully',
'bg=green;fg=black'
);
$output->writeln($formattedBlock);
if ($input->getOption("generate-sql")) {
$output->writeln(' ');
@@ -80,7 +82,6 @@ class ModuleGenerateModelCommand extends BaseModuleGenerate
protected function generateSql(OutputInterface $output)
{
$command = $this->getApplication()->find("module:generate:sql");
$command->run(
@@ -108,7 +109,7 @@ class ModuleGenerateModelCommand extends BaseModuleGenerate
);
$verifyDirectories = array(
THELIA_MODULE_DIR . DS . "Thelia",
THELIA_MODULE_DIR . "Thelia",
$this->moduleDirectory . DS . "Model" . DS . "Thelia"
);
@@ -117,7 +118,5 @@ class ModuleGenerateModelCommand extends BaseModuleGenerate
$fs->remove($directory);
}
}
}
}

View File

@@ -13,6 +13,7 @@
namespace Thelia\Command;
use Propel\Generator\Command\SqlBuildCommand;
use Symfony\Component\Console\Helper\FormatterHelper;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
@@ -24,7 +25,7 @@ use Symfony\Component\Filesystem\Filesystem;
*
* Class ModuleGenerateSqlCommand
* @package Thelia\Command
* @author Manuel Raynaud <mraynaud@openstudio.fr>
* @author Manuel Raynaud <manu@raynaud.io>
*/
class ModuleGenerateSqlCommand extends BaseModuleGenerate
{
@@ -44,7 +45,7 @@ class ModuleGenerateSqlCommand extends BaseModuleGenerate
public function execute(InputInterface $input, OutputInterface $output)
{
$this->module = $this->formatModuleName($input->getArgument("name"));
$this->moduleDirectory = THELIA_MODULE_DIR . DS . $this->module;
$this->moduleDirectory = THELIA_MODULE_DIR . $this->module;
$fs = new Filesystem();
@@ -68,12 +69,15 @@ class ModuleGenerateSqlCommand extends BaseModuleGenerate
$output
);
$output->renderBlock(array(
'',
'Sql generated successfuly',
'File available in your module config directory',
''
), 'bg=green;fg=black');
/** @var FormatterHelper $formatter */
$formatter = $this->getHelper('formatter');
$formattedBlock = $formatter->formatBlock(
[
'Sql generated successfully',
'File available in your module config directory',
],
'bg=green;fg=black'
);
$output->writeln($formattedBlock);
}
}

View File

@@ -0,0 +1,87 @@
<?php
/*************************************************************************************/
/* This file is part of the Thelia package. */
/* */
/* Copyright (c) OpenStudio */
/* email : dev@thelia.net */
/* web : http://www.thelia.net */
/* */
/* For the full copyright and license information, please view the LICENSE.txt */
/* file that was distributed with this source code. */
/*************************************************************************************/
namespace Thelia\Command;
use Symfony\Component\Console\Helper\Table;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Thelia\Model\Map\ModuleTableMap;
use Thelia\Model\ModuleQuery;
use Thelia\Module\BaseModule;
/**
* Class ModuleListCommand
* @package Thelia\Command
* @author Benjamin Perche <bperche@openstudio.fr>
*/
class ModuleListCommand extends ContainerAwareCommand
{
protected function configure()
{
$this
->setName('module:list')
->setDescription('List the modules')
;
}
/**
* @param InputInterface $input
* @param OutputInterface $output
* @return int|null|void
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$helper = new Table($output);
$helper->addRows($this->getModulesData());
$helper
->setHeaders(["Code", "Active", "Type", "Version"])
->render()
;
}
protected function getModulesData()
{
$moduleData = ModuleQuery::create()
->orderByType()
->addAsColumn("code", ModuleTableMap::CODE)
->addAsColumn("active", "IF(".ModuleTableMap::ACTIVATE.", \"Yes\", \"No\")")
->addAsColumn("type", ModuleTableMap::TYPE)
->addAsColumn("version", ModuleTableMap::VERSION)
->select([
"code",
"active",
"type",
"version",
])
->find()
->toArray()
;
foreach ($moduleData as &$row) {
switch ($row["type"]) {
case BaseModule::CLASSIC_MODULE_TYPE:
$row["type"] = "classic";
break;
case BaseModule::DELIVERY_MODULE_TYPE:
$row["type"] = "delivery";
break;
case BaseModule::PAYMENT_MODULE_TYPE:
$row["type"] = "payment";
break;
}
}
return $moduleData;
}
}

View File

@@ -0,0 +1,219 @@
<?php
/*************************************************************************************/
/* This file is part of the Thelia package. */
/* */
/* Copyright (c) OpenStudio */
/* email : dev@thelia.net */
/* web : http://www.thelia.net */
/* */
/* For the full copyright and license information, please view the LICENSE.txt */
/* file that was distributed with this source code. */
/*************************************************************************************/
namespace Thelia\Command;
use Propel\Runtime\ActiveQuery\Criteria;
use Symfony\Component\Console\Helper\FormatterHelper;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ConfirmationQuestion;
use Thelia\Core\Event\TheliaEvents;
use Thelia\Core\Event\UpdatePositionEvent;
use Thelia\Model\ModuleQuery;
use Symfony\Component\Console\Helper\QuestionHelper;
/**
* Class ModulePositionCommand
* Set modules position
*
* @package Thelia\Command
* @author Jérôme Billiras <jerome.billiras+github@gmail.com>
*/
class ModulePositionCommand extends ContainerAwareCommand
{
/**
* @var \Thelia\Model\ModuleQuery
*/
protected $moduleQuery;
/**
* @var array Modules list
*/
protected $modulesList = [];
/**
* @var array Modules positions list
*/
protected $positionsList = [];
public function __construct($name = null)
{
parent::__construct($name);
$this->moduleQuery = new ModuleQuery;
}
protected function configure()
{
$this
->setName('module:position')
->setDescription('Set module(s) position')
->addArgument(
'modules',
InputArgument::REQUIRED | InputArgument::IS_ARRAY,
'Module in format moduleName:[+|-]position where position is an integer or up or down.'
)
;
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$argsList = $input->getArgument('modules');
array_walk($argsList, [$this, 'checkModuleArgument']);
if (!$this->checkPositions($input, $output, $isAbsolute)) {
return;
}
if ($isAbsolute) {
array_multisort($this->positionsList, SORT_ASC, SORT_REGULAR, $this->modulesList);
}
$maxPositionByType = $this->cleanPosition();
foreach ($this->modulesList as $moduleIdx => $moduleName) {
$this->moduleQuery->clear();
$module = $this->moduleQuery->findOneByCode($moduleName);
$position = $this->positionsList[$moduleIdx];
if ($position === 'up') {
$event = new UpdatePositionEvent($module->getId(), UpdatePositionEvent::POSITION_UP);
} elseif ($position === 'down') {
$event = new UpdatePositionEvent($module->getId(), UpdatePositionEvent::POSITION_DOWN);
} else {
if ($position[0] === '+' || $position[0] === '-') {
$position = $module->getPosition() + $position;
}
if ($position < 1) {
$position = 1;
}
$maxPosition = $maxPositionByType[$module->getType()];
if ($position > $maxPosition) {
$position = $maxPosition;
}
$event = new UpdatePositionEvent($module->getId(), UpdatePositionEvent::POSITION_ABSOLUTE, $position);
}
$this->getDispatcher()->dispatch(TheliaEvents::MODULE_UPDATE_POSITION, $event);
}
/** @var FormatterHelper $formatter */
$formatter = $this->getHelper('formatter');
$formattedBlock = $formatter->formatBlock('Module position(s) updated', 'bg=green;fg=black', true);
$output->writeln($formattedBlock);
}
/**
* Check a module argument format
*
* @param string $paramValue
*
* @throws \InvalidArgumentException
* @throws \RuntimeException
*/
protected function checkModuleArgument($paramValue)
{
if (!preg_match('#^([a-z0-9]+):([\+-]?[0-9]+|up|down)$#i', $paramValue, $matches)) {
throw new \InvalidArgumentException(
'Arguments must be in format moduleName:[+|-]position where position is an integer or up or down.'
);
}
$this->moduleQuery->clear();
$module = $this->moduleQuery->findOneByCode($matches[1]);
if ($module === null) {
throw new \RuntimeException(sprintf('%s module does not exists. Try to refresh first.', $matches[1]));
}
$this->modulesList[] = $matches[1];
$this->positionsList[] = $matches[2];
}
/**
* Reorder modules positions (without holes)
*
* @return array Maximum position by type
*/
protected function cleanPosition()
{
$modulesType = [];
$this->moduleQuery->clear();
$modules = $this->moduleQuery->orderByPosition(Criteria::ASC);
/** @var \Thelia\Model\Module $module */
foreach ($modules as $module) {
if (!isset($modulesType[$module->getType()])) {
$modulesType[$module->getType()] = 0;
}
$module
->setPosition(++$modulesType[$module->getType()])
->save()
;
}
return $modulesType;
}
/**
* Check positions consistency
*
* @param InputInterface $input An InputInterface instance
* @param OutputInterface $output An OutputInterface instance
* @param bool $isAbsolute Set to true or false according to position values
*
* @throws \InvalidArgumentException
*
* @return bool Continue or stop command
*/
protected function checkPositions(InputInterface $input, OutputInterface $output, &$isAbsolute = false)
{
$isRelative = false;
foreach (array_count_values($this->positionsList) as $value => $count) {
if (is_int($value) && $value[0] !== '+' && $value[0] !== '-') {
$isAbsolute = true;
if ($count > 1) {
throw new \InvalidArgumentException('Two (or more) absolute positions are identical.');
}
} else {
$isRelative = true;
}
}
if ($isAbsolute && $isRelative) {
/** @var FormatterHelper $formatter */
$formatter = $this->getHelper('formatter');
$formattedBlock = $formatter->formatBlock(
'Mix absolute and relative positions may produce unexpected results !',
'bg=yellow;fg=black',
true
);
$output->writeln($formattedBlock);
/** @var QuestionHelper $helper */
$helper = $this->getHelper('question');
$question = new ConfirmationQuestion('<question>Do you want to continue ? y/[n]<question>', false);
return $helper->ask($input, $output, $question);
}
return true;
}
}

View File

@@ -14,6 +14,7 @@ namespace Thelia\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Thelia\Exception\InvalidModuleException;
use Thelia\Module\ModuleManagement;
/**
@@ -36,13 +37,20 @@ class ModuleRefreshCommand extends ContainerAwareCommand
{
try {
$moduleManagement = new ModuleManagement;
$moduleManagement->updateModules();
$moduleManagement->updateModules($this->getContainer());
} catch (InvalidModuleException $ime) {
throw new \RuntimeException(
sprintf('One or more modules could not be refreshed : %s', $ime->getErrorsAsString("\n"))
);
} catch (\Exception $e) {
throw new \RuntimeException(sprintf('Refresh modules list fail with Exception : [%d] %s', $e->getCode(), $e->getMessage()));
throw new \RuntimeException(
sprintf('Refresh modules list fail with Exception : [%d] %s', $e->getCode(), $e->getMessage())
);
}
if (method_exists($output, 'renderBlock')) {
$output->renderBlock([
$output->renderBlock(
[
'',
'Modules list successfully refreshed',
''

View File

@@ -40,5 +40,4 @@ class TheliaConsoleOutput extends ConsoleOutput
$this->writeln($output);
}
}

View File

@@ -11,16 +11,18 @@
/*************************************************************************************/
namespace Thelia\Command;
use Propel\Runtime\Connection\ConnectionWrapper;
use \Thelia\Model\Map\ProductTableMap;
use Propel\Runtime\Propel;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Thelia\Install\Database;
/**
* Class ReloadDatabasesCommand
* @package Thelia\Command
* @author Manuel Raynaud <mraynaud@openstudio.fr>
* @author Manuel Raynaud <manu@raynaud.io>
*/
class ReloadDatabaseCommand extends BaseModuleGenerate
{
@@ -40,7 +42,8 @@ class ReloadDatabaseCommand extends BaseModuleGenerate
public function execute(InputInterface $input, OutputInterface $output)
{
$connection = Propel::getConnection(\Thelia\Model\Map\ProductTableMap::DATABASE_NAME);
/** @var ConnectionWrapper $connection */
$connection = Propel::getConnection(ProductTableMap::DATABASE_NAME);
$connection = $connection->getWrappedConnection();
$tables = $connection->query("SHOW TABLES");

View File

@@ -0,0 +1,47 @@
<?php
/*************************************************************************************/
/* This file is part of the Thelia package. */
/* */
/* Copyright (c) OpenStudio */
/* email : dev@thelia.net */
/* web : http://www.thelia.net */
/* */
/* For the full copyright and license information, please view the LICENSE.txt */
/* file that was distributed with this source code. */
/*************************************************************************************/
namespace Thelia\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Thelia\Core\Event\Sale\SaleActiveStatusCheckEvent;
use Thelia\Core\Event\TheliaEvents;
/**
* Class SaleCheckActivationCommand
* @package Thelia\Command
* @author manuel raynaud <manu@raynaud.io>
*/
class SaleCheckActivationCommand extends ContainerAwareCommand
{
public function configure()
{
$this
->setName("sale:check-activation")
->setDescription("check the activation and deactivation dates of sales, and perform the required action depending on the current date.");
}
public function execute(InputInterface $input, OutputInterface $output)
{
try {
$this->getDispatcher()->dispatch(
TheliaEvents::CHECK_SALE_ACTIVATION_EVENT,
new SaleActiveStatusCheckEvent()
);
$output->writeln("<info>Sale verification processed successfully</info>");
} catch (\Exception $ex) {
$output->writeln(sprintf("<error>Error : %s</error>", $ex->getMessage()));
}
}
}

View File

@@ -16,6 +16,9 @@ use Thelia\Module\BaseModule;
class %%CLASSNAME%% extends BaseModule
{
/** @var string */
const DOMAIN_NAME = '%%DOMAINNAME%%';
/*
* You may now override BaseModuleInterface methods, such as:
* install, destroy, preActivation, postActivation, preDeactivation, postDeactivation

View File

@@ -0,0 +1,55 @@
# %%MODULENAMETITLE%%
Add a short description here. You can also add a screenshot if needed.
## Installation
### Manually
* Copy the module into ```<thelia_root>/local/modules/``` directory and be sure that the name of the module is %%MODULENAME%%.
* Activate it in your thelia administration panel
### Composer
Add it in your main thelia composer.json file
```
composer require your-vendor/%%COMPOSERNAME%%-module:~1.0
```
## Usage
Explain here how to use your module, how to configure it, etc.
## Hook
If your module use one or more hook, fill this part. Explain which hooks are used.
## Loop
If your module declare one or more loop, describe them here like this :
[loop name]
### Input arguments
|Argument |Description |
|--- |--- |
|**arg1** | describe arg1 with an exemple. |
|**arg2** | describe arg2 with an exemple. |
### Output arguments
|Variable |Description |
|--- |--- |
|$VAR1 | describe $VAR1 variable |
|$VAR2 | describe $VAR2 variable |
### Exemple
Add a complete exemple of your loop
## Other ?
If you have other think to put, feel free to complete your readme as you want.

View File

@@ -0,0 +1,11 @@
{
"name": "your-vendor/%%COMPOSERNAME%%-module",
"license": "LGPL-3.0+",
"type": "thelia-module",
"require": {
"thelia/installer": "~1.1"
},
"extra": {
"installer-name": "%%MODULENAME%%"
}
}

View File

@@ -6,19 +6,19 @@
<loops>
<!-- sample definition
<loop name="MySuperLoop" class="MyModule\Loop\MySuperLoop" />
<loop name="MySuperLoop" class="%%NAMESPACE%%\Loop\MySuperLoop" />
-->
</loops>
<forms>
<!--
<form name="MyFormName" class="MyModule\Form\MySuperForm" />
<form name="MyFormName" class="%%NAMESPACE%%\Form\MySuperForm" />
-->
</forms>
<commands>
<!--
<command class="MyModule\Command\MySuperCommand" />
<command class="%%NAMESPACE%%\Command\MySuperCommand" />
-->
</commands>
@@ -28,6 +28,14 @@
</services>
-->
<!--
<hooks>
<hook id="%%NAMESPACE_LOWER%%.hook" class="%%NAMESPACE%%\Hook\MySuperHook">
<tag name="hook.event_listener" event="main.body.bottom" type="front|back|pdf|email" method="onMainBodyBottom" />
</hook>
</hooks>
-->
<!--
<exports>

View File

@@ -0,0 +1,50 @@
<?xml version="1.0" encoding="UTF-8" ?>
<config xmlns="http://thelia.net/schema/dic/config"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://thelia.net/schema/dic/config http://thelia.net/schema/dic/config/thelia-1.0.xsd">
<loops>
<!-- sample definition
<loop name="MySuperLoop" class="%%NAMESPACE%%\Loop\MySuperLoop" />
-->
</loops>
<forms>
<!--
<form name="MyFormName" class="%%NAMESPACE%%\Form\MySuperForm" />
-->
</forms>
<commands>
<!--
<command class="%%NAMESPACE%%\Command\MySuperCommand" />
-->
</commands>
<!--
<services>
</services>
-->
<!--
<hooks>
<hook id="%%NAMESPACE_LOWER%%.hook" class="%%NAMESPACE%%\Hook\MySuperHook">
<tag name="hook.event_listener" event="main.body.bottom" type="front|back|pdf|email" method="onMainBodyBottom" />
</hook>
</hooks>
-->
<!--
<exports>
</exports>
-->
<!--
<imports>
</imports>
-->
</config>

View File

@@ -0,0 +1,50 @@
<?xml version="1.0" encoding="UTF-8" ?>
<config xmlns="http://thelia.net/schema/dic/config"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://thelia.net/schema/dic/config http://thelia.net/schema/dic/config/thelia-1.0.xsd">
<loops>
<!-- sample definition
<loop name="MySuperLoop" class="%%NAMESPACE%%\Loop\MySuperLoop" />
-->
</loops>
<forms>
<!--
<form name="MyFormName" class="%%NAMESPACE%%\Form\MySuperForm" />
-->
</forms>
<commands>
<!--
<command class="%%NAMESPACE%%\Command\MySuperCommand" />
-->
</commands>
<!--
<services>
</services>
-->
<!--
<hooks>
<hook id="%%NAMESPACE_LOWER%%.hook" class="%%NAMESPACE%%\Hook\MySuperHook">
<tag name="hook.event_listener" event="main.body.bottom" type="front|back|pdf|email" method="onMainBodyBottom" />
</hook>
</hooks>
-->
<!--
<exports>
</exports>
-->
<!--
<imports>
</imports>
-->
</config>

View File

@@ -0,0 +1,50 @@
<?xml version="1.0" encoding="UTF-8" ?>
<config xmlns="http://thelia.net/schema/dic/config"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://thelia.net/schema/dic/config http://thelia.net/schema/dic/config/thelia-1.0.xsd">
<loops>
<!-- sample definition
<loop name="MySuperLoop" class="%%NAMESPACE%%\Loop\MySuperLoop" />
-->
</loops>
<forms>
<!--
<form name="MyFormName" class="%%NAMESPACE%%\Form\MySuperForm" />
-->
</forms>
<commands>
<!--
<command class="%%NAMESPACE%%\Command\MySuperCommand" />
-->
</commands>
<!--
<services>
</services>
-->
<!--
<hooks>
<hook id="%%NAMESPACE_LOWER%%.hook" class="%%NAMESPACE%%\Hook\MySuperHook">
<tag name="hook.event_listener" event="main.body.bottom" type="front|back|pdf|email" method="onMainBodyBottom" />
</hook>
</hooks>
-->
<!--
<exports>
</exports>
-->
<!--
<imports>
</imports>
-->
</config>

View File

@@ -1,18 +1,43 @@
<?xml version="1.0" encoding="UTF-8"?>
<module>
<module xmlns="http://thelia.net/schema/dic/module"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://thelia.net/schema/dic/module http://thelia.net/schema/dic/module/module-2_2.xsd">
<fullnamespace>%%NAMESPACE%%\%%CLASSNAME%%</fullnamespace>
<descriptive locale="en_US">
<title>Automatically generated module - please update module.xml file</title>
<!--
<subtitle></subtitle>
<description></description>
<postscriptum></postscriptum>
-->
</descriptive>
<descriptive locale="fr_FR">
<title>Module généré autromatiquement - editez le fichier module.xml</title>
<title>Module généré automatiquement - éditez le fichier module.xml</title>
</descriptive>
<!-- <logo></logo> -->
<!--<images-folder>images</images-folder>-->
<languages>
<language>en_US</language>
<language>fr_FR</language>
</languages>
<version></version>
<author>
<name></name>
<email></email>
</author>
<authors>
<author>
<name></name>
<email></email>
</author>
</authors>
<type>classic</type>
<thelia>2.0.0</thelia>
<!--
module dependencies
<required>
<module version="&gt;=0.1">Front</module>
<module version="~1.0">HookCart</module>
<module version="&gt;0.2">HookSearch</module>
</required>
-->
<thelia>2.2.0</thelia>
<stability>other</stability>
<mandatory>0</mandatory>
<hidden>0</hidden>
</module>

View File

@@ -1,7 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<database defaultIdMethod="native" name="thelia" namespace="%%NAMESPACE%%\Model">
<database defaultIdMethod="native" name="thelia"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="%%XSD_LOCATION%%" >
<!--
See propel documentation on http://propelorm.org for all information about schema file
<table name="product_rel" namespace="%%NAMESPACE%%\Model">
<column autoIncrement="true" name="id" primaryKey="true" required="true" type="INTEGER" />
<column defaultValue="0" name="visible" required="true" type="TINYINT" />
<column defaultValue="0" name="position" required="true" type="INTEGER" />
<column name="title" size="255" type="VARCHAR" />
<column name="description" type="CLOB" />
<column name="chapo" type="LONGVARCHAR" />
<column name="postscriptum" type="LONGVARCHAR" />
<foreign-key foreignTable="product" name="fk_product_id" onDelete="CASCADE" onUpdate="RESTRICT">
<reference foreign="id" local="product_id" />
</foreign-key>
<behavior name="timestampable" />
<behavior name="i18n">
<parameter name="i18n_columns" value="title, description, chapo, postscriptum" />
</behavior>
<behavior name="versionable">
<parameter name="log_created_at" value="true" />
<parameter name="log_created_by" value="true" />
</behavior>
</table>
-->
<external-schema filename="local/config/schema.xml" referenceOnly="true" />
</database>