Initial commit

This commit is contained in:
2021-03-23 13:54:38 +01:00
commit 82b142ff95
16941 changed files with 2617212 additions and 0 deletions

View File

@@ -0,0 +1,108 @@
<?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\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;
use Thelia\Core\Event\TheliaEvents;
use Thelia\Model\Address as AddressModel;
use Thelia\Model\Map\AddressTableMap;
/**
* Class Address
* @package Thelia\Action
* @author Manuel Raynaud <manu@raynaud.io>
*/
class Address extends BaseAction implements EventSubscriberInterface
{
public function create(AddressCreateOrUpdateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$address = new AddressModel();
$address->setCustomer($event->getCustomer());
$this->createOrUpdate($address, $event, $dispatcher);
}
public function update(AddressCreateOrUpdateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$addressModel = $event->getAddress();
$this->createOrUpdate($addressModel, $event, $dispatcher);
}
public function delete(AddressEvent $event)
{
$address = $event->getAddress();
$address->delete();
}
public function useDefault(AddressEvent $event)
{
$address = $event->getAddress();
$address->makeItDefault();
}
protected function createOrUpdate(AddressModel $addressModel, AddressCreateOrUpdateEvent $event, $dispatcher)
{
$addressModel->setDispatcher($dispatcher);
$con = Propel::getWriteConnection(AddressTableMap::DATABASE_NAME);
$con->beginTransaction();
try {
$addressModel
->setLabel($event->getLabel())
->setTitleId($event->getTitle())
->setFirstname($event->getFirstname())
->setLastname($event->getLastname())
->setAddress1($event->getAddress1())
->setAddress2($event->getAddress2())
->setAddress3($event->getAddress3())
->setZipcode($event->getZipcode())
->setCity($event->getCity())
->setCountryId($event->getCountry())
->setStateId($event->getState())
->setCellphone($event->getCellphone())
->setPhone($event->getPhone())
->setCompany($event->getCompany())
->save()
;
if ($event->getIsDefault() && !$addressModel->getIsDefault()) {
$addressModel->makeItDefault();
}
$event->setAddress($addressModel);
$con->commit();
} catch (PropelException $e) {
$con->rollback();
throw $e;
}
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents()
{
return array(
TheliaEvents::ADDRESS_CREATE => array("create", 128),
TheliaEvents::ADDRESS_UPDATE => array("update", 128),
TheliaEvents::ADDRESS_DELETE => array("delete", 128),
TheliaEvents::ADDRESS_DEFAULT => array('useDefault', 128),
);
}
}

View File

@@ -0,0 +1,155 @@
<?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\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, $eventName, EventDispatcherInterface $dispatcher)
{
$administrator = new AdminModel();
$administrator
->setDispatcher($dispatcher)
->setFirstname($event->getFirstname())
->setLastname($event->getLastname())
->setEmail($event->getEmail())
->setLogin($event->getLogin())
->setPassword($event->getPassword())
->setProfileId($event->getProfile())
->setLocale($event->getLocale())
;
$administrator->save();
$event->setAdministrator($administrator);
}
/**
* @param AdministratorEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function update(AdministratorEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if (null !== $administrator = AdminQuery::create()->findPk($event->getId())) {
$administrator
->setDispatcher($dispatcher)
->setFirstname($event->getFirstname())
->setLastname($event->getLastname())
->setLogin($event->getLogin())
->setEmail($event->getEmail())
->setProfileId($event->getProfile())
->setLocale($event->getLocale())
;
if ('' !== $event->getPassword()) {
$administrator->setPassword($event->getPassword());
}
$administrator->save();
$event->setAdministrator($administrator);
}
}
/**
* @param AdministratorEvent $event
*/
public function delete(AdministratorEvent $event)
{
if (null !== $administrator = AdminQuery::create()->findPk($event->getId())) {
$administrator
->delete()
;
$event->setAdministrator($administrator);
}
}
public function updatePassword(AdministratorUpdatePasswordEvent $event)
{
$admin = $event->getAdmin();
$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}
*/
public static function getSubscribedEvents()
{
return array(
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_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

@@ -0,0 +1,137 @@
<?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\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\Area as AreaModel;
use Thelia\Model\AreaQuery;
use Thelia\Model\CountryArea;
use Thelia\Model\CountryAreaQuery;
/**
* Class Area
* @package Thelia\Action
* @author Manuel Raynaud <manu@raynaud.io>
*/
class Area extends BaseAction implements EventSubscriberInterface
{
public function addCountry(AreaAddCountryEvent $event)
{
$countryIds = $event->getCountryId();
$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)
{
CountryAreaQuery::create()
->filterByCountryId($event->getCountryId())
->filterByStateId($event->getStateId())
->filterByAreaId($event->getAreaId())
->delete();
if (null !== $area = AreaQuery::create()->findPk($event->getAreaId())) {
$event->setArea($area);
}
}
public function updatePostage(AreaUpdatePostageEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if (null !== $area = AreaQuery::create()->findPk($event->getAreaId())) {
$area->setDispatcher($dispatcher);
$area
->setPostage($event->getPostage())
->save();
$event->setArea($area);
}
}
public function delete(AreaDeleteEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if (null !== $area = AreaQuery::create()->findPk($event->getAreaId())) {
$area->setDispatcher($dispatcher);
$area->delete();
$event->setArea($area);
}
}
public function create(AreaCreateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$area = new AreaModel();
$area
->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);
}
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents()
{
return array(
TheliaEvents::AREA_ADD_COUNTRY => array('addCountry', 128),
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_UPDATE => array('update', 128)
);
}
}

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\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;
use Thelia\Core\Event\UpdatePositionEvent;
use Thelia\Core\Event\Attribute\AttributeEvent;
use Thelia\Model\AttributeTemplate;
use Thelia\Model\AttributeTemplateQuery;
use Thelia\Model\TemplateQuery;
class Attribute extends BaseAction implements EventSubscriberInterface
{
/**
* Create a new attribute entry
*
* @param AttributeCreateEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function create(AttributeCreateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$attribute = new AttributeModel();
$attribute
->setDispatcher($dispatcher)
->setLocale($event->getLocale())
->setTitle($event->getTitle())
->save()
;
$event->setAttribute($attribute);
// Add atribute to all product templates if required
if ($event->getAddToAllTemplates() != 0) {
$this->doAddToAllTemplates($attribute);
}
}
/**
* Change a product attribute
*
* @param AttributeUpdateEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function update(AttributeUpdateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if (null !== $attribute = AttributeQuery::create()->findPk($event->getAttributeId())) {
$attribute
->setDispatcher($dispatcher)
->setLocale($event->getLocale())
->setTitle($event->getTitle())
->setDescription($event->getDescription())
->setChapo($event->getChapo())
->setPostscriptum($event->getPostscriptum())
->save();
$event->setAttribute($attribute);
}
}
/**
* Delete a product attribute entry
*
* @param AttributeDeleteEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function delete(AttributeDeleteEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if (null !== ($attribute = AttributeQuery::create()->findPk($event->getAttributeId()))) {
$attribute
->setDispatcher($dispatcher)
->delete()
;
$event->setAttribute($attribute);
}
}
/**
* 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(AttributeQuery::create(), $event, $dispatcher);
}
protected function doAddToAllTemplates(AttributeModel $attribute)
{
$templates = TemplateQuery::create()->find();
foreach ($templates as $template) {
$attribute_template = new AttributeTemplate();
if (null === AttributeTemplateQuery::create()->filterByAttribute($attribute)->filterByTemplate($template)->findOne()) {
$attribute_template
->setAttribute($attribute)
->setTemplate($template)
->save()
;
}
}
}
public function addToAllTemplates(AttributeEvent $event)
{
$this->doAddToAllTemplates($event->getAttribute());
}
public function removeFromAllTemplates(AttributeEvent $event)
{
// Delete this attribute from all product templates
AttributeTemplateQuery::create()->filterByAttribute($event->getAttribute())->delete();
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents()
{
return array(
TheliaEvents::ATTRIBUTE_CREATE => array("create", 128),
TheliaEvents::ATTRIBUTE_UPDATE => array("update", 128),
TheliaEvents::ATTRIBUTE_DELETE => array("delete", 128),
TheliaEvents::ATTRIBUTE_UPDATE_POSITION => array("updatePosition", 128),
TheliaEvents::ATTRIBUTE_REMOVE_FROM_ALL_TEMPLATES => array("removeFromAllTemplates", 128),
TheliaEvents::ATTRIBUTE_ADD_TO_ALL_TEMPLATES => array("addToAllTemplates", 128),
);
}
}

View File

@@ -0,0 +1,119 @@
<?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\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;
use Thelia\Core\Event\UpdatePositionEvent;
class AttributeAv extends BaseAction implements EventSubscriberInterface
{
/**
* Create a new attribute entry
*
* @param AttributeAvCreateEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function create(AttributeAvCreateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$attribute = new AttributeAvModel();
$attribute
->setDispatcher($dispatcher)
->setAttributeId($event->getAttributeId())
->setLocale($event->getLocale())
->setTitle($event->getTitle())
->save()
;
$event->setAttributeAv($attribute);
}
/**
* Change a product attribute
*
* @param AttributeAvUpdateEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function update(AttributeAvUpdateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if (null !== $attribute = AttributeAvQuery::create()->findPk($event->getAttributeAvId())) {
$attribute
->setDispatcher($dispatcher)
->setLocale($event->getLocale())
->setTitle($event->getTitle())
->setDescription($event->getDescription())
->setChapo($event->getChapo())
->setPostscriptum($event->getPostscriptum())
->save();
$event->setAttributeAv($attribute);
}
}
/**
* Delete a product attribute entry
*
* @param AttributeAvDeleteEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function delete(AttributeAvDeleteEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if (null !== ($attribute = AttributeAvQuery::create()->findPk($event->getAttributeAvId()))) {
$attribute
->setDispatcher($dispatcher)
->delete()
;
$event->setAttributeAv($attribute);
}
}
/**
* 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(AttributeAvQuery::create(), $event);
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents()
{
return array(
TheliaEvents::ATTRIBUTE_AV_CREATE => array("create", 128),
TheliaEvents::ATTRIBUTE_AV_UPDATE => array("update", 128),
TheliaEvents::ATTRIBUTE_AV_DELETE => array("delete", 128),
TheliaEvents::ATTRIBUTE_AV_UPDATE_POSITION => array("updatePosition", 128),
);
}
}

View File

@@ -0,0 +1,146 @@
<?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\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
{
/**
* Changes object position, selecting absolute ou relative change.
*
* @param ModelCriteria $query
* @param UpdatePositionEvent $event
* @param EventDispatcherInterface $dispatcher
*
* @return null
*/
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($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();
}
}
}
/**
* @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();
}
}
}
/**
* Changes SEO Fields for an object.
*
* @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, EventDispatcherInterface $dispatcher = null)
{
if (null !== $object = $query->findPk($event->getObjectId())) {
$object
//for backward compatibility
->setDispatcher($dispatcher !== null ? $dispatcher : $event->getDispatcher())
->setLocale($event->getLocale())
->setMetaTitle($event->getMetaTitle())
->setMetaDescription($event->getMetaDescription())
->setMetaKeywords($event->getMetaKeywords())
->save()
;
// Update the rewritten URL, if required
try {
$object->setRewrittenUrl($event->getLocale(), $event->getUrl());
} catch (UrlRewritingException $e) {
throw new FormValidationException($e->getMessage(), $e->getCode());
}
$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

@@ -0,0 +1,276 @@
<?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\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;
/**
*
* Cached file management actions. This class handles file caching in the web space
*
* Basically, files are stored outside the web space (by default in local/media/<dirname>),
* and cached in the web space (by default in web/local/<dirname>).
*
* In the file cache directory, a subdirectory for files categories (eg. product, category, folder, etc.) is
* automatically created, and the cached file is created here. Plugin may use their own subdirectory as required.
*
* A copy (or symbolic link, by default) of the original file is created in the cache.
*
* @package Thelia\Action
* @author Franck Allimant <franck@cqfdev.fr>
*
*/
abstract class BaseCachedFile extends BaseAction
{
/**
* @var FileManager
*/
protected $fileManager;
public function __construct(FileManager $fileManager)
{
$this->fileManager = $fileManager;
}
/**
* @return string root of the file cache directory in web space
*/
abstract protected function getCacheDirFromWebRoot();
/**
* Clear the file cache. Is a subdirectory is specified, only this directory is cleared.
* If no directory is specified, the whole cache is cleared.
* Only files are deleted, directories will remain.
*
* @param CachedFileEvent $event
*/
public function clearCache(CachedFileEvent $event)
{
$path = $this->getCachePath($event->getCacheSubdirectory(), false);
$this->clearDirectory($path);
}
/**
* Recursively clears the specified directory.
*
* @param string $path the directory path
*/
protected function clearDirectory($path)
{
$iterator = new \DirectoryIterator($path);
/** @var \DirectoryIterator $fileinfo */
foreach ($iterator as $fileinfo) {
if ($fileinfo->isDot()) {
continue;
}
if ($fileinfo->isFile() || $fileinfo->isLink()) {
@unlink($fileinfo->getPathname());
} elseif ($fileinfo->isDir()) {
$this->clearDirectory($fileinfo->getPathname());
}
}
}
/**
* Return the absolute URL to the cached file
*
* @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
*/
protected function getCacheFileURL($subdir, $safe_filename)
{
$path = $this->getCachePathFromWebRoot($subdir);
return URL::getInstance()->absoluteUrl(sprintf("%s/%s", $path, $safe_filename), null, URL::PATH_TO_FILE);
}
/**
* Return the full path of the cached file
*
* @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
* @return string the cache directory path relative to Web Root
*/
protected function getCacheFilePath($subdir, $filename, $forceOriginalFile = false, $hashed_options = null)
{
$path = $this->getCachePath($subdir);
$safe_filename = preg_replace("[^:alnum:\-\._]", "-", strtolower(basename($filename)));
// Keep original safe name if no tranformations are applied
if ($forceOriginalFile || $hashed_options == null) {
return sprintf("%s/%s", $path, $safe_filename);
} else {
return sprintf("%s/%s-%s", $path, $hashed_options, $safe_filename);
}
}
/**
* Return the cache directory path relative to Web Root
*
* @param string $subdir the subdirectory related to cache base, or null to get the cache directory only.
* @return string the cache directory path relative to Web Root
*/
protected function getCachePathFromWebRoot($subdir = null)
{
$cache_dir_from_web_root = $this->getCacheDirFromWebRoot();
if ($subdir != null) {
$safe_subdir = basename($subdir);
$path = sprintf("%s/%s", $cache_dir_from_web_root, $safe_subdir);
} else {
$path = $cache_dir_from_web_root;
}
// Check if path is valid, e.g. in the cache dir
return $path;
}
/**
* 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
*
* @throws \RuntimeException if cache directory cannot be created
* @throws \InvalidArgumentException ii path is invalid, e.g. not in the cache dir
*
* @return string the absolute cache directory path
*/
protected function getCachePath($subdir = null, $create_if_not_exists = true)
{
$cache_base = $this->getCachePathFromWebRoot($subdir);
$web_root = rtrim(THELIA_WEB_DIR, '/');
$path = sprintf("%s/%s", $web_root, $cache_base);
// 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));
}
}
// Check if path is valid, e.g. in the cache dir
$cache_base = realpath(sprintf("%s/%s", $web_root, $this->getCachePathFromWebRoot()));
if (strpos(realpath($path), $cache_base) !== 0) {
throw new \InvalidArgumentException(sprintf("Invalid cache path %s, with subdirectory %s", $path, $subdir));
}
return $path;
}
/**
* Take care of saving a file in the database and file storage
*
* @param FileCreateOrUpdateEvent $event Image event
*
* @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();
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()
)
);
}
$newUploadedFile = $this->fileManager->copyUploadedFile($event->getModel(), $event->getUploadedFile());
$event->setUploadedFile($newUploadedFile);
$con->commit();
} catch (\Exception $e) {
$con->rollBack();
throw $e;
}
}
/**
* Take care of updating file in the database and file storage
*
* @param FileCreateOrUpdateEvent $event Image event
*
* @throws \Thelia\Exception\FileException
*/
public function updateFile(FileCreateOrUpdateEvent $event)
{
// Copy and save file
if ($event->getUploadedFile()) {
// Remove old picture file from file storage
$url = $event->getModel()->getUploadDir() . '/' . $event->getOldModel()->getFile();
unlink(str_replace('..', '', $url));
$newUploadedFile = $this->fileManager->copyUploadedFile($event->getModel(), $event->getUploadedFile());
$event->setUploadedFile($newUploadedFile);
}
// Update image modifications
$event->getModel()->save();
$event->setModel($event->getModel());
}
/**
* Deleting file in the database and in storage
*
* @param FileDeleteEvent $event Image event
*/
public function deleteFile(FileDeleteEvent $event)
{
$this->fileManager->deleteFile($event->getFileToDelete());
}
public function updatePosition(UpdateFilePositionEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$this->genericUpdatePosition($event->getQuery(), $event, $dispatcher);
}
public function toggleVisibility(FileToggleVisibilityEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$this->genericToggleVisibility($event->getQuery(), $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\HttpKernel\Exception\NotFoundHttpException;
use Thelia\Core\Event\Brand\BrandCreateEvent;
use Thelia\Core\Event\Brand\BrandDeleteEvent;
use Thelia\Core\Event\Brand\BrandToggleVisibilityEvent;
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;
/**
* Class Brand
*
* @package Thelia\Action
* @author Franck Allimant <franck@cqfdev.fr>
*/
class Brand extends BaseAction implements EventSubscriberInterface
{
public function create(BrandCreateEvent $event)
{
$brand = new BrandModel();
$brand
->setVisible($event->getVisible())
->setLocale($event->getLocale())
->setTitle($event->getTitle())
->save()
;
$event->setBrand($brand);
}
/**
* process update brand
*
* @param BrandUpdateEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function update(BrandUpdateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if (null !== $brand = BrandQuery::create()->findPk($event->getBrandId())) {
$brand->setDispatcher($dispatcher);
$brand
->setVisible($event->getVisible())
->setLogoImageId(intval($event->getLogoImageId()) == 0 ? null : $event->getLogoImageId())
->setLocale($event->getLocale())
->setTitle($event->getTitle())
->setDescription($event->getDescription())
->setChapo($event->getChapo())
->setPostscriptum($event->getPostscriptum())
->save()
;
$event->setBrand($brand);
}
}
/**
* Toggle Brand visibility
*
* @param BrandToggleVisibilityEvent $event
* @param string $eventName
* @param EventDispatcherInterface $dispatcher
* @throws \Exception
* @throws \Propel\Runtime\Exception\PropelException
*/
public function toggleVisibility(BrandToggleVisibilityEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$brand = $event->getBrand();
$brand
->setDispatcher($dispatcher)
->setVisible(!$brand->getVisible())
->save();
$event->setBrand($brand);
}
/**
* Change Brand SEO
*
* @param UpdateSeoEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
* @return Object
*/
public function updateSeo(UpdateSeoEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
return $this->genericUpdateSeo(BrandQuery::create(), $event, $dispatcher);
}
public function delete(BrandDeleteEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if (null !== $brand = BrandQuery::create()->findPk($event->getBrandId())) {
$brand->setDispatcher($dispatcher)->delete();
$event->setBrand($brand);
}
}
public function updatePosition(UpdatePositionEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$this->genericUpdatePosition(BrandQuery::create(), $event, $dispatcher);
}
/**
* 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()
{
return array(
TheliaEvents::BRAND_CREATE => array('create', 128),
TheliaEvents::BRAND_UPDATE => array('update', 128),
TheliaEvents::BRAND_DELETE => array('delete', 128),
TheliaEvents::BRAND_UPDATE_SEO => array('updateSeo', 128),
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

@@ -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\Cache\Adapter\AdapterInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Filesystem\Filesystem;
use Thelia\Core\Event\Cache\CacheEvent;
use Thelia\Core\Event\TheliaEvents;
/**
* Class Cache
* @package Thelia\Action
* @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);
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents()
{
return array(
TheliaEvents::CACHE_CLEAR => array('cacheClear', 128)
);
}
}

View File

@@ -0,0 +1,550 @@
<?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\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\Customer as CustomerModel;
use Thelia\Model\ProductSaleElements;
use Thelia\Model\Tools\ProductPriceTools;
use Thelia\Tools\TokenProvider;
/**
*
* Class Cart where all actions are manage like adding, modifying or delete items.
*
* Class Cart
* @package Thelia\Action
* @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, $eventName, EventDispatcherInterface $dispatcher)
{
$cart = $event->getCart();
$newness = $event->getNewness();
$append = $event->getAppend();
$quantity = $event->getQuantity();
$currency = $cart->getCurrency();
$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();
}
$productSaleElementsId = $event->getProductSaleElementsId();
$productId = $event->getProduct();
// 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);
if (null !== $productSaleElements) {
$productPrices = $productSaleElements->getPricesByCurrency($currency, $discount);
$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();
}
$event->setCartItem($cartItem);
}
/**
*
* Delete specify article present into cart
*
* @param \Thelia\Core\Event\Cart\CartEvent $event
*/
public function deleteItem(CartEvent $event)
{
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();
}
}
/**
* Clear the cart
* @param CartEvent $event
*/
public function clear(CartEvent $event)
{
if (null !== $cart = $event->getCart()) {
$cart->delete();
}
}
/**
*
* Modify article's quantity
*
* 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, $eventName, EventDispatcherInterface $dispatcher)
{
if ((null !== $cartItemId = $event->getCartItemId()) && (null !== $quantity = $event->getQuantity())) {
$cart = $event->getCart();
$cartItem = CartItemQuery::create()
->filterByCartId($cart->getId())
->filterById($cartItemId)
->findOne();
if ($cartItem) {
$event->setCartItem(
$this->updateQuantity($dispatcher, $cartItem, $quantity)
);
}
}
}
public function updateCart(CurrencyChangeEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$cart = $event->getRequest()->getSession()->getSessionCart($dispatcher);
if (null !== $cart) {
$this->updateCartPrices($cart, $event->getCurrency());
}
}
/**
*
* Refresh article's price
*
* @param \Thelia\Model\Cart $cart
* @param \Thelia\Model\Currency $currency
*/
public function updateCartPrices(CartModel $cart, CurrencyModel $currency)
{
$customer = $cart->getCustomer();
$discount = 0;
if (null !== $customer && $customer->getDiscount() > 0) {
$discount = $customer->getDiscount();
}
// cart item
foreach ($cart->getCartItems() as $cartItem) {
$productSaleElements = $cartItem->getProductSaleElements();
$productPrice = $productSaleElements->getPricesByCurrency($currency, $discount);
$cartItem
->setPrice($productPrice->getPrice())
->setPromoPrice($productPrice->getPromoPrice());
$cartItem->save();
}
// update the currency cart
$cart->setCurrencyId($currency->getId());
$cart->save();
}
/**
* increase the quantity for an existing cartItem
*
* @param EventDispatcherInterface $dispatcher
* @param CartItem $cartItem
* @param float $quantity
*
* @throws \Exception
* @throws \Propel\Runtime\Exception\PropelException
* @return CartItem
*/
protected function updateQuantity(EventDispatcherInterface $dispatcher, CartItem $cartItem, $quantity)
{
$cartItem->setDisptacher($dispatcher);
$cartItem->updateQuantity($quantity)
->save();
return $cartItem;
}
/**
* try to attach a new item to an existing cart
*
* @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,
CartModel $cart,
$productId,
ProductSaleElements $productSaleElements,
$quantity,
ProductPriceTools $productPrices
) {
$cartItem = new CartItem();
$cartItem->setDisptacher($dispatcher);
$cartItem
->setCart($cart)
->setProductId($productId)
->setProductSaleElementsId($productSaleElements->getId())
->setQuantity($quantity)
->setPrice($productPrices->getPrice())
->setPromoPrice($productPrices->getPromoPrice())
->setPromo($productSaleElements->getPromo())
->setPriceEndOfLife(time() + ConfigQuery::read("cart.priceEOF", 60*60*24*30))
->save();
return $cartItem;
}
/**
* find a specific record in CartItem table using the Cart id, the product id
* and the product_sale_elements id
*
* @param int $cartId
* @param int $productId
* @param int $productSaleElementsId
* @return CartItem
*
* @deprecated this method is deprecated. Dispatch a TheliaEvents::CART_FINDITEM instead
*/
protected function findItem($cartId, $productId, $productSaleElementsId)
{
return CartItemQuery::create()
->filterByCartId($cartId)
->filterByProductId($productId)
->filterByProductSaleElementsId($productSaleElementsId)
->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

@@ -0,0 +1,268 @@
<?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\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;
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
{
/**
* Create a new category entry
*
* @param \Thelia\Core\Event\Category\CategoryCreateEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function create(CategoryCreateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$category = new CategoryModel();
$category
->setDispatcher($dispatcher)
->setLocale($event->getLocale())
->setParent($event->getParent())
->setVisible($event->getVisible())
->setTitle($event->getTitle())
->save()
;
$event->setCategory($category);
}
/**
* Change a category
*
* @param \Thelia\Core\Event\Category\CategoryUpdateEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function update(CategoryUpdateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if (null !== $category = CategoryQuery::create()->findPk($event->getCategoryId())) {
$category
->setDispatcher($dispatcher)
->setDefaultTemplateId($event->getDefaultTemplateId() == 0 ? null : $event->getDefaultTemplateId())
->setLocale($event->getLocale())
->setTitle($event->getTitle())
->setDescription($event->getDescription())
->setChapo($event->getChapo())
->setPostscriptum($event->getPostscriptum())
->setParent($event->getParent())
->setVisible($event->getVisible())
->save();
$event->setCategory($category);
}
}
/**
* Change a Category SEO
*
* @param UpdateSeoEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
* @return Object
*/
public function updateSeo(UpdateSeoEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
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, $eventName, EventDispatcherInterface $dispatcher)
{
if (null !== $category = CategoryQuery::create()->findPk($event->getCategoryId())) {
$con = Propel::getWriteConnection(CategoryTableMap::DATABASE_NAME);
$con->beginTransaction();
try {
$fileList = ['images' => [], 'documentList' => []];
// 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 CategoryToggleVisibilityEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function toggleVisibility(CategoryToggleVisibilityEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$category = $event->getCategory();
$category
->setDispatcher($dispatcher)
->setVisible($category->getVisible() ? false : true)
->save()
;
$event->setCategory($category);
}
/**
* 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(CategoryQuery::create(), $event, $dispatcher);
}
public function addContent(CategoryAddContentEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if (CategoryAssociatedContentQuery::create()
->filterByContentId($event->getContentId())
->filterByCategory($event->getCategory())->count() <= 0) {
$content = new CategoryAssociatedContent();
$content
->setDispatcher($dispatcher)
->setCategory($event->getCategory())
->setContentId($event->getContentId())
->save()
;
}
}
public function removeContent(CategoryDeleteContentEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$content = CategoryAssociatedContentQuery::create()
->filterByContentId($event->getContentId())
->filterByCategory($event->getCategory())->findOne()
;
if ($content !== null) {
$content
->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}
*/
public static function getSubscribedEvents()
{
return array(
TheliaEvents::CATEGORY_CREATE => array("create", 128),
TheliaEvents::CATEGORY_UPDATE => array("update", 128),
TheliaEvents::CATEGORY_DELETE => array("delete", 128),
TheliaEvents::CATEGORY_TOGGLE_VISIBILITY => array("toggleVisibility", 128),
TheliaEvents::CATEGORY_UPDATE_POSITION => array("updatePosition", 128),
TheliaEvents::CATEGORY_UPDATE_SEO => array("updateSeo", 128),
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

@@ -0,0 +1,128 @@
<?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\Config\ConfigCreateEvent;
use Thelia\Core\Event\Config\ConfigDeleteEvent;
use Thelia\Core\Event\Config\ConfigUpdateEvent;
use Thelia\Core\Event\TheliaEvents;
use Thelia\Model\Config as ConfigModel;
use Thelia\Model\ConfigQuery;
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, $eventName, EventDispatcherInterface $dispatcher)
{
$config = new ConfigModel();
$config->setDispatcher($dispatcher)
->setName($event->getEventName())
->setValue($event->getValue())
->setLocale($event->getLocale())
->setTitle($event->getTitle())
->setHidden($event->getHidden())
->setSecured($event->getSecured())
->save();
$event->setConfig($config);
}
/**
* Change a configuration entry value
*
* @param \Thelia\Core\Event\Config\ConfigUpdateEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function setValue(ConfigUpdateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if (null !== $config = ConfigQuery::create()->findPk($event->getConfigId())) {
if ($event->getValue() !== $config->getValue()) {
$config->setDispatcher($dispatcher)->setValue($event->getValue())->save();
$event->setConfig($config);
}
}
}
/**
* Change a configuration entry
*
* @param \Thelia\Core\Event\Config\ConfigUpdateEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function modify(ConfigUpdateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if (null !== $config = ConfigQuery::create()->findPk($event->getConfigId())) {
$config->setDispatcher($dispatcher)
->setName($event->getEventName())
->setValue($event->getValue())
->setHidden($event->getHidden())
->setSecured($event->getSecured())
->setLocale($event->getLocale())
->setTitle($event->getTitle())
->setDescription($event->getDescription())
->setChapo($event->getChapo())
->setPostscriptum($event->getPostscriptum())
->save();
$event->setConfig($config);
}
}
/**
* Delete a configuration entry
*
* @param \Thelia\Core\Event\Config\ConfigDeleteEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function delete(ConfigDeleteEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if (null !== ($config = ConfigQuery::create()->findPk($event->getConfigId()))) {
if (!$config->getSecured()) {
$config->setDispatcher($dispatcher)->delete();
$event->setConfig($config);
}
}
}
/**
* {@inheritDoc}
*/
public static function getSubscribedEvents()
{
return array(
TheliaEvents::CONFIG_CREATE => array(
"create", 128
), TheliaEvents::CONFIG_SETVALUE => array(
"setValue", 128
), TheliaEvents::CONFIG_UPDATE => array(
"modify", 128
), TheliaEvents::CONFIG_DELETE => array(
"delete", 128
),
);
}
}

View File

@@ -0,0 +1,263 @@
<?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\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 <manu@raynaud.io>
*/
class Content extends BaseAction implements EventSubscriberInterface
{
public function create(ContentCreateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$content = (new ContentModel)
->setDispatcher($dispatcher)
->setVisible($event->getVisible())
->setLocale($event->getLocale())
->setTitle($event->getTitle())
->create($event->getDefaultFolder())
;
$event->setContent($content);
}
/**
* process update content
*
* @param ContentUpdateEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
* @throws PropelException
* @throws \Exception
*/
public function update(ContentUpdateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if (null !== $content = ContentQuery::create()->findPk($event->getContentId())) {
$con = Propel::getWriteConnection(ContentTableMap::DATABASE_NAME);
$con->beginTransaction();
$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->setDefaultFolder($event->getDefaultFolder());
$event->setContent($content);
$con->commit();
} catch (PropelException $e) {
$con->rollBack();
throw $e;
}
}
}
/**
* Change Content SEO
*
* @param UpdateSeoEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
* @return Object
*/
public function updateSeo(UpdateSeoEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
return $this->genericUpdateSeo(ContentQuery::create(), $event, $dispatcher);
}
public function updatePosition(UpdatePositionEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$this->genericUpdateDelegatePosition(
ContentFolderQuery::create()
->filterByContentId($event->getObjectId())
->filterByFolderId($event->getReferrerId()),
$event,
$dispatcher
);
}
public function toggleVisibility(ContentToggleVisibilityEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$content = $event->getContent();
$content
->setDispatcher($dispatcher)
->setVisible(!$content->getVisible())
->save();
$event->setContent($content);
}
public function delete(ContentDeleteEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if (null !== $content = ContentQuery::create()->findPk($event->getContentId())) {
$con = Propel::getWriteConnection(ContentTableMap::DATABASE_NAME);
$con->beginTransaction();
try {
$fileList = ['images' => [], 'documentList' => []];
$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;
}
}
}
/**
*
* associate a folder to a content if the association already does not exists
*
* @param ContentAddFolderEvent $event
*/
public function addFolder(ContentAddFolderEvent $event)
{
if (ContentFolderQuery::create()
->filterByContent($event->getContent())
->filterByFolderId($event->getFolderId())
->count() <= 0
) {
$contentFolder = (new ContentFolder())
->setFolderId($event->getFolderId())
->setContent($event->getContent())
->setDefaultFolder(false);
$contentFolder
->setPosition($contentFolder->getNextPosition())
->save();
}
}
public function removeFolder(ContentRemoveFolderEvent $event)
{
$contentFolder = ContentFolderQuery::create()
->filterByContent($event->getContent())
->filterByFolderId($event->getFolderId())
->findOne();
if (null !== $contentFolder) {
$contentFolder->delete();
}
}
/**
* Check if is a content view and if content_id is visible
*
* @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_UPDATE => array('update', 128),
TheliaEvents::CONTENT_DELETE => array('delete', 128),
TheliaEvents::CONTENT_TOGGLE_VISIBILITY => array('toggleVisibility', 128),
TheliaEvents::CONTENT_UPDATE_POSITION => array('updatePosition', 128),
TheliaEvents::CONTENT_UPDATE_SEO => array('updateSeo', 128),
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

@@ -0,0 +1,121 @@
<?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\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;
use Thelia\Model\CountryQuery;
/**
* Class Country
* @package Thelia\Action
* @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())
->setDescription($event->getDescription())
->save();
$event->setCountry($country);
}
}
public function delete(CountryDeleteEvent $event)
{
if (null !== $country = CountryQuery::create()->findPk($event->getCountryId())) {
$country->delete();
$event->setCountry($country);
}
}
public function toggleDefault(CountryToggleDefaultEvent $event)
{
if (null !== $country = CountryQuery::create()->findPk($event->getCountryId())) {
$country->toggleDefault();
$event->setCountry($country);
}
}
/**
* Toggle Country visibility
*
* @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()
{
return array(
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_VISIBILITY => array('toggleVisibility', 128)
);
}
}

View File

@@ -0,0 +1,466 @@
<?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\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\Coupon\CouponFactory;
use Thelia\Coupon\CouponManager;
use Thelia\Coupon\Type\CouponInterface;
use Thelia\Model\Coupon as CouponModel;
use Thelia\Model\CouponCountry;
use Thelia\Model\CouponCountryQuery;
use Thelia\Model\CouponModule;
use Thelia\Model\CouponModuleQuery;
use Thelia\Model\CouponQuery;
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
*
* @package Coupon
* @author Guillaume MOREL <gmorel@openstudio.fr>, Franck Allimant <franck@cqfdev.fr>
*
*/
class Coupon extends BaseAction implements EventSubscriberInterface
{
/** @var RequestStack */
protected $requestStack;
/** @var CouponFactory $couponFactory */
protected $couponFactory;
/** @var CouponManager $couponManager */
protected $couponManager;
/** @var ConditionInterface $noConditionRule */
protected $noConditionRule;
/** @var ConditionFactory $conditionFactory */
protected $conditionFactory;
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;
$this->conditionFactory = $conditionFactory;
}
/**
* 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, $eventName, EventDispatcherInterface $dispatcher)
{
$coupon = new CouponModel();
$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, $eventName, EventDispatcherInterface $dispatcher)
{
$coupon = $event->getCouponModel();
$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, $eventName, EventDispatcherInterface $dispatcher)
{
$modelCoupon = $event->getCouponModel();
$this->createOrUpdateCondition($modelCoupon, $event, $dispatcher);
}
/**
* Clear all coupons in session.
*
* @param Event $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function clearAllCoupons(Event $event, $eventName, EventDispatcherInterface $dispatcher)
{
// Tell coupons to clear any data they may have stored
$this->couponManager->clear();
$this->getSession()->setConsumedCoupons(array());
$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, $eventName, EventDispatcherInterface $dispatcher)
{
$totalDiscount = 0;
$isValid = false;
/** @var CouponInterface $coupon */
$coupon = $this->couponFactory->buildCouponFromCode($event->getCode());
if ($coupon) {
$isValid = $coupon->isMatching();
if ($isValid) {
$this->couponManager->pushCouponInSession($event->getCode());
$totalDiscount = $this->couponManager->getDiscount();
$this->getSession()
->getSessionCart($dispatcher)
->setDiscount($totalDiscount)
->save();
$this->getSession()
->getOrder()
->setDiscount($totalDiscount)
;
}
}
$event->setIsValid($isValid);
$event->setDiscount($totalDiscount);
}
public function updateOrderDiscount(Event $event, $eventName, EventDispatcherInterface $dispatcher)
{
$discount = $this->couponManager->getDiscount();
$this->getSession()
->getSessionCart($dispatcher)
->setDiscount($discount)
->save();
$this->getSession()
->getOrder()
->setDiscount($discount);
}
/**
* Call the Model and delegate the create or delete action
* Feed the Event with the updated model
*
* @param CouponModel $coupon Model to save
* @param CouponCreateOrUpdateEvent $event Event containing data
* @param EventDispatcherInterface $dispatcher
*/
protected function createOrUpdate(CouponModel $coupon, CouponCreateOrUpdateEvent $event, EventDispatcherInterface $dispatcher)
{
$coupon->setDispatcher($dispatcher);
// Set default condition if none found
/** @var ConditionInterface $noConditionRule */
$noConditionRule = $this->noConditionRule;
/** @var ConditionFactory $conditionFactory */
$conditionFactory = $this->conditionFactory;
$couponRuleCollection = new ConditionCollection();
$couponRuleCollection[] = $noConditionRule;
$defaultSerializedRule = $conditionFactory->serializeConditionCollection(
$couponRuleCollection
);
$coupon->createOrUpdate(
$event->getCode(),
$event->getTitle(),
$event->getEffects(),
$event->getServiceId(),
$event->isRemovingPostage(),
$event->getShortDescription(),
$event->getDescription(),
$event->isEnabled(),
$event->getExpirationDate(),
$event->isAvailableOnSpecialOffers(),
$event->isCumulative(),
$event->getMaxUsage(),
$defaultSerializedRule,
$event->getLocale(),
$event->getFreeShippingForCountries(),
$event->getFreeShippingForMethods(),
$event->getPerCustomerUsageCount(),
$event->getStartDate()
);
$event->setCouponModel($coupon);
}
/**
* Call the Model and delegate the create or delete action
* Feed the Event with the updated model
*
* @param CouponModel $coupon Model to save
* @param CouponCreateOrUpdateEvent $event Event containing data
* @param EventDispatcherInterface $dispatcher
*/
protected function createOrUpdateCondition(CouponModel $coupon, CouponCreateOrUpdateEvent $event, EventDispatcherInterface $dispatcher)
{
$coupon->setDispatcher($dispatcher);
/** @var ConditionFactory $conditionFactory */
$conditionFactory = $this->conditionFactory;
$coupon->createOrUpdateConditions(
$conditionFactory->serializeConditionCollection($event->getConditions()),
$event->getLocale()
);
$event->setCouponModel($coupon);
}
/**
* @param \Thelia\Core\Event\Order\OrderEvent $event
*/
public function testFreePostage(OrderEvent $event)
{
$order = $event->getOrder();
if ($this->couponManager->isCouponRemovingPostage($order)) {
$order->setPostage(0);
$event->setOrder($order);
$event->stopPropagation();
}
}
/**
* @param \Thelia\Core\Event\Order\OrderEvent $event
*
* @throws \Exception if something goes wrong.
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function afterOrder(OrderEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
/** @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->getCode());
$couponModel->setLocale($this->getSession()->getLang()->getLocale());
/* decrease coupon quantity */
$this->couponManager->decrementQuantity($couponModel, $event->getOrder()->getCustomerId());
/* memorize coupon */
$orderCoupon = new OrderCoupon();
$orderCoupon->setOrder($event->getOrder())
->setCode($couponModel->getCode())
->setType($couponModel->getType())
->setAmount($couponCode->exec())
->setTitle($couponModel->getTitle())
->setShortDescription($couponModel->getShortDescription())
->setDescription($couponModel->getDescription())
->setStartDate($couponModel->getStartDate())
->setExpirationDate($couponModel->getExpirationDate())
->setIsCumulative($couponModel->getIsCumulative())
->setIsRemovingPostage($couponModel->getIsRemovingPostage())
->setIsAvailableOnSpecialOffers($couponModel->getIsAvailableOnSpecialOffers())
->setSerializedConditions($couponModel->getSerializedConditions())
->setPerCustomerUsageCount($couponModel->getPerCustomerUsageCount())
;
$orderCoupon->save();
// Copy order coupon free shipping data for countries and modules
$couponCountries = CouponCountryQuery::create()->filterByCouponId($couponModel->getId())->find();
/** @var CouponCountry $couponCountry */
foreach ($couponCountries as $couponCountry) {
$occ = new OrderCouponCountry();
$occ
->setCouponId($orderCoupon->getId())
->setCountryId($couponCountry->getCountryId())
->save();
;
}
$couponModules = CouponModuleQuery::create()->filterByCouponId($couponModel->getId())->find();
/** @var CouponModule $couponModule */
foreach ($couponModules as $couponModule) {
$ocm = new OrderCouponModule();
$ocm
->setCouponId($orderCoupon->getId())
->setModuleId($couponModule->getModuleId())
->save();
;
}
}
$con->commit();
} catch (\Exception $ex) {
$con->rollBack();
throw($ex);
}
}
// Clear all coupons.
$dispatcher->dispatch(TheliaEvents::COUPON_CLEAR_ALL);
}
/**
* Cancels order coupons usage when order is canceled or refunded,
* or use canceled coupons again if the order is no longer canceled or refunded
*
* @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

@@ -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\Action;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Thelia\Core\Event\Currency\CurrencyCreateEvent;
use Thelia\Core\Event\Currency\CurrencyDeleteEvent;
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, $eventName, EventDispatcherInterface $dispatcher)
{
$currency = new CurrencyModel();
$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()
;
$event->setCurrency($currency);
}
/**
* Change a currency
*
* @param \Thelia\Core\Event\Currency\CurrencyUpdateEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function update(CurrencyUpdateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if (null !== $currency = CurrencyQuery::create()->findPk($event->getCurrencyId())) {
$currency
->setDispatcher($dispatcher)
->setLocale($event->getLocale())
->setName($event->getCurrencyName())
->setSymbol($event->getSymbol())
->setFormat($event->getFormat())
->setRate($event->getRate())
->setCode(strtoupper($event->getCode()))
->save();
$event->setCurrency($currency);
}
}
/**
* Set the default currency
*
* @param CurrencyUpdateEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
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($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, $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($dispatcher)
->delete()
;
$event->setCurrency($currency);
}
}
public function updateRates(CurrencyUpdateRateEvent $event)
{
if (null === $defaultCurrency = CurrencyQuery::create()->findOneByByDefault(true)) {
throw new \RuntimeException('Unable to find a default currency, please define a default currency.');
}
$defaultCurrency->setRate(1)->save();
$currencies = CurrencyQuery::create()->filterByByDefault(false);
$baseValue = new Number('1');
/** @var \Thelia\Model\Currency $currency */
foreach ($currencies as $currency) {
try {
$rate = $this->currencyConverter
->from($defaultCurrency->getCode())
->to($currency->getCode())
->convert($baseValue);
$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());
}
}
}
/**
* Changes position, selecting absolute ou relative change.
*
* @param UpdatePositionEvent $event
* @param string $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function updatePosition(UpdatePositionEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$this->genericUpdatePosition(CurrencyQuery::create(), $event, $dispatcher);
}
/**
* {@inheritDoc}
*/
public static function getSubscribedEvents()
{
return array(
TheliaEvents::CURRENCY_CREATE => array("create", 128),
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

@@ -0,0 +1,239 @@
<?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\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\Translation\Translator;
use Thelia\Exception\CustomerException;
use Thelia\Mailer\MailerFactory;
use Thelia\Model\ConfigQuery;
use Thelia\Model\Customer as CustomerModel;
use Thelia\Model\CustomerQuery;
use Thelia\Tools\Password;
/**
*
* customer class where all actions are managed
*
* Class Customer
* @package Thelia\Action
* @author Manuel Raynaud <manu@raynaud.io>
*/
class Customer extends BaseAction implements EventSubscriberInterface
{
/** @var SecurityContext */
protected $securityContext;
/** @var MailerFactory */
protected $mailer;
public function __construct(SecurityContext $securityContext, MailerFactory $mailer)
{
$this->securityContext = $securityContext;
$this->mailer = $mailer;
}
public function create(CustomerCreateOrUpdateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$customer = new CustomerModel();
$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 customerConfirmationEmail(CustomerEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$customer = $event->getCustomer();
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());
}
if ($event->getFirstname() !== null) {
$customer->setFirstname($event->getFirstname());
}
if ($event->getLastname() !== null) {
$customer->setLastname($event->getLastname());
}
if ($event->getEmail() !== null) {
$customer->setEmail($event->getEmail(), $event->getEmailUpdateAllowed());
}
if ($event->getPassword() !== null) {
$customer->setPassword($event->getPassword());
}
if ($event->getReseller() !== null) {
$customer->setReseller($event->getReseller());
}
if ($event->getSponsor() !== null) {
$customer->setSponsor($event->getSponsor());
}
if ($event->getDiscount() !== null) {
$customer->setDiscount($event->getDiscount());
}
$customer->save();
$event->setCustomer($customer);
}
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"));
}
$customer->delete();
}
}
private function createOrUpdateCustomer(CustomerModel $customer, CustomerCreateOrUpdateEvent $event, EventDispatcherInterface $dispatcher)
{
$customer->setDispatcher($dispatcher);
$customer->createOrUpdate(
$event->getTitle(),
$event->getFirstname(),
$event->getLastname(),
$event->getAddress1(),
$event->getAddress2(),
$event->getAddress3(),
$event->getPhone(),
$event->getCellphone(),
$event->getZipcode(),
$event->getCity(),
$event->getCountry(),
$event->getEmail(),
$event->getPassword(),
$event->getLangId(),
$event->getReseller(),
$event->getSponsor(),
$event->getDiscount(),
$event->getCompany(),
$event->getRef(),
$event->getEmailUpdateAllowed(),
$event->getState()
);
$event->setCustomer($customer);
}
public function login(CustomerLoginEvent $event)
{
$customer = $event->getCustomer();
if (method_exists($customer, 'clearDispatcher')) {
$customer->clearDispatcher();
}
$this->securityContext->setCustomerUser($event->getCustomer());
}
/**
* Perform user logout. The user is redirected to the provided view, if any.
*
* @param ActionEvent $event
*/
public function logout(/** @noinspection PhpUnusedParameterInspection */ ActionEvent $event)
{
$this->securityContext->clearCustomerUser();
}
public function lostPassword(LostPasswordEvent $event)
{
if (null !== $customer = CustomerQuery::create()->filterByEmail($event->getEmail())->findOne()) {
$password = Password::generateRandom(8);
$customer
->setPassword($password)
->save()
;
$this->mailer->sendEmailToCustomer('lost_password', $customer, ['password' => $password]);
}
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents()
{
return array(
TheliaEvents::CUSTOMER_CREATEACCOUNT => array('create', 128),
TheliaEvents::CUSTOMER_UPDATEACCOUNT => array('modify', 128),
TheliaEvents::CUSTOMER_UPDATEPROFILE => array('updateProfile', 128),
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::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

@@ -0,0 +1,125 @@
<?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\Document\DocumentEvent;
use Thelia\Core\Event\TheliaEvents;
use Thelia\Exception\DocumentException;
use Thelia\Model\ConfigQuery;
use Thelia\Tools\URL;
/**
*
* Document management actions. This class handles document processing an caching.
*
* Basically, documents are stored outside the web space (by default in local/media/documents),
* and cached in the web space (by default in web/local/documents).
*
* In the documents caches directory, a subdirectory for documents categories (eg. product, category, folder, etc.) is
* automatically created, and the cached document is created here. Plugin may use their own subdirectory as required.
*
* The cached document name contains a hash of the processing options, and the original (normalized) name of the document.
*
* A copy (or symbolic link, by default) of the original document is always created in the cache, so that the full
* resolution document is always available.
*
* If a problem occurs, an DocumentException may be thrown.
*
* @package Thelia\Action
* @author Franck Allimant <franck@cqfdev.fr>
*
*/
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' . DS . 'documents');
}
/**
* Process document and write the result in the document cache.
*
* When the original document is required, create either a symbolic link with the
* original document in the cache dir, or copy it in the cache dir if it's not already done.
*
* This method updates the cache_file_path and file_url attributes of the event
*
* @param DocumentEvent $event Event
*
* @throws \Thelia\Exception\DocumentException
* @throws \InvalidArgumentException , DocumentException
*/
public function processDocument(DocumentEvent $event)
{
$subdir = $event->getCacheSubdirectory();
$sourceFile = $event->getSourceFilepath();
if (null == $subdir || null == $sourceFile) {
throw new \InvalidArgumentException("Cache sub-directory and source file path cannot be null");
}
$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(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));
}
} else {
// mode = 'copy'
if (false === @copy($sourceFile, $originalDocumentPathInCache)) {
throw new DocumentException(sprintf("Failed to copy %s in %s document cache directory", basename($sourceFile), $subdir));
}
}
}
// Compute the document URL
$documentUrl = $this->getCacheFileURL($subdir, basename($originalDocumentPathInCache));
// Update the event with file path and file URL
$event->setDocumentPath($documentUrl);
$event->setDocumentUrl(URL::getInstance()->absoluteUrl($documentUrl, null, URL::PATH_TO_FILE));
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents()
{
return array(
TheliaEvents::DOCUMENT_PROCESS => array("processDocument", 128),
// Implemented in parent class BaseCachedFile
TheliaEvents::DOCUMENT_CLEAR_CACHE => array("clearCache", 128),
TheliaEvents::DOCUMENT_DELETE => array("deleteFile", 128),
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

@@ -0,0 +1,79 @@
<?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\TheliaEvents;
use Thelia\Core\Event\UpdatePositionEvent;
use Thelia\Handler\ExportHandler;
use Thelia\Model\ExportCategoryQuery;
use Thelia\Model\ExportQuery;
/**
* Class Export
* @author Jérôme Billiras <jbilliras@openstudio.fr>
*/
class Export extends BaseAction implements EventSubscriberInterface
{
/**
* @var \Thelia\Handler\ExportHandler The export handler
*/
protected $handler;
/**
* @param \Thelia\Handler\ExportHandler $exportHandler The export handler
*/
public function __construct(ExportHandler $exportHandler)
{
$this->handler = $exportHandler;
}
public static function getSubscribedEvents()
{
return [
TheliaEvents::EXPORT_CHANGE_POSITION => [
['exportChangePosition', 128]
],
TheliaEvents::EXPORT_CATEGORY_CHANGE_POSITION => [
['exportCategoryChangePosition', 128]
]
];
}
/**
* Handle export change position event
*
* @param UpdatePositionEvent $updatePositionEvent
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function exportChangePosition(UpdatePositionEvent $updatePositionEvent, $eventName, EventDispatcherInterface $dispatcher)
{
$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

@@ -0,0 +1,159 @@
<?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\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;
use Thelia\Core\Event\UpdatePositionEvent;
use Thelia\Core\Event\Feature\FeatureEvent;
use Thelia\Model\FeatureTemplate;
use Thelia\Model\FeatureTemplateQuery;
use Thelia\Model\TemplateQuery;
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, $eventName, EventDispatcherInterface $dispatcher)
{
$feature = new FeatureModel();
$feature
->setDispatcher($dispatcher)
->setLocale($event->getLocale())
->setTitle($event->getTitle())
->save()
;
$event->setFeature($feature);
// Add atribute to all product templates if required
if ($event->getAddToAllTemplates() != 0) {
$this->doAddToAllTemplates($feature);
}
}
/**
* Change a product feature
*
* @param \Thelia\Core\Event\Feature\FeatureUpdateEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function update(FeatureUpdateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if (null !== $feature = FeatureQuery::create()->findPk($event->getFeatureId())) {
$feature
->setDispatcher($dispatcher)
->setLocale($event->getLocale())
->setTitle($event->getTitle())
->setDescription($event->getDescription())
->setChapo($event->getChapo())
->setPostscriptum($event->getPostscriptum())
->save();
$event->setFeature($feature);
}
}
/**
* Delete a product feature entry
*
* @param FeatureDeleteEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function delete(FeatureDeleteEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if (null !== ($feature = FeatureQuery::create()->findPk($event->getFeatureId()))) {
$feature
->setDispatcher($dispatcher)
->delete()
;
$event->setFeature($feature);
}
}
/**
* 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(FeatureQuery::create(), $event, $dispatcher);
}
protected function doAddToAllTemplates(FeatureModel $feature)
{
$templates = TemplateQuery::create()->find();
foreach ($templates as $template) {
$feature_template = new FeatureTemplate();
if (null === FeatureTemplateQuery::create()->filterByFeature($feature)->filterByTemplate($template)->findOne()) {
$feature_template
->setFeature($feature)
->setTemplate($template)
->save()
;
}
}
}
public function addToAllTemplates(FeatureEvent $event)
{
$this->doAddToAllTemplates($event->getFeature());
}
public function removeFromAllTemplates(FeatureEvent $event)
{
// Delete this feature from all product templates
FeatureTemplateQuery::create()->filterByFeature($event->getFeature())->delete();
}
/**
* {@inheritDoc}
*/
public static function getSubscribedEvents()
{
return array(
TheliaEvents::FEATURE_CREATE => array("create", 128),
TheliaEvents::FEATURE_UPDATE => array("update", 128),
TheliaEvents::FEATURE_DELETE => array("delete", 128),
TheliaEvents::FEATURE_UPDATE_POSITION => array("updatePosition", 128),
TheliaEvents::FEATURE_REMOVE_FROM_ALL_TEMPLATES => array("removeFromAllTemplates", 128),
TheliaEvents::FEATURE_ADD_TO_ALL_TEMPLATES => array("addToAllTemplates", 128),
);
}
}

View File

@@ -0,0 +1,119 @@
<?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\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;
use Thelia\Core\Event\UpdatePositionEvent;
class FeatureAv extends BaseAction implements EventSubscriberInterface
{
/**
* Create a new feature entry
*
* @param FeatureAvCreateEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function create(FeatureAvCreateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$feature = new FeatureAvModel();
$feature
->setDispatcher($dispatcher)
->setFeatureId($event->getFeatureId())
->setLocale($event->getLocale())
->setTitle($event->getTitle())
->save()
;
$event->setFeatureAv($feature);
}
/**
* Change a product feature
*
* @param FeatureAvUpdateEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function update(FeatureAvUpdateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if (null !== $feature = FeatureAvQuery::create()->findPk($event->getFeatureAvId())) {
$feature
->setDispatcher($dispatcher)
->setLocale($event->getLocale())
->setTitle($event->getTitle())
->setDescription($event->getDescription())
->setChapo($event->getChapo())
->setPostscriptum($event->getPostscriptum())
->save();
$event->setFeatureAv($feature);
}
}
/**
* Delete a product feature entry
*
* @param FeatureAvDeleteEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function delete(FeatureAvDeleteEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if (null !== ($feature = FeatureAvQuery::create()->findPk($event->getFeatureAvId()))) {
$feature
->setDispatcher($dispatcher)
->delete()
;
$event->setFeatureAv($feature);
}
}
/**
* 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(FeatureAvQuery::create(), $event, $dispatcher);
}
/**
* {@inheritDoc}
*/
public static function getSubscribedEvents()
{
return array(
TheliaEvents::FEATURE_AV_CREATE => array("create", 128),
TheliaEvents::FEATURE_AV_UPDATE => array("update", 128),
TheliaEvents::FEATURE_AV_DELETE => array("delete", 128),
TheliaEvents::FEATURE_AV_UPDATE_POSITION => array("updatePosition", 128),
);
}
}

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

@@ -0,0 +1,208 @@
<?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\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;
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 <manu@raynaud.io>
*/
class Folder extends BaseAction implements EventSubscriberInterface
{
public function update(FolderUpdateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if (null !== $folder = FolderQuery::create()->findPk($event->getFolderId())) {
$folder->setDispatcher($dispatcher);
$folder
->setParent($event->getParent())
->setVisible($event->getVisible())
->setLocale($event->getLocale())
->setTitle($event->getTitle())
->setDescription($event->getDescription())
->setChapo($event->getChapo())
->setPostscriptum($event->getPostscriptum())
->save();
;
$event->setFolder($folder);
}
}
/**
* Change Folder SEO
*
* @param UpdateSeoEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
* @return Object
*/
public function updateSeo(UpdateSeoEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
return $this->genericUpdateSeo(FolderQuery::create(), $event, $dispatcher);
}
public function delete(FolderDeleteEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if (null !== $folder = FolderQuery::create()->findPk($event->getFolderId())) {
$con = Propel::getWriteConnection(FolderTableMap::DATABASE_NAME);
$con->beginTransaction();
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;
}
}
}
public function create(FolderCreateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$folder = new FolderModel();
$folder->setDispatcher($dispatcher);
$folder
->setParent($event->getParent())
->setVisible($event->getVisible())
->setLocale($event->getLocale())
->setTitle($event->getTitle())
->save();
$event->setFolder($folder);
}
public function toggleVisibility(FolderToggleVisibilityEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$folder = $event->getFolder();
$folder
->setDispatcher($dispatcher)
->setVisible(!$folder->getVisible())
->save();
$event->setFolder($folder);
}
public function updatePosition(UpdatePositionEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if (null !== $folder = FolderQuery::create()->findPk($event->getObjectId())) {
$folder->setDispatcher($dispatcher);
switch ($event->getMode()) {
case UpdatePositionEvent::POSITION_ABSOLUTE:
$folder->changeAbsolutePosition($event->getPosition());
break;
case UpdatePositionEvent::POSITION_DOWN:
$folder->movePositionDown();
break;
case UpdatePositionEvent::POSITION_UP:
$folder->movePositionUp();
break;
}
}
}
/**
* Check if is a folder view and if folder_id is visible
*
* @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()
{
return array(
TheliaEvents::FOLDER_CREATE => array("create", 128),
TheliaEvents::FOLDER_UPDATE => array("update", 128),
TheliaEvents::FOLDER_DELETE => array("delete", 128),
TheliaEvents::FOLDER_TOGGLE_VISIBILITY => array("toggleVisibility", 128),
TheliaEvents::FOLDER_UPDATE_POSITION => array("updatePosition", 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

@@ -0,0 +1,114 @@
<?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\HttpKernel\Event\GetResponseForExceptionEvent;
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;
/**
*
* 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 */
protected $parser;
public function __construct(ParserInterface $parser)
{
$this->parser = $parser;
}
public function checkHttpException(GetResponseForExceptionEvent $event)
{
$exception = $event->getException();
if ($exception instanceof NotFoundHttpException) {
$this->display404($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(
$this->parser->getTemplateHelper()->getActiveAdminTemplate()
);
$message = $event->getException()->getMessage();
$response = Response::create(
$this->parser->render(
'general_error.html',
array(
"error_message" => $message
)
),
403
);
$event->setResponse($response);
}
protected function display404(GetResponseForExceptionEvent $event)
{
// Define the template thant shoud be used
$this->parser->setTemplateDefinition(
$this->parser->getTemplateHelper()->getActiveFrontTemplate()
);
$response = new Response($this->parser->render(ConfigQuery::getPageNotFoundView()), 404);
$event->setResponse($response);
}
protected function displayException(GetResponseForExceptionEvent $event)
{
/** @var \Symfony\Component\HttpKernel\Exception\HttpException $exception */
$exception = $event->getException();
$event->setResponse(
new Response(
$exception->getMessage(),
$exception->getStatusCode(),
$exception->getHeaders()
)
);
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents()
{
return array(
KernelEvents::EXCEPTION => ["checkHttpException", 128],
);
}
}

View File

@@ -0,0 +1,423 @@
<?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 Imagine\Image\Box;
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;
/**
*
* Image management actions. This class handles image processing and caching.
*
* Basically, images are stored outside of the web space (by default in local/media/images),
* and cached inside the web space (by default in web/local/images).
*
* In the images caches directory, a subdirectory for images categories (eg. product, category, folder, etc.) is
* automatically created, and the cached image is created here. Plugin may use their own subdirectory as required.
*
* The cached image name contains a hash of the processing options, and the original (normalized) name of the image.
*
* A copy (or symbolic link, by default) of the original image is always created in the cache, so that the full
* resolution image is always available.
*
* Various image processing options are available :
*
* - resizing, with border, crop, or by keeping image aspect ratio
* - rotation, in degrees, positive or negative
* - background color, applyed to empty background when creating borders or rotating
* - effects. The effects are applied in the specified order. The following effects are available:
* - gamma:value : change the image Gamma to the specified value. Example: gamma:0.7
* - grayscale or greyscale: switch image to grayscale
* - colorize:color : apply a color mask to the image. Exemple: colorize:#ff2244
* - negative : transform the image in its negative equivalent
* - vflip or vertical_flip : vertical flip
* - hflip or horizontal_flip : horizontal flip
*
* If a problem occurs, an ImageException may be thrown.
*
* @package Thelia\Action
* @author Franck Allimant <franck@cqfdev.fr>
*
*/
class Image extends BaseCachedFile implements EventSubscriberInterface
{
// Resize mode constants
const EXACT_RATIO_WITH_BORDERS = 1;
const EXACT_RATIO_WITH_CROP = 2;
const KEEP_IMAGE_RATIO = 3;
/**
* @return string root of the image cache directory in web space
*/
protected function getCacheDirFromWebRoot()
{
return ConfigQuery::read('image_cache_dir_from_web_root', 'cache' . DS . 'images');
}
/**
* Process image and write the result in the image cache.
*
* If the image already exists in cache, the cache file is immediately returned, without any processing
* If the original (full resolution) image is required, create either a symbolic link with the
* original image in the cache dir, or copy it in the cache dir.
*
* 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, $eventName, EventDispatcherInterface $dispatcher)
{
$subdir = $event->getCacheSubdirectory();
$source_file = $event->getSourceFilepath();
if (null == $subdir || null == $source_file) {
throw new \InvalidArgumentException("Cache sub-directory and source file path cannot be null");
}
// Find cached file path
$cacheFilePath = $this->getCacheFilePath($subdir, $source_file, $event->isOriginalImage(), $event->getOptionsHash());
$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 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));
}
} 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));
}
}
}
// 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);
$dispatcher->dispatch(TheliaEvents::IMAGE_PREPROCESSING, $event);
$image = $event->getImageObject();
$background_color = $event->getBackgroundColor();
$palette = new RGB();
if ($background_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,
$event->getAllowZoom()
);
// Rotate if required
$rotation = intval($event->getRotation());
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 'negative':
$image->effects()->negative();
break;
case 'horizontal_flip':
case 'hflip':
$image->flipHorizontally();
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]);
$image->effects()->gamma($gamma);
}
break;
case 'colorize':
// Syntax: colorize:couleur. Exemple: colorize:#ff00cc
if (isset($params[1])) {
$the_color = $palette->color($params[1]);
$image->effects()->colorize($the_color);
}
break;
}
}
$quality = $event->getQuality();
if (is_null($quality)) {
$quality = ConfigQuery::read('default_images_quality_percent', 75);
}
// Allow image post-processing (watermarging, or other stuff...)
$event->setImageObject($image);
$dispatcher->dispatch(TheliaEvents::IMAGE_POSTPROCESSING, $event);
$image = $event->getImageObject();
$image->save(
$cacheFilePath,
array('quality' => $quality)
);
} else {
throw new ImageException(sprintf("Source file %s cannot be opened.", basename($source_file)));
}
}
}
// Compute the image URL
$processed_image_url = $this->getCacheFileURL($subdir, basename($cacheFilePath));
// compute the full resolution image path in cache
$original_image_url = $this->getCacheFileURL($subdir, basename($originalImagePathInCache));
// Update the event with file path and file URL
$event->setCacheFilepath($cacheFilePath);
$event->setCacheOriginalFilepath($originalImagePathInCache);
$event->setFileUrl(URL::getInstance()->absoluteUrl($processed_image_url, null, URL::PATH_TO_FILE));
$event->setOriginalFileUrl(URL::getInstance()->absoluteUrl($original_image_url, null, URL::PATH_TO_FILE));
}
/**
* Process image resizing, with borders or cropping. If $dest_width and $dest_height
* are both null, no resize is performed.
*
* @param ImagineInterface $imagine the Imagine instance
* @param ImageInterface $image the image to process
* @param int $dest_width the required width
* @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,
$allow_zoom = false
) {
if (! (is_null($dest_width) && is_null($dest_height))) {
$width_orig = $image->getSize()->getWidth();
$height_orig = $image->getSize()->getHeight();
$ratio = $width_orig / $height_orig;
if (is_null($dest_width)) {
$dest_width = $dest_height * $ratio;
}
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;
$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;
}
// 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
$resize_height = $dest_height;
$resize_width = intval(($width_orig * $resize_height) / $height_orig);
if ($resize_mode == self::EXACT_RATIO_WITH_CROP) {
$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 = $resize_width;
}
} else {
// Image width > image height
$resize_width = $dest_width;
$resize_height = intval($height_orig * $dest_width / $width_orig);
if ($resize_mode == self::EXACT_RATIO_WITH_CROP) {
$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 = $resize_height;
}
}
$image->resize(new Box($resize_width, $resize_height));
if ($resize_mode == self::EXACT_RATIO_WITH_BORDERS) {
$border_width = intval(($dest_width - $resize_width) / 2);
$border_height = intval(($dest_height - $resize_height) / 2);
$canvas = new Box($dest_width, $dest_height);
return $imagine->create($canvas, $bg_color)
->paste($image, new Point($border_width, $border_height));
} elseif ($resize_mode == self::EXACT_RATIO_WITH_CROP) {
$image->crop(
new Point($delta_x, $delta_y),
new Box($dest_width, $dest_height)
);
}
}
return $image;
}
/**
* Create a new Imagine object using current driver configuration
*
* @return ImagineInterface
*/
protected function createImagineInstance()
{
$driver = ConfigQuery::read("imagine_graphic_driver", "gd");
switch ($driver) {
case 'imagick':
$image = new ImagickImagine();
break;
case 'gmagick':
$image = new GmagickImagine();
break;
case 'gd':
default:
$image = new Imagine();
}
return $image;
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents()
{
return array(
TheliaEvents::IMAGE_PROCESS => array("processImage", 128),
// Implemented in parent class BaseCachedFile
TheliaEvents::IMAGE_CLEAR_CACHE => array("clearCache", 128),
TheliaEvents::IMAGE_DELETE => array("deleteFile", 128),
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

@@ -0,0 +1,79 @@
<?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\TheliaEvents;
use Thelia\Core\Event\UpdatePositionEvent;
use Thelia\Handler\ImportHandler;
use Thelia\Model\ImportCategoryQuery;
use Thelia\Model\ImportQuery;
/**
* Class Import
* @author Jérôme Billiras <jbilliras@openstudio.fr>
*/
class Import extends BaseAction implements EventSubscriberInterface
{
/**
* @var \Thelia\Handler\ImportHandler The import handler
*/
protected $handler;
/**
* @param \Thelia\Handler\ImportHandler $importHandler The import handler
*/
public function __construct(ImportHandler $importHandler)
{
$this->handler = $importHandler;
}
public static function getSubscribedEvents()
{
return [
TheliaEvents::IMPORT_CHANGE_POSITION => [
['importChangePosition', 128]
],
TheliaEvents::IMPORT_CATEGORY_CHANGE_POSITION => [
['importCategoryChangePosition', 128]
]
];
}
/**
* Handle import change position event
*
* @param UpdatePositionEvent $updatePositionEvent
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function importChangePosition(UpdatePositionEvent $updatePositionEvent, $eventName, EventDispatcherInterface $dispatcher)
{
$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

@@ -0,0 +1,238 @@
<?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\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;
use Thelia\Model\Lang as LangModel;
/**
* Class Lang
* @package Thelia\Action
* @author Manuel Raynaud <manu@raynaud.io>
*/
class Lang extends BaseAction implements EventSubscriberInterface
{
/** @var TemplateHelperInterface */
protected $templateHelper;
/** @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($dispatcher);
$lang->setTitle($event->getTitle())
->setLocale($event->getLocale())
->setCode($event->getCode())
->setDateTimeFormat($event->getDateTimeFormat())
->setDateFormat($event->getDateFormat())
->setTimeFormat($event->getTimeFormat())
->setDecimalSeparator($event->getDecimalSeparator())
->setThousandsSeparator($event->getThousandsSeparator())
->setDecimals($event->getDecimals())
->save();
$event->setLang($lang);
}
}
public function toggleDefault(LangToggleDefaultEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if (null !== $lang = LangQuery::create()->findPk($event->getLangId())) {
$lang->setDispatcher($dispatcher);
$lang->toggleDefault();
$event->setLang($lang);
}
}
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($dispatcher)
->setTitle($event->getTitle())
->setCode($event->getCode())
->setLocale($event->getLocale())
->setDateTimeFormat($event->getDateTimeFormat())
->setDateFormat($event->getDateFormat())
->setTimeFormat($event->getTimeFormat())
->setDecimalSeparator($event->getDecimalSeparator())
->setThousandsSeparator($event->getThousandsSeparator())
->setDecimals($event->getDecimals())
->save();
$event->setLang($lang);
}
public function delete(LangDeleteEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if (null !== $lang = LangQuery::create()->findPk($event->getLangId())) {
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);
}
}
public function defaultBehavior(LangDefaultBehaviorEvent $event)
{
ConfigQuery::create()
->filterByName('default_lang_without_translation')
->update(array('Value' => $event->getDefaultBehavior()));
}
public function langUrl(LangUrlEvent $event)
{
foreach ($event->getUrl() as $id => $url) {
LangQuery::create()
->filterById($id)
->update(array('Url' => $url));
}
}
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);
}
}
/**
* {@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_FIX_MISSING_FLAG => array('fixMissingFlag', 128)
);
}
}

View File

@@ -0,0 +1,51 @@
<?php
/*************************************************************************************/
/* This file is part of the Thelia package. */
/* */
/* Copyright (c) OpenStudio */
/* email : dev@thelia.net */
/* web : http://www.thelia.net */
/* */
/* For the full copyright and license information, please view the LICENSE.txt */
/* file that was distributed with this source code. */
/*************************************************************************************/
namespace Thelia\Action;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Thelia\Core\Event\MailingSystem\MailingSystemEvent;
use Thelia\Core\Event\TheliaEvents;
use Thelia\Model\ConfigQuery;
class MailingSystem extends BaseAction implements EventSubscriberInterface
{
/**
* @param MailingSystemEvent $event
*/
public function update(MailingSystemEvent $event)
{
if ($event->getEnabled()) {
ConfigQuery::enableSmtp();
} else {
ConfigQuery::disableSmtp();
}
ConfigQuery::setSmtpHost($event->getHost());
ConfigQuery::setSmtpPort($event->getPort());
ConfigQuery::setSmtpEncryption($event->getEncryption());
ConfigQuery::setSmtpUsername($event->getUsername());
ConfigQuery::setSmtpPassword($event->getPassword());
ConfigQuery::setSmtpAuthMode($event->getAuthMode());
ConfigQuery::setSmtpTimeout($event->getTimeout());
ConfigQuery::setSmtpSourceIp($event->getSourceIp());
}
/**
* {@inheritDoc}
*/
public static function getSubscribedEvents()
{
return array(
TheliaEvents::MAILING_SYSTEM_UPDATE => array("update", 128),
);
}
}

View File

@@ -0,0 +1,116 @@
<?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\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;
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, $eventName, EventDispatcherInterface $dispatcher)
{
$message = new MessageModel();
$message
->setDispatcher($dispatcher)
->setName($event->getMessageName())
->setLocale($event->getLocale())
->setTitle($event->getTitle())
->setSecured($event->getSecured())
->save()
;
$event->setMessage($message);
}
/**
* Change a message
*
* @param \Thelia\Core\Event\Message\MessageUpdateEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function modify(MessageUpdateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if (null !== $message = MessageQuery::create()->findPk($event->getMessageId())) {
$message
->setDispatcher($dispatcher)
->setName($event->getMessageName())
->setSecured($event->getSecured())
->setLocale($event->getLocale())
->setTitle($event->getTitle())
->setSubject($event->getSubject())
->setHtmlMessage($event->getHtmlMessage())
->setTextMessage($event->getTextMessage())
->setHtmlLayoutFileName($event->getHtmlLayoutFileName())
->setHtmlTemplateFileName($event->getHtmlTemplateFileName())
->setTextLayoutFileName($event->getTextLayoutFileName())
->setTextTemplateFileName($event->getTextTemplateFileName())
->save();
$event->setMessage($message);
}
}
/**
* Delete a messageuration entry
*
* @param \Thelia\Core\Event\Message\MessageDeleteEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function delete(MessageDeleteEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if (null !== ($message = MessageQuery::create()->findPk($event->getMessageId()))) {
$message
->setDispatcher($dispatcher)
->delete()
;
$event->setMessage($message);
}
}
/**
* {@inheritDoc}
*/
public static function getSubscribedEvents()
{
return array(
TheliaEvents::MESSAGE_CREATE => array("create", 128),
TheliaEvents::MESSAGE_UPDATE => array("modify", 128),
TheliaEvents::MESSAGE_DELETE => array("delete", 128),
);
}
}

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

@@ -0,0 +1,467 @@
<?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 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 <manu@raynaud.io>
*/
class Module extends BaseAction implements EventSubscriberInterface
{
/** @var ContainerInterface */
protected $container;
public function __construct(ContainerInterface $container)
{
$this->container = $container;
}
public function toggleActivation(ModuleToggleActivationEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if (null !== $module = ModuleQuery::create()->findPk($event->getModuleId())) {
$moduleInstance = $module->createInstance();
if (method_exists($moduleInstance, 'setContainer')) {
$moduleInstance->setContainer($this->container);
if ($module->getActivate() == BaseModule::IS_ACTIVATED) {
$moduleInstance->deActivate($module);
} else {
$moduleInstance->activate($module);
}
}
$event->setModule($module);
$this->cacheClear($dispatcher);
}
}
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())) {
$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 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();
$instance->destroy($con, $event->getDeleteData());
$fs = new Filesystem();
$fs->remove($path);
} catch (\ReflectionException $ex) {
// Happens probably because the module directory has been deleted.
// 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',
['%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);
$con->commit();
$event->setModule($module);
$this->cacheClear($dispatcher);
} catch (\Exception $e) {
$con->rollBack();
throw $e;
}
}
}
/**
* @param ModuleEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function update(ModuleEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if (null !== $module = ModuleQuery::create()->findPk($event->getId())) {
$module
->setDispatcher($dispatcher)
->setLocale($event->getLocale())
->setTitle($event->getTitle())
->setChapo($event->getChapo())
->setDescription($event->getDescription())
->setPostscriptum($event->getPostscriptum());
$module->save();
$event->setModule($module);
}
}
/**
* @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
*
* @throws \RuntimeException if no payment module can be found.
*/
public function pay(OrderPaymentEvent $event)
{
$order = $event->getOrder();
/* call pay method */
if (null === $paymentModule = ModuleQuery::create()->findPk($order->getPaymentModuleId())) {
throw new \RuntimeException(
Translator::getInstance()->trans(
"Failed to find a payment Module with ID=%mid for order ID=%oid",
[
"%mid" => $order->getPaymentModuleId(),
"%oid" => $order->getId()
]
)
);
}
$paymentModuleInstance = $paymentModule->getPaymentModuleInstance($this->container);
$response = $paymentModuleInstance->pay($order);
if (null !== $response && $response instanceof Response) {
$event->setResponse($response);
}
}
/**
* 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(ModuleQuery::create(), $event, $dispatcher);
$this->cacheClear($dispatcher);
}
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 [
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

@@ -0,0 +1,119 @@
<?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\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 <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)
{
// 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
->setUnsubscribed(true)
->save();
$event->setNewsletter($nl);
}
}
public function update(NewsletterEvent $event)
{
if (null !== $nl = NewsletterQuery::create()->findPk($event->getId())) {
$nl->setEmail($event->getEmail())
->setFirstname($event->getFirstname())
->setLastname($event->getLastname())
->setLocale($event->getLocale())
->save();
$event->setNewsletter($nl);
}
}
/**
* @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_CONFIRM_SUBSCRIPTION => array('confirmSubscription', 128)
);
}
}

View File

@@ -0,0 +1,741 @@
<?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\EventDispatcher;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
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\Security\SecurityContext;
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\Lang as LangModel;
use Thelia\Model\Map\OrderTableMap;
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;
/**
*
* Class Order
* @package Thelia\Action
* @author Etienne Roudeix <eroudeix@openstudio.fr>
*/
class Order extends BaseAction implements EventSubscriberInterface
{
/** @var RequestStack */
protected $requestStack;
/** @var MailerFactory */
protected $mailer;
/** @var SecurityContext */
protected $securityContext;
public function __construct(RequestStack $requestStack, MailerFactory $mailer, SecurityContext $securityContext)
{
$this->requestStack = $requestStack;
$this->mailer = $mailer;
$this->securityContext = $securityContext;
}
/**
* @param \Thelia\Core\Event\Order\OrderEvent $event
*/
public function setDeliveryAddress(OrderEvent $event)
{
$order = $event->getOrder();
$order->setChoosenDeliveryAddress($event->getDeliveryAddress());
$event->setOrder($order);
}
/**
* @param \Thelia\Core\Event\Order\OrderEvent $event
*/
public function setDeliveryModule(OrderEvent $event)
{
$order = $event->getOrder();
$deliveryModuleId = $event->getDeliveryModule();
$order->setDeliveryModuleId($deliveryModuleId);
// 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);
}
/**
* @param \Thelia\Core\Event\Order\OrderEvent $event
*/
public function setPostage(OrderEvent $event)
{
$order = $event->getOrder();
$order->setPostage($event->getPostage());
$order->setPostageTax($event->getPostageTax());
$order->setPostageTaxRuleTitle($event->getPostageTaxRuleTitle());
$event->setOrder($order);
}
/**
* @param \Thelia\Core\Event\Order\OrderEvent $event
*/
public function setInvoiceAddress(OrderEvent $event)
{
$order = $event->getOrder();
$order->setChoosenInvoiceAddress($event->getInvoiceAddress());
$event->setOrder($order);
}
/**
* @param \Thelia\Core\Event\Order\OrderEvent $event
*/
public function setPaymentModule(OrderEvent $event)
{
$order = $event->getOrder();
$order->setPaymentModuleId($event->getPaymentModule());
$event->setOrder($order);
}
/**
* @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);
$cartItems = $cart->getCartItems();
/* fulfill order */
$placedOrder->setCustomerId($customer->getId());
$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())
->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())
->setCellphone($invoiceAddress->getCellphone())
->setCountryId($invoiceAddress->getCountryId())
->setStateId($deliveryAddress->getStateId())
->save($con);
$placedOrder->setDeliveryOrderAddressId($deliveryOrderAddress->getId());
$placedOrder->setInvoiceOrderAddressId($invoiceOrderAddress->getId());
$taxCountry = $deliveryAddress->getCountry();
}
$placedOrder->setStatusId(
OrderStatusQuery::getNotPaidStatus()->getId()
);
$placedOrder->setCartId($cart->getId());
/* memorize discount */
$placedOrder->setDiscount(
$cart->getDiscount()
);
$placedOrder->save($con);
/* fulfill order_products and decrease stock */
foreach ($cartItems as $cartItem) {
$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()
&& true === ConfigQuery::checkAvailableStock()
&& $useStock) {
throw new TheliaProcessException("Not enough stock", TheliaProcessException::CART_ITEM_NOT_ENOUGH_STOCK, $cartItem);
}
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()
);
$orderProduct = new OrderProduct();
$orderProduct
->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())
->setWasNew($pse->getNewness())
->setWasInPromo($cartItem->getPromo())
->setWeight($pse->getWeight())
->setTaxRuleTitle($taxRuleI18n->getTitle())
->setTaxRuleDescription($taxRuleI18n->getDescription())
->setEanCode($pse->getEanCode())
->setCartItemId($cartItem->getId())
->setDispatcher($dispatcher)
->save($con)
;
/* fulfill order_product_tax */
/** @var OrderProductTax $tax */
foreach ($taxDetail as $tax) {
$tax->setOrderProductId($orderProduct->getId());
$tax->save($con);
}
/* 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();
$orderAttributeCombination
->setOrderProductId($orderProduct->getId())
->setAttributeTitle($attribute->getTitle())
->setAttributeChapo($attribute->getChapo())
->setAttributeDescription($attribute->getDescription())
->setAttributePostscriptum($attribute->getPostscriptum())
->setAttributeAvTitle($attributeAv->getTitle())
->setAttributeAvChapo($attributeAv->getChapo())
->setAttributeAvDescription($attributeAv->getDescription())
->setAttributeAvPostscriptum($attributeAv->getPostscriptum())
->save($con);
}
}
$con->commit();
return $placedOrder;
}
/**
* 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, $eventName, EventDispatcherInterface $dispatcher)
{
$paymentModule = ModuleQuery::create()->findPk($event->getOrder()->getPaymentModuleId());
/** @var \Thelia\Module\PaymentModuleInterface $paymentModuleInstance */
$paymentModuleInstance = $paymentModule->createInstance();
$event->setPlacedOrder(
$this->createOrder(
$dispatcher,
$event->getOrder(),
$event->getCurrency(),
$event->getLang(),
$event->getCart(),
$event->getCustomer(),
$this->isModuleManageStockOnCreation(
$dispatcher,
$paymentModuleInstance
),
$event->getUseOrderDefinedAddresses()
)
);
$event->setOrder(new OrderModel());
}
/**
* @param OrderEvent $event
*
* @throws \Thelia\Exception\TheliaProcessException
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
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(
$dispatcher,
$event->getOrder(),
$session->getCurrency(),
$session->getLang(),
$session->getSessionCart($dispatcher),
$this->securityContext->getCustomerUser(),
$this->isModuleManageStockOnCreation(
$dispatcher,
$paymentModuleInstance
)
);
$dispatcher->dispatch(TheliaEvents::ORDER_BEFORE_PAYMENT, new OrderEvent($placedOrder));
/* but memorize placed order */
$event->setOrder(new OrderModel());
$event->setPlacedOrder($placedOrder);
/* call pay method */
$payEvent = new OrderPaymentEvent($placedOrder);
$dispatcher->dispatch(TheliaEvents::MODULE_PAY, $payEvent);
if ($payEvent->hasResponse()) {
$event->setResponse($payEvent->getResponse());
}
}
/**
* @param OrderEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function orderBeforePayment(OrderEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$dispatcher ->dispatch(TheliaEvents::ORDER_SEND_CONFIRMATION_EMAIL, clone $event);
$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.
*
* @param OrderEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function orderCartClear(/** @noinspection PhpUnusedParameterInspection */ OrderEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
// 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, $eventName, EventDispatcherInterface $dispatcher)
{
$order = $event->getOrder();
$newStatus = $event->getStatus();
$paymentModule = ModuleQuery::create()->findPk($order->getPaymentModuleId());
/** @var PaymentModuleInterface $paymentModuleInstance */
$paymentModuleInstance = $paymentModule->createInstance();
$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
*/
public function updateDeliveryRef(OrderEvent $event)
{
$order = $event->getOrder();
$order->setDeliveryRef($event->getDeliveryRef());
$order->save();
$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
*/
public function updateAddress(OrderAddressEvent $event)
{
$orderAddress = $event->getOrderAddress();
$orderAddress
->setCustomerTitleId($event->getTitle())
->setCompany($event->getCompany())
->setFirstname($event->getFirstname())
->setLastname($event->getLastname())
->setAddress1($event->getAddress1())
->setAddress2($event->getAddress2())
->setAddress3($event->getAddress3())
->setZipcode($event->getZipcode())
->setCity($event->getCity())
->setCountryId($event->getCountry())
->setStateId($event->getState())
->setPhone($event->getPhone())
->setCellphone($event->getCellphone())
;
$orderAddress->save();
$event->setOrderAddress($orderAddress);
}
/**
* Check if a payment module manage stock on creation
*
* @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()
{
return array(
TheliaEvents::ORDER_SET_DELIVERY_ADDRESS => array("setDeliveryAddress", 128),
TheliaEvents::ORDER_SET_DELIVERY_MODULE => array("setDeliveryModule", 128),
TheliaEvents::ORDER_SET_POSTAGE => array("setPostage", 128),
TheliaEvents::ORDER_SET_INVOICE_ADDRESS => array("setInvoiceAddress", 128),
TheliaEvents::ORDER_SET_PAYMENT_MODULE => array("setPaymentModule", 128),
TheliaEvents::ORDER_PAY => array("create", 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),
);
}
/**
* Returns the session from the current request
*
* @return \Thelia\Core\HttpFoundation\Session\Session
*/
protected function 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

@@ -0,0 +1,54 @@
<?php
/*************************************************************************************/
/* This file is part of the Thelia package. */
/* */
/* Copyright (c) OpenStudio */
/* email : dev@thelia.net */
/* web : http://www.thelia.net */
/* */
/* For the full copyright and license information, please view the LICENSE.txt */
/* file that was distributed with this source code. */
/*************************************************************************************/
namespace Thelia\Action;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Thelia\Core\Event\PdfEvent;
use Thelia\Core\Event\TheliaEvents;
/**
* Class Pdf
* @package Thelia\Action
* @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->setDefaultFont($event->getFontName());
$html2pdf->pdf->SetDisplayMode('real');
$html2pdf->writeHTML($event->getContent());
$event->setPdf($html2pdf->output(null, 'S'));
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents()
{
return array(
TheliaEvents::GENERATE_PDF => array("generatePdf", 128)
);
}
}

View File

@@ -0,0 +1,943 @@
<?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\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\Feature\FeatureAvCreateEvent;
use Thelia\Core\Event\Feature\FeatureAvDeleteEvent;
use Thelia\Core\Event\FeatureProduct\FeatureProductDeleteEvent;
use Thelia\Core\Event\FeatureProduct\FeatureProductUpdateEvent;
use Thelia\Core\Event\File\FileDeleteEvent;
use Thelia\Core\Event\Product\ProductAddAccessoryEvent;
use Thelia\Core\Event\Product\ProductAddCategoryEvent;
use Thelia\Core\Event\Product\ProductAddContentEvent;
use Thelia\Core\Event\Product\ProductCloneEvent;
use Thelia\Core\Event\Product\ProductCreateEvent;
use Thelia\Core\Event\Product\ProductDeleteAccessoryEvent;
use Thelia\Core\Event\Product\ProductDeleteCategoryEvent;
use Thelia\Core\Event\Product\ProductDeleteContentEvent;
use Thelia\Core\Event\Product\ProductDeleteEvent;
use Thelia\Core\Event\Product\ProductSetTemplateEvent;
use Thelia\Core\Event\Product\ProductToggleVisibilityEvent;
use Thelia\Core\Event\Product\ProductUpdateEvent;
use Thelia\Core\Event\ProductSaleElement\ProductSaleElementDeleteEvent;
use Thelia\Core\Event\Template\TemplateDeleteAttributeEvent;
use Thelia\Core\Event\Template\TemplateDeleteFeatureEvent;
use Thelia\Core\Event\TheliaEvents;
use Thelia\Core\Event\UpdatePositionEvent;
use Thelia\Core\Event\UpdateSeoEvent;
use Thelia\Core\Event\ViewCheckEvent;
use Thelia\Model\Accessory;
use Thelia\Model\AccessoryQuery;
use Thelia\Model\AttributeTemplateQuery;
use Thelia\Model\Currency as CurrencyModel;
use Thelia\Model\FeatureAvI18n;
use Thelia\Model\FeatureAvI18nQuery;
use Thelia\Model\FeatureAvQuery;
use Thelia\Model\FeatureProduct;
use Thelia\Model\FeatureProductQuery;
use Thelia\Model\FeatureTemplateQuery;
use Thelia\Model\Map\AttributeTemplateTableMap;
use Thelia\Model\Map\FeatureTemplateTableMap;
use Thelia\Model\Map\ProductSaleElementsTableMap;
use Thelia\Model\Map\ProductTableMap;
use Thelia\Model\Product as ProductModel;
use Thelia\Model\ProductAssociatedContent;
use Thelia\Model\ProductAssociatedContentQuery;
use Thelia\Model\ProductCategory;
use Thelia\Model\ProductCategoryQuery;
use Thelia\Model\ProductDocument;
use Thelia\Model\ProductDocumentQuery;
use Thelia\Model\ProductI18n;
use Thelia\Model\ProductI18nQuery;
use Thelia\Model\ProductImage;
use Thelia\Model\ProductImageQuery;
use Thelia\Model\ProductPrice;
use Thelia\Model\ProductPriceQuery;
use Thelia\Model\ProductQuery;
use Thelia\Model\ProductSaleElementsQuery;
use Thelia\Model\TaxRuleQuery;
class Product extends BaseAction implements EventSubscriberInterface
{
/** @var EventDispatcherInterface */
protected $eventDispatcher;
public function __construct(EventDispatcherInterface $eventDispatcher)
{
$this->eventDispatcher = $eventDispatcher;
}
/**
* Create a new product entry
*
* @param \Thelia\Core\Event\Product\ProductCreateEvent $event
*/
public function create(ProductCreateEvent $event)
{
$defaultTaxRuleId = null;
if (null !== $defaultTaxRule = TaxRuleQuery::create()->findOneByIsDefault(true)) {
$defaultTaxRuleId = $defaultTaxRule->getId();
}
$product = new ProductModel();
$product
->setDispatcher($this->eventDispatcher)
->setRef($event->getRef())
->setLocale($event->getLocale())
->setTitle($event->getTitle())
->setVisible($event->getVisible() ? 1 : 0)
->setVirtual($event->getVirtual() ? 1 : 0)
->setTemplateId($event->getTemplateId())
->create(
$event->getDefaultCategory(),
$event->getBasePrice(),
$event->getCurrencyId(),
// Set the default tax rule if not defined
$event->getTaxRuleId() ?: $defaultTaxRuleId,
$event->getBaseWeight(),
$event->getBaseQuantity()
)
;
$event->setProduct($product);
}
/*******************
* CLONING PROCESS *
*******************/
/**
* @param ProductCloneEvent $event
* @throws \Exception
*/
public function cloneProduct(ProductCloneEvent $event)
{
$con = Propel::getWriteConnection(ProductTableMap::DATABASE_NAME);
$con->beginTransaction();
try {
// Get important datas
$lang = $event->getLang();
$originalProduct = $event->getOriginalProduct();
if (null === $originalProductDefaultI18n = ProductI18nQuery::create()
->findPk([$originalProduct->getId(), $lang])) {
// No i18n entry for the current language. Try to find one for creating the product.
// It will be updated later by updateClone()
$originalProductDefaultI18n = ProductI18nQuery::create()
->findOneById($originalProduct->getId())
;
}
$originalProductDefaultPrice = ProductPriceQuery::create()
->findOneByProductSaleElementsId($originalProduct->getDefaultSaleElements()->getId());
// Cloning process
$this->createClone($event, $originalProductDefaultI18n, $originalProductDefaultPrice);
$this->updateClone($event, $originalProductDefaultPrice);
$this->cloneFeatureCombination($event);
$this->cloneAssociatedContent($event);
$this->cloneAccessories($event);
// Dispatch event for file cloning
$this->eventDispatcher->dispatch(TheliaEvents::FILE_CLONE, $event);
// Dispatch event for PSE cloning
$this->eventDispatcher->dispatch(TheliaEvents::PSE_CLONE, $event);
$con->commit();
} catch (\Exception $e) {
$con->rollBack();
throw $e;
}
}
public function createClone(ProductCloneEvent $event, ProductI18n $originalProductDefaultI18n, ProductPrice $originalProductDefaultPrice)
{
// Build event and dispatch creation of the clone product
$createCloneEvent = new ProductCreateEvent();
$createCloneEvent
->setTitle($originalProductDefaultI18n->getTitle())
->setRef($event->getRef())
->setLocale($event->getLang())
->setVisible(0)
->setVirtual($event->getOriginalProduct()->getVirtual())
->setTaxRuleId($event->getOriginalProduct()->getTaxRuleId())
->setDefaultCategory($event->getOriginalProduct()->getDefaultCategoryId())
->setBasePrice($originalProductDefaultPrice->getPrice())
->setCurrencyId($originalProductDefaultPrice->getCurrencyId())
->setBaseWeight($event->getOriginalProduct()->getDefaultSaleElements()->getWeight());
$this->eventDispatcher->dispatch(TheliaEvents::PRODUCT_CREATE, $createCloneEvent);
$event->setClonedProduct($createCloneEvent->getProduct());
}
public function updateClone(ProductCloneEvent $event, ProductPrice $originalProductDefaultPrice)
{
// Get original product's I18ns
$originalProductI18ns = ProductI18nQuery::create()
->findById($event->getOriginalProduct()->getId());
/** @var ProductI18n $originalProductI18n */
foreach ($originalProductI18ns as $originalProductI18n) {
$clonedProductUpdateEvent = new ProductUpdateEvent($event->getClonedProduct()->getId());
$clonedProductUpdateEvent
->setRef($event->getClonedProduct()->getRef())
->setVisible($event->getClonedProduct()->getVisible())
->setVirtual($event->getClonedProduct()->getVirtual())
->setLocale($originalProductI18n->getLocale())
->setTitle($originalProductI18n->getTitle())
->setChapo($originalProductI18n->getChapo())
->setDescription($originalProductI18n->getDescription())
->setPostscriptum($originalProductI18n->getPostscriptum())
->setBasePrice($originalProductDefaultPrice->getPrice())
->setCurrencyId($originalProductDefaultPrice->getCurrencyId())
->setBaseWeight($event->getOriginalProduct()->getDefaultSaleElements()->getWeight())
->setTaxRuleId($event->getOriginalProduct()->getTaxRuleId())
->setBrandId($event->getOriginalProduct()->getBrandId())
->setDefaultCategory($event->getOriginalProduct()->getDefaultCategoryId());
$this->eventDispatcher->dispatch(TheliaEvents::PRODUCT_UPDATE, $clonedProductUpdateEvent);
// SEO info
$clonedProductUpdateSeoEvent = new UpdateSeoEvent($event->getClonedProduct()->getId());
$clonedProductUpdateSeoEvent
->setLocale($originalProductI18n->getLocale())
->setMetaTitle($originalProductI18n->getMetaTitle())
->setMetaDescription($originalProductI18n->getMetaDescription())
->setMetaKeywords($originalProductI18n->getMetaKeywords())
->setUrl(null);
$this->eventDispatcher->dispatch(TheliaEvents::PRODUCT_UPDATE_SEO, $clonedProductUpdateSeoEvent);
}
$event->setClonedProduct($clonedProductUpdateEvent->getProduct());
// Set clone's template
$clonedProductUpdateTemplateEvent = new ProductSetTemplateEvent(
$event->getClonedProduct(),
$event->getOriginalProduct()->getTemplateId(),
$originalProductDefaultPrice->getCurrencyId()
);
$this->eventDispatcher->dispatch(TheliaEvents::PRODUCT_SET_TEMPLATE, $clonedProductUpdateTemplateEvent);
}
public function cloneFeatureCombination(ProductCloneEvent $event)
{
// Get original product FeatureProduct list
$originalProductFeatureList = FeatureProductQuery::create()
->findByProductId($event->getOriginalProduct()->getId());
// Set clone product FeatureProducts
/** @var FeatureProduct $originalProductFeature */
foreach ($originalProductFeatureList as $originalProductFeature) {
// Get original FeatureAvI18n list
$originalProductFeatureAvI18nList = FeatureAvI18nQuery::create()
->findById($originalProductFeature->getFeatureAvId());
/** @var FeatureAvI18n $originalProductFeatureAvI18n */
foreach ($originalProductFeatureAvI18nList as $originalProductFeatureAvI18n) {
// Create a FeatureProduct for each FeatureAv (not for each FeatureAvI18n)
$clonedProductCreateFeatureEvent = new FeatureProductUpdateEvent(
$event->getClonedProduct()->getId(),
$originalProductFeature->getFeatureId(),
$originalProductFeature->getFeatureAvId()
);
$clonedProductCreateFeatureEvent->setLocale($originalProductFeatureAvI18n->getLocale());
// If it's a free text value, pass the FeatureAvI18n's title as featureValue to the event
if ($originalProductFeature->getFreeTextValue() !== null) {
$clonedProductCreateFeatureEvent->setFeatureValue($originalProductFeatureAvI18n->getTitle());
$clonedProductCreateFeatureEvent->setIsTextValue(true);
}
$this->eventDispatcher->dispatch(TheliaEvents::PRODUCT_FEATURE_UPDATE_VALUE, $clonedProductCreateFeatureEvent);
}
}
}
public function cloneAssociatedContent(ProductCloneEvent $event)
{
// Get original product associated contents
$originalProductAssocConts = ProductAssociatedContentQuery::create()
->findByProductId($event->getOriginalProduct()->getId());
// Set clone product associated contents
/** @var ProductAssociatedContent $originalProductAssocCont */
foreach ($originalProductAssocConts as $originalProductAssocCont) {
$clonedProductCreatePAC = new ProductAddContentEvent($event->getClonedProduct(), $originalProductAssocCont->getContentId());
$this->eventDispatcher->dispatch(TheliaEvents::PRODUCT_ADD_CONTENT, $clonedProductCreatePAC);
}
}
public function cloneAccessories(ProductCloneEvent $event)
{
// Get original product accessories
$originalProductAccessoryList = AccessoryQuery::create()
->findByProductId($event->getOriginalProduct()->getId());
// Set clone product accessories
/** @var Accessory $originalProductAccessory */
foreach ($originalProductAccessoryList as $originalProductAccessory) {
$clonedProductAddAccessoryEvent = new ProductAddAccessoryEvent($event->getClonedProduct(), $originalProductAccessory->getAccessory());
$this->eventDispatcher->dispatch(TheliaEvents::PRODUCT_ADD_ACCESSORY, $clonedProductAddAccessoryEvent);
}
}
/***************
* END CLONING *
***************/
/**
* Change a product
*
* @param \Thelia\Core\Event\Product\ProductUpdateEvent $event
* @throws PropelException
* @throws \Exception
*/
public function update(ProductUpdateEvent $event)
{
if (null !== $product = ProductQuery::create()->findPk($event->getProductId())) {
$con = Propel::getWriteConnection(ProductTableMap::DATABASE_NAME);
$con->beginTransaction();
try {
$prevRef = $product->getRef();
$product
->setDispatcher($this->eventDispatcher)
->setRef($event->getRef())
->setLocale($event->getLocale())
->setTitle($event->getTitle())
->setDescription($event->getDescription())
->setChapo($event->getChapo())
->setPostscriptum($event->getPostscriptum())
->setVisible($event->getVisible() ? 1 : 0)
->setVirtual($event->getVirtual() ? 1 : 0)
->setBrandId($event->getBrandId() <= 0 ? null : $event->getBrandId())
->save($con)
;
// Update default PSE (if product has no attributes and the product's ref change)
$defaultPseRefChange = $prevRef !== $product->getRef()
&& 0 === $product->getDefaultSaleElements()->countAttributeCombinations();
if ($defaultPseRefChange) {
$defaultPse = $product->getDefaultSaleElements();
$defaultPse->setRef($product->getRef())->save();
}
// Update default category (if required)
$product->setDefaultCategory($event->getDefaultCategory());
$event->setProduct($product);
$con->commit();
} catch (PropelException $e) {
$con->rollBack();
throw $e;
}
}
}
/**
* @param UpdateSeoEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
* @return mixed
*/
public function updateSeo(UpdateSeoEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
return $this->genericUpdateSeo(ProductQuery::create(), $event, $dispatcher);
}
/**
* Delete a product entry
*
* @param \Thelia\Core\Event\Product\ProductDeleteEvent $event
* @throws \Exception
*/
public function delete(ProductDeleteEvent $event)
{
if (null !== $product = ProductQuery::create()->findPk($event->getProductId())) {
$con = Propel::getWriteConnection(ProductTableMap::DATABASE_NAME);
$con->beginTransaction();
try {
$fileList = ['images' => [], 'documentList' => []];
// Get product's files to delete after product deletion
$fileList['images']['list'] = ProductImageQuery::create()
->findByProductId($event->getProductId());
$fileList['images']['type'] = TheliaEvents::IMAGE_DELETE;
$fileList['documentList']['list'] = ProductDocumentQuery::create()
->findByProductId($event->getProductId());
$fileList['documentList']['type'] = TheliaEvents::DOCUMENT_DELETE;
// Delete product
$product
->setDispatcher($this->eventDispatcher)
->delete($con)
;
$event->setProduct($product);
// Dispatch delete product's files event
foreach ($fileList as $fileTypeList) {
foreach ($fileTypeList['list'] as $fileToDelete) {
$fileDeleteEvent = new FileDeleteEvent($fileToDelete);
$this->eventDispatcher->dispatch($fileTypeList['type'], $fileDeleteEvent);
}
}
$con->commit();
} catch (\Exception $e) {
$con->rollBack();
throw $e;
}
}
}
/**
* Toggle product visibility. No form used here
*
* @param ProductToggleVisibilityEvent $event
*/
public function toggleVisibility(ProductToggleVisibilityEvent $event)
{
$product = $event->getProduct();
$product
->setDispatcher($this->eventDispatcher)
->setVisible($product->getVisible() ? false : true)
->save()
;
$event->setProduct($product);
}
/**
* Changes position, selecting absolute ou relative change.
*
* @param UpdatePositionEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function updatePosition(UpdatePositionEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$this->genericUpdateDelegatePosition(
ProductCategoryQuery::create()
->filterByProductId($event->getObjectId())
->filterByCategoryId($event->getReferrerId()),
$event,
$dispatcher
);
}
public function addContent(ProductAddContentEvent $event)
{
if (ProductAssociatedContentQuery::create()
->filterByContentId($event->getContentId())
->filterByProduct($event->getProduct())->count() <= 0) {
$content = new ProductAssociatedContent();
$content
->setDispatcher($this->eventDispatcher)
->setProduct($event->getProduct())
->setContentId($event->getContentId())
->save()
;
}
}
public function removeContent(ProductDeleteContentEvent $event)
{
$content = ProductAssociatedContentQuery::create()
->filterByContentId($event->getContentId())
->filterByProduct($event->getProduct())->findOne()
;
if ($content !== null) {
$content
->setDispatcher($this->eventDispatcher)
->delete()
;
}
}
public function addCategory(ProductAddCategoryEvent $event)
{
if (ProductCategoryQuery::create()
->filterByProduct($event->getProduct())
->filterByCategoryId($event->getCategoryId())
->count() <= 0) {
$productCategory = (new ProductCategory())
->setProduct($event->getProduct())
->setCategoryId($event->getCategoryId())
->setDefaultCategory(false);
$productCategory
->setPosition($productCategory->getNextPosition())
->save();
}
}
public function removeCategory(ProductDeleteCategoryEvent $event)
{
$productCategory = ProductCategoryQuery::create()
->filterByProduct($event->getProduct())
->filterByCategoryId($event->getCategoryId())
->findOne();
if ($productCategory != null) {
$productCategory->delete();
}
}
public function addAccessory(ProductAddAccessoryEvent $event)
{
if (AccessoryQuery::create()
->filterByAccessory($event->getAccessoryId())
->filterByProductId($event->getProduct()->getId())->count() <= 0) {
$accessory = new Accessory();
$accessory
->setDispatcher($this->eventDispatcher)
->setProductId($event->getProduct()->getId())
->setAccessory($event->getAccessoryId())
->save()
;
}
}
public function removeAccessory(ProductDeleteAccessoryEvent $event)
{
$accessory = AccessoryQuery::create()
->filterByAccessory($event->getAccessoryId())
->filterByProductId($event->getProduct()->getId())->findOne()
;
if ($accessory !== null) {
$accessory
->setDispatcher($this->eventDispatcher)
->delete()
;
}
}
public function setProductTemplate(ProductSetTemplateEvent $event)
{
$con = Propel::getWriteConnection(ProductTableMap::DATABASE_NAME);
$con->beginTransaction();
try {
$product = $event->getProduct();
// Check differences between current coobination and the next one, and clear obsoletes values.
$nextTemplateId = $event->getTemplateId();
$currentTemplateId = $product->getTemplateId();
// 1. Process product features.
$currentFeatures = FeatureTemplateQuery::create()
->filterByTemplateId($currentTemplateId)
->select([ FeatureTemplateTableMap::FEATURE_ID ])
->find($con);
$nextFeatures = FeatureTemplateQuery::create()
->filterByTemplateId($nextTemplateId)
->select([ FeatureTemplateTableMap::FEATURE_ID ])
->find($con);
// Find features values we shoud delete. To do this, we have to
// find all features in $currentFeatures that are not present in $nextFeatures
$featuresToDelete = array_diff($currentFeatures->getData(), $nextFeatures->getData());
// Delete obsolete features values
foreach ($featuresToDelete as $featureId) {
$this->eventDispatcher->dispatch(
TheliaEvents::PRODUCT_FEATURE_DELETE_VALUE,
new FeatureProductDeleteEvent($product->getId(), $featureId)
);
}
// 2. Process product Attributes
$currentAttributes = AttributeTemplateQuery::create()
->filterByTemplateId($currentTemplateId)
->select([ AttributeTemplateTableMap::ATTRIBUTE_ID ])
->find($con);
$nextAttributes = AttributeTemplateQuery::create()
->filterByTemplateId($nextTemplateId)
->select([ AttributeTemplateTableMap::ATTRIBUTE_ID ])
->find($con);
// Find attributes values we shoud delete. To do this, we have to
// find all attributes in $currentAttributes that are not present in $nextAttributes
$attributesToDelete = array_diff($currentAttributes->getData(), $nextAttributes->getData());
// Find PSE which includes $attributesToDelete for the current product/
$pseToDelete = ProductSaleElementsQuery::create()
->filterByProductId($product->getId())
->useAttributeCombinationQuery()
->filterByAttributeId($attributesToDelete, Criteria::IN)
->endUse()
->select([ ProductSaleElementsTableMap::ID ])
->find();
// Delete obsolete PSEs
foreach ($pseToDelete->getData() as $pseId) {
$this->eventDispatcher->dispatch(
TheliaEvents::PRODUCT_DELETE_PRODUCT_SALE_ELEMENT,
new ProductSaleElementDeleteEvent(
$pseId,
CurrencyModel::getDefaultCurrency()->getId()
)
);
}
// Update the product template
$template_id = $event->getTemplateId();
// Set it to null if it's zero.
if ($template_id <= 0) {
$template_id = null;
}
$product->setTemplateId($template_id)->save($con);
$product->clearProductSaleElementss();
$event->setProduct($product);
// Store all the stuff !
$con->commit();
} catch (\Exception $ex) {
$con->rollBack();
throw $ex;
}
}
/**
* Changes accessry position, selecting absolute ou relative change.
*
* @param UpdatePositionEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
* @return Object
*/
public function updateAccessoryPosition(UpdatePositionEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
return $this->genericUpdatePosition(AccessoryQuery::create(), $event, $dispatcher);
}
/**
* Changes position, selecting absolute ou relative change.
*
* @param UpdatePositionEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
* @return Object
*/
public function updateContentPosition(UpdatePositionEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
return $this->genericUpdatePosition(ProductAssociatedContentQuery::create(), $event, $dispatcher);
}
/**
* Update the value of a product feature.
*
* @param FeatureProductUpdateEvent $event
*/
public function updateFeatureProductValue(FeatureProductUpdateEvent $event)
{
// Prepare the FeatureAv's ID
$featureAvId = $event->getFeatureValue();
// Search for existing FeatureProduct
$featureProductQuery = FeatureProductQuery::create()
->filterByProductId($event->getProductId())
->filterByFeatureId($event->getFeatureId())
;
// If it's not a free text value, we can filter by the event's featureValue (which is an ID)
if ($event->getFeatureValue() !== null && $event->getIsTextValue() === false) {
$featureProductQuery->filterByFeatureAvId($featureAvId);
}
$featureProduct = $featureProductQuery->findOne();
// If the FeatureProduct does not exist, create it
if ($featureProduct === null) {
$featureProduct = new FeatureProduct();
$featureProduct
->setDispatcher($this->eventDispatcher)
->setProductId($event->getProductId())
->setFeatureId($event->getFeatureId())
;
// If it's a free text value, create a FeatureAv to handle i18n
if ($event->getIsTextValue() === true) {
$featureProduct->setFreeTextValue(true);
$createFeatureAvEvent = new FeatureAvCreateEvent();
$createFeatureAvEvent
->setFeatureId($event->getFeatureId())
->setLocale($event->getLocale())
->setTitle($event->getFeatureValue());
$this->eventDispatcher->dispatch(TheliaEvents::FEATURE_AV_CREATE, $createFeatureAvEvent);
$featureAvId = $createFeatureAvEvent->getFeatureAv()->getId();
}
} // Else if the FeatureProduct exists and is a free text value
elseif ($featureProduct !== null && $event->getIsTextValue() === true) {
// Get the FeatureAv
$freeTextFeatureAv = FeatureAvQuery::create()
->filterByFeatureProduct($featureProduct)
->findOneByFeatureId($event->getFeatureId());
// Get the FeatureAvI18n by locale
$freeTextFeatureAvI18n = FeatureAvI18nQuery::create()
->filterById($freeTextFeatureAv->getId())
->findOneByLocale($event->getLocale());
// Nothing found for this lang and the new value is not empty : create FeatureAvI18n
if ($freeTextFeatureAvI18n === null && !empty($featureAvId)) {
$featureAvI18n = new FeatureAvI18n();
$featureAvI18n
->setId($freeTextFeatureAv->getId())
->setLocale($event->getLocale())
->setTitle($event->getFeatureValue())
->save();
$featureAvId = $featureAvI18n->getId();
} // Else if i18n exists but new value is empty : delete FeatureAvI18n
elseif ($freeTextFeatureAvI18n !== null && empty($featureAvId)) {
$freeTextFeatureAvI18n->delete();
// Check if there are still some FeatureAvI18n for this FeatureAv
$freeTextFeatureAvI18ns = FeatureAvI18nQuery::create()
->findById($freeTextFeatureAv->getId());
// If there are no more FeatureAvI18ns for this FeatureAv, remove the corresponding FeatureProduct & FeatureAv
if (count($freeTextFeatureAvI18ns) == 0) {
$deleteFeatureProductEvent = new FeatureProductDeleteEvent($event->getProductId(), $event->getFeatureId());
$this->eventDispatcher->dispatch(TheliaEvents::PRODUCT_FEATURE_DELETE_VALUE, $deleteFeatureProductEvent);
$deleteFeatureAvEvent = new FeatureAvDeleteEvent($freeTextFeatureAv->getId());
$this->eventDispatcher->dispatch(TheliaEvents::FEATURE_AV_DELETE, $deleteFeatureAvEvent);
return;
}
} // Else if a FeatureAvI18n is found and the new value is not empty : update existing FeatureAvI18n
elseif ($freeTextFeatureAvI18n !== null && !empty($featureAvId)) {
$freeTextFeatureAvI18n->setTitle($featureAvId);
$freeTextFeatureAvI18n->save();
$featureAvId = $freeTextFeatureAvI18n->getId();
}
} // Else the FeatureProduct exists and is not a free text value
else {
$featureAvId = $event->getFeatureValue();
}
$featureProduct->setFeatureAvId($featureAvId);
$featureProduct->save();
$event->setFeatureProduct($featureProduct);
}
/**
* Delete a product feature value
*
* @param FeatureProductDeleteEvent $event
*/
public function deleteFeatureProductValue(FeatureProductDeleteEvent $event)
{
FeatureProductQuery::create()
->filterByProductId($event->getProductId())
->filterByFeatureId($event->getFeatureId())
->delete()
;
}
public function deleteImagePSEAssociations(FileDeleteEvent $event)
{
$model = $event->getFileToDelete();
if ($model instanceof ProductImage) {
$model->getProductSaleElementsProductImages()->delete();
}
}
public function deleteDocumentPSEAssociations(FileDeleteEvent $event)
{
$model = $event->getFileToDelete();
if ($model instanceof ProductDocument) {
$model->getProductSaleElementsProductDocuments()->delete();
}
}
/**
* When a feature is removed from a template, the products which are using this feature should be updated.
*
* @param TemplateDeleteFeatureEvent $event
* @param string $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function deleteTemplateFeature(TemplateDeleteFeatureEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
// Detete the removed feature in all products which are using this template
$products = ProductQuery::create()
->filterByTemplateId($event->getTemplate()->getId())
->find()
;
foreach ($products as $product) {
$dispatcher->dispatch(
TheliaEvents::PRODUCT_FEATURE_DELETE_VALUE,
new FeatureProductDeleteEvent($product->getId(), $event->getFeatureId())
);
}
}
/**
* When an attribute is removed from a template, the conbinations and PSE of products which are using this template
* should be updated.
*
* @param TemplateDeleteAttributeEvent $event
* @param string $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function deleteTemplateAttribute(TemplateDeleteAttributeEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
// Detete the removed attribute in all products which are using this template
$pseToDelete = ProductSaleElementsQuery::create()
->useProductQuery()
->filterByTemplateId($event->getTemplate()->getId())
->endUse()
->useAttributeCombinationQuery()
->filterByAttributeId($event->getAttributeId())
->endUse()
->select([ ProductSaleElementsTableMap::ID ])
->find();
$currencyId = CurrencyModel::getDefaultCurrency()->getId();
foreach ($pseToDelete->getData() as $pseId) {
$dispatcher->dispatch(
TheliaEvents::PRODUCT_DELETE_PRODUCT_SALE_ELEMENT,
new ProductSaleElementDeleteEvent(
$pseId,
$currencyId
)
);
}
}
/**
* Check if is a product view and if product_id is visible
*
* @param ViewCheckEvent $event
* @param string $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function viewCheck(ViewCheckEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if ($event->getView() == 'product') {
$product = ProductQuery::create()
->filterById($event->getViewId())
->filterByVisible(1)
->count();
if ($product == 0) {
$dispatcher->dispatch(TheliaEvents::VIEW_PRODUCT_ID_NOT_VISIBLE, $event);
}
}
}
/**
* @param ViewCheckEvent $event
* @throws NotFoundHttpException
*/
public function viewProductIdNotVisible(ViewCheckEvent $event)
{
throw new NotFoundHttpException();
}
/**
* {@inheritDoc}
*/
public static function getSubscribedEvents()
{
return array(
TheliaEvents::PRODUCT_CREATE => array("create", 128),
TheliaEvents::PRODUCT_CLONE => array("cloneProduct", 128),
TheliaEvents::PRODUCT_UPDATE => array("update", 128),
TheliaEvents::PRODUCT_DELETE => array("delete", 128),
TheliaEvents::PRODUCT_TOGGLE_VISIBILITY => array("toggleVisibility", 128),
TheliaEvents::PRODUCT_UPDATE_POSITION => array("updatePosition", 128),
TheliaEvents::PRODUCT_UPDATE_SEO => array("updateSeo", 128),
TheliaEvents::PRODUCT_ADD_CONTENT => array("addContent", 128),
TheliaEvents::PRODUCT_REMOVE_CONTENT => array("removeContent", 128),
TheliaEvents::PRODUCT_UPDATE_CONTENT_POSITION => array("updateContentPosition", 128),
TheliaEvents::PRODUCT_ADD_ACCESSORY => array("addAccessory", 128),
TheliaEvents::PRODUCT_REMOVE_ACCESSORY => array("removeAccessory", 128),
TheliaEvents::PRODUCT_UPDATE_ACCESSORY_POSITION => array("updateAccessoryPosition", 128),
TheliaEvents::PRODUCT_ADD_CATEGORY => array("addCategory", 128),
TheliaEvents::PRODUCT_REMOVE_CATEGORY => array("removeCategory", 128),
TheliaEvents::PRODUCT_SET_TEMPLATE => array("setProductTemplate", 128),
TheliaEvents::PRODUCT_FEATURE_UPDATE_VALUE => array("updateFeatureProductValue", 128),
TheliaEvents::PRODUCT_FEATURE_DELETE_VALUE => array("deleteFeatureProductValue", 128),
TheliaEvents::TEMPLATE_DELETE_ATTRIBUTE => array("deleteTemplateAttribute", 128),
TheliaEvents::TEMPLATE_DELETE_FEATURE => array("deleteTemplateFeature", 128),
// Those two have to be executed before
TheliaEvents::IMAGE_DELETE => array("deleteImagePSEAssociations", 192),
TheliaEvents::DOCUMENT_DELETE => array("deleteDocumentPSEAssociations", 192),
TheliaEvents::VIEW_CHECK => array('viewCheck', 128),
TheliaEvents::VIEW_PRODUCT_ID_NOT_VISIBLE => array('viewProductIdNotVisible', 128),
);
}
}

View File

@@ -0,0 +1,484 @@
<?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\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;
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
*/
public function create(ProductSaleElementCreateEvent $event)
{
$con = Propel::getWriteConnection(ProductSaleElementsTableMap::DATABASE_NAME);
$con->beginTransaction();
try {
// Check if we have a PSE without combination, this is the "default" PSE. Attach the combination to this PSE
$salesElement = ProductSaleElementsQuery::create()
->filterByProductId($event->getProduct()->getId())
->joinAttributeCombination(null, Criteria::LEFT_JOIN)
->add(AttributeCombinationTableMap::PRODUCT_SALE_ELEMENTS_ID, null, Criteria::ISNULL)
->findOne($con);
if ($salesElement == null) {
// Create a new default product sale element
$salesElement = $event->getProduct()->createProductSaleElement($con, 0, 0, 0, $event->getCurrencyId(), false);
} else {
// This (new) one is the default
$salesElement->setIsDefault(true)->save($con);
}
// Attach combination, if defined.
$combinationAttributes = $event->getAttributeAvList();
if (count($combinationAttributes) > 0) {
foreach ($combinationAttributes as $attributeAvId) {
$attributeAv = AttributeAvQuery::create()->findPk($attributeAvId);
if ($attributeAv !== null) {
$attributeCombination = new AttributeCombination();
$attributeCombination
->setAttributeAvId($attributeAvId)
->setAttribute($attributeAv->getAttribute())
->setProductSaleElements($salesElement)
->save($con);
}
}
}
$event->setProductSaleElement($salesElement);
// Store all the stuff !
$con->commit();
} catch (\Exception $ex) {
$con->rollback();
throw $ex;
}
}
/**
* Update an existing product sale element
*
* @param ProductSaleElementUpdateEvent $event
* @throws \Exception
*/
public function update(ProductSaleElementUpdateEvent $event)
{
$salesElement = ProductSaleElementsQuery::create()->findPk($event->getProductSaleElementId());
$con = Propel::getWriteConnection(ProductSaleElementsTableMap::DATABASE_NAME);
$con->beginTransaction();
try {
// Update the product's tax rule
$event->getProduct()->setTaxRuleId($event->getTaxRuleId())->save($con);
// If product sale element is not defined, create it.
if ($salesElement == null) {
$salesElement = new ProductSaleElements();
$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())
->setQuantity($event->getQuantity())
->setPromo($event->getOnsale())
->setNewness($event->getIsnew())
->setWeight($event->getWeight())
->setIsDefault($event->getIsDefault())
->setEanCode($event->getEanCode())
->save()
;
// Update/create price for current currency
$productPrice = ProductPriceQuery::create()
->filterByCurrencyId($event->getCurrencyId())
->filterByProductSaleElementsId($salesElement->getId())
->findOne($con);
// If price is not defined, create it.
if ($productPrice == null) {
$productPrice = new ProductPrice();
$productPrice
->setProductSaleElements($salesElement)
->setCurrencyId($event->getCurrencyId())
;
}
// Check if we have to store the price
$productPrice->setFromDefaultCurrency($event->getFromDefaultCurrency());
if ($event->getFromDefaultCurrency() == 0) {
// Store the price
$productPrice
->setPromoPrice($event->getSalePrice())
->setPrice($event->getPrice())
;
} else {
// Do not store the price.
$productPrice
->setPromoPrice(0)
->setPrice(0)
;
}
$productPrice->save($con);
// Store all the stuff !
$con->commit();
} catch (\Exception $ex) {
$con->rollback();
throw $ex;
}
}
/**
* Delete a product sale element
*
* @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);
$con->beginTransaction();
try {
$pse->delete($con);
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
$newDefaultPse = ProductSaleElementsQuery::create()
->filterByProductId($product->getId())
->filterById($pse->getId(), Criteria::NOT_EQUAL)
->orderByCreatedAt(Criteria::DESC)
->findOne($con)
;
if (null !== $newDefaultPse) {
$newDefaultPse->setIsDefault(true)->save($con);
}
}
// Store all the stuff !
$con->commit();
} catch (\Exception $ex) {
$con->rollback();
throw $ex;
}
}
}
/**
* Generate combinations. All existing combinations for the product are deleted.
*
* @param ProductCombinationGenerationEvent $event
* @throws \Exception
*/
public function generateCombinations(ProductCombinationGenerationEvent $event)
{
$con = Propel::getWriteConnection(ProductSaleElementsTableMap::DATABASE_NAME);
$con->beginTransaction();
try {
// Delete all product's productSaleElement
ProductSaleElementsQuery::create()->filterByProductId($event->product->getId())->delete();
$isDefault = true;
// 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()
);
$isDefault = false;
$this->createCombination($con, $saleElement, $combinationAttributesAvIds);
}
// Store all the stuff !
$con->commit();
} catch (\Exception $ex) {
$con->rollback();
throw $ex;
}
}
/**
* Create a combination for a given product sale element
*
* @param ConnectionInterface $con the Propel connection
* @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) {
$attributeCombination = new AttributeCombination();
$attributeCombination
->setAttributeAvId($attributeAvId)
->setAttribute($attributeAv->getAttribute())
->setProductSaleElements($salesElement)
->save($con);
}
}
}
/*******************
* 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}
*/
public static function getSubscribedEvents()
{
return array(
TheliaEvents::PRODUCT_ADD_PRODUCT_SALE_ELEMENT => array("create", 128),
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

@@ -0,0 +1,151 @@
<?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\Profile\ProfileEvent;
use Thelia\Core\Event\TheliaEvents;
use Thelia\Core\Security\AccessManager;
use Thelia\Model\ModuleQuery;
use Thelia\Model\Profile as ProfileModel;
use Thelia\Model\ProfileModule;
use Thelia\Model\ProfileModuleQuery;
use Thelia\Model\ProfileQuery;
use Thelia\Model\ProfileResource;
use Thelia\Model\ProfileResourceQuery;
use Thelia\Model\ResourceQuery;
class Profile extends BaseAction implements EventSubscriberInterface
{
/**
* @param ProfileEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function create(ProfileEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$profile = new ProfileModel();
$profile
->setDispatcher($dispatcher)
->setCode($event->getCode())
->setLocale($event->getLocale())
->setTitle($event->getTitle())
->setChapo($event->getChapo())
->setDescription($event->getDescription())
->setPostscriptum($event->getPostscriptum())
;
$profile->save();
$event->setProfile($profile);
}
/**
* @param ProfileEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function update(ProfileEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if (null !== $profile = ProfileQuery::create()->findPk($event->getId())) {
$profile
->setDispatcher($dispatcher)
->setLocale($event->getLocale())
->setTitle($event->getTitle())
->setChapo($event->getChapo())
->setDescription($event->getDescription())
->setPostscriptum($event->getPostscriptum())
;
$profile->save();
$event->setProfile($profile);
}
}
/**
* @param ProfileEvent $event
*/
public function updateResourceAccess(ProfileEvent $event)
{
if (null !== $profile = ProfileQuery::create()->findPk($event->getId())) {
ProfileResourceQuery::create()->filterByProfileId($event->getId())->delete();
foreach ($event->getResourceAccess() as $resourceCode => $accesses) {
$manager = new AccessManager(0);
$manager->build($accesses);
$profileResource = new ProfileResource();
$profileResource->setProfileId($event->getId())
->setResource(ResourceQuery::create()->findOneByCode($resourceCode))
->setAccess($manager->getAccessValue());
$profileResource->save();
}
$event->setProfile($profile);
}
}
/**
* @param ProfileEvent $event
*/
public function updateModuleAccess(ProfileEvent $event)
{
if (null !== $profile = ProfileQuery::create()->findPk($event->getId())) {
ProfileModuleQuery::create()->filterByProfileId($event->getId())->delete();
foreach ($event->getModuleAccess() as $moduleCode => $accesses) {
$manager = new AccessManager(0);
$manager->build($accesses);
$profileModule = new ProfileModule();
$profileModule->setProfileId($event->getId())
->setModule(ModuleQuery::create()->findOneByCode($moduleCode))
->setAccess($manager->getAccessValue());
$profileModule->save();
}
$event->setProfile($profile);
}
}
/**
* @param ProfileEvent $event
*/
public function delete(ProfileEvent $event)
{
if (null !== $profile = ProfileQuery::create()->findPk($event->getId())) {
$profile
->delete()
;
$event->setProfile($profile);
}
}
/**
* {@inheritDoc}
*/
public static function getSubscribedEvents()
{
return array(
TheliaEvents::PROFILE_CREATE => array("create", 128),
TheliaEvents::PROFILE_UPDATE => array("update", 128),
TheliaEvents::PROFILE_DELETE => array("delete", 128),
TheliaEvents::PROFILE_RESOURCE_ACCESS_UPDATE => array("updateResourceAccess", 128),
TheliaEvents::PROFILE_MODULE_ACCESS_UPDATE => array("updateModuleAccess", 128),
);
}
}

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

@@ -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\EventSubscriberInterface;
use Thelia\Core\Event\ShippingZone\ShippingZoneAddAreaEvent;
use Thelia\Core\Event\ShippingZone\ShippingZoneRemoveAreaEvent;
use Thelia\Core\Event\TheliaEvents;
use Thelia\Model\AreaDeliveryModule;
use Thelia\Model\AreaDeliveryModuleQuery;
/**
* Class ShippingZone
* @package Thelia\Action
* @author Manuel Raynaud <manu@raynaud.io>
*/
class ShippingZone extends BaseAction implements EventSubscriberInterface
{
public function addArea(ShippingZoneAddAreaEvent $event)
{
$areaDelivery = new AreaDeliveryModule();
$areaDelivery
->setAreaId($event->getAreaId())
->setDeliveryModuleId($event->getShippingZoneId())
->save();
}
public function removeArea(ShippingZoneRemoveAreaEvent $event)
{
$areaDelivery = AreaDeliveryModuleQuery::create()
->filterByAreaId($event->getAreaId())
->filterByDeliveryModuleId($event->getShippingZoneId())
->findOne();
if ($areaDelivery) {
$areaDelivery->delete();
} else {
throw new \RuntimeException(sprintf('areaDeliveryModule not found with area_id = %d and delivery_module_id = %d', $event->getAreaId(), $event->getShippingZoneId()));
}
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents()
{
return array(
TheliaEvents::SHIPPING_ZONE_ADD_AREA => array('addArea', 128),
TheliaEvents::SHIPPING_ZONE_REMOVE_AREA => array('removeArea', 128),
);
}
}

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

@@ -0,0 +1,95 @@
<?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\Tax\TaxEvent;
use Thelia\Core\Event\TheliaEvents;
use Thelia\Model\Tax as TaxModel;
use Thelia\Model\TaxQuery;
class Tax extends BaseAction implements EventSubscriberInterface
{
/**
* @param TaxEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function create(TaxEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$tax = new TaxModel();
$tax
->setDispatcher($dispatcher)
->setRequirements($event->getRequirements())
->setType($event->getType())
->setLocale($event->getLocale())
->setTitle($event->getTitle())
->setDescription($event->getDescription())
;
$tax->save();
$event->setTax($tax);
}
/**
* @param TaxEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function update(TaxEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if (null !== $tax = TaxQuery::create()->findPk($event->getId())) {
$tax
->setDispatcher($dispatcher)
->setRequirements($event->getRequirements())
->setType($event->getType())
->setLocale($event->getLocale())
->setTitle($event->getTitle())
->setDescription($event->getDescription())
;
$tax->save();
$event->setTax($tax);
}
}
/**
* @param TaxEvent $event
*/
public function delete(TaxEvent $event)
{
if (null !== $tax = TaxQuery::create()->findPk($event->getId())) {
$tax
->delete()
;
$event->setTax($tax);
}
}
/**
* {@inheritDoc}
*/
public static function getSubscribedEvents()
{
return array(
TheliaEvents::TAX_CREATE => array("create", 128),
TheliaEvents::TAX_UPDATE => array("update", 128),
TheliaEvents::TAX_DELETE => array("delete", 128),
);
}
}

View File

@@ -0,0 +1,205 @@
<?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\Tax\TaxRuleEvent;
use Thelia\Core\Event\TheliaEvents;
use Thelia\Model\TaxRuleCountry;
use Thelia\Model\TaxRuleCountryQuery;
use Thelia\Model\TaxRule as TaxRuleModel;
use Thelia\Model\TaxRuleQuery;
class TaxRule extends BaseAction implements EventSubscriberInterface
{
/**
* @param TaxRuleEvent $event
*/
public function create(TaxRuleEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$taxRule = new TaxRuleModel();
$taxRule
->setDispatcher($dispatcher)
->setLocale($event->getLocale())
->setTitle($event->getTitle())
->setDescription($event->getDescription())
;
$taxRule->save();
$event->setTaxRule($taxRule)->setId($taxRule->getId());
}
/**
* @param TaxRuleEvent $event
*/
public function update(TaxRuleEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if (null !== $taxRule = TaxRuleQuery::create()->findPk($event->getId())) {
$taxRule
->setDispatcher($dispatcher)
->setLocale($event->getLocale())
->setTitle($event->getTitle())
->setDescription($event->getDescription())
->save()
;
$event->setTaxRule($taxRule);
}
}
/**
* @param TaxRuleEvent $event
*/
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());
/* 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 ($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($countryId)
->setStateId($stateId ?: null)
->setTaxId($samePositionTax)
->setPosition($position);
$taxModel->save();
}
} else {
$taxModel = new TaxRuleCountry();
$taxModel->setTaxRule($taxRule)
->setCountryId($countryId)
->setStateId($stateId ?: null)
->setTaxId($tax)
->setPosition($position);
$taxModel->save();
}
$position++;
}
}
$event->setTaxRule($taxRule);
}
}
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()
;
$event->setTaxRule($taxRule);
}
}
/**
* @param TaxRuleEvent $event
*/
public function setDefault(TaxRuleEvent $event)
{
if (null !== $taxRule = TaxRuleQuery::create()->findPk($event->getId())) {
TaxRuleQuery::create()->update(array(
"IsDefault" => 0
));
$taxRule->setIsDefault(1)->save();
$event->setTaxRule($taxRule);
}
}
/**
* {@inheritDoc}
*/
public static function getSubscribedEvents()
{
return array(
TheliaEvents::TAX_RULE_CREATE => array("create", 128),
TheliaEvents::TAX_RULE_UPDATE => array("update", 128),
TheliaEvents::TAX_RULE_TAXES_UPDATE => array("updateTaxes", 128),
TheliaEvents::TAX_RULE_DELETE => array("delete", 128),
TheliaEvents::TAX_RULE_SET_DEFAULT => array("setDefault", 128),
);
}
}

View File

@@ -0,0 +1,288 @@
<?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\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Thelia\Core\Event\Template\TemplateAddAttributeEvent;
use Thelia\Core\Event\Template\TemplateAddFeatureEvent;
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
{
/**
* Create a new template entry
*
* @param \Thelia\Core\Event\Template\TemplateCreateEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function create(TemplateCreateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$template = new TemplateModel();
$template
->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, $eventName, EventDispatcherInterface $dispatcher)
{
if (null !== $template = TemplateQuery::create()->findPk($event->getTemplateId())) {
$template
->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, $eventName, EventDispatcherInterface $dispatcher)
{
if (null !== ($template = TemplateQuery::create()->findPk($event->getTemplateId()))) {
// Check if template is used by a product
$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($productCount);
}
}
public function addAttribute(TemplateAddAttributeEvent $event)
{
if (null === AttributeTemplateQuery::create()
->filterByAttributeId($event->getAttributeId())
->filterByTemplate($event->getTemplate())
->findOne()) {
$attributeTemplate = new AttributeTemplate();
$attributeTemplate
->setAttributeId($event->getAttributeId())
->setTemplate($event->getTemplate())
->save()
;
}
}
/**
* Changes position, selecting absolute ou relative change.
*
* @param UpdatePositionEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function updateAttributePosition(UpdatePositionEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$this->genericUpdatePosition(AttributeTemplateQuery::create(), $event, $dispatcher);
}
/**
* Changes position, selecting absolute ou relative change.
*
* @param UpdatePositionEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function updateFeaturePosition(UpdatePositionEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$this->genericUpdatePosition(FeatureTemplateQuery::create(), $event, $dispatcher);
}
public function deleteAttribute(TemplateDeleteAttributeEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$attributeTemplate = AttributeTemplateQuery::create()
->filterByAttributeId($event->getAttributeId())
->filterByTemplate($event->getTemplate())->findOne()
;
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()
) {
$featureTemplate = new FeatureTemplate();
$featureTemplate
->setFeatureId($event->getFeatureId())
->setTemplate($event->getTemplate())
->save()
;
}
}
public function deleteFeature(TemplateDeleteFeatureEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$featureTemplate = FeatureTemplateQuery::create()
->filterByFeatureId($event->getFeatureId())
->filterByTemplate($event->getTemplate())->findOne()
;
if ($featureTemplate !== null) {
$featureTemplate
->setDispatcher($dispatcher)
->delete();
} else {
// Prevent event propagation
$event->stopPropagation();
}
}
/**
* {@inheritDoc}
*/
public static function getSubscribedEvents()
{
return array(
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

@@ -0,0 +1,54 @@
<?php
/*************************************************************************************/
/* This file is part of the Thelia package. */
/* */
/* Copyright (c) OpenStudio */
/* email : dev@thelia.net */
/* web : http://www.thelia.net */
/* */
/* For the full copyright and license information, please view the LICENSE.txt */
/* file that was distributed with this source code. */
/*************************************************************************************/
namespace Thelia\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 Thelia\Core\HttpFoundation\Session\Session;
use Thelia\Core\Event\TheliaEvents;
use Thelia\Core\Event\Cart\CartEvent;
/**
* managed cart
*
* Trait CartTrait
* @package Thelia\Cart
* @author Manuel Raynaud <manu@raynaud.io>
*
* @deprecated CartTrait is deprecated, please use Session::getSessionCart method instead
*/
trait CartTrait
{
/**
*
* search if cart already exists in session. If not try to create a new one or duplicate an old one.
*
* @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)
{
trigger_error(
'CartTrait is deprecated, please use Session::getSessionCart method instead',
E_USER_DEPRECATED
);
return $request->getSession()->getSessionCart($dispatcher);
}
}

View File

@@ -0,0 +1,98 @@
<?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\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;
/**
* command line for updating admin password
*
* php Thelia admin:updatePassword
*
* Class AdminUpdatePasswordCommand
* @package Thelia\Command
* @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.
*/
protected function configure()
{
$this
->setName('admin:updatePassword')
->setDescription('change administrator password')
->setHelp('The <info>admin:updatePassword</info> command allows you to change the password for a given administrator')
->addArgument(
'login',
InputArgument::REQUIRED,
'Login for administrator you want to change the password'
)
->addOption(
'password',
null,
InputOption::VALUE_REQUIRED,
'Desired password. If this option is omitted, a random password is generated and shown in this prompt after'
)
;
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$this->init();
$login = $input->getArgument('login');
if (null === $admin = AdminQuery::create()->filterByLogin($login)->findOne()) {
throw new \RuntimeException(sprintf('Admin with login %s does not exists', $login));
}
$password = $input->getOption('password') ?: Password::generateRandom();
$event = new AdministratorUpdatePasswordEvent($admin);
$event->setPassword($password);
$this->getDispatcher()->dispatch(TheliaEvents::ADMINISTRATOR_UPDATEPASSWORD, $event);
$output->writeln(array(
'',
sprintf('<info>admin %s password updated</info>', $login),
sprintf('<info>new password is : %s</info>', $password),
''
));
}
}

View File

@@ -0,0 +1,72 @@
<?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;
/**
* base class for module commands
*
* Class BaseModuleGenerate
* @package Thelia\Command
* @author Manuel Raynaud <manu@raynaud.io>
*/
abstract class BaseModuleGenerate extends ContainerAwareCommand
{
protected $module;
protected $moduleDirectory;
protected $reservedKeyWords = array(
'thelia'
);
protected $neededDirectories = array(
'Config',
'Model',
'Loop',
'Command',
'Controller',
'EventListeners',
'I18n',
'templates',
'Hook',
);
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));
}
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

@@ -0,0 +1,112 @@
<?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\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 <manu@raynaud.io>
*
*/
class CacheClear extends ContainerAwareCommand
{
protected function configure()
{
$this
->setName("cache:clear")
->setDescription("Invalidate all caches")
->addOption(
"without-assets",
null,
InputOption::VALUE_NONE,
"do not clear the assets cache in the web space"
)
->addOption(
'with-images',
null,
InputOption::VALUE_NONE,
'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 . ConfigQuery::read('asset_dir_from_web_root', 'assets'), $output);
}
if ($input->getOption('with-images')) {
$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)
{
$output->writeln(sprintf("Clearing cache in <info>%s</info> directory", $dir));
try {
$cacheEvent = new CacheEvent($dir);
$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)) {
$output->writeln(sprintf("<info>%s cache dir already cleared</info>", $dir));
return;
}
throw $e;
} catch (IOException $e) {
$output->writeln(sprintf("Error during clearing of cache : %s", $e->getMessage()));
}
$output->writeln(sprintf("<info>%s cache directory cleared successfully</info>", $dir));
}
}

View File

@@ -0,0 +1,53 @@
<?php
/*************************************************************************************/
/* This file is part of the Thelia package. */
/* */
/* Copyright (c) OpenStudio */
/* email : dev@thelia.net */
/* web : http://www.thelia.net */
/* */
/* For the full copyright and license information, please view the LICENSE.txt */
/* file that was distributed with this source code. */
/*************************************************************************************/
namespace Thelia\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Thelia\Core\Event\Image\ImageEvent;
use Thelia\Core\HttpFoundation\Request;
use Symfony\Component\Console\Input\InputArgument;
use Thelia\Core\Event\TheliaEvents;
class ClearImageCache extends ContainerAwareCommand
{
protected function configure()
{
$this
->setName("image-cache:clear")
->setDescription("Empty part or whole web space image cache")
->addArgument("subdir", InputArgument::OPTIONAL, "Clear only the specified subdirectory")
;
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$request = new Request();
try {
$event = new ImageEvent($request);
$subdir = $input->getArgument('subdir');
if (! is_null($subdir)) {
$event->setCacheSubdirectory($subdir);
}
$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()));
}
}
}

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

@@ -0,0 +1,133 @@
<?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\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
{
/**
* @var ContainerInterface
*/
private $container;
/**
* @return ContainerInterface
*/
protected function getContainer()
{
if (null === $this->container) {
/** @var Application $application */
$application = $this->getApplication();
$this->container = $application->getKernel()->getContainer();
}
return $this->container;
}
/**
* @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

@@ -0,0 +1,205 @@
<?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\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
{
/**
* Configure the command
*/
protected function configure()
{
$this
->setName("admin:create")
->setDescription("Create a new administrator user")
->setHelp("The <info>admin:create</info> command create a new administration user.")
->addOption(
'login_name',
null,
InputOption::VALUE_OPTIONAL,
'Admin login name',
null
)
->addOption(
'first_name',
null,
InputOption::VALUE_OPTIONAL,
'User first name',
null
)
->addOption(
"last_name",
null,
InputOption::VALUE_OPTIONAL,
'User last name',
null
)
->addOption(
"email",
null,
InputOption::VALUE_OPTIONAL,
'Admin email address',
null
)
->addOption(
"locale",
null,
InputOption::VALUE_OPTIONAL,
'Preferred locale (default: en_US)',
null
)
->addOption(
'password',
null,
InputOption::VALUE_OPTIONAL,
'Password',
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();
$output->writeln(array(
"",
"<info>User ".$admin->getLogin()." successfully created.</info>",
""
));
}
protected function enterData(
QuestionHelper $helper,
InputInterface $input,
OutputInterface $output,
$label,
$errorMessage,
$hidden = false
) {
$question = new Question($this->decorateInfo($label));
if ($hidden) {
$question->setHidden(true);
$question->setHiddenFallback(false);
}
$question->setValidator(function ($value) use (&$errorMessage) {
if (trim($value) == '') {
throw new \Exception($errorMessage);
}
return $value;
});
return $helper->ask($input, $output, $question);
}
/**
* Ask to user all needed information
*
* @param InputInterface $input
* @param OutputInterface $output
* @return array
*/
protected function getAdminInfo(InputInterface $input, OutputInterface $output)
{
/** @var QuestionHelper $helper */
$helper = $this->getHelper('question');
$admin = new Admin();
$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($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;
}
$output->writeln("Passwords are different, please try again.");
} while (true);
$admin->setProfile(null);
return $admin;
}
protected function decorateInfo($text)
{
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

@@ -0,0 +1,101 @@
<?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\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Thelia\Core\Security\Resource\AdminResources;
use Thelia\Model\Map\ResourceI18nTableMap;
use Thelia\Model\Map\ResourceTableMap;
class GenerateResources extends ContainerAwareCommand
{
/**
* Configure the command
*/
protected function configure()
{
$this
->setName("thelia:generate-resources")
->setDescription("Outputs admin resources")
->setHelp("The <info>thelia:generate-resources</info> outputs admin resources.")
->addOption(
'output',
null,
InputOption::VALUE_OPTIONAL,
'Output format amid (string, sql, sql-i18n)',
null
)
;
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$class = new \ReflectionClass('Thelia\Core\Security\Resource\AdminResources');
$constants = $class->getConstants();
if (count($constants) == 0) {
throw new \RuntimeException('No resources found');
}
switch ($input->getOption("output")) {
case 'sql':
$output->writeln(
'INSERT INTO ' . ResourceTableMap::TABLE_NAME . ' (`id`, `code`, `created_at`, `updated_at`) VALUES '
);
$compteur = 0;
foreach ($constants as $constant => $value) {
if ($constant == AdminResources::SUPERADMINISTRATOR) {
continue;
}
$compteur++;
$output->writeln(
"($compteur, '$value', NOW(), NOW())" . ($constant === key(array_slice($constants, -1, 1, true)) ? ';' : ',')
);
}
break;
case 'sql-i18n':
$output->writeln(
'INSERT INTO ' . ResourceI18nTableMap::TABLE_NAME . ' (`id`, `locale`, `title`) VALUES '
);
$compteur = 0;
foreach ($constants as $constant => $value) {
if ($constant == AdminResources::SUPERADMINISTRATOR) {
continue;
}
$compteur++;
$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)) ? ';' : ',')
);
}
break;
default:
foreach ($constants as $constant => $value) {
if ($constant == AdminResources::SUPERADMINISTRATOR) {
continue;
}
$output->writeln('[' . $constant . "] => " . $value);
}
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

@@ -0,0 +1,345 @@
<?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\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 <manu@raynaud.io>
*/
class Install extends ContainerAwareCommand
{
/**
* Configure the command
*/
protected function configure()
{
$this
->setName("thelia:install")
->setDescription("Install thelia using cli tools. For now Thelia only use mysql database")
->setHelp("The <info>thelia:install</info> command install Thelia database and create config file needed.")
->addOption(
"db_host",
null,
InputOption::VALUE_OPTIONAL,
"host for your database",
"localhost"
)
->addOption(
"db_username",
null,
InputOption::VALUE_OPTIONAL,
"username for your database"
)
->addOption(
"db_password",
null,
InputOption::VALUE_OPTIONAL,
"password for your database"
)
->addOption(
"db_name",
null,
InputOption::VALUE_OPTIONAL,
"database name"
)
->addOption(
"db_port",
null,
InputOption::VALUE_OPTIONAL,
"database port",
"3306"
)
;
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$output->writeln(array(
'',
'Welcome to Thelia install process',
'You need information about your database configuration (host, username, password, database name, etc)',
'',
'<info>Caution : You are installing Thelia in cli mode, we verify some information, but this information are only available for the cli php sapi</info>',
'<info>This informations can be different in your apache or cgi php.ini files</info>',
''
));
$this->checkPermission($output);
$connectionInfo = array(
"host" => $input->getOption("db_host"),
"dbName" => $input->getOption("db_name"),
"username" => $input->getOption("db_username"),
"password" => $input->getOption("db_password"),
"port" => $input->getOption("db_port")
);
while (false === $connection = $this->tryConnection($connectionInfo, $output)) {
$connectionInfo = $this->getConnectionInfo($input, $output);
}
$database = new Database($connection);
$database->createDatabase($connectionInfo["dbName"]);
$output->writeln(array(
"",
"<info>Creating Thelia database, please wait</info>",
""
));
$database->insertSql($connectionInfo["dbName"]);
$this->manageSecret($database);
$output->writeln(array(
"",
"<info>Database created without errors</info>",
"<info>Creating file configuration, please wait</info>",
""
));
$this->createConfigFile($connectionInfo);
$output->writeln(array(
"",
"<info>Config file created with success. Your thelia is installed</info>",
""
));
}
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
*
* @param \Symfony\Component\Console\Output\OutputInterface $output
*/
protected function checkPermission(OutputInterface $output)
{
$output->writeln(array(
"Checking some permissions"
));
/** @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>"
)
)
);
} else {
$output->writeln(array(
sprintf(
"<error>%s </error>%s",
$data['text'],
sprintf("<error>%s</error>", $data["hint"])
)
));
}
}
if (false === $isValid) {
throw new \RuntimeException('Please put correct permissions and reload install process');
}
}
/**
* rename database config file and complete it
*
* @param array $connectionInfo
*/
protected function createConfigFile($connectionInfo)
{
$fs = new Filesystem();
$sampleConfigFile = THELIA_CONF_DIR . "database.yml.sample";
$configFile = THELIA_CONF_DIR . "database.yml";
$fs->copy($sampleConfigFile, $configFile, true);
$configContent = file_get_contents($configFile);
$configContent = str_replace("%DRIVER%", "mysql", $configContent);
$configContent = str_replace("%USERNAME%", $connectionInfo["username"], $configContent);
$configContent = str_replace("%PASSWORD%", $connectionInfo["password"], $configContent);
$configContent = str_replace(
"%DSN%",
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"));
}
/**
* test database access
*
* @param $connectionInfo
* @param OutputInterface $output
* @return bool|\PDO
*/
protected function tryConnection($connectionInfo, OutputInterface $output)
{
if (is_null($connectionInfo["dbName"])) {
return false;
}
$dsn = "mysql:host=%s;port=%s";
try {
$connection = new \PDO(
sprintf($dsn, $connectionInfo["host"], $connectionInfo["port"]),
$connectionInfo["username"],
$connectionInfo["password"]
);
$connection->query('SET NAMES \'UTF8\'');
} catch (\PDOException $e) {
$output->writeln(array(
"<error>Wrong connection information</error>"
));
return false;
}
return $connection;
}
/**
* Ask to user all needed information
*
* @param InputInterface $input
* @param OutputInterface $output
* @return array
*/
protected function getConnectionInfo(InputInterface $input, OutputInterface $output)
{
/** @var QuestionHelper $helper */
$helper = $this->getHelper('question');
$connectionInfo = array();
$connectionInfo['host'] = $this->enterData(
$helper,
$input,
$output,
"Database host [default: localhost] : ",
"You must specify a database host",
false,
"localhost"
);
$connectionInfo['port'] = $this->enterData(
$helper,
$input,
$output,
"Database port [default: 3306] : ",
"You must specify a database port",
false,
"3306"
);
$connectionInfo['dbName'] = $this->enterData(
$helper,
$input,
$output,
"Database name (if database does not exist, Thelia will try to create it) : ",
"You must specify a database name"
);
$connectionInfo['username'] = $this->enterData(
$helper,
$input,
$output,
"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

@@ -0,0 +1,93 @@
<?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\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
*
* Class ModuleActivateCommand
* @package Thelia\Command
* @author Etienne Roudeix <eroudeix@openstudio.fr>
*
*/
class ModuleActivateCommand extends BaseModuleGenerate
{
protected function configure()
{
$this
->setName("module:activate")
->setDescription("Activates a module")
->addOption(
"with-dependencies",
null,
InputOption::VALUE_NONE,
'activate module recursively'
)
->addArgument(
"module",
InputArgument::REQUIRED,
"module to activate"
);
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$moduleCode = $this->formatModuleName($input->getArgument("module"));
$module = ModuleQuery::create()->findOneByCode($moduleCode);
if (null === $module) {
throw new \RuntimeException(sprintf("module %s not found", $moduleCode));
}
if ($module->getActivate() == BaseModule::IS_ACTIVATED) {
throw new \RuntimeException(sprintf("module %s is already actived", $moduleCode));
}
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()
)
);
}
//impossible to change output class in CommandTester...
if (method_exists($output, "renderBlock")) {
$output->renderBlock(array(
'',
sprintf("Activation succeed for module %s", $moduleCode),
''
), "bg=green;fg=black");
}
}
}

View File

@@ -0,0 +1,134 @@
<?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\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
*
* Class ModuleDeactivateCommand
* @package Thelia\Command
* @author Nicolas Villa <nicolas@libre-shop.com>
*
*/
class ModuleDeactivateCommand extends BaseModuleGenerate
{
protected function configure()
{
$this
->setName("module:deactivate")
->setDescription("Deactivate a module")
->addOption(
"with-dependencies",
null,
InputOption::VALUE_NONE,
'activate module recursively'
)
->addArgument(
"module",
InputArgument::REQUIRED,
"module to deactivate"
)
->addOption(
"assume-yes",
'y',
InputOption::VALUE_NONE,
'Assume to deactivate a mandatory module'
)
;
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$moduleCode = $this->formatModuleName($input->getArgument("module"));
$module = ModuleQuery::create()->findOneByCode($moduleCode);
if (null === $module) {
throw new \RuntimeException(sprintf("module %s not found", $moduleCode));
}
if ($module->getActivate() == BaseModule::IS_NOT_ACTIVATED) {
throw new \RuntimeException(sprintf("module %s is already deactivated", $moduleCode));
}
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()));
}
//impossible to change output class in CommandTester...
if (method_exists($output, "renderBlock")) {
$output->renderBlock(array(
'',
sprintf("Deactivation succeed for module %s", $moduleCode),
''
), "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

@@ -0,0 +1,226 @@
<?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\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Filesystem\Filesystem;
/**
* generate a new Module
*
* Class ModuleGenerateCommand
* @package Thelia\Command
* @author Manuel Raynaud <manu@raynaud.io>
*/
class ModuleGenerateCommand extends BaseModuleGenerate
{
protected function configure()
{
$this
->setName("module:generate")
->setDescription("generate all needed files for creating a new Module")
->addArgument(
"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 . $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($output, "renderBlock")) {
// impossible to change output class in CommandTester...
$output->renderBlock(array(
'',
sprintf("module %s create with success", $this->module),
"You can now configure your module and complete module.xml file",
''
), "bg=green;fg=black");
}
}
private function createDirectories()
{
$fs = new Filesystem();
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()
{
$fs = new Filesystem();
try {
$skeletonDir = str_replace("/", DIRECTORY_SEPARATOR, __DIR__ . "/Skeleton/Module/");
// config.xml file
$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);
// Readme.md file
$filename = $this->moduleDirectory . DIRECTORY_SEPARATOR . "Readme.md";
if (!$fs->exists($filename)) {
$readmeContent = file_get_contents($skeletonDir . "Readme.md");
// 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]));
$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
$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("%%DOMAINNAME%%", strtolower($this->module), $classContent);
file_put_contents($filename, $classContent);
}
// schema.xml file
$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(
'%%XSD_LOCATION%%',
$fs->makePathRelative(
THELIA_VENDOR . 'propel/propel/resources/xsd/',
$this->moduleDirectory
) . 'database.xsd',
$schemaContent
);
file_put_contents($filename, $schemaContent);
}
// routing.xml file
$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);
file_put_contents($filename, $routingContent);
}
// I18n sample files
$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
);
}
$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

@@ -0,0 +1,122 @@
<?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\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;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Filesystem\Filesystem;
/**
* generate class model for a specific module
*
* Class ModuleGenerateModelCommand
* @package Thelia\Command
* @author Manuel Raynaud <manu@raynaud.io>
*/
class ModuleGenerateModelCommand extends BaseModuleGenerate
{
protected function configure()
{
$this
->setName("module:generate:model")
->setDescription("generate model for a specific module")
->addArgument(
"name",
InputArgument::REQUIRED,
"module name"
)
->addOption(
"generate-sql",
null,
InputOption::VALUE_NONE,
"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 . $this->module;
$fs = new Filesystem();
if ($fs->exists($this->moduleDirectory) === false) {
throw new \RuntimeException(sprintf("%s module does not exists", $this->module));
}
if ($fs->exists($this->moduleDirectory . DS . "Config" . DS . "schema.xml") === false) {
throw new \RuntimeException("schema.xml not found in Config directory. Needed file for generating model");
}
$this->generateModel($output);
/** @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(' ');
$this->generateSql($output);
}
}
protected function generateSql(OutputInterface $output)
{
$command = $this->getApplication()->find("module:generate:sql");
$command->run(
new ArrayInput(array(
"command" => $command->getName(),
"name" => $this->module
)),
$output
);
}
protected function generateModel(OutputInterface $output)
{
$fs = new Filesystem();
$moduleBuildPropel = new ModelBuildCommand();
$moduleBuildPropel->setApplication($this->getApplication());
$moduleBuildPropel->run(
new ArrayInput(array(
"command" => $moduleBuildPropel->getName(),
"--output-dir" => THELIA_MODULE_DIR,
"--input-dir" => $this->moduleDirectory . DS ."Config"
)),
$output
);
$verifyDirectories = array(
THELIA_MODULE_DIR . "Thelia",
$this->moduleDirectory . DS . "Model" . DS . "Thelia"
);
foreach ($verifyDirectories as $directory) {
if ($fs->exists($directory)) {
$fs->remove($directory);
}
}
}
}

View File

@@ -0,0 +1,83 @@
<?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\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;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Filesystem\Filesystem;
/**
* generate sql for a specific module
*
* Class ModuleGenerateSqlCommand
* @package Thelia\Command
* @author Manuel Raynaud <manu@raynaud.io>
*/
class ModuleGenerateSqlCommand extends BaseModuleGenerate
{
public function configure()
{
$this
->setName("module:generate:sql")
->setDescription("Generate the sql from schema.xml file")
->addArgument(
"name",
InputArgument::REQUIRED,
"Module name"
)
;
}
public function execute(InputInterface $input, OutputInterface $output)
{
$this->module = $this->formatModuleName($input->getArgument("name"));
$this->moduleDirectory = THELIA_MODULE_DIR . $this->module;
$fs = new Filesystem();
if ($fs->exists($this->moduleDirectory) === false) {
throw new \RuntimeException(sprintf("%s module does not exists", $this->module));
}
if ($fs->exists($this->moduleDirectory . DS . "Config" . DS . "schema.xml") === false) {
throw new \RuntimeException("schema.xml not found in Config directory. Needed file for generating model");
}
$sqlBuild = new SqlBuildCommand();
$sqlBuild->setApplication($this->getApplication());
$sqlBuild->run(
new ArrayInput(array(
"command" => $sqlBuild->getName(),
"--output-dir" => $this->moduleDirectory . DS ."Config",
"--input-dir" => $this->moduleDirectory . DS ."Config"
)),
$output
);
/** @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

@@ -0,0 +1,62 @@
<?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\Exception\InvalidModuleException;
use Thelia\Module\ModuleManagement;
/**
* Class ModuleRefreshCommand
* Refresh modules list
*
* @package Thelia\Command
* @author Jérôme Billiras <jbilliras@openstudio.fr>
*/
class ModuleRefreshCommand extends ContainerAwareCommand
{
protected function configure()
{
$this
->setName('module:refresh')
->setDescription('Refresh modules list');
}
protected function execute(InputInterface $input, OutputInterface $output)
{
try {
$moduleManagement = new ModuleManagement;
$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())
);
}
if (method_exists($output, 'renderBlock')) {
$output->renderBlock(
[
'',
'Modules list successfully refreshed',
''
],
'bg=green;fg=black'
);
}
}
}

View File

@@ -0,0 +1,43 @@
<?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\Output;
use Symfony\Component\Console\Output\ConsoleOutput;
class TheliaConsoleOutput extends ConsoleOutput
{
public function renderBlock(array $messages, $style = "info")
{
$strlen = function ($string) {
if (!function_exists('mb_strlen')) {
return strlen($string);
}
if (false === $encoding = mb_detect_encoding($string)) {
return strlen($string);
}
return mb_strlen($string, $encoding);
};
$length = 0;
foreach ($messages as $message) {
$length = ($strlen($message) > $length) ? $strlen($message) : $length;
}
$output = array();
foreach ($messages as $message) {
$output[] = "<" . $style . ">" . " " . $message . str_repeat(' ', $length - $strlen($message)) . " </" . $style . ">";
}
$this->writeln($output);
}
}

View File

@@ -0,0 +1,68 @@
<?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\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 <manu@raynaud.io>
*/
class ReloadDatabaseCommand extends BaseModuleGenerate
{
public function configure()
{
$this
->setName("thelia:dev:reloadDB")
->setDescription("erase current database and create new one")
/* ->addOption(
"load-fixtures",
null,
InputOption::VALUE_NONE,
"load fixtures in databases"
)*/
;
}
public function execute(InputInterface $input, OutputInterface $output)
{
/** @var ConnectionWrapper $connection */
$connection = Propel::getConnection(ProductTableMap::DATABASE_NAME);
$connection = $connection->getWrappedConnection();
$tables = $connection->query("SHOW TABLES");
$connection->query("SET FOREIGN_KEY_CHECKS = 0");
foreach ($tables as $table) {
$connection->query(sprintf("DROP TABLE `%s`", $table[0]));
}
$connection->query("SET FOREIGN_KEY_CHECKS = 1");
$database = new Database($connection);
$output->writeln(array(
'',
'<info>starting reloaded database, please wait</info>'
));
$database->insertSql();
$output->writeln(array(
'',
'<info>Database reloaded with success</info>',
''
));
}
}

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

@@ -0,0 +1,28 @@
<?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 %%NAMESPACE%%;
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
*
* Have fun !
*/
}

View File

@@ -0,0 +1,4 @@
<?php
return array(
// 'an english string' => 'The displayed english string',
);

View File

@@ -0,0 +1,4 @@
<?php
return array(
// 'an english string' => 'La traduction française de la chaine',
);

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

@@ -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

@@ -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,43 @@
<?xml version="1.0" encoding="UTF-8"?>
<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é 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>
<authors>
<author>
<name></name>
<email></email>
</author>
</authors>
<type>classic</type>
<!--
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

@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8" ?>
<routes xmlns="http://symfony.com/schema/routing"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing/routing-1.0.xsd">
<!--
if a /admin/module/%%CLASSNAME_LOWER%%/ route is provided, a "Configuration" button will be displayed
for the module in the module list. Clicking this button will invoke this route.
<route id="my_route_id" path="/admin/module/%%CLASSNAME_LOWER%%">
<default key="_controller">%%NAMESPACE%%\Full\Class\Name\Of\YourConfigurationController::methodName</default>
</route>
<route id="my_route_id" path="/admin/module/%%CLASSNAME_LOWER%%/route-name">
<default key="_controller">%%NAMESPACE%%\Full\Class\Name\Of\YourAdminController::methodName</default>
</route>
<route id="my_route_id" path="/my/route/name">
<default key="_controller">%%NAMESPACE%%\Full\Class\Name\Of\YourOtherController::methodName</default>
</route>
...add as many routes as required.
<route>
...
</route>
-->
</routes>

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<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

@@ -0,0 +1,191 @@
<?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\Condition;
use ArrayAccess;
use Countable;
use Iterator;
use Thelia\Condition\Implementation\ConditionInterface;
/**
* Manage a set of ConditionInterface
*
* @package Condition
* @author Guillaume MOREL <gmorel@openstudio.fr>
*
*/
class ConditionCollection implements Iterator, Countable, ArrayAccess
{
/** @var ConditionInterface[] */
protected $conditions = [];
/**
* (PHP 5 &gt;= 5.0.0)
* Return the current element
* @link http://php.net/manual/en/iterator.current.php
*
* @return mixed Can return any type.
*/
public function current()
{
$var = current($this->conditions);
return $var;
}
/**
* (PHP 5 &gt;= 5.0.0)
* Move forward to next element
* @link http://php.net/manual/en/iterator.next.php
*
* @return void Any returned value is ignored.
*/
public function next()
{
next($this->conditions);
}
/**
* (PHP 5 &gt;= 5.0.0)
* Return the key of the current element
* @link http://php.net/manual/en/iterator.key.php
*
* @return mixed scalar on success, or null on failure.
*/
public function key()
{
$var = key($this->conditions);
return $var;
}
/**
* (PHP 5 &gt;= 5.0.0)
* Checks if current position is valid
* @link http://php.net/manual/en/iterator.valid.php
*
* @return boolean The return value will be casted to boolean and then evaluated.
* Returns true on success or false on failure.
*/
public function valid()
{
$key = key($this->conditions);
$var = ($key !== null && $key !== false);
return $var;
}
/**
* (PHP 5 &gt;= 5.0.0)
* Rewind the Iterator to the first element
* @link http://php.net/manual/en/iterator.rewind.php
*
* @return void Any returned value is ignored.
*/
public function rewind()
{
reset($this->conditions);
}
/**
* (PHP 5 &gt;= 5.1.0)
* Count elements of an object
* @link http://php.net/manual/en/countable.count.php
*
* @return int The custom count as an integer.
* The return value is cast to an integer.
*/
public function count()
{
return count($this->conditions);
}
/**
* (PHP 5 >= 5.0.0)
* Whether a offset exists
* @link http://php.net/manual/en/arrayaccess.offsetexists.php
* @param mixed $offset
* An offset to check for.
*
* @return boolean true on success or false on failure.
* The return value will be casted to boolean if non-boolean was returned.
*/
public function offsetExists($offset)
{
return isset($this->conditions[$offset]);
}
/**
* (PHP 5 >= 5.0.0)
* Offset to retrieve
* @link http://php.net/manual/en/arrayaccess.offsetget.php
* @param mixed $offset
* The offset to retrieve.
*
* @return mixed Can return all value types.
*/
public function offsetGet($offset)
{
return isset($this->conditions[$offset]) ? $this->conditions[$offset] : null;
}
/**
* (PHP 5 >= 5.0.0)
* Offset to set
* @link http://php.net/manual/en/arrayaccess.offsetset.php
* @param mixed $offset
* The offset to assign the value to.
* @param mixed $value
* The value to set.
*
* @return void
*/
public function offsetSet($offset, $value)
{
if (is_null($offset)) {
$this->conditions[] = $value;
} else {
$this->conditions[$offset] = $value;
}
}
/**
* (PHP 5 >= 5.0.0)
* Offset to unset
* @link http://php.net/manual/en/arrayaccess.offsetunset.php
* @param mixed $offset
* The offset to unset.
*
* @return void
*/
public function offsetUnset($offset)
{
unset($this->conditions[$offset]);
}
/**
* Allow to compare 2 set of conditions
*
* @return string Jsoned data
*/
public function __toString()
{
$arrayToSerialize = [];
/** @var ConditionInterface $condition */
foreach ($this as $condition) {
$arrayToSerialize[] = $condition->getSerializableCondition();
}
return json_encode($arrayToSerialize);
}
}

View File

@@ -0,0 +1,125 @@
<?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\Condition;
use Thelia\Condition\Implementation\ConditionInterface;
/**
* Validate Conditions
*
* @package Condition
* @author Guillaume MOREL <gmorel@openstudio.fr>
*
*/
class ConditionEvaluator
{
/**
* Check if an Event matches SerializableCondition
*
* @param ConditionCollection $conditions Conditions to check against the Event
*
* @return bool
*/
public function isMatching(ConditionCollection $conditions)
{
$isMatching = true;
/** @var ConditionInterface $condition */
foreach ($conditions as $condition) {
if (!$condition->isMatching()) {
return false;
}
}
return $isMatching;
}
/**
* Do variable comparison
*
* @param mixed $v1 Variable 1
* @param string $o Operator ex : Operators::DIFFERENT
* @param mixed $v2 Variable 2
*
* @throws \Exception
* @return bool
*/
public function variableOpComparison($v1, $o, $v2)
{
if ($o == Operators::DIFFERENT) {
return ($v1 != $v2);
}
switch ($o) {
case Operators::SUPERIOR:
// >
if ($v1 > $v2) {
return true;
} else {
continue;
}
break;
case Operators::SUPERIOR_OR_EQUAL:
// >=
if ($v1 >= $v2) {
return true;
} else {
continue;
}
break;
case Operators::INFERIOR:
// <
if ($v1 < $v2) {
return true;
} else {
continue;
}
break;
case Operators::INFERIOR_OR_EQUAL:
// <=
if ($v1 <= $v2) {
return true;
} else {
continue;
}
break;
case Operators::EQUAL:
// ==
if ($v1 == $v2) {
return true;
} else {
continue;
}
break;
case Operators::IN:
// in
if (in_array($v1, $v2)) {
return true;
} else {
continue;
}
break;
case Operators::OUT:
// not in
if (!in_array($v1, $v2)) {
return true;
} else {
continue;
}
break;
default:
throw new \Exception('Unrecognized operator ' . $o);
}
return false;
}
}

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\Condition;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Thelia\Condition\Implementation\ConditionInterface;
use Thelia\Coupon\FacadeInterface;
/**
* Manage how Condition could interact with the current application state (Thelia)
*
* @package Constraint
* @author Guillaume MOREL <gmorel@openstudio.fr>
*
*/
class ConditionFactory
{
/** @var ContainerInterface Service Container */
protected $container = null;
/** @var FacadeInterface Provide necessary value from Thelia */
protected $adapter;
/** @var array ConditionCollection to process*/
protected $conditions = null;
/**
* Constructor
*
* @param ContainerInterface $container Service container
*/
public function __construct(ContainerInterface $container)
{
$this->container = $container;
$this->adapter = $container->get('thelia.facade');
}
/**
* Serialize a collection of conditions
*
* @param ConditionCollection $collection A collection of conditions
*
* @return string A ready to be stored Condition collection
*/
public function serializeConditionCollection(ConditionCollection $collection)
{
if ($collection->count() == 0) {
/** @var ConditionInterface $conditionNone */
$conditionNone = $this->container->get(
'thelia.condition.match_for_everyone'
);
$collection[] = $conditionNone;
}
$serializableConditions = [];
/** @var $condition ConditionInterface */
foreach ($collection as $condition) {
$serializableConditions[] = $condition->getSerializableCondition();
}
return base64_encode(json_encode($serializableConditions));
}
/**
* Unserialize a collection of conditions
*
* @param string $serializedConditions Serialized Conditions
*
* @return ConditionCollection Conditions ready to be processed
*/
public function unserializeConditionCollection($serializedConditions)
{
$unserializedConditions = json_decode(base64_decode($serializedConditions));
$collection = new ConditionCollection();
if (!empty($unserializedConditions)) {
/** @var SerializableCondition $condition */
foreach ($unserializedConditions as $condition) {
if ($this->container->has($condition->conditionServiceId)) {
/** @var ConditionInterface $conditionManager */
$conditionManager = $this->build(
$condition->conditionServiceId,
(array) $condition->operators,
(array) $condition->values
);
$collection[] = clone $conditionManager;
}
}
}
return $collection;
}
/**
* Build a Condition from form
*
* @param string $conditionServiceId Condition class name
* @param array $operators Condition Operator (<, >, = )
* @param array $values Values setting this Condition
*
* @throws \InvalidArgumentException
* @return ConditionInterface Ready to use Condition or false
*/
public function build($conditionServiceId, array $operators, array $values)
{
if (!$this->container->has($conditionServiceId)) {
return false;
}
/** @var ConditionInterface $condition */
$condition = $this->container->get($conditionServiceId);
$condition->setValidatorsFromForm($operators, $values);
return clone $condition;
}
/**
* Get Condition inputs from serviceId
*
* @param string $conditionServiceId ConditionManager class name
*
* @return array Ready to be drawn condition inputs
*/
public function getInputsFromServiceId($conditionServiceId)
{
if (!$this->container->has($conditionServiceId)) {
return false;
}
/** @var ConditionInterface $condition */
$condition = $this->container->get($conditionServiceId);
return $this->getInputsFromConditionInterface($condition);
}
/**
* Get Condition inputs from serviceId
*
* @param ConditionInterface $condition ConditionManager
*
* @return array Ready to be drawn condition inputs
*/
public function getInputsFromConditionInterface(ConditionInterface $condition)
{
return $condition->getValidators();
}
}

View File

@@ -0,0 +1,35 @@
<?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\Condition;
/**
* Manage how Condition could interact with each others
*
* @package Condition
* @author Guillaume MOREL <gmorel@openstudio.fr>
*
*/
class ConditionOrganizer implements ConditionOrganizerInterface
{
/**
* Organize ConditionInterface
*
* @param array $conditions Array of ConditionInterface
*
* @return array Array of ConditionInterface sorted
*/
public function organize(array $conditions)
{
// @todo: Implement organize() method.
}
}

View File

@@ -0,0 +1,32 @@
<?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\Condition;
/**
* Manage how Condition could interact with each other
*
* @package Condition
* @author Guillaume MOREL <gmorel@openstudio.fr>
*
*/
interface ConditionOrganizerInterface
{
/**
* Organize ConditionInterface
*
* @param array $conditions Array of ConditionInterface
*
* @return array Array of ConditionInterface sorted
*/
public function organize(array $conditions);
}

View File

@@ -0,0 +1,155 @@
<?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\Condition\Implementation;
use Thelia\Condition\Operators;
use Thelia\Coupon\FacadeInterface;
use Thelia\Exception\InvalidConditionValueException;
use Thelia\Model\Country;
use Thelia\Model\CountryQuery;
/**
* Check a Checkout against its Product number
*
* @package Condition
* @author Franck Allimant <franck@cqfdev.fr>
*
*/
abstract class AbstractMatchCountries extends ConditionAbstract
{
/** Condition 1st parameter : quantity */
const COUNTRIES_LIST = 'countries';
/**
* @inheritdoc
*/
public function __construct(FacadeInterface $facade)
{
$this->availableOperators = [
self::COUNTRIES_LIST => [
Operators::IN,
Operators::OUT
]
];
parent::__construct($facade);
}
abstract protected function getSummaryLabel($cntryStrList, $i18nOperator);
abstract protected function getFormLabel();
/**
* @inheritdoc
*/
public function setValidatorsFromForm(array $operators, array $values)
{
$this->checkComparisonOperatorValue($operators, self::COUNTRIES_LIST);
// Use default values if data is not defined.
if (! isset($operators[self::COUNTRIES_LIST]) || ! isset($values[self::COUNTRIES_LIST])) {
$operators[self::COUNTRIES_LIST] = Operators::IN;
$values[self::COUNTRIES_LIST] = [];
}
// Be sure that the value is an array, make one if required
if (! is_array($values[self::COUNTRIES_LIST])) {
$values[self::COUNTRIES_LIST] = array($values[self::COUNTRIES_LIST]);
}
// Check that at least one category is selected
if (empty($values[self::COUNTRIES_LIST])) {
throw new InvalidConditionValueException(
get_class(),
self::COUNTRIES_LIST
);
}
$this->operators = [ self::COUNTRIES_LIST => $operators[self::COUNTRIES_LIST] ];
$this->values = [ self::COUNTRIES_LIST => $values[self::COUNTRIES_LIST] ];
return $this;
}
/**
* @inheritdoc
*/
public function isMatching()
{
// The delivery address should match one of the selected countries.
/* TODO !!!! */
return $this->conditionValidator->variableOpComparison(
$this->facade->getNbArticlesInCart(),
$this->operators[self::COUNTRIES_LIST],
$this->values[self::COUNTRIES_LIST]
);
}
/**
* @inheritdoc
*/
public function getSummary()
{
$i18nOperator = Operators::getI18n(
$this->translator,
$this->operators[self::COUNTRIES_LIST]
);
$cntryStrList = '';
$cntryIds = $this->values[self::COUNTRIES_LIST];
if (null !== $cntryList = CountryQuery::create()->findPks($cntryIds)) {
/** @var Country $cntry */
foreach ($cntryList as $cntry) {
$cntryStrList .= $cntry->setLocale($this->getCurrentLocale())->getTitle() . ', ';
}
$cntryStrList = rtrim($cntryStrList, ', ');
}
return $this->getSummaryLabel($cntryStrList, $i18nOperator);
}
/**
* @inheritdoc
*/
protected function generateInputs()
{
return array(
self::COUNTRIES_LIST => array(
'availableOperators' => $this->availableOperators[self::COUNTRIES_LIST],
'value' => '',
'selectedOperator' => Operators::IN
)
);
}
/**
* @inheritdoc
*/
public function drawBackOfficeInputs()
{
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(),
'countryLabel' => $this->getFormLabel()
]
);
}
}

View File

@@ -0,0 +1,203 @@
<?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\Condition\Implementation;
use Thelia\Condition\Operators;
use Thelia\Coupon\FacadeInterface;
use Thelia\Exception\InvalidConditionValueException;
use Thelia\Model\CartItem;
use Thelia\Model\Category;
use Thelia\Model\CategoryQuery;
/**
* Check a Checkout against its Product number
*
* @package Condition
* @author Franck Allimant <franck@cqfdev.fr>
*
*/
class CartContainsCategories extends ConditionAbstract
{
/** Condition 1st parameter : quantity */
const CATEGORIES_LIST = 'categories';
/**
* @inheritdoc
*/
public function __construct(FacadeInterface $facade)
{
$this->availableOperators = [
self::CATEGORIES_LIST => [
Operators::IN,
Operators::OUT
]
];
parent::__construct($facade);
}
/**
* @inheritdoc
*/
public function getServiceId()
{
return 'thelia.condition.cart_contains_categories';
}
/**
* @inheritdoc
*/
public function setValidatorsFromForm(array $operators, array $values)
{
$this->checkComparisonOperatorValue($operators, self::CATEGORIES_LIST);
// Use default values if data is not defined.
if (! isset($operators[self::CATEGORIES_LIST]) || ! isset($values[self::CATEGORIES_LIST])) {
$operators[self::CATEGORIES_LIST] = Operators::IN;
$values[self::CATEGORIES_LIST] = [];
}
// Be sure that the value is an array, make one if required
if (! is_array($values[self::CATEGORIES_LIST])) {
$values[self::CATEGORIES_LIST] = array($values[self::CATEGORIES_LIST]);
}
// Check that at least one category is selected
if (empty($values[self::CATEGORIES_LIST])) {
throw new InvalidConditionValueException(
get_class(),
self::CATEGORIES_LIST
);
}
$this->operators = [ self::CATEGORIES_LIST => $operators[self::CATEGORIES_LIST] ];
$this->values = [ self::CATEGORIES_LIST => $values[self::CATEGORIES_LIST] ];
return $this;
}
/**
* @inheritdoc
*/
public function isMatching()
{
$cartItems = $this->facade->getCart()->getCartItems();
/** @var CartItem $cartItem */
foreach ($cartItems as $cartItem) {
$categories = $cartItem->getProduct()->getCategories();
/** @var Category $category */
foreach ($categories as $category) {
if (! $this->conditionValidator->variableOpComparison(
$category->getId(),
$this->operators[self::CATEGORIES_LIST],
$this->values[self::CATEGORIES_LIST]
)) {
// cart item doesn't match go to next cart item
continue 2;
}
}
// cart item match
return true;
}
return false;
}
/**
* @inheritdoc
*/
public function getName()
{
return $this->translator->trans(
'Cart contains categories condition',
[]
);
}
/**
* @inheritdoc
*/
public function getToolTip()
{
$toolTip = $this->translator->trans(
'The coupon applies if the cart contains at least one product of the selected categories',
[]
);
return $toolTip;
}
/**
* @inheritdoc
*/
public function getSummary()
{
$i18nOperator = Operators::getI18n(
$this->translator,
$this->operators[self::CATEGORIES_LIST]
);
$catStrList = '';
$catIds = $this->values[self::CATEGORIES_LIST];
if (null !== $catList = CategoryQuery::create()->findPks($catIds)) {
/** @var Category $cat */
foreach ($catList as $cat) {
$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>',
[
'%categories_list%' => $catStrList,
'%op%' => $i18nOperator
]
);
return $toolTip;
}
/**
* @inheritdoc
*/
protected function generateInputs()
{
return array(
self::CATEGORIES_LIST => array(
'availableOperators' => $this->availableOperators[self::CATEGORIES_LIST],
'value' => '',
'selectedOperator' => Operators::IN
)
);
}
/**
* @inheritdoc
*/
public function drawBackOfficeInputs()
{
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

@@ -0,0 +1,195 @@
<?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\Condition\Implementation;
use Thelia\Condition\Operators;
use Thelia\Coupon\FacadeInterface;
use Thelia\Exception\InvalidConditionValueException;
use Thelia\Model\ProductQuery;
use Thelia\Model\CartItem;
use Thelia\Model\Product;
/**
* Check a Checkout against its Product number
*
* @package Condition
* @author Franck Allimant <franck@cqfdev.fr>
*
*/
class CartContainsProducts extends ConditionAbstract
{
/** Condition 1st parameter : quantity */
const PRODUCTS_LIST = 'products';
/**
* @inheritdoc
*/
public function __construct(FacadeInterface $facade)
{
$this->availableOperators = [
self::PRODUCTS_LIST => [
Operators::IN,
Operators::OUT
]
];
parent::__construct($facade);
}
/**
* @inheritdoc
*/
public function getServiceId()
{
return 'thelia.condition.cart_contains_products';
}
/**
* @inheritdoc
*/
public function setValidatorsFromForm(array $operators, array $values)
{
$this->checkComparisonOperatorValue($operators, self::PRODUCTS_LIST);
// Use default values if data is not defined.
if (! isset($operators[self::PRODUCTS_LIST]) || ! isset($values[self::PRODUCTS_LIST])) {
$operators[self::PRODUCTS_LIST] = Operators::IN;
$values[self::PRODUCTS_LIST] = [];
}
// Be sure that the value is an array, make one if required
if (! is_array($values[self::PRODUCTS_LIST])) {
$values[self::PRODUCTS_LIST] = array($values[self::PRODUCTS_LIST]);
}
// Check that at least one product is selected
if (empty($values[self::PRODUCTS_LIST])) {
throw new InvalidConditionValueException(
get_class(),
self::PRODUCTS_LIST
);
}
$this->operators = [ self::PRODUCTS_LIST => $operators[self::PRODUCTS_LIST] ];
$this->values = [ self::PRODUCTS_LIST => $values[self::PRODUCTS_LIST] ];
return $this;
}
/**
* @inheritdoc
*/
public function isMatching()
{
$cartItems = $this->facade->getCart()->getCartItems();
/** @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;
}
}
return false;
}
/**
* @inheritdoc
*/
public function getName()
{
return $this->translator->trans(
'Cart contains specific products',
[]
);
}
/**
* @inheritdoc
*/
public function getToolTip()
{
$toolTip = $this->translator->trans(
'The coupon applies if the cart contains at least one product of the specified product list',
[]
);
return $toolTip;
}
/**
* @inheritdoc
*/
public function getSummary()
{
$i18nOperator = Operators::getI18n(
$this->translator,
$this->operators[self::PRODUCTS_LIST]
);
$prodStrList = '';
$prodIds = $this->values[self::PRODUCTS_LIST];
if (null !== $prodList = ProductQuery::create()->findPks($prodIds)) {
/** @var Product $prod */
foreach ($prodList as $prod) {
$prodStrList .= $prod->setLocale($this->getCurrentLocale())->getTitle() . ', ';
}
$prodStrList = rtrim($prodStrList, ', ');
}
$toolTip = $this->translator->trans(
'Cart contains at least a product %op% <strong>%products_list%</strong>',
[
'%products_list%' => $prodStrList,
'%op%' => $i18nOperator
]
);
return $toolTip;
}
/**
* @inheritdoc
*/
protected function generateInputs()
{
return array(
self::PRODUCTS_LIST => array(
'availableOperators' => $this->availableOperators[self::PRODUCTS_LIST],
'value' => '',
'selectedOperator' => Operators::IN
)
);
}
/**
* @inheritdoc
*/
public function drawBackOfficeInputs()
{
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

@@ -0,0 +1,351 @@
<?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\Condition\Implementation;
use Thelia\Condition\ConditionEvaluator;
use Thelia\Condition\Operators;
use Thelia\Condition\SerializableCondition;
use Thelia\Core\Translation\Translator;
use Thelia\Coupon\FacadeInterface;
use Thelia\Exception\InvalidConditionOperatorException;
use Thelia\Exception\InvalidConditionValueException;
use Thelia\Model\CurrencyQuery;
use Thelia\Model\Currency;
use Thelia\Type\FloatType;
/**
* Assist in writing a condition of whether the Condition is applied or not
*
* @package Constraint
* @author Guillaume MOREL <gmorel@openstudio.fr>
*
*/
abstract class ConditionAbstract implements ConditionInterface
{
/** @var array Available Operators (Operators::CONST) */
protected $availableOperators = [];
/** @var array Parameters validating parameters against */
protected $validators = [];
/** @var FacadeInterface Provide necessary value from Thelia */
protected $facade = null;
/** @var Translator Service Translator */
protected $translator = null;
/** @var array Operators set by Admin in BackOffice */
protected $operators = [];
/** @var array Values set by Admin in BackOffice */
protected $values = [];
/** @var ConditionEvaluator Conditions validator */
protected $conditionValidator = null;
/**
* Constructor
*
* @param FacadeInterface $facade Service Facade
*/
public function __construct(FacadeInterface $facade)
{
$this->facade = $facade;
$this->translator = $facade->getTranslator();
$this->conditionValidator = $facade->getConditionEvaluator();
}
/**
* @param array $operatorList the list of comparison operator values, as entered in the condition parameter form
* @param string $parameterName the name of the parameter to check
*
* @return $this
*
* @throws \Thelia\Exception\InvalidConditionOperatorException if the operator value is not in the allowed value
*/
protected function checkComparisonOperatorValue($operatorList, $parameterName)
{
$isOperator1Legit = $this->isOperatorLegit(
$operatorList[$parameterName],
$this->availableOperators[$parameterName]
);
if (!$isOperator1Legit) {
throw new InvalidConditionOperatorException(
get_class(),
$parameterName
);
}
return $this;
}
/**
* Return all validators
*
* @return array
*/
public function getValidators()
{
$this->validators = $this->generateInputs();
$translatedInputs = [];
foreach ($this->validators as $key => $validator) {
$translatedOperators = [];
foreach ($validator['availableOperators'] as $availableOperators) {
$translatedOperators[$availableOperators] = Operators::getI18n(
$this->translator,
$availableOperators
);
}
$validator['availableOperators'] = $translatedOperators;
$translatedInputs[$key] = $validator;
}
$validators = [
'inputs' => $translatedInputs,
'setOperators' => $this->operators,
'setValues' => $this->values
];
return $validators;
}
/**
* Generate inputs ready to be drawn.
*
* TODO: what these "inputs ready to be drawn" is not clear.
*
* @throws \Thelia\Exception\NotImplementedException
* @return array
*/
protected function generateInputs()
{
throw new \Thelia\Exception\NotImplementedException(
'The generateInputs method must be implemented in ' . get_class()
);
}
/**
* Get ConditionManager Service id
*
* @return string
*/
public function getServiceId()
{
throw new \Thelia\Exception\NotImplementedException(
'The getServiceId method must be implemented in ' . get_class()
);
}
/**
* Validate if Operator given is available for this Condition
*
* @param string $operator Operator to validate ex <
* @param array $availableOperators Available operators
*
* @return bool
*/
protected function isOperatorLegit($operator, array $availableOperators)
{
return in_array($operator, $availableOperators);
}
/**
* Return a serializable Condition
*
* @return SerializableCondition
*/
public function getSerializableCondition()
{
$serializableCondition = new SerializableCondition();
$serializableCondition->conditionServiceId = $this->getServiceId();
$serializableCondition->operators = $this->operators;
$serializableCondition->values = $this->values;
return $serializableCondition;
}
/**
* Check if currency if valid or not
*
* @param string $currencyValue Currency EUR|USD|..
*
* @return bool
* @throws \Thelia\Exception\InvalidConditionValueException
*/
protected function isCurrencyValid($currencyValue)
{
$availableCurrencies = $this->facade->getAvailableCurrencies();
/** @var Currency $currency */
$currencyFound = false;
foreach ($availableCurrencies as $currency) {
if ($currencyValue == $currency->getCode()) {
$currencyFound = true;
}
}
if (!$currencyFound) {
throw new InvalidConditionValueException(
get_class(),
'currency'
);
}
return true;
}
/**
* Check if price is valid
*
* @param float $priceValue Price value to check
*
* @return bool
* @throws \Thelia\Exception\InvalidConditionValueException
*/
protected function isPriceValid($priceValue)
{
$floatType = new FloatType();
if (!$floatType->isValid($priceValue) || $priceValue <= 0) {
throw new InvalidConditionValueException(
get_class(),
'price'
);
}
return true;
}
/**
* Draw the operator input displayed in the BackOffice
* allowing Admin to set its Coupon Conditions
*
* @param string $inputKey Input key (ex: self::INPUT1)
*
* @return string HTML string
*/
protected function drawBackOfficeInputOperators($inputKey)
{
$html = '';
$inputs = $this->getValidators();
if (isset($inputs['inputs'][$inputKey])) {
$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
]
);
}
return $html;
}
/**
* Draw the base input displayed in the BackOffice
* allowing Admin to set its Coupon Conditions
*
* @param string $label I18n input label
* @param string $inputKey Input key (ex: self::INPUT1)
*
* @return string HTML string
*/
protected function drawBackOfficeBaseInputsText($label, $inputKey)
{
$operatorSelectHtml = $this->drawBackOfficeInputOperators($inputKey);
$currentValue = '';
if (isset($this->values) && isset($this->values[$inputKey])) {
$currentValue = $this->values[$inputKey];
}
return $this->facade->getParser()->render(
'coupon/conditions-fragments/base-input-text.html',
[
'label' => $label,
'inputKey' => $inputKey,
'currentValue' => $currentValue,
'operatorSelectHtml' => $operatorSelectHtml
]
);
}
/**
* Draw the quantity input displayed in the BackOffice
* allowing Admin to set its Coupon Conditions
*
* @param string $inputKey Input key (ex: self::INPUT1)
* @param int $max Maximum selectable
* @param int $min Minimum selectable
*
* @return string HTML string
*/
protected function drawBackOfficeInputQuantityValues($inputKey, $max = 10, $min = 0)
{
return $this->facade->getParser()->render(
'coupon/condition-fragments/quantity-selector.html',
[
'min' => $min,
'max' => $max,
'value' => isset($this->values[$inputKey]) ? $this->values[$inputKey] : '',
'inputKey' => $inputKey
]
);
}
/**
* Draw the currency input displayed in the BackOffice
* allowing Admin to set its Coupon Conditions
*
* @param string $inputKey Input key (ex: self::INPUT1)
*
* @return string HTML string
*/
protected function drawBackOfficeCurrencyInput($inputKey)
{
$currencies = CurrencyQuery::create()->find();
$cleanedCurrencies = [];
/** @var Currency $currency */
foreach ($currencies as $currency) {
$cleanedCurrencies[$currency->getCode()] = $currency->getSymbol();
}
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

@@ -0,0 +1,107 @@
<?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\Condition\Implementation;
use Thelia\Condition\SerializableCondition;
use Thelia\Coupon\FacadeInterface;
use Thelia\Exception\InvalidConditionOperatorException;
use Thelia\Exception\InvalidConditionValueException;
/**
* Manage how the application checks its state in order to check if it matches the implemented condition
*
* @package Condition
* @author Guillaume MOREL <gmorel@openstudio.fr>
*
*/
interface ConditionInterface
{
/**
* Constructor
*
* @param FacadeInterface $adapter Service adapter
*/
public function __construct(FacadeInterface $adapter);
/**
* Get Condition Service id
*
* @return string
*/
public function getServiceId();
/**
* Check validators relevancy and store them
*
* @param array $operators an array of operators (greater than, less than, etc.) entered in the condition parameter input form, one for each condition defined by the Condition
* @param array $values an array of values entered in in the condition parameter input form, one for each condition defined by the Condition
*
* @throws InvalidConditionOperatorException
* @throws InvalidConditionValueException
*
* @return $this
*/
public function setValidatorsFromForm(array $operators, array $values);
/**
* Test if the current application state matches conditions
*
* @return bool
*/
public function isMatching();
/**
* Get I18n name
*
* @return string
*/
public function getName();
/**
* Get I18n tooltip
* Explain in detail what the Condition checks
*
* @return string
*/
public function getToolTip();
/**
* Get I18n summary
* Explain briefly the condition with given values
*
* @return string
*/
public function getSummary();
/**
* Return all validators
*
* @return array
*/
public function getValidators();
/**
* Return a serializable Condition
*
* @return SerializableCondition
*/
public function getSerializableCondition();
/**
* Draw the input displayed in the BackOffice
* allowing Admin to set its Coupon Conditions
*
* @return string HTML string
*/
public function drawBackOfficeInputs();
}

View File

@@ -0,0 +1,190 @@
<?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\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;
/**
* Check a Checkout against its Product number
*
* @package Condition
* @author Franck Allimant <franck@cqfdev.fr>
*
*/
class ForSomeCustomers extends ConditionAbstract
{
const CUSTOMERS_LIST = 'customers';
/**
* @inheritdoc
*/
public function __construct(FacadeInterface $facade)
{
$this->availableOperators = [
self::CUSTOMERS_LIST => [
Operators::IN,
Operators::OUT
]
];
parent::__construct($facade);
}
/**
* @inheritdoc
*/
public function getServiceId()
{
return 'thelia.condition.for_some_customers';
}
/**
* @inheritdoc
*/
public function setValidatorsFromForm(array $operators, array $values)
{
$this->checkComparisonOperatorValue($operators, self::CUSTOMERS_LIST);
// Use default values if data is not defined.
if (! isset($operators[self::CUSTOMERS_LIST]) || ! isset($values[self::CUSTOMERS_LIST])) {
$operators[self::CUSTOMERS_LIST] = Operators::IN;
$values[self::CUSTOMERS_LIST] = [];
}
// Be sure that the value is an array, make one if required
if (! is_array($values[self::CUSTOMERS_LIST])) {
$values[self::CUSTOMERS_LIST] = array($values[self::CUSTOMERS_LIST]);
}
// Check that at least one product is selected
if (empty($values[self::CUSTOMERS_LIST])) {
throw new InvalidConditionValueException(
get_class(),
self::CUSTOMERS_LIST
);
}
$this->operators = [ self::CUSTOMERS_LIST => $operators[self::CUSTOMERS_LIST] ];
$this->values = [ self::CUSTOMERS_LIST => $values[self::CUSTOMERS_LIST] ];
return $this;
}
/**
* @inheritdoc
*/
public function isMatching()
{
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]
);
}
/**
* @inheritdoc
*/
public function getName()
{
return $this->translator->trans(
'For one ore more customers',
[]
);
}
/**
* @inheritdoc
*/
public function getToolTip()
{
$toolTip = $this->translator->trans(
'The coupon applies to some customers only',
[]
);
return $toolTip;
}
/**
* @inheritdoc
*/
public function getSummary()
{
$i18nOperator = Operators::getI18n(
$this->translator,
$this->operators[self::CUSTOMERS_LIST]
);
$custStrList = '';
$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().'), ';
}
$custStrList = rtrim($custStrList, ', ');
}
$toolTip = $this->translator->trans(
'Customer is %op% <strong>%customer_list%</strong>',
[
'%customer_list%' => $custStrList,
'%op%' => $i18nOperator
]
);
return $toolTip;
}
/**
* @inheritdoc
*/
protected function generateInputs()
{
return array(
self::CUSTOMERS_LIST => array(
'availableOperators' => $this->availableOperators[self::CUSTOMERS_LIST],
'value' => '',
'selectedOperator' => Operators::IN
)
);
}
/**
* @inheritdoc
*/
public function drawBackOfficeInputs()
{
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()
]
);
}
}

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