Initial commit
This commit is contained in:
2
.env.dist
Normal file
2
.env.dist
Normal file
@@ -0,0 +1,2 @@
|
||||
# This file is a "template" of which env vars need to be defined for your application
|
||||
# Copy this file to .env file for development, create environment variables when deploying to production
|
||||
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
@@ -0,0 +1 @@
|
||||
CHANGELOG.md merge=union
|
||||
81
.gitignore
vendored
Normal file
81
.gitignore
vendored
Normal file
@@ -0,0 +1,81 @@
|
||||
local/config/build.properties
|
||||
local/config/config_db.php
|
||||
local/config/build
|
||||
local/config/database.yml
|
||||
local/config/database.yml.sample.save
|
||||
local/config/key
|
||||
core/vendor
|
||||
local/config/runtime-conf.xml
|
||||
cache/*
|
||||
log/*
|
||||
bin/*
|
||||
local/session/*
|
||||
coverage
|
||||
.idea
|
||||
.buildpath
|
||||
.project
|
||||
.settings/
|
||||
local/media/documents/*
|
||||
local/media/images/*
|
||||
!local/media/images/store/favicon.png
|
||||
!local/media/images/store/logo.png
|
||||
!local/media/images/store/banner.jpg
|
||||
web/assets/*
|
||||
web/cache/*
|
||||
web/tinymce
|
||||
web/media
|
||||
phpdoc*.log
|
||||
xhprof/
|
||||
phpunit.phar
|
||||
.DS_Store
|
||||
phpmyadmin
|
||||
composer.phar
|
||||
web/.htaccess
|
||||
local/I18n/*.php
|
||||
.docker/mysql-data/*
|
||||
!.docker/mysql-data/.gitkeep
|
||||
|
||||
# Ignore everything in the "modules" directory, except the "default modules"
|
||||
local/modules/*
|
||||
!local/modules/Cheque/
|
||||
!local/modules/Carousel/
|
||||
!local/modules/Front/
|
||||
!local/modules/FreeOrder/
|
||||
!local/modules/Tinymce/
|
||||
!local/modules/Colissimo/
|
||||
!local/modules/Hook*/
|
||||
!local/modules/VirtualProductDelivery/
|
||||
!local/modules/VirtualProductControl/
|
||||
!local/modules/TheliaSmarty/
|
||||
!local/modules/TheliaMigrateCountry/
|
||||
local/modules/HookTest/
|
||||
|
||||
# Ignore everything in the "templates" directory, except the "default template"
|
||||
templates/*
|
||||
!templates/frontOffice
|
||||
templates/frontOffice/*
|
||||
!templates/frontOffice/default
|
||||
!templates/backOffice
|
||||
templates/backOffice/*
|
||||
!templates/backOffice/default
|
||||
!templates/email
|
||||
templates/email/*
|
||||
!templates/email/default
|
||||
!templates/pdf
|
||||
templates/pdf/*
|
||||
!templates/pdf/default
|
||||
|
||||
#Ignore CodeKit
|
||||
codekit-config.json
|
||||
config.codekit
|
||||
.codekit-cache
|
||||
|
||||
#MacOS
|
||||
.DS_Store
|
||||
|
||||
# Ignore casperjs screenshots
|
||||
tests/functionnal/casperjs/screenshot/
|
||||
tests/phpunit/Thelia/Tests/Api/fixtures/visuel*.png
|
||||
/.docker/
|
||||
/.github/
|
||||
/.well-known/
|
||||
3
.htaccess
Normal file
3
.htaccess
Normal file
@@ -0,0 +1,3 @@
|
||||
#<IfModule mod_alias.c>
|
||||
#RedirectMatch 301 ^/$ https://auxbieauxlegumes.fr/web
|
||||
#</IfModule>
|
||||
1312
CHANGELOG.md
Normal file
1312
CHANGELOG.md
Normal file
File diff suppressed because it is too large
Load Diff
51
CONTRIBUTORS.md
Normal file
51
CONTRIBUTORS.md
Normal file
@@ -0,0 +1,51 @@
|
||||
CONTRIBUTORS
|
||||
============
|
||||
|
||||
If you contributes or contributed to this project and do not appear in this list below,
|
||||
please email us (info@thelia.net) or fork this file on GitHub and send a pull-request.
|
||||
|
||||
- Manuel Raynaud (lunika)
|
||||
- Franck Allimant (roadster31)
|
||||
- Etienne Roudeix (etienneroudeix)
|
||||
- Benjamin Perche (lovenunu)
|
||||
- Julien Chanseaume (bibich)
|
||||
- Christophe Laffont (touffies)
|
||||
- Michaël Espeche (mespeche)
|
||||
- Gilles Bourgeat (gillesbourgeat)
|
||||
- Guillaume Morel (gmorel)
|
||||
- Yochima (Yochima)
|
||||
- Goleo Bruno (badsuricate)
|
||||
- (zzuutt)
|
||||
- Jérôme Billiras (bilhackmac)
|
||||
- Emmanuel Nurit (enurit)
|
||||
- (Asturyan)
|
||||
- Etienne Perriere (Mertiozys)
|
||||
- (griotteau)
|
||||
- Alban Baixas (Alban-io)
|
||||
- Christophe (InformatiqueProg)
|
||||
- (jodeq)
|
||||
- (nicolasleon)
|
||||
- Chevrier (AnthonyMeedle)
|
||||
- Romain Ducher (air-dex)
|
||||
- Antony Penalver (Soldras)
|
||||
- David Rimbault (Id4v)
|
||||
- Stéphanie Pinet (stephaniepinet)
|
||||
- Damien Foulhoux (Lucanis)
|
||||
- Vincent Lopes-Vicente (lopes-vincent)
|
||||
- (alex63530)
|
||||
- Arnault Pachot (apachot)
|
||||
- Cédric Sibaud (csibaud)
|
||||
- Stéphane Calisti (csteph85)
|
||||
- Alex Rock Ancelet (Pierstoval)
|
||||
- Baptiste Cabarrou (bcabarrou)
|
||||
- Adrien Bourroux (driou)
|
||||
- (AdeDidou)
|
||||
- DirtyF (DirtyF)
|
||||
- (xockduo)
|
||||
- Michaël Marinetti (Asenar)
|
||||
- Mickaël (Kira-kyuukyoku)
|
||||
- Quentin Dufour (superboum)
|
||||
- Lespes (knjeurope)
|
||||
- Tarun Garg (tarun1793)
|
||||
- poum (poum)
|
||||
- Luis Cordova (cordoval)
|
||||
4
COPYRIGHT.txt
Normal file
4
COPYRIGHT.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
THELIA
|
||||
Copyright (C) 2005-2015 OpenStudio
|
||||
|
||||
THELIA application uses externals components and libraries which are released under their own LGPL compatible license terms.
|
||||
165
LICENSE.txt
Normal file
165
LICENSE.txt
Normal 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.
|
||||
227
Readme.md
Normal file
227
Readme.md
Normal file
@@ -0,0 +1,227 @@
|
||||
Readme
|
||||
======
|
||||
|
||||
## Warning
|
||||
### This is the development repository of Thelia. 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 download a packaged, ready-to-use distribution of the most recent version of Thelia please download [thelia.zip](https://thelia.net/download/thelia.zip)
|
||||
|
||||
Thelia
|
||||
------
|
||||
[](https://github.com/thelia/thelia/actions?query=workflow%3A"test")
|
||||
[](https://scrutinizer-ci.com/g/thelia/thelia/)
|
||||
[](https://packagist.org/packages/thelia/thelia)
|
||||
|
||||
[Thelia](https://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.
|
||||
|
||||
A repository containing all thelia modules is available at this address : https://github.com/thelia-modules
|
||||
|
||||
Compatibility
|
||||
------------
|
||||
|
||||
| | Thelia 2.1 | Thelia 2.2 | Thelia 2.3 | Thelia 2.4 |
|
||||
| ------------- |:-------------:| -----:| -----:| -----:|
|
||||
| PHP | 5.4 5.5 5.6 | 5.4 5.5 5.6 | 5.5 5.6 7.0 7.1 | 5.6 7.0 7.1 7.2 7.3 |
|
||||
| MySQL | 5.5 5.6 | 5.5 5.6 | 5.5 5.6 | 5.5 5.6 5.7 |
|
||||
| Symfony | 2.3 | 2.3 | 2.8 | 2.8 |
|
||||
|
||||
Requirements
|
||||
------------
|
||||
|
||||
* PHP
|
||||
* Required extensions :
|
||||
* PDO_Mysql
|
||||
* openssl
|
||||
* intl
|
||||
* gd
|
||||
* curl
|
||||
* dom
|
||||
* safe_mode off
|
||||
* memory_limit at least 128M, preferably 256.
|
||||
* post\_max\_size 20M
|
||||
* upload\_max\_filesize 2M
|
||||
* date.timezone must be defined
|
||||
* Web Server Apache 2 or Nginx
|
||||
* MySQL 5
|
||||
|
||||
|
||||
### MySQL 5.6
|
||||
|
||||
As of MySQL 5.6, default configuration sets the sql_mode value to
|
||||
|
||||
```
|
||||
STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION
|
||||
```
|
||||
|
||||
This 'STRICT_TRANS_TABLES' configuration results in SQL errors when no default value is defined on NOT NULL columns and the value is empty or invalid.
|
||||
|
||||
You can edit this default config in ` /etc/my.cnf ` and change the sql_mode to remove the STRICT_TRANS_TABLES part
|
||||
|
||||
```
|
||||
[mysqld]
|
||||
sql_mode=NO_ENGINE_SUBSTITUTION
|
||||
```
|
||||
|
||||
Assuming your sql_mode is the default one, you can change the value directly on the run by running the following SQL Command
|
||||
|
||||
```sql
|
||||
SET @@GLOBAL.sql_mode='NO_ENGINE_SUBSTITUTION', @@SESSION.sql_mode='NO_ENGINE_SUBSTITUTION'
|
||||
```
|
||||
|
||||
For more information on sql_mode you can consult the [MySQL doc](https://dev.mysql.com/doc/refman/5.0/fr/server-sql-mode.html "sql Mode")
|
||||
|
||||
## Archive builders
|
||||
Thelia's archive builder's needs external libraries.
|
||||
For zip archives, you need PECL zip. See [PHP Doc](https://php.net/manual/en/zip.installation.php)
|
||||
|
||||
For tar archives, you need PECL phar. Moreover, you need to deactivate php.ini option "phar.readonly":
|
||||
```ini
|
||||
phar.readonly = Off
|
||||
```
|
||||
|
||||
For tar.bz2 archives, you need tar's dependencies and the extension "bzip2". See [PHP Doc](https://php.net/manual/fr/book.bzip2.php)
|
||||
|
||||
For tar.gz archives, you need tar's dependencies and the extension "zlib". See [PHP Doc](https://fr2.php.net/manual/fr/book.zlib.php)
|
||||
|
||||
## Download Thelia 2 and install its dependencies
|
||||
|
||||
You can get the sources from git and then let composer install dependencies, or use composer to install the whole thelia project into a specific directory
|
||||
|
||||
### Using git for download and composer for dependencies
|
||||
|
||||
``` bash
|
||||
$ git clone --recursive https://github.com/thelia/thelia path
|
||||
$ cd path
|
||||
$ git checkout 2.4.3 (2.3.5 or 2.2.6)
|
||||
$ curl -sS https://getcomposer.org/installer | php
|
||||
$ php composer.phar install
|
||||
```
|
||||
|
||||
### Using composer for both download and dependencies
|
||||
|
||||
``` bash
|
||||
$ curl -sS https://getcomposer.org/installer | php
|
||||
$ php composer.phar create-project thelia/thelia path/ 2.4.3 (2.3.5 or 2.2.6)
|
||||
```
|
||||
|
||||
If something goes wrong during the install process, you can restart Thelia install wizard with
|
||||
the following command : `php composer.phar run-script post-create-project-cmd`
|
||||
|
||||
## Install it
|
||||
|
||||
You can install Thelia by two different way
|
||||
|
||||
### Using install wizard
|
||||
|
||||
Installing thelia with the web install wizard allow to create an administrator, add some informations about your shop, etc
|
||||
|
||||
First of all, you have to configure a vhost as describe in [configuration](https://doc.thelia.net/en/documentation/configuration.html) section.
|
||||
|
||||
The install wizard in accessible with your favorite browser :
|
||||
|
||||
``` bash
|
||||
https://yourdomain.tld/[/subdomain_if_needed]/install
|
||||
```
|
||||
|
||||
For example, I have thelia downloaded at https://thelia.net and my vhost is correctly configured, I have to reach this address :
|
||||
|
||||
``` bash
|
||||
https://thelia.net/install
|
||||
```
|
||||
|
||||
### Using cli tools
|
||||
|
||||
``` bash
|
||||
$ php Thelia thelia:install
|
||||
```
|
||||
|
||||
or if you use a Thelia project :
|
||||
|
||||
``` bash
|
||||
$ php composer.phar run-script post-create-project-cmd
|
||||
```
|
||||
|
||||
You just have to follow all instructions.
|
||||
|
||||
### Docker and docker compose
|
||||
|
||||
This repo contains all the configuration needed to run Thelia with docker and docker-compose.
|
||||
Warning, this docker configuration is not ready for production.
|
||||
|
||||
It requires obviously [docker](https://docker.com/) and [docker-compose](https://docs.docker.com/compose/)
|
||||
|
||||
To install Thelia within Docker, run :
|
||||
|
||||
```
|
||||
docker-compose up -d
|
||||
docker-compose exec php-fpm composer install
|
||||
docker-compose exec php-fpm php Thelia thelia:install
|
||||
```
|
||||
|
||||
By default if you haven't changed the `docker-compose.yml` you'll have to answer these questions like this
|
||||
|
||||
```
|
||||
Database host [default: localhost] : mariadb
|
||||
```
|
||||
```
|
||||
Database port [default: 3306] : 3306
|
||||
```
|
||||
```
|
||||
Database name (if database does not exist, Thelia will try to create it) : thelia
|
||||
```
|
||||
|
||||
```
|
||||
Database username : thelia
|
||||
```
|
||||
|
||||
```
|
||||
Database pasword : thelia
|
||||
```
|
||||
|
||||
Next just go to http://localhost:8080 and you should see your Thelia installed !
|
||||
|
||||
tip : create an alias for docker-compose, it's boring to write it all the time
|
||||
|
||||
If you want add some sample data just execute this command (still in your container)
|
||||
``` bash
|
||||
docker-compose exec php-fpm php setup/import.php
|
||||
```
|
||||
|
||||
If you want to access your database from your computer (with DBeaver, Sequel Pro or anything else) by default the host is `localhost` and the port is `8086`
|
||||
|
||||
Obviously you can modify all the configuration for your own case, for example the php version or add environment variable for the database configuration. Each time you modify the configuration, you have to rebuild it :
|
||||
|
||||
```
|
||||
docker-compose build --no-cache
|
||||
```
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
|
||||
Thelia documentation is available at https://doc.thelia.net
|
||||
|
||||
|
||||
Contribute
|
||||
----------
|
||||
|
||||
See the documentation : http://doc.thelia.net/en/documentation/contribute.html
|
||||
|
||||
|
||||
If you submit modifications that adds new data or change the structure of the database, take a look to https://doc.thelia.net/en/documentation/contribute.html#sql-scripts-modification
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
Consult the page : https://localhost/thelia/web/index_dev.php
|
||||
|
||||
You can create a virtual host and choose the web folder for root directory.
|
||||
|
||||
To run tests (phpunit required) :
|
||||
|
||||
``` bash
|
||||
$ phpunit
|
||||
```
|
||||
|
||||
We still have a lot of work to achieve but enjoy this part.
|
||||
5
Thelia
Normal file
5
Thelia
Normal file
@@ -0,0 +1,5 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
$bootstrapFile = __DIR__ . "/core/vendor/autoload.php";
|
||||
include __DIR__ . "/core/Thelia";
|
||||
68
UPDATE.md
Normal file
68
UPDATE.md
Normal file
@@ -0,0 +1,68 @@
|
||||
# How to update your Thelia ?
|
||||
|
||||
If you have already installed Thelia but a new version is available, you can update easily.
|
||||
|
||||
Before proceeding to the update, it's strongly recommended to backup your website (files and database).
|
||||
You can backup your database with tools such as [phpmyadmin](http://www.phpmyadmin.net)
|
||||
or [mysqldump](dev.mysql.com/doc/refman/5.6/en/mysqldump.html).
|
||||
|
||||
## 1. Update files
|
||||
|
||||
- Download the latest version of Thelia : <http://thelia.net/download/thelia.zip>
|
||||
- Extract the zip in a temporary directory
|
||||
- Then you should replace (not only copy) all the files from the new Thelia version :
|
||||
- all files from root directory
|
||||
- bin (*optional*)
|
||||
- core (**mandatory**)
|
||||
- setup (**mandatory**)
|
||||
- Then, you have to merge (copy in your existent directories) these other directories. Normally,
|
||||
you haven't modify files inside these directories (just created new ones - like your frontOffice template).
|
||||
But If you have modified files, you should proceed carefully and try to report all your changes.
|
||||
- local/config
|
||||
- local/modules
|
||||
- templates
|
||||
- web
|
||||
|
||||
|
||||
## 2. Update database
|
||||
|
||||
Then you have 2 different ways to proceed. In each method, a backup of your database can be automatically
|
||||
performed if you want to. If an error is encountered, then your database will be restored.
|
||||
But if your database is quite large, it's better to make a backup manually.
|
||||
|
||||
### 2.1. use the update script
|
||||
|
||||
In a command shell, go to the root directory of your installation, run and follow instructions :
|
||||
|
||||
```bash
|
||||
php setup/update.php
|
||||
```
|
||||
|
||||
### 2.2. use the update wizard
|
||||
|
||||
An update wizard is available in the ```web/install``` directory. It's the same directory used by the install wizard.
|
||||
|
||||
**You have to protect the web folder if your site is public (htaccess, List of allowed IP, ...).**
|
||||
|
||||
The update wizard in accessible with your favorite browser :
|
||||
|
||||
```bash
|
||||
http://yourdomain.tld/[/subdomain_if_needed]/install
|
||||
```
|
||||
|
||||
Note:
|
||||
|
||||
- the wizard is available only if your Thelia is not already in the latest version.
|
||||
- at the end of the process, the install directory will be removed.
|
||||
|
||||
|
||||
## 3. Clear cache
|
||||
|
||||
Once the update is done successfully, you have to clear all caches :
|
||||
|
||||
- clear all caches in all environment :
|
||||
- ```php Thelia cache:clear```
|
||||
- ```php Thelia cache:clear --env=prod```
|
||||
|
||||
If the command fails, you can do it manually. Just delete the content of
|
||||
the ```cache``` and ```web/cache``` directories.
|
||||
131
UPGRADE-2.3.md
Normal file
131
UPGRADE-2.3.md
Normal file
@@ -0,0 +1,131 @@
|
||||
|
||||
UPGRADE FROM 2.2 to 2.3
|
||||
=======================
|
||||
|
||||
|
||||
EventDispatcher
|
||||
----------------
|
||||
|
||||
* The `getDispatcher()` and `getName()` methods from `Symfony\Component\EventDispatcher\Event`
|
||||
are deprecated, the event dispatcher instance and event name can be received in the listener call instead.
|
||||
|
||||
Before:
|
||||
|
||||
```php
|
||||
use Symfony\Component\EventDispatcher\Event;
|
||||
|
||||
class Foo
|
||||
{
|
||||
public function myFooListener(Event $event)
|
||||
{
|
||||
$dispatcher = $event->getDispatcher();
|
||||
$eventName = $event->getName();
|
||||
$dispatcher->dispatch('log', $event);
|
||||
|
||||
// ... more code
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
After:
|
||||
|
||||
```php
|
||||
use Symfony\Component\EventDispatcher\Event;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
|
||||
class MyListenerClass
|
||||
{
|
||||
public function myListenerMethod(Event $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
$dispatcher->dispatch('log', $event);
|
||||
|
||||
// ... more code
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
While this above is sufficient for most uses, **if your module must be compatible with versions less than 2.3, or if your module uses multiple EventDispatcher instances,** you might need to specifically inject a known instance of the `EventDispatcher` into your listeners. This could be done using constructor or setter injection as follows:
|
||||
|
||||
```php
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
|
||||
class MyListenerClass
|
||||
{
|
||||
protected $dispatcher = null;
|
||||
|
||||
public function __construct(EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
$this->dispatcher = $dispatcher;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Request and RequestStack
|
||||
----------------
|
||||
|
||||
* The `Request` service are deprecated, you must now use the `RequestStack` service.
|
||||
|
||||
##### In your loops
|
||||
The way to recover the request does not change.
|
||||
|
||||
To get the current request
|
||||
|
||||
```php
|
||||
class MyLoopClass extends BaseLoop implements PropelSearchLoopInterface
|
||||
{
|
||||
public function buildModelCriteria()
|
||||
{
|
||||
// Get the current request
|
||||
$request = $this->getCurrentRequest();
|
||||
// Or
|
||||
$request = $this->requestStack->getCurrentRequest();
|
||||
|
||||
// ... more code
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
##### In your controllers
|
||||
It's not recommended to use `getRequest()` and `getSession()`, the Request instance can be received in the action method parameters.
|
||||
However, the `getRequest()` method returns the current request.
|
||||
**Warning !!** This is not compatible with Thelia 2.0, because it uses Symfony 2.2
|
||||
|
||||
To get the current request
|
||||
|
||||
```php
|
||||
use Thelia\Core\HttpFoundation\Request;
|
||||
|
||||
class MyControllerClass extends ...
|
||||
{
|
||||
public function MyActionMethod(Request $request, $query_parameters ...)
|
||||
{
|
||||
$session = $request->getSession();
|
||||
// ... more code
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Container Scopes
|
||||
----------------
|
||||
|
||||
* The "container scopes" concept no longer exists in Thelia 2.3.
|
||||
For backward compatibility, the attributes `scope` is automatically removed of the xml configuration files.
|
||||
**Warning !!** The attributes `scope` are always needed for your modules compatible with Thelia < 2.3.
|
||||
[See the Symfony documentation for more information](http://symfony.com/doc/2.8/cookbook/service_container/scopes.html)
|
||||
|
||||
|
||||
Unit Test
|
||||
----------------
|
||||
|
||||
* The `SecurityContext`, `ParserContext`, `TokenProvider`, `TheliaFormFactory`, `TaxEngine` services are no longer dependent on "Request", but "RequestSTack".
|
||||
This may break your unit tests.
|
||||
|
||||
For more information about the upgrade from Symfony 2.3 to Symfony 2.8
|
||||
----------------
|
||||
|
||||
[Upgrade from Symfony 2.3 to 2.4](https://github.com/symfony/symfony/blob/2.8/UPGRADE-2.4.md)
|
||||
[Upgrade from Symfony 2.4 to 2.5](https://github.com/symfony/symfony/blob/2.8/UPGRADE-2.5.md)
|
||||
[Upgrade from Symfony 2.5 to 2.6](https://github.com/symfony/symfony/blob/2.8/UPGRADE-2.6.md)
|
||||
[Upgrade from Symfony 2.6 to 2.7](https://github.com/symfony/symfony/blob/2.8/UPGRADE-2.7.md)
|
||||
[Upgrade from Symfony 2.7 to 2.8](https://github.com/symfony/symfony/blob/2.8/UPGRADE-2.8.md)
|
||||
[Upgrade from Symfony 2.8 to 3.0](https://github.com/symfony/symfony/blob/2.8/UPGRADE-3.0.md)
|
||||
101
composer.json
Normal file
101
composer.json
Normal file
@@ -0,0 +1,101 @@
|
||||
{
|
||||
"name": "thelia/thelia",
|
||||
"description": "Thelia is an ecommerce CMS.",
|
||||
"license": "LGPL-3.0+",
|
||||
"homepage": "http://thelia.net/",
|
||||
"keywords": [
|
||||
"ecommerce",
|
||||
"cms",
|
||||
"cms ecommerce",
|
||||
"ecommerce cms"
|
||||
],
|
||||
"support": {
|
||||
"forum": "http://thelia.net/forum",
|
||||
"wiki": "http://doc.thelia.net"
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.6 <7.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.*",
|
||||
"symfony/finder": "2.8.*",
|
||||
"symfony/browser-kit": "2.8.*",
|
||||
"symfony/http-foundation": "2.8.*",
|
||||
"symfony/form": "2.8.*",
|
||||
"symfony/class-loader": "2.8.*",
|
||||
"symfony/dotenv": "^3.4|^4.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.7.1",
|
||||
"swiftmailer/swiftmailer": "5.4.*",
|
||||
"oyejorge/less.php": "1.7.*",
|
||||
"michelf/php-markdown": "1.6.*",
|
||||
"smarty/smarty": "3.1.33",
|
||||
"commerceguys/addressing": "0.8.*",
|
||||
"symfony/cache": "^3.4|^4.0",
|
||||
"symfony/polyfill-php56": "^1.0",
|
||||
"symfony/polyfill-php70": "^1.0",
|
||||
"symfony/polyfill-php71": "^1.0",
|
||||
"symfony/polyfill-php72": "^1.0",
|
||||
"spipu/html2pdf": "^5.0",
|
||||
"symfony/polyfill-php73": "^1.0",
|
||||
"symfony/lock": "^3.4|^4.0",
|
||||
"thelia/propel": "dev-thelia-2.4",
|
||||
"symfony/var-dumper": "^2.0|^3.0|^4.0",
|
||||
"thelia/colissimo-module": "^2.4",
|
||||
"thelia/smarty-module": "^2.4"
|
||||
},
|
||||
"require-dev": {
|
||||
"fzaninotto/faker": "1.5.*",
|
||||
"thelia/hooktest-module": "~1.1",
|
||||
"thelia/hooktest-template": "~1.1",
|
||||
"phpunit/phpunit": "^5.0"
|
||||
},
|
||||
"minimum-stability": "stable",
|
||||
"config": {
|
||||
"vendor-dir": "core/vendor",
|
||||
"bin-dir": "bin"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Thelia\\": "core/lib/Thelia"
|
||||
},
|
||||
"psr-0": {
|
||||
"": "local/modules/"
|
||||
},
|
||||
"files": [
|
||||
"core/bootstrap.php"
|
||||
]
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Thelia\\Tests\\" : "tests/phpunit/Thelia/Tests"
|
||||
}
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.4-dev"
|
||||
}
|
||||
}
|
||||
}
|
||||
5426
composer.lock
generated
Normal file
5426
composer.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
165
core/LICENSE.txt
Normal file
165
core/LICENSE.txt
Normal 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.
|
||||
47
core/Readme.md
Normal file
47
core/Readme.md
Normal file
@@ -0,0 +1,47 @@
|
||||
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
|
||||
------
|
||||
[](https://travis-ci.org/thelia/thelia) [](https://packagist.org/packages/thelia/thelia) [](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
|
||||
|
||||
|
||||
Compatibility
|
||||
------------
|
||||
|
||||
| | Thelia 2.1 | Thelia 2.2 | Thelia 2.3 | Thelia 2.4 |
|
||||
| ------------- |:-------------:| -----:| -----:| -----:|
|
||||
| PHP | 5.4 5.5 5.6 | 5.4 5.5 5.6 | 5.5 5.6 7.0 7.1 | 5.6 7.0 7.1 7.2 |
|
||||
| MySQL | 5.5 5.6 | 5.5 5.6 | 5.5 5.6 | 5.5 5.6 5.7 |
|
||||
| Symfony | 2.3 | 2.3 | 2.8 | 2.8 |
|
||||
|
||||
Requirements
|
||||
------------
|
||||
|
||||
* PHP
|
||||
* Required extensions :
|
||||
* PDO_Mysql
|
||||
* openssl
|
||||
* intl
|
||||
* gd
|
||||
* curl
|
||||
* dom
|
||||
* safe_mode off
|
||||
* memory_limit at least 128M, preferably 256.
|
||||
* post\_max\_size 20M
|
||||
* upload\_max\_filesize 2M
|
||||
* date.timezone must be defined
|
||||
* Web Server Apache 2 or Nginx
|
||||
* MySQL 5
|
||||
58
core/Thelia
Normal file
58
core/Thelia
Normal file
@@ -0,0 +1,58 @@
|
||||
<?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;
|
||||
use Symfony\Component\Dotenv\Dotenv;
|
||||
|
||||
if (file_exists(THELIA_ROOT.'.env')) {
|
||||
(new Dotenv())->load(THELIA_ROOT.'.env');
|
||||
}
|
||||
|
||||
$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());
|
||||
102
core/bootstrap.php
Normal file
102
core/bootstrap.php
Normal file
@@ -0,0 +1,102 @@
|
||||
<?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. */
|
||||
/*************************************************************************************/
|
||||
|
||||
// Check php version
|
||||
|
||||
if (version_compare(phpversion(), "5.6", "<")) {
|
||||
die(sprintf(
|
||||
"Thelia needs at least php 5.6, but you are using php %s. Please upgrade before using Thelia.\n",
|
||||
phpversion()
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Thelia essential definitions
|
||||
*/
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// this will be used in our Propel model builders
|
||||
if (!defined('THELIA_PROPEL_BUILD_MODEL_PATH')) {
|
||||
define('THELIA_PROPEL_BUILD_MODEL_PATH', THELIA_CACHE_DIR . 'propel' . DS . 'model' . DS);
|
||||
}
|
||||
|
||||
// this will be used in our Propel model builders
|
||||
if (!defined('THELIA_PROPEL_BUILD_DATABASE_PATH')) {
|
||||
define('THELIA_PROPEL_BUILD_DATABASE_PATH', THELIA_CACHE_DIR . 'propel' . DS . 'database' . 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);
|
||||
} 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
79
core/composer.json
Normal 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"
|
||||
},
|
||||
"require": {
|
||||
"php": "^5.6|^7.0",
|
||||
"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.*",
|
||||
"symfony/finder": "2.8.*",
|
||||
"symfony/browser-kit": "2.8.*",
|
||||
"symfony/http-foundation": "2.8.*",
|
||||
"symfony/form": "2.8.*",
|
||||
"symfony/class-loader": "2.8.*",
|
||||
"symfony/dotenv": "^3.4|^4.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.7.1",
|
||||
"swiftmailer/swiftmailer": "5.4.*",
|
||||
"oyejorge/less.php": "1.7.*",
|
||||
"michelf/php-markdown": "1.6.*",
|
||||
"smarty/smarty": "3.1.33",
|
||||
"thelia/propel": "dev-thelia-2.4",
|
||||
"commerceguys/addressing": "0.8.*",
|
||||
"symfony/cache": "^3.4|^4.0",
|
||||
"symfony/polyfill-php56": "^1.0",
|
||||
"symfony/polyfill-php70": "^1.0",
|
||||
"symfony/polyfill-php71": "^1.0",
|
||||
"symfony/polyfill-php72": "^1.0",
|
||||
"spipu/html2pdf": "^5.0",
|
||||
"symfony/polyfill-php73": "^1.0",
|
||||
"symfony/lock": "^3.4|^4.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"fzaninotto/faker": "1.5.*",
|
||||
"thelia/hooktest-module": "~1.1",
|
||||
"thelia/hooktest-template": "~1.1",
|
||||
"phpunit/phpunit": "^5.0",
|
||||
"symfony/var-dumper": "^2.0|^3.0|^4.0"
|
||||
},
|
||||
"bin": [
|
||||
"Thelia"
|
||||
],
|
||||
"minimum-stability": "stable",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Thelia\\": "lib/Thelia/"
|
||||
},
|
||||
"files": [
|
||||
"bootstrap.php"
|
||||
]
|
||||
}
|
||||
}
|
||||
5372
core/composer.lock
generated
Normal file
5372
core/composer.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
108
core/lib/Thelia/Action/Address.php
Normal file
108
core/lib/Thelia/Action/Address.php
Normal file
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
/*************************************************************************************/
|
||||
/* This file is part of the Thelia package. */
|
||||
/* */
|
||||
/* Copyright (c) OpenStudio */
|
||||
/* email : dev@thelia.net */
|
||||
/* web : http://www.thelia.net */
|
||||
/* */
|
||||
/* For the full copyright and license information, please view the LICENSE.txt */
|
||||
/* file that was distributed with this source code. */
|
||||
/*************************************************************************************/
|
||||
|
||||
namespace Thelia\Action;
|
||||
|
||||
use Propel\Runtime\Exception\PropelException;
|
||||
use Propel\Runtime\Propel;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Thelia\Core\Event\Address\AddressCreateOrUpdateEvent;
|
||||
use Thelia\Core\Event\Address\AddressEvent;
|
||||
use Thelia\Core\Event\TheliaEvents;
|
||||
use Thelia\Model\Address as AddressModel;
|
||||
use Thelia\Model\Map\AddressTableMap;
|
||||
|
||||
/**
|
||||
* Class Address
|
||||
* @package Thelia\Action
|
||||
* @author Manuel Raynaud <manu@raynaud.io>
|
||||
*/
|
||||
class Address extends BaseAction implements EventSubscriberInterface
|
||||
{
|
||||
public function create(AddressCreateOrUpdateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
$address = new AddressModel();
|
||||
$address->setCustomer($event->getCustomer());
|
||||
$this->createOrUpdate($address, $event, $dispatcher);
|
||||
}
|
||||
|
||||
public function update(AddressCreateOrUpdateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
$addressModel = $event->getAddress();
|
||||
|
||||
$this->createOrUpdate($addressModel, $event, $dispatcher);
|
||||
}
|
||||
|
||||
public function delete(AddressEvent $event)
|
||||
{
|
||||
$address = $event->getAddress();
|
||||
|
||||
$address->delete();
|
||||
}
|
||||
|
||||
public function useDefault(AddressEvent $event)
|
||||
{
|
||||
$address = $event->getAddress();
|
||||
|
||||
$address->makeItDefault();
|
||||
}
|
||||
|
||||
protected function createOrUpdate(AddressModel $addressModel, AddressCreateOrUpdateEvent $event, $dispatcher)
|
||||
{
|
||||
$addressModel->setDispatcher($dispatcher);
|
||||
$con = Propel::getWriteConnection(AddressTableMap::DATABASE_NAME);
|
||||
$con->beginTransaction();
|
||||
try {
|
||||
$addressModel
|
||||
->setLabel($event->getLabel())
|
||||
->setTitleId($event->getTitle())
|
||||
->setFirstname($event->getFirstname())
|
||||
->setLastname($event->getLastname())
|
||||
->setAddress1($event->getAddress1())
|
||||
->setAddress2($event->getAddress2())
|
||||
->setAddress3($event->getAddress3())
|
||||
->setZipcode($event->getZipcode())
|
||||
->setCity($event->getCity())
|
||||
->setCountryId($event->getCountry())
|
||||
->setStateId($event->getState())
|
||||
->setCellphone($event->getCellphone())
|
||||
->setPhone($event->getPhone())
|
||||
->setCompany($event->getCompany())
|
||||
->save()
|
||||
;
|
||||
|
||||
if ($event->getIsDefault() && !$addressModel->getIsDefault()) {
|
||||
$addressModel->makeItDefault();
|
||||
}
|
||||
|
||||
$event->setAddress($addressModel);
|
||||
$con->commit();
|
||||
} catch (PropelException $e) {
|
||||
$con->rollback();
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
TheliaEvents::ADDRESS_CREATE => array("create", 128),
|
||||
TheliaEvents::ADDRESS_UPDATE => array("update", 128),
|
||||
TheliaEvents::ADDRESS_DELETE => array("delete", 128),
|
||||
TheliaEvents::ADDRESS_DEFAULT => array('useDefault', 128),
|
||||
);
|
||||
}
|
||||
}
|
||||
155
core/lib/Thelia/Action/Administrator.php
Normal file
155
core/lib/Thelia/Action/Administrator.php
Normal file
@@ -0,0 +1,155 @@
|
||||
<?php
|
||||
/*************************************************************************************/
|
||||
/* This file is part of the Thelia package. */
|
||||
/* */
|
||||
/* Copyright (c) OpenStudio */
|
||||
/* email : dev@thelia.net */
|
||||
/* web : http://www.thelia.net */
|
||||
/* */
|
||||
/* For the full copyright and license information, please view the LICENSE.txt */
|
||||
/* file that was distributed with this source code. */
|
||||
/*************************************************************************************/
|
||||
|
||||
namespace Thelia\Action;
|
||||
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Thelia\Core\Event\Administrator\AdministratorEvent;
|
||||
use Thelia\Core\Event\Administrator\AdministratorUpdatePasswordEvent;
|
||||
use Thelia\Core\Event\TheliaEvents;
|
||||
use Thelia\Mailer\MailerFactory;
|
||||
use Thelia\Model\Admin as AdminModel;
|
||||
use Thelia\Model\AdminQuery;
|
||||
use Thelia\Model\ConfigQuery;
|
||||
use Thelia\Tools\TokenProvider;
|
||||
|
||||
class Administrator extends BaseAction implements EventSubscriberInterface
|
||||
{
|
||||
/** @var MailerFactory */
|
||||
protected $mailer;
|
||||
|
||||
/** @var TokenProvider */
|
||||
protected $tokenProvider;
|
||||
|
||||
public function __construct(MailerFactory $mailer, TokenProvider $tokenProvider)
|
||||
{
|
||||
$this->mailer = $mailer;
|
||||
$this->tokenProvider = $tokenProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param AdministratorEvent $event
|
||||
* @param $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function create(AdministratorEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
$administrator = new AdminModel();
|
||||
|
||||
$administrator
|
||||
->setDispatcher($dispatcher)
|
||||
->setFirstname($event->getFirstname())
|
||||
->setLastname($event->getLastname())
|
||||
->setEmail($event->getEmail())
|
||||
->setLogin($event->getLogin())
|
||||
->setPassword($event->getPassword())
|
||||
->setProfileId($event->getProfile())
|
||||
->setLocale($event->getLocale())
|
||||
;
|
||||
|
||||
$administrator->save();
|
||||
|
||||
$event->setAdministrator($administrator);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param AdministratorEvent $event
|
||||
* @param $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function update(AdministratorEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
if (null !== $administrator = AdminQuery::create()->findPk($event->getId())) {
|
||||
$administrator
|
||||
->setDispatcher($dispatcher)
|
||||
->setFirstname($event->getFirstname())
|
||||
->setLastname($event->getLastname())
|
||||
->setLogin($event->getLogin())
|
||||
->setEmail($event->getEmail())
|
||||
->setProfileId($event->getProfile())
|
||||
->setLocale($event->getLocale())
|
||||
;
|
||||
|
||||
if ('' !== $event->getPassword()) {
|
||||
$administrator->setPassword($event->getPassword());
|
||||
}
|
||||
|
||||
$administrator->save();
|
||||
|
||||
$event->setAdministrator($administrator);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param AdministratorEvent $event
|
||||
*/
|
||||
public function delete(AdministratorEvent $event)
|
||||
{
|
||||
if (null !== $administrator = AdminQuery::create()->findPk($event->getId())) {
|
||||
$administrator
|
||||
->delete()
|
||||
;
|
||||
|
||||
$event->setAdministrator($administrator);
|
||||
}
|
||||
}
|
||||
|
||||
public function updatePassword(AdministratorUpdatePasswordEvent $event)
|
||||
{
|
||||
$admin = $event->getAdmin();
|
||||
|
||||
$admin
|
||||
->setPassword($event->getPassword())
|
||||
->setPasswordRenewToken(null)
|
||||
->save();
|
||||
}
|
||||
|
||||
public function createPassword(AdministratorEvent $event)
|
||||
{
|
||||
$admin = $event->getAdministrator();
|
||||
|
||||
$email = $admin->getEmail();
|
||||
|
||||
if (! empty($email)) {
|
||||
$renewToken = $this->tokenProvider->getToken();
|
||||
|
||||
$admin
|
||||
->setPasswordRenewToken($renewToken)
|
||||
->save();
|
||||
|
||||
$this->mailer->sendEmailMessage(
|
||||
'new_admin_password',
|
||||
[ ConfigQuery::getStoreEmail() => ConfigQuery::getStoreName() ],
|
||||
[ $email => $admin->getFirstname() . ' ' . $admin->getLastname() ],
|
||||
[
|
||||
'token' => $renewToken,
|
||||
'admin' => $admin
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
TheliaEvents::ADMINISTRATOR_CREATE => array('create', 128),
|
||||
TheliaEvents::ADMINISTRATOR_UPDATE => array('update', 128),
|
||||
TheliaEvents::ADMINISTRATOR_DELETE => array('delete', 128),
|
||||
TheliaEvents::ADMINISTRATOR_UPDATEPASSWORD => array('updatePassword', 128),
|
||||
TheliaEvents::ADMINISTRATOR_CREATEPASSWORD => array('createPassword', 128)
|
||||
);
|
||||
}
|
||||
}
|
||||
65
core/lib/Thelia/Action/Api.php
Normal file
65
core/lib/Thelia/Action/Api.php
Normal 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],
|
||||
];
|
||||
}
|
||||
}
|
||||
137
core/lib/Thelia/Action/Area.php
Normal file
137
core/lib/Thelia/Action/Area.php
Normal file
@@ -0,0 +1,137 @@
|
||||
<?php
|
||||
/*************************************************************************************/
|
||||
/* This file is part of the Thelia package. */
|
||||
/* */
|
||||
/* Copyright (c) OpenStudio */
|
||||
/* email : dev@thelia.net */
|
||||
/* web : http://www.thelia.net */
|
||||
/* */
|
||||
/* For the full copyright and license information, please view the LICENSE.txt */
|
||||
/* file that was distributed with this source code. */
|
||||
/*************************************************************************************/
|
||||
|
||||
namespace Thelia\Action;
|
||||
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Thelia\Core\Event\Area\AreaAddCountryEvent;
|
||||
use Thelia\Core\Event\Area\AreaCreateEvent;
|
||||
use Thelia\Core\Event\Area\AreaDeleteEvent;
|
||||
use Thelia\Core\Event\Area\AreaRemoveCountryEvent;
|
||||
use Thelia\Core\Event\Area\AreaUpdateEvent;
|
||||
use Thelia\Core\Event\Area\AreaUpdatePostageEvent;
|
||||
use Thelia\Core\Event\TheliaEvents;
|
||||
use Thelia\Model\Area as AreaModel;
|
||||
use Thelia\Model\AreaQuery;
|
||||
use Thelia\Model\CountryArea;
|
||||
use Thelia\Model\CountryAreaQuery;
|
||||
|
||||
/**
|
||||
* Class Area
|
||||
* @package Thelia\Action
|
||||
* @author Manuel Raynaud <manu@raynaud.io>
|
||||
*/
|
||||
class Area extends BaseAction implements EventSubscriberInterface
|
||||
{
|
||||
public function addCountry(AreaAddCountryEvent $event)
|
||||
{
|
||||
$countryIds = $event->getCountryId();
|
||||
|
||||
$areaId = $event->getAreaId();
|
||||
|
||||
foreach ($countryIds as $countryId) {
|
||||
$countryArea = new CountryArea();
|
||||
|
||||
$country = explode('-', $countryId);
|
||||
if (\count($country) === 1) {
|
||||
$country[1] = null;
|
||||
}
|
||||
if ($country[1] == 0) {
|
||||
$country[1] = null;
|
||||
}
|
||||
|
||||
$countryArea
|
||||
->setAreaId($areaId)
|
||||
->setCountryId($country[0])
|
||||
->setStateId($country[1])
|
||||
->save()
|
||||
;
|
||||
}
|
||||
|
||||
$event->setArea(AreaQuery::create()->findPk($areaId));
|
||||
}
|
||||
|
||||
public function removeCountry(AreaRemoveCountryEvent $event)
|
||||
{
|
||||
CountryAreaQuery::create()
|
||||
->filterByCountryId($event->getCountryId())
|
||||
->filterByStateId($event->getStateId())
|
||||
->filterByAreaId($event->getAreaId())
|
||||
->delete();
|
||||
|
||||
if (null !== $area = AreaQuery::create()->findPk($event->getAreaId())) {
|
||||
$event->setArea($area);
|
||||
}
|
||||
}
|
||||
|
||||
public function updatePostage(AreaUpdatePostageEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
if (null !== $area = AreaQuery::create()->findPk($event->getAreaId())) {
|
||||
$area->setDispatcher($dispatcher);
|
||||
$area
|
||||
->setPostage($event->getPostage())
|
||||
->save();
|
||||
|
||||
$event->setArea($area);
|
||||
}
|
||||
}
|
||||
|
||||
public function delete(AreaDeleteEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
if (null !== $area = AreaQuery::create()->findPk($event->getAreaId())) {
|
||||
$area->setDispatcher($dispatcher);
|
||||
$area->delete();
|
||||
|
||||
$event->setArea($area);
|
||||
}
|
||||
}
|
||||
|
||||
public function create(AreaCreateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
$area = new AreaModel();
|
||||
|
||||
$area
|
||||
->setDispatcher($dispatcher)
|
||||
->setName($event->getAreaName())
|
||||
->save();
|
||||
|
||||
$event->setArea($area);
|
||||
}
|
||||
|
||||
public function update(AreaUpdateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
if (null !== $area = AreaQuery::create()->findPk($event->getAreaId())) {
|
||||
$area
|
||||
->setDispatcher($dispatcher)
|
||||
->setName($event->getAreaName())
|
||||
->save();
|
||||
|
||||
$event->setArea($area);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
TheliaEvents::AREA_ADD_COUNTRY => array('addCountry', 128),
|
||||
TheliaEvents::AREA_REMOVE_COUNTRY => array('removeCountry', 128),
|
||||
TheliaEvents::AREA_POSTAGE_UPDATE => array('updatePostage', 128),
|
||||
TheliaEvents::AREA_DELETE => array('delete', 128),
|
||||
TheliaEvents::AREA_CREATE => array('create', 128),
|
||||
TheliaEvents::AREA_UPDATE => array('update', 128)
|
||||
);
|
||||
}
|
||||
}
|
||||
157
core/lib/Thelia/Action/Attribute.php
Normal file
157
core/lib/Thelia/Action/Attribute.php
Normal file
@@ -0,0 +1,157 @@
|
||||
<?php
|
||||
/*************************************************************************************/
|
||||
/* This file is part of the Thelia package. */
|
||||
/* */
|
||||
/* Copyright (c) OpenStudio */
|
||||
/* email : dev@thelia.net */
|
||||
/* web : http://www.thelia.net */
|
||||
/* */
|
||||
/* For the full copyright and license information, please view the LICENSE.txt */
|
||||
/* file that was distributed with this source code. */
|
||||
/*************************************************************************************/
|
||||
|
||||
namespace Thelia\Action;
|
||||
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Thelia\Model\AttributeQuery;
|
||||
use Thelia\Model\Attribute as AttributeModel;
|
||||
use Thelia\Core\Event\TheliaEvents;
|
||||
use Thelia\Core\Event\Attribute\AttributeUpdateEvent;
|
||||
use Thelia\Core\Event\Attribute\AttributeCreateEvent;
|
||||
use Thelia\Core\Event\Attribute\AttributeDeleteEvent;
|
||||
use Thelia\Core\Event\UpdatePositionEvent;
|
||||
use Thelia\Core\Event\Attribute\AttributeEvent;
|
||||
use Thelia\Model\AttributeTemplate;
|
||||
use Thelia\Model\AttributeTemplateQuery;
|
||||
use Thelia\Model\TemplateQuery;
|
||||
|
||||
class Attribute extends BaseAction implements EventSubscriberInterface
|
||||
{
|
||||
/**
|
||||
* Create a new attribute entry
|
||||
*
|
||||
* @param AttributeCreateEvent $event
|
||||
* @param $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function create(AttributeCreateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
$attribute = new AttributeModel();
|
||||
|
||||
$attribute
|
||||
->setDispatcher($dispatcher)
|
||||
->setLocale($event->getLocale())
|
||||
->setTitle($event->getTitle())
|
||||
->save()
|
||||
;
|
||||
|
||||
$event->setAttribute($attribute);
|
||||
|
||||
// Add atribute to all product templates if required
|
||||
if ($event->getAddToAllTemplates() != 0) {
|
||||
$this->doAddToAllTemplates($attribute);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Change a product attribute
|
||||
*
|
||||
* @param AttributeUpdateEvent $event
|
||||
* @param $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function update(AttributeUpdateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
if (null !== $attribute = AttributeQuery::create()->findPk($event->getAttributeId())) {
|
||||
$attribute
|
||||
->setDispatcher($dispatcher)
|
||||
|
||||
->setLocale($event->getLocale())
|
||||
->setTitle($event->getTitle())
|
||||
->setDescription($event->getDescription())
|
||||
->setChapo($event->getChapo())
|
||||
->setPostscriptum($event->getPostscriptum())
|
||||
|
||||
->save();
|
||||
|
||||
$event->setAttribute($attribute);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a product attribute entry
|
||||
*
|
||||
* @param AttributeDeleteEvent $event
|
||||
* @param $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function delete(AttributeDeleteEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
if (null !== ($attribute = AttributeQuery::create()->findPk($event->getAttributeId()))) {
|
||||
$attribute
|
||||
->setDispatcher($dispatcher)
|
||||
->delete()
|
||||
;
|
||||
|
||||
$event->setAttribute($attribute);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes position, selecting absolute ou relative change.
|
||||
*
|
||||
* @param UpdatePositionEvent $event
|
||||
* @param $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function updatePosition(UpdatePositionEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
$this->genericUpdatePosition(AttributeQuery::create(), $event, $dispatcher);
|
||||
}
|
||||
|
||||
protected function doAddToAllTemplates(AttributeModel $attribute)
|
||||
{
|
||||
$templates = TemplateQuery::create()->find();
|
||||
|
||||
foreach ($templates as $template) {
|
||||
$attribute_template = new AttributeTemplate();
|
||||
|
||||
if (null === AttributeTemplateQuery::create()->filterByAttribute($attribute)->filterByTemplate($template)->findOne()) {
|
||||
$attribute_template
|
||||
->setAttribute($attribute)
|
||||
->setTemplate($template)
|
||||
->save()
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function addToAllTemplates(AttributeEvent $event)
|
||||
{
|
||||
$this->doAddToAllTemplates($event->getAttribute());
|
||||
}
|
||||
|
||||
public function removeFromAllTemplates(AttributeEvent $event)
|
||||
{
|
||||
// Delete this attribute from all product templates
|
||||
AttributeTemplateQuery::create()->filterByAttribute($event->getAttribute())->delete();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
TheliaEvents::ATTRIBUTE_CREATE => array("create", 128),
|
||||
TheliaEvents::ATTRIBUTE_UPDATE => array("update", 128),
|
||||
TheliaEvents::ATTRIBUTE_DELETE => array("delete", 128),
|
||||
TheliaEvents::ATTRIBUTE_UPDATE_POSITION => array("updatePosition", 128),
|
||||
|
||||
TheliaEvents::ATTRIBUTE_REMOVE_FROM_ALL_TEMPLATES => array("removeFromAllTemplates", 128),
|
||||
TheliaEvents::ATTRIBUTE_ADD_TO_ALL_TEMPLATES => array("addToAllTemplates", 128),
|
||||
|
||||
);
|
||||
}
|
||||
}
|
||||
119
core/lib/Thelia/Action/AttributeAv.php
Normal file
119
core/lib/Thelia/Action/AttributeAv.php
Normal file
@@ -0,0 +1,119 @@
|
||||
<?php
|
||||
/*************************************************************************************/
|
||||
/* This file is part of the Thelia package. */
|
||||
/* */
|
||||
/* Copyright (c) OpenStudio */
|
||||
/* email : dev@thelia.net */
|
||||
/* web : http://www.thelia.net */
|
||||
/* */
|
||||
/* For the full copyright and license information, please view the LICENSE.txt */
|
||||
/* file that was distributed with this source code. */
|
||||
/*************************************************************************************/
|
||||
|
||||
namespace Thelia\Action;
|
||||
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Thelia\Model\AttributeAvQuery;
|
||||
use Thelia\Model\AttributeAv as AttributeAvModel;
|
||||
use Thelia\Core\Event\TheliaEvents;
|
||||
use Thelia\Core\Event\Attribute\AttributeAvUpdateEvent;
|
||||
use Thelia\Core\Event\Attribute\AttributeAvCreateEvent;
|
||||
use Thelia\Core\Event\Attribute\AttributeAvDeleteEvent;
|
||||
use Thelia\Core\Event\UpdatePositionEvent;
|
||||
|
||||
class AttributeAv extends BaseAction implements EventSubscriberInterface
|
||||
{
|
||||
/**
|
||||
* Create a new attribute entry
|
||||
*
|
||||
* @param AttributeAvCreateEvent $event
|
||||
* @param $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function create(AttributeAvCreateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
$attribute = new AttributeAvModel();
|
||||
|
||||
$attribute
|
||||
->setDispatcher($dispatcher)
|
||||
|
||||
->setAttributeId($event->getAttributeId())
|
||||
->setLocale($event->getLocale())
|
||||
->setTitle($event->getTitle())
|
||||
|
||||
->save()
|
||||
;
|
||||
|
||||
$event->setAttributeAv($attribute);
|
||||
}
|
||||
|
||||
/**
|
||||
* Change a product attribute
|
||||
*
|
||||
* @param AttributeAvUpdateEvent $event
|
||||
* @param $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function update(AttributeAvUpdateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
if (null !== $attribute = AttributeAvQuery::create()->findPk($event->getAttributeAvId())) {
|
||||
$attribute
|
||||
->setDispatcher($dispatcher)
|
||||
|
||||
->setLocale($event->getLocale())
|
||||
->setTitle($event->getTitle())
|
||||
->setDescription($event->getDescription())
|
||||
->setChapo($event->getChapo())
|
||||
->setPostscriptum($event->getPostscriptum())
|
||||
|
||||
->save();
|
||||
|
||||
$event->setAttributeAv($attribute);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a product attribute entry
|
||||
*
|
||||
* @param AttributeAvDeleteEvent $event
|
||||
* @param $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function delete(AttributeAvDeleteEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
if (null !== ($attribute = AttributeAvQuery::create()->findPk($event->getAttributeAvId()))) {
|
||||
$attribute
|
||||
->setDispatcher($dispatcher)
|
||||
->delete()
|
||||
;
|
||||
|
||||
$event->setAttributeAv($attribute);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes position, selecting absolute ou relative change.
|
||||
*
|
||||
* @param UpdatePositionEvent $event
|
||||
* @param $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function updatePosition(UpdatePositionEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
$this->genericUpdatePosition(AttributeAvQuery::create(), $event);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
TheliaEvents::ATTRIBUTE_AV_CREATE => array("create", 128),
|
||||
TheliaEvents::ATTRIBUTE_AV_UPDATE => array("update", 128),
|
||||
TheliaEvents::ATTRIBUTE_AV_DELETE => array("delete", 128),
|
||||
TheliaEvents::ATTRIBUTE_AV_UPDATE_POSITION => array("updatePosition", 128),
|
||||
);
|
||||
}
|
||||
}
|
||||
146
core/lib/Thelia/Action/BaseAction.php
Normal file
146
core/lib/Thelia/Action/BaseAction.php
Normal file
@@ -0,0 +1,146 @@
|
||||
<?php
|
||||
/*************************************************************************************/
|
||||
/* This file is part of the Thelia package. */
|
||||
/* */
|
||||
/* Copyright (c) OpenStudio */
|
||||
/* email : dev@thelia.net */
|
||||
/* web : http://www.thelia.net */
|
||||
/* */
|
||||
/* For the full copyright and license information, please view the LICENSE.txt */
|
||||
/* file that was distributed with this source code. */
|
||||
/*************************************************************************************/
|
||||
namespace Thelia\Action;
|
||||
|
||||
use Propel\Runtime\ActiveQuery\ModelCriteria;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Thelia\Core\Event\ToggleVisibilityEvent;
|
||||
use Thelia\Core\Event\UpdatePositionEvent;
|
||||
use Thelia\Core\Event\UpdateSeoEvent;
|
||||
use Thelia\Exception\UrlRewritingException;
|
||||
use Thelia\Form\Exception\FormValidationException;
|
||||
use Thelia\Model\ProductCategory;
|
||||
|
||||
class BaseAction
|
||||
{
|
||||
/**
|
||||
* Changes object position, selecting absolute ou relative change.
|
||||
*
|
||||
* @param ModelCriteria $query
|
||||
* @param UpdatePositionEvent $event
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
protected function genericUpdatePosition(ModelCriteria $query, UpdatePositionEvent $event, EventDispatcherInterface $dispatcher = null)
|
||||
{
|
||||
if (null !== $object = $query->findPk($event->getObjectId())) {
|
||||
if (!isset(class_uses($object)['Thelia\Model\Tools\PositionManagementTrait'])) {
|
||||
throw new \InvalidArgumentException("Your model does not implement the PositionManagementTrait trait");
|
||||
}
|
||||
|
||||
$object->setDispatcher($dispatcher !== null ? $dispatcher : $event->getDispatcher());
|
||||
|
||||
$mode = $event->getMode();
|
||||
|
||||
if ($mode == UpdatePositionEvent::POSITION_ABSOLUTE) {
|
||||
$object->changeAbsolutePosition($event->getPosition());
|
||||
} elseif ($mode == UpdatePositionEvent::POSITION_UP) {
|
||||
$object->movePositionUp();
|
||||
} elseif ($mode == UpdatePositionEvent::POSITION_DOWN) {
|
||||
$object->movePositionDown();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ModelCriteria $query
|
||||
* @param UpdatePositionEvent $event
|
||||
* @param EventDispatcherInterface|null $dispatcher
|
||||
*
|
||||
* @since 2.3
|
||||
*/
|
||||
protected function genericUpdateDelegatePosition(ModelCriteria $query, UpdatePositionEvent $event, EventDispatcherInterface $dispatcher = null)
|
||||
{
|
||||
if (null !== $object = $query->findOne()) {
|
||||
if (!isset(class_uses($object)['Thelia\Model\Tools\PositionManagementTrait'])) {
|
||||
throw new \InvalidArgumentException("Your model does not implement the PositionManagementTrait trait");
|
||||
}
|
||||
|
||||
//$object->setDispatcher($dispatcher !== null ? $dispatcher : $event->getDispatcher());
|
||||
|
||||
$mode = $event->getMode();
|
||||
|
||||
if ($mode == UpdatePositionEvent::POSITION_ABSOLUTE) {
|
||||
$object->changeAbsolutePosition($event->getPosition());
|
||||
} elseif ($mode == UpdatePositionEvent::POSITION_UP) {
|
||||
$object->movePositionUp();
|
||||
} elseif ($mode == UpdatePositionEvent::POSITION_DOWN) {
|
||||
$object->movePositionDown();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes SEO Fields for an object.
|
||||
*
|
||||
* @param ModelCriteria $query
|
||||
* @param UpdateSeoEvent $event
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
*
|
||||
* @return mixed an SEOxxx object
|
||||
* @throws FormValidationException if a rewritten URL cannot be created
|
||||
*/
|
||||
protected function genericUpdateSeo(ModelCriteria $query, UpdateSeoEvent $event, EventDispatcherInterface $dispatcher = null)
|
||||
{
|
||||
if (null !== $object = $query->findPk($event->getObjectId())) {
|
||||
$object
|
||||
//for backward compatibility
|
||||
->setDispatcher($dispatcher !== null ? $dispatcher : $event->getDispatcher())
|
||||
|
||||
->setLocale($event->getLocale())
|
||||
->setMetaTitle($event->getMetaTitle())
|
||||
->setMetaDescription($event->getMetaDescription())
|
||||
->setMetaKeywords($event->getMetaKeywords())
|
||||
|
||||
->save()
|
||||
;
|
||||
|
||||
// Update the rewritten URL, if required
|
||||
try {
|
||||
$object->setRewrittenUrl($event->getLocale(), $event->getUrl());
|
||||
} catch (UrlRewritingException $e) {
|
||||
throw new FormValidationException($e->getMessage(), $e->getCode());
|
||||
}
|
||||
|
||||
$event->setObject($object);
|
||||
}
|
||||
|
||||
return $object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle visibility for an object
|
||||
*
|
||||
* @param ModelCriteria $query
|
||||
* @param ToggleVisibilityEvent $event
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function genericToggleVisibility(ModelCriteria $query, ToggleVisibilityEvent $event, EventDispatcherInterface $dispatcher = null)
|
||||
{
|
||||
if (null !== $object = $query->findPk($event->getObjectId())) {
|
||||
$newVisibility = !$object->getVisible();
|
||||
$object
|
||||
//for backward compatibility
|
||||
->setDispatcher($dispatcher !== null ? $dispatcher : $event->getDispatcher())
|
||||
->setVisible($newVisibility)
|
||||
->save()
|
||||
;
|
||||
|
||||
$event->setObject($object);
|
||||
}
|
||||
|
||||
return $object;
|
||||
}
|
||||
}
|
||||
290
core/lib/Thelia/Action/BaseCachedFile.php
Normal file
290
core/lib/Thelia/Action/BaseCachedFile.php
Normal file
@@ -0,0 +1,290 @@
|
||||
<?php
|
||||
/*************************************************************************************/
|
||||
/* This file is part of the Thelia package. */
|
||||
/* */
|
||||
/* Copyright (c) OpenStudio */
|
||||
/* email : dev@thelia.net */
|
||||
/* web : http://www.thelia.net */
|
||||
/* */
|
||||
/* For the full copyright and license information, please view the LICENSE.txt */
|
||||
/* file that was distributed with this source code. */
|
||||
/*************************************************************************************/
|
||||
namespace Thelia\Action;
|
||||
|
||||
use Propel\Runtime\Propel;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Thelia\Core\Event\CachedFileEvent;
|
||||
use Thelia\Core\Event\File\FileCreateOrUpdateEvent;
|
||||
use Thelia\Core\Event\File\FileDeleteEvent;
|
||||
use Thelia\Core\Event\File\FileToggleVisibilityEvent;
|
||||
use Thelia\Core\Event\UpdateFilePositionEvent;
|
||||
use Thelia\Exception\FileException;
|
||||
use Thelia\Files\FileManager;
|
||||
use Thelia\Model\ConfigQuery;
|
||||
use Thelia\Model\Map\ProductImageTableMap;
|
||||
use Thelia\Tools\URL;
|
||||
|
||||
/**
|
||||
*
|
||||
* Cached file management actions. This class handles file caching in the web space
|
||||
*
|
||||
* Basically, files are stored outside the web space (by default in local/media/<dirname>),
|
||||
* and cached in the web space (by default in web/local/<dirname>).
|
||||
*
|
||||
* In the file cache directory, a subdirectory for files categories (eg. product, category, folder, etc.) is
|
||||
* automatically created, and the cached file is created here. Plugin may use their own subdirectory as required.
|
||||
*
|
||||
* A copy (or symbolic link, by default) of the original file is created in the cache.
|
||||
*
|
||||
* @package Thelia\Action
|
||||
* @author Franck Allimant <franck@cqfdev.fr>
|
||||
*
|
||||
*/
|
||||
abstract class BaseCachedFile extends BaseAction
|
||||
{
|
||||
/**
|
||||
* @var FileManager
|
||||
*/
|
||||
protected $fileManager;
|
||||
|
||||
/** @var null|string */
|
||||
protected $cdnBaseUrl;
|
||||
|
||||
public function __construct(FileManager $fileManager)
|
||||
{
|
||||
$this->fileManager = $fileManager;
|
||||
|
||||
$this->cdnBaseUrl = ConfigQuery::read('cdn.documents-base-url', null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string root of the file cache directory in web space
|
||||
*/
|
||||
abstract protected function getCacheDirFromWebRoot();
|
||||
|
||||
/**
|
||||
* @param string $url the fully qualified CDN URL that will be used to create doucments URL.
|
||||
*/
|
||||
public function setCdnBaseUrl($url)
|
||||
{
|
||||
$this->cdnBaseUrl = $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the file cache. Is a subdirectory is specified, only this directory is cleared.
|
||||
* If no directory is specified, the whole cache is cleared.
|
||||
* Only files are deleted, directories will remain.
|
||||
*
|
||||
* @param CachedFileEvent $event
|
||||
*/
|
||||
public function clearCache(CachedFileEvent $event)
|
||||
{
|
||||
$path = $this->getCachePath($event->getCacheSubdirectory(), false);
|
||||
|
||||
$this->clearDirectory($path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively clears the specified directory.
|
||||
*
|
||||
* @param string $path the directory path
|
||||
*/
|
||||
protected function clearDirectory($path)
|
||||
{
|
||||
$iterator = new \DirectoryIterator($path);
|
||||
|
||||
/** @var \DirectoryIterator $fileinfo */
|
||||
foreach ($iterator as $fileinfo) {
|
||||
if ($fileinfo->isDot()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($fileinfo->isFile() || $fileinfo->isLink()) {
|
||||
@unlink($fileinfo->getPathname());
|
||||
} elseif ($fileinfo->isDir()) {
|
||||
$this->clearDirectory($fileinfo->getPathname());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the absolute URL to the cached file
|
||||
*
|
||||
* @param string $subdir the subdirectory related to cache base
|
||||
* @param string $safe_filename the safe filename, as returned by getCacheFilePath()
|
||||
* @return string the absolute URL to the cached file
|
||||
*/
|
||||
protected function getCacheFileURL($subdir, $safe_filename)
|
||||
{
|
||||
$path = $this->getCachePathFromWebRoot($subdir);
|
||||
|
||||
return URL::getInstance()->absoluteUrl(sprintf("%s/%s", $path, $safe_filename), null, URL::PATH_TO_FILE, $this->cdnBaseUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the full path of the cached file
|
||||
*
|
||||
* @param string $subdir the subdirectory related to cache base
|
||||
* @param string $filename the filename
|
||||
* @param boolean $forceOriginalFile if true, the original file path in the cache dir is returned.
|
||||
* @param string $hashed_options a hash of transformation options, or null if no transformations have been applied
|
||||
* @return string the cache directory path relative to Web Root
|
||||
*/
|
||||
protected function getCacheFilePath($subdir, $filename, $forceOriginalFile = false, $hashed_options = null)
|
||||
{
|
||||
$path = $this->getCachePath($subdir);
|
||||
|
||||
$safe_filename = preg_replace("[^:alnum:\-\._]", "-", strtolower(basename($filename)));
|
||||
|
||||
// Keep original safe name if no tranformations are applied
|
||||
if ($forceOriginalFile || $hashed_options == null) {
|
||||
return sprintf("%s/%s", $path, $safe_filename);
|
||||
} else {
|
||||
return sprintf("%s/%s-%s", $path, $hashed_options, $safe_filename);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the cache directory path relative to Web Root
|
||||
*
|
||||
* @param string $subdir the subdirectory related to cache base, or null to get the cache directory only.
|
||||
* @return string the cache directory path relative to Web Root
|
||||
*/
|
||||
protected function getCachePathFromWebRoot($subdir = null)
|
||||
{
|
||||
$cache_dir_from_web_root = $this->getCacheDirFromWebRoot();
|
||||
|
||||
if ($subdir != null) {
|
||||
$safe_subdir = basename($subdir);
|
||||
|
||||
$path = sprintf("%s/%s", $cache_dir_from_web_root, $safe_subdir);
|
||||
} else {
|
||||
$path = $cache_dir_from_web_root;
|
||||
}
|
||||
|
||||
// Check if path is valid, e.g. in the cache dir
|
||||
return $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the absolute cache directory path
|
||||
*
|
||||
* @param string $subdir the subdirectory related to cache base, or null to get the cache base directory.
|
||||
* @param bool $create_if_not_exists create the directory if it is not found
|
||||
*
|
||||
* @throws \RuntimeException if cache directory cannot be created
|
||||
* @throws \InvalidArgumentException ii path is invalid, e.g. not in the cache dir
|
||||
*
|
||||
* @return string the absolute cache directory path
|
||||
*/
|
||||
protected function getCachePath($subdir = null, $create_if_not_exists = true)
|
||||
{
|
||||
$cache_base = $this->getCachePathFromWebRoot($subdir);
|
||||
|
||||
$web_root = rtrim(THELIA_WEB_DIR, '/');
|
||||
|
||||
$path = sprintf("%s/%s", $web_root, $cache_base);
|
||||
|
||||
// Create directory (recursively) if it does not exists.
|
||||
if ($create_if_not_exists && !is_dir($path)) {
|
||||
if (!@mkdir($path, 0777, true)) {
|
||||
throw new \RuntimeException(sprintf("Failed to create %s file in cache directory", $path));
|
||||
}
|
||||
}
|
||||
|
||||
// Check if path is valid, e.g. in the cache dir
|
||||
$cache_base = realpath(sprintf("%s/%s", $web_root, $this->getCachePathFromWebRoot()));
|
||||
|
||||
if (strpos(realpath($path), $cache_base) !== 0) {
|
||||
throw new \InvalidArgumentException(sprintf("Invalid cache path %s, with subdirectory %s", $path, $subdir));
|
||||
}
|
||||
|
||||
return $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Take care of saving a file in the database and file storage
|
||||
*
|
||||
* @param FileCreateOrUpdateEvent $event Image event
|
||||
*
|
||||
* @throws \Thelia\Exception\FileException|\Exception
|
||||
*
|
||||
*/
|
||||
public function saveFile(FileCreateOrUpdateEvent $event)
|
||||
{
|
||||
$model = $event->getModel();
|
||||
$model->setFile(sprintf("tmp/%s", $event->getUploadedFile()->getFilename()));
|
||||
$con = Propel::getWriteConnection(ProductImageTableMap::DATABASE_NAME);
|
||||
$con->beginTransaction();
|
||||
|
||||
try {
|
||||
$nbModifiedLines = $model->save($con);
|
||||
$event->setModel($model);
|
||||
|
||||
if (!$nbModifiedLines) {
|
||||
throw new FileException(
|
||||
sprintf(
|
||||
'File "%s" (type %s) with parent id %s failed to be saved',
|
||||
$event->getParentName(),
|
||||
\get_class($model),
|
||||
$event->getParentId()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$newUploadedFile = $this->fileManager->copyUploadedFile($event->getModel(), $event->getUploadedFile());
|
||||
|
||||
$event->setUploadedFile($newUploadedFile);
|
||||
$con->commit();
|
||||
} catch (\Exception $e) {
|
||||
$con->rollBack();
|
||||
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Take care of updating file in the database and file storage
|
||||
*
|
||||
* @param FileCreateOrUpdateEvent $event Image event
|
||||
*
|
||||
* @throws \Thelia\Exception\FileException
|
||||
*/
|
||||
public function updateFile(FileCreateOrUpdateEvent $event)
|
||||
{
|
||||
// Copy and save file
|
||||
if ($event->getUploadedFile()) {
|
||||
// Remove old picture file from file storage
|
||||
$url = $event->getModel()->getUploadDir() . '/' . $event->getOldModel()->getFile();
|
||||
unlink(str_replace('..', '', $url));
|
||||
|
||||
$newUploadedFile = $this->fileManager->copyUploadedFile($event->getModel(), $event->getUploadedFile());
|
||||
$event->setUploadedFile($newUploadedFile);
|
||||
}
|
||||
|
||||
// Update image modifications
|
||||
$event->getModel()->save();
|
||||
|
||||
$event->setModel($event->getModel());
|
||||
}
|
||||
|
||||
/**
|
||||
* Deleting file in the database and in storage
|
||||
*
|
||||
* @param FileDeleteEvent $event Image event
|
||||
*/
|
||||
public function deleteFile(FileDeleteEvent $event)
|
||||
{
|
||||
$this->fileManager->deleteFile($event->getFileToDelete());
|
||||
}
|
||||
|
||||
public function updatePosition(UpdateFilePositionEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
$this->genericUpdatePosition($event->getQuery(), $event, $dispatcher);
|
||||
}
|
||||
|
||||
public function toggleVisibility(FileToggleVisibilityEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
$this->genericToggleVisibility($event->getQuery(), $event, $dispatcher);
|
||||
}
|
||||
}
|
||||
175
core/lib/Thelia/Action/Brand.php
Normal file
175
core/lib/Thelia/Action/Brand.php
Normal file
@@ -0,0 +1,175 @@
|
||||
<?php
|
||||
/*************************************************************************************/
|
||||
/* This file is part of the Thelia package. */
|
||||
/* */
|
||||
/* Copyright (c) OpenStudio */
|
||||
/* email : dev@thelia.net */
|
||||
/* web : http://www.thelia.net */
|
||||
/* */
|
||||
/* For the full copyright and license information, please view the LICENSE.txt */
|
||||
/* file that was distributed with this source code. */
|
||||
/*************************************************************************************/
|
||||
|
||||
namespace Thelia\Action;
|
||||
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
use Thelia\Core\Event\Brand\BrandCreateEvent;
|
||||
use Thelia\Core\Event\Brand\BrandDeleteEvent;
|
||||
use Thelia\Core\Event\Brand\BrandToggleVisibilityEvent;
|
||||
use Thelia\Core\Event\Brand\BrandUpdateEvent;
|
||||
use Thelia\Core\Event\TheliaEvents;
|
||||
use Thelia\Core\Event\UpdatePositionEvent;
|
||||
use Thelia\Core\Event\UpdateSeoEvent;
|
||||
use Thelia\Core\Event\ViewCheckEvent;
|
||||
use Thelia\Model\Brand as BrandModel;
|
||||
use Thelia\Model\BrandQuery;
|
||||
|
||||
/**
|
||||
* Class Brand
|
||||
*
|
||||
* @package Thelia\Action
|
||||
* @author Franck Allimant <franck@cqfdev.fr>
|
||||
*/
|
||||
class Brand extends BaseAction implements EventSubscriberInterface
|
||||
{
|
||||
public function create(BrandCreateEvent $event)
|
||||
{
|
||||
$brand = new BrandModel();
|
||||
|
||||
$brand
|
||||
->setVisible($event->getVisible())
|
||||
->setLocale($event->getLocale())
|
||||
->setTitle($event->getTitle())
|
||||
->save()
|
||||
;
|
||||
|
||||
$event->setBrand($brand);
|
||||
}
|
||||
|
||||
/**
|
||||
* process update brand
|
||||
*
|
||||
* @param BrandUpdateEvent $event
|
||||
* @param $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function update(BrandUpdateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
if (null !== $brand = BrandQuery::create()->findPk($event->getBrandId())) {
|
||||
$brand->setDispatcher($dispatcher);
|
||||
|
||||
$brand
|
||||
->setVisible($event->getVisible())
|
||||
->setLogoImageId(\intval($event->getLogoImageId()) == 0 ? null : $event->getLogoImageId())
|
||||
->setLocale($event->getLocale())
|
||||
->setTitle($event->getTitle())
|
||||
->setDescription($event->getDescription())
|
||||
->setChapo($event->getChapo())
|
||||
->setPostscriptum($event->getPostscriptum())
|
||||
->save()
|
||||
;
|
||||
|
||||
$event->setBrand($brand);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle Brand visibility
|
||||
*
|
||||
* @param BrandToggleVisibilityEvent $event
|
||||
* @param string $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
* @throws \Exception
|
||||
* @throws \Propel\Runtime\Exception\PropelException
|
||||
*/
|
||||
public function toggleVisibility(BrandToggleVisibilityEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
$brand = $event->getBrand();
|
||||
|
||||
$brand
|
||||
->setDispatcher($dispatcher)
|
||||
->setVisible(!$brand->getVisible())
|
||||
->save();
|
||||
|
||||
$event->setBrand($brand);
|
||||
}
|
||||
|
||||
/**
|
||||
* Change Brand SEO
|
||||
*
|
||||
* @param UpdateSeoEvent $event
|
||||
* @param $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
* @return Object
|
||||
*/
|
||||
public function updateSeo(UpdateSeoEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
return $this->genericUpdateSeo(BrandQuery::create(), $event, $dispatcher);
|
||||
}
|
||||
|
||||
public function delete(BrandDeleteEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
if (null !== $brand = BrandQuery::create()->findPk($event->getBrandId())) {
|
||||
$brand->setDispatcher($dispatcher)->delete();
|
||||
|
||||
$event->setBrand($brand);
|
||||
}
|
||||
}
|
||||
|
||||
public function updatePosition(UpdatePositionEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
$this->genericUpdatePosition(BrandQuery::create(), $event, $dispatcher);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if is a brand view and if brand_id is visible
|
||||
*
|
||||
* @param ViewCheckEvent $event
|
||||
* @param string $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function viewCheck(ViewCheckEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
if ($event->getView() == 'brand') {
|
||||
$brand = BrandQuery::create()
|
||||
->filterById($event->getViewId())
|
||||
->filterByVisible(1)
|
||||
->count();
|
||||
|
||||
if ($brand == 0) {
|
||||
$dispatcher->dispatch(TheliaEvents::VIEW_BRAND_ID_NOT_VISIBLE, $event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ViewCheckEvent $event
|
||||
* @throws NotFoundHttpException
|
||||
*/
|
||||
public function viewBrandIdNotVisible(ViewCheckEvent $event)
|
||||
{
|
||||
throw new NotFoundHttpException();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
TheliaEvents::BRAND_CREATE => array('create', 128),
|
||||
TheliaEvents::BRAND_UPDATE => array('update', 128),
|
||||
TheliaEvents::BRAND_DELETE => array('delete', 128),
|
||||
|
||||
TheliaEvents::BRAND_UPDATE_SEO => array('updateSeo', 128),
|
||||
|
||||
TheliaEvents::BRAND_UPDATE_POSITION => array('updatePosition', 128),
|
||||
TheliaEvents::BRAND_TOGGLE_VISIBILITY => array('toggleVisibility', 128),
|
||||
|
||||
TheliaEvents::VIEW_CHECK => array('viewCheck', 128),
|
||||
TheliaEvents::VIEW_BRAND_ID_NOT_VISIBLE => array('viewBrandIdNotVisible', 128),
|
||||
);
|
||||
}
|
||||
}
|
||||
96
core/lib/Thelia/Action/Cache.php
Normal file
96
core/lib/Thelia/Action/Cache.php
Normal file
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
/*************************************************************************************/
|
||||
/* This file is part of the Thelia package. */
|
||||
/* */
|
||||
/* Copyright (c) OpenStudio */
|
||||
/* email : dev@thelia.net */
|
||||
/* web : http://www.thelia.net */
|
||||
/* */
|
||||
/* For the full copyright and license information, please view the LICENSE.txt */
|
||||
/* file that was distributed with this source code. */
|
||||
/*************************************************************************************/
|
||||
|
||||
namespace Thelia\Action;
|
||||
|
||||
use Symfony\Component\Cache\Adapter\AdapterInterface;
|
||||
use Symfony\Component\Console\ConsoleEvents;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\Filesystem\Filesystem;
|
||||
use Symfony\Component\HttpKernel\KernelEvents;
|
||||
use Thelia\Core\Event\Cache\CacheEvent;
|
||||
use Thelia\Core\Event\TheliaEvents;
|
||||
|
||||
/**
|
||||
* Class Cache
|
||||
* @package Thelia\Action
|
||||
* @author Manuel Raynaud <manu@raynaud.io>
|
||||
* @author Gilles Bourgeat <gilles.bourgeat@gmail.com>
|
||||
*/
|
||||
class Cache extends BaseAction implements EventSubscriberInterface
|
||||
{
|
||||
/** @var AdapterInterface */
|
||||
protected $adapter;
|
||||
|
||||
/**
|
||||
* @var CacheEvent[]
|
||||
*/
|
||||
protected $onTerminateCacheClearEvents = [];
|
||||
|
||||
/**
|
||||
* CacheListener constructor.
|
||||
* @param AdapterInterface $adapter
|
||||
*/
|
||||
public function __construct(AdapterInterface $adapter)
|
||||
{
|
||||
$this->adapter = $adapter;
|
||||
}
|
||||
|
||||
public function cacheClear(CacheEvent $event)
|
||||
{
|
||||
if (!$event->isOnKernelTerminate()) {
|
||||
$this->execCacheClear($event);
|
||||
return;
|
||||
}
|
||||
|
||||
$findDir = false;
|
||||
foreach ($this->onTerminateCacheClearEvents as $cacheEvent) {
|
||||
if ($cacheEvent->getDir() === $event->getDir()) {
|
||||
$findDir = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$findDir) {
|
||||
$this->onTerminateCacheClearEvents[] = $event;
|
||||
}
|
||||
}
|
||||
|
||||
public function onTerminate()
|
||||
{
|
||||
foreach ($this->onTerminateCacheClearEvents as $cacheEvent) {
|
||||
$this->execCacheClear($cacheEvent);
|
||||
}
|
||||
}
|
||||
|
||||
protected function execCacheClear(CacheEvent $event)
|
||||
{
|
||||
$this->adapter->clear();
|
||||
|
||||
$dir = $event->getDir();
|
||||
|
||||
$fs = new Filesystem();
|
||||
$fs->remove($dir);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
TheliaEvents::CACHE_CLEAR => ['cacheClear', 128],
|
||||
KernelEvents::TERMINATE => ['onTerminate', 128],
|
||||
ConsoleEvents::TERMINATE => ['onTerminate', 128]
|
||||
);
|
||||
}
|
||||
}
|
||||
546
core/lib/Thelia/Action/Cart.php
Normal file
546
core/lib/Thelia/Action/Cart.php
Normal file
@@ -0,0 +1,546 @@
|
||||
<?php
|
||||
/*************************************************************************************/
|
||||
/* This file is part of the Thelia package. */
|
||||
/* */
|
||||
/* Copyright (c) OpenStudio */
|
||||
/* email : dev@thelia.net */
|
||||
/* web : http://www.thelia.net */
|
||||
/* */
|
||||
/* For the full copyright and license information, please view the LICENSE.txt */
|
||||
/* file that was distributed with this source code. */
|
||||
/*************************************************************************************/
|
||||
|
||||
namespace Thelia\Action;
|
||||
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
use Thelia\Core\Event\Cart\CartCreateEvent;
|
||||
use Thelia\Core\Event\Cart\CartDuplicationEvent;
|
||||
use Thelia\Core\Event\Cart\CartPersistEvent;
|
||||
use Thelia\Core\Event\Cart\CartRestoreEvent;
|
||||
use Thelia\Core\Event\Cart\CartEvent;
|
||||
use Thelia\Core\Event\Currency\CurrencyChangeEvent;
|
||||
use Thelia\Core\Event\TheliaEvents;
|
||||
use Thelia\Model\Base\CustomerQuery;
|
||||
use Thelia\Model\Base\ProductSaleElementsQuery;
|
||||
use Thelia\Model\Currency as CurrencyModel;
|
||||
use Thelia\Model\CartItem;
|
||||
use Thelia\Model\Cart as CartModel;
|
||||
use Thelia\Model\CartItemQuery;
|
||||
use Thelia\Model\CartQuery;
|
||||
use Thelia\Model\ConfigQuery;
|
||||
use Thelia\Model\Customer as CustomerModel;
|
||||
use Thelia\Model\ProductSaleElements;
|
||||
use Thelia\Model\Tools\ProductPriceTools;
|
||||
use Thelia\Tools\TokenProvider;
|
||||
|
||||
/**
|
||||
*
|
||||
* Class Cart where all actions are manage like adding, modifying or delete items.
|
||||
*
|
||||
* Class Cart
|
||||
* @package Thelia\Action
|
||||
* @author Manuel Raynaud <manu@raynaud.io>
|
||||
*/
|
||||
class Cart extends BaseAction implements EventSubscriberInterface
|
||||
{
|
||||
/** @var RequestStack */
|
||||
protected $requestStack;
|
||||
|
||||
/** @var TokenProvider */
|
||||
protected $tokenProvider;
|
||||
|
||||
public function __construct(RequestStack $requestStack, TokenProvider $tokenProvider)
|
||||
{
|
||||
$this->requestStack = $requestStack;
|
||||
|
||||
$this->tokenProvider = $tokenProvider;
|
||||
}
|
||||
|
||||
public function persistCart(CartPersistEvent $event)
|
||||
{
|
||||
$cart = $event->getCart();
|
||||
|
||||
if ($cart->isNew()) {
|
||||
$cart
|
||||
->setToken($this->generateCartCookieIdentifier())
|
||||
->save();
|
||||
$this->getSession()->setSessionCart($cart);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* add an article in the current cart
|
||||
*
|
||||
* @param \Thelia\Core\Event\Cart\CartEvent $event
|
||||
* @param $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function addItem(CartEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
$cart = $event->getCart();
|
||||
$newness = $event->getNewness();
|
||||
$append = $event->getAppend();
|
||||
$quantity = $event->getQuantity();
|
||||
$currency = $cart->getCurrency();
|
||||
$customer = $cart->getCustomer();
|
||||
$discount = 0;
|
||||
|
||||
if ($cart->isNew()) {
|
||||
$persistEvent = new CartPersistEvent($cart);
|
||||
$dispatcher->dispatch(TheliaEvents::CART_PERSIST, $persistEvent);
|
||||
}
|
||||
|
||||
if (null !== $customer && $customer->getDiscount() > 0) {
|
||||
$discount = $customer->getDiscount();
|
||||
}
|
||||
|
||||
$productSaleElementsId = $event->getProductSaleElementsId();
|
||||
$productId = $event->getProduct();
|
||||
|
||||
// Search for an identical item in the cart
|
||||
$findItemEvent = clone $event;
|
||||
|
||||
$dispatcher->dispatch(TheliaEvents::CART_FINDITEM, $findItemEvent);
|
||||
|
||||
$cartItem = $findItemEvent->getCartItem();
|
||||
|
||||
if ($cartItem === null || $newness) {
|
||||
$productSaleElements = ProductSaleElementsQuery::create()->findPk($productSaleElementsId);
|
||||
|
||||
if (null !== $productSaleElements) {
|
||||
$productPrices = $productSaleElements->getPricesByCurrency($currency, $discount);
|
||||
|
||||
$cartItem = $this->doAddItem($dispatcher, $cart, $productId, $productSaleElements, $quantity, $productPrices);
|
||||
}
|
||||
} elseif ($append && $cartItem !== null) {
|
||||
$cartItem->addQuantity($quantity)->save();
|
||||
}
|
||||
|
||||
$event->setCartItem($cartItem);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Delete specify article present into cart
|
||||
*
|
||||
* @param \Thelia\Core\Event\Cart\CartEvent $event
|
||||
*/
|
||||
public function deleteItem(CartEvent $event)
|
||||
{
|
||||
if (null !== $cartItemId = $event->getCartItemId()) {
|
||||
$cart = $event->getCart();
|
||||
CartItemQuery::create()
|
||||
->filterByCartId($cart->getId())
|
||||
->filterById($cartItemId)
|
||||
->delete();
|
||||
|
||||
// Force an update of the Cart object to provide
|
||||
// to other listeners an updated CartItem collection.
|
||||
$cart->clearCartItems();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the cart
|
||||
* @param CartEvent $event
|
||||
*/
|
||||
public function clear(CartEvent $event)
|
||||
{
|
||||
if (null !== $cart = $event->getCart()) {
|
||||
$cart->delete();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Modify article's quantity
|
||||
*
|
||||
* don't use Form here just test the Request.
|
||||
*
|
||||
* @param \Thelia\Core\Event\Cart\CartEvent $event
|
||||
* @param $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function changeItem(CartEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
if ((null !== $cartItemId = $event->getCartItemId()) && (null !== $quantity = $event->getQuantity())) {
|
||||
$cart = $event->getCart();
|
||||
|
||||
$cartItem = CartItemQuery::create()
|
||||
->filterByCartId($cart->getId())
|
||||
->filterById($cartItemId)
|
||||
->findOne();
|
||||
|
||||
if ($cartItem) {
|
||||
$event->setCartItem(
|
||||
$this->updateQuantity($dispatcher, $cartItem, $quantity)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function updateCart(CurrencyChangeEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
$cart = $event->getRequest()->getSession()->getSessionCart($dispatcher);
|
||||
|
||||
if (null !== $cart) {
|
||||
$this->updateCartPrices($cart, $event->getCurrency());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Refresh article's price
|
||||
*
|
||||
* @param \Thelia\Model\Cart $cart
|
||||
* @param \Thelia\Model\Currency $currency
|
||||
*/
|
||||
public function updateCartPrices(CartModel $cart, CurrencyModel $currency)
|
||||
{
|
||||
$customer = $cart->getCustomer();
|
||||
$discount = 0;
|
||||
|
||||
if (null !== $customer && $customer->getDiscount() > 0) {
|
||||
$discount = $customer->getDiscount();
|
||||
}
|
||||
|
||||
// cart item
|
||||
foreach ($cart->getCartItems() as $cartItem) {
|
||||
$productSaleElements = $cartItem->getProductSaleElements();
|
||||
|
||||
$productPrice = $productSaleElements->getPricesByCurrency($currency, $discount);
|
||||
|
||||
$cartItem
|
||||
->setPrice($productPrice->getPrice())
|
||||
->setPromoPrice($productPrice->getPromoPrice());
|
||||
|
||||
$cartItem->save();
|
||||
}
|
||||
|
||||
// update the currency cart
|
||||
$cart->setCurrencyId($currency->getId());
|
||||
$cart->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* increase the quantity for an existing cartItem
|
||||
*
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
* @param CartItem $cartItem
|
||||
* @param float $quantity
|
||||
*
|
||||
* @throws \Exception
|
||||
* @throws \Propel\Runtime\Exception\PropelException
|
||||
* @return CartItem
|
||||
*/
|
||||
protected function updateQuantity(EventDispatcherInterface $dispatcher, CartItem $cartItem, $quantity)
|
||||
{
|
||||
$cartItem->setDisptacher($dispatcher);
|
||||
$cartItem->updateQuantity($quantity)
|
||||
->save();
|
||||
|
||||
return $cartItem;
|
||||
}
|
||||
|
||||
/**
|
||||
* try to attach a new item to an existing cart
|
||||
*
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
* @param \Thelia\Model\Cart $cart
|
||||
* @param int $productId
|
||||
* @param ProductSaleElements $productSaleElements
|
||||
* @param float $quantity
|
||||
* @param ProductPriceTools $productPrices
|
||||
*
|
||||
* @return CartItem
|
||||
*/
|
||||
protected function doAddItem(
|
||||
EventDispatcherInterface $dispatcher,
|
||||
CartModel $cart,
|
||||
$productId,
|
||||
ProductSaleElements $productSaleElements,
|
||||
$quantity,
|
||||
ProductPriceTools $productPrices
|
||||
) {
|
||||
$cartItem = new CartItem();
|
||||
$cartItem->setDisptacher($dispatcher);
|
||||
$cartItem
|
||||
->setCart($cart)
|
||||
->setProductId($productId)
|
||||
->setProductSaleElementsId($productSaleElements->getId())
|
||||
->setQuantity($quantity)
|
||||
->setPrice($productPrices->getPrice())
|
||||
->setPromoPrice($productPrices->getPromoPrice())
|
||||
->setPromo($productSaleElements->getPromo())
|
||||
->setPriceEndOfLife(time() + ConfigQuery::read("cart.priceEOF", 60*60*24*30))
|
||||
->save();
|
||||
|
||||
return $cartItem;
|
||||
}
|
||||
|
||||
/**
|
||||
* find a specific record in CartItem table using the Cart id, the product id
|
||||
* and the product_sale_elements id
|
||||
*
|
||||
* @param int $cartId
|
||||
* @param int $productId
|
||||
* @param int $productSaleElementsId
|
||||
* @return CartItem
|
||||
*
|
||||
* @deprecated this method is deprecated. Dispatch a TheliaEvents::CART_FINDITEM instead
|
||||
*/
|
||||
protected function findItem($cartId, $productId, $productSaleElementsId)
|
||||
{
|
||||
return CartItemQuery::create()
|
||||
->filterByCartId($cartId)
|
||||
->filterByProductId($productId)
|
||||
->filterByProductSaleElementsId($productSaleElementsId)
|
||||
->findOne();
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a specific record in CartItem table using the current CartEvent
|
||||
*
|
||||
* @param CartEvent $event the cart event
|
||||
*/
|
||||
public function findCartItem(CartEvent $event)
|
||||
{
|
||||
// Do not try to find a cartItem if one exists in the event, as previous event handlers
|
||||
// mays have put it in th event.
|
||||
if (null === $event->getCartItem() && null !== $foundItem = CartItemQuery::create()
|
||||
->filterByCartId($event->getCart()->getId())
|
||||
->filterByProductId($event->getProduct())
|
||||
->filterByProductSaleElementsId($event->getProductSaleElementsId())
|
||||
->findOne()) {
|
||||
$event->setCartItem($foundItem);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Search if cart already exists in session. If not try to restore it from the cart cookie,
|
||||
* or duplicate an old one.
|
||||
*
|
||||
* @param CartRestoreEvent $cartRestoreEvent
|
||||
* @param $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function restoreCurrentCart(CartRestoreEvent $cartRestoreEvent, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
$cookieName = ConfigQuery::read("cart.cookie_name", 'thelia_cart');
|
||||
$persistentCookie = ConfigQuery::read("cart.use_persistent_cookie", 1);
|
||||
|
||||
$cart = null;
|
||||
|
||||
if ($this->requestStack->getCurrentRequest()->cookies->has($cookieName) && $persistentCookie) {
|
||||
$cart = $this->managePersistentCart($cartRestoreEvent, $cookieName, $dispatcher);
|
||||
} elseif (!$persistentCookie) {
|
||||
$cart = $this->manageNonPersistentCookie($cartRestoreEvent, $dispatcher);
|
||||
}
|
||||
|
||||
// Still no cart ? Create a new one.
|
||||
if (null === $cart) {
|
||||
$cart = $this->dispatchNewCart($dispatcher);
|
||||
}
|
||||
|
||||
$cartRestoreEvent->setCart($cart);
|
||||
}
|
||||
|
||||
/**
|
||||
* The cart token is not saved in a cookie, if the cart is present in session, we just change the customer id
|
||||
* if needed or create duplicate the current cart if the customer is not the same as customer already present in
|
||||
* the cart.
|
||||
*
|
||||
* @param CartRestoreEvent $cartRestoreEvent
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
* @return CartModel
|
||||
* @throws \Exception
|
||||
* @throws \Propel\Runtime\Exception\PropelException
|
||||
*/
|
||||
protected function manageNonPersistentCookie(CartRestoreEvent $cartRestoreEvent, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
$cart = $cartRestoreEvent->getCart();
|
||||
|
||||
if (null === $cart) {
|
||||
$cart = $this->dispatchNewCart($dispatcher);
|
||||
} else {
|
||||
$cart = $this->manageCartDuplicationAtCustomerLogin($cart, $dispatcher);
|
||||
}
|
||||
|
||||
return $cart;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* The cart token is saved in a cookie so we try to retrieve it. Then the customer is checked.
|
||||
*
|
||||
* @param CartRestoreEvent $cartRestoreEvent
|
||||
* @param $cookieName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
* @return CartModel
|
||||
* @throws \Exception
|
||||
* @throws \Propel\Runtime\Exception\PropelException
|
||||
*/
|
||||
protected function managePersistentCart(CartRestoreEvent $cartRestoreEvent, $cookieName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
// The cart cookie exists -> get the cart token
|
||||
$token = $this->requestStack->getCurrentRequest()->cookies->get($cookieName);
|
||||
|
||||
// Check if a cart exists for this token
|
||||
if (null !== $cart = CartQuery::create()->findOneByToken($token)) {
|
||||
$cart = $this->manageCartDuplicationAtCustomerLogin($cart, $dispatcher);
|
||||
}
|
||||
|
||||
return $cart;
|
||||
}
|
||||
|
||||
protected function manageCartDuplicationAtCustomerLogin(CartModel $cart, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
/** @var CustomerModel $customer */
|
||||
if (null !== $customer = $this->getSession()->getCustomerUser()) {
|
||||
// Check if we have to duplicate the existing cart.
|
||||
|
||||
$duplicateCart = true;
|
||||
|
||||
// A customer is logged in.
|
||||
if (null === $cart->getCustomerId()) {
|
||||
// If the customer has a discount, whe have to duplicate the cart,
|
||||
// so that the discount will be applied to the products in cart.
|
||||
|
||||
if (0 === $customer->getDiscount() || 0 === $cart->countCartItems()) {
|
||||
// If no discount, or an empty cart, there's no need to duplicate.
|
||||
$duplicateCart = false;
|
||||
}
|
||||
}
|
||||
|
||||
if ($duplicateCart) {
|
||||
// Duplicate the cart
|
||||
$cart = $this->duplicateCart($dispatcher, $cart, $customer);
|
||||
} else {
|
||||
// No duplication required, just assign the cart to the customer
|
||||
$cart->setCustomerId($customer->getId())->save();
|
||||
}
|
||||
} elseif ($cart->getCustomerId() != null) {
|
||||
// The cart belongs to another user
|
||||
if (0 === $cart->countCartItems()) {
|
||||
// No items in cart, assign it to nobody.
|
||||
$cart->setCustomerId(null)->save();
|
||||
} else {
|
||||
// Some itemls in cart, duplicate it without assigning a customer ID.
|
||||
$cart = $this->duplicateCart($dispatcher, $cart);
|
||||
}
|
||||
}
|
||||
|
||||
return $cart;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
* @return CartModel
|
||||
*/
|
||||
protected function dispatchNewCart(EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
$cartCreateEvent = new CartCreateEvent();
|
||||
|
||||
$dispatcher->dispatch(TheliaEvents::CART_CREATE_NEW, $cartCreateEvent);
|
||||
|
||||
return $cartCreateEvent->getCart();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new, empty cart object, and assign it to the current customer, if any.
|
||||
*
|
||||
* @param CartCreateEvent $cartCreateEvent
|
||||
*/
|
||||
public function createEmptyCart(CartCreateEvent $cartCreateEvent)
|
||||
{
|
||||
$cart = new CartModel();
|
||||
|
||||
$cart->setCurrency($this->getSession()->getCurrency(true));
|
||||
|
||||
/** @var CustomerModel $customer */
|
||||
if (null !== $customer = $this->getSession()->getCustomerUser()) {
|
||||
$cart->setCustomer(CustomerQuery::create()->findPk($customer->getId()));
|
||||
}
|
||||
|
||||
$this->getSession()->setSessionCart($cart);
|
||||
|
||||
if (ConfigQuery::read("cart.use_persistent_cookie", 1) == 1) {
|
||||
// set cart_use_cookie to "" to remove the cart cookie
|
||||
// see Thelia\Core\EventListener\ResponseListener
|
||||
$this->getSession()->set("cart_use_cookie", "");
|
||||
}
|
||||
|
||||
$cartCreateEvent->setCart($cart);
|
||||
}
|
||||
|
||||
/**
|
||||
* Duplicate an existing Cart. If a customer ID is provided the created cart will be attached to this customer.
|
||||
*
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
* @param CartModel $cart
|
||||
* @param CustomerModel $customer
|
||||
* @return CartModel
|
||||
*/
|
||||
protected function duplicateCart(EventDispatcherInterface $dispatcher, CartModel $cart, CustomerModel $customer = null)
|
||||
{
|
||||
$newCart = $cart->duplicate(
|
||||
$this->generateCartCookieIdentifier(),
|
||||
$customer,
|
||||
$this->getSession()->getCurrency(),
|
||||
$dispatcher
|
||||
);
|
||||
|
||||
$cartEvent = new CartDuplicationEvent($newCart, $cart);
|
||||
$dispatcher->dispatch(TheliaEvents::CART_DUPLICATE, $cartEvent);
|
||||
|
||||
return $cartEvent->getDuplicatedCart();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the cart cookie identifier, or return null if the cart is only managed in the session object,
|
||||
* not in a client cookie.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function generateCartCookieIdentifier()
|
||||
{
|
||||
$id = null;
|
||||
|
||||
if (ConfigQuery::read("cart.use_persistent_cookie", 1) == 1) {
|
||||
$id = $this->tokenProvider->getToken();
|
||||
$this->getSession()->set('cart_use_cookie', $id);
|
||||
}
|
||||
|
||||
return $id;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
TheliaEvents::CART_PERSIST => array("persistCart", 128),
|
||||
TheliaEvents::CART_RESTORE_CURRENT => array("restoreCurrentCart", 128),
|
||||
TheliaEvents::CART_CREATE_NEW => array("createEmptyCart", 128),
|
||||
TheliaEvents::CART_ADDITEM => array("addItem", 128),
|
||||
TheliaEvents::CART_FINDITEM => array("findCartItem", 128),
|
||||
TheliaEvents::CART_DELETEITEM => array("deleteItem", 128),
|
||||
TheliaEvents::CART_UPDATEITEM => array("changeItem", 128),
|
||||
TheliaEvents::CART_CLEAR => array("clear", 128),
|
||||
TheliaEvents::CHANGE_DEFAULT_CURRENCY => array("updateCart", 128),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the session from the current request
|
||||
*
|
||||
* @return \Thelia\Core\HttpFoundation\Session\Session
|
||||
*/
|
||||
protected function getSession()
|
||||
{
|
||||
return $this->requestStack->getCurrentRequest()->getSession();
|
||||
}
|
||||
}
|
||||
268
core/lib/Thelia/Action/Category.php
Normal file
268
core/lib/Thelia/Action/Category.php
Normal file
@@ -0,0 +1,268 @@
|
||||
<?php
|
||||
/*************************************************************************************/
|
||||
/* This file is part of the Thelia package. */
|
||||
/* */
|
||||
/* Copyright (c) OpenStudio */
|
||||
/* email : dev@thelia.net */
|
||||
/* web : http://www.thelia.net */
|
||||
/* */
|
||||
/* For the full copyright and license information, please view the LICENSE.txt */
|
||||
/* file that was distributed with this source code. */
|
||||
/*************************************************************************************/
|
||||
|
||||
namespace Thelia\Action;
|
||||
|
||||
use Propel\Runtime\Propel;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
use Thelia\Core\Event\File\FileDeleteEvent;
|
||||
use Thelia\Core\Event\UpdateSeoEvent;
|
||||
use Thelia\Model\CategoryDocumentQuery;
|
||||
use Thelia\Model\CategoryImageQuery;
|
||||
use Thelia\Model\CategoryQuery;
|
||||
use Thelia\Model\Category as CategoryModel;
|
||||
use Thelia\Core\Event\TheliaEvents;
|
||||
use Thelia\Core\Event\Category\CategoryUpdateEvent;
|
||||
use Thelia\Core\Event\Category\CategoryCreateEvent;
|
||||
use Thelia\Core\Event\Category\CategoryDeleteEvent;
|
||||
use Thelia\Core\Event\UpdatePositionEvent;
|
||||
use Thelia\Core\Event\Category\CategoryToggleVisibilityEvent;
|
||||
use Thelia\Core\Event\Category\CategoryAddContentEvent;
|
||||
use Thelia\Core\Event\Category\CategoryDeleteContentEvent;
|
||||
use Thelia\Core\Event\ViewCheckEvent;
|
||||
use Thelia\Model\CategoryAssociatedContent;
|
||||
use Thelia\Model\CategoryAssociatedContentQuery;
|
||||
use Thelia\Model\Map\CategoryTableMap;
|
||||
|
||||
class Category extends BaseAction implements EventSubscriberInterface
|
||||
{
|
||||
/**
|
||||
* Create a new category entry
|
||||
*
|
||||
* @param \Thelia\Core\Event\Category\CategoryCreateEvent $event
|
||||
* @param $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function create(CategoryCreateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
$category = new CategoryModel();
|
||||
|
||||
$category
|
||||
->setDispatcher($dispatcher)
|
||||
|
||||
->setLocale($event->getLocale())
|
||||
->setParent($event->getParent())
|
||||
->setVisible($event->getVisible())
|
||||
->setTitle($event->getTitle())
|
||||
|
||||
->save()
|
||||
;
|
||||
|
||||
$event->setCategory($category);
|
||||
}
|
||||
|
||||
/**
|
||||
* Change a category
|
||||
*
|
||||
* @param \Thelia\Core\Event\Category\CategoryUpdateEvent $event
|
||||
* @param $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function update(CategoryUpdateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
if (null !== $category = CategoryQuery::create()->findPk($event->getCategoryId())) {
|
||||
$category
|
||||
->setDispatcher($dispatcher)
|
||||
->setDefaultTemplateId($event->getDefaultTemplateId() == 0 ? null : $event->getDefaultTemplateId())
|
||||
->setLocale($event->getLocale())
|
||||
->setTitle($event->getTitle())
|
||||
->setDescription($event->getDescription())
|
||||
->setChapo($event->getChapo())
|
||||
->setPostscriptum($event->getPostscriptum())
|
||||
|
||||
->setParent($event->getParent())
|
||||
->setVisible($event->getVisible())
|
||||
|
||||
->save();
|
||||
|
||||
$event->setCategory($category);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Change a Category SEO
|
||||
*
|
||||
* @param UpdateSeoEvent $event
|
||||
* @param $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
* @return Object
|
||||
*/
|
||||
public function updateSeo(UpdateSeoEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
return $this->genericUpdateSeo(CategoryQuery::create(), $event, $dispatcher);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a category entry
|
||||
*
|
||||
* @param \Thelia\Core\Event\Category\CategoryDeleteEvent $event
|
||||
* @param $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function delete(CategoryDeleteEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
if (null !== $category = CategoryQuery::create()->findPk($event->getCategoryId())) {
|
||||
$con = Propel::getWriteConnection(CategoryTableMap::DATABASE_NAME);
|
||||
$con->beginTransaction();
|
||||
|
||||
try {
|
||||
$fileList = ['images' => [], 'documentList' => []];
|
||||
|
||||
// Get category's files to delete after category deletion
|
||||
$fileList['images']['list'] = CategoryImageQuery::create()
|
||||
->findByCategoryId($event->getCategoryId());
|
||||
$fileList['images']['type'] = TheliaEvents::IMAGE_DELETE;
|
||||
|
||||
$fileList['documentList']['list'] = CategoryDocumentQuery::create()
|
||||
->findByCategoryId($event->getCategoryId());
|
||||
$fileList['documentList']['type'] = TheliaEvents::DOCUMENT_DELETE;
|
||||
|
||||
// Delete category
|
||||
$category
|
||||
->setDispatcher($dispatcher)
|
||||
->delete($con);
|
||||
|
||||
$event->setCategory($category);
|
||||
|
||||
// Dispatch delete category's files event
|
||||
foreach ($fileList as $fileTypeList) {
|
||||
foreach ($fileTypeList['list'] as $fileToDelete) {
|
||||
$fileDeleteEvent = new FileDeleteEvent($fileToDelete);
|
||||
$dispatcher->dispatch($fileTypeList['type'], $fileDeleteEvent);
|
||||
}
|
||||
}
|
||||
|
||||
$con->commit();
|
||||
} catch (\Exception $e) {
|
||||
$con->rollback();
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle category visibility. No form used here
|
||||
*
|
||||
* @param CategoryToggleVisibilityEvent $event
|
||||
* @param $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function toggleVisibility(CategoryToggleVisibilityEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
$category = $event->getCategory();
|
||||
|
||||
$category
|
||||
->setDispatcher($dispatcher)
|
||||
->setVisible($category->getVisible() ? false : true)
|
||||
->save()
|
||||
;
|
||||
|
||||
$event->setCategory($category);
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes position, selecting absolute ou relative change.
|
||||
*
|
||||
* @param UpdatePositionEvent $event
|
||||
* @param $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function updatePosition(UpdatePositionEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
$this->genericUpdatePosition(CategoryQuery::create(), $event, $dispatcher);
|
||||
}
|
||||
|
||||
public function addContent(CategoryAddContentEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
if (CategoryAssociatedContentQuery::create()
|
||||
->filterByContentId($event->getContentId())
|
||||
->filterByCategory($event->getCategory())->count() <= 0) {
|
||||
$content = new CategoryAssociatedContent();
|
||||
|
||||
$content
|
||||
->setDispatcher($dispatcher)
|
||||
->setCategory($event->getCategory())
|
||||
->setContentId($event->getContentId())
|
||||
->save()
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
public function removeContent(CategoryDeleteContentEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
$content = CategoryAssociatedContentQuery::create()
|
||||
->filterByContentId($event->getContentId())
|
||||
->filterByCategory($event->getCategory())->findOne()
|
||||
;
|
||||
|
||||
if ($content !== null) {
|
||||
$content
|
||||
->setDispatcher($dispatcher)
|
||||
->delete();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if is a category view and if category_id is visible
|
||||
*
|
||||
* @param ViewCheckEvent $event
|
||||
* @param string $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function viewCheck(ViewCheckEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
if ($event->getView() == 'category') {
|
||||
$category = CategoryQuery::create()
|
||||
->filterById($event->getViewId())
|
||||
->filterByVisible(1)
|
||||
->count();
|
||||
|
||||
if ($category == 0) {
|
||||
$dispatcher->dispatch(TheliaEvents::VIEW_CATEGORY_ID_NOT_VISIBLE, $event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ViewCheckEvent $event
|
||||
* @throws NotFoundHttpException
|
||||
*/
|
||||
public function viewcategoryIdNotVisible(ViewCheckEvent $event)
|
||||
{
|
||||
throw new NotFoundHttpException();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
TheliaEvents::CATEGORY_CREATE => array("create", 128),
|
||||
TheliaEvents::CATEGORY_UPDATE => array("update", 128),
|
||||
TheliaEvents::CATEGORY_DELETE => array("delete", 128),
|
||||
TheliaEvents::CATEGORY_TOGGLE_VISIBILITY => array("toggleVisibility", 128),
|
||||
|
||||
TheliaEvents::CATEGORY_UPDATE_POSITION => array("updatePosition", 128),
|
||||
TheliaEvents::CATEGORY_UPDATE_SEO => array("updateSeo", 128),
|
||||
|
||||
TheliaEvents::CATEGORY_ADD_CONTENT => array("addContent", 128),
|
||||
TheliaEvents::CATEGORY_REMOVE_CONTENT => array("removeContent", 128),
|
||||
|
||||
TheliaEvents::VIEW_CHECK => array('viewCheck', 128),
|
||||
TheliaEvents::VIEW_CATEGORY_ID_NOT_VISIBLE => array('viewcategoryIdNotVisible', 128),
|
||||
);
|
||||
}
|
||||
}
|
||||
128
core/lib/Thelia/Action/Config.php
Normal file
128
core/lib/Thelia/Action/Config.php
Normal file
@@ -0,0 +1,128 @@
|
||||
<?php
|
||||
/*************************************************************************************/
|
||||
/* This file is part of the Thelia package. */
|
||||
/* */
|
||||
/* Copyright (c) OpenStudio */
|
||||
/* email : dev@thelia.net */
|
||||
/* web : http://www.thelia.net */
|
||||
/* */
|
||||
/* For the full copyright and license information, please view the LICENSE.txt */
|
||||
/* file that was distributed with this source code. */
|
||||
/*************************************************************************************/
|
||||
|
||||
namespace Thelia\Action;
|
||||
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Thelia\Core\Event\Config\ConfigCreateEvent;
|
||||
use Thelia\Core\Event\Config\ConfigDeleteEvent;
|
||||
use Thelia\Core\Event\Config\ConfigUpdateEvent;
|
||||
use Thelia\Core\Event\TheliaEvents;
|
||||
use Thelia\Model\Config as ConfigModel;
|
||||
use Thelia\Model\ConfigQuery;
|
||||
|
||||
class Config extends BaseAction implements EventSubscriberInterface
|
||||
{
|
||||
/**
|
||||
* Create a new configuration entry
|
||||
*
|
||||
* @param \Thelia\Core\Event\Config\ConfigCreateEvent $event
|
||||
* @param $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function create(ConfigCreateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
$config = new ConfigModel();
|
||||
|
||||
$config->setDispatcher($dispatcher)
|
||||
->setName($event->getEventName())
|
||||
->setValue($event->getValue())
|
||||
->setLocale($event->getLocale())
|
||||
->setTitle($event->getTitle())
|
||||
->setHidden($event->getHidden())
|
||||
->setSecured($event->getSecured())
|
||||
->save();
|
||||
|
||||
$event->setConfig($config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Change a configuration entry value
|
||||
*
|
||||
* @param \Thelia\Core\Event\Config\ConfigUpdateEvent $event
|
||||
* @param $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function setValue(ConfigUpdateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
if (null !== $config = ConfigQuery::create()->findPk($event->getConfigId())) {
|
||||
if ($event->getValue() !== $config->getValue()) {
|
||||
$config->setDispatcher($dispatcher)->setValue($event->getValue())->save();
|
||||
|
||||
$event->setConfig($config);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Change a configuration entry
|
||||
*
|
||||
* @param \Thelia\Core\Event\Config\ConfigUpdateEvent $event
|
||||
* @param $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function modify(ConfigUpdateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
if (null !== $config = ConfigQuery::create()->findPk($event->getConfigId())) {
|
||||
$config->setDispatcher($dispatcher)
|
||||
->setName($event->getEventName())
|
||||
->setValue($event->getValue())
|
||||
->setHidden($event->getHidden())
|
||||
->setSecured($event->getSecured())
|
||||
->setLocale($event->getLocale())
|
||||
->setTitle($event->getTitle())
|
||||
->setDescription($event->getDescription())
|
||||
->setChapo($event->getChapo())
|
||||
->setPostscriptum($event->getPostscriptum())
|
||||
->save();
|
||||
|
||||
$event->setConfig($config);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a configuration entry
|
||||
*
|
||||
* @param \Thelia\Core\Event\Config\ConfigDeleteEvent $event
|
||||
* @param $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function delete(ConfigDeleteEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
if (null !== ($config = ConfigQuery::create()->findPk($event->getConfigId()))) {
|
||||
if (!$config->getSecured()) {
|
||||
$config->setDispatcher($dispatcher)->delete();
|
||||
|
||||
$event->setConfig($config);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
TheliaEvents::CONFIG_CREATE => array(
|
||||
"create", 128
|
||||
), TheliaEvents::CONFIG_SETVALUE => array(
|
||||
"setValue", 128
|
||||
), TheliaEvents::CONFIG_UPDATE => array(
|
||||
"modify", 128
|
||||
), TheliaEvents::CONFIG_DELETE => array(
|
||||
"delete", 128
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
263
core/lib/Thelia/Action/Content.php
Normal file
263
core/lib/Thelia/Action/Content.php
Normal file
@@ -0,0 +1,263 @@
|
||||
<?php
|
||||
/*************************************************************************************/
|
||||
/* This file is part of the Thelia package. */
|
||||
/* */
|
||||
/* Copyright (c) OpenStudio */
|
||||
/* email : dev@thelia.net */
|
||||
/* web : http://www.thelia.net */
|
||||
/* */
|
||||
/* For the full copyright and license information, please view the LICENSE.txt */
|
||||
/* file that was distributed with this source code. */
|
||||
/*************************************************************************************/
|
||||
|
||||
namespace Thelia\Action;
|
||||
|
||||
use Propel\Runtime\Exception\PropelException;
|
||||
use Propel\Runtime\Propel;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
use Thelia\Core\Event\Content\ContentAddFolderEvent;
|
||||
use Thelia\Core\Event\Content\ContentCreateEvent;
|
||||
use Thelia\Core\Event\Content\ContentDeleteEvent;
|
||||
use Thelia\Core\Event\Content\ContentRemoveFolderEvent;
|
||||
use Thelia\Core\Event\Content\ContentToggleVisibilityEvent;
|
||||
use Thelia\Core\Event\Content\ContentUpdateEvent;
|
||||
use Thelia\Core\Event\File\FileDeleteEvent;
|
||||
use Thelia\Core\Event\TheliaEvents;
|
||||
use Thelia\Core\Event\UpdatePositionEvent;
|
||||
use Thelia\Core\Event\UpdateSeoEvent;
|
||||
use Thelia\Core\Event\ViewCheckEvent;
|
||||
use Thelia\Model\ContentDocumentQuery;
|
||||
use Thelia\Model\ContentFolder;
|
||||
use Thelia\Model\ContentFolderQuery;
|
||||
use Thelia\Model\ContentImageQuery;
|
||||
use Thelia\Model\ContentQuery;
|
||||
use Thelia\Model\Content as ContentModel;
|
||||
use Thelia\Model\Map\ContentTableMap;
|
||||
|
||||
/**
|
||||
* Class Content
|
||||
* @package Thelia\Action
|
||||
* @author manuel raynaud <manu@raynaud.io>
|
||||
*/
|
||||
class Content extends BaseAction implements EventSubscriberInterface
|
||||
{
|
||||
public function create(ContentCreateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
$content = (new ContentModel)
|
||||
->setDispatcher($dispatcher)
|
||||
->setVisible($event->getVisible())
|
||||
->setLocale($event->getLocale())
|
||||
->setTitle($event->getTitle())
|
||||
->create($event->getDefaultFolder())
|
||||
;
|
||||
|
||||
$event->setContent($content);
|
||||
}
|
||||
|
||||
/**
|
||||
* process update content
|
||||
*
|
||||
* @param ContentUpdateEvent $event
|
||||
* @param $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
* @throws PropelException
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function update(ContentUpdateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
if (null !== $content = ContentQuery::create()->findPk($event->getContentId())) {
|
||||
$con = Propel::getWriteConnection(ContentTableMap::DATABASE_NAME);
|
||||
$con->beginTransaction();
|
||||
|
||||
$content->setDispatcher($dispatcher);
|
||||
try {
|
||||
$content
|
||||
->setVisible($event->getVisible())
|
||||
->setLocale($event->getLocale())
|
||||
->setTitle($event->getTitle())
|
||||
->setDescription($event->getDescription())
|
||||
->setChapo($event->getChapo())
|
||||
->setPostscriptum($event->getPostscriptum())
|
||||
->save($con)
|
||||
;
|
||||
|
||||
$content->setDefaultFolder($event->getDefaultFolder());
|
||||
|
||||
$event->setContent($content);
|
||||
$con->commit();
|
||||
} catch (PropelException $e) {
|
||||
$con->rollBack();
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Change Content SEO
|
||||
*
|
||||
* @param UpdateSeoEvent $event
|
||||
* @param $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
* @return Object
|
||||
*/
|
||||
public function updateSeo(UpdateSeoEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
return $this->genericUpdateSeo(ContentQuery::create(), $event, $dispatcher);
|
||||
}
|
||||
|
||||
public function updatePosition(UpdatePositionEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
$this->genericUpdateDelegatePosition(
|
||||
ContentFolderQuery::create()
|
||||
->filterByContentId($event->getObjectId())
|
||||
->filterByFolderId($event->getReferrerId()),
|
||||
$event,
|
||||
$dispatcher
|
||||
);
|
||||
}
|
||||
|
||||
public function toggleVisibility(ContentToggleVisibilityEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
$content = $event->getContent();
|
||||
|
||||
$content
|
||||
->setDispatcher($dispatcher)
|
||||
->setVisible(!$content->getVisible())
|
||||
->save();
|
||||
|
||||
$event->setContent($content);
|
||||
}
|
||||
|
||||
public function delete(ContentDeleteEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
if (null !== $content = ContentQuery::create()->findPk($event->getContentId())) {
|
||||
$con = Propel::getWriteConnection(ContentTableMap::DATABASE_NAME);
|
||||
$con->beginTransaction();
|
||||
|
||||
try {
|
||||
$fileList = ['images' => [], 'documentList' => []];
|
||||
|
||||
$defaultFolderId = $content->getDefaultFolderId();
|
||||
|
||||
// Get content's files to delete after content deletion
|
||||
$fileList['images']['list'] = ContentImageQuery::create()
|
||||
->findByContentId($event->getContentId());
|
||||
$fileList['images']['type'] = TheliaEvents::IMAGE_DELETE;
|
||||
|
||||
$fileList['documentList']['list'] = ContentDocumentQuery::create()
|
||||
->findByContentId($event->getContentId());
|
||||
$fileList['documentList']['type'] = TheliaEvents::DOCUMENT_DELETE;
|
||||
|
||||
// Delete content
|
||||
$content->setDispatcher($dispatcher)
|
||||
->delete($con);
|
||||
|
||||
$event->setDefaultFolderId($defaultFolderId);
|
||||
$event->setContent($content);
|
||||
|
||||
// Dispatch delete content's files event
|
||||
foreach ($fileList as $fileTypeList) {
|
||||
foreach ($fileTypeList['list'] as $fileToDelete) {
|
||||
$fileDeleteEvent = new FileDeleteEvent($fileToDelete);
|
||||
$dispatcher->dispatch($fileTypeList['type'], $fileDeleteEvent);
|
||||
}
|
||||
}
|
||||
|
||||
$con->commit();
|
||||
} catch (\Exception $e) {
|
||||
$con->rollback();
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* associate a folder to a content if the association already does not exists
|
||||
*
|
||||
* @param ContentAddFolderEvent $event
|
||||
*/
|
||||
public function addFolder(ContentAddFolderEvent $event)
|
||||
{
|
||||
if (ContentFolderQuery::create()
|
||||
->filterByContent($event->getContent())
|
||||
->filterByFolderId($event->getFolderId())
|
||||
->count() <= 0
|
||||
) {
|
||||
$contentFolder = (new ContentFolder())
|
||||
->setFolderId($event->getFolderId())
|
||||
->setContent($event->getContent())
|
||||
->setDefaultFolder(false);
|
||||
|
||||
$contentFolder
|
||||
->setPosition($contentFolder->getNextPosition())
|
||||
->save();
|
||||
}
|
||||
}
|
||||
|
||||
public function removeFolder(ContentRemoveFolderEvent $event)
|
||||
{
|
||||
$contentFolder = ContentFolderQuery::create()
|
||||
->filterByContent($event->getContent())
|
||||
->filterByFolderId($event->getFolderId())
|
||||
->findOne();
|
||||
|
||||
if (null !== $contentFolder) {
|
||||
$contentFolder->delete();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if is a content view and if content_id is visible
|
||||
*
|
||||
* @param ViewCheckEvent $event
|
||||
* @param string $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function viewCheck(ViewCheckEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
if ($event->getView() == 'content') {
|
||||
$content = ContentQuery::create()
|
||||
->filterById($event->getViewId())
|
||||
->filterByVisible(1)
|
||||
->count();
|
||||
|
||||
if ($content == 0) {
|
||||
$dispatcher->dispatch(TheliaEvents::VIEW_CONTENT_ID_NOT_VISIBLE, $event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ViewCheckEvent $event
|
||||
* @throws NotFoundHttpException
|
||||
*/
|
||||
public function viewContentIdNotVisible(ViewCheckEvent $event)
|
||||
{
|
||||
throw new NotFoundHttpException();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
TheliaEvents::CONTENT_CREATE => array('create', 128),
|
||||
TheliaEvents::CONTENT_UPDATE => array('update', 128),
|
||||
TheliaEvents::CONTENT_DELETE => array('delete', 128),
|
||||
TheliaEvents::CONTENT_TOGGLE_VISIBILITY => array('toggleVisibility', 128),
|
||||
|
||||
TheliaEvents::CONTENT_UPDATE_POSITION => array('updatePosition', 128),
|
||||
TheliaEvents::CONTENT_UPDATE_SEO => array('updateSeo', 128),
|
||||
|
||||
TheliaEvents::CONTENT_ADD_FOLDER => array('addFolder', 128),
|
||||
TheliaEvents::CONTENT_REMOVE_FOLDER => array('removeFolder', 128),
|
||||
|
||||
TheliaEvents::VIEW_CHECK => array('viewCheck', 128),
|
||||
TheliaEvents::VIEW_CONTENT_ID_NOT_VISIBLE => array('viewContentIdNotVisible', 128),
|
||||
);
|
||||
}
|
||||
}
|
||||
121
core/lib/Thelia/Action/Country.php
Normal file
121
core/lib/Thelia/Action/Country.php
Normal file
@@ -0,0 +1,121 @@
|
||||
<?php
|
||||
/*************************************************************************************/
|
||||
/* This file is part of the Thelia package. */
|
||||
/* */
|
||||
/* Copyright (c) OpenStudio */
|
||||
/* email : dev@thelia.net */
|
||||
/* web : http://www.thelia.net */
|
||||
/* */
|
||||
/* For the full copyright and license information, please view the LICENSE.txt */
|
||||
/* file that was distributed with this source code. */
|
||||
/*************************************************************************************/
|
||||
|
||||
namespace Thelia\Action;
|
||||
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Thelia\Core\Event\Country\CountryCreateEvent;
|
||||
use Thelia\Core\Event\Country\CountryDeleteEvent;
|
||||
use Thelia\Core\Event\Country\CountryToggleDefaultEvent;
|
||||
use Thelia\Core\Event\Country\CountryToggleVisibilityEvent;
|
||||
use Thelia\Core\Event\Country\CountryUpdateEvent;
|
||||
use Thelia\Core\Event\TheliaEvents;
|
||||
use Thelia\Model\Country as CountryModel;
|
||||
use Thelia\Model\CountryQuery;
|
||||
|
||||
/**
|
||||
* Class Country
|
||||
* @package Thelia\Action
|
||||
* @author Manuel Raynaud <manu@raynaud.io>
|
||||
*/
|
||||
class Country extends BaseAction implements EventSubscriberInterface
|
||||
{
|
||||
public function create(CountryCreateEvent $event)
|
||||
{
|
||||
$country = new CountryModel();
|
||||
|
||||
$country
|
||||
->setVisible($event->isVisible())
|
||||
->setIsocode($event->getIsocode())
|
||||
->setIsoalpha2($event->getIsoAlpha2())
|
||||
->setIsoalpha3($event->getIsoAlpha3())
|
||||
->setHasStates($event->isHasStates())
|
||||
->setLocale($event->getLocale())
|
||||
->setTitle($event->getTitle())
|
||||
->save();
|
||||
|
||||
$event->setCountry($country);
|
||||
}
|
||||
|
||||
public function update(CountryUpdateEvent $event)
|
||||
{
|
||||
if (null !== $country = CountryQuery::create()->findPk($event->getCountryId())) {
|
||||
$country
|
||||
->setVisible($event->isVisible())
|
||||
->setIsocode($event->getIsocode())
|
||||
->setIsoalpha2($event->getIsoAlpha2())
|
||||
->setIsoalpha3($event->getIsoAlpha3())
|
||||
->setHasStates($event->isHasStates())
|
||||
->setNeedZipCode($event->isNeedZipCode())
|
||||
->setZipCodeFormat($event->getZipCodeFormat())
|
||||
->setLocale($event->getLocale())
|
||||
->setTitle($event->getTitle())
|
||||
->setChapo($event->getChapo())
|
||||
->setDescription($event->getDescription())
|
||||
->save();
|
||||
|
||||
$event->setCountry($country);
|
||||
}
|
||||
}
|
||||
|
||||
public function delete(CountryDeleteEvent $event)
|
||||
{
|
||||
if (null !== $country = CountryQuery::create()->findPk($event->getCountryId())) {
|
||||
$country->delete();
|
||||
|
||||
$event->setCountry($country);
|
||||
}
|
||||
}
|
||||
|
||||
public function toggleDefault(CountryToggleDefaultEvent $event)
|
||||
{
|
||||
if (null !== $country = CountryQuery::create()->findPk($event->getCountryId())) {
|
||||
$country->toggleDefault();
|
||||
|
||||
$event->setCountry($country);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle Country visibility
|
||||
*
|
||||
* @param CountryToggleVisibilityEvent $event
|
||||
* @param $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function toggleVisibility(CountryToggleVisibilityEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
$country = $event->getCountry();
|
||||
|
||||
$country
|
||||
->setDispatcher($dispatcher)
|
||||
->setVisible(!$country->getVisible())
|
||||
->save();
|
||||
|
||||
$event->setCountry($country);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
TheliaEvents::COUNTRY_CREATE => array('create', 128),
|
||||
TheliaEvents::COUNTRY_UPDATE => array('update', 128),
|
||||
TheliaEvents::COUNTRY_DELETE => array('delete', 128),
|
||||
TheliaEvents::COUNTRY_TOGGLE_DEFAULT => array('toggleDefault', 128),
|
||||
TheliaEvents::COUNTRY_TOGGLE_VISIBILITY => array('toggleVisibility', 128)
|
||||
);
|
||||
}
|
||||
}
|
||||
466
core/lib/Thelia/Action/Coupon.php
Normal file
466
core/lib/Thelia/Action/Coupon.php
Normal file
@@ -0,0 +1,466 @@
|
||||
<?php
|
||||
/*************************************************************************************/
|
||||
/* This file is part of the Thelia package. */
|
||||
/* */
|
||||
/* Copyright (c) OpenStudio */
|
||||
/* email : dev@thelia.net */
|
||||
/* web : http://www.thelia.net */
|
||||
/* */
|
||||
/* For the full copyright and license information, please view the LICENSE.txt */
|
||||
/* file that was distributed with this source code. */
|
||||
/*************************************************************************************/
|
||||
|
||||
namespace Thelia\Action;
|
||||
|
||||
use Propel\Runtime\Propel;
|
||||
use Symfony\Component\EventDispatcher\Event;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
use Thelia\Condition\ConditionCollection;
|
||||
use Thelia\Condition\ConditionFactory;
|
||||
use Thelia\Condition\Implementation\ConditionInterface;
|
||||
use Thelia\Core\Event\Coupon\CouponConsumeEvent;
|
||||
use Thelia\Core\Event\Coupon\CouponCreateOrUpdateEvent;
|
||||
use Thelia\Core\Event\Coupon\CouponDeleteEvent;
|
||||
use Thelia\Core\Event\Order\OrderEvent;
|
||||
use Thelia\Core\Event\TheliaEvents;
|
||||
use Thelia\Coupon\CouponFactory;
|
||||
use Thelia\Coupon\CouponManager;
|
||||
use Thelia\Coupon\Type\CouponInterface;
|
||||
use Thelia\Model\Coupon as CouponModel;
|
||||
use Thelia\Model\CouponCountry;
|
||||
use Thelia\Model\CouponCountryQuery;
|
||||
use Thelia\Model\CouponModule;
|
||||
use Thelia\Model\CouponModuleQuery;
|
||||
use Thelia\Model\CouponQuery;
|
||||
use Thelia\Model\Map\OrderCouponTableMap;
|
||||
use Thelia\Model\OrderCoupon;
|
||||
use Thelia\Model\OrderCouponCountry;
|
||||
use Thelia\Model\OrderCouponModule;
|
||||
use Thelia\Model\OrderCouponQuery;
|
||||
use Thelia\Model\OrderStatusQuery;
|
||||
|
||||
/**
|
||||
* Process Coupon Events
|
||||
*
|
||||
* @package Coupon
|
||||
* @author Guillaume MOREL <gmorel@openstudio.fr>, Franck Allimant <franck@cqfdev.fr>
|
||||
*
|
||||
*/
|
||||
class Coupon extends BaseAction implements EventSubscriberInterface
|
||||
{
|
||||
/** @var RequestStack */
|
||||
protected $requestStack;
|
||||
|
||||
/** @var CouponFactory $couponFactory */
|
||||
protected $couponFactory;
|
||||
|
||||
/** @var CouponManager $couponManager */
|
||||
protected $couponManager;
|
||||
|
||||
/** @var ConditionInterface $noConditionRule */
|
||||
protected $noConditionRule;
|
||||
|
||||
/** @var ConditionFactory $conditionFactory */
|
||||
protected $conditionFactory;
|
||||
|
||||
public function __construct(
|
||||
RequestStack $requestStack,
|
||||
CouponFactory $couponFactory,
|
||||
CouponManager $couponManager,
|
||||
ConditionInterface $noConditionRule,
|
||||
ConditionFactory $conditionFactory
|
||||
) {
|
||||
$this->requestStack = $requestStack;
|
||||
$this->couponFactory = $couponFactory;
|
||||
$this->couponManager = $couponManager;
|
||||
$this->noConditionRule = $noConditionRule;
|
||||
$this->conditionFactory = $conditionFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Occurring when a Coupon is about to be created
|
||||
*
|
||||
* @param CouponCreateOrUpdateEvent $event Event creation or update Coupon
|
||||
* @param $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function create(CouponCreateOrUpdateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
$coupon = new CouponModel();
|
||||
|
||||
$this->createOrUpdate($coupon, $event, $dispatcher);
|
||||
}
|
||||
|
||||
/**
|
||||
* Occurring when a Coupon is about to be updated
|
||||
*
|
||||
* @param CouponCreateOrUpdateEvent $event Event creation or update Coupon
|
||||
* @param $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function update(CouponCreateOrUpdateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
$coupon = $event->getCouponModel();
|
||||
|
||||
$this->createOrUpdate($coupon, $event, $dispatcher);
|
||||
}
|
||||
|
||||
public function delete(CouponDeleteEvent $event)
|
||||
{
|
||||
$coupon = $event->getCoupon();
|
||||
|
||||
if (null === $coupon) {
|
||||
throw new \InvalidArgumentException(
|
||||
sprintf(
|
||||
"The coupon id '%d' doesn't exist",
|
||||
$event->getCouponId()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$coupon->delete();
|
||||
|
||||
$event->setCoupon(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Occurring when a Coupon condition is about to be updated
|
||||
*
|
||||
* @param CouponCreateOrUpdateEvent $event Event creation or update Coupon condition
|
||||
* @param $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function updateCondition(CouponCreateOrUpdateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
$modelCoupon = $event->getCouponModel();
|
||||
|
||||
$this->createOrUpdateCondition($modelCoupon, $event, $dispatcher);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all coupons in session.
|
||||
*
|
||||
* @param Event $event
|
||||
* @param $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function clearAllCoupons(Event $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
// Tell coupons to clear any data they may have stored
|
||||
$this->couponManager->clear();
|
||||
|
||||
$this->getSession()->setConsumedCoupons(array());
|
||||
|
||||
$this->updateOrderDiscount($event, $eventName, $dispatcher);
|
||||
}
|
||||
|
||||
/**
|
||||
* Occurring when a Coupon condition is about to be consumed
|
||||
*
|
||||
* @param CouponConsumeEvent $event Event consuming Coupon
|
||||
* @param $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function consume(CouponConsumeEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
$totalDiscount = 0;
|
||||
$isValid = false;
|
||||
|
||||
/** @var CouponInterface $coupon */
|
||||
$coupon = $this->couponFactory->buildCouponFromCode($event->getCode());
|
||||
|
||||
if ($coupon) {
|
||||
$isValid = $coupon->isMatching();
|
||||
|
||||
if ($isValid) {
|
||||
$this->couponManager->pushCouponInSession($event->getCode());
|
||||
$totalDiscount = $this->couponManager->getDiscount();
|
||||
|
||||
$this->getSession()
|
||||
->getSessionCart($dispatcher)
|
||||
->setDiscount($totalDiscount)
|
||||
->save();
|
||||
|
||||
$this->getSession()
|
||||
->getOrder()
|
||||
->setDiscount($totalDiscount)
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
$event->setIsValid($isValid);
|
||||
$event->setDiscount($totalDiscount);
|
||||
}
|
||||
|
||||
public function updateOrderDiscount(Event $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
$discount = $this->couponManager->getDiscount();
|
||||
|
||||
$this->getSession()
|
||||
->getSessionCart($dispatcher)
|
||||
->setDiscount($discount)
|
||||
->save();
|
||||
|
||||
$this->getSession()
|
||||
->getOrder()
|
||||
->setDiscount($discount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call the Model and delegate the create or delete action
|
||||
* Feed the Event with the updated model
|
||||
*
|
||||
* @param CouponModel $coupon Model to save
|
||||
* @param CouponCreateOrUpdateEvent $event Event containing data
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
protected function createOrUpdate(CouponModel $coupon, CouponCreateOrUpdateEvent $event, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
$coupon->setDispatcher($dispatcher);
|
||||
|
||||
// Set default condition if none found
|
||||
/** @var ConditionInterface $noConditionRule */
|
||||
$noConditionRule = $this->noConditionRule;
|
||||
/** @var ConditionFactory $conditionFactory */
|
||||
$conditionFactory = $this->conditionFactory;
|
||||
$couponRuleCollection = new ConditionCollection();
|
||||
$couponRuleCollection[] = $noConditionRule;
|
||||
$defaultSerializedRule = $conditionFactory->serializeConditionCollection(
|
||||
$couponRuleCollection
|
||||
);
|
||||
|
||||
$coupon->createOrUpdate(
|
||||
$event->getCode(),
|
||||
$event->getTitle(),
|
||||
$event->getEffects(),
|
||||
$event->getServiceId(),
|
||||
$event->isRemovingPostage(),
|
||||
$event->getShortDescription(),
|
||||
$event->getDescription(),
|
||||
$event->isEnabled(),
|
||||
$event->getExpirationDate(),
|
||||
$event->isAvailableOnSpecialOffers(),
|
||||
$event->isCumulative(),
|
||||
$event->getMaxUsage(),
|
||||
$defaultSerializedRule,
|
||||
$event->getLocale(),
|
||||
$event->getFreeShippingForCountries(),
|
||||
$event->getFreeShippingForMethods(),
|
||||
$event->getPerCustomerUsageCount(),
|
||||
$event->getStartDate()
|
||||
);
|
||||
|
||||
$event->setCouponModel($coupon);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call the Model and delegate the create or delete action
|
||||
* Feed the Event with the updated model
|
||||
*
|
||||
* @param CouponModel $coupon Model to save
|
||||
* @param CouponCreateOrUpdateEvent $event Event containing data
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
protected function createOrUpdateCondition(CouponModel $coupon, CouponCreateOrUpdateEvent $event, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
$coupon->setDispatcher($dispatcher);
|
||||
|
||||
/** @var ConditionFactory $conditionFactory */
|
||||
$conditionFactory = $this->conditionFactory;
|
||||
|
||||
$coupon->createOrUpdateConditions(
|
||||
$conditionFactory->serializeConditionCollection($event->getConditions()),
|
||||
$event->getLocale()
|
||||
);
|
||||
|
||||
$event->setCouponModel($coupon);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Thelia\Core\Event\Order\OrderEvent $event
|
||||
*/
|
||||
public function testFreePostage(OrderEvent $event)
|
||||
{
|
||||
$order = $event->getOrder();
|
||||
|
||||
if ($this->couponManager->isCouponRemovingPostage($order)) {
|
||||
$order->setPostage(0);
|
||||
|
||||
$event->setOrder($order);
|
||||
|
||||
$event->stopPropagation();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Thelia\Core\Event\Order\OrderEvent $event
|
||||
*
|
||||
* @throws \Exception if something goes wrong.
|
||||
* @param $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function afterOrder(OrderEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
/** @var CouponInterface[] $consumedCoupons */
|
||||
$consumedCoupons = $this->couponManager->getCouponsKept();
|
||||
|
||||
if (\is_array($consumedCoupons) && \count($consumedCoupons) > 0) {
|
||||
$con = Propel::getWriteConnection(OrderCouponTableMap::DATABASE_NAME);
|
||||
$con->beginTransaction();
|
||||
|
||||
try {
|
||||
foreach ($consumedCoupons as $couponCode) {
|
||||
$couponQuery = CouponQuery::create();
|
||||
$couponModel = $couponQuery->findOneByCode($couponCode->getCode());
|
||||
$couponModel->setLocale($this->getSession()->getLang()->getLocale());
|
||||
|
||||
/* decrease coupon quantity */
|
||||
$this->couponManager->decrementQuantity($couponModel, $event->getOrder()->getCustomerId());
|
||||
|
||||
/* memorize coupon */
|
||||
$orderCoupon = new OrderCoupon();
|
||||
$orderCoupon->setOrder($event->getOrder())
|
||||
->setCode($couponModel->getCode())
|
||||
->setType($couponModel->getType())
|
||||
->setAmount($couponCode->exec())
|
||||
|
||||
->setTitle($couponModel->getTitle())
|
||||
->setShortDescription($couponModel->getShortDescription())
|
||||
->setDescription($couponModel->getDescription())
|
||||
|
||||
->setStartDate($couponModel->getStartDate())
|
||||
->setExpirationDate($couponModel->getExpirationDate())
|
||||
->setIsCumulative($couponModel->getIsCumulative())
|
||||
->setIsRemovingPostage($couponModel->getIsRemovingPostage())
|
||||
->setIsAvailableOnSpecialOffers($couponModel->getIsAvailableOnSpecialOffers())
|
||||
->setSerializedConditions($couponModel->getSerializedConditions())
|
||||
->setPerCustomerUsageCount($couponModel->getPerCustomerUsageCount())
|
||||
;
|
||||
$orderCoupon->save();
|
||||
|
||||
// Copy order coupon free shipping data for countries and modules
|
||||
$couponCountries = CouponCountryQuery::create()->filterByCouponId($couponModel->getId())->find();
|
||||
|
||||
/** @var CouponCountry $couponCountry */
|
||||
foreach ($couponCountries as $couponCountry) {
|
||||
$occ = new OrderCouponCountry();
|
||||
|
||||
$occ
|
||||
->setCouponId($orderCoupon->getId())
|
||||
->setCountryId($couponCountry->getCountryId())
|
||||
->save();
|
||||
;
|
||||
}
|
||||
|
||||
$couponModules = CouponModuleQuery::create()->filterByCouponId($couponModel->getId())->find();
|
||||
|
||||
/** @var CouponModule $couponModule */
|
||||
foreach ($couponModules as $couponModule) {
|
||||
$ocm = new OrderCouponModule();
|
||||
|
||||
$ocm
|
||||
->setCouponId($orderCoupon->getId())
|
||||
->setModuleId($couponModule->getModuleId())
|
||||
->save();
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
$con->commit();
|
||||
} catch (\Exception $ex) {
|
||||
$con->rollBack();
|
||||
|
||||
throw($ex);
|
||||
}
|
||||
}
|
||||
|
||||
// Clear all coupons.
|
||||
$dispatcher->dispatch(TheliaEvents::COUPON_CLEAR_ALL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels order coupons usage when order is canceled or refunded,
|
||||
* or use canceled coupons again if the order is no longer canceled or refunded
|
||||
*
|
||||
* @param OrderEvent $event
|
||||
* @param string $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
* @throws \Exception
|
||||
* @throws \Propel\Runtime\Exception\PropelException
|
||||
*/
|
||||
public function orderStatusChange(OrderEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
// The order has been canceled or refunded ?
|
||||
if ($event->getOrder()->isCancelled() || $event->getOrder()->isRefunded()) {
|
||||
// Cancel usage of all coupons for this order
|
||||
$usedCoupons = OrderCouponQuery::create()
|
||||
->filterByUsageCanceled(false)
|
||||
->findByOrderId($event->getOrder()->getId());
|
||||
|
||||
$customerId = $event->getOrder()->getCustomerId();
|
||||
|
||||
/** @var OrderCoupon $usedCoupon */
|
||||
foreach ($usedCoupons as $usedCoupon) {
|
||||
if (null !== $couponModel = CouponQuery::create()->findOneByCode($usedCoupon->getCode())) {
|
||||
// If the coupon still exists, restore one usage to the usage count.
|
||||
$this->couponManager->incrementQuantity($couponModel, $customerId);
|
||||
}
|
||||
|
||||
// Mark coupon usage as canceled in the OrderCoupon table
|
||||
$usedCoupon->setUsageCanceled(true)->save();
|
||||
}
|
||||
} else {
|
||||
// Mark canceled coupons for this order as used again
|
||||
$usedCoupons = OrderCouponQuery::create()
|
||||
->filterByUsageCanceled(true)
|
||||
->findByOrderId($event->getOrder()->getId());
|
||||
|
||||
$customerId = $event->getOrder()->getCustomerId();
|
||||
|
||||
/** @var OrderCoupon $usedCoupon */
|
||||
foreach ($usedCoupons as $usedCoupon) {
|
||||
if (null !== $couponModel = CouponQuery::create()->findOneByCode($usedCoupon->getCode())) {
|
||||
// If the coupon still exists, mark the coupon as used
|
||||
$this->couponManager->decrementQuantity($couponModel, $customerId);
|
||||
}
|
||||
|
||||
// The coupon is no longer canceled
|
||||
$usedCoupon->setUsageCanceled(false)->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
TheliaEvents::COUPON_CREATE => array("create", 128),
|
||||
TheliaEvents::COUPON_UPDATE => array("update", 128),
|
||||
TheliaEvents::COUPON_DELETE => array("delete", 128),
|
||||
TheliaEvents::COUPON_CONSUME => array("consume", 128),
|
||||
TheliaEvents::COUPON_CLEAR_ALL => array("clearAllCoupons", 128),
|
||||
TheliaEvents::COUPON_CONDITION_UPDATE => array("updateCondition", 128),
|
||||
TheliaEvents::ORDER_SET_POSTAGE => array("testFreePostage", 132),
|
||||
TheliaEvents::ORDER_BEFORE_PAYMENT => array("afterOrder", 128),
|
||||
TheliaEvents::ORDER_UPDATE_STATUS => array("orderStatusChange", 10),
|
||||
TheliaEvents::CART_ADDITEM => array("updateOrderDiscount", 10),
|
||||
TheliaEvents::CART_UPDATEITEM => array("updateOrderDiscount", 10),
|
||||
TheliaEvents::CART_DELETEITEM => array("updateOrderDiscount", 10),
|
||||
TheliaEvents::CUSTOMER_LOGIN => array("updateOrderDiscount", 10)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the session from the current request
|
||||
*
|
||||
* @return \Thelia\Core\HttpFoundation\Session\Session
|
||||
*/
|
||||
protected function getSession()
|
||||
{
|
||||
return $this->requestStack->getCurrentRequest()->getSession();
|
||||
}
|
||||
}
|
||||
219
core/lib/Thelia/Action/Currency.php
Normal file
219
core/lib/Thelia/Action/Currency.php
Normal file
@@ -0,0 +1,219 @@
|
||||
<?php
|
||||
/*************************************************************************************/
|
||||
/* This file is part of the Thelia package. */
|
||||
/* */
|
||||
/* Copyright (c) OpenStudio */
|
||||
/* email : dev@thelia.net */
|
||||
/* web : http://www.thelia.net */
|
||||
/* */
|
||||
/* For the full copyright and license information, please view the LICENSE.txt */
|
||||
/* file that was distributed with this source code. */
|
||||
/*************************************************************************************/
|
||||
|
||||
namespace Thelia\Action;
|
||||
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Thelia\Core\Event\Currency\CurrencyCreateEvent;
|
||||
use Thelia\Core\Event\Currency\CurrencyDeleteEvent;
|
||||
use Thelia\Core\Event\Currency\CurrencyUpdateEvent;
|
||||
use Thelia\Core\Event\Currency\CurrencyUpdateRateEvent;
|
||||
use Thelia\Core\Event\TheliaEvents;
|
||||
use Thelia\Core\Event\UpdatePositionEvent;
|
||||
use Thelia\Core\Translation\Translator;
|
||||
use Thelia\CurrencyConverter\CurrencyConverter;
|
||||
use Thelia\CurrencyConverter\Exception\CurrencyNotFoundException;
|
||||
use Thelia\Log\Tlog;
|
||||
use Thelia\Math\Number;
|
||||
use Thelia\Model\Currency as CurrencyModel;
|
||||
use Thelia\Model\CurrencyQuery;
|
||||
|
||||
class Currency extends BaseAction implements EventSubscriberInterface
|
||||
{
|
||||
/** @var CurrencyConverter */
|
||||
protected $currencyConverter;
|
||||
|
||||
public function __construct(CurrencyConverter $currencyConverter)
|
||||
{
|
||||
$this->currencyConverter = $currencyConverter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new currencyuration entry
|
||||
*
|
||||
* @param \Thelia\Core\Event\Currency\CurrencyCreateEvent $event
|
||||
* @param $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function create(CurrencyCreateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
$currency = new CurrencyModel();
|
||||
|
||||
$isDefault = CurrencyQuery::create()->count() === 0;
|
||||
|
||||
$currency
|
||||
->setDispatcher($dispatcher)
|
||||
->setLocale($event->getLocale())
|
||||
->setName($event->getCurrencyName())
|
||||
->setSymbol($event->getSymbol())
|
||||
->setFormat($event->getFormat())
|
||||
->setRate($event->getRate())
|
||||
->setCode(strtoupper($event->getCode()))
|
||||
->setByDefault($isDefault)
|
||||
->save()
|
||||
;
|
||||
|
||||
$event->setCurrency($currency);
|
||||
}
|
||||
|
||||
/**
|
||||
* Change a currency
|
||||
*
|
||||
* @param \Thelia\Core\Event\Currency\CurrencyUpdateEvent $event
|
||||
* @param $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function update(CurrencyUpdateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
if (null !== $currency = CurrencyQuery::create()->findPk($event->getCurrencyId())) {
|
||||
$currency
|
||||
->setDispatcher($dispatcher)
|
||||
|
||||
->setLocale($event->getLocale())
|
||||
->setName($event->getCurrencyName())
|
||||
->setSymbol($event->getSymbol())
|
||||
->setFormat($event->getFormat())
|
||||
->setRate($event->getRate())
|
||||
->setCode(strtoupper($event->getCode()))
|
||||
|
||||
->save();
|
||||
|
||||
$event->setCurrency($currency);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default currency
|
||||
*
|
||||
* @param CurrencyUpdateEvent $event
|
||||
* @param $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function setDefault(CurrencyUpdateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
if (null !== $currency = CurrencyQuery::create()->findPk($event->getCurrencyId())) {
|
||||
// Reset default status
|
||||
CurrencyQuery::create()->filterByByDefault(true)->update(array('ByDefault' => false));
|
||||
|
||||
$currency
|
||||
->setDispatcher($dispatcher)
|
||||
->setVisible($event->getVisible())
|
||||
->setByDefault($event->getIsDefault())
|
||||
->save()
|
||||
;
|
||||
|
||||
// Update rates when setting a new default currency
|
||||
if ($event->getIsDefault()) {
|
||||
$updateRateEvent = new CurrencyUpdateRateEvent();
|
||||
|
||||
$dispatcher->dispatch(TheliaEvents::CURRENCY_UPDATE_RATES, $updateRateEvent);
|
||||
}
|
||||
|
||||
$event->setCurrency($currency);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param CurrencyUpdateEvent $event
|
||||
*/
|
||||
public function setVisible(CurrencyUpdateEvent $event)
|
||||
{
|
||||
if (null !== $currency = CurrencyQuery::create()->findPk($event->getCurrencyId())) {
|
||||
if (!$currency->getByDefault()) {
|
||||
$currency->setVisible($event->getVisible())->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a currencyuration entry
|
||||
*
|
||||
* @param \Thelia\Core\Event\Currency\CurrencyDeleteEvent $event
|
||||
* @param $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function delete(CurrencyDeleteEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
if (null !== ($currency = CurrencyQuery::create()->findPk($event->getCurrencyId()))) {
|
||||
if ($currency->getByDefault()) {
|
||||
throw new \RuntimeException(
|
||||
Translator::getInstance()->trans('It is not allowed to delete the default currency')
|
||||
);
|
||||
}
|
||||
|
||||
$currency
|
||||
->setDispatcher($dispatcher)
|
||||
->delete()
|
||||
;
|
||||
|
||||
$event->setCurrency($currency);
|
||||
}
|
||||
}
|
||||
|
||||
public function updateRates(CurrencyUpdateRateEvent $event)
|
||||
{
|
||||
if (null === $defaultCurrency = CurrencyQuery::create()->findOneByByDefault(true)) {
|
||||
throw new \RuntimeException('Unable to find a default currency, please define a default currency.');
|
||||
}
|
||||
|
||||
$defaultCurrency->setRate(1)->save();
|
||||
|
||||
$currencies = CurrencyQuery::create()->filterByByDefault(false);
|
||||
$baseValue = new Number('1');
|
||||
|
||||
/** @var \Thelia\Model\Currency $currency */
|
||||
foreach ($currencies as $currency) {
|
||||
try {
|
||||
$rate = $this->currencyConverter
|
||||
->from($defaultCurrency->getCode())
|
||||
->to($currency->getCode())
|
||||
->convert($baseValue);
|
||||
|
||||
$currency->setRate($rate->getNumber(-1))->save();
|
||||
} catch (CurrencyNotFoundException $ex) {
|
||||
Tlog::getInstance()->addError(
|
||||
sprintf("Unable to find exchange rate for currency %s, ID %d", $currency->getCode(), $currency->getId())
|
||||
);
|
||||
$event->addUndefinedRate($currency->getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes position, selecting absolute ou relative change.
|
||||
*
|
||||
* @param UpdatePositionEvent $event
|
||||
* @param string $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function updatePosition(UpdatePositionEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
$this->genericUpdatePosition(CurrencyQuery::create(), $event, $dispatcher);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
TheliaEvents::CURRENCY_CREATE => array("create", 128),
|
||||
TheliaEvents::CURRENCY_UPDATE => array("update", 128),
|
||||
TheliaEvents::CURRENCY_DELETE => array("delete", 128),
|
||||
TheliaEvents::CURRENCY_SET_DEFAULT => array("setDefault", 128),
|
||||
TheliaEvents::CURRENCY_SET_VISIBLE => array("setVisible", 128),
|
||||
TheliaEvents::CURRENCY_UPDATE_RATES => array("updateRates", 128),
|
||||
TheliaEvents::CURRENCY_UPDATE_POSITION => array("updatePosition", 128)
|
||||
);
|
||||
}
|
||||
}
|
||||
292
core/lib/Thelia/Action/Customer.php
Normal file
292
core/lib/Thelia/Action/Customer.php
Normal file
@@ -0,0 +1,292 @@
|
||||
<?php
|
||||
/*************************************************************************************/
|
||||
/* This file is part of the Thelia package. */
|
||||
/* */
|
||||
/* Copyright (c) OpenStudio */
|
||||
/* email : dev@thelia.net */
|
||||
/* web : http://www.thelia.net */
|
||||
/* */
|
||||
/* For the full copyright and license information, please view the LICENSE.txt */
|
||||
/* file that was distributed with this source code. */
|
||||
/*************************************************************************************/
|
||||
|
||||
namespace Thelia\Action;
|
||||
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
use Thelia\Core\Event\ActionEvent;
|
||||
use Thelia\Core\Event\Customer\CustomerCreateOrUpdateEvent;
|
||||
use Thelia\Core\Event\Customer\CustomerEvent;
|
||||
use Thelia\Core\Event\Customer\CustomerLoginEvent;
|
||||
use Thelia\Core\Event\LostPasswordEvent;
|
||||
use Thelia\Core\Event\TheliaEvents;
|
||||
use Thelia\Core\Security\SecurityContext;
|
||||
use Thelia\Core\Translation\Translator;
|
||||
use Thelia\Exception\CustomerException;
|
||||
use Thelia\Mailer\MailerFactory;
|
||||
use Thelia\Model\ConfigQuery;
|
||||
use Thelia\Model\Customer as CustomerModel;
|
||||
use Thelia\Model\CustomerQuery;
|
||||
use Thelia\Model\LangQuery;
|
||||
use Thelia\Tools\Password;
|
||||
|
||||
/**
|
||||
*
|
||||
* customer class where all actions are managed
|
||||
*
|
||||
* Class Customer
|
||||
* @package Thelia\Action
|
||||
* @author Manuel Raynaud <manu@raynaud.io>
|
||||
*/
|
||||
class Customer extends BaseAction implements EventSubscriberInterface
|
||||
{
|
||||
/** @var SecurityContext */
|
||||
protected $securityContext;
|
||||
|
||||
/** @var MailerFactory */
|
||||
protected $mailer;
|
||||
|
||||
/** @var RequestStack */
|
||||
protected $requestStack;
|
||||
|
||||
public function __construct(SecurityContext $securityContext, MailerFactory $mailer, RequestStack $requestStack = null)
|
||||
{
|
||||
$this->securityContext = $securityContext;
|
||||
$this->mailer = $mailer;
|
||||
$this->requestStack = $requestStack;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param CustomerCreateOrUpdateEvent $event
|
||||
* @param $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
* @throws \Propel\Runtime\Exception\PropelException
|
||||
*/
|
||||
public function create(CustomerCreateOrUpdateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
$customer = new CustomerModel();
|
||||
|
||||
$plainPassword = $event->getPassword();
|
||||
|
||||
$this->createOrUpdateCustomer($customer, $event, $dispatcher);
|
||||
|
||||
if ($event->getNotifyCustomerOfAccountCreation()) {
|
||||
$this->mailer->sendEmailToCustomer(
|
||||
'customer_account_created',
|
||||
$customer,
|
||||
[ 'password' => $plainPassword ]
|
||||
);
|
||||
}
|
||||
|
||||
$dispatcher->dispatch(
|
||||
TheliaEvents::SEND_ACCOUNT_CONFIRMATION_EMAIL,
|
||||
new CustomerEvent($customer)
|
||||
);
|
||||
}
|
||||
|
||||
public function customerConfirmationEmail(CustomerEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
$customer = $event->getCustomer();
|
||||
|
||||
if (ConfigQuery::isCustomerEmailConfirmationEnable() && $customer->getConfirmationToken() !== null) {
|
||||
$this->mailer->sendEmailToCustomer(
|
||||
'customer_confirmation',
|
||||
$customer,
|
||||
['customer_id' => $customer->getId()]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param CustomerCreateOrUpdateEvent $event
|
||||
* @param $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
* @throws \Propel\Runtime\Exception\PropelException
|
||||
*/
|
||||
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 ($event->getNotifyCustomerOfAccountModification() && (! empty($plainPassword) || $emailChanged)) {
|
||||
$this->mailer->sendEmailToCustomer('customer_account_changed', $customer, ['password' => $plainPassword]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param CustomerCreateOrUpdateEvent $event
|
||||
* @param $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
* @throws \Propel\Runtime\Exception\PropelException
|
||||
*/
|
||||
public function updateProfile(CustomerCreateOrUpdateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
$customer = $event->getCustomer();
|
||||
|
||||
$customer->setDispatcher($dispatcher);
|
||||
|
||||
if ($event->getTitle() !== null) {
|
||||
$customer->setTitleId($event->getTitle());
|
||||
}
|
||||
|
||||
if ($event->getFirstname() !== null) {
|
||||
$customer->setFirstname($event->getFirstname());
|
||||
}
|
||||
|
||||
if ($event->getLastname() !== null) {
|
||||
$customer->setLastname($event->getLastname());
|
||||
}
|
||||
|
||||
if ($event->getEmail() !== null) {
|
||||
$customer->setEmail($event->getEmail(), $event->getEmailUpdateAllowed());
|
||||
}
|
||||
|
||||
if ($event->getPassword() !== null) {
|
||||
$customer->setPassword($event->getPassword());
|
||||
}
|
||||
|
||||
if ($event->getReseller() !== null) {
|
||||
$customer->setReseller($event->getReseller());
|
||||
}
|
||||
|
||||
if ($event->getSponsor() !== null) {
|
||||
$customer->setSponsor($event->getSponsor());
|
||||
}
|
||||
|
||||
if ($event->getDiscount() !== null) {
|
||||
$customer->setDiscount($event->getDiscount());
|
||||
}
|
||||
|
||||
if ($event->getLangId() !== null) {
|
||||
$customer->setLangId($event->getLangId());
|
||||
}
|
||||
|
||||
$customer->save();
|
||||
|
||||
$event->setCustomer($customer);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param CustomerEvent $event
|
||||
* @throws \Propel\Runtime\Exception\PropelException
|
||||
*/
|
||||
public function delete(CustomerEvent $event)
|
||||
{
|
||||
if (null !== $customer = $event->getCustomer()) {
|
||||
if (true === $customer->hasOrder()) {
|
||||
throw new CustomerException(Translator::getInstance()->trans("Impossible to delete a customer who already have orders"));
|
||||
}
|
||||
|
||||
$customer->delete();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param CustomerModel $customer
|
||||
* @param CustomerCreateOrUpdateEvent $event
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
* @throws \Propel\Runtime\Exception\PropelException
|
||||
*/
|
||||
private function createOrUpdateCustomer(CustomerModel $customer, CustomerCreateOrUpdateEvent $event, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
$customer->setDispatcher($dispatcher);
|
||||
|
||||
$customer->createOrUpdate(
|
||||
$event->getTitle(),
|
||||
$event->getFirstname(),
|
||||
$event->getLastname(),
|
||||
$event->getAddress1(),
|
||||
$event->getAddress2(),
|
||||
$event->getAddress3(),
|
||||
$event->getPhone(),
|
||||
$event->getCellphone(),
|
||||
$event->getZipcode(),
|
||||
$event->getCity(),
|
||||
$event->getCountry(),
|
||||
$event->getEmail(),
|
||||
$event->getPassword(),
|
||||
$event->getLangId(),
|
||||
$event->getReseller(),
|
||||
$event->getSponsor(),
|
||||
$event->getDiscount(),
|
||||
$event->getCompany(),
|
||||
$event->getRef(),
|
||||
$event->getEmailUpdateAllowed(),
|
||||
$event->getState()
|
||||
);
|
||||
|
||||
$event->setCustomer($customer);
|
||||
}
|
||||
|
||||
public function login(CustomerLoginEvent $event)
|
||||
{
|
||||
$customer = $event->getCustomer();
|
||||
|
||||
if (method_exists($customer, 'clearDispatcher')) {
|
||||
$customer->clearDispatcher();
|
||||
}
|
||||
|
||||
$this->securityContext->setCustomerUser($event->getCustomer());
|
||||
|
||||
// Set the preferred customer language
|
||||
if (null !== $this->requestStack
|
||||
&&
|
||||
! empty($customer->getLangId())
|
||||
&&
|
||||
(null !== $lang = LangQuery::create()->findPk($customer->getLangId()))
|
||||
) {
|
||||
$this->requestStack->getCurrentRequest()->getSession()->setLang($lang);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform user logout. The user is redirected to the provided view, if any.
|
||||
*
|
||||
* @param ActionEvent $event
|
||||
*/
|
||||
public function logout(/** @noinspection PhpUnusedParameterInspection */ ActionEvent $event)
|
||||
{
|
||||
$this->securityContext->clearCustomerUser();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param LostPasswordEvent $event
|
||||
* @throws \Propel\Runtime\Exception\PropelException
|
||||
*/
|
||||
public function lostPassword(LostPasswordEvent $event)
|
||||
{
|
||||
if (null !== $customer = CustomerQuery::create()->filterByEmail($event->getEmail())->findOne()) {
|
||||
$password = Password::generateRandom(8);
|
||||
|
||||
$customer
|
||||
->setPassword($password)
|
||||
->save()
|
||||
;
|
||||
|
||||
$this->mailer->sendEmailToCustomer('lost_password', $customer, ['password' => $password]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
TheliaEvents::CUSTOMER_CREATEACCOUNT => array('create', 128),
|
||||
TheliaEvents::CUSTOMER_UPDATEACCOUNT => array('modify', 128),
|
||||
TheliaEvents::CUSTOMER_UPDATEPROFILE => array('updateProfile', 128),
|
||||
TheliaEvents::CUSTOMER_LOGOUT => array('logout', 128),
|
||||
TheliaEvents::CUSTOMER_LOGIN => array('login', 128),
|
||||
TheliaEvents::CUSTOMER_DELETEACCOUNT => array('delete', 128),
|
||||
TheliaEvents::LOST_PASSWORD => array('lostPassword', 128),
|
||||
TheliaEvents::SEND_ACCOUNT_CONFIRMATION_EMAIL => array('customerConfirmationEmail', 128)
|
||||
);
|
||||
}
|
||||
}
|
||||
111
core/lib/Thelia/Action/CustomerTitle.php
Normal file
111
core/lib/Thelia/Action/CustomerTitle.php
Normal 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"),
|
||||
);
|
||||
}
|
||||
}
|
||||
79
core/lib/Thelia/Action/Delivery.php
Normal file
79
core/lib/Thelia/Action/Delivery.php
Normal file
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
/*************************************************************************************/
|
||||
/* This file is part of the Thelia package. */
|
||||
/* */
|
||||
/* Copyright (c) OpenStudio */
|
||||
/* email : dev@thelia.net */
|
||||
/* web : http://www.thelia.net */
|
||||
/* */
|
||||
/* For the full copyright and license information, please view the LICENSE.txt */
|
||||
/* file that was distributed with this source code. */
|
||||
/*************************************************************************************/
|
||||
|
||||
namespace Thelia\Action;
|
||||
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Thelia\Core\Event\Delivery\DeliveryPostageEvent;
|
||||
use Thelia\Core\Event\TheliaEvents;
|
||||
use Thelia\Module\AbstractDeliveryModule;
|
||||
use Thelia\Module\DeliveryModuleWithStateInterface;
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
/** @var AbstractDeliveryModule $module */
|
||||
$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;
|
||||
}
|
||||
|
||||
// Add state param to isValidDelivery only if module handle state
|
||||
$isValidModule = $module instanceof DeliveryModuleWithStateInterface
|
||||
? $module->isValidDelivery($event->getCountry(), $event->getState())
|
||||
: $module->isValidDelivery($event->getCountry());
|
||||
|
||||
$event->setValidModule($isValidModule)
|
||||
->setDeliveryMode($module->getDeliveryMode());
|
||||
|
||||
if ($event->isValidModule()) {
|
||||
// Add state param to getPostage only if module handle state
|
||||
$modulePostage = $module instanceof DeliveryModuleWithStateInterface
|
||||
? $module->getPostage($event->getCountry(), $event->getState())
|
||||
: $module->getPostage($event->getCountry());
|
||||
|
||||
$event->setPostage($modulePostage);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return [
|
||||
TheliaEvents::MODULE_DELIVERY_GET_POSTAGE => ['getPostage', 128]
|
||||
];
|
||||
}
|
||||
}
|
||||
125
core/lib/Thelia/Action/Document.php
Normal file
125
core/lib/Thelia/Action/Document.php
Normal file
@@ -0,0 +1,125 @@
|
||||
<?php
|
||||
/*************************************************************************************/
|
||||
/* This file is part of the Thelia package. */
|
||||
/* */
|
||||
/* Copyright (c) OpenStudio */
|
||||
/* email : dev@thelia.net */
|
||||
/* web : http://www.thelia.net */
|
||||
/* */
|
||||
/* For the full copyright and license information, please view the LICENSE.txt */
|
||||
/* file that was distributed with this source code. */
|
||||
/*************************************************************************************/
|
||||
namespace Thelia\Action;
|
||||
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Thelia\Core\Event\Document\DocumentEvent;
|
||||
use Thelia\Core\Event\TheliaEvents;
|
||||
use Thelia\Exception\DocumentException;
|
||||
use Thelia\Model\ConfigQuery;
|
||||
use Thelia\Tools\URL;
|
||||
|
||||
/**
|
||||
*
|
||||
* Document management actions. This class handles document processing an caching.
|
||||
*
|
||||
* Basically, documents are stored outside the web space (by default in local/media/documents),
|
||||
* and cached in the web space (by default in web/local/documents).
|
||||
*
|
||||
* In the documents caches directory, a subdirectory for documents categories (eg. product, category, folder, etc.) is
|
||||
* automatically created, and the cached document is created here. Plugin may use their own subdirectory as required.
|
||||
*
|
||||
* The cached document name contains a hash of the processing options, and the original (normalized) name of the document.
|
||||
*
|
||||
* A copy (or symbolic link, by default) of the original document is always created in the cache, so that the full
|
||||
* resolution document is always available.
|
||||
*
|
||||
* If a problem occurs, an DocumentException may be thrown.
|
||||
*
|
||||
* @package Thelia\Action
|
||||
* @author Franck Allimant <franck@cqfdev.fr>
|
||||
*
|
||||
*/
|
||||
class Document extends BaseCachedFile implements EventSubscriberInterface
|
||||
{
|
||||
/**
|
||||
* @var string Config key for document delivery mode
|
||||
*/
|
||||
const CONFIG_DELIVERY_MODE = 'original_document_delivery_mode';
|
||||
|
||||
/**
|
||||
* @return string root of the document cache directory in web space
|
||||
*/
|
||||
protected function getCacheDirFromWebRoot()
|
||||
{
|
||||
return ConfigQuery::read('document_cache_dir_from_web_root', 'cache' . DS . 'documents');
|
||||
}
|
||||
|
||||
/**
|
||||
* Process document and write the result in the document cache.
|
||||
*
|
||||
* When the original document is required, create either a symbolic link with the
|
||||
* original document in the cache dir, or copy it in the cache dir if it's not already done.
|
||||
*
|
||||
* This method updates the cache_file_path and file_url attributes of the event
|
||||
*
|
||||
* @param DocumentEvent $event Event
|
||||
*
|
||||
* @throws \Thelia\Exception\DocumentException
|
||||
* @throws \InvalidArgumentException , DocumentException
|
||||
*/
|
||||
public function processDocument(DocumentEvent $event)
|
||||
{
|
||||
$subdir = $event->getCacheSubdirectory();
|
||||
$sourceFile = $event->getSourceFilepath();
|
||||
|
||||
if (null == $subdir || null == $sourceFile) {
|
||||
throw new \InvalidArgumentException("Cache sub-directory and source file path cannot be null");
|
||||
}
|
||||
|
||||
$originalDocumentPathInCache = $this->getCacheFilePath($subdir, $sourceFile, true);
|
||||
|
||||
if (! file_exists($originalDocumentPathInCache)) {
|
||||
if (! file_exists($sourceFile)) {
|
||||
throw new DocumentException(sprintf("Source document file %s does not exists.", $sourceFile));
|
||||
}
|
||||
|
||||
$mode = ConfigQuery::read(self::CONFIG_DELIVERY_MODE, 'symlink');
|
||||
|
||||
if ($mode == 'symlink') {
|
||||
if (false === symlink($sourceFile, $originalDocumentPathInCache)) {
|
||||
throw new DocumentException(sprintf("Failed to create symbolic link for %s in %s document cache directory", basename($sourceFile), $subdir));
|
||||
}
|
||||
} else {
|
||||
// mode = 'copy'
|
||||
if (false === @copy($sourceFile, $originalDocumentPathInCache)) {
|
||||
throw new DocumentException(sprintf("Failed to copy %s in %s document cache directory", basename($sourceFile), $subdir));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Compute the document URL
|
||||
$documentUrl = $this->getCacheFileURL($subdir, basename($originalDocumentPathInCache));
|
||||
|
||||
// Update the event with file path and file URL
|
||||
$event->setDocumentPath($documentUrl);
|
||||
$event->setDocumentUrl(URL::getInstance()->absoluteUrl($documentUrl, null, URL::PATH_TO_FILE, $this->cdnBaseUrl));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
TheliaEvents::DOCUMENT_PROCESS => array("processDocument", 128),
|
||||
|
||||
// Implemented in parent class BaseCachedFile
|
||||
TheliaEvents::DOCUMENT_CLEAR_CACHE => array("clearCache", 128),
|
||||
TheliaEvents::DOCUMENT_DELETE => array("deleteFile", 128),
|
||||
TheliaEvents::DOCUMENT_SAVE => array("saveFile", 128),
|
||||
TheliaEvents::DOCUMENT_UPDATE => array("updateFile", 128),
|
||||
TheliaEvents::DOCUMENT_UPDATE_POSITION => array("updatePosition", 128),
|
||||
TheliaEvents::DOCUMENT_TOGGLE_VISIBILITY => array("toggleVisibility", 128),
|
||||
);
|
||||
}
|
||||
}
|
||||
79
core/lib/Thelia/Action/Export.php
Normal file
79
core/lib/Thelia/Action/Export.php
Normal file
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
/*************************************************************************************/
|
||||
/* This file is part of the Thelia package. */
|
||||
/* */
|
||||
/* Copyright (c) OpenStudio */
|
||||
/* email : dev@thelia.net */
|
||||
/* web : http://www.thelia.net */
|
||||
/* */
|
||||
/* For the full copyright and license information, please view the LICENSE.txt */
|
||||
/* file that was distributed with this source code. */
|
||||
/*************************************************************************************/
|
||||
|
||||
namespace Thelia\Action;
|
||||
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Thelia\Core\Event\TheliaEvents;
|
||||
use Thelia\Core\Event\UpdatePositionEvent;
|
||||
use Thelia\Handler\ExportHandler;
|
||||
use Thelia\Model\ExportCategoryQuery;
|
||||
use Thelia\Model\ExportQuery;
|
||||
|
||||
/**
|
||||
* Class Export
|
||||
* @author Jérôme Billiras <jbilliras@openstudio.fr>
|
||||
*/
|
||||
class Export extends BaseAction implements EventSubscriberInterface
|
||||
{
|
||||
/**
|
||||
* @var \Thelia\Handler\ExportHandler The export handler
|
||||
*/
|
||||
protected $handler;
|
||||
|
||||
/**
|
||||
* @param \Thelia\Handler\ExportHandler $exportHandler The export handler
|
||||
*/
|
||||
public function __construct(ExportHandler $exportHandler)
|
||||
{
|
||||
$this->handler = $exportHandler;
|
||||
}
|
||||
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return [
|
||||
TheliaEvents::EXPORT_CHANGE_POSITION => [
|
||||
['exportChangePosition', 128]
|
||||
],
|
||||
TheliaEvents::EXPORT_CATEGORY_CHANGE_POSITION => [
|
||||
['exportCategoryChangePosition', 128]
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle export change position event
|
||||
*
|
||||
* @param UpdatePositionEvent $updatePositionEvent
|
||||
* @param $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function exportChangePosition(UpdatePositionEvent $updatePositionEvent, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
$this->handler->getExport($updatePositionEvent->getObjectId(), true);
|
||||
$this->genericUpdatePosition(new ExportQuery, $updatePositionEvent, $dispatcher);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle export category change position event
|
||||
*
|
||||
* @param UpdatePositionEvent $updatePositionEvent
|
||||
* @param $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function exportCategoryChangePosition(UpdatePositionEvent $updatePositionEvent, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
$this->handler->getCategory($updatePositionEvent->getObjectId(), true);
|
||||
$this->genericUpdatePosition(new ExportCategoryQuery, $updatePositionEvent, $dispatcher);
|
||||
}
|
||||
}
|
||||
159
core/lib/Thelia/Action/Feature.php
Normal file
159
core/lib/Thelia/Action/Feature.php
Normal file
@@ -0,0 +1,159 @@
|
||||
<?php
|
||||
/*************************************************************************************/
|
||||
/* This file is part of the Thelia package. */
|
||||
/* */
|
||||
/* Copyright (c) OpenStudio */
|
||||
/* email : dev@thelia.net */
|
||||
/* web : http://www.thelia.net */
|
||||
/* */
|
||||
/* For the full copyright and license information, please view the LICENSE.txt */
|
||||
/* file that was distributed with this source code. */
|
||||
/*************************************************************************************/
|
||||
|
||||
namespace Thelia\Action;
|
||||
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Thelia\Model\FeatureQuery;
|
||||
use Thelia\Model\Feature as FeatureModel;
|
||||
use Thelia\Core\Event\TheliaEvents;
|
||||
use Thelia\Core\Event\Feature\FeatureUpdateEvent;
|
||||
use Thelia\Core\Event\Feature\FeatureCreateEvent;
|
||||
use Thelia\Core\Event\Feature\FeatureDeleteEvent;
|
||||
use Thelia\Core\Event\UpdatePositionEvent;
|
||||
use Thelia\Core\Event\Feature\FeatureEvent;
|
||||
use Thelia\Model\FeatureTemplate;
|
||||
use Thelia\Model\FeatureTemplateQuery;
|
||||
use Thelia\Model\TemplateQuery;
|
||||
|
||||
class Feature extends BaseAction implements EventSubscriberInterface
|
||||
{
|
||||
/**
|
||||
* Create a new feature entry
|
||||
*
|
||||
* @param \Thelia\Core\Event\Feature\FeatureCreateEvent $event
|
||||
* @param $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function create(FeatureCreateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
$feature = new FeatureModel();
|
||||
|
||||
$feature
|
||||
->setDispatcher($dispatcher)
|
||||
|
||||
->setLocale($event->getLocale())
|
||||
->setTitle($event->getTitle())
|
||||
|
||||
->save()
|
||||
;
|
||||
|
||||
$event->setFeature($feature);
|
||||
|
||||
// Add atribute to all product templates if required
|
||||
if ($event->getAddToAllTemplates() != 0) {
|
||||
$this->doAddToAllTemplates($feature);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Change a product feature
|
||||
*
|
||||
* @param \Thelia\Core\Event\Feature\FeatureUpdateEvent $event
|
||||
* @param $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function update(FeatureUpdateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
if (null !== $feature = FeatureQuery::create()->findPk($event->getFeatureId())) {
|
||||
$feature
|
||||
->setDispatcher($dispatcher)
|
||||
|
||||
->setLocale($event->getLocale())
|
||||
->setTitle($event->getTitle())
|
||||
->setDescription($event->getDescription())
|
||||
->setChapo($event->getChapo())
|
||||
->setPostscriptum($event->getPostscriptum())
|
||||
|
||||
->save();
|
||||
|
||||
$event->setFeature($feature);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a product feature entry
|
||||
*
|
||||
* @param FeatureDeleteEvent $event
|
||||
* @param $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function delete(FeatureDeleteEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
if (null !== ($feature = FeatureQuery::create()->findPk($event->getFeatureId()))) {
|
||||
$feature
|
||||
->setDispatcher($dispatcher)
|
||||
->delete()
|
||||
;
|
||||
|
||||
$event->setFeature($feature);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes position, selecting absolute ou relative change.
|
||||
*
|
||||
* @param UpdatePositionEvent $event
|
||||
* @param $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function updatePosition(UpdatePositionEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
$this->genericUpdatePosition(FeatureQuery::create(), $event, $dispatcher);
|
||||
}
|
||||
|
||||
protected function doAddToAllTemplates(FeatureModel $feature)
|
||||
{
|
||||
$templates = TemplateQuery::create()->find();
|
||||
|
||||
foreach ($templates as $template) {
|
||||
$feature_template = new FeatureTemplate();
|
||||
|
||||
if (null === FeatureTemplateQuery::create()->filterByFeature($feature)->filterByTemplate($template)->findOne()) {
|
||||
$feature_template
|
||||
->setFeature($feature)
|
||||
->setTemplate($template)
|
||||
->save()
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function addToAllTemplates(FeatureEvent $event)
|
||||
{
|
||||
$this->doAddToAllTemplates($event->getFeature());
|
||||
}
|
||||
|
||||
public function removeFromAllTemplates(FeatureEvent $event)
|
||||
{
|
||||
// Delete this feature from all product templates
|
||||
FeatureTemplateQuery::create()->filterByFeature($event->getFeature())->delete();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
TheliaEvents::FEATURE_CREATE => array("create", 128),
|
||||
TheliaEvents::FEATURE_UPDATE => array("update", 128),
|
||||
TheliaEvents::FEATURE_DELETE => array("delete", 128),
|
||||
TheliaEvents::FEATURE_UPDATE_POSITION => array("updatePosition", 128),
|
||||
|
||||
TheliaEvents::FEATURE_REMOVE_FROM_ALL_TEMPLATES => array("removeFromAllTemplates", 128),
|
||||
TheliaEvents::FEATURE_ADD_TO_ALL_TEMPLATES => array("addToAllTemplates", 128),
|
||||
|
||||
);
|
||||
}
|
||||
}
|
||||
119
core/lib/Thelia/Action/FeatureAv.php
Normal file
119
core/lib/Thelia/Action/FeatureAv.php
Normal file
@@ -0,0 +1,119 @@
|
||||
<?php
|
||||
/*************************************************************************************/
|
||||
/* This file is part of the Thelia package. */
|
||||
/* */
|
||||
/* Copyright (c) OpenStudio */
|
||||
/* email : dev@thelia.net */
|
||||
/* web : http://www.thelia.net */
|
||||
/* */
|
||||
/* For the full copyright and license information, please view the LICENSE.txt */
|
||||
/* file that was distributed with this source code. */
|
||||
/*************************************************************************************/
|
||||
|
||||
namespace Thelia\Action;
|
||||
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Thelia\Model\FeatureAvQuery;
|
||||
use Thelia\Model\FeatureAv as FeatureAvModel;
|
||||
use Thelia\Core\Event\TheliaEvents;
|
||||
use Thelia\Core\Event\Feature\FeatureAvUpdateEvent;
|
||||
use Thelia\Core\Event\Feature\FeatureAvCreateEvent;
|
||||
use Thelia\Core\Event\Feature\FeatureAvDeleteEvent;
|
||||
use Thelia\Core\Event\UpdatePositionEvent;
|
||||
|
||||
class FeatureAv extends BaseAction implements EventSubscriberInterface
|
||||
{
|
||||
/**
|
||||
* Create a new feature entry
|
||||
*
|
||||
* @param FeatureAvCreateEvent $event
|
||||
* @param $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function create(FeatureAvCreateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
$feature = new FeatureAvModel();
|
||||
|
||||
$feature
|
||||
->setDispatcher($dispatcher)
|
||||
|
||||
->setFeatureId($event->getFeatureId())
|
||||
->setLocale($event->getLocale())
|
||||
->setTitle($event->getTitle())
|
||||
|
||||
->save()
|
||||
;
|
||||
|
||||
$event->setFeatureAv($feature);
|
||||
}
|
||||
|
||||
/**
|
||||
* Change a product feature
|
||||
*
|
||||
* @param FeatureAvUpdateEvent $event
|
||||
* @param $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function update(FeatureAvUpdateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
if (null !== $feature = FeatureAvQuery::create()->findPk($event->getFeatureAvId())) {
|
||||
$feature
|
||||
->setDispatcher($dispatcher)
|
||||
|
||||
->setLocale($event->getLocale())
|
||||
->setTitle($event->getTitle())
|
||||
->setDescription($event->getDescription())
|
||||
->setChapo($event->getChapo())
|
||||
->setPostscriptum($event->getPostscriptum())
|
||||
|
||||
->save();
|
||||
|
||||
$event->setFeatureAv($feature);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a product feature entry
|
||||
*
|
||||
* @param FeatureAvDeleteEvent $event
|
||||
* @param $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function delete(FeatureAvDeleteEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
if (null !== ($feature = FeatureAvQuery::create()->findPk($event->getFeatureAvId()))) {
|
||||
$feature
|
||||
->setDispatcher($dispatcher)
|
||||
->delete()
|
||||
;
|
||||
|
||||
$event->setFeatureAv($feature);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes position, selecting absolute ou relative change.
|
||||
*
|
||||
* @param UpdatePositionEvent $event
|
||||
* @param $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function updatePosition(UpdatePositionEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
$this->genericUpdatePosition(FeatureAvQuery::create(), $event, $dispatcher);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
TheliaEvents::FEATURE_AV_CREATE => array("create", 128),
|
||||
TheliaEvents::FEATURE_AV_UPDATE => array("update", 128),
|
||||
TheliaEvents::FEATURE_AV_DELETE => array("delete", 128),
|
||||
TheliaEvents::FEATURE_AV_UPDATE_POSITION => array("updatePosition", 128),
|
||||
);
|
||||
}
|
||||
}
|
||||
175
core/lib/Thelia/Action/File.php
Normal file
175
core/lib/Thelia/Action/File.php
Normal 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)
|
||||
);
|
||||
}
|
||||
}
|
||||
208
core/lib/Thelia/Action/Folder.php
Normal file
208
core/lib/Thelia/Action/Folder.php
Normal file
@@ -0,0 +1,208 @@
|
||||
<?php
|
||||
/*************************************************************************************/
|
||||
/* This file is part of the Thelia package. */
|
||||
/* */
|
||||
/* Copyright (c) OpenStudio */
|
||||
/* email : dev@thelia.net */
|
||||
/* web : http://www.thelia.net */
|
||||
/* */
|
||||
/* For the full copyright and license information, please view the LICENSE.txt */
|
||||
/* file that was distributed with this source code. */
|
||||
/*************************************************************************************/
|
||||
|
||||
namespace Thelia\Action;
|
||||
|
||||
use Propel\Runtime\Propel;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
use Thelia\Core\Event\File\FileDeleteEvent;
|
||||
use Thelia\Core\Event\Folder\FolderCreateEvent;
|
||||
use Thelia\Core\Event\Folder\FolderDeleteEvent;
|
||||
use Thelia\Core\Event\Folder\FolderToggleVisibilityEvent;
|
||||
use Thelia\Core\Event\Folder\FolderUpdateEvent;
|
||||
use Thelia\Core\Event\TheliaEvents;
|
||||
use Thelia\Core\Event\UpdatePositionEvent;
|
||||
use Thelia\Core\Event\UpdateSeoEvent;
|
||||
use Thelia\Core\Event\ViewCheckEvent;
|
||||
use Thelia\Model\FolderDocumentQuery;
|
||||
use Thelia\Model\FolderImageQuery;
|
||||
use Thelia\Model\FolderQuery;
|
||||
use Thelia\Model\Folder as FolderModel;
|
||||
use Thelia\Model\Map\FolderTableMap;
|
||||
|
||||
/**
|
||||
* Class Folder
|
||||
* @package Thelia\Action
|
||||
* @author Manuel Raynaud <manu@raynaud.io>
|
||||
*/
|
||||
class Folder extends BaseAction implements EventSubscriberInterface
|
||||
{
|
||||
public function update(FolderUpdateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
if (null !== $folder = FolderQuery::create()->findPk($event->getFolderId())) {
|
||||
$folder->setDispatcher($dispatcher);
|
||||
|
||||
$folder
|
||||
->setParent($event->getParent())
|
||||
->setVisible($event->getVisible())
|
||||
->setLocale($event->getLocale())
|
||||
->setTitle($event->getTitle())
|
||||
->setDescription($event->getDescription())
|
||||
->setChapo($event->getChapo())
|
||||
->setPostscriptum($event->getPostscriptum())
|
||||
->save();
|
||||
;
|
||||
|
||||
$event->setFolder($folder);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Change Folder SEO
|
||||
*
|
||||
* @param UpdateSeoEvent $event
|
||||
* @param $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
* @return Object
|
||||
*/
|
||||
public function updateSeo(UpdateSeoEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
return $this->genericUpdateSeo(FolderQuery::create(), $event, $dispatcher);
|
||||
}
|
||||
|
||||
public function delete(FolderDeleteEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
if (null !== $folder = FolderQuery::create()->findPk($event->getFolderId())) {
|
||||
$con = Propel::getWriteConnection(FolderTableMap::DATABASE_NAME);
|
||||
$con->beginTransaction();
|
||||
|
||||
try {
|
||||
$fileList = ['images' => [], 'documentList' => []];
|
||||
|
||||
// Get folder's files to delete after folder deletion
|
||||
$fileList['images']['list'] = FolderImageQuery::create()
|
||||
->findByFolderId($event->getFolderId());
|
||||
$fileList['images']['type'] = TheliaEvents::IMAGE_DELETE;
|
||||
|
||||
$fileList['documentList']['list'] = FolderDocumentQuery::create()
|
||||
->findByFolderId($event->getFolderId());
|
||||
$fileList['documentList']['type'] = TheliaEvents::DOCUMENT_DELETE;
|
||||
|
||||
// Delete folder
|
||||
$folder->setDispatcher($dispatcher)
|
||||
->delete($con);
|
||||
|
||||
$event->setFolder($folder);
|
||||
|
||||
// Dispatch delete folder's files event
|
||||
foreach ($fileList as $fileTypeList) {
|
||||
foreach ($fileTypeList['list'] as $fileToDelete) {
|
||||
$fileDeleteEvent = new FileDeleteEvent($fileToDelete);
|
||||
$dispatcher->dispatch($fileTypeList['type'], $fileDeleteEvent);
|
||||
}
|
||||
}
|
||||
|
||||
$con->commit();
|
||||
} catch (\Exception $e) {
|
||||
$con->rollback();
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function create(FolderCreateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
$folder = new FolderModel();
|
||||
$folder->setDispatcher($dispatcher);
|
||||
|
||||
$folder
|
||||
->setParent($event->getParent())
|
||||
->setVisible($event->getVisible())
|
||||
->setLocale($event->getLocale())
|
||||
->setTitle($event->getTitle())
|
||||
->save();
|
||||
|
||||
$event->setFolder($folder);
|
||||
}
|
||||
|
||||
public function toggleVisibility(FolderToggleVisibilityEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
$folder = $event->getFolder();
|
||||
|
||||
$folder
|
||||
->setDispatcher($dispatcher)
|
||||
->setVisible(!$folder->getVisible())
|
||||
->save();
|
||||
|
||||
$event->setFolder($folder);
|
||||
}
|
||||
|
||||
public function updatePosition(UpdatePositionEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
if (null !== $folder = FolderQuery::create()->findPk($event->getObjectId())) {
|
||||
$folder->setDispatcher($dispatcher);
|
||||
|
||||
switch ($event->getMode()) {
|
||||
case UpdatePositionEvent::POSITION_ABSOLUTE:
|
||||
$folder->changeAbsolutePosition($event->getPosition());
|
||||
break;
|
||||
case UpdatePositionEvent::POSITION_DOWN:
|
||||
$folder->movePositionDown();
|
||||
break;
|
||||
case UpdatePositionEvent::POSITION_UP:
|
||||
$folder->movePositionUp();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if is a folder view and if folder_id is visible
|
||||
*
|
||||
* @param ViewCheckEvent $event
|
||||
* @param string $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function viewCheck(ViewCheckEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
if ($event->getView() == 'folder') {
|
||||
$folder = FolderQuery::create()
|
||||
->filterById($event->getViewId())
|
||||
->filterByVisible(1)
|
||||
->count();
|
||||
|
||||
if ($folder == 0) {
|
||||
$dispatcher->dispatch(TheliaEvents::VIEW_FOLDER_ID_NOT_VISIBLE, $event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ViewCheckEvent $event
|
||||
* @throws NotFoundHttpException
|
||||
*/
|
||||
public function viewFolderIdNotVisible(ViewCheckEvent $event)
|
||||
{
|
||||
throw new NotFoundHttpException();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
TheliaEvents::FOLDER_CREATE => array("create", 128),
|
||||
TheliaEvents::FOLDER_UPDATE => array("update", 128),
|
||||
TheliaEvents::FOLDER_DELETE => array("delete", 128),
|
||||
TheliaEvents::FOLDER_TOGGLE_VISIBILITY => array("toggleVisibility", 128),
|
||||
|
||||
TheliaEvents::FOLDER_UPDATE_POSITION => array("updatePosition", 128),
|
||||
TheliaEvents::FOLDER_UPDATE_SEO => array('updateSeo', 128),
|
||||
|
||||
TheliaEvents::VIEW_CHECK => array('viewCheck', 128),
|
||||
TheliaEvents::VIEW_FOLDER_ID_NOT_VISIBLE => array('viewFolderIdNotVisible', 128),
|
||||
);
|
||||
}
|
||||
}
|
||||
168
core/lib/Thelia/Action/Hook.php
Normal file
168
core/lib/Thelia/Action/Hook.php
Normal 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),
|
||||
|
||||
);
|
||||
}
|
||||
}
|
||||
114
core/lib/Thelia/Action/HttpException.php
Normal file
114
core/lib/Thelia/Action/HttpException.php
Normal file
@@ -0,0 +1,114 @@
|
||||
<?php
|
||||
/*************************************************************************************/
|
||||
/* This file is part of the Thelia package. */
|
||||
/* */
|
||||
/* Copyright (c) OpenStudio */
|
||||
/* email : dev@thelia.net */
|
||||
/* web : http://www.thelia.net */
|
||||
/* */
|
||||
/* For the full copyright and license information, please view the LICENSE.txt */
|
||||
/* file that was distributed with this source code. */
|
||||
/*************************************************************************************/
|
||||
|
||||
namespace Thelia\Action;
|
||||
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
|
||||
use Symfony\Component\HttpKernel\Exception\HttpException as BaseHttpException;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
use Symfony\Component\HttpKernel\KernelEvents;
|
||||
use Thelia\Core\HttpFoundation\Response;
|
||||
use Thelia\Core\Template\ParserInterface;
|
||||
use Thelia\Exception\AdminAccessDenied;
|
||||
use Thelia\Model\ConfigQuery;
|
||||
|
||||
/**
|
||||
*
|
||||
* Class HttpException
|
||||
* @package Thelia\Action
|
||||
* @author Etienne Roudeix <eroudeix@openstudio.fr>
|
||||
* @author Manuel Raynaud <manu@raynaud.io>
|
||||
*/
|
||||
class HttpException extends BaseAction implements EventSubscriberInterface
|
||||
{
|
||||
/** @var ParserInterface */
|
||||
protected $parser;
|
||||
|
||||
public function __construct(ParserInterface $parser)
|
||||
{
|
||||
$this->parser = $parser;
|
||||
}
|
||||
|
||||
public function checkHttpException(GetResponseForExceptionEvent $event)
|
||||
{
|
||||
$exception = $event->getException();
|
||||
if ($exception instanceof NotFoundHttpException) {
|
||||
$this->display404($event);
|
||||
}
|
||||
|
||||
if ($exception instanceof AdminAccessDenied) {
|
||||
$this->displayAdminGeneralError($event);
|
||||
}
|
||||
|
||||
if ($exception instanceof BaseHttpException && null === $event->getResponse()) {
|
||||
$this->displayException($event);
|
||||
}
|
||||
}
|
||||
|
||||
protected function displayAdminGeneralError(GetResponseForExceptionEvent $event)
|
||||
{
|
||||
// Define the template thant shoud be used
|
||||
$this->parser->setTemplateDefinition(
|
||||
$this->parser->getTemplateHelper()->getActiveAdminTemplate()
|
||||
);
|
||||
|
||||
$message = $event->getException()->getMessage();
|
||||
|
||||
$response = Response::create(
|
||||
$this->parser->render(
|
||||
'general_error.html',
|
||||
array(
|
||||
"error_message" => $message
|
||||
)
|
||||
),
|
||||
403
|
||||
);
|
||||
|
||||
$event->setResponse($response);
|
||||
}
|
||||
|
||||
protected function display404(GetResponseForExceptionEvent $event)
|
||||
{
|
||||
// Define the template thant shoud be used
|
||||
$this->parser->setTemplateDefinition(
|
||||
$this->parser->getTemplateHelper()->getActiveFrontTemplate()
|
||||
);
|
||||
|
||||
$response = new Response($this->parser->render(ConfigQuery::getPageNotFoundView()), 404);
|
||||
|
||||
$event->setResponse($response);
|
||||
}
|
||||
|
||||
protected function displayException(GetResponseForExceptionEvent $event)
|
||||
{
|
||||
/** @var \Symfony\Component\HttpKernel\Exception\HttpException $exception */
|
||||
$exception = $event->getException();
|
||||
$event->setResponse(
|
||||
new Response(
|
||||
$exception->getMessage(),
|
||||
$exception->getStatusCode(),
|
||||
$exception->getHeaders()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
KernelEvents::EXCEPTION => ["checkHttpException", 128],
|
||||
);
|
||||
}
|
||||
}
|
||||
431
core/lib/Thelia/Action/Image.php
Normal file
431
core/lib/Thelia/Action/Image.php
Normal file
@@ -0,0 +1,431 @@
|
||||
<?php
|
||||
/*************************************************************************************/
|
||||
/* This file is part of the Thelia package. */
|
||||
/* */
|
||||
/* Copyright (c) OpenStudio */
|
||||
/* email : dev@thelia.net */
|
||||
/* web : http://www.thelia.net */
|
||||
/* */
|
||||
/* For the full copyright and license information, please view the LICENSE.txt */
|
||||
/* file that was distributed with this source code. */
|
||||
/*************************************************************************************/
|
||||
|
||||
namespace Thelia\Action;
|
||||
|
||||
use Imagine\Image\Box;
|
||||
use Imagine\Image\ImageInterface;
|
||||
use Imagine\Image\ImagineInterface;
|
||||
use Imagine\Image\Palette\RGB;
|
||||
use Imagine\Image\Point;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Thelia\Core\Event\Image\ImageEvent;
|
||||
use Thelia\Core\Event\TheliaEvents;
|
||||
use Thelia\Exception\ImageException;
|
||||
use Thelia\Files\FileManager;
|
||||
use Thelia\Model\ConfigQuery;
|
||||
use Thelia\Tools\URL;
|
||||
use Imagine\Imagick\Imagine as ImagickImagine;
|
||||
use Imagine\Gmagick\Imagine as GmagickImagine;
|
||||
use Imagine\Gd\Imagine;
|
||||
|
||||
/**
|
||||
*
|
||||
* Image management actions. This class handles image processing and caching.
|
||||
*
|
||||
* Basically, images are stored outside of the web space (by default in local/media/images),
|
||||
* and cached inside the web space (by default in web/local/images).
|
||||
*
|
||||
* In the images caches directory, a subdirectory for images categories (eg. product, category, folder, etc.) is
|
||||
* automatically created, and the cached image is created here. Plugin may use their own subdirectory as required.
|
||||
*
|
||||
* The cached image name contains a hash of the processing options, and the original (normalized) name of the image.
|
||||
*
|
||||
* A copy (or symbolic link, by default) of the original image is always created in the cache, so that the full
|
||||
* resolution image is always available.
|
||||
*
|
||||
* Various image processing options are available :
|
||||
*
|
||||
* - resizing, with border, crop, or by keeping image aspect ratio
|
||||
* - rotation, in degrees, positive or negative
|
||||
* - background color, applyed to empty background when creating borders or rotating
|
||||
* - effects. The effects are applied in the specified order. The following effects are available:
|
||||
* - gamma:value : change the image Gamma to the specified value. Example: gamma:0.7
|
||||
* - grayscale or greyscale: switch image to grayscale
|
||||
* - colorize:color : apply a color mask to the image. Exemple: colorize:#ff2244
|
||||
* - negative : transform the image in its negative equivalent
|
||||
* - vflip or vertical_flip : vertical flip
|
||||
* - hflip or horizontal_flip : horizontal flip
|
||||
*
|
||||
* If a problem occurs, an ImageException may be thrown.
|
||||
*
|
||||
* @package Thelia\Action
|
||||
* @author Franck Allimant <franck@cqfdev.fr>
|
||||
*
|
||||
*/
|
||||
class Image extends BaseCachedFile implements EventSubscriberInterface
|
||||
{
|
||||
// Resize mode constants
|
||||
const EXACT_RATIO_WITH_BORDERS = 1;
|
||||
const EXACT_RATIO_WITH_CROP = 2;
|
||||
const KEEP_IMAGE_RATIO = 3;
|
||||
|
||||
/**
|
||||
* @return string root of the image cache directory in web space
|
||||
*/
|
||||
protected function getCacheDirFromWebRoot()
|
||||
{
|
||||
return ConfigQuery::read('image_cache_dir_from_web_root', 'cache' . DS . 'images');
|
||||
}
|
||||
|
||||
/**
|
||||
* Process image and write the result in the image cache.
|
||||
*
|
||||
* If the image already exists in cache, the cache file is immediately returned, without any processing
|
||||
* If the original (full resolution) image is required, create either a symbolic link with the
|
||||
* original image in the cache dir, or copy it in the cache dir.
|
||||
*
|
||||
* This method updates the cache_file_path and file_url attributes of the event
|
||||
*
|
||||
* @param ImageEvent $event
|
||||
* @param string $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
*
|
||||
* @throws \Thelia\Exception\ImageException
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function processImage(ImageEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
$subdir = $event->getCacheSubdirectory();
|
||||
$source_file = $event->getSourceFilepath();
|
||||
|
||||
if (null == $subdir || null == $source_file) {
|
||||
throw new \InvalidArgumentException("Cache sub-directory and source file path cannot be null");
|
||||
}
|
||||
|
||||
// Find cached file path
|
||||
$cacheFilePath = $this->getCacheFilePath($subdir, $source_file, $event->isOriginalImage(), $event->getOptionsHash());
|
||||
|
||||
$originalImagePathInCache = $this->getCacheFilePath($subdir, $source_file, true);
|
||||
|
||||
if (! file_exists($cacheFilePath)) {
|
||||
if (! file_exists($source_file)) {
|
||||
throw new ImageException(sprintf("Source image file %s does not exists.", $source_file));
|
||||
}
|
||||
|
||||
// Create a cached version of the original image in the web space, if not exists
|
||||
|
||||
if (! file_exists($originalImagePathInCache)) {
|
||||
$mode = ConfigQuery::read('original_image_delivery_mode', 'symlink');
|
||||
|
||||
if ($mode == 'symlink') {
|
||||
if (false === symlink($source_file, $originalImagePathInCache)) {
|
||||
throw new ImageException(sprintf("Failed to create symbolic link for %s in %s image cache directory", basename($source_file), $subdir));
|
||||
}
|
||||
} else {
|
||||
// mode = 'copy'
|
||||
if (false === @copy($source_file, $originalImagePathInCache)) {
|
||||
throw new ImageException(sprintf("Failed to copy %s in %s image cache directory", basename($source_file), $subdir));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Process image only if we have some transformations to do.
|
||||
if (! $event->isOriginalImage()) {
|
||||
// We have to process the image.
|
||||
$imagine = $this->createImagineInstance();
|
||||
|
||||
$image = $imagine->open($source_file);
|
||||
|
||||
if ($image) {
|
||||
// Allow image pre-processing (watermarging, or other stuff...)
|
||||
$event->setImageObject($image);
|
||||
$dispatcher->dispatch(TheliaEvents::IMAGE_PREPROCESSING, $event);
|
||||
$image = $event->getImageObject();
|
||||
|
||||
$background_color = $event->getBackgroundColor();
|
||||
|
||||
$palette = new RGB();
|
||||
|
||||
if ($background_color != null) {
|
||||
$bg_color = $palette->color($background_color);
|
||||
} else {
|
||||
// Define a fully transparent white background color
|
||||
$bg_color = $palette->color('fff', 0);
|
||||
}
|
||||
|
||||
// Apply resize
|
||||
$image = $this->applyResize(
|
||||
$imagine,
|
||||
$image,
|
||||
$event->getWidth(),
|
||||
$event->getHeight(),
|
||||
$event->getResizeMode(),
|
||||
$bg_color,
|
||||
$event->getAllowZoom()
|
||||
);
|
||||
|
||||
// Rotate if required
|
||||
$rotation = \intval($event->getRotation());
|
||||
|
||||
if ($rotation != 0) {
|
||||
$image->rotate($rotation, $bg_color);
|
||||
}
|
||||
|
||||
// Flip
|
||||
// Process each effects
|
||||
foreach ($event->getEffects() as $effect) {
|
||||
$effect = trim(strtolower($effect));
|
||||
|
||||
$params = explode(':', $effect);
|
||||
|
||||
switch ($params[0]) {
|
||||
|
||||
case 'greyscale':
|
||||
case 'grayscale':
|
||||
$image->effects()->grayscale();
|
||||
break;
|
||||
|
||||
case 'negative':
|
||||
$image->effects()->negative();
|
||||
break;
|
||||
|
||||
case 'horizontal_flip':
|
||||
case 'hflip':
|
||||
$image->flipHorizontally();
|
||||
break;
|
||||
|
||||
case 'vertical_flip':
|
||||
case 'vflip':
|
||||
$image->flipVertically();
|
||||
break;
|
||||
|
||||
case 'gamma':
|
||||
// Syntax: gamma:value. Exemple: gamma:0.7
|
||||
if (isset($params[1])) {
|
||||
$gamma = \floatval($params[1]);
|
||||
|
||||
$image->effects()->gamma($gamma);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'colorize':
|
||||
// Syntax: colorize:couleur. Exemple: colorize:#ff00cc
|
||||
if (isset($params[1])) {
|
||||
$the_color = $palette->color($params[1]);
|
||||
|
||||
$image->effects()->colorize($the_color);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'blur':
|
||||
if (isset($params[1])) {
|
||||
$blur_level = \intval($params[1]);
|
||||
|
||||
$image->effects()->blur($blur_level);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$quality = $event->getQuality();
|
||||
|
||||
if (\is_null($quality)) {
|
||||
$quality = ConfigQuery::read('default_images_quality_percent', 75);
|
||||
}
|
||||
|
||||
// Allow image post-processing (watermarging, or other stuff...)
|
||||
$event->setImageObject($image);
|
||||
$dispatcher->dispatch(TheliaEvents::IMAGE_POSTPROCESSING, $event);
|
||||
$image = $event->getImageObject();
|
||||
|
||||
$image->save(
|
||||
$cacheFilePath,
|
||||
array('quality' => $quality)
|
||||
);
|
||||
} else {
|
||||
throw new ImageException(sprintf("Source file %s cannot be opened.", basename($source_file)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Compute the image URL
|
||||
$processed_image_url = $this->getCacheFileURL($subdir, basename($cacheFilePath));
|
||||
|
||||
// compute the full resolution image path in cache
|
||||
$original_image_url = $this->getCacheFileURL($subdir, basename($originalImagePathInCache));
|
||||
|
||||
// Update the event with file path and file URL
|
||||
$event->setCacheFilepath($cacheFilePath);
|
||||
$event->setCacheOriginalFilepath($originalImagePathInCache);
|
||||
|
||||
$event->setFileUrl(URL::getInstance()->absoluteUrl($processed_image_url, null, URL::PATH_TO_FILE, $this->cdnBaseUrl));
|
||||
$event->setOriginalFileUrl(URL::getInstance()->absoluteUrl($original_image_url, null, URL::PATH_TO_FILE, $this->cdnBaseUrl));
|
||||
}
|
||||
|
||||
/**
|
||||
* Process image resizing, with borders or cropping. If $dest_width and $dest_height
|
||||
* are both null, no resize is performed.
|
||||
*
|
||||
* @param ImagineInterface $imagine the Imagine instance
|
||||
* @param ImageInterface $image the image to process
|
||||
* @param int $dest_width the required width
|
||||
* @param int $dest_height the required height
|
||||
* @param int $resize_mode the resize mode (crop / bands / keep image ratio)p
|
||||
* @param string $bg_color the bg_color used for bands
|
||||
* @param bool $allow_zoom if true, image may be zoomed to matchrequired size. If false, image is not zoomed.
|
||||
* @return ImageInterface the resized image.
|
||||
*/
|
||||
protected function applyResize(
|
||||
ImagineInterface $imagine,
|
||||
ImageInterface $image,
|
||||
$dest_width,
|
||||
$dest_height,
|
||||
$resize_mode,
|
||||
$bg_color,
|
||||
$allow_zoom = false
|
||||
) {
|
||||
if (! (\is_null($dest_width) && \is_null($dest_height))) {
|
||||
$width_orig = $image->getSize()->getWidth();
|
||||
$height_orig = $image->getSize()->getHeight();
|
||||
|
||||
$ratio = $width_orig / $height_orig;
|
||||
|
||||
if (\is_null($dest_width)) {
|
||||
$dest_width = $dest_height * $ratio;
|
||||
}
|
||||
|
||||
if (\is_null($dest_height)) {
|
||||
$dest_height = $dest_width / $ratio;
|
||||
}
|
||||
|
||||
if (\is_null($resize_mode)) {
|
||||
$resize_mode = self::KEEP_IMAGE_RATIO;
|
||||
}
|
||||
|
||||
$width_diff = $dest_width / $width_orig;
|
||||
$height_diff = $dest_height / $height_orig;
|
||||
|
||||
$delta_x = $delta_y = $border_width = $border_height = 0;
|
||||
|
||||
if ($width_diff > 1 && $height_diff > 1) {
|
||||
// Set the default final size. If zoom is allowed, we will get the required
|
||||
// image dimension. Otherwise, the final image may be smaller than required.
|
||||
if ($allow_zoom) {
|
||||
$resize_width = $dest_width;
|
||||
$resize_height = $dest_height;
|
||||
} else {
|
||||
$resize_width = $width_orig;
|
||||
$resize_height = $height_orig;
|
||||
}
|
||||
|
||||
// When cropping, be sure to always generate an image which is
|
||||
// not smaller than the required size, zooming it if required.
|
||||
if ($resize_mode == self::EXACT_RATIO_WITH_CROP) {
|
||||
if ($allow_zoom) {
|
||||
if ($width_diff > $height_diff) {
|
||||
$resize_width = $dest_width;
|
||||
$resize_height = \intval($height_orig * $dest_width / $width_orig);
|
||||
$delta_y = ($resize_height - $dest_height) / 2;
|
||||
} else {
|
||||
$resize_height = $dest_height;
|
||||
$resize_width = \intval(($width_orig * $resize_height) / $height_orig);
|
||||
$delta_x = ($resize_width - $dest_width) / 2;
|
||||
}
|
||||
} else {
|
||||
// No zoom : final image may be smaller than the required size.
|
||||
$dest_width = $resize_width;
|
||||
$dest_height = $resize_height;
|
||||
}
|
||||
}
|
||||
} elseif ($width_diff > $height_diff) {
|
||||
// Image height > image width
|
||||
$resize_height = $dest_height;
|
||||
$resize_width = \intval(($width_orig * $resize_height) / $height_orig);
|
||||
|
||||
if ($resize_mode == self::EXACT_RATIO_WITH_CROP) {
|
||||
$resize_width = $dest_width;
|
||||
$resize_height = \intval($height_orig * $dest_width / $width_orig);
|
||||
$delta_y = ($resize_height - $dest_height) / 2;
|
||||
} elseif ($resize_mode != self::EXACT_RATIO_WITH_BORDERS) {
|
||||
$dest_width = $resize_width;
|
||||
}
|
||||
} else {
|
||||
// Image width > image height
|
||||
$resize_width = $dest_width;
|
||||
$resize_height = \intval($height_orig * $dest_width / $width_orig);
|
||||
|
||||
if ($resize_mode == self::EXACT_RATIO_WITH_CROP) {
|
||||
$resize_height = $dest_height;
|
||||
$resize_width = \intval(($width_orig * $resize_height) / $height_orig);
|
||||
$delta_x = ($resize_width - $dest_width) / 2;
|
||||
} elseif ($resize_mode != self::EXACT_RATIO_WITH_BORDERS) {
|
||||
$dest_height = $resize_height;
|
||||
}
|
||||
}
|
||||
|
||||
$image->resize(new Box($resize_width, $resize_height));
|
||||
|
||||
if ($resize_mode == self::EXACT_RATIO_WITH_BORDERS) {
|
||||
$border_width = \intval(($dest_width - $resize_width) / 2);
|
||||
$border_height = \intval(($dest_height - $resize_height) / 2);
|
||||
|
||||
$canvas = new Box($dest_width, $dest_height);
|
||||
|
||||
return $imagine->create($canvas, $bg_color)
|
||||
->paste($image, new Point($border_width, $border_height));
|
||||
} elseif ($resize_mode == self::EXACT_RATIO_WITH_CROP) {
|
||||
$image->crop(
|
||||
new Point($delta_x, $delta_y),
|
||||
new Box($dest_width, $dest_height)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $image;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new Imagine object using current driver configuration
|
||||
*
|
||||
* @return ImagineInterface
|
||||
*/
|
||||
protected function createImagineInstance()
|
||||
{
|
||||
$driver = ConfigQuery::read("imagine_graphic_driver", "gd");
|
||||
|
||||
switch ($driver) {
|
||||
case 'imagick':
|
||||
$image = new ImagickImagine();
|
||||
break;
|
||||
|
||||
case 'gmagick':
|
||||
$image = new GmagickImagine();
|
||||
break;
|
||||
|
||||
case 'gd':
|
||||
default:
|
||||
$image = new Imagine();
|
||||
}
|
||||
|
||||
return $image;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
TheliaEvents::IMAGE_PROCESS => array("processImage", 128),
|
||||
|
||||
// Implemented in parent class BaseCachedFile
|
||||
TheliaEvents::IMAGE_CLEAR_CACHE => array("clearCache", 128),
|
||||
TheliaEvents::IMAGE_DELETE => array("deleteFile", 128),
|
||||
TheliaEvents::IMAGE_SAVE => array("saveFile", 128),
|
||||
TheliaEvents::IMAGE_UPDATE => array("updateFile", 128),
|
||||
TheliaEvents::IMAGE_UPDATE_POSITION => array("updatePosition", 128),
|
||||
TheliaEvents::IMAGE_TOGGLE_VISIBILITY => array("toggleVisibility", 128),
|
||||
);
|
||||
}
|
||||
}
|
||||
79
core/lib/Thelia/Action/Import.php
Normal file
79
core/lib/Thelia/Action/Import.php
Normal file
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
/*************************************************************************************/
|
||||
/* This file is part of the Thelia package. */
|
||||
/* */
|
||||
/* Copyright (c) OpenStudio */
|
||||
/* email : dev@thelia.net */
|
||||
/* web : http://www.thelia.net */
|
||||
/* */
|
||||
/* For the full copyright and license information, please view the LICENSE.txt */
|
||||
/* file that was distributed with this source code. */
|
||||
/*************************************************************************************/
|
||||
|
||||
namespace Thelia\Action;
|
||||
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Thelia\Core\Event\TheliaEvents;
|
||||
use Thelia\Core\Event\UpdatePositionEvent;
|
||||
use Thelia\Handler\ImportHandler;
|
||||
use Thelia\Model\ImportCategoryQuery;
|
||||
use Thelia\Model\ImportQuery;
|
||||
|
||||
/**
|
||||
* Class Import
|
||||
* @author Jérôme Billiras <jbilliras@openstudio.fr>
|
||||
*/
|
||||
class Import extends BaseAction implements EventSubscriberInterface
|
||||
{
|
||||
/**
|
||||
* @var \Thelia\Handler\ImportHandler The import handler
|
||||
*/
|
||||
protected $handler;
|
||||
|
||||
/**
|
||||
* @param \Thelia\Handler\ImportHandler $importHandler The import handler
|
||||
*/
|
||||
public function __construct(ImportHandler $importHandler)
|
||||
{
|
||||
$this->handler = $importHandler;
|
||||
}
|
||||
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return [
|
||||
TheliaEvents::IMPORT_CHANGE_POSITION => [
|
||||
['importChangePosition', 128]
|
||||
],
|
||||
TheliaEvents::IMPORT_CATEGORY_CHANGE_POSITION => [
|
||||
['importCategoryChangePosition', 128]
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle import change position event
|
||||
*
|
||||
* @param UpdatePositionEvent $updatePositionEvent
|
||||
* @param $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function importChangePosition(UpdatePositionEvent $updatePositionEvent, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
$this->handler->getImport($updatePositionEvent->getObjectId(), true);
|
||||
$this->genericUpdatePosition(new ImportQuery, $updatePositionEvent, $dispatcher);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle import category change position event
|
||||
*
|
||||
* @param UpdatePositionEvent $updatePositionEvent
|
||||
* @param $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function importCategoryChangePosition(UpdatePositionEvent $updatePositionEvent, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
$this->handler->getCategory($updatePositionEvent->getObjectId(), true);
|
||||
$this->genericUpdatePosition(new ImportCategoryQuery, $updatePositionEvent, $dispatcher);
|
||||
}
|
||||
}
|
||||
283
core/lib/Thelia/Action/Lang.php
Normal file
283
core/lib/Thelia/Action/Lang.php
Normal file
@@ -0,0 +1,283 @@
|
||||
<?php
|
||||
/*************************************************************************************/
|
||||
/* This file is part of the Thelia package. */
|
||||
/* */
|
||||
/* Copyright (c) OpenStudio */
|
||||
/* email : dev@thelia.net */
|
||||
/* web : http://www.thelia.net */
|
||||
/* */
|
||||
/* For the full copyright and license information, please view the LICENSE.txt */
|
||||
/* file that was distributed with this source code. */
|
||||
/*************************************************************************************/
|
||||
|
||||
namespace Thelia\Action;
|
||||
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\Filesystem\Filesystem;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
use Thelia\Core\Event\Lang\LangCreateEvent;
|
||||
use Thelia\Core\Event\Lang\LangDefaultBehaviorEvent;
|
||||
use Thelia\Core\Event\Lang\LangDeleteEvent;
|
||||
use Thelia\Core\Event\Lang\LangEvent;
|
||||
use Thelia\Core\Event\Lang\LangToggleActiveEvent;
|
||||
use Thelia\Core\Event\Lang\LangToggleDefaultEvent;
|
||||
use Thelia\Core\Event\Lang\LangToggleVisibleEvent;
|
||||
use Thelia\Core\Event\Lang\LangUpdateEvent;
|
||||
use Thelia\Core\Event\TheliaEvents;
|
||||
use Thelia\Core\HttpFoundation\Session\Session;
|
||||
use Thelia\Core\Template\Exception\TemplateException;
|
||||
use Thelia\Core\Template\TemplateHelperInterface;
|
||||
use Thelia\Core\Translation\Translator;
|
||||
use Thelia\Form\Lang\LangUrlEvent;
|
||||
use Thelia\Model\ConfigQuery;
|
||||
use Thelia\Model\LangQuery;
|
||||
use Thelia\Model\Lang as LangModel;
|
||||
|
||||
/**
|
||||
* Class Lang
|
||||
* @package Thelia\Action
|
||||
* @author Manuel Raynaud <manu@raynaud.io>
|
||||
*/
|
||||
class Lang extends BaseAction implements EventSubscriberInterface
|
||||
{
|
||||
/** @var TemplateHelperInterface */
|
||||
protected $templateHelper;
|
||||
|
||||
/** @var RequestStack */
|
||||
protected $requestStack;
|
||||
|
||||
public function __construct(TemplateHelperInterface $templateHelper, RequestStack $requestStack)
|
||||
{
|
||||
$this->templateHelper = $templateHelper;
|
||||
$this->requestStack = $requestStack;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param LangUpdateEvent $event
|
||||
* @param $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
* @throws \Propel\Runtime\Exception\PropelException
|
||||
*/
|
||||
public function update(LangUpdateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
if (null !== $lang = LangQuery::create()->findPk($event->getId())) {
|
||||
$lang->setDispatcher($dispatcher);
|
||||
|
||||
$lang->setTitle($event->getTitle())
|
||||
->setLocale($event->getLocale())
|
||||
->setCode($event->getCode())
|
||||
->setDateTimeFormat($event->getDateTimeFormat())
|
||||
->setDateFormat($event->getDateFormat())
|
||||
->setTimeFormat($event->getTimeFormat())
|
||||
->setDecimalSeparator($event->getDecimalSeparator())
|
||||
->setThousandsSeparator($event->getThousandsSeparator())
|
||||
->setDecimals($event->getDecimals())
|
||||
->save();
|
||||
|
||||
$event->setLang($lang);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param LangToggleDefaultEvent $event
|
||||
* @param $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
* @throws \Propel\Runtime\Exception\PropelException
|
||||
*/
|
||||
public function toggleDefault(LangToggleDefaultEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
if (null !== $lang = LangQuery::create()->findPk($event->getLangId())) {
|
||||
$lang->setDispatcher($dispatcher);
|
||||
|
||||
$lang->toggleDefault();
|
||||
|
||||
$event->setLang($lang);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param LangToggleActiveEvent $event
|
||||
* @throws \Propel\Runtime\Exception\PropelException
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param LangToggleVisibleEvent $event
|
||||
* @throws \Propel\Runtime\Exception\PropelException
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param LangCreateEvent $event
|
||||
* @param $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
* @throws \Propel\Runtime\Exception\PropelException
|
||||
*/
|
||||
public function create(LangCreateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
$lang = new LangModel();
|
||||
|
||||
$lang
|
||||
->setDispatcher($dispatcher)
|
||||
->setTitle($event->getTitle())
|
||||
->setCode($event->getCode())
|
||||
->setLocale($event->getLocale())
|
||||
->setDateTimeFormat($event->getDateTimeFormat())
|
||||
->setDateFormat($event->getDateFormat())
|
||||
->setTimeFormat($event->getTimeFormat())
|
||||
->setDecimalSeparator($event->getDecimalSeparator())
|
||||
->setThousandsSeparator($event->getThousandsSeparator())
|
||||
->setDecimals($event->getDecimals())
|
||||
->save();
|
||||
|
||||
$event->setLang($lang);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param LangDeleteEvent $event
|
||||
* @param $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
* @throws \Propel\Runtime\Exception\PropelException
|
||||
*/
|
||||
public function delete(LangDeleteEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
if (null !== $lang = LangQuery::create()->findPk($event->getLangId())) {
|
||||
if ($lang->getByDefault()) {
|
||||
throw new \RuntimeException(
|
||||
Translator::getInstance()->trans('It is not allowed to delete the default language')
|
||||
);
|
||||
}
|
||||
|
||||
$lang->setDispatcher($dispatcher)
|
||||
->delete();
|
||||
|
||||
/** @var Session $session */
|
||||
$session = $this->requestStack->getCurrentRequest()->getSession();
|
||||
|
||||
// If we've just deleted the current admin edition language, set it to the default one.
|
||||
if ($lang->getId() == $session->getAdminEditionLang()->getId()) {
|
||||
$session->setAdminEditionLang(LangModel::getDefaultLanguage());
|
||||
}
|
||||
|
||||
// If we've just deleted the current admin language, set it to the default one.
|
||||
if ($lang->getId() == $session->getLang()->getId()) {
|
||||
$session->setLang(LangModel::getDefaultLanguage());
|
||||
}
|
||||
|
||||
$event->setLang($lang);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param LangDefaultBehaviorEvent $event
|
||||
* @throws \Exception
|
||||
* @throws \Propel\Runtime\Exception\PropelException
|
||||
*/
|
||||
public function defaultBehavior(LangDefaultBehaviorEvent $event)
|
||||
{
|
||||
ConfigQuery::create()
|
||||
->filterByName('default_lang_without_translation')
|
||||
->update(array('Value' => $event->getDefaultBehavior()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param LangUrlEvent $event
|
||||
* @throws \Exception
|
||||
* @throws \Propel\Runtime\Exception\PropelException
|
||||
*/
|
||||
public function langUrl(LangUrlEvent $event)
|
||||
{
|
||||
foreach ($event->getUrl() as $id => $url) {
|
||||
LangQuery::create()
|
||||
->filterById($id)
|
||||
->update(array('Url' => $url));
|
||||
}
|
||||
}
|
||||
|
||||
public function fixMissingFlag(LangEvent $event)
|
||||
{
|
||||
// Be sure that a lang have a flag, otherwise copy the
|
||||
// "unknown" flag
|
||||
$adminTemplate = $this->templateHelper->getActiveAdminTemplate();
|
||||
$unknownFlag = ConfigQuery::getUnknownFlagPath();
|
||||
|
||||
try {
|
||||
$unknownFlagPath = $adminTemplate->getTemplateFilePath($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);
|
||||
}
|
||||
} catch (TemplateException $ex) {
|
||||
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)
|
||||
),
|
||||
0,
|
||||
$ex
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
TheliaEvents::LANG_UPDATE => array('update', 128),
|
||||
TheliaEvents::LANG_TOGGLEDEFAULT => array('toggleDefault', 128),
|
||||
TheliaEvents::LANG_TOGGLEACTIVE => array('toggleActive', 128),
|
||||
TheliaEvents::LANG_TOGGLEVISIBLE => array('toggleVisible', 128),
|
||||
TheliaEvents::LANG_CREATE => array('create', 128),
|
||||
TheliaEvents::LANG_DELETE => array('delete', 128),
|
||||
TheliaEvents::LANG_DEFAULTBEHAVIOR => array('defaultBehavior', 128),
|
||||
TheliaEvents::LANG_URL => array('langUrl', 128),
|
||||
TheliaEvents::LANG_FIX_MISSING_FLAG => array('fixMissingFlag', 128)
|
||||
);
|
||||
}
|
||||
}
|
||||
51
core/lib/Thelia/Action/MailingSystem.php
Normal file
51
core/lib/Thelia/Action/MailingSystem.php
Normal file
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
/*************************************************************************************/
|
||||
/* This file is part of the Thelia package. */
|
||||
/* */
|
||||
/* Copyright (c) OpenStudio */
|
||||
/* email : dev@thelia.net */
|
||||
/* web : http://www.thelia.net */
|
||||
/* */
|
||||
/* For the full copyright and license information, please view the LICENSE.txt */
|
||||
/* file that was distributed with this source code. */
|
||||
/*************************************************************************************/
|
||||
|
||||
namespace Thelia\Action;
|
||||
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Thelia\Core\Event\MailingSystem\MailingSystemEvent;
|
||||
use Thelia\Core\Event\TheliaEvents;
|
||||
use Thelia\Model\ConfigQuery;
|
||||
|
||||
class MailingSystem extends BaseAction implements EventSubscriberInterface
|
||||
{
|
||||
/**
|
||||
* @param MailingSystemEvent $event
|
||||
*/
|
||||
public function update(MailingSystemEvent $event)
|
||||
{
|
||||
if ($event->getEnabled()) {
|
||||
ConfigQuery::enableSmtp();
|
||||
} else {
|
||||
ConfigQuery::disableSmtp();
|
||||
}
|
||||
ConfigQuery::setSmtpHost($event->getHost());
|
||||
ConfigQuery::setSmtpPort($event->getPort());
|
||||
ConfigQuery::setSmtpEncryption($event->getEncryption());
|
||||
ConfigQuery::setSmtpUsername($event->getUsername());
|
||||
ConfigQuery::setSmtpPassword($event->getPassword());
|
||||
ConfigQuery::setSmtpAuthMode($event->getAuthMode());
|
||||
ConfigQuery::setSmtpTimeout($event->getTimeout());
|
||||
ConfigQuery::setSmtpSourceIp($event->getSourceIp());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
TheliaEvents::MAILING_SYSTEM_UPDATE => array("update", 128),
|
||||
);
|
||||
}
|
||||
}
|
||||
116
core/lib/Thelia/Action/Message.php
Normal file
116
core/lib/Thelia/Action/Message.php
Normal file
@@ -0,0 +1,116 @@
|
||||
<?php
|
||||
/*************************************************************************************/
|
||||
/* This file is part of the Thelia package. */
|
||||
/* */
|
||||
/* Copyright (c) OpenStudio */
|
||||
/* email : dev@thelia.net */
|
||||
/* web : http://www.thelia.net */
|
||||
/* */
|
||||
/* For the full copyright and license information, please view the LICENSE.txt */
|
||||
/* file that was distributed with this source code. */
|
||||
/*************************************************************************************/
|
||||
|
||||
namespace Thelia\Action;
|
||||
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Thelia\Model\MessageQuery;
|
||||
use Thelia\Model\Message as MessageModel;
|
||||
use Thelia\Core\Event\TheliaEvents;
|
||||
use Thelia\Core\Event\Message\MessageUpdateEvent;
|
||||
use Thelia\Core\Event\Message\MessageCreateEvent;
|
||||
use Thelia\Core\Event\Message\MessageDeleteEvent;
|
||||
|
||||
class Message extends BaseAction implements EventSubscriberInterface
|
||||
{
|
||||
/**
|
||||
* Create a new messageuration entry
|
||||
*
|
||||
* @param \Thelia\Core\Event\Message\MessageCreateEvent $event
|
||||
* @param $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function create(MessageCreateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
$message = new MessageModel();
|
||||
|
||||
$message
|
||||
->setDispatcher($dispatcher)
|
||||
|
||||
->setName($event->getMessageName())
|
||||
->setLocale($event->getLocale())
|
||||
->setTitle($event->getTitle())
|
||||
->setSecured($event->getSecured())
|
||||
|
||||
->save()
|
||||
;
|
||||
|
||||
$event->setMessage($message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Change a message
|
||||
*
|
||||
* @param \Thelia\Core\Event\Message\MessageUpdateEvent $event
|
||||
* @param $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function modify(MessageUpdateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
if (null !== $message = MessageQuery::create()->findPk($event->getMessageId())) {
|
||||
$message
|
||||
->setDispatcher($dispatcher)
|
||||
|
||||
->setName($event->getMessageName())
|
||||
->setSecured($event->getSecured())
|
||||
|
||||
->setLocale($event->getLocale())
|
||||
|
||||
->setTitle($event->getTitle())
|
||||
->setSubject($event->getSubject())
|
||||
|
||||
->setHtmlMessage($event->getHtmlMessage())
|
||||
->setTextMessage($event->getTextMessage())
|
||||
|
||||
->setHtmlLayoutFileName($event->getHtmlLayoutFileName())
|
||||
->setHtmlTemplateFileName($event->getHtmlTemplateFileName())
|
||||
->setTextLayoutFileName($event->getTextLayoutFileName())
|
||||
->setTextTemplateFileName($event->getTextTemplateFileName())
|
||||
|
||||
->save();
|
||||
|
||||
$event->setMessage($message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a messageuration entry
|
||||
*
|
||||
* @param \Thelia\Core\Event\Message\MessageDeleteEvent $event
|
||||
* @param $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function delete(MessageDeleteEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
if (null !== ($message = MessageQuery::create()->findPk($event->getMessageId()))) {
|
||||
$message
|
||||
->setDispatcher($dispatcher)
|
||||
->delete()
|
||||
;
|
||||
|
||||
$event->setMessage($message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
TheliaEvents::MESSAGE_CREATE => array("create", 128),
|
||||
TheliaEvents::MESSAGE_UPDATE => array("modify", 128),
|
||||
TheliaEvents::MESSAGE_DELETE => array("delete", 128),
|
||||
);
|
||||
}
|
||||
}
|
||||
75
core/lib/Thelia/Action/MetaData.php
Normal file
75
core/lib/Thelia/Action/MetaData.php
Normal 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),
|
||||
);
|
||||
}
|
||||
}
|
||||
467
core/lib/Thelia/Action/Module.php
Normal file
467
core/lib/Thelia/Action/Module.php
Normal file
@@ -0,0 +1,467 @@
|
||||
<?php
|
||||
/*************************************************************************************/
|
||||
/* This file is part of the Thelia package. */
|
||||
/* */
|
||||
/* Copyright (c) OpenStudio */
|
||||
/* email : dev@thelia.net */
|
||||
/* web : http://www.thelia.net */
|
||||
/* */
|
||||
/* For the full copyright and license information, please view the LICENSE.txt */
|
||||
/* file that was distributed with this source code. */
|
||||
/*************************************************************************************/
|
||||
|
||||
namespace Thelia\Action;
|
||||
|
||||
use Exception;
|
||||
use Propel\Runtime\Propel;
|
||||
use SplFileInfo;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\Filesystem\Exception\IOException;
|
||||
use Symfony\Component\Filesystem\Filesystem;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Thelia\Core\Event\Cache\CacheEvent;
|
||||
use Thelia\Core\Event\Module\ModuleDeleteEvent;
|
||||
use Thelia\Core\Event\Module\ModuleEvent;
|
||||
use Thelia\Core\Event\Module\ModuleInstallEvent;
|
||||
use Thelia\Core\Event\Module\ModuleToggleActivationEvent;
|
||||
use Thelia\Core\Event\Order\OrderPaymentEvent;
|
||||
use Thelia\Core\Event\TheliaEvents;
|
||||
use Thelia\Core\Event\UpdatePositionEvent;
|
||||
use Thelia\Core\Translation\Translator;
|
||||
use Thelia\Exception\FileNotFoundException;
|
||||
use Thelia\Exception\ModuleException;
|
||||
use Thelia\Log\Tlog;
|
||||
use Thelia\Model\Base\OrderQuery;
|
||||
use Thelia\Model\Map\ModuleTableMap;
|
||||
use Thelia\Model\ModuleQuery;
|
||||
use Thelia\Module\BaseModule;
|
||||
use Thelia\Module\ModuleManagement;
|
||||
use Thelia\Module\Validator\ModuleValidator;
|
||||
|
||||
/**
|
||||
* Class Module
|
||||
* @package Thelia\Action
|
||||
* @author Manuel Raynaud <manu@raynaud.io>
|
||||
*/
|
||||
class Module extends BaseAction implements EventSubscriberInterface
|
||||
{
|
||||
/** @var ContainerInterface */
|
||||
protected $container;
|
||||
|
||||
public function __construct(ContainerInterface $container)
|
||||
{
|
||||
$this->container = $container;
|
||||
}
|
||||
|
||||
public function toggleActivation(ModuleToggleActivationEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
if (null !== $module = ModuleQuery::create()->findPk($event->getModuleId())) {
|
||||
$moduleInstance = $module->createInstance();
|
||||
|
||||
if (method_exists($moduleInstance, 'setContainer')) {
|
||||
$moduleInstance->setContainer($this->container);
|
||||
if ($module->getActivate() == BaseModule::IS_ACTIVATED) {
|
||||
$moduleInstance->deActivate($module);
|
||||
} else {
|
||||
$moduleInstance->activate($module);
|
||||
}
|
||||
}
|
||||
|
||||
$event->setModule($module);
|
||||
|
||||
$this->cacheClear($dispatcher);
|
||||
}
|
||||
}
|
||||
|
||||
public function checkToggleActivation(ModuleToggleActivationEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
if (true === $event->isNoCheck()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (null !== $module = ModuleQuery::create()->findPk($event->getModuleId())) {
|
||||
try {
|
||||
if ($module->getActivate() == BaseModule::IS_ACTIVATED) {
|
||||
if ($module->getMandatory() == BaseModule::IS_MANDATORY && $event->getAssumeDeactivate() === false) {
|
||||
throw new \Exception(
|
||||
Translator::getInstance()->trans('Can\'t deactivate a secure module')
|
||||
);
|
||||
}
|
||||
|
||||
if ($event->isRecursive()) {
|
||||
$this->recursiveDeactivation($event, $eventName, $dispatcher);
|
||||
}
|
||||
$this->checkDeactivation($module);
|
||||
} else {
|
||||
if ($event->isRecursive()) {
|
||||
$this->recursiveActivation($event, $eventName, $dispatcher);
|
||||
}
|
||||
$this->checkActivation($module);
|
||||
}
|
||||
} catch (\Exception $ex) {
|
||||
$event->stopPropagation();
|
||||
throw $ex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if module can be activated : supported version of Thelia, module dependencies.
|
||||
*
|
||||
* @param \Thelia\Model\Module $module
|
||||
* @throws Exception if activation fails.
|
||||
* @return bool true if the module can be activated, otherwise false
|
||||
*/
|
||||
private function checkActivation($module)
|
||||
{
|
||||
try {
|
||||
$moduleValidator = new ModuleValidator($module->getAbsoluteBaseDir());
|
||||
$moduleValidator->validate(false);
|
||||
} catch (\Exception $ex) {
|
||||
throw $ex;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if module can be deactivated safely because other modules
|
||||
* could have dependencies to this module
|
||||
*
|
||||
* @param \Thelia\Model\Module $module
|
||||
* @return bool true if the module can be deactivated, otherwise false
|
||||
*/
|
||||
private function checkDeactivation($module)
|
||||
{
|
||||
$moduleValidator = new ModuleValidator($module->getAbsoluteBaseDir());
|
||||
|
||||
$modules = $moduleValidator->getModulesDependOf();
|
||||
|
||||
if (\count($modules) > 0) {
|
||||
$moduleList = implode(', ', array_column($modules, 'code'));
|
||||
|
||||
$message = (\count($modules) == 1)
|
||||
? Translator::getInstance()->trans(
|
||||
'%s has dependency to module %s. You have to deactivate this module before.'
|
||||
)
|
||||
: Translator::getInstance()->trans(
|
||||
'%s have dependencies to module %s. You have to deactivate these modules before.'
|
||||
);
|
||||
|
||||
throw new ModuleException(
|
||||
sprintf($message, $moduleList, $moduleValidator->getModuleDefinition()->getCode())
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get dependencies of the current module and activate it if needed
|
||||
*
|
||||
* @param ModuleToggleActivationEvent $event
|
||||
* @param $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function recursiveActivation(ModuleToggleActivationEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
if (null !== $module = ModuleQuery::create()->findPk($event->getModuleId())) {
|
||||
$moduleValidator = new ModuleValidator($module->getAbsoluteBaseDir());
|
||||
$dependencies = $moduleValidator->getCurrentModuleDependencies();
|
||||
foreach ($dependencies as $defMod) {
|
||||
$submodule = ModuleQuery::create()
|
||||
->findOneByCode($defMod["code"]);
|
||||
if ($submodule && $submodule->getActivate() != BaseModule::IS_ACTIVATED) {
|
||||
$subevent = new ModuleToggleActivationEvent($submodule->getId());
|
||||
$subevent->setRecursive(true);
|
||||
$dispatcher->dispatch(TheliaEvents::MODULE_TOGGLE_ACTIVATION, $subevent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get modules having current module in dependence and deactivate it if needed
|
||||
*
|
||||
* @param ModuleToggleActivationEvent $event
|
||||
* @param $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function recursiveDeactivation(ModuleToggleActivationEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
if (null !== $module = ModuleQuery::create()->findPk($event->getModuleId())) {
|
||||
$moduleValidator = new ModuleValidator($module->getAbsoluteBaseDir());
|
||||
$dependencies = $moduleValidator->getModulesDependOf(true);
|
||||
foreach ($dependencies as $defMod) {
|
||||
$submodule = ModuleQuery::create()
|
||||
->findOneByCode($defMod["code"]);
|
||||
if ($submodule && $submodule->getActivate() == BaseModule::IS_ACTIVATED) {
|
||||
$subevent = new ModuleToggleActivationEvent($submodule->getId());
|
||||
$subevent->setRecursive(true);
|
||||
$dispatcher->dispatch(TheliaEvents::MODULE_TOGGLE_ACTIVATION, $subevent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function delete(ModuleDeleteEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
$con = Propel::getWriteConnection(ModuleTableMap::DATABASE_NAME);
|
||||
$con->beginTransaction();
|
||||
|
||||
if (null !== $module = ModuleQuery::create()->findPk($event->getModuleId(), $con)) {
|
||||
try {
|
||||
if (null === $module->getFullNamespace()) {
|
||||
throw new \LogicException(
|
||||
Translator::getInstance()->trans(
|
||||
'Cannot instantiate module "%name%": the namespace is null. Maybe the model is not loaded ?',
|
||||
['%name%' => $module->getCode()]
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// If the module is referenced by an order, display a meaningful error
|
||||
// instead of 'delete cannot delete' caused by a constraint violation.
|
||||
// FIXME: we hav to find a way to delete modules used by order.
|
||||
if (OrderQuery::create()->filterByDeliveryModuleId($module->getId())->count() > 0
|
||||
||
|
||||
OrderQuery::create()->filterByPaymentModuleId($module->getId())->count() > 0
|
||||
) {
|
||||
throw new \LogicException(
|
||||
Translator::getInstance()->trans(
|
||||
'The module "%name%" is currently in use by at least one order, and can\'t be deleted.',
|
||||
['%name%' => $module->getCode()]
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
if ($module->getMandatory() == BaseModule::IS_MANDATORY && $event->getAssumeDelete() === false) {
|
||||
throw new \Exception(
|
||||
Translator::getInstance()->trans('Can\'t remove a core module')
|
||||
);
|
||||
}
|
||||
// First, try to create an instance
|
||||
$instance = $module->createInstance();
|
||||
|
||||
// Then, if module is activated, check if we can deactivate it
|
||||
if ($module->getActivate()) {
|
||||
// check for modules that depend of this one
|
||||
$this->checkDeactivation($module);
|
||||
}
|
||||
|
||||
$instance->setContainer($this->container);
|
||||
|
||||
$path = $module->getAbsoluteBaseDir();
|
||||
|
||||
$instance->destroy($con, $event->getDeleteData());
|
||||
|
||||
$fs = new Filesystem();
|
||||
$fs->remove($path);
|
||||
} catch (\ReflectionException $ex) {
|
||||
// Happens probably because the module directory has been deleted.
|
||||
// Log a warning, and delete the database entry.
|
||||
Tlog::getInstance()->addWarning(
|
||||
Translator::getInstance()->trans(
|
||||
'Failed to create instance of module "%name%" when trying to delete module. Module directory has probably been deleted',
|
||||
['%name%' => $module->getCode()]
|
||||
)
|
||||
);
|
||||
} catch (FileNotFoundException $fnfe) {
|
||||
// The module directory has been deleted.
|
||||
// Log a warning, and delete the database entry.
|
||||
Tlog::getInstance()->addWarning(
|
||||
Translator::getInstance()->trans(
|
||||
'Module "%name%" directory was not found',
|
||||
['%name%' => $module->getCode()]
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$module->delete($con);
|
||||
|
||||
$con->commit();
|
||||
|
||||
$event->setModule($module);
|
||||
$this->cacheClear($dispatcher);
|
||||
} catch (\Exception $e) {
|
||||
$con->rollBack();
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ModuleEvent $event
|
||||
* @param $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function update(ModuleEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
if (null !== $module = ModuleQuery::create()->findPk($event->getId())) {
|
||||
$module
|
||||
->setDispatcher($dispatcher)
|
||||
->setLocale($event->getLocale())
|
||||
->setTitle($event->getTitle())
|
||||
->setChapo($event->getChapo())
|
||||
->setDescription($event->getDescription())
|
||||
->setPostscriptum($event->getPostscriptum());
|
||||
|
||||
$module->save();
|
||||
|
||||
$event->setModule($module);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Thelia\Core\Event\Module\ModuleInstallEvent $event
|
||||
* @param $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
*
|
||||
* @throws \Exception
|
||||
* @throws \Symfony\Component\Filesystem\Exception\IOException
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function install(ModuleInstallEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
$moduleDefinition = $event->getModuleDefinition();
|
||||
|
||||
$oldModule = ModuleQuery::create()->findOneByFullNamespace($moduleDefinition->getNamespace());
|
||||
|
||||
$fs = new Filesystem();
|
||||
|
||||
$activated = false;
|
||||
|
||||
// check existing module
|
||||
if (null !== $oldModule) {
|
||||
$activated = $oldModule->getActivate();
|
||||
|
||||
if ($activated) {
|
||||
// deactivate
|
||||
$toggleEvent = new ModuleToggleActivationEvent($oldModule->getId());
|
||||
// disable the check of the module because it's already done
|
||||
$toggleEvent->setNoCheck(true);
|
||||
|
||||
$dispatcher->dispatch(TheliaEvents::MODULE_TOGGLE_ACTIVATION, $toggleEvent);
|
||||
}
|
||||
|
||||
// delete
|
||||
$modulePath = $oldModule->getAbsoluteBaseDir();
|
||||
|
||||
$deleteEvent = new ModuleDeleteEvent($oldModule);
|
||||
|
||||
try {
|
||||
$dispatcher->dispatch(TheliaEvents::MODULE_DELETE, $deleteEvent);
|
||||
} catch (Exception $ex) {
|
||||
// if module has not been deleted
|
||||
if ($fs->exists($modulePath)) {
|
||||
throw $ex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// move new module
|
||||
$modulePath = sprintf('%s%s', THELIA_MODULE_DIR, $event->getModuleDefinition()->getCode());
|
||||
|
||||
try {
|
||||
$fs->mirror($event->getModulePath(), $modulePath);
|
||||
} catch (IOException $ex) {
|
||||
if (!$fs->exists($modulePath)) {
|
||||
throw $ex;
|
||||
}
|
||||
}
|
||||
|
||||
// Update the module
|
||||
$moduleDescriptorFile = sprintf('%s%s%s%s%s', $modulePath, DS, 'Config', DS, 'module.xml');
|
||||
$moduleManagement = new ModuleManagement($this->container);
|
||||
$file = new SplFileInfo($moduleDescriptorFile);
|
||||
$module = $moduleManagement->updateModule($file, $this->container);
|
||||
|
||||
// activate if old was activated
|
||||
if ($activated) {
|
||||
$toggleEvent = new ModuleToggleActivationEvent($module->getId());
|
||||
$toggleEvent->setNoCheck(true);
|
||||
|
||||
$dispatcher->dispatch(TheliaEvents::MODULE_TOGGLE_ACTIVATION, $toggleEvent);
|
||||
}
|
||||
|
||||
$event->setModule($module);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call the payment method of the payment module of the given order
|
||||
*
|
||||
* @param OrderPaymentEvent $event
|
||||
*
|
||||
* @throws \RuntimeException if no payment module can be found.
|
||||
*/
|
||||
public function pay(OrderPaymentEvent $event)
|
||||
{
|
||||
$order = $event->getOrder();
|
||||
|
||||
/* call pay method */
|
||||
if (null === $paymentModule = ModuleQuery::create()->findPk($order->getPaymentModuleId())) {
|
||||
throw new \RuntimeException(
|
||||
Translator::getInstance()->trans(
|
||||
"Failed to find a payment Module with ID=%mid for order ID=%oid",
|
||||
[
|
||||
"%mid" => $order->getPaymentModuleId(),
|
||||
"%oid" => $order->getId()
|
||||
]
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$paymentModuleInstance = $paymentModule->getPaymentModuleInstance($this->container);
|
||||
|
||||
$response = $paymentModuleInstance->pay($order);
|
||||
|
||||
if (null !== $response && $response instanceof Response) {
|
||||
$event->setResponse($response);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes position, selecting absolute ou relative change.
|
||||
*
|
||||
* @param UpdatePositionEvent $event
|
||||
* @param $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function updatePosition(UpdatePositionEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
$this->genericUpdatePosition(ModuleQuery::create(), $event, $dispatcher);
|
||||
|
||||
$this->cacheClear($dispatcher);
|
||||
}
|
||||
|
||||
protected function cacheClear(EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
$cacheEvent = new CacheEvent(
|
||||
$this->container->getParameter('kernel.cache_dir')
|
||||
);
|
||||
|
||||
$dispatcher->dispatch(TheliaEvents::CACHE_CLEAR, $cacheEvent);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return [
|
||||
TheliaEvents::MODULE_TOGGLE_ACTIVATION => [
|
||||
['checkToggleActivation', 255],
|
||||
['toggleActivation', 128],
|
||||
],
|
||||
TheliaEvents::MODULE_UPDATE_POSITION => ['updatePosition', 128],
|
||||
TheliaEvents::MODULE_DELETE => ['delete', 128],
|
||||
TheliaEvents::MODULE_UPDATE => ['update', 128],
|
||||
TheliaEvents::MODULE_INSTALL => ['install', 128],
|
||||
TheliaEvents::MODULE_PAY => ['pay', 128],
|
||||
];
|
||||
}
|
||||
}
|
||||
252
core/lib/Thelia/Action/ModuleHook.php
Normal file
252
core/lib/Thelia/Action/ModuleHook.php
Normal 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),
|
||||
|
||||
);
|
||||
}
|
||||
}
|
||||
120
core/lib/Thelia/Action/Newsletter.php
Normal file
120
core/lib/Thelia/Action/Newsletter.php
Normal file
@@ -0,0 +1,120 @@
|
||||
<?php
|
||||
/*************************************************************************************/
|
||||
/* This file is part of the Thelia package. */
|
||||
/* */
|
||||
/* Copyright (c) OpenStudio */
|
||||
/* email : dev@thelia.net */
|
||||
/* web : http://www.thelia.net */
|
||||
/* */
|
||||
/* For the full copyright and license information, please view the LICENSE.txt */
|
||||
/* file that was distributed with this source code. */
|
||||
/*************************************************************************************/
|
||||
|
||||
namespace Thelia\Action;
|
||||
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Thelia\Core\Event\Newsletter\NewsletterEvent;
|
||||
use Thelia\Core\Event\TheliaEvents;
|
||||
use Thelia\Mailer\MailerFactory;
|
||||
use Thelia\Model\ConfigQuery;
|
||||
use Thelia\Model\NewsletterQuery;
|
||||
use Thelia\Model\Newsletter as NewsletterModel;
|
||||
|
||||
/**
|
||||
* Class Newsletter
|
||||
* @package Thelia\Action
|
||||
* @author Manuel Raynaud <manu@raynaud.io>
|
||||
*/
|
||||
class Newsletter extends BaseAction implements EventSubscriberInterface
|
||||
{
|
||||
/** @var MailerFactory */
|
||||
protected $mailer;
|
||||
|
||||
/** @var EventDispatcherInterface */
|
||||
protected $dispatcher;
|
||||
|
||||
public function __construct(MailerFactory $mailer, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
$this->mailer = $mailer;
|
||||
$this->dispatcher = $dispatcher;
|
||||
}
|
||||
|
||||
public function subscribe(NewsletterEvent $event)
|
||||
{
|
||||
// test if the email is already registered and unsubscribed
|
||||
if (null === $newsletter = NewsletterQuery::create()->findOneByEmail($event->getEmail())) {
|
||||
$newsletter = new NewsletterModel();
|
||||
}
|
||||
|
||||
$newsletter
|
||||
->setEmail($event->getEmail())
|
||||
->setFirstname($event->getFirstname())
|
||||
->setLastname($event->getLastname())
|
||||
->setLocale($event->getLocale())
|
||||
->setUnsubscribed(false)
|
||||
->save();
|
||||
|
||||
$event->setNewsletter($newsletter);
|
||||
|
||||
if (ConfigQuery::getNotifyNewsletterSubscription()) {
|
||||
$this->dispatcher->dispatch(TheliaEvents::NEWSLETTER_CONFIRM_SUBSCRIPTION, $event);
|
||||
}
|
||||
}
|
||||
|
||||
public function unsubscribe(NewsletterEvent $event)
|
||||
{
|
||||
if (null !== $nl = NewsletterQuery::create()->findPk($event->getId())) {
|
||||
$nl
|
||||
->setUnsubscribed(true)
|
||||
->save();
|
||||
|
||||
$event->setNewsletter($nl);
|
||||
}
|
||||
}
|
||||
|
||||
public function update(NewsletterEvent $event)
|
||||
{
|
||||
if (null !== $nl = NewsletterQuery::create()->findPk($event->getId())) {
|
||||
$nl->setEmail($event->getEmail())
|
||||
->setFirstname($event->getFirstname())
|
||||
->setLastname($event->getLastname())
|
||||
->setUnsubscribed(false)
|
||||
->setLocale($event->getLocale())
|
||||
->save();
|
||||
|
||||
$event->setNewsletter($nl);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.3.0-alpha2
|
||||
*/
|
||||
public function confirmSubscription(NewsletterEvent $event)
|
||||
{
|
||||
$this->mailer->sendEmailMessage(
|
||||
'newsletter_subscription_confirmation',
|
||||
[ ConfigQuery::getStoreEmail() => ConfigQuery::getStoreName() ],
|
||||
[ $event->getEmail() => $event->getFirstname()." ".$event->getLastname() ],
|
||||
[
|
||||
'email' => $event->getEmail(),
|
||||
'firstname' => $event->getFirstname(),
|
||||
'lastname' => $event->getLastname()
|
||||
],
|
||||
$event->getLocale()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
TheliaEvents::NEWSLETTER_SUBSCRIBE => array('subscribe', 128),
|
||||
TheliaEvents::NEWSLETTER_UPDATE => array('update', 128),
|
||||
TheliaEvents::NEWSLETTER_UNSUBSCRIBE => array('unsubscribe', 128),
|
||||
TheliaEvents::NEWSLETTER_CONFIRM_SUBSCRIPTION => array('confirmSubscription', 128)
|
||||
);
|
||||
}
|
||||
}
|
||||
753
core/lib/Thelia/Action/Order.php
Normal file
753
core/lib/Thelia/Action/Order.php
Normal file
@@ -0,0 +1,753 @@
|
||||
<?php
|
||||
/*************************************************************************************/
|
||||
/* This file is part of the Thelia package. */
|
||||
/* */
|
||||
/* Copyright (c) OpenStudio */
|
||||
/* email : dev@thelia.net */
|
||||
/* web : http://www.thelia.net */
|
||||
/* */
|
||||
/* For the full copyright and license information, please view the LICENSE.txt */
|
||||
/* file that was distributed with this source code. */
|
||||
/*************************************************************************************/
|
||||
|
||||
namespace Thelia\Action;
|
||||
|
||||
use Propel\Runtime\Propel;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
use Thelia\Core\Event\Order\GetStockUpdateOperationOnOrderStatusChangeEvent;
|
||||
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\Product\VirtualProductOrderHandleEvent;
|
||||
use Thelia\Core\Event\TheliaEvents;
|
||||
use Thelia\Core\HttpFoundation\Request;
|
||||
use Thelia\Core\Security\SecurityContext;
|
||||
use Thelia\Core\Security\User\UserInterface;
|
||||
use Thelia\Exception\TheliaProcessException;
|
||||
use Thelia\Log\Tlog;
|
||||
use Thelia\Mailer\MailerFactory;
|
||||
use Thelia\Model\AddressQuery;
|
||||
use Thelia\Model\Cart as CartModel;
|
||||
use Thelia\Model\ConfigQuery;
|
||||
use Thelia\Model\Currency as CurrencyModel;
|
||||
use Thelia\Model\Lang as LangModel;
|
||||
use Thelia\Model\Map\OrderTableMap;
|
||||
use Thelia\Model\ModuleQuery;
|
||||
use Thelia\Model\Order as ModelOrder;
|
||||
use Thelia\Model\Order as OrderModel;
|
||||
use Thelia\Model\OrderAddress;
|
||||
use Thelia\Model\OrderAddressQuery;
|
||||
use Thelia\Model\OrderProduct;
|
||||
use Thelia\Model\OrderProductAttributeCombination;
|
||||
use Thelia\Model\OrderProductTax;
|
||||
use Thelia\Model\OrderStatusQuery;
|
||||
use Thelia\Model\OrderVersionQuery;
|
||||
use Thelia\Model\ProductI18n;
|
||||
use Thelia\Model\ProductSaleElements;
|
||||
use Thelia\Model\ProductSaleElementsQuery;
|
||||
use Thelia\Model\TaxRuleI18n;
|
||||
use Thelia\Tools\I18n;
|
||||
|
||||
/**
|
||||
*
|
||||
* Class Order
|
||||
* @package Thelia\Action
|
||||
* @author Etienne Roudeix <eroudeix@openstudio.fr>
|
||||
* @author Franck Allimant <thelia@cqfdev.fr>
|
||||
*/
|
||||
class Order extends BaseAction implements EventSubscriberInterface
|
||||
{
|
||||
/** @var RequestStack */
|
||||
protected $requestStack;
|
||||
|
||||
/** @var MailerFactory */
|
||||
protected $mailer;
|
||||
|
||||
/** @var SecurityContext */
|
||||
protected $securityContext;
|
||||
|
||||
public function __construct(RequestStack $requestStack, MailerFactory $mailer, SecurityContext $securityContext)
|
||||
{
|
||||
$this->requestStack = $requestStack;
|
||||
$this->mailer = $mailer;
|
||||
$this->securityContext = $securityContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Thelia\Core\Event\Order\OrderEvent $event
|
||||
*/
|
||||
public function setDeliveryAddress(OrderEvent $event)
|
||||
{
|
||||
$order = $event->getOrder();
|
||||
|
||||
$order->setChoosenDeliveryAddress($event->getDeliveryAddress());
|
||||
|
||||
$event->setOrder($order);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Thelia\Core\Event\Order\OrderEvent $event
|
||||
*/
|
||||
public function setDeliveryModule(OrderEvent $event)
|
||||
{
|
||||
$order = $event->getOrder();
|
||||
|
||||
$deliveryModuleId = $event->getDeliveryModule();
|
||||
|
||||
$order->setDeliveryModuleId($deliveryModuleId);
|
||||
|
||||
// Reset postage cost if the delivery module had been removed
|
||||
if ($deliveryModuleId <= 0) {
|
||||
$order->setPostage(0);
|
||||
$order->setPostageTax(0);
|
||||
$order->setPostageTaxRuleTitle(null);
|
||||
}
|
||||
|
||||
$event->setOrder($order);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Thelia\Core\Event\Order\OrderEvent $event
|
||||
*/
|
||||
public function setPostage(OrderEvent $event)
|
||||
{
|
||||
$order = $event->getOrder();
|
||||
|
||||
$order->setPostage($event->getPostage());
|
||||
$order->setPostageTax($event->getPostageTax());
|
||||
$order->setPostageTaxRuleTitle($event->getPostageTaxRuleTitle());
|
||||
|
||||
$event->setOrder($order);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Thelia\Core\Event\Order\OrderEvent $event
|
||||
*/
|
||||
public function setInvoiceAddress(OrderEvent $event)
|
||||
{
|
||||
$order = $event->getOrder();
|
||||
|
||||
$order->setChoosenInvoiceAddress($event->getInvoiceAddress());
|
||||
|
||||
$event->setOrder($order);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Thelia\Core\Event\Order\OrderEvent $event
|
||||
*/
|
||||
public function setPaymentModule(OrderEvent $event)
|
||||
{
|
||||
$order = $event->getOrder();
|
||||
|
||||
$order->setPaymentModuleId($event->getPaymentModule());
|
||||
|
||||
$event->setOrder($order);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
* @param ModelOrder $sessionOrder
|
||||
* @param CurrencyModel $currency
|
||||
* @param LangModel $lang
|
||||
* @param CartModel $cart
|
||||
* @param UserInterface $customer
|
||||
* @param bool $unusedArgument deprecated argument. Will be removed in 2.5
|
||||
* @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,
|
||||
$unusedArgument = null,
|
||||
$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::COL_CREATED_AT);
|
||||
$placedOrder->resetModified(OrderTableMap::COL_UPDATED_AT);
|
||||
$placedOrder->resetModified(OrderTableMap::COL_VERSION_CREATED_AT);
|
||||
|
||||
$placedOrder->setDispatcher($dispatcher);
|
||||
|
||||
$cartItems = $cart->getCartItems();
|
||||
|
||||
/* fulfill order */
|
||||
$placedOrder->setCustomerId($customer->getId());
|
||||
$placedOrder->setCurrencyId($currency->getId());
|
||||
$placedOrder->setCurrencyRate($currency->getRate());
|
||||
$placedOrder->setLangId($lang->getId());
|
||||
|
||||
if ($useOrderDefinedAddresses) {
|
||||
$taxCountry =
|
||||
OrderAddressQuery::create()
|
||||
->findPk($placedOrder->getDeliveryOrderAddressId())
|
||||
->getCountry()
|
||||
;
|
||||
} else {
|
||||
$deliveryAddress = AddressQuery::create()->findPk($sessionOrder->getChoosenDeliveryAddress());
|
||||
$invoiceAddress = AddressQuery::create()->findPk($sessionOrder->getChoosenInvoiceAddress());
|
||||
|
||||
/* hard save the delivery and invoice addresses */
|
||||
$deliveryOrderAddress = new OrderAddress();
|
||||
$deliveryOrderAddress
|
||||
->setCustomerTitleId($deliveryAddress->getTitleId())
|
||||
->setCompany($deliveryAddress->getCompany())
|
||||
->setFirstname($deliveryAddress->getFirstname())
|
||||
->setLastname($deliveryAddress->getLastname())
|
||||
->setAddress1($deliveryAddress->getAddress1())
|
||||
->setAddress2($deliveryAddress->getAddress2())
|
||||
->setAddress3($deliveryAddress->getAddress3())
|
||||
->setZipcode($deliveryAddress->getZipcode())
|
||||
->setCity($deliveryAddress->getCity())
|
||||
->setPhone($deliveryAddress->getPhone())
|
||||
->setCellphone($deliveryAddress->getCellphone())
|
||||
->setCountryId($deliveryAddress->getCountryId())
|
||||
->setStateId($deliveryAddress->getStateId())
|
||||
->save($con);
|
||||
|
||||
$invoiceOrderAddress = new OrderAddress();
|
||||
$invoiceOrderAddress
|
||||
->setCustomerTitleId($invoiceAddress->getTitleId())
|
||||
->setCompany($invoiceAddress->getCompany())
|
||||
->setFirstname($invoiceAddress->getFirstname())
|
||||
->setLastname($invoiceAddress->getLastname())
|
||||
->setAddress1($invoiceAddress->getAddress1())
|
||||
->setAddress2($invoiceAddress->getAddress2())
|
||||
->setAddress3($invoiceAddress->getAddress3())
|
||||
->setZipcode($invoiceAddress->getZipcode())
|
||||
->setCity($invoiceAddress->getCity())
|
||||
->setPhone($invoiceAddress->getPhone())
|
||||
->setCellphone($invoiceAddress->getCellphone())
|
||||
->setCountryId($invoiceAddress->getCountryId())
|
||||
->setStateId($deliveryAddress->getStateId())
|
||||
->save($con);
|
||||
|
||||
$placedOrder->setDeliveryOrderAddressId($deliveryOrderAddress->getId());
|
||||
$placedOrder->setInvoiceOrderAddressId($invoiceOrderAddress->getId());
|
||||
|
||||
$taxCountry = $deliveryAddress->getCountry();
|
||||
}
|
||||
|
||||
$placedOrder->setStatusId(
|
||||
OrderStatusQuery::getNotPaidStatus()->getId()
|
||||
);
|
||||
|
||||
$placedOrder->setCartId($cart->getId());
|
||||
|
||||
/* memorize discount */
|
||||
$placedOrder->setDiscount(
|
||||
$cart->getDiscount()
|
||||
);
|
||||
|
||||
$placedOrder->save($con);
|
||||
|
||||
$manageStock = $placedOrder->isStockManagedOnOrderCreation($dispatcher);
|
||||
|
||||
/* fulfill order_products and decrease stock */
|
||||
|
||||
foreach ($cartItems as $cartItem) {
|
||||
$product = $cartItem->getProduct();
|
||||
|
||||
/* get translation */
|
||||
/** @var ProductI18n $productI18n */
|
||||
$productI18n = I18n::forceI18nRetrieving($lang->getLocale(), 'Product', $product->getId());
|
||||
|
||||
$pse = $cartItem->getProductSaleElements();
|
||||
|
||||
// get the virtual document path
|
||||
$virtualDocumentEvent = new VirtualProductOrderHandleEvent($placedOrder, $pse->getId());
|
||||
// essentially used for virtual product. modules that handles virtual product can
|
||||
// allow the use of stock even for virtual products
|
||||
$useStock = true;
|
||||
$virtual = 0;
|
||||
|
||||
// if the product is virtual, dispatch an event to collect information
|
||||
if ($product->getVirtual() === 1) {
|
||||
$dispatcher->dispatch(TheliaEvents::VIRTUAL_PRODUCT_ORDER_HANDLE, $virtualDocumentEvent);
|
||||
$useStock = $virtualDocumentEvent->isUseStock();
|
||||
$virtual = $virtualDocumentEvent->isVirtual() ? 1 : 0;
|
||||
}
|
||||
|
||||
/* check still in stock */
|
||||
if ($cartItem->getQuantity() > $pse->getQuantity()
|
||||
&& true === ConfigQuery::checkAvailableStock()
|
||||
&& $useStock) {
|
||||
throw new TheliaProcessException("Not enough stock", TheliaProcessException::CART_ITEM_NOT_ENOUGH_STOCK, $cartItem);
|
||||
}
|
||||
|
||||
if ($useStock && $manageStock) {
|
||||
/* decrease stock for non virtual product */
|
||||
$allowNegativeStock = \intval(ConfigQuery::read('allow_negative_stock', 0));
|
||||
$newStock = $pse->getQuantity() - $cartItem->getQuantity();
|
||||
//Forbid negative stock
|
||||
if ($newStock < 0 && 0 === $allowNegativeStock) {
|
||||
$newStock = 0;
|
||||
}
|
||||
$pse->setQuantity(
|
||||
$newStock
|
||||
);
|
||||
|
||||
$pse->save($con);
|
||||
}
|
||||
|
||||
/* get tax */
|
||||
/** @var TaxRuleI18n $taxRuleI18n */
|
||||
$taxRuleI18n = I18n::forceI18nRetrieving($lang->getLocale(), 'TaxRule', $product->getTaxRuleId());
|
||||
|
||||
$taxDetail = $product->getTaxRule()->getTaxDetail(
|
||||
$product,
|
||||
$taxCountry,
|
||||
$cartItem->getPrice(),
|
||||
$cartItem->getPromoPrice(),
|
||||
$lang->getLocale()
|
||||
);
|
||||
|
||||
$orderProduct = new OrderProduct();
|
||||
$orderProduct
|
||||
->setOrderId($placedOrder->getId())
|
||||
->setProductRef($product->getRef())
|
||||
->setProductSaleElementsRef($pse->getRef())
|
||||
->setProductSaleElementsId($pse->getId())
|
||||
->setTitle($productI18n->getTitle())
|
||||
->setChapo($productI18n->getChapo())
|
||||
->setDescription($productI18n->getDescription())
|
||||
->setPostscriptum($productI18n->getPostscriptum())
|
||||
->setVirtual($virtual)
|
||||
->setVirtualDocument($virtualDocumentEvent->getPath())
|
||||
->setQuantity($cartItem->getQuantity())
|
||||
->setPrice($cartItem->getPrice())
|
||||
->setPromoPrice($cartItem->getPromoPrice())
|
||||
->setWasNew($pse->getNewness())
|
||||
->setWasInPromo($cartItem->getPromo())
|
||||
->setWeight($pse->getWeight())
|
||||
->setTaxRuleTitle($taxRuleI18n->getTitle())
|
||||
->setTaxRuleDescription($taxRuleI18n->getDescription())
|
||||
->setEanCode($pse->getEanCode())
|
||||
->setCartItemId($cartItem->getId())
|
||||
->setDispatcher($dispatcher)
|
||||
->save($con)
|
||||
;
|
||||
|
||||
/* fulfill order_product_tax */
|
||||
/** @var OrderProductTax $tax */
|
||||
foreach ($taxDetail as $tax) {
|
||||
$tax->setOrderProductId($orderProduct->getId());
|
||||
$tax->save($con);
|
||||
}
|
||||
|
||||
/* fulfill order_attribute_combination and decrease stock */
|
||||
foreach ($pse->getAttributeCombinations() as $attributeCombination) {
|
||||
/** @var \Thelia\Model\Attribute $attribute */
|
||||
$attribute = I18n::forceI18nRetrieving($lang->getLocale(), 'Attribute', $attributeCombination->getAttributeId());
|
||||
|
||||
/** @var \Thelia\Model\AttributeAv $attributeAv */
|
||||
$attributeAv = I18n::forceI18nRetrieving($lang->getLocale(), 'AttributeAv', $attributeCombination->getAttributeAvId());
|
||||
|
||||
$orderAttributeCombination = new OrderProductAttributeCombination();
|
||||
$orderAttributeCombination
|
||||
->setOrderProductId($orderProduct->getId())
|
||||
->setAttributeTitle($attribute->getTitle())
|
||||
->setAttributeChapo($attribute->getChapo())
|
||||
->setAttributeDescription($attribute->getDescription())
|
||||
->setAttributePostscriptum($attribute->getPostscriptum())
|
||||
->setAttributeAvTitle($attributeAv->getTitle())
|
||||
->setAttributeAvChapo($attributeAv->getChapo())
|
||||
->setAttributeAvDescription($attributeAv->getDescription())
|
||||
->setAttributeAvPostscriptum($attributeAv->getPostscriptum())
|
||||
->save($con);
|
||||
}
|
||||
}
|
||||
|
||||
$con->commit();
|
||||
|
||||
return $placedOrder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an order outside of the front-office context, e.g. manually from the back-office.
|
||||
* @param OrderManualEvent $event
|
||||
* @param $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
* @throws \Exception
|
||||
* @throws \Propel\Runtime\Exception\PropelException
|
||||
*/
|
||||
public function createManual(OrderManualEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
$event->setPlacedOrder(
|
||||
$this->createOrder(
|
||||
$dispatcher,
|
||||
$event->getOrder(),
|
||||
$event->getCurrency(),
|
||||
$event->getLang(),
|
||||
$event->getCart(),
|
||||
$event->getCustomer(),
|
||||
null,
|
||||
$event->getUseOrderDefinedAddresses()
|
||||
)
|
||||
);
|
||||
|
||||
$event->setOrder(new OrderModel());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param OrderEvent $event
|
||||
*
|
||||
* @throws \Thelia\Exception\TheliaProcessException
|
||||
* @param $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
* @throws \Exception
|
||||
* @throws \Propel\Runtime\Exception\PropelException
|
||||
*/
|
||||
public function create(OrderEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
$session = $this->getSession();
|
||||
|
||||
$order = $event->getOrder();
|
||||
$paymentModule = ModuleQuery::create()->findPk($order->getPaymentModuleId());
|
||||
|
||||
$placedOrder = $this->createOrder(
|
||||
$dispatcher,
|
||||
$event->getOrder(),
|
||||
$session->getCurrency(),
|
||||
$session->getLang(),
|
||||
$session->getSessionCart($dispatcher),
|
||||
$this->securityContext->getCustomerUser()
|
||||
);
|
||||
|
||||
$dispatcher->dispatch(TheliaEvents::ORDER_BEFORE_PAYMENT, new OrderEvent($placedOrder));
|
||||
|
||||
/* but memorize placed order */
|
||||
$event->setOrder(new OrderModel());
|
||||
$event->setPlacedOrder($placedOrder);
|
||||
|
||||
/* call pay method */
|
||||
$payEvent = new OrderPaymentEvent($placedOrder);
|
||||
|
||||
$dispatcher->dispatch(TheliaEvents::MODULE_PAY, $payEvent);
|
||||
|
||||
if ($payEvent->hasResponse()) {
|
||||
$event->setResponse($payEvent->getResponse());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param OrderEvent $event
|
||||
* @param $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function orderBeforePayment(OrderEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
$dispatcher->dispatch(TheliaEvents::ORDER_SEND_CONFIRMATION_EMAIL, clone $event);
|
||||
|
||||
$dispatcher->dispatch(TheliaEvents::ORDER_SEND_NOTIFICATION_EMAIL, clone $event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the cart and the order in the customer session once the order is placed,
|
||||
* and the payment performed.
|
||||
*
|
||||
* @param OrderEvent $event
|
||||
* @param $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function orderCartClear(/** @noinspection PhpUnusedParameterInspection */ OrderEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
// Empty cart and clear current order
|
||||
$session = $this->getSession();
|
||||
|
||||
$session->clearSessionCart($dispatcher);
|
||||
|
||||
$session->setOrder(new OrderModel());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param OrderEvent $event
|
||||
*
|
||||
* @throws \Exception if the message cannot be loaded.
|
||||
*/
|
||||
public function sendConfirmationEmail(OrderEvent $event)
|
||||
{
|
||||
$this->mailer->sendEmailToCustomer(
|
||||
'order_confirmation',
|
||||
$event->getOrder()->getCustomer(),
|
||||
[
|
||||
'order_id' => $event->getOrder()->getId(),
|
||||
'order_ref' => $event->getOrder()->getRef()
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param OrderEvent $event
|
||||
*
|
||||
* @throws \Exception if the message cannot be loaded.
|
||||
*/
|
||||
public function sendNotificationEmail(OrderEvent $event)
|
||||
{
|
||||
$this->mailer->sendEmailToShopManagers(
|
||||
'order_notification',
|
||||
[
|
||||
'order_id' => $event->getOrder()->getId(),
|
||||
'order_ref' => $event->getOrder()->getRef()
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param OrderEvent $event
|
||||
* @param $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
* @throws \Exception
|
||||
* @throws \Propel\Runtime\Exception\PropelException
|
||||
*/
|
||||
public function updateStatus(OrderEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
$order = $event->getOrder();
|
||||
$newStatus = $event->getStatus();
|
||||
|
||||
$con = Propel::getConnection(OrderTableMap::DATABASE_NAME);
|
||||
|
||||
// Prevent partial stock update on status change.
|
||||
$con->beginTransaction();
|
||||
|
||||
try {
|
||||
$this->updateQuantity($order, $newStatus, $dispatcher);
|
||||
|
||||
$order->setStatusId($newStatus)->save();
|
||||
|
||||
$event->setOrder($order);
|
||||
|
||||
$con->commit();
|
||||
} catch (\Exception $ex) {
|
||||
$con->rollBack();
|
||||
|
||||
throw $ex;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a stock update is required on order products for a given order status change, and compute if
|
||||
* the stock should be decreased or increased.
|
||||
*
|
||||
* @param GetStockUpdateOperationOnOrderStatusChangeEvent $event
|
||||
* @param $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
* @throws \Propel\Runtime\Exception\PropelException
|
||||
*/
|
||||
public function getStockUpdateOnOrderStatusChange(GetStockUpdateOperationOnOrderStatusChangeEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
// The order
|
||||
$order = $event->getOrder();
|
||||
|
||||
// The new order status
|
||||
$newStatus = $event->getNewOrderStatus();
|
||||
|
||||
if ($newStatus->getId() !== $order->getStatusId()) {
|
||||
// We have to change the stock in the following cases :
|
||||
// 1) The order is currently paid, and will become unpaid (get products back in stock unconditionnaly)
|
||||
// 2) The order is currently unpaid, and will become paid (remove products from stock, except if was done at order creation $manageStockOnCreation == false)
|
||||
// 3) The order is currently NOT PAID, and will become canceled or the like (get products back in stock if it was done at order creation $manageStockOnCreation == true)
|
||||
|
||||
// We consider the ManageStockOnCreation flag only if the order status as not yet changed.
|
||||
// Count distinct order statuses (e.g. NOT_PAID to something else) in the order version table.
|
||||
if (OrderVersionQuery::create()->groupByStatusId()->filterById($order->getId())->count() > 1) {
|
||||
// A status change occured. Ignore $manageStockOnCreation
|
||||
$manageStockOnCreation = false;
|
||||
} else {
|
||||
// A status has not yet occured. Consider the ManageStockOnCreation flag
|
||||
$manageStockOnCreation = $order->isStockManagedOnOrderCreation($dispatcher);
|
||||
}
|
||||
|
||||
if (($order->isPaid(false) && $newStatus->isNotPaid(false)) // Case 1
|
||||
||
|
||||
($order->isNotPaid(true) && $newStatus->isNotPaid(false) && $manageStockOnCreation === true) // Case 3
|
||||
) {
|
||||
$event->setOperation($event::INCREASE_STOCK);
|
||||
}
|
||||
|
||||
if ($order->isNotPaid(false) // Case 2
|
||||
&&
|
||||
$newStatus->isPaid(false)
|
||||
&&
|
||||
$manageStockOnCreation === false) {
|
||||
$event->setOperation($event::DECREASE_STOCK);
|
||||
}
|
||||
|
||||
Tlog::getInstance()->addInfo(
|
||||
"Checking stock operation for status change of order : " . $order->getRef()
|
||||
. ", version: " . $order->getVersion()
|
||||
. ", manageStockOnCreation: " . ($manageStockOnCreation ? 0 : 1)
|
||||
. ", paid:" . ($order->isPaid(false) ? 1 : 0)
|
||||
. ", is not paid:" . ($order->isNotPaid(false) ? 1 : 0)
|
||||
. ", new status paid:" . ($newStatus->isPaid(false) ? 1 : 0)
|
||||
. ", new status is not paid:" . ($newStatus->isNotPaid(false) ? 1 : 0)
|
||||
. " = operation: " . $event->getOperation()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update order products stock after an order status change
|
||||
*
|
||||
* @param OrderModel $order
|
||||
* @param int $newStatus the new status ID
|
||||
* @throws \Exception
|
||||
* @throws \Propel\Runtime\Exception\PropelException
|
||||
*/
|
||||
protected function updateQuantity(ModelOrder $order, $newStatus, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
if ($newStatus !== $order->getStatusId()) {
|
||||
if (null !== $newStatusModel = OrderStatusQuery::create()->findPk($newStatus)) {
|
||||
$operationEvent = new GetStockUpdateOperationOnOrderStatusChangeEvent($order, $newStatusModel);
|
||||
|
||||
$dispatcher->dispatch(
|
||||
TheliaEvents::ORDER_GET_STOCK_UPDATE_OPERATION_ON_ORDER_STATUS_CHANGE,
|
||||
$operationEvent
|
||||
);
|
||||
|
||||
if ($operationEvent->getOperation() !== $operationEvent::DO_NOTHING) {
|
||||
$orderProductList = $order->getOrderProducts();
|
||||
|
||||
/** @var OrderProduct $orderProduct */
|
||||
foreach ($orderProductList as $orderProduct) {
|
||||
$productSaleElementsId = $orderProduct->getProductSaleElementsId();
|
||||
|
||||
/** @var ProductSaleElements $productSaleElements */
|
||||
if (null !== $productSaleElements = ProductSaleElementsQuery::create()->findPk($productSaleElementsId)) {
|
||||
$offset = 0;
|
||||
|
||||
if ($operationEvent->getOperation() == $operationEvent::INCREASE_STOCK) {
|
||||
$offset = $orderProduct->getQuantity();
|
||||
} elseif ($operationEvent->getOperation() == $operationEvent::DECREASE_STOCK) {
|
||||
/* Check if we have enough stock */
|
||||
if ($orderProduct->getQuantity() > $productSaleElements->getQuantity() && true === ConfigQuery::checkAvailableStock()) {
|
||||
throw new TheliaProcessException($productSaleElements->getRef() . " : Not enough stock 2");
|
||||
}
|
||||
|
||||
$offset = -$orderProduct->getQuantity();
|
||||
}
|
||||
|
||||
Tlog::getInstance()->addError("Product stock: " . $productSaleElements->getQuantity() . " -> " . ($productSaleElements->getQuantity() + $offset));
|
||||
|
||||
$productSaleElements
|
||||
->setQuantity($productSaleElements->getQuantity() + $offset)
|
||||
->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param OrderEvent $event
|
||||
* @throws \Propel\Runtime\Exception\PropelException
|
||||
*/
|
||||
public function updateDeliveryRef(OrderEvent $event)
|
||||
{
|
||||
$order = $event->getOrder();
|
||||
|
||||
$order->setDeliveryRef($event->getDeliveryRef())->save();
|
||||
|
||||
$event->setOrder($order);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param OrderEvent $event
|
||||
* @throws \Propel\Runtime\Exception\PropelException
|
||||
*/
|
||||
public function updateTransactionRef(OrderEvent $event)
|
||||
{
|
||||
$order = $event->getOrder();
|
||||
|
||||
$order->setTransactionRef($event->getTransactionRef())->save();
|
||||
|
||||
$event->setOrder($order);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param OrderAddressEvent $event
|
||||
* @throws \Propel\Runtime\Exception\PropelException
|
||||
*/
|
||||
public function updateAddress(OrderAddressEvent $event)
|
||||
{
|
||||
$orderAddress = $event->getOrderAddress();
|
||||
|
||||
$orderAddress
|
||||
->setCustomerTitleId($event->getTitle())
|
||||
->setCompany($event->getCompany())
|
||||
->setFirstname($event->getFirstname())
|
||||
->setLastname($event->getLastname())
|
||||
->setAddress1($event->getAddress1())
|
||||
->setAddress2($event->getAddress2())
|
||||
->setAddress3($event->getAddress3())
|
||||
->setZipcode($event->getZipcode())
|
||||
->setCity($event->getCity())
|
||||
->setCountryId($event->getCountry())
|
||||
->setStateId($event->getState())
|
||||
->setPhone($event->getPhone())
|
||||
->setCellphone($event->getCellphone())
|
||||
->save()
|
||||
;
|
||||
|
||||
$event->setOrderAddress($orderAddress);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
TheliaEvents::ORDER_SET_DELIVERY_ADDRESS => [ "setDeliveryAddress", 128 ],
|
||||
TheliaEvents::ORDER_SET_DELIVERY_MODULE => [ "setDeliveryModule", 128 ],
|
||||
TheliaEvents::ORDER_SET_POSTAGE => [ "setPostage", 128 ],
|
||||
TheliaEvents::ORDER_SET_INVOICE_ADDRESS => [ "setInvoiceAddress", 128 ],
|
||||
TheliaEvents::ORDER_SET_PAYMENT_MODULE => [ "setPaymentModule", 128 ],
|
||||
TheliaEvents::ORDER_PAY => [ "create", 128 ],
|
||||
TheliaEvents::ORDER_CART_CLEAR => [ "orderCartClear", 128 ],
|
||||
TheliaEvents::ORDER_BEFORE_PAYMENT => [ "orderBeforePayment", 128 ],
|
||||
TheliaEvents::ORDER_SEND_CONFIRMATION_EMAIL => [ "sendConfirmationEmail", 128 ],
|
||||
TheliaEvents::ORDER_SEND_NOTIFICATION_EMAIL => [ "sendNotificationEmail", 128 ],
|
||||
TheliaEvents::ORDER_UPDATE_STATUS => [ "updateStatus", 128 ],
|
||||
TheliaEvents::ORDER_UPDATE_DELIVERY_REF => [ "updateDeliveryRef", 128 ],
|
||||
TheliaEvents::ORDER_UPDATE_TRANSACTION_REF => [ "updateTransactionRef", 128 ],
|
||||
TheliaEvents::ORDER_UPDATE_ADDRESS => [ "updateAddress", 128 ],
|
||||
TheliaEvents::ORDER_CREATE_MANUAL => [ "createManual", 128 ],
|
||||
TheliaEvents::ORDER_GET_STOCK_UPDATE_OPERATION_ON_ORDER_STATUS_CHANGE => [ "getStockUpdateOnOrderStatusChange", 128 ],
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the session from the current request
|
||||
*
|
||||
* @return \Thelia\Core\HttpFoundation\Session\Session
|
||||
*/
|
||||
protected function getSession()
|
||||
{
|
||||
/** @var Request $request */
|
||||
$request = $this->requestStack->getCurrentRequest();
|
||||
|
||||
return $request->getSession();
|
||||
}
|
||||
}
|
||||
147
core/lib/Thelia/Action/OrderStatus.php
Normal file
147
core/lib/Thelia/Action/OrderStatus.php
Normal 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;
|
||||
}
|
||||
}
|
||||
65
core/lib/Thelia/Action/Payment.php
Normal file
65
core/lib/Thelia/Action/Payment.php
Normal 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\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())
|
||||
->setMinimumAmount($module->getMinimumAmount())
|
||||
->setMaximumAmount($module->getMaximumAmount());
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return [
|
||||
TheliaEvents::MODULE_PAYMENT_IS_VALID => ['isValid', 128],
|
||||
];
|
||||
}
|
||||
}
|
||||
56
core/lib/Thelia/Action/Pdf.php
Normal file
56
core/lib/Thelia/Action/Pdf.php
Normal file
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
/*************************************************************************************/
|
||||
/* This file is part of the Thelia package. */
|
||||
/* */
|
||||
/* Copyright (c) OpenStudio */
|
||||
/* email : dev@thelia.net */
|
||||
/* web : http://www.thelia.net */
|
||||
/* */
|
||||
/* For the full copyright and license information, please view the LICENSE.txt */
|
||||
/* file that was distributed with this source code. */
|
||||
/*************************************************************************************/
|
||||
|
||||
namespace Thelia\Action;
|
||||
|
||||
use Spipu\Html2Pdf\Html2Pdf;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Thelia\Core\Event\PdfEvent;
|
||||
use Thelia\Core\Event\TheliaEvents;
|
||||
|
||||
/**
|
||||
* Class Pdf
|
||||
* @package Thelia\Action
|
||||
* @author Manuel Raynaud <manu@raynaud.io>
|
||||
*/
|
||||
class Pdf extends BaseAction implements EventSubscriberInterface
|
||||
{
|
||||
public function generatePdf(PdfEvent $event)
|
||||
{
|
||||
$html2pdf = new Html2Pdf(
|
||||
$event->getOrientation(),
|
||||
$event->getFormat(),
|
||||
$event->getLang(),
|
||||
$event->getUnicode(),
|
||||
$event->getEncoding(),
|
||||
$event->getMarges()
|
||||
);
|
||||
|
||||
$html2pdf->setDefaultFont($event->getFontName());
|
||||
|
||||
$html2pdf->pdf->SetDisplayMode('real');
|
||||
|
||||
$html2pdf->writeHTML($event->getContent());
|
||||
|
||||
$event->setPdf($html2pdf->output('output.pdf', 'S'));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
TheliaEvents::GENERATE_PDF => array("generatePdf", 128)
|
||||
);
|
||||
}
|
||||
}
|
||||
960
core/lib/Thelia/Action/Product.php
Normal file
960
core/lib/Thelia/Action/Product.php
Normal file
@@ -0,0 +1,960 @@
|
||||
<?php
|
||||
/*************************************************************************************/
|
||||
/* This file is part of the Thelia package. */
|
||||
/* */
|
||||
/* Copyright (c) OpenStudio */
|
||||
/* email : dev@thelia.net */
|
||||
/* web : http://www.thelia.net */
|
||||
/* */
|
||||
/* For the full copyright and license information, please view the LICENSE.txt */
|
||||
/* file that was distributed with this source code. */
|
||||
/*************************************************************************************/
|
||||
|
||||
namespace Thelia\Action;
|
||||
|
||||
use Propel\Runtime\ActiveQuery\Criteria;
|
||||
use Propel\Runtime\Exception\PropelException;
|
||||
use Propel\Runtime\Propel;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
use Thelia\Core\Event\Feature\FeatureAvCreateEvent;
|
||||
use Thelia\Core\Event\Feature\FeatureAvDeleteEvent;
|
||||
use Thelia\Core\Event\FeatureProduct\FeatureProductDeleteEvent;
|
||||
use Thelia\Core\Event\FeatureProduct\FeatureProductUpdateEvent;
|
||||
use Thelia\Core\Event\File\FileDeleteEvent;
|
||||
use Thelia\Core\Event\Product\ProductAddAccessoryEvent;
|
||||
use Thelia\Core\Event\Product\ProductAddCategoryEvent;
|
||||
use Thelia\Core\Event\Product\ProductAddContentEvent;
|
||||
use Thelia\Core\Event\Product\ProductCloneEvent;
|
||||
use Thelia\Core\Event\Product\ProductCreateEvent;
|
||||
use Thelia\Core\Event\Product\ProductDeleteAccessoryEvent;
|
||||
use Thelia\Core\Event\Product\ProductDeleteCategoryEvent;
|
||||
use Thelia\Core\Event\Product\ProductDeleteContentEvent;
|
||||
use Thelia\Core\Event\Product\ProductDeleteEvent;
|
||||
use Thelia\Core\Event\Product\ProductSetTemplateEvent;
|
||||
use Thelia\Core\Event\Product\ProductToggleVisibilityEvent;
|
||||
use Thelia\Core\Event\Product\ProductUpdateEvent;
|
||||
use Thelia\Core\Event\ProductSaleElement\ProductSaleElementDeleteEvent;
|
||||
use Thelia\Core\Event\Template\TemplateDeleteAttributeEvent;
|
||||
use Thelia\Core\Event\Template\TemplateDeleteFeatureEvent;
|
||||
use Thelia\Core\Event\TheliaEvents;
|
||||
use Thelia\Core\Event\UpdatePositionEvent;
|
||||
use Thelia\Core\Event\UpdateSeoEvent;
|
||||
use Thelia\Core\Event\ViewCheckEvent;
|
||||
use Thelia\Model\Accessory;
|
||||
use Thelia\Model\AccessoryQuery;
|
||||
use Thelia\Model\AttributeTemplateQuery;
|
||||
use Thelia\Model\Currency as CurrencyModel;
|
||||
use Thelia\Model\FeatureAvI18n;
|
||||
use Thelia\Model\FeatureAvI18nQuery;
|
||||
use Thelia\Model\FeatureAvQuery;
|
||||
use Thelia\Model\FeatureProduct;
|
||||
use Thelia\Model\FeatureProductQuery;
|
||||
use Thelia\Model\FeatureTemplateQuery;
|
||||
use Thelia\Model\Map\AttributeTemplateTableMap;
|
||||
use Thelia\Model\Map\FeatureTemplateTableMap;
|
||||
use Thelia\Model\Map\ProductSaleElementsTableMap;
|
||||
use Thelia\Model\Map\ProductTableMap;
|
||||
use Thelia\Model\Product as ProductModel;
|
||||
use Thelia\Model\ProductAssociatedContent;
|
||||
use Thelia\Model\ProductAssociatedContentQuery;
|
||||
use Thelia\Model\ProductCategory;
|
||||
use Thelia\Model\ProductCategoryQuery;
|
||||
use Thelia\Model\ProductDocument;
|
||||
use Thelia\Model\ProductDocumentQuery;
|
||||
use Thelia\Model\ProductI18n;
|
||||
use Thelia\Model\ProductI18nQuery;
|
||||
use Thelia\Model\ProductImage;
|
||||
use Thelia\Model\ProductImageQuery;
|
||||
use Thelia\Model\ProductPrice;
|
||||
use Thelia\Model\ProductPriceQuery;
|
||||
use Thelia\Model\ProductQuery;
|
||||
use Thelia\Model\ProductSaleElementsQuery;
|
||||
use Thelia\Model\TaxRuleQuery;
|
||||
|
||||
class Product extends BaseAction implements EventSubscriberInterface
|
||||
{
|
||||
/** @var EventDispatcherInterface */
|
||||
protected $eventDispatcher;
|
||||
|
||||
public function __construct(EventDispatcherInterface $eventDispatcher)
|
||||
{
|
||||
$this->eventDispatcher = $eventDispatcher;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new product entry
|
||||
*
|
||||
* @param \Thelia\Core\Event\Product\ProductCreateEvent $event
|
||||
*/
|
||||
public function create(ProductCreateEvent $event)
|
||||
{
|
||||
$defaultTaxRuleId = null;
|
||||
if (null !== $defaultTaxRule = TaxRuleQuery::create()->findOneByIsDefault(true)) {
|
||||
$defaultTaxRuleId = $defaultTaxRule->getId();
|
||||
}
|
||||
|
||||
$product = new ProductModel();
|
||||
|
||||
$product
|
||||
->setDispatcher($this->eventDispatcher)
|
||||
|
||||
->setRef($event->getRef())
|
||||
->setLocale($event->getLocale())
|
||||
->setTitle($event->getTitle())
|
||||
->setVisible($event->getVisible() ? 1 : 0)
|
||||
->setVirtual($event->getVirtual() ? 1 : 0)
|
||||
->setTemplateId($event->getTemplateId())
|
||||
|
||||
->create(
|
||||
$event->getDefaultCategory(),
|
||||
$event->getBasePrice(),
|
||||
$event->getCurrencyId(),
|
||||
// Set the default tax rule if not defined
|
||||
$event->getTaxRuleId() ?: $defaultTaxRuleId,
|
||||
$event->getBaseWeight(),
|
||||
$event->getBaseQuantity()
|
||||
)
|
||||
;
|
||||
|
||||
$event->setProduct($product);
|
||||
}
|
||||
|
||||
/*******************
|
||||
* CLONING PROCESS *
|
||||
*******************/
|
||||
|
||||
/**
|
||||
* @param ProductCloneEvent $event
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function cloneProduct(ProductCloneEvent $event)
|
||||
{
|
||||
$con = Propel::getWriteConnection(ProductTableMap::DATABASE_NAME);
|
||||
$con->beginTransaction();
|
||||
|
||||
try {
|
||||
// Get important datas
|
||||
$lang = $event->getLang();
|
||||
$originalProduct = $event->getOriginalProduct();
|
||||
|
||||
if (null === $originalProductDefaultI18n = ProductI18nQuery::create()
|
||||
->findPk([$originalProduct->getId(), $lang])) {
|
||||
// No i18n entry for the current language. Try to find one for creating the product.
|
||||
// It will be updated later by updateClone()
|
||||
$originalProductDefaultI18n = ProductI18nQuery::create()
|
||||
->findOneById($originalProduct->getId())
|
||||
;
|
||||
}
|
||||
|
||||
$originalProductDefaultPrice = ProductPriceQuery::create()
|
||||
->findOneByProductSaleElementsId($originalProduct->getDefaultSaleElements()->getId());
|
||||
|
||||
// Cloning process
|
||||
|
||||
$this->createClone($event, $originalProductDefaultI18n, $originalProductDefaultPrice);
|
||||
|
||||
$this->updateClone($event, $originalProductDefaultPrice);
|
||||
|
||||
$this->cloneFeatureCombination($event);
|
||||
|
||||
$this->cloneAssociatedContent($event);
|
||||
|
||||
$this->cloneAccessories($event);
|
||||
|
||||
$this->cloneAdditionalCategories($event);
|
||||
|
||||
// Dispatch event for file cloning
|
||||
$this->eventDispatcher->dispatch(TheliaEvents::FILE_CLONE, $event);
|
||||
|
||||
// Dispatch event for PSE cloning
|
||||
$this->eventDispatcher->dispatch(TheliaEvents::PSE_CLONE, $event);
|
||||
|
||||
$con->commit();
|
||||
} catch (\Exception $e) {
|
||||
$con->rollBack();
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
public function createClone(ProductCloneEvent $event, ProductI18n $originalProductDefaultI18n, ProductPrice $originalProductDefaultPrice)
|
||||
{
|
||||
// Build event and dispatch creation of the clone product
|
||||
$createCloneEvent = new ProductCreateEvent();
|
||||
$createCloneEvent
|
||||
->setTitle($originalProductDefaultI18n->getTitle())
|
||||
->setRef($event->getRef())
|
||||
->setLocale($event->getLang())
|
||||
->setVisible(0)
|
||||
->setVirtual($event->getOriginalProduct()->getVirtual())
|
||||
->setTaxRuleId($event->getOriginalProduct()->getTaxRuleId())
|
||||
->setDefaultCategory($event->getOriginalProduct()->getDefaultCategoryId())
|
||||
->setBasePrice($originalProductDefaultPrice->getPrice())
|
||||
->setCurrencyId($originalProductDefaultPrice->getCurrencyId())
|
||||
->setBaseWeight($event->getOriginalProduct()->getDefaultSaleElements()->getWeight());
|
||||
|
||||
$this->eventDispatcher->dispatch(TheliaEvents::PRODUCT_CREATE, $createCloneEvent);
|
||||
|
||||
$event->setClonedProduct($createCloneEvent->getProduct());
|
||||
}
|
||||
|
||||
public function updateClone(ProductCloneEvent $event, ProductPrice $originalProductDefaultPrice)
|
||||
{
|
||||
// Get original product's I18ns
|
||||
$originalProductI18ns = ProductI18nQuery::create()
|
||||
->findById($event->getOriginalProduct()->getId());
|
||||
|
||||
/** @var ProductI18n $originalProductI18n */
|
||||
foreach ($originalProductI18ns as $originalProductI18n) {
|
||||
$clonedProductUpdateEvent = new ProductUpdateEvent($event->getClonedProduct()->getId());
|
||||
$clonedProductUpdateEvent
|
||||
->setRef($event->getClonedProduct()->getRef())
|
||||
->setVisible($event->getClonedProduct()->getVisible())
|
||||
->setVirtual($event->getClonedProduct()->getVirtual())
|
||||
|
||||
->setLocale($originalProductI18n->getLocale())
|
||||
->setTitle($originalProductI18n->getTitle())
|
||||
->setChapo($originalProductI18n->getChapo())
|
||||
->setDescription($originalProductI18n->getDescription())
|
||||
->setPostscriptum($originalProductI18n->getPostscriptum())
|
||||
|
||||
->setBasePrice($originalProductDefaultPrice->getPrice())
|
||||
->setCurrencyId($originalProductDefaultPrice->getCurrencyId())
|
||||
->setBaseWeight($event->getOriginalProduct()->getDefaultSaleElements()->getWeight())
|
||||
->setTaxRuleId($event->getOriginalProduct()->getTaxRuleId())
|
||||
->setBrandId($event->getOriginalProduct()->getBrandId())
|
||||
->setDefaultCategory($event->getOriginalProduct()->getDefaultCategoryId());
|
||||
|
||||
$this->eventDispatcher->dispatch(TheliaEvents::PRODUCT_UPDATE, $clonedProductUpdateEvent);
|
||||
|
||||
// SEO info
|
||||
$clonedProductUpdateSeoEvent = new UpdateSeoEvent($event->getClonedProduct()->getId());
|
||||
$clonedProductUpdateSeoEvent
|
||||
->setLocale($originalProductI18n->getLocale())
|
||||
->setMetaTitle($originalProductI18n->getMetaTitle())
|
||||
->setMetaDescription($originalProductI18n->getMetaDescription())
|
||||
->setMetaKeywords($originalProductI18n->getMetaKeywords())
|
||||
->setUrl(null);
|
||||
$this->eventDispatcher->dispatch(TheliaEvents::PRODUCT_UPDATE_SEO, $clonedProductUpdateSeoEvent);
|
||||
}
|
||||
|
||||
$event->setClonedProduct($clonedProductUpdateEvent->getProduct());
|
||||
|
||||
// Set clone's template
|
||||
$clonedProductUpdateTemplateEvent = new ProductSetTemplateEvent(
|
||||
$event->getClonedProduct(),
|
||||
$event->getOriginalProduct()->getTemplateId(),
|
||||
$originalProductDefaultPrice->getCurrencyId()
|
||||
);
|
||||
|
||||
$this->eventDispatcher->dispatch(TheliaEvents::PRODUCT_SET_TEMPLATE, $clonedProductUpdateTemplateEvent);
|
||||
}
|
||||
|
||||
public function cloneFeatureCombination(ProductCloneEvent $event)
|
||||
{
|
||||
// Get original product FeatureProduct list
|
||||
$originalProductFeatureList = FeatureProductQuery::create()
|
||||
->findByProductId($event->getOriginalProduct()->getId());
|
||||
|
||||
// Set clone product FeatureProducts
|
||||
/** @var FeatureProduct $originalProductFeature */
|
||||
foreach ($originalProductFeatureList as $originalProductFeature) {
|
||||
// Get original FeatureAvI18n list
|
||||
$originalProductFeatureAvI18nList = FeatureAvI18nQuery::create()
|
||||
->findById($originalProductFeature->getFeatureAvId());
|
||||
|
||||
/** @var FeatureAvI18n $originalProductFeatureAvI18n */
|
||||
foreach ($originalProductFeatureAvI18nList as $originalProductFeatureAvI18n) {
|
||||
// Create a FeatureProduct for each FeatureAv (not for each FeatureAvI18n)
|
||||
$clonedProductCreateFeatureEvent = new FeatureProductUpdateEvent(
|
||||
$event->getClonedProduct()->getId(),
|
||||
$originalProductFeature->getFeatureId(),
|
||||
$originalProductFeature->getFeatureAvId()
|
||||
);
|
||||
$clonedProductCreateFeatureEvent->setLocale($originalProductFeatureAvI18n->getLocale());
|
||||
|
||||
// If it's a free text value, pass the FeatureAvI18n's title as featureValue to the event
|
||||
if ($originalProductFeature->getIsFreeText()) {
|
||||
$clonedProductCreateFeatureEvent->setFeatureValue($originalProductFeatureAvI18n->getTitle());
|
||||
$clonedProductCreateFeatureEvent->setIsTextValue(true);
|
||||
}
|
||||
|
||||
$this->eventDispatcher->dispatch(TheliaEvents::PRODUCT_FEATURE_UPDATE_VALUE, $clonedProductCreateFeatureEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function cloneAssociatedContent(ProductCloneEvent $event)
|
||||
{
|
||||
// Get original product associated contents
|
||||
$originalProductAssocConts = ProductAssociatedContentQuery::create()
|
||||
->findByProductId($event->getOriginalProduct()->getId());
|
||||
|
||||
// Set clone product associated contents
|
||||
/** @var ProductAssociatedContent $originalProductAssocCont */
|
||||
foreach ($originalProductAssocConts as $originalProductAssocCont) {
|
||||
$clonedProductCreatePAC = new ProductAddContentEvent($event->getClonedProduct(), $originalProductAssocCont->getContentId());
|
||||
$this->eventDispatcher->dispatch(TheliaEvents::PRODUCT_ADD_CONTENT, $clonedProductCreatePAC);
|
||||
}
|
||||
}
|
||||
|
||||
public function cloneAccessories(ProductCloneEvent $event)
|
||||
{
|
||||
// Get original product accessories
|
||||
$originalProductAccessoryList = AccessoryQuery::create()
|
||||
->findByProductId($event->getOriginalProduct()->getId());
|
||||
|
||||
// Set clone product accessories
|
||||
/** @var Accessory $originalProductAccessory */
|
||||
foreach ($originalProductAccessoryList as $originalProductAccessory) {
|
||||
$clonedProductAddAccessoryEvent = new ProductAddAccessoryEvent($event->getClonedProduct(), $originalProductAccessory->getAccessory());
|
||||
$this->eventDispatcher->dispatch(TheliaEvents::PRODUCT_ADD_ACCESSORY, $clonedProductAddAccessoryEvent);
|
||||
}
|
||||
}
|
||||
|
||||
public function cloneAdditionalCategories(ProductCloneEvent $event)
|
||||
{
|
||||
// Get original product additional categories
|
||||
$originalProductAdditionalCategoryList = ProductCategoryQuery::create()
|
||||
->filterByProductId($event->getOriginalProduct()->getId())
|
||||
->filterByDefaultCategory(false)
|
||||
->find();
|
||||
|
||||
// Set clone product additional categories
|
||||
/** @var ProductCategory $originalProductCategory */
|
||||
foreach ($originalProductAdditionalCategoryList as $originalProductCategory) {
|
||||
$clonedProductAddCategoryEvent = new ProductAddCategoryEvent($event->getClonedProduct(), $originalProductCategory->getCategoryId());
|
||||
$this->eventDispatcher->dispatch(TheliaEvents::PRODUCT_ADD_CATEGORY, $clonedProductAddCategoryEvent);
|
||||
}
|
||||
}
|
||||
|
||||
/***************
|
||||
* END CLONING *
|
||||
***************/
|
||||
|
||||
/**
|
||||
* Change a product
|
||||
*
|
||||
* @param \Thelia\Core\Event\Product\ProductUpdateEvent $event
|
||||
* @throws PropelException
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function update(ProductUpdateEvent $event)
|
||||
{
|
||||
if (null !== $product = ProductQuery::create()->findPk($event->getProductId())) {
|
||||
$con = Propel::getWriteConnection(ProductTableMap::DATABASE_NAME);
|
||||
$con->beginTransaction();
|
||||
|
||||
try {
|
||||
$prevRef = $product->getRef();
|
||||
|
||||
$product
|
||||
->setDispatcher($this->eventDispatcher)
|
||||
->setRef($event->getRef())
|
||||
->setLocale($event->getLocale())
|
||||
->setTitle($event->getTitle())
|
||||
->setDescription($event->getDescription())
|
||||
->setChapo($event->getChapo())
|
||||
->setPostscriptum($event->getPostscriptum())
|
||||
->setVisible($event->getVisible() ? 1 : 0)
|
||||
->setVirtual($event->getVirtual() ? 1 : 0)
|
||||
->setBrandId($event->getBrandId() <= 0 ? null : $event->getBrandId())
|
||||
|
||||
->save($con)
|
||||
;
|
||||
|
||||
// Update default PSE (if product has no attributes and the product's ref change)
|
||||
$defaultPseRefChange = $prevRef !== $product->getRef()
|
||||
&& 0 === $product->getDefaultSaleElements()->countAttributeCombinations();
|
||||
if ($defaultPseRefChange) {
|
||||
$defaultPse = $product->getDefaultSaleElements();
|
||||
$defaultPse->setRef($product->getRef())->save();
|
||||
}
|
||||
|
||||
// Update default category (if required)
|
||||
$product->setDefaultCategory($event->getDefaultCategory());
|
||||
|
||||
$event->setProduct($product);
|
||||
$con->commit();
|
||||
} catch (PropelException $e) {
|
||||
$con->rollBack();
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param UpdateSeoEvent $event
|
||||
* @param $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
* @return mixed
|
||||
*/
|
||||
public function updateSeo(UpdateSeoEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
return $this->genericUpdateSeo(ProductQuery::create(), $event, $dispatcher);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a product entry
|
||||
*
|
||||
* @param \Thelia\Core\Event\Product\ProductDeleteEvent $event
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function delete(ProductDeleteEvent $event)
|
||||
{
|
||||
if (null !== $product = ProductQuery::create()->findPk($event->getProductId())) {
|
||||
$con = Propel::getWriteConnection(ProductTableMap::DATABASE_NAME);
|
||||
$con->beginTransaction();
|
||||
|
||||
try {
|
||||
$fileList = ['images' => [], 'documentList' => []];
|
||||
|
||||
// Get product's files to delete after product deletion
|
||||
$fileList['images']['list'] = ProductImageQuery::create()
|
||||
->findByProductId($event->getProductId());
|
||||
$fileList['images']['type'] = TheliaEvents::IMAGE_DELETE;
|
||||
|
||||
$fileList['documentList']['list'] = ProductDocumentQuery::create()
|
||||
->findByProductId($event->getProductId());
|
||||
$fileList['documentList']['type'] = TheliaEvents::DOCUMENT_DELETE;
|
||||
|
||||
// Delete product
|
||||
$product
|
||||
->setDispatcher($this->eventDispatcher)
|
||||
->delete($con)
|
||||
;
|
||||
|
||||
$event->setProduct($product);
|
||||
|
||||
// Dispatch delete product's files event
|
||||
foreach ($fileList as $fileTypeList) {
|
||||
foreach ($fileTypeList['list'] as $fileToDelete) {
|
||||
$fileDeleteEvent = new FileDeleteEvent($fileToDelete);
|
||||
$this->eventDispatcher->dispatch($fileTypeList['type'], $fileDeleteEvent);
|
||||
}
|
||||
}
|
||||
|
||||
$con->commit();
|
||||
} catch (\Exception $e) {
|
||||
$con->rollBack();
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle product visibility. No form used here
|
||||
*
|
||||
* @param ProductToggleVisibilityEvent $event
|
||||
*/
|
||||
public function toggleVisibility(ProductToggleVisibilityEvent $event)
|
||||
{
|
||||
$product = $event->getProduct();
|
||||
|
||||
$product
|
||||
->setDispatcher($this->eventDispatcher)
|
||||
->setVisible($product->getVisible() ? false : true)
|
||||
->save()
|
||||
;
|
||||
|
||||
$event->setProduct($product);
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes position, selecting absolute ou relative change.
|
||||
*
|
||||
* @param UpdatePositionEvent $event
|
||||
* @param $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function updatePosition(UpdatePositionEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
$this->genericUpdateDelegatePosition(
|
||||
ProductCategoryQuery::create()
|
||||
->filterByProductId($event->getObjectId())
|
||||
->filterByCategoryId($event->getReferrerId()),
|
||||
$event,
|
||||
$dispatcher
|
||||
);
|
||||
}
|
||||
|
||||
public function addContent(ProductAddContentEvent $event)
|
||||
{
|
||||
if (ProductAssociatedContentQuery::create()
|
||||
->filterByContentId($event->getContentId())
|
||||
->filterByProduct($event->getProduct())->count() <= 0) {
|
||||
$content = new ProductAssociatedContent();
|
||||
|
||||
$content
|
||||
->setDispatcher($this->eventDispatcher)
|
||||
->setProduct($event->getProduct())
|
||||
->setContentId($event->getContentId())
|
||||
->save()
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
public function removeContent(ProductDeleteContentEvent $event)
|
||||
{
|
||||
$content = ProductAssociatedContentQuery::create()
|
||||
->filterByContentId($event->getContentId())
|
||||
->filterByProduct($event->getProduct())->findOne()
|
||||
;
|
||||
|
||||
if ($content !== null) {
|
||||
$content
|
||||
->setDispatcher($this->eventDispatcher)
|
||||
->delete()
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
public function addCategory(ProductAddCategoryEvent $event)
|
||||
{
|
||||
if (ProductCategoryQuery::create()
|
||||
->filterByProduct($event->getProduct())
|
||||
->filterByCategoryId($event->getCategoryId())
|
||||
->count() <= 0) {
|
||||
$productCategory = (new ProductCategory())
|
||||
->setProduct($event->getProduct())
|
||||
->setCategoryId($event->getCategoryId())
|
||||
->setDefaultCategory(false);
|
||||
|
||||
$productCategory
|
||||
->setPosition($productCategory->getNextPosition())
|
||||
->save();
|
||||
}
|
||||
}
|
||||
|
||||
public function removeCategory(ProductDeleteCategoryEvent $event)
|
||||
{
|
||||
$productCategory = ProductCategoryQuery::create()
|
||||
->filterByProduct($event->getProduct())
|
||||
->filterByCategoryId($event->getCategoryId())
|
||||
->findOne();
|
||||
|
||||
if ($productCategory != null) {
|
||||
$productCategory->delete();
|
||||
}
|
||||
}
|
||||
|
||||
public function addAccessory(ProductAddAccessoryEvent $event)
|
||||
{
|
||||
if (AccessoryQuery::create()
|
||||
->filterByAccessory($event->getAccessoryId())
|
||||
->filterByProductId($event->getProduct()->getId())->count() <= 0) {
|
||||
$accessory = new Accessory();
|
||||
|
||||
$accessory
|
||||
->setDispatcher($this->eventDispatcher)
|
||||
->setProductId($event->getProduct()->getId())
|
||||
->setAccessory($event->getAccessoryId())
|
||||
->save()
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
public function removeAccessory(ProductDeleteAccessoryEvent $event)
|
||||
{
|
||||
$accessory = AccessoryQuery::create()
|
||||
->filterByAccessory($event->getAccessoryId())
|
||||
->filterByProductId($event->getProduct()->getId())->findOne()
|
||||
;
|
||||
|
||||
if ($accessory !== null) {
|
||||
$accessory
|
||||
->setDispatcher($this->eventDispatcher)
|
||||
->delete()
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
public function setProductTemplate(ProductSetTemplateEvent $event)
|
||||
{
|
||||
$con = Propel::getWriteConnection(ProductTableMap::DATABASE_NAME);
|
||||
|
||||
$con->beginTransaction();
|
||||
|
||||
try {
|
||||
$product = $event->getProduct();
|
||||
|
||||
// Check differences between current coobination and the next one, and clear obsoletes values.
|
||||
$nextTemplateId = $event->getTemplateId();
|
||||
$currentTemplateId = $product->getTemplateId();
|
||||
|
||||
// 1. Process product features.
|
||||
|
||||
$currentFeatures = FeatureTemplateQuery::create()
|
||||
->filterByTemplateId($currentTemplateId)
|
||||
->select([ FeatureTemplateTableMap::COL_FEATURE_ID ])
|
||||
->find($con);
|
||||
|
||||
$nextFeatures = FeatureTemplateQuery::create()
|
||||
->filterByTemplateId($nextTemplateId)
|
||||
->select([ FeatureTemplateTableMap::COL_FEATURE_ID ])
|
||||
->find($con);
|
||||
|
||||
// Find features values we shoud delete. To do this, we have to
|
||||
// find all features in $currentFeatures that are not present in $nextFeatures
|
||||
$featuresToDelete = array_diff($currentFeatures->getData(), $nextFeatures->getData());
|
||||
|
||||
// Delete obsolete features values
|
||||
foreach ($featuresToDelete as $featureId) {
|
||||
$this->eventDispatcher->dispatch(
|
||||
TheliaEvents::PRODUCT_FEATURE_DELETE_VALUE,
|
||||
new FeatureProductDeleteEvent($product->getId(), $featureId)
|
||||
);
|
||||
}
|
||||
|
||||
// 2. Process product Attributes
|
||||
|
||||
$currentAttributes = AttributeTemplateQuery::create()
|
||||
->filterByTemplateId($currentTemplateId)
|
||||
->select([ AttributeTemplateTableMap::COL_ATTRIBUTE_ID ])
|
||||
->find($con);
|
||||
|
||||
$nextAttributes = AttributeTemplateQuery::create()
|
||||
->filterByTemplateId($nextTemplateId)
|
||||
->select([ AttributeTemplateTableMap::COL_ATTRIBUTE_ID ])
|
||||
->find($con);
|
||||
|
||||
// Find attributes values we shoud delete. To do this, we have to
|
||||
// find all attributes in $currentAttributes that are not present in $nextAttributes
|
||||
$attributesToDelete = array_diff($currentAttributes->getData(), $nextAttributes->getData());
|
||||
|
||||
// Find PSE which includes $attributesToDelete for the current product/
|
||||
$pseToDelete = ProductSaleElementsQuery::create()
|
||||
->filterByProductId($product->getId())
|
||||
->useAttributeCombinationQuery()
|
||||
->filterByAttributeId($attributesToDelete, Criteria::IN)
|
||||
->endUse()
|
||||
->select([ ProductSaleElementsTableMap::COL_ID ])
|
||||
->find();
|
||||
|
||||
// Delete obsolete PSEs
|
||||
foreach ($pseToDelete->getData() as $pseId) {
|
||||
$this->eventDispatcher->dispatch(
|
||||
TheliaEvents::PRODUCT_DELETE_PRODUCT_SALE_ELEMENT,
|
||||
new ProductSaleElementDeleteEvent(
|
||||
$pseId,
|
||||
CurrencyModel::getDefaultCurrency()->getId()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Update the product template
|
||||
$template_id = $event->getTemplateId();
|
||||
|
||||
// Set it to null if it's zero.
|
||||
if ($template_id <= 0) {
|
||||
$template_id = null;
|
||||
}
|
||||
|
||||
$product->setTemplateId($template_id)->save($con);
|
||||
|
||||
$product->clearProductSaleElementss();
|
||||
|
||||
$event->setProduct($product);
|
||||
|
||||
// Store all the stuff !
|
||||
$con->commit();
|
||||
} catch (\Exception $ex) {
|
||||
$con->rollBack();
|
||||
|
||||
throw $ex;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes accessry position, selecting absolute ou relative change.
|
||||
*
|
||||
* @param UpdatePositionEvent $event
|
||||
* @param $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
* @return Object
|
||||
*/
|
||||
public function updateAccessoryPosition(UpdatePositionEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
return $this->genericUpdatePosition(AccessoryQuery::create(), $event, $dispatcher);
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes position, selecting absolute ou relative change.
|
||||
*
|
||||
* @param UpdatePositionEvent $event
|
||||
* @param $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
* @return Object
|
||||
*/
|
||||
public function updateContentPosition(UpdatePositionEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
return $this->genericUpdatePosition(ProductAssociatedContentQuery::create(), $event, $dispatcher);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the value of a product feature.
|
||||
*
|
||||
* @param FeatureProductUpdateEvent $event
|
||||
*/
|
||||
public function updateFeatureProductValue(FeatureProductUpdateEvent $event)
|
||||
{
|
||||
// Prepare the FeatureAv's ID
|
||||
$featureAvId = $event->getFeatureValue();
|
||||
|
||||
// Search for existing FeatureProduct
|
||||
$featureProductQuery = FeatureProductQuery::create()
|
||||
->filterByProductId($event->getProductId())
|
||||
->filterByFeatureId($event->getFeatureId())
|
||||
;
|
||||
|
||||
// If it's not a free text value, we can filter by the event's featureValue (which is an ID)
|
||||
if ($event->getFeatureValue() !== null && $event->getIsTextValue() === false) {
|
||||
$featureProductQuery->filterByFeatureAvId($featureAvId);
|
||||
}
|
||||
|
||||
$featureProduct = $featureProductQuery->findOne();
|
||||
|
||||
// If the FeatureProduct does not exist, create it
|
||||
if ($featureProduct === null) {
|
||||
$featureProduct = new FeatureProduct();
|
||||
|
||||
$featureProduct
|
||||
->setDispatcher($this->eventDispatcher)
|
||||
->setProductId($event->getProductId())
|
||||
->setFeatureId($event->getFeatureId())
|
||||
;
|
||||
|
||||
// If it's a free text value, create a FeatureAv to handle i18n
|
||||
if ($event->getIsTextValue() === true) {
|
||||
$featureProduct->setIsFreeText(true);
|
||||
|
||||
$createFeatureAvEvent = new FeatureAvCreateEvent();
|
||||
$createFeatureAvEvent
|
||||
->setFeatureId($event->getFeatureId())
|
||||
->setLocale($event->getLocale())
|
||||
->setTitle($event->getFeatureValue());
|
||||
$this->eventDispatcher->dispatch(TheliaEvents::FEATURE_AV_CREATE, $createFeatureAvEvent);
|
||||
|
||||
$featureAvId = $createFeatureAvEvent->getFeatureAv()->getId();
|
||||
}
|
||||
} // Else if the FeatureProduct exists and is a free text value
|
||||
elseif ($featureProduct !== null && $event->getIsTextValue() === true) {
|
||||
// Get the FeatureAv
|
||||
$freeTextFeatureAv = FeatureAvQuery::create()
|
||||
->filterByFeatureProduct($featureProduct)
|
||||
->findOneByFeatureId($event->getFeatureId());
|
||||
|
||||
// Get the FeatureAvI18n by locale
|
||||
$freeTextFeatureAvI18n = FeatureAvI18nQuery::create()
|
||||
->filterById($freeTextFeatureAv->getId())
|
||||
->findOneByLocale($event->getLocale());
|
||||
|
||||
// Nothing found for this lang and the new value is not empty : create FeatureAvI18n
|
||||
if ($freeTextFeatureAvI18n === null && !empty($featureAvId)) {
|
||||
$featureAvI18n = new FeatureAvI18n();
|
||||
$featureAvI18n
|
||||
->setId($freeTextFeatureAv->getId())
|
||||
->setLocale($event->getLocale())
|
||||
->setTitle($event->getFeatureValue())
|
||||
->save();
|
||||
|
||||
$featureAvId = $featureAvI18n->getId();
|
||||
} // Else if i18n exists but new value is empty : delete FeatureAvI18n
|
||||
elseif ($freeTextFeatureAvI18n !== null && empty($featureAvId)) {
|
||||
$freeTextFeatureAvI18n->delete();
|
||||
|
||||
// Check if there are still some FeatureAvI18n for this FeatureAv
|
||||
$freeTextFeatureAvI18ns = FeatureAvI18nQuery::create()
|
||||
->findById($freeTextFeatureAv->getId());
|
||||
|
||||
// If there are no more FeatureAvI18ns for this FeatureAv, remove the corresponding FeatureProduct & FeatureAv
|
||||
if (\count($freeTextFeatureAvI18ns) == 0) {
|
||||
$deleteFeatureProductEvent = new FeatureProductDeleteEvent($event->getProductId(), $event->getFeatureId());
|
||||
$this->eventDispatcher->dispatch(TheliaEvents::PRODUCT_FEATURE_DELETE_VALUE, $deleteFeatureProductEvent);
|
||||
|
||||
$deleteFeatureAvEvent = new FeatureAvDeleteEvent($freeTextFeatureAv->getId());
|
||||
$this->eventDispatcher->dispatch(TheliaEvents::FEATURE_AV_DELETE, $deleteFeatureAvEvent);
|
||||
}
|
||||
return;
|
||||
} // Else if a FeatureAvI18n is found and the new value is not empty : update existing FeatureAvI18n
|
||||
elseif ($freeTextFeatureAvI18n !== null && !empty($featureAvId)) {
|
||||
$freeTextFeatureAvI18n->setTitle($featureAvId);
|
||||
$freeTextFeatureAvI18n->save();
|
||||
|
||||
$featureAvId = $freeTextFeatureAvI18n->getId();
|
||||
} //To prevent Integrity constraint violation
|
||||
elseif (empty($featureAvId)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$featureProduct->setFeatureAvId($featureAvId);
|
||||
|
||||
$featureProduct->save();
|
||||
|
||||
$event->setFeatureProduct($featureProduct);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a product feature value
|
||||
*
|
||||
* @param FeatureProductDeleteEvent $event
|
||||
*/
|
||||
public function deleteFeatureProductValue(FeatureProductDeleteEvent $event)
|
||||
{
|
||||
FeatureProductQuery::create()
|
||||
->filterByProductId($event->getProductId())
|
||||
->filterByFeatureId($event->getFeatureId())
|
||||
->delete()
|
||||
;
|
||||
}
|
||||
|
||||
public function deleteImagePSEAssociations(FileDeleteEvent $event)
|
||||
{
|
||||
$model = $event->getFileToDelete();
|
||||
|
||||
if ($model instanceof ProductImage) {
|
||||
$model->getProductSaleElementsProductImages()->delete();
|
||||
}
|
||||
}
|
||||
|
||||
public function deleteDocumentPSEAssociations(FileDeleteEvent $event)
|
||||
{
|
||||
$model = $event->getFileToDelete();
|
||||
|
||||
if ($model instanceof ProductDocument) {
|
||||
$model->getProductSaleElementsProductDocuments()->delete();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* When a feature is removed from a template, the products which are using this feature should be updated.
|
||||
*
|
||||
* @param TemplateDeleteFeatureEvent $event
|
||||
* @param string $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function deleteTemplateFeature(TemplateDeleteFeatureEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
// Detete the removed feature in all products which are using this template
|
||||
$products = ProductQuery::create()
|
||||
->filterByTemplateId($event->getTemplate()->getId())
|
||||
->find()
|
||||
;
|
||||
|
||||
foreach ($products as $product) {
|
||||
$dispatcher->dispatch(
|
||||
TheliaEvents::PRODUCT_FEATURE_DELETE_VALUE,
|
||||
new FeatureProductDeleteEvent($product->getId(), $event->getFeatureId())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* When an attribute is removed from a template, the conbinations and PSE of products which are using this template
|
||||
* should be updated.
|
||||
*
|
||||
* @param TemplateDeleteAttributeEvent $event
|
||||
* @param string $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function deleteTemplateAttribute(TemplateDeleteAttributeEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
// Detete the removed attribute in all products which are using this template
|
||||
$pseToDelete = ProductSaleElementsQuery::create()
|
||||
->useProductQuery()
|
||||
->filterByTemplateId($event->getTemplate()->getId())
|
||||
->endUse()
|
||||
->useAttributeCombinationQuery()
|
||||
->filterByAttributeId($event->getAttributeId())
|
||||
->endUse()
|
||||
->select([ ProductSaleElementsTableMap::COL_ID ])
|
||||
->find();
|
||||
|
||||
$currencyId = CurrencyModel::getDefaultCurrency()->getId();
|
||||
|
||||
foreach ($pseToDelete->getData() as $pseId) {
|
||||
$dispatcher->dispatch(
|
||||
TheliaEvents::PRODUCT_DELETE_PRODUCT_SALE_ELEMENT,
|
||||
new ProductSaleElementDeleteEvent(
|
||||
$pseId,
|
||||
$currencyId
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if is a product view and if product_id is visible
|
||||
*
|
||||
* @param ViewCheckEvent $event
|
||||
* @param string $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function viewCheck(ViewCheckEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
if ($event->getView() == 'product') {
|
||||
$product = ProductQuery::create()
|
||||
->filterById($event->getViewId())
|
||||
->filterByVisible(1)
|
||||
->count();
|
||||
|
||||
if ($product == 0) {
|
||||
$dispatcher->dispatch(TheliaEvents::VIEW_PRODUCT_ID_NOT_VISIBLE, $event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ViewCheckEvent $event
|
||||
* @throws NotFoundHttpException
|
||||
*/
|
||||
public function viewProductIdNotVisible(ViewCheckEvent $event)
|
||||
{
|
||||
throw new NotFoundHttpException();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
TheliaEvents::PRODUCT_CREATE => array("create", 128),
|
||||
TheliaEvents::PRODUCT_CLONE => array("cloneProduct", 128),
|
||||
TheliaEvents::PRODUCT_UPDATE => array("update", 128),
|
||||
TheliaEvents::PRODUCT_DELETE => array("delete", 128),
|
||||
TheliaEvents::PRODUCT_TOGGLE_VISIBILITY => array("toggleVisibility", 128),
|
||||
|
||||
TheliaEvents::PRODUCT_UPDATE_POSITION => array("updatePosition", 128),
|
||||
TheliaEvents::PRODUCT_UPDATE_SEO => array("updateSeo", 128),
|
||||
|
||||
TheliaEvents::PRODUCT_ADD_CONTENT => array("addContent", 128),
|
||||
TheliaEvents::PRODUCT_REMOVE_CONTENT => array("removeContent", 128),
|
||||
TheliaEvents::PRODUCT_UPDATE_CONTENT_POSITION => array("updateContentPosition", 128),
|
||||
|
||||
TheliaEvents::PRODUCT_ADD_ACCESSORY => array("addAccessory", 128),
|
||||
TheliaEvents::PRODUCT_REMOVE_ACCESSORY => array("removeAccessory", 128),
|
||||
TheliaEvents::PRODUCT_UPDATE_ACCESSORY_POSITION => array("updateAccessoryPosition", 128),
|
||||
|
||||
TheliaEvents::PRODUCT_ADD_CATEGORY => array("addCategory", 128),
|
||||
TheliaEvents::PRODUCT_REMOVE_CATEGORY => array("removeCategory", 128),
|
||||
|
||||
TheliaEvents::PRODUCT_SET_TEMPLATE => array("setProductTemplate", 128),
|
||||
|
||||
TheliaEvents::PRODUCT_FEATURE_UPDATE_VALUE => array("updateFeatureProductValue", 128),
|
||||
TheliaEvents::PRODUCT_FEATURE_DELETE_VALUE => array("deleteFeatureProductValue", 128),
|
||||
|
||||
TheliaEvents::TEMPLATE_DELETE_ATTRIBUTE => array("deleteTemplateAttribute", 128),
|
||||
TheliaEvents::TEMPLATE_DELETE_FEATURE => array("deleteTemplateFeature", 128),
|
||||
|
||||
// Those two have to be executed before
|
||||
TheliaEvents::IMAGE_DELETE => array("deleteImagePSEAssociations", 192),
|
||||
TheliaEvents::DOCUMENT_DELETE => array("deleteDocumentPSEAssociations", 192),
|
||||
|
||||
TheliaEvents::VIEW_CHECK => array('viewCheck', 128),
|
||||
TheliaEvents::VIEW_PRODUCT_ID_NOT_VISIBLE => array('viewProductIdNotVisible', 128),
|
||||
);
|
||||
}
|
||||
}
|
||||
521
core/lib/Thelia/Action/ProductSaleElement.php
Normal file
521
core/lib/Thelia/Action/ProductSaleElement.php
Normal file
@@ -0,0 +1,521 @@
|
||||
<?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\Propel;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Thelia\Core\Event\Product\ProductCloneEvent;
|
||||
use Thelia\Core\Event\Product\ProductCombinationGenerationEvent;
|
||||
use Thelia\Core\Event\ProductSaleElement\ProductSaleElementCreateEvent;
|
||||
use Thelia\Core\Event\ProductSaleElement\ProductSaleElementDeleteEvent;
|
||||
use Thelia\Core\Event\ProductSaleElement\ProductSaleElementUpdateEvent;
|
||||
use Thelia\Core\Event\TheliaEvents;
|
||||
use Thelia\Core\Template\Loop\ProductSaleElementsDocument;
|
||||
use Thelia\Core\Template\Loop\ProductSaleElementsImage;
|
||||
use Thelia\Model\AttributeAvQuery;
|
||||
use Thelia\Model\AttributeCombination;
|
||||
use Thelia\Model\AttributeCombinationQuery;
|
||||
use Thelia\Model\Map\AttributeCombinationTableMap;
|
||||
use Thelia\Model\Map\ProductSaleElementsTableMap;
|
||||
use Thelia\Model\ProductDocumentQuery;
|
||||
use Thelia\Model\ProductImageQuery;
|
||||
use Thelia\Model\ProductPrice;
|
||||
use Thelia\Model\ProductPriceQuery;
|
||||
use Thelia\Model\ProductSaleElements;
|
||||
use Thelia\Model\ProductSaleElementsProductDocument;
|
||||
use Thelia\Model\ProductSaleElementsProductDocumentQuery;
|
||||
use Thelia\Model\ProductSaleElementsProductImage;
|
||||
use Thelia\Model\ProductSaleElementsProductImageQuery;
|
||||
use Thelia\Model\ProductSaleElementsQuery;
|
||||
|
||||
class ProductSaleElement extends BaseAction implements EventSubscriberInterface
|
||||
{
|
||||
/** @var EventDispatcherInterface */
|
||||
protected $eventDispatcher;
|
||||
|
||||
public function __construct(EventDispatcherInterface $eventDispatcher)
|
||||
{
|
||||
$this->eventDispatcher = $eventDispatcher;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new product sale element, with or without combination
|
||||
*
|
||||
* @param ProductSaleElementCreateEvent $event
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function create(ProductSaleElementCreateEvent $event)
|
||||
{
|
||||
$con = Propel::getWriteConnection(ProductSaleElementsTableMap::DATABASE_NAME);
|
||||
|
||||
$con->beginTransaction();
|
||||
|
||||
try {
|
||||
// Check if we have a PSE without combination, this is the "default" PSE. Attach the combination to this PSE
|
||||
$salesElement = ProductSaleElementsQuery::create()
|
||||
->filterByProductId($event->getProduct()->getId())
|
||||
->joinAttributeCombination(null, Criteria::LEFT_JOIN)
|
||||
->add(AttributeCombinationTableMap::COL_PRODUCT_SALE_ELEMENTS_ID, null, Criteria::ISNULL)
|
||||
->findOne($con);
|
||||
|
||||
if ($salesElement == null) {
|
||||
// Create a new default product sale element
|
||||
$salesElement = $event->getProduct()->createProductSaleElement($con, 0, 0, 0, $event->getCurrencyId(), false);
|
||||
} else {
|
||||
// This (new) one is the default
|
||||
$salesElement->setIsDefault(true)->save($con);
|
||||
}
|
||||
|
||||
// Attach combination, if defined.
|
||||
$combinationAttributes = $event->getAttributeAvList();
|
||||
|
||||
if (\count($combinationAttributes) > 0) {
|
||||
foreach ($combinationAttributes as $attributeAvId) {
|
||||
$attributeAv = AttributeAvQuery::create()->findPk($attributeAvId);
|
||||
|
||||
if ($attributeAv !== null) {
|
||||
$attributeCombination = new AttributeCombination();
|
||||
|
||||
$attributeCombination
|
||||
->setAttributeAvId($attributeAvId)
|
||||
->setAttribute($attributeAv->getAttribute())
|
||||
->setProductSaleElements($salesElement)
|
||||
->save($con);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$event->setProductSaleElement($salesElement);
|
||||
|
||||
// Store all the stuff !
|
||||
$con->commit();
|
||||
} catch (\Exception $ex) {
|
||||
$con->rollback();
|
||||
|
||||
throw $ex;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update an existing product sale element
|
||||
*
|
||||
* @param ProductSaleElementUpdateEvent $event
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function update(ProductSaleElementUpdateEvent $event)
|
||||
{
|
||||
$salesElement = ProductSaleElementsQuery::create()->findPk($event->getProductSaleElementId());
|
||||
|
||||
$con = Propel::getWriteConnection(ProductSaleElementsTableMap::DATABASE_NAME);
|
||||
|
||||
$con->beginTransaction();
|
||||
|
||||
try {
|
||||
// Update the product's tax rule
|
||||
$event->getProduct()->setTaxRuleId($event->getTaxRuleId())->save($con);
|
||||
|
||||
// If product sale element is not defined, create it.
|
||||
if ($salesElement == null) {
|
||||
$salesElement = new ProductSaleElements();
|
||||
|
||||
$salesElement->setProduct($event->getProduct());
|
||||
}
|
||||
|
||||
$defaultStatus = $event->getIsDefault();
|
||||
|
||||
// If this PSE will become the default one, be sure to have *only one* default for this product
|
||||
if ($defaultStatus) {
|
||||
ProductSaleElementsQuery::create()
|
||||
->filterByProduct($event->getProduct())
|
||||
->filterByIsDefault(true)
|
||||
->filterById($event->getProductSaleElementId(), Criteria::NOT_EQUAL)
|
||||
->update(['IsDefault' => false], $con)
|
||||
;
|
||||
} else {
|
||||
// We will not allow the default PSE to become non default if no other default PSE exists for this product.
|
||||
if ($salesElement->getIsDefault() && ProductSaleElementsQuery::create()
|
||||
->filterByProduct($event->getProduct())
|
||||
->filterByIsDefault(true)
|
||||
->filterById($salesElement->getId(), Criteria::NOT_EQUAL)
|
||||
->count() === 0) {
|
||||
// Prevent setting the only default PSE to non-default
|
||||
$defaultStatus = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Update sale element
|
||||
$salesElement
|
||||
->setRef($event->getReference())
|
||||
->setQuantity($event->getQuantity())
|
||||
->setPromo($event->getOnsale())
|
||||
->setNewness($event->getIsnew())
|
||||
->setWeight($event->getWeight())
|
||||
->setIsDefault($defaultStatus)
|
||||
->setEanCode($event->getEanCode())
|
||||
->save()
|
||||
;
|
||||
|
||||
// Update/create price for current currency
|
||||
$productPrice = ProductPriceQuery::create()
|
||||
->filterByCurrencyId($event->getCurrencyId())
|
||||
->filterByProductSaleElementsId($salesElement->getId())
|
||||
->findOne($con);
|
||||
|
||||
// If price is not defined, create it.
|
||||
if ($productPrice == null) {
|
||||
$productPrice = new ProductPrice();
|
||||
|
||||
$productPrice
|
||||
->setProductSaleElements($salesElement)
|
||||
->setCurrencyId($event->getCurrencyId())
|
||||
;
|
||||
}
|
||||
|
||||
// Check if we have to store the price
|
||||
$productPrice->setFromDefaultCurrency($event->getFromDefaultCurrency());
|
||||
|
||||
if ($event->getFromDefaultCurrency() == 0) {
|
||||
// Store the price
|
||||
$productPrice
|
||||
->setPromoPrice($event->getSalePrice())
|
||||
->setPrice($event->getPrice())
|
||||
;
|
||||
} else {
|
||||
// Do not store the price.
|
||||
$productPrice
|
||||
->setPromoPrice(0)
|
||||
->setPrice(0)
|
||||
;
|
||||
}
|
||||
|
||||
$productPrice->save($con);
|
||||
|
||||
// Store all the stuff !
|
||||
$con->commit();
|
||||
} catch (\Exception $ex) {
|
||||
$con->rollback();
|
||||
|
||||
throw $ex;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a product sale element
|
||||
*
|
||||
* @param ProductSaleElementDeleteEvent $event
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function delete(ProductSaleElementDeleteEvent $event)
|
||||
{
|
||||
if (null !== $pse = ProductSaleElementsQuery::create()->findPk($event->getProductSaleElementId())) {
|
||||
$product = $pse->getProduct();
|
||||
|
||||
$con = Propel::getWriteConnection(ProductSaleElementsTableMap::DATABASE_NAME);
|
||||
|
||||
$con->beginTransaction();
|
||||
|
||||
try {
|
||||
// If we are deleting the last PSE of the product, don(t delete it, but insteaf
|
||||
// transform it into a PSE detached for any attribute combination, so that product
|
||||
// prices, weight, stock and attributes will not be lost.
|
||||
if ($product->countSaleElements($con) === 1) {
|
||||
$pse
|
||||
->setIsDefault(true)
|
||||
->save($con);
|
||||
// Delete the related attribute combination.
|
||||
AttributeCombinationQuery::create()
|
||||
->filterByProductSaleElementsId($pse->getId())
|
||||
->delete($con);
|
||||
} else {
|
||||
// Delete the PSE
|
||||
$pse->delete($con);
|
||||
// If we deleted the default PSE, make the last created one the default
|
||||
if ($pse->getIsDefault()) {
|
||||
$newDefaultPse = ProductSaleElementsQuery::create()
|
||||
->filterByProductId($product->getId())
|
||||
->filterById($pse->getId(), Criteria::NOT_EQUAL)
|
||||
->orderByCreatedAt(Criteria::DESC)
|
||||
->findOne($con)
|
||||
;
|
||||
if (null !== $newDefaultPse) {
|
||||
$newDefaultPse->setIsDefault(true)->save($con);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Store all the stuff !
|
||||
$con->commit();
|
||||
} catch (\Exception $ex) {
|
||||
$con->rollback();
|
||||
|
||||
throw $ex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate combinations. All existing combinations for the product are deleted.
|
||||
*
|
||||
* @param ProductCombinationGenerationEvent $event
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function generateCombinations(ProductCombinationGenerationEvent $event)
|
||||
{
|
||||
$con = Propel::getWriteConnection(ProductSaleElementsTableMap::DATABASE_NAME);
|
||||
|
||||
$con->beginTransaction();
|
||||
|
||||
try {
|
||||
// Delete all product's productSaleElement
|
||||
ProductSaleElementsQuery::create()->filterByProductId($event->product->getId())->delete();
|
||||
|
||||
$isDefault = true;
|
||||
|
||||
// Create all combinations
|
||||
foreach ($event->getCombinations() as $combinationAttributesAvIds) {
|
||||
// Create the PSE
|
||||
$saleElement = $event->getProduct()->createProductSaleElement(
|
||||
$con,
|
||||
$event->getWeight(),
|
||||
$event->getPrice(),
|
||||
$event->getSalePrice(),
|
||||
$event->getCurrencyId(),
|
||||
$isDefault,
|
||||
$event->getOnsale(),
|
||||
$event->getIsnew(),
|
||||
$event->getQuantity(),
|
||||
$event->getEanCode(),
|
||||
$event->getReference()
|
||||
);
|
||||
|
||||
$isDefault = false;
|
||||
|
||||
$this->createCombination($con, $saleElement, $combinationAttributesAvIds);
|
||||
}
|
||||
|
||||
// Store all the stuff !
|
||||
$con->commit();
|
||||
} catch (\Exception $ex) {
|
||||
$con->rollback();
|
||||
|
||||
throw $ex;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a combination for a given product sale element
|
||||
*
|
||||
* @param ConnectionInterface $con the Propel connection
|
||||
* @param ProductSaleElements $salesElement the product sale element
|
||||
* @param array $combinationAttributes an array oif attributes av IDs
|
||||
* @throws \Propel\Runtime\Exception\PropelException
|
||||
*/
|
||||
protected function createCombination(ConnectionInterface $con, ProductSaleElements $salesElement, $combinationAttributes)
|
||||
{
|
||||
foreach ($combinationAttributes as $attributeAvId) {
|
||||
$attributeAv = AttributeAvQuery::create()->findPk($attributeAvId);
|
||||
|
||||
if ($attributeAv !== null) {
|
||||
$attributeCombination = new AttributeCombination();
|
||||
|
||||
$attributeCombination
|
||||
->setAttributeAvId($attributeAvId)
|
||||
->setAttribute($attributeAv->getAttribute())
|
||||
->setProductSaleElements($salesElement)
|
||||
->save($con);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*******************
|
||||
* CLONING PROCESS *
|
||||
*******************/
|
||||
|
||||
/**
|
||||
* Clone product's PSEs and associated datas
|
||||
*
|
||||
* @param ProductCloneEvent $event
|
||||
* @throws \Propel\Runtime\Exception\PropelException
|
||||
*/
|
||||
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');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ProductCloneEvent $event
|
||||
* @param ProductSaleElements $originalProductPSE
|
||||
* @param $currencyId
|
||||
* @return int
|
||||
* @throws \Propel\Runtime\Exception\PropelException
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $clonedProductId
|
||||
* @param $clonedProductPSEId
|
||||
* @param $originalProductPSEFiles
|
||||
* @param $type
|
||||
* @throws \Propel\Runtime\Exception\PropelException
|
||||
*/
|
||||
public function clonePSEAssociatedFiles($clonedProductId, $clonedProductPSEId, $originalProductPSEFiles, $type)
|
||||
{
|
||||
/** @var ProductSaleElementsDocument|ProductSaleElementsImage $originalProductPSEFile */
|
||||
foreach ($originalProductPSEFiles as $originalProductPSEFile) {
|
||||
$originalProductFilePositionQuery = [];
|
||||
$originalProductPSEFileId = null;
|
||||
|
||||
// Get file's original position
|
||||
switch ($type) {
|
||||
case 'image':
|
||||
$originalProductFilePositionQuery = ProductImageQuery::create();
|
||||
$originalProductPSEFileId = $originalProductPSEFile->getProductImageId();
|
||||
break;
|
||||
case 'document':
|
||||
$originalProductFilePositionQuery = ProductDocumentQuery::create();
|
||||
$originalProductPSEFileId = $originalProductPSEFile->getProductDocumentId();
|
||||
break;
|
||||
}
|
||||
$originalProductFilePosition = $originalProductFilePositionQuery
|
||||
->select(['POSITION'])
|
||||
->findPk($originalProductPSEFileId);
|
||||
|
||||
// Get cloned file ID to link to the cloned PSE
|
||||
switch ($type) {
|
||||
case 'image':
|
||||
$clonedProductFileIdToLinkToPSEQuery = ProductImageQuery::create();
|
||||
break;
|
||||
case 'document':
|
||||
$clonedProductFileIdToLinkToPSEQuery = ProductDocumentQuery::create();
|
||||
break;
|
||||
}
|
||||
|
||||
$clonedProductFileIdToLinkToPSE = $clonedProductFileIdToLinkToPSEQuery
|
||||
->filterByProductId($clonedProductId)
|
||||
->filterByPosition($originalProductFilePosition)
|
||||
->select(['ID'])
|
||||
->findOne();
|
||||
|
||||
// Save association
|
||||
switch ($type) {
|
||||
case 'image':
|
||||
$assoc = new ProductSaleElementsProductImage();
|
||||
$assoc->setProductImageId($clonedProductFileIdToLinkToPSE);
|
||||
break;
|
||||
case 'document':
|
||||
$assoc = new ProductSaleElementsProductDocument();
|
||||
$assoc->setProductDocumentId($clonedProductFileIdToLinkToPSE);
|
||||
break;
|
||||
}
|
||||
$assoc
|
||||
->setProductSaleElementsId($clonedProductPSEId)
|
||||
->save();
|
||||
}
|
||||
}
|
||||
|
||||
/***************
|
||||
* END CLONING *
|
||||
***************/
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
TheliaEvents::PRODUCT_ADD_PRODUCT_SALE_ELEMENT => array("create", 128),
|
||||
TheliaEvents::PRODUCT_UPDATE_PRODUCT_SALE_ELEMENT => array("update", 128),
|
||||
TheliaEvents::PRODUCT_DELETE_PRODUCT_SALE_ELEMENT => array("delete", 128),
|
||||
TheliaEvents::PRODUCT_COMBINATION_GENERATION => array("generateCombinations", 128),
|
||||
TheliaEvents::PSE_CLONE => array("clonePSE", 128)
|
||||
);
|
||||
}
|
||||
}
|
||||
151
core/lib/Thelia/Action/Profile.php
Normal file
151
core/lib/Thelia/Action/Profile.php
Normal file
@@ -0,0 +1,151 @@
|
||||
<?php
|
||||
/*************************************************************************************/
|
||||
/* This file is part of the Thelia package. */
|
||||
/* */
|
||||
/* Copyright (c) OpenStudio */
|
||||
/* email : dev@thelia.net */
|
||||
/* web : http://www.thelia.net */
|
||||
/* */
|
||||
/* For the full copyright and license information, please view the LICENSE.txt */
|
||||
/* file that was distributed with this source code. */
|
||||
/*************************************************************************************/
|
||||
|
||||
namespace Thelia\Action;
|
||||
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Thelia\Core\Event\Profile\ProfileEvent;
|
||||
use Thelia\Core\Event\TheliaEvents;
|
||||
use Thelia\Core\Security\AccessManager;
|
||||
use Thelia\Model\ModuleQuery;
|
||||
use Thelia\Model\Profile as ProfileModel;
|
||||
use Thelia\Model\ProfileModule;
|
||||
use Thelia\Model\ProfileModuleQuery;
|
||||
use Thelia\Model\ProfileQuery;
|
||||
use Thelia\Model\ProfileResource;
|
||||
use Thelia\Model\ProfileResourceQuery;
|
||||
use Thelia\Model\ResourceQuery;
|
||||
|
||||
class Profile extends BaseAction implements EventSubscriberInterface
|
||||
{
|
||||
/**
|
||||
* @param ProfileEvent $event
|
||||
* @param $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function create(ProfileEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
$profile = new ProfileModel();
|
||||
|
||||
$profile
|
||||
->setDispatcher($dispatcher)
|
||||
->setCode($event->getCode())
|
||||
->setLocale($event->getLocale())
|
||||
->setTitle($event->getTitle())
|
||||
->setChapo($event->getChapo())
|
||||
->setDescription($event->getDescription())
|
||||
->setPostscriptum($event->getPostscriptum())
|
||||
;
|
||||
|
||||
$profile->save();
|
||||
|
||||
$event->setProfile($profile);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ProfileEvent $event
|
||||
* @param $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function update(ProfileEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
if (null !== $profile = ProfileQuery::create()->findPk($event->getId())) {
|
||||
$profile
|
||||
->setDispatcher($dispatcher)
|
||||
->setLocale($event->getLocale())
|
||||
->setTitle($event->getTitle())
|
||||
->setChapo($event->getChapo())
|
||||
->setDescription($event->getDescription())
|
||||
->setPostscriptum($event->getPostscriptum())
|
||||
;
|
||||
|
||||
$profile->save();
|
||||
|
||||
$event->setProfile($profile);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ProfileEvent $event
|
||||
*/
|
||||
public function updateResourceAccess(ProfileEvent $event)
|
||||
{
|
||||
if (null !== $profile = ProfileQuery::create()->findPk($event->getId())) {
|
||||
ProfileResourceQuery::create()->filterByProfileId($event->getId())->delete();
|
||||
foreach ($event->getResourceAccess() as $resourceCode => $accesses) {
|
||||
$manager = new AccessManager(0);
|
||||
$manager->build($accesses);
|
||||
|
||||
$profileResource = new ProfileResource();
|
||||
$profileResource->setProfileId($event->getId())
|
||||
->setResource(ResourceQuery::create()->findOneByCode($resourceCode))
|
||||
->setAccess($manager->getAccessValue());
|
||||
|
||||
$profileResource->save();
|
||||
}
|
||||
|
||||
$event->setProfile($profile);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ProfileEvent $event
|
||||
*/
|
||||
public function updateModuleAccess(ProfileEvent $event)
|
||||
{
|
||||
if (null !== $profile = ProfileQuery::create()->findPk($event->getId())) {
|
||||
ProfileModuleQuery::create()->filterByProfileId($event->getId())->delete();
|
||||
foreach ($event->getModuleAccess() as $moduleCode => $accesses) {
|
||||
$manager = new AccessManager(0);
|
||||
$manager->build($accesses);
|
||||
|
||||
$profileModule = new ProfileModule();
|
||||
$profileModule->setProfileId($event->getId())
|
||||
->setModule(ModuleQuery::create()->findOneByCode($moduleCode))
|
||||
->setAccess($manager->getAccessValue());
|
||||
|
||||
$profileModule->save();
|
||||
}
|
||||
|
||||
$event->setProfile($profile);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ProfileEvent $event
|
||||
*/
|
||||
public function delete(ProfileEvent $event)
|
||||
{
|
||||
if (null !== $profile = ProfileQuery::create()->findPk($event->getId())) {
|
||||
$profile
|
||||
->delete()
|
||||
;
|
||||
|
||||
$event->setProfile($profile);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
TheliaEvents::PROFILE_CREATE => array("create", 128),
|
||||
TheliaEvents::PROFILE_UPDATE => array("update", 128),
|
||||
TheliaEvents::PROFILE_DELETE => array("delete", 128),
|
||||
TheliaEvents::PROFILE_RESOURCE_ACCESS_UPDATE => array("updateResourceAccess", 128),
|
||||
TheliaEvents::PROFILE_MODULE_ACCESS_UPDATE => array("updateModuleAccess", 128),
|
||||
);
|
||||
}
|
||||
}
|
||||
60
core/lib/Thelia/Action/RedirectException.php
Normal file
60
core/lib/Thelia/Action/RedirectException.php
Normal 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),
|
||||
];
|
||||
}
|
||||
}
|
||||
498
core/lib/Thelia/Action/Sale.php
Normal file
498
core/lib/Thelia/Action/Sale.php
Normal file
@@ -0,0 +1,498 @@
|
||||
<?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\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
|
||||
* @throws PropelException
|
||||
*/
|
||||
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 may have been removed from the selection.
|
||||
$sale->setActive(false);
|
||||
|
||||
$dispatcher->dispatch(
|
||||
TheliaEvents::UPDATE_PRODUCT_SALE_STATUS,
|
||||
new ProductSaleStatusUpdateEvent($sale)
|
||||
);
|
||||
|
||||
$sale
|
||||
->setActive($event->getActive())
|
||||
->setStartDate($event->getStartDate())
|
||||
->setEndDate($event->getEndDate())
|
||||
->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)
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
// Update related products sale status if the Sale is active. This is not required if the sale is
|
||||
// not active, as we de-activated promotion for this sale at the beginning ofd this method
|
||||
if ($sale->getActive()) {
|
||||
$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 \Exception
|
||||
*/
|
||||
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),
|
||||
);
|
||||
}
|
||||
}
|
||||
63
core/lib/Thelia/Action/ShippingZone.php
Normal file
63
core/lib/Thelia/Action/ShippingZone.php
Normal file
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
/*************************************************************************************/
|
||||
/* This file is part of the Thelia package. */
|
||||
/* */
|
||||
/* Copyright (c) OpenStudio */
|
||||
/* email : dev@thelia.net */
|
||||
/* web : http://www.thelia.net */
|
||||
/* */
|
||||
/* For the full copyright and license information, please view the LICENSE.txt */
|
||||
/* file that was distributed with this source code. */
|
||||
/*************************************************************************************/
|
||||
|
||||
namespace Thelia\Action;
|
||||
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Thelia\Core\Event\ShippingZone\ShippingZoneAddAreaEvent;
|
||||
use Thelia\Core\Event\ShippingZone\ShippingZoneRemoveAreaEvent;
|
||||
use Thelia\Core\Event\TheliaEvents;
|
||||
use Thelia\Model\AreaDeliveryModule;
|
||||
use Thelia\Model\AreaDeliveryModuleQuery;
|
||||
|
||||
/**
|
||||
* Class ShippingZone
|
||||
* @package Thelia\Action
|
||||
* @author Manuel Raynaud <manu@raynaud.io>
|
||||
*/
|
||||
class ShippingZone extends BaseAction implements EventSubscriberInterface
|
||||
{
|
||||
public function addArea(ShippingZoneAddAreaEvent $event)
|
||||
{
|
||||
$areaDelivery = new AreaDeliveryModule();
|
||||
|
||||
$areaDelivery
|
||||
->setAreaId($event->getAreaId())
|
||||
->setDeliveryModuleId($event->getShippingZoneId())
|
||||
->save();
|
||||
}
|
||||
|
||||
public function removeArea(ShippingZoneRemoveAreaEvent $event)
|
||||
{
|
||||
$areaDelivery = AreaDeliveryModuleQuery::create()
|
||||
->filterByAreaId($event->getAreaId())
|
||||
->filterByDeliveryModuleId($event->getShippingZoneId())
|
||||
->findOne();
|
||||
|
||||
if ($areaDelivery) {
|
||||
$areaDelivery->delete();
|
||||
} else {
|
||||
throw new \RuntimeException(sprintf('areaDeliveryModule not found with area_id = %d and delivery_module_id = %d', $event->getAreaId(), $event->getShippingZoneId()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
TheliaEvents::SHIPPING_ZONE_ADD_AREA => array('addArea', 128),
|
||||
TheliaEvents::SHIPPING_ZONE_REMOVE_AREA => array('removeArea', 128),
|
||||
);
|
||||
}
|
||||
}
|
||||
103
core/lib/Thelia/Action/State.php
Normal file
103
core/lib/Thelia/Action/State.php
Normal 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)
|
||||
);
|
||||
}
|
||||
}
|
||||
95
core/lib/Thelia/Action/Tax.php
Normal file
95
core/lib/Thelia/Action/Tax.php
Normal file
@@ -0,0 +1,95 @@
|
||||
<?php
|
||||
/*************************************************************************************/
|
||||
/* This file is part of the Thelia package. */
|
||||
/* */
|
||||
/* Copyright (c) OpenStudio */
|
||||
/* email : dev@thelia.net */
|
||||
/* web : http://www.thelia.net */
|
||||
/* */
|
||||
/* For the full copyright and license information, please view the LICENSE.txt */
|
||||
/* file that was distributed with this source code. */
|
||||
/*************************************************************************************/
|
||||
|
||||
namespace Thelia\Action;
|
||||
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Thelia\Core\Event\Tax\TaxEvent;
|
||||
use Thelia\Core\Event\TheliaEvents;
|
||||
use Thelia\Model\Tax as TaxModel;
|
||||
use Thelia\Model\TaxQuery;
|
||||
|
||||
class Tax extends BaseAction implements EventSubscriberInterface
|
||||
{
|
||||
/**
|
||||
* @param TaxEvent $event
|
||||
* @param $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function create(TaxEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
$tax = new TaxModel();
|
||||
|
||||
$tax
|
||||
->setDispatcher($dispatcher)
|
||||
->setRequirements($event->getRequirements())
|
||||
->setType($event->getType())
|
||||
->setLocale($event->getLocale())
|
||||
->setTitle($event->getTitle())
|
||||
->setDescription($event->getDescription())
|
||||
;
|
||||
|
||||
$tax->save();
|
||||
|
||||
$event->setTax($tax);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TaxEvent $event
|
||||
* @param $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function update(TaxEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
if (null !== $tax = TaxQuery::create()->findPk($event->getId())) {
|
||||
$tax
|
||||
->setDispatcher($dispatcher)
|
||||
->setRequirements($event->getRequirements())
|
||||
->setType($event->getType())
|
||||
->setLocale($event->getLocale())
|
||||
->setTitle($event->getTitle())
|
||||
->setDescription($event->getDescription())
|
||||
;
|
||||
|
||||
$tax->save();
|
||||
|
||||
$event->setTax($tax);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TaxEvent $event
|
||||
*/
|
||||
public function delete(TaxEvent $event)
|
||||
{
|
||||
if (null !== $tax = TaxQuery::create()->findPk($event->getId())) {
|
||||
$tax
|
||||
->delete()
|
||||
;
|
||||
|
||||
$event->setTax($tax);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
TheliaEvents::TAX_CREATE => array("create", 128),
|
||||
TheliaEvents::TAX_UPDATE => array("update", 128),
|
||||
TheliaEvents::TAX_DELETE => array("delete", 128),
|
||||
);
|
||||
}
|
||||
}
|
||||
205
core/lib/Thelia/Action/TaxRule.php
Normal file
205
core/lib/Thelia/Action/TaxRule.php
Normal file
@@ -0,0 +1,205 @@
|
||||
<?php
|
||||
/*************************************************************************************/
|
||||
/* This file is part of the Thelia package. */
|
||||
/* */
|
||||
/* Copyright (c) OpenStudio */
|
||||
/* email : dev@thelia.net */
|
||||
/* web : http://www.thelia.net */
|
||||
/* */
|
||||
/* For the full copyright and license information, please view the LICENSE.txt */
|
||||
/* file that was distributed with this source code. */
|
||||
/*************************************************************************************/
|
||||
|
||||
namespace Thelia\Action;
|
||||
|
||||
use Propel\Runtime\ActiveQuery\Criteria;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Thelia\Core\Event\Tax\TaxRuleEvent;
|
||||
use Thelia\Core\Event\TheliaEvents;
|
||||
use Thelia\Model\TaxRuleCountry;
|
||||
use Thelia\Model\TaxRuleCountryQuery;
|
||||
use Thelia\Model\TaxRule as TaxRuleModel;
|
||||
use Thelia\Model\TaxRuleQuery;
|
||||
|
||||
class TaxRule extends BaseAction implements EventSubscriberInterface
|
||||
{
|
||||
/**
|
||||
* @param TaxRuleEvent $event
|
||||
*/
|
||||
public function create(TaxRuleEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
$taxRule = new TaxRuleModel();
|
||||
|
||||
$taxRule
|
||||
->setDispatcher($dispatcher)
|
||||
->setLocale($event->getLocale())
|
||||
->setTitle($event->getTitle())
|
||||
->setDescription($event->getDescription())
|
||||
;
|
||||
|
||||
$taxRule->save();
|
||||
|
||||
$event->setTaxRule($taxRule)->setId($taxRule->getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TaxRuleEvent $event
|
||||
*/
|
||||
public function update(TaxRuleEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
if (null !== $taxRule = TaxRuleQuery::create()->findPk($event->getId())) {
|
||||
$taxRule
|
||||
->setDispatcher($dispatcher)
|
||||
->setLocale($event->getLocale())
|
||||
->setTitle($event->getTitle())
|
||||
->setDescription($event->getDescription())
|
||||
->save()
|
||||
;
|
||||
|
||||
$event->setTaxRule($taxRule);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TaxRuleEvent $event
|
||||
*/
|
||||
public function updateTaxes(TaxRuleEvent $event)
|
||||
{
|
||||
if (null !== $taxRule = TaxRuleQuery::create()->findPk($event->getId())) {
|
||||
$taxList = $this->getArrayFromJson($event->getTaxList());
|
||||
$countryList = $this->getArrayFromJson22Compat($event->getCountryList());
|
||||
$countryDeletedList = $this->getArrayFromJson22Compat($event->getCountryDeletedList());
|
||||
|
||||
/* clean the current tax rule for the countries/states */
|
||||
$deletes = array_merge($countryList, $countryDeletedList);
|
||||
foreach ($deletes as $item) {
|
||||
TaxRuleCountryQuery::create()
|
||||
->filterByTaxRule($taxRule)
|
||||
->filterByCountryId(\intval($item[0]), Criteria::EQUAL)
|
||||
->filterByStateId(\intval($item[1]) !== 0 ? $item[1] : null, Criteria::EQUAL)
|
||||
->delete();
|
||||
}
|
||||
|
||||
/* for each country */
|
||||
foreach ($countryList as $item) {
|
||||
$position = 1;
|
||||
$countryId = \intval($item[0]);
|
||||
$stateId = \intval($item[1]);
|
||||
|
||||
/* on applique les nouvelles regles */
|
||||
foreach ($taxList as $tax) {
|
||||
if (\is_array($tax)) {
|
||||
foreach ($tax as $samePositionTax) {
|
||||
$taxModel = new TaxRuleCountry();
|
||||
$taxModel->setTaxRule($taxRule)
|
||||
->setCountryId($countryId)
|
||||
->setStateId($stateId ?: null)
|
||||
->setTaxId($samePositionTax)
|
||||
->setPosition($position);
|
||||
$taxModel->save();
|
||||
}
|
||||
} else {
|
||||
$taxModel = new TaxRuleCountry();
|
||||
$taxModel->setTaxRule($taxRule)
|
||||
->setCountryId($countryId)
|
||||
->setStateId($stateId ?: null)
|
||||
->setTaxId($tax)
|
||||
->setPosition($position);
|
||||
$taxModel->save();
|
||||
}
|
||||
$position++;
|
||||
}
|
||||
}
|
||||
|
||||
$event->setTaxRule($taxRule);
|
||||
}
|
||||
}
|
||||
|
||||
protected function getArrayFromJson($obj)
|
||||
{
|
||||
if (\is_null($obj)) {
|
||||
$obj = [];
|
||||
} else {
|
||||
$obj = \is_array($obj)
|
||||
? $obj
|
||||
: json_decode($obj, true);
|
||||
}
|
||||
|
||||
return $obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method ensures compatibility with the 2.2.x country arrays passed throught the TaxRuleEvent
|
||||
*
|
||||
* In 2.2.x, the TaxRuleEvent::getXXXCountryList() methods returned an array of country IDs. [ country ID, country ID ...].
|
||||
* From 2.3.0-alpha1, these functions are expected to return an array of arrays, each one containing a country ID and
|
||||
* a state ID. [ [ country ID, state ID], [ country ID, state ID], ...].
|
||||
*
|
||||
* This method checks the $obj parameter, and create a 2.3.0-alpha1 compatible return value if $obj is expressed using
|
||||
* the 2.2.x form.
|
||||
*
|
||||
* @param array $obj
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getArrayFromJson22Compat($obj)
|
||||
{
|
||||
$obj = $this->getArrayFromJson($obj);
|
||||
|
||||
if (isset($obj[0]) && ! \is_array($obj[0])) {
|
||||
$objEx = [];
|
||||
foreach ($obj as $item) {
|
||||
$objEx[] = [$item, 0];
|
||||
}
|
||||
|
||||
return $objEx;
|
||||
}
|
||||
|
||||
return $obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TaxRuleEvent $event
|
||||
*/
|
||||
public function delete(TaxRuleEvent $event)
|
||||
{
|
||||
if (null !== $taxRule = TaxRuleQuery::create()->findPk($event->getId())) {
|
||||
$taxRule
|
||||
->delete()
|
||||
;
|
||||
|
||||
$event->setTaxRule($taxRule);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TaxRuleEvent $event
|
||||
*/
|
||||
public function setDefault(TaxRuleEvent $event)
|
||||
{
|
||||
if (null !== $taxRule = TaxRuleQuery::create()->findPk($event->getId())) {
|
||||
TaxRuleQuery::create()->update(array(
|
||||
"IsDefault" => 0
|
||||
));
|
||||
|
||||
$taxRule->setIsDefault(1)->save();
|
||||
|
||||
$event->setTaxRule($taxRule);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
TheliaEvents::TAX_RULE_CREATE => array("create", 128),
|
||||
TheliaEvents::TAX_RULE_UPDATE => array("update", 128),
|
||||
TheliaEvents::TAX_RULE_TAXES_UPDATE => array("updateTaxes", 128),
|
||||
TheliaEvents::TAX_RULE_DELETE => array("delete", 128),
|
||||
TheliaEvents::TAX_RULE_SET_DEFAULT => array("setDefault", 128),
|
||||
);
|
||||
}
|
||||
}
|
||||
288
core/lib/Thelia/Action/Template.php
Normal file
288
core/lib/Thelia/Action/Template.php
Normal file
@@ -0,0 +1,288 @@
|
||||
<?php
|
||||
/*************************************************************************************/
|
||||
/* This file is part of the Thelia package. */
|
||||
/* */
|
||||
/* Copyright (c) OpenStudio */
|
||||
/* email : dev@thelia.net */
|
||||
/* web : http://www.thelia.net */
|
||||
/* */
|
||||
/* For the full copyright and license information, please view the LICENSE.txt */
|
||||
/* file that was distributed with this source code. */
|
||||
/*************************************************************************************/
|
||||
|
||||
namespace Thelia\Action;
|
||||
|
||||
use Propel\Runtime\Propel;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Thelia\Core\Event\Template\TemplateAddAttributeEvent;
|
||||
use Thelia\Core\Event\Template\TemplateAddFeatureEvent;
|
||||
use Thelia\Core\Event\Template\TemplateCreateEvent;
|
||||
use Thelia\Core\Event\Template\TemplateDeleteAttributeEvent;
|
||||
use Thelia\Core\Event\Template\TemplateDeleteEvent;
|
||||
use Thelia\Core\Event\Template\TemplateDeleteFeatureEvent;
|
||||
use Thelia\Core\Event\Template\TemplateDuplicateEvent;
|
||||
use Thelia\Core\Event\Template\TemplateUpdateEvent;
|
||||
use Thelia\Core\Event\TheliaEvents;
|
||||
use Thelia\Core\Event\UpdatePositionEvent;
|
||||
use Thelia\Core\Translation\Translator;
|
||||
use Thelia\Model\AttributeTemplate;
|
||||
use Thelia\Model\AttributeTemplateQuery;
|
||||
use Thelia\Model\CategoryQuery;
|
||||
use Thelia\Model\FeatureTemplate;
|
||||
use Thelia\Model\FeatureTemplateQuery;
|
||||
use Thelia\Model\Map\TemplateTableMap;
|
||||
use Thelia\Model\ProductQuery;
|
||||
use Thelia\Model\Template as TemplateModel;
|
||||
use Thelia\Model\TemplateQuery;
|
||||
|
||||
class Template extends BaseAction implements EventSubscriberInterface
|
||||
{
|
||||
/**
|
||||
* Create a new template entry
|
||||
*
|
||||
* @param \Thelia\Core\Event\Template\TemplateCreateEvent $event
|
||||
* @param $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function create(TemplateCreateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
$template = new TemplateModel();
|
||||
|
||||
$template
|
||||
->setDispatcher($dispatcher)
|
||||
|
||||
->setLocale($event->getLocale())
|
||||
->setName($event->getTemplateName())
|
||||
|
||||
->save()
|
||||
;
|
||||
|
||||
$event->setTemplate($template);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dupliucate an existing template entry
|
||||
*
|
||||
* @param \Thelia\Core\Event\Template\TemplateCreateEvent $event
|
||||
* @param $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function duplicate(TemplateDuplicateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
if (null !== $source = TemplateQuery::create()->findPk($event->getSourceTemplateId())) {
|
||||
$source->setLocale($event->getLocale());
|
||||
|
||||
$createEvent = new TemplateCreateEvent();
|
||||
$createEvent
|
||||
->setLocale($event->getLocale())
|
||||
->setTemplateName(
|
||||
Translator::getInstance()->trans("Copy of %tpl", ["%tpl" => $source->getName() ])
|
||||
);
|
||||
|
||||
$dispatcher->dispatch(TheliaEvents::TEMPLATE_CREATE, $createEvent);
|
||||
|
||||
$clone = $createEvent->getTemplate();
|
||||
|
||||
$attrList = AttributeTemplateQuery::create()->findByTemplateId($source->getId());
|
||||
|
||||
/** @var $feat AttributeTemplate */
|
||||
foreach ($attrList as $feat) {
|
||||
$dispatcher->dispatch(
|
||||
TheliaEvents::TEMPLATE_ADD_ATTRIBUTE,
|
||||
new TemplateAddAttributeEvent($clone, $feat->getAttributeId())
|
||||
);
|
||||
}
|
||||
|
||||
$featList = FeatureTemplateQuery::create()->findByTemplateId($source->getId());
|
||||
|
||||
/** @var $feat FeatureTemplate */
|
||||
foreach ($featList as $feat) {
|
||||
$dispatcher->dispatch(
|
||||
TheliaEvents::TEMPLATE_ADD_FEATURE,
|
||||
new TemplateAddFeatureEvent($clone, $feat->getFeatureId())
|
||||
);
|
||||
}
|
||||
|
||||
$event->setTemplate($clone);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Change a product template
|
||||
*
|
||||
* @param \Thelia\Core\Event\Template\TemplateUpdateEvent $event
|
||||
* @param $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function update(TemplateUpdateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
if (null !== $template = TemplateQuery::create()->findPk($event->getTemplateId())) {
|
||||
$template
|
||||
->setDispatcher($dispatcher)
|
||||
|
||||
->setLocale($event->getLocale())
|
||||
->setName($event->getTemplateName())
|
||||
->save();
|
||||
|
||||
$event->setTemplate($template);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a product template entry
|
||||
*
|
||||
* @param \Thelia\Core\Event\Template\TemplateDeleteEvent $event
|
||||
* @param $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function delete(TemplateDeleteEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
if (null !== ($template = TemplateQuery::create()->findPk($event->getTemplateId()))) {
|
||||
// Check if template is used by a product
|
||||
$productCount = ProductQuery::create()->findByTemplateId($template->getId())->count();
|
||||
|
||||
if ($productCount <= 0) {
|
||||
$con = Propel::getWriteConnection(TemplateTableMap::DATABASE_NAME);
|
||||
$con->beginTransaction();
|
||||
|
||||
try {
|
||||
$template
|
||||
->setDispatcher($dispatcher)
|
||||
->delete($con);
|
||||
|
||||
// We have to also delete any reference of this template in category tables
|
||||
// We can't use a FK here, as the DefaultTemplateId column may be NULL
|
||||
// so let's take care of this.
|
||||
CategoryQuery::create()
|
||||
->filterByDefaultTemplateId($event->getTemplateId())
|
||||
->update([ 'DefaultTemplateId' => null], $con);
|
||||
|
||||
$con->commit();
|
||||
} catch (\Exception $ex) {
|
||||
$con->rollback();
|
||||
|
||||
throw $ex;
|
||||
}
|
||||
}
|
||||
|
||||
$event->setTemplate($template);
|
||||
|
||||
$event->setProductCount($productCount);
|
||||
}
|
||||
}
|
||||
|
||||
public function addAttribute(TemplateAddAttributeEvent $event)
|
||||
{
|
||||
if (null === AttributeTemplateQuery::create()
|
||||
->filterByAttributeId($event->getAttributeId())
|
||||
->filterByTemplate($event->getTemplate())
|
||||
->findOne()) {
|
||||
$attributeTemplate = new AttributeTemplate();
|
||||
|
||||
$attributeTemplate
|
||||
->setAttributeId($event->getAttributeId())
|
||||
->setTemplate($event->getTemplate())
|
||||
->save()
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes position, selecting absolute ou relative change.
|
||||
*
|
||||
* @param UpdatePositionEvent $event
|
||||
* @param $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function updateAttributePosition(UpdatePositionEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
$this->genericUpdatePosition(AttributeTemplateQuery::create(), $event, $dispatcher);
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes position, selecting absolute ou relative change.
|
||||
*
|
||||
* @param UpdatePositionEvent $event
|
||||
* @param $eventName
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function updateFeaturePosition(UpdatePositionEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
$this->genericUpdatePosition(FeatureTemplateQuery::create(), $event, $dispatcher);
|
||||
}
|
||||
|
||||
public function deleteAttribute(TemplateDeleteAttributeEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
$attributeTemplate = AttributeTemplateQuery::create()
|
||||
->filterByAttributeId($event->getAttributeId())
|
||||
->filterByTemplate($event->getTemplate())->findOne()
|
||||
;
|
||||
|
||||
if ($attributeTemplate !== null) {
|
||||
$attributeTemplate
|
||||
->setDispatcher($dispatcher)
|
||||
->delete();
|
||||
} else {
|
||||
// Prevent event propagation
|
||||
$event->stopPropagation();
|
||||
}
|
||||
}
|
||||
|
||||
public function addFeature(TemplateAddFeatureEvent $event)
|
||||
{
|
||||
if (null === FeatureTemplateQuery::create()
|
||||
->filterByFeatureId($event->getFeatureId())
|
||||
->filterByTemplate($event->getTemplate())
|
||||
->findOne()
|
||||
) {
|
||||
$featureTemplate = new FeatureTemplate();
|
||||
|
||||
$featureTemplate
|
||||
->setFeatureId($event->getFeatureId())
|
||||
->setTemplate($event->getTemplate())
|
||||
->save()
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
public function deleteFeature(TemplateDeleteFeatureEvent $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
$featureTemplate = FeatureTemplateQuery::create()
|
||||
->filterByFeatureId($event->getFeatureId())
|
||||
->filterByTemplate($event->getTemplate())->findOne()
|
||||
;
|
||||
|
||||
if ($featureTemplate !== null) {
|
||||
$featureTemplate
|
||||
->setDispatcher($dispatcher)
|
||||
->delete();
|
||||
} else {
|
||||
// Prevent event propagation
|
||||
$event->stopPropagation();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
TheliaEvents::TEMPLATE_CREATE => array("create", 128),
|
||||
TheliaEvents::TEMPLATE_UPDATE => array("update", 128),
|
||||
TheliaEvents::TEMPLATE_DELETE => array("delete", 128),
|
||||
TheliaEvents::TEMPLATE_DUPLICATE => array("duplicate", 128),
|
||||
|
||||
TheliaEvents::TEMPLATE_ADD_ATTRIBUTE => array("addAttribute", 128),
|
||||
TheliaEvents::TEMPLATE_DELETE_ATTRIBUTE => array("deleteAttribute", 128),
|
||||
|
||||
TheliaEvents::TEMPLATE_ADD_FEATURE => array("addFeature", 128),
|
||||
TheliaEvents::TEMPLATE_DELETE_FEATURE => array("deleteFeature", 128),
|
||||
|
||||
TheliaEvents::TEMPLATE_CHANGE_ATTRIBUTE_POSITION => array('updateAttributePosition', 128),
|
||||
TheliaEvents::TEMPLATE_CHANGE_FEATURE_POSITION => array('updateFeaturePosition', 128),
|
||||
);
|
||||
}
|
||||
}
|
||||
372
core/lib/Thelia/Action/Translation.php
Normal file
372
core/lib/Thelia/Action/Translation.php
Normal 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]
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
54
core/lib/Thelia/Cart/CartTrait.php
Normal file
54
core/lib/Thelia/Cart/CartTrait.php
Normal file
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
/*************************************************************************************/
|
||||
/* This file is part of the Thelia package. */
|
||||
/* */
|
||||
/* Copyright (c) OpenStudio */
|
||||
/* email : dev@thelia.net */
|
||||
/* web : http://www.thelia.net */
|
||||
/* */
|
||||
/* For the full copyright and license information, please view the LICENSE.txt */
|
||||
/* file that was distributed with this source code. */
|
||||
/*************************************************************************************/
|
||||
|
||||
namespace Thelia\Cart;
|
||||
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Thelia\Core\HttpFoundation\Request;
|
||||
use Thelia\Model\CartQuery;
|
||||
use Thelia\Model\Cart as CartModel;
|
||||
use Thelia\Model\ConfigQuery;
|
||||
use Thelia\Model\Customer;
|
||||
use Thelia\Core\HttpFoundation\Session\Session;
|
||||
use Thelia\Core\Event\TheliaEvents;
|
||||
use Thelia\Core\Event\Cart\CartEvent;
|
||||
|
||||
/**
|
||||
* managed cart
|
||||
*
|
||||
* Trait CartTrait
|
||||
* @package Thelia\Cart
|
||||
* @author Manuel Raynaud <manu@raynaud.io>
|
||||
*
|
||||
* @deprecated CartTrait is deprecated, please use Session::getSessionCart method instead
|
||||
*/
|
||||
trait CartTrait
|
||||
{
|
||||
/**
|
||||
*
|
||||
* search if cart already exists in session. If not try to create a new one or duplicate an old one.
|
||||
*
|
||||
* @param EventDispatcherInterface $dispatcher the event dispatcher
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
* @deprecated use Session::getSessionCart method instead
|
||||
* @return \Thelia\Model\Cart
|
||||
*/
|
||||
public function getCart(EventDispatcherInterface $dispatcher, Request $request)
|
||||
{
|
||||
trigger_error(
|
||||
'CartTrait is deprecated, please use Session::getSessionCart method instead',
|
||||
E_USER_DEPRECATED
|
||||
);
|
||||
|
||||
return $request->getSession()->getSessionCart($dispatcher);
|
||||
}
|
||||
}
|
||||
98
core/lib/Thelia/Command/AdminUpdatePasswordCommand.php
Normal file
98
core/lib/Thelia/Command/AdminUpdatePasswordCommand.php
Normal file
@@ -0,0 +1,98 @@
|
||||
<?php
|
||||
/*************************************************************************************/
|
||||
/* This file is part of the Thelia package. */
|
||||
/* */
|
||||
/* Copyright (c) OpenStudio */
|
||||
/* email : dev@thelia.net */
|
||||
/* web : http://www.thelia.net */
|
||||
/* */
|
||||
/* For the full copyright and license information, please view the LICENSE.txt */
|
||||
/* file that was distributed with this source code. */
|
||||
/*************************************************************************************/
|
||||
|
||||
namespace Thelia\Command;
|
||||
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage;
|
||||
use Thelia\Core\Event\Administrator\AdministratorUpdatePasswordEvent;
|
||||
use Thelia\Core\Event\TheliaEvents;
|
||||
use Thelia\Core\HttpFoundation\Request;
|
||||
use Thelia\Core\HttpFoundation\Session\Session;
|
||||
use Thelia\Model\AdminQuery;
|
||||
use Thelia\Tools\Password;
|
||||
|
||||
/**
|
||||
* command line for updating admin password
|
||||
*
|
||||
* php Thelia admin:updatePassword
|
||||
*
|
||||
* Class AdminUpdatePasswordCommand
|
||||
* @package Thelia\Command
|
||||
* @author Manuel Raynaud <manu@raynaud.io>
|
||||
*/
|
||||
class AdminUpdatePasswordCommand extends ContainerAwareCommand
|
||||
{
|
||||
protected function init()
|
||||
{
|
||||
$container = $this->getContainer();
|
||||
|
||||
$request = new Request();
|
||||
$request->setSession(new Session(new MockArraySessionStorage()));
|
||||
|
||||
/** @var RequestStack $requestStack */
|
||||
$requestStack = $container->get('request_stack');
|
||||
$requestStack->push($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the current command.
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setName('admin:updatePassword')
|
||||
->setDescription('change administrator password')
|
||||
->setHelp('The <info>admin:updatePassword</info> command allows you to change the password for a given administrator')
|
||||
->addArgument(
|
||||
'login',
|
||||
InputArgument::REQUIRED,
|
||||
'Login for administrator you want to change the password'
|
||||
)
|
||||
->addOption(
|
||||
'password',
|
||||
null,
|
||||
InputOption::VALUE_REQUIRED,
|
||||
'Desired password. If this option is omitted, a random password is generated and shown in this prompt after'
|
||||
)
|
||||
;
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$this->init();
|
||||
|
||||
$login = $input->getArgument('login');
|
||||
|
||||
if (null === $admin = AdminQuery::create()->filterByLogin($login)->findOne()) {
|
||||
throw new \RuntimeException(sprintf('Admin with login %s does not exists', $login));
|
||||
}
|
||||
|
||||
$password = $input->getOption('password') ?: Password::generateRandom();
|
||||
|
||||
$event = new AdministratorUpdatePasswordEvent($admin);
|
||||
$event->setPassword($password);
|
||||
|
||||
$this->getDispatcher()->dispatch(TheliaEvents::ADMINISTRATOR_UPDATEPASSWORD, $event);
|
||||
|
||||
$output->writeln(array(
|
||||
'',
|
||||
sprintf('<info>admin %s password updated</info>', $login),
|
||||
sprintf('<info>new password is : %s</info>', $password),
|
||||
''
|
||||
));
|
||||
}
|
||||
}
|
||||
112
core/lib/Thelia/Command/BaseModuleGenerate.php
Normal file
112
core/lib/Thelia/Command/BaseModuleGenerate.php
Normal file
@@ -0,0 +1,112 @@
|
||||
<?php
|
||||
/*************************************************************************************/
|
||||
/* This file is part of the Thelia package. */
|
||||
/* */
|
||||
/* Copyright (c) OpenStudio */
|
||||
/* email : dev@thelia.net */
|
||||
/* web : http://www.thelia.net */
|
||||
/* */
|
||||
/* For the full copyright and license information, please view the LICENSE.txt */
|
||||
/* file that was distributed with this source code. */
|
||||
/*************************************************************************************/
|
||||
|
||||
namespace Thelia\Command;
|
||||
|
||||
use Propel\Generator\Builder\Util\SchemaReader;
|
||||
use Propel\Generator\Exception\SchemaException;
|
||||
use Propel\Generator\Model\Schema;
|
||||
use Symfony\Component\Filesystem\Filesystem;
|
||||
use Thelia\Core\Propel\Schema\SchemaCombiner;
|
||||
use Thelia\Core\Propel\Schema\SchemaLocator;
|
||||
use Thelia\Core\PropelInitService;
|
||||
use Thelia\Module\Validator\ModuleValidator;
|
||||
|
||||
/**
|
||||
* base class for module commands
|
||||
*
|
||||
* Class BaseModuleGenerate
|
||||
* @package Thelia\Command
|
||||
* @author Manuel Raynaud <manu@raynaud.io>
|
||||
*/
|
||||
abstract class BaseModuleGenerate extends ContainerAwareCommand
|
||||
{
|
||||
protected $module;
|
||||
protected $moduleDirectory;
|
||||
|
||||
protected $reservedKeyWords = array(
|
||||
'thelia'
|
||||
);
|
||||
|
||||
protected $neededDirectories = array(
|
||||
'Config',
|
||||
'Model',
|
||||
'Loop',
|
||||
'Command',
|
||||
'Controller',
|
||||
'EventListeners',
|
||||
'I18n',
|
||||
'templates',
|
||||
'Hook',
|
||||
);
|
||||
|
||||
protected function verifyExistingModule()
|
||||
{
|
||||
if (file_exists($this->moduleDirectory)) {
|
||||
throw new \RuntimeException(
|
||||
sprintf(
|
||||
"%s module already exists. Use --force option to force generation.",
|
||||
$this->module
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
protected function formatModuleName($name)
|
||||
{
|
||||
if (\in_array(strtolower($name), $this->reservedKeyWords)) {
|
||||
throw new \RuntimeException(sprintf("%s module name is a reserved keyword", $name));
|
||||
}
|
||||
|
||||
return ucfirst($name);
|
||||
}
|
||||
|
||||
protected function validModuleName($name)
|
||||
{
|
||||
if (!preg_match('#^[A-Z]([A-Za-z\d])+$#', $name)) {
|
||||
throw new \RuntimeException(
|
||||
sprintf("%s module name is not a valid name, it must be in CamelCase. (ex: MyModuleName)", $name)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
protected function checkModuleSchema()
|
||||
{
|
||||
$moduleValidator = new ModuleValidator($this->moduleDirectory);
|
||||
$moduleValidator->checkModulePropelSchema();
|
||||
}
|
||||
|
||||
protected function generateGlobalSchemaForModule()
|
||||
{
|
||||
/** @var SchemaLocator $schemaLocator */
|
||||
$schemaLocator = $this->getContainer()->get('thelia.propel.schema.locator');
|
||||
/** @var PropelInitService $propelInitService */
|
||||
$propelInitService = $this->getContainer()->get('thelia.propel.init');
|
||||
|
||||
$schemaCombiner = new SchemaCombiner(
|
||||
$schemaLocator->findForModules([$this->module])
|
||||
);
|
||||
|
||||
$fs = new Filesystem();
|
||||
$schemasDir = "{$propelInitService->getPropelCacheDir()}/schema-{$this->module}";
|
||||
$fs->mkdir($schemasDir);
|
||||
|
||||
foreach ($schemaCombiner->getDatabases() as $database) {
|
||||
file_put_contents(
|
||||
"{$schemasDir}/{$database}.schema.xml",
|
||||
$schemaCombiner->getCombinedDocument($database)->saveXML()
|
||||
);
|
||||
}
|
||||
|
||||
return $schemasDir;
|
||||
}
|
||||
}
|
||||
112
core/lib/Thelia/Command/CacheClear.php
Normal file
112
core/lib/Thelia/Command/CacheClear.php
Normal file
@@ -0,0 +1,112 @@
|
||||
<?php
|
||||
/*************************************************************************************/
|
||||
/* This file is part of the Thelia package. */
|
||||
/* */
|
||||
/* Copyright (c) OpenStudio */
|
||||
/* email : dev@thelia.net */
|
||||
/* web : http://www.thelia.net */
|
||||
/* */
|
||||
/* For the full copyright and license information, please view the LICENSE.txt */
|
||||
/* file that was distributed with this source code. */
|
||||
/*************************************************************************************/
|
||||
|
||||
namespace Thelia\Command;
|
||||
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Filesystem\Exception\IOException;
|
||||
use Thelia\Core\Event\Cache\CacheEvent;
|
||||
use Thelia\Core\Event\TheliaEvents;
|
||||
use Thelia\Model\ConfigQuery;
|
||||
|
||||
/**
|
||||
* clear the cache
|
||||
*
|
||||
* Class CacheClear
|
||||
* @package Thelia\Command
|
||||
* @author Manuel Raynaud <manu@raynaud.io>
|
||||
*
|
||||
*/
|
||||
class CacheClear extends ContainerAwareCommand
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setName("cache:clear")
|
||||
->setDescription("Invalidate all caches")
|
||||
->addOption(
|
||||
"without-assets",
|
||||
null,
|
||||
InputOption::VALUE_NONE,
|
||||
"do not clear the assets cache in the web space"
|
||||
)
|
||||
->addOption(
|
||||
'with-images',
|
||||
null,
|
||||
InputOption::VALUE_NONE,
|
||||
'clear images generated in `image_cache_dir_from_web_root` or web/cache/images directory'
|
||||
)
|
||||
->addOption(
|
||||
'with-documents',
|
||||
null,
|
||||
InputOption::VALUE_NONE,
|
||||
'clear documents generated in `document_cache_dir_from_web_root` or web/cache/documents directory'
|
||||
)
|
||||
;
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$cacheDir = $this->getContainer()->getParameter("kernel.cache_dir");
|
||||
|
||||
$this->clearCache($cacheDir, $output);
|
||||
|
||||
if (!$input->getOption('without-assets')) {
|
||||
$this->clearCache(THELIA_WEB_DIR . ConfigQuery::read('asset_dir_from_web_root', 'assets'), $output);
|
||||
}
|
||||
|
||||
if ($input->getOption('with-images')) {
|
||||
$this->clearCache(
|
||||
THELIA_WEB_DIR . ConfigQuery::read(
|
||||
'image_cache_dir_from_web_root',
|
||||
'cache' . DS . 'images'
|
||||
),
|
||||
$output
|
||||
);
|
||||
}
|
||||
|
||||
if ($input->getOption('with-documents')) {
|
||||
$this->clearCache(
|
||||
THELIA_WEB_DIR . ConfigQuery::read(
|
||||
'document_cache_dir_from_web_root',
|
||||
'cache' . DS . 'documents'
|
||||
),
|
||||
$output
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
protected function clearCache($dir, OutputInterface $output)
|
||||
{
|
||||
$output->writeln(sprintf("Clearing cache in <info>%s</info> directory", $dir));
|
||||
|
||||
try {
|
||||
$cacheEvent = new CacheEvent($dir, false);
|
||||
$this->getDispatcher()->dispatch(TheliaEvents::CACHE_CLEAR, $cacheEvent);
|
||||
} catch (\UnexpectedValueException $e) {
|
||||
// throws same exception code for does not exist and permission denied ...
|
||||
if (!file_exists($dir)) {
|
||||
$output->writeln(sprintf("<info>%s cache dir already cleared</info>", $dir));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
throw $e;
|
||||
} catch (IOException $e) {
|
||||
$output->writeln(sprintf("Error during clearing of cache : %s", $e->getMessage()));
|
||||
}
|
||||
|
||||
$output->writeln(sprintf("<info>%s cache directory cleared successfully</info>", $dir));
|
||||
}
|
||||
}
|
||||
53
core/lib/Thelia/Command/ClearImageCache.php
Normal file
53
core/lib/Thelia/Command/ClearImageCache.php
Normal file
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
/*************************************************************************************/
|
||||
/* This file is part of the Thelia package. */
|
||||
/* */
|
||||
/* Copyright (c) OpenStudio */
|
||||
/* email : dev@thelia.net */
|
||||
/* web : http://www.thelia.net */
|
||||
/* */
|
||||
/* For the full copyright and license information, please view the LICENSE.txt */
|
||||
/* file that was distributed with this source code. */
|
||||
/*************************************************************************************/
|
||||
|
||||
namespace Thelia\Command;
|
||||
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Thelia\Core\Event\Image\ImageEvent;
|
||||
use Thelia\Core\HttpFoundation\Request;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Thelia\Core\Event\TheliaEvents;
|
||||
|
||||
class ClearImageCache extends ContainerAwareCommand
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setName("image-cache:clear")
|
||||
->setDescription("Empty part or whole web space image cache")
|
||||
->addArgument("subdir", InputArgument::OPTIONAL, "Clear only the specified subdirectory")
|
||||
;
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$request = new Request();
|
||||
|
||||
try {
|
||||
$event = new ImageEvent($request);
|
||||
|
||||
$subdir = $input->getArgument('subdir');
|
||||
|
||||
if (! \is_null($subdir)) {
|
||||
$event->setCacheSubdirectory($subdir);
|
||||
}
|
||||
|
||||
$this->getDispatcher()->dispatch(TheliaEvents::IMAGE_CLEAR_CACHE, $event);
|
||||
|
||||
$output->writeln(sprintf('%s image cache successfully cleared.', \is_null($subdir) ? 'Entire' : ucfirst($subdir)));
|
||||
} catch (\Exception $ex) {
|
||||
$output->writeln(sprintf("Failed to clear image cache: %s", $ex->getMessage()));
|
||||
}
|
||||
}
|
||||
}
|
||||
225
core/lib/Thelia/Command/ConfigCommand.php
Normal file
225
core/lib/Thelia/Command/ConfigCommand.php
Normal file
@@ -0,0 +1,225 @@
|
||||
<?php
|
||||
/*******************************************************************************/
|
||||
/* This file is part of the Thelia package. */
|
||||
/* */
|
||||
/* Copyright (c) OpenStudio */
|
||||
/* email : dev@thelia.net */
|
||||
/* web : http://www.thelia.net */
|
||||
/* */
|
||||
/* For the full copyright and license information, please view the LICENSE.txt */
|
||||
/* file that was distributed with this source code. */
|
||||
/*************************************************************************************/
|
||||
|
||||
|
||||
namespace Thelia\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
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
133
core/lib/Thelia/Command/ContainerAwareCommand.php
Normal file
133
core/lib/Thelia/Command/ContainerAwareCommand.php
Normal file
@@ -0,0 +1,133 @@
|
||||
<?php
|
||||
/*************************************************************************************/
|
||||
/* This file is part of the Thelia package. */
|
||||
/* */
|
||||
/* Copyright (c) OpenStudio */
|
||||
/* email : dev@thelia.net */
|
||||
/* web : http://www.thelia.net */
|
||||
/* */
|
||||
/* For the full copyright and license information, please view the LICENSE.txt */
|
||||
/* file that was distributed with this source code. */
|
||||
/*************************************************************************************/
|
||||
|
||||
namespace Thelia\Command;
|
||||
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage;
|
||||
use Symfony\Component\Routing\RequestContext;
|
||||
use Thelia\Core\Application;
|
||||
use Thelia\Core\HttpFoundation\Request;
|
||||
use Thelia\Core\HttpFoundation\Session\Session;
|
||||
use Thelia\Core\Translation\Translator;
|
||||
use Thelia\Model\ConfigQuery;
|
||||
use Thelia\Model\Lang;
|
||||
use Thelia\Model\LangQuery;
|
||||
use Thelia\Tools\URL;
|
||||
|
||||
/**
|
||||
* Command.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Gilles Bourgeat <gbourgeat@openstudio.fr>
|
||||
*/
|
||||
class ContainerAwareCommand extends Command implements ContainerAwareInterface
|
||||
{
|
||||
/**
|
||||
* @var ContainerInterface
|
||||
*/
|
||||
private $container;
|
||||
|
||||
/**
|
||||
* @return ContainerInterface
|
||||
*/
|
||||
protected function getContainer()
|
||||
{
|
||||
if (null === $this->container) {
|
||||
/** @var Application $application */
|
||||
$application = $this->getApplication();
|
||||
$this->container = $application->getKernel()->getContainer();
|
||||
}
|
||||
|
||||
return $this->container;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ContainerAwareInterface::setContainer()
|
||||
* @param ContainerInterface $container
|
||||
*/
|
||||
public function setContainer(ContainerInterface $container = null)
|
||||
{
|
||||
$this->container = $container;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Symfony\Component\EventDispatcher\EventDispatcher
|
||||
*/
|
||||
public function getDispatcher()
|
||||
{
|
||||
$container = $this->getContainer();
|
||||
|
||||
// Initialize Thelia translator, if not already done.
|
||||
try {
|
||||
Translator::getInstance();
|
||||
} catch (\Exception $ex) {
|
||||
$this->container->get('thelia.translator');
|
||||
}
|
||||
|
||||
return $container->get('event_dispatcher');
|
||||
}
|
||||
|
||||
/**
|
||||
* For init an Request, if your command has need an Request
|
||||
* @param Lang|null $lang
|
||||
* @since 2.3
|
||||
*/
|
||||
protected function initRequest(Lang $lang = null)
|
||||
{
|
||||
$container = $this->getContainer();
|
||||
|
||||
$request = Request::create($this->getBaseUrl($lang));
|
||||
$request->setSession(new Session(new MockArraySessionStorage()));
|
||||
$container->set("request_stack", new RequestStack());
|
||||
$container->get('request_stack')->push($request);
|
||||
|
||||
$requestContext = new RequestContext();
|
||||
$requestContext->fromRequest($request);
|
||||
$url = $container->get('thelia.url.manager');
|
||||
$url->setRequestContext($requestContext);
|
||||
$this->getContainer()->get('router.admin')->setContext($requestContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Lang|null $lang
|
||||
* @return string
|
||||
* @since 2.3
|
||||
*/
|
||||
protected function getBaseUrl(Lang $lang = null)
|
||||
{
|
||||
$baseUrl = '';
|
||||
|
||||
if ((int) ConfigQuery::read('one_domain_foreach_lang') === 1) {
|
||||
if ($lang === null) {
|
||||
$lang = LangQuery::create()->findOneByByDefault(true);
|
||||
}
|
||||
|
||||
$baseUrl = $lang->getUrl();
|
||||
}
|
||||
|
||||
$baseUrl = trim($baseUrl);
|
||||
|
||||
if (empty($baseUrl)) {
|
||||
$baseUrl = ConfigQuery::read('url_site');
|
||||
}
|
||||
|
||||
if (empty($baseUrl)) {
|
||||
$baseUrl = 'http://localhost';
|
||||
}
|
||||
|
||||
return $baseUrl;
|
||||
}
|
||||
}
|
||||
205
core/lib/Thelia/Command/CreateAdminUser.php
Normal file
205
core/lib/Thelia/Command/CreateAdminUser.php
Normal file
@@ -0,0 +1,205 @@
|
||||
<?php
|
||||
/*************************************************************************************/
|
||||
/* This file is part of the Thelia package. */
|
||||
/* */
|
||||
/* Copyright (c) OpenStudio */
|
||||
/* email : dev@thelia.net */
|
||||
/* web : http://www.thelia.net */
|
||||
/* */
|
||||
/* For the full copyright and license information, please view the LICENSE.txt */
|
||||
/* file that was distributed with this source code. */
|
||||
/*************************************************************************************/
|
||||
|
||||
namespace Thelia\Command;
|
||||
|
||||
use Symfony\Component\Console\Helper\QuestionHelper;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Question\Question;
|
||||
use Thelia\Model\Admin;
|
||||
use Thelia\Model\AdminQuery;
|
||||
|
||||
class CreateAdminUser extends ContainerAwareCommand
|
||||
{
|
||||
/**
|
||||
* Configure the command
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setName("admin:create")
|
||||
->setDescription("Create a new administrator user")
|
||||
->setHelp("The <info>admin:create</info> command create a new administration user.")
|
||||
->addOption(
|
||||
'login_name',
|
||||
null,
|
||||
InputOption::VALUE_OPTIONAL,
|
||||
'Admin login name',
|
||||
null
|
||||
)
|
||||
->addOption(
|
||||
'first_name',
|
||||
null,
|
||||
InputOption::VALUE_OPTIONAL,
|
||||
'User first name',
|
||||
null
|
||||
)
|
||||
->addOption(
|
||||
"last_name",
|
||||
null,
|
||||
InputOption::VALUE_OPTIONAL,
|
||||
'User last name',
|
||||
null
|
||||
)
|
||||
->addOption(
|
||||
"email",
|
||||
null,
|
||||
InputOption::VALUE_OPTIONAL,
|
||||
'Admin email address',
|
||||
null
|
||||
)
|
||||
->addOption(
|
||||
"locale",
|
||||
null,
|
||||
InputOption::VALUE_OPTIONAL,
|
||||
'Preferred locale (default: en_US)',
|
||||
null
|
||||
)
|
||||
->addOption(
|
||||
'password',
|
||||
null,
|
||||
InputOption::VALUE_OPTIONAL,
|
||||
'Password',
|
||||
null
|
||||
)
|
||||
;
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$output->writeln('Please enter the admin user information:');
|
||||
|
||||
/** @var Admin $admin */
|
||||
$admin = $this->getAdminInfo($input, $output);
|
||||
|
||||
$admin->save();
|
||||
|
||||
$output->writeln(array(
|
||||
"",
|
||||
"<info>User ".$admin->getLogin()." successfully created.</info>",
|
||||
""
|
||||
));
|
||||
}
|
||||
|
||||
protected function enterData(
|
||||
QuestionHelper $helper,
|
||||
InputInterface $input,
|
||||
OutputInterface $output,
|
||||
$label,
|
||||
$errorMessage,
|
||||
$hidden = false
|
||||
) {
|
||||
$question = new Question($this->decorateInfo($label));
|
||||
|
||||
if ($hidden) {
|
||||
$question->setHidden(true);
|
||||
$question->setHiddenFallback(false);
|
||||
}
|
||||
|
||||
$question->setValidator(function ($value) use (&$errorMessage) {
|
||||
if (trim($value) == '') {
|
||||
throw new \Exception($errorMessage);
|
||||
}
|
||||
|
||||
return $value;
|
||||
});
|
||||
|
||||
return $helper->ask($input, $output, $question);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ask to user all needed information
|
||||
*
|
||||
* @param InputInterface $input
|
||||
* @param OutputInterface $output
|
||||
* @return array
|
||||
*/
|
||||
protected function getAdminInfo(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
/** @var QuestionHelper $helper */
|
||||
$helper = $this->getHelper('question');
|
||||
|
||||
$admin = new Admin();
|
||||
|
||||
$admin->setLogin($input->getOption("login_name") ?: $this->enterLogin($helper, $input, $output));
|
||||
$admin->setFirstname($input->getOption("first_name") ?: $this->enterData($helper, $input, $output, "User first name : ", "Please enter user first name."));
|
||||
$admin->setLastname($input->getOption("last_name") ?: $this->enterData($helper, $input, $output, "User last name : ", "Please enter user last name."));
|
||||
|
||||
$admin->setLocale($input->getOption("locale") ?: 'en_US');
|
||||
$admin->setEmail($input->getOption("email") ?: $this->enterEmail($helper, $input, $output));
|
||||
|
||||
do {
|
||||
$password = $input->getOption("password") ?: $this->enterData($helper, $input, $output, "Password : ", "Please enter a password.", true);
|
||||
$password_again = $input->getOption("password") ?: $this->enterData($helper, $input, $output, "Password (again): ", "Please enter the password again.", true);
|
||||
|
||||
if (! empty($password) && $password == $password_again) {
|
||||
$admin->setPassword($password);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
$output->writeln("Passwords are different, please try again.");
|
||||
} while (true);
|
||||
|
||||
$admin->setProfile(null);
|
||||
|
||||
return $admin;
|
||||
}
|
||||
|
||||
protected function decorateInfo($text)
|
||||
{
|
||||
return sprintf("<info>%s</info>", $text);
|
||||
}
|
||||
|
||||
|
||||
protected function enterLogin(QuestionHelper $helper, InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$question = new Question($this->decorateInfo("Admin login name : "));
|
||||
|
||||
$question->setValidator(function ($answer) {
|
||||
$answer = trim($answer);
|
||||
if (empty($answer)) {
|
||||
throw new \RuntimeException("Please enter a login name.");
|
||||
}
|
||||
|
||||
if (AdminQuery::create()->findOneByLogin($answer)) {
|
||||
throw new \RuntimeException("An administrator with this login already exists.");
|
||||
}
|
||||
|
||||
return $answer;
|
||||
});
|
||||
|
||||
return $helper->ask($input, $output, $question);
|
||||
}
|
||||
|
||||
protected function enterEmail(QuestionHelper $helper, InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$question = new Question($this->decorateInfo("Admin email or empty value : "));
|
||||
|
||||
$question->setValidator(function ($answer) {
|
||||
$answer = trim($answer);
|
||||
if (!empty($answer) && !filter_var($answer, FILTER_VALIDATE_EMAIL)) {
|
||||
throw new \RuntimeException("Please enter an email or an empty value.");
|
||||
}
|
||||
|
||||
if (AdminQuery::create()->findOneByEmail($answer)) {
|
||||
throw new \RuntimeException("An administrator with this email already exists.");
|
||||
}
|
||||
|
||||
return !empty($answer) ? $answer : uniqid('CHANGE_ME_');
|
||||
});
|
||||
|
||||
return $helper->ask($input, $output, $question);
|
||||
}
|
||||
}
|
||||
88
core/lib/Thelia/Command/DiffDatabaseCommand.php
Normal file
88
core/lib/Thelia/Command/DiffDatabaseCommand.php
Normal file
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
namespace Thelia\Command;
|
||||
|
||||
use Symfony\Component\Console\Helper\FormatterHelper;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Filesystem\Filesystem;
|
||||
use Symfony\Component\Finder\Finder;
|
||||
use Symfony\Component\Finder\SplFileInfo;
|
||||
use Propel\Generator\Command\MigrationDiffCommand as PropelMigrationDiffCommand;
|
||||
use Thelia\Core\PropelInitService;
|
||||
|
||||
/**
|
||||
* Generate a SQL diff between the current database structure and the current global schema, using the Propel migration
|
||||
* system.
|
||||
*/
|
||||
class DiffDatabaseCommand extends ContainerAwareCommand
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setName('thelia:dev:db:diff')
|
||||
->setDescription('Generate SQL to update the database(s) structure to the global Propel schema');
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
/** @var FormatterHelper $formatter */
|
||||
$formatter = $this->getHelper('formatter');
|
||||
|
||||
$output->writeln($formatter->formatBlock(
|
||||
'This is an experimental command that may be changed or removed at any time.',
|
||||
'bg=yellow;fg=black'
|
||||
));
|
||||
|
||||
/** @var PropelInitService $propelInitService */
|
||||
$propelInitService = $this->getContainer()->get('thelia.propel.init');
|
||||
|
||||
// rebuild the global schema
|
||||
$propelInitService->buildPropelGlobalSchema();
|
||||
|
||||
// call the Propel migration:diff command
|
||||
$fs = new Filesystem();
|
||||
$fs->remove($propelInitService->getPropelMigrationDir());
|
||||
|
||||
$propelInitService->runCommand(
|
||||
new PropelMigrationDiffCommand(),
|
||||
[
|
||||
'--config-dir' => $propelInitService->getPropelConfigDir(),
|
||||
'--schema-dir' => $propelInitService->getPropelSchemaDir(),
|
||||
'--output-dir' => $propelInitService->getPropelMigrationDir(),
|
||||
]
|
||||
);
|
||||
|
||||
// get the generated migration class
|
||||
$finder = new Finder();
|
||||
$finder
|
||||
->files()
|
||||
->in($propelInitService->getPropelMigrationDir());
|
||||
|
||||
if ($finder->count() != 1) {
|
||||
$output->writeln('Could not find the generated migration class.');
|
||||
return;
|
||||
}
|
||||
|
||||
// get the first (and only) found file
|
||||
$i = $finder->getIterator();
|
||||
$i->rewind();
|
||||
/** @var SplFileInfo $migration */
|
||||
$migration = $i->current();
|
||||
|
||||
// instantiate the migration class
|
||||
require $migration->getRealPath();
|
||||
$migrationClassName = pathinfo($migration->getFilename(), PATHINFO_FILENAME);
|
||||
$migrationClass = new $migrationClassName();
|
||||
|
||||
// output the generated SQL
|
||||
foreach ($migrationClass->getUpSQL() as $databaseName => $upSQL) {
|
||||
$output->writeln("-- DATABASE {$databaseName}, UP");
|
||||
$output->writeln($upSQL);
|
||||
}
|
||||
foreach ($migrationClass->getDownSQL() as $databaseName => $downSQL) {
|
||||
$output->writeln("-- DATABASE {$databaseName}, DOWN");
|
||||
$output->writeln($downSQL);
|
||||
}
|
||||
}
|
||||
}
|
||||
241
core/lib/Thelia/Command/ExportCommand.php
Normal file
241
core/lib/Thelia/Command/ExportCommand.php
Normal 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()
|
||||
;
|
||||
}
|
||||
}
|
||||
101
core/lib/Thelia/Command/GenerateResources.php
Normal file
101
core/lib/Thelia/Command/GenerateResources.php
Normal file
@@ -0,0 +1,101 @@
|
||||
<?php
|
||||
/*************************************************************************************/
|
||||
/* This file is part of the Thelia package. */
|
||||
/* */
|
||||
/* Copyright (c) OpenStudio */
|
||||
/* email : dev@thelia.net */
|
||||
/* web : http://www.thelia.net */
|
||||
/* */
|
||||
/* For the full copyright and license information, please view the LICENSE.txt */
|
||||
/* file that was distributed with this source code. */
|
||||
/*************************************************************************************/
|
||||
|
||||
namespace Thelia\Command;
|
||||
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Thelia\Core\Security\Resource\AdminResources;
|
||||
use Thelia\Model\Map\ResourceI18nTableMap;
|
||||
use Thelia\Model\Map\ResourceTableMap;
|
||||
|
||||
class GenerateResources extends ContainerAwareCommand
|
||||
{
|
||||
/**
|
||||
* Configure the command
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setName("thelia:generate-resources")
|
||||
->setDescription("Outputs admin resources")
|
||||
->setHelp("The <info>thelia:generate-resources</info> outputs admin resources.")
|
||||
->addOption(
|
||||
'output',
|
||||
null,
|
||||
InputOption::VALUE_OPTIONAL,
|
||||
'Output format amid (string, sql, sql-i18n)',
|
||||
null
|
||||
)
|
||||
;
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$class = new \ReflectionClass('Thelia\Core\Security\Resource\AdminResources');
|
||||
|
||||
$constants = $class->getConstants();
|
||||
|
||||
if (\count($constants) == 0) {
|
||||
throw new \RuntimeException('No resources found');
|
||||
}
|
||||
|
||||
switch ($input->getOption("output")) {
|
||||
case 'sql':
|
||||
$output->writeln(
|
||||
'INSERT INTO ' . ResourceTableMap::TABLE_NAME . ' (`id`, `code`, `created_at`, `updated_at`) VALUES '
|
||||
);
|
||||
$compteur = 0;
|
||||
foreach ($constants as $constant => $value) {
|
||||
if ($constant == AdminResources::SUPERADMINISTRATOR) {
|
||||
continue;
|
||||
}
|
||||
$compteur++;
|
||||
$output->writeln(
|
||||
"($compteur, '$value', NOW(), NOW())" . ($constant === key(\array_slice($constants, -1, 1, true)) ? ';' : ',')
|
||||
);
|
||||
}
|
||||
break;
|
||||
case 'sql-i18n':
|
||||
$output->writeln(
|
||||
'INSERT INTO ' . ResourceI18nTableMap::TABLE_NAME . ' (`id`, `locale`, `title`) VALUES '
|
||||
);
|
||||
$compteur = 0;
|
||||
foreach ($constants as $constant => $value) {
|
||||
if ($constant == AdminResources::SUPERADMINISTRATOR) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$compteur++;
|
||||
|
||||
$title = ucwords(str_replace('.', ' / ', str_replace('admin.', '', $value)));
|
||||
|
||||
$output->writeln(
|
||||
"($compteur, 'en_US', '$title'),"
|
||||
);
|
||||
$output->writeln(
|
||||
"($compteur, 'fr_FR', '$title')" . ($constant === key(\array_slice($constants, -1, 1, true)) ? ';' : ',')
|
||||
);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
foreach ($constants as $constant => $value) {
|
||||
if ($constant == AdminResources::SUPERADMINISTRATOR) {
|
||||
continue;
|
||||
}
|
||||
$output->writeln('[' . $constant . "] => " . $value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
221
core/lib/Thelia/Command/GenerateSQLCommand.php
Normal file
221
core/lib/Thelia/Command/GenerateSQLCommand.php
Normal 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;
|
||||
}
|
||||
}
|
||||
157
core/lib/Thelia/Command/HookCleanCommand.php
Normal file
157
core/lib/Thelia/Command/HookCleanCommand.php
Normal 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()));
|
||||
}
|
||||
}
|
||||
}
|
||||
141
core/lib/Thelia/Command/ImportCommand.php
Normal file
141
core/lib/Thelia/Command/ImportCommand.php
Normal 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()
|
||||
;
|
||||
}
|
||||
}
|
||||
345
core/lib/Thelia/Command/Install.php
Normal file
345
core/lib/Thelia/Command/Install.php
Normal file
@@ -0,0 +1,345 @@
|
||||
<?php
|
||||
/*************************************************************************************/
|
||||
/* This file is part of the Thelia package. */
|
||||
/* */
|
||||
/* Copyright (c) OpenStudio */
|
||||
/* email : dev@thelia.net */
|
||||
/* web : http://www.thelia.net */
|
||||
/* */
|
||||
/* For the full copyright and license information, please view the LICENSE.txt */
|
||||
/* file that was distributed with this source code. */
|
||||
/*************************************************************************************/
|
||||
|
||||
namespace Thelia\Command;
|
||||
|
||||
use Symfony\Component\Console\Helper\QuestionHelper;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Question\Question;
|
||||
use Symfony\Component\Filesystem\Filesystem;
|
||||
use Thelia\Core\Translation\Translator;
|
||||
use Thelia\Install\CheckPermission;
|
||||
use Thelia\Install\Database;
|
||||
use Thelia\Tools\TokenProvider;
|
||||
|
||||
/**
|
||||
* try to install a new instance of Thelia
|
||||
*
|
||||
* Class Install
|
||||
* @package Thelia\Command
|
||||
* @author Manuel Raynaud <manu@raynaud.io>
|
||||
*/
|
||||
class Install extends ContainerAwareCommand
|
||||
{
|
||||
/**
|
||||
* Configure the command
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setName("thelia:install")
|
||||
->setDescription("Install thelia using cli tools. For now Thelia only use mysql database")
|
||||
->setHelp("The <info>thelia:install</info> command install Thelia database and create config file needed.")
|
||||
->addOption(
|
||||
"db_host",
|
||||
null,
|
||||
InputOption::VALUE_OPTIONAL,
|
||||
"host for your database",
|
||||
"localhost"
|
||||
)
|
||||
->addOption(
|
||||
"db_username",
|
||||
null,
|
||||
InputOption::VALUE_OPTIONAL,
|
||||
"username for your database"
|
||||
)
|
||||
->addOption(
|
||||
"db_password",
|
||||
null,
|
||||
InputOption::VALUE_OPTIONAL,
|
||||
"password for your database"
|
||||
)
|
||||
->addOption(
|
||||
"db_name",
|
||||
null,
|
||||
InputOption::VALUE_OPTIONAL,
|
||||
"database name"
|
||||
)
|
||||
->addOption(
|
||||
"db_port",
|
||||
null,
|
||||
InputOption::VALUE_OPTIONAL,
|
||||
"database port",
|
||||
"3306"
|
||||
)
|
||||
;
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$output->writeln(array(
|
||||
'',
|
||||
'Welcome to Thelia install process',
|
||||
'You need information about your database configuration (host, username, password, database name, etc)',
|
||||
'',
|
||||
'<info>Caution : You are installing Thelia in cli mode, we verify some information, but this information are only available for the cli php sapi</info>',
|
||||
'<info>This informations can be different in your apache or cgi php.ini files</info>',
|
||||
''
|
||||
));
|
||||
|
||||
$this->checkPermission($output);
|
||||
|
||||
$connectionInfo = array(
|
||||
"host" => $input->getOption("db_host"),
|
||||
"dbName" => $input->getOption("db_name"),
|
||||
"username" => $input->getOption("db_username"),
|
||||
"password" => $input->getOption("db_password"),
|
||||
"port" => $input->getOption("db_port")
|
||||
);
|
||||
|
||||
while (false === $connection = $this->tryConnection($connectionInfo, $output)) {
|
||||
$connectionInfo = $this->getConnectionInfo($input, $output);
|
||||
}
|
||||
|
||||
$database = new Database($connection);
|
||||
|
||||
$database->createDatabase($connectionInfo["dbName"]);
|
||||
|
||||
$output->writeln(array(
|
||||
"",
|
||||
"<info>Creating Thelia database, please wait</info>",
|
||||
""
|
||||
));
|
||||
$database->insertSql($connectionInfo["dbName"]);
|
||||
$this->manageSecret($database);
|
||||
|
||||
$output->writeln(array(
|
||||
"",
|
||||
"<info>Database created without errors</info>",
|
||||
"<info>Creating file configuration, please wait</info>",
|
||||
""
|
||||
));
|
||||
|
||||
$this->createConfigFile($connectionInfo);
|
||||
|
||||
$output->writeln(array(
|
||||
"",
|
||||
"<info>Config file created with success. Your thelia is installed</info>",
|
||||
""
|
||||
));
|
||||
}
|
||||
|
||||
protected function manageSecret(Database $database)
|
||||
{
|
||||
$secret = TokenProvider::generateToken();
|
||||
$sql = "UPDATE `config` SET `value`=? WHERE `name`='form.secret'";
|
||||
$database->execute($sql, [$secret]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if needed directories have write permission
|
||||
*
|
||||
* @param \Symfony\Component\Console\Output\OutputInterface $output
|
||||
*/
|
||||
protected function checkPermission(OutputInterface $output)
|
||||
{
|
||||
$output->writeln(array(
|
||||
"Checking some permissions"
|
||||
));
|
||||
|
||||
/** @var Translator $translator */
|
||||
$translator = $this->getContainer()->get('thelia.translator');
|
||||
|
||||
$permissions = new CheckPermission(false, $translator);
|
||||
$isValid = $permissions->exec();
|
||||
|
||||
foreach ($permissions->getValidationMessages() as $item => $data) {
|
||||
if ($data['status']) {
|
||||
$output->writeln(
|
||||
array(
|
||||
sprintf(
|
||||
"<info>%s ...</info> %s",
|
||||
$data['text'],
|
||||
"<info>Ok</info>"
|
||||
)
|
||||
)
|
||||
);
|
||||
} else {
|
||||
$output->writeln(array(
|
||||
sprintf(
|
||||
"<error>%s </error>%s",
|
||||
$data['text'],
|
||||
sprintf("<error>%s</error>", $data["hint"])
|
||||
)
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
if (false === $isValid) {
|
||||
throw new \RuntimeException('Please put correct permissions and reload install process');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* rename database config file and complete it
|
||||
*
|
||||
* @param array $connectionInfo
|
||||
*/
|
||||
protected function createConfigFile($connectionInfo)
|
||||
{
|
||||
$fs = new Filesystem();
|
||||
|
||||
$sampleConfigFile = THELIA_CONF_DIR . "database.yml.sample";
|
||||
$configFile = THELIA_CONF_DIR . "database.yml";
|
||||
|
||||
$fs->copy($sampleConfigFile, $configFile, true);
|
||||
|
||||
$configContent = file_get_contents($configFile);
|
||||
|
||||
$configContent = str_replace("%DRIVER%", "mysql", $configContent);
|
||||
$configContent = str_replace("%USERNAME%", $connectionInfo["username"], $configContent);
|
||||
$configContent = str_replace("%PASSWORD%", $connectionInfo["password"], $configContent);
|
||||
$configContent = str_replace(
|
||||
"%DSN%",
|
||||
sprintf("mysql:host=%s;dbname=%s;port=%s", $connectionInfo["host"], $connectionInfo["dbName"], $connectionInfo['port']),
|
||||
$configContent
|
||||
);
|
||||
|
||||
file_put_contents($configFile, $configContent);
|
||||
|
||||
$fs->remove($this->getContainer()->getParameter("kernel.cache_dir"));
|
||||
}
|
||||
|
||||
/**
|
||||
* test database access
|
||||
*
|
||||
* @param $connectionInfo
|
||||
* @param OutputInterface $output
|
||||
* @return bool|\PDO
|
||||
*/
|
||||
protected function tryConnection($connectionInfo, OutputInterface $output)
|
||||
{
|
||||
if (\is_null($connectionInfo["dbName"])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$dsn = "mysql:host=%s;port=%s";
|
||||
|
||||
try {
|
||||
$connection = new \PDO(
|
||||
sprintf($dsn, $connectionInfo["host"], $connectionInfo["port"]),
|
||||
$connectionInfo["username"],
|
||||
$connectionInfo["password"]
|
||||
);
|
||||
$connection->query('SET NAMES \'UTF8\'');
|
||||
} catch (\PDOException $e) {
|
||||
$output->writeln(array(
|
||||
"<error>Wrong connection information</error>"
|
||||
));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return $connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ask to user all needed information
|
||||
*
|
||||
* @param InputInterface $input
|
||||
* @param OutputInterface $output
|
||||
* @return array
|
||||
*/
|
||||
protected function getConnectionInfo(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
/** @var QuestionHelper $helper */
|
||||
$helper = $this->getHelper('question');
|
||||
|
||||
$connectionInfo = array();
|
||||
|
||||
$connectionInfo['host'] = $this->enterData(
|
||||
$helper,
|
||||
$input,
|
||||
$output,
|
||||
"Database host [default: localhost] : ",
|
||||
"You must specify a database host",
|
||||
false,
|
||||
"localhost"
|
||||
);
|
||||
|
||||
$connectionInfo['port'] = $this->enterData(
|
||||
$helper,
|
||||
$input,
|
||||
$output,
|
||||
"Database port [default: 3306] : ",
|
||||
"You must specify a database port",
|
||||
false,
|
||||
"3306"
|
||||
);
|
||||
|
||||
$connectionInfo['dbName'] = $this->enterData(
|
||||
$helper,
|
||||
$input,
|
||||
$output,
|
||||
"Database name (if database does not exist, Thelia will try to create it) : ",
|
||||
"You must specify a database name"
|
||||
);
|
||||
|
||||
$connectionInfo['username'] = $this->enterData(
|
||||
$helper,
|
||||
$input,
|
||||
$output,
|
||||
"Database username : ",
|
||||
"You must specify a database username"
|
||||
);
|
||||
|
||||
$connectionInfo['password'] = $this->enterData(
|
||||
$helper,
|
||||
$input,
|
||||
$output,
|
||||
"Database password : ",
|
||||
"You must specify a database username",
|
||||
true,
|
||||
null,
|
||||
true
|
||||
);
|
||||
|
||||
return $connectionInfo;
|
||||
}
|
||||
|
||||
protected function enterData(
|
||||
QuestionHelper $helper,
|
||||
InputInterface $input,
|
||||
OutputInterface $output,
|
||||
$label,
|
||||
$errorMessage,
|
||||
$hidden = false,
|
||||
$defaultValue = null,
|
||||
$beEmpty = false
|
||||
) {
|
||||
$question = new Question($label, $defaultValue);
|
||||
|
||||
if ($hidden) {
|
||||
$question->setHidden(true);
|
||||
$question->setHiddenFallback(false);
|
||||
}
|
||||
|
||||
$question->setValidator(function ($value) use (&$errorMessage, &$beEmpty) {
|
||||
if (trim($value) == '') {
|
||||
if (\is_null($value) && !$beEmpty) {
|
||||
throw new \Exception($errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
return $value;
|
||||
});
|
||||
|
||||
return $helper->ask($input, $output, $question);
|
||||
}
|
||||
|
||||
protected function decorateInfo($text)
|
||||
{
|
||||
return sprintf("<info>%s</info>", $text);
|
||||
}
|
||||
}
|
||||
93
core/lib/Thelia/Command/ModuleActivateCommand.php
Normal file
93
core/lib/Thelia/Command/ModuleActivateCommand.php
Normal file
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
/*************************************************************************************/
|
||||
/* This file is part of the Thelia package. */
|
||||
/* */
|
||||
/* Copyright (c) OpenStudio */
|
||||
/* email : dev@thelia.net */
|
||||
/* web : http://www.thelia.net */
|
||||
/* */
|
||||
/* For the full copyright and license information, please view the LICENSE.txt */
|
||||
/* file that was distributed with this source code. */
|
||||
/*************************************************************************************/
|
||||
|
||||
namespace Thelia\Command;
|
||||
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Thelia\Core\Event\Module\ModuleToggleActivationEvent;
|
||||
use Thelia\Core\Event\TheliaEvents;
|
||||
use Thelia\Model\ModuleQuery;
|
||||
use Thelia\Module\BaseModule;
|
||||
|
||||
/**
|
||||
* activates a module
|
||||
*
|
||||
* Class ModuleActivateCommand
|
||||
* @package Thelia\Command
|
||||
* @author Etienne Roudeix <eroudeix@openstudio.fr>
|
||||
*
|
||||
*/
|
||||
class ModuleActivateCommand extends BaseModuleGenerate
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setName("module:activate")
|
||||
->setDescription("Activates a module")
|
||||
->addOption(
|
||||
"with-dependencies",
|
||||
null,
|
||||
InputOption::VALUE_NONE,
|
||||
'activate module recursively'
|
||||
)
|
||||
->addArgument(
|
||||
"module",
|
||||
InputArgument::REQUIRED,
|
||||
"module to activate"
|
||||
);
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$moduleCode = $this->formatModuleName($input->getArgument("module"));
|
||||
|
||||
$module = ModuleQuery::create()->findOneByCode($moduleCode);
|
||||
|
||||
if (null === $module) {
|
||||
throw new \RuntimeException(sprintf("module %s not found", $moduleCode));
|
||||
}
|
||||
|
||||
if ($module->getActivate() == BaseModule::IS_ACTIVATED) {
|
||||
throw new \RuntimeException(sprintf("module %s is already actived", $moduleCode));
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
$event = new ModuleToggleActivationEvent($module->getId());
|
||||
if ($input->getOption("with-dependencies")) {
|
||||
$event->setRecursive(true);
|
||||
}
|
||||
|
||||
$this->getDispatcher()->dispatch(TheliaEvents::MODULE_TOGGLE_ACTIVATION, $event);
|
||||
} catch (\Exception $e) {
|
||||
throw new \RuntimeException(
|
||||
sprintf(
|
||||
"Activation fail with Exception : [%d] %s",
|
||||
$e->getCode(),
|
||||
$e->getMessage()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
//impossible to change output class in CommandTester...
|
||||
if (method_exists($output, "renderBlock")) {
|
||||
$output->renderBlock(array(
|
||||
'',
|
||||
sprintf("Activation succeed for module %s", $moduleCode),
|
||||
''
|
||||
), "bg=green;fg=black");
|
||||
}
|
||||
}
|
||||
}
|
||||
134
core/lib/Thelia/Command/ModuleDeactivateCommand.php
Normal file
134
core/lib/Thelia/Command/ModuleDeactivateCommand.php
Normal file
@@ -0,0 +1,134 @@
|
||||
<?php
|
||||
/*************************************************************************************/
|
||||
/* This file is part of the Thelia package. */
|
||||
/* */
|
||||
/* Copyright (c) OpenStudio */
|
||||
/* email : dev@thelia.net */
|
||||
/* web : http://www.thelia.net */
|
||||
/* */
|
||||
/* For the full copyright and license information, please view the LICENSE.txt */
|
||||
/* file that was distributed with this source code. */
|
||||
/*************************************************************************************/
|
||||
|
||||
namespace Thelia\Command;
|
||||
|
||||
use Symfony\Component\Console\Helper\QuestionHelper;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Question\ConfirmationQuestion;
|
||||
use Thelia\Action\Module;
|
||||
use Thelia\Core\Event\Module\ModuleToggleActivationEvent;
|
||||
use Thelia\Core\Event\TheliaEvents;
|
||||
use Thelia\Model\ModuleQuery;
|
||||
use Thelia\Module\BaseModule;
|
||||
|
||||
/**
|
||||
* Deactivates a module
|
||||
*
|
||||
* Class ModuleDeactivateCommand
|
||||
* @package Thelia\Command
|
||||
* @author Nicolas Villa <nicolas@libre-shop.com>
|
||||
*
|
||||
*/
|
||||
class ModuleDeactivateCommand extends BaseModuleGenerate
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setName("module:deactivate")
|
||||
->setDescription("Deactivate a module")
|
||||
->addOption(
|
||||
"with-dependencies",
|
||||
null,
|
||||
InputOption::VALUE_NONE,
|
||||
'activate module recursively'
|
||||
)
|
||||
->addArgument(
|
||||
"module",
|
||||
InputArgument::REQUIRED,
|
||||
"module to deactivate"
|
||||
)
|
||||
->addOption(
|
||||
"assume-yes",
|
||||
'y',
|
||||
InputOption::VALUE_NONE,
|
||||
'Assume to deactivate a mandatory module'
|
||||
)
|
||||
;
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$moduleCode = $this->formatModuleName($input->getArgument("module"));
|
||||
|
||||
$module = ModuleQuery::create()->findOneByCode($moduleCode);
|
||||
|
||||
if (null === $module) {
|
||||
throw new \RuntimeException(sprintf("module %s not found", $moduleCode));
|
||||
}
|
||||
|
||||
if ($module->getActivate() == BaseModule::IS_NOT_ACTIVATED) {
|
||||
throw new \RuntimeException(sprintf("module %s is already deactivated", $moduleCode));
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
$event = new ModuleToggleActivationEvent($module->getId());
|
||||
|
||||
$module = ModuleQuery::create()->findPk($module->getId());
|
||||
if ($module->getMandatory() == BaseModule::IS_MANDATORY) {
|
||||
if (!$this->askConfirmation($input, $output)) {
|
||||
return;
|
||||
}
|
||||
$event->setAssumeDeactivate(true);
|
||||
}
|
||||
|
||||
if ($input->getOption("with-dependencies")) {
|
||||
$event->setRecursive(true);
|
||||
}
|
||||
$this->getDispatcher()->dispatch(TheliaEvents::MODULE_TOGGLE_ACTIVATION, $event);
|
||||
} catch (\Exception $e) {
|
||||
throw new \RuntimeException(sprintf("Deactivation fail with Exception : [%d] %s", $e->getCode(), $e->getMessage()));
|
||||
}
|
||||
|
||||
//impossible to change output class in CommandTester...
|
||||
if (method_exists($output, "renderBlock")) {
|
||||
$output->renderBlock(array(
|
||||
'',
|
||||
sprintf("Deactivation succeed for module %s", $moduleCode),
|
||||
''
|
||||
), "bg=green;fg=black");
|
||||
}
|
||||
}
|
||||
|
||||
private function askConfirmation(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$assumeYes = $input->getOption("assume-yes");
|
||||
$moduleCode = $input->getArgument("module");
|
||||
|
||||
if (!$assumeYes) {
|
||||
/** @var QuestionHelper $helper */
|
||||
$helper = $this->getHelper('question');
|
||||
$questionText = "Module ";
|
||||
$questionText .= (empty($moduleCode))
|
||||
? ""
|
||||
: $moduleCode;
|
||||
$questionText .= " is mandatory.\n";
|
||||
$questionText .= "Would you like to deactivate the module ";
|
||||
$questionText .= (empty($moduleCode))
|
||||
? ""
|
||||
: $moduleCode;
|
||||
$questionText .= " ? (yes, or no) ";
|
||||
|
||||
$question = new ConfirmationQuestion($questionText, false);
|
||||
|
||||
if (!$helper->ask($input, $output, $question)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
235
core/lib/Thelia/Command/ModuleGenerateCommand.php
Normal file
235
core/lib/Thelia/Command/ModuleGenerateCommand.php
Normal file
@@ -0,0 +1,235 @@
|
||||
<?php
|
||||
/*************************************************************************************/
|
||||
/* This file is part of the Thelia package. */
|
||||
/* */
|
||||
/* Copyright (c) OpenStudio */
|
||||
/* email : dev@thelia.net */
|
||||
/* web : http://www.thelia.net */
|
||||
/* */
|
||||
/* For the full copyright and license information, please view the LICENSE.txt */
|
||||
/* file that was distributed with this source code. */
|
||||
/*************************************************************************************/
|
||||
|
||||
namespace Thelia\Command;
|
||||
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Filesystem\Filesystem;
|
||||
|
||||
/**
|
||||
* generate a new Module
|
||||
*
|
||||
* Class ModuleGenerateCommand
|
||||
* @package Thelia\Command
|
||||
* @author Manuel Raynaud <manu@raynaud.io>
|
||||
*/
|
||||
class ModuleGenerateCommand extends BaseModuleGenerate
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setName("module:generate")
|
||||
->setDescription("generate all needed files for creating a new Module")
|
||||
->addArgument(
|
||||
"name",
|
||||
InputArgument::REQUIRED,
|
||||
"name wanted for your Module"
|
||||
)
|
||||
->addOption(
|
||||
'force',
|
||||
null,
|
||||
InputOption::VALUE_NONE,
|
||||
'If defined, it will update the module with missing directories and files (no overrides).'
|
||||
)
|
||||
;
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$this->module = $this->formatModuleName($input->getArgument("name"));
|
||||
$this->moduleDirectory = THELIA_MODULE_DIR . $this->module;
|
||||
|
||||
$this->validModuleName($this->module);
|
||||
|
||||
try {
|
||||
$this->verifyExistingModule();
|
||||
} catch (\RuntimeException $ex) {
|
||||
if (false === $input->getOption('force')) {
|
||||
throw $ex;
|
||||
}
|
||||
}
|
||||
|
||||
$this->createDirectories();
|
||||
$this->createFiles();
|
||||
if (method_exists($output, "renderBlock")) {
|
||||
// impossible to change output class in CommandTester...
|
||||
$output->renderBlock(array(
|
||||
'',
|
||||
sprintf("module %s create with success", $this->module),
|
||||
"You can now configure your module and complete module.xml file",
|
||||
''
|
||||
), "bg=green;fg=black");
|
||||
}
|
||||
}
|
||||
|
||||
private function createDirectories()
|
||||
{
|
||||
$fs = new Filesystem();
|
||||
|
||||
if (!$fs->exists($this->moduleDirectory)) {
|
||||
$fs->mkdir($this->moduleDirectory);
|
||||
}
|
||||
|
||||
foreach ($this->neededDirectories as $directory) {
|
||||
if (!$fs->exists($this->moduleDirectory . DIRECTORY_SEPARATOR . $directory)) {
|
||||
$fs->mkdir($this->moduleDirectory . DIRECTORY_SEPARATOR . $directory);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function copyConfigFile($filename, $skeletonDir, Filesystem $fs)
|
||||
{
|
||||
$filename = $this->moduleDirectory . DIRECTORY_SEPARATOR . "Config" . DIRECTORY_SEPARATOR . $filename;
|
||||
if (!$fs->exists($filename)) {
|
||||
$configContent = file_get_contents($skeletonDir . "config.xml");
|
||||
|
||||
$configContent = str_replace("%%CLASSNAME%%", $this->module, $configContent);
|
||||
$configContent = str_replace("%%NAMESPACE%%", $this->module, $configContent);
|
||||
$configContent = str_replace("%%NAMESPACE_LOWER%%", strtolower($this->module), $configContent);
|
||||
|
||||
file_put_contents(
|
||||
$filename,
|
||||
$configContent
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private function createFiles()
|
||||
{
|
||||
$fs = new Filesystem();
|
||||
|
||||
try {
|
||||
$skeletonDir = str_replace("/", DIRECTORY_SEPARATOR, __DIR__ . "/Skeleton/Module/");
|
||||
|
||||
// config.xml file
|
||||
$this->copyConfigFile("config.xml", $skeletonDir, $fs);
|
||||
$this->copyConfigFile("config_prod.xml", $skeletonDir, $fs);
|
||||
$this->copyConfigFile("config_dev.xml", $skeletonDir, $fs);
|
||||
$this->copyConfigFile("config_test.xml", $skeletonDir, $fs);
|
||||
|
||||
// Readme.md file
|
||||
$filename = $this->moduleDirectory . DIRECTORY_SEPARATOR . "Readme.md";
|
||||
if (!$fs->exists($filename)) {
|
||||
$readmeContent = file_get_contents($skeletonDir . "Readme.md");
|
||||
|
||||
// generate title for readme
|
||||
preg_match_all('!([A-Z][A-Z0-9]*(?=$|[A-Z][a-z0-9])|[A-Za-z][a-z0-9]+)!', $this->module, $readmeTitle);
|
||||
$composerFinalName = strtolower(implode("-", $readmeTitle[0]));
|
||||
|
||||
$readmeContent = str_replace("%%MODULENAME%%", $this->module, $readmeContent);
|
||||
$readmeContent = str_replace("%%MODULENAMETITLE%%", implode(" ", $readmeTitle[0]), $readmeContent);
|
||||
$readmeContent = str_replace("%%COMPOSERNAME%%", $composerFinalName, $readmeContent);
|
||||
|
||||
file_put_contents($filename, $readmeContent);
|
||||
}
|
||||
|
||||
// composer.json file
|
||||
$filename = $this->moduleDirectory . DIRECTORY_SEPARATOR . "composer.json";
|
||||
if (!$fs->exists($filename)) {
|
||||
$composerContent = file_get_contents($skeletonDir . "composer.json");
|
||||
|
||||
// generate composer module name
|
||||
preg_match_all('!([A-Z][A-Z0-9]*(?=$|[A-Z][a-z0-9])|[A-Za-z][a-z0-9]+)!', $this->module, $composerName);
|
||||
|
||||
$composerContent = str_replace("%%MODULENAME%%", $this->module, $composerContent);
|
||||
$composerContent = str_replace("%%COMPOSERNAME%%", strtolower(implode("-", $composerName[0])), $composerContent);
|
||||
|
||||
file_put_contents($filename, $composerContent);
|
||||
}
|
||||
|
||||
// module.xml file
|
||||
$filename = $this->moduleDirectory . DIRECTORY_SEPARATOR . "Config". DIRECTORY_SEPARATOR . "module.xml";
|
||||
if (!$fs->exists($filename)) {
|
||||
$moduleContent = file_get_contents($skeletonDir . "module.xml");
|
||||
|
||||
$moduleContent = str_replace("%%CLASSNAME%%", $this->module, $moduleContent);
|
||||
$moduleContent = str_replace("%%NAMESPACE%%", $this->module, $moduleContent);
|
||||
|
||||
file_put_contents($filename, $moduleContent);
|
||||
}
|
||||
|
||||
// PHP Class template
|
||||
$filename = $this->moduleDirectory . DIRECTORY_SEPARATOR . $this->module . ".php";
|
||||
if (!$fs->exists($filename)) {
|
||||
$classContent = file_get_contents($skeletonDir . "Class.php.template");
|
||||
|
||||
$classContent = str_replace("%%CLASSNAME%%", $this->module, $classContent);
|
||||
$classContent = str_replace("%%NAMESPACE%%", $this->module, $classContent);
|
||||
$classContent = str_replace("%%DOMAINNAME%%", strtolower($this->module), $classContent);
|
||||
|
||||
file_put_contents($filename, $classContent);
|
||||
}
|
||||
|
||||
// schema.xml file
|
||||
$filename = $this->moduleDirectory . DIRECTORY_SEPARATOR . "Config" . DIRECTORY_SEPARATOR . "schema.xml";
|
||||
if (!$fs->exists($filename)) {
|
||||
$schemaContent = file_get_contents($skeletonDir . "schema.xml");
|
||||
|
||||
$fs = new Filesystem();
|
||||
|
||||
// for backward compatibility
|
||||
if ($fs->exists(THELIA_VENDOR . 'thelia/propel/resources/xsd')) {
|
||||
$endPath = THELIA_VENDOR . 'thelia/propel/resources/xsd';
|
||||
} else {
|
||||
$endPath = THELIA_VENDOR . 'propel/propel/resources/xsd';
|
||||
}
|
||||
|
||||
$schemaContent = str_replace("%%NAMESPACE%%", $this->module, $schemaContent);
|
||||
$schemaContent = str_replace(
|
||||
'%%XSD_LOCATION%%',
|
||||
$fs->makePathRelative(
|
||||
$endPath,
|
||||
$this->moduleDirectory
|
||||
) . 'database.xsd',
|
||||
$schemaContent
|
||||
);
|
||||
|
||||
file_put_contents($filename, $schemaContent);
|
||||
}
|
||||
|
||||
// routing.xml file
|
||||
$filename = $this->moduleDirectory . DIRECTORY_SEPARATOR . "Config" . DIRECTORY_SEPARATOR . "routing.xml";
|
||||
if (!$fs->exists($filename)) {
|
||||
$routingContent = file_get_contents($skeletonDir . "routing.xml");
|
||||
|
||||
$routingContent = str_replace("%%NAMESPACE%%", $this->module, $routingContent);
|
||||
$routingContent = str_replace("%%CLASSNAME_LOWER%%", strtolower($this->module), $routingContent);
|
||||
|
||||
file_put_contents($filename, $routingContent);
|
||||
}
|
||||
|
||||
// I18n sample files
|
||||
$filename = $this->moduleDirectory . DIRECTORY_SEPARATOR . "I18n" . DIRECTORY_SEPARATOR . "fr_FR.php";
|
||||
if (!$fs->exists($filename)) {
|
||||
$fs->copy(
|
||||
$skeletonDir . DIRECTORY_SEPARATOR . "I18n" . DIRECTORY_SEPARATOR . "fr_FR.php",
|
||||
$filename
|
||||
);
|
||||
}
|
||||
|
||||
$filename = $this->moduleDirectory . DIRECTORY_SEPARATOR . "I18n" . DIRECTORY_SEPARATOR . "en_US.php";
|
||||
if (!$fs->exists($filename)) {
|
||||
$fs->copy(
|
||||
$skeletonDir . DIRECTORY_SEPARATOR . "I18n" . DIRECTORY_SEPARATOR . "en_US.php",
|
||||
$this->moduleDirectory . DIRECTORY_SEPARATOR . "I18n" . DIRECTORY_SEPARATOR . "en_US.php"
|
||||
);
|
||||
}
|
||||
} catch (\Exception $ex) {
|
||||
$fs->remove($this->moduleDirectory);
|
||||
|
||||
throw $ex;
|
||||
}
|
||||
}
|
||||
}
|
||||
115
core/lib/Thelia/Command/ModuleGenerateModelCommand.php
Normal file
115
core/lib/Thelia/Command/ModuleGenerateModelCommand.php
Normal file
@@ -0,0 +1,115 @@
|
||||
<?php
|
||||
/*************************************************************************************/
|
||||
/* This file is part of the Thelia package. */
|
||||
/* */
|
||||
/* Copyright (c) OpenStudio */
|
||||
/* email : dev@thelia.net */
|
||||
/* web : http://www.thelia.net */
|
||||
/* */
|
||||
/* For the full copyright and license information, please view the LICENSE.txt */
|
||||
/* file that was distributed with this source code. */
|
||||
/*************************************************************************************/
|
||||
|
||||
namespace Thelia\Command;
|
||||
|
||||
use Propel\Generator\Command\ModelBuildCommand;
|
||||
use Symfony\Component\Console\Helper\FormatterHelper;
|
||||
use Symfony\Component\Console\Input\ArrayInput;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Filesystem\Filesystem;
|
||||
use Thelia\Core\PropelInitService;
|
||||
|
||||
/**
|
||||
* generate class model for a specific module
|
||||
*
|
||||
* Class ModuleGenerateModelCommand
|
||||
* @package Thelia\Command
|
||||
* @author Manuel Raynaud <manu@raynaud.io>
|
||||
*/
|
||||
class ModuleGenerateModelCommand extends BaseModuleGenerate
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setName("module:generate:model")
|
||||
->setDescription("generate model for a specific module")
|
||||
->addArgument(
|
||||
"name",
|
||||
InputArgument::REQUIRED,
|
||||
"module name"
|
||||
)
|
||||
->addOption(
|
||||
"generate-sql",
|
||||
null,
|
||||
InputOption::VALUE_NONE,
|
||||
"with this option generate sql file at the same time"
|
||||
)
|
||||
;
|
||||
}
|
||||
|
||||
public function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$this->module = $this->formatModuleName($input->getArgument("name"));
|
||||
$this->moduleDirectory = THELIA_MODULE_DIR . $this->module;
|
||||
|
||||
$fs = new Filesystem();
|
||||
|
||||
if ($fs->exists($this->moduleDirectory) === false) {
|
||||
throw new \RuntimeException(sprintf("%s module does not exists", $this->module));
|
||||
}
|
||||
|
||||
if ($fs->exists($this->moduleDirectory . DS . "Config" . DS . "schema.xml") === false) {
|
||||
throw new \RuntimeException("schema.xml not found in Config directory. Needed file for generating model");
|
||||
}
|
||||
|
||||
$this->checkModuleSchema();
|
||||
|
||||
$this->generateModel($output);
|
||||
|
||||
/** @var FormatterHelper $formatter */
|
||||
$formatter = $this->getHelper('formatter');
|
||||
$formattedBlock = $formatter->formatBlock(
|
||||
'Model generated successfully',
|
||||
'bg=green;fg=black'
|
||||
);
|
||||
$output->writeln($formattedBlock);
|
||||
|
||||
if ($input->getOption("generate-sql")) {
|
||||
$output->writeln(' ');
|
||||
$this->generateSql($output);
|
||||
}
|
||||
}
|
||||
|
||||
protected function generateSql(OutputInterface $output)
|
||||
{
|
||||
$command = $this->getApplication()->find("module:generate:sql");
|
||||
|
||||
$command->run(
|
||||
new ArrayInput(array(
|
||||
"command" => $command->getName(),
|
||||
"name" => $this->module
|
||||
)),
|
||||
$output
|
||||
);
|
||||
}
|
||||
|
||||
protected function generateModel(OutputInterface $output)
|
||||
{
|
||||
$schemaDir = $this->generateGlobalSchemaForModule();
|
||||
|
||||
/** @var PropelInitService $propelInitService */
|
||||
$propelInitService = $this->getContainer()->get('thelia.propel.init');
|
||||
|
||||
$propelInitService->runCommand(
|
||||
new ModelBuildCommand(),
|
||||
[
|
||||
"--config-dir" => $propelInitService->getPropelConfigDir(),
|
||||
"--schema-dir" => $schemaDir,
|
||||
],
|
||||
$output
|
||||
);
|
||||
}
|
||||
}
|
||||
86
core/lib/Thelia/Command/ModuleGenerateSqlCommand.php
Normal file
86
core/lib/Thelia/Command/ModuleGenerateSqlCommand.php
Normal file
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
/*************************************************************************************/
|
||||
/* This file is part of the Thelia package. */
|
||||
/* */
|
||||
/* Copyright (c) OpenStudio */
|
||||
/* email : dev@thelia.net */
|
||||
/* web : http://www.thelia.net */
|
||||
/* */
|
||||
/* For the full copyright and license information, please view the LICENSE.txt */
|
||||
/* file that was distributed with this source code. */
|
||||
/*************************************************************************************/
|
||||
|
||||
namespace Thelia\Command;
|
||||
|
||||
use Propel\Generator\Command\SqlBuildCommand;
|
||||
use Symfony\Component\Console\Helper\FormatterHelper;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Filesystem\Filesystem;
|
||||
use Thelia\Core\PropelInitService;
|
||||
|
||||
/**
|
||||
* generate sql for a specific module
|
||||
*
|
||||
* Class ModuleGenerateSqlCommand
|
||||
* @package Thelia\Command
|
||||
* @author Manuel Raynaud <manu@raynaud.io>
|
||||
*/
|
||||
class ModuleGenerateSqlCommand extends BaseModuleGenerate
|
||||
{
|
||||
public function configure()
|
||||
{
|
||||
$this
|
||||
->setName("module:generate:sql")
|
||||
->setDescription("Generate the sql from schema.xml file")
|
||||
->addArgument(
|
||||
"name",
|
||||
InputArgument::REQUIRED,
|
||||
"Module name"
|
||||
)
|
||||
;
|
||||
}
|
||||
|
||||
public function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$this->module = $this->formatModuleName($input->getArgument("name"));
|
||||
$this->moduleDirectory = THELIA_MODULE_DIR . $this->module;
|
||||
|
||||
$fs = new Filesystem();
|
||||
|
||||
if ($fs->exists($this->moduleDirectory) === false) {
|
||||
throw new \RuntimeException(sprintf("%s module does not exists", $this->module));
|
||||
}
|
||||
|
||||
if ($fs->exists($this->moduleDirectory . DS . "Config" . DS . "schema.xml") === false) {
|
||||
throw new \RuntimeException("schema.xml not found in Config directory. Needed file for generating model");
|
||||
}
|
||||
|
||||
$this->checkModuleSchema();
|
||||
|
||||
/** @var PropelInitService $propelInitService */
|
||||
$propelInitService = $this->getContainer()->get('thelia.propel.init');
|
||||
|
||||
$propelInitService->runCommand(
|
||||
new SqlBuildCommand(),
|
||||
[
|
||||
"--output-dir" => "{$this->moduleDirectory}/Config",
|
||||
"--schema-dir" => "{$this->moduleDirectory}/Config",
|
||||
"--config-dir" => $propelInitService->getPropelConfigDir(),
|
||||
],
|
||||
$output
|
||||
);
|
||||
|
||||
/** @var FormatterHelper $formatter */
|
||||
$formatter = $this->getHelper('formatter');
|
||||
$formattedBlock = $formatter->formatBlock(
|
||||
[
|
||||
'Sql generated successfully',
|
||||
'File available in your module config directory',
|
||||
],
|
||||
'bg=green;fg=black'
|
||||
);
|
||||
$output->writeln($formattedBlock);
|
||||
}
|
||||
}
|
||||
87
core/lib/Thelia/Command/ModuleListCommand.php
Normal file
87
core/lib/Thelia/Command/ModuleListCommand.php
Normal 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::COL_CODE)
|
||||
->addAsColumn("active", "IF(".ModuleTableMap::COL_ACTIVATE.", \"Yes\", \"No\")")
|
||||
->addAsColumn("type", ModuleTableMap::COL_TYPE)
|
||||
->addAsColumn("version", ModuleTableMap::COL_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;
|
||||
}
|
||||
}
|
||||
219
core/lib/Thelia/Command/ModulePositionCommand.php
Normal file
219
core/lib/Thelia/Command/ModulePositionCommand.php
Normal 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;
|
||||
}
|
||||
}
|
||||
62
core/lib/Thelia/Command/ModuleRefreshCommand.php
Normal file
62
core/lib/Thelia/Command/ModuleRefreshCommand.php
Normal file
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
/*************************************************************************************/
|
||||
/* This file is part of the Thelia package. */
|
||||
/* */
|
||||
/* Copyright (c) OpenStudio */
|
||||
/* email : dev@thelia.net */
|
||||
/* web : http://www.thelia.net */
|
||||
/* */
|
||||
/* For the full copyright and license information, please view the LICENSE.txt */
|
||||
/* file that was distributed with this source code. */
|
||||
/*************************************************************************************/
|
||||
|
||||
namespace Thelia\Command;
|
||||
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Thelia\Exception\InvalidModuleException;
|
||||
use Thelia\Module\ModuleManagement;
|
||||
|
||||
/**
|
||||
* Class ModuleRefreshCommand
|
||||
* Refresh modules list
|
||||
*
|
||||
* @package Thelia\Command
|
||||
* @author Jérôme Billiras <jbilliras@openstudio.fr>
|
||||
*/
|
||||
class ModuleRefreshCommand extends ContainerAwareCommand
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setName('module:refresh')
|
||||
->setDescription('Refresh modules list');
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
try {
|
||||
$moduleManagement = new ModuleManagement($this->getContainer());
|
||||
$moduleManagement->updateModules($this->getContainer());
|
||||
} catch (InvalidModuleException $ime) {
|
||||
throw new \RuntimeException(
|
||||
sprintf('One or more modules could not be refreshed : %s', $ime->getErrorsAsString("\n"))
|
||||
);
|
||||
} catch (\Exception $e) {
|
||||
throw new \RuntimeException(
|
||||
sprintf('Refresh modules list fail with Exception : [%d] %s', $e->getCode(), $e->getMessage())
|
||||
);
|
||||
}
|
||||
|
||||
if (method_exists($output, 'renderBlock')) {
|
||||
$output->renderBlock(
|
||||
[
|
||||
'',
|
||||
'Modules list successfully refreshed',
|
||||
''
|
||||
],
|
||||
'bg=green;fg=black'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
43
core/lib/Thelia/Command/Output/TheliaConsoleOutput.php
Normal file
43
core/lib/Thelia/Command/Output/TheliaConsoleOutput.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
/*************************************************************************************/
|
||||
/* This file is part of the Thelia package. */
|
||||
/* */
|
||||
/* Copyright (c) OpenStudio */
|
||||
/* email : dev@thelia.net */
|
||||
/* web : http://www.thelia.net */
|
||||
/* */
|
||||
/* For the full copyright and license information, please view the LICENSE.txt */
|
||||
/* file that was distributed with this source code. */
|
||||
/*************************************************************************************/
|
||||
|
||||
namespace Thelia\Command\Output;
|
||||
|
||||
use Symfony\Component\Console\Output\ConsoleOutput;
|
||||
|
||||
class TheliaConsoleOutput extends ConsoleOutput
|
||||
{
|
||||
public function renderBlock(array $messages, $style = "info")
|
||||
{
|
||||
$strlen = function ($string) {
|
||||
if (!function_exists('mb_strlen')) {
|
||||
return \strlen($string);
|
||||
}
|
||||
|
||||
if (false === $encoding = mb_detect_encoding($string)) {
|
||||
return \strlen($string);
|
||||
}
|
||||
|
||||
return \mb_strlen($string, $encoding);
|
||||
};
|
||||
$length = 0;
|
||||
foreach ($messages as $message) {
|
||||
$length = ($strlen($message) > $length) ? $strlen($message) : $length;
|
||||
}
|
||||
$output = array();
|
||||
foreach ($messages as $message) {
|
||||
$output[] = "<" . $style . ">" . " " . $message . str_repeat(' ', $length - $strlen($message)) . " </" . $style . ">";
|
||||
}
|
||||
|
||||
$this->writeln($output);
|
||||
}
|
||||
}
|
||||
61
core/lib/Thelia/Command/ReloadDatabaseCommand.php
Normal file
61
core/lib/Thelia/Command/ReloadDatabaseCommand.php
Normal file
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
/*************************************************************************************/
|
||||
/* This file is part of the Thelia package. */
|
||||
/* */
|
||||
/* Copyright (c) OpenStudio */
|
||||
/* email : dev@thelia.net */
|
||||
/* web : http://www.thelia.net */
|
||||
/* */
|
||||
/* For the full copyright and license information, please view the LICENSE.txt */
|
||||
/* file that was distributed with this source code. */
|
||||
/*************************************************************************************/
|
||||
|
||||
namespace Thelia\Command;
|
||||
|
||||
use Propel\Runtime\Connection\ConnectionWrapper;
|
||||
use \Thelia\Model\Map\ProductTableMap;
|
||||
use Propel\Runtime\Propel;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Thelia\Install\Database;
|
||||
|
||||
/**
|
||||
* Class ReloadDatabasesCommand
|
||||
* @package Thelia\Command
|
||||
* @author Manuel Raynaud <manu@raynaud.io>
|
||||
*/
|
||||
class ReloadDatabaseCommand extends BaseModuleGenerate
|
||||
{
|
||||
public function configure()
|
||||
{
|
||||
$this
|
||||
->setName("thelia:dev:reloadDB")
|
||||
->setDescription("erase current database and create new one")
|
||||
/* ->addOption(
|
||||
"load-fixtures",
|
||||
null,
|
||||
InputOption::VALUE_NONE,
|
||||
"load fixtures in databases"
|
||||
)*/
|
||||
;
|
||||
}
|
||||
|
||||
public function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
/** @var ConnectionWrapper $connection */
|
||||
$connection = Propel::getConnection(ProductTableMap::DATABASE_NAME);
|
||||
$connection = $connection->getWrappedConnection();
|
||||
|
||||
$database = new Database($connection);
|
||||
$output->writeln(array(
|
||||
'',
|
||||
'<info>starting reloaded database, please wait</info>'
|
||||
));
|
||||
$database->insertSql();
|
||||
$output->writeln(array(
|
||||
'',
|
||||
'<info>Database reloaded with success</info>',
|
||||
''
|
||||
));
|
||||
}
|
||||
}
|
||||
47
core/lib/Thelia/Command/SaleCheckActivationCommand.php
Normal file
47
core/lib/Thelia/Command/SaleCheckActivationCommand.php
Normal 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()));
|
||||
}
|
||||
}
|
||||
}
|
||||
28
core/lib/Thelia/Command/Skeleton/Module/Class.php.template
Normal file
28
core/lib/Thelia/Command/Skeleton/Module/Class.php.template
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
/*************************************************************************************/
|
||||
/* This file is part of the Thelia package. */
|
||||
/* */
|
||||
/* Copyright (c) OpenStudio */
|
||||
/* email : dev@thelia.net */
|
||||
/* web : http://www.thelia.net */
|
||||
/* */
|
||||
/* For the full copyright and license information, please view the LICENSE.txt */
|
||||
/* file that was distributed with this source code. */
|
||||
/*************************************************************************************/
|
||||
|
||||
namespace %%NAMESPACE%%;
|
||||
|
||||
use Thelia\Module\BaseModule;
|
||||
|
||||
class %%CLASSNAME%% extends BaseModule
|
||||
{
|
||||
/** @var string */
|
||||
const DOMAIN_NAME = '%%DOMAINNAME%%';
|
||||
|
||||
/*
|
||||
* You may now override BaseModuleInterface methods, such as:
|
||||
* install, destroy, preActivation, postActivation, preDeactivation, postDeactivation
|
||||
*
|
||||
* Have fun !
|
||||
*/
|
||||
}
|
||||
4
core/lib/Thelia/Command/Skeleton/Module/I18n/en_US.php
Normal file
4
core/lib/Thelia/Command/Skeleton/Module/I18n/en_US.php
Normal file
@@ -0,0 +1,4 @@
|
||||
<?php
|
||||
return array(
|
||||
// 'an english string' => 'The displayed english string',
|
||||
);
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user