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

@@ -14,16 +14,26 @@ namespace Thelia\Module;
use Propel\Runtime\Connection\ConnectionInterface;
use Propel\Runtime\Propel;
use Symfony\Component\DependencyInjection\ContainerAware;
use Symfony\Component\DependencyInjection\ContainerAwareTrait;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\HttpFoundation\Request;
use Thelia\Core\Event\Hook\HookCreateAllEvent;
use Thelia\Core\Event\Hook\HookUpdateEvent;
use Thelia\Core\Event\TheliaEvents;
use Thelia\Core\HttpFoundation\Session\Session;
use Thelia\Core\Template\TemplateDefinition;
use Thelia\Core\Translation\Translator;
use Thelia\Exception\ModuleException;
use Thelia\Log\Tlog;
use Thelia\Model\Cart;
use Thelia\Model\Country;
use Thelia\Model\HookQuery;
use Thelia\Model\Lang;
use Thelia\Model\LangQuery;
use Thelia\Model\Map\ModuleImageTableMap;
use Thelia\Model\Map\ModuleTableMap;
use Thelia\Model\Module;
use Thelia\Model\ModuleConfigQuery;
use Thelia\Model\ModuleI18n;
use Thelia\Model\ModuleI18nQuery;
use Thelia\Model\ModuleImage;
@@ -32,20 +42,33 @@ use Thelia\Model\Order;
use Thelia\TaxEngine\TaxEngine;
use Thelia\Tools\Image;
class BaseModule extends ContainerAware implements BaseModuleInterface
class BaseModule implements BaseModuleInterface
{
const CLASSIC_MODULE_TYPE = 1;
const DELIVERY_MODULE_TYPE = 2;
const PAYMENT_MODULE_TYPE = 3;
use ContainerAwareTrait;
const IS_ACTIVATED = 1;
const CLASSIC_MODULE_TYPE = 1;
const DELIVERY_MODULE_TYPE = 2;
const PAYMENT_MODULE_TYPE = 3;
const MODULE_CATEGORIES = 'classic,delivery,payment,marketplace,price,accounting,seo,administration,statistic';
const IS_ACTIVATED = 1;
const IS_NOT_ACTIVATED = 0;
const IS_MANDATORY = 1;
const IS_NOT_MANDATORY = 0;
const IS_HIDDEN = 1;
const IS_NOT_HIDDEN = 0;
protected $reflected;
protected $dispatcher = null;
protected $request = null;
// Do no use this attribute directly, use getModuleModel() instead.
private $moduleModel = null;
public function activate($moduleModel = null)
{
if (null === $moduleModel) {
@@ -56,6 +79,7 @@ class BaseModule extends ContainerAware implements BaseModuleInterface
$con = Propel::getWriteConnection(ModuleTableMap::DATABASE_NAME);
$con->beginTransaction();
try {
$this->initializeCoreI18n();
if ($this->preActivation($con)) {
$moduleModel->setActivate(self::IS_ACTIVATED);
$moduleModel->save($con);
@@ -66,6 +90,8 @@ class BaseModule extends ContainerAware implements BaseModuleInterface
$con->rollBack();
throw $e;
}
$this->registerHooks();
}
}
@@ -89,7 +115,6 @@ class BaseModule extends ContainerAware implements BaseModuleInterface
$con->rollBack();
throw $e;
}
}
}
@@ -126,7 +151,7 @@ class BaseModule extends ContainerAware implements BaseModuleInterface
{
if ($this->hasRequest() === false) {
// Try to get request from container.
$this->setRequest($this->getContainer()->get('request'));
$this->setRequest($this->getContainer()->get('request_stack')->getCurrentRequest());
}
if ($this->hasRequest() === false) {
@@ -146,8 +171,17 @@ class BaseModule extends ContainerAware implements BaseModuleInterface
$this->dispatcher = $dispatcher;
}
/**
* @return EventDispatcherInterface
* @throws \RuntimeException
*/
public function getDispatcher()
{
if ($this->hasDispatcher() === false) {
// Try to get dispatcher from container.
$this->setDispatcher($this->getContainer()->get('event_dispatcher'));
}
if ($this->hasDispatcher() === false) {
throw new \RuntimeException("Sorry, the dispatcher is not available in this context");
}
@@ -156,16 +190,16 @@ class BaseModule extends ContainerAware implements BaseModuleInterface
}
/**
* Sets a module titles for various languages
*
* @param Module $module the module.
* @param array $titles an associative array of locale => title_string
* @inheritdoc
*/
public function setTitle(Module $module, $titles)
{
if (is_array($titles)) {
foreach ($titles as $locale => $title) {
$moduleI18n = ModuleI18nQuery::create()->filterById($module->getId())->filterByLocale($locale)->findOne();
$moduleI18n = ModuleI18nQuery::create()
->filterById($module->getId())->filterByLocale($locale)
->findOne();
if (null === $moduleI18n) {
$moduleI18n = new ModuleI18n();
$moduleI18n
@@ -183,17 +217,25 @@ class BaseModule extends ContainerAware implements BaseModuleInterface
}
/**
* Ensure the proper deployment of the module's images.
*
* TODO : this method does not take care of internationalization. This is a bug.
*
* @param Module $module the module
* @param string $folderPath the image folder path
* @param ConnectionInterface $con
*
* @throws \Thelia\Exception\ModuleException
* @throws \Exception
* @throws \UnexpectedValueException
* @inheritdoc
*/
public static function getConfigValue($variableName, $defaultValue = null, $valueLocale = null)
{
return ModuleConfigQuery::create()
->getConfigValue(self::getModuleId(), $variableName, $defaultValue, $valueLocale);
}
/**
* @inheritdoc
*/
public static function setConfigValue($variableName, $variableValue, $valueLocale = null, $createIfNotExists = true)
{
ModuleConfigQuery::create()
->setConfigValue(self::getModuleId(), $variableName, $variableValue, $valueLocale, $createIfNotExists);
}
/**
* @inheritdoc
*/
public function deployImageFolder(Module $module, $folderPath, ConnectionInterface $con = null)
{
@@ -214,13 +256,11 @@ class BaseModule extends ContainerAware implements BaseModuleInterface
foreach ($directoryBrowser as $directoryContent) {
/* is it a file ? */
if ($directoryContent->isFile()) {
$fileName = $directoryContent->getFilename();
$filePath = $directoryContent->getPathName();
/* is it a picture ? */
if ( Image::isImage($filePath) ) {
if (Image::isImage($filePath)) {
$con->beginTransaction();
$image = new ModuleImage();
@@ -228,12 +268,18 @@ class BaseModule extends ContainerAware implements BaseModuleInterface
$image->setPosition($imagePosition);
$image->save($con);
$imageDirectory = sprintf("%s/../../../../local/media/images/module", __DIR__);
$imageDirectory = sprintf("%s/media/images/module", THELIA_LOCAL_DIR);
$imageFileName = sprintf("%s-%d-%s", $module->getCode(), $image->getId(), $fileName);
$increment = 0;
while (file_exists($imageDirectory . '/' . $imageFileName)) {
$imageFileName = sprintf("%s-%d-%d-%s", $module->getCode(), $image->getId(), $increment, $fileName);
$imageFileName = sprintf(
"%s-%d-%d-%s",
$module->getCode(),
$image->getId(),
$increment,
$fileName
);
$increment++;
}
@@ -242,13 +288,19 @@ class BaseModule extends ContainerAware implements BaseModuleInterface
if (! is_dir($imageDirectory)) {
if (! @mkdir($imageDirectory, 0777, true)) {
$con->rollBack();
throw new ModuleException(sprintf("Cannot create directory : %s", $imageDirectory), ModuleException::CODE_NOT_FOUND);
throw new ModuleException(
sprintf("Cannot create directory : %s", $imageDirectory),
ModuleException::CODE_NOT_FOUND
);
}
}
if (! @copy($filePath, $imagePath)) {
$con->rollBack();
throw new ModuleException(sprintf("Cannot copy file : %s to : %s", $filePath, $imagePath), ModuleException::CODE_NOT_FOUND);
throw new ModuleException(
sprintf("Cannot copy file : %s to : %s", $filePath, $imagePath),
ModuleException::CODE_NOT_FOUND
);
}
$image->setFile($imageFileName);
@@ -262,27 +314,70 @@ class BaseModule extends ContainerAware implements BaseModuleInterface
}
/**
* @return Module
* @throws \Thelia\Exception\ModuleException
* @inheritdoc
*/
public function getModuleModel()
{
$moduleModel = ModuleQuery::create()->findOneByCode($this->getCode());
if (null === $this->moduleModel) {
$this->moduleModel = ModuleQuery::create()->findOneByCode($this->getCode());
if (null === $moduleModel) {
throw new ModuleException(sprintf("Module Code `%s` not found", $this->getCode()), ModuleException::CODE_NOT_FOUND);
if (null === $this->moduleModel) {
throw new ModuleException(
sprintf("Module Code `%s` not found", $this->getCode()),
ModuleException::CODE_NOT_FOUND
);
}
}
return $moduleModel;
return $this->moduleModel;
}
public function getCode()
/**
* Module A may use static method from module B, thus we have to cache
* a couple (module code => module id).
*
* @return int The module id, in a static way, with a cache
*/
private static $moduleIds = [];
/**
* @inheritdoc
*/
public static function getModuleId()
{
if (null === $this->reflected) {
$this->reflected = new \ReflectionObject($this);
$code = self::getModuleCode();
if (! isset(self::$moduleIds[$code])) {
if (null === $module = ModuleQuery::create()->findOneByCode($code)) {
throw new ModuleException(
sprintf("Module Code `%s` not found", $code),
ModuleException::CODE_NOT_FOUND
);
}
self::$moduleIds[$code] = $module->getId();
}
return basename(dirname($this->reflected->getFileName()));
return self::$moduleIds[$code];
}
/**
* @inheritdoc
*/
public static function getModuleCode()
{
$fullClassName = explode('\\', get_called_class());
return end($fullClassName);
}
/*
* The module code
*/
public function getCode()
{
return self::getModuleCode();
}
/**
@@ -329,7 +424,7 @@ class BaseModule extends ContainerAware implements BaseModuleInterface
$session = $this->getRequest()->getSession();
/** @var Cart $cart */
$cart = $session->getCart();
$cart = $session->getSessionCart($this->getDispatcher());
/** @var Order $order */
$order = $session->getOrder();
@@ -340,49 +435,23 @@ class BaseModule extends ContainerAware implements BaseModuleInterface
/** @var Country $country */
$country = $taxEngine->getDeliveryCountry();
$amount = $with_tax ? $cart->getTaxedAmount($country, $with_discount) : $cart->getTotalAmount($with_discount);
$state = $taxEngine->getDeliveryState();
$amount = $with_tax ? $cart->getTaxedAmount($country, $with_discount, $state) : $cart->getTotalAmount($with_discount);
if ($with_postage) {
$amount += $order->getPostage();
if ($with_tax) {
$amount += $order->getPostage();
} else {
$amount += $order->getPostage() - $order->getPostageTax();
}
}
return $amount;
}
/**
*
* This method adds new compilers to Thelia container
*
* You must return an array. This array can contain :
* - arrays
* - one or many instance(s) of \Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface
*
* in the first case, your array must contains 2 indexes. The first is the compiler instance and the second the compilerPass type.
* Example :
* return array(
* array(
* new \MyModule\DependencyInjection\Compiler\MySuperCompilerPass(),
* \Symfony\Component\DependencyInjection\Compiler\PassConfig::TYPE_BEFORE_OPTIMIZATION
* )
* );
*
* In the seconde case, just an instance of CompilerPassInterface.
* Example :
* return array (
* new \MyModule\DependencyInjection\Compiler\MySuperCompilerPass()
* );
*
* But you can combine both behaviors
* Example :
*
* return array(
* new \MyModule\DependencyInjection\Compiler\MySuperCompilerPass(),
* array(
* new \MyModule\DependencyInjection\Compiler\MyOtherSuperCompilerPass(),
* Symfony\Component\DependencyInjection\Compiler\PassConfig::TYPE_BEFORE_OPTIMIZATION
* )
* );
*
* @inheritdoc
*/
public static function getCompilers()
{
@@ -390,10 +459,7 @@ class BaseModule extends ContainerAware implements BaseModuleInterface
}
/**
* This method is called when the plugin is installed for the first time, using
* zip upload method.
*
* @param ConnectionInterface $con
* @inheritdoc
*/
public function install(ConnectionInterface $con = null)
{
@@ -401,11 +467,15 @@ class BaseModule extends ContainerAware implements BaseModuleInterface
}
/**
* This method is called before the module activation, and may prevent it by returning false.
*
* @param ConnectionInterface $con
*
* @return bool true to continue module activation, false to prevent it.
* @inheritdoc
*/
public function update($currentVersion, $newVersion, ConnectionInterface $con = null)
{
// Override this method to do something useful.
}
/**
* @inheritdoc
*/
public function preActivation(ConnectionInterface $con = null)
{
@@ -414,9 +484,7 @@ class BaseModule extends ContainerAware implements BaseModuleInterface
}
/**
* This method is called just after the module was successfully activated.
*
* @param ConnectionInterface $con
* @inheritdoc
*/
public function postActivation(ConnectionInterface $con = null)
{
@@ -424,10 +492,7 @@ class BaseModule extends ContainerAware implements BaseModuleInterface
}
/**
* This method is called before the module de-activation, and may prevent it by returning false.
*
* @param ConnectionInterface $con
* @return bool true to continue module de-activation, false to prevent it.
* @inheritdoc
*/
public function preDeactivation(ConnectionInterface $con = null)
{
@@ -435,20 +500,274 @@ class BaseModule extends ContainerAware implements BaseModuleInterface
return true;
}
/**
* @inheritdoc
*/
public function postDeactivation(ConnectionInterface $con = null)
{
// Override this method to do something useful.
}
/**
* This method is called just before the deletion of the module, giving the module an opportunity
* to delete its data.
*
* @param ConnectionInterface $con
* @param bool $deleteModuleData if true, the module should remove all its data from the system.
* @inheritdoc
*/
public function destroy(ConnectionInterface $con = null, $deleteModuleData = false)
{
// Override this method to do something useful.
}
/**
* @inheritdoc
*/
public function getHooks()
{
return array();
}
/**
* @inheritdoc
*/
public function registerHooks()
{
$moduleHooks = $this->getHooks();
if (is_array($moduleHooks) && !empty($moduleHooks)) {
$allowedTypes = (array) TemplateDefinition::getStandardTemplatesSubdirsIterator();
$defaultLang = Lang::getDefaultLanguage();
$defaultLocale = $defaultLang->getLocale();
/**
* @var EventDispatcherInterface $dispatcher
*/
$dispatcher = $this->container->get("event_dispatcher");
foreach ($moduleHooks as $hook) {
$isValid = is_array($hook) &&
isset($hook["type"]) &&
array_key_exists($hook["type"], $allowedTypes) &&
isset($hook["code"]) &&
is_string($hook["code"]) &&
!empty($hook["code"])
;
if (!$isValid) {
Tlog::getInstance()->notice("The module ".$this->getCode()." tried to register an invalid hook");
continue;
}
/**
* Create or update hook db entry.
*
* @var \Thelia\Model\Hook $hookModel
*/
list($hookModel, $updateData) = $this->createOrUpdateHook($hook, $dispatcher, $defaultLocale);
/**
* Update translations
*/
$event = new HookUpdateEvent($hookModel->getId());
foreach ($updateData as $locale => $data) {
$event
->setCode($hookModel->getCode())
->setNative($hookModel->getNative())
->setByModule($hookModel->getByModule())
->setActive($hookModel->getActivate())
->setBlock($hookModel->getBlock())
->setNative($hookModel->getNative())
->setType($hookModel->getType())
->setLocale($locale)
->setChapo($data["chapo"])
->setTitle($data["title"])
->setDescription($data["description"])
;
$dispatcher->dispatch(TheliaEvents::HOOK_UPDATE, $event);
}
}
}
}
protected function createOrUpdateHook(array $hook, EventDispatcherInterface $dispatcher, $defaultLocale)
{
$hookModel = HookQuery::create()->filterByCode($hook["code"])->findOne();
if ($hookModel === null) {
$event = new HookCreateAllEvent();
} else {
$event = new HookUpdateEvent($hookModel->getId());
}
/**
* Get used I18n variables
*/
$locale = $defaultLocale;
list($titles, $descriptions, $chapos) = $this->getHookI18nInfo($hook, $defaultLocale);
/**
* If the default locale exists
* extract it to save it in create action
*
* otherwise take the first
*/
if (isset($titles[$defaultLocale])) {
$title = $titles[$defaultLocale];
unset($titles[$defaultLocale]);
} else {
reset($titles);
$locale = key($titles);
$title = array_shift($titles);
}
$description = $this->arrayKeyPop($locale, $descriptions);
$chapo = $this->arrayKeyPop($locale, $chapos);
/**
* Set data
*/
$event
->setBlock(isset($hook["block"]) && (bool) $hook["block"])
->setLocale($locale)
->setTitle($title)
->setDescription($description)
->setChapo($chapo)
->setType($hook["type"])
->setCode($hook["code"])
->setNative(false)
->setByModule(isset($hook["module"]) && (bool) $hook["module"])
->setActive(isset($hook["active"]) && (bool) $hook["active"])
;
/**
* Dispatch the event
*/
$dispatcher->dispatch(
(
$hookModel === null ?
TheliaEvents::HOOK_CREATE_ALL :
TheliaEvents::HOOK_UPDATE
),
$event
);
return [
$event->getHook(),
$this->formatHookDataForI18n($titles, $descriptions, $chapos)
];
}
protected function formatHookDataForI18n(array $titles, array $descriptions, array $chapos)
{
$locales = array_merge(
array_keys($titles),
array_keys($descriptions),
array_keys($chapos)
);
$locales = array_unique($locales);
$data = array();
foreach ($locales as $locale) {
$data[$locale] = [
'title' => !isset($titles[$locale]) ? null : $titles[$locale],
'description' => !isset($descriptions[$locale]) ? null: $descriptions[$locale],
'chapo' => !isset($chapos[$locale]) ? null : $chapos[$locale]
];
}
return $data;
}
protected function getHookI18nInfo(array $hook, $defaultLocale)
{
$titles = array();
$descriptions = array();
$chapos = array();
/**
* Get the defined titles
*/
if (isset($hook["title"])) {
$titles = $this->extractI18nValues($hook["title"], $defaultLocale);
}
/**
* Then the defined descriptions
*/
if (isset($hook["description"])) {
$descriptions = $this->extractI18nValues($hook["description"], $defaultLocale);
}
/**
* Then the short descriptions
*/
if (isset($hook["chapo"])) {
$chapos = $this->extractI18nValues($hook["chapo"], $defaultLocale);
}
return [$titles, $descriptions, $chapos];
}
protected function extractI18nValues($data, $defaultLocale)
{
$returnData = array();
if (is_array($data)) {
foreach ($data as $key => $value) {
if (!is_string($key)) {
continue;
}
$returnData[$key] = $value;
}
} elseif (is_scalar($data)) {
$returnData[$defaultLocale] = $data;
}
return $returnData;
}
protected function arrayKeyPop($key, array &$array)
{
$value = null;
if (array_key_exists($key, $array)) {
$value = $array[$key];
unset($array[$key]);
}
return $value;
}
/**
* Add core translations of the module to use in `preActivation` and `postActivation`
* when the module is not yest activated and translations are not available
*/
private function initializeCoreI18n()
{
if ($this->hasContainer()) {
/** @var Translator $translator */
$translator = $this->container->get('thelia.translator');
if (null !== $translator) {
$i18nPath = sprintf('%s%s/I18n/', THELIA_MODULE_DIR, $this->getCode());
$languages = LangQuery::create()->find();
foreach ($languages as $language) {
$locale = $language->getLocale();
$i18nFile = sprintf('%s%s.php', $i18nPath, $locale);
if (is_file($i18nFile) && is_readable($i18nFile)) {
$translator->addResource('php', $i18nFile, $locale, strtolower(self::getModuleCode()));
}
}
}
}
}
}