Inital commit

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

165
core/LICENSE.txt Normal file
View File

@@ -0,0 +1,165 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
This version of the GNU Lesser General Public License incorporates
the terms and conditions of version 3 of the GNU General Public
License, supplemented by the additional permissions listed below.
0. Additional Definitions.
As used herein, "this License" refers to version 3 of the GNU Lesser
General Public License, and the "GNU GPL" refers to version 3 of the GNU
General Public License.
"The Library" refers to a covered work governed by this License,
other than an Application or a Combined Work as defined below.
An "Application" is any work that makes use of an interface provided
by the Library, but which is not otherwise based on the Library.
Defining a subclass of a class defined by the Library is deemed a mode
of using an interface provided by the Library.
A "Combined Work" is a work produced by combining or linking an
Application with the Library. The particular version of the Library
with which the Combined Work was made is also called the "Linked
Version".
The "Minimal Corresponding Source" for a Combined Work means the
Corresponding Source for the Combined Work, excluding any source code
for portions of the Combined Work that, considered in isolation, are
based on the Application, and not on the Linked Version.
The "Corresponding Application Code" for a Combined Work means the
object code and/or source code for the Application, including any data
and utility programs needed for reproducing the Combined Work from the
Application, but excluding the System Libraries of the Combined Work.
1. Exception to Section 3 of the GNU GPL.
You may convey a covered work under sections 3 and 4 of this License
without being bound by section 3 of the GNU GPL.
2. Conveying Modified Versions.
If you modify a copy of the Library, and, in your modifications, a
facility refers to a function or data to be supplied by an Application
that uses the facility (other than as an argument passed when the
facility is invoked), then you may convey a copy of the modified
version:
a) under this License, provided that you make a good faith effort to
ensure that, in the event an Application does not supply the
function or data, the facility still operates, and performs
whatever part of its purpose remains meaningful, or
b) under the GNU GPL, with none of the additional permissions of
this License applicable to that copy.
3. Object Code Incorporating Material from Library Header Files.
The object code form of an Application may incorporate material from
a header file that is part of the Library. You may convey such object
code under terms of your choice, provided that, if the incorporated
material is not limited to numerical parameters, data structure
layouts and accessors, or small macros, inline functions and templates
(ten or fewer lines in length), you do both of the following:
a) Give prominent notice with each copy of the object code that the
Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the object code with a copy of the GNU GPL and this license
document.
4. Combined Works.
You may convey a Combined Work under terms of your choice that,
taken together, effectively do not restrict modification of the
portions of the Library contained in the Combined Work and reverse
engineering for debugging such modifications, if you also do each of
the following:
a) Give prominent notice with each copy of the Combined Work that
the Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the Combined Work with a copy of the GNU GPL and this license
document.
c) For a Combined Work that displays copyright notices during
execution, include the copyright notice for the Library among
these notices, as well as a reference directing the user to the
copies of the GNU GPL and this license document.
d) Do one of the following:
0) Convey the Minimal Corresponding Source under the terms of this
License, and the Corresponding Application Code in a form
suitable for, and under terms that permit, the user to
recombine or relink the Application with a modified version of
the Linked Version to produce a modified Combined Work, in the
manner specified by section 6 of the GNU GPL for conveying
Corresponding Source.
1) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (a) uses at run time
a copy of the Library already present on the user's computer
system, and (b) will operate properly with a modified version
of the Library that is interface-compatible with the Linked
Version.
e) Provide Installation Information, but only if you would otherwise
be required to provide such information under section 6 of the
GNU GPL, and only to the extent that such information is
necessary to install and execute a modified version of the
Combined Work produced by recombining or relinking the
Application with a modified version of the Linked Version. (If
you use option 4d0, the Installation Information must accompany
the Minimal Corresponding Source and Corresponding Application
Code. If you use option 4d1, you must provide the Installation
Information in the manner specified by section 6 of the GNU GPL
for conveying Corresponding Source.)
5. Combined Libraries.
You may place library facilities that are a work based on the
Library side by side in a single library together with other library
facilities that are not Applications and are not covered by this
License, and convey such a combined library under terms of your
choice, if you do both of the following:
a) Accompany the combined library with a copy of the same work based
on the Library, uncombined with any other library facilities,
conveyed under the terms of this License.
b) Give prominent notice with the combined library that part of it
is a work based on the Library, and explaining where to find the
accompanying uncombined form of the same work.
6. Revised Versions of the GNU Lesser General Public License.
The Free Software Foundation may publish revised and/or new versions
of the GNU Lesser General Public License from time to time. Such new
versions will be similar in spirit to the present version, but may
differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the
Library as you received it specifies that a certain numbered version
of the GNU Lesser General Public License "or any later version"
applies to it, you have the option of following the terms and
conditions either of that published version or of any later version
published by the Free Software Foundation. If the Library as you
received it does not specify a version number of the GNU Lesser
General Public License, you may choose any version of the GNU Lesser
General Public License ever published by the Free Software Foundation.
If the Library as you received it specifies that a proxy can decide
whether future versions of the GNU Lesser General Public License shall
apply, that proxy's public statement of acceptance of any version is
permanent authorization for you to choose that version for the
Library.

38
core/Readme.md Normal file
View File

@@ -0,0 +1,38 @@
Readme
======
## This is the repository of Thelia core. All the pull requests on this repo will be ignored.
### If you want to create a project, please take a look at [thelia/thelia-project](https://github.com/thelia/thelia-project)
### If you want to contribute to Thelia, please take a look at [thelia/thelia](https://github.com/thelia/thelia)
Thelia
------
[![Build Status](https://travis-ci.org/thelia/thelia.png?branch=master)](https://travis-ci.org/thelia/thelia) [![License](https://poser.pugx.org/thelia/thelia/license.png)](https://packagist.org/packages/thelia/thelia) [![Scrutinizer Quality Score](https://scrutinizer-ci.com/g/thelia/thelia/badges/quality-score.png?s=61e3e04a69bffd71c29b08e5392080317a546716)](https://scrutinizer-ci.com/g/thelia/thelia/)
[Thelia](http://thelia.net/) is an open source tool for creating e-business websites and managing online content. This software is published under LGPL.
This is the new major version of Thelia.
You can download this version and have a try or take a look at the source code (or anything you wish, respecting LGPL). See http://thelia.net/ web site for more information.
A repository containing all thelia modules is available at this address : https://github.com/thelia-modules
Requirements
------------
* php 5.5
* Required extensions :
* PDO_Mysql
* openssl
* intl
* gd
* curl
* calendar
* dom
* fileinfo
* safe_mode off
* memory_limit at least 128M, preferably 256.
* post_max_size 20M
* upload_max_filesize 2M
* apache 2
* mysql

53
core/Thelia Executable file
View File

@@ -0,0 +1,53 @@
<?php
if (php_sapi_name() != 'cli') {
throw new \Exception('this script can only be launched with cli sapi');
}
set_time_limit(0);
// allow cache to be cleared by php client or web
umask(0002);
$bootstrapToggle = false;
$bootstraped = false;
// Autoload bootstrap
foreach ($argv as $arg) {
if ($arg === '-b') {
$bootstrapToggle = true;
continue;
}
if ($bootstrapToggle) {
require __DIR__ . DIRECTORY_SEPARATOR . $arg;
$bootstraped = true;
}
}
if (!$bootstraped) {
if (isset($bootstrapFile)) {
require $bootstrapFile;
} elseif (is_file($file = __DIR__ . '/vendor/autoload.php')) {
require $file;
} else {
echo "No autoload file found. Please use the -b argument to include yours";
exit(1);
}
}
use Thelia\Core\Thelia;
use Thelia\Core\Application;
use Thelia\Command\Output\TheliaConsoleOutput;
use Symfony\Component\Console\Input\ArgvInput;
$input = new ArgvInput();
$env = $input->getParameterOption(array('--env', '-e'), getenv('THELIA_ENV') ?: 'dev');
$debug = getenv('THELIA_DEBUG') !== '0' && !$input->hasParameterOption(array('--no-debug', '')) && $env !== 'prod';
$thelia = new Thelia($env, $debug);
$application = new Application($thelia);
$application->getContainer()->get('thelia.translator');
$application->run($input, new TheliaConsoleOutput());

View File

@@ -10,27 +10,83 @@
/* file that was distributed with this source code. */
/*************************************************************************************/
// Check php version
if (version_compare(phpversion(), "5.4", "<")) {
die(sprintf(
"Thelia needs at least php 5.4, but you are using php %s. Please upgrade before using Thelia.\n",
phpversion()
));
}
/**
* Thelia essential definitions
*/
define('DS' , DIRECTORY_SEPARATOR);
define('THELIA_ROOT' , rtrim(realpath(dirname(__DIR__)), DS) . DS);
define('THELIA_LOCAL_DIR' , THELIA_ROOT . 'local' . DS);
define('THELIA_CONF_DIR' , THELIA_LOCAL_DIR . 'config' . DS);
define('THELIA_MODULE_DIR' , THELIA_LOCAL_DIR . 'modules' . DS);
define('THELIA_WEB_DIR' , THELIA_ROOT . 'web' . DS);
define('THELIA_CACHE_DIR' , THELIA_ROOT . 'cache' . DS);
define('THELIA_LOG_DIR' , THELIA_ROOT . 'log' . DS);
define('THELIA_TEMPLATE_DIR' , THELIA_ROOT . 'templates' . DS);
$loader = require __DIR__ . "/vendor/autoload.php";
if (!defined('DS')) {
define('DS', DIRECTORY_SEPARATOR);
}
if (!defined('THELIA_ROOT')) {
define('THELIA_ROOT', rtrim(realpath(dirname(__DIR__)), DS) . DS);
}
if (!defined('THELIA_LIB')) {
define('THELIA_LIB', THELIA_ROOT . 'core' . DS . 'lib' . DS . 'Thelia' . DS);
}
if (!defined('THELIA_VENDOR')) {
define('THELIA_VENDOR', THELIA_ROOT . 'core' . DS . 'vendor' . DS);
}
if (!defined('THELIA_LOCAL_DIR')) {
define('THELIA_LOCAL_DIR', THELIA_ROOT . 'local' . DS);
}
if (!defined('THELIA_CONF_DIR')) {
define('THELIA_CONF_DIR', THELIA_LOCAL_DIR . 'config' . DS);
}
if (!defined('THELIA_MODULE_DIR')) {
define('THELIA_MODULE_DIR', THELIA_LOCAL_DIR . 'modules' . DS);
}
if (!defined('THELIA_WEB_DIR')) {
define('THELIA_WEB_DIR', THELIA_ROOT . 'web' . DS);
}
if (!defined('THELIA_CACHE_DIR')) {
define('THELIA_CACHE_DIR', THELIA_ROOT . 'cache' . DS);
}
if (!defined('THELIA_LOG_DIR')) {
define('THELIA_LOG_DIR', THELIA_ROOT . 'log' . DS);
}
if (!defined('THELIA_TEMPLATE_DIR')) {
define('THELIA_TEMPLATE_DIR', THELIA_ROOT . 'templates' . DS);
}
if (!defined('THELIA_SETUP_DIRECTORY')) {
define('THELIA_SETUP_DIRECTORY', THELIA_ROOT . 'setup' . DS);
}
if (!defined('THELIA_SETUP_WIZARD_DIRECTORY')) {
define('THELIA_SETUP_WIZARD_DIRECTORY', THELIA_ROOT . 'web' . DS . 'install' . DS);
}
if (!file_exists(THELIA_CONF_DIR . 'database.yml') && !defined('THELIA_INSTALL_MODE')) {
$sapi = php_sapi_name();
if (substr($sapi, 0, 3) == 'cli') {
define('THELIA_INSTALL_MODE', true);
} else {
} elseif (file_exists(THELIA_ROOT . DS . 'web' . DS . 'install')) {
$request = \Thelia\Core\HttpFoundation\Request::createFromGlobals();
header('Location: '.$request->getUriForPath('/install'));
} else {
header($_SERVER['SERVER_PROTOCOL'] . ' 500 Thelia is not installed', true, 500);
die(sprintf(
"Thelia is not installed. <a href=\"%s\" target=\"_blank\">More information</a>\n",
"http://doc.thelia.net/en/documentation/installation/index.html#using-cli-tools"
));
}
}

79
core/composer.json Normal file
View File

@@ -0,0 +1,79 @@
{
"name": "thelia/core",
"description": "Core of Thelia ecommerce CMS.",
"license": "LGPL-3.0+",
"homepage": "http://thelia.net/",
"support": {
"forum": "http://thelia.net/forum",
"wiki": "http://doc.thelia.net"
},
"repositories": [
{
"type": "vcs",
"url": "https://github.com/thelia/Propel2"
}
],
"require": {
"php": ">=5.4",
"ircmaxell/password-compat": "1.0.*",
"psr/log": "1.0",
"symfony/config": "2.8.*",
"symfony/console": "2.8.*",
"symfony/dependency-injection": "2.8.*",
"symfony/event-dispatcher": "2.8.*",
"symfony/http-kernel": "2.8.*",
"symfony/routing": "2.8.*",
"symfony/filesystem": "2.8.*",
"symfony/yaml": "2.8.*",
"symfony/translation": "2.8.*",
"symfony-cmf/routing": "1.3.*",
"symfony/validator": "2.8.*",
"symfony/options-resolver": "2.8.*",
"symfony/security": "2.8.*",
"symfony/expression-language": "2.8.*",
"symfony/process": "2.8.*",
"symfony/dom-crawler": "2.8.*",
"symfony/property-access": "2.8.*",
"symfony/serializer": "2.8.*",
"ensepar/html2pdf": "1.0.1",
"symfony/finder": "2.8.*",
"symfony/browser-kit": "2.8.*",
"symfony/http-foundation": "2.8.*",
"symfony/form": "2.8.*",
"symfony/class-loader": "2.8.*",
"symfony/icu": "1.0",
"stack/builder": "1.0.*",
"thelia/currency-converter": "~1.0",
"doctrine/cache": "1.5.*",
"kriswallsmith/assetic": "1.3.*",
"ptachoire/cssembed": "1.0.*",
"simplepie/simplepie": "1.3.*",
"imagine/imagine": "0.6.*",
"swiftmailer/swiftmailer": "5.4.*",
"oyejorge/less.php": "1.7.*",
"michelf/php-markdown": "1.6.*",
"smarty/smarty": "3.1.20",
"ramsey/array_column": "~1.1",
"propel/propel": "dev-thelia-2.3",
"commerceguys/addressing": "0.8.*",
"symfony/cache": "~3.1.0"
},
"require-dev": {
"fzaninotto/faker": "1.5.*",
"thelia/hooktest-module": "~1.1",
"thelia/hooktest-template": "~1.1",
"phpunit/phpunit": "4.8.*"
},
"bin": [
"Thelia"
],
"minimum-stability": "stable",
"autoload": {
"psr-4": {
"Thelia\\": "lib/Thelia/"
},
"files": [
"bootstrap.php"
]
}
}

View File

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

View File

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

View File

@@ -0,0 +1,65 @@
<?php
/*************************************************************************************/
/* This file is part of the Thelia package. */
/* */
/* Copyright (c) OpenStudio */
/* email : dev@thelia.net */
/* web : http://www.thelia.net */
/* */
/* For the full copyright and license information, please view the LICENSE.txt */
/* file that was distributed with this source code. */
/*************************************************************************************/
namespace Thelia\Action;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Thelia\Core\Event\Api\ApiCreateEvent;
use Thelia\Core\Event\Api\ApiDeleteEvent;
use Thelia\Core\Event\Api\ApiUpdateEvent;
use Thelia\Core\Event\TheliaEvents;
use Thelia\Model\Api as ApiModel;
/**
* Class Api
* @package Thelia\Action
* @author Manuel Raynaud <manu@raynaud.io>
*/
class Api extends BaseAction implements EventSubscriberInterface
{
public function createApi(ApiCreateEvent $event)
{
$api = new ApiModel();
$api->setLabel($event->getLabel())
->setProfileId($event->getProfile())
->save()
;
}
public function deleteApi(ApiDeleteEvent $event)
{
$api = $event->getApi();
$api->delete();
}
public function updateApi(ApiUpdateEvent $event)
{
$api = $event->getApi();
$api->setProfileId($event->getProfile())
->save();
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents()
{
return [
TheliaEvents::API_CREATE => ['createApi', 128],
TheliaEvents::API_DELETE => ['deleteApi', 128],
TheliaEvents::API_UPDATE => ['updateApi', 128],
];
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,111 @@
<?php
/*************************************************************************************/
/* This file is part of the Thelia package. */
/* */
/* Copyright (c) OpenStudio */
/* email : dev@thelia.net */
/* web : http://www.thelia.net */
/* */
/* For the full copyright and license information, please view the LICENSE.txt */
/* file that was distributed with this source code. */
/*************************************************************************************/
namespace Thelia\Action;
use Propel\Runtime\Propel;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Thelia\Core\Event\CustomerTitle\CustomerTitleEvent;
use Thelia\Core\Event\TheliaEvents;
use Thelia\Model\CustomerTitle as CustomerTitleModel;
use Thelia\Model\Map\CustomerTitleTableMap;
/**
* Class CustomerTitle
* @package Thelia\Action
* @author Benjamin Perche <bperche@openstudio.fr>
*/
class CustomerTitle extends BaseAction implements EventSubscriberInterface
{
public function create(CustomerTitleEvent $event)
{
$this->createOrUpdate($event, new CustomerTitleModel());
}
public function update(CustomerTitleEvent $event)
{
$this->checkCustomerTitle($event);
$this->createOrUpdate($event, $event->getCustomerTitle());
}
public function delete(CustomerTitleEvent $event)
{
$this->checkCustomerTitle($event);
$con = Propel::getConnection(CustomerTitleTableMap::DATABASE_NAME);
$con->beginTransaction();
try {
$event->getCustomerTitle()->delete();
$con->commit();
} catch (\Exception $e) {
$con->rollBack();
throw $e;
}
$event->setCustomerTitle(null);
}
protected function checkCustomerTitle(CustomerTitleEvent $event)
{
if (null === $event->getCustomerTitle()) {
throw new \LogicException(
"You must set the customer title before its update"
);
}
}
protected function createOrUpdate(CustomerTitleEvent $event, CustomerTitleModel $customerTitle)
{
$con = Propel::getConnection(CustomerTitleTableMap::DATABASE_NAME);
$con->beginTransaction();
$i18n = $customerTitle->getTranslation($event->getLocale(), $con);
try {
$i18n
->setShort($event->getShort())
->setLong($event->getLong())
;
$customerTitle->save($con);
if ($event->isDefault()) {
$customerTitle->toggleDefault($con);
$event->setDefault(false);
}
$con->commit();
} catch (\Exception $e) {
$con->rollBack();
throw $e;
}
$event->setCustomerTitle($customerTitle);
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents()
{
return array(
TheliaEvents::CUSTOMER_TITLE_CREATE => array("create"),
TheliaEvents::CUSTOMER_TITLE_UPDATE => array("update"),
TheliaEvents::CUSTOMER_TITLE_DELETE => array("delete"),
);
}
}

View File

@@ -0,0 +1,66 @@
<?php
/*************************************************************************************/
/* This file is part of the Thelia package. */
/* */
/* Copyright (c) OpenStudio */
/* email : dev@thelia.net */
/* web : http://www.thelia.net */
/* */
/* For the full copyright and license information, please view the LICENSE.txt */
/* file that was distributed with this source code. */
/*************************************************************************************/
namespace Thelia\Action;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Thelia\Core\Event\Delivery\DeliveryPostageEvent;
use Thelia\Core\Event\TheliaEvents;
/**
* Class Delivery
* @package Thelia\Action
* @author Julien Chanséaume <julien@thelia.net>
*/
class Delivery implements EventSubscriberInterface
{
/**
* Get postage from module using the classical module functions
*
* @param DeliveryPostageEvent $event
*/
public function getPostage(DeliveryPostageEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$module = $event->getModule();
// dispatch event to target specific module
$dispatcher->dispatch(
TheliaEvents::getModuleEvent(
TheliaEvents::MODULE_DELIVERY_GET_POSTAGE,
$module->getCode()
),
$event
);
if ($event->isPropagationStopped()) {
return;
}
// call legacy module method
$event->setValidModule($module->isValidDelivery($event->getCountry()));
if ($event->isValidModule()) {
$event->setPostage($module->getPostage($event->getCountry()));
}
}
/**
* @inheritdoc
*/
public static function getSubscribedEvents()
{
return [
TheliaEvents::MODULE_DELIVERY_GET_POSTAGE => ['getPostage', 128]
];
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,168 @@
<?php
/*************************************************************************************/
/* This file is part of the Thelia package. */
/* */
/* Copyright (c) OpenStudio */
/* email : dev@thelia.net */
/* web : http://www.thelia.net */
/* */
/* For the full copyright and license information, please view the LICENSE.txt */
/* file that was distributed with this source code. */
/*************************************************************************************/
namespace Thelia\Action;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Thelia\Core\Event\Cache\CacheEvent;
use Thelia\Core\Event\Hook\HookCreateAllEvent;
use Thelia\Core\Event\Hook\HookCreateEvent;
use Thelia\Core\Event\Hook\HookDeactivationEvent;
use Thelia\Core\Event\Hook\HookDeleteEvent;
use Thelia\Core\Event\Hook\HookToggleActivationEvent;
use Thelia\Core\Event\Hook\HookToggleNativeEvent;
use Thelia\Core\Event\Hook\HookUpdateEvent;
use Thelia\Core\Event\TheliaEvents;
use Thelia\Model\Hook as HookModel;
use Thelia\Model\HookQuery;
/**
* Class HookAction
* @package Thelia\Action
* @author Julien Chanséaume <jchanseaume@openstudio.fr>
*/
class Hook extends BaseAction implements EventSubscriberInterface
{
/** @var string */
protected $cacheDir;
public function __construct($cacheDir)
{
$this->cacheDir = $cacheDir;
}
public function create(HookCreateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$hook = new HookModel();
$hook
->setLocale($event->getLocale())
->setCode($event->getCode())
->setType($event->getType())
->setNative($event->getNative())
->setActivate($event->getActive())
->setTitle($event->getTitle())
->save();
$event->setHook($hook);
$this->cacheClear($dispatcher);
}
public function update(HookUpdateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if (null !== $hook = HookQuery::create()->findPk($event->getHookId())) {
$hook
->setLocale($event->getLocale())
->setCode($event->getCode())
->setType($event->getType())
->setNative($event->getNative())
->setActivate($event->getActive())
->setBlock($event->getBlock())
->setByModule($event->getByModule())
->setTitle($event->getTitle())
->setChapo($event->getChapo())
->setDescription($event->getDescription())
->save();
$event->setHook($hook);
$this->cacheClear($dispatcher);
}
}
public function delete(HookDeleteEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if (null !== $hook = HookQuery::create()->findPk($event->getHookId())) {
$hook->delete();
$event->setHook($hook);
$this->cacheClear($dispatcher);
}
}
public function createAll(HookCreateAllEvent $event)
{
$hook = new HookModel();
$hook
->setLocale($event->getLocale())
->setCode($event->getCode())
->setType($event->getType())
->setNative($event->getNative())
->setActivate($event->getActive())
->setBlock($event->getBlock())
->setByModule($event->getByModule())
->setTitle($event->getTitle())
->setChapo($event->getChapo())
->setDescription($event->getDescription())
->save();
$event->setHook($hook);
}
public function deactivation(HookDeactivationEvent $event)
{
if (null !== $hook = HookQuery::create()->findPk($event->getHookId())) {
$hook
->setActivate(false)
->save();
$event->setHook($hook);
}
}
public function toggleNative(HookToggleNativeEvent $event)
{
if (null !== $hook = HookQuery::create()->findPk($event->getHookId())) {
$hook
->setNative(!$hook->getNative())
->save();
$event->setHook($hook);
}
}
public function toggleActivation(HookToggleActivationEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
if (null !== $hook = HookQuery::create()->findPk($event->getHookId())) {
$hook
->setActivate(!$hook->getActivate())
->save();
$event->setHook($hook);
$this->cacheClear($dispatcher);
}
}
protected function cacheClear(EventDispatcherInterface $dispatcher)
{
$cacheEvent = new CacheEvent($this->cacheDir);
$dispatcher->dispatch(TheliaEvents::CACHE_CLEAR, $cacheEvent);
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents()
{
return array(
TheliaEvents::HOOK_CREATE => array('create', 128),
TheliaEvents::HOOK_UPDATE => array('update', 128),
TheliaEvents::HOOK_DELETE => array('delete', 128),
TheliaEvents::HOOK_TOGGLE_ACTIVATION => array('toggleActivation', 128),
TheliaEvents::HOOK_TOGGLE_NATIVE => array('toggleNative', 128),
TheliaEvents::HOOK_CREATE_ALL => array('createAll', 128),
TheliaEvents::HOOK_DEACTIVATION => array('deactivation', 128),
);
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,75 @@
<?php
/*************************************************************************************/
/* This file is part of the Thelia package. */
/* */
/* Copyright (c) OpenStudio */
/* email : dev@thelia.net */
/* web : http://www.thelia.net */
/* */
/* For the full copyright and license information, please view the LICENSE.txt */
/* file that was distributed with this source code. */
/*************************************************************************************/
namespace Thelia\Action;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Thelia\Core\Event\MetaData\MetaDataCreateOrUpdateEvent;
use Thelia\Core\Event\MetaData\MetaDataDeleteEvent;
use Thelia\Core\Event\TheliaEvents;
use Thelia\Model\MetaData as MetaDataModel;
use Thelia\Model\MetaDataQuery;
/**
* Class MetaData
* @package Thelia\Action
* @author Julien Chanséaume <jchanseaume@openstudio.fr>
*/
class MetaData extends BaseAction implements EventSubscriberInterface
{
public function createOrUpdate(MetaDataCreateOrUpdateEvent $event)
{
$metaData = MetaDataQuery::create()
->filterByMetaKey($event->getMetaKey())
->filterByElementKey($event->getElementKey())
->filterByElementId($event->getElementId())
->findOne();
if (null === $metaData) {
$metaData = new MetaDataModel();
$metaData
->setMetaKey($event->getMetaKey())
->setElementKey($event->getElementkey())
->setElementId($event->getElementId());
}
$metaData->
setValue($event->getValue());
$metaData->save();
$event->setMetaData($metaData);
}
public function delete(MetaDataDeleteEvent $event)
{
$metaData = MetaDataQuery::create()
->filterByMetaKey($event->getMetaKey())
->filterByElementKey($event->getElementKey())
->filterByElementId($event->getElementId())
->findOne();
$event->setMetaData($metaData);
if (null !== $metaData) {
$metaData->delete();
}
}
/**
* @inheritdoc
*/
public static function getSubscribedEvents()
{
return array(
TheliaEvents::META_DATA_CREATE => array('createOrUpdate', 128),
TheliaEvents::META_DATA_UPDATE => array('createOrUpdate', 128),
TheliaEvents::META_DATA_DELETE => array('delete', 128),
);
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,63 @@
<?php
/*************************************************************************************/
/* This file is part of the Thelia package. */
/* */
/* Copyright (c) OpenStudio */
/* email : dev@thelia.net */
/* web : http://www.thelia.net */
/* */
/* For the full copyright and license information, please view the LICENSE.txt */
/* file that was distributed with this source code. */
/*************************************************************************************/
namespace Thelia\Action;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Thelia\Core\Event\Payment\IsValidPaymentEvent;
use Thelia\Core\Event\TheliaEvents;
/**
* Class Payment
* @package Thelia\Action
* @author Julien Chanséaume <julien@thelia.net>
*/
class Payment implements EventSubscriberInterface
{
/**
* Check if a module is valid
*
* @param IsValidPaymentEvent $event
*/
public function isValid(IsValidPaymentEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$module = $event->getModule();
// dispatch event to target specific module
$dispatcher->dispatch(
TheliaEvents::getModuleEvent(
TheliaEvents::MODULE_PAYMENT_IS_VALID,
$module->getCode()
),
$event
);
if ($event->isPropagationStopped()) {
return;
}
// call legacy module method
$event->setValidModule($module->isValidPayment());
}
/**
* @inheritdoc
*/
public static function getSubscribedEvents()
{
return [
TheliaEvents::MODULE_PAYMENT_IS_VALID => ['isValid', 128],
];
}
}

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

@@ -0,0 +1,60 @@
<?php
/*************************************************************************************/
/* This file is part of the Thelia package. */
/* */
/* Copyright (c) OpenStudio */
/* email : dev@thelia.net */
/* web : http://www.thelia.net */
/* */
/* For the full copyright and license information, please view the LICENSE.txt */
/* file that was distributed with this source code. */
/*************************************************************************************/
namespace Thelia\Action;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Thelia\Tools\URL;
use Thelia\Core\Security\Exception\AuthenticationException;
use Thelia\Core\HttpKernel\Exception\RedirectException as ExceptionRedirectException;
/**
* Class RedirectException
* @package Thelia\Action
* @author manuel raynaud <manu@raynaud.io>
*/
class RedirectException extends BaseAction implements EventSubscriberInterface
{
/** @var URL */
protected $urlManager;
public function __construct(URL $urlManager)
{
$this->urlManager = $urlManager;
}
public function checkRedirectException(GetResponseForExceptionEvent $event)
{
$exception = $event->getException();
if ($exception instanceof ExceptionRedirectException) {
$response = RedirectResponse::create($exception->getUrl(), $exception->getStatusCode());
$event->setResponse($response);
} elseif ($exception instanceof AuthenticationException) {
// Redirect to the login template
$response = RedirectResponse::create($this->urlManager->viewUrl($exception->getLoginTemplate()));
$event->setResponse($response);
}
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents()
{
return [
KernelEvents::EXCEPTION => array("checkRedirectException", 128),
];
}
}

View File

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

View File

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

View File

@@ -0,0 +1,103 @@
<?php
/*************************************************************************************/
/* This file is part of the Thelia package. */
/* */
/* Copyright (c) OpenStudio */
/* email : dev@thelia.net */
/* web : http://www.thelia.net */
/* */
/* For the full copyright and license information, please view the LICENSE.txt */
/* file that was distributed with this source code. */
/*************************************************************************************/
namespace Thelia\Action;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Thelia\Core\Event\State\StateCreateEvent;
use Thelia\Core\Event\State\StateDeleteEvent;
use Thelia\Core\Event\State\StateToggleVisibilityEvent;
use Thelia\Core\Event\State\StateUpdateEvent;
use Thelia\Core\Event\TheliaEvents;
use Thelia\Model\State as StateModel;
use Thelia\Model\StateQuery;
/**
* Class State
* @package Thelia\Action
* @author Julien Chanséaume <julien@thelia.net>
*/
class State extends BaseAction implements EventSubscriberInterface
{
public function create(StateCreateEvent $event)
{
$state = new StateModel();
$state
->setVisible($event->isVisible())
->setCountryId($event->getCountry())
->setIsocode($event->getIsocode())
->setLocale($event->getLocale())
->setTitle($event->getTitle())
->save()
;
$event->setState($state);
}
public function update(StateUpdateEvent $event)
{
if (null !== $state = StateQuery::create()->findPk($event->getStateId())) {
$state
->setVisible($event->isVisible())
->setCountryId($event->getCountry())
->setIsocode($event->getIsocode())
->setLocale($event->getLocale())
->setTitle($event->getTitle())
->save()
;
$event->setState($state);
}
}
public function delete(StateDeleteEvent $event)
{
if (null !== $state = StateQuery::create()->findPk($event->getStateId())) {
$state->delete();
$event->setState($state);
}
}
/**
* Toggle State visibility
*
* @param StateToggleVisibilityEvent $event
*/
public function toggleVisibility(StateToggleVisibilityEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$state = $event->getState();
$state
->setDispatcher($dispatcher)
->setVisible(!$state->getVisible())
->save()
;
$event->setState($state);
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents()
{
return array(
TheliaEvents::STATE_CREATE => array('create', 128),
TheliaEvents::STATE_UPDATE => array('update', 128),
TheliaEvents::STATE_DELETE => array('delete', 128),
TheliaEvents::STATE_TOGGLE_VISIBILITY => array('toggleVisibility', 128)
);
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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