Inital commit

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

View File

@@ -11,8 +11,10 @@
/*************************************************************************************/
namespace Thelia\Action;
use Propel\Runtime\Exception\PropelException;
use Propel\Runtime\Propel;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Thelia\Core\Event\Address\AddressCreateOrUpdateEvent;
use Thelia\Core\Event\Address\AddressEvent;
@@ -23,23 +25,22 @@ use Thelia\Model\Map\AddressTableMap;
/**
* Class Address
* @package Thelia\Action
* @author Manuel Raynaud <mraynaud@openstudio.fr>
* @author Manuel Raynaud <manu@raynaud.io>
*/
class Address extends BaseAction implements EventSubscriberInterface
{
public function create(AddressCreateOrUpdateEvent $event)
public function create(AddressCreateOrUpdateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$address = new AddressModel();
$address->setCustomer($event->getCustomer());
$this->createOrUpdate($address, $event);
$this->createOrUpdate($address, $event, $dispatcher);
}
public function update(AddressCreateOrUpdateEvent $event)
public function update(AddressCreateOrUpdateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$addressModel = $event->getAddress();
$this->createOrUpdate($addressModel, $event);
$this->createOrUpdate($addressModel, $event, $dispatcher);
}
public function delete(AddressEvent $event)
@@ -56,9 +57,9 @@ class Address extends BaseAction implements EventSubscriberInterface
$address->makeItDefault();
}
protected function createOrUpdate(AddressModel $addressModel, AddressCreateOrUpdateEvent $event)
protected function createOrUpdate(AddressModel $addressModel, AddressCreateOrUpdateEvent $event, $dispatcher)
{
$addressModel->setDispatcher($event->getDispatcher());
$addressModel->setDispatcher($dispatcher);
$con = Propel::getWriteConnection(AddressTableMap::DATABASE_NAME);
$con->beginTransaction();
try {
@@ -73,6 +74,7 @@ class Address extends BaseAction implements EventSubscriberInterface
->setZipcode($event->getZipcode())
->setCity($event->getCity())
->setCountryId($event->getCountry())
->setStateId($event->getState())
->setCellphone($event->getCellphone())
->setPhone($event->getPhone())
->setCompany($event->getCompany())
@@ -85,33 +87,14 @@ class Address extends BaseAction implements EventSubscriberInterface
$event->setAddress($addressModel);
$con->commit();
} catch (PropelException $e) {
$con->rollback();
throw $e;
}
}
/**
* Returns an array of event names this subscriber wants to listen to.
*
* The array keys are event names and the value can be:
*
* * The method name to call (priority defaults to 0)
* * An array composed of the method name to call and the priority
* * An array of arrays composed of the method names to call and respective
* priorities, or 0 if unset
*
* For instance:
*
* * array('eventName' => 'methodName')
* * array('eventName' => array('methodName', $priority))
* * array('eventName' => array(array('methodName1', $priority), array('methodName2'))
*
* @return array The event names to listen to
*
* @api
* {@inheritdoc}
*/
public static function getSubscribedEvents()
{

View File

@@ -12,26 +12,45 @@
namespace Thelia\Action;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Thelia\Core\Event\Administrator\AdministratorEvent;
use Thelia\Core\Event\Administrator\AdministratorUpdatePasswordEvent;
use Thelia\Core\Event\TheliaEvents;
use Thelia\Mailer\MailerFactory;
use Thelia\Model\Admin as AdminModel;
use Thelia\Model\AdminQuery;
use Thelia\Model\ConfigQuery;
use Thelia\Tools\TokenProvider;
class Administrator extends BaseAction implements EventSubscriberInterface
{
/** @var MailerFactory */
protected $mailer;
/** @var TokenProvider */
protected $tokenProvider;
public function __construct(MailerFactory $mailer, TokenProvider $tokenProvider)
{
$this->mailer = $mailer;
$this->tokenProvider = $tokenProvider;
}
/**
* @param AdministratorEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function create(AdministratorEvent $event)
public function create(AdministratorEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$administrator = new AdminModel();
$administrator
->setDispatcher($event->getDispatcher())
->setDispatcher($dispatcher)
->setFirstname($event->getFirstname())
->setLastname($event->getLastname())
->setEmail($event->getEmail())
->setLogin($event->getLogin())
->setPassword($event->getPassword())
->setProfileId($event->getProfile())
@@ -45,16 +64,18 @@ class Administrator extends BaseAction implements EventSubscriberInterface
/**
* @param AdministratorEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function update(AdministratorEvent $event)
public function update(AdministratorEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if (null !== $administrator = AdminQuery::create()->findPk($event->getId())) {
$administrator
->setDispatcher($event->getDispatcher())
->setDispatcher($dispatcher)
->setFirstname($event->getFirstname())
->setLastname($event->getLastname())
->setLogin($event->getLogin())
->setEmail($event->getEmail())
->setProfileId($event->getProfile())
->setLocale($event->getLocale())
;
@@ -75,7 +96,6 @@ class Administrator extends BaseAction implements EventSubscriberInterface
public function delete(AdministratorEvent $event)
{
if (null !== $administrator = AdminQuery::create()->findPk($event->getId())) {
$administrator
->delete()
;
@@ -87,12 +107,40 @@ class Administrator extends BaseAction implements EventSubscriberInterface
public function updatePassword(AdministratorUpdatePasswordEvent $event)
{
$admin = $event->getAdmin();
$admin->setPassword($event->getPassword())
$admin
->setPassword($event->getPassword())
->setPasswordRenewToken(null)
->save();
}
public function createPassword(AdministratorEvent $event)
{
$admin = $event->getAdministrator();
$email = $admin->getEmail();
if (! empty($email)) {
$renewToken = $this->tokenProvider->getToken();
$admin
->setPasswordRenewToken($renewToken)
->save();
$this->mailer->sendEmailMessage(
'new_admin_password',
[ ConfigQuery::getStoreEmail() => ConfigQuery::getStoreName() ],
[ $email => $admin->getFirstname() . ' ' . $admin->getLastname() ],
[
'token' => $renewToken,
'admin' => $admin
]
);
}
}
/**
* {@inheritDoc}
* {@inheritdoc}
*/
public static function getSubscribedEvents()
{
@@ -100,7 +148,8 @@ class Administrator extends BaseAction implements EventSubscriberInterface
TheliaEvents::ADMINISTRATOR_CREATE => array('create', 128),
TheliaEvents::ADMINISTRATOR_UPDATE => array('update', 128),
TheliaEvents::ADMINISTRATOR_DELETE => array('delete', 128),
TheliaEvents::ADMINISTRATOR_UPDATEPASSWORD => array('updatePassword', 128)
TheliaEvents::ADMINISTRATOR_UPDATEPASSWORD => array('updatePassword', 128),
TheliaEvents::ADMINISTRATOR_CREATEPASSWORD => array('createPassword', 128)
);
}
}

View File

@@ -0,0 +1,65 @@
<?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\Action;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Thelia\Core\Event\Api\ApiCreateEvent;
use Thelia\Core\Event\Api\ApiDeleteEvent;
use Thelia\Core\Event\Api\ApiUpdateEvent;
use Thelia\Core\Event\TheliaEvents;
use Thelia\Model\Api as ApiModel;
/**
* Class Api
* @package Thelia\Action
* @author Manuel Raynaud <manu@raynaud.io>
*/
class Api extends BaseAction implements EventSubscriberInterface
{
public function createApi(ApiCreateEvent $event)
{
$api = new ApiModel();
$api->setLabel($event->getLabel())
->setProfileId($event->getProfile())
->save()
;
}
public function deleteApi(ApiDeleteEvent $event)
{
$api = $event->getApi();
$api->delete();
}
public function updateApi(ApiUpdateEvent $event)
{
$api = $event->getApi();
$api->setProfileId($event->getProfile())
->save();
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents()
{
return [
TheliaEvents::API_CREATE => ['createApi', 128],
TheliaEvents::API_DELETE => ['deleteApi', 128],
TheliaEvents::API_UPDATE => ['updateApi', 128],
];
}
}

View File

@@ -11,52 +11,73 @@
/*************************************************************************************/
namespace Thelia\Action;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Thelia\Core\Event\Area\AreaAddCountryEvent;
use Thelia\Core\Event\Area\AreaCreateEvent;
use Thelia\Core\Event\Area\AreaDeleteEvent;
use Thelia\Core\Event\Area\AreaRemoveCountryEvent;
use Thelia\Core\Event\Area\AreaUpdateEvent;
use Thelia\Core\Event\Area\AreaUpdatePostageEvent;
use Thelia\Core\Event\TheliaEvents;
use Thelia\Model\AreaQuery;
use Thelia\Model\CountryQuery;
use Thelia\Model\Area as AreaModel;
use Thelia\Model\AreaQuery;
use Thelia\Model\CountryArea;
use Thelia\Model\CountryAreaQuery;
/**
* Class Area
* @package Thelia\Action
* @author Manuel Raynaud <mraynaud@openstudio.fr>
* @author Manuel Raynaud <manu@raynaud.io>
*/
class Area extends BaseAction implements EventSubscriberInterface
{
public function addCountry(AreaAddCountryEvent $event)
{
if (null !== $country = CountryQuery::create()->findPk($event->getCountryId())) {
$country->setDispatcher($event->getDispatcher());
$country->setAreaId($event->getAreaId())
->save();
$countryIds = $event->getCountryId();
$event->setArea($country->getArea());
$areaId = $event->getAreaId();
foreach ($countryIds as $countryId) {
$countryArea = new CountryArea();
$country = explode('-', $countryId);
if (count($country) === 1) {
$country[1] = null;
}
if ($country[1] == 0) {
$country[1] = null;
}
$countryArea
->setAreaId($areaId)
->setCountryId($country[0])
->setStateId($country[1])
->save()
;
}
$event->setArea(AreaQuery::create()->findPk($areaId));
}
public function removeCountry(AreaRemoveCountryEvent $event)
{
if (null !== $country = CountryQuery::create()->findPk($event->getCountryId())) {
$event->setArea($country->getArea());
CountryAreaQuery::create()
->filterByCountryId($event->getCountryId())
->filterByStateId($event->getStateId())
->filterByAreaId($event->getAreaId())
->delete();
$country->setDispatcher($event->getDispatcher());
$country->setAreaId(null)
->save();
if (null !== $area = AreaQuery::create()->findPk($event->getAreaId())) {
$event->setArea($area);
}
}
public function updatePostage(AreaUpdatePostageEvent $event)
public function updatePostage(AreaUpdatePostageEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if (null !== $area = AreaQuery::create()->findPk($event->getAreaId())) {
$area->setDispatcher($event->getDispatcher());
$area->setDispatcher($dispatcher);
$area
->setPostage($event->getPostage())
->save();
@@ -65,47 +86,42 @@ class Area extends BaseAction implements EventSubscriberInterface
}
}
public function delete(AreaDeleteEvent $event)
public function delete(AreaDeleteEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if (null !== $area = AreaQuery::create()->findPk($event->getAreaId())) {
$area->setDispatcher($event->getDispatcher());
$area->setDispatcher($dispatcher);
$area->delete();
$event->setArea($area);
}
}
public function create(AreaCreateEvent $event)
public function create(AreaCreateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$area = new AreaModel();
$area
->setDispatcher($event->getDispatcher())
->setDispatcher($dispatcher)
->setName($event->getAreaName())
->save();
$event->setArea($area);
}
public function update(AreaUpdateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if (null !== $area = AreaQuery::create()->findPk($event->getAreaId())) {
$area
->setDispatcher($dispatcher)
->setName($event->getAreaName())
->save();
$event->setArea($area);
}
}
/**
* Returns an array of event names this subscriber wants to listen to.
*
* The array keys are event names and the value can be:
*
* * The method name to call (priority defaults to 0)
* * An array composed of the method name to call and the priority
* * An array of arrays composed of the method names to call and respective
* priorities, or 0 if unset
*
* For instance:
*
* * array('eventName' => 'methodName')
* * array('eventName' => array('methodName', $priority))
* * array('eventName' => array(array('methodName1', $priority), array('methodName2'))
*
* @return array The event names to listen to
*
* @api
* {@inheritdoc}
*/
public static function getSubscribedEvents()
{
@@ -114,7 +130,8 @@ class Area extends BaseAction implements EventSubscriberInterface
TheliaEvents::AREA_REMOVE_COUNTRY => array('removeCountry', 128),
TheliaEvents::AREA_POSTAGE_UPDATE => array('updatePostage', 128),
TheliaEvents::AREA_DELETE => array('delete', 128),
TheliaEvents::AREA_CREATE => array('create', 128)
TheliaEvents::AREA_CREATE => array('create', 128),
TheliaEvents::AREA_UPDATE => array('update', 128)
);
}
}

View File

@@ -12,13 +12,11 @@
namespace Thelia\Action;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Thelia\Model\AttributeQuery;
use Thelia\Model\Attribute as AttributeModel;
use Thelia\Core\Event\TheliaEvents;
use Thelia\Core\Event\Attribute\AttributeUpdateEvent;
use Thelia\Core\Event\Attribute\AttributeCreateEvent;
use Thelia\Core\Event\Attribute\AttributeDeleteEvent;
@@ -34,17 +32,17 @@ class Attribute extends BaseAction implements EventSubscriberInterface
* Create a new attribute entry
*
* @param AttributeCreateEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function create(AttributeCreateEvent $event)
public function create(AttributeCreateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$attribute = new AttributeModel();
$attribute
->setDispatcher($event->getDispatcher())
->setDispatcher($dispatcher)
->setLocale($event->getLocale())
->setTitle($event->getTitle())
->save()
;
@@ -60,14 +58,14 @@ class Attribute extends BaseAction implements EventSubscriberInterface
* Change a product attribute
*
* @param AttributeUpdateEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function update(AttributeUpdateEvent $event)
public function update(AttributeUpdateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if (null !== $attribute = AttributeQuery::create()->findPk($event->getAttributeId())) {
$attribute
->setDispatcher($event->getDispatcher())
->setDispatcher($dispatcher)
->setLocale($event->getLocale())
->setTitle($event->getTitle())
@@ -85,14 +83,14 @@ class Attribute extends BaseAction implements EventSubscriberInterface
* Delete a product attribute entry
*
* @param AttributeDeleteEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function delete(AttributeDeleteEvent $event)
public function delete(AttributeDeleteEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if (null !== ($attribute = AttributeQuery::create()->findPk($event->getAttributeId()))) {
$attribute
->setDispatcher($event->getDispatcher())
->setDispatcher($dispatcher)
->delete()
;
@@ -103,11 +101,13 @@ class Attribute extends BaseAction implements EventSubscriberInterface
/**
* Changes position, selecting absolute ou relative change.
*
* @param CategoryChangePositionEvent $event
* @param UpdatePositionEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function updatePosition(UpdatePositionEvent $event)
public function updatePosition(UpdatePositionEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$this->genericUpdatePosition(AttributeQuery::create(), $event);
$this->genericUpdatePosition(AttributeQuery::create(), $event, $dispatcher);
}
protected function doAddToAllTemplates(AttributeModel $attribute)
@@ -115,7 +115,6 @@ class Attribute extends BaseAction implements EventSubscriberInterface
$templates = TemplateQuery::create()->find();
foreach ($templates as $template) {
$attribute_template = new AttributeTemplate();
if (null === AttributeTemplateQuery::create()->filterByAttribute($attribute)->filterByTemplate($template)->findOne()) {
@@ -140,7 +139,7 @@ class Attribute extends BaseAction implements EventSubscriberInterface
}
/**
* {@inheritDoc}
* {@inheritdoc}
*/
public static function getSubscribedEvents()
{

View File

@@ -12,13 +12,11 @@
namespace Thelia\Action;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Thelia\Model\AttributeAvQuery;
use Thelia\Model\AttributeAv as AttributeAvModel;
use Thelia\Core\Event\TheliaEvents;
use Thelia\Core\Event\Attribute\AttributeAvUpdateEvent;
use Thelia\Core\Event\Attribute\AttributeAvCreateEvent;
use Thelia\Core\Event\Attribute\AttributeAvDeleteEvent;
@@ -30,13 +28,15 @@ class AttributeAv extends BaseAction implements EventSubscriberInterface
* Create a new attribute entry
*
* @param AttributeAvCreateEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function create(AttributeAvCreateEvent $event)
public function create(AttributeAvCreateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$attribute = new AttributeAvModel();
$attribute
->setDispatcher($event->getDispatcher())
->setDispatcher($dispatcher)
->setAttributeId($event->getAttributeId())
->setLocale($event->getLocale())
@@ -51,15 +51,15 @@ class AttributeAv extends BaseAction implements EventSubscriberInterface
/**
* Change a product attribute
*
* @param \Thelia\Core\Event\Attribute\AttributeAvUpdateEvent $event
* @param AttributeAvUpdateEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function update(AttributeAvUpdateEvent $event)
public function update(AttributeAvUpdateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if (null !== $attribute = AttributeAvQuery::create()->findPk($event->getAttributeAvId())) {
$attribute
->setDispatcher($event->getDispatcher())
->setDispatcher($dispatcher)
->setLocale($event->getLocale())
->setTitle($event->getTitle())
@@ -77,14 +77,14 @@ class AttributeAv extends BaseAction implements EventSubscriberInterface
* Delete a product attribute entry
*
* @param AttributeAvDeleteEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function delete(AttributeAvDeleteEvent $event)
public function delete(AttributeAvDeleteEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if (null !== ($attribute = AttributeAvQuery::create()->findPk($event->getAttributeAvId()))) {
$attribute
->setDispatcher($event->getDispatcher())
->setDispatcher($dispatcher)
->delete()
;
@@ -95,15 +95,17 @@ class AttributeAv extends BaseAction implements EventSubscriberInterface
/**
* Changes position, selecting absolute ou relative change.
*
* @param CategoryChangePositionEvent $event
* @param UpdatePositionEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function updatePosition(UpdatePositionEvent $event)
public function updatePosition(UpdatePositionEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$this->genericUpdatePosition(AttributeAvQuery::create(), $event);
}
/**
* {@inheritDoc}
* {@inheritdoc}
*/
public static function getSubscribedEvents()
{

View File

@@ -12,12 +12,13 @@
namespace Thelia\Action;
use Propel\Runtime\ActiveQuery\ModelCriteria;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Thelia\Core\Event\ToggleVisibilityEvent;
use Thelia\Core\Event\UpdatePositionEvent;
use Thelia\Core\Event\UpdateSeoEvent;
use Thelia\Exception\UrlRewritingException;
use Thelia\Form\Exception\FormValidationException;
use Thelia\Model\ProductCategory;
class BaseAction
{
@@ -26,23 +27,56 @@ class BaseAction
*
* @param ModelCriteria $query
* @param UpdatePositionEvent $event
* @param EventDispatcherInterface $dispatcher
*
* @return null
*/
protected function genericUpdatePosition(ModelCriteria $query, UpdatePositionEvent $event)
protected function genericUpdatePosition(ModelCriteria $query, UpdatePositionEvent $event, EventDispatcherInterface $dispatcher = null)
{
if (null !== $object = $query->findPk($event->getObjectId())) {
if (!isset(class_uses($object)['Thelia\Model\Tools\PositionManagementTrait'])) {
throw new \InvalidArgumentException("Your model does not implement the PositionManagementTrait trait");
}
$object->setDispatcher($event->getDispatcher());
$object->setDispatcher($dispatcher !== null ? $dispatcher : $event->getDispatcher());
$mode = $event->getMode();
if ($mode == UpdatePositionEvent::POSITION_ABSOLUTE)
if ($mode == UpdatePositionEvent::POSITION_ABSOLUTE) {
$object->changeAbsolutePosition($event->getPosition());
else if ($mode == UpdatePositionEvent::POSITION_UP)
} elseif ($mode == UpdatePositionEvent::POSITION_UP) {
$object->movePositionUp();
else if ($mode == UpdatePositionEvent::POSITION_DOWN)
} elseif ($mode == UpdatePositionEvent::POSITION_DOWN) {
$object->movePositionDown();
}
}
}
/**
* @param ModelCriteria $query
* @param UpdatePositionEvent $event
* @param EventDispatcherInterface|null $dispatcher
*
* @since 2.3
*/
protected function genericUpdateDelegatePosition(ModelCriteria $query, UpdatePositionEvent $event, EventDispatcherInterface $dispatcher = null)
{
if (null !== $object = $query->findOne()) {
if (!isset(class_uses($object)['Thelia\Model\Tools\PositionManagementTrait'])) {
throw new \InvalidArgumentException("Your model does not implement the PositionManagementTrait trait");
}
//$object->setDispatcher($dispatcher !== null ? $dispatcher : $event->getDispatcher());
$mode = $event->getMode();
if ($mode == UpdatePositionEvent::POSITION_ABSOLUTE) {
$object->changeAbsolutePosition($event->getPosition());
} elseif ($mode == UpdatePositionEvent::POSITION_UP) {
$object->movePositionUp();
} elseif ($mode == UpdatePositionEvent::POSITION_DOWN) {
$object->movePositionDown();
}
}
}
@@ -51,16 +85,17 @@ class BaseAction
*
* @param ModelCriteria $query
* @param UpdateSeoEvent $event
* @param EventDispatcherInterface $dispatcher
*
* @return mixed an SEOxxx object
* @throws FormValidationException if a rewritten URL cannot be created
*/
protected function genericUpdateSeo(ModelCriteria $query, UpdateSeoEvent $event)
protected function genericUpdateSeo(ModelCriteria $query, UpdateSeoEvent $event, EventDispatcherInterface $dispatcher = null)
{
if (null !== $object = $query->findPk($event->getObjectId())) {
$object
->setDispatcher($event->getDispatcher())
//for backward compatibility
->setDispatcher($dispatcher !== null ? $dispatcher : $event->getDispatcher())
->setLocale($event->getLocale())
->setMetaTitle($event->getMetaTitle())
@@ -77,7 +112,33 @@ class BaseAction
throw new FormValidationException($e->getMessage(), $e->getCode());
}
$event->setObject($object);
$event->setObject($object);
}
return $object;
}
/**
* Toggle visibility for an object
*
* @param ModelCriteria $query
* @param ToggleVisibilityEvent $event
* @param EventDispatcherInterface $dispatcher
*
* @return mixed
*/
public function genericToggleVisibility(ModelCriteria $query, ToggleVisibilityEvent $event, EventDispatcherInterface $dispatcher = null)
{
if (null !== $object = $query->findPk($event->getObjectId())) {
$newVisibility = !$object->getVisible();
$object
//for backward compatibility
->setDispatcher($dispatcher !== null ? $dispatcher : $event->getDispatcher())
->setVisible($newVisibility)
->save()
;
$event->setObject($object);
}
return $object;

View File

@@ -11,12 +11,16 @@
/*************************************************************************************/
namespace Thelia\Action;
use Propel\Runtime\Propel;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Thelia\Core\Event\CachedFileEvent;
use Thelia\Core\Event\File\FileCreateOrUpdateEvent;
use Thelia\Core\Event\File\FileDeleteEvent;
use Thelia\Core\Event\File\FileToggleVisibilityEvent;
use Thelia\Core\Event\UpdateFilePositionEvent;
use Thelia\Exception\FileException;
use Thelia\Files\FileManager;
use Thelia\Model\Map\ProductImageTableMap;
use Thelia\Tools\URL;
/**
@@ -77,8 +81,9 @@ abstract class BaseCachedFile extends BaseAction
/** @var \DirectoryIterator $fileinfo */
foreach ($iterator as $fileinfo) {
if ($fileinfo->isDot()) continue;
if ($fileinfo->isDot()) {
continue;
}
if ($fileinfo->isFile() || $fileinfo->isLink()) {
@unlink($fileinfo->getPathname());
@@ -91,7 +96,7 @@ abstract class BaseCachedFile extends BaseAction
/**
* Return the absolute URL to the cached file
*
* @param string $subdir the subdirectory related to cache base
* @param string $subdir the subdirectory related to cache base
* @param string $safe_filename the safe filename, as returned by getCacheFilePath()
* @return string the absolute URL to the cached file
*/
@@ -105,10 +110,10 @@ abstract class BaseCachedFile extends BaseAction
/**
* Return the full path of the cached file
*
* @param string $subdir the subdirectory related to cache base
* @param string $filename the filename
* @param string $subdir the subdirectory related to cache base
* @param string $filename the filename
* @param boolean $forceOriginalFile if true, the original file path in the cache dir is returned.
* @param string $hashed_options a hash of transformation options, or null if no transformations have been applied
* @param string $hashed_options a hash of transformation options, or null if no transformations have been applied
* @return string the cache directory path relative to Web Root
*/
protected function getCacheFilePath($subdir, $filename, $forceOriginalFile = false, $hashed_options = null)
@@ -117,11 +122,12 @@ abstract class BaseCachedFile extends BaseAction
$safe_filename = preg_replace("[^:alnum:\-\._]", "-", strtolower(basename($filename)));
// Keep original safe name if no tranformations are applied
if ($forceOriginalFile || $hashed_options == null)
// Keep original safe name if no tranformations are applied
if ($forceOriginalFile || $hashed_options == null) {
return sprintf("%s/%s", $path, $safe_filename);
else
} else {
return sprintf("%s/%s-%s", $path, $hashed_options, $safe_filename);
}
}
/**
@@ -138,8 +144,9 @@ abstract class BaseCachedFile extends BaseAction
$safe_subdir = basename($subdir);
$path = sprintf("%s/%s", $cache_dir_from_web_root, $safe_subdir);
} else
} else {
$path = $cache_dir_from_web_root;
}
// Check if path is valid, e.g. in the cache dir
return $path;
@@ -148,8 +155,8 @@ abstract class BaseCachedFile extends BaseAction
/**
* Return the absolute cache directory path
*
* @param string $subdir the subdirectory related to cache base, or null to get the cache base directory.
* @param bool $create_if_not_exists create the directory if it is not found
* @param string $subdir the subdirectory related to cache base, or null to get the cache base directory.
* @param bool $create_if_not_exists create the directory if it is not found
*
* @throws \RuntimeException if cache directory cannot be created
* @throws \InvalidArgumentException ii path is invalid, e.g. not in the cache dir
@@ -167,7 +174,7 @@ abstract class BaseCachedFile extends BaseAction
// Create directory (recursively) if it does not exists.
if ($create_if_not_exists && !is_dir($path)) {
if (!@mkdir($path, 0777, true)) {
throw new \RuntimeException(sprintf("Failed to create %s file in cache directory", $path));
throw new \RuntimeException(sprintf("Failed to create %s file in cache directory", $path));
}
}
@@ -186,30 +193,40 @@ abstract class BaseCachedFile extends BaseAction
*
* @param FileCreateOrUpdateEvent $event Image event
*
* @throws \Thelia\Exception\FileException
* @throws \Thelia\Exception\FileException|\Exception
*
*/
public function saveFile(FileCreateOrUpdateEvent $event)
{
$model = $event->getModel();
$model->setFile(sprintf("tmp/%s", $event->getUploadedFile()->getFilename()));
$con = Propel::getWriteConnection(ProductImageTableMap::DATABASE_NAME);
$con->beginTransaction();
$nbModifiedLines = $model->save();
$event->setModel($model);
try {
$nbModifiedLines = $model->save($con);
$event->setModel($model);
if (!$nbModifiedLines) {
throw new FileException(
sprintf(
'File "%s" (type %s) with parent id %s failed to be saved',
$event->getParentName(),
get_class($model),
$event->getParentId()
)
);
if (!$nbModifiedLines) {
throw new FileException(
sprintf(
'File "%s" (type %s) with parent id %s failed to be saved',
$event->getParentName(),
get_class($model),
$event->getParentId()
)
);
}
$newUploadedFile = $this->fileManager->copyUploadedFile($event->getModel(), $event->getUploadedFile());
$event->setUploadedFile($newUploadedFile);
$con->commit();
} catch (\Exception $e) {
$con->rollBack();
throw $e;
}
$newUploadedFile = $this->fileManager->copyUploadedFile($event->getModel(), $event->getUploadedFile());
$event->setUploadedFile($newUploadedFile);
}
/**
@@ -247,9 +264,13 @@ abstract class BaseCachedFile extends BaseAction
$this->fileManager->deleteFile($event->getFileToDelete());
}
public function updatePosition(UpdateFilePositionEvent $event)
public function updatePosition(UpdateFilePositionEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$this->genericUpdatePosition($event->getQuery(), $event);
$this->genericUpdatePosition($event->getQuery(), $event, $dispatcher);
}
public function toggleVisibility(FileToggleVisibilityEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$this->genericToggleVisibility($event->getQuery(), $event, $dispatcher);
}
}

View File

@@ -12,7 +12,9 @@
namespace Thelia\Action;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Thelia\Core\Event\Brand\BrandCreateEvent;
use Thelia\Core\Event\Brand\BrandDeleteEvent;
use Thelia\Core\Event\Brand\BrandToggleVisibilityEvent;
@@ -20,6 +22,7 @@ use Thelia\Core\Event\Brand\BrandUpdateEvent;
use Thelia\Core\Event\TheliaEvents;
use Thelia\Core\Event\UpdatePositionEvent;
use Thelia\Core\Event\UpdateSeoEvent;
use Thelia\Core\Event\ViewCheckEvent;
use Thelia\Model\Brand as BrandModel;
use Thelia\Model\BrandQuery;
@@ -31,7 +34,6 @@ use Thelia\Model\BrandQuery;
*/
class Brand extends BaseAction implements EventSubscriberInterface
{
public function create(BrandCreateEvent $event)
{
$brand = new BrandModel();
@@ -50,11 +52,13 @@ class Brand extends BaseAction implements EventSubscriberInterface
* process update brand
*
* @param BrandUpdateEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function update(BrandUpdateEvent $event)
public function update(BrandUpdateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if (null !== $brand = BrandQuery::create()->findPk($event->getBrandId())) {
$brand->setDispatcher($event->getDispatcher());
$brand->setDispatcher($dispatcher);
$brand
->setVisible($event->getVisible())
@@ -75,13 +79,17 @@ class Brand extends BaseAction implements EventSubscriberInterface
* Toggle Brand visibility
*
* @param BrandToggleVisibilityEvent $event
* @param string $eventName
* @param EventDispatcherInterface $dispatcher
* @throws \Exception
* @throws \Propel\Runtime\Exception\PropelException
*/
public function toggleVisibility(BrandToggleVisibilityEvent $event)
public function toggleVisibility(BrandToggleVisibilityEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$brand = $event->getBrand();
$brand
->setDispatcher($event->getDispatcher())
->setDispatcher($dispatcher)
->setVisible(!$brand->getVisible())
->save();
@@ -91,31 +99,62 @@ class Brand extends BaseAction implements EventSubscriberInterface
/**
* Change Brand SEO
*
* @param \Thelia\Core\Event\UpdateSeoEvent $event
*
* @return mixed
* @param UpdateSeoEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
* @return Object
*/
public function updateSeo(UpdateSeoEvent $event)
public function updateSeo(UpdateSeoEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
return $this->genericUpdateSeo(BrandQuery::create(), $event);
return $this->genericUpdateSeo(BrandQuery::create(), $event, $dispatcher);
}
public function delete(BrandDeleteEvent $event)
public function delete(BrandDeleteEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if (null !== $brand = BrandQuery::create()->findPk($event->getBrandId())) {
$brand->setDispatcher($event->getDispatcher())->delete();
$brand->setDispatcher($dispatcher)->delete();
$event->setBrand($brand);
}
}
public function updatePosition(UpdatePositionEvent $event)
public function updatePosition(UpdatePositionEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$this->genericUpdatePosition(BrandQuery::create(), $event);
$this->genericUpdatePosition(BrandQuery::create(), $event, $dispatcher);
}
/**
* @inheritdoc
* Check if is a brand view and if brand_id is visible
*
* @param ViewCheckEvent $event
* @param string $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function viewCheck(ViewCheckEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if ($event->getView() == 'brand') {
$brand = BrandQuery::create()
->filterById($event->getViewId())
->filterByVisible(1)
->count();
if ($brand == 0) {
$dispatcher->dispatch(TheliaEvents::VIEW_BRAND_ID_NOT_VISIBLE, $event);
}
}
}
/**
* @param ViewCheckEvent $event
* @throws NotFoundHttpException
*/
public function viewBrandIdNotVisible(ViewCheckEvent $event)
{
throw new NotFoundHttpException();
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents()
{
@@ -128,6 +167,9 @@ class Brand extends BaseAction implements EventSubscriberInterface
TheliaEvents::BRAND_UPDATE_POSITION => array('updatePosition', 128),
TheliaEvents::BRAND_TOGGLE_VISIBILITY => array('toggleVisibility', 128),
TheliaEvents::VIEW_CHECK => array('viewCheck', 128),
TheliaEvents::VIEW_BRAND_ID_NOT_VISIBLE => array('viewBrandIdNotVisible', 128),
);
}
}

View File

@@ -11,6 +11,8 @@
/*************************************************************************************/
namespace Thelia\Action;
use Symfony\Component\Cache\Adapter\AdapterInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Filesystem\Filesystem;
use Thelia\Core\Event\Cache\CacheEvent;
@@ -19,39 +21,35 @@ use Thelia\Core\Event\TheliaEvents;
/**
* Class Cache
* @package Thelia\Action
* @author Manuel Raynaud <mraynaud@openstudio.fr>
* @author Manuel Raynaud <manu@raynaud.io>
*/
class Cache extends BaseAction implements EventSubscriberInterface
{
/** @var AdapterInterface */
protected $adapter;
/**
* CacheListener constructor.
* @param AdapterInterface $adapter
*/
public function __construct(AdapterInterface $adapter)
{
$this->adapter = $adapter;
}
public function cacheClear(CacheEvent $event)
{
// clear cache on thelia.cache service
$this->adapter->clear();
$dir = $event->getDir();
$fs = new Filesystem();
$fs->remove($dir);
}
/**
* Returns an array of event names this subscriber wants to listen to.
*
* The array keys are event names and the value can be:
*
* * The method name to call (priority defaults to 0)
* * An array composed of the method name to call and the priority
* * An array of arrays composed of the method names to call and respective
* priorities, or 0 if unset
*
* For instance:
*
* * array('eventName' => 'methodName')
* * array('eventName' => array('methodName', $priority))
* * array('eventName' => array(array('methodName1', $priority), array('methodName2'))
*
* @return array The event names to listen to
*
* @api
* {@inheritdoc}
*/
public static function getSubscribedEvents()
{

View File

@@ -14,15 +14,27 @@ namespace Thelia\Action;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\RequestStack;
use Thelia\Core\Event\Cart\CartCreateEvent;
use Thelia\Core\Event\Cart\CartDuplicationEvent;
use Thelia\Core\Event\Cart\CartPersistEvent;
use Thelia\Core\Event\Cart\CartRestoreEvent;
use Thelia\Core\Event\Cart\CartEvent;
use Thelia\Core\Event\Currency\CurrencyChangeEvent;
use Thelia\Core\Event\TheliaEvents;
use Thelia\Exception\TheliaProcessException;
use Thelia\Model\Base\CustomerQuery;
use Thelia\Model\Base\ProductSaleElementsQuery;
use Thelia\Model\Currency as CurrencyModel;
use Thelia\Model\CartItem;
use Thelia\Model\Cart as CartModel;
use Thelia\Model\CartItemQuery;
use Thelia\Model\CartQuery;
use Thelia\Model\ConfigQuery;
use Thelia\Model\Currency;
use Thelia\Model\ProductSaleElementsQuery;
use Thelia\Model\Customer as CustomerModel;
use Thelia\Model\ProductSaleElements;
use Thelia\Model\Tools\ProductPriceTools;
use Thelia\Tools\TokenProvider;
/**
*
@@ -30,19 +42,44 @@ use Thelia\Model\Tools\ProductPriceTools;
*
* Class Cart
* @package Thelia\Action
* @author Manuel Raynaud <mraynaud@openstudio.fr>
* @author Manuel Raynaud <manu@raynaud.io>
*/
class Cart extends BaseAction implements EventSubscriberInterface
{
/** @var RequestStack */
protected $requestStack;
/** @var TokenProvider */
protected $tokenProvider;
public function __construct(RequestStack $requestStack, TokenProvider $tokenProvider)
{
$this->requestStack = $requestStack;
$this->tokenProvider = $tokenProvider;
}
public function persistCart(CartPersistEvent $event)
{
$cart = $event->getCart();
if ($cart->isNew()) {
$cart
->setToken($this->generateCartCookieIdentifier())
->save();
$this->getSession()->setSessionCart($cart);
}
}
/**
*
* add an article in the current cart
*
* @param \Thelia\Core\Event\Cart\CartEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function addItem(CartEvent $event)
public function addItem(CartEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$cart = $event->getCart();
$newness = $event->getNewness();
$append = $event->getAppend();
@@ -51,6 +88,11 @@ class Cart extends BaseAction implements EventSubscriberInterface
$customer = $cart->getCustomer();
$discount = 0;
if ($cart->isNew()) {
$persistEvent = new CartPersistEvent($cart);
$dispatcher->dispatch(TheliaEvents::CART_PERSIST, $persistEvent);
}
if (null !== $customer && $customer->getDiscount() > 0) {
$discount = $customer->getDiscount();
}
@@ -58,29 +100,29 @@ class Cart extends BaseAction implements EventSubscriberInterface
$productSaleElementsId = $event->getProductSaleElementsId();
$productId = $event->getProduct();
$cartItem = $this->findItem($cart->getId(), $productId, $productSaleElementsId);
// Search for an identical item in the cart
$findItemEvent = clone $event;
$dispatcher->dispatch(TheliaEvents::CART_FINDITEM, $findItemEvent);
$cartItem = $findItemEvent->getCartItem();
if ($cartItem === null || $newness) {
$productSaleElements = ProductSaleElementsQuery::create()
->findPk($productSaleElementsId);
$productSaleElements = ProductSaleElementsQuery::create()->findPk($productSaleElementsId);
if (null !== $productSaleElements) {
$productPrices = $productSaleElements->getPricesByCurrency($currency, $discount);
$event->setCartItem(
$this->doAddItem($event->getDispatcher(), $cart, $productId, $productSaleElements, $quantity, $productPrices)
);
$cartItem = $this->doAddItem($dispatcher, $cart, $productId, $productSaleElements, $quantity, $productPrices);
} else {
// We did no find any PSE... Something is wrong with the DB, just throw an exception.
throw new TheliaProcessException("This item cannot be added to the cart: no matching product sale element was found.");
}
} elseif ($append && $cartItem !== null) {
$cartItem->addQuantity($quantity)->save();
}
if ($append && $cartItem !== null) {
$cartItem->addQuantity($quantity)
->save();
$event->setCartItem(
$cartItem
);
}
$event->setCartItem($cartItem);
}
/**
@@ -91,13 +133,16 @@ class Cart extends BaseAction implements EventSubscriberInterface
*/
public function deleteItem(CartEvent $event)
{
if (null !== $cartItemId = $event->getCartItem()) {
if (null !== $cartItemId = $event->getCartItemId()) {
$cart = $event->getCart();
CartItemQuery::create()
->filterByCartId($cart->getId())
->filterById($cartItemId)
->delete();
// Force an update of the Cart object to provide
// to other listeners an updated CartItem collection.
$cart->clearCartItems();
}
}
@@ -119,10 +164,12 @@ class Cart extends BaseAction implements EventSubscriberInterface
* don't use Form here just test the Request.
*
* @param \Thelia\Core\Event\Cart\CartEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function changeItem(CartEvent $event)
public function changeItem(CartEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if ((null !== $cartItemId = $event->getCartItem()) && (null !== $quantity = $event->getQuantity())) {
if ((null !== $cartItemId = $event->getCartItemId()) && (null !== $quantity = $event->getQuantity())) {
$cart = $event->getCart();
$cartItem = CartItemQuery::create()
@@ -132,16 +179,16 @@ class Cart extends BaseAction implements EventSubscriberInterface
if ($cartItem) {
$event->setCartItem(
$this->updateQuantity($event->getDispatcher(), $cartItem, $quantity)
$this->updateQuantity($dispatcher, $cartItem, $quantity)
);
}
}
}
public function updateCart(CurrencyChangeEvent $event)
public function updateCart(CurrencyChangeEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$session = $event->getRequest()->getSession();
$cart = $session->getCart();
$cart = $event->getRequest()->getSession()->getSessionCart($dispatcher);
if (null !== $cart) {
$this->updateCartPrices($cart, $event->getCurrency());
}
@@ -154,9 +201,8 @@ class Cart extends BaseAction implements EventSubscriberInterface
* @param \Thelia\Model\Cart $cart
* @param \Thelia\Model\Currency $currency
*/
public function updateCartPrices(\Thelia\Model\Cart $cart, Currency $currency)
public function updateCartPrices(CartModel $cart, CurrencyModel $currency)
{
$customer = $cart->getCustomer();
$discount = 0;
@@ -180,47 +226,17 @@ class Cart extends BaseAction implements EventSubscriberInterface
// update the currency cart
$cart->setCurrencyId($currency->getId());
$cart->save();
}
/**
* Returns an array of event names this subscriber wants to listen to.
*
* The array keys are event names and the value can be:
*
* * The method name to call (priority defaults to 0)
* * An array composed of the method name to call and the priority
* * An array of arrays composed of the method names to call and respective
* priorities, or 0 if unset
*
* For instance:
*
* * array('eventName' => 'methodName')
* * array('eventName' => array('methodName', $priority))
* * array('eventName' => array(array('methodName1', $priority), array('methodName2'))
*
* @return array The event names to listen to
*
* @api
*/
public static function getSubscribedEvents()
{
return array(
TheliaEvents::CART_ADDITEM => array("addItem", 128),
TheliaEvents::CART_DELETEITEM => array("deleteItem", 128),
TheliaEvents::CART_UPDATEITEM => array("changeItem", 128),
TheliaEvents::CART_CLEAR => array("clear", 128),
TheliaEvents::CHANGE_DEFAULT_CURRENCY => array("updateCart", 128),
);
}
/**
* increase the quantity for an existing cartItem
*
* @param EventDispatcherInterface $dispatcher
* @param CartItem $cartItem
* @param float $quantity
* @param float $quantity
*
* @throws \Exception
* @throws \Propel\Runtime\Exception\PropelException
* @return CartItem
*/
protected function updateQuantity(EventDispatcherInterface $dispatcher, CartItem $cartItem, $quantity)
@@ -235,17 +251,23 @@ class Cart extends BaseAction implements EventSubscriberInterface
/**
* try to attach a new item to an existing cart
*
* @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher
* @param \Thelia\Model\Cart $cart
* @param int $productId
* @param \Thelia\Model\ProductSaleElements $productSaleElements
* @param float $quantity
* @param ProductPriceTools $productPrices
* @param EventDispatcherInterface $dispatcher
* @param \Thelia\Model\Cart $cart
* @param int $productId
* @param ProductSaleElements $productSaleElements
* @param float $quantity
* @param ProductPriceTools $productPrices
*
* @return CartItem
*/
protected function doAddItem(EventDispatcherInterface $dispatcher, \Thelia\Model\Cart $cart, $productId, \Thelia\Model\ProductSaleElements $productSaleElements, $quantity, ProductPriceTools $productPrices)
{
protected function doAddItem(
EventDispatcherInterface $dispatcher,
CartModel $cart,
$productId,
ProductSaleElements $productSaleElements,
$quantity,
ProductPriceTools $productPrices
) {
$cartItem = new CartItem();
$cartItem->setDisptacher($dispatcher);
$cartItem
@@ -269,7 +291,9 @@ class Cart extends BaseAction implements EventSubscriberInterface
* @param int $cartId
* @param int $productId
* @param int $productSaleElementsId
* @return ChildCartItem
* @return CartItem
*
* @deprecated this method is deprecated. Dispatch a TheliaEvents::CART_FINDITEM instead
*/
protected function findItem($cartId, $productId, $productSaleElementsId)
{
@@ -280,4 +304,247 @@ class Cart extends BaseAction implements EventSubscriberInterface
->findOne();
}
/**
* Find a specific record in CartItem table using the current CartEvent
*
* @param CartEvent $event the cart event
*/
public function findCartItem(CartEvent $event)
{
// Do not try to find a cartItem if one exists in the event, as previous event handlers
// mays have put it in th event.
if (null === $event->getCartItem() && null !== $foundItem = CartItemQuery::create()
->filterByCartId($event->getCart()->getId())
->filterByProductId($event->getProduct())
->filterByProductSaleElementsId($event->getProductSaleElementsId())
->findOne()) {
$event->setCartItem($foundItem);
}
}
/**
* Search if cart already exists in session. If not try to restore it from the cart cookie,
* or duplicate an old one.
*
* @param CartRestoreEvent $cartRestoreEvent
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function restoreCurrentCart(CartRestoreEvent $cartRestoreEvent, $eventName, EventDispatcherInterface $dispatcher)
{
$cookieName = ConfigQuery::read("cart.cookie_name", 'thelia_cart');
$persistentCookie = ConfigQuery::read("cart.use_persistent_cookie", 1);
$cart = null;
if ($this->requestStack->getCurrentRequest()->cookies->has($cookieName) && $persistentCookie) {
$cart = $this->managePersistentCart($cartRestoreEvent, $cookieName, $dispatcher);
} elseif (!$persistentCookie) {
$cart = $this->manageNonPersistentCookie($cartRestoreEvent, $dispatcher);
}
// Still no cart ? Create a new one.
if (null === $cart) {
$cart = $this->dispatchNewCart($dispatcher);
}
$cartRestoreEvent->setCart($cart);
}
/**
* The cart token is not saved in a cookie, if the cart is present in session, we just change the customer id
* if needed or create duplicate the current cart if the customer is not the same as customer already present in
* the cart.
*
* @param CartRestoreEvent $cartRestoreEvent
* @param EventDispatcherInterface $dispatcher
* @return CartModel
* @throws \Exception
* @throws \Propel\Runtime\Exception\PropelException
*/
protected function manageNonPersistentCookie(CartRestoreEvent $cartRestoreEvent, EventDispatcherInterface $dispatcher)
{
$cart = $cartRestoreEvent->getCart();
if (null === $cart) {
$cart = $this->dispatchNewCart($dispatcher);
} else {
$cart = $this->manageCartDuplicationAtCustomerLogin($cart, $dispatcher);
}
return $cart;
}
/**
*
* The cart token is saved in a cookie so we try to retrieve it. Then the customer is checked.
*
* @param CartRestoreEvent $cartRestoreEvent
* @param $cookieName
* @param EventDispatcherInterface $dispatcher
* @return CartModel
* @throws \Exception
* @throws \Propel\Runtime\Exception\PropelException
*/
protected function managePersistentCart(CartRestoreEvent $cartRestoreEvent, $cookieName, EventDispatcherInterface $dispatcher)
{
// The cart cookie exists -> get the cart token
$token = $this->requestStack->getCurrentRequest()->cookies->get($cookieName);
// Check if a cart exists for this token
if (null !== $cart = CartQuery::create()->findOneByToken($token)) {
$cart = $this->manageCartDuplicationAtCustomerLogin($cart, $dispatcher);
}
return $cart;
}
protected function manageCartDuplicationAtCustomerLogin(CartModel $cart, EventDispatcherInterface $dispatcher)
{
/** @var CustomerModel $customer */
if (null !== $customer = $this->getSession()->getCustomerUser()) {
// Check if we have to duplicate the existing cart.
$duplicateCart = true;
// A customer is logged in.
if (null === $cart->getCustomerId()) {
// If the customer has a discount, whe have to duplicate the cart,
// so that the discount will be applied to the products in cart.
if (0 === $customer->getDiscount() || 0 === $cart->countCartItems()) {
// If no discount, or an empty cart, there's no need to duplicate.
$duplicateCart = false;
}
}
if ($duplicateCart) {
// Duplicate the cart
$cart = $this->duplicateCart($dispatcher, $cart, $customer);
} else {
// No duplication required, just assign the cart to the customer
$cart->setCustomerId($customer->getId())->save();
}
} elseif ($cart->getCustomerId() != null) {
// The cart belongs to another user
if (0 === $cart->countCartItems()) {
// No items in cart, assign it to nobody.
$cart->setCustomerId(null)->save();
} else {
// Some itemls in cart, duplicate it without assigning a customer ID.
$cart = $this->duplicateCart($dispatcher, $cart);
}
}
return $cart;
}
/**
* @param EventDispatcherInterface $dispatcher
* @return CartModel
*/
protected function dispatchNewCart(EventDispatcherInterface $dispatcher)
{
$cartCreateEvent = new CartCreateEvent();
$dispatcher->dispatch(TheliaEvents::CART_CREATE_NEW, $cartCreateEvent);
return $cartCreateEvent->getCart();
}
/**
* Create a new, empty cart object, and assign it to the current customer, if any.
*
* @param CartCreateEvent $cartCreateEvent
*/
public function createEmptyCart(CartCreateEvent $cartCreateEvent)
{
$cart = new CartModel();
$cart->setCurrency($this->getSession()->getCurrency(true));
/** @var CustomerModel $customer */
if (null !== $customer = $this->getSession()->getCustomerUser()) {
$cart->setCustomer(CustomerQuery::create()->findPk($customer->getId()));
}
$this->getSession()->setSessionCart($cart);
if (ConfigQuery::read("cart.use_persistent_cookie", 1) == 1) {
// set cart_use_cookie to "" to remove the cart cookie
// see Thelia\Core\EventListener\ResponseListener
$this->getSession()->set("cart_use_cookie", "");
}
$cartCreateEvent->setCart($cart);
}
/**
* Duplicate an existing Cart. If a customer ID is provided the created cart will be attached to this customer.
*
* @param EventDispatcherInterface $dispatcher
* @param CartModel $cart
* @param CustomerModel $customer
* @return CartModel
*/
protected function duplicateCart(EventDispatcherInterface $dispatcher, CartModel $cart, CustomerModel $customer = null)
{
$newCart = $cart->duplicate(
$this->generateCartCookieIdentifier(),
$customer,
$this->getSession()->getCurrency(),
$dispatcher
);
$cartEvent = new CartDuplicationEvent($newCart, $cart);
$dispatcher->dispatch(TheliaEvents::CART_DUPLICATE, $cartEvent);
return $cartEvent->getDuplicatedCart();
}
/**
* Generate the cart cookie identifier, or return null if the cart is only managed in the session object,
* not in a client cookie.
*
* @return string
*/
protected function generateCartCookieIdentifier()
{
$id = null;
if (ConfigQuery::read("cart.use_persistent_cookie", 1) == 1) {
$id = $this->tokenProvider->getToken();
$this->getSession()->set('cart_use_cookie', $id);
}
return $id;
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents()
{
return array(
TheliaEvents::CART_PERSIST => array("persistCart", 128),
TheliaEvents::CART_RESTORE_CURRENT => array("restoreCurrentCart", 128),
TheliaEvents::CART_CREATE_NEW => array("createEmptyCart", 128),
TheliaEvents::CART_ADDITEM => array("addItem", 128),
TheliaEvents::CART_FINDITEM => array("findCartItem", 128),
TheliaEvents::CART_DELETEITEM => array("deleteItem", 128),
TheliaEvents::CART_UPDATEITEM => array("changeItem", 128),
TheliaEvents::CART_CLEAR => array("clear", 128),
TheliaEvents::CHANGE_DEFAULT_CURRENCY => array("updateCart", 128),
);
}
/**
* Returns the session from the current request
*
* @return \Thelia\Core\HttpFoundation\Session\Session
*/
protected function getSession()
{
return $this->requestStack->getCurrentRequest()->getSession();
}
}

View File

@@ -12,14 +12,17 @@
namespace Thelia\Action;
use Propel\Runtime\Propel;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Thelia\Core\Event\File\FileDeleteEvent;
use Thelia\Core\Event\UpdateSeoEvent;
use Thelia\Model\CategoryDocumentQuery;
use Thelia\Model\CategoryImageQuery;
use Thelia\Model\CategoryQuery;
use Thelia\Model\Category as CategoryModel;
use Thelia\Core\Event\TheliaEvents;
use Thelia\Core\Event\Category\CategoryUpdateEvent;
use Thelia\Core\Event\Category\CategoryCreateEvent;
use Thelia\Core\Event\Category\CategoryDeleteEvent;
@@ -27,8 +30,10 @@ use Thelia\Core\Event\UpdatePositionEvent;
use Thelia\Core\Event\Category\CategoryToggleVisibilityEvent;
use Thelia\Core\Event\Category\CategoryAddContentEvent;
use Thelia\Core\Event\Category\CategoryDeleteContentEvent;
use Thelia\Core\Event\ViewCheckEvent;
use Thelia\Model\CategoryAssociatedContent;
use Thelia\Model\CategoryAssociatedContentQuery;
use Thelia\Model\Map\CategoryTableMap;
class Category extends BaseAction implements EventSubscriberInterface
{
@@ -36,13 +41,15 @@ class Category extends BaseAction implements EventSubscriberInterface
* Create a new category entry
*
* @param \Thelia\Core\Event\Category\CategoryCreateEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function create(CategoryCreateEvent $event)
public function create(CategoryCreateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$category = new CategoryModel();
$category
->setDispatcher($event->getDispatcher())
->setDispatcher($dispatcher)
->setLocale($event->getLocale())
->setParent($event->getParent())
@@ -59,14 +66,15 @@ class Category extends BaseAction implements EventSubscriberInterface
* Change a category
*
* @param \Thelia\Core\Event\Category\CategoryUpdateEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function update(CategoryUpdateEvent $event)
public function update(CategoryUpdateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if (null !== $category = CategoryQuery::create()->findPk($event->getCategoryId())) {
$category
->setDispatcher($event->getDispatcher())
->setDispatcher($dispatcher)
->setDefaultTemplateId($event->getDefaultTemplateId() == 0 ? null : $event->getDefaultTemplateId())
->setLocale($event->getLocale())
->setTitle($event->getTitle())
->setDescription($event->getDescription())
@@ -85,44 +93,78 @@ class Category extends BaseAction implements EventSubscriberInterface
/**
* Change a Category SEO
*
* @param \Thelia\Core\Event\UpdateSeoEvent $event
*
* @return mixed
* @param UpdateSeoEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
* @return Object
*/
public function updateSeo(UpdateSeoEvent $event)
public function updateSeo(UpdateSeoEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
return $this->genericUpdateSeo(CategoryQuery::create(), $event);
return $this->genericUpdateSeo(CategoryQuery::create(), $event, $dispatcher);
}
/**
* Delete a category entry
*
* @param \Thelia\Core\Event\Category\CategoryDeleteEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
* @throws \Exception
*/
public function delete(CategoryDeleteEvent $event)
public function delete(CategoryDeleteEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if (null !== $category = CategoryQuery::create()->findPk($event->getCategoryId())) {
$con = Propel::getWriteConnection(CategoryTableMap::DATABASE_NAME);
$con->beginTransaction();
$category
->setDispatcher($event->getDispatcher())
->delete()
;
try {
$fileList = ['images' => [], 'documentList' => []];
$event->setCategory($category);
// Get category's files to delete after category deletion
$fileList['images']['list'] = CategoryImageQuery::create()
->findByCategoryId($event->getCategoryId());
$fileList['images']['type'] = TheliaEvents::IMAGE_DELETE;
$fileList['documentList']['list'] = CategoryDocumentQuery::create()
->findByCategoryId($event->getCategoryId());
$fileList['documentList']['type'] = TheliaEvents::DOCUMENT_DELETE;
// Delete category
$category
->setDispatcher($dispatcher)
->delete($con);
$event->setCategory($category);
// Dispatch delete category's files event
foreach ($fileList as $fileTypeList) {
foreach ($fileTypeList['list'] as $fileToDelete) {
$fileDeleteEvent = new FileDeleteEvent($fileToDelete);
$dispatcher->dispatch($fileTypeList['type'], $fileDeleteEvent);
}
}
$con->commit();
} catch (\Exception $e) {
$con->rollback();
throw $e;
}
}
}
/**
* Toggle category visibility. No form used here
*
* @param ActionEvent $event
* @param CategoryToggleVisibilityEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function toggleVisibility(CategoryToggleVisibilityEvent $event)
public function toggleVisibility(CategoryToggleVisibilityEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$category = $event->getCategory();
$category = $event->getCategory();
$category
->setDispatcher($event->getDispatcher())
$category
->setDispatcher($dispatcher)
->setVisible($category->getVisible() ? false : true)
->save()
;
@@ -133,31 +175,32 @@ class Category extends BaseAction implements EventSubscriberInterface
/**
* Changes position, selecting absolute ou relative change.
*
* @param CategoryChangePositionEvent $event
* @param UpdatePositionEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function updatePosition(UpdatePositionEvent $event)
public function updatePosition(UpdatePositionEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$this->genericUpdatePosition(CategoryQuery::create(), $event);
$this->genericUpdatePosition(CategoryQuery::create(), $event, $dispatcher);
}
public function addContent(CategoryAddContentEvent $event)
public function addContent(CategoryAddContentEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if (CategoryAssociatedContentQuery::create()
->filterByContentId($event->getContentId())
->filterByCategory($event->getCategory())->count() <= 0) {
$content = new CategoryAssociatedContent();
$content
->setDispatcher($event->getDispatcher())
->setDispatcher($dispatcher)
->setCategory($event->getCategory())
->setContentId($event->getContentId())
->save()
;
}
}
}
public function removeContent(CategoryDeleteContentEvent $event)
public function removeContent(CategoryDeleteContentEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$content = CategoryAssociatedContentQuery::create()
->filterByContentId($event->getContentId())
@@ -166,11 +209,41 @@ class Category extends BaseAction implements EventSubscriberInterface
if ($content !== null) {
$content
->setDispatcher($event->getDispatcher())
->setDispatcher($dispatcher)
->delete();
}
}
/**
* Check if is a category view and if category_id is visible
*
* @param ViewCheckEvent $event
* @param string $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function viewCheck(ViewCheckEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if ($event->getView() == 'category') {
$category = CategoryQuery::create()
->filterById($event->getViewId())
->filterByVisible(1)
->count();
if ($category == 0) {
$dispatcher->dispatch(TheliaEvents::VIEW_CATEGORY_ID_NOT_VISIBLE, $event);
}
}
}
/**
* @param ViewCheckEvent $event
* @throws NotFoundHttpException
*/
public function viewcategoryIdNotVisible(ViewCheckEvent $event)
{
throw new NotFoundHttpException();
}
/**
* {@inheritDoc}
*/
@@ -188,6 +261,8 @@ class Category extends BaseAction implements EventSubscriberInterface
TheliaEvents::CATEGORY_ADD_CONTENT => array("addContent", 128),
TheliaEvents::CATEGORY_REMOVE_CONTENT => array("removeContent", 128),
TheliaEvents::VIEW_CHECK => array('viewCheck', 128),
TheliaEvents::VIEW_CATEGORY_ID_NOT_VISIBLE => array('viewcategoryIdNotVisible', 128),
);
}
}

View File

@@ -11,6 +11,8 @@
/*************************************************************************************/
namespace Thelia\Action;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Thelia\Core\Event\Config\ConfigCreateEvent;
use Thelia\Core\Event\Config\ConfigDeleteEvent;
@@ -25,12 +27,14 @@ class Config extends BaseAction implements EventSubscriberInterface
* Create a new configuration entry
*
* @param \Thelia\Core\Event\Config\ConfigCreateEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function create(ConfigCreateEvent $event)
public function create(ConfigCreateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$config = new ConfigModel();
$config->setDispatcher($event->getDispatcher())
$config->setDispatcher($dispatcher)
->setName($event->getEventName())
->setValue($event->getValue())
->setLocale($event->getLocale())
@@ -46,15 +50,14 @@ class Config extends BaseAction implements EventSubscriberInterface
* Change a configuration entry value
*
* @param \Thelia\Core\Event\Config\ConfigUpdateEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function setValue(ConfigUpdateEvent $event)
public function setValue(ConfigUpdateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if (null !== $config = ConfigQuery::create()->findPk($event->getConfigId())) {
if ($event->getValue() !== $config->getValue()) {
$config->setDispatcher($event->getDispatcher())->setValue($event->getValue())->save();
$config->setDispatcher($dispatcher)->setValue($event->getValue())->save();
$event->setConfig($config);
}
@@ -65,13 +68,13 @@ class Config extends BaseAction implements EventSubscriberInterface
* Change a configuration entry
*
* @param \Thelia\Core\Event\Config\ConfigUpdateEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function modify(ConfigUpdateEvent $event)
public function modify(ConfigUpdateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if (null !== $config = ConfigQuery::create()->findPk($event->getConfigId())) {
$config->setDispatcher($event->getDispatcher())
$config->setDispatcher($dispatcher)
->setName($event->getEventName())
->setValue($event->getValue())
->setHidden($event->getHidden())
@@ -91,15 +94,14 @@ class Config extends BaseAction implements EventSubscriberInterface
* Delete a configuration entry
*
* @param \Thelia\Core\Event\Config\ConfigDeleteEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function delete(ConfigDeleteEvent $event)
public function delete(ConfigDeleteEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if (null !== ($config = ConfigQuery::create()->findPk($event->getConfigId()))) {
if (!$config->getSecured()) {
$config->setDispatcher($event->getDispatcher())->delete();
$config->setDispatcher($dispatcher)->delete();
$event->setConfig($config);
}

View File

@@ -12,34 +12,41 @@
namespace Thelia\Action;
use Propel\Runtime\Exception\PropelException;
use Propel\Runtime\Propel;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Thelia\Core\Event\Content\ContentAddFolderEvent;
use Thelia\Core\Event\Content\ContentCreateEvent;
use Thelia\Core\Event\Content\ContentDeleteEvent;
use Thelia\Core\Event\Content\ContentRemoveFolderEvent;
use Thelia\Core\Event\Content\ContentToggleVisibilityEvent;
use Thelia\Core\Event\Content\ContentUpdateEvent;
use Thelia\Core\Event\File\FileDeleteEvent;
use Thelia\Core\Event\TheliaEvents;
use Thelia\Core\Event\UpdatePositionEvent;
use Thelia\Core\Event\UpdateSeoEvent;
use Thelia\Core\Event\ViewCheckEvent;
use Thelia\Model\ContentDocumentQuery;
use Thelia\Model\ContentFolder;
use Thelia\Model\ContentFolderQuery;
use Thelia\Model\ContentImageQuery;
use Thelia\Model\ContentQuery;
use Thelia\Model\Content as ContentModel;
use Thelia\Model\Map\ContentTableMap;
/**
* Class Content
* @package Thelia\Action
* @author manuel raynaud <mraynaud@openstudio.fr>
* @author manuel raynaud <manu@raynaud.io>
*/
class Content extends BaseAction implements EventSubscriberInterface
{
public function create(ContentCreateEvent $event)
public function create(ContentCreateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$content = new ContentModel();
$content
$content = (new ContentModel)
->setDispatcher($dispatcher)
->setVisible($event->getVisible())
->setLocale($event->getLocale())
->setTitle($event->getTitle())
@@ -53,68 +60,116 @@ class Content extends BaseAction implements EventSubscriberInterface
* process update content
*
* @param ContentUpdateEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
* @throws PropelException
* @throws \Exception
*/
public function update(ContentUpdateEvent $event)
public function update(ContentUpdateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if (null !== $content = ContentQuery::create()->findPk($event->getContentId())) {
$content->setDispatcher($event->getDispatcher());
$con = Propel::getWriteConnection(ContentTableMap::DATABASE_NAME);
$con->beginTransaction();
$content
->setVisible($event->getVisible())
->setLocale($event->getLocale())
->setTitle($event->getTitle())
->setDescription($event->getDescription())
->setChapo($event->getChapo())
->setPostscriptum($event->getPostscriptum())
->save()
;
$content->setDispatcher($dispatcher);
try {
$content
->setVisible($event->getVisible())
->setLocale($event->getLocale())
->setTitle($event->getTitle())
->setDescription($event->getDescription())
->setChapo($event->getChapo())
->setPostscriptum($event->getPostscriptum())
->save($con)
;
$content->updateDefaultFolder($event->getDefaultFolder());
$content->setDefaultFolder($event->getDefaultFolder());
$event->setContent($content);
$event->setContent($content);
$con->commit();
} catch (PropelException $e) {
$con->rollBack();
throw $e;
}
}
}
/**
* Change Content SEO
*
* @param \Thelia\Core\Event\UpdateSeoEvent $event
*
* @return mixed
* @param UpdateSeoEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
* @return Object
*/
public function updateSeo(UpdateSeoEvent $event)
public function updateSeo(UpdateSeoEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
return $this->genericUpdateSeo(ContentQuery::create(), $event);
return $this->genericUpdateSeo(ContentQuery::create(), $event, $dispatcher);
}
public function updatePosition(UpdatePositionEvent $event)
public function updatePosition(UpdatePositionEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$this->genericUpdatePosition(ContentQuery::create(), $event);
$this->genericUpdateDelegatePosition(
ContentFolderQuery::create()
->filterByContentId($event->getObjectId())
->filterByFolderId($event->getReferrerId()),
$event,
$dispatcher
);
}
public function toggleVisibility(ContentToggleVisibilityEvent $event)
public function toggleVisibility(ContentToggleVisibilityEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$content = $event->getContent();
$content
->setDispatcher($event->getDispatcher())
->setDispatcher($dispatcher)
->setVisible(!$content->getVisible())
->save();
$event->setContent($content);
}
public function delete(ContentDeleteEvent $event)
public function delete(ContentDeleteEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if (null !== $content = ContentQuery::create()->findPk($event->getContentId())) {
$defaultFolderId = $content->getDefaultFolderId();
$con = Propel::getWriteConnection(ContentTableMap::DATABASE_NAME);
$con->beginTransaction();
$content->setDispatcher($event->getDispatcher())
->delete();
try {
$fileList = ['images' => [], 'documentList' => []];
$event->setDefaultFolderId($defaultFolderId);
$event->setContent($content);
$defaultFolderId = $content->getDefaultFolderId();
// Get content's files to delete after content deletion
$fileList['images']['list'] = ContentImageQuery::create()
->findByContentId($event->getContentId());
$fileList['images']['type'] = TheliaEvents::IMAGE_DELETE;
$fileList['documentList']['list'] = ContentDocumentQuery::create()
->findByContentId($event->getContentId());
$fileList['documentList']['type'] = TheliaEvents::DOCUMENT_DELETE;
// Delete content
$content->setDispatcher($dispatcher)
->delete($con);
$event->setDefaultFolderId($defaultFolderId);
$event->setContent($content);
// Dispatch delete content's files event
foreach ($fileList as $fileTypeList) {
foreach ($fileTypeList['list'] as $fileToDelete) {
$fileDeleteEvent = new FileDeleteEvent($fileToDelete);
$dispatcher->dispatch($fileTypeList['type'], $fileDeleteEvent);
}
}
$con->commit();
} catch (\Exception $e) {
$con->rollback();
throw $e;
}
}
}
@@ -126,17 +181,18 @@ class Content extends BaseAction implements EventSubscriberInterface
*/
public function addFolder(ContentAddFolderEvent $event)
{
if(ContentFolderQuery::create()
if (ContentFolderQuery::create()
->filterByContent($event->getContent())
->filterByFolderId($event->getFolderId())
->count() <= 0
) {
$contentFolder = new ContentFolder();
$contentFolder
$contentFolder = (new ContentFolder())
->setFolderId($event->getFolderId())
->setContent($event->getContent())
->setDefaultFolder(false)
->setDefaultFolder(false);
$contentFolder
->setPosition($contentFolder->getNextPosition())
->save();
}
}
@@ -154,29 +210,42 @@ class Content extends BaseAction implements EventSubscriberInterface
}
/**
* Returns an array of event names this subscriber wants to listen to.
* Check if is a content view and if content_id is visible
*
* The array keys are event names and the value can be:
*
* * The method name to call (priority defaults to 0)
* * An array composed of the method name to call and the priority
* * An array of arrays composed of the method names to call and respective
* priorities, or 0 if unset
*
* For instance:
*
* * array('eventName' => 'methodName')
* * array('eventName' => array('methodName', $priority))
* * array('eventName' => array(array('methodName1', $priority), array('methodName2'))
*
* @return array The event names to listen to
*
* @api
* @param ViewCheckEvent $event
* @param string $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function viewCheck(ViewCheckEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if ($event->getView() == 'content') {
$content = ContentQuery::create()
->filterById($event->getViewId())
->filterByVisible(1)
->count();
if ($content == 0) {
$dispatcher->dispatch(TheliaEvents::VIEW_CONTENT_ID_NOT_VISIBLE, $event);
}
}
}
/**
* @param ViewCheckEvent $event
* @throws NotFoundHttpException
*/
public function viewContentIdNotVisible(ViewCheckEvent $event)
{
throw new NotFoundHttpException();
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents()
{
return array(
TheliaEvents::CONTENT_CREATE => array('create', 128),
TheliaEvents::CONTENT_CREATE => array('create', 128),
TheliaEvents::CONTENT_UPDATE => array('update', 128),
TheliaEvents::CONTENT_DELETE => array('delete', 128),
TheliaEvents::CONTENT_TOGGLE_VISIBILITY => array('toggleVisibility', 128),
@@ -186,7 +255,9 @@ class Content extends BaseAction implements EventSubscriberInterface
TheliaEvents::CONTENT_ADD_FOLDER => array('addFolder', 128),
TheliaEvents::CONTENT_REMOVE_FOLDER => array('removeFolder', 128),
TheliaEvents::VIEW_CHECK => array('viewCheck', 128),
TheliaEvents::VIEW_CONTENT_ID_NOT_VISIBLE => array('viewContentIdNotVisible', 128),
);
}
}

View File

@@ -12,10 +12,12 @@
namespace Thelia\Action;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Thelia\Core\Event\Country\CountryCreateEvent;
use Thelia\Core\Event\Country\CountryDeleteEvent;
use Thelia\Core\Event\Country\CountryToggleDefaultEvent;
use Thelia\Core\Event\Country\CountryToggleVisibilityEvent;
use Thelia\Core\Event\Country\CountryUpdateEvent;
use Thelia\Core\Event\TheliaEvents;
use Thelia\Model\Country as CountryModel;
@@ -24,34 +26,38 @@ use Thelia\Model\CountryQuery;
/**
* Class Country
* @package Thelia\Action
* @author Manuel Raynaud <mraynaud@openstudio.fr>
* @author Manuel Raynaud <manu@raynaud.io>
*/
class Country extends BaseAction implements EventSubscriberInterface
{
public function create(CountryCreateEvent $event)
{
$country = new CountryModel();
$country
->setVisible($event->isVisible())
->setIsocode($event->getIsocode())
->setIsoalpha2($event->getIsoAlpha2())
->setIsoalpha3($event->getIsoAlpha3())
->setHasStates($event->isHasStates())
->setLocale($event->getLocale())
->setTitle($event->getTitle())
->save();
$event->setCountry($country);
}
public function update(CountryUpdateEvent $event)
{
if (null !== $country = CountryQuery::create()->findPk($event->getCountryId())) {
$country
->setVisible($event->isVisible())
->setIsocode($event->getIsocode())
->setIsoalpha2($event->getIsoAlpha2())
->setIsoalpha3($event->getIsoAlpha3())
->setHasStates($event->isHasStates())
->setNeedZipCode($event->isNeedZipCode())
->setZipCodeFormat($event->getZipCodeFormat())
->setLocale($event->getLocale())
->setTitle($event->getTitle())
->setChapo($event->getChapo())
@@ -73,7 +79,7 @@ class Country extends BaseAction implements EventSubscriberInterface
public function toggleDefault(CountryToggleDefaultEvent $event)
{
if ( null !== $country = CountryQuery::create()->findPk($event->getCountryId())) {
if (null !== $country = CountryQuery::create()->findPk($event->getCountryId())) {
$country->toggleDefault();
$event->setCountry($country);
@@ -81,24 +87,26 @@ class Country extends BaseAction implements EventSubscriberInterface
}
/**
* Returns an array of event names this subscriber wants to listen to.
* Toggle Country visibility
*
* The array keys are event names and the value can be:
*
* * The method name to call (priority defaults to 0)
* * An array composed of the method name to call and the priority
* * An array of arrays composed of the method names to call and respective
* priorities, or 0 if unset
*
* For instance:
*
* * array('eventName' => 'methodName')
* * array('eventName' => array('methodName', $priority))
* * array('eventName' => array(array('methodName1', $priority), array('methodName2'))
*
* @return array The event names to listen to
*
* @api
* @param CountryToggleVisibilityEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function toggleVisibility(CountryToggleVisibilityEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$country = $event->getCountry();
$country
->setDispatcher($dispatcher)
->setVisible(!$country->getVisible())
->save();
$event->setCountry($country);
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents()
{
@@ -106,7 +114,8 @@ class Country extends BaseAction implements EventSubscriberInterface
TheliaEvents::COUNTRY_CREATE => array('create', 128),
TheliaEvents::COUNTRY_UPDATE => array('update', 128),
TheliaEvents::COUNTRY_DELETE => array('delete', 128),
TheliaEvents::COUNTRY_TOGGLE_DEFAULT => array('toggleDefault', 128)
TheliaEvents::COUNTRY_TOGGLE_DEFAULT => array('toggleDefault', 128),
TheliaEvents::COUNTRY_TOGGLE_VISIBILITY => array('toggleVisibility', 128)
);
}
}

View File

@@ -13,15 +13,18 @@
namespace Thelia\Action;
use Propel\Runtime\Propel;
use Symfony\Component\EventDispatcher\Event;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\RequestStack;
use Thelia\Condition\ConditionCollection;
use Thelia\Condition\ConditionFactory;
use Thelia\Condition\Implementation\ConditionInterface;
use Thelia\Core\Event\Coupon\CouponConsumeEvent;
use Thelia\Core\Event\Coupon\CouponCreateOrUpdateEvent;
use Thelia\Core\Event\Coupon\CouponDeleteEvent;
use Thelia\Core\Event\Order\OrderEvent;
use Thelia\Core\Event\TheliaEvents;
use Thelia\Core\HttpFoundation\Request;
use Thelia\Coupon\CouponFactory;
use Thelia\Coupon\CouponManager;
use Thelia\Coupon\Type\CouponInterface;
@@ -35,6 +38,8 @@ use Thelia\Model\Map\OrderCouponTableMap;
use Thelia\Model\OrderCoupon;
use Thelia\Model\OrderCouponCountry;
use Thelia\Model\OrderCouponModule;
use Thelia\Model\OrderCouponQuery;
use Thelia\Model\OrderStatusQuery;
/**
* Process Coupon Events
@@ -45,10 +50,8 @@ use Thelia\Model\OrderCouponModule;
*/
class Coupon extends BaseAction implements EventSubscriberInterface
{
/**
* @var \Thelia\Core\HttpFoundation\Request
*/
protected $request;
/** @var RequestStack */
protected $requestStack;
/** @var CouponFactory $couponFactory */
protected $couponFactory;
@@ -62,11 +65,14 @@ class Coupon extends BaseAction implements EventSubscriberInterface
/** @var ConditionFactory $conditionFactory */
protected $conditionFactory;
public function __construct(Request $request,
CouponFactory $couponFactory, CouponManager $couponManager,
ConditionInterface $noConditionRule, ConditionFactory $conditionFactory)
{
$this->request = $request;
public function __construct(
RequestStack $requestStack,
CouponFactory $couponFactory,
CouponManager $couponManager,
ConditionInterface $noConditionRule,
ConditionFactory $conditionFactory
) {
$this->requestStack = $requestStack;
$this->couponFactory = $couponFactory;
$this->couponManager = $couponManager;
$this->noConditionRule = $noConditionRule;
@@ -77,57 +83,87 @@ class Coupon extends BaseAction implements EventSubscriberInterface
* Occurring when a Coupon is about to be created
*
* @param CouponCreateOrUpdateEvent $event Event creation or update Coupon
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function create(CouponCreateOrUpdateEvent $event)
public function create(CouponCreateOrUpdateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$coupon = new CouponModel();
$this->createOrUpdate($coupon, $event);
$this->createOrUpdate($coupon, $event, $dispatcher);
}
/**
* Occurring when a Coupon is about to be updated
*
* @param CouponCreateOrUpdateEvent $event Event creation or update Coupon
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function update(CouponCreateOrUpdateEvent $event)
public function update(CouponCreateOrUpdateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$coupon = $event->getCouponModel();
$this->createOrUpdate($coupon, $event);
$this->createOrUpdate($coupon, $event, $dispatcher);
}
public function delete(CouponDeleteEvent $event)
{
$coupon = $event->getCoupon();
if (null === $coupon) {
throw new \InvalidArgumentException(
sprintf(
"The coupon id '%d' doesn't exist",
$event->getCouponId()
)
);
}
$coupon->delete();
$event->setCoupon(null);
}
/**
* Occurring when a Coupon condition is about to be updated
*
* @param CouponCreateOrUpdateEvent $event Event creation or update Coupon condition
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function updateCondition(CouponCreateOrUpdateEvent $event)
public function updateCondition(CouponCreateOrUpdateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$modelCoupon = $event->getCouponModel();
$this->createOrUpdateCondition($modelCoupon, $event);
$this->createOrUpdateCondition($modelCoupon, $event, $dispatcher);
}
/**
* Clear all coupons in session.
*
* @param Event $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function clearAllCoupons()
public function clearAllCoupons(Event $event, $eventName, EventDispatcherInterface $dispatcher)
{
// Tell coupons to clear any data they may have stored
$this->couponManager->clear();
$this->request->getSession()->setConsumedCoupons(array());
$this->getSession()->setConsumedCoupons(array());
$this->updateOrderDiscount(null);
$this->updateOrderDiscount($event, $eventName, $dispatcher);
}
/**
* Occurring when a Coupon condition is about to be consumed
*
* @param CouponConsumeEvent $event Event consuming Coupon
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function consume(CouponConsumeEvent $event)
public function consume(CouponConsumeEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$totalDiscount = 0;
$isValid = false;
@@ -136,56 +172,38 @@ class Coupon extends BaseAction implements EventSubscriberInterface
$coupon = $this->couponFactory->buildCouponFromCode($event->getCode());
if ($coupon) {
$isValid = $coupon->isMatching();
if ($isValid) {
$consumedCoupons = $this->request->getSession()->getConsumedCoupons();
if (!isset($consumedCoupons) || !$consumedCoupons) {
$consumedCoupons = array();
}
if (!isset($consumedCoupons[$event->getCode()])) {
// Prevent accumulation of the same Coupon on a Checkout
$consumedCoupons[$event->getCode()] = $event->getCode();
$this->request->getSession()->setConsumedCoupons($consumedCoupons);
}
$this->couponManager->pushCouponInSession($event->getCode());
$totalDiscount = $this->couponManager->getDiscount();
$this->request
->getSession()
->getCart()
$this->getSession()
->getSessionCart($dispatcher)
->setDiscount($totalDiscount)
->save();
$this->request
->getSession()
$this->getSession()
->getOrder()
->setDiscount($totalDiscount)
;
}
}
}
$event->setIsValid($isValid);
$event->setDiscount($totalDiscount);
}
public function updateOrderDiscount(/** @noinspection PhpUnusedParameterInspection */ $event)
public function updateOrderDiscount(Event $event, $eventName, EventDispatcherInterface $dispatcher)
{
$discount = $this->couponManager->getDiscount();
$this->request
->getSession()
->getCart()
$this->getSession()
->getSessionCart($dispatcher)
->setDiscount($discount)
->save();
$this->request
->getSession()
$this->getSession()
->getOrder()
->setDiscount($discount);
}
@@ -196,10 +214,11 @@ class Coupon extends BaseAction implements EventSubscriberInterface
*
* @param CouponModel $coupon Model to save
* @param CouponCreateOrUpdateEvent $event Event containing data
* @param EventDispatcherInterface $dispatcher
*/
protected function createOrUpdate(CouponModel $coupon, CouponCreateOrUpdateEvent $event)
protected function createOrUpdate(CouponModel $coupon, CouponCreateOrUpdateEvent $event, EventDispatcherInterface $dispatcher)
{
$coupon->setDispatcher($event->getDispatcher());
$coupon->setDispatcher($dispatcher);
// Set default condition if none found
/** @var ConditionInterface $noConditionRule */
@@ -229,7 +248,8 @@ class Coupon extends BaseAction implements EventSubscriberInterface
$event->getLocale(),
$event->getFreeShippingForCountries(),
$event->getFreeShippingForMethods(),
$event->getPerCustomerUsageCount()
$event->getPerCustomerUsageCount(),
$event->getStartDate()
);
$event->setCouponModel($coupon);
@@ -241,10 +261,11 @@ class Coupon extends BaseAction implements EventSubscriberInterface
*
* @param CouponModel $coupon Model to save
* @param CouponCreateOrUpdateEvent $event Event containing data
* @param EventDispatcherInterface $dispatcher
*/
protected function createOrUpdateCondition(CouponModel $coupon, CouponCreateOrUpdateEvent $event)
protected function createOrUpdateCondition(CouponModel $coupon, CouponCreateOrUpdateEvent $event, EventDispatcherInterface $dispatcher)
{
$coupon->setDispatcher($event->getDispatcher());
$coupon->setDispatcher($dispatcher);
/** @var ConditionFactory $conditionFactory */
$conditionFactory = $this->conditionFactory;
@@ -265,7 +286,6 @@ class Coupon extends BaseAction implements EventSubscriberInterface
$order = $event->getOrder();
if ($this->couponManager->isCouponRemovingPostage($order)) {
$order->setPostage(0);
$event->setOrder($order);
@@ -278,21 +298,23 @@ class Coupon extends BaseAction implements EventSubscriberInterface
* @param \Thelia\Core\Event\Order\OrderEvent $event
*
* @throws \Exception if something goes wrong.
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function afterOrder(OrderEvent $event)
public function afterOrder(OrderEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$consumedCoupons = $this->request->getSession()->getConsumedCoupons();
if (is_array($consumedCoupons)) {
/** @var CouponInterface[] $consumedCoupons */
$consumedCoupons = $this->couponManager->getCouponsKept();
if (is_array($consumedCoupons) && count($consumedCoupons) > 0) {
$con = Propel::getWriteConnection(OrderCouponTableMap::DATABASE_NAME);
$con->beginTransaction();
try {
foreach ($consumedCoupons as $couponCode) {
$couponQuery = CouponQuery::create();
$couponModel = $couponQuery->findOneByCode($couponCode);
$couponModel->setLocale($this->request->getSession()->getLang()->getLocale());
$couponModel = $couponQuery->findOneByCode($couponCode->getCode());
$couponModel->setLocale($this->getSession()->getLang()->getLocale());
/* decrease coupon quantity */
$this->couponManager->decrementQuantity($couponModel, $event->getOrder()->getCustomerId());
@@ -302,12 +324,13 @@ class Coupon extends BaseAction implements EventSubscriberInterface
$orderCoupon->setOrder($event->getOrder())
->setCode($couponModel->getCode())
->setType($couponModel->getType())
->setAmount($couponModel->getAmount())
->setAmount($couponCode->exec())
->setTitle($couponModel->getTitle())
->setShortDescription($couponModel->getShortDescription())
->setDescription($couponModel->getDescription())
->setStartDate($couponModel->getStartDate())
->setExpirationDate($couponModel->getExpirationDate())
->setIsCumulative($couponModel->getIsCumulative())
->setIsRemovingPostage($couponModel->getIsRemovingPostage())
@@ -354,42 +377,90 @@ class Coupon extends BaseAction implements EventSubscriberInterface
}
// Clear all coupons.
$event->getDispatcher()->dispatch(TheliaEvents::COUPON_CLEAR_ALL);
$dispatcher->dispatch(TheliaEvents::COUPON_CLEAR_ALL);
}
/**
* Returns an array of event names this subscriber listens to.
* Cancels order coupons usage when order is canceled or refunded,
* or use canceled coupons again if the order is no longer canceled or refunded
*
* The array keys are event names and the value can be:
*
* * The method name to call (priority defaults to 0)
* * An array composed of the method name to call and the priority
* * An array of arrays composed of the method names to call and respective
* priorities, or 0 if unset
*
* For instance:
*
* * array('eventName' => 'methodName')
* * array('eventName' => array('methodName', $priority))
* * array('eventName' => array(array('methodName1', $priority), array('methodName2'))
*
* @return array The event names to listen to
*
* @api
* @param OrderEvent $event
* @param string $eventName
* @param EventDispatcherInterface $dispatcher
* @throws \Exception
* @throws \Propel\Runtime\Exception\PropelException
*/
public function orderStatusChange(OrderEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
// The order has been canceled or refunded ?
if ($event->getOrder()->isCancelled() || $event->getOrder()->isRefunded()) {
// Cancel usage of all coupons for this order
$usedCoupons = OrderCouponQuery::create()
->filterByUsageCanceled(false)
->findByOrderId($event->getOrder()->getId());
$customerId = $event->getOrder()->getCustomerId();
/** @var OrderCoupon $usedCoupon */
foreach ($usedCoupons as $usedCoupon) {
if (null !== $couponModel = CouponQuery::create()->findOneByCode($usedCoupon->getCode())) {
// If the coupon still exists, restore one usage to the usage count.
$this->couponManager->incrementQuantity($couponModel, $customerId);
}
// Mark coupon usage as canceled in the OrderCoupon table
$usedCoupon->setUsageCanceled(true)->save();
}
} else {
// Mark canceled coupons for this order as used again
$usedCoupons = OrderCouponQuery::create()
->filterByUsageCanceled(true)
->findByOrderId($event->getOrder()->getId());
$customerId = $event->getOrder()->getCustomerId();
/** @var OrderCoupon $usedCoupon */
foreach ($usedCoupons as $usedCoupon) {
if (null !== $couponModel = CouponQuery::create()->findOneByCode($usedCoupon->getCode())) {
// If the coupon still exists, mark the coupon as used
$this->couponManager->decrementQuantity($couponModel, $customerId);
}
// The coupon is no longer canceled
$usedCoupon->setUsageCanceled(false)->save();
}
}
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents()
{
return array(
TheliaEvents::COUPON_CREATE => array("create", 128),
TheliaEvents::COUPON_UPDATE => array("update", 128),
TheliaEvents::COUPON_DELETE => array("delete", 128),
TheliaEvents::COUPON_CONSUME => array("consume", 128),
TheliaEvents::COUPON_CLEAR_ALL => array("clearAllCoupons", 128),
TheliaEvents::COUPON_CONDITION_UPDATE => array("updateCondition", 128),
TheliaEvents::ORDER_SET_POSTAGE => array("testFreePostage", 132),
TheliaEvents::ORDER_BEFORE_PAYMENT => array("afterOrder", 128),
TheliaEvents::ORDER_UPDATE_STATUS => array("orderStatusChange", 10),
TheliaEvents::CART_ADDITEM => array("updateOrderDiscount", 10),
TheliaEvents::CART_UPDATEITEM => array("updateOrderDiscount", 10),
TheliaEvents::CART_DELETEITEM => array("updateOrderDiscount", 10),
TheliaEvents::CUSTOMER_LOGIN => array("updateOrderDiscount", 10)
);
}
/**
* Returns the session from the current request
*
* @return \Thelia\Core\HttpFoundation\Session\Session
*/
protected function getSession()
{
return $this->requestStack->getCurrentRequest()->getSession();
}
}

View File

@@ -12,40 +12,54 @@
namespace Thelia\Action;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Thelia\Core\Event\ActionEvent;
use Thelia\Model\CurrencyQuery;
use Thelia\Model\Currency as CurrencyModel;
use Thelia\Core\Event\TheliaEvents;
use Thelia\Core\Event\Currency\CurrencyUpdateEvent;
use Thelia\Core\Event\Currency\CurrencyCreateEvent;
use Thelia\Core\Event\Currency\CurrencyDeleteEvent;
use Thelia\Model\ConfigQuery;
use Thelia\Core\Event\Currency\CurrencyUpdateEvent;
use Thelia\Core\Event\Currency\CurrencyUpdateRateEvent;
use Thelia\Core\Event\TheliaEvents;
use Thelia\Core\Event\UpdatePositionEvent;
use Thelia\Core\Translation\Translator;
use Thelia\CurrencyConverter\CurrencyConverter;
use Thelia\CurrencyConverter\Exception\CurrencyNotFoundException;
use Thelia\Log\Tlog;
use Thelia\Math\Number;
use Thelia\Model\Currency as CurrencyModel;
use Thelia\Model\CurrencyQuery;
class Currency extends BaseAction implements EventSubscriberInterface
{
/** @var CurrencyConverter */
protected $currencyConverter;
public function __construct(CurrencyConverter $currencyConverter)
{
$this->currencyConverter = $currencyConverter;
}
/**
* Create a new currencyuration entry
*
* @param \Thelia\Core\Event\Currency\CurrencyCreateEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function create(CurrencyCreateEvent $event)
public function create(CurrencyCreateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$currency = new CurrencyModel();
$currency
->setDispatcher($event->getDispatcher())
$isDefault = CurrencyQuery::create()->count() === 0;
$currency
->setDispatcher($dispatcher)
->setLocale($event->getLocale())
->setName($event->getCurrencyName())
->setSymbol($event->getSymbol())
->setFormat($event->getFormat())
->setRate($event->getRate())
->setCode(strtoupper($event->getCode()))
->setByDefault($isDefault)
->save()
;
@@ -56,17 +70,19 @@ class Currency extends BaseAction implements EventSubscriberInterface
* Change a currency
*
* @param \Thelia\Core\Event\Currency\CurrencyUpdateEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function update(CurrencyUpdateEvent $event)
public function update(CurrencyUpdateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if (null !== $currency = CurrencyQuery::create()->findPk($event->getCurrencyId())) {
$currency
->setDispatcher($event->getDispatcher())
->setDispatcher($dispatcher)
->setLocale($event->getLocale())
->setName($event->getCurrencyName())
->setSymbol($event->getSymbol())
->setFormat($event->getFormat())
->setRate($event->getRate())
->setCode(strtoupper($event->getCode()))
@@ -80,36 +96,63 @@ class Currency extends BaseAction implements EventSubscriberInterface
* Set the default currency
*
* @param CurrencyUpdateEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function setDefault(CurrencyUpdateEvent $event)
public function setDefault(CurrencyUpdateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if (null !== $currency = CurrencyQuery::create()->findPk($event->getCurrencyId())) {
// Reset default status
CurrencyQuery::create()->filterByByDefault(true)->update(array('ByDefault' => false));
$currency
->setDispatcher($event->getDispatcher())
->setDispatcher($dispatcher)
->setVisible($event->getVisible())
->setByDefault($event->getIsDefault())
->save()
;
// Update rates when setting a new default currency
if ($event->getIsDefault()) {
$updateRateEvent = new CurrencyUpdateRateEvent();
$dispatcher->dispatch(TheliaEvents::CURRENCY_UPDATE_RATES, $updateRateEvent);
}
$event->setCurrency($currency);
}
}
/**
* @param CurrencyUpdateEvent $event
*/
public function setVisible(CurrencyUpdateEvent $event)
{
if (null !== $currency = CurrencyQuery::create()->findPk($event->getCurrencyId())) {
if (!$currency->getByDefault()) {
$currency->setVisible($event->getVisible())->save();
}
}
}
/**
* Delete a currencyuration entry
*
* @param \Thelia\Core\Event\Currency\CurrencyDeleteEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function delete(CurrencyDeleteEvent $event)
public function delete(CurrencyDeleteEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if (null !== ($currency = CurrencyQuery::create()->findPk($event->getCurrencyId()))) {
if ($currency->getByDefault()) {
throw new \RuntimeException(
Translator::getInstance()->trans('It is not allowed to delete the default currency')
);
}
$currency
->setDispatcher($event->getDispatcher())
->setDispatcher($dispatcher)
->delete()
;
@@ -117,39 +160,45 @@ class Currency extends BaseAction implements EventSubscriberInterface
}
}
public function updateRates(ActionEvent $event)
public function updateRates(CurrencyUpdateRateEvent $event)
{
$rates_url = ConfigQuery::read('currency_rate_update_url', 'http://www.ecb.int/stats/eurofxref/eurofxref-daily.xml');
if (null === $defaultCurrency = CurrencyQuery::create()->findOneByByDefault(true)) {
throw new \RuntimeException('Unable to find a default currency, please define a default currency.');
}
$rate_data = @file_get_contents($rates_url);
$defaultCurrency->setRate(1)->save();
if ($rate_data && $sxe = new \SimpleXMLElement($rate_data)) {
$currencies = CurrencyQuery::create()->filterByByDefault(false);
$baseValue = new Number('1');
foreach ($sxe->Cube[0]->Cube[0]->Cube as $last) {
$code = strtoupper($last["currency"]);
$rate = floatval($last['rate']);
/** @var \Thelia\Model\Currency $currency */
foreach ($currencies as $currency) {
try {
$rate = $this->currencyConverter
->from($defaultCurrency->getCode())
->to($currency->getCode())
->convert($baseValue);
if (null !== $currency = CurrencyQuery::create()->findOneByCode($code)) {
$currency
->setDispatcher($event->getDispatcher())
->setRate($rate)
->save()
;
}
$currency->setRate($rate->getNumber(-1))->save();
} catch (CurrencyNotFoundException $ex) {
Tlog::getInstance()->addError(
sprintf("Unable to find exchange rate for currency %s, ID %d", $currency->getCode(), $currency->getId())
);
$event->addUndefinedRate($currency->getId());
}
} else {
throw new \RuntimeException(sprintf("Failed to get currency rates data from URL %s", $rates_url));
}
}
/**
* Changes position, selecting absolute ou relative change.
*
* @param CategoryChangePositionEvent $event
* @param UpdatePositionEvent $event
* @param string $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function updatePosition(UpdatePositionEvent $event)
public function updatePosition(UpdatePositionEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$this->genericUpdatePosition(CurrencyQuery::create(), $event);
$this->genericUpdatePosition(CurrencyQuery::create(), $event, $dispatcher);
}
/**
@@ -162,6 +211,7 @@ class Currency extends BaseAction implements EventSubscriberInterface
TheliaEvents::CURRENCY_UPDATE => array("update", 128),
TheliaEvents::CURRENCY_DELETE => array("delete", 128),
TheliaEvents::CURRENCY_SET_DEFAULT => array("setDefault", 128),
TheliaEvents::CURRENCY_SET_VISIBLE => array("setVisible", 128),
TheliaEvents::CURRENCY_UPDATE_RATES => array("updateRates", 128),
TheliaEvents::CURRENCY_UPDATE_POSITION => array("updatePosition", 128)
);

View File

@@ -12,23 +12,21 @@
namespace Thelia\Action;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Thelia\Core\Event\ActionEvent;
use Thelia\Core\Event\Customer\CustomerCreateOrUpdateEvent;
use Thelia\Core\Event\Customer\CustomerEvent;
use Thelia\Core\Event\Customer\CustomerLoginEvent;
use Thelia\Core\Event\LostPasswordEvent;
use Thelia\Core\Event\TheliaEvents;
use Thelia\Core\Security\SecurityContext;
use Thelia\Core\Template\ParserInterface;
use Thelia\Core\Translation\Translator;
use Thelia\Exception\CustomerException;
use Thelia\Mailer\MailerFactory;
use Thelia\Model\ConfigQuery;
use Thelia\Model\Customer as CustomerModel;
use Thelia\Core\Event\Customer\CustomerLoginEvent;
use Thelia\Model\CustomerQuery;
use Thelia\Model\LangQuery;
use Thelia\Model\MessageQuery;
use Thelia\Tools\Password;
/**
@@ -37,46 +35,77 @@ use Thelia\Tools\Password;
*
* Class Customer
* @package Thelia\Action
* @author Manuel Raynaud <mraynaud@openstudio.fr>
* @author Manuel Raynaud <manu@raynaud.io>
*/
class Customer extends BaseAction implements EventSubscriberInterface
{
/** @var SecurityContext */
protected $securityContext;
protected $parser;
/** @var MailerFactory */
protected $mailer;
public function __construct(SecurityContext $securityContext, ParserInterface $parser, MailerFactory $mailer)
public function __construct(SecurityContext $securityContext, MailerFactory $mailer)
{
$this->securityContext = $securityContext;
$this->mailer = $mailer;
$this->parser = $parser;
}
public function create(CustomerCreateOrUpdateEvent $event)
public function create(CustomerCreateOrUpdateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$customer = new CustomerModel();
$this->createOrUpdateCustomer($customer, $event);
$plainPassword = $event->getPassword();
$this->createOrUpdateCustomer($customer, $event, $dispatcher);
if ($event->getNotifyCustomerOfAccountCreation()) {
$this->mailer->sendEmailToCustomer(
'customer_account_created',
$customer,
[ 'password' => $plainPassword ]
);
}
$dispatcher->dispatch(
TheliaEvents::SEND_ACCOUNT_CONFIRMATION_EMAIL,
new CustomerEvent($customer)
);
}
public function modify(CustomerCreateOrUpdateEvent $event)
{
$customer = $event->getCustomer();
$this->createOrUpdateCustomer($customer, $event);
}
public function updateProfile(CustomerCreateOrUpdateEvent $event)
public function customerConfirmationEmail(CustomerEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$customer = $event->getCustomer();
$customer->setDispatcher($event->getDispatcher());
if (ConfigQuery::isCustomerEmailConfirmationEnable() && $customer->getConfirmationToken() !== null && $customer !== null) {
$this->mailer->sendEmailToCustomer(
'customer_confirmation',
$customer,
['customer_id' => $customer->getId()]
);
}
}
public function modify(CustomerCreateOrUpdateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$plainPassword = $event->getPassword();
$customer = $event->getCustomer();
$emailChanged = $customer->getEmail() !== $event->getEmail();
$this->createOrUpdateCustomer($customer, $event, $dispatcher);
if (! empty($plainPassword) || $emailChanged) {
$this->mailer->sendEmailToCustomer('customer_account_changed', $customer, ['password' => $plainPassword]);
}
}
public function updateProfile(CustomerCreateOrUpdateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$customer = $event->getCustomer();
$customer->setDispatcher($dispatcher);
if ($event->getTitle() !== null) {
$customer->setTitleId($event->getTitle());
@@ -91,7 +120,7 @@ class Customer extends BaseAction implements EventSubscriberInterface
}
if ($event->getEmail() !== null) {
$customer->setEmail($event->getEmail());
$customer->setEmail($event->getEmail(), $event->getEmailUpdateAllowed());
}
if ($event->getPassword() !== null) {
@@ -118,7 +147,6 @@ class Customer extends BaseAction implements EventSubscriberInterface
public function delete(CustomerEvent $event)
{
if (null !== $customer = $event->getCustomer()) {
if (true === $customer->hasOrder()) {
throw new CustomerException(Translator::getInstance()->trans("Impossible to delete a customer who already have orders"));
}
@@ -127,9 +155,9 @@ class Customer extends BaseAction implements EventSubscriberInterface
}
}
private function createOrUpdateCustomer(CustomerModel $customer, CustomerCreateOrUpdateEvent $event)
private function createOrUpdateCustomer(CustomerModel $customer, CustomerCreateOrUpdateEvent $event, EventDispatcherInterface $dispatcher)
{
$customer->setDispatcher($event->getDispatcher());
$customer->setDispatcher($dispatcher);
$customer->createOrUpdate(
$event->getTitle(),
@@ -145,12 +173,14 @@ class Customer extends BaseAction implements EventSubscriberInterface
$event->getCountry(),
$event->getEmail(),
$event->getPassword(),
$event->getLang(),
$event->getLangId(),
$event->getReseller(),
$event->getSponsor(),
$event->getDiscount(),
$event->getCompany(),
$event->getRef()
$event->getRef(),
$event->getEmailUpdateAllowed(),
$event->getState()
);
$event->setCustomer($customer);
@@ -171,84 +201,27 @@ class Customer extends BaseAction implements EventSubscriberInterface
*
* @param ActionEvent $event
*/
public function logout(ActionEvent $event)
public function logout(/** @noinspection PhpUnusedParameterInspection */ ActionEvent $event)
{
$this->securityContext->clearCustomerUser();
}
public function lostPassword(LostPasswordEvent $event)
{
$contact_email = ConfigQuery::read('store_email');
if (null !== $customer = CustomerQuery::create()->filterByEmail($event->getEmail())->findOne()) {
$password = Password::generateRandom(8);
if ($contact_email) {
if (null !== $customer = CustomerQuery::create()->filterByEmail($event->getEmail())->findOne()) {
$customer
->setPassword($password)
->save()
;
$password = Password::generateRandom(8);
$customer
->setPassword($password)
->save()
;
if ($customer->getLang() !== null) {
$lang = LangQuery::create()
->findPk($customer->getLang());
$locale = $lang->getLocale();
} else {
$lang = LangQuery::create()
->filterByByDefault(1)
->findOne();
$locale = $lang->getLocale();
}
$message = MessageQuery::create()
->filterByName('lost_password')
->findOne();
$message->setLocale($locale);
if (false === $message) {
throw new \Exception("Failed to load message 'order_confirmation'.");
}
$this->parser->assign('password', $password);
$instance = \Swift_Message::newInstance()
->addTo($customer->getEmail(), $customer->getFirstname()." ".$customer->getLastname())
->addFrom($contact_email, ConfigQuery::read('store_name'))
;
// Build subject and body
$message->buildMessage($this->parser, $instance);
$this->mailer->send($instance);
}
$this->mailer->sendEmailToCustomer('lost_password', $customer, ['password' => $password]);
}
}
/**
* Returns an array of event names this subscriber wants to listen to.
*
* The array keys are event names and the value can be:
*
* * The method name to call (priority defaults to 0)
* * An array composed of the method name to call and the priority
* * An array of arrays composed of the method names to call and respective
* priorities, or 0 if unset
*
* For instance:
*
* * array('eventName' => 'methodName')
* * array('eventName' => array('methodName', $priority))
* * array('eventName' => array(array('methodName1', $priority), array('methodName2'))
*
* @return array The event names to listen to
*
* @api
* {@inheritdoc}
*/
public static function getSubscribedEvents()
{
@@ -259,7 +232,8 @@ class Customer extends BaseAction implements EventSubscriberInterface
TheliaEvents::CUSTOMER_LOGOUT => array('logout', 128),
TheliaEvents::CUSTOMER_LOGIN => array('login', 128),
TheliaEvents::CUSTOMER_DELETEACCOUNT => array('delete', 128),
TheliaEvents::LOST_PASSWORD => array('lostPassword', 128)
TheliaEvents::LOST_PASSWORD => array('lostPassword', 128),
TheliaEvents::SEND_ACCOUNT_CONFIRMATION_EMAIL => array('customerConfirmationEmail', 128)
);
}
}

View File

@@ -0,0 +1,111 @@
<?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\Action;
use Propel\Runtime\Propel;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Thelia\Core\Event\CustomerTitle\CustomerTitleEvent;
use Thelia\Core\Event\TheliaEvents;
use Thelia\Model\CustomerTitle as CustomerTitleModel;
use Thelia\Model\Map\CustomerTitleTableMap;
/**
* Class CustomerTitle
* @package Thelia\Action
* @author Benjamin Perche <bperche@openstudio.fr>
*/
class CustomerTitle extends BaseAction implements EventSubscriberInterface
{
public function create(CustomerTitleEvent $event)
{
$this->createOrUpdate($event, new CustomerTitleModel());
}
public function update(CustomerTitleEvent $event)
{
$this->checkCustomerTitle($event);
$this->createOrUpdate($event, $event->getCustomerTitle());
}
public function delete(CustomerTitleEvent $event)
{
$this->checkCustomerTitle($event);
$con = Propel::getConnection(CustomerTitleTableMap::DATABASE_NAME);
$con->beginTransaction();
try {
$event->getCustomerTitle()->delete();
$con->commit();
} catch (\Exception $e) {
$con->rollBack();
throw $e;
}
$event->setCustomerTitle(null);
}
protected function checkCustomerTitle(CustomerTitleEvent $event)
{
if (null === $event->getCustomerTitle()) {
throw new \LogicException(
"You must set the customer title before its update"
);
}
}
protected function createOrUpdate(CustomerTitleEvent $event, CustomerTitleModel $customerTitle)
{
$con = Propel::getConnection(CustomerTitleTableMap::DATABASE_NAME);
$con->beginTransaction();
$i18n = $customerTitle->getTranslation($event->getLocale(), $con);
try {
$i18n
->setShort($event->getShort())
->setLong($event->getLong())
;
$customerTitle->save($con);
if ($event->isDefault()) {
$customerTitle->toggleDefault($con);
$event->setDefault(false);
}
$con->commit();
} catch (\Exception $e) {
$con->rollBack();
throw $e;
}
$event->setCustomerTitle($customerTitle);
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents()
{
return array(
TheliaEvents::CUSTOMER_TITLE_CREATE => array("create"),
TheliaEvents::CUSTOMER_TITLE_UPDATE => array("update"),
TheliaEvents::CUSTOMER_TITLE_DELETE => array("delete"),
);
}
}

View File

@@ -0,0 +1,66 @@
<?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\Action;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Thelia\Core\Event\Delivery\DeliveryPostageEvent;
use Thelia\Core\Event\TheliaEvents;
/**
* Class Delivery
* @package Thelia\Action
* @author Julien Chanséaume <julien@thelia.net>
*/
class Delivery implements EventSubscriberInterface
{
/**
* Get postage from module using the classical module functions
*
* @param DeliveryPostageEvent $event
*/
public function getPostage(DeliveryPostageEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$module = $event->getModule();
// dispatch event to target specific module
$dispatcher->dispatch(
TheliaEvents::getModuleEvent(
TheliaEvents::MODULE_DELIVERY_GET_POSTAGE,
$module->getCode()
),
$event
);
if ($event->isPropagationStopped()) {
return;
}
// call legacy module method
$event->setValidModule($module->isValidDelivery($event->getCountry()));
if ($event->isValidModule()) {
$event->setPostage($module->getPostage($event->getCountry()));
}
}
/**
* @inheritdoc
*/
public static function getSubscribedEvents()
{
return [
TheliaEvents::MODULE_DELIVERY_GET_POSTAGE => ['getPostage', 128]
];
}
}

View File

@@ -41,12 +41,17 @@ use Thelia\Tools\URL;
*/
class Document extends BaseCachedFile implements EventSubscriberInterface
{
/**
* @var string Config key for document delivery mode
*/
const CONFIG_DELIVERY_MODE = 'original_document_delivery_mode';
/**
* @return string root of the document cache directory in web space
*/
protected function getCacheDirFromWebRoot()
{
return ConfigQuery::read('document_cache_dir_from_web_root', 'cache');
return ConfigQuery::read('document_cache_dir_from_web_root', 'cache' . DS . 'documents');
}
/**
@@ -74,20 +79,19 @@ class Document extends BaseCachedFile implements EventSubscriberInterface
$originalDocumentPathInCache = $this->getCacheFilePath($subdir, $sourceFile, true);
if (! file_exists($originalDocumentPathInCache)) {
if (! file_exists($sourceFile)) {
throw new DocumentException(sprintf("Source document file %s does not exists.", $sourceFile));
}
$mode = ConfigQuery::read('original_document_delivery_mode', 'symlink');
$mode = ConfigQuery::read(self::CONFIG_DELIVERY_MODE, 'symlink');
if ($mode == 'symlink') {
if (false == symlink($sourceFile, $originalDocumentPathInCache)) {
throw new DocumentException(sprintf("Failed to create symbolic link for %s in %s document cache directory", basename($sourceFile), $subdir));
if (false === symlink($sourceFile, $originalDocumentPathInCache)) {
throw new DocumentException(sprintf("Failed to create symbolic link for %s in %s document cache directory", basename($sourceFile), $subdir));
}
} else {
// mode = 'copy'
if (false == @copy($sourceFile, $originalDocumentPathInCache)) {
if (false === @copy($sourceFile, $originalDocumentPathInCache)) {
throw new DocumentException(sprintf("Failed to copy %s in %s document cache directory", basename($sourceFile), $subdir));
}
}
@@ -101,6 +105,9 @@ class Document extends BaseCachedFile implements EventSubscriberInterface
$event->setDocumentUrl(URL::getInstance()->absoluteUrl($documentUrl, null, URL::PATH_TO_FILE));
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents()
{
return array(
@@ -112,6 +119,7 @@ class Document extends BaseCachedFile implements EventSubscriberInterface
TheliaEvents::DOCUMENT_SAVE => array("saveFile", 128),
TheliaEvents::DOCUMENT_UPDATE => array("updateFile", 128),
TheliaEvents::DOCUMENT_UPDATE_POSITION => array("updatePosition", 128),
TheliaEvents::DOCUMENT_TOGGLE_VISIBILITY => array("toggleVisibility", 128),
);
}
}

View File

@@ -11,77 +11,69 @@
/*************************************************************************************/
namespace Thelia\Action;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Thelia\Core\Event\Cache\CacheEvent;
use Thelia\Core\Event\TheliaEvents;
use Thelia\Core\Event\UpdatePositionEvent;
use Thelia\Handler\ExportHandler;
use Thelia\Model\ExportCategoryQuery;
use Thelia\Model\ExportQuery;
/**
* Class Export
* @package Thelia\Action
* @author Benjamin Perche <bperche@openstudio.fr>
* @author Jérôme Billiras <jbilliras@openstudio.fr>
*/
class Export extends BaseAction implements EventSubscriberInterface
{
protected $environment;
/**
* @var \Thelia\Handler\ExportHandler The export handler
*/
protected $handler;
public function __construct($environment)
/**
* @param \Thelia\Handler\ExportHandler $exportHandler The export handler
*/
public function __construct(ExportHandler $exportHandler)
{
$this->environment = $environment;
$this->handler = $exportHandler;
}
public function changeCategoryPosition(UpdatePositionEvent $event)
public static function getSubscribedEvents()
{
$this->genericUpdatePosition(new ExportCategoryQuery(), $event);
$this->cacheClear($event->getDispatcher());
}
public function changeExportPosition(UpdatePositionEvent $event)
{
$this->genericUpdatePosition(new ExportQuery(), $event);
$this->cacheClear($event->getDispatcher());
}
protected function cacheClear(EventDispatcherInterface $dispatcher)
{
$cacheEvent = new CacheEvent(
$this->environment
);
$dispatcher->dispatch(TheliaEvents::CACHE_CLEAR, $cacheEvent);
return [
TheliaEvents::EXPORT_CHANGE_POSITION => [
['exportChangePosition', 128]
],
TheliaEvents::EXPORT_CATEGORY_CHANGE_POSITION => [
['exportCategoryChangePosition', 128]
]
];
}
/**
* Returns an array of event names this subscriber wants to listen to.
* Handle export change position event
*
* The array keys are event names and the value can be:
*
* * The method name to call (priority defaults to 0)
* * An array composed of the method name to call and the priority
* * An array of arrays composed of the method names to call and respective
* priorities, or 0 if unset
*
* For instance:
*
* * array('eventName' => 'methodName')
* * array('eventName' => array('methodName', $priority))
* * array('eventName' => array(array('methodName1', $priority), array('methodName2'))
*
* @return array The event names to listen to
*
* @api
* @param UpdatePositionEvent $updatePositionEvent
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public static function getSubscribedEvents()
public function exportChangePosition(UpdatePositionEvent $updatePositionEvent, $eventName, EventDispatcherInterface $dispatcher)
{
return array(
TheliaEvents::EXPORT_CATEGORY_CHANGE_POSITION => array("changeCategoryPosition", 128),
TheliaEvents::EXPORT_CHANGE_POSITION => array("changeExportPosition", 128),
);
$this->handler->getExport($updatePositionEvent->getObjectId(), true);
$this->genericUpdatePosition(new ExportQuery, $updatePositionEvent, $dispatcher);
}
/**
* Handle export category change position event
*
* @param UpdatePositionEvent $updatePositionEvent
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function exportCategoryChangePosition(UpdatePositionEvent $updatePositionEvent, $eventName, EventDispatcherInterface $dispatcher)
{
$this->handler->getCategory($updatePositionEvent->getObjectId(), true);
$this->genericUpdatePosition(new ExportCategoryQuery, $updatePositionEvent, $dispatcher);
}
}

View File

@@ -12,13 +12,11 @@
namespace Thelia\Action;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Thelia\Model\FeatureQuery;
use Thelia\Model\Feature as FeatureModel;
use Thelia\Core\Event\TheliaEvents;
use Thelia\Core\Event\Feature\FeatureUpdateEvent;
use Thelia\Core\Event\Feature\FeatureCreateEvent;
use Thelia\Core\Event\Feature\FeatureDeleteEvent;
@@ -34,13 +32,15 @@ class Feature extends BaseAction implements EventSubscriberInterface
* Create a new feature entry
*
* @param \Thelia\Core\Event\Feature\FeatureCreateEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function create(FeatureCreateEvent $event)
public function create(FeatureCreateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$feature = new FeatureModel();
$feature
->setDispatcher($event->getDispatcher())
->setDispatcher($dispatcher)
->setLocale($event->getLocale())
->setTitle($event->getTitle())
@@ -60,14 +60,14 @@ class Feature extends BaseAction implements EventSubscriberInterface
* Change a product feature
*
* @param \Thelia\Core\Event\Feature\FeatureUpdateEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function update(FeatureUpdateEvent $event)
public function update(FeatureUpdateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if (null !== $feature = FeatureQuery::create()->findPk($event->getFeatureId())) {
$feature
->setDispatcher($event->getDispatcher())
->setDispatcher($dispatcher)
->setLocale($event->getLocale())
->setTitle($event->getTitle())
@@ -85,14 +85,14 @@ class Feature extends BaseAction implements EventSubscriberInterface
* Delete a product feature entry
*
* @param FeatureDeleteEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function delete(FeatureDeleteEvent $event)
public function delete(FeatureDeleteEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if (null !== ($feature = FeatureQuery::create()->findPk($event->getFeatureId()))) {
$feature
->setDispatcher($event->getDispatcher())
->setDispatcher($dispatcher)
->delete()
;
@@ -104,10 +104,12 @@ class Feature extends BaseAction implements EventSubscriberInterface
* Changes position, selecting absolute ou relative change.
*
* @param UpdatePositionEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function updatePosition(UpdatePositionEvent $event)
public function updatePosition(UpdatePositionEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$this->genericUpdatePosition(FeatureQuery::create(), $event);
$this->genericUpdatePosition(FeatureQuery::create(), $event, $dispatcher);
}
protected function doAddToAllTemplates(FeatureModel $feature)
@@ -115,7 +117,6 @@ class Feature extends BaseAction implements EventSubscriberInterface
$templates = TemplateQuery::create()->find();
foreach ($templates as $template) {
$feature_template = new FeatureTemplate();
if (null === FeatureTemplateQuery::create()->filterByFeature($feature)->filterByTemplate($template)->findOne()) {

View File

@@ -12,13 +12,11 @@
namespace Thelia\Action;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Thelia\Model\FeatureAvQuery;
use Thelia\Model\FeatureAv as FeatureAvModel;
use Thelia\Core\Event\TheliaEvents;
use Thelia\Core\Event\Feature\FeatureAvUpdateEvent;
use Thelia\Core\Event\Feature\FeatureAvCreateEvent;
use Thelia\Core\Event\Feature\FeatureAvDeleteEvent;
@@ -30,13 +28,15 @@ class FeatureAv extends BaseAction implements EventSubscriberInterface
* Create a new feature entry
*
* @param FeatureAvCreateEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function create(FeatureAvCreateEvent $event)
public function create(FeatureAvCreateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$feature = new FeatureAvModel();
$feature
->setDispatcher($event->getDispatcher())
->setDispatcher($dispatcher)
->setFeatureId($event->getFeatureId())
->setLocale($event->getLocale())
@@ -52,14 +52,14 @@ class FeatureAv extends BaseAction implements EventSubscriberInterface
* Change a product feature
*
* @param FeatureAvUpdateEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function update(FeatureAvUpdateEvent $event)
public function update(FeatureAvUpdateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if (null !== $feature = FeatureAvQuery::create()->findPk($event->getFeatureAvId())) {
$feature
->setDispatcher($event->getDispatcher())
->setDispatcher($dispatcher)
->setLocale($event->getLocale())
->setTitle($event->getTitle())
@@ -77,14 +77,14 @@ class FeatureAv extends BaseAction implements EventSubscriberInterface
* Delete a product feature entry
*
* @param FeatureAvDeleteEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function delete(FeatureAvDeleteEvent $event)
public function delete(FeatureAvDeleteEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if (null !== ($feature = FeatureAvQuery::create()->findPk($event->getFeatureAvId()))) {
$feature
->setDispatcher($event->getDispatcher())
->setDispatcher($dispatcher)
->delete()
;
@@ -96,10 +96,12 @@ class FeatureAv extends BaseAction implements EventSubscriberInterface
* Changes position, selecting absolute ou relative change.
*
* @param UpdatePositionEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function updatePosition(UpdatePositionEvent $event)
public function updatePosition(UpdatePositionEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$this->genericUpdatePosition(FeatureAvQuery::create(), $event);
$this->genericUpdatePosition(FeatureAvQuery::create(), $event, $dispatcher);
}
/**

View File

@@ -0,0 +1,175 @@
<?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\Action;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Thelia\Core\Event\File\FileCreateOrUpdateEvent;
use Thelia\Core\Event\Product\ProductCloneEvent;
use Thelia\Core\Event\TheliaEvents;
use Thelia\Log\Tlog;
use Thelia\Model\ProductDocument;
use Thelia\Model\ProductDocumentI18n;
use Thelia\Model\ProductDocumentI18nQuery;
use Thelia\Model\ProductDocumentQuery;
use Thelia\Model\ProductImage;
use Thelia\Model\ProductImageI18nQuery;
use Thelia\Model\ProductImageQuery;
/**
* Class File
*
* @package Thelia\Action
* @author Etienne Perriere <eperriere@openstudio.fr>
*/
class File extends BaseAction implements EventSubscriberInterface
{
public function cloneFile(ProductCloneEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$originalProductId = $event->getOriginalProduct()->getId();
$clonedProduct = $event->getClonedProduct();
foreach ($event->getTypes() as $type) {
$originalProductFiles = [];
switch ($type) {
case 'images':
$originalProductFiles = ProductImageQuery::create()
->findByProductId($originalProductId);
break;
case 'documents':
$originalProductFiles = ProductDocumentQuery::create()
->findByProductId($originalProductId);
break;
}
// Set clone's files
/** @var ProductDocument|ProductImage $originalProductFile */
foreach ($originalProductFiles as $originalProductFile) {
$srcPath = $originalProductFile->getUploadDir() . DS . $originalProductFile->getFile();
if (file_exists($srcPath)) {
$ext = pathinfo($srcPath, PATHINFO_EXTENSION);
$clonedProductFile = [];
switch ($type) {
case 'images':
$fileName = $clonedProduct->getRef().'.'.$ext;
$clonedProductFile = new ProductImage();
break;
case 'documents':
$fileName = pathinfo($originalProductFile->getFile(), PATHINFO_FILENAME).'-'.$clonedProduct->getRef().'.'.$ext;
$clonedProductFile = new ProductDocument();
break;
}
// Copy a temporary file of the source file as it will be deleted by IMAGE_SAVE or DOCUMENT_SAVE event
$srcTmp = $srcPath.'.tmp';
copy($srcPath, $srcTmp);
// Get file mimeType
$finfo = new \finfo();
$fileMimeType = $finfo->file($srcPath, FILEINFO_MIME_TYPE);
// Get file event's parameters
$clonedProductFile
->setProductId($clonedProduct->getId())
->setVisible($originalProductFile->getVisible())
->setPosition($originalProductFile->getPosition())
->setLocale($clonedProduct->getLocale())
->setTitle($clonedProduct->getTitle());
$clonedProductCopiedFile = new UploadedFile($srcPath, $fileName, $fileMimeType, filesize($srcPath), null, true);
// Create and dispatch event
$clonedProductCreateFileEvent = new FileCreateOrUpdateEvent($clonedProduct->getId());
$clonedProductCreateFileEvent
->setModel($clonedProductFile)
->setUploadedFile($clonedProductCopiedFile)
->setParentName($clonedProduct->getTitle());
$originalProductFileI18ns = [];
switch ($type) {
case 'images':
$dispatcher->dispatch(TheliaEvents::IMAGE_SAVE, $clonedProductCreateFileEvent);
// Get original product image I18n
$originalProductFileI18ns = ProductImageI18nQuery::create()
->findById($originalProductFile->getId());
break;
case 'documents':
$dispatcher->dispatch(TheliaEvents::DOCUMENT_SAVE, $clonedProductCreateFileEvent);
// Get original product document I18n
$originalProductFileI18ns = ProductDocumentI18nQuery::create()
->findById($originalProductFile->getId());
break;
}
// Set temporary source file as original one
rename($srcTmp, $srcPath);
// Clone file's I18n
$this->cloneFileI18n($originalProductFileI18ns, $clonedProductFile, $type, $event, $dispatcher);
} else {
Tlog::getInstance()->addWarning("Failed to find media file $srcPath");
}
}
}
}
public function cloneFileI18n($originalProductFileI18ns, $clonedProductFile, $type, ProductCloneEvent $event, EventDispatcherInterface $dispatcher)
{
// Set clone files I18n
/** @var ProductDocumentI18n $originalProductFileI18n */
foreach ($originalProductFileI18ns as $originalProductFileI18n) {
// Update file with current I18n info. Update or create I18n according to existing or absent Locale in DB
$clonedProductFile
->setLocale($originalProductFileI18n->getLocale())
->setTitle($originalProductFileI18n->getTitle())
->setDescription($originalProductFileI18n->getDescription())
->setChapo($originalProductFileI18n->getChapo())
->setPostscriptum($originalProductFileI18n->getPostscriptum());
// Create and dispatch event
$clonedProductUpdateFileEvent = new FileCreateOrUpdateEvent($event->getClonedProduct()->getId());
$clonedProductUpdateFileEvent->setModel($clonedProductFile);
switch ($type) {
case 'images':
$dispatcher->dispatch(TheliaEvents::IMAGE_UPDATE, $clonedProductUpdateFileEvent);
break;
case 'documents':
$dispatcher->dispatch(TheliaEvents::DOCUMENT_UPDATE, $clonedProductUpdateFileEvent);
break;
}
}
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents()
{
return array(
TheliaEvents::FILE_CLONE => array("cloneFile", 128)
);
}
}

View File

@@ -11,7 +11,12 @@
/*************************************************************************************/
namespace Thelia\Action;
use Propel\Runtime\Propel;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Thelia\Core\Event\File\FileDeleteEvent;
use Thelia\Core\Event\Folder\FolderCreateEvent;
use Thelia\Core\Event\Folder\FolderDeleteEvent;
use Thelia\Core\Event\Folder\FolderToggleVisibilityEvent;
@@ -19,21 +24,24 @@ use Thelia\Core\Event\Folder\FolderUpdateEvent;
use Thelia\Core\Event\TheliaEvents;
use Thelia\Core\Event\UpdatePositionEvent;
use Thelia\Core\Event\UpdateSeoEvent;
use Thelia\Core\Event\ViewCheckEvent;
use Thelia\Model\FolderDocumentQuery;
use Thelia\Model\FolderImageQuery;
use Thelia\Model\FolderQuery;
use Thelia\Model\Folder as FolderModel;
use Thelia\Model\Map\FolderTableMap;
/**
* Class Folder
* @package Thelia\Action
* @author Manuel Raynaud <mraynaud@openstudio.fr>
* @author Manuel Raynaud <manu@raynaud.io>
*/
class Folder extends BaseAction implements EventSubscriberInterface
{
public function update(FolderUpdateEvent $event)
public function update(FolderUpdateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if (null !== $folder = FolderQuery::create()->findPk($event->getFolderId())) {
$folder->setDispatcher($event->getDispatcher());
$folder->setDispatcher($dispatcher);
$folder
->setParent($event->getParent())
@@ -53,32 +61,60 @@ class Folder extends BaseAction implements EventSubscriberInterface
/**
* Change Folder SEO
*
* @param \Thelia\Core\Event\UpdateSeoEvent $event
*
* @return mixed
* @param UpdateSeoEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
* @return Object
*/
public function updateSeo(UpdateSeoEvent $event)
public function updateSeo(UpdateSeoEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
return $this->genericUpdateSeo(FolderQuery::create(), $event);
return $this->genericUpdateSeo(FolderQuery::create(), $event, $dispatcher);
}
public function delete(FolderDeleteEvent $event)
public function delete(FolderDeleteEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if (null !== $folder = FolderQuery::create()->findPk($event->getFolderId())) {
$folder->setDispatcher($event->getDispatcher())
->delete();
$con = Propel::getWriteConnection(FolderTableMap::DATABASE_NAME);
$con->beginTransaction();
$event->setFolder($folder);
try {
$fileList = ['images' => [], 'documentList' => []];
// Get folder's files to delete after folder deletion
$fileList['images']['list'] = FolderImageQuery::create()
->findByFolderId($event->getFolderId());
$fileList['images']['type'] = TheliaEvents::IMAGE_DELETE;
$fileList['documentList']['list'] = FolderDocumentQuery::create()
->findByFolderId($event->getFolderId());
$fileList['documentList']['type'] = TheliaEvents::DOCUMENT_DELETE;
// Delete folder
$folder->setDispatcher($dispatcher)
->delete($con);
$event->setFolder($folder);
// Dispatch delete folder's files event
foreach ($fileList as $fileTypeList) {
foreach ($fileTypeList['list'] as $fileToDelete) {
$fileDeleteEvent = new FileDeleteEvent($fileToDelete);
$dispatcher->dispatch($fileTypeList['type'], $fileDeleteEvent);
}
}
$con->commit();
} catch (\Exception $e) {
$con->rollback();
throw $e;
}
}
}
/**
* @param FolderCreateEvent $event
*/
public function create(FolderCreateEvent $event)
public function create(FolderCreateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$folder = new FolderModel();
$folder->setDispatcher($event->getDispatcher());
$folder->setDispatcher($dispatcher);
$folder
->setParent($event->getParent())
@@ -90,23 +126,22 @@ class Folder extends BaseAction implements EventSubscriberInterface
$event->setFolder($folder);
}
public function toggleVisibility(FolderToggleVisibilityEvent $event)
public function toggleVisibility(FolderToggleVisibilityEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$folder = $event->getFolder();
$folder
->setDispatcher($event->getDispatcher())
->setDispatcher($dispatcher)
->setVisible(!$folder->getVisible())
->save();
$event->setFolder($folder);
}
public function updatePosition(UpdatePositionEvent $event)
public function updatePosition(UpdatePositionEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if (null !== $folder = FolderQuery::create()->findPk($event->getObjectId())) {
$folder->setDispatcher($event->getDispatcher());
$folder->setDispatcher($dispatcher);
switch ($event->getMode()) {
case UpdatePositionEvent::POSITION_ABSOLUTE:
@@ -123,24 +158,37 @@ class Folder extends BaseAction implements EventSubscriberInterface
}
/**
* Returns an array of event names this subscriber wants to listen to.
* Check if is a folder view and if folder_id is visible
*
* The array keys are event names and the value can be:
*
* * The method name to call (priority defaults to 0)
* * An array composed of the method name to call and the priority
* * An array of arrays composed of the method names to call and respective
* priorities, or 0 if unset
*
* For instance:
*
* * array('eventName' => 'methodName')
* * array('eventName' => array('methodName', $priority))
* * array('eventName' => array(array('methodName1', $priority), array('methodName2'))
*
* @return array The event names to listen to
*
* @api
* @param ViewCheckEvent $event
* @param string $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function viewCheck(ViewCheckEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if ($event->getView() == 'folder') {
$folder = FolderQuery::create()
->filterById($event->getViewId())
->filterByVisible(1)
->count();
if ($folder == 0) {
$dispatcher->dispatch(TheliaEvents::VIEW_FOLDER_ID_NOT_VISIBLE, $event);
}
}
}
/**
* @param ViewCheckEvent $event
* @throws NotFoundHttpException
*/
public function viewFolderIdNotVisible(ViewCheckEvent $event)
{
throw new NotFoundHttpException();
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents()
{
@@ -151,7 +199,10 @@ class Folder extends BaseAction implements EventSubscriberInterface
TheliaEvents::FOLDER_TOGGLE_VISIBILITY => array("toggleVisibility", 128),
TheliaEvents::FOLDER_UPDATE_POSITION => array("updatePosition", 128),
TheliaEvents::FOLDER_UPDATE_SEO => array('updateSeo', 128)
TheliaEvents::FOLDER_UPDATE_SEO => array('updateSeo', 128),
TheliaEvents::VIEW_CHECK => array('viewCheck', 128),
TheliaEvents::VIEW_FOLDER_ID_NOT_VISIBLE => array('viewFolderIdNotVisible', 128),
);
}
}

View File

@@ -0,0 +1,168 @@
<?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\Action;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Thelia\Core\Event\Cache\CacheEvent;
use Thelia\Core\Event\Hook\HookCreateAllEvent;
use Thelia\Core\Event\Hook\HookCreateEvent;
use Thelia\Core\Event\Hook\HookDeactivationEvent;
use Thelia\Core\Event\Hook\HookDeleteEvent;
use Thelia\Core\Event\Hook\HookToggleActivationEvent;
use Thelia\Core\Event\Hook\HookToggleNativeEvent;
use Thelia\Core\Event\Hook\HookUpdateEvent;
use Thelia\Core\Event\TheliaEvents;
use Thelia\Model\Hook as HookModel;
use Thelia\Model\HookQuery;
/**
* Class HookAction
* @package Thelia\Action
* @author Julien Chanséaume <jchanseaume@openstudio.fr>
*/
class Hook extends BaseAction implements EventSubscriberInterface
{
/** @var string */
protected $cacheDir;
public function __construct($cacheDir)
{
$this->cacheDir = $cacheDir;
}
public function create(HookCreateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$hook = new HookModel();
$hook
->setLocale($event->getLocale())
->setCode($event->getCode())
->setType($event->getType())
->setNative($event->getNative())
->setActivate($event->getActive())
->setTitle($event->getTitle())
->save();
$event->setHook($hook);
$this->cacheClear($dispatcher);
}
public function update(HookUpdateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if (null !== $hook = HookQuery::create()->findPk($event->getHookId())) {
$hook
->setLocale($event->getLocale())
->setCode($event->getCode())
->setType($event->getType())
->setNative($event->getNative())
->setActivate($event->getActive())
->setBlock($event->getBlock())
->setByModule($event->getByModule())
->setTitle($event->getTitle())
->setChapo($event->getChapo())
->setDescription($event->getDescription())
->save();
$event->setHook($hook);
$this->cacheClear($dispatcher);
}
}
public function delete(HookDeleteEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if (null !== $hook = HookQuery::create()->findPk($event->getHookId())) {
$hook->delete();
$event->setHook($hook);
$this->cacheClear($dispatcher);
}
}
public function createAll(HookCreateAllEvent $event)
{
$hook = new HookModel();
$hook
->setLocale($event->getLocale())
->setCode($event->getCode())
->setType($event->getType())
->setNative($event->getNative())
->setActivate($event->getActive())
->setBlock($event->getBlock())
->setByModule($event->getByModule())
->setTitle($event->getTitle())
->setChapo($event->getChapo())
->setDescription($event->getDescription())
->save();
$event->setHook($hook);
}
public function deactivation(HookDeactivationEvent $event)
{
if (null !== $hook = HookQuery::create()->findPk($event->getHookId())) {
$hook
->setActivate(false)
->save();
$event->setHook($hook);
}
}
public function toggleNative(HookToggleNativeEvent $event)
{
if (null !== $hook = HookQuery::create()->findPk($event->getHookId())) {
$hook
->setNative(!$hook->getNative())
->save();
$event->setHook($hook);
}
}
public function toggleActivation(HookToggleActivationEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if (null !== $hook = HookQuery::create()->findPk($event->getHookId())) {
$hook
->setActivate(!$hook->getActivate())
->save();
$event->setHook($hook);
$this->cacheClear($dispatcher);
}
}
protected function cacheClear(EventDispatcherInterface $dispatcher)
{
$cacheEvent = new CacheEvent($this->cacheDir);
$dispatcher->dispatch(TheliaEvents::CACHE_CLEAR, $cacheEvent);
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents()
{
return array(
TheliaEvents::HOOK_CREATE => array('create', 128),
TheliaEvents::HOOK_UPDATE => array('update', 128),
TheliaEvents::HOOK_DELETE => array('delete', 128),
TheliaEvents::HOOK_TOGGLE_ACTIVATION => array('toggleActivation', 128),
TheliaEvents::HOOK_TOGGLE_NATIVE => array('toggleNative', 128),
TheliaEvents::HOOK_CREATE_ALL => array('createAll', 128),
TheliaEvents::HOOK_DEACTIVATION => array('deactivation', 128),
);
}
}

View File

@@ -13,32 +13,30 @@
namespace Thelia\Action;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Thelia\Core\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Symfony\Component\HttpKernel\Exception\HttpException as BaseHttpException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\HttpKernel\KernelEvents;
use Thelia\Core\HttpFoundation\Response;
use Thelia\Core\Template\ParserInterface;
use Thelia\Exception\AdminAccessDenied;
use Thelia\Model\ConfigQuery;
use Thelia\Core\Template\TemplateHelper;
/**
*
* Class HttpException
* @package Thelia\Action
* @author Etienne Roudeix <eroudeix@openstudio.fr>
* @author Manuel Raynaud <manu@raynaud.io>
*/
class HttpException extends BaseAction implements EventSubscriberInterface
{
/**
* @var ParserInterface
*/
/** @var ParserInterface */
protected $parser;
public function __construct(ParserInterface $parser)
{
$this->parser = $parser;
$this->parser = $parser;
}
public function checkHttpException(GetResponseForExceptionEvent $event)
@@ -48,29 +46,33 @@ class HttpException extends BaseAction implements EventSubscriberInterface
$this->display404($event);
}
if ($exception instanceof AccessDeniedHttpException) {
$this->display403($event);
}
if ($exception instanceof AdminAccessDenied) {
$this->displayAdminGeneralError($event);
}
if ($exception instanceof BaseHttpException && null === $event->getResponse()) {
$this->displayException($event);
}
}
protected function displayAdminGeneralError(GetResponseForExceptionEvent $event)
{
// Define the template thant shoud be used
$this->parser->setTemplateDefinition(TemplateHelper::getInstance()->getActiveAdminTemplate());
$this->parser->setTemplateDefinition(
$this->parser->getTemplateHelper()->getActiveAdminTemplate()
);
$message = $event->getException()->getMessage();
$response = Response::create(
$this->parser->render('general_error.html',
$this->parser->render(
'general_error.html',
array(
"error_message" => $message
)),
)
),
403
) ;
);
$event->setResponse($response);
}
@@ -78,42 +80,35 @@ class HttpException extends BaseAction implements EventSubscriberInterface
protected function display404(GetResponseForExceptionEvent $event)
{
// Define the template thant shoud be used
$this->parser->setTemplateDefinition(TemplateHelper::getInstance()->getActiveFrontTemplate());
$this->parser->setTemplateDefinition(
$this->parser->getTemplateHelper()->getActiveFrontTemplate()
);
$response = new Response($this->parser->render(ConfigQuery::getPageNotFoundView()), 404);
$event->setResponse($response);
}
protected function display403(GetResponseForExceptionEvent $event)
protected function displayException(GetResponseForExceptionEvent $event)
{
$event->setResponse(new Response("You don't have access to this resources", 403));
/** @var \Symfony\Component\HttpKernel\Exception\HttpException $exception */
$exception = $event->getException();
$event->setResponse(
new Response(
$exception->getMessage(),
$exception->getStatusCode(),
$exception->getHeaders()
)
);
}
/**
* Returns an array of event names this subscriber wants to listen to.
*
* The array keys are event names and the value can be:
*
* * The method name to call (priority defaults to 0)
* * An array composed of the method name to call and the priority
* * An array of arrays composed of the method names to call and respective
* priorities, or 0 if unset
*
* For instance:
*
* * array('eventName' => 'methodName')
* * array('eventName' => array('methodName', $priority))
* * array('eventName' => array(array('methodName1', $priority), array('methodName2'))
*
* @return array The event names to listen to
*
* @api
* {@inheritdoc}
*/
public static function getSubscribedEvents()
{
return array(
KernelEvents::EXCEPTION => array("checkHttpException", 128),
KernelEvents::EXCEPTION => ["checkHttpException", 128],
);
}
}

View File

@@ -13,16 +13,21 @@
namespace Thelia\Action;
use Imagine\Image\Box;
use Imagine\Image\Color;
use Imagine\Image\ImageInterface;
use Imagine\Image\ImagineInterface;
use Imagine\Image\Palette\RGB;
use Imagine\Image\Point;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Thelia\Core\Event\Image\ImageEvent;
use Thelia\Core\Event\TheliaEvents;
use Thelia\Exception\ImageException;
use Thelia\Files\FileManager;
use Thelia\Model\ConfigQuery;
use Thelia\Tools\URL;
use Imagine\Imagick\Imagine as ImagickImagine;
use Imagine\Gmagick\Imagine as GmagickImagine;
use Imagine\Gd\Imagine;
/**
*
@@ -70,7 +75,7 @@ class Image extends BaseCachedFile implements EventSubscriberInterface
*/
protected function getCacheDirFromWebRoot()
{
return ConfigQuery::read('image_cache_dir_from_web_root', 'cache');
return ConfigQuery::read('image_cache_dir_from_web_root', 'cache' . DS . 'images');
}
/**
@@ -83,13 +88,14 @@ class Image extends BaseCachedFile implements EventSubscriberInterface
* This method updates the cache_file_path and file_url attributes of the event
*
* @param ImageEvent $event
* @param string $eventName
* @param EventDispatcherInterface $dispatcher
*
* @throws \Thelia\Exception\ImageException
* @throws \InvalidArgumentException
*/
public function processImage(ImageEvent $event)
public function processImage(ImageEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$subdir = $event->getCacheSubdirectory();
$source_file = $event->getSourceFilepath();
@@ -103,23 +109,22 @@ class Image extends BaseCachedFile implements EventSubscriberInterface
$originalImagePathInCache = $this->getCacheFilePath($subdir, $source_file, true);
if (! file_exists($cacheFilePath)) {
if (! file_exists($source_file)) {
throw new ImageException(sprintf("Source image file %s does not exists.", $source_file));
}
// Create a chached version of the original image in the web space, if not exists
// Create a cached version of the original image in the web space, if not exists
if (! file_exists($originalImagePathInCache)) {
$mode = ConfigQuery::read('original_image_delivery_mode', 'symlink');
if ($mode == 'symlink') {
if (false == symlink($source_file, $originalImagePathInCache)) {
throw new ImageException(sprintf("Failed to create symbolic link for %s in %s image cache directory", basename($source_file), $subdir));
if (false === symlink($source_file, $originalImagePathInCache)) {
throw new ImageException(sprintf("Failed to create symbolic link for %s in %s image cache directory", basename($source_file), $subdir));
}
} else {// mode = 'copy'
if (false == @copy($source_file, $originalImagePathInCache)) {
} else {
// mode = 'copy'
if (false === @copy($source_file, $originalImagePathInCache)) {
throw new ImageException(sprintf("Failed to copy %s in %s image cache directory", basename($source_file), $subdir));
}
}
@@ -127,97 +132,109 @@ class Image extends BaseCachedFile implements EventSubscriberInterface
// Process image only if we have some transformations to do.
if (! $event->isOriginalImage()) {
// We have to process the image.
$imagine = $this->createImagineInstance();
$image = $imagine->open($source_file);
if ($image) {
// Allow image pre-processing (watermarging, or other stuff...)
$event->setImageObject($image);
$event->getDispatcher()->dispatch(TheliaEvents::IMAGE_PREPROCESSING, $event);
$dispatcher->dispatch(TheliaEvents::IMAGE_PREPROCESSING, $event);
$image = $event->getImageObject();
$background_color = $event->getBackgroundColor();
$palette = new RGB();
if ($background_color != null) {
$bg_color = new Color($background_color);
} else
$bg_color = null;
$bg_color = $palette->color($background_color);
} else {
// Define a fully transparent white background color
$bg_color = $palette->color('fff', 0);
}
// Apply resize
$image = $this->applyResize($imagine, $image, $event->getWidth(), $event->getHeight(), $event->getResizeMode(), $bg_color);
$image = $this->applyResize(
$imagine,
$image,
$event->getWidth(),
$event->getHeight(),
$event->getResizeMode(),
$bg_color,
$event->getAllowZoom()
);
// Rotate if required
$rotation = intval($event->getRotation());
if ($rotation != 0)
if ($rotation != 0) {
$image->rotate($rotation, $bg_color);
}
// Flip
// Process each effects
foreach ($event->getEffects() as $effect) {
$effect = trim(strtolower($effect));
$params = explode(':', $effect);
switch ($params[0]) {
case 'greyscale':
case 'grayscale':
$image->effects()->grayscale();
break;
case 'greyscale':
case 'grayscale':
$image->effects()->grayscale();
break;
case 'negative':
$image->effects()->negative();
break;
case 'negative':
$image->effects()->negative();
break;
case 'horizontal_flip':
case 'hflip':
$image->flipHorizontally();
break;
case 'horizontal_flip':
case 'hflip':
$image->flipHorizontally();
break;
case 'vertical_flip':
case 'vflip':
$image-> flipVertically();
break;
case 'vertical_flip':
case 'vflip':
$image->flipVertically();
break;
case 'gamma':
// Syntax: gamma:value. Exemple: gamma:0.7
if (isset($params[1])) {
$gamma = floatval($params[1]);
case 'gamma':
// Syntax: gamma:value. Exemple: gamma:0.7
if (isset($params[1])) {
$gamma = floatval($params[1]);
$image->effects()->gamma($gamma);
}
break;
$image->effects()->gamma($gamma);
}
break;
case 'colorize':
// Syntax: colorize:couleur. Exemple: colorize:#ff00cc
if (isset($params[1])) {
$the_color = new Color($params[1]);
case 'colorize':
// Syntax: colorize:couleur. Exemple: colorize:#ff00cc
if (isset($params[1])) {
$the_color = $palette->color($params[1]);
$image->effects()->colorize($the_color);
}
break;
$image->effects()->colorize($the_color);
}
break;
}
}
$quality = $event->getQuality();
if (is_null($quality)) $quality = ConfigQuery::read('default_image_quality_percent', 75);
if (is_null($quality)) {
$quality = ConfigQuery::read('default_images_quality_percent', 75);
}
// Allow image post-processing (watermarging, or other stuff...)
$event->setImageObject($image);
$event->getDispatcher()->dispatch(TheliaEvents::IMAGE_POSTPROCESSING, $event);
$dispatcher->dispatch(TheliaEvents::IMAGE_POSTPROCESSING, $event);
$image = $event->getImageObject();
$image->save(
$cacheFilePath,
array('quality' => $quality)
);
$cacheFilePath,
array('quality' => $quality)
);
} else {
throw new ImageException(sprintf("Source file %s cannot be opened.", basename($source_file)));
}
@@ -248,23 +265,35 @@ class Image extends BaseCachedFile implements EventSubscriberInterface
* @param int $dest_height the required height
* @param int $resize_mode the resize mode (crop / bands / keep image ratio)p
* @param string $bg_color the bg_color used for bands
* @param bool $allow_zoom if true, image may be zoomed to matchrequired size. If false, image is not zoomed.
* @return ImageInterface the resized image.
*/
protected function applyResize(ImagineInterface $imagine, ImageInterface $image, $dest_width, $dest_height, $resize_mode, $bg_color)
{
protected function applyResize(
ImagineInterface $imagine,
ImageInterface $image,
$dest_width,
$dest_height,
$resize_mode,
$bg_color,
$allow_zoom = false
) {
if (! (is_null($dest_width) && is_null($dest_height))) {
$width_orig = $image->getSize()->getWidth();
$height_orig = $image->getSize()->getHeight();
if (is_null($dest_width))
$dest_width = $width_orig;
$ratio = $width_orig / $height_orig;
if (is_null($dest_height))
$dest_height = $height_orig;
if (is_null($dest_width)) {
$dest_width = $dest_height * $ratio;
}
if (is_null($resize_mode))
if (is_null($dest_height)) {
$dest_height = $dest_width / $ratio;
}
if (is_null($resize_mode)) {
$resize_mode = self::KEEP_IMAGE_RATIO;
}
$width_diff = $dest_width / $width_orig;
$height_diff = $dest_height / $height_orig;
@@ -272,45 +301,66 @@ class Image extends BaseCachedFile implements EventSubscriberInterface
$delta_x = $delta_y = $border_width = $border_height = 0;
if ($width_diff > 1 && $height_diff > 1) {
// Set the default final size. If zoom is allowed, we will get the required
// image dimension. Otherwise, the final image may be smaller than required.
if ($allow_zoom) {
$resize_width = $dest_width;
$resize_height = $dest_height;
} else {
$resize_width = $width_orig;
$resize_height = $height_orig;
}
$next_width = $width_orig;
$next_height = $height_orig;
$dest_width = ($resize_mode == self::EXACT_RATIO_WITH_BORDERS ? $dest_width : $next_width);
$dest_height = ($resize_mode == self::EXACT_RATIO_WITH_BORDERS ? $dest_height : $next_height);
// When cropping, be sure to always generate an image which is
// not smaller than the required size, zooming it if required.
if ($resize_mode == self::EXACT_RATIO_WITH_CROP) {
if ($allow_zoom) {
if ($width_diff > $height_diff) {
$resize_width = $dest_width;
$resize_height = intval($height_orig * $dest_width / $width_orig);
$delta_y = ($resize_height - $dest_height) / 2;
} else {
$resize_height = $dest_height;
$resize_width = intval(($width_orig * $resize_height) / $height_orig);
$delta_x = ($resize_width - $dest_width) / 2;
}
} else {
// No zoom : final image may be smaller than the required size.
$dest_width = $resize_width;
$dest_height = $resize_height;
}
}
} elseif ($width_diff > $height_diff) {
// Image height > image width
$next_height = $dest_height;
$next_width = intval(($width_orig * $next_height) / $height_orig);
$resize_height = $dest_height;
$resize_width = intval(($width_orig * $resize_height) / $height_orig);
if ($resize_mode == self::EXACT_RATIO_WITH_CROP) {
$next_width = $dest_width;
$next_height = intval($height_orig * $dest_width / $width_orig);
$delta_y = ($next_height - $dest_height) / 2;
$resize_width = $dest_width;
$resize_height = intval($height_orig * $dest_width / $width_orig);
$delta_y = ($resize_height - $dest_height) / 2;
} elseif ($resize_mode != self::EXACT_RATIO_WITH_BORDERS) {
$dest_width = $next_width;
$dest_width = $resize_width;
}
} else {
// Image width > image height
$next_width = $dest_width;
$next_height = intval($height_orig * $dest_width / $width_orig);
$resize_width = $dest_width;
$resize_height = intval($height_orig * $dest_width / $width_orig);
if ($resize_mode == self::EXACT_RATIO_WITH_CROP) {
$next_height = $dest_height;
$next_width = intval(($width_orig * $next_height) / $height_orig);
$delta_x = ($next_width - $dest_width) / 2;
$resize_height = $dest_height;
$resize_width = intval(($width_orig * $resize_height) / $height_orig);
$delta_x = ($resize_width - $dest_width) / 2;
} elseif ($resize_mode != self::EXACT_RATIO_WITH_BORDERS) {
$dest_height = $next_height;
$dest_height = $resize_height;
}
}
$image->resize(new Box($next_width, $next_height));
$image->resize(new Box($resize_width, $resize_height));
if ($resize_mode == self::EXACT_RATIO_WITH_BORDERS) {
$border_width = intval(($dest_width - $next_width) / 2);
$border_height = intval(($dest_height - $next_height) / 2);
$border_width = intval(($dest_width - $resize_width) / 2);
$border_height = intval(($dest_height - $resize_height) / 2);
$canvas = new Box($dest_width, $dest_height);
@@ -337,22 +387,25 @@ class Image extends BaseCachedFile implements EventSubscriberInterface
$driver = ConfigQuery::read("imagine_graphic_driver", "gd");
switch ($driver) {
case 'imagik':
$image = new \Imagine\Imagick\Imagine();
break;
case 'imagick':
$image = new ImagickImagine();
break;
case 'gmagick':
$image = new \Imagine\Gmagick\Imagine();
break;
case 'gmagick':
$image = new GmagickImagine();
break;
case 'gd':
default:
$image = new \Imagine\Gd\Imagine();
case 'gd':
default:
$image = new Imagine();
}
return $image;
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents()
{
return array(
@@ -364,6 +417,7 @@ class Image extends BaseCachedFile implements EventSubscriberInterface
TheliaEvents::IMAGE_SAVE => array("saveFile", 128),
TheliaEvents::IMAGE_UPDATE => array("updateFile", 128),
TheliaEvents::IMAGE_UPDATE_POSITION => array("updatePosition", 128),
TheliaEvents::IMAGE_TOGGLE_VISIBILITY => array("toggleVisibility", 128),
);
}
}

View File

@@ -11,77 +11,69 @@
/*************************************************************************************/
namespace Thelia\Action;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Thelia\Core\Event\Cache\CacheEvent;
use Thelia\Core\Event\TheliaEvents;
use Thelia\Core\Event\UpdatePositionEvent;
use Thelia\Handler\ImportHandler;
use Thelia\Model\ImportCategoryQuery;
use Thelia\Model\ImportQuery;
/**
* Class Import
* @package Thelia\Action
* @author Benjamin Perche <bperche@openstudio.fr>
* @author Jérôme Billiras <jbilliras@openstudio.fr>
*/
class Import extends BaseAction implements EventSubscriberInterface
{
protected $environment;
/**
* @var \Thelia\Handler\ImportHandler The import handler
*/
protected $handler;
public function __construct($environment)
/**
* @param \Thelia\Handler\ImportHandler $importHandler The import handler
*/
public function __construct(ImportHandler $importHandler)
{
$this->environment = $environment;
$this->handler = $importHandler;
}
public function changeCategoryPosition(UpdatePositionEvent $event)
public static function getSubscribedEvents()
{
$this->genericUpdatePosition(new ImportCategoryQuery(), $event);
$this->cacheClear($event->getDispatcher());
}
public function changeImportPosition(UpdatePositionEvent $event)
{
$this->genericUpdatePosition(new ImportQuery(), $event);
$this->cacheClear($event->getDispatcher());
}
protected function cacheClear(EventDispatcherInterface $dispatcher)
{
$cacheEvent = new CacheEvent(
$this->environment
);
$dispatcher->dispatch(TheliaEvents::CACHE_CLEAR, $cacheEvent);
return [
TheliaEvents::IMPORT_CHANGE_POSITION => [
['importChangePosition', 128]
],
TheliaEvents::IMPORT_CATEGORY_CHANGE_POSITION => [
['importCategoryChangePosition', 128]
]
];
}
/**
* Returns an array of event names this subscriber wants to listen to.
* Handle import change position event
*
* The array keys are event names and the value can be:
*
* * The method name to call (priority defaults to 0)
* * An array composed of the method name to call and the priority
* * An array of arrays composed of the method names to call and respective
* priorities, or 0 if unset
*
* For instance:
*
* * array('eventName' => 'methodName')
* * array('eventName' => array('methodName', $priority))
* * array('eventName' => array(array('methodName1', $priority), array('methodName2'))
*
* @return array The event names to listen to
*
* @api
* @param UpdatePositionEvent $updatePositionEvent
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public static function getSubscribedEvents()
public function importChangePosition(UpdatePositionEvent $updatePositionEvent, $eventName, EventDispatcherInterface $dispatcher)
{
return array(
TheliaEvents::IMPORT_CATEGORY_CHANGE_POSITION => array("changeCategoryPosition", 128),
TheliaEvents::IMPORT_CHANGE_POSITION => array("changeImportPosition", 128),
);
$this->handler->getImport($updatePositionEvent->getObjectId(), true);
$this->genericUpdatePosition(new ImportQuery, $updatePositionEvent, $dispatcher);
}
/**
* Handle import category change position event
*
* @param UpdatePositionEvent $updatePositionEvent
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function importCategoryChangePosition(UpdatePositionEvent $updatePositionEvent, $eventName, EventDispatcherInterface $dispatcher)
{
$this->handler->getCategory($updatePositionEvent->getObjectId(), true);
$this->genericUpdatePosition(new ImportCategoryQuery, $updatePositionEvent, $dispatcher);
}
}

View File

@@ -11,13 +11,23 @@
/*************************************************************************************/
namespace Thelia\Action;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\HttpFoundation\RequestStack;
use Thelia\Core\Event\Lang\LangCreateEvent;
use Thelia\Core\Event\Lang\LangDefaultBehaviorEvent;
use Thelia\Core\Event\Lang\LangDeleteEvent;
use Thelia\Core\Event\Lang\LangEvent;
use Thelia\Core\Event\Lang\LangToggleActiveEvent;
use Thelia\Core\Event\Lang\LangToggleDefaultEvent;
use Thelia\Core\Event\Lang\LangToggleVisibleEvent;
use Thelia\Core\Event\Lang\LangUpdateEvent;
use Thelia\Core\Event\TheliaEvents;
use Thelia\Core\HttpFoundation\Session\Session;
use Thelia\Core\Template\TemplateHelperInterface;
use Thelia\Core\Translation\Translator;
use Thelia\Form\Lang\LangUrlEvent;
use Thelia\Model\ConfigQuery;
use Thelia\Model\LangQuery;
@@ -26,19 +36,31 @@ use Thelia\Model\Lang as LangModel;
/**
* Class Lang
* @package Thelia\Action
* @author Manuel Raynaud <mraynaud@openstudio.fr>
* @author Manuel Raynaud <manu@raynaud.io>
*/
class Lang extends BaseAction implements EventSubscriberInterface
{
/** @var TemplateHelperInterface */
protected $templateHelper;
public function update(LangUpdateEvent $event)
/** @var RequestStack */
protected $requestStack;
public function __construct(TemplateHelperInterface $templateHelper, RequestStack $requestStack)
{
$this->templateHelper = $templateHelper;
$this->requestStack = $requestStack;
}
public function update(LangUpdateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if (null !== $lang = LangQuery::create()->findPk($event->getId())) {
$lang->setDispatcher($event->getDispatcher());
$lang->setDispatcher($dispatcher);
$lang->setTitle($event->getTitle())
->setLocale($event->getLocale())
->setCode($event->getCode())
->setDateTimeFormat($event->getDateTimeFormat())
->setDateFormat($event->getDateFormat())
->setTimeFormat($event->getTimeFormat())
->setDecimalSeparator($event->getDecimalSeparator())
@@ -50,10 +72,10 @@ class Lang extends BaseAction implements EventSubscriberInterface
}
}
public function toggleDefault(LangToggleDefaultEvent $event)
public function toggleDefault(LangToggleDefaultEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if (null !== $lang = LangQuery::create()->findPk($event->getLangId())) {
$lang->setDispatcher($event->getDispatcher());
$lang->setDispatcher($dispatcher);
$lang->toggleDefault();
@@ -61,15 +83,58 @@ class Lang extends BaseAction implements EventSubscriberInterface
}
}
public function create(LangCreateEvent $event)
public function toggleActive(LangToggleActiveEvent $event)
{
if (null !== $lang = LangQuery::create()->findPk($event->getLangId())) {
if ($lang->getByDefault()) {
throw new \RuntimeException(
Translator::getInstance()->trans('Cannot disable the default language')
);
}
$lang->setActive($lang->getActive() ? 0 : 1);
if (!$lang->getActive()) {
$lang->setVisible(0);
}
$lang->save();
$event->setLang($lang);
}
}
public function toggleVisible(LangToggleVisibleEvent $event)
{
if (null !== $lang = LangQuery::create()->findPk($event->getLangId())) {
if ($lang->getByDefault()) {
throw new \RuntimeException(
Translator::getInstance()->trans('Cannot hide the default language')
);
}
$lang->setVisible($lang->getVisible() ? 0 : 1);
if (!$lang->getActive() && $lang->getVisible()) {
$lang->setActive(1);
}
$lang->save();
$event->setLang($lang);
}
}
public function create(LangCreateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$lang = new LangModel();
$lang
->setDispatcher($event->getDispatcher())
->setDispatcher($dispatcher)
->setTitle($event->getTitle())
->setCode($event->getCode())
->setLocale($event->getLocale())
->setDateTimeFormat($event->getDateTimeFormat())
->setDateFormat($event->getDateFormat())
->setTimeFormat($event->getTimeFormat())
->setDecimalSeparator($event->getDecimalSeparator())
@@ -80,12 +145,31 @@ class Lang extends BaseAction implements EventSubscriberInterface
$event->setLang($lang);
}
public function delete(LangDeleteEvent $event)
public function delete(LangDeleteEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if (null !== $lang = LangQuery::create()->findPk($event->getLangId())) {
$lang->setDispatcher($event->getDispatcher())
if ($lang->getByDefault()) {
throw new \RuntimeException(
Translator::getInstance()->trans('It is not allowed to delete the default language')
);
}
$lang->setDispatcher($dispatcher)
->delete();
/** @var Session $session */
$session = $this->requestStack->getCurrentRequest()->getSession();
// If we've just deleted the current admin edition language, set it to the default one.
if ($lang->getId() == $session->getAdminEditionLang()->getId()) {
$session->setAdminEditionLang(LangModel::getDefaultLanguage());
}
// If we've just deleted the current admin language, set it to the default one.
if ($lang->getId() == $session->getLang()->getId()) {
$session->setLang(LangModel::getDefaultLanguage());
}
$event->setLang($lang);
}
}
@@ -106,35 +190,49 @@ class Lang extends BaseAction implements EventSubscriberInterface
}
}
public function fixMissingFlag(LangEvent $event)
{
// Be sure that a lang have a flag, otherwise copy the
// "unknown" flag
$adminTemplate = $this->templateHelper->getActiveAdminTemplate();
$unknownFlag = ConfigQuery::getUnknownFlagPath();
$unknownFlagPath = $adminTemplate->getAbsolutePath().DS.$unknownFlag;
if (! file_exists($unknownFlagPath)) {
throw new \RuntimeException(
Translator::getInstance()->trans(
"The image which replaces an undefined country flag (%file) was not found. Please check unknown-flag-path configuration variable, and check that the image exists.",
array("%file" => $unknownFlag)
)
);
}
// Check if the country flag exists
$countryFlag = rtrim(dirname($unknownFlagPath), DS).DS.$event->getLang()->getCode().'.png';
if (! file_exists($countryFlag)) {
$fs = new Filesystem();
$fs->copy($unknownFlagPath, $countryFlag);
}
}
/**
* Returns an array of event names this subscriber wants to listen to.
*
* The array keys are event names and the value can be:
*
* * The method name to call (priority defaults to 0)
* * An array composed of the method name to call and the priority
* * An array of arrays composed of the method names to call and respective
* priorities, or 0 if unset
*
* For instance:
*
* * array('eventName' => 'methodName')
* * array('eventName' => array('methodName', $priority))
* * array('eventName' => array(array('methodName1', $priority), array('methodName2'))
*
* @return array The event names to listen to
*
* @api
* {@inheritdoc}
*/
public static function getSubscribedEvents()
{
return array(
TheliaEvents::LANG_UPDATE => array('update', 128),
TheliaEvents::LANG_TOGGLEDEFAULT => array('toggleDefault', 128),
TheliaEvents::LANG_TOGGLEACTIVE => array('toggleActive', 128),
TheliaEvents::LANG_TOGGLEVISIBLE => array('toggleVisible', 128),
TheliaEvents::LANG_CREATE => array('create', 128),
TheliaEvents::LANG_DELETE => array('delete', 128),
TheliaEvents::LANG_DEFAULTBEHAVIOR => array('defaultBehavior', 128),
TheliaEvents::LANG_URL => array('langUrl', 128)
TheliaEvents::LANG_URL => array('langUrl', 128),
TheliaEvents::LANG_FIX_MISSING_FLAG => array('fixMissingFlag', 128)
);
}
}

View File

@@ -45,7 +45,7 @@ class MailingSystem extends BaseAction implements EventSubscriberInterface
public static function getSubscribedEvents()
{
return array(
TheliaEvents::MAILING_SYSTEM_UPDATE => array("update", 128),
TheliaEvents::MAILING_SYSTEM_UPDATE => array("update", 128),
);
}
}

View File

@@ -12,13 +12,11 @@
namespace Thelia\Action;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Thelia\Model\MessageQuery;
use Thelia\Model\Message as MessageModel;
use Thelia\Core\Event\TheliaEvents;
use Thelia\Core\Event\Message\MessageUpdateEvent;
use Thelia\Core\Event\Message\MessageCreateEvent;
use Thelia\Core\Event\Message\MessageDeleteEvent;
@@ -29,19 +27,19 @@ class Message extends BaseAction implements EventSubscriberInterface
* Create a new messageuration entry
*
* @param \Thelia\Core\Event\Message\MessageCreateEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function create(MessageCreateEvent $event)
public function create(MessageCreateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$message = new MessageModel();
$message
->setDispatcher($event->getDispatcher())
->setDispatcher($dispatcher)
->setName($event->getMessageName())
->setLocale($event->getLocale())
->setTitle($event->getTitle())
->setSecured($event->getSecured())
->save()
@@ -54,14 +52,14 @@ class Message extends BaseAction implements EventSubscriberInterface
* Change a message
*
* @param \Thelia\Core\Event\Message\MessageUpdateEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function modify(MessageUpdateEvent $event)
public function modify(MessageUpdateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if (null !== $message = MessageQuery::create()->findPk($event->getMessageId())) {
$message
->setDispatcher($event->getDispatcher())
->setDispatcher($dispatcher)
->setName($event->getMessageName())
->setSecured($event->getSecured())
@@ -89,14 +87,14 @@ class Message extends BaseAction implements EventSubscriberInterface
* Delete a messageuration entry
*
* @param \Thelia\Core\Event\Message\MessageDeleteEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function delete(MessageDeleteEvent $event)
public function delete(MessageDeleteEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if (null !== ($message = MessageQuery::create()->findPk($event->getMessageId()))) {
$message
->setDispatcher($event->getDispatcher())
->setDispatcher($dispatcher)
->delete()
;

View File

@@ -0,0 +1,75 @@
<?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\Action;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Thelia\Core\Event\MetaData\MetaDataCreateOrUpdateEvent;
use Thelia\Core\Event\MetaData\MetaDataDeleteEvent;
use Thelia\Core\Event\TheliaEvents;
use Thelia\Model\MetaData as MetaDataModel;
use Thelia\Model\MetaDataQuery;
/**
* Class MetaData
* @package Thelia\Action
* @author Julien Chanséaume <jchanseaume@openstudio.fr>
*/
class MetaData extends BaseAction implements EventSubscriberInterface
{
public function createOrUpdate(MetaDataCreateOrUpdateEvent $event)
{
$metaData = MetaDataQuery::create()
->filterByMetaKey($event->getMetaKey())
->filterByElementKey($event->getElementKey())
->filterByElementId($event->getElementId())
->findOne();
if (null === $metaData) {
$metaData = new MetaDataModel();
$metaData
->setMetaKey($event->getMetaKey())
->setElementKey($event->getElementkey())
->setElementId($event->getElementId());
}
$metaData->
setValue($event->getValue());
$metaData->save();
$event->setMetaData($metaData);
}
public function delete(MetaDataDeleteEvent $event)
{
$metaData = MetaDataQuery::create()
->filterByMetaKey($event->getMetaKey())
->filterByElementKey($event->getElementKey())
->filterByElementId($event->getElementId())
->findOne();
$event->setMetaData($metaData);
if (null !== $metaData) {
$metaData->delete();
}
}
/**
* @inheritdoc
*/
public static function getSubscribedEvents()
{
return array(
TheliaEvents::META_DATA_CREATE => array('createOrUpdate', 128),
TheliaEvents::META_DATA_UPDATE => array('createOrUpdate', 128),
TheliaEvents::META_DATA_DELETE => array('delete', 128),
);
}
}

View File

@@ -11,35 +11,43 @@
/*************************************************************************************/
namespace Thelia\Action;
use Exception;
use Propel\Runtime\Propel;
use SplFileInfo;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Filesystem\Exception\IOException;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\HttpFoundation\Response;
use Thelia\Core\Event\Cache\CacheEvent;
use Thelia\Core\Event\Module\ModuleDeleteEvent;
use Thelia\Core\Event\Module\ModuleEvent;
use Thelia\Core\Event\Module\ModuleInstallEvent;
use Thelia\Core\Event\Module\ModuleToggleActivationEvent;
use Thelia\Core\Event\Order\OrderPaymentEvent;
use Thelia\Core\Event\TheliaEvents;
use Thelia\Core\Event\UpdatePositionEvent;
use Thelia\Core\Translation\Translator;
use Thelia\Exception\FileNotFoundException;
use Thelia\Exception\ModuleException;
use Thelia\Log\Tlog;
use Thelia\Model\Base\OrderQuery;
use Thelia\Model\Map\ModuleTableMap;
use Thelia\Model\ModuleQuery;
use Thelia\Module\BaseModule;
use Thelia\Module\ModuleManagement;
use Thelia\Module\Validator\ModuleValidator;
/**
* Class Module
* @package Thelia\Action
* @author Manuel Raynaud <mraynaud@openstudio.fr>
* @author Manuel Raynaud <manu@raynaud.io>
*/
class Module extends BaseAction implements EventSubscriberInterface
{
/**
* @var ContainerInterface
*/
/** @var ContainerInterface */
protected $container;
public function __construct(ContainerInterface $container)
@@ -47,13 +55,12 @@ class Module extends BaseAction implements EventSubscriberInterface
$this->container = $container;
}
public function toggleActivation(ModuleToggleActivationEvent $event)
public function toggleActivation(ModuleToggleActivationEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if (null !== $module = ModuleQuery::create()->findPk($event->getModuleId())) {
$moduleInstance = $module->createInstance();
if ( method_exists($moduleInstance, 'setContainer')) {
if (method_exists($moduleInstance, 'setContainer')) {
$moduleInstance->setContainer($this->container);
if ($module->getActivate() == BaseModule::IS_ACTIVATED) {
$moduleInstance->deActivate($module);
@@ -64,28 +71,188 @@ class Module extends BaseAction implements EventSubscriberInterface
$event->setModule($module);
$this->cacheClear($event->getDispatcher());
$this->cacheClear($dispatcher);
}
}
public function delete(ModuleDeleteEvent $event)
public function checkToggleActivation(ModuleToggleActivationEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if (true === $event->isNoCheck()) {
return;
}
if (null !== $module = ModuleQuery::create()->findPk($event->getModuleId())) {
try {
if ($module->getActivate() == BaseModule::IS_ACTIVATED) {
if ($module->getMandatory() == BaseModule::IS_MANDATORY && $event->getAssumeDeactivate() === false) {
throw new \Exception(
Translator::getInstance()->trans('Can\'t deactivate a secure module')
);
}
if ($event->isRecursive()) {
$this->recursiveDeactivation($event, $eventName, $dispatcher);
}
$this->checkDeactivation($module);
} else {
if ($event->isRecursive()) {
$this->recursiveActivation($event, $eventName, $dispatcher);
}
$this->checkActivation($module);
}
} catch (\Exception $ex) {
$event->stopPropagation();
throw $ex;
}
}
}
/**
* Check if module can be activated : supported version of Thelia, module dependencies.
*
* @param \Thelia\Model\Module $module
* @throws Exception if activation fails.
* @return bool true if the module can be activated, otherwise false
*/
private function checkActivation($module)
{
try {
$moduleValidator = new ModuleValidator($module->getAbsoluteBaseDir());
$moduleValidator->validate(false);
} catch (\Exception $ex) {
throw $ex;
}
return true;
}
/**
* Check if module can be deactivated safely because other modules
* could have dependencies to this module
*
* @param \Thelia\Model\Module $module
* @return bool true if the module can be deactivated, otherwise false
*/
private function checkDeactivation($module)
{
$moduleValidator = new ModuleValidator($module->getAbsoluteBaseDir());
$modules = $moduleValidator->getModulesDependOf();
if (count($modules) > 0) {
$moduleList = implode(', ', array_column($modules, 'code'));
$message = (count($modules) == 1)
? Translator::getInstance()->trans(
'%s has dependency to module %s. You have to deactivate this module before.'
)
: Translator::getInstance()->trans(
'%s have dependencies to module %s. You have to deactivate these modules before.'
);
throw new ModuleException(
sprintf($message, $moduleList, $moduleValidator->getModuleDefinition()->getCode())
);
}
return true;
}
/**
* Get dependencies of the current module and activate it if needed
*
* @param ModuleToggleActivationEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function recursiveActivation(ModuleToggleActivationEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if (null !== $module = ModuleQuery::create()->findPk($event->getModuleId())) {
$con = Propel::getWriteConnection(ModuleTableMap::DATABASE_NAME);
$con->beginTransaction();
$moduleValidator = new ModuleValidator($module->getAbsoluteBaseDir());
$dependencies = $moduleValidator->getCurrentModuleDependencies();
foreach ($dependencies as $defMod) {
$submodule = ModuleQuery::create()
->findOneByCode($defMod["code"]);
if ($submodule && $submodule->getActivate() != BaseModule::IS_ACTIVATED) {
$subevent = new ModuleToggleActivationEvent($submodule->getId());
$subevent->setRecursive(true);
$dispatcher->dispatch(TheliaEvents::MODULE_TOGGLE_ACTIVATION, $subevent);
}
}
}
}
/**
* Get modules having current module in dependence and deactivate it if needed
*
* @param ModuleToggleActivationEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function recursiveDeactivation(ModuleToggleActivationEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if (null !== $module = ModuleQuery::create()->findPk($event->getModuleId())) {
$moduleValidator = new ModuleValidator($module->getAbsoluteBaseDir());
$dependencies = $moduleValidator->getModulesDependOf(true);
foreach ($dependencies as $defMod) {
$submodule = ModuleQuery::create()
->findOneByCode($defMod["code"]);
if ($submodule && $submodule->getActivate() == BaseModule::IS_ACTIVATED) {
$subevent = new ModuleToggleActivationEvent($submodule->getId());
$subevent->setRecursive(true);
$dispatcher->dispatch(TheliaEvents::MODULE_TOGGLE_ACTIVATION, $subevent);
}
}
}
}
public function delete(ModuleDeleteEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$con = Propel::getWriteConnection(ModuleTableMap::DATABASE_NAME);
$con->beginTransaction();
if (null !== $module = ModuleQuery::create()->findPk($event->getModuleId(), $con)) {
try {
if (null === $module->getFullNamespace()) {
throw new \LogicException(
Translator::getInstance()->trans(
'Cannot instanciante module "%name%": the namespace is null. Maybe the model is not loaded ?',
array('%name%' => $module->getCode())
));
'Cannot instantiate module "%name%": the namespace is null. Maybe the model is not loaded ?',
['%name%' => $module->getCode()]
)
);
}
// If the module is referenced by an order, display a meaningful error
// instead of 'delete cannot delete' caused by a constraint violation.
// FIXME: we hav to find a way to delete modules used by order.
if (OrderQuery::create()->filterByDeliveryModuleId($module->getId())->count() > 0
||
OrderQuery::create()->filterByPaymentModuleId($module->getId())->count() > 0
) {
throw new \LogicException(
Translator::getInstance()->trans(
'The module "%name%" is currently in use by at least one order, and can\'t be deleted.',
['%name%' => $module->getCode()]
)
);
}
try {
if ($module->getMandatory() == BaseModule::IS_MANDATORY && $event->getAssumeDelete() === false) {
throw new \Exception(
Translator::getInstance()->trans('Can\'t remove a core module')
);
}
// First, try to create an instance
$instance = $module->createInstance();
// Then, if module is activated, check if we can deactivate it
if ($module->getActivate()) {
// check for modules that depend of this one
$this->checkDeactivation($module);
}
$instance->setContainer($this->container);
$path = $module->getAbsoluteBaseDir();
@@ -99,8 +266,19 @@ class Module extends BaseAction implements EventSubscriberInterface
// Log a warning, and delete the database entry.
Tlog::getInstance()->addWarning(
Translator::getInstance()->trans(
'Failed to create instance of module "%name%" when trying to delete module. Module directory has probably been deleted', array('%name%' => $module->getCode())
));
'Failed to create instance of module "%name%" when trying to delete module. Module directory has probably been deleted',
['%name%' => $module->getCode()]
)
);
} catch (FileNotFoundException $fnfe) {
// The module directory has been deleted.
// Log a warning, and delete the database entry.
Tlog::getInstance()->addWarning(
Translator::getInstance()->trans(
'Module "%name%" directory was not found',
['%name%' => $module->getCode()]
)
);
}
$module->delete($con);
@@ -108,8 +286,7 @@ class Module extends BaseAction implements EventSubscriberInterface
$con->commit();
$event->setModule($module);
$this->cacheClear($event->getDispatcher());
$this->cacheClear($dispatcher);
} catch (\Exception $e) {
$con->rollBack();
throw $e;
@@ -119,19 +296,19 @@ class Module extends BaseAction implements EventSubscriberInterface
/**
* @param ModuleEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function update(ModuleEvent $event)
public function update(ModuleEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if (null !== $module = ModuleQuery::create()->findPk($event->getId())) {
$module
->setDispatcher($event->getDispatcher())
->setDispatcher($dispatcher)
->setLocale($event->getLocale())
->setTitle($event->getTitle())
->setChapo($event->getChapo())
->setDescription($event->getDescription())
->setPostscriptum($event->getPostscriptum())
;
->setPostscriptum($event->getPostscriptum());
$module->save();
@@ -139,10 +316,86 @@ class Module extends BaseAction implements EventSubscriberInterface
}
}
/**
* @param \Thelia\Core\Event\Module\ModuleInstallEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*
* @throws \Exception
* @throws \Symfony\Component\Filesystem\Exception\IOException
* @throws \Exception
*/
public function install(ModuleInstallEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$moduleDefinition = $event->getModuleDefinition();
$oldModule = ModuleQuery::create()->findOneByFullNamespace($moduleDefinition->getNamespace());
$fs = new Filesystem();
$activated = false;
// check existing module
if (null !== $oldModule) {
$activated = $oldModule->getActivate();
if ($activated) {
// deactivate
$toggleEvent = new ModuleToggleActivationEvent($oldModule->getId());
// disable the check of the module because it's already done
$toggleEvent->setNoCheck(true);
$dispatcher->dispatch(TheliaEvents::MODULE_TOGGLE_ACTIVATION, $toggleEvent);
}
// delete
$modulePath = $oldModule->getAbsoluteBaseDir();
$deleteEvent = new ModuleDeleteEvent($oldModule);
try {
$dispatcher->dispatch(TheliaEvents::MODULE_DELETE, $deleteEvent);
} catch (Exception $ex) {
// if module has not been deleted
if ($fs->exists($modulePath)) {
throw $ex;
}
}
}
// move new module
$modulePath = sprintf('%s%s', THELIA_MODULE_DIR, $event->getModuleDefinition()->getCode());
try {
$fs->mirror($event->getModulePath(), $modulePath);
} catch (IOException $ex) {
if (!$fs->exists($modulePath)) {
throw $ex;
}
}
// Update the module
$moduleDescriptorFile = sprintf('%s%s%s%s%s', $modulePath, DS, 'Config', DS, 'module.xml');
$moduleManagement = new ModuleManagement();
$file = new SplFileInfo($moduleDescriptorFile);
$module = $moduleManagement->updateModule($file, $this->container);
// activate if old was activated
if ($activated) {
$toggleEvent = new ModuleToggleActivationEvent($module->getId());
$toggleEvent->setNoCheck(true);
$dispatcher->dispatch(TheliaEvents::MODULE_TOGGLE_ACTIVATION, $toggleEvent);
}
$event->setModule($module);
}
/**
* Call the payment method of the payment module of the given order
*
* @param OrderPaymentEvent $event
* @param OrderPaymentEvent $event
*
* @throws \RuntimeException if no payment module can be found.
*/
public function pay(OrderPaymentEvent $event)
@@ -154,18 +407,19 @@ class Module extends BaseAction implements EventSubscriberInterface
throw new \RuntimeException(
Translator::getInstance()->trans(
"Failed to find a payment Module with ID=%mid for order ID=%oid",
array(
[
"%mid" => $order->getPaymentModuleId(),
"%oid" => $order->getId()
))
]
)
);
}
$paymentModuleInstance = $paymentModule->getModuleInstance($this->container);
$paymentModuleInstance = $paymentModule->getPaymentModuleInstance($this->container);
$response = $paymentModuleInstance->pay($order);
if (null !== $response && $response instanceof \Thelia\Core\HttpFoundation\Response) {
if (null !== $response && $response instanceof Response) {
$event->setResponse($response);
}
}
@@ -174,12 +428,14 @@ class Module extends BaseAction implements EventSubscriberInterface
* Changes position, selecting absolute ou relative change.
*
* @param UpdatePositionEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function updatePosition(UpdatePositionEvent $event)
public function updatePosition(UpdatePositionEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$this->genericUpdatePosition(ModuleQuery::create(), $event);
$this->genericUpdatePosition(ModuleQuery::create(), $event, $dispatcher);
$this->cacheClear($event->getDispatcher());
$this->cacheClear($dispatcher);
}
protected function cacheClear(EventDispatcherInterface $dispatcher)
@@ -192,33 +448,20 @@ class Module extends BaseAction implements EventSubscriberInterface
}
/**
* Returns an array of event names this subscriber wants to listen to.
*
* The array keys are event names and the value can be:
*
* * The method name to call (priority defaults to 0)
* * An array composed of the method name to call and the priority
* * An array of arrays composed of the method names to call and respective
* priorities, or 0 if unset
*
* For instance:
*
* * array('eventName' => 'methodName')
* * array('eventName' => array('methodName', $priority))
* * array('eventName' => array(array('methodName1', $priority), array('methodName2'))
*
* @return array The event names to listen to
*
* @api
* @inheritdoc
*/
public static function getSubscribedEvents()
{
return array(
TheliaEvents::MODULE_TOGGLE_ACTIVATION => array('toggleActivation', 128),
TheliaEvents::MODULE_UPDATE_POSITION => array('updatePosition', 128),
TheliaEvents::MODULE_DELETE => array('delete', 128),
TheliaEvents::MODULE_UPDATE => array('update', 128),
TheliaEvents::MODULE_PAY => array('pay', 128),
);
return [
TheliaEvents::MODULE_TOGGLE_ACTIVATION => [
['checkToggleActivation', 255],
['toggleActivation', 128],
],
TheliaEvents::MODULE_UPDATE_POSITION => ['updatePosition', 128],
TheliaEvents::MODULE_DELETE => ['delete', 128],
TheliaEvents::MODULE_UPDATE => ['update', 128],
TheliaEvents::MODULE_INSTALL => ['install', 128],
TheliaEvents::MODULE_PAY => ['pay', 128],
];
}
}

View File

@@ -0,0 +1,252 @@
<?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\Action;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Thelia\Core\Event\Cache\CacheEvent;
use Thelia\Core\Event\Hook\HookToggleActivationEvent;
use Thelia\Core\Event\Hook\HookUpdateEvent;
use Thelia\Core\Event\Hook\ModuleHookCreateEvent;
use Thelia\Core\Event\Hook\ModuleHookDeleteEvent;
use Thelia\Core\Event\Hook\ModuleHookToggleActivationEvent;
use Thelia\Core\Event\Hook\ModuleHookUpdateEvent;
use Thelia\Core\Event\Module\ModuleDeleteEvent;
use Thelia\Core\Event\Module\ModuleToggleActivationEvent;
use Thelia\Core\Event\TheliaEvents;
use Thelia\Core\Event\UpdatePositionEvent;
use Thelia\Core\Translation\Translator;
use Thelia\Model\Base\IgnoredModuleHookQuery;
use Thelia\Model\HookQuery;
use Thelia\Model\IgnoredModuleHook;
use Thelia\Model\ModuleHook as ModuleHookModel;
use Thelia\Model\ModuleHookQuery;
use Thelia\Model\ModuleQuery;
use Thelia\Module\BaseModule;
/**
* Class ModuleHook
* @package Thelia\Action
* @author Julien Chanséaume <jchanseaume@openstudio.fr>
*/
class ModuleHook extends BaseAction implements EventSubscriberInterface
{
/** @var string */
protected $cacheDir;
public function __construct($cacheDir)
{
$this->cacheDir = $cacheDir;
}
public function toggleModuleActivation(ModuleToggleActivationEvent $event)
{
if (null !== $module = ModuleQuery::create()->findPk($event->getModuleId())) {
ModuleHookQuery::create()
->filterByModuleId($module->getId())
->update(array('ModuleActive' => ($module->getActivate() == BaseModule::IS_ACTIVATED)));
}
return $event;
}
public function deleteModule(ModuleDeleteEvent $event)
{
if ($event->getModuleId()) {
ModuleHookQuery::create()
->filterByModuleId($event->getModuleId())
->delete();
}
return $event;
}
protected function isModuleActive($module_id)
{
if (null !== $module = ModuleQuery::create()->findPk($module_id)) {
return $module->getActivate();
}
return false;
}
protected function isHookActive($hook_id)
{
if (null !== $hook = HookQuery::create()->findPk($hook_id)) {
return $hook->getActivate();
}
return false;
}
protected function getLastPositionInHook($hook_id)
{
$result = ModuleHookQuery::create()
->filterByHookId($hook_id)
->withColumn('MAX(ModuleHook.position)', 'maxPos')
->groupBy('ModuleHook.hook_id')
->select(array('maxPos'))
->findOne();
return intval($result) + 1;
}
public function createModuleHook(ModuleHookCreateEvent $event)
{
$moduleHook = new ModuleHookModel();
// todo: test if classname and method exists
$moduleHook
->setModuleId($event->getModuleId())
->setHookId($event->getHookId())
->setActive(false)
->setClassname($event->getClassname())
->setMethod($event->getMethod())
->setModuleActive($this->isModuleActive($event->getModuleId()))
->setHookActive($this->isHookActive($event->getHookId()))
->setPosition($this->getLastPositionInHook($event->getHookId()))
->setTemplates($event->getTemplates())
->save();
// Be sure to delete this module hook from the ignored module hook table
IgnoredModuleHookQuery::create()
->filterByHookId($event->getHookId())
->filterByModuleId($event->getModuleId())
->delete();
$event->setModuleHook($moduleHook);
}
public function updateModuleHook(ModuleHookUpdateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if (null !== $moduleHook = ModuleHookQuery::create()->findPk($event->getModuleHookId())) {
// todo: test if classname and method exists
$moduleHook
->setHookId($event->getHookId())
->setModuleId($event->getModuleId())
->setClassname($event->getClassname())
->setMethod($event->getMethod())
->setActive($event->getActive())
->setHookActive($this->isHookActive($event->getHookId()))
->setTemplates($event->getTemplates())
->save();
$event->setModuleHook($moduleHook);
$this->cacheClear($dispatcher);
}
}
public function deleteModuleHook(ModuleHookDeleteEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if (null !== $moduleHook = ModuleHookQuery::create()->findPk($event->getModuleHookId())) {
$moduleHook->delete();
$event->setModuleHook($moduleHook);
// Prevent hook recreation by RegisterListenersPass::registerHook()
// We store the method here to be able to retreive it when
// we need to get all hook declared by a module
$imh = new IgnoredModuleHook();
$imh
->setModuleId($moduleHook->getModuleId())
->setHookId($moduleHook->getHookId())
->setMethod($moduleHook->getMethod())
->setClassname($moduleHook->getClassname())
->save();
$this->cacheClear($dispatcher);
}
}
public function toggleModuleHookActivation(ModuleHookToggleActivationEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if (null !== $moduleHook = $event->getModuleHook()) {
if ($moduleHook->getModuleActive()) {
$moduleHook->setActive(!$moduleHook->getActive());
$moduleHook->save();
} else {
throw new \LogicException(Translator::getInstance()->trans("The module has to be activated."));
}
}
$this->cacheClear($dispatcher);
return $event;
}
/**
* Changes position, selecting absolute ou relative change.
*
* @param UpdatePositionEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*
* @return UpdatePositionEvent $event
*/
public function updateModuleHookPosition(UpdatePositionEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$this->genericUpdatePosition(ModuleHookQuery::create(), $event, $dispatcher);
$this->cacheClear($dispatcher);
return $event;
}
public function updateHook(HookUpdateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if ($event->hasHook()) {
$hook = $event->getHook();
ModuleHookQuery::create()
->filterByHookId($hook->getId())
->update(array('HookActive' => $hook->getActivate()));
$this->cacheClear($dispatcher);
}
}
public function toggleHookActivation(HookToggleActivationEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if ($event->hasHook()) {
$hook = $event->getHook();
ModuleHookQuery::create()
->filterByHookId($hook->getId())
->update(array('HookActive' => $hook->getActivate()));
$this->cacheClear($dispatcher);
}
}
protected function cacheClear(EventDispatcherInterface $dispatcher)
{
$cacheEvent = new CacheEvent($this->cacheDir);
$dispatcher->dispatch(TheliaEvents::CACHE_CLEAR, $cacheEvent);
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents()
{
return array(
TheliaEvents::MODULE_HOOK_CREATE => array('createModuleHook', 128),
TheliaEvents::MODULE_HOOK_UPDATE => array('updateModuleHook', 128),
TheliaEvents::MODULE_HOOK_DELETE => array('deleteModuleHook', 128),
TheliaEvents::MODULE_HOOK_UPDATE_POSITION => array('updateModuleHookPosition', 128),
TheliaEvents::MODULE_HOOK_TOGGLE_ACTIVATION => array('toggleModuleHookActivation', 128),
TheliaEvents::MODULE_TOGGLE_ACTIVATION => array('toggleModuleActivation', 64),
TheliaEvents::MODULE_DELETE => array('deleteModule', 64),
TheliaEvents::HOOK_TOGGLE_ACTIVATION => array('toggleHookActivation', 64),
TheliaEvents::HOOK_UPDATE => array('updateHook', 64),
);
}
}

View File

@@ -11,39 +11,63 @@
/*************************************************************************************/
namespace Thelia\Action;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Thelia\Core\Event\Newsletter\NewsletterEvent;
use Thelia\Core\Event\TheliaEvents;
use Thelia\Mailer\MailerFactory;
use Thelia\Model\ConfigQuery;
use Thelia\Model\NewsletterQuery;
use Thelia\Model\Newsletter as NewsletterModel;
/**
* Class Newsletter
* @package Thelia\Action
* @author Manuel Raynaud <mraynaud@openstudio.fr>
* @author Manuel Raynaud <manu@raynaud.io>
*/
class Newsletter extends BaseAction implements EventSubscriberInterface
{
/** @var MailerFactory */
protected $mailer;
/** @var EventDispatcherInterface */
protected $dispatcher;
public function __construct(MailerFactory $mailer, EventDispatcherInterface $dispatcher)
{
$this->mailer = $mailer;
$this->dispatcher = $dispatcher;
}
public function subscribe(NewsletterEvent $event)
{
$newsletter = new NewsletterModel();
// test if the email is already registered and unsubscribed
if (null === $newsletter = NewsletterQuery::create()->findOneByEmail($event->getEmail())) {
$newsletter = new NewsletterModel();
}
$newsletter
->setEmail($event->getEmail())
->setFirstname($event->getFirstname())
->setLastname($event->getLastname())
->setLocale($event->getLocale())
->setUnsubscribed(false)
->save();
$event->setNewsletter($newsletter);
if (ConfigQuery::getNotifyNewsletterSubscription()) {
$this->dispatcher->dispatch(TheliaEvents::NEWSLETTER_CONFIRM_SUBSCRIPTION, $event);
}
}
public function unsubscribe(NewsletterEvent $event)
{
if (null !== $nl = NewsletterQuery::create()->findPk($event->getId())) {
$nl->delete();
$nl
->setUnsubscribed(true)
->save();
$event->setNewsletter($nl);
}
@@ -63,31 +87,33 @@ class Newsletter extends BaseAction implements EventSubscriberInterface
}
/**
* Returns an array of event names this subscriber wants to listen to.
*
* The array keys are event names and the value can be:
*
* * The method name to call (priority defaults to 0)
* * An array composed of the method name to call and the priority
* * An array of arrays composed of the method names to call and respective
* priorities, or 0 if unset
*
* For instance:
*
* * array('eventName' => 'methodName')
* * array('eventName' => array('methodName', $priority))
* * array('eventName' => array(array('methodName1', $priority), array('methodName2'))
*
* @return array The event names to listen to
*
* @api
* @since 2.3.0-alpha2
*/
public function confirmSubscription(NewsletterEvent $event)
{
$this->mailer->sendEmailMessage(
'newsletter_subscription_confirmation',
[ ConfigQuery::getStoreEmail() => ConfigQuery::getStoreName() ],
[ $event->getEmail() => $event->getFirstname()." ".$event->getLastname() ],
[
'email' => $event->getEmail(),
'firstname' => $event->getFirstname(),
'lastname' => $event->getLastname()
],
$event->getLocale()
);
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents()
{
return array(
TheliaEvents::NEWSLETTER_SUBSCRIBE => array('subscribe', 128),
TheliaEvents::NEWSLETTER_UPDATE => array('update', 128),
TheliaEvents::NEWSLETTER_UNSUBSCRIBE => array('unsubscribe', 128)
TheliaEvents::NEWSLETTER_UNSUBSCRIBE => array('unsubscribe', 128),
TheliaEvents::NEWSLETTER_CONFIRM_SUBSCRIPTION => array('confirmSubscription', 128)
);
}
}

View File

@@ -12,32 +12,42 @@
namespace Thelia\Action;
use Propel\Runtime\Propel;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Thelia\Cart\CartTrait;
use Symfony\Component\HttpFoundation\RequestStack;
use Thelia\Core\Event\Order\OrderAddressEvent;
use Thelia\Core\Event\Order\OrderEvent;
use Thelia\Core\Event\Order\OrderManualEvent;
use Thelia\Core\Event\Order\OrderPaymentEvent;
use Thelia\Core\Event\Payment\ManageStockOnCreationEvent;
use Thelia\Core\Event\Product\VirtualProductOrderHandleEvent;
use Thelia\Core\Event\TheliaEvents;
use Thelia\Core\HttpFoundation\Request;
use Thelia\Core\Security\SecurityContext;
use Thelia\Core\Template\ParserInterface;
use Thelia\Core\Security\User\UserInterface;
use Thelia\Exception\TheliaProcessException;
use Thelia\Mailer\MailerFactory;
use Thelia\Model\AddressQuery;
use Thelia\Model\Cart as CartModel;
use Thelia\Model\ConfigQuery;
use Thelia\Model\Currency as CurrencyModel;
use Thelia\Model\Customer as CustomerModel;
use Thelia\Model\Lang as LangModel;
use Thelia\Model\Map\OrderTableMap;
use Thelia\Model\MessageQuery;
use Thelia\Model\ModuleQuery;
use Thelia\Model\Order as ModelOrder;
use Thelia\Model\Order as OrderModel;
use Thelia\Model\OrderAddress;
use Thelia\Model\OrderAddressQuery;
use Thelia\Model\OrderProduct;
use Thelia\Model\OrderProductAttributeCombination;
use Thelia\Model\OrderProductTax;
use Thelia\Model\OrderStatusQuery;
use Thelia\Model\ProductI18n;
use Thelia\Model\ProductSaleElements;
use Thelia\Model\ProductSaleElementsQuery;
use Thelia\Model\TaxRuleI18n;
use Thelia\Module\PaymentModuleInterface;
use Thelia\Tools\I18n;
/**
@@ -48,29 +58,18 @@ use Thelia\Tools\I18n;
*/
class Order extends BaseAction implements EventSubscriberInterface
{
use CartTrait;
/** @var RequestStack */
protected $requestStack;
/**
* @var \Thelia\Core\HttpFoundation\Request
*/
protected $request;
/**
* @var MailerFactory
*/
/** @var MailerFactory */
protected $mailer;
/**
* @var ParserInterface
*/
protected $parser;
/**
* @var SecurityContext
*/
/** @var SecurityContext */
protected $securityContext;
public function __construct(Request $request, ParserInterface $parser, MailerFactory $mailer, SecurityContext $securityContext)
public function __construct(RequestStack $requestStack, MailerFactory $mailer, SecurityContext $securityContext)
{
$this->request = $request;
$this->parser = $parser;
$this->requestStack = $requestStack;
$this->mailer = $mailer;
$this->securityContext = $securityContext;
}
@@ -101,6 +100,8 @@ class Order extends BaseAction implements EventSubscriberInterface
// Reset postage cost if the delivery module had been removed
if ($deliveryModuleId <= 0) {
$order->setPostage(0);
$order->setPostageTax(0);
$order->setPostageTaxRuleTitle(null);
}
$event->setOrder($order);
@@ -114,6 +115,8 @@ class Order extends BaseAction implements EventSubscriberInterface
$order = $event->getOrder();
$order->setPostage($event->getPostage());
$order->setPostageTax($event->getPostageTax());
$order->setPostageTaxRuleTitle($event->getPostageTaxRuleTitle());
$event->setOrder($order);
}
@@ -142,20 +145,48 @@ class Order extends BaseAction implements EventSubscriberInterface
$event->setOrder($order);
}
protected function createOrder(EventDispatcherInterface $dispatcher, ModelOrder $sessionOrder, CurrencyModel $currency, LangModel $lang, CartModel $cart, CustomerModel $customer)
{
$con = \Propel\Runtime\Propel::getConnection(
OrderTableMap::DATABASE_NAME
/**
* @param EventDispatcherInterface $dispatcher
* @param ModelOrder $sessionOrder
* @param CurrencyModel $currency
* @param LangModel $lang
* @param CartModel $cart
* @param UserInterface $customer
* @param bool $manageStock decrement stock when order is created if true
* @param bool $useOrderDefinedAddresses if true, the delivery and invoice OrderAddresses will be used instead of creating new OrderAdresses using Order::getChoosenXXXAddress()
* @return ModelOrder
* @throws \Exception
* @throws \Propel\Runtime\Exception\PropelException
*/
protected function createOrder(
EventDispatcherInterface $dispatcher,
ModelOrder $sessionOrder,
CurrencyModel $currency,
LangModel $lang,
CartModel $cart,
UserInterface $customer,
$manageStock,
$useOrderDefinedAddresses = false
) {
$con = Propel::getConnection(
OrderTableMap::DATABASE_NAME
);
$con->beginTransaction();
$placedOrder = $sessionOrder->copy();
// Be sure to create a brand new order, as copy raises the modified flag for all fields
// and will also copy order reference and id.
$placedOrder->setId(null)->setRef(null)->setNew(true);
// Dates should be marked as not updated so that Propel will update them.
$placedOrder->resetModified(OrderTableMap::CREATED_AT);
$placedOrder->resetModified(OrderTableMap::UPDATED_AT);
$placedOrder->resetModified(OrderTableMap::VERSION_CREATED_AT);
$placedOrder->setDispatcher($dispatcher);
$deliveryAddress = AddressQuery::create()->findPk($sessionOrder->getChoosenDeliveryAddress());
$taxCountry = $deliveryAddress->getCountry();
$invoiceAddress = AddressQuery::create()->findPk($sessionOrder->getChoosenInvoiceAddress());
$cartItems = $cart->getCartItems();
/* fulfill order */
@@ -163,50 +194,67 @@ class Order extends BaseAction implements EventSubscriberInterface
$placedOrder->setCurrencyId($currency->getId());
$placedOrder->setCurrencyRate($currency->getRate());
$placedOrder->setLangId($lang->getId());
if ($useOrderDefinedAddresses) {
$taxCountry =
OrderAddressQuery::create()
->findPk($placedOrder->getDeliveryOrderAddressId())
->getCountry()
;
} else {
$deliveryAddress = AddressQuery::create()->findPk($sessionOrder->getChoosenDeliveryAddress());
$invoiceAddress = AddressQuery::create()->findPk($sessionOrder->getChoosenInvoiceAddress());
/* hard save the delivery and invoice addresses */
$deliveryOrderAddress = new OrderAddress();
$deliveryOrderAddress
->setCustomerTitleId($deliveryAddress->getTitleId())
->setCompany($deliveryAddress->getCompany())
->setFirstname($deliveryAddress->getFirstname())
->setLastname($deliveryAddress->getLastname())
->setAddress1($deliveryAddress->getAddress1())
->setAddress2($deliveryAddress->getAddress2())
->setAddress3($deliveryAddress->getAddress3())
->setZipcode($deliveryAddress->getZipcode())
->setCity($deliveryAddress->getCity())
->setPhone($deliveryAddress->getPhone())
->setCountryId($deliveryAddress->getCountryId())
->save($con)
;
/* hard save the delivery and invoice addresses */
$deliveryOrderAddress = new OrderAddress();
$deliveryOrderAddress
->setCustomerTitleId($deliveryAddress->getTitleId())
->setCompany($deliveryAddress->getCompany())
->setFirstname($deliveryAddress->getFirstname())
->setLastname($deliveryAddress->getLastname())
->setAddress1($deliveryAddress->getAddress1())
->setAddress2($deliveryAddress->getAddress2())
->setAddress3($deliveryAddress->getAddress3())
->setZipcode($deliveryAddress->getZipcode())
->setCity($deliveryAddress->getCity())
->setPhone($deliveryAddress->getPhone())
->setCellphone($deliveryAddress->getCellphone())
->setCountryId($deliveryAddress->getCountryId())
->setStateId($deliveryAddress->getStateId())
->save($con);
$invoiceOrderAddress = new OrderAddress();
$invoiceOrderAddress
->setCustomerTitleId($invoiceAddress->getTitleId())
->setCompany($invoiceAddress->getCompany())
->setFirstname($invoiceAddress->getFirstname())
->setLastname($invoiceAddress->getLastname())
->setAddress1($invoiceAddress->getAddress1())
->setAddress2($invoiceAddress->getAddress2())
->setAddress3($invoiceAddress->getAddress3())
->setZipcode($invoiceAddress->getZipcode())
->setCity($invoiceAddress->getCity())
->setPhone($invoiceAddress->getPhone())
->setCountryId($invoiceAddress->getCountryId())
->save($con)
;
$invoiceOrderAddress = new OrderAddress();
$invoiceOrderAddress
->setCustomerTitleId($invoiceAddress->getTitleId())
->setCompany($invoiceAddress->getCompany())
->setFirstname($invoiceAddress->getFirstname())
->setLastname($invoiceAddress->getLastname())
->setAddress1($invoiceAddress->getAddress1())
->setAddress2($invoiceAddress->getAddress2())
->setAddress3($invoiceAddress->getAddress3())
->setZipcode($invoiceAddress->getZipcode())
->setCity($invoiceAddress->getCity())
->setPhone($invoiceAddress->getPhone())
->setCellphone($invoiceAddress->getCellphone())
->setCountryId($invoiceAddress->getCountryId())
->setStateId($deliveryAddress->getStateId())
->save($con);
$placedOrder->setDeliveryOrderAddressId($deliveryOrderAddress->getId());
$placedOrder->setInvoiceOrderAddressId($invoiceOrderAddress->getId());
$placedOrder->setDeliveryOrderAddressId($deliveryOrderAddress->getId());
$placedOrder->setInvoiceOrderAddressId($invoiceOrderAddress->getId());
$taxCountry = $deliveryAddress->getCountry();
}
$placedOrder->setStatusId(
OrderStatusQuery::getNotPaidStatus()->getId()
OrderStatusQuery::getNotPaidStatus()->getId()
);
$placedOrder->setCartId($cart->getId());
/* memorize discount */
$placedOrder->setDiscount(
$cart->getDiscount()
$cart->getDiscount()
);
$placedOrder->save($con);
@@ -217,30 +265,57 @@ class Order extends BaseAction implements EventSubscriberInterface
$product = $cartItem->getProduct();
/* get translation */
/** @var ProductI18n $productI18n */
$productI18n = I18n::forceI18nRetrieving($lang->getLocale(), 'Product', $product->getId());
$pse = $cartItem->getProductSaleElements();
// get the virtual document path
$virtualDocumentEvent = new VirtualProductOrderHandleEvent($placedOrder, $pse->getId());
// essentially used for virtual product. modules that handles virtual product can
// allow the use of stock even for virtual products
$useStock = true;
$virtual = 0;
// if the product is virtual, dispatch an event to collect information
if ($product->getVirtual() === 1) {
$dispatcher->dispatch(TheliaEvents::VIRTUAL_PRODUCT_ORDER_HANDLE, $virtualDocumentEvent);
$useStock = $virtualDocumentEvent->isUseStock();
$virtual = $virtualDocumentEvent->isVirtual() ? 1 : 0;
}
/* check still in stock */
if ($cartItem->getQuantity() > $pse->getQuantity()) {
if ($cartItem->getQuantity() > $pse->getQuantity()
&& true === ConfigQuery::checkAvailableStock()
&& $useStock) {
throw new TheliaProcessException("Not enough stock", TheliaProcessException::CART_ITEM_NOT_ENOUGH_STOCK, $cartItem);
}
/* decrease stock */
$pse->setQuantity(
$pse->getQuantity() - $cartItem->getQuantity()
);
$pse->save($con);
if ($useStock && $manageStock) {
/* decrease stock for non virtual product */
$allowNegativeStock = intval(ConfigQuery::read('allow_negative_stock', 0));
$newStock = $pse->getQuantity() - $cartItem->getQuantity();
//Forbid negative stock
if ($newStock < 0 && 0 === $allowNegativeStock) {
$newStock = 0;
}
$pse->setQuantity(
$newStock
);
$pse->save($con);
}
/* get tax */
/** @var TaxRuleI18n $taxRuleI18n */
$taxRuleI18n = I18n::forceI18nRetrieving($lang->getLocale(), 'TaxRule', $product->getTaxRuleId());
$taxDetail = $product->getTaxRule()->getTaxDetail(
$product,
$taxCountry,
$cartItem->getPrice(),
$cartItem->getPromoPrice(),
$lang->getLocale()
$product,
$taxCountry,
$cartItem->getPrice(),
$cartItem->getPromoPrice(),
$lang->getLocale()
);
$orderProduct = new OrderProduct();
@@ -248,10 +323,13 @@ class Order extends BaseAction implements EventSubscriberInterface
->setOrderId($placedOrder->getId())
->setProductRef($product->getRef())
->setProductSaleElementsRef($pse->getRef())
->setProductSaleElementsId($pse->getId())
->setTitle($productI18n->getTitle())
->setChapo($productI18n->getChapo())
->setDescription($productI18n->getDescription())
->setPostscriptum($productI18n->getPostscriptum())
->setVirtual($virtual)
->setVirtualDocument($virtualDocumentEvent->getPath())
->setQuantity($cartItem->getQuantity())
->setPrice($cartItem->getPrice())
->setPromoPrice($cartItem->getPromoPrice())
@@ -261,12 +339,13 @@ class Order extends BaseAction implements EventSubscriberInterface
->setTaxRuleTitle($taxRuleI18n->getTitle())
->setTaxRuleDescription($taxRuleI18n->getDescription())
->setEanCode($pse->getEanCode())
->setCartIemId($cartItem->getId())
->setCartItemId($cartItem->getId())
->setDispatcher($dispatcher)
->save($con)
->save($con)
;
/* fulfill order_product_tax */
/** @var OrderProductTax $tax */
foreach ($taxDetail as $tax) {
$tax->setOrderProductId($orderProduct->getId());
$tax->save($con);
@@ -274,7 +353,10 @@ class Order extends BaseAction implements EventSubscriberInterface
/* fulfill order_attribute_combination and decrease stock */
foreach ($pse->getAttributeCombinations() as $attributeCombination) {
/** @var \Thelia\Model\Attribute $attribute */
$attribute = I18n::forceI18nRetrieving($lang->getLocale(), 'Attribute', $attributeCombination->getAttributeId());
/** @var \Thelia\Model\AttributeAv $attributeAv */
$attributeAv = I18n::forceI18nRetrieving($lang->getLocale(), 'AttributeAv', $attributeCombination->getAttributeAvId());
$orderAttributeCombination = new OrderProductAttributeCombination();
@@ -288,7 +370,7 @@ class Order extends BaseAction implements EventSubscriberInterface
->setAttributeAvChapo($attributeAv->getChapo())
->setAttributeAvDescription($attributeAv->getDescription())
->setAttributeAvPostscriptum($attributeAv->getPostscriptum())
->save($con);
->save($con);
}
}
@@ -299,50 +381,72 @@ class Order extends BaseAction implements EventSubscriberInterface
/**
* Create an order outside of the front-office context, e.g. manually from the back-office.
* @param OrderManualEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function createManual(OrderManualEvent $event)
public function createManual(OrderManualEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$paymentModule = ModuleQuery::create()->findPk($event->getOrder()->getPaymentModuleId());
/** @var \Thelia\Module\PaymentModuleInterface $paymentModuleInstance */
$paymentModuleInstance = $paymentModule->createInstance();
$event->setPlacedOrder(
$this->createOrder(
$event->getDispatcher(),
$dispatcher,
$event->getOrder(),
$event->getCurrency(),
$event->getLang(),
$event->getCart(),
$event->getCustomer()
$event->getCustomer(),
$this->isModuleManageStockOnCreation(
$dispatcher,
$paymentModuleInstance
),
$event->getUseOrderDefinedAddresses()
)
);
$event->setOrder(new \Thelia\Model\Order());
$event->setOrder(new OrderModel());
}
/**
* @param OrderEvent $event
*
* @throws \Thelia\Exception\TheliaProcessException
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function create(OrderEvent $event)
public function create(OrderEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$session = $this->getSession();
$order = $event->getOrder();
$paymentModule = ModuleQuery::create()->findPk($order->getPaymentModuleId());
/** @var \Thelia\Module\PaymentModuleInterface $paymentModuleInstance */
$paymentModuleInstance = $paymentModule->createInstance();
$placedOrder = $this->createOrder(
$event->getDispatcher(),
$dispatcher,
$event->getOrder(),
$session->getCurrency(),
$session->getLang(),
$session->getCart(),
$this->securityContext->getCustomerUser()
$session->getSessionCart($dispatcher),
$this->securityContext->getCustomerUser(),
$this->isModuleManageStockOnCreation(
$dispatcher,
$paymentModuleInstance
)
);
$event->getDispatcher()->dispatch(TheliaEvents::ORDER_BEFORE_PAYMENT, new OrderEvent($placedOrder));
$dispatcher->dispatch(TheliaEvents::ORDER_BEFORE_PAYMENT, new OrderEvent($placedOrder));
/* but memorize placed order */
$event->setOrder(new \Thelia\Model\Order());
$event->setOrder(new OrderModel());
$event->setPlacedOrder($placedOrder);
/* empty cart */
$dispatcher = $event->getDispatcher();
/* call pay method */
$payEvent = new OrderPaymentEvent($placedOrder);
@@ -354,68 +458,177 @@ class Order extends BaseAction implements EventSubscriberInterface
}
/**
* @param \Thelia\Core\Event\Order\OrderEvent $event
* @param OrderEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function sendOrderEmail(OrderEvent $event)
public function orderBeforePayment(OrderEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$contact_email = ConfigQuery::read('store_email');
$dispatcher ->dispatch(TheliaEvents::ORDER_SEND_CONFIRMATION_EMAIL, clone $event);
if ($contact_email) {
$message = MessageQuery::create()
->filterByName('order_confirmation')
->findOne();
if (false === $message) {
throw new \Exception("Failed to load message 'order_confirmation'.");
}
$order = $event->getOrder();
$customer = $order->getCustomer();
$this->parser->assign('order_id', $order->getId());
$this->parser->assign('order_ref', $order->getRef());
$message
->setLocale($order->getLang()->getLocale());
$instance = \Swift_Message::newInstance()
->addTo($customer->getEmail(), $customer->getFirstname()." ".$customer->getLastname())
->addFrom($contact_email, ConfigQuery::read('store_name'))
;
// Build subject and body
$message->buildMessage($this->parser, $instance);
$this->getMailer()->send($instance);
}
$dispatcher->dispatch(TheliaEvents::ORDER_SEND_NOTIFICATION_EMAIL, clone $event);
}
/**
* Clear the cart and the order in the customer session once the order is placed,
* and the payment performed.
*
* return an instance of \Swift_Mailer with good Transporter configured.
*
* @return \Swift_Mailer
* @param OrderEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function getMailer()
public function orderCartClear(/** @noinspection PhpUnusedParameterInspection */ OrderEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
return $this->mailer->getSwiftMailer();
// Empty cart and clear current order
$session = $this->getSession();
$session->clearSessionCart($dispatcher);
$session->setOrder(new OrderModel());
}
/**
* @param OrderEvent $event
*
* @throws \Exception if the message cannot be loaded.
*/
public function sendConfirmationEmail(OrderEvent $event)
{
$this->mailer->sendEmailToCustomer(
'order_confirmation',
$event->getOrder()->getCustomer(),
[
'order_id' => $event->getOrder()->getId(),
'order_ref' => $event->getOrder()->getRef()
]
);
}
/**
* @param OrderEvent $event
*
* @throws \Exception if the message cannot be loaded.
*/
public function sendNotificationEmail(OrderEvent $event)
{
$this->mailer->sendEmailToShopManagers(
'order_notification',
[
'order_id' => $event->getOrder()->getId(),
'order_ref' => $event->getOrder()->getRef()
]
);
}
/**
* @param OrderEvent $event
*/
public function updateStatus(OrderEvent $event)
public function updateStatus(OrderEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$order = $event->getOrder();
$newStatus = $event->getStatus();
$paymentModule = ModuleQuery::create()->findPk($order->getPaymentModuleId());
/** @var PaymentModuleInterface $paymentModuleInstance */
$paymentModuleInstance = $paymentModule->createInstance();
$order->setStatusId($event->getStatus());
$manageStockOnCreation = $this->isModuleManageStockOnCreation(
$dispatcher,
$paymentModuleInstance
);
$this->updateQuantity($order, $newStatus, $manageStockOnCreation);
$order->setStatusId($newStatus);
$order->save();
$event->setOrder($order);
}
/**
* @param ModelOrder $order
* @param $newStatus $newStatus the new status ID
* @throws \Thelia\Exception\TheliaProcessException
*/
public function updateQuantity(ModelOrder $order, $newStatus, $manageStockOnCreation = true)
{
$canceledStatus = OrderStatusQuery::getCancelledStatus()->getId();
$paidStatus = OrderStatusQuery::getPaidStatus()->getId();
if ($newStatus == $canceledStatus || $order->isCancelled()) {
$this->updateQuantityForCanceledOrder($order, $newStatus, $canceledStatus);
} elseif ($paidStatus == $newStatus && $order->isNotPaid() && $order->getVersion() == 1) {
$this->updateQuantityForPaidOrder($order, $manageStockOnCreation);
}
}
/**
* @param ModelOrder $order
* @throws \Exception
* @throws \Propel\Runtime\Exception\PropelException
*/
protected function updateQuantityForPaidOrder(ModelOrder $order, $manageStockOnCreation)
{
$paymentModule = ModuleQuery::create()->findPk($order->getPaymentModuleId());
/** @var \Thelia\Module\PaymentModuleInterface $paymentModuleInstance */
$paymentModuleInstance = $paymentModule->createInstance();
if (false === $manageStockOnCreation) {
$orderProductList = $order->getOrderProducts();
/** @var OrderProduct $orderProduct */
foreach ($orderProductList as $orderProduct) {
$productSaleElementsId = $orderProduct->getProductSaleElementsId();
/** @var ProductSaleElements $productSaleElements */
if (null !== $productSaleElements = ProductSaleElementsQuery::create()->findPk($productSaleElementsId)) {
/* check still in stock */
if ($orderProduct->getQuantity() > $productSaleElements->getQuantity() && true === ConfigQuery::checkAvailableStock()) {
throw new TheliaProcessException($productSaleElements->getRef() . " : Not enough stock");
}
$productSaleElements->setQuantity($productSaleElements->getQuantity() - $orderProduct->getQuantity());
$productSaleElements->save();
}
}
}
}
/**
* Update product quantity if new status is canceled or if old status is canceled.
*
* @param ModelOrder $order
* @param $newStatus
* @param $canceledStatus
* @throws \Exception
* @throws \Propel\Runtime\Exception\PropelException
*/
protected function updateQuantityForCanceledOrder(ModelOrder $order, $newStatus, $canceledStatus)
{
$orderProductList = $order->getOrderProducts();
/** @var OrderProduct $orderProduct */
foreach ($orderProductList as $orderProduct) {
$productSaleElementsId = $orderProduct->getProductSaleElementsId();
/** @var ProductSaleElements $productSaleElements */
if (null !== $productSaleElements = ProductSaleElementsQuery::create()->findPk($productSaleElementsId)) {
if ($newStatus == $canceledStatus) {
$productSaleElements->setQuantity($productSaleElements->getQuantity() + $orderProduct->getQuantity());
} else {
/* check still in stock */
if ($orderProduct->getQuantity() > $productSaleElements->getQuantity() && true === ConfigQuery::checkAvailableStock()) {
throw new TheliaProcessException($productSaleElements->getRef() . " : Not enough stock");
}
$productSaleElements->setQuantity($productSaleElements->getQuantity() - $orderProduct->getQuantity());
}
$productSaleElements->save();
}
}
}
/**
* @param OrderEvent $event
*/
@@ -429,6 +642,19 @@ class Order extends BaseAction implements EventSubscriberInterface
$event->setOrder($order);
}
/**
* @param OrderEvent $event
*/
public function updateTransactionRef(OrderEvent $event)
{
$order = $event->getOrder();
$order->setTransactionRef($event->getTransactionRef());
$order->save();
$event->setOrder($order);
}
/**
* @param OrderAddressEvent $event
*/
@@ -447,7 +673,9 @@ class Order extends BaseAction implements EventSubscriberInterface
->setZipcode($event->getZipcode())
->setCity($event->getCity())
->setCountryId($event->getCountry())
->setStateId($event->getState())
->setPhone($event->getPhone())
->setCellphone($event->getCellphone())
;
$orderAddress->save();
@@ -455,24 +683,30 @@ class Order extends BaseAction implements EventSubscriberInterface
}
/**
* Returns an array of event names this subscriber wants to listen to.
* Check if a payment module manage stock on creation
*
* The array keys are event names and the value can be:
*
* * The method name to call (priority defaults to 0)
* * An array composed of the method name to call and the priority
* * An array of arrays composed of the method names to call and respective
* priorities, or 0 if unset
*
* For instance:
*
* * array('eventName' => 'methodName')
* * array('eventName' => array('methodName', $priority))
* * array('eventName' => array(array('methodName1', $priority), array('methodName2'))
*
* @return array The event names to listen to
*
* @api
* @param EventDispatcher $dispatcher
* @param PaymentModuleInterface $module
* @return bool if the module manage stock on creation, false otherwise
*/
protected function isModuleManageStockOnCreation(EventDispatcherInterface $dispatcher, PaymentModuleInterface $module)
{
$event = new ManageStockOnCreationEvent($module);
$dispatcher->dispatch(
TheliaEvents::getModuleEvent(
TheliaEvents::MODULE_PAYMENT_MANAGE_STOCK,
$module->getCode()
)
);
return (null !== $event->getManageStock())
? $event->getManageStock()
: $module->manageStockOnCreation();
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents()
{
@@ -483,9 +717,13 @@ class Order extends BaseAction implements EventSubscriberInterface
TheliaEvents::ORDER_SET_INVOICE_ADDRESS => array("setInvoiceAddress", 128),
TheliaEvents::ORDER_SET_PAYMENT_MODULE => array("setPaymentModule", 128),
TheliaEvents::ORDER_PAY => array("create", 128),
TheliaEvents::ORDER_BEFORE_PAYMENT => array("sendOrderEmail", 128),
TheliaEvents::ORDER_CART_CLEAR => array("orderCartClear", 128),
TheliaEvents::ORDER_BEFORE_PAYMENT => array("orderBeforePayment", 128),
TheliaEvents::ORDER_SEND_CONFIRMATION_EMAIL => array("sendConfirmationEmail", 128),
TheliaEvents::ORDER_SEND_NOTIFICATION_EMAIL => array("sendNotificationEmail", 128),
TheliaEvents::ORDER_UPDATE_STATUS => array("updateStatus", 128),
TheliaEvents::ORDER_UPDATE_DELIVERY_REF => array("updateDeliveryRef", 128),
TheliaEvents::ORDER_UPDATE_TRANSACTION_REF => array("updateTransactionRef", 128),
TheliaEvents::ORDER_UPDATE_ADDRESS => array("updateAddress", 128),
TheliaEvents::ORDER_CREATE_MANUAL => array("createManual", 128),
);
@@ -498,6 +736,6 @@ class Order extends BaseAction implements EventSubscriberInterface
*/
protected function getSession()
{
return $this->request->getSession();
return $this->requestStack->getCurrentRequest()->getSession();
}
}

View File

@@ -0,0 +1,147 @@
<?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\Action;
use Propel\Runtime\ActiveQuery\Criteria;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Thelia\Core\Event\OrderStatus\OrderStatusCreateEvent;
use Thelia\Core\Event\OrderStatus\OrderStatusDeleteEvent;
use Thelia\Core\Event\OrderStatus\OrderStatusEvent;
use Thelia\Core\Event\OrderStatus\OrderStatusUpdateEvent;
use Thelia\Core\Event\TheliaEvents;
use Thelia\Core\Event\UpdatePositionEvent;
use Thelia\Core\Translation\Translator;
use Thelia\Model\OrderQuery;
use Thelia\Model\OrderStatus as OrderStatusModel;
use Thelia\Model\OrderStatusQuery;
/**
* Class OrderStatus
* @package Thelia\Action
* @author Gilles Bourgeat <gbourgeat@openstudio.fr>
* @since 2.4
*/
class OrderStatus extends BaseAction implements EventSubscriberInterface
{
/**
* @param OrderStatusCreateEvent $event
*/
public function create(OrderStatusCreateEvent $event)
{
$this->createOrUpdate($event, new OrderStatusModel());
}
/**
* @param OrderStatusUpdateEvent $event
*/
public function update(OrderStatusUpdateEvent $event)
{
$orderStatus = $this->getOrderStatus($event);
$this->createOrUpdate($event, $orderStatus);
}
/**
* @param OrderStatusDeleteEvent $event
* @throws \Exception
*/
public function delete(OrderStatusDeleteEvent $event)
{
$orderStatus = $this->getOrderStatus($event);
if ($orderStatus->getProtectedStatus()) {
throw new \Exception(
Translator::getInstance()->trans('This status is protected.')
. ' ' . Translator::getInstance()->trans('You can not delete it.')
);
}
if (null !== OrderQuery::create()->findOneByStatusId($orderStatus->getId())) {
throw new \Exception(
Translator::getInstance()->trans('Some commands use this status.')
. ' ' . Translator::getInstance()->trans('You can not delete it.')
);
}
$orderStatus->delete();
$event->setOrderStatus($orderStatus);
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents()
{
return array(
TheliaEvents::ORDER_STATUS_CREATE => ["create", 128],
TheliaEvents::ORDER_STATUS_UPDATE => ["update", 128],
TheliaEvents::ORDER_STATUS_DELETE => ["delete", 128],
TheliaEvents::ORDER_STATUS_UPDATE_POSITION => ["updatePosition", 128]
);
}
/**
* @param OrderStatusEvent $event
* @param OrderStatusModel $orderStatus
*/
protected function createOrUpdate(OrderStatusEvent $event, OrderStatusModel $orderStatus)
{
$orderStatus
->setCode(!$orderStatus->getProtectedStatus() ? $event->getCode() : $orderStatus->getCode())
->setColor($event->getColor())
// i18n
->setLocale($event->getLocale())
->setTitle($event->getTitle())
->setDescription($event->getDescription())
->setPostscriptum($event->getPostscriptum())
->setChapo($event->getChapo());
if ($orderStatus->getId() === null) {
$orderStatus->setPosition(
OrderStatusQuery::create()->orderByPosition(Criteria::DESC)->findOne()->getPosition() + 1
);
}
$orderStatus->save();
$event->setOrderStatus($orderStatus);
}
/**
* Changes position, selecting absolute ou relative change.
*
* @param UpdatePositionEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function updatePosition(UpdatePositionEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$this->genericUpdatePosition(OrderStatusQuery::create(), $event, $dispatcher);
}
/**
* @param OrderStatusUpdateEvent $event
* @return OrderStatusModel
*/
protected function getOrderStatus(OrderStatusUpdateEvent $event)
{
if (null === $orderStatus = OrderStatusQuery::create()->findOneById($event->getId())) {
throw new \LogicException(
"Order status not found"
);
}
return $orderStatus;
}
}

View File

@@ -0,0 +1,63 @@
<?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\Action;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Thelia\Core\Event\Payment\IsValidPaymentEvent;
use Thelia\Core\Event\TheliaEvents;
/**
* Class Payment
* @package Thelia\Action
* @author Julien Chanséaume <julien@thelia.net>
*/
class Payment implements EventSubscriberInterface
{
/**
* Check if a module is valid
*
* @param IsValidPaymentEvent $event
*/
public function isValid(IsValidPaymentEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$module = $event->getModule();
// dispatch event to target specific module
$dispatcher->dispatch(
TheliaEvents::getModuleEvent(
TheliaEvents::MODULE_PAYMENT_IS_VALID,
$module->getCode()
),
$event
);
if ($event->isPropagationStopped()) {
return;
}
// call legacy module method
$event->setValidModule($module->isValidPayment());
}
/**
* @inheritdoc
*/
public static function getSubscribedEvents()
{
return [
TheliaEvents::MODULE_PAYMENT_IS_VALID => ['isValid', 128],
];
}
}

View File

@@ -11,6 +11,7 @@
/*************************************************************************************/
namespace Thelia\Action;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Thelia\Core\Event\PdfEvent;
use Thelia\Core\Event\TheliaEvents;
@@ -18,15 +19,22 @@ use Thelia\Core\Event\TheliaEvents;
/**
* Class Pdf
* @package Thelia\Action
* @author Manuel Raynaud <mraynaud@openstudio.fr>
* @author Manuel Raynaud <manu@raynaud.io>
*/
class Pdf extends BaseAction implements EventSubscriberInterface
{
public function generatePdf(PdfEvent $event)
{
$html2pdf = new \HTML2PDF(
$event->getOrientation(),
$event->getFormat(),
$event->getLang(),
$event->getUnicode(),
$event->getEncoding(),
$event->getMarges()
);
$html2pdf = new \HTML2PDF($event->getOrientation(), $event->getFormat(), $event->getLang(), $event->getUnicode(), $event->getEncoding(), $event->getMarges());
$html2pdf->setDefaultFont($event->getFontName());
$html2pdf->pdf->SetDisplayMode('real');
@@ -35,24 +43,7 @@ class Pdf extends BaseAction implements EventSubscriberInterface
}
/**
* Returns an array of event names this subscriber wants to listen to.
*
* The array keys are event names and the value can be:
*
* * The method name to call (priority defaults to 0)
* * An array composed of the method name to call and the priority
* * An array of arrays composed of the method names to call and respective
* priorities, or 0 if unset
*
* For instance:
*
* * array('eventName' => 'methodName')
* * array('eventName' => array('methodName', $priority))
* * array('eventName' => array(array('methodName1', $priority), array('methodName2'))
*
* @return array The event names to listen to
*
* @api
* {@inheritdoc}
*/
public static function getSubscribedEvents()
{

File diff suppressed because it is too large Load Diff

View File

@@ -12,21 +12,30 @@
namespace Thelia\Action;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Thelia\Core\Event\Product\ProductCloneEvent;
use Thelia\Core\Event\TheliaEvents;
use Thelia\Core\Event\ProductSaleElement\ProductSaleElementCreateEvent;
use Thelia\Core\Template\Loop\ProductSaleElementsDocument;
use Thelia\Core\Template\Loop\ProductSaleElementsImage;
use Thelia\Model\AttributeCombinationQuery;
use Thelia\Model\Map\ProductSaleElementsTableMap;
use Thelia\Model\ProductDocumentQuery;
use Thelia\Model\ProductImageQuery;
use Thelia\Model\ProductSaleElements;
use Thelia\Model\ProductPrice;
use Thelia\Model\AttributeCombination;
use Thelia\Core\Event\ProductSaleElement\ProductSaleElementDeleteEvent;
use Thelia\Model\ProductSaleElementsProductDocument;
use Thelia\Model\ProductSaleElementsProductDocumentQuery;
use Thelia\Model\ProductSaleElementsProductImage;
use Thelia\Model\ProductSaleElementsProductImageQuery;
use Thelia\Model\ProductSaleElementsQuery;
use Thelia\Core\Event\ProductSaleElement\ProductSaleElementUpdateEvent;
use Thelia\Model\ProductPriceQuery;
use Propel\Runtime\Propel;
use Thelia\Model\AttributeAvQuery;
use Thelia\Model\Map\AttributeCombinationTableMap;
use Propel\Runtime\ActiveQuery\Criteria;
use Thelia\Core\Event\Product\ProductCombinationGenerationEvent;
@@ -34,11 +43,19 @@ use Propel\Runtime\Connection\ConnectionInterface;
class ProductSaleElement extends BaseAction implements EventSubscriberInterface
{
/** @var EventDispatcherInterface */
protected $eventDispatcher;
public function __construct(EventDispatcherInterface $eventDispatcher)
{
$this->eventDispatcher = $eventDispatcher;
}
/**
* Create a new product sale element, with or without combination
*
* @param ProductSaleElementCreateEvent $event
* @throws Exception
* @throws \Exception
*/
public function create(ProductSaleElementCreateEvent $event)
{
@@ -56,7 +73,7 @@ class ProductSaleElement extends BaseAction implements EventSubscriberInterface
if ($salesElement == null) {
// Create a new default product sale element
$salesElement = $event->getProduct()->createProductSaleElement($con, 0, 0, 0, $event->getCurrencyId(), true);
$salesElement = $event->getProduct()->createProductSaleElement($con, 0, 0, 0, $event->getCurrencyId(), false);
} else {
// This (new) one is the default
$salesElement->setIsDefault(true)->save($con);
@@ -66,9 +83,7 @@ class ProductSaleElement extends BaseAction implements EventSubscriberInterface
$combinationAttributes = $event->getAttributeAvList();
if (count($combinationAttributes) > 0) {
foreach ($combinationAttributes as $attributeAvId) {
$attributeAv = AttributeAvQuery::create()->findPk($attributeAvId);
if ($attributeAv !== null) {
@@ -88,7 +103,6 @@ class ProductSaleElement extends BaseAction implements EventSubscriberInterface
// Store all the stuff !
$con->commit();
} catch (\Exception $ex) {
$con->rollback();
throw $ex;
@@ -98,7 +112,8 @@ class ProductSaleElement extends BaseAction implements EventSubscriberInterface
/**
* Update an existing product sale element
*
* @param ProductSaleElementUpdateEvent $event
* @param ProductSaleElementUpdateEvent $event
* @throws \Exception
*/
public function update(ProductSaleElementUpdateEvent $event)
{
@@ -109,7 +124,6 @@ class ProductSaleElement extends BaseAction implements EventSubscriberInterface
$con->beginTransaction();
try {
// Update the product's tax rule
$event->getProduct()->setTaxRuleId($event->getTaxRuleId())->save($con);
@@ -120,6 +134,16 @@ class ProductSaleElement extends BaseAction implements EventSubscriberInterface
$salesElement->setProduct($event->getProduct());
}
// If this PSE is the default one, be sure to have *only one* default for this product
if ($event->getIsDefault()) {
ProductSaleElementsQuery::create()
->filterByProduct($event->getProduct())
->filterByIsDefault(true)
->filterById($event->getProductSaleElementId(), Criteria::NOT_EQUAL)
->update(['IsDefault' => false], $con)
;
}
// Update sale element
$salesElement
->setRef($event->getReference())
@@ -140,7 +164,6 @@ class ProductSaleElement extends BaseAction implements EventSubscriberInterface
// If price is not defined, create it.
if ($productPrice == null) {
$productPrice = new ProductPrice();
$productPrice
@@ -171,7 +194,6 @@ class ProductSaleElement extends BaseAction implements EventSubscriberInterface
// Store all the stuff !
$con->commit();
} catch (\Exception $ex) {
$con->rollback();
throw $ex;
@@ -181,12 +203,12 @@ class ProductSaleElement extends BaseAction implements EventSubscriberInterface
/**
* Delete a product sale element
*
* @param ProductSaleElementDeleteEvent $event
* @param ProductSaleElementDeleteEvent $event
* @throws \Exception
*/
public function delete(ProductSaleElementDeleteEvent $event)
{
if (null !== $pse = ProductSaleElementsQuery::create()->findPk($event->getProductSaleElementId())) {
$product = $pse->getProduct();
$con = Propel::getWriteConnection(ProductSaleElementsTableMap::DATABASE_NAME);
@@ -194,28 +216,28 @@ class ProductSaleElement extends BaseAction implements EventSubscriberInterface
$con->beginTransaction();
try {
$pse->delete($con);
if ($product->countSaleElements() <= 0) {
if ($product->countSaleElements($con) <= 0) {
// If we just deleted the last PSE, create a default one
$product->createProductSaleElement($con, 0, 0, 0, $event->getCurrencyId(), true);
} elseif ($pse->getIsDefault()) {
// If we deleted the default PSE, make the last created one the default
$pse = ProductSaleElementsQuery::create()
$newDefaultPse = ProductSaleElementsQuery::create()
->filterByProductId($product->getId())
->filterById($pse->getId(), Criteria::NOT_EQUAL)
->orderByCreatedAt(Criteria::DESC)
->findOne($con)
;
$pse->setIsDefault(true)->save($con);
if (null !== $newDefaultPse) {
$newDefaultPse->setIsDefault(true)->save($con);
}
}
// Store all the stuff !
$con->commit();
} catch (\Exception $ex) {
$con->rollback();
throw $ex;
@@ -226,7 +248,8 @@ class ProductSaleElement extends BaseAction implements EventSubscriberInterface
/**
* Generate combinations. All existing combinations for the product are deleted.
*
* @param ProductCombinationGenerationEvent $event
* @param ProductCombinationGenerationEvent $event
* @throws \Exception
*/
public function generateCombinations(ProductCombinationGenerationEvent $event)
{
@@ -235,7 +258,6 @@ class ProductSaleElement extends BaseAction implements EventSubscriberInterface
$con->beginTransaction();
try {
// Delete all product's productSaleElement
ProductSaleElementsQuery::create()->filterByProductId($event->product->getId())->delete();
@@ -243,20 +265,19 @@ class ProductSaleElement extends BaseAction implements EventSubscriberInterface
// Create all combinations
foreach ($event->getCombinations() as $combinationAttributesAvIds) {
// Create the PSE
$saleElement = $event->getProduct()->createProductSaleElement(
$con,
$event->getWeight(),
$event->getPrice(),
$event->getSalePrice(),
$event->getCurrencyId(),
$isDefault,
$event->getOnsale(),
$event->getIsnew(),
$event->getQuantity(),
$event->getEanCode(),
$event->getReference()
$con,
$event->getWeight(),
$event->getPrice(),
$event->getSalePrice(),
$event->getCurrencyId(),
$isDefault,
$event->getOnsale(),
$event->getIsnew(),
$event->getQuantity(),
$event->getEanCode(),
$event->getReference()
);
$isDefault = false;
@@ -267,7 +288,6 @@ class ProductSaleElement extends BaseAction implements EventSubscriberInterface
// Store all the stuff !
$con->commit();
} catch (\Exception $ex) {
$con->rollback();
throw $ex;
@@ -278,13 +298,12 @@ class ProductSaleElement extends BaseAction implements EventSubscriberInterface
* Create a combination for a given product sale element
*
* @param ConnectionInterface $con the Propel connection
* @param ProductSaleElement $salesElement the product sale element
* @param unknown $combinationAttributes an array oif attributes av IDs
* @param ProductSaleElements $salesElement the product sale element
* @param array $combinationAttributes an array oif attributes av IDs
*/
protected function createCombination(ConnectionInterface $con, ProductSaleElements $salesElement, $combinationAttributes)
{
foreach ($combinationAttributes as $attributeAvId) {
$attributeAv = AttributeAvQuery::create()->findPk($attributeAvId);
if ($attributeAv !== null) {
@@ -299,6 +318,156 @@ class ProductSaleElement extends BaseAction implements EventSubscriberInterface
}
}
/*******************
* CLONING PROCESS *
*******************/
/**
* Clone product's PSEs and associated datas
*
* @param ProductCloneEvent $event
*/
public function clonePSE(ProductCloneEvent $event)
{
$clonedProduct = $event->getClonedProduct();
// Get original product's PSEs
$originalProductPSEs = ProductSaleElementsQuery::create()
->orderByIsDefault(Criteria::DESC)
->findByProductId($event->getOriginalProduct()->getId());
/**
* Handle PSEs
*
* @var int $key
* @var ProductSaleElements $originalProductPSE
*/
foreach ($originalProductPSEs as $key => $originalProductPSE) {
$currencyId = ProductPriceQuery::create()
->filterByProductSaleElementsId($originalProductPSE->getId())
->select('CURRENCY_ID')
->findOne();
// The default PSE, created at the same time as the clone product, is overwritten
$clonedProductPSEId = $this->createClonePSE($event, $originalProductPSE, $currencyId);
$this->updateClonePSE($event, $clonedProductPSEId, $originalProductPSE, $key);
// PSE associated images
$originalProductPSEImages = ProductSaleElementsProductImageQuery::create()
->findByProductSaleElementsId($originalProductPSE->getId());
if (null !== $originalProductPSEImages) {
$this->clonePSEAssociatedFiles($clonedProduct->getId(), $clonedProductPSEId, $originalProductPSEImages, $type = 'image');
}
// PSE associated documents
$originalProductPSEDocuments = ProductSaleElementsProductDocumentQuery::create()
->findByProductSaleElementsId($originalProductPSE->getId());
if (null !== $originalProductPSEDocuments) {
$this->clonePSEAssociatedFiles($clonedProduct->getId(), $clonedProductPSEId, $originalProductPSEDocuments, $type = 'document');
}
}
}
public function createClonePSE(ProductCloneEvent $event, ProductSaleElements $originalProductPSE, $currencyId)
{
$attributeCombinationList = AttributeCombinationQuery::create()
->filterByProductSaleElementsId($originalProductPSE->getId())
->select(['ATTRIBUTE_AV_ID'])
->find();
$clonedProductCreatePSEEvent = new ProductSaleElementCreateEvent($event->getClonedProduct(), $attributeCombinationList, $currencyId);
$this->eventDispatcher->dispatch(TheliaEvents::PRODUCT_ADD_PRODUCT_SALE_ELEMENT, $clonedProductCreatePSEEvent);
return $clonedProductCreatePSEEvent->getProductSaleElement()->getId();
}
public function updateClonePSE(ProductCloneEvent $event, $clonedProductPSEId, ProductSaleElements $originalProductPSE, $key)
{
$originalProductPSEPrice = ProductPriceQuery::create()
->findOneByProductSaleElementsId($originalProductPSE->getId());
$clonedProductUpdatePSEEvent = new ProductSaleElementUpdateEvent($event->getClonedProduct(), $clonedProductPSEId);
$clonedProductUpdatePSEEvent
->setReference($event->getClonedProduct()->getRef().'-'.($key + 1))
->setIsdefault($originalProductPSE->getIsDefault())
->setFromDefaultCurrency(0)
->setWeight($originalProductPSE->getWeight())
->setQuantity($originalProductPSE->getQuantity())
->setOnsale($originalProductPSE->getPromo())
->setIsnew($originalProductPSE->getNewness())
->setEanCode($originalProductPSE->getEanCode())
->setTaxRuleId($event->getOriginalProduct()->getTaxRuleId())
->setPrice($originalProductPSEPrice->getPrice())
->setSalePrice($originalProductPSEPrice->getPromoPrice())
->setCurrencyId($originalProductPSEPrice->getCurrencyId());
$this->eventDispatcher->dispatch(TheliaEvents::PRODUCT_UPDATE_PRODUCT_SALE_ELEMENT, $clonedProductUpdatePSEEvent);
}
public function clonePSEAssociatedFiles($clonedProductId, $clonedProductPSEId, $originalProductPSEFiles, $type)
{
/** @var ProductSaleElementsDocument|ProductSaleElementsImage $originalProductPSEFile */
foreach ($originalProductPSEFiles as $originalProductPSEFile) {
$originalProductFilePositionQuery = [];
$originalProductPSEFileId = null;
// Get file's original position
switch ($type) {
case 'image':
$originalProductFilePositionQuery = ProductImageQuery::create();
$originalProductPSEFileId = $originalProductPSEFile->getProductImageId();
break;
case 'document':
$originalProductFilePositionQuery = ProductDocumentQuery::create();
$originalProductPSEFileId = $originalProductPSEFile->getProductDocumentId();
break;
}
$originalProductFilePosition = $originalProductFilePositionQuery
->select(['POSITION'])
->findPk($originalProductPSEFileId);
// Get cloned file ID to link to the cloned PSE
switch ($type) {
case 'image':
$clonedProductFileIdToLinkToPSEQuery = ProductImageQuery::create();
break;
case 'document':
$clonedProductFileIdToLinkToPSEQuery = ProductDocumentQuery::create();
break;
}
$clonedProductFileIdToLinkToPSE = $clonedProductFileIdToLinkToPSEQuery
->filterByProductId($clonedProductId)
->filterByPosition($originalProductFilePosition)
->select(['ID'])
->findOne();
// Save association
switch ($type) {
case 'image':
$assoc = new ProductSaleElementsProductImage();
$assoc->setProductImageId($clonedProductFileIdToLinkToPSE);
break;
case 'document':
$assoc = new ProductSaleElementsProductDocument();
$assoc->setProductDocumentId($clonedProductFileIdToLinkToPSE);
break;
}
$assoc
->setProductSaleElementsId($clonedProductPSEId)
->save();
}
}
/***************
* END CLONING *
***************/
/**
* {@inheritDoc}
*/
@@ -309,7 +478,7 @@ class ProductSaleElement extends BaseAction implements EventSubscriberInterface
TheliaEvents::PRODUCT_UPDATE_PRODUCT_SALE_ELEMENT => array("update", 128),
TheliaEvents::PRODUCT_DELETE_PRODUCT_SALE_ELEMENT => array("delete", 128),
TheliaEvents::PRODUCT_COMBINATION_GENERATION => array("generateCombinations", 128),
TheliaEvents::PSE_CLONE => array("clonePSE", 128)
);
}
}

View File

@@ -12,6 +12,7 @@
namespace Thelia\Action;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Thelia\Core\Event\Profile\ProfileEvent;
use Thelia\Core\Event\TheliaEvents;
@@ -29,13 +30,15 @@ class Profile extends BaseAction implements EventSubscriberInterface
{
/**
* @param ProfileEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function create(ProfileEvent $event)
public function create(ProfileEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$profile = new ProfileModel();
$profile
->setDispatcher($event->getDispatcher())
->setDispatcher($dispatcher)
->setCode($event->getCode())
->setLocale($event->getLocale())
->setTitle($event->getTitle())
@@ -51,13 +54,14 @@ class Profile extends BaseAction implements EventSubscriberInterface
/**
* @param ProfileEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function update(ProfileEvent $event)
public function update(ProfileEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if (null !== $profile = ProfileQuery::create()->findPk($event->getId())) {
$profile
->setDispatcher($event->getDispatcher())
->setDispatcher($dispatcher)
->setLocale($event->getLocale())
->setTitle($event->getTitle())
->setChapo($event->getChapo())
@@ -85,10 +89,9 @@ class Profile extends BaseAction implements EventSubscriberInterface
$profileResource = new ProfileResource();
$profileResource->setProfileId($event->getId())
->setResource(ResourceQuery::create()->findOneByCode($resourceCode))
->setAccess( $manager->getAccessValue() );
->setAccess($manager->getAccessValue());
$profileResource->save();
}
$event->setProfile($profile);
@@ -109,10 +112,9 @@ class Profile extends BaseAction implements EventSubscriberInterface
$profileModule = new ProfileModule();
$profileModule->setProfileId($event->getId())
->setModule(ModuleQuery::create()->findOneByCode($moduleCode))
->setAccess( $manager->getAccessValue() );
->setAccess($manager->getAccessValue());
$profileModule->save();
}
$event->setProfile($profile);
@@ -125,7 +127,6 @@ class Profile extends BaseAction implements EventSubscriberInterface
public function delete(ProfileEvent $event)
{
if (null !== $profile = ProfileQuery::create()->findPk($event->getId())) {
$profile
->delete()
;

View File

@@ -0,0 +1,60 @@
<?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\Action;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Thelia\Tools\URL;
use Thelia\Core\Security\Exception\AuthenticationException;
use Thelia\Core\HttpKernel\Exception\RedirectException as ExceptionRedirectException;
/**
* Class RedirectException
* @package Thelia\Action
* @author manuel raynaud <manu@raynaud.io>
*/
class RedirectException extends BaseAction implements EventSubscriberInterface
{
/** @var URL */
protected $urlManager;
public function __construct(URL $urlManager)
{
$this->urlManager = $urlManager;
}
public function checkRedirectException(GetResponseForExceptionEvent $event)
{
$exception = $event->getException();
if ($exception instanceof ExceptionRedirectException) {
$response = RedirectResponse::create($exception->getUrl(), $exception->getStatusCode());
$event->setResponse($response);
} elseif ($exception instanceof AuthenticationException) {
// Redirect to the login template
$response = RedirectResponse::create($this->urlManager->viewUrl($exception->getLoginTemplate()));
$event->setResponse($response);
}
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents()
{
return [
KernelEvents::EXCEPTION => array("checkRedirectException", 128),
];
}
}

View File

@@ -0,0 +1,505 @@
<?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\Action;
use Propel\Runtime\ActiveQuery\Criteria;
use Propel\Runtime\Connection\ConnectionInterface;
use Propel\Runtime\Exception\PropelException;
use Propel\Runtime\Propel;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Thelia\Core\Event\Sale\ProductSaleStatusUpdateEvent;
use Thelia\Core\Event\Sale\SaleActiveStatusCheckEvent;
use Thelia\Core\Event\Sale\SaleClearStatusEvent;
use Thelia\Core\Event\Sale\SaleCreateEvent;
use Thelia\Core\Event\Sale\SaleDeleteEvent;
use Thelia\Core\Event\Sale\SaleToggleActivityEvent;
use Thelia\Core\Event\Sale\SaleUpdateEvent;
use Thelia\Core\Event\TheliaEvents;
use Thelia\Model\Base\ProductPriceQuery;
use Thelia\Model\Country as CountryModel;
use Thelia\Model\Map\SaleTableMap;
use Thelia\Model\ProductSaleElements;
use Thelia\Model\ProductSaleElementsQuery;
use Thelia\Model\Sale as SaleModel;
use Thelia\Model\SaleOffsetCurrency;
use Thelia\Model\SaleOffsetCurrencyQuery;
use Thelia\Model\SaleProduct;
use Thelia\Model\SaleProductQuery;
use Thelia\Model\SaleQuery;
use Thelia\TaxEngine\Calculator;
/**
* Class Sale
*
* @package Thelia\Action
* @author Franck Allimant <franck@cqfdev.fr>
*/
class Sale extends BaseAction implements EventSubscriberInterface
{
/**
* Update PSE for a given product
*
* @param array $pseList an array of priduct sale elements
* @param bool $promoStatus true if the PSEs are on sale, false otherwise
* @param int $offsetType the offset type, see SaleModel::OFFSET_* constants
* @param Calculator $taxCalculator the tax calculator
* @param array $saleOffsetByCurrency an array of price offset for each currency (currency ID => offset_amount)
* @param ConnectionInterface $con
*/
protected function updateProductSaleElementsPrices($pseList, $promoStatus, $offsetType, Calculator $taxCalculator, $saleOffsetByCurrency, ConnectionInterface $con)
{
/** @var ProductSaleElements $pse */
foreach ($pseList as $pse) {
if ($pse->getPromo()!= $promoStatus) {
$pse
->setPromo($promoStatus)
->save($con)
;
}
/** @var SaleOffsetCurrency $offsetByCurrency */
foreach ($saleOffsetByCurrency as $currencyId => $offset) {
$productPrice = ProductPriceQuery::create()
->filterByProductSaleElementsId($pse->getId())
->filterByCurrencyId($currencyId)
->findOne($con);
if (null !== $productPrice) {
// Get the taxed price
$priceWithTax = $taxCalculator->getTaxedPrice($productPrice->getPrice());
// Remove the price offset to get the taxed promo price
switch ($offsetType) {
case SaleModel::OFFSET_TYPE_AMOUNT:
$promoPrice = max(0, $priceWithTax - $offset);
break;
case SaleModel::OFFSET_TYPE_PERCENTAGE:
$promoPrice = $priceWithTax * (1 - $offset / 100);
break;
default:
$promoPrice = $priceWithTax;
}
// and then get the untaxed promo price.
$promoPrice = $taxCalculator->getUntaxedPrice($promoPrice);
$productPrice
->setPromoPrice($promoPrice)
->save($con)
;
}
}
}
}
/**
* Update the promo status of the sale's selected products and combinations
*
* @param ProductSaleStatusUpdateEvent $event
* @throws \RuntimeException
* @throws \Exception
* @throws \Propel\Runtime\Exception\PropelException
*/
public function updateProductsSaleStatus(ProductSaleStatusUpdateEvent $event)
{
$taxCalculator = new Calculator();
$sale = $event->getSale();
// Get all selected product sale elements for this sale
if (null !== $saleProducts = SaleProductQuery::create()->filterBySale($sale)->orderByProductId()) {
$saleOffsetByCurrency = $sale->getPriceOffsets();
$offsetType = $sale->getPriceOffsetType();
$con = Propel::getWriteConnection(SaleTableMap::DATABASE_NAME);
$con->beginTransaction();
try {
/** @var SaleProduct $saleProduct */
foreach ($saleProducts as $saleProduct) {
// Reset all sale status on product's PSE
ProductSaleElementsQuery::create()
->filterByProductId($saleProduct->getProductId())
->update([ 'Promo' => false], $con)
;
$taxCalculator->load(
$saleProduct->getProduct($con),
CountryModel::getShopLocation()
);
$attributeAvId = $saleProduct->getAttributeAvId();
$pseRequest = ProductSaleElementsQuery::create()
->filterByProductId($saleProduct->getProductId())
;
// If no attribute AV id is defined, consider ALL product combinations
if (! is_null($attributeAvId)) {
// Find PSE attached to combination containing this attribute av :
// SELECT * from product_sale_elements pse
// left join attribute_combination ac on ac.product_sale_elements_id = pse.id
// where pse.product_id=363
// and ac.attribute_av_id = 7
// group by pse.id
$pseRequest
->useAttributeCombinationQuery(null, Criteria::LEFT_JOIN)
->filterByAttributeAvId($attributeAvId)
->endUse()
;
}
$pseList = $pseRequest->find();
if (null !== $pseList) {
$this->updateProductSaleElementsPrices(
$pseList,
$sale->getActive(),
$offsetType,
$taxCalculator,
$saleOffsetByCurrency,
$con
);
}
}
$con->commit();
} catch (PropelException $e) {
$con->rollback();
throw $e;
}
}
}
/**
* Create a new Sale
*
* @param SaleCreateEvent $event
*/
public function create(SaleCreateEvent $event)
{
$sale = new SaleModel();
$sale
->setLocale($event->getLocale())
->setTitle($event->getTitle())
->setSaleLabel($event->getSaleLabel())
->save()
;
$event->setSale($sale);
}
/**
* Process update sale
*
* @param SaleUpdateEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
* @throws PropelException
*/
public function update(SaleUpdateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if (null !== $sale = SaleQuery::create()->findPk($event->getSaleId())) {
$sale->setDispatcher($dispatcher);
$con = Propel::getWriteConnection(SaleTableMap::DATABASE_NAME);
$con->beginTransaction();
try {
// Disable all promo flag on sale's currently selected products,
// to reset promo status of the products that have been removed from the selection.
$sale->setActive(false);
$now = new \DateTime();
$startDate = $event->getStartDate();
$endDate = $event->getEndDate();
$update = ($startDate <= $now && $now <= $endDate);
if ($update) {
$dispatcher->dispatch(
TheliaEvents::UPDATE_PRODUCT_SALE_STATUS,
new ProductSaleStatusUpdateEvent($sale)
);
}
$sale
->setActive($event->getActive())
->setStartDate($startDate)
->setEndDate($endDate)
->setPriceOffsetType($event->getPriceOffsetType())
->setDisplayInitialPrice($event->getDisplayInitialPrice())
->setLocale($event->getLocale())
->setSaleLabel($event->getSaleLabel())
->setTitle($event->getTitle())
->setDescription($event->getDescription())
->setChapo($event->getChapo())
->setPostscriptum($event->getPostscriptum())
->save($con)
;
$event->setSale($sale);
// Update price offsets
SaleOffsetCurrencyQuery::create()->filterBySaleId($sale->getId())->delete($con);
foreach ($event->getPriceOffsets() as $currencyId => $priceOffset) {
$saleOffset = new SaleOffsetCurrency();
$saleOffset
->setCurrencyId($currencyId)
->setSaleId($sale->getId())
->setPriceOffsetValue($priceOffset)
->save($con)
;
}
// Update products
SaleProductQuery::create()->filterBySaleId($sale->getId())->delete($con);
$productAttributesArray = $event->getProductAttributes();
foreach ($event->getProducts() as $productId) {
if (isset($productAttributesArray[$productId])) {
foreach ($productAttributesArray[$productId] as $attributeId) {
$saleProduct = new SaleProduct();
$saleProduct
->setSaleId($sale->getId())
->setProductId($productId)
->setAttributeAvId($attributeId)
->save($con)
;
}
} else {
$saleProduct = new SaleProduct();
$saleProduct
->setSaleId($sale->getId())
->setProductId($productId)
->setAttributeAvId(null)
->save($con)
;
}
}
if ($update) {
// Update related products sale status
$dispatcher->dispatch(
TheliaEvents::UPDATE_PRODUCT_SALE_STATUS,
new ProductSaleStatusUpdateEvent($sale)
);
}
$con->commit();
} catch (PropelException $e) {
$con->rollback();
throw $e;
}
}
}
/**
* Toggle Sale activity
*
* @param SaleToggleActivityEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
* @throws \Propel\Runtime\Exception\PropelException
*/
public function toggleActivity(SaleToggleActivityEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$sale = $event->getSale();
$con = Propel::getWriteConnection(SaleTableMap::DATABASE_NAME);
$con->beginTransaction();
try {
$sale
->setDispatcher($dispatcher)
->setActive(!$sale->getActive())
->save($con);
// Update related products sale status
$dispatcher->dispatch(
TheliaEvents::UPDATE_PRODUCT_SALE_STATUS,
new ProductSaleStatusUpdateEvent($sale)
);
$event->setSale($sale);
$con->commit();
} catch (PropelException $e) {
$con->rollback();
throw $e;
}
}
/**
* Delete a sale
*
* @param SaleDeleteEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
* @throws \Propel\Runtime\Exception\PropelException
*/
public function delete(SaleDeleteEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if (null !== $sale = SaleQuery::create()->findPk($event->getSaleId())) {
$con = Propel::getWriteConnection(SaleTableMap::DATABASE_NAME);
$con->beginTransaction();
try {
// Update related products sale status, if required
if ($sale->getActive()) {
$sale->setActive(false);
// Update related products sale status
$dispatcher->dispatch(
TheliaEvents::UPDATE_PRODUCT_SALE_STATUS,
new ProductSaleStatusUpdateEvent($sale)
);
}
$sale->setDispatcher($dispatcher)->delete($con);
$event->setSale($sale);
$con->commit();
} catch (PropelException $e) {
$con->rollback();
throw $e;
}
}
}
/**
* Clear all sales
*
* @param SaleClearStatusEvent $event
* @throws \Propel\Runtime\Exception\PropelException
*/
public function clearStatus(/** @noinspection PhpUnusedParameterInspection */ SaleClearStatusEvent $event)
{
$con = Propel::getWriteConnection(SaleTableMap::DATABASE_NAME);
$con->beginTransaction();
try {
// Set the active status of all Sales to false
SaleQuery::create()
->filterByActive(true)
->update([ 'Active' => false ], $con)
;
// Reset all sale status on PSE
ProductSaleElementsQuery::create()
->filterByPromo(true)
->update([ 'Promo' => false], $con)
;
$con->commit();
} catch (PropelException $e) {
$con->rollback();
throw $e;
}
}
/**
* This method check the activation and deactivation dates of sales, and perform
* the required action depending on the current date.
*
* @param SaleActiveStatusCheckEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
* @throws \Propel\Runtime\Exception\PropelException
*/
public function checkSaleActivation(SaleActiveStatusCheckEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$con = Propel::getWriteConnection(SaleTableMap::DATABASE_NAME);
$con->beginTransaction();
try {
$now = time();
// Disable expired sales
if (null !== $salesToDisable = SaleQuery::create()
->filterByActive(true)
->filterByEndDate($now, Criteria::LESS_THAN)
->find()) {
/** @var SaleModel $sale */
foreach ($salesToDisable as $sale) {
$sale->setActive(false)->save();
// Update related products sale status
$dispatcher->dispatch(
TheliaEvents::UPDATE_PRODUCT_SALE_STATUS,
new ProductSaleStatusUpdateEvent($sale)
);
}
}
// Enable sales that should be enabled.
if (null !== $salesToEnable = SaleQuery::create()
->filterByActive(false)
->filterByStartDate($now, Criteria::LESS_EQUAL)
->filterByEndDate($now, Criteria::GREATER_EQUAL)
->find()) {
/** @var SaleModel $sale */
foreach ($salesToEnable as $sale) {
$sale->setActive(true)->save();
// Update related products sale status
$dispatcher->dispatch(
TheliaEvents::UPDATE_PRODUCT_SALE_STATUS,
new ProductSaleStatusUpdateEvent($sale)
);
}
}
$con->commit();
} catch (PropelException $e) {
$con->rollback();
throw $e;
}
}
/**
* @inheritdoc
*/
public static function getSubscribedEvents()
{
return array(
TheliaEvents::SALE_CREATE => array('create', 128),
TheliaEvents::SALE_UPDATE => array('update', 128),
TheliaEvents::SALE_DELETE => array('delete', 128),
TheliaEvents::SALE_TOGGLE_ACTIVITY => array('toggleActivity', 128),
TheliaEvents::SALE_CLEAR_SALE_STATUS => array('clearStatus', 128),
TheliaEvents::UPDATE_PRODUCT_SALE_STATUS => array('updateProductsSaleStatus', 128),
TheliaEvents::CHECK_SALE_ACTIVATION_EVENT => array('checkSaleActivation', 128),
);
}
}

View File

@@ -11,6 +11,7 @@
/*************************************************************************************/
namespace Thelia\Action;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Thelia\Core\Event\ShippingZone\ShippingZoneAddAreaEvent;
use Thelia\Core\Event\ShippingZone\ShippingZoneRemoveAreaEvent;
@@ -21,11 +22,10 @@ use Thelia\Model\AreaDeliveryModuleQuery;
/**
* Class ShippingZone
* @package Thelia\Action
* @author Manuel Raynaud <mraynaud@openstudio.fr>
* @author Manuel Raynaud <manu@raynaud.io>
*/
class ShippingZone extends BaseAction implements EventSubscriberInterface
{
public function addArea(ShippingZoneAddAreaEvent $event)
{
$areaDelivery = new AreaDeliveryModule();
@@ -51,24 +51,7 @@ class ShippingZone extends BaseAction implements EventSubscriberInterface
}
/**
* Returns an array of event names this subscriber wants to listen to.
*
* The array keys are event names and the value can be:
*
* * The method name to call (priority defaults to 0)
* * An array composed of the method name to call and the priority
* * An array of arrays composed of the method names to call and respective
* priorities, or 0 if unset
*
* For instance:
*
* * array('eventName' => 'methodName')
* * array('eventName' => array('methodName', $priority))
* * array('eventName' => array(array('methodName1', $priority), array('methodName2'))
*
* @return array The event names to listen to
*
* @api
* {@inheritdoc}
*/
public static function getSubscribedEvents()
{

View File

@@ -0,0 +1,103 @@
<?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\Action;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Thelia\Core\Event\State\StateCreateEvent;
use Thelia\Core\Event\State\StateDeleteEvent;
use Thelia\Core\Event\State\StateToggleVisibilityEvent;
use Thelia\Core\Event\State\StateUpdateEvent;
use Thelia\Core\Event\TheliaEvents;
use Thelia\Model\State as StateModel;
use Thelia\Model\StateQuery;
/**
* Class State
* @package Thelia\Action
* @author Julien Chanséaume <julien@thelia.net>
*/
class State extends BaseAction implements EventSubscriberInterface
{
public function create(StateCreateEvent $event)
{
$state = new StateModel();
$state
->setVisible($event->isVisible())
->setCountryId($event->getCountry())
->setIsocode($event->getIsocode())
->setLocale($event->getLocale())
->setTitle($event->getTitle())
->save()
;
$event->setState($state);
}
public function update(StateUpdateEvent $event)
{
if (null !== $state = StateQuery::create()->findPk($event->getStateId())) {
$state
->setVisible($event->isVisible())
->setCountryId($event->getCountry())
->setIsocode($event->getIsocode())
->setLocale($event->getLocale())
->setTitle($event->getTitle())
->save()
;
$event->setState($state);
}
}
public function delete(StateDeleteEvent $event)
{
if (null !== $state = StateQuery::create()->findPk($event->getStateId())) {
$state->delete();
$event->setState($state);
}
}
/**
* Toggle State visibility
*
* @param StateToggleVisibilityEvent $event
*/
public function toggleVisibility(StateToggleVisibilityEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$state = $event->getState();
$state
->setDispatcher($dispatcher)
->setVisible(!$state->getVisible())
->save()
;
$event->setState($state);
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents()
{
return array(
TheliaEvents::STATE_CREATE => array('create', 128),
TheliaEvents::STATE_UPDATE => array('update', 128),
TheliaEvents::STATE_DELETE => array('delete', 128),
TheliaEvents::STATE_TOGGLE_VISIBILITY => array('toggleVisibility', 128)
);
}
}

View File

@@ -12,6 +12,7 @@
namespace Thelia\Action;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Thelia\Core\Event\Tax\TaxEvent;
use Thelia\Core\Event\TheliaEvents;
@@ -22,13 +23,15 @@ class Tax extends BaseAction implements EventSubscriberInterface
{
/**
* @param TaxEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function create(TaxEvent $event)
public function create(TaxEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$tax = new TaxModel();
$tax
->setDispatcher($event->getDispatcher())
->setDispatcher($dispatcher)
->setRequirements($event->getRequirements())
->setType($event->getType())
->setLocale($event->getLocale())
@@ -43,13 +46,14 @@ class Tax extends BaseAction implements EventSubscriberInterface
/**
* @param TaxEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function update(TaxEvent $event)
public function update(TaxEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if (null !== $tax = TaxQuery::create()->findPk($event->getId())) {
$tax
->setDispatcher($event->getDispatcher())
->setDispatcher($dispatcher)
->setRequirements($event->getRequirements())
->setType($event->getType())
->setLocale($event->getLocale())
@@ -69,7 +73,6 @@ class Tax extends BaseAction implements EventSubscriberInterface
public function delete(TaxEvent $event)
{
if (null !== $tax = TaxQuery::create()->findPk($event->getId())) {
$tax
->delete()
;

View File

@@ -13,6 +13,7 @@
namespace Thelia\Action;
use Propel\Runtime\ActiveQuery\Criteria;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Thelia\Core\Event\Tax\TaxRuleEvent;
use Thelia\Core\Event\TheliaEvents;
@@ -26,31 +27,30 @@ class TaxRule extends BaseAction implements EventSubscriberInterface
/**
* @param TaxRuleEvent $event
*/
public function create(TaxRuleEvent $event)
public function create(TaxRuleEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$taxRule = new TaxRuleModel();
$taxRule
->setDispatcher($event->getDispatcher())
->setDispatcher($dispatcher)
->setLocale($event->getLocale())
->setTitle($event->getTitle())
->setDescription($event->getDescription())
;
;
$taxRule->save();
$event->setTaxRule($taxRule);
$event->setTaxRule($taxRule)->setId($taxRule->getId());
}
/**
* @param TaxRuleEvent $event
*/
public function update(TaxRuleEvent $event)
public function update(TaxRuleEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if (null !== $taxRule = TaxRuleQuery::create()->findPk($event->getId())) {
$taxRule
->setDispatcher($event->getDispatcher())
->setDispatcher($dispatcher)
->setLocale($event->getLocale())
->setTitle($event->getTitle())
->setDescription($event->getDescription())
@@ -67,25 +67,34 @@ class TaxRule extends BaseAction implements EventSubscriberInterface
public function updateTaxes(TaxRuleEvent $event)
{
if (null !== $taxRule = TaxRuleQuery::create()->findPk($event->getId())) {
$taxList = $this->getArrayFromJson($event->getTaxList());
$countryList = $this->getArrayFromJson22Compat($event->getCountryList());
$countryDeletedList = $this->getArrayFromJson22Compat($event->getCountryDeletedList());
$taxList = json_decode($event->getTaxList(), true);
/* clean the current tax rule for the countries */
TaxRuleCountryQuery::create()
->filterByTaxRule($taxRule)
->filterByCountryId($event->getCountryList(), Criteria::IN)
->delete();
/* clean the current tax rule for the countries/states */
$deletes = array_merge($countryList, $countryDeletedList);
foreach ($deletes as $item) {
TaxRuleCountryQuery::create()
->filterByTaxRule($taxRule)
->filterByCountryId(intval($item[0]), Criteria::EQUAL)
->filterByStateId(intval($item[1]) !== 0 ? $item[1] : null, Criteria::EQUAL)
->delete();
}
/* for each country */
foreach ($event->getCountryList() as $country) {
foreach ($countryList as $item) {
$position = 1;
$countryId = intval($item[0]);
$stateId = intval($item[1]);
/* on applique les nouvelles regles */
foreach ($taxList as $tax) {
if (is_array($tax)) {
foreach ($tax as $samePositionTax) {
$taxModel = new TaxRuleCountry();
$taxModel->setTaxRule($taxRule)
->setCountryId($country)
->setCountryId($countryId)
->setStateId($stateId ?: null)
->setTaxId($samePositionTax)
->setPosition($position);
$taxModel->save();
@@ -93,7 +102,8 @@ class TaxRule extends BaseAction implements EventSubscriberInterface
} else {
$taxModel = new TaxRuleCountry();
$taxModel->setTaxRule($taxRule)
->setCountryId($country)
->setCountryId($countryId)
->setStateId($stateId ?: null)
->setTaxId($tax)
->setPosition($position);
$taxModel->save();
@@ -106,13 +116,55 @@ class TaxRule extends BaseAction implements EventSubscriberInterface
}
}
protected function getArrayFromJson($obj)
{
if (is_null($obj)) {
$obj = [];
} else {
$obj = is_array($obj)
? $obj
: json_decode($obj, true);
}
return $obj;
}
/**
* This method ensures compatibility with the 2.2.x country arrays passed throught the TaxRuleEvent
*
* In 2.2.x, the TaxRuleEvent::getXXXCountryList() methods returned an array of country IDs. [ country ID, country ID ...].
* From 2.3.0-alpha1, these functions are expected to return an array of arrays, each one containing a country ID and
* a state ID. [ [ country ID, state ID], [ country ID, state ID], ...].
*
* This method checks the $obj parameter, and create a 2.3.0-alpha1 compatible return value if $obj is expressed using
* the 2.2.x form.
*
* @param array $obj
*
* @return array
*/
protected function getArrayFromJson22Compat($obj)
{
$obj = $this->getArrayFromJson($obj);
if (isset($obj[0]) && ! is_array($obj[0])) {
$objEx = [];
foreach ($obj as $item) {
$objEx[] = [$item, 0];
}
return $objEx;
}
return $obj;
}
/**
* @param TaxRuleEvent $event
*/
public function delete(TaxRuleEvent $event)
{
if (null !== $taxRule = TaxRuleQuery::create()->findPk($event->getId())) {
$taxRule
->delete()
;
@@ -127,7 +179,6 @@ class TaxRule extends BaseAction implements EventSubscriberInterface
public function setDefault(TaxRuleEvent $event)
{
if (null !== $taxRule = TaxRuleQuery::create()->findPk($event->getId())) {
TaxRuleQuery::create()->update(array(
"IsDefault" => 0
));

View File

@@ -12,26 +12,29 @@
namespace Thelia\Action;
use Propel\Runtime\Propel;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Thelia\Model\TemplateQuery;
use Thelia\Model\Template as TemplateModel;
use Thelia\Core\Event\TheliaEvents;
use Thelia\Core\Event\Template\TemplateUpdateEvent;
use Thelia\Core\Event\Template\TemplateCreateEvent;
use Thelia\Core\Event\Template\TemplateDeleteEvent;
use Thelia\Core\Event\UpdatePositionEvent;
use Thelia\Model\ProductQuery;
use Thelia\Core\Event\Template\TemplateAddAttributeEvent;
use Thelia\Core\Event\Template\TemplateDeleteAttributeEvent;
use Thelia\Model\AttributeTemplateQuery;
use Thelia\Model\AttributeTemplate;
use Thelia\Core\Event\Template\TemplateDeleteFeatureEvent;
use Thelia\Core\Event\Template\TemplateAddFeatureEvent;
use Thelia\Model\FeatureTemplateQuery;
use Thelia\Core\Event\Template\TemplateCreateEvent;
use Thelia\Core\Event\Template\TemplateDeleteAttributeEvent;
use Thelia\Core\Event\Template\TemplateDeleteEvent;
use Thelia\Core\Event\Template\TemplateDeleteFeatureEvent;
use Thelia\Core\Event\Template\TemplateDuplicateEvent;
use Thelia\Core\Event\Template\TemplateUpdateEvent;
use Thelia\Core\Event\TheliaEvents;
use Thelia\Core\Event\UpdatePositionEvent;
use Thelia\Core\Translation\Translator;
use Thelia\Model\AttributeTemplate;
use Thelia\Model\AttributeTemplateQuery;
use Thelia\Model\CategoryQuery;
use Thelia\Model\FeatureTemplate;
use Thelia\Model\FeatureTemplateQuery;
use Thelia\Model\Map\TemplateTableMap;
use Thelia\Model\ProductQuery;
use Thelia\Model\Template as TemplateModel;
use Thelia\Model\TemplateQuery;
class Template extends BaseAction implements EventSubscriberInterface
{
@@ -39,137 +42,228 @@ class Template extends BaseAction implements EventSubscriberInterface
* Create a new template entry
*
* @param \Thelia\Core\Event\Template\TemplateCreateEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function create(TemplateCreateEvent $event)
public function create(TemplateCreateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$template = new TemplateModel();
$template
->setDispatcher($event->getDispatcher())
->setDispatcher($dispatcher)
->setLocale($event->getLocale())
->setName($event->getTemplateName())
->save()
;
$event->setTemplate($template);
}
/**
* Dupliucate an existing template entry
*
* @param \Thelia\Core\Event\Template\TemplateCreateEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function duplicate(TemplateDuplicateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if (null !== $source = TemplateQuery::create()->findPk($event->getSourceTemplateId())) {
$source->setLocale($event->getLocale());
$createEvent = new TemplateCreateEvent();
$createEvent
->setLocale($event->getLocale())
->setTemplateName(
Translator::getInstance()->trans("Copy of %tpl", ["%tpl" => $source->getName() ])
);
$dispatcher->dispatch(TheliaEvents::TEMPLATE_CREATE, $createEvent);
$clone = $createEvent->getTemplate();
$attrList = AttributeTemplateQuery::create()->findByTemplateId($source->getId());
/** @var $feat AttributeTemplate */
foreach ($attrList as $feat) {
$dispatcher->dispatch(
TheliaEvents::TEMPLATE_ADD_ATTRIBUTE,
new TemplateAddAttributeEvent($clone, $feat->getAttributeId())
);
}
$featList = FeatureTemplateQuery::create()->findByTemplateId($source->getId());
/** @var $feat FeatureTemplate */
foreach ($featList as $feat) {
$dispatcher->dispatch(
TheliaEvents::TEMPLATE_ADD_FEATURE,
new TemplateAddFeatureEvent($clone, $feat->getFeatureId())
);
}
$event->setTemplate($clone);
}
}
/**
* Change a product template
*
* @param \Thelia\Core\Event\Template\TemplateUpdateEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function update(TemplateUpdateEvent $event)
public function update(TemplateUpdateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if (null !== $template = TemplateQuery::create()->findPk($event->getTemplateId())) {
$template
->setDispatcher($event->getDispatcher())
->setLocale($event->getLocale())
->setName($event->getTemplateName())
->save();
->setDispatcher($dispatcher)
->setLocale($event->getLocale())
->setName($event->getTemplateName())
->save();
$event->setTemplate($template);
}
}
/**
* Delete a product template entry
*
* @param \Thelia\Core\Event\Template\TemplateDeleteEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
* @throws \Exception
*/
public function delete(TemplateDeleteEvent $event)
public function delete(TemplateDeleteEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if (null !== ($template = TemplateQuery::create()->findPk($event->getTemplateId()))) {
// Check if template is used by a product
$product_count = ProductQuery::create()->findByTemplateId($template->getId())->count();
if ($product_count <= 0) {
$template
->setDispatcher($event->getDispatcher())
->delete()
;
$productCount = ProductQuery::create()->findByTemplateId($template->getId())->count();
if ($productCount <= 0) {
$con = Propel::getWriteConnection(TemplateTableMap::DATABASE_NAME);
$con->beginTransaction();
try {
$template
->setDispatcher($dispatcher)
->delete($con);
// We have to also delete any reference of this template in category tables
// We can't use a FK here, as the DefaultTemplateId column may be NULL
// so let's take care of this.
CategoryQuery::create()
->filterByDefaultTemplateId($event->getTemplateId())
->update([ 'DefaultTemplateId' => null], $con);
$con->commit();
} catch (\Exception $ex) {
$con->rollback();
throw $ex;
}
}
$event->setTemplate($template);
$event->setProductCount($product_count);
$event->setProductCount($productCount);
}
}
public function addAttribute(TemplateAddAttributeEvent $event)
{
if (null === AttributeTemplateQuery::create()->filterByAttributeId($event->getAttributeId())->filterByTemplate($event->getTemplate())->findOne()) {
$attribute_template = new AttributeTemplate();
$attribute_template
if (null === AttributeTemplateQuery::create()
->filterByAttributeId($event->getAttributeId())
->filterByTemplate($event->getTemplate())
->findOne()) {
$attributeTemplate = new AttributeTemplate();
$attributeTemplate
->setAttributeId($event->getAttributeId())
->setTemplate($event->getTemplate())
->save()
->save()
;
}
}
/**
* Changes position, selecting absolute ou relative change.
*
* @param CategoryChangePositionEvent $event
* @param UpdatePositionEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function updateAttributePosition(UpdatePositionEvent $event)
public function updateAttributePosition(UpdatePositionEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
return $this->genericUpdatePosition(AttributeTemplateQuery::create(), $event);
$this->genericUpdatePosition(AttributeTemplateQuery::create(), $event, $dispatcher);
}
/**
* Changes position, selecting absolute ou relative change.
*
* @param CategoryChangePositionEvent $event
* @param UpdatePositionEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function updateFeaturePosition(UpdatePositionEvent $event)
public function updateFeaturePosition(UpdatePositionEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
return $this->genericUpdatePosition(FeatureTemplateQuery::create(), $event);
$this->genericUpdatePosition(FeatureTemplateQuery::create(), $event, $dispatcher);
}
public function deleteAttribute(TemplateDeleteAttributeEvent $event)
public function deleteAttribute(TemplateDeleteAttributeEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$attribute_template = AttributeTemplateQuery::create()
$attributeTemplate = AttributeTemplateQuery::create()
->filterByAttributeId($event->getAttributeId())
->filterByTemplate($event->getTemplate())->findOne()
;
if ($attribute_template !== null) $attribute_template->delete();
if ($attributeTemplate !== null) {
$attributeTemplate
->setDispatcher($dispatcher)
->delete();
} else {
// Prevent event propagation
$event->stopPropagation();
}
}
public function addFeature(TemplateAddFeatureEvent $event)
{
if (null === FeatureTemplateQuery::create()->filterByFeatureId($event->getFeatureId())->filterByTemplate($event->getTemplate())->findOne()) {
$feature_template = new FeatureTemplate();
$feature_template
->setFeatureId($event->getFeatureId())
->setTemplate($event->getTemplate())
->save()
if (null === FeatureTemplateQuery::create()
->filterByFeatureId($event->getFeatureId())
->filterByTemplate($event->getTemplate())
->findOne()
) {
$featureTemplate = new FeatureTemplate();
$featureTemplate
->setFeatureId($event->getFeatureId())
->setTemplate($event->getTemplate())
->save()
;
}
}
public function deleteFeature(TemplateDeleteFeatureEvent $event)
public function deleteFeature(TemplateDeleteFeatureEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$feature_template = FeatureTemplateQuery::create()
$featureTemplate = FeatureTemplateQuery::create()
->filterByFeatureId($event->getFeatureId())
->filterByTemplate($event->getTemplate())->findOne()
;
if ($feature_template !== null) $feature_template->delete();
if ($featureTemplate !== null) {
$featureTemplate
->setDispatcher($dispatcher)
->delete();
} else {
// Prevent event propagation
$event->stopPropagation();
}
}
/**
* {@inheritDoc}
*/
@@ -179,16 +273,16 @@ class Template extends BaseAction implements EventSubscriberInterface
TheliaEvents::TEMPLATE_CREATE => array("create", 128),
TheliaEvents::TEMPLATE_UPDATE => array("update", 128),
TheliaEvents::TEMPLATE_DELETE => array("delete", 128),
TheliaEvents::TEMPLATE_DUPLICATE => array("duplicate", 128),
TheliaEvents::TEMPLATE_ADD_ATTRIBUTE => array("addAttribute", 128),
TheliaEvents::TEMPLATE_DELETE_ATTRIBUTE => array("deleteAttribute", 128),
TheliaEvents::TEMPLATE_ADD_FEATURE => array("addFeature", 128),
TheliaEvents::TEMPLATE_DELETE_FEATURE => array("deleteFeature", 128),
TheliaEvents::TEMPLATE_CHANGE_ATTRIBUTE_POSITION => array('updateAttributePosition', 128),
TheliaEvents::TEMPLATE_CHANGE_FEATURE_POSITION => array('updateFeaturePosition', 128),
);
}
}

View File

@@ -0,0 +1,372 @@
<?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\Action;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Filesystem\Filesystem;
use Thelia\Core\Event\Cache\CacheEvent;
use Thelia\Core\Event\TheliaEvents;
use Thelia\Core\Event\Translation\TranslationEvent;
use Thelia\Core\Translation\Translator;
use Thelia\Log\Tlog;
/**
* Class Translation
* @package Thelia\Action
* @author Manuel Raynaud <manu@raynaud.io>
*/
class Translation extends BaseAction implements EventSubscriberInterface
{
/** @var ContainerInterface */
protected $container;
public function __construct(ContainerInterface $container)
{
$this->container = $container;
}
public function getTranslatableStrings(TranslationEvent $event)
{
$stringCount = $this->walkDir(
$event->getDirectory(),
$event->getMode(),
$event->getLocale(),
$event->getDomain(),
$strings
);
$event
->setTranslatableStrings($strings)
->setTranslatableStringCount($stringCount)
;
}
/**
* Recursively examine files in a directory tree, and extract translatable strings.
*
* Returns an array of translatable strings, each item having with the following structure:
* 'files' an array of file names in which the string appears,
* 'text' the translatable text
* 'translation' => the text translation, or an empty string if none available.
* 'dollar' => true if the translatable text contains a $
*
* @param string $directory the path to the directory to examine
* @param string $walkMode type of file scanning: WALK_MODE_PHP or WALK_MODE_TEMPLATE
* @param string $currentLocale the current locale
* @param string $domain the translation domain (fontoffice, backoffice, module, etc...)
* @param array $strings the list of strings
* @throws \InvalidArgumentException if $walkMode contains an invalid value
* @return number the total number of translatable texts
*/
protected function walkDir($directory, $walkMode, $currentLocale, $domain, &$strings)
{
$numTexts = 0;
if ($walkMode == TranslationEvent::WALK_MODE_PHP) {
$prefix = '\-\>[\s]*trans[\s]*\([\s]*';
$allowedExts = array('php');
} elseif ($walkMode == TranslationEvent::WALK_MODE_TEMPLATE) {
$prefix = '\{intl(?:.*?)[\s]l=[\s]*';
$allowedExts = array('html', 'tpl', 'xml', 'txt');
} else {
throw new \InvalidArgumentException(
Translator::getInstance()->trans(
'Invalid value for walkMode parameter: %value',
array('%value' => $walkMode)
)
);
}
try {
Tlog::getInstance()->debug("Walking in $directory, in mode $walkMode");
/** @var \DirectoryIterator $fileInfo */
foreach (new \DirectoryIterator($directory) as $fileInfo) {
if ($fileInfo->isDot()) {
continue;
}
if ($fileInfo->isDir()) {
$numTexts += $this->walkDir(
$fileInfo->getPathName(),
$walkMode,
$currentLocale,
$domain,
$strings
);
}
if ($fileInfo->isFile()) {
$ext = $fileInfo->getExtension();
if (in_array($ext, $allowedExts)) {
if ($content = file_get_contents($fileInfo->getPathName())) {
$short_path = $this->normalizePath($fileInfo->getPathName());
Tlog::getInstance()->debug("Examining file $short_path\n");
$matches = array();
if (preg_match_all(
'/'.$prefix.'((?<![\\\\])[\'"])((?:.(?!(?<![\\\\])\1))*.?)*?\1/ms',
$content,
$matches
)) {
Tlog::getInstance()->debug("Strings found: ", $matches[2]);
$idx = 0;
foreach ($matches[2] as $match) {
$hash = md5($match);
if (isset($strings[$hash])) {
if (! in_array($short_path, $strings[$hash]['files'])) {
$strings[$hash]['files'][] = $short_path;
}
} else {
$numTexts++;
// remove \' (or \"), that will prevent the translator to work properly, as
// "abc \def\" ghi" will be passed as abc "def" ghi to the translator.
$quote = $matches[1][$idx];
$match = str_replace("\\$quote", $quote, $match);
// Ignore empty strings
if (strlen($match) == 0) {
continue;
}
$strings[$hash] = array(
'files' => array($short_path),
'text' => $match,
'translation' => Translator::getInstance()->trans(
$match,
[],
$domain,
$currentLocale,
false,
false
),
'custom_fallback' => Translator::getInstance()->trans(
sprintf(
Translator::GLOBAL_FALLBACK_KEY,
$domain,
$match
),
[],
Translator::GLOBAL_FALLBACK_DOMAIN,
$currentLocale,
false,
false
),
'global_fallback' => Translator::getInstance()->trans(
$match,
[],
Translator::GLOBAL_FALLBACK_DOMAIN,
$currentLocale,
false,
false
),
'dollar' => strstr($match, '$') !== false
);
}
$idx++;
}
}
}
}
}
}
} catch (\UnexpectedValueException $ex) {
// Directory does not exists => ignore it.
}
return $numTexts;
}
public function writeTranslationFile(TranslationEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$file = $event->getTranslationFilePath();
$fs = new Filesystem();
if (! $fs->exists($file) && true === $event->isCreateFileIfNotExists()) {
$dir = dirname($file);
if (! $fs->exists($file)) {
$fs->mkdir($dir);
$this->cacheClear($dispatcher);
}
}
if ($fp = @fopen($file, 'w')) {
fwrite($fp, '<' . "?php\n\n");
fwrite($fp, "return array(\n");
$texts = $event->getTranslatableStrings();
$translations = $event->getTranslatedStrings();
// Sort keys alphabetically while keeping index
asort($texts);
foreach ($texts as $key => $text) {
// Write only defined (not empty) translations
if (! empty($translations[$key])) {
$text = str_replace("'", "\'", $text);
$translation = str_replace("'", "\'", $translations[$key]);
fwrite($fp, sprintf(" '%s' => '%s',\n", $text, $translation));
}
}
fwrite($fp, ");\n");
@fclose($fp);
} else {
throw new \RuntimeException(
Translator::getInstance()->trans(
'Failed to open translation file %file. Please be sure that this file is writable by your Web server',
array('%file' => $file)
)
);
}
}
public function writeFallbackFile(TranslationEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$file = THELIA_LOCAL_DIR . 'I18n' . DS . $event->getLocale() . '.php';
$fs = new Filesystem();
$translations = [];
if (! $fs->exists($file)) {
if (true === $event->isCreateFileIfNotExists()) {
$dir = dirname($file);
$fs->mkdir($dir);
$this->cacheClear($dispatcher);
} else {
throw new \RuntimeException(
Translator::getInstance()->trans(
'Failed to open translation file %file. Please be sure that this file is writable by your Web server',
array('%file' => $file)
)
);
}
} else {
/*$loader = new PhpFileLoader();
$catalogue = $loade r->load($file);
$translations = $catalogue->all();
*/
$translations = require $file;
if (! is_array($translations)) {
$translations = [];
}
}
if ($fp = @fopen($file, 'w')) {
$texts = $event->getTranslatableStrings();
$customs = $event->getCustomFallbackStrings();
$globals = $event->getGlobalFallbackStrings();
// just reset current translations for this domain to remove strings that do not exist anymore
$translations[$event->getDomain()] = [];
foreach ($texts as $key => $text) {
if (!empty($customs[$key])) {
$translations[$event->getDomain()][$text] = $customs[$key];
}
if (!empty($globals[$key])) {
$translations[$text] = $globals[$key];
} else {
unset($translations[$text]);
}
}
fwrite($fp, '<' . "?php\n\n");
fwrite($fp, "return [\n");
// Sort keys alphabetically while keeping index
ksort($translations);
foreach ($translations as $key => $text) {
// Write only defined (not empty) translations
if (!empty($translations[$key])) {
if (is_array($translations[$key])) {
$key = str_replace("'", "\'", $key);
fwrite($fp, sprintf(" '%s' => [\n", $key));
ksort($translations[$key]);
foreach ($translations[$key] as $subKey => $subText) {
$subKey = str_replace("'", "\'", $subKey);
$translation = str_replace("'", "\'", $subText);
fwrite($fp, sprintf(" '%s' => '%s',\n", $subKey, $translation));
}
fwrite($fp, " ],\n");
} else {
$key = str_replace("'", "\'", $key);
$translation = str_replace("'", "\'", $text);
fwrite($fp, sprintf(" '%s' => '%s',\n", $key, $translation));
}
}
}
fwrite($fp, "];\n");
@fclose($fp);
}
}
protected function normalizePath($path)
{
$path = str_replace(
str_replace('\\', '/', THELIA_ROOT),
'',
str_replace('\\', '/', realpath($path))
);
return ltrim($path, '/');
}
protected function cacheClear(EventDispatcherInterface $dispatcher)
{
$cacheEvent = new CacheEvent(
$this->container->getParameter('kernel.cache_dir')
);
$dispatcher->dispatch(TheliaEvents::CACHE_CLEAR, $cacheEvent);
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents()
{
return array(
TheliaEvents::TRANSLATION_GET_STRINGS => array('getTranslatableStrings', 128),
TheliaEvents::TRANSLATION_WRITE_FILE => [
['writeTranslationFile', 128],
['writeFallbackFile', 128]
]
);
}
}

View File

@@ -13,11 +13,11 @@
namespace Thelia\Cart;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Thelia\Core\HttpFoundation\Request;
use Thelia\Model\CartQuery;
use Thelia\Model\Cart as CartModel;
use Thelia\Model\ConfigQuery;
use Thelia\Model\Customer;
use Symfony\Component\HttpFoundation\Request;
use Thelia\Core\HttpFoundation\Session\Session;
use Thelia\Core\Event\TheliaEvents;
use Thelia\Core\Event\Cart\CartEvent;
@@ -27,7 +27,9 @@ use Thelia\Core\Event\Cart\CartEvent;
*
* Trait CartTrait
* @package Thelia\Cart
* @author Manuel Raynaud <mraynaud@openstudio.fr>
* @author Manuel Raynaud <manu@raynaud.io>
*
* @deprecated CartTrait is deprecated, please use Session::getSessionCart method instead
*/
trait CartTrait
{
@@ -37,101 +39,16 @@ trait CartTrait
*
* @param EventDispatcherInterface $dispatcher the event dispatcher
* @param \Symfony\Component\HttpFoundation\Request $request
* @deprecated use Session::getSessionCart method instead
* @return \Thelia\Model\Cart
*/
public function getCart(EventDispatcherInterface $dispatcher, Request $request)
{
$session = $request->getSession();
if (null !== $cart = $session->getCart()) {
return $cart;
}
if ($request->cookies->has("thelia_cart")) {
//le cookie de panier existe, on le récupère
$token = $request->cookies->get("thelia_cart");
$cart = CartQuery::create()->findOneByToken($token);
if ($cart) {
//le panier existe en base
$customer = $session->getCustomerUser();
if ($customer) {
if ($cart->getCustomerId() != $customer->getId()) {
//le customer du panier n'est pas le mm que celui connecté, il faut cloner le panier sans le customer_id
$cart = $this->duplicateCart($dispatcher, $cart, $session, $customer);
}
} else {
if ($cart->getCustomerId() != null) {
//il faut dupliquer le panier sans le customer_id
$cart = $this->duplicateCart($dispatcher, $cart, $session);
}
}
} else {
$cart = $this->createCart($session);
}
} else {
//le cookie de panier n'existe pas, il va falloir le créer et faire un enregistrement en base.
$cart = $this->createCart($session);
}
$session->setCart($cart->getId());
return $cart;
}
/**
* @param \Thelia\Core\HttpFoundation\Session\Session $session
* @return \Thelia\Model\Cart
*/
protected function createCart(Session $session)
{
$cart = new CartModel();
$cart->setToken($this->generateCookie($session));
$cart->setCurrency($session->getCurrency(true));
if (null !== $customer = $session->getCustomerUser()) {
$cart->setCustomer($customer);
}
$cart->save();
$session->setCart($cart->getId());
return $cart;
}
/**
* try to duplicate existing Cart. Customer is here to determine if this cart belong to him.
*
* @param \Thelia\Model\Cart $cart
* @param \Thelia\Core\HttpFoundation\Session\Session $session
* @param \Thelia\Model\Customer $customer
* @return \Thelia\Model\Cart
*/
protected function duplicateCart(EventDispatcherInterface $dispatcher, CartModel $cart, Session $session, Customer $customer = null)
{
$currency = $session->getCurrency();
$newCart = $cart->duplicate($this->generateCookie($session), $customer, $currency, $dispatcher);
$session->setCart($newCart->getId());
$cartEvent = new CartEvent($newCart);
$dispatcher->dispatch(TheliaEvents::CART_DUPLICATE, $cartEvent);
return $cartEvent->getCart();
}
protected function generateCookie(Session $session)
{
$id = null;
if (ConfigQuery::read("cart.session_only", 0) == 0) {
$id = uniqid('', true);
$session->set('cart_use_cookie', $id);
}
return $id;
trigger_error(
'CartTrait is deprecated, please use Session::getSessionCart method instead',
E_USER_DEPRECATED
);
return $request->getSession()->getSessionCart($dispatcher);
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -26,7 +26,7 @@ use Thelia\Condition\Implementation\ConditionInterface;
*/
class ConditionCollection implements Iterator, Countable, ArrayAccess
{
/** @var array Array of ConditionInterface */
/** @var ConditionInterface[] */
protected $conditions = [];
/**
@@ -188,5 +188,4 @@ class ConditionCollection implements Iterator, Countable, ArrayAccess
return json_encode($arrayToSerialize);
}
}

View File

@@ -36,13 +36,11 @@ class ConditionEvaluator
/** @var ConditionInterface $condition */
foreach ($conditions as $condition) {
if (!$condition->isMatching()) {
$isMatching = false;
break;
return false;
}
}
return $isMatching;
}
/**
@@ -62,7 +60,7 @@ class ConditionEvaluator
}
switch ($o) {
case Operators::SUPERIOR :
case Operators::SUPERIOR:
// >
if ($v1 > $v2) {
return true;
@@ -70,7 +68,7 @@ class ConditionEvaluator
continue;
}
break;
case Operators::SUPERIOR_OR_EQUAL :
case Operators::SUPERIOR_OR_EQUAL:
// >=
if ($v1 >= $v2) {
return true;
@@ -78,7 +76,7 @@ class ConditionEvaluator
continue;
}
break;
case Operators::INFERIOR :
case Operators::INFERIOR:
// <
if ($v1 < $v2) {
return true;
@@ -86,7 +84,7 @@ class ConditionEvaluator
continue;
}
break;
case Operators::INFERIOR_OR_EQUAL :
case Operators::INFERIOR_OR_EQUAL:
// <=
if ($v1 <= $v2) {
return true;
@@ -94,7 +92,7 @@ class ConditionEvaluator
continue;
}
break;
case Operators::EQUAL :
case Operators::EQUAL:
// ==
if ($v1 == $v2) {
return true;

View File

@@ -84,7 +84,6 @@ class ConditionFactory
$collection = new ConditionCollection();
if (!empty($unserializedConditions)) {
/** @var SerializableCondition $condition */
foreach ($unserializedConditions as $condition) {
if ($this->container->has($condition->conditionServiceId)) {

View File

@@ -32,5 +32,4 @@ class ConditionOrganizer implements ConditionOrganizerInterface
{
// @todo: Implement organize() method.
}
}

View File

@@ -28,7 +28,7 @@ use Thelia\Model\CountryQuery;
abstract class AbstractMatchCountries extends ConditionAbstract
{
/** Condition 1st parameter : quantity */
CONST COUNTRIES_LIST = 'countries';
const COUNTRIES_LIST = 'countries';
/**
* @inheritdoc
@@ -70,7 +70,8 @@ abstract class AbstractMatchCountries extends ConditionAbstract
// Check that at least one category is selected
if (empty($values[self::COUNTRIES_LIST])) {
throw new InvalidConditionValueException(
get_class(), self::COUNTRIES_LIST
get_class(),
self::COUNTRIES_LIST
);
}
@@ -102,7 +103,8 @@ abstract class AbstractMatchCountries extends ConditionAbstract
public function getSummary()
{
$i18nOperator = Operators::getI18n(
$this->translator, $this->operators[self::COUNTRIES_LIST]
$this->translator,
$this->operators[self::COUNTRIES_LIST]
);
$cntryStrList = '';
@@ -110,10 +112,9 @@ abstract class AbstractMatchCountries extends ConditionAbstract
$cntryIds = $this->values[self::COUNTRIES_LIST];
if (null !== $cntryList = CountryQuery::create()->findPks($cntryIds)) {
/** @var Country $cntry */
foreach ($cntryList as $cntry) {
$cntryStrList .= $cntry->getTitle() . ', ';
$cntryStrList .= $cntry->setLocale($this->getCurrentLocale())->getTitle() . ', ';
}
$cntryStrList = rtrim($cntryStrList, ', ');
@@ -141,7 +142,9 @@ abstract class AbstractMatchCountries extends ConditionAbstract
*/
public function drawBackOfficeInputs()
{
return $this->facade->getParser()->render('coupon/condition-fragments/countries-condition.html', [
return $this->facade->getParser()->render(
'coupon/condition-fragments/countries-condition.html',
[
'operatorSelectHtml' => $this->drawBackOfficeInputOperators(self::COUNTRIES_LIST),
'countries_field_name' => self::COUNTRIES_LIST,
'values' => isset($this->values[self::COUNTRIES_LIST]) ? $this->values[self::COUNTRIES_LIST] : array(),

View File

@@ -29,7 +29,7 @@ use Thelia\Model\CategoryQuery;
class CartContainsCategories extends ConditionAbstract
{
/** Condition 1st parameter : quantity */
CONST CATEGORIES_LIST = 'categories';
const CATEGORIES_LIST = 'categories';
/**
* @inheritdoc
@@ -75,7 +75,8 @@ class CartContainsCategories extends ConditionAbstract
// Check that at least one category is selected
if (empty($values[self::CATEGORIES_LIST])) {
throw new InvalidConditionValueException(
get_class(), self::CATEGORIES_LIST
get_class(),
self::CATEGORIES_LIST
);
}
@@ -94,23 +95,22 @@ class CartContainsCategories extends ConditionAbstract
/** @var CartItem $cartItem */
foreach ($cartItems as $cartItem) {
$categories = $cartItem->getProduct()->getCategories();
/** @var Category $category */
foreach ($categories as $category) {
$catecoryInCart = $this->conditionValidator->variableOpComparison(
if (! $this->conditionValidator->variableOpComparison(
$category->getId(),
$this->operators[self::CATEGORIES_LIST],
$this->values[self::CATEGORIES_LIST]
);
if ($catecoryInCart) {
return true;
)) {
// cart item doesn't match go to next cart item
continue 2;
}
}
}
// cart item match
return true;
}
return false;
}
@@ -144,7 +144,8 @@ class CartContainsCategories extends ConditionAbstract
public function getSummary()
{
$i18nOperator = Operators::getI18n(
$this->translator, $this->operators[self::CATEGORIES_LIST]
$this->translator,
$this->operators[self::CATEGORIES_LIST]
);
$catStrList = '';
@@ -152,17 +153,17 @@ class CartContainsCategories extends ConditionAbstract
$catIds = $this->values[self::CATEGORIES_LIST];
if (null !== $catList = CategoryQuery::create()->findPks($catIds)) {
/** @var Category $cat */
foreach ($catList as $cat) {
$catStrList .= $cat->getTitle() . ', ';
$catStrList .= $cat->setLocale($this->getCurrentLocale())->getTitle() . ', ';
}
$catStrList = rtrim($catStrList, ', ');
}
$toolTip = $this->translator->trans(
'At least one of cart products categories is %op% <strong>%categories_list%</strong>', [
'At least one of cart products categories is %op% <strong>%categories_list%</strong>',
[
'%categories_list%' => $catStrList,
'%op%' => $i18nOperator
]
@@ -190,7 +191,9 @@ class CartContainsCategories extends ConditionAbstract
*/
public function drawBackOfficeInputs()
{
return $this->facade->getParser()->render('coupon/condition-fragments/cart-contains-categories-condition.html', [
return $this->facade->getParser()->render(
'coupon/condition-fragments/cart-contains-categories-condition.html',
[
'operatorSelectHtml' => $this->drawBackOfficeInputOperators(self::CATEGORIES_LIST),
'categories_field_name' => self::CATEGORIES_LIST,
'values' => isset($this->values[self::CATEGORIES_LIST]) ? $this->values[self::CATEGORIES_LIST] : array()

View File

@@ -75,7 +75,8 @@ class CartContainsProducts extends ConditionAbstract
// Check that at least one product is selected
if (empty($values[self::PRODUCTS_LIST])) {
throw new InvalidConditionValueException(
get_class(), self::PRODUCTS_LIST
get_class(),
self::PRODUCTS_LIST
);
}
@@ -94,15 +95,14 @@ class CartContainsProducts extends ConditionAbstract
/** @var CartItem $cartItem */
foreach ($cartItems as $cartItem) {
if ($this->conditionValidator->variableOpComparison(
$cartItem->getProduct()->getId(),
$this->operators[self::PRODUCTS_LIST],
$this->values[self::PRODUCTS_LIST])) {
return true;
$cartItem->getProduct()->getId(),
$this->operators[self::PRODUCTS_LIST],
$this->values[self::PRODUCTS_LIST]
)) {
return true;
}
}
return false;
}
@@ -136,7 +136,8 @@ class CartContainsProducts extends ConditionAbstract
public function getSummary()
{
$i18nOperator = Operators::getI18n(
$this->translator, $this->operators[self::PRODUCTS_LIST]
$this->translator,
$this->operators[self::PRODUCTS_LIST]
);
$prodStrList = '';
@@ -144,17 +145,17 @@ class CartContainsProducts extends ConditionAbstract
$prodIds = $this->values[self::PRODUCTS_LIST];
if (null !== $prodList = ProductQuery::create()->findPks($prodIds)) {
/** @var Product $prod */
foreach ($prodList as $prod) {
$prodStrList .= $prod->getTitle() . ', ';
$prodStrList .= $prod->setLocale($this->getCurrentLocale())->getTitle() . ', ';
}
$prodStrList = rtrim($prodStrList, ', ');
}
$toolTip = $this->translator->trans(
'Cart contains at least a product %op% <strong>%products_list%</strong>', [
'Cart contains at least a product %op% <strong>%products_list%</strong>',
[
'%products_list%' => $prodStrList,
'%op%' => $i18nOperator
]
@@ -182,7 +183,9 @@ class CartContainsProducts extends ConditionAbstract
*/
public function drawBackOfficeInputs()
{
return $this->facade->getParser()->render('coupon/condition-fragments/cart-contains-products-condition.html', [
return $this->facade->getParser()->render(
'coupon/condition-fragments/cart-contains-products-condition.html',
[
'operatorSelectHtml' => $this->drawBackOfficeInputOperators(self::PRODUCTS_LIST),
'products_field_name' => self::PRODUCTS_LIST,
'values' => isset($this->values[self::PRODUCTS_LIST]) ? $this->values[self::PRODUCTS_LIST] : array()

View File

@@ -82,7 +82,8 @@ abstract class ConditionAbstract implements ConditionInterface
if (!$isOperator1Legit) {
throw new InvalidConditionOperatorException(
get_class(), $parameterName
get_class(),
$parameterName
);
}
@@ -101,7 +102,6 @@ abstract class ConditionAbstract implements ConditionInterface
$translatedInputs = [];
foreach ($this->validators as $key => $validator) {
$translatedOperators = [];
foreach ($validator['availableOperators'] as $availableOperators) {
@@ -162,7 +162,7 @@ abstract class ConditionAbstract implements ConditionInterface
*/
protected function isOperatorLegit($operator, array $availableOperators)
{
return in_array($operator, $availableOperators);
return in_array($operator, $availableOperators);
}
/**
@@ -201,7 +201,8 @@ abstract class ConditionAbstract implements ConditionInterface
}
if (!$currencyFound) {
throw new InvalidConditionValueException(
get_class(), 'currency'
get_class(),
'currency'
);
}
@@ -221,7 +222,8 @@ abstract class ConditionAbstract implements ConditionInterface
$floatType = new FloatType();
if (!$floatType->isValid($priceValue) || $priceValue <= 0) {
throw new InvalidConditionValueException(
get_class(), 'price'
get_class(),
'price'
);
}
@@ -243,8 +245,9 @@ abstract class ConditionAbstract implements ConditionInterface
$inputs = $this->getValidators();
if (isset($inputs['inputs'][$inputKey])) {
$html = $this->facade->getParser()->render('coupon/condition-fragments/condition-selector.html', [
$html = $this->facade->getParser()->render(
'coupon/condition-fragments/condition-selector.html',
[
'operators' => $inputs['inputs'][$inputKey]['availableOperators'],
'value' => isset($this->operators[$inputKey]) ? $this->operators[$inputKey] : '',
'inputKey' => $inputKey
@@ -273,7 +276,9 @@ abstract class ConditionAbstract implements ConditionInterface
$currentValue = $this->values[$inputKey];
}
return $this->facade->getParser()->render('coupon/conditions-fragments/base-input-text.html', [
return $this->facade->getParser()->render(
'coupon/conditions-fragments/base-input-text.html',
[
'label' => $label,
'inputKey' => $inputKey,
'currentValue' => $currentValue,
@@ -294,7 +299,9 @@ abstract class ConditionAbstract implements ConditionInterface
*/
protected function drawBackOfficeInputQuantityValues($inputKey, $max = 10, $min = 0)
{
return $this->facade->getParser()->render('coupon/condition-fragments/quantity-selector.html', [
return $this->facade->getParser()->render(
'coupon/condition-fragments/quantity-selector.html',
[
'min' => $min,
'max' => $max,
'value' => isset($this->values[$inputKey]) ? $this->values[$inputKey] : '',
@@ -322,11 +329,23 @@ abstract class ConditionAbstract implements ConditionInterface
$cleanedCurrencies[$currency->getCode()] = $currency->getSymbol();
}
return $this->facade->getParser()->render('coupon/condition-fragments/currency-selector.html', [
return $this->facade->getParser()->render(
'coupon/condition-fragments/currency-selector.html',
[
'currencies' => $cleanedCurrencies,
'value' => isset($this->values[$inputKey]) ? $this->values[$inputKey] : '',
'inputKey' => $inputKey
]
);
}
/**
* A helper to het the current locale.
*
* @return string the current locale.
*/
protected function getCurrentLocale()
{
return $this->facade->getRequest()->getSession()->getLang()->getLocale();
}
}

View File

@@ -15,6 +15,7 @@ namespace Thelia\Condition\Implementation;
use Thelia\Condition\Operators;
use Thelia\Coupon\FacadeInterface;
use Thelia\Exception\InvalidConditionValueException;
use Thelia\Exception\UnmatchableConditionException;
use Thelia\Model\Customer;
use Thelia\Model\CustomerQuery;
@@ -73,7 +74,8 @@ class ForSomeCustomers extends ConditionAbstract
// Check that at least one product is selected
if (empty($values[self::CUSTOMERS_LIST])) {
throw new InvalidConditionValueException(
get_class(), self::CUSTOMERS_LIST
get_class(),
self::CUSTOMERS_LIST
);
}
@@ -88,13 +90,15 @@ class ForSomeCustomers extends ConditionAbstract
*/
public function isMatching()
{
$customer = $this->facade->getCustomer();
if (null === $customer = $this->facade->getCustomer()) {
throw new UnmatchableConditionException();
}
return $this->conditionValidator->variableOpComparison(
$customer->getId(),
$this->operators[self::CUSTOMERS_LIST],
$this->values[self::CUSTOMERS_LIST]
);
return $this->conditionValidator->variableOpComparison(
$customer->getId(),
$this->operators[self::CUSTOMERS_LIST],
$this->values[self::CUSTOMERS_LIST]
);
}
/**
@@ -127,7 +131,8 @@ class ForSomeCustomers extends ConditionAbstract
public function getSummary()
{
$i18nOperator = Operators::getI18n(
$this->translator, $this->operators[self::CUSTOMERS_LIST]
$this->translator,
$this->operators[self::CUSTOMERS_LIST]
);
$custStrList = '';
@@ -135,7 +140,6 @@ class ForSomeCustomers extends ConditionAbstract
$custIds = $this->values[self::CUSTOMERS_LIST];
if (null !== $custList = CustomerQuery::create()->findPks($custIds)) {
/** @var Customer $cust */
foreach ($custList as $cust) {
$custStrList .= $cust->getLastname() . ' ' . $cust->getFirstname() . ' ('.$cust->getRef().'), ';
@@ -145,7 +149,8 @@ class ForSomeCustomers extends ConditionAbstract
}
$toolTip = $this->translator->trans(
'Customer is %op% <strong>%customer_list%</strong>', [
'Customer is %op% <strong>%customer_list%</strong>',
[
'%customer_list%' => $custStrList,
'%op%' => $i18nOperator
]
@@ -173,7 +178,9 @@ class ForSomeCustomers extends ConditionAbstract
*/
public function drawBackOfficeInputs()
{
return $this->facade->getParser()->render('coupon/condition-fragments/customers-condition.html', [
return $this->facade->getParser()->render(
'coupon/condition-fragments/customers-condition.html',
[
'operatorSelectHtml' => $this->drawBackOfficeInputOperators(self::CUSTOMERS_LIST),
'customers_field_name' => self::CUSTOMERS_LIST,
'values' => isset($this->values[self::CUSTOMERS_LIST]) ? $this->values[self::CUSTOMERS_LIST] : array()

View File

@@ -12,6 +12,8 @@
namespace Thelia\Condition\Implementation;
use Thelia\Exception\UnmatchableConditionException;
/**
* Check a Checkout against its Product number
*
@@ -34,7 +36,11 @@ class MatchBillingCountries extends AbstractMatchCountries
*/
public function isMatching()
{
$billingAddress = $this->facade->getCustomer()->getDefaultAddress();
if (null === $customer = $this->facade->getCustomer()) {
throw new UnmatchableConditionException();
}
$billingAddress = $customer->getDefaultAddress();
return $this->conditionValidator->variableOpComparison(
$billingAddress->getCountryId(),
@@ -70,7 +76,8 @@ class MatchBillingCountries extends AbstractMatchCountries
protected function getSummaryLabel($cntryStrList, $i18nOperator)
{
return $this->translator->trans(
'Only if order billing country is %op% <strong>%countries_list%</strong>', [
'Only if order billing country is %op% <strong>%countries_list%</strong>',
[
'%countries_list%' => $cntryStrList,
'%op%' => $i18nOperator
]
@@ -80,7 +87,8 @@ class MatchBillingCountries extends AbstractMatchCountries
protected function getFormLabel()
{
return $this->translator->trans(
'Billing country is', []
'Billing country is',
[]
);
}
}

View File

@@ -12,6 +12,8 @@
namespace Thelia\Condition\Implementation;
use Thelia\Exception\UnmatchableConditionException;
/**
* Check a Checkout against its Product number
*
@@ -34,7 +36,13 @@ class MatchDeliveryCountries extends AbstractMatchCountries
*/
public function isMatching()
{
$deliveryAddress = $this->facade->getDeliveryAddress();
if (null === $customer = $this->facade->getCustomer()) {
throw new UnmatchableConditionException();
}
if (null === $deliveryAddress = $this->facade->getDeliveryAddress()) {
throw new UnmatchableConditionException();
}
return $this->conditionValidator->variableOpComparison(
$deliveryAddress->getCountryId(),
@@ -70,7 +78,8 @@ class MatchDeliveryCountries extends AbstractMatchCountries
protected function getSummaryLabel($cntryStrList, $i18nOperator)
{
return $this->translator->trans(
'Only if order shipping country is %op% <strong>%countries_list%</strong>', [
'Only if order shipping country is %op% <strong>%countries_list%</strong>',
[
'%countries_list%' => $cntryStrList,
'%op%' => $i18nOperator
]
@@ -80,7 +89,8 @@ class MatchDeliveryCountries extends AbstractMatchCountries
protected function getFormLabel()
{
return $this->translator->trans(
'Delivery country is', []
'Delivery country is',
[]
);
}
}

View File

@@ -114,5 +114,4 @@ class MatchForEveryone extends ConditionAbstract
// No input
return '';
}
}

View File

@@ -28,10 +28,10 @@ use Thelia\Model\CurrencyQuery;
class MatchForTotalAmount extends ConditionAbstract
{
/** Condition 1st parameter : price */
CONST CART_TOTAL = 'price';
const CART_TOTAL = 'price';
/** Condition 1st parameter : currency */
CONST CART_CURRENCY = 'currency';
const CART_CURRENCY = 'currency';
public function __construct(FacadeInterface $facade)
{
@@ -164,7 +164,7 @@ class MatchForTotalAmount extends ConditionAbstract
*/
protected function generateInputs()
{
$currencies = CurrencyQuery::create()->find();
$currencies = CurrencyQuery::create()->filterByVisible(true)->find();
$cleanedCurrencies = [];

View File

@@ -26,7 +26,7 @@ use Thelia\Exception\InvalidConditionValueException;
class MatchForXArticles extends ConditionAbstract
{
/** Condition 1st parameter : quantity */
CONST CART_QUANTITY = 'quantity';
const CART_QUANTITY = 'quantity';
/**
* @inheritdoc
@@ -63,7 +63,8 @@ class MatchForXArticles extends ConditionAbstract
if (intval($values[self::CART_QUANTITY]) <= 0) {
throw new InvalidConditionValueException(
get_class(), 'quantity'
get_class(),
'quantity'
);
}
@@ -126,7 +127,8 @@ class MatchForXArticles extends ConditionAbstract
public function getSummary()
{
$i18nOperator = Operators::getI18n(
$this->translator, $this->operators[self::CART_QUANTITY]
$this->translator,
$this->operators[self::CART_QUANTITY]
);
$toolTip = $this->translator->trans(
@@ -173,7 +175,9 @@ class MatchForXArticles extends ConditionAbstract
*/
protected function drawBackOfficeBaseInputsText($label, $inputKey)
{
return $this->facade->getParser()->render('coupon/condition-fragments/cart-item-count-condition.html', [
return $this->facade->getParser()->render(
'coupon/condition-fragments/cart-item-count-condition.html',
[
'label' => $label,
'operatorSelectHtml' => $this->drawBackOfficeInputOperators($inputKey),
'quantitySelectHtml' => $this->drawBackOfficeInputQuantityValues($inputKey, 20, 1)

Some files were not shown because too many files have changed in this diff Show More