Merge branch 'master' into template
Conflicts: core/lib/Thelia/Core/Event/TheliaEvents.php core/lib/Thelia/Model/Base/Module.php core/lib/Thelia/Model/Base/ModuleQuery.php core/lib/Thelia/Model/Map/ModuleTableMap.php install/thelia.sql local/config/schema.xml
This commit is contained in:
178
core/lib/Thelia/Action/BaseCachedFile.php
Normal file
178
core/lib/Thelia/Action/BaseCachedFile.php
Normal file
@@ -0,0 +1,178 @@
|
||||
<?php
|
||||
/*************************************************************************************/
|
||||
/* */
|
||||
/* Thelia */
|
||||
/* */
|
||||
/* Copyright (c) OpenStudio */
|
||||
/* email : info@thelia.net */
|
||||
/* web : http://www.thelia.net */
|
||||
/* */
|
||||
/* This program is free software; you can redistribute it and/or modify */
|
||||
/* it under the terms of the GNU General Public License as published by */
|
||||
/* the Free Software Foundation; either version 3 of the License */
|
||||
/* */
|
||||
/* This program is distributed in the hope that it will be useful, */
|
||||
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
|
||||
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
|
||||
/* GNU General Public License for more details. */
|
||||
/* */
|
||||
/* You should have received a copy of the GNU General Public License */
|
||||
/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
/* */
|
||||
/*************************************************************************************/
|
||||
|
||||
namespace Thelia\Action;
|
||||
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
|
||||
use Thelia\Core\Event\CachedFileEvent;
|
||||
use Thelia\Model\ConfigQuery;
|
||||
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
|
||||
{
|
||||
/**
|
||||
* @return string root of the file cache directory in web space
|
||||
*/
|
||||
protected abstract function getCacheDirFromWebRoot();
|
||||
|
||||
/**
|
||||
* Clear the file cache. Is a subdirectory is specified, only this directory is cleared.
|
||||
* If no directory is specified, the whole cache is cleared.
|
||||
* Only files are deleted, directories will remain.
|
||||
*
|
||||
* @param CachedFileEvent $event
|
||||
*/
|
||||
public function clearCache(CachedFileEvent $event)
|
||||
{
|
||||
$path = $this->getCachePath($event->getCacheSubdirectory(), false);
|
||||
|
||||
$this->clearDirectory($path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively clears the specified directory.
|
||||
*
|
||||
* @param string $path the directory path
|
||||
*/
|
||||
protected function clearDirectory($path)
|
||||
{
|
||||
$iterator = new \DirectoryIterator($path);
|
||||
|
||||
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 $filename the safe filename, as returned by getCacheFilePath()
|
||||
* @return string the absolute URL to the cached file
|
||||
*/
|
||||
protected function getCacheFileURL($subdir, $safe_filename)
|
||||
{
|
||||
$path = $this->getCachePathFromWebRoot($subdir);
|
||||
|
||||
return URL::getInstance()->absoluteUrl(sprintf("%s/%s", $path, $safe_filename), null, URL::PATH_TO_FILE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the full path of the cached file
|
||||
*
|
||||
* @param string $subdir the subdirectory related to cache base
|
||||
* @param string $filename the filename
|
||||
* @param string $hashed_options a hash of transformation options, or null if no transformations have been applied
|
||||
* @param boolean $forceOriginalDocument if true, the original file path in the cache dir is returned.
|
||||
* @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.
|
||||
* @throws \RuntimeException if cache directory cannot be created
|
||||
* @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/%s file in cache directory", $cache_base));
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
@@ -24,52 +24,88 @@
|
||||
namespace Thelia\Action;
|
||||
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Thelia\Core\Event\TheliaEvents;
|
||||
use Thelia\Model\Category as CategoryModel;
|
||||
|
||||
use Thelia\Model\CategoryQuery;
|
||||
use Thelia\Model\Category as CategoryModel;
|
||||
|
||||
use Propel\Runtime\ActiveQuery\Criteria;
|
||||
use Propel\Runtime\Propel;
|
||||
use Thelia\Model\Map\CategoryTableMap;
|
||||
use Thelia\Core\Event\TheliaEvents;
|
||||
|
||||
use Thelia\Core\Event\CategoryUpdateEvent;
|
||||
use Thelia\Core\Event\CategoryCreateEvent;
|
||||
use Thelia\Core\Event\CategoryDeleteEvent;
|
||||
use Thelia\Model\ConfigQuery;
|
||||
use Thelia\Core\Event\UpdatePositionEvent;
|
||||
use Thelia\Core\Event\CategoryToggleVisibilityEvent;
|
||||
use Thelia\Core\Event\CategoryChangePositionEvent;
|
||||
|
||||
class Category extends BaseAction implements EventSubscriberInterface
|
||||
{
|
||||
/**
|
||||
* Create a new category entry
|
||||
*
|
||||
* @param CategoryCreateEvent $event
|
||||
*/
|
||||
public function create(CategoryCreateEvent $event)
|
||||
{
|
||||
$category = new CategoryModel();
|
||||
|
||||
$category
|
||||
->setDispatcher($this->getDispatcher())
|
||||
->create(
|
||||
$event->getTitle(),
|
||||
$event->getParent(),
|
||||
$event->getLocale()
|
||||
);
|
||||
|
||||
->setLocale($event->getLocale())
|
||||
->setTitle($event->getTitle())
|
||||
->setParent($event->getParent())
|
||||
->setVisible($event->getVisible())
|
||||
|
||||
->save()
|
||||
;
|
||||
|
||||
$event->setCategory($category);
|
||||
}
|
||||
|
||||
public function update(CategoryChangeEvent $event)
|
||||
/**
|
||||
* Change a category
|
||||
*
|
||||
* @param CategoryUpdateEvent $event
|
||||
*/
|
||||
public function update(CategoryUpdateEvent $event)
|
||||
{
|
||||
$search = CategoryQuery::create();
|
||||
|
||||
if (null !== $category = CategoryQuery::create()->findPk($event->getCategoryId())) {
|
||||
|
||||
$category
|
||||
->setDispatcher($this->getDispatcher())
|
||||
|
||||
->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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a category
|
||||
* Delete a category entry
|
||||
*
|
||||
* @param ActionEvent $event
|
||||
* @param CategoryDeleteEvent $event
|
||||
*/
|
||||
public function delete(CategoryDeleteEvent $event)
|
||||
{
|
||||
$category = CategoryQuery::create()->findPk($event->getCategoryId());
|
||||
if (null !== $category = CategoryQuery::create()->findPk($event->getCategoryId())) {
|
||||
|
||||
if ($category !== null) {
|
||||
$category
|
||||
->setDispatcher($this->getDispatcher())
|
||||
->delete()
|
||||
;
|
||||
|
||||
$category->setDispatcher($this->getDispatcher())->delete();
|
||||
$event->setCategory($category);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,178 +116,48 @@ class Category extends BaseAction implements EventSubscriberInterface
|
||||
*/
|
||||
public function toggleVisibility(CategoryToggleVisibilityEvent $event)
|
||||
{
|
||||
$category = CategoryQuery::create()->findPk($event->getCategoryId());
|
||||
$category = $event->getCategory();
|
||||
|
||||
if ($category !== null) {
|
||||
|
||||
$category
|
||||
->setDispatcher($this->getDispatcher())
|
||||
->setVisible($category->getVisible() ? false : true)
|
||||
->save()
|
||||
$category
|
||||
->setDispatcher($this->getDispatcher())
|
||||
->setVisible($category->getVisible() ? false : true)
|
||||
->save()
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes position, selecting absolute ou relative change.
|
||||
*
|
||||
* @param CategoryChangePositionEvent $event
|
||||
*/
|
||||
public function updatePosition(UpdatePositionEvent $event)
|
||||
{
|
||||
if (null !== $category = CategoryQuery::create()->findPk($event->getObjectId())) {
|
||||
|
||||
$category->setDispatcher($this->getDispatcher());
|
||||
|
||||
$mode = $event->getMode();
|
||||
|
||||
if ($mode == UpdatePositionEvent::POSITION_ABSOLUTE)
|
||||
return $category->changeAbsolutePosition($event->getPosition());
|
||||
else if ($mode == UpdatePositionEvent::POSITION_UP)
|
||||
return $category->movePositionUp();
|
||||
else if ($mode == UpdatePositionEvent::POSITION_DOWN)
|
||||
return $category->movePositionDown();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes category position, selecting absolute ou relative change.
|
||||
*
|
||||
* @param CategoryChangePositionEvent $event
|
||||
*/
|
||||
public function changePosition(CategoryChangePositionEvent $event)
|
||||
{
|
||||
if ($event->getMode() == CategoryChangePositionEvent::POSITION_ABSOLUTE)
|
||||
return $this->changeAbsolutePosition($event);
|
||||
else
|
||||
return $this->exchangePosition($event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Move up or down a category
|
||||
*
|
||||
* @param CategoryChangePositionEvent $event
|
||||
*/
|
||||
protected function exchangePosition(CategoryChangePositionEvent $event)
|
||||
{
|
||||
$category = CategoryQuery::create()->findPk($event->getCategoryId());
|
||||
|
||||
if ($category !== null) {
|
||||
|
||||
// The current position of the category
|
||||
$my_position = $category->getPosition();
|
||||
|
||||
// Find category to exchange position with
|
||||
$search = CategoryQuery::create()
|
||||
->filterByParent($category->getParent());
|
||||
|
||||
// Up or down ?
|
||||
if ($event->getMode() == CategoryChangePositionEvent::POSITION_UP) {
|
||||
// Find the category immediately before me
|
||||
$search->filterByPosition(array('max' => $my_position-1))->orderByPosition(Criteria::DESC);
|
||||
} elseif ($event->getMode() == CategoryChangePositionEvent::POSITION_DOWN) {
|
||||
// Find the category immediately after me
|
||||
$search->filterByPosition(array('min' => $my_position+1))->orderByPosition(Criteria::ASC);
|
||||
} else
|
||||
|
||||
return;
|
||||
|
||||
$result = $search->findOne();
|
||||
|
||||
// If we found the proper category, exchange their positions
|
||||
if ($result) {
|
||||
|
||||
$cnx = Propel::getWriteConnection(CategoryTableMap::DATABASE_NAME);
|
||||
|
||||
$cnx->beginTransaction();
|
||||
|
||||
try {
|
||||
$category
|
||||
->setDispatcher($this->getDispatcher())
|
||||
->setPosition($result->getPosition())
|
||||
->save()
|
||||
;
|
||||
|
||||
$result->setPosition($my_position)->save();
|
||||
|
||||
$cnx->commit();
|
||||
} catch (Exception $e) {
|
||||
$cnx->rollback();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes category position
|
||||
*
|
||||
* @param CategoryChangePositionEvent $event
|
||||
*/
|
||||
protected function changeAbsolutePosition(CategoryChangePositionEvent $event)
|
||||
{
|
||||
$category = CategoryQuery::create()->findPk($event->getCategoryId());
|
||||
|
||||
if ($category !== null) {
|
||||
|
||||
// The required position
|
||||
$new_position = $event->getPosition();
|
||||
|
||||
// The current position
|
||||
$current_position = $category->getPosition();
|
||||
|
||||
if ($new_position != null && $new_position > 0 && $new_position != $current_position) {
|
||||
|
||||
// Find categories to offset
|
||||
$search = CategoryQuery::create()->filterByParent($category->getParent());
|
||||
|
||||
if ($new_position > $current_position) {
|
||||
// The new position is after the current position -> we will offset + 1 all categories located between us and the new position
|
||||
$search->filterByPosition(array('min' => 1+$current_position, 'max' => $new_position));
|
||||
|
||||
$delta = -1;
|
||||
} else {
|
||||
// The new position is brefore the current position -> we will offset - 1 all categories located between us and the new position
|
||||
$search->filterByPosition(array('min' => $new_position, 'max' => $current_position - 1));
|
||||
|
||||
$delta = 1;
|
||||
}
|
||||
|
||||
$results = $search->find();
|
||||
|
||||
$cnx = Propel::getWriteConnection(CategoryTableMap::DATABASE_NAME);
|
||||
|
||||
$cnx->beginTransaction();
|
||||
|
||||
try {
|
||||
foreach ($results as $result) {
|
||||
$result->setPosition($result->getPosition() + $delta)->save($cnx);
|
||||
}
|
||||
|
||||
$category
|
||||
->setDispatcher($this->getDispatcher())
|
||||
->setPosition($new_position)
|
||||
->save($cnx)
|
||||
;
|
||||
|
||||
$cnx->commit();
|
||||
} catch (Exception $e) {
|
||||
$cnx->rollback();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of event names this subscriber listens to.
|
||||
*
|
||||
* The array keys are event names and the value can be:
|
||||
*
|
||||
* * The method name to call (priority defaults to 0)
|
||||
* * An array composed of the method name to call and the priority
|
||||
* * An array of arrays composed of the method names to call and respective
|
||||
* priorities, or 0 if unset
|
||||
*
|
||||
* For instance:
|
||||
*
|
||||
* * array('eventName' => 'methodName')
|
||||
* * array('eventName' => array('methodName', $priority))
|
||||
* * array('eventName' => array(array('methodName1', $priority), array('methodName2'))
|
||||
*
|
||||
* @return array The event names to listen to
|
||||
*
|
||||
* @api
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
TheliaEvents::CATEGORY_CREATE => array("create", 128),
|
||||
TheliaEvents::CATEGORY_UPDATE => array("update", 128),
|
||||
TheliaEvents::CATEGORY_DELETE => array("delete", 128),
|
||||
|
||||
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_CHANGE_POSITION => array("changePosition", 128),
|
||||
|
||||
"action.updateCategoryPositionU" => array("changePositionUp", 128),
|
||||
"action.updateCategoryPositionDown" => array("changePositionDown", 128),
|
||||
"action.updateCategoryPosition" => array("changePosition", 128),
|
||||
TheliaEvents::CATEGORY_UPDATE_POSITION => array("updatePosition", 128)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
/*************************************************************************************/
|
||||
|
||||
namespace Thelia\Action;
|
||||
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
|
||||
use Thelia\Model\ConfigQuery;
|
||||
@@ -45,18 +44,9 @@ class Config extends BaseAction implements EventSubscriberInterface
|
||||
{
|
||||
$config = new ConfigModel();
|
||||
|
||||
$config
|
||||
->setDispatcher($this->getDispatcher())
|
||||
|
||||
->setName($event->getEventName())
|
||||
->setValue($event->getValue())
|
||||
->setLocale($event->getLocale())
|
||||
->setTitle($event->getTitle())
|
||||
->setHidden($event->getHidden())
|
||||
->setSecured($event->getSecured())
|
||||
|
||||
->save()
|
||||
;
|
||||
$config->setDispatcher($this->getDispatcher())->setName($event->getEventName())->setValue($event->getValue())
|
||||
->setLocale($event->getLocale())->setTitle($event->getTitle())->setHidden($event->getHidden())
|
||||
->setSecured($event->getSecured())->save();
|
||||
|
||||
$event->setConfig($config);
|
||||
}
|
||||
@@ -70,18 +60,13 @@ class Config extends BaseAction implements EventSubscriberInterface
|
||||
{
|
||||
$search = ConfigQuery::create();
|
||||
|
||||
if (null !== $config = $search->findOneById($event->getConfigId())) {
|
||||
if (null !== $config = $search->findPk($event->getConfigId())) {
|
||||
|
||||
if ($event->getValue() !== $config->getValue()) {
|
||||
|
||||
$config
|
||||
->setDispatcher($this->getDispatcher())
|
||||
$config->setDispatcher($this->getDispatcher())->setValue($event->getValue())->save();
|
||||
|
||||
->setValue($event->getValue())
|
||||
->save()
|
||||
;
|
||||
|
||||
$event->setConfig($config);
|
||||
$event->setConfig($config);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -95,23 +80,12 @@ class Config extends BaseAction implements EventSubscriberInterface
|
||||
{
|
||||
$search = ConfigQuery::create();
|
||||
|
||||
if (null !== $config = ConfigQuery::create()->findOneById($event->getConfigId())) {
|
||||
if (null !== $config = ConfigQuery::create()->findPk($event->getConfigId())) {
|
||||
|
||||
$config
|
||||
->setDispatcher($this->getDispatcher())
|
||||
|
||||
->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();
|
||||
$config->setDispatcher($this->getDispatcher())->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);
|
||||
}
|
||||
@@ -125,14 +99,11 @@ class Config extends BaseAction implements EventSubscriberInterface
|
||||
public function delete(ConfigDeleteEvent $event)
|
||||
{
|
||||
|
||||
if (null !== ($config = ConfigQuery::create()->findOneById($event->getConfigId()))) {
|
||||
if (null !== ($config = ConfigQuery::create()->findPk($event->getConfigId()))) {
|
||||
|
||||
if (! $config->getSecured()) {
|
||||
if (!$config->getSecured()) {
|
||||
|
||||
$config
|
||||
->setDispatcher($this->getDispatcher())
|
||||
->delete()
|
||||
;
|
||||
$config->setDispatcher($this->getDispatcher())->delete();
|
||||
|
||||
$event->setConfig($config);
|
||||
}
|
||||
@@ -145,10 +116,15 @@ class Config extends BaseAction implements EventSubscriberInterface
|
||||
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),
|
||||
TheliaEvents::CONFIG_CREATE => array(
|
||||
"create", 128
|
||||
), TheliaEvents::CONFIG_SETVALUE => array(
|
||||
"setValue", 128
|
||||
), TheliaEvents::CONFIG_UPDATE => array(
|
||||
"modify", 128
|
||||
), TheliaEvents::CONFIG_DELETE => array(
|
||||
"delete", 128
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ class Currency extends BaseAction implements EventSubscriberInterface
|
||||
{
|
||||
$search = CurrencyQuery::create();
|
||||
|
||||
if (null !== $currency = CurrencyQuery::create()->findOneById($event->getCurrencyId())) {
|
||||
if (null !== $currency = CurrencyQuery::create()->findPk($event->getCurrencyId())) {
|
||||
|
||||
$currency
|
||||
->setDispatcher($this->getDispatcher())
|
||||
@@ -97,7 +97,7 @@ class Currency extends BaseAction implements EventSubscriberInterface
|
||||
{
|
||||
$search = CurrencyQuery::create();
|
||||
|
||||
if (null !== $currency = CurrencyQuery::create()->findOneById($event->getCurrencyId())) {
|
||||
if (null !== $currency = CurrencyQuery::create()->findPk($event->getCurrencyId())) {
|
||||
|
||||
if ($currency->getByDefault() != $event->getIsDefault()) {
|
||||
|
||||
@@ -123,7 +123,7 @@ class Currency extends BaseAction implements EventSubscriberInterface
|
||||
public function delete(CurrencyDeleteEvent $event)
|
||||
{
|
||||
|
||||
if (null !== ($currency = CurrencyQuery::create()->findOneById($event->getCurrencyId()))) {
|
||||
if (null !== ($currency = CurrencyQuery::create()->findPk($event->getCurrencyId()))) {
|
||||
|
||||
$currency
|
||||
->setDispatcher($this->getDispatcher())
|
||||
|
||||
139
core/lib/Thelia/Action/Document.php
Normal file
139
core/lib/Thelia/Action/Document.php
Normal file
@@ -0,0 +1,139 @@
|
||||
<?php
|
||||
/*************************************************************************************/
|
||||
/* */
|
||||
/* Thelia */
|
||||
/* */
|
||||
/* Copyright (c) OpenStudio */
|
||||
/* email : info@thelia.net */
|
||||
/* web : http://www.thelia.net */
|
||||
/* */
|
||||
/* This program is free software; you can redistribute it and/or modify */
|
||||
/* it under the terms of the GNU General Public License as published by */
|
||||
/* the Free Software Foundation; either version 3 of the License */
|
||||
/* */
|
||||
/* This program is distributed in the hope that it will be useful, */
|
||||
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
|
||||
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
|
||||
/* GNU General Public License for more details. */
|
||||
/* */
|
||||
/* You should have received a copy of the GNU General Public License */
|
||||
/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
/* */
|
||||
/*************************************************************************************/
|
||||
|
||||
namespace Thelia\Action;
|
||||
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
|
||||
use Thelia\Core\Event\DocumentEvent;
|
||||
use Thelia\Model\ConfigQuery;
|
||||
use Thelia\Tools\URL;
|
||||
|
||||
use Imagine\Document\ImagineInterface;
|
||||
use Imagine\Document\DocumentInterface;
|
||||
use Imagine\Document\Box;
|
||||
use Imagine\Document\Color;
|
||||
use Imagine\Document\Point;
|
||||
use Thelia\Exception\DocumentException;
|
||||
use Thelia\Core\Event\TheliaEvents;
|
||||
|
||||
/**
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Various document processing options are available :
|
||||
*
|
||||
* - resizing, with border, crop, or by keeping document 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 document Gamma to the specified value. Example: gamma:0.7
|
||||
* - grayscale or greyscale: switch document to grayscale
|
||||
* - colorize:color : apply a color mask to the document. Exemple: colorize:#ff2244
|
||||
* - negative : transform the document in its negative equivalent
|
||||
* - vflip or vertical_flip : vertical flip
|
||||
* - hflip or horizontal_flip : horizontal flip
|
||||
*
|
||||
* If a problem occurs, an DocumentException may be thrown.
|
||||
*
|
||||
* @package Thelia\Action
|
||||
* @author Franck Allimant <franck@cqfdev.fr>
|
||||
*
|
||||
*/
|
||||
class Document extends BaseCachedFile implements EventSubscriberInterface
|
||||
{
|
||||
/**
|
||||
* @return string root of the document cache directory in web space
|
||||
*/
|
||||
protected function getCacheDirFromWebRoot() {
|
||||
return ConfigQuery::read('document_cache_dir_from_web_root', 'cache');
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @throws \InvalidArgumentException, DocumentException
|
||||
*/
|
||||
public function processDocument(DocumentEvent $event)
|
||||
{
|
||||
$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");
|
||||
}
|
||||
|
||||
$originalDocumentPathInCache = $this->getCacheFilePath($subdir, $source_file, true);
|
||||
|
||||
if (! file_exists($originalDocumentPathInCache)) {
|
||||
|
||||
if (! file_exists($source_file)) {
|
||||
throw new DocumentException(sprintf("Source document file %s does not exists.", $source_file));
|
||||
}
|
||||
|
||||
$mode = ConfigQuery::read('original_document_delivery_mode', 'symlink');
|
||||
|
||||
if ($mode == 'symlink') {
|
||||
if (false == symlink($source_file, $originalDocumentPathInCache)) {
|
||||
throw new DocumentException(sprintf("Failed to create symbolic link for %s in %s document cache directory", basename($source_file), $subdir));
|
||||
}
|
||||
} else {// mode = 'copy'
|
||||
if (false == @copy($source_file, $originalDocumentPathInCache)) {
|
||||
throw new DocumentException(sprintf("Failed to copy %s in %s document cache directory", basename($source_file), $subdir));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Compute the document URL
|
||||
$document_url = $this->getCacheFileURL($subdir, basename($originalDocumentPathInCache));
|
||||
|
||||
// Update the event with file path and file URL
|
||||
$event->setDocumentPath($originalDocumentPathInCache);
|
||||
$event->setDocumentUrl(URL::getInstance()->absoluteUrl($document_url, null, URL::PATH_TO_FILE));
|
||||
}
|
||||
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
TheliaEvents::DOCUMENT_PROCESS => array("processDocument", 128),
|
||||
TheliaEvents::DOCUMENT_CLEAR_CACHE => array("clearCache", 128),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -71,7 +71,7 @@ use Thelia\Core\Event\TheliaEvents;
|
||||
* @author Franck Allimant <franck@cqfdev.fr>
|
||||
*
|
||||
*/
|
||||
class Image extends BaseAction implements EventSubscriberInterface
|
||||
class Image extends BaseCachedFile implements EventSubscriberInterface
|
||||
{
|
||||
// Resize mode constants
|
||||
const EXACT_RATIO_WITH_BORDERS = 1;
|
||||
@@ -79,38 +79,10 @@ class Image extends BaseAction implements EventSubscriberInterface
|
||||
const KEEP_IMAGE_RATIO = 3;
|
||||
|
||||
/**
|
||||
* Clear the image 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 ImageEvent $event
|
||||
* @return string root of the image cache directory in web space
|
||||
*/
|
||||
public function clearCache(ImageEvent $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);
|
||||
|
||||
foreach ($iterator as $fileinfo) {
|
||||
|
||||
if ($fileinfo->isDot()) continue;
|
||||
|
||||
if ($fileinfo->isFile() || $fileinfo->isLink()) {
|
||||
@unlink($fileinfo->getPathname());
|
||||
} elseif ($fileinfo->isDir()) {
|
||||
$this->clearDirectory($fileinfo->getPathname());
|
||||
}
|
||||
}
|
||||
protected function getCacheDirFromWebRoot() {
|
||||
return ConfigQuery::read('image_cache_dir_from_web_root', 'cache');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -138,9 +110,9 @@ class Image extends BaseAction implements EventSubscriberInterface
|
||||
// echo basename($source_file).": ";
|
||||
|
||||
// Find cached file path
|
||||
$cacheFilePath = $this->getCacheFilePath($subdir, $source_file, $event);
|
||||
$cacheFilePath = $this->getCacheFilePath($subdir, $source_file, $event->isOriginalImage(), $event->getOptionsHash());
|
||||
|
||||
$originalImagePathInCache = $this->getCacheFilePath($subdir, $source_file, $event, true);
|
||||
$originalImagePathInCache = $this->getCacheFilePath($subdir, $source_file, true);
|
||||
|
||||
if (! file_exists($cacheFilePath)) {
|
||||
|
||||
@@ -359,94 +331,6 @@ class Image extends BaseAction implements EventSubscriberInterface
|
||||
return $image;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the absolute URL to the cached image
|
||||
*
|
||||
* @param string $subdir the subdirectory related to cache base
|
||||
* @param string $filename the safe filename, as returned by getCacheFilePath()
|
||||
* @return string the absolute URL to the cached image
|
||||
*/
|
||||
protected function getCacheFileURL($subdir, $safe_filename)
|
||||
{
|
||||
$path = $this->getCachePathFromWebRoot($subdir);
|
||||
|
||||
return URL::getInstance()->absoluteUrl(sprintf("%s/%s", $path, $safe_filename), null, URL::PATH_TO_FILE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the full path of the cached file
|
||||
*
|
||||
* @param string $subdir the subdirectory related to cache base
|
||||
* @param string $filename the filename
|
||||
* @param boolean $forceOriginalImage if true, the origiunal image path in the cache dir is returned.
|
||||
* @return string the cache directory path relative to Web Root
|
||||
*/
|
||||
protected function getCacheFilePath($subdir, $filename, ImageEvent $event, $forceOriginalImage = false)
|
||||
{
|
||||
$path = $this->getCachePath($subdir);
|
||||
|
||||
$safe_filename = preg_replace("[^:alnum:\-\._]", "-", strtolower(basename($filename)));
|
||||
|
||||
// Keep original safe name if no tranformations are applied
|
||||
if ($forceOriginalImage || $event->isOriginalImage())
|
||||
return sprintf("%s/%s", $path, $safe_filename);
|
||||
else
|
||||
return sprintf("%s/%s-%s", $path, $event->getOptionsHash(), $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 = ConfigQuery::read('image_cache_dir_from_web_root', 'cache');
|
||||
|
||||
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.
|
||||
* @throws \RuntimeException if cache directory cannot be created
|
||||
* @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 ImageException(sprintf("Failed to create %s/%s image cache directory", $cache_base));
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new Imagine object using current driver configuration
|
||||
*
|
||||
|
||||
@@ -70,7 +70,7 @@ class Message extends BaseAction implements EventSubscriberInterface
|
||||
{
|
||||
$search = MessageQuery::create();
|
||||
|
||||
if (null !== $message = MessageQuery::create()->findOneById($event->getMessageId())) {
|
||||
if (null !== $message = MessageQuery::create()->findPk($event->getMessageId())) {
|
||||
|
||||
$message
|
||||
->setDispatcher($this->getDispatcher())
|
||||
@@ -99,7 +99,7 @@ class Message extends BaseAction implements EventSubscriberInterface
|
||||
public function delete(MessageDeleteEvent $event)
|
||||
{
|
||||
|
||||
if (null !== ($message = MessageQuery::create()->findOneById($event->getMessageId()))) {
|
||||
if (null !== ($message = MessageQuery::create()->findPk($event->getMessageId()))) {
|
||||
|
||||
$message
|
||||
->setDispatcher($this->getDispatcher())
|
||||
|
||||
Reference in New Issue
Block a user