Initial commit
This commit is contained in:
56
core/lib/Thelia/Module/AbstractAdminResourcesCompiler.php
Normal file
56
core/lib/Thelia/Module/AbstractAdminResourcesCompiler.php
Normal file
@@ -0,0 +1,56 @@
|
||||
<?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\Module;
|
||||
|
||||
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
||||
|
||||
/**
|
||||
* Class AbastractAdminResourcesCompiler
|
||||
* @package Thelia\Module
|
||||
* @since 2.3
|
||||
* @author Penalver Antony <apenalver@openstudio.fr>
|
||||
*/
|
||||
abstract class AbstractAdminResourcesCompiler implements CompilerPassInterface
|
||||
{
|
||||
/**
|
||||
* @return Array of resources
|
||||
* Exemple :
|
||||
* [
|
||||
* "ADDRESS" => "admin.address",
|
||||
* ...
|
||||
* ]
|
||||
*/
|
||||
abstract public function getResources();
|
||||
|
||||
/**
|
||||
* @return string ModuleCode
|
||||
*/
|
||||
abstract public function getModuleCode();
|
||||
|
||||
/**
|
||||
* Allow module to add resources in AdminResources Service
|
||||
* @param \Symfony\Component\DependencyInjection\ContainerBuilder $container
|
||||
*/
|
||||
public function process(\Symfony\Component\DependencyInjection\ContainerBuilder $container)
|
||||
{
|
||||
if (!$container->hasDefinition("thelia.admin.resources")) {
|
||||
return;
|
||||
}
|
||||
|
||||
/** @var \Symfony\Component\DependencyInjection\Definition $adminResources */
|
||||
$adminResources = $container->getDefinition("thelia.admin.resources");
|
||||
|
||||
$adminResources->addMethodCall("addModuleResources", [$this->getResources(), $this->getModuleCode()]);
|
||||
}
|
||||
}
|
||||
57
core/lib/Thelia/Module/AbstractDeliveryModule.php
Normal file
57
core/lib/Thelia/Module/AbstractDeliveryModule.php
Normal file
@@ -0,0 +1,57 @@
|
||||
<?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\Module;
|
||||
|
||||
use Thelia\Model\Area;
|
||||
use Thelia\Model\AreaDeliveryModuleQuery;
|
||||
use Thelia\Model\Country;
|
||||
use Thelia\Model\State;
|
||||
|
||||
abstract class AbstractDeliveryModule extends BaseModule implements DeliveryModuleInterface
|
||||
{
|
||||
// This class is the base class for delivery modules
|
||||
// It may contains common methods in the future.
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function handleVirtualProductDelivery()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the first area that matches the given country for the given module
|
||||
* @param Country $country
|
||||
* @param BaseModule $module
|
||||
* @return Area|null
|
||||
*/
|
||||
public function getAreaForCountry(Country $country)
|
||||
{
|
||||
$area = null;
|
||||
|
||||
if (null !== $areaDeliveryModule = AreaDeliveryModuleQuery::create()->findByCountryAndModule(
|
||||
$country,
|
||||
$this->getModuleModel()
|
||||
)) {
|
||||
$area = $areaDeliveryModule->getArea();
|
||||
}
|
||||
|
||||
return $area;
|
||||
}
|
||||
|
||||
public function getDeliveryMode()
|
||||
{
|
||||
return "delivery";
|
||||
}
|
||||
}
|
||||
58
core/lib/Thelia/Module/AbstractDeliveryModuleWithState.php
Normal file
58
core/lib/Thelia/Module/AbstractDeliveryModuleWithState.php
Normal file
@@ -0,0 +1,58 @@
|
||||
<?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\Module;
|
||||
|
||||
use Thelia\Model\Area;
|
||||
use Thelia\Model\AreaDeliveryModuleQuery;
|
||||
use Thelia\Model\Country;
|
||||
use Thelia\Model\State;
|
||||
|
||||
abstract class AbstractDeliveryModuleWithState extends BaseModule implements DeliveryModuleWithStateInterface
|
||||
{
|
||||
// This class is the base class for delivery modules
|
||||
// It may contains common methods in the future.
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function handleVirtualProductDelivery()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the first area that matches the given country for the given module
|
||||
* @param Country $country
|
||||
* @param State $state
|
||||
* @return Area|null
|
||||
*/
|
||||
public function getAreaForCountry(Country $country, State $state = null)
|
||||
{
|
||||
$area = null;
|
||||
|
||||
if (null !== $areaDeliveryModule = AreaDeliveryModuleQuery::create()->findByCountryAndModule(
|
||||
$country,
|
||||
$this->getModuleModel(),
|
||||
$state
|
||||
)) {
|
||||
$area = $areaDeliveryModule->getArea();
|
||||
}
|
||||
|
||||
return $area;
|
||||
}
|
||||
|
||||
public function getDeliveryMode()
|
||||
{
|
||||
return "delivery";
|
||||
}
|
||||
}
|
||||
117
core/lib/Thelia/Module/AbstractPaymentModule.php
Normal file
117
core/lib/Thelia/Module/AbstractPaymentModule.php
Normal file
@@ -0,0 +1,117 @@
|
||||
<?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\Module;
|
||||
|
||||
use Symfony\Component\Routing\Router;
|
||||
use Thelia\Core\HttpFoundation\Response;
|
||||
use Thelia\Core\Template\ParserInterface;
|
||||
use Thelia\Core\Template\TemplateHelperInterface;
|
||||
use Thelia\Model\Country;
|
||||
use Thelia\Model\Order;
|
||||
use Thelia\Model\State;
|
||||
use Thelia\Tools\URL;
|
||||
|
||||
abstract class AbstractPaymentModule extends BaseModule implements PaymentModuleInterface
|
||||
{
|
||||
/**
|
||||
* Render the payment gateway template. The module should provide the gateway URL and the form fields names and values.
|
||||
*
|
||||
* @param Order $order the order
|
||||
* @param string $gateway_url the payment gateway URL
|
||||
* @param array $form_data an associative array of form data, that will be rendered as hiddent fields
|
||||
*
|
||||
* @return Response the HTTP response.
|
||||
*/
|
||||
public function generateGatewayFormResponse($order, $gateway_url, $form_data)
|
||||
{
|
||||
/** @var ParserInterface $parser */
|
||||
$parser = $this->getContainer()->get("thelia.parser");
|
||||
|
||||
$parser->setTemplateDefinition(
|
||||
$parser->getTemplateHelper()->getActiveFrontTemplate()
|
||||
);
|
||||
|
||||
$renderedTemplate = $parser->render(
|
||||
"order-payment-gateway.html",
|
||||
array(
|
||||
"order_id" => $order->getId(),
|
||||
"cart_count" => $this->getRequest()->getSession()->getSessionCart($this->getDispatcher())->getCartItems()->count(),
|
||||
"gateway_url" => $gateway_url,
|
||||
"payment_form_data" => $form_data
|
||||
)
|
||||
);
|
||||
|
||||
return Response::create($renderedTemplate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the order payment success page URL
|
||||
*
|
||||
* @param int $order_id the order ID
|
||||
* @return string the order payment success page URL
|
||||
*/
|
||||
public function getPaymentSuccessPageUrl($order_id)
|
||||
{
|
||||
$frontOfficeRouter = $this->getContainer()->get('router.front');
|
||||
|
||||
return URL::getInstance()->absoluteUrl(
|
||||
$frontOfficeRouter->generate(
|
||||
"order.placed",
|
||||
array("order_id" => $order_id),
|
||||
Router::ABSOLUTE_URL
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Redirect the customer to the failure payment page. if $message is null, a generic message is displayed.
|
||||
*
|
||||
* @param int $order_id the order ID
|
||||
* @param string|null $message an error message.
|
||||
*
|
||||
* @return string the order payment failure page URL
|
||||
*/
|
||||
public function getPaymentFailurePageUrl($order_id, $message)
|
||||
{
|
||||
$frontOfficeRouter = $this->getContainer()->get('router.front');
|
||||
|
||||
return URL::getInstance()->absoluteUrl(
|
||||
$frontOfficeRouter->generate(
|
||||
"order.failed",
|
||||
array(
|
||||
"order_id" => $order_id,
|
||||
"message" => $message
|
||||
),
|
||||
Router::ABSOLUTE_URL
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inherited
|
||||
*/
|
||||
public function manageStockOnCreation()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getMinimumAmount()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getMaximumAmount()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
809
core/lib/Thelia/Module/BaseModule.php
Normal file
809
core/lib/Thelia/Module/BaseModule.php
Normal file
@@ -0,0 +1,809 @@
|
||||
<?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\Module;
|
||||
|
||||
use Propel\Runtime\Connection\ConnectionInterface;
|
||||
use Propel\Runtime\Propel;
|
||||
use Symfony\Component\DependencyInjection\ContainerAwareTrait;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\Filesystem\Filesystem;
|
||||
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\Thelia;
|
||||
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;
|
||||
use Thelia\Model\ModuleQuery;
|
||||
use Thelia\Model\Order;
|
||||
use Thelia\TaxEngine\TaxEngine;
|
||||
use Thelia\Tools\Image;
|
||||
|
||||
class BaseModule implements BaseModuleInterface
|
||||
{
|
||||
use ContainerAwareTrait;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* @param Module $moduleModel
|
||||
* @throws \Propel\Runtime\Exception\PropelException
|
||||
* @throws \Throwable
|
||||
*/
|
||||
public function activate($moduleModel = null)
|
||||
{
|
||||
if (null === $moduleModel) {
|
||||
$moduleModel = $this->getModuleModel();
|
||||
}
|
||||
|
||||
if ($moduleModel->getActivate() === self::IS_NOT_ACTIVATED) {
|
||||
$moduleModel->setActivate(self::IS_ACTIVATED);
|
||||
$moduleModel->save();
|
||||
|
||||
// Refresh propel cache to be sure that module's model is created
|
||||
// when the module's initialization methods will be called.
|
||||
|
||||
/** @var Thelia $theliaKernel */
|
||||
$theliaKernel = $this->container->get('kernel');
|
||||
|
||||
$theliaKernel->initializePropelService(true, $cacheRefresh);
|
||||
|
||||
$con = Propel::getWriteConnection(ModuleTableMap::DATABASE_NAME);
|
||||
$con->beginTransaction();
|
||||
try {
|
||||
$this->initializeCoreI18n();
|
||||
if ($this->preActivation($con)) {
|
||||
$this->postActivation($con);
|
||||
$con->commit();
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$con->rollBack();
|
||||
$moduleModel->setActivate(self::IS_NOT_ACTIVATED);
|
||||
$moduleModel->save();
|
||||
throw $e;
|
||||
}
|
||||
|
||||
$this->registerHooks();
|
||||
}
|
||||
}
|
||||
|
||||
public function deActivate($moduleModel = null)
|
||||
{
|
||||
if (null === $moduleModel) {
|
||||
$moduleModel = $this->getModuleModel();
|
||||
}
|
||||
if ($moduleModel->getActivate() == self::IS_ACTIVATED) {
|
||||
$con = Propel::getWriteConnection(ModuleTableMap::DATABASE_NAME);
|
||||
$con->beginTransaction();
|
||||
try {
|
||||
if ($this->preDeactivation($con)) {
|
||||
$moduleModel->setActivate(self::IS_NOT_ACTIVATED);
|
||||
$moduleModel->save($con);
|
||||
$this->postDeactivation($con);
|
||||
|
||||
$con->commit();
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$con->rollBack();
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function hasContainer()
|
||||
{
|
||||
return null !== $this->container;
|
||||
}
|
||||
|
||||
public function getContainer()
|
||||
{
|
||||
if ($this->hasContainer() === false) {
|
||||
throw new \RuntimeException("Sorry, container is not available in this context");
|
||||
}
|
||||
|
||||
return $this->container;
|
||||
}
|
||||
|
||||
public function hasRequest()
|
||||
{
|
||||
return null !== $this->request;
|
||||
}
|
||||
|
||||
public function setRequest(Request $request)
|
||||
{
|
||||
$this->request = $request;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Thelia\Core\HttpFoundation\Request the request.
|
||||
*
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public function getRequest()
|
||||
{
|
||||
if ($this->hasRequest() === false) {
|
||||
// Try to get request from container.
|
||||
$this->setRequest($this->getContainer()->get('request_stack')->getCurrentRequest());
|
||||
}
|
||||
|
||||
if ($this->hasRequest() === false) {
|
||||
throw new \RuntimeException("Sorry, the request is not available in this context");
|
||||
}
|
||||
|
||||
return $this->request;
|
||||
}
|
||||
|
||||
public function hasDispatcher()
|
||||
{
|
||||
return null !== $this->dispatcher;
|
||||
}
|
||||
|
||||
public function setDispatcher(EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
$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");
|
||||
}
|
||||
|
||||
return $this->dispatcher;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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();
|
||||
|
||||
if (null === $moduleI18n) {
|
||||
$moduleI18n = new ModuleI18n();
|
||||
$moduleI18n
|
||||
->setId($module->getId())
|
||||
->setLocale($locale)
|
||||
->setTitle($title)
|
||||
;
|
||||
$moduleI18n->save();
|
||||
} else {
|
||||
$moduleI18n->setTitle($title);
|
||||
$moduleI18n->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @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)
|
||||
{
|
||||
try {
|
||||
$directoryBrowser = new \DirectoryIterator($folderPath);
|
||||
} catch (\UnexpectedValueException $e) {
|
||||
throw $e;
|
||||
}
|
||||
if (null === $con) {
|
||||
$con = Propel::getConnection(
|
||||
ModuleImageTableMap::DATABASE_NAME
|
||||
);
|
||||
}
|
||||
|
||||
/* browse the directory */
|
||||
$imagePosition = 1;
|
||||
/** @var \DirectoryIterator $directoryContent */
|
||||
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)) {
|
||||
$con->beginTransaction();
|
||||
|
||||
$image = new ModuleImage();
|
||||
$image->setModuleId($module->getId());
|
||||
$image->setPosition($imagePosition);
|
||||
$image->save($con);
|
||||
|
||||
$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
|
||||
);
|
||||
$increment++;
|
||||
}
|
||||
|
||||
$imagePath = sprintf('%s/%s', $imageDirectory, $imageFileName);
|
||||
|
||||
if (! is_dir($imageDirectory)) {
|
||||
if (! @mkdir($imageDirectory, 0777, true)) {
|
||||
$con->rollBack();
|
||||
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
|
||||
);
|
||||
}
|
||||
|
||||
$image->setFile($imageFileName);
|
||||
$image->save($con);
|
||||
|
||||
$con->commit();
|
||||
$imagePosition++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getModuleModel()
|
||||
{
|
||||
if (null === $this->moduleModel) {
|
||||
$this->moduleModel = ModuleQuery::create()->findOneByCode($this->getCode());
|
||||
|
||||
if (null === $this->moduleModel) {
|
||||
throw new ModuleException(
|
||||
sprintf("Module Code `%s` not found", $this->getCode()),
|
||||
ModuleException::CODE_NOT_FOUND
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->moduleModel;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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()
|
||||
{
|
||||
$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 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();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this module is the payment module for a given order
|
||||
*
|
||||
* @param Order $order an order
|
||||
* @return bool true if this module is the payment module for the given order.
|
||||
*/
|
||||
public function isPaymentModuleFor(Order $order)
|
||||
{
|
||||
$model = $this->getModuleModel();
|
||||
|
||||
return $order->getPaymentModuleId() == $model->getId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this module is the delivery module for a given order
|
||||
*
|
||||
* @param Order $order an order
|
||||
* @return bool true if this module is the delivery module for the given order.
|
||||
*/
|
||||
public function isDeliveryModuleFor(Order $order)
|
||||
{
|
||||
$model = $this->getModuleModel();
|
||||
|
||||
return $order->getDeliveryModuleId() == $model->getId();
|
||||
}
|
||||
|
||||
/**
|
||||
* A convenient method to get the current order total, with or without tax, discount or postage.
|
||||
* This method operates on the order currently in the user's session, and should not be used to
|
||||
* get the total amount of an order already stored in the database. For such orders, use
|
||||
* Order::getTotalAmount() method.
|
||||
*
|
||||
* @param bool $with_tax if true, to total price will include tax amount
|
||||
* @param bool $with_discount if true, the total price will include discount, if any
|
||||
* @param bool $with_postage if true, the total price will include the delivery costs, if any.
|
||||
*
|
||||
* @return float|int the current order amount.
|
||||
*/
|
||||
public function getCurrentOrderTotalAmount($with_tax = true, $with_discount = true, $with_postage = true)
|
||||
{
|
||||
/** @var Session $session */
|
||||
$session = $this->getRequest()->getSession();
|
||||
|
||||
/** @var Cart $cart */
|
||||
$cart = $session->getSessionCart($this->getDispatcher());
|
||||
|
||||
/** @var Order $order */
|
||||
$order = $session->getOrder();
|
||||
|
||||
/** @var TaxEngine $taxEngine */
|
||||
$taxEngine = $this->getContainer()->get("thelia.taxengine");
|
||||
|
||||
/** @var Country $country */
|
||||
$country = $taxEngine->getDeliveryCountry();
|
||||
|
||||
$state = $taxEngine->getDeliveryState();
|
||||
|
||||
$amount = $with_tax ? $cart->getTaxedAmount($country, $with_discount, $state) : $cart->getTotalAmount($with_discount, $country, $state);
|
||||
|
||||
if ($with_postage) {
|
||||
if ($with_tax) {
|
||||
$amount += $order->getPostage();
|
||||
} else {
|
||||
$amount += $order->getPostage() - $order->getPostageTax();
|
||||
}
|
||||
}
|
||||
|
||||
return $amount;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static function getCompilers()
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function install(ConnectionInterface $con = null)
|
||||
{
|
||||
// Override this method to do something useful.
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function update($currentVersion, $newVersion, ConnectionInterface $con = null)
|
||||
{
|
||||
// Override this method to do something useful.
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function preActivation(ConnectionInterface $con = null)
|
||||
{
|
||||
// Override this method to do something useful.
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function postActivation(ConnectionInterface $con = null)
|
||||
{
|
||||
// Override this method to do something useful.
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function preDeactivation(ConnectionInterface $con = null)
|
||||
{
|
||||
// Override this method to do something useful.
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function postDeactivation(ConnectionInterface $con = null)
|
||||
{
|
||||
// Override this method to do something useful.
|
||||
}
|
||||
|
||||
/**
|
||||
* @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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.4
|
||||
* @return string
|
||||
*/
|
||||
protected function getPropelSchemaDir()
|
||||
{
|
||||
return THELIA_MODULE_DIR . $this->getCode() . DS . 'Config' . DS . 'schema.xml';
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.4
|
||||
* @return bool
|
||||
*/
|
||||
protected function hasPropelSchema()
|
||||
{
|
||||
return (new Filesystem())->exists($this->getPropelSchemaDir());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
250
core/lib/Thelia/Module/BaseModuleInterface.php
Normal file
250
core/lib/Thelia/Module/BaseModuleInterface.php
Normal file
@@ -0,0 +1,250 @@
|
||||
<?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\Module;
|
||||
|
||||
use Propel\Runtime\Connection\ConnectionInterface;
|
||||
use Thelia\Model\Module;
|
||||
|
||||
interface BaseModuleInterface
|
||||
{
|
||||
/**
|
||||
* This method is called when the plugin is installed for the first time
|
||||
*
|
||||
* @param ConnectionInterface $con
|
||||
*/
|
||||
public function install(ConnectionInterface $con = null);
|
||||
|
||||
/**
|
||||
* This method is called when a newer version of the plugin is installed
|
||||
*
|
||||
* @param string $currentVersion the current (installed) module version, as defined in the module.xml file
|
||||
* @param string $newVersion the new module version, as defined in the module.xml file
|
||||
* @param ConnectionInterface $con
|
||||
*/
|
||||
public function update($currentVersion, $newVersion, ConnectionInterface $con = null);
|
||||
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public function destroy(ConnectionInterface $con = null, $deleteModuleData = false);
|
||||
|
||||
/**
|
||||
* This method is called when the module is activated
|
||||
*
|
||||
* @param Module $moduleModel the module
|
||||
*/
|
||||
public function activate($moduleModel = null);
|
||||
|
||||
/**
|
||||
* This method is called when the module is deactivated
|
||||
*
|
||||
* @param Module $moduleModel the module
|
||||
*/
|
||||
public function deActivate($moduleModel = null);
|
||||
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public function preActivation(ConnectionInterface $con = null);
|
||||
|
||||
/**
|
||||
* This method is called just after the module was successfully activated.
|
||||
*
|
||||
* @param ConnectionInterface $con
|
||||
*/
|
||||
public function postActivation(ConnectionInterface $con = null);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public function preDeactivation(ConnectionInterface $con = null);
|
||||
|
||||
/**
|
||||
* This method is called just after the module was successfully deactivated.
|
||||
*
|
||||
* @param ConnectionInterface $con
|
||||
*/
|
||||
public function postDeactivation(ConnectionInterface $con = null);
|
||||
|
||||
|
||||
/**
|
||||
* Sets a module titles for various languages
|
||||
*
|
||||
* @param Module $module the module.
|
||||
* @param array $titles an associative array of locale => title_string
|
||||
*/
|
||||
public function setTitle(Module $module, $titles);
|
||||
|
||||
/**
|
||||
* Get a module's configuration variable
|
||||
*
|
||||
* @param string $variableName the variable name
|
||||
* @param string $defaultValue the default value, if variable is not defined
|
||||
* @param null $valueLocale the required locale, or null to get default one
|
||||
* @return string the variable value
|
||||
*/
|
||||
public static function getConfigValue($variableName, $defaultValue = null, $valueLocale = null);
|
||||
|
||||
/**
|
||||
* Set module configuration variable, creating it if required
|
||||
*
|
||||
* @param string $variableName the variable name
|
||||
* @param string $variableValue the variable value
|
||||
* @param null $valueLocale the locale, or null if not required
|
||||
* @param bool $createIfNotExists if true, the variable will be created if not already defined
|
||||
* @throws \LogicException if variable does not exists and $createIfNotExists is false
|
||||
* @return $this;
|
||||
*/
|
||||
public static function setConfigValue(
|
||||
$variableName,
|
||||
$variableValue,
|
||||
$valueLocale = null,
|
||||
$createIfNotExists = true
|
||||
);
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public function deployImageFolder(Module $module, $folderPath, ConnectionInterface $con = null);
|
||||
|
||||
/**
|
||||
* @return Module
|
||||
* @throws \Thelia\Exception\ModuleException
|
||||
*/
|
||||
public function getModuleModel();
|
||||
|
||||
/**
|
||||
* @return string The module id
|
||||
*/
|
||||
public static function getModuleId();
|
||||
|
||||
/**
|
||||
* @return string The module code, in a static way
|
||||
*/
|
||||
public static function getModuleCode();
|
||||
|
||||
/**
|
||||
* @return string The module code
|
||||
*/
|
||||
public function getCode();
|
||||
|
||||
/**
|
||||
*
|
||||
* 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
|
||||
* )
|
||||
* );
|
||||
*
|
||||
*/
|
||||
public static function getCompilers();
|
||||
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*
|
||||
* This method must be used when your module defines hooks.
|
||||
* Override this and return an array of your hooks names to register them
|
||||
*
|
||||
* This returned value must be like the example, only type and code are mandatory
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* return array(
|
||||
*
|
||||
* // Only register the title in the default language
|
||||
* array(
|
||||
* "type" => TemplateDefinition::BACK_OFFICE,
|
||||
* "code" => "my_super_hook_name",
|
||||
* "title" => "My hook",
|
||||
* "description" => "My hook is really, really great",
|
||||
* ),
|
||||
*
|
||||
* // Manage i18n
|
||||
* array(
|
||||
* "type" => TemplateDefinition::FRONT_OFFICE,
|
||||
* "code" => "my_hook_name",
|
||||
* "title" => array(
|
||||
* "fr_FR" => "Mon Hook",
|
||||
* "en_US" => "My hook",
|
||||
* ),
|
||||
* "description" => array(
|
||||
* "fr_FR" => "Mon hook est vraiment super",
|
||||
* "en_US" => "My hook is really, really great",
|
||||
* ),
|
||||
* "chapo" => array(
|
||||
* "fr_FR" => "Mon hook est vraiment super",
|
||||
* "en_US" => "My hook is really, really great",
|
||||
* ),
|
||||
* "block" => true,
|
||||
* "active" => true
|
||||
* )
|
||||
* );
|
||||
*/
|
||||
public function getHooks();
|
||||
|
||||
/**
|
||||
* Create or update module hooks returned by the `getHooks` function
|
||||
*/
|
||||
public function registerHooks();
|
||||
}
|
||||
274
core/lib/Thelia/Module/BasePaymentModuleController.php
Normal file
274
core/lib/Thelia/Module/BasePaymentModuleController.php
Normal file
@@ -0,0 +1,274 @@
|
||||
<?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\Module;
|
||||
|
||||
use Symfony\Component\Routing\Router;
|
||||
use Thelia\Controller\Front\BaseFrontController;
|
||||
use Thelia\Core\Event\Order\OrderEvent;
|
||||
use Thelia\Core\Event\TheliaEvents;
|
||||
use Thelia\Core\HttpKernel\Exception\RedirectException;
|
||||
use Thelia\Log\Tlog;
|
||||
use Thelia\Model\OrderQuery;
|
||||
use Thelia\Model\OrderStatusQuery;
|
||||
|
||||
/**
|
||||
* This class implement the minimum
|
||||
*
|
||||
* @package Thelia\Module
|
||||
* @author Thelia <info@thelia.net>
|
||||
*/
|
||||
abstract class BasePaymentModuleController extends BaseFrontController
|
||||
{
|
||||
protected $log = null;
|
||||
|
||||
/**
|
||||
* Return a module identifier used to calculate the name of the log file,
|
||||
* and in the log messages.
|
||||
*
|
||||
* @return string the module code
|
||||
*/
|
||||
abstract protected function getModuleCode();
|
||||
|
||||
/**
|
||||
* Returns the module-specific logger, initializing it if required.
|
||||
*
|
||||
* @return Tlog a Tlog instance
|
||||
*/
|
||||
protected function getLog()
|
||||
{
|
||||
if ($this->log == null) {
|
||||
$this->log = Tlog::getNewInstance();
|
||||
|
||||
$logFilePath = $this->getLogFilePath();
|
||||
|
||||
$this->log->setPrefix("#LEVEL: #DATE #HOUR: ");
|
||||
$this->log->setDestinations("\\Thelia\\Log\\Destination\\TlogDestinationFile");
|
||||
$this->log->setConfig("\\Thelia\\Log\\Destination\\TlogDestinationFile", 0, $logFilePath);
|
||||
$this->log->setLevel(Tlog::INFO);
|
||||
}
|
||||
|
||||
return $this->log;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string The path to the module's log file.
|
||||
*/
|
||||
protected function getLogFilePath()
|
||||
{
|
||||
return sprintf(THELIA_ROOT . "log" . DS . "%s.log", strtolower($this->getModuleCode()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the confirmation of an order. This method should be called
|
||||
* once the module has performed the required checks to confirm a valid payment.
|
||||
*
|
||||
* @param int $orderId the order ID
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function confirmPayment($orderId)
|
||||
{
|
||||
try {
|
||||
$orderId = \intval($orderId);
|
||||
|
||||
if (null !== $order = $this->getOrder($orderId)) {
|
||||
$this->getLog()->addInfo(
|
||||
$this->getTranslator()->trans(
|
||||
"Processing confirmation of order ref. %ref, ID %id",
|
||||
array('%ref' => $order->getRef(), '%id' => $order->getId())
|
||||
)
|
||||
);
|
||||
|
||||
$event = new OrderEvent($order);
|
||||
|
||||
$event->setStatus(OrderStatusQuery::getPaidStatus()->getId());
|
||||
|
||||
$this->dispatch(TheliaEvents::ORDER_UPDATE_STATUS, $event);
|
||||
|
||||
$this->getLog()->addInfo(
|
||||
$this->getTranslator()->trans(
|
||||
"Order ref. %ref, ID %id has been successfully paid.",
|
||||
array('%ref' => $order->getRef(), '%id' => $order->getId())
|
||||
)
|
||||
);
|
||||
}
|
||||
} catch (\Exception $ex) {
|
||||
$this->getLog()->addError(
|
||||
$this->getTranslator()->trans(
|
||||
"Error occured while processing order ref. %ref, ID %id: %err",
|
||||
array(
|
||||
'%err' => $ex->getMessage(),
|
||||
'%ref' => !isset($order) ? "?" : $order->getRef(),
|
||||
'%id' => !isset($order) ? "?" : $order->getId()
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
throw $ex;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the transaction/payment ref in the order
|
||||
*
|
||||
* @param int $orderId the order ID
|
||||
* @param int $transactionRef the transaction reference
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function saveTransactionRef($orderId, $transactionRef)
|
||||
{
|
||||
try {
|
||||
$orderId = \intval($orderId);
|
||||
|
||||
if (null !== $order = $this->getOrder($orderId)) {
|
||||
$event = new OrderEvent($order);
|
||||
|
||||
$event->setTransactionRef($transactionRef);
|
||||
|
||||
$this->dispatch(TheliaEvents::ORDER_UPDATE_TRANSACTION_REF, $event);
|
||||
|
||||
$this->getLog()->addInfo(
|
||||
$this->getTranslator()->trans(
|
||||
"Payment transaction %transaction_ref for order ref. %ref, ID %id has been successfully saved.",
|
||||
[
|
||||
'%transaction_ref' => $transactionRef,
|
||||
'%ref' => $order->getRef(),
|
||||
'%id' => $order->getId()
|
||||
]
|
||||
)
|
||||
);
|
||||
}
|
||||
} catch (\Exception $ex) {
|
||||
$this->getLog()->addError(
|
||||
$this->getTranslator()->trans(
|
||||
"Error occurred while saving payment transaction %transaction_ref for order ID %id.",
|
||||
[
|
||||
'%transaction_ref' => $transactionRef,
|
||||
'%id' => $orderId
|
||||
]
|
||||
)
|
||||
);
|
||||
|
||||
throw $ex;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the cancellation of a payment on the payment gateway. The order will go back to the
|
||||
* "not paid" status.
|
||||
*
|
||||
* @param int $orderId the order ID
|
||||
*/
|
||||
public function cancelPayment($orderId)
|
||||
{
|
||||
try {
|
||||
$orderId = \intval($orderId);
|
||||
|
||||
if (null !== $order = $this->getOrder($orderId)) {
|
||||
$this->getLog()->addInfo(
|
||||
$this->getTranslator()->trans(
|
||||
"Processing cancelation of payment for order ref. %ref",
|
||||
array('%ref' => $order->getRef())
|
||||
)
|
||||
);
|
||||
|
||||
$event = new OrderEvent($order);
|
||||
|
||||
$event->setStatus(OrderStatusQuery::getNotPaidStatus()->getId());
|
||||
|
||||
$this->dispatch(TheliaEvents::ORDER_UPDATE_STATUS, $event);
|
||||
|
||||
$this->getLog()->addInfo(
|
||||
$this->getTranslator()->trans(
|
||||
"Order ref. %ref is now unpaid.",
|
||||
array('%ref' => $order->getRef())
|
||||
)
|
||||
);
|
||||
}
|
||||
} catch (\Exception $ex) {
|
||||
$this->getLog()->addError(
|
||||
$this->getTranslator()->trans(
|
||||
"Error occurred while cancelling order ref. %ref, ID %id: %err",
|
||||
array(
|
||||
'%err' => $ex->getMessage(),
|
||||
'%ref' => !isset($order) ? "?" : $order->getRef(),
|
||||
'%id' => !isset($order) ? "?" : $order->getId()
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
throw $ex;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an order and issue a log message if not found.
|
||||
*
|
||||
* @param $orderId
|
||||
* @return null|\Thelia\Model\Order
|
||||
*/
|
||||
protected function getOrder($orderId)
|
||||
{
|
||||
if (null == $order = OrderQuery::create()->findPk($orderId)) {
|
||||
$this->getLog()->addError(
|
||||
$this->getTranslator()->trans("Unknown order ID: %id", array('%id' => $orderId))
|
||||
);
|
||||
}
|
||||
|
||||
return $order;
|
||||
}
|
||||
|
||||
/**
|
||||
* Redirect the customer to the successful payment page.
|
||||
*
|
||||
* @param int $orderId the order ID
|
||||
*/
|
||||
public function redirectToSuccessPage($orderId)
|
||||
{
|
||||
$this->getLog()->addInfo("Redirecting customer to payment success page");
|
||||
|
||||
throw new RedirectException(
|
||||
$this->retrieveUrlFromRouteId(
|
||||
'order.placed',
|
||||
[],
|
||||
[
|
||||
'order_id' => $orderId
|
||||
],
|
||||
Router::ABSOLUTE_PATH
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Redirect the customer to the failure payment page. if $message is null, a generic message is displayed.
|
||||
*
|
||||
* @param int $orderId the order ID
|
||||
* @param string|null $message an error message.
|
||||
*/
|
||||
public function redirectToFailurePage($orderId, $message)
|
||||
{
|
||||
$this->getLog()->addInfo("Redirecting customer to payment failure page");
|
||||
|
||||
throw new RedirectException(
|
||||
$this->retrieveUrlFromRouteId(
|
||||
'order.failed',
|
||||
[],
|
||||
[
|
||||
'order_id' => $orderId,
|
||||
'message' => $message
|
||||
],
|
||||
Router::ABSOLUTE_PATH
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
51
core/lib/Thelia/Module/DeliveryModuleInterface.php
Normal file
51
core/lib/Thelia/Module/DeliveryModuleInterface.php
Normal file
@@ -0,0 +1,51 @@
|
||||
<?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\Module;
|
||||
|
||||
use Thelia\Model\Country;
|
||||
use Thelia\Model\OrderPostage;
|
||||
use Thelia\Module\Exception\DeliveryException;
|
||||
|
||||
interface DeliveryModuleInterface extends BaseModuleInterface
|
||||
{
|
||||
/**
|
||||
* This method is called by the Delivery loop, to check if the current module has to be displayed to the customer.
|
||||
* Override it to implements your delivery rules/
|
||||
*
|
||||
* If you return true, the delivery method will de displayed to the customer
|
||||
* If you return false, the delivery method will not be displayed
|
||||
*
|
||||
* @param Country $country the country to deliver to.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function isValidDelivery(Country $country);
|
||||
|
||||
/**
|
||||
* Calculate and return delivery price in the shop's default currency
|
||||
*
|
||||
* @param Country $country the country to deliver to.
|
||||
*
|
||||
* @return OrderPostage|float the delivery price
|
||||
* @throws DeliveryException if the postage price cannot be calculated.
|
||||
*/
|
||||
public function getPostage(Country $country);
|
||||
|
||||
/**
|
||||
*
|
||||
* This method return true if your delivery manages virtual product delivery.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function handleVirtualProductDelivery();
|
||||
}
|
||||
54
core/lib/Thelia/Module/DeliveryModuleWithStateInterface.php
Normal file
54
core/lib/Thelia/Module/DeliveryModuleWithStateInterface.php
Normal file
@@ -0,0 +1,54 @@
|
||||
<?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\Module;
|
||||
|
||||
use Thelia\Model\Country;
|
||||
use Thelia\Model\OrderPostage;
|
||||
use Thelia\Model\State;
|
||||
use Thelia\Module\Exception\DeliveryException;
|
||||
|
||||
interface DeliveryModuleWithStateInterface extends BaseModuleInterface
|
||||
{
|
||||
/**
|
||||
* This method is called by the Delivery loop, to check if the current module has to be displayed to the customer.
|
||||
* Override it to implements your delivery rules/
|
||||
*
|
||||
* If you return true, the delivery method will de displayed to the customer
|
||||
* If you return false, the delivery method will not be displayed
|
||||
*
|
||||
* @param Country $country the country to deliver to.
|
||||
* @param State $state
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function isValidDelivery(Country $country, State $state = null);
|
||||
|
||||
/**
|
||||
* Calculate and return delivery price in the shop's default currency
|
||||
*
|
||||
* @param Country $country the country to deliver to.
|
||||
* @param State $state
|
||||
*
|
||||
* @return OrderPostage|float the delivery price
|
||||
* @throws DeliveryException if the postage price cannot be calculated.
|
||||
*/
|
||||
public function getPostage(Country $country, State $state = null);
|
||||
|
||||
/**
|
||||
*
|
||||
* This method return true if your delivery manages virtual product delivery.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function handleVirtualProductDelivery();
|
||||
}
|
||||
17
core/lib/Thelia/Module/Exception/DeliveryException.php
Normal file
17
core/lib/Thelia/Module/Exception/DeliveryException.php
Normal file
@@ -0,0 +1,17 @@
|
||||
<?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\Module\Exception;
|
||||
|
||||
class DeliveryException extends \RuntimeException
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<?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\Module\Exception;
|
||||
|
||||
/**
|
||||
* Class InvalidXmlDocumentException
|
||||
* @package Thelia\Module\Exception
|
||||
* @author Manuel Raynaud <manu@raynaud.io>
|
||||
*/
|
||||
class InvalidXmlDocumentException extends \RuntimeException
|
||||
{
|
||||
}
|
||||
132
core/lib/Thelia/Module/ModuleDescriptorValidator.php
Normal file
132
core/lib/Thelia/Module/ModuleDescriptorValidator.php
Normal file
@@ -0,0 +1,132 @@
|
||||
<?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\Module;
|
||||
|
||||
use ErrorException;
|
||||
use Symfony\Component\Finder\Finder;
|
||||
use Thelia\Module\Exception\InvalidXmlDocumentException;
|
||||
|
||||
/**
|
||||
* Class ModuleDescriptorValidator
|
||||
* @package Thelia\Module
|
||||
* @author Manuel Raynaud <manu@raynaud.io>
|
||||
*/
|
||||
class ModuleDescriptorValidator
|
||||
{
|
||||
protected static $versions = [
|
||||
'1' => 'module.xsd',
|
||||
'2' => 'module-2_1.xsd',
|
||||
'3' => 'module-2_2.xsd'
|
||||
];
|
||||
|
||||
/** @var Finder */
|
||||
protected $xsdFinder;
|
||||
|
||||
protected $moduleVersion;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->xsdFinder = new Finder();
|
||||
$this->xsdFinder
|
||||
->name('*.xsd')
|
||||
->in(__DIR__ . '/schema/module/');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getModuleVersion()
|
||||
{
|
||||
return $this->moduleVersion;
|
||||
}
|
||||
|
||||
public function validate($xml_file, $version = null)
|
||||
{
|
||||
$dom = new \DOMDocument();
|
||||
$errors = [];
|
||||
|
||||
if ($dom->load($xml_file)) {
|
||||
/** @var \SplFileInfo $xsdFile */
|
||||
foreach ($this->xsdFinder as $xsdFile) {
|
||||
$xsdVersion = array_search($xsdFile->getBasename(), self::$versions);
|
||||
|
||||
if (false === $xsdVersion || (null !== $version && $version != $xsdVersion)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$errors = $this->schemaValidate($dom, $xsdFile);
|
||||
|
||||
if (\count($errors) === 0) {
|
||||
$this->moduleVersion = $xsdVersion;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new InvalidXmlDocumentException(
|
||||
sprintf(
|
||||
"%s file is not a valid file : %s",
|
||||
$xml_file,
|
||||
implode(", ", $errors)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the schema of a XML file with a given xsd file
|
||||
*
|
||||
* @param \DOMDocument $dom The XML document
|
||||
* @param \SplFileInfo $xsdFile The XSD file
|
||||
* @return array an array of errors if validation fails, otherwise an empty array
|
||||
*/
|
||||
protected function schemaValidate(\DOMDocument $dom, \SplFileInfo $xsdFile)
|
||||
{
|
||||
$errorMessages = [];
|
||||
|
||||
try {
|
||||
libxml_use_internal_errors(true);
|
||||
|
||||
if (!$dom->schemaValidate($xsdFile->getRealPath())) {
|
||||
$errors = libxml_get_errors();
|
||||
|
||||
foreach ($errors as $error) {
|
||||
$errorMessages[] = sprintf(
|
||||
'XML error "%s" [%d] (Code %d) in %s on line %d column %d' . "\n",
|
||||
$error->message,
|
||||
$error->level,
|
||||
$error->code,
|
||||
$error->file,
|
||||
$error->line,
|
||||
$error->column
|
||||
);
|
||||
}
|
||||
|
||||
libxml_clear_errors();
|
||||
}
|
||||
|
||||
libxml_use_internal_errors(false);
|
||||
} catch (ErrorException $ex) {
|
||||
libxml_use_internal_errors(false);
|
||||
}
|
||||
|
||||
return $errorMessages;
|
||||
}
|
||||
|
||||
public function getDescriptor($xml_file)
|
||||
{
|
||||
$this->validate($xml_file);
|
||||
|
||||
return @simplexml_load_file($xml_file);
|
||||
}
|
||||
}
|
||||
225
core/lib/Thelia/Module/ModuleManagement.php
Normal file
225
core/lib/Thelia/Module/ModuleManagement.php
Normal 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\Module;
|
||||
|
||||
use Propel\Runtime\Connection\ConnectionInterface;
|
||||
use Propel\Runtime\Propel;
|
||||
use SplFileInfo;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\Finder\Finder;
|
||||
use Thelia\Core\Event\Cache\CacheEvent;
|
||||
use Thelia\Core\Event\TheliaEvents;
|
||||
use Thelia\Exception\InvalidModuleException;
|
||||
use Thelia\Log\Tlog;
|
||||
use Thelia\Model\Map\ModuleTableMap;
|
||||
use Thelia\Model\Module;
|
||||
use Thelia\Model\ModuleQuery;
|
||||
|
||||
/**
|
||||
* Class ModuleManagement
|
||||
* @package Thelia\Module
|
||||
* @author Manuel Raynaud <manu@raynaud.io>
|
||||
*/
|
||||
class ModuleManagement
|
||||
{
|
||||
protected $baseModuleDir;
|
||||
protected $reflected;
|
||||
|
||||
/** @var ModuleDescriptorValidator $descriptorValidator */
|
||||
protected $descriptorValidator;
|
||||
|
||||
protected $container;
|
||||
|
||||
public function __construct(ContainerInterface $container)
|
||||
{
|
||||
$this->container = $container;
|
||||
$this->baseModuleDir = THELIA_MODULE_DIR;
|
||||
}
|
||||
|
||||
public function updateModules(ContainerInterface $container)
|
||||
{
|
||||
$finder = new Finder();
|
||||
|
||||
$finder
|
||||
->name('module.xml')
|
||||
->in($this->baseModuleDir . '*' . DS . 'Config')
|
||||
;
|
||||
|
||||
$errors = [];
|
||||
|
||||
$modulesUpdated = [];
|
||||
|
||||
foreach ($finder as $file) {
|
||||
try {
|
||||
$this->updateModule($file, $container);
|
||||
} catch (\Exception $ex) {
|
||||
// Guess module code
|
||||
$moduleCode = basename(dirname(dirname($file)));
|
||||
|
||||
$errors[$moduleCode] = $ex;
|
||||
}
|
||||
}
|
||||
|
||||
if (\count($errors) > 0) {
|
||||
throw new InvalidModuleException($errors);
|
||||
}
|
||||
|
||||
if (\count($modulesUpdated)) {
|
||||
$this->cacheClear();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update module information, and invoke install() for new modules (e.g. modules
|
||||
* just discovered), or update() modules for which version number ha changed.
|
||||
*
|
||||
* @param SplFileInfo $file the module.xml file descriptor
|
||||
* @param ContainerInterface $container the container
|
||||
*
|
||||
* @return Module
|
||||
*
|
||||
* @throws \Exception
|
||||
* @throws \Propel\Runtime\Exception\PropelException
|
||||
*/
|
||||
public function updateModule($file, ContainerInterface $container)
|
||||
{
|
||||
$descriptorValidator = $this->getDescriptorValidator();
|
||||
|
||||
$content = $descriptorValidator->getDescriptor($file->getRealPath());
|
||||
$reflected = new \ReflectionClass((string)$content->fullnamespace);
|
||||
$code = basename(dirname($reflected->getFileName()));
|
||||
$version = (string)$content->version;
|
||||
$mandatory = \intval($content->mandatory);
|
||||
$hidden = \intval($content->hidden);
|
||||
|
||||
$module = ModuleQuery::create()->filterByCode($code)->findOne();
|
||||
|
||||
if (null === $module) {
|
||||
$module = new Module();
|
||||
$module->setActivate(0);
|
||||
|
||||
$action = 'install';
|
||||
} elseif ($version !== $module->getVersion()) {
|
||||
$currentVersion = $module->getVersion();
|
||||
$action = 'update';
|
||||
} else {
|
||||
$action = 'none';
|
||||
}
|
||||
|
||||
$con = Propel::getWriteConnection(ModuleTableMap::DATABASE_NAME);
|
||||
$con->beginTransaction();
|
||||
|
||||
try {
|
||||
$module
|
||||
->setCode($code)
|
||||
->setVersion($version)
|
||||
->setFullNamespace((string)$content->fullnamespace)
|
||||
->setType($this->getModuleType($reflected))
|
||||
->setCategory((string)$content->type)
|
||||
->setMandatory($mandatory)
|
||||
->setHidden($hidden)
|
||||
->save($con)
|
||||
;
|
||||
|
||||
// Update the module images, title and description when the module is installed, but not after
|
||||
// as these data may have been modified byt the administrator
|
||||
if ('install' === $action) {
|
||||
$this->saveDescription($module, $content, $con);
|
||||
|
||||
if (isset($content->{"images-folder"}) && !$module->isModuleImageDeployed($con)) {
|
||||
/** @var \Thelia\Module\BaseModule $moduleInstance */
|
||||
$moduleInstance = $reflected->newInstance();
|
||||
$imagesFolder = THELIA_MODULE_DIR . $code . DS . (string)$content->{"images-folder"};
|
||||
$moduleInstance->deployImageFolder($module, $imagesFolder, $con);
|
||||
}
|
||||
}
|
||||
|
||||
// Tell the module to install() or update()
|
||||
$instance = $module->createInstance();
|
||||
|
||||
$instance->setContainer($container);
|
||||
|
||||
if ($action == 'install') {
|
||||
$instance->install($con);
|
||||
} elseif ($action == 'update') {
|
||||
$instance->update($currentVersion, $version, $con);
|
||||
}
|
||||
|
||||
if ($action !== 'none') {
|
||||
$instance->registerHooks();
|
||||
}
|
||||
|
||||
$con->commit();
|
||||
} catch (\Exception $ex) {
|
||||
Tlog::getInstance()->addError("Failed to update module " . $module->getCode(), $ex);
|
||||
|
||||
$con->rollBack();
|
||||
throw $ex;
|
||||
}
|
||||
|
||||
return $module;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Thelia\Module\ModuleDescriptorValidator
|
||||
*/
|
||||
public function getDescriptorValidator()
|
||||
{
|
||||
if (null === $this->descriptorValidator) {
|
||||
$this->descriptorValidator = new ModuleDescriptorValidator();
|
||||
}
|
||||
|
||||
return $this->descriptorValidator;
|
||||
}
|
||||
|
||||
protected function cacheClear()
|
||||
{
|
||||
$cacheEvent = new CacheEvent(
|
||||
$this->container->getParameter('kernel.cache_dir')
|
||||
);
|
||||
|
||||
$this->container->get('event_dispatcher')->dispatch(TheliaEvents::CACHE_CLEAR, $cacheEvent);
|
||||
}
|
||||
|
||||
private function getModuleType(\ReflectionClass $reflected)
|
||||
{
|
||||
if (
|
||||
$reflected->implementsInterface('Thelia\Module\DeliveryModuleInterface')
|
||||
||
|
||||
$reflected->implementsInterface('Thelia\Module\DeliveryModuleWithStateInterface')
|
||||
) {
|
||||
return BaseModule::DELIVERY_MODULE_TYPE;
|
||||
} elseif ($reflected->implementsInterface('Thelia\Module\PaymentModuleInterface')) {
|
||||
return BaseModule::PAYMENT_MODULE_TYPE;
|
||||
} else {
|
||||
return BaseModule::CLASSIC_MODULE_TYPE;
|
||||
}
|
||||
}
|
||||
|
||||
private function saveDescription(Module $module, \SimpleXMLElement $content, ConnectionInterface $con)
|
||||
{
|
||||
foreach ($content->descriptive as $description) {
|
||||
$locale = (string)$description->attributes()->locale;
|
||||
|
||||
$module
|
||||
->setLocale($locale)
|
||||
->setTitle($description->title)
|
||||
->setDescription(isset($description->description) ? $description->description : null)
|
||||
->setPostscriptum(isset($description->postscriptum) ? $description->postscriptum : null)
|
||||
->setChapo(isset($description->subtitle) ? $description->subtitle : null)
|
||||
->save($con)
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
53
core/lib/Thelia/Module/PaymentModuleInterface.php
Normal file
53
core/lib/Thelia/Module/PaymentModuleInterface.php
Normal file
@@ -0,0 +1,53 @@
|
||||
<?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\Module;
|
||||
|
||||
use Thelia\Model\Order;
|
||||
|
||||
interface PaymentModuleInterface extends BaseModuleInterface
|
||||
{
|
||||
/**
|
||||
*
|
||||
* Method used by payment gateway.
|
||||
*
|
||||
* If this method return a \Thelia\Core\HttpFoundation\Response instance, this response is send to the
|
||||
* browser.
|
||||
*
|
||||
* In many cases, it's necessary to send a form to the payment gateway. On your response you can return this form already
|
||||
* completed, ready to be sent
|
||||
*
|
||||
* @param \Thelia\Model\Order $order processed order
|
||||
* @return null|\Thelia\Core\HttpFoundation\Response
|
||||
*/
|
||||
public function pay(Order $order);
|
||||
|
||||
/**
|
||||
*
|
||||
* This method is call on Payment loop.
|
||||
*
|
||||
* If you return true, the payment method will de display
|
||||
* If you return false, the payment method will not be display
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function isValidPayment();
|
||||
|
||||
|
||||
/**
|
||||
* if you want, you can manage stock in your module instead of order process.
|
||||
* Return false to decrease the stock when order status switch to pay
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function manageStockOnCreation();
|
||||
}
|
||||
249
core/lib/Thelia/Module/Validator/ModuleDefinition.php
Normal file
249
core/lib/Thelia/Module/Validator/ModuleDefinition.php
Normal file
@@ -0,0 +1,249 @@
|
||||
<?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\Module\Validator;
|
||||
|
||||
/**
|
||||
* Class ModuleDefinition
|
||||
* @package Thelia\Module\Validator
|
||||
* @author Julien Chanséaume <jchanseaume@openstudio.fr>
|
||||
*/
|
||||
class ModuleDefinition
|
||||
{
|
||||
/** @var string */
|
||||
protected $code;
|
||||
|
||||
/** @var string */
|
||||
protected $namespace;
|
||||
|
||||
/** @var string */
|
||||
protected $type;
|
||||
|
||||
/** @var string */
|
||||
protected $logo;
|
||||
|
||||
/** @var array */
|
||||
protected $languages = [];
|
||||
|
||||
/** @var array */
|
||||
protected $descriptives = [];
|
||||
|
||||
/** @var string */
|
||||
protected $theliaVersion;
|
||||
|
||||
/** @var string */
|
||||
protected $version;
|
||||
|
||||
/** @var array */
|
||||
protected $dependencies = [];
|
||||
|
||||
/** @var string */
|
||||
protected $documentation;
|
||||
|
||||
/** @var string */
|
||||
protected $stability;
|
||||
|
||||
/** @var array */
|
||||
protected $authors = [];
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getAuthors()
|
||||
{
|
||||
return $this->authors;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $authors
|
||||
*/
|
||||
public function setAuthors($authors)
|
||||
{
|
||||
$this->authors = $authors;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getCode()
|
||||
{
|
||||
return $this->code;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $code
|
||||
*/
|
||||
public function setCode($code)
|
||||
{
|
||||
$this->code = $code;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getDependencies()
|
||||
{
|
||||
return $this->dependencies;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $dependencies
|
||||
*/
|
||||
public function setDependencies($dependencies)
|
||||
{
|
||||
$this->dependencies = $dependencies;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getDescriptives()
|
||||
{
|
||||
return $this->descriptives;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $descriptives
|
||||
*/
|
||||
public function setDescriptives($descriptives)
|
||||
{
|
||||
$this->descriptives = $descriptives;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getDocumentation()
|
||||
{
|
||||
return $this->documentation;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $documentation
|
||||
*/
|
||||
public function setDocumentation($documentation)
|
||||
{
|
||||
$this->documentation = $documentation;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getLanguages()
|
||||
{
|
||||
return $this->languages;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $languages
|
||||
*/
|
||||
public function setLanguages($languages)
|
||||
{
|
||||
$this->languages = $languages;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getLogo()
|
||||
{
|
||||
return $this->logo;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $logo
|
||||
*/
|
||||
public function setLogo($logo)
|
||||
{
|
||||
$this->logo = $logo;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getTheliaVersion()
|
||||
{
|
||||
return $this->theliaVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $theliaVersion
|
||||
*/
|
||||
public function setTheliaVersion($theliaVersion)
|
||||
{
|
||||
$this->theliaVersion = $theliaVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getNamespace()
|
||||
{
|
||||
return $this->namespace;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $namespace
|
||||
*/
|
||||
public function setNamespace($namespace)
|
||||
{
|
||||
$this->namespace = $namespace;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getStability()
|
||||
{
|
||||
return $this->stability;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $stability
|
||||
*/
|
||||
public function setStability($stability)
|
||||
{
|
||||
$this->stability = $stability;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getType()
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $type
|
||||
*/
|
||||
public function setType($type)
|
||||
{
|
||||
$this->type = $type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getVersion()
|
||||
{
|
||||
return $this->version;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $version
|
||||
*/
|
||||
public function setVersion($version)
|
||||
{
|
||||
$this->version = $version;
|
||||
}
|
||||
}
|
||||
532
core/lib/Thelia/Module/Validator/ModuleValidator.php
Normal file
532
core/lib/Thelia/Module/Validator/ModuleValidator.php
Normal file
@@ -0,0 +1,532 @@
|
||||
<?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\Module\Validator;
|
||||
|
||||
use Symfony\Component\Config\Definition\Exception\Exception;
|
||||
use Symfony\Component\Filesystem\Filesystem;
|
||||
use Thelia\Core\Thelia;
|
||||
use Thelia\Core\Translation\Translator;
|
||||
use Thelia\Exception\FileNotFoundException;
|
||||
use Thelia\Exception\ModuleException;
|
||||
use Thelia\Model\Module;
|
||||
use Thelia\Model\ModuleQuery;
|
||||
use Thelia\Module\BaseModule;
|
||||
use Thelia\Module\Exception\InvalidXmlDocumentException;
|
||||
use Thelia\Module\ModuleDescriptorValidator;
|
||||
use Thelia\Tools\Version\Version;
|
||||
|
||||
/**
|
||||
* Class ModuleValidartor
|
||||
*
|
||||
* @package Thelia\Module\Validator
|
||||
* @author Julien Chanséaume <jchanseaume@openstudio.fr>
|
||||
*/
|
||||
class ModuleValidator
|
||||
{
|
||||
protected $modulePath;
|
||||
|
||||
/** @var \SimpleXMLElement */
|
||||
protected $moduleDescriptor;
|
||||
|
||||
/** @var ModuleDefinition */
|
||||
protected $moduleDefinition;
|
||||
|
||||
protected $moduleVersion;
|
||||
|
||||
/** @var Translator */
|
||||
protected $translator;
|
||||
|
||||
/** @var array array of errors */
|
||||
protected $errors = [];
|
||||
|
||||
protected $moduleDirName;
|
||||
|
||||
/**
|
||||
* @param string $modulePath the path of the module directory
|
||||
* @param \Thelia\Core\Translation\Translator $translator FOR UNIT TEST PURPOSE ONLY
|
||||
*/
|
||||
public function __construct($modulePath = null, $translator = null)
|
||||
{
|
||||
$this->translator = $translator;
|
||||
|
||||
$this->modulePath = $modulePath;
|
||||
|
||||
$this->moduleDirName = basename($this->modulePath);
|
||||
|
||||
$this->checkDirectoryStructure();
|
||||
|
||||
$this->loadModuleDescriptor();
|
||||
|
||||
$this->loadModuleDefinition();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $modulePath
|
||||
*/
|
||||
public function setModulePath($modulePath)
|
||||
{
|
||||
$this->modulePath = $modulePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getModulePath()
|
||||
{
|
||||
return $this->modulePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ModuleDescriptorValidator|null
|
||||
*/
|
||||
public function getModuleDescriptor()
|
||||
{
|
||||
return $this->moduleDescriptor;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ModuleDefinition|null
|
||||
*/
|
||||
public function getModuleDefinition()
|
||||
{
|
||||
return $this->moduleDefinition;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getModuleVersion()
|
||||
{
|
||||
return $this->moduleVersion;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getErrors()
|
||||
{
|
||||
return $this->errors;
|
||||
}
|
||||
|
||||
protected function trans($id, array $parameters = [])
|
||||
{
|
||||
if (null === $this->translator) {
|
||||
try {
|
||||
$this->translator = Translator::getInstance();
|
||||
} catch (\RuntimeException $e) {
|
||||
return strtr($id, $parameters);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->translator->trans($id, $parameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a module, checks :
|
||||
*
|
||||
* - version of Thelia
|
||||
* - modules dependencies
|
||||
*
|
||||
* @param bool $checkCurrentVersion if true it will also check if the module is
|
||||
* already installed (not activated - present in module list)
|
||||
*/
|
||||
public function validate($checkCurrentVersion = true)
|
||||
{
|
||||
if (null === $this->moduleDescriptor) {
|
||||
throw new \Exception(
|
||||
$this->trans(
|
||||
"The %name module definition has not been initialized.",
|
||||
[ '%name' => $this->moduleDirName ]
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$this->checkVersion();
|
||||
|
||||
if (true === $checkCurrentVersion) {
|
||||
$this->checkModuleVersion();
|
||||
}
|
||||
|
||||
$this->checkModuleDependencies();
|
||||
$this->checkModulePropelSchema();
|
||||
}
|
||||
|
||||
protected function checkDirectoryStructure()
|
||||
{
|
||||
if (false === file_exists($this->modulePath)) {
|
||||
throw new FileNotFoundException(
|
||||
$this->trans(
|
||||
"Module %name directory doesn't exists.",
|
||||
[ '%name' => $this->moduleDirName]
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$path = sprintf("%s/Config/module.xml", $this->modulePath);
|
||||
if (false === file_exists($path)) {
|
||||
throw new FileNotFoundException(
|
||||
$this->trans(
|
||||
"Module %name should have a module.xml in the Config directory.",
|
||||
[ '%name' => $this->moduleDirName]
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$path = sprintf("%s/Config/config.xml", $this->modulePath);
|
||||
if (false === file_exists($path)) {
|
||||
throw new FileNotFoundException(
|
||||
$this->trans(
|
||||
"Module %name should have a config.xml in the Config directory.",
|
||||
[ '%name' => $this->moduleDirName]
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
protected function loadModuleDescriptor()
|
||||
{
|
||||
$path = sprintf("%s/Config/module.xml", $this->modulePath);
|
||||
|
||||
$descriptorValidator = new ModuleDescriptorValidator();
|
||||
|
||||
try {
|
||||
// validation with xsd
|
||||
$this->moduleDescriptor = $descriptorValidator->getDescriptor($path);
|
||||
$this->moduleVersion = $descriptorValidator->getModuleVersion();
|
||||
} catch (InvalidXmlDocumentException $ex) {
|
||||
throw $ex;
|
||||
}
|
||||
}
|
||||
|
||||
public function loadModuleDefinition()
|
||||
{
|
||||
if (null === $this->moduleDescriptor) {
|
||||
throw new \Exception(
|
||||
$this->trans(
|
||||
"The %name module descriptor has not been initialized.",
|
||||
[ '%name' => $this->moduleDirName ]
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$moduleDefinition = new ModuleDefinition();
|
||||
|
||||
// Try to guess the proper module name, using the descriptor information.
|
||||
$fullnamespace = trim((string)$this->moduleDescriptor->fullnamespace);
|
||||
|
||||
$namespaceComponents = explode("\\", $fullnamespace);
|
||||
|
||||
if (! isset($namespaceComponents[0]) || empty($namespaceComponents[0])) {
|
||||
throw new ModuleException(
|
||||
$this->trans(
|
||||
"Unable to get module code from the fullnamespace element of the module descriptor: '%val'",
|
||||
[
|
||||
'%name' => $this->moduleDirName,
|
||||
'%val' => $fullnamespace
|
||||
]
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Assume the module code is the first component of the declared namespace
|
||||
$moduleDefinition->setCode($namespaceComponents[0]);
|
||||
$moduleDefinition->setNamespace($fullnamespace);
|
||||
$moduleDefinition->setVersion((string)$this->moduleDescriptor->version);
|
||||
|
||||
$this->getModuleLanguages($moduleDefinition);
|
||||
|
||||
$this->getModuleDescriptives($moduleDefinition);
|
||||
|
||||
$this->getModuleDependencies($moduleDefinition);
|
||||
|
||||
$this->getModuleAuthors($moduleDefinition);
|
||||
|
||||
$moduleDefinition->setLogo((string)$this->moduleDescriptor->logo);
|
||||
$moduleDefinition->setTheliaVersion((string)$this->moduleDescriptor->thelia);
|
||||
$moduleDefinition->setType((string)$this->moduleDescriptor->type);
|
||||
$moduleDefinition->setStability((string)$this->moduleDescriptor->stability);
|
||||
|
||||
// documentation
|
||||
$moduleDefinition->setDocumentation((string)$this->moduleDescriptor->documentation);
|
||||
|
||||
$this->moduleDefinition = $moduleDefinition;
|
||||
}
|
||||
|
||||
public function checkModulePropelSchema()
|
||||
{
|
||||
$schemaFile = $this->getModulePath() . DS . "Config" . DS . "schema.xml";
|
||||
$fs = new Filesystem();
|
||||
|
||||
if ($fs->exists($schemaFile) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (preg_match('/<behavior.*name="versionable".*\/>/s', preg_replace('/<!--(.|\s)*?-->/', '', file_get_contents($schemaFile)))) {
|
||||
throw new ModuleException(
|
||||
"On Thelia version >= 2.4.0 the behavior \"versionnable\" is not available for modules, please remove this behavior from your module schema."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
protected function checkVersion()
|
||||
{
|
||||
if ($this->moduleDefinition->getTheliaVersion()) {
|
||||
if (!Version::test(Thelia::THELIA_VERSION, $this->moduleDefinition->getTheliaVersion(), false, ">=")) {
|
||||
throw new ModuleException(
|
||||
$this->trans(
|
||||
"The module %name requires Thelia %version or newer",
|
||||
[
|
||||
'%name' => $this->moduleDirName,
|
||||
'%version' => $this->moduleDefinition->getTheliaVersion()
|
||||
]
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function checkModuleVersion()
|
||||
{
|
||||
$module = ModuleQuery::create()
|
||||
->findOneByFullNamespace($this->moduleDefinition->getNamespace());
|
||||
|
||||
if (null !== $module) {
|
||||
if (version_compare($module->getVersion(), $this->moduleDefinition->getVersion(), '>=')) {
|
||||
throw new ModuleException(
|
||||
$this->trans(
|
||||
"The module %name is already installed in the same or greater version.",
|
||||
[ '%name' => $this->moduleDirName]
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function checkModuleDependencies()
|
||||
{
|
||||
$errors = [];
|
||||
|
||||
foreach ($this->moduleDefinition->getDependencies() as $dependency) {
|
||||
$module = ModuleQuery::create()
|
||||
->findOneByCode($dependency[0]);
|
||||
|
||||
$pass = false;
|
||||
|
||||
if (null !== $module) {
|
||||
if ($module->getActivate() === BaseModule::IS_ACTIVATED) {
|
||||
if ("" == $dependency[1] || Version::test($module->getVersion(), $dependency[1], false, ">=")) {
|
||||
$pass = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (false === $pass) {
|
||||
if ('' !== $dependency[1]) {
|
||||
$errors[] = $this->trans(
|
||||
'%module (version: %version)',
|
||||
[
|
||||
'%module' => $dependency[0],
|
||||
'%version' => $dependency[1]
|
||||
]
|
||||
);
|
||||
} else {
|
||||
$errors[] = sprintf('%s', $dependency[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (\count($errors) > 0) {
|
||||
$errorsMessage = $this->trans(
|
||||
'To activate module %name, the following modules should be activated first: %modules',
|
||||
['%name' => $this->moduleDirName, '%modules' => implode(', ', $errors)]
|
||||
);
|
||||
|
||||
throw new ModuleException($errorsMessage);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array of modules that depend of the current module
|
||||
*
|
||||
* @param bool|null $active if true only search in activated module, false only deactivated and null on all modules
|
||||
* @return array array of array with `code` which is the module code that depends of this current module and
|
||||
* `version` which is the required version of current module
|
||||
*/
|
||||
public function getModulesDependOf($active = true)
|
||||
{
|
||||
$code = $this->getModuleDefinition()->getCode();
|
||||
$query = ModuleQuery::create();
|
||||
$dependantModules = [];
|
||||
|
||||
if (true === $active) {
|
||||
$query->findByActivate(1);
|
||||
} elseif (false === $active) {
|
||||
$query->findByActivate(0);
|
||||
}
|
||||
|
||||
$modules = $query->find();
|
||||
|
||||
/** @var Module $module */
|
||||
foreach ($modules as $module) {
|
||||
try {
|
||||
$validator = new ModuleValidator($module->getAbsoluteBaseDir());
|
||||
|
||||
$definition = $validator->getModuleDefinition();
|
||||
$dependencies = $definition->getDependencies();
|
||||
|
||||
if (\count($dependencies) > 0) {
|
||||
foreach ($dependencies as $dependency) {
|
||||
if ($dependency[0] == $code) {
|
||||
$dependantModules[] = [
|
||||
'code' => $definition->getCode(),
|
||||
'version' => $dependency[1]
|
||||
];
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (\Exception $ex) {
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
return $dependantModules;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the dependencies of this module.
|
||||
* @param bool $recursive Whether to also get the dependencies of dependencies, their dependencies, and so on...
|
||||
* @return array Array of dependencies as ["code" => ..., "version" => ...]. No check for duplicates is made.
|
||||
*/
|
||||
public function getCurrentModuleDependencies($recursive = false)
|
||||
{
|
||||
if (empty($this->moduleDescriptor->required)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$dependencies = [];
|
||||
foreach ($this->moduleDescriptor->required->module as $dependency) {
|
||||
$dependencyArray = [
|
||||
"code" => (string)$dependency,
|
||||
"version" => (string)$dependency['version'],
|
||||
];
|
||||
if (!\in_array($dependencyArray, $dependencies)) {
|
||||
$dependencies[] = $dependencyArray;
|
||||
}
|
||||
|
||||
if ($recursive) {
|
||||
$recursiveModuleValidator = new ModuleValidator(THELIA_MODULE_DIR . '/' . (string)$dependency);
|
||||
array_merge(
|
||||
$dependencies,
|
||||
$recursiveModuleValidator->getCurrentModuleDependencies(true)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $dependencies;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ModuleDefinition $moduleDefinition
|
||||
*/
|
||||
protected function getModuleLanguages(ModuleDefinition $moduleDefinition)
|
||||
{
|
||||
$languages = [];
|
||||
if ($this->getModuleVersion() != "1") {
|
||||
foreach ($this->moduleDescriptor->languages->language as $language) {
|
||||
$languages[] = (string)$language;
|
||||
}
|
||||
}
|
||||
$moduleDefinition->setLanguages($languages);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ModuleDefinition $moduleDefinition
|
||||
*/
|
||||
protected function getModuleDescriptives(ModuleDefinition $moduleDefinition)
|
||||
{
|
||||
$descriptives = [];
|
||||
foreach ($this->moduleDescriptor->descriptive as $descriptive) {
|
||||
$descriptives[(string)$descriptive['locale']] = [
|
||||
'title' => (string)$descriptive->title,
|
||||
'subtitle' => (string)$descriptive->subtitle,
|
||||
'description' => (string)$descriptive->description,
|
||||
'postscriptum' => (string)$descriptive->postscriptum,
|
||||
];
|
||||
}
|
||||
$moduleDefinition->setDescriptives($descriptives);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ModuleDefinition $moduleDefinition
|
||||
*/
|
||||
protected function getModuleDependencies(ModuleDefinition $moduleDefinition)
|
||||
{
|
||||
$dependencies = [];
|
||||
if (is_countable($this->moduleDescriptor->required) && 0 !== \count($this->moduleDescriptor->required)) {
|
||||
foreach ($this->moduleDescriptor->required->module as $dependency) {
|
||||
$dependencies[] = [
|
||||
(string)$dependency,
|
||||
(string)$dependency['version'],
|
||||
];
|
||||
}
|
||||
}
|
||||
$moduleDefinition->setDependencies($dependencies);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ModuleDefinition $moduleDefinition
|
||||
*/
|
||||
protected function getModuleAuthors(ModuleDefinition $moduleDefinition)
|
||||
{
|
||||
$authors = [];
|
||||
|
||||
if (is_countable($this->moduleDescriptor->author) && 0 !== \count($this->moduleDescriptor->author)) {
|
||||
foreach ($this->moduleDescriptor->author as $author) {
|
||||
$authors[] = [
|
||||
(string)$author->name,
|
||||
(string)$author->company,
|
||||
(string)$author->email,
|
||||
(string)$author->website
|
||||
];
|
||||
}
|
||||
} else {
|
||||
$authors = $this->getModuleAuthors22($moduleDefinition);
|
||||
}
|
||||
$moduleDefinition->setAuthors($authors);
|
||||
}
|
||||
|
||||
protected function getModuleAuthors22(ModuleDefinition $moduleDefinition)
|
||||
{
|
||||
$authors = [];
|
||||
|
||||
if (!is_countable($this->moduleDescriptor->authors->author)
|
||||
|| 0 === \count($this->moduleDescriptor->authors->author)
|
||||
) {
|
||||
return $authors;
|
||||
}
|
||||
|
||||
foreach ($this->moduleDescriptor->authors->author as $author) {
|
||||
$authors[] = [
|
||||
(string)$author->name,
|
||||
(string)$author->company,
|
||||
(string)$author->email,
|
||||
(string)$author->website
|
||||
];
|
||||
}
|
||||
|
||||
return $authors;
|
||||
}
|
||||
}
|
||||
159
core/lib/Thelia/Module/schema/module/module-2_1.xsd
Normal file
159
core/lib/Thelia/Module/schema/module/module-2_1.xsd
Normal file
@@ -0,0 +1,159 @@
|
||||
<?xml version="1.0" encoding='UTF-8'?>
|
||||
<xs:schema
|
||||
xmlns="http://thelia.net/schema/dic/module"
|
||||
xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||
targetNamespace="http://thelia.net/schema/dic/module"
|
||||
attributeFormDefault="unqualified"
|
||||
elementFormDefault="qualified"
|
||||
>
|
||||
|
||||
<xs:element name="module">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element type="xs:string" minOccurs="1" name="fullnamespace">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The full namespace for the main class module (for example MyModule\MyModule)</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="descriptive" minOccurs="1" maxOccurs="unbounded">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Complete description, each description must be identify by ISO CODE 639</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element type="xs:string" name="title" minOccurs="1" maxOccurs="1"/>
|
||||
<xs:element type="xs:string" name="subtitle" minOccurs="0" maxOccurs="1"/>
|
||||
<xs:element type="xs:string" name="description" minOccurs="0" maxOccurs="1"/>
|
||||
<xs:element type="xs:string" name="postscriptum" minOccurs="0" maxOccurs="1"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute type="xs:string" name="locale"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element type="xs:string" minOccurs="0" maxOccurs="1" name="logo">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The path to the logo of the module (for example myModule.png). The recommended size is 128x128.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element type="xs:string" minOccurs="0" maxOccurs="1" name="images-folder">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The folder that contains the images to deploy.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="languages">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Module languages supported : fr_FR, en_US, ...</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element name="language" minOccurs="1" maxOccurs="unbounded">
|
||||
<xs:simpleType>
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:pattern value="[a-z]{2}_[A-Z]{2}"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:element>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element type="xs:string" name="version">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Module version</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="author">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Module author</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element type="xs:string" name="name" minOccurs="1" maxOccurs="1"/>
|
||||
<xs:element type="xs:string" name="company" minOccurs="0" maxOccurs="1"/>
|
||||
<xs:element type="xs:string" name="email" minOccurs="1" maxOccurs="1"/>
|
||||
<xs:element type="xs:anyURI" name="website" minOccurs="0" maxOccurs="1"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="type">
|
||||
<xs:annotation>
|
||||
<xs:documentation>module type : classic, delivery, payment, ...</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:simpleType>
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="classic"/>
|
||||
<xs:enumeration value="delivery"/>
|
||||
<xs:enumeration value="payment"/>
|
||||
<xs:enumeration value="marketplace"/>
|
||||
<xs:enumeration value="price"/>
|
||||
<xs:enumeration value="accounting"/>
|
||||
<xs:enumeration value="seo"/>
|
||||
<xs:enumeration value="administration"/>
|
||||
<xs:enumeration value="statistic"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:element>
|
||||
<xs:element name="tags" minOccurs="0" maxOccurs="1">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Module tags</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element type="xs:string" name="tag" minOccurs="0" maxOccurs="unbounded" />
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="required" minOccurs="0" maxOccurs="1">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Plugins that should be presents. Plugins are identified by their codes and an optional minimal version</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element name="module" maxOccurs="unbounded" minOccurs="0">
|
||||
<xs:complexType>
|
||||
<xs:simpleContent>
|
||||
<xs:extension base="xs:string">
|
||||
<xs:attribute type="xs:string" name="version" use="optional"/>
|
||||
</xs:extension>
|
||||
</xs:simpleContent>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="thelia" minOccurs="0" type="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>minimum required version of Thelia in 'dot' format (for example 1.2.3.4)</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="stability">
|
||||
<xs:annotation>
|
||||
<xs:documentation>current module stability: alpha, beta, rc, prod</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:simpleType>
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="alpha"/>
|
||||
<xs:enumeration value="beta"/>
|
||||
<xs:enumeration value="rc"/>
|
||||
<xs:enumeration value="prod"/>
|
||||
<xs:enumeration value="other"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:element>
|
||||
<xs:element type="xs:string" name="documentation" minOccurs="0" maxOccurs="1">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The name of the directory containing te documentation.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element type="xs:anyURI" name="urlmiseajour" minOccurs="0" maxOccurs="1">
|
||||
<xs:annotation>
|
||||
<xs:documentation>URL to test if a new version of the module exists. Will be called with two get parameters : module name, current version</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element type="xs:anyURI" name="updateurl" minOccurs="0" maxOccurs="1">
|
||||
<xs:annotation>
|
||||
<xs:documentation>URL to test if a new version of the module exists. Will be called with two get parameters : module name, current version</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
</xs:schema>
|
||||
187
core/lib/Thelia/Module/schema/module/module-2_2.xsd
Normal file
187
core/lib/Thelia/Module/schema/module/module-2_2.xsd
Normal file
@@ -0,0 +1,187 @@
|
||||
<?xml version="1.0" encoding='UTF-8'?>
|
||||
<xs:schema
|
||||
xmlns="http://thelia.net/schema/dic/module"
|
||||
xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||
targetNamespace="http://thelia.net/schema/dic/module"
|
||||
attributeFormDefault="unqualified"
|
||||
elementFormDefault="qualified"
|
||||
>
|
||||
|
||||
<xs:element name="module">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element type="xs:string" minOccurs="1" name="fullnamespace">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The full namespace for the main class module (for example MyModule\MyModule)</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="descriptive" minOccurs="1" maxOccurs="unbounded">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Complete description, each description must be identify by ISO CODE 639</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element type="xs:string" name="title" minOccurs="1" maxOccurs="1"/>
|
||||
<xs:element type="xs:string" name="subtitle" minOccurs="0" maxOccurs="1"/>
|
||||
<xs:element type="xs:string" name="description" minOccurs="0" maxOccurs="1"/>
|
||||
<xs:element type="xs:string" name="postscriptum" minOccurs="0" maxOccurs="1"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute type="xs:string" name="locale"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element type="xs:string" minOccurs="0" maxOccurs="1" name="logo">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The path to the logo of the module (for example myModule.png). The recommended size is 128x128.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element type="xs:string" minOccurs="0" maxOccurs="1" name="images-folder">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The folder that contains the images to deploy.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="languages">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Module languages supported : fr_FR, en_US, ...</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element name="language" minOccurs="1" maxOccurs="unbounded">
|
||||
<xs:simpleType>
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:pattern value="[a-z]{2}_[A-Z]{2}"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:element>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element type="xs:string" name="version">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Module version</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="authors" maxOccurs="1" minOccurs="0">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Module authors</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element name="author" maxOccurs="unbounded" minOccurs="0">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element type="xs:string" name="name" minOccurs="1" maxOccurs="1"/>
|
||||
<xs:element type="xs:string" name="company" minOccurs="0" maxOccurs="1"/>
|
||||
<xs:element type="xs:string" name="email" minOccurs="1" maxOccurs="1"/>
|
||||
<xs:element type="xs:anyURI" name="website" minOccurs="0" maxOccurs="1"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="type">
|
||||
<xs:annotation>
|
||||
<xs:documentation>module type : classic, delivery, payment, ...</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:simpleType>
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="classic"/>
|
||||
<xs:enumeration value="delivery"/>
|
||||
<xs:enumeration value="payment"/>
|
||||
<xs:enumeration value="marketplace"/>
|
||||
<xs:enumeration value="price"/>
|
||||
<xs:enumeration value="accounting"/>
|
||||
<xs:enumeration value="seo"/>
|
||||
<xs:enumeration value="administration"/>
|
||||
<xs:enumeration value="statistic"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:element>
|
||||
<xs:element name="tags" minOccurs="0" maxOccurs="1">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Module tags</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element type="xs:string" name="tag" minOccurs="0" maxOccurs="unbounded" />
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="required" minOccurs="0" maxOccurs="1">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Plugins that should be presents. Plugins are identified by their codes and an optional minimal version</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element name="module" maxOccurs="unbounded" minOccurs="0">
|
||||
<xs:complexType>
|
||||
<xs:simpleContent>
|
||||
<xs:extension base="xs:string">
|
||||
<xs:attribute type="xs:string" name="version" use="optional"/>
|
||||
</xs:extension>
|
||||
</xs:simpleContent>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="thelia" minOccurs="0" type="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>minimum required version of Thelia in 'dot' format (for example 1.2.3.4)</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="stability">
|
||||
<xs:annotation>
|
||||
<xs:documentation>current module stability: alpha, beta, rc, prod</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:simpleType>
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="alpha"/>
|
||||
<xs:enumeration value="beta"/>
|
||||
<xs:enumeration value="rc"/>
|
||||
<xs:enumeration value="prod"/>
|
||||
<xs:enumeration value="other"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:element>
|
||||
<xs:element type="xs:string" name="documentation" minOccurs="0" maxOccurs="1">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The name of the directory containing te documentation.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element type="xs:anyURI" name="urlmiseajour" minOccurs="0" maxOccurs="1">
|
||||
<xs:annotation>
|
||||
<xs:documentation>URL to test if a new version of the module exists. Will be called with two get parameters : module name, current version</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element type="xs:anyURI" name="updateurl" minOccurs="0" maxOccurs="1">
|
||||
<xs:annotation>
|
||||
<xs:documentation>URL to test if a new version of the module exists. Will be called with two get parameters : module name, current version</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="mandatory" minOccurs="0" maxOccurs="1">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Flag for protected module</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:simpleType>
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="0"/>
|
||||
<xs:enumeration value="1"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:element>
|
||||
<xs:element name="hidden" minOccurs="0" maxOccurs="1">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Flag for visible module</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:simpleType>
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="0"/>
|
||||
<xs:enumeration value="1"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:element>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
</xs:schema>
|
||||
110
core/lib/Thelia/Module/schema/module/module.xsd
Normal file
110
core/lib/Thelia/Module/schema/module/module.xsd
Normal file
@@ -0,0 +1,110 @@
|
||||
<?xml version="1.0" encoding='UTF-8'?>
|
||||
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
||||
<xs:element name="module">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element type="xs:string" minOccurs="1" name="fullnamespace">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The full namespace for the main class module (for example MyModule\MyModule)</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="descriptive" maxOccurs="unbounded">
|
||||
<xs:annotation>
|
||||
<xs:documentation>complete description, each description must be identify by ISO CODE 639</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element type="xs:string" name="title" minOccurs="1" maxOccurs="1"/>
|
||||
<xs:element type="xs:string" name="subtitle" minOccurs="0" maxOccurs="1"/>
|
||||
<xs:element type="xs:string" name="description" minOccurs="0" maxOccurs="1"/>
|
||||
<xs:element type="xs:string" name="postscriptum" minOccurs="0" maxOccurs="1"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute type="xs:string" name="locale"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element type="xs:string" name="version">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Module version</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="author">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Module author</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element type="xs:string" name="name" minOccurs="1" maxOccurs="1"/>
|
||||
<xs:element type="xs:string" name="company" minOccurs="0" maxOccurs="1"/>
|
||||
<xs:element type="xs:string" name="email" minOccurs="1" maxOccurs="1"/>
|
||||
<xs:element type="xs:anyURI" name="website" minOccurs="0" maxOccurs="1"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="type">
|
||||
<xs:annotation>
|
||||
<xs:documentation>module type : classic, delivery, payment</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:simpleType>
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="classic"/>
|
||||
<xs:enumeration value="delivery"/>
|
||||
<xs:enumeration value="payment"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:element>
|
||||
<xs:element name="required" minOccurs="0" maxOccurs="1">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Les plugins qui doivent déjà être présents</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element name="module" maxOccurs="unbounded" minOccurs="0">
|
||||
<xs:complexType>
|
||||
<xs:simpleContent>
|
||||
<xs:extension base="xs:string">
|
||||
<xs:attribute type="xs:string" name="version" use="optional"/>
|
||||
</xs:extension>
|
||||
</xs:simpleContent>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="thelia">
|
||||
<xs:annotation>
|
||||
<xs:documentation>minimum required version of Thelia in 'dot' format (for example 1.2.3.4)</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:simpleType>
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:pattern value="[0-9.]+"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:element>
|
||||
<xs:element name="stability">
|
||||
<xs:annotation>
|
||||
<xs:documentation>current module stability: alpha, beta, rc, prod</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:simpleType>
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="alpha"/>
|
||||
<xs:enumeration value="beta"/>
|
||||
<xs:enumeration value="rc"/>
|
||||
<xs:enumeration value="prod"/>
|
||||
<xs:enumeration value="other"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:element>
|
||||
<xs:element type="xs:string" name="documentation" minOccurs="0" maxOccurs="1">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Le nom du fichier contenant la documentation. Ce fichier doit se trouver dans le répertoire du plugin.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element type="xs:anyURI" name="urlmiseajour" minOccurs="0" maxOccurs="1">
|
||||
<xs:annotation>
|
||||
<xs:documentation>L'URL a interroger pour vérifier la présence d'une nouvelle version, appellé avec le nom du plugin et sa version</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
</xs:schema>
|
||||
Reference in New Issue
Block a user