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:
23
Readme.md
23
Readme.md
@@ -12,14 +12,33 @@ Here is the most recent developed code for the next major version (v2). You can
|
||||
|
||||
Most part of the code can possibly change, a large part will be refactor soon, graphical setup does not exist yet.
|
||||
|
||||
Requirements
|
||||
------------
|
||||
|
||||
* php 5.4
|
||||
* apache 2
|
||||
* mysql 5
|
||||
|
||||
If you use Mac OSX, it still doesn't use php 5.4 as default php version... There are many solutions for you :
|
||||
|
||||
* use linux (the best one)
|
||||
* use last MAMP version and put the php bin directory in your path :
|
||||
|
||||
```bash
|
||||
export PATH=/Applications/MAMP/bin/php/php5.4.x/bin/:$PATH
|
||||
```
|
||||
|
||||
* configure a complete development environment : http://php-osx.liip.ch/
|
||||
* use a virtual machine with vagrant and puppet : https://puphpet.com/
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
``` bash
|
||||
$ git clone --recursive https://github.com/thelia/thelia.git
|
||||
$ cd thelia
|
||||
$ wget http://getcomposer.org/composer.phar
|
||||
$ php composer.phar install
|
||||
$ curl -sS https://getcomposer.org/installer | php
|
||||
$ php composer.phar install --optimize-autoloader
|
||||
```
|
||||
|
||||
Finish the installation using cli tools :
|
||||
|
||||
@@ -36,7 +36,8 @@
|
||||
"simplepie/simplepie": "dev-master",
|
||||
|
||||
"imagine/imagine": "dev-master",
|
||||
"symfony/icu": "1.0"
|
||||
"symfony/icu": "1.0",
|
||||
"swiftmailer/swiftmailer": "5.0.*"
|
||||
},
|
||||
"require-dev" : {
|
||||
"phpunit/phpunit": "3.7.*",
|
||||
@@ -53,9 +54,5 @@
|
||||
"": "local/modules/",
|
||||
"Thelia" : "core/lib/"
|
||||
}
|
||||
},
|
||||
"scripts" : {
|
||||
"post-update-cmd": "composer dump-autoload -o",
|
||||
"post-install-cmd": "composer dump-autoload -o"
|
||||
}
|
||||
}
|
||||
|
||||
51
composer.lock
generated
51
composer.lock
generated
@@ -3,7 +3,7 @@
|
||||
"This file locks the dependencies of your project to a known state",
|
||||
"Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file"
|
||||
],
|
||||
"hash": "28dfdc7a840f9e70df422581f82a871f",
|
||||
"hash": "a40be01c82e68ba0c446dc204d2667da",
|
||||
"packages": [
|
||||
{
|
||||
"name": "imagine/imagine",
|
||||
@@ -445,6 +445,55 @@
|
||||
],
|
||||
"time": "2013-07-02 16:38:47"
|
||||
},
|
||||
{
|
||||
"name": "swiftmailer/swiftmailer",
|
||||
"version": "v5.0.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/swiftmailer/swiftmailer.git",
|
||||
"reference": "f3917ecef35a4e4d98b303eb9fee463bc983f379"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/f3917ecef35a4e4d98b303eb9fee463bc983f379",
|
||||
"reference": "f3917ecef35a4e4d98b303eb9fee463bc983f379",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.2.4"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "5.1-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"files": [
|
||||
"lib/swift_required.php"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Fabien Potencier",
|
||||
"email": "fabien@symfony.com"
|
||||
},
|
||||
{
|
||||
"name": "Chris Corbyn"
|
||||
}
|
||||
],
|
||||
"description": "Swiftmailer, free feature-rich PHP mailer",
|
||||
"homepage": "http://swiftmailer.org",
|
||||
"keywords": [
|
||||
"mail",
|
||||
"mailer"
|
||||
],
|
||||
"time": "2013-08-30 12:35:21"
|
||||
},
|
||||
{
|
||||
"name": "symfony-cmf/routing",
|
||||
"version": "1.0.0",
|
||||
|
||||
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())
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
<form name="thelia.address.update" class="Thelia\Form\AddressUpdateForm" />
|
||||
|
||||
<form name="thelia.admin.category.creation" class="Thelia\Form\CategoryCreationForm"/>
|
||||
<form name="thelia.admin.category.deletion" class="Thelia\Form\CategoryModificationForm"/>
|
||||
<form name="thelia.admin.category.modification" class="Thelia\Form\CategoryModificationForm"/>
|
||||
|
||||
<form name="thelia.admin.product.creation" class="Thelia\Form\ProductCreationForm"/>
|
||||
<form name="thelia.admin.product.deletion" class="Thelia\Form\ProductModificationForm"/>
|
||||
@@ -269,6 +269,10 @@
|
||||
<tag name="thelia.coupon.addCoupon"/>
|
||||
</service>
|
||||
|
||||
<service id="mailer" class="Thelia\Mailer\MailerFactory">
|
||||
<argument type="service" id="event_dispatcher"/>
|
||||
</service>
|
||||
|
||||
|
||||
</services>
|
||||
|
||||
|
||||
@@ -85,7 +85,7 @@
|
||||
</route>
|
||||
|
||||
<route id="admin.categories.set-default" path="/admin/categories/toggle-online">
|
||||
<default key="_controller">Thelia\Controller\Admin\CategoryController::toggleOnlineAction</default>
|
||||
<default key="_controller">Thelia\Controller\Admin\CategoryController::setToggleVisibilityAction</default>
|
||||
</route>
|
||||
|
||||
<route id="admin.categories.delete" path="/admin/categories/delete">
|
||||
|
||||
@@ -240,6 +240,17 @@ abstract class AbstractCrudController extends BaseAdminController
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Put in this method post object position change processing if required.
|
||||
*
|
||||
* @param unknown $deleteEvent the delete event
|
||||
* @return Response a response, or null to continue normal processing
|
||||
*/
|
||||
protected function performAdditionalUpdatePositionAction($positionChangeEvent)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current list order identifier, updating it in the same time.
|
||||
*/
|
||||
@@ -309,14 +320,18 @@ abstract class AbstractCrudController extends BaseAdminController
|
||||
$this->adminLogAppend(sprintf("%s %s (ID %s) created", ucfirst($this->objectName), $this->getObjectLabel($createdObject), $this->getObjectId($createdObject)));
|
||||
}
|
||||
|
||||
$this->performAdditionalCreateAction($createEvent);
|
||||
$response = $this->performAdditionalCreateAction($createEvent);
|
||||
|
||||
// Substitute _ID_ in the URL with the ID of the created object
|
||||
$successUrl = str_replace('_ID_', $this->getObjectId($createdObject), $creationForm->getSuccessUrl());
|
||||
|
||||
// Redirect to the success URL
|
||||
$this->redirect($successUrl);
|
||||
if ($response == null) {
|
||||
// Substitute _ID_ in the URL with the ID of the created object
|
||||
$successUrl = str_replace('_ID_', $this->getObjectId($createdObject), $creationForm->getSuccessUrl());
|
||||
|
||||
// Redirect to the success URL
|
||||
$this->redirect($successUrl);
|
||||
}
|
||||
else {
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
catch (FormValidationException $ex) {
|
||||
// Form cannot be validated
|
||||
@@ -396,16 +411,21 @@ abstract class AbstractCrudController extends BaseAdminController
|
||||
$this->adminLogAppend(sprintf("%s %s (ID %s) modified", ucfirst($this->objectName), $this->getObjectLabel($changedObject), $this->getObjectId($changedObject)));
|
||||
}
|
||||
|
||||
$this->performAdditionalUpdateAction($changeEvent);
|
||||
$response = $this->performAdditionalUpdateAction($changeEvent);
|
||||
|
||||
// If we have to stay on the same page, do not redirect to the succesUrl,
|
||||
// just redirect to the edit page again.
|
||||
if ($this->getRequest()->get('save_mode') == 'stay') {
|
||||
$this->redirectToEditionTemplate($this->getRequest());
|
||||
if ($response == null) {
|
||||
// If we have to stay on the same page, do not redirect to the succesUrl,
|
||||
// just redirect to the edit page again.
|
||||
if ($this->getRequest()->get('save_mode') == 'stay') {
|
||||
$this->redirectToEditionTemplate($this->getRequest());
|
||||
}
|
||||
|
||||
// Redirect to the success URL
|
||||
$this->redirect($changeForm->getSuccessUrl());
|
||||
}
|
||||
else {
|
||||
return $response;
|
||||
}
|
||||
|
||||
// Redirect to the success URL
|
||||
$this->redirect($changeForm->getSuccessUrl());
|
||||
}
|
||||
catch (FormValidationException $ex) {
|
||||
// Form cannot be validated
|
||||
@@ -452,7 +472,14 @@ abstract class AbstractCrudController extends BaseAdminController
|
||||
return $this->errorPage($ex);
|
||||
}
|
||||
|
||||
$this->redirectToListTemplate();
|
||||
$response = $this->performAdditionalUpdatePositionAction($event);
|
||||
|
||||
if ($response == null) {
|
||||
$this->redirectToListTemplate();
|
||||
}
|
||||
else {
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -475,7 +502,7 @@ abstract class AbstractCrudController extends BaseAdminController
|
||||
return $this->errorPage($ex);
|
||||
}
|
||||
|
||||
$this->redirectToRoute('admin.categories.default');
|
||||
$this->redirectToListTemplate();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -33,7 +33,7 @@ use Thelia\Form\AttributeAvCreationForm;
|
||||
use Thelia\Core\Event\UpdatePositionEvent;
|
||||
|
||||
/**
|
||||
* Manages attributes-av sent by mail
|
||||
* Manages attributes-av
|
||||
*
|
||||
* @author Franck Allimant <franck@cqfdev.fr>
|
||||
*/
|
||||
|
||||
@@ -37,7 +37,7 @@ use Thelia\Core\Event\AttributeAvUpdateEvent;
|
||||
use Thelia\Core\Event\AttributeEvent;
|
||||
|
||||
/**
|
||||
* Manages attributes sent by mail
|
||||
* Manages attributes
|
||||
*
|
||||
* @author Franck Allimant <franck@cqfdev.fr>
|
||||
*/
|
||||
|
||||
@@ -23,226 +23,160 @@
|
||||
|
||||
namespace Thelia\Controller\Admin;
|
||||
|
||||
use Thelia\Core\Event\TheliaEvents;
|
||||
use Thelia\Core\Event\CategoryCreateEvent;
|
||||
use Thelia\Form\CategoryCreationForm;
|
||||
use Thelia\Core\Event\CategoryDeleteEvent;
|
||||
use Thelia\Core\Event\CategoryUpdatePositionEvent;
|
||||
use Thelia\Core\Event\TheliaEvents;
|
||||
use Thelia\Core\Event\CategoryUpdateEvent;
|
||||
use Thelia\Core\Event\CategoryCreateEvent;
|
||||
use Thelia\Model\CategoryQuery;
|
||||
use Thelia\Form\CategoryModificationForm;
|
||||
use Thelia\Form\CategoryCreationForm;
|
||||
use Thelia\Core\Event\UpdatePositionEvent;
|
||||
use Thelia\Core\Event\CategoryToggleVisibilityEvent;
|
||||
|
||||
class CategoryController extends BaseAdminController
|
||||
/**
|
||||
* Manages categories
|
||||
*
|
||||
* @author Franck Allimant <franck@cqfdev.fr>
|
||||
*/
|
||||
class CategoryController extends AbstractCrudController
|
||||
{
|
||||
/**
|
||||
* Render the categories list, ensuring the sort order is set.
|
||||
*
|
||||
* @return Symfony\Component\HttpFoundation\Response the response
|
||||
*/
|
||||
protected function renderList()
|
||||
{
|
||||
return $this->render('categories', $this->getTemplateArgs());
|
||||
public function __construct() {
|
||||
parent::__construct(
|
||||
'category',
|
||||
'manual',
|
||||
'category_order',
|
||||
|
||||
'admin.categories.default',
|
||||
'admin.categories.create',
|
||||
'admin.categories.update',
|
||||
'admin.categories.delete',
|
||||
|
||||
TheliaEvents::CATEGORY_CREATE,
|
||||
TheliaEvents::CATEGORY_UPDATE,
|
||||
TheliaEvents::CATEGORY_DELETE,
|
||||
TheliaEvents::CATEGORY_TOGGLE_VISIBILITY,
|
||||
TheliaEvents::CATEGORY_UPDATE_POSITION
|
||||
);
|
||||
}
|
||||
|
||||
protected function getTemplateArgs()
|
||||
{
|
||||
// Get the category ID
|
||||
$category_id = $this->getRequest()->get('category_id', 0);
|
||||
protected function getCreationForm() {
|
||||
return new CategoryCreationForm($this->getRequest());
|
||||
}
|
||||
|
||||
// Find the current category order
|
||||
$category_order = $this->getRequest()->get(
|
||||
'order',
|
||||
$this->getSession()->get('admin.category_order', 'manual')
|
||||
protected function getUpdateForm() {
|
||||
return new CategoryModificationForm($this->getRequest());
|
||||
}
|
||||
|
||||
protected function getCreationEvent($formData) {
|
||||
$createEvent = new CategoryCreateEvent();
|
||||
|
||||
$createEvent
|
||||
->setTitle($formData['title'])
|
||||
->setLocale($formData["locale"])
|
||||
->setParent($formData['parent'])
|
||||
->setVisible($formData['visible'])
|
||||
;
|
||||
|
||||
return $createEvent;
|
||||
}
|
||||
|
||||
protected function getUpdateEvent($formData) {
|
||||
$changeEvent = new CategoryUpdateEvent($formData['id']);
|
||||
|
||||
// Create and dispatch the change event
|
||||
$changeEvent
|
||||
->setLocale($formData['locale'])
|
||||
->setTitle($formData['title'])
|
||||
->setChapo($formData['chapo'])
|
||||
->setDescription($formData['description'])
|
||||
->setPostscriptum($formData['postscriptum'])
|
||||
->setVisible($formData['visible'])
|
||||
->setUrl($formData['url'])
|
||||
->setParent($formData['parent'])
|
||||
;
|
||||
|
||||
return $changeEvent;
|
||||
}
|
||||
|
||||
protected function createUpdatePositionEvent($positionChangeMode, $positionValue) {
|
||||
|
||||
return new UpdatePositionEvent(
|
||||
$this->getRequest()->get('category_id', null),
|
||||
$positionChangeMode,
|
||||
$positionValue
|
||||
);
|
||||
}
|
||||
|
||||
protected function getDeleteEvent() {
|
||||
return new CategoryDeleteEvent($this->getRequest()->get('category_id', 0));
|
||||
}
|
||||
|
||||
protected function eventContainsObject($event) {
|
||||
return $event->hasCategory();
|
||||
}
|
||||
|
||||
protected function hydrateObjectForm($object) {
|
||||
|
||||
// Prepare the data that will hydrate the form
|
||||
$data = array(
|
||||
'id' => $object->getId(),
|
||||
'locale' => $object->getLocale(),
|
||||
'title' => $object->getTitle(),
|
||||
'chapo' => $object->getChapo(),
|
||||
'description' => $object->getDescription(),
|
||||
'postscriptum' => $object->getPostscriptum(),
|
||||
'visible' => $object->getVisible(),
|
||||
'url' => $object->getRewritenUrl($this->getCurrentEditionLocale()),
|
||||
'parent' => $object->getParent()
|
||||
);
|
||||
|
||||
$args = array(
|
||||
'current_category_id' => $category_id,
|
||||
'category_order' => $category_order,
|
||||
// Setup the object form
|
||||
return new CategoryModificationForm($this->getRequest(), "form", $data);
|
||||
}
|
||||
|
||||
protected function getObjectFromEvent($event) {
|
||||
return $event->hasCategory() ? $event->getCategory() : null;
|
||||
}
|
||||
|
||||
protected function getExistingObject() {
|
||||
return CategoryQuery::create()
|
||||
->joinWithI18n($this->getCurrentEditionLocale())
|
||||
->findOneById($this->getRequest()->get('category_id', 0));
|
||||
}
|
||||
|
||||
protected function getObjectLabel($object) {
|
||||
return $object->getTitle();
|
||||
}
|
||||
|
||||
protected function getObjectId($object) {
|
||||
return $object->getId();
|
||||
}
|
||||
|
||||
protected function renderListTemplate($currentOrder) {
|
||||
return $this->render('categories',
|
||||
array(
|
||||
'category_order' => $currentOrder,
|
||||
'category_id' => $this->getRequest()->get('category_id', 0)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
protected function renderEditionTemplate() {
|
||||
return $this->render('category-edit', array('category_id' => $this->getRequest()->get('category_id', 0)));
|
||||
}
|
||||
|
||||
protected function redirectToEditionTemplate() {
|
||||
$this->redirectToRoute(
|
||||
"admin.categories.update",
|
||||
array('category_id' => $this->getRequest()->get('category_id', 0))
|
||||
);
|
||||
|
||||
// Store the current sort order in session
|
||||
$this->getSession()->set('admin.category_order', $category_order);
|
||||
|
||||
return $args;
|
||||
}
|
||||
|
||||
/**
|
||||
* The default action is displaying the categories list.
|
||||
*
|
||||
* @return Symfony\Component\HttpFoundation\Response the response
|
||||
*/
|
||||
public function defaultAction()
|
||||
{
|
||||
if (null !== $response = $this->checkAuth("admin.categories.view")) return $response;
|
||||
return $this->renderList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new category object
|
||||
*
|
||||
* @return Symfony\Component\HttpFoundation\Response the response
|
||||
*/
|
||||
public function createAction()
|
||||
{
|
||||
// Check current user authorization
|
||||
if (null !== $response = $this->checkAuth("admin.categories.create")) return $response;
|
||||
|
||||
$error_msg = false;
|
||||
|
||||
// Create the Creation Form
|
||||
$creationForm = new CategoryCreationForm($this->getRequest());
|
||||
|
||||
try {
|
||||
|
||||
// Validate the form, create the CategoryCreation event and dispatch it.
|
||||
$form = $this->validateForm($creationForm, "POST");
|
||||
|
||||
$data = $form->getData();
|
||||
|
||||
$createEvent = new CategoryCreateEvent(
|
||||
$data["title"],
|
||||
$data["parent"],
|
||||
$data["locale"]
|
||||
);
|
||||
|
||||
$this->dispatch(TheliaEvents::CATEGORY_CREATE, $createEvent);
|
||||
|
||||
if (! $createEvent->hasCategory()) throw new \LogicException($this->getTranslator()->trans("No category was created."));
|
||||
|
||||
$createdObject = $createEvent->getCategory();
|
||||
|
||||
// Log category creation
|
||||
$this->adminLogAppend(sprintf("Category %s (ID %s) created", $createdObject->getTitle(), $createdObject->getId()));
|
||||
|
||||
// Substitute _ID_ in the URL with the ID of the created object
|
||||
$successUrl = str_replace('_ID_', $createdObject->getId(), $creationForm->getSuccessUrl());
|
||||
|
||||
// Redirect to the success URL
|
||||
$this->redirect($successUrl);
|
||||
} catch (FormValidationException $ex) {
|
||||
// Form cannot be validated
|
||||
$error_msg = $this->createStandardFormValidationErrorMessage($ex);
|
||||
} catch (\Exception $ex) {
|
||||
// Any other error
|
||||
$error_msg = $ex->getMessage();
|
||||
}
|
||||
|
||||
$this->setupFormErrorContext("category creation", $error_msg, $creationForm, $ex);
|
||||
|
||||
// At this point, the form has error, and should be redisplayed.
|
||||
return $this->renderList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a category object for modification, and display the edit template.
|
||||
*
|
||||
* @return Symfony\Component\HttpFoundation\Response the response
|
||||
*/
|
||||
public function changeAction()
|
||||
{
|
||||
// Check current user authorization
|
||||
if (null !== $response = $this->checkAuth("admin.categories.update")) return $response;
|
||||
|
||||
// Load the category object
|
||||
$category = CategoryQuery::create()
|
||||
->joinWithI18n($this->getCurrentEditionLocale())
|
||||
->findOneById($this->getRequest()->get('category_id'));
|
||||
|
||||
if ($category != null) {
|
||||
|
||||
// Prepare the data that will hydrate the form
|
||||
$data = array(
|
||||
'id' => $category->getId(),
|
||||
'locale' => $category->getLocale(),
|
||||
'title' => $category->getTitle(),
|
||||
'chapo' => $category->getChapo(),
|
||||
'description' => $category->getDescription(),
|
||||
'postscriptum' => $category->getPostscriptum(),
|
||||
'parent' => $category->getParent(),
|
||||
'visible' => $category->getVisible() ? true : false,
|
||||
'url' => $category->getUrl($this->getCurrentEditionLocale())
|
||||
// tbc !!!
|
||||
);
|
||||
|
||||
// Setup the object form
|
||||
$changeForm = new CategoryModificationForm($this->getRequest(), "form", $data);
|
||||
|
||||
// Pass it to the parser
|
||||
$this->getParserContext()->addForm($changeForm);
|
||||
}
|
||||
|
||||
// Render the edition template.
|
||||
return $this->render('category-edit', $this->getTemplateArgs());
|
||||
}
|
||||
|
||||
/**
|
||||
* Save changes on a modified category object, and either go back to the category list, or stay on the edition page.
|
||||
*
|
||||
* @return Symfony\Component\HttpFoundation\Response the response
|
||||
*/
|
||||
public function saveChangeAction()
|
||||
{
|
||||
// Check current user authorization
|
||||
if (null !== $response = $this->checkAuth("admin.categories.update")) return $response;
|
||||
|
||||
$error_msg = false;
|
||||
|
||||
// Create the form from the request
|
||||
$changeForm = new CategoryModificationForm($this->getRequest());
|
||||
|
||||
// Get the category ID
|
||||
$category_id = $this->getRequest()->get('category_id');
|
||||
|
||||
try {
|
||||
|
||||
// Check the form against constraints violations
|
||||
$form = $this->validateForm($changeForm, "POST");
|
||||
|
||||
// Get the form field values
|
||||
$data = $form->getData();
|
||||
|
||||
$changeEvent = new CategoryUpdateEvent($data['id']);
|
||||
|
||||
// Create and dispatch the change event
|
||||
$changeEvent
|
||||
->setCategoryName($data['name'])
|
||||
->setLocale($data["locale"])
|
||||
->setSymbol($data['symbol'])
|
||||
->setCode($data['code'])
|
||||
->setRate($data['rate'])
|
||||
;
|
||||
|
||||
$this->dispatch(TheliaEvents::CATEGORY_UPDATE, $changeEvent);
|
||||
|
||||
if (! $createEvent->hasCategory()) throw new \LogicException($this->getTranslator()->trans("No category was updated."));
|
||||
|
||||
// Log category modification
|
||||
$changedObject = $changeEvent->getCategory();
|
||||
|
||||
$this->adminLogAppend(sprintf("Category %s (ID %s) modified", $changedObject->getTitle(), $changedObject->getId()));
|
||||
|
||||
// If we have to stay on the same page, do not redirect to the succesUrl,
|
||||
// just redirect to the edit page again.
|
||||
if ($this->getRequest()->get('save_mode') == 'stay') {
|
||||
$this->redirectToRoute(
|
||||
"admin.categories.update",
|
||||
array('category_id' => $category_id)
|
||||
protected function redirectToListTemplate() {
|
||||
$this->redirectToRoute(
|
||||
'admin.categories.default',
|
||||
array('category_id' => $this->getRequest()->get('category_id', 0))
|
||||
);
|
||||
}
|
||||
|
||||
// Redirect to the success URL
|
||||
$this->redirect($changeForm->getSuccessUrl());
|
||||
} catch (FormValidationException $ex) {
|
||||
// Form cannot be validated
|
||||
$error_msg = $this->createStandardFormValidationErrorMessage($ex);
|
||||
} catch (\Exception $ex) {
|
||||
// Any other error
|
||||
$error_msg = $ex->getMessage();
|
||||
}
|
||||
|
||||
$this->setupFormErrorContext("category modification", $error_msg, $changeForm, $ex);
|
||||
|
||||
// At this point, the form has errors, and should be redisplayed.
|
||||
return $this->render('category-edit', array('category_id' => $category_id));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -253,74 +187,41 @@ class CategoryController extends BaseAdminController
|
||||
// Check current user authorization
|
||||
if (null !== $response = $this->checkAuth("admin.categories.update")) return $response;
|
||||
|
||||
$changeEvent = new CategoryUpdateEvent($this->getRequest()->get('category_id', 0));
|
||||
|
||||
// Create and dispatch the change event
|
||||
$changeEvent->setIsDefault(true);
|
||||
$event = new CategoryToggleVisibilityEvent($this->getExistingObject());
|
||||
|
||||
try {
|
||||
$this->dispatch(TheliaEvents::CATEGORY_SET_DEFAULT, $changeEvent);
|
||||
$this->dispatch(TheliaEvents::CATEGORY_TOGGLE_VISIBILITY, $event);
|
||||
} catch (\Exception $ex) {
|
||||
// Any error
|
||||
return $this->errorPage($ex);
|
||||
}
|
||||
|
||||
$this->redirectToRoute('admin.categories.default');
|
||||
// Ajax response -> no action
|
||||
return $this->nullResponse();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update categoryposition
|
||||
*/
|
||||
public function updatePositionAction()
|
||||
protected function performAdditionalDeleteAction($deleteEvent)
|
||||
{
|
||||
// Check current user authorization
|
||||
if (null !== $response = $this->checkAuth("admin.categories.update")) return $response;
|
||||
// Redirect to parent category list
|
||||
$this->redirectToRoute(
|
||||
'admin.categories.default',
|
||||
array('category_id' => $deleteEvent->getCategory()->getParent())
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
$mode = $this->getRequest()->get('mode', null);
|
||||
protected function performAdditionalUpdatePositionAction($event)
|
||||
{
|
||||
|
||||
if ($mode == 'up')
|
||||
$mode = CategoryUpdatePositionEvent::POSITION_UP;
|
||||
else if ($mode == 'down')
|
||||
$mode = CategoryUpdatePositionEvent::POSITION_DOWN;
|
||||
else
|
||||
$mode = CategoryUpdatePositionEvent::POSITION_ABSOLUTE;
|
||||
$category = CategoryQuery::create()->findPk($event->getObjectId());
|
||||
|
||||
$position = $this->getRequest()->get('position', null);
|
||||
|
||||
$event = new CategoryUpdatePositionEvent(
|
||||
$this->getRequest()->get('category_id', null),
|
||||
$mode,
|
||||
$this->getRequest()->get('position', null)
|
||||
if ($category != null) {
|
||||
// Redirect to parent category list
|
||||
$this->redirectToRoute(
|
||||
'admin.categories.default',
|
||||
array('category_id' => $category->getParent())
|
||||
);
|
||||
|
||||
$this->dispatch(TheliaEvents::CATEGORY_UPDATE_POSITION, $event);
|
||||
} catch (\Exception $ex) {
|
||||
// Any error
|
||||
return $this->errorPage($ex);
|
||||
}
|
||||
|
||||
$this->redirectToRoute('admin.categories.default');
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a category object
|
||||
*
|
||||
* @return Symfony\Component\HttpFoundation\Response the response
|
||||
*/
|
||||
public function deleteAction()
|
||||
{
|
||||
// Check current user authorization
|
||||
if (null !== $response = $this->checkAuth("admin.categories.delete")) return $response;
|
||||
|
||||
// Get the category id, and dispatch the deleted request
|
||||
$event = new CategoryDeleteEvent($this->getRequest()->get('category_id'));
|
||||
|
||||
$this->dispatch(TheliaEvents::CATEGORY_DELETE, $event);
|
||||
|
||||
if ($event->hasCategory())
|
||||
$this->adminLogAppend(sprintf("Category %s (ID %s) deleted", $event->getCategory()->getTitle(), $event->getCategory()->getId()));
|
||||
|
||||
$this->redirectToRoute('admin.categories.default');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ use Thelia\Form\ConfigCreationForm;
|
||||
use Thelia\Core\Event\UpdatePositionEvent;
|
||||
|
||||
/**
|
||||
* Manages variables sent by mail
|
||||
* Manages variables
|
||||
*
|
||||
* @author Franck Allimant <franck@cqfdev.fr>
|
||||
*/
|
||||
|
||||
@@ -33,7 +33,7 @@ use Thelia\Form\CurrencyCreationForm;
|
||||
use Thelia\Core\Event\UpdatePositionEvent;
|
||||
|
||||
/**
|
||||
* Manages currencies sent by mail
|
||||
* Manages currencies
|
||||
*
|
||||
* @author Franck Allimant <franck@cqfdev.fr>
|
||||
*/
|
||||
|
||||
@@ -33,7 +33,7 @@ use Thelia\Form\FeatureAvCreationForm;
|
||||
use Thelia\Core\Event\UpdatePositionEvent;
|
||||
|
||||
/**
|
||||
* Manages features-av sent by mail
|
||||
* Manages features-av
|
||||
*
|
||||
* @author Franck Allimant <franck@cqfdev.fr>
|
||||
*/
|
||||
|
||||
@@ -37,7 +37,7 @@ use Thelia\Core\Event\FeatureAvUpdateEvent;
|
||||
use Thelia\Core\Event\FeatureEvent;
|
||||
|
||||
/**
|
||||
* Manages features sent by mail
|
||||
* Manages features
|
||||
*
|
||||
* @author Franck Allimant <franck@cqfdev.fr>
|
||||
*/
|
||||
|
||||
@@ -41,7 +41,7 @@ use Thelia\Core\Event\TemplateAddFeatureEvent;
|
||||
use Thelia\Core\Event\TemplateDeleteFeatureEvent;
|
||||
|
||||
/**
|
||||
* Manages templates sent by mail
|
||||
* Manages product templates
|
||||
*
|
||||
* @author Franck Allimant <franck@cqfdev.fr>
|
||||
*/
|
||||
|
||||
@@ -281,4 +281,16 @@ class BaseController extends ContainerAware
|
||||
$this->accessDenied();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* return an instance of \Swift_Mailer with good Transporter configured.
|
||||
*
|
||||
* @return \Swift_Mailer
|
||||
*/
|
||||
public function getMailer()
|
||||
{
|
||||
$mailer = $this->container->get('mailer');
|
||||
return $mailer->getSwiftMailer();
|
||||
}
|
||||
}
|
||||
|
||||
94
core/lib/Thelia/Core/Event/CachedFileEvent.php
Normal file
94
core/lib/Thelia/Core/Event/CachedFileEvent.php
Normal file
@@ -0,0 +1,94 @@
|
||||
<?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\Core\Event;
|
||||
|
||||
class CachedFileEvent extends ActionEvent
|
||||
{
|
||||
/**
|
||||
* @var string The complete file name (with path) of the source file
|
||||
*/
|
||||
protected $source_filepath = null;
|
||||
/**
|
||||
* @var string The target subdirectory in the cache
|
||||
*/
|
||||
protected $cache_subdirectory = null;
|
||||
|
||||
/**
|
||||
* @var string The absolute URL of the cached file (in the web space)
|
||||
*/
|
||||
protected $file_url = null;
|
||||
|
||||
/**
|
||||
* @var string The absolute path of the cached file
|
||||
*/
|
||||
protected $cache_filepath = null;
|
||||
|
||||
public function getFileUrl()
|
||||
{
|
||||
return $this->file_url;
|
||||
}
|
||||
|
||||
public function setFileUrl($file_url)
|
||||
{
|
||||
$this->file_url = $file_url;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCacheFilepath()
|
||||
{
|
||||
return $this->cache_filepath;
|
||||
}
|
||||
|
||||
public function setCacheFilepath($cache_filepath)
|
||||
{
|
||||
$this->cache_filepath = $cache_filepath;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getSourceFilepath()
|
||||
{
|
||||
return $this->source_filepath;
|
||||
}
|
||||
|
||||
public function setSourceFilepath($source_filepath)
|
||||
{
|
||||
$this->source_filepath = $source_filepath;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCacheSubdirectory()
|
||||
{
|
||||
return $this->cache_subdirectory;
|
||||
}
|
||||
|
||||
public function setCacheSubdirectory($cache_subdirectory)
|
||||
{
|
||||
$this->cache_subdirectory = $cache_subdirectory;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -28,13 +28,7 @@ class CategoryCreateEvent extends CategoryEvent
|
||||
protected $title;
|
||||
protected $parent;
|
||||
protected $locale;
|
||||
|
||||
public function __construct($title, $parent, $locale)
|
||||
{
|
||||
$this->title = $title;
|
||||
$this->parent = $parent;
|
||||
$this->locale = $locale;
|
||||
}
|
||||
protected $visible;
|
||||
|
||||
public function getTitle()
|
||||
{
|
||||
@@ -71,4 +65,16 @@ class CategoryCreateEvent extends CategoryEvent
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getVisible()
|
||||
{
|
||||
return $this->visible;
|
||||
}
|
||||
|
||||
public function setVisible($visible)
|
||||
{
|
||||
$this->visible = $visible;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
||||
28
core/lib/Thelia/Core/Event/CategoryToggleVisibilityEvent.php
Normal file
28
core/lib/Thelia/Core/Event/CategoryToggleVisibilityEvent.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?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\Core\Event;
|
||||
|
||||
class CategoryToggleVisibilityEvent extends CategoryEvent
|
||||
{
|
||||
}
|
||||
@@ -32,7 +32,6 @@ class CategoryUpdateEvent extends CategoryCreateEvent
|
||||
protected $postscriptum;
|
||||
|
||||
protected $url;
|
||||
protected $visibility;
|
||||
protected $parent;
|
||||
|
||||
public function __construct($category_id)
|
||||
@@ -100,18 +99,6 @@ class CategoryUpdateEvent extends CategoryCreateEvent
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getVisibility()
|
||||
{
|
||||
return $this->visibility;
|
||||
}
|
||||
|
||||
public function setVisibility($visibility)
|
||||
{
|
||||
$this->visibility = $visibility;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getParent()
|
||||
{
|
||||
return $this->parent;
|
||||
|
||||
67
core/lib/Thelia/Core/Event/DocumentEvent.php
Normal file
67
core/lib/Thelia/Core/Event/DocumentEvent.php
Normal file
@@ -0,0 +1,67 @@
|
||||
<?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\Core\Event;
|
||||
|
||||
class DocumentEvent extends CachedFileEvent
|
||||
{
|
||||
protected $document_path;
|
||||
protected $document_url;
|
||||
|
||||
/**
|
||||
* @return the document file path
|
||||
*/
|
||||
public function getDocumentPath()
|
||||
{
|
||||
return $this->document_path;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $document_path the document file path
|
||||
*/
|
||||
public function setDocumentPath($document_path)
|
||||
{
|
||||
$this->document_path = $document_path;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the document URL
|
||||
*/
|
||||
public function getDocumentUrl()
|
||||
{
|
||||
return $this->document_url;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $document_url the document URL
|
||||
*/
|
||||
public function setDocumentUrl($document_url)
|
||||
{
|
||||
$this->document_url = $document_url;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -23,22 +23,8 @@
|
||||
|
||||
namespace Thelia\Core\Event;
|
||||
|
||||
class ImageEvent extends ActionEvent
|
||||
class ImageEvent extends CachedFileEvent
|
||||
{
|
||||
/**
|
||||
* @var string The complete file name (with path) of the source image
|
||||
*/
|
||||
protected $source_filepath = null;
|
||||
/**
|
||||
* @var string The target subdirectory in the image cache
|
||||
*/
|
||||
protected $cache_subdirectory = null;
|
||||
|
||||
/**
|
||||
* @var string The absolute URL of the cached image (in the web space)
|
||||
*/
|
||||
protected $file_url = null;
|
||||
|
||||
/**
|
||||
* @var string The absolute path of the cached image file
|
||||
*/
|
||||
@@ -121,6 +107,8 @@ class ImageEvent extends ActionEvent
|
||||
public function setCategory($category)
|
||||
{
|
||||
$this->category = $category;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getWidth()
|
||||
@@ -131,6 +119,8 @@ class ImageEvent extends ActionEvent
|
||||
public function setWidth($width)
|
||||
{
|
||||
$this->width = $width;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getHeight()
|
||||
@@ -141,6 +131,8 @@ class ImageEvent extends ActionEvent
|
||||
public function setHeight($height)
|
||||
{
|
||||
$this->height = $height;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getResizeMode()
|
||||
@@ -151,6 +143,8 @@ class ImageEvent extends ActionEvent
|
||||
public function setResizeMode($resize_mode)
|
||||
{
|
||||
$this->resize_mode = $resize_mode;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getBackgroundColor()
|
||||
@@ -161,6 +155,8 @@ class ImageEvent extends ActionEvent
|
||||
public function setBackgroundColor($background_color)
|
||||
{
|
||||
$this->background_color = $background_color;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getEffects()
|
||||
@@ -171,6 +167,8 @@ class ImageEvent extends ActionEvent
|
||||
public function setEffects(array $effects)
|
||||
{
|
||||
$this->effects = $effects;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getRotation()
|
||||
@@ -181,46 +179,8 @@ class ImageEvent extends ActionEvent
|
||||
public function setRotation($rotation)
|
||||
{
|
||||
$this->rotation = $rotation;
|
||||
}
|
||||
|
||||
public function getFileUrl()
|
||||
{
|
||||
return $this->file_url;
|
||||
}
|
||||
|
||||
public function setFileUrl($file_url)
|
||||
{
|
||||
$this->file_url = $file_url;
|
||||
}
|
||||
|
||||
public function getCacheFilepath()
|
||||
{
|
||||
return $this->cache_filepath;
|
||||
}
|
||||
|
||||
public function setCacheFilepath($cache_filepath)
|
||||
{
|
||||
$this->cache_filepath = $cache_filepath;
|
||||
}
|
||||
|
||||
public function getSourceFilepath()
|
||||
{
|
||||
return $this->source_filepath;
|
||||
}
|
||||
|
||||
public function setSourceFilepath($source_filepath)
|
||||
{
|
||||
$this->source_filepath = $source_filepath;
|
||||
}
|
||||
|
||||
public function getCacheSubdirectory()
|
||||
{
|
||||
return $this->cache_subdirectory;
|
||||
}
|
||||
|
||||
public function setCacheSubdirectory($cache_subdirectory)
|
||||
{
|
||||
$this->cache_subdirectory = $cache_subdirectory;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getQuality()
|
||||
@@ -231,6 +191,8 @@ class ImageEvent extends ActionEvent
|
||||
public function setQuality($quality)
|
||||
{
|
||||
$this->quality = $quality;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getOriginalFileUrl()
|
||||
@@ -241,6 +203,8 @@ class ImageEvent extends ActionEvent
|
||||
public function setOriginalFileUrl($original_file_url)
|
||||
{
|
||||
$this->original_file_url = $original_file_url;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCacheOriginalFilepath()
|
||||
@@ -251,5 +215,7 @@ class ImageEvent extends ActionEvent
|
||||
public function setCacheOriginalFilepath($cache_original_filepath)
|
||||
{
|
||||
$this->cache_original_filepath = $cache_original_filepath;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
||||
52
core/lib/Thelia/Core/Event/MailTransporterEvent.php
Normal file
52
core/lib/Thelia/Core/Event/MailTransporterEvent.php
Normal file
@@ -0,0 +1,52 @@
|
||||
<?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\Core\Event;
|
||||
|
||||
|
||||
/**
|
||||
* Class MailTransporterEvent
|
||||
* @package Thelia\Core\Event
|
||||
* @author Manuel Raynaud <mraynaud@openstudio.fr>
|
||||
*/
|
||||
class MailTransporterEvent extends ActionEvent {
|
||||
/**
|
||||
* @var \Swift_Transport
|
||||
*/
|
||||
protected $transporter;
|
||||
|
||||
public function setMailerTransporter(\Swift_Transport $transporter)
|
||||
{
|
||||
$this->transporter = $transporter;
|
||||
}
|
||||
|
||||
public function getTransporter()
|
||||
{
|
||||
return $this->transporter;
|
||||
}
|
||||
|
||||
public function hasTransporter()
|
||||
{
|
||||
return null !== $this->transporter;
|
||||
}
|
||||
}
|
||||
@@ -145,50 +145,21 @@ final class TheliaEvents
|
||||
|
||||
// -- END ADDRESS MANAGEMENT ---------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Sent once the category creation form has been successfully validated, and before category insertion in the database.
|
||||
*/
|
||||
const BEFORE_CREATECATEGORY = "action.before_createcategory";
|
||||
// -- Categories management -----------------------------------------------
|
||||
|
||||
/**
|
||||
* Create, change or delete a category
|
||||
*/
|
||||
const CATEGORY_CREATE = "action.createCategory";
|
||||
const CATEGORY_UPDATE = "action.updateCategory";
|
||||
const CATEGORY_DELETE = "action.deleteCategory";
|
||||
|
||||
/**
|
||||
* Toggle category visibility
|
||||
*/
|
||||
const CATEGORY_CREATE = "action.createCategory";
|
||||
const CATEGORY_UPDATE = "action.updateCategory";
|
||||
const CATEGORY_DELETE = "action.deleteCategory";
|
||||
const CATEGORY_TOGGLE_VISIBILITY = "action.toggleCategoryVisibility";
|
||||
const CATEGORY_UPDATE_POSITION = "action.updateCategoryPosition";
|
||||
|
||||
/**
|
||||
* Change category position
|
||||
*/
|
||||
const CATEGORY_CHANGE_POSITION = "action.updateCategoryPosition";
|
||||
|
||||
/**
|
||||
* Sent just after a successful insert of a new category in the database.
|
||||
*/
|
||||
const BEFORE_CREATECATEGORY = "action.before_createcategory";
|
||||
const AFTER_CREATECATEGORY = "action.after_createcategory";
|
||||
/**
|
||||
* Sent befonre deleting a category
|
||||
*/
|
||||
const BEFORE_DELETECATEGORY = "action.before_deletecategory";
|
||||
|
||||
/**
|
||||
* Sent just after a successful delete of a category from the database.
|
||||
*/
|
||||
const BEFORE_DELETECATEGORY = "action.before_deletecategory";
|
||||
const AFTER_DELETECATEGORY = "action.after_deletecategory";
|
||||
|
||||
/**
|
||||
* Sent just before a successful change of a category in the database.
|
||||
*/
|
||||
const BEFORE_UPDATECATEGORY = "action.before_updateCategory";
|
||||
|
||||
/**
|
||||
* Sent just after a successful change of a category in the database.
|
||||
*/
|
||||
const AFTER_UPDATECATEGORY = "action.after_updateCategory";
|
||||
|
||||
/**
|
||||
@@ -230,6 +201,11 @@ final class TheliaEvents
|
||||
*/
|
||||
const IMAGE_PROCESS = "action.processImage";
|
||||
|
||||
/**
|
||||
* Sent on document processing
|
||||
*/
|
||||
const DOCUMENT_PROCESS = "action.processDocument";
|
||||
|
||||
/**
|
||||
* Sent on image cache clear request
|
||||
*/
|
||||
@@ -437,4 +413,9 @@ final class TheliaEvents
|
||||
const BEFORE_DELETEFEATURE_AV = "action.before_deleteFeatureAv";
|
||||
const AFTER_DELETEFEATURE_AV = "action.after_deleteFeatureAv";
|
||||
|
||||
/**
|
||||
* sent when system find a mailer transporter.
|
||||
*/
|
||||
const MAILTRANSPORTER_CONFIG = 'action.mailertransporter.config';
|
||||
|
||||
}
|
||||
|
||||
@@ -173,6 +173,22 @@ class Category extends BaseI18nLoop
|
||||
$loopResult = new LoopResult($categories);
|
||||
|
||||
foreach ($categories as $category) {
|
||||
|
||||
// Find previous and next category
|
||||
$previous = CategoryQuery::create()
|
||||
->filterByParent($category->getParent())
|
||||
->filterByPosition($category->getPosition(), Criteria::LESS_THAN)
|
||||
->orderByPosition(Criteria::DESC)
|
||||
->findOne()
|
||||
;
|
||||
|
||||
$next = CategoryQuery::create()
|
||||
->filterByParent($category->getParent())
|
||||
->filterByPosition($category->getPosition(), Criteria::GREATER_THAN)
|
||||
->orderByPosition(Criteria::ASC)
|
||||
->findOne()
|
||||
;
|
||||
|
||||
/*
|
||||
* no cause pagination lost :
|
||||
* if ($this->getNotEmpty() && $category->countAllProducts() == 0) continue;
|
||||
@@ -193,7 +209,13 @@ class Category extends BaseI18nLoop
|
||||
->set("PRODUCT_COUNT", $category->countChild())
|
||||
->set("VISIBLE", $category->getVisible() ? "1" : "0")
|
||||
->set("POSITION", $category->getPosition())
|
||||
;
|
||||
|
||||
->set("HAS_PREVIOUS", $previous != null ? 1 : 0)
|
||||
->set("HAS_NEXT" , $next != null ? 1 : 0)
|
||||
|
||||
->set("PREVIOUS", $previous != null ? $previous->getId() : -1)
|
||||
->set("NEXT" , $next != null ? $next->getId() : -1)
|
||||
;
|
||||
|
||||
$loopResult->addRow($loopResultRow);
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ class CategoryTree extends BaseI18nLoop
|
||||
}
|
||||
|
||||
// changement de rubrique
|
||||
protected function buildCategoryTree($parent, $visible, $level, $max_level, array $exclude, LoopResult &$loopResult)
|
||||
protected function buildCategoryTree($parent, $visible, $level, $max_level, $exclude, LoopResult &$loopResult)
|
||||
{
|
||||
if ($level > $max_level) return;
|
||||
|
||||
@@ -73,7 +73,7 @@ class CategoryTree extends BaseI18nLoop
|
||||
|
||||
if ($visible != BooleanOrBothType::ANY) $search->filterByVisible($visible);
|
||||
|
||||
$search->filterById($exclude, Criteria::NOT_IN);
|
||||
if ($exclude != null) $search->filterById($exclude, Criteria::NOT_IN);
|
||||
|
||||
$search->orderByPosition(Criteria::ASC);
|
||||
|
||||
|
||||
277
core/lib/Thelia/Core/Template/Loop/Document.php
Normal file
277
core/lib/Thelia/Core/Template/Loop/Document.php
Normal file
@@ -0,0 +1,277 @@
|
||||
<?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\Core\Template\Loop;
|
||||
use Thelia\Core\Template\Element\BaseI18nLoop;
|
||||
use Thelia\Core\Template\Loop\Argument\Argument;
|
||||
use Thelia\Core\Event\DocumentEvent;
|
||||
use Thelia\Core\Event\TheliaEvents;
|
||||
use Thelia\Core\Template\Loop\Argument\ArgumentCollection;
|
||||
use Thelia\Type\TypeCollection;
|
||||
use Thelia\Type\EnumListType;
|
||||
use Propel\Runtime\ActiveQuery\Criteria;
|
||||
use Thelia\Model\ConfigQuery;
|
||||
use Thelia\Core\Template\Element\LoopResultRow;
|
||||
use Thelia\Core\Template\Element\LoopResult;
|
||||
use Thelia\Type\EnumType;
|
||||
use Thelia\Log\Tlog;
|
||||
|
||||
/**
|
||||
* The document loop
|
||||
*
|
||||
* @author Franck Allimant <franck@cqfdev.fr>
|
||||
*/
|
||||
class Document extends BaseI18nLoop
|
||||
{
|
||||
public $timestampable = true;
|
||||
|
||||
/**
|
||||
* @var array Possible document sources
|
||||
*/
|
||||
protected $possible_sources = array('category', 'product', 'folder', 'content');
|
||||
|
||||
/**
|
||||
* @return \Thelia\Core\Template\Loop\Argument\ArgumentCollection
|
||||
*/
|
||||
protected function getArgDefinitions()
|
||||
{
|
||||
$collection = new ArgumentCollection(
|
||||
|
||||
Argument::createIntListTypeArgument('id'),
|
||||
Argument::createIntListTypeArgument('exclude'),
|
||||
new Argument(
|
||||
'order',
|
||||
new TypeCollection(
|
||||
new EnumListType(array('alpha', 'alpha-reverse', 'manual', 'manual-reverse', 'random'))
|
||||
),
|
||||
'manual'
|
||||
),
|
||||
Argument::createIntTypeArgument('lang'),
|
||||
|
||||
Argument::createIntTypeArgument('category'),
|
||||
Argument::createIntTypeArgument('product'),
|
||||
Argument::createIntTypeArgument('folder'),
|
||||
Argument::createIntTypeArgument('content'),
|
||||
|
||||
new Argument(
|
||||
'source',
|
||||
new TypeCollection(
|
||||
new EnumType($this->possible_sources)
|
||||
)
|
||||
),
|
||||
Argument::createIntTypeArgument('source_id')
|
||||
);
|
||||
|
||||
// Add possible document sources
|
||||
foreach ($this->possible_sources as $source) {
|
||||
$collection->addArgument(Argument::createIntTypeArgument($source));
|
||||
}
|
||||
|
||||
return $collection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dynamically create the search query, and set the proper filter and order
|
||||
*
|
||||
* @param string $source a valid source identifier (@see $possible_sources)
|
||||
* @param int $object_id the source object ID
|
||||
* @return ModelCriteria the propel Query object
|
||||
*/
|
||||
protected function createSearchQuery($source, $object_id)
|
||||
{
|
||||
$object = ucfirst($source);
|
||||
|
||||
$queryClass = sprintf("\Thelia\Model\%sDocumentQuery", $object);
|
||||
$filterMethod = sprintf("filterBy%sId", $object);
|
||||
|
||||
// xxxDocumentQuery::create()
|
||||
$method = new \ReflectionMethod($queryClass, 'create');
|
||||
$search = $method->invoke(null); // Static !
|
||||
|
||||
// $query->filterByXXX(id)
|
||||
if (! is_null($object_id)) {
|
||||
$method = new \ReflectionMethod($queryClass, $filterMethod);
|
||||
$method->invoke($search, $object_id);
|
||||
}
|
||||
|
||||
$orders = $this->getOrder();
|
||||
|
||||
// Results ordering
|
||||
foreach ($orders as $order) {
|
||||
switch ($order) {
|
||||
case "alpha":
|
||||
$search->addAscendingOrderByColumn('i18n_TITLE');
|
||||
break;
|
||||
case "alpha-reverse":
|
||||
$search->addDescendingOrderByColumn('i18n_TITLE');
|
||||
break;
|
||||
case "manual-reverse":
|
||||
$search->orderByPosition(Criteria::DESC);
|
||||
break;
|
||||
case "manual":
|
||||
$search->orderByPosition(Criteria::ASC);
|
||||
break;
|
||||
case "random":
|
||||
$search->clearOrderByColumns();
|
||||
$search->addAscendingOrderByColumn('RAND()');
|
||||
break(2);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $search;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dynamically create the search query, and set the proper filter and order
|
||||
*
|
||||
* @param string $object_type (returned) the a valid source identifier (@see $possible_sources)
|
||||
* @param string $object_id (returned) the ID of the source object
|
||||
* @return ModelCriteria the propel Query object
|
||||
*/
|
||||
protected function getSearchQuery(&$object_type, &$object_id)
|
||||
{
|
||||
$search = null;
|
||||
|
||||
// Check form source="product" source_id="123" style arguments
|
||||
$source = $this->getSource();
|
||||
|
||||
if (! is_null($source)) {
|
||||
|
||||
$source_id = $this->getSourceId();
|
||||
$id = $this->getId();
|
||||
|
||||
// echo "source = ".$this->getSource().", id=".$source_id." - ".$this->getArg('source_id')->getValue()."<br />";
|
||||
|
||||
if (is_null($source_id) && is_null($id)) {
|
||||
throw new \InvalidArgumentException("If 'source' argument is specified, 'id' or 'source_id' argument should be specified");
|
||||
}
|
||||
|
||||
$search = $this->createSearchQuery($source, $source_id);
|
||||
|
||||
$object_type = $source;
|
||||
$object_id = $source_id;
|
||||
} else {
|
||||
// Check for product="id" folder="id", etc. style arguments
|
||||
foreach ($this->possible_sources as $source) {
|
||||
|
||||
$argValue = intval($this->getArgValue($source));
|
||||
|
||||
if ($argValue > 0) {
|
||||
|
||||
$search = $this->createSearchQuery($source, $argValue);
|
||||
|
||||
$object_type = $source;
|
||||
$object_id = $argValue;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($search == null)
|
||||
throw new \InvalidArgumentException(sprintf("Unable to find document source. Valid sources are %s", implode(',', $this->possible_sources)));
|
||||
|
||||
return $search;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param unknown $pagination
|
||||
*/
|
||||
public function exec(&$pagination)
|
||||
{
|
||||
// Select the proper query to use, and get the object type
|
||||
$object_type = $object_id = null;
|
||||
|
||||
$search = $this->getSearchQuery($object_type, $object_id);
|
||||
|
||||
/* manage translations */
|
||||
$locale = $this->configureI18nProcessing($search);
|
||||
|
||||
$id = $this->getId();
|
||||
|
||||
if (! is_null($id)) {
|
||||
$search->filterById($id, Criteria::IN);
|
||||
}
|
||||
|
||||
$exclude = $this->getExclude();
|
||||
if (!is_null($exclude))
|
||||
$search->filterById($exclude, Criteria::NOT_IN);
|
||||
|
||||
// Create document processing event
|
||||
$event = new DocumentEvent($this->request);
|
||||
|
||||
// echo "sql=".$search->toString();
|
||||
|
||||
$results = $this->search($search, $pagination);
|
||||
|
||||
$loopResult = new LoopResult($results);
|
||||
|
||||
foreach ($results as $result) {
|
||||
|
||||
// Create document processing event
|
||||
$event = new DocumentEvent($this->request);
|
||||
|
||||
// Put source document file path
|
||||
$source_filepath = sprintf("%s%s/%s/%s",
|
||||
THELIA_ROOT,
|
||||
ConfigQuery::read('documents_library_path', 'local/media/documents'),
|
||||
$object_type,
|
||||
$result->getFile()
|
||||
);
|
||||
|
||||
$event->setSourceFilepath($source_filepath);
|
||||
$event->setCacheSubdirectory($object_type);
|
||||
|
||||
try {
|
||||
// Dispatch document processing event
|
||||
$this->dispatcher->dispatch(TheliaEvents::DOCUMENT_PROCESS, $event);
|
||||
|
||||
$loopResultRow = new LoopResultRow($loopResult, $result, $this->versionable, $this->timestampable, $this->countable);
|
||||
|
||||
$loopResultRow
|
||||
->set("ID" , $result->getId())
|
||||
->set("LOCALE" ,$locale)
|
||||
->set("DOCUMENT_URL" , $event->getFileUrl())
|
||||
->set("DOCUMENT_PATH" , $event->getCacheFilepath())
|
||||
->set("ORIGINAL_DOCUMENT_PATH", $source_filepath)
|
||||
->set("TITLE" , $result->getVirtualColumn('i18n_TITLE'))
|
||||
->set("CHAPO" , $result->getVirtualColumn('i18n_CHAPO'))
|
||||
->set("DESCRIPTION" , $result->getVirtualColumn('i18n_DESCRIPTION'))
|
||||
->set("POSTSCRIPTUM" , $result->getVirtualColumn('i18n_POSTSCRIPTUM'))
|
||||
->set("POSITION" , $result->getPosition())
|
||||
->set("OBJECT_TYPE" , $object_type)
|
||||
->set("OBJECT_ID" , $object_id)
|
||||
;
|
||||
|
||||
$loopResult->addRow($loopResultRow);
|
||||
}
|
||||
catch (\Exception $ex) {
|
||||
// Ignore the result and log an error
|
||||
Tlog::getInstance()->addError("Failed to process document in document loop: ", $this->args);
|
||||
}
|
||||
}
|
||||
|
||||
return $loopResult;
|
||||
}
|
||||
}
|
||||
@@ -123,8 +123,10 @@ class Image extends BaseI18nLoop
|
||||
$search = $method->invoke(null); // Static !
|
||||
|
||||
// $query->filterByXXX(id)
|
||||
$method = new \ReflectionMethod($queryClass, $filterMethod);
|
||||
$method->invoke($search, $object_id);
|
||||
if (! is_null($object_id)) {
|
||||
$method = new \ReflectionMethod($queryClass, $filterMethod);
|
||||
$method->invoke($search, $object_id);
|
||||
}
|
||||
|
||||
$orders = $this->getOrder();
|
||||
|
||||
@@ -171,11 +173,12 @@ class Image extends BaseI18nLoop
|
||||
if (! is_null($source)) {
|
||||
|
||||
$source_id = $this->getSourceId();
|
||||
$id = $this->getId();
|
||||
|
||||
// echo "source = ".$this->getSource().", id=".$source_id." - ".$this->getArg('source_id')->getValue()."<br />";
|
||||
//echo "source = ".$this->getSource()."source_id=$source_id, id=$id<br />";
|
||||
|
||||
if (is_null($source_id)) {
|
||||
throw new \InvalidArgumentException("'source_id' argument cannot be null if 'source' argument is specified.");
|
||||
if (is_null($source_id) && is_null($id)) {
|
||||
throw new \InvalidArgumentException("If 'source' argument is specified, 'id' or 'source_id' argument should be specified");
|
||||
}
|
||||
|
||||
$search = $this->createSearchQuery($source, $source_id);
|
||||
@@ -259,14 +262,13 @@ class Image extends BaseI18nLoop
|
||||
|
||||
}
|
||||
|
||||
// echo "sql=".$search->toString();
|
||||
//echo "sql=".$search->toString();
|
||||
|
||||
$results = $this->search($search, $pagination);
|
||||
|
||||
$loopResult = new LoopResult($results);
|
||||
|
||||
foreach ($results as $result) {
|
||||
|
||||
// Create image processing event
|
||||
$event = new ImageEvent($this->request);
|
||||
|
||||
@@ -282,7 +284,7 @@ class Image extends BaseI18nLoop
|
||||
// Put source image file path
|
||||
$source_filepath = sprintf("%s%s/%s/%s",
|
||||
THELIA_ROOT,
|
||||
ConfigQuery::read('documents_library_path', 'local/media/images'),
|
||||
ConfigQuery::read('images_library_path', 'local/media/images'),
|
||||
$object_type,
|
||||
$result->getFile()
|
||||
);
|
||||
|
||||
36
core/lib/Thelia/Exception/DocumentException.php
Normal file
36
core/lib/Thelia/Exception/DocumentException.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?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\Exception;
|
||||
|
||||
use Thelia\Log\Tlog;
|
||||
|
||||
class DocumentException extends \RuntimeException
|
||||
{
|
||||
public function __construct($message, $code = null, $previous = null)
|
||||
{
|
||||
Tlog::getInstance()->addError($message);
|
||||
|
||||
parent::__construct($message, $code, $previous);
|
||||
}
|
||||
}
|
||||
@@ -39,7 +39,8 @@ class CategoryCreationForm extends BaseForm
|
||||
"for" => "title"
|
||||
)
|
||||
))
|
||||
->add("parent", "integer", array(
|
||||
->add("parent", "text", array(
|
||||
"label" => Translator::getInstance()->trans("Parent category *"),
|
||||
"constraints" => array(
|
||||
new NotBlank()
|
||||
)
|
||||
@@ -49,6 +50,9 @@ class CategoryCreationForm extends BaseForm
|
||||
new NotBlank()
|
||||
)
|
||||
))
|
||||
->add("visible", "integer", array(
|
||||
"label" => Translator::getInstance()->trans("This category is online on the front office.")
|
||||
))
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ namespace Thelia\Form;
|
||||
|
||||
use Symfony\Component\Validator\Constraints\GreaterThan;
|
||||
use Thelia\Core\Translation\Translator;
|
||||
use Symfony\Component\Validator\Constraints\NotBlank;
|
||||
|
||||
class CategoryModificationForm extends CategoryCreationForm
|
||||
{
|
||||
@@ -36,12 +37,13 @@ class CategoryModificationForm extends CategoryCreationForm
|
||||
$this->formBuilder
|
||||
->add("id", "hidden", array("constraints" => array(new GreaterThan(array('value' => 0)))))
|
||||
|
||||
->add("visible", "checkbox", array(
|
||||
"label" => Translator::getInstance()->trans("This category is online on the front office.")
|
||||
->add("url", "text", array(
|
||||
"label" => Translator::getInstance()->trans("Rewriten URL *"),
|
||||
"constraints" => array(new NotBlank())
|
||||
))
|
||||
;
|
||||
|
||||
// Add standard description fields
|
||||
// Add standard description fields, excluding title and locale, which a re defined in parent class
|
||||
$this->addStandardDescFields(array('title', 'locale'));
|
||||
}
|
||||
|
||||
|
||||
91
core/lib/Thelia/Mailer/MailerFactory.php
Normal file
91
core/lib/Thelia/Mailer/MailerFactory.php
Normal file
@@ -0,0 +1,91 @@
|
||||
<?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\Mailer;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Thelia\Core\Event\MailTransporterEvent;
|
||||
use Thelia\Core\Event\TheliaEvents;
|
||||
use Thelia\Model\ConfigQuery;
|
||||
|
||||
|
||||
/**
|
||||
* Class MailerFactory
|
||||
* @package Thelia\Mailer
|
||||
* @author Manuel Raynaud <mraynaud@openstudio.fr>
|
||||
*/
|
||||
class MailerFactory {
|
||||
/**
|
||||
* @var \Swift_Mailer
|
||||
*/
|
||||
protected $swiftMailer;
|
||||
|
||||
protected $dispatcher;
|
||||
|
||||
public function __construct(EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
|
||||
$this->dispatcher = $dispatcher;
|
||||
|
||||
$transporterEvent = new MailTransporterEvent();
|
||||
$this->dispatcher->dispatch(TheliaEvents::MAILTRANSPORTER_CONFIG, $transporterEvent);
|
||||
|
||||
if($transporterEvent->hasTransporter()) {
|
||||
$transporter = $transporterEvent->getTransporter();
|
||||
} else {
|
||||
if (ConfigQuery::read("smtp.enabled")) {
|
||||
$transporter = $this->configureSmtp();
|
||||
} else {
|
||||
$transporter = \Swift_MailTransport::newInstance();
|
||||
}
|
||||
}
|
||||
|
||||
$this->swiftMailer = new \Swift_Mailer($transporter);
|
||||
}
|
||||
|
||||
private function configureSmtp()
|
||||
{
|
||||
$smtpTransporter = new \Swift_SmtpTransport();
|
||||
$smtpTransporter->setHost(Configquery::read('smtp.host', 'localhost'))
|
||||
->setPort(ConfigQuery::read('smtp.host'))
|
||||
->setEncryption(ConfigQuery::read('smtp.encryption'))
|
||||
->setUsername(ConfigQuery::read('smtp.username'))
|
||||
->setPassword(ConfigQuery::read('smtp.password'))
|
||||
->setAuthMode(ConfigQuery::read('smtp.authmode'))
|
||||
->setTimeout(ConfigQuery::read('smtp.timeout', 30))
|
||||
->setSourceIp(ConfigQuery::read('smtp.sourceip'))
|
||||
;
|
||||
return $smtpTransporter;
|
||||
}
|
||||
|
||||
public function send(\Swift_Mime_Message $message, &$failedRecipients = null)
|
||||
{
|
||||
$this->swiftMailer->send($message, $failedRecipients);
|
||||
}
|
||||
|
||||
public function getSwiftMailer()
|
||||
{
|
||||
return $this->swiftMailer;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -15,6 +15,8 @@ class Category extends BaseCategory
|
||||
|
||||
use \Thelia\Model\Tools\PositionManagementTrait;
|
||||
|
||||
use \Thelia\Model\Tools\UrlRewritingTrait;
|
||||
|
||||
/**
|
||||
* @return int number of child for the current category
|
||||
*/
|
||||
@@ -23,30 +25,12 @@ class Category extends BaseCategory
|
||||
return CategoryQuery::countChild($this->getId());
|
||||
}
|
||||
|
||||
public function getUrl($locale)
|
||||
{
|
||||
return URL::getInstance()->retrieve('category', $this->getId(), $locale)->toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new category.
|
||||
*
|
||||
* @param string $title the category title
|
||||
* @param int $parent the ID of the parent category
|
||||
* @param string $locale the locale of the title
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function create($title, $parent, $locale)
|
||||
{
|
||||
$this
|
||||
->setLocale($locale)
|
||||
->setTitle($title)
|
||||
->setParent($parent)
|
||||
->setVisible(1)
|
||||
->setPosition($this->getNextPosition($parent))
|
||||
;
|
||||
|
||||
$this->save();
|
||||
}
|
||||
protected function getRewritenUrlViewName() {
|
||||
return 'category';
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -71,18 +55,38 @@ class Category extends BaseCategory
|
||||
return $countProduct;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate next position relative to our parent
|
||||
*/
|
||||
protected function addCriteriaToPositionQuery($query) {
|
||||
$query->filterByParent($this->getParent());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function preInsert(ConnectionInterface $con = null)
|
||||
{
|
||||
$this->setPosition($this->getNextPosition());
|
||||
|
||||
$this->generateRewritenUrl($this->getLocale());
|
||||
|
||||
$this->dispatchEvent(TheliaEvents::BEFORE_CREATECATEGORY, new CategoryEvent($this));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function postInsert(ConnectionInterface $con = null)
|
||||
{
|
||||
$this->dispatchEvent(TheliaEvents::AFTER_CREATECATEGORY, new CategoryEvent($this));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function preUpdate(ConnectionInterface $con = null)
|
||||
{
|
||||
$this->dispatchEvent(TheliaEvents::BEFORE_UPDATECATEGORY, new CategoryEvent($this));
|
||||
@@ -90,16 +94,27 @@ class Category extends BaseCategory
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function postUpdate(ConnectionInterface $con = null)
|
||||
{
|
||||
$this->dispatchEvent(TheliaEvents::AFTER_UPDATECATEGORY, new CategoryEvent($this));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function preDelete(ConnectionInterface $con = null)
|
||||
{
|
||||
$this->dispatchEvent(TheliaEvents::BEFORE_DELETECATEGORY, new CategoryEvent($this));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function postDelete(ConnectionInterface $con = null)
|
||||
{
|
||||
$this->dispatchEvent(TheliaEvents::AFTER_DELETECATEGORY, new CategoryEvent($this));
|
||||
|
||||
@@ -3,8 +3,26 @@
|
||||
namespace Thelia\Model;
|
||||
|
||||
use Thelia\Model\Base\CategoryDocument as BaseCategoryDocument;
|
||||
use Propel\Runtime\Connection\ConnectionInterface;
|
||||
|
||||
class CategoryDocument extends BaseCategoryDocument
|
||||
class CategoryDocument extends BaseCategoryDocument
|
||||
{
|
||||
use \Thelia\Model\Tools\PositionManagementTrait;
|
||||
|
||||
/**
|
||||
* Calculate next position relative to our parent
|
||||
*/
|
||||
protected function addCriteriaToPositionQuery($query) {
|
||||
$query->filterByCategory($this->getCategory());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function preInsert(ConnectionInterface $con = null)
|
||||
{
|
||||
$this->setPosition($this->getNextPosition());
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,8 +3,26 @@
|
||||
namespace Thelia\Model;
|
||||
|
||||
use Thelia\Model\Base\CategoryImage as BaseCategoryImage;
|
||||
use Propel\Runtime\Connection\ConnectionInterface;
|
||||
|
||||
class CategoryImage extends BaseCategoryImage
|
||||
class CategoryImage extends BaseCategoryImage
|
||||
{
|
||||
use \Thelia\Model\Tools\PositionManagementTrait;
|
||||
|
||||
/**
|
||||
* Calculate next position relative to our parent
|
||||
*/
|
||||
protected function addCriteriaToPositionQuery($query) {
|
||||
$query->filterByCategory($this->getCategory());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function preInsert(ConnectionInterface $con = null)
|
||||
{
|
||||
$this->setPosition($this->getNextPosition());
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,11 +4,41 @@ namespace Thelia\Model;
|
||||
|
||||
use Thelia\Model\Base\Content as BaseContent;
|
||||
use Thelia\Tools\URL;
|
||||
use Propel\Runtime\Connection\ConnectionInterface;
|
||||
|
||||
class Content extends BaseContent
|
||||
{
|
||||
public function getUrl($locale)
|
||||
use \Thelia\Model\Tools\ModelEventDispatcherTrait;
|
||||
|
||||
use \Thelia\Model\Tools\PositionManagementTrait;
|
||||
|
||||
use \Thelia\Model\Tools\UrlRewritingTrait;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected function getRewritenUrlViewName() {
|
||||
return 'content';
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate next position relative to our parent
|
||||
*/
|
||||
protected function addCriteriaToPositionQuery($query) {
|
||||
|
||||
// TODO: Find the default folder for this content,
|
||||
// and generate the position relative to this folder
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function preInsert(ConnectionInterface $con = null)
|
||||
{
|
||||
return URL::getInstance()->retrieve('content', $this->getId(), $locale)->toString();
|
||||
$this->setPosition($this->getNextPosition());
|
||||
|
||||
$this->generateRewritenUrl($this->getLocale());
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,8 +3,26 @@
|
||||
namespace Thelia\Model;
|
||||
|
||||
use Thelia\Model\Base\ContentDocument as BaseContentDocument;
|
||||
use Propel\Runtime\Connection\ConnectionInterface;
|
||||
|
||||
class ContentDocument extends BaseContentDocument
|
||||
class ContentDocument extends BaseContentDocument
|
||||
{
|
||||
use \Thelia\Model\Tools\PositionManagementTrait;
|
||||
|
||||
/**
|
||||
* Calculate next position relative to our parent
|
||||
*/
|
||||
protected function addCriteriaToPositionQuery($query) {
|
||||
$query->filterByContent($this->getContent());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function preInsert(ConnectionInterface $con = null)
|
||||
{
|
||||
$this->setPosition($this->getNextPosition());
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,8 +3,26 @@
|
||||
namespace Thelia\Model;
|
||||
|
||||
use Thelia\Model\Base\ContentImage as BaseContentImage;
|
||||
use Propel\Runtime\Connection\ConnectionInterface;
|
||||
|
||||
class ContentImage extends BaseContentImage
|
||||
class ContentImage extends BaseContentImage
|
||||
{
|
||||
use \Thelia\Model\Tools\PositionManagementTrait;
|
||||
|
||||
}
|
||||
/**
|
||||
* Calculate next position relative to our parent
|
||||
*/
|
||||
protected function addCriteriaToPositionQuery($query) {
|
||||
$query->filterByContent($this->getContent());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function preInsert(ConnectionInterface $con = null)
|
||||
{
|
||||
$this->setPosition($this->getNextPosition());
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -4,9 +4,23 @@ namespace Thelia\Model;
|
||||
|
||||
use Thelia\Model\Base\Folder as BaseFolder;
|
||||
use Thelia\Tools\URL;
|
||||
use Propel\Runtime\Connection\ConnectionInterface;
|
||||
|
||||
class Folder extends BaseFolder
|
||||
{
|
||||
use \Thelia\Model\Tools\ModelEventDispatcherTrait;
|
||||
|
||||
use \Thelia\Model\Tools\PositionManagementTrait;
|
||||
|
||||
use \Thelia\Model\Tools\UrlRewritingTrait;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected function getRewritenUrlViewName() {
|
||||
return 'folder';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int number of contents for the folder
|
||||
*/
|
||||
@@ -15,11 +29,6 @@ class Folder extends BaseFolder
|
||||
return FolderQuery::countChild($this->getId());
|
||||
}
|
||||
|
||||
public function getUrl($locale)
|
||||
{
|
||||
return URL::getInstance()->retrieve('folder', $this->getId(), $locale)->toString();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* count all products for current category and sub categories
|
||||
@@ -43,4 +52,23 @@ class Folder extends BaseFolder
|
||||
return $contentsCount;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate next position relative to our parent
|
||||
*/
|
||||
protected function addCriteriaToPositionQuery($query) {
|
||||
$query->filterByParent($this->getParent());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function preInsert(ConnectionInterface $con = null)
|
||||
{
|
||||
$this->setPosition($this->getNextPosition());
|
||||
|
||||
$this->generateRewritenUrl($this->getLocale());
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,8 +3,26 @@
|
||||
namespace Thelia\Model;
|
||||
|
||||
use Thelia\Model\Base\FolderDocument as BaseFolderDocument;
|
||||
use Propel\Runtime\Connection\ConnectionInterface;
|
||||
|
||||
class FolderDocument extends BaseFolderDocument
|
||||
class FolderDocument extends BaseFolderDocument
|
||||
{
|
||||
use \Thelia\Model\Tools\PositionManagementTrait;
|
||||
|
||||
/**
|
||||
* Calculate next position relative to our parent
|
||||
*/
|
||||
protected function addCriteriaToPositionQuery($query) {
|
||||
$query->filterByFolder($this->getFolder());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function preInsert(ConnectionInterface $con = null)
|
||||
{
|
||||
$this->setPosition($this->getNextPosition());
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,8 +3,26 @@
|
||||
namespace Thelia\Model;
|
||||
|
||||
use Thelia\Model\Base\FolderImage as BaseFolderImage;
|
||||
use Propel\Runtime\Connection\ConnectionInterface;
|
||||
|
||||
class FolderImage extends BaseFolderImage
|
||||
class FolderImage extends BaseFolderImage
|
||||
{
|
||||
use \Thelia\Model\Tools\PositionManagementTrait;
|
||||
|
||||
/**
|
||||
* Calculate next position relative to our parent
|
||||
*/
|
||||
protected function addCriteriaToPositionQuery($query) {
|
||||
$query->filterByFolder($this->getFolder());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function preInsert(ConnectionInterface $con = null)
|
||||
{
|
||||
$this->setPosition($this->getNextPosition());
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,19 +6,29 @@ use Propel\Runtime\Exception\PropelException;
|
||||
use Thelia\Model\Base\Product as BaseProduct;
|
||||
use Thelia\Tools\URL;
|
||||
use Thelia\TaxEngine\Calculator;
|
||||
use Propel\Runtime\Connection\ConnectionInterface;
|
||||
|
||||
class Product extends BaseProduct
|
||||
{
|
||||
public function getUrl($locale)
|
||||
{
|
||||
return URL::getInstance()->retrieve('product', $this->getId(), $locale)->toString();
|
||||
use \Thelia\Model\Tools\ModelEventDispatcherTrait;
|
||||
|
||||
use \Thelia\Model\Tools\PositionManagementTrait;
|
||||
|
||||
use \Thelia\Model\Tools\UrlRewritingTrait;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected function getRewritenUrlViewName() {
|
||||
return 'product';
|
||||
}
|
||||
|
||||
public function getRealLowestPrice($virtualColumnName = 'real_lowest_price')
|
||||
{
|
||||
try {
|
||||
$amount = $this->getVirtualColumn($virtualColumnName);
|
||||
} catch(PropelException $e) {
|
||||
}
|
||||
catch(PropelException $e) {
|
||||
throw new PropelException("Virtual column `$virtualColumnName` does not exist in Product::getRealLowestPrice");
|
||||
}
|
||||
|
||||
@@ -30,4 +40,26 @@ class Product extends BaseProduct
|
||||
$taxCalculator = new Calculator();
|
||||
return round($taxCalculator->load($this, $country)->getTaxedPrice($this->getRealLowestPrice()), 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate next position relative to our default category
|
||||
*/
|
||||
protected function addCriteriaToPositionQuery($query) {
|
||||
|
||||
// TODO: Find the default category for this product,
|
||||
// and generate the position relative to this category
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function preInsert(ConnectionInterface $con = null)
|
||||
{
|
||||
$this->setPosition($this->getNextPosition());
|
||||
|
||||
$this->generateRewritenUrl($this->getLocale());
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,8 +3,27 @@
|
||||
namespace Thelia\Model;
|
||||
|
||||
use Thelia\Model\Base\ProductDocument as BaseProductDocument;
|
||||
use Propel\Runtime\Connection\ConnectionInterface;
|
||||
|
||||
class ProductDocument extends BaseProductDocument
|
||||
class ProductDocument extends BaseProductDocument
|
||||
{
|
||||
use \Thelia\Model\Tools\PositionManagementTrait;
|
||||
|
||||
/**
|
||||
* Calculate next position relative to our parent
|
||||
*/
|
||||
protected function addCriteriaToPositionQuery($query) {
|
||||
$query->filterByProduct($this->getProduct());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function preInsert(ConnectionInterface $con = null)
|
||||
{
|
||||
$this->setPosition($this->getNextPosition());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,8 +3,26 @@
|
||||
namespace Thelia\Model;
|
||||
|
||||
use Thelia\Model\Base\ProductImage as BaseProductImage;
|
||||
use Propel\Runtime\Connection\ConnectionInterface;
|
||||
|
||||
class ProductImage extends BaseProductImage
|
||||
class ProductImage extends BaseProductImage
|
||||
{
|
||||
use \Thelia\Model\Tools\PositionManagementTrait;
|
||||
|
||||
/**
|
||||
* Calculate next position relative to our parent
|
||||
*/
|
||||
protected function addCriteriaToPositionQuery($query) {
|
||||
$query->filterByProduct($this->getProduct());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function preInsert(ConnectionInterface $con = null)
|
||||
{
|
||||
$this->setPosition($this->getNextPosition());
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
78
core/lib/Thelia/Model/Tools/UrlRewritingTrait.php
Normal file
78
core/lib/Thelia/Model/Tools/UrlRewritingTrait.php
Normal file
@@ -0,0 +1,78 @@
|
||||
<?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\Model\Tools;
|
||||
|
||||
use Thelia\Tools\URL;
|
||||
/**
|
||||
* A trait for managing Rewriten URLs from model classes
|
||||
*/
|
||||
trait UrlRewritingTrait {
|
||||
|
||||
/**
|
||||
* @returns string the view name of the rewriten object (e.g., 'category', 'product')
|
||||
*/
|
||||
protected abstract function getRewritenUrlViewName();
|
||||
|
||||
/**
|
||||
* Get the object URL for the given locale, rewriten if rewriting is enabled.
|
||||
*
|
||||
* @param string $locale a valid locale (e.g. en_US)
|
||||
*/
|
||||
public function getUrl($locale)
|
||||
{
|
||||
return URL::getInstance()->retrieve($this->getRewritenUrlViewName(), $this->getId(), $locale)->toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a rewriten URL from the object title, and store it in the rewriting table
|
||||
*
|
||||
* @param string $locale a valid locale (e.g. en_US)
|
||||
*/
|
||||
public function generateRewritenUrl($locale)
|
||||
{
|
||||
URL::getInstance()->generateRewritenUrl($this->getRewritenUrlViewName(), $this->getId(), $locale, $this->getTitle());
|
||||
}
|
||||
|
||||
/**
|
||||
* return the rewriten URL for the given locale
|
||||
*
|
||||
* @param string $locale a valid locale (e.g. en_US)
|
||||
*/
|
||||
public function getRewritenUrl($locale)
|
||||
{
|
||||
return "fake url - TODO";
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the rewriten URL for the given locale
|
||||
*
|
||||
* @param string $locale a valid locale (e.g. en_US)
|
||||
*/
|
||||
public function setRewritenUrl($locale, $url)
|
||||
{
|
||||
// TODO - code me !
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
248
core/lib/Thelia/Tests/Action/DocumentTest.php
Normal file
248
core/lib/Thelia/Tests/Action/DocumentTest.php
Normal file
@@ -0,0 +1,248 @@
|
||||
<?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\Tests\Action\DocumentTest;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage;
|
||||
use Thelia\Core\HttpFoundation\Request;
|
||||
use Thelia\Core\HttpFoundation\Session\Session;
|
||||
|
||||
use Thelia\Action\Document;
|
||||
use Thelia\Core\Event\DocumentEvent;
|
||||
use Thelia\Model\ConfigQuery;
|
||||
|
||||
/**
|
||||
* Class DocumentTest
|
||||
*
|
||||
* @package Thelia\Tests\Action\DocumentTest
|
||||
*/
|
||||
class DocumentTest extends \Thelia\Tests\TestCaseWithURLToolSetup
|
||||
{
|
||||
protected $request;
|
||||
|
||||
protected $session;
|
||||
|
||||
public function getContainer()
|
||||
{
|
||||
$container = new \Symfony\Component\DependencyInjection\ContainerBuilder();
|
||||
|
||||
$dispatcher = $this->getMock("Symfony\Component\EventDispatcher\EventDispatcherInterface");
|
||||
|
||||
$container->set("event_dispatcher", $dispatcher);
|
||||
|
||||
$request = new Request();
|
||||
$request->setSession($this->session);
|
||||
|
||||
$container->set("request", $request);
|
||||
|
||||
return $container;
|
||||
}
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$this->session = new Session(new MockArraySessionStorage());
|
||||
$this->request = new Request();
|
||||
|
||||
$this->request->setSession($this->session);
|
||||
|
||||
// mock cache configuration.
|
||||
$config = ConfigQuery::create()->filterByName('document_cache_dir_from_web_root')->findOne();
|
||||
|
||||
if ($config != null) {
|
||||
$this->cache_dir_from_web_root = $config->getValue();
|
||||
|
||||
$config->setValue(__DIR__."/assets/documents/cache");
|
||||
|
||||
$config->setValue($this->cache_dir_from_web_root)->save();
|
||||
}
|
||||
}
|
||||
|
||||
public static function setUpBeforeClass()
|
||||
{
|
||||
$dir = THELIA_WEB_DIR."/cache/tests";
|
||||
if ($dh = @opendir($dir)) {
|
||||
while ($file = readdir($dh)) {
|
||||
if ($file == '.' || $file == '..') continue;
|
||||
|
||||
unlink(sprintf("%s/%s", $dir, $file));
|
||||
}
|
||||
|
||||
closedir($dh);
|
||||
}
|
||||
}
|
||||
|
||||
public function tearDown()
|
||||
{
|
||||
// restore cache configuration.
|
||||
$config = ConfigQuery::create()->filterByName('document_cache_dir_from_web_root')->findOne();
|
||||
|
||||
if ($config != null) {
|
||||
$config->setValue($this->cache_dir_from_web_root)->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Documentevent is empty, mandatory parameters not specified.
|
||||
*
|
||||
* @expectedException \InvalidArgumentException
|
||||
*/
|
||||
public function testProcessEmptyDocumentEvent()
|
||||
{
|
||||
$event = new DocumentEvent($this->request);
|
||||
|
||||
$document = new Document($this->getContainer());
|
||||
|
||||
$document->processDocument($event);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Try to process a non-existent file
|
||||
*
|
||||
* @expectedException \InvalidArgumentException
|
||||
*/
|
||||
public function testProcessNonExistentDocument()
|
||||
{
|
||||
$event = new DocumentEvent($this->request);
|
||||
|
||||
$document = new Document($this->getContainer());
|
||||
|
||||
$event->setCacheFilepath("blablabla.txt");
|
||||
$event->setCacheSubdirectory("tests");
|
||||
|
||||
$document->processDocument($event);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Try to process a file outside of the cache
|
||||
*
|
||||
* @expectedException \InvalidArgumentException
|
||||
*/
|
||||
public function testProcessDocumentOutsideValidPath()
|
||||
{
|
||||
$event = new DocumentEvent($this->request);
|
||||
|
||||
$document = new Document($this->getContainer());
|
||||
|
||||
$event->setCacheFilepath("blablabla.pdf");
|
||||
$event->setCacheSubdirectory("../../../");
|
||||
|
||||
$document->processDocument($event);
|
||||
}
|
||||
|
||||
/**
|
||||
* No operation done on source file -> copie !
|
||||
*/
|
||||
public function testProcessDocumentCopy()
|
||||
{
|
||||
$event = new DocumentEvent($this->request);
|
||||
|
||||
$event->setSourceFilepath(__DIR__."/assets/documents/sources/test-document-1.txt");
|
||||
$event->setCacheSubdirectory("tests");
|
||||
|
||||
$document = new Document($this->getContainer());
|
||||
|
||||
// mock cache configuration.
|
||||
$config = ConfigQuery::create()->filterByName('original_document_delivery_mode')->findOne();
|
||||
|
||||
if ($config != null) {
|
||||
$oldval = $config->getValue();
|
||||
$config->setValue('copy')->save();
|
||||
}
|
||||
|
||||
$document->processDocument($event);
|
||||
|
||||
if ($config != null) $config->setValue($oldval)->save();
|
||||
|
||||
$imgdir = ConfigQuery::read('document_cache_dir_from_web_root');
|
||||
|
||||
$this->assertFileExists(THELIA_WEB_DIR."/$imgdir/tests/test-document-1.txt");
|
||||
}
|
||||
|
||||
/**
|
||||
* No operation done on source file -> link !
|
||||
*/
|
||||
public function testProcessDocumentSymlink()
|
||||
{
|
||||
$event = new DocumentEvent($this->request);
|
||||
|
||||
$event->setSourceFilepath(__DIR__."/assets/documents/sources/test-document-2.txt");
|
||||
$event->setCacheSubdirectory("tests");
|
||||
|
||||
$document = new Document($this->getContainer());
|
||||
|
||||
// mock cache configuration.
|
||||
$config = ConfigQuery::create()->filterByName('original_document_delivery_mode')->findOne();
|
||||
|
||||
if ($config != null) {
|
||||
$oldval = $config->getValue();
|
||||
$config->setValue('symlink')->save();
|
||||
}
|
||||
|
||||
$document->processDocument($event);
|
||||
|
||||
if ($config != null) $config->setValue($oldval)->save();
|
||||
|
||||
$imgdir = ConfigQuery::read('document_cache_dir_from_web_root');
|
||||
|
||||
$this->assertFileExists(THELIA_WEB_DIR."/$imgdir/tests/test-document-2.txt");
|
||||
}
|
||||
|
||||
public function testClearTestsCache()
|
||||
{
|
||||
$event = new DocumentEvent($this->request);
|
||||
|
||||
$event->setCacheSubdirectory('tests');
|
||||
|
||||
$document = new Document($this->getContainer());
|
||||
|
||||
$document->clearCache($event);
|
||||
}
|
||||
|
||||
public function testClearWholeCache()
|
||||
{
|
||||
$event = new DocumentEvent($this->request);
|
||||
|
||||
$document = new Document($this->getContainer());
|
||||
|
||||
$document->clearCache($event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to clear directory ouside of the cache
|
||||
*
|
||||
* @expectedException \InvalidArgumentException
|
||||
*/
|
||||
public function testClearUnallowedPathCache()
|
||||
{
|
||||
$event = new DocumentEvent($this->request);
|
||||
|
||||
$event->setCacheSubdirectory('../../../..');
|
||||
|
||||
$document = new Document($this->getContainer());
|
||||
|
||||
$document->clearCache($event);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
This is a text document.
|
||||
@@ -0,0 +1 @@
|
||||
This is a text document.
|
||||
89
core/lib/Thelia/Tests/Core/Template/Loop/DocumentTest.php
Normal file
89
core/lib/Thelia/Tests/Core/Template/Loop/DocumentTest.php
Normal file
@@ -0,0 +1,89 @@
|
||||
<?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\Tests\Core\Template\Loop;
|
||||
|
||||
use Thelia\Model\DocumentQuery;
|
||||
use Thelia\Tests\Core\Template\Element\BaseLoopTestor;
|
||||
|
||||
use Thelia\Core\Template\Loop\Document;
|
||||
use Thelia\Model\ProductDocumentQuery;
|
||||
use Thelia\Model\CategoryDocumentQuery;
|
||||
use Thelia\Model\ContentDocumentQuery;
|
||||
use Thelia\Model\FolderDocumentQuery;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Etienne Roudeix <eroudeix@openstudio.fr>
|
||||
*
|
||||
*/
|
||||
class DocumentTest extends BaseLoopTestor
|
||||
{
|
||||
public function getTestedClassName()
|
||||
{
|
||||
return 'Thelia\Core\Template\Loop\Document';
|
||||
}
|
||||
|
||||
public function getTestedInstance()
|
||||
{
|
||||
return new Document($this->container);
|
||||
}
|
||||
|
||||
public function getMandatoryArguments()
|
||||
{
|
||||
return array('source' => 'product', 'id' => 1);
|
||||
}
|
||||
|
||||
public function testSearchByProductId()
|
||||
{
|
||||
$document = ProductDocumentQuery::create()->findOne();
|
||||
|
||||
$this->baseTestSearchById($document->getId());
|
||||
}
|
||||
|
||||
public function testSearchByFolderId()
|
||||
{
|
||||
$document = FolderDocumentQuery::create()->findOne();
|
||||
|
||||
$this->baseTestSearchById($document->getId());
|
||||
}
|
||||
|
||||
public function testSearchByContentId()
|
||||
{
|
||||
$document = ContentDocumentQuery::create()->findOne();
|
||||
|
||||
$this->baseTestSearchById($document->getId());
|
||||
}
|
||||
|
||||
public function testSearchByCategoryId()
|
||||
{
|
||||
$document = CategoryDocumentQuery::create()->findOne();
|
||||
|
||||
$this->baseTestSearchById($document->getId());
|
||||
}
|
||||
|
||||
public function testSearchLimit()
|
||||
{
|
||||
$this->baseTestSearchWithLimit(1);
|
||||
}
|
||||
}
|
||||
89
core/lib/Thelia/Tests/Core/Template/Loop/ImageTest.php
Normal file
89
core/lib/Thelia/Tests/Core/Template/Loop/ImageTest.php
Normal file
@@ -0,0 +1,89 @@
|
||||
<?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\Tests\Core\Template\Loop;
|
||||
|
||||
use Thelia\Model\ImageQuery;
|
||||
use Thelia\Tests\Core\Template\Element\BaseLoopTestor;
|
||||
|
||||
use Thelia\Core\Template\Loop\Image;
|
||||
use Thelia\Model\ProductImageQuery;
|
||||
use Thelia\Model\CategoryImageQuery;
|
||||
use Thelia\Model\ContentImageQuery;
|
||||
use Thelia\Model\FolderImageQuery;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Etienne Roudeix <eroudeix@openstudio.fr>
|
||||
*
|
||||
*/
|
||||
class ImageTest extends BaseLoopTestor
|
||||
{
|
||||
public function getTestedClassName()
|
||||
{
|
||||
return 'Thelia\Core\Template\Loop\Image';
|
||||
}
|
||||
|
||||
public function getTestedInstance()
|
||||
{
|
||||
return new Image($this->container);
|
||||
}
|
||||
|
||||
public function getMandatoryArguments()
|
||||
{
|
||||
return array('source' => 'product', 'id' => 1);
|
||||
}
|
||||
|
||||
public function testSearchByProductId()
|
||||
{
|
||||
$image = ProductImageQuery::create()->findOne();
|
||||
|
||||
$this->baseTestSearchById($image->getId());
|
||||
}
|
||||
|
||||
public function testSearchByFolderId()
|
||||
{
|
||||
$image = FolderImageQuery::create()->findOne();
|
||||
|
||||
$this->baseTestSearchById($image->getId());
|
||||
}
|
||||
|
||||
public function testSearchByContentId()
|
||||
{
|
||||
$image = ContentImageQuery::create()->findOne();
|
||||
|
||||
$this->baseTestSearchById($image->getId());
|
||||
}
|
||||
|
||||
public function testSearchByCategoryId()
|
||||
{
|
||||
$image = CategoryImageQuery::create()->findOne();
|
||||
|
||||
$this->baseTestSearchById($image->getId());
|
||||
}
|
||||
|
||||
public function testSearchLimit()
|
||||
{
|
||||
$this->baseTestSearchWithLimit(1);
|
||||
}
|
||||
}
|
||||
@@ -43,14 +43,15 @@ class URL
|
||||
|
||||
protected static $instance = null;
|
||||
|
||||
public function __construct(ContainerInterface $container)
|
||||
public function __construct(ContainerInterface $container = null)
|
||||
{
|
||||
// Allow singleton style calls once intanciated.
|
||||
// For this to work, the URL service has to be instanciated very early. This is done manually
|
||||
// in TheliaHttpKernel, by calling $this->container->get('thelia.url.manager');
|
||||
self::$instance = $this;
|
||||
|
||||
$this->requestContext = $container->get('router.admin')->getContext();
|
||||
if ($container !== null)
|
||||
$this->requestContext = $container->get('router.admin')->getContext();
|
||||
|
||||
$this->retriever = new RewritingRetriever();
|
||||
$this->resolver = new RewritingResolver();
|
||||
@@ -183,6 +184,7 @@ class URL
|
||||
|
||||
return $this->absoluteUrl($path, $parameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a rewritten URL from a view, a view id and a locale
|
||||
*
|
||||
@@ -261,4 +263,50 @@ class URL
|
||||
|
||||
return $this->resolver;
|
||||
}
|
||||
|
||||
protected function sanitize($string, $force_lowercase = true, $alphabetic_only = false)
|
||||
{
|
||||
static $strip = array("~", "`", "!", "@", "#", "$", "%", "^", "&", "*", "(", ")", "_", "=", "+", "[", "{", "]",
|
||||
"}", "\\", "|", ";", ":", "\"", "'", "‘", "’", "“", "”", "–", "—",
|
||||
"—", "–", ",", "<", ".", ">", "/", "?");
|
||||
|
||||
$clean = trim(str_replace($strip, "", strip_tags($string)));
|
||||
|
||||
$clean = preg_replace('/\s+/', "-", $clean);
|
||||
|
||||
$clean = ($alphabetic_only) ? preg_replace("/[^a-zA-Z0-9]/", "", $clean) : $clean ;
|
||||
|
||||
return ($force_lowercase) ?
|
||||
(function_exists('mb_strtolower')) ?
|
||||
mb_strtolower($clean, 'UTF-8') :
|
||||
strtolower($clean) :
|
||||
$clean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Genenerate the file part of a rewriten URL from a given baseString, using a view, a view id and a locale
|
||||
*
|
||||
* @param $view
|
||||
* @param $viewId
|
||||
* @param $viewLocale
|
||||
* @param $baseString the string to be converted in a valid URL
|
||||
*
|
||||
* @return A valid file part URL.
|
||||
*/
|
||||
public function generateRewritenUrl($view, $viewId, $viewLocale, $baseString)
|
||||
{
|
||||
// Borrowed from http://stackoverflow.com/questions/2668854/sanitizing-strings-to-make-them-url-and-filename-safe
|
||||
|
||||
// Replace all weird characters with dashes
|
||||
$string = preg_replace('/[^\w\-~_\.]+/u', '-', $baseString);
|
||||
|
||||
// Only allow one dash separator at a time (and make string lowercase)
|
||||
$cleanString = mb_strtolower(preg_replace('/--+/u', '-', $string), 'UTF-8');
|
||||
|
||||
$urlFilePart = $cleanString . ".html";
|
||||
|
||||
// TODO :
|
||||
// check if URL url already exists, and add a numeric suffix, or the like
|
||||
// insert the URL in the rewriting table
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,12 +4,7 @@ use Thelia\Constraint\Rule\AvailableForTotalAmountManager;
|
||||
use Thelia\Constraint\Rule\AvailableForXArticlesManager;
|
||||
use Thelia\Constraint\Rule\Operators;
|
||||
use Thelia\Coupon\CouponRuleCollection;
|
||||
use Thelia\Model\ProductImage;
|
||||
use Thelia\Model\CategoryImage;
|
||||
use Thelia\Model\FolderImage;
|
||||
use Thelia\Model\ContentImage;
|
||||
use Imagine\Image\Color;
|
||||
use Imagine\Image\Point;
|
||||
|
||||
|
||||
require __DIR__ . '/../core/bootstrap.php';
|
||||
|
||||
@@ -23,12 +18,17 @@ $con = \Propel\Runtime\Propel::getConnection(
|
||||
);
|
||||
$con->beginTransaction();
|
||||
|
||||
// Intialize URL management
|
||||
$url = new Thelia\Tools\URL();
|
||||
|
||||
$currency = \Thelia\Model\CurrencyQuery::create()->filterByCode('EUR')->findOne();
|
||||
|
||||
try {
|
||||
$stmt = $con->prepare("SET foreign_key_checks = 0");
|
||||
$stmt->execute();
|
||||
|
||||
echo "Clearing tables\n";
|
||||
|
||||
$productAssociatedContent = Thelia\Model\ProductAssociatedContentQuery::create()
|
||||
->find();
|
||||
$productAssociatedContent->delete();
|
||||
@@ -125,9 +125,24 @@ try {
|
||||
->find();
|
||||
$productPrice->delete();
|
||||
|
||||
\Thelia\Model\ProductImageQuery::create()->find()->delete();
|
||||
\Thelia\Model\CategoryImageQuery::create()->find()->delete();
|
||||
\Thelia\Model\FolderImageQuery::create()->find()->delete();
|
||||
\Thelia\Model\ContentImageQuery::create()->find()->delete();
|
||||
|
||||
\Thelia\Model\ProductDocumentQuery::create()->find()->delete();
|
||||
\Thelia\Model\CategoryDocumentQuery::create()->find()->delete();
|
||||
\Thelia\Model\FolderDocumentQuery::create()->find()->delete();
|
||||
\Thelia\Model\ContentDocumentQuery::create()->find()->delete();
|
||||
|
||||
\Thelia\Model\CouponQuery::create()->find()->delete();
|
||||
|
||||
$stmt = $con->prepare("SET foreign_key_checks = 1");
|
||||
|
||||
$stmt->execute();
|
||||
|
||||
echo "Creating customer\n";
|
||||
|
||||
//customer
|
||||
$customer = new Thelia\Model\Customer();
|
||||
$customer->createOrUpdate(
|
||||
@@ -185,6 +200,8 @@ try {
|
||||
}
|
||||
}
|
||||
|
||||
echo "Creating features\n";
|
||||
|
||||
//features and features_av
|
||||
$featureList = array();
|
||||
for($i=0; $i<4; $i++) {
|
||||
@@ -208,6 +225,8 @@ try {
|
||||
}
|
||||
}
|
||||
|
||||
echo "Creating attributes\n";
|
||||
|
||||
//attributes and attributes_av
|
||||
$attributeList = array();
|
||||
for($i=0; $i<4; $i++) {
|
||||
@@ -230,6 +249,8 @@ try {
|
||||
}
|
||||
}
|
||||
|
||||
echo "Creating templates\n";
|
||||
|
||||
$template = new Thelia\Model\Template();
|
||||
setI18n($faker, $template, array("Name" => 20));
|
||||
$template->save();
|
||||
@@ -252,6 +273,8 @@ try {
|
||||
->save();
|
||||
}
|
||||
|
||||
echo "Creating folders and content\n";
|
||||
|
||||
//folders and contents
|
||||
$contentIdList = array();
|
||||
for($i=0; $i<4; $i++) {
|
||||
@@ -263,10 +286,14 @@ try {
|
||||
|
||||
$folder->save();
|
||||
|
||||
$image = new FolderImage();
|
||||
$image = new \Thelia\Model\FolderImage();
|
||||
$image->setFolderId($folder->getId());
|
||||
generate_image($image, 1, 'folder', $folder->getId());
|
||||
|
||||
$document = new \Thelia\Model\FolderDocument();
|
||||
$document->setFolderId($folder->getId());
|
||||
generate_document($document, 1, 'folder', $folder->getId());
|
||||
|
||||
for($j=1; $j<rand(0, 5); $j++) {
|
||||
$subfolder = new Thelia\Model\Folder();
|
||||
$subfolder->setParent($folder->getId());
|
||||
@@ -276,10 +303,14 @@ try {
|
||||
|
||||
$subfolder->save();
|
||||
|
||||
$image = new FolderImage();
|
||||
$image = new \Thelia\Model\FolderImage();
|
||||
$image->setFolderId($subfolder->getId());
|
||||
generate_image($image, 1, 'folder', $subfolder->getId());
|
||||
|
||||
$document = new \Thelia\Model\FolderDocument();
|
||||
$document->setFolderId($folder->getId());
|
||||
generate_document($document, 1, 'folder', $subfolder->getId());
|
||||
|
||||
for($k=0; $k<rand(0, 5); $k++) {
|
||||
$content = new Thelia\Model\Content();
|
||||
$content->addFolder($subfolder);
|
||||
@@ -291,13 +322,20 @@ try {
|
||||
$contentId = $content->getId();
|
||||
$contentIdList[] = $contentId;
|
||||
|
||||
$image = new ContentImage();
|
||||
$image->setContentId($content->getId());
|
||||
$image = new \Thelia\Model\ContentImage();
|
||||
$image->setContentId($contentId);
|
||||
generate_image($image, 1, 'content', $contentId);
|
||||
|
||||
$document = new \Thelia\Model\ContentDocument();
|
||||
$document->setContentId($contentId);
|
||||
generate_document($document, 1, 'content', $contentId);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
echo "Creating categories and products\n";
|
||||
|
||||
//categories and products
|
||||
$productIdList = array();
|
||||
$categoryIdList = array();
|
||||
@@ -405,6 +443,8 @@ try {
|
||||
}
|
||||
}
|
||||
|
||||
echo "Generating coupns fixtures\n";
|
||||
|
||||
generateCouponFixtures($thelia);
|
||||
|
||||
$con->commit();
|
||||
@@ -429,10 +469,14 @@ function createProduct($faker, $category, $position, $template, &$productIdList)
|
||||
$productId = $product->getId();
|
||||
$productIdList[] = $productId;
|
||||
|
||||
$image = new ProductImage();
|
||||
$image = new \Thelia\Model\ProductImage();
|
||||
$image->setProductId($productId);
|
||||
generate_image($image, 1, 'product', $productId);
|
||||
|
||||
$document = new \Thelia\Model\ProductDocument();
|
||||
$document->setProductId($productId);
|
||||
generate_document($document, 1, 'product', $productId);
|
||||
|
||||
return $product;
|
||||
}
|
||||
|
||||
@@ -464,10 +508,14 @@ function createCategory($faker, $parent, $position, &$categoryIdList, $contentId
|
||||
->save();
|
||||
}
|
||||
|
||||
$image = new CategoryImage();
|
||||
$image = new \Thelia\Model\CategoryImage();
|
||||
$image->setCategoryId($categoryId);
|
||||
generate_image($image, 1, 'category', $categoryId);
|
||||
|
||||
$document = new \Thelia\Model\CategoryDocument();
|
||||
$document->setCategoryId($categoryId);
|
||||
generate_document($document, 1, 'category', $categoryId);
|
||||
|
||||
return $category;
|
||||
}
|
||||
|
||||
@@ -480,37 +528,36 @@ function generate_image($image, $position, $typeobj, $id) {
|
||||
->setDescription($faker->text(250))
|
||||
->setChapo($faker->text(40))
|
||||
->setPostscriptum($faker->text(40))
|
||||
->setPosition($position)
|
||||
->setFile(sprintf("sample-image-%s.png", $id))
|
||||
->save()
|
||||
;
|
||||
|
||||
// Generate images
|
||||
$imagine = new Imagine\Gd\Imagine();
|
||||
$image = $imagine->create(new Imagine\Image\Box(320,240), new Color('#E9730F'));
|
||||
$image = $imagine->create(new Imagine\Image\Box(320,240), new Imagine\Image\Color('#E9730F'));
|
||||
|
||||
$white = new Color('#FFF');
|
||||
$white = new Imagine\Image\Color('#FFF');
|
||||
|
||||
$font = $imagine->font(__DIR__.'/faker-assets/FreeSans.ttf', 14, $white);
|
||||
|
||||
$tbox = $font->box("THELIA");
|
||||
$image->draw()->text("THELIA", $font, new Point((320 - $tbox->getWidth()) / 2, 30));
|
||||
$image->draw()->text("THELIA", $font, new Imagine\Image\Point((320 - $tbox->getWidth()) / 2, 30));
|
||||
|
||||
$str = sprintf("%s sample image", ucfirst($typeobj));
|
||||
$tbox = $font->box($str);
|
||||
$image->draw()->text($str, $font, new Point((320 - $tbox->getWidth()) / 2, 80));
|
||||
$image->draw()->text($str, $font, new Imagine\Image\Point((320 - $tbox->getWidth()) / 2, 80));
|
||||
|
||||
$font = $imagine->font(__DIR__.'/faker-assets/FreeSans.ttf', 18, $white);
|
||||
|
||||
$str = sprintf("%s ID %d", strtoupper($typeobj), $id);
|
||||
$tbox = $font->box($str);
|
||||
$image->draw()->text($str, $font, new Point((320 - $tbox->getWidth()) / 2, 180));
|
||||
$image->draw()->text($str, $font, new Imagine\Image\Point((320 - $tbox->getWidth()) / 2, 180));
|
||||
|
||||
$image->draw()
|
||||
->line(new Point(0, 0), new Point(319, 0), $white)
|
||||
->line(new Point(319, 0), new Point(319, 239), $white)
|
||||
->line(new Point(319, 239), new Point(0,239), $white)
|
||||
->line(new Point(0, 239), new Point(0, 0), $white)
|
||||
->line(new Imagine\Image\Point(0, 0), new Imagine\Image\Point(319, 0), $white)
|
||||
->line(new Imagine\Image\Point(319, 0), new Imagine\Image\Point(319, 239), $white)
|
||||
->line(new Imagine\Image\Point(319, 239), new Imagine\Image\Point(0,239), $white)
|
||||
->line(new Imagine\Image\Point(0, 239), new Imagine\Image\Point(0, 0), $white)
|
||||
;
|
||||
|
||||
$image_file = sprintf("%s/../local/media/images/%s/sample-image-%s.png", __DIR__, $typeobj, $id);
|
||||
@@ -520,6 +567,26 @@ function generate_image($image, $position, $typeobj, $id) {
|
||||
$image->save($image_file);
|
||||
}
|
||||
|
||||
function generate_document($document, $position, $typeobj, $id) {
|
||||
|
||||
global $faker;
|
||||
|
||||
$document
|
||||
->setTitle($faker->text(20))
|
||||
->setDescription($faker->text(250))
|
||||
->setChapo($faker->text(40))
|
||||
->setPostscriptum($faker->text(40))
|
||||
->setFile(sprintf("sample-document-%s.txt", $id))
|
||||
->save()
|
||||
;
|
||||
|
||||
$document_file = sprintf("%s/../local/media/documents/%s/sample-document-%s.txt", __DIR__, $typeobj, $id);
|
||||
|
||||
if (! is_dir(dirname($document_file))) mkdir(dirname($document_file), 0777, true);
|
||||
|
||||
file_put_contents($document_file, $faker->text(256));
|
||||
}
|
||||
|
||||
function setI18n($faker, &$object, $fields = array('Title' => 20, 'Description' => 50) )
|
||||
{
|
||||
$localeList = $localeList = array('fr_FR', 'en_US', 'es_ES', 'it_IT');
|
||||
|
||||
@@ -13,8 +13,11 @@ INSERT INTO `config` (`name`, `value`, `secured`, `hidden`, `created_at`, `updat
|
||||
('imagine_graphic_driver', 'gd', 0, 0, NOW(), NOW()),
|
||||
('default_images_quality_percent', '75', 0, 0, NOW(), NOW()),
|
||||
('original_image_delivery_mode', 'symlink', 0, 0, NOW(), NOW()),
|
||||
('original_document_delivery_mode', 'symlink', 0, 0, NOW(), NOW()),
|
||||
('images_library_path', 'local/media/images', 0, 0, NOW(), NOW()),
|
||||
('documents_library_path', 'local/media/documents', 0, 0, NOW(), NOW()),
|
||||
('image_cache_dir_from_web_root', 'cache/images', 0, 0, NOW(), NOW()),
|
||||
('document_cache_dir_from_web_root', 'cache/documents', 0, 0, NOW(), NOW()),
|
||||
('currency_rate_update_url', 'http://www.ecb.int/stats/eurofxref/eurofxref-daily.xml', 0, 0, NOW(), NOW()),
|
||||
('page_not_found_view', '404.html', 0, 0, NOW(), NOW()),
|
||||
('use_tax_free_amounts', 0, 0, 0, NOW(), NOW()),
|
||||
|
||||
@@ -8,7 +8,7 @@ echo -e "\n\e[01;34m[INFO] Clearing caches\e[00m\n"
|
||||
php Thelia cache:clear
|
||||
|
||||
echo -e "\n\e[01;34m[INFO] Downloading vendors\e[00m\n"
|
||||
composer install --prefer-dist
|
||||
composer install --prefer-dist --optimize-autoloader
|
||||
|
||||
cd local/config/
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
|
||||
.topbar {
|
||||
|
||||
background: url("@{imgDir}/top.jpg") repeat-x;
|
||||
background: url("@{imgDir}/top.jpg") repeat-x;
|
||||
font-weight: bold;
|
||||
|
||||
.version-info {
|
||||
@@ -202,7 +202,7 @@
|
||||
border-bottom: 2px solid #A5CED8;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
|
||||
// The action bar on the right
|
||||
.actions {
|
||||
text-align: right;
|
||||
@@ -217,6 +217,10 @@
|
||||
text-transform: none;
|
||||
}
|
||||
}
|
||||
|
||||
.inner-actions {
|
||||
margin-top: 0.5em;
|
||||
}
|
||||
}
|
||||
|
||||
// The overall form container
|
||||
|
||||
@@ -1,396 +1,260 @@
|
||||
{extends file="admin-layout.tpl"}
|
||||
|
||||
{block name="page-title"}{intl l='Catalog'}{/block}
|
||||
{block name="page-title"}{intl l='Categories'}{/block}
|
||||
|
||||
{block name="check-permissions"}admin.catalog.view{/block}
|
||||
{block name="check-permissions"}admin.categories.view{/block}
|
||||
|
||||
{block name="main-content"}
|
||||
<div class="catalog">
|
||||
<div id="wrapper" class="container">
|
||||
<div class="categories">
|
||||
|
||||
{include file="includes/catalog-breadcrumb.html"}
|
||||
<div id="wrapper" class="container">
|
||||
|
||||
{module_include location='catalog_top'}
|
||||
{include file="includes/catalog-breadcrumb.html"}
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="general-block-decorator">
|
||||
<table class="table table-striped table-condensed" id="category_list">
|
||||
<caption>
|
||||
{* display parent category name, and get current cat ID *}
|
||||
{loop name="category_title" type="category" visible="*" id=$current_category_id}
|
||||
{intl l="Categories in %cat" cat=$TITLE}
|
||||
{$cat_id = $ID}
|
||||
{/loop}
|
||||
{elseloop rel="category_title"}
|
||||
{intl l="Top level categories"}
|
||||
{/elseloop}
|
||||
{module_include location='categories_top'}
|
||||
|
||||
{module_include location='category_list_caption'}
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="general-block-decorator">
|
||||
<table class="table table-striped table-condensed" id="category_list">
|
||||
<caption>
|
||||
{* display parent category name, and get current cat ID *}
|
||||
{loop name="category_title" type="category" visible="*" id=$category_id}
|
||||
{intl l="Categories in %cat" cat=$TITLE}
|
||||
{$cat_id = $ID}
|
||||
{/loop}
|
||||
{elseloop rel="category_title"}
|
||||
{intl l="Top level categories"}
|
||||
{/elseloop}
|
||||
|
||||
{loop type="auth" name="can_create" roles="ADMIN" permissions="admin.categories.create"}
|
||||
<a class="btn btn-default btn-primary action-btn" title="{intl l='Add a new category'}" href="#add_category_dialog" data-toggle="modal">
|
||||
<span class="glyphicon glyphicon-plus-sign"></span>
|
||||
</a>
|
||||
{/loop}
|
||||
</caption>
|
||||
{module_include location='category_list_caption'}
|
||||
|
||||
{ifloop rel="category_list"}
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="object-title">
|
||||
{admin_sortable_header
|
||||
current_order=$category_order
|
||||
order='id'
|
||||
reverse_order='id_reverse'
|
||||
path={url path='/admin/catalog' id_category=$current_category_id}
|
||||
label="{intl l='ID'}"
|
||||
}
|
||||
</th>
|
||||
{loop type="auth" name="can_create" roles="ADMIN" permissions="admin.categories.create"}
|
||||
<a class="btn btn-default btn-primary action-btn" title="{intl l='Add a new category'}" href="#category_creation_dialog" data-toggle="modal">
|
||||
<span class="glyphicon glyphicon-plus-sign"></span>
|
||||
</a>
|
||||
{/loop}
|
||||
</caption>
|
||||
|
||||
<th class="object-image"> </th>
|
||||
{ifloop rel="category_list"}
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="object-title">
|
||||
{admin_sortable_header
|
||||
current_order=$category_order
|
||||
order='id'
|
||||
reverse_order='id_reverse'
|
||||
path={url path='/admin/categories' id_category=$category_id}
|
||||
request_parameter_name='category_order'
|
||||
label="{intl l='ID'}"
|
||||
}
|
||||
</th>
|
||||
|
||||
<th class="object-title">
|
||||
{admin_sortable_header
|
||||
current_order=$category_order
|
||||
order='alpha'
|
||||
reverse_order='alpha_reverse'
|
||||
path={url path='/admin/catalog' id_category=$current_category_id}
|
||||
label="{intl l='Category title'}"
|
||||
}
|
||||
</th>
|
||||
<th class="object-image"> </th>
|
||||
|
||||
{module_include location='category_list_header'}
|
||||
<th class="object-title">
|
||||
{admin_sortable_header
|
||||
current_order=$category_order
|
||||
order='alpha'
|
||||
reverse_order='alpha_reverse'
|
||||
path={url path='/admin/categories' id_category=$category_id}
|
||||
request_parameter_name='category_order'
|
||||
label="{intl l='Category title'}"
|
||||
}
|
||||
</th>
|
||||
|
||||
<th>
|
||||
{admin_sortable_header
|
||||
current_order=$category_order
|
||||
order='visible'
|
||||
reverse_order='visible_reverse'
|
||||
path={url path='/admin/catalog' id_category=$current_category_id}
|
||||
label="{intl l='Online'}"
|
||||
}
|
||||
</th>
|
||||
{module_include location='category_list_header'}
|
||||
|
||||
<th>
|
||||
{admin_sortable_header
|
||||
current_order=$category_order
|
||||
order='manual'
|
||||
reverse_order='manual_reverse'
|
||||
path={url path='/admin/catalog' id_category=$current_category_id}
|
||||
label="{intl l='Position'}"
|
||||
}
|
||||
</th>
|
||||
<th>
|
||||
{admin_sortable_header
|
||||
current_order=$category_order
|
||||
order='visible'
|
||||
reverse_order='visible_reverse'
|
||||
path={url path='/admin/categories' id_category=$category_id}
|
||||
request_parameter_name='category_order'
|
||||
label="{intl l='Online'}"
|
||||
}
|
||||
</th>
|
||||
|
||||
<th class="actions">{intl l='Actions'}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<th>
|
||||
{admin_sortable_header
|
||||
current_order=$category_order
|
||||
order='manual'
|
||||
reverse_order='manual_reverse'
|
||||
path={url path='/admin/categories' id_category=$category_id}
|
||||
request_parameter_name='category_order'
|
||||
label="{intl l='Position'}"
|
||||
}
|
||||
</th>
|
||||
|
||||
<tbody>
|
||||
{loop name="category_list" type="category" visible="*" parent=$current_category_id order=$category_order backend_context="1" lang=$lang_id}
|
||||
<tr>
|
||||
<td>{$ID}</td>
|
||||
<th class="actions">{intl l='Actions'}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<td>
|
||||
{loop type="image" name="cat_image" source="category" source_id="$ID" limit="1" width="50" height="50" resize_mode="crop" backend_context="1"}
|
||||
<a href="{url path='admin/catalog' category_id=$ID}" title="{intl l='Browse this category'}"><img class="img-thumbnail" src="{$IMAGE_URL}" alt="{$TITLE}" /></a>
|
||||
{/loop}
|
||||
</td>
|
||||
<tbody>
|
||||
{loop name="category_list" type="category" visible="*" parent=$category_id order=$category_order backend_context="1" lang=$lang_id}
|
||||
<tr>
|
||||
<td>{$ID}</td>
|
||||
|
||||
<td class="object-title">
|
||||
<a href="{url path='admin/catalog' category_id=$ID}" title="{intl l='Browse this category'}">
|
||||
{$TITLE}
|
||||
</a>
|
||||
</td>
|
||||
|
||||
{module_include location='category_list_row'}
|
||||
|
||||
<td>
|
||||
{loop type="auth" name="can_change" roles="ADMIN" permissions="admin.categories.edit"}
|
||||
<div class="make-switch switch-small" data-on="success" data-off="danger" data-on-label="<i class='glyphicon glyphicon-ok'></i>" data-off-label="<i class='glyphicon glyphicon-remove'></i>">
|
||||
<input type="checkbox" data-id="{$ID}" class="categoryVisibleToggle" {if $VISIBLE == 1}checked="checked"{/if}>
|
||||
</div>
|
||||
{/loop}
|
||||
|
||||
{elseloop rel="can_change"}
|
||||
<div class="make-switch switch-small" data-on="success" data-off="danger" data-on-label="<i class='glyphicon glyphicon-ok'></i>" data-off-label="<i class='glyphicon glyphicon-remove'></i>">
|
||||
<input type="checkbox" class="disabled" disabled="disabled" {if $VISIBLE == 1}checked="checked"{/if}>
|
||||
</div>
|
||||
{/elseloop}
|
||||
</td>
|
||||
|
||||
<td>
|
||||
{admin_position_block
|
||||
permission="admin.categories.edit"
|
||||
path={url path='admin/category/update-position' category_id=$ID}
|
||||
url_parameter="category_id"
|
||||
in_place_edit_class="categoryPositionChange"
|
||||
position=$POSITION
|
||||
id=$ID
|
||||
}
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<div class="btn-group">
|
||||
<a class="btn btn-default btn-xs" title="{intl l='Browse this category'}" href="{url path='admin/category' category_id=$ID}"><i class="glyphicon glyphicon-folder-open"></i></a>
|
||||
|
||||
{loop type="auth" name="can_change" roles="ADMIN" permissions="admin.categories.edit"}
|
||||
<a class="btn btn-default btn-xs" title="{intl l='Edit this category'}" href="{url path='/admin/categories/update' category_id=$ID}"><i class="glyphicon glyphicon-edit"></i></a>
|
||||
{/loop}
|
||||
|
||||
{loop type="auth" name="can_delete" roles="ADMIN" permissions="admin.categories.delete"}
|
||||
<a class="btn btn-default btn-xs category-delete" title="{intl l='Delete this category and all its contents'}" href="#delete_category_dialog" data-id="{$ID}" data-toggle="modal"><i class="glyphicon glyphicon-trash"></i></a>
|
||||
{/loop}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{/loop}
|
||||
</tbody>
|
||||
{/ifloop}
|
||||
|
||||
{elseloop rel="category_list"}
|
||||
<thead>
|
||||
<tr>
|
||||
<td class="message">
|
||||
<div class="alert alert-info">
|
||||
{loop type="auth" name="can_create" roles="ADMIN" permissions="admin.categories.create"}
|
||||
{intl l="This category has no sub-categories. To create a new one, click the + button above."}
|
||||
{/loop}
|
||||
|
||||
{elseloop rel="can_create"}
|
||||
{intl l="This category has no sub-categories."}
|
||||
{/elseloop}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</thead>
|
||||
{/elseloop}
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="general-block-decorator">
|
||||
<table class="table table-striped table-condensed">
|
||||
<caption>
|
||||
{* display parent category name *}
|
||||
{loop name="category_title" type="category" visible="*" id=$current_category_id}
|
||||
{intl l="Products in %cat" cat=$TITLE}
|
||||
{/loop}
|
||||
|
||||
{elseloop rel="category_title"}
|
||||
{intl l="Top level Products"}
|
||||
{/elseloop}
|
||||
|
||||
{module_include location='product_list_caption'}
|
||||
|
||||
<a class="btn btn-default btn-primary action-btn" title="{intl l='Add a new product'}" href="#productAddModal" data-toggle="modal">
|
||||
<span class="glyphicon glyphicon-plus-sign"></span>
|
||||
</a>
|
||||
</caption>
|
||||
|
||||
{ifloop rel="product_list"}
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="object-title">
|
||||
{admin_sortable_header
|
||||
current_order=$product_order
|
||||
order='id'
|
||||
reverse_order='id_reverse'
|
||||
path={url path='/admin/product' category_id=$current_category_id}
|
||||
label="{intl l='ID'}"
|
||||
}
|
||||
|
||||
<th> </th>
|
||||
|
||||
<th class="object-title">
|
||||
{admin_sortable_header
|
||||
current_order=$product_order
|
||||
order='ref'
|
||||
reverse_order='ref_reverse'
|
||||
path={url path='/admin/product' category_id=$current_category_id}
|
||||
label="{intl l='Reference'}"
|
||||
}
|
||||
</th>
|
||||
|
||||
<th class="object-title">
|
||||
{admin_sortable_header
|
||||
current_order=$product_order
|
||||
order='alpha'
|
||||
reverse_order='alpha_reverse'
|
||||
path={url path='/admin/product' category_id=$current_category_id}
|
||||
label="{intl l='Product title'}"
|
||||
}
|
||||
|
||||
{module_include location='product_list_header'}
|
||||
|
||||
<th>
|
||||
{admin_sortable_header
|
||||
current_order=$product_order
|
||||
order='visible'
|
||||
reverse_order='visible_reverse'
|
||||
path={url path='/admin/product' category_id=$current_category_id}
|
||||
label="{intl l='Online'}"
|
||||
}
|
||||
</th>
|
||||
|
||||
<th>
|
||||
{admin_sortable_header
|
||||
current_order=$product_order
|
||||
order='manual'
|
||||
reverse_order='manual_reverse'
|
||||
path={url path='/admin/product' category_id=$current_category_id}
|
||||
label="{intl l='Position'}"
|
||||
}
|
||||
</th>
|
||||
|
||||
<th> </th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
{loop name="product_list" type="product" category=$current_category_id order="manual"}
|
||||
<tr>
|
||||
<td>{$ID}</td>
|
||||
|
||||
<td>
|
||||
{loop type="image" name="cat_image" source="product" source_id="$ID" limit="1" width="50" height="50" resize_mode="crop" backend_context="1"}
|
||||
<a href="{url path='admin/product/edit' id=$ID}" title="{intl l='Edit this product'}">
|
||||
<img src="{$IMAGE_URL}" alt="{$TITLE}" />
|
||||
</a>
|
||||
{/loop}
|
||||
|
||||
<td class="object-title"><a href="{url path='admin/product/edit' id=$ID}" title="{intl l='Edit this product'}">{$REF}</a></td>
|
||||
|
||||
<td class="object-title"><a href="{url path='admin/product/edit' id=$ID}" title="{intl l='Edit this product'}">{$TITLE}</a></td>
|
||||
|
||||
{module_include location='product_list_row'}
|
||||
|
||||
<td>
|
||||
{loop type="auth" name="can_change" roles="ADMIN" permissions="admin.products.edit"}
|
||||
<div class="make-switch switch-small" data-on="success" data-off="danger" data-on-label="<i class='glyphicon glyphicon-ok'></i>" data-off-label="<i class='glyphicon glyphicon-remove'></i>">
|
||||
<input type="checkbox" data-id="{$ID}" class="productVisibleToggle" {if $VISIBLE == 1}checked="checked"{/if}>
|
||||
</div>
|
||||
{/loop}
|
||||
|
||||
{elseloop rel="can_change"}
|
||||
<div class="make-switch switch-small" data-on="success" data-off="danger" data-on-label="<i class='glyphicon glyphicon-ok'></i>" data-off-label="<i class='glyphicon glyphicon-remove'></i>">
|
||||
<input type="checkbox" data-id="{$ID}" class="displayToggle" {if $VISIBLE == 1}checked="checked"{/if}>
|
||||
</div>
|
||||
{/elseloop}
|
||||
</td>
|
||||
|
||||
<td>
|
||||
{admin_position_block
|
||||
permission="admin.product.edit"
|
||||
path={url path='admin/product' category_id=$ID}
|
||||
url_parameter="product_id"
|
||||
in_place_edit_class="productPositionChange"
|
||||
position=$POSITION
|
||||
id=$ID
|
||||
}
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<div class="btn-group">
|
||||
{loop type="auth" name="can_change" roles="ADMIN" permissions="admin.product.edit"}
|
||||
<a class="btn btn-default btn-xs" title="{intl l='Edit this product'}" href="{url path='admin/product/edit' product_id=$ID}"><i class="glyphicon glyphicon-edit"></i></a>
|
||||
{/loop}
|
||||
|
||||
{loop type="auth" name="can_change" roles="ADMIN" permissions="admin.product.delete"}
|
||||
<a class="btn btn-default btn-xs product-delete" title="{intl l='Delete this product'}" href="{url path='admin/product/delete' id=$ID}"><i class="glyphicon glyphicon-trash"></i></a>
|
||||
<td>
|
||||
{loop type="image" name="cat_image" source="category" source_id="$ID" limit="1" width="50" height="50" resize_mode="crop" backend_context="1"}
|
||||
<a href="{url path='admin/catalog' category_id=$ID}" title="{intl l='Browse this category'}"><img class="img-thumbnail" src="{$IMAGE_URL}" alt="{$TITLE}" /></a>
|
||||
{/loop}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{/loop}
|
||||
</tbody>
|
||||
{/ifloop}
|
||||
</td>
|
||||
|
||||
{elseloop rel="product_list"}
|
||||
<thead>
|
||||
<tr>
|
||||
<td class="message"><div class="alert alert-info">{intl l="This category doesn't have any products. To add a new product, <strong>click the + button</strong> above."}</div></td>
|
||||
</tr>
|
||||
</thead>
|
||||
{/elseloop}
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<td class="object-title">
|
||||
<a href="{url path='admin/catalog' category_id=$ID}" title="{intl l='Browse this category'}">
|
||||
{$TITLE}
|
||||
</a>
|
||||
</td>
|
||||
|
||||
{module_include location='catalog_bottom'}
|
||||
</div>
|
||||
</div>
|
||||
{module_include location='category_list_row'}
|
||||
|
||||
{* Adding a new Category *}
|
||||
<td>
|
||||
{loop type="auth" name="can_change" roles="ADMIN" permissions="admin.categories.edit"}
|
||||
<div class="make-switch switch-small categoryVisibleToggle" data-id="{$ID}" data-on="success" data-off="danger" data-on-label="<i class='glyphicon glyphicon-ok'></i>" data-off-label="<i class='glyphicon glyphicon-remove'></i>">
|
||||
<input type="checkbox" class="categoryVisibleToggle" {if $VISIBLE == 1}checked="checked"{/if}>
|
||||
</div>
|
||||
{/loop}
|
||||
|
||||
{form name="thelia.admin.category.creation"}
|
||||
{elseloop rel="can_change"}
|
||||
<div class="make-switch switch-small" data-on="success" data-off="danger" data-on-label="<i class='glyphicon glyphicon-ok'></i>" data-off-label="<i class='glyphicon glyphicon-remove'></i>">
|
||||
<input type="checkbox" class="disabled" disabled="disabled" {if $VISIBLE == 1}checked="checked"{/if}>
|
||||
</div>
|
||||
{/elseloop}
|
||||
</td>
|
||||
|
||||
<td>
|
||||
{admin_position_block
|
||||
permission="admin.categories.edit"
|
||||
path={url path='admin/categories/update-position' category_id=$ID}
|
||||
url_parameter="category_id"
|
||||
in_place_edit_class="categoryPositionChange"
|
||||
position=$POSITION
|
||||
id=$ID
|
||||
}
|
||||
</td>
|
||||
|
||||
<td class="actions">
|
||||
<div class="btn-group">
|
||||
<a class="btn btn-default btn-xs" title="{intl l='Browse this category'}" href="{url path='admin/categories' category_id=$ID}"><i class="glyphicon glyphicon-folder-open"></i></a>
|
||||
|
||||
{loop type="auth" name="can_change" roles="ADMIN" permissions="admin.categories.edit"}
|
||||
<a class="btn btn-default btn-xs" title="{intl l='Edit this category'}" href="{url path='/admin/categories/update' category_id=$ID}"><i class="glyphicon glyphicon-edit"></i></a>
|
||||
{/loop}
|
||||
|
||||
{loop type="auth" name="can_delete" roles="ADMIN" permissions="admin.categories.delete"}
|
||||
<a class="btn btn-default btn-xs category-delete" title="{intl l='Delete this category and all its contents'}" href="#category_delete_dialog" data-id="{$ID}" data-toggle="modal"><i class="glyphicon glyphicon-trash"></i></a>
|
||||
{/loop}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{/loop}
|
||||
</tbody>
|
||||
{/ifloop}
|
||||
|
||||
{elseloop rel="category_list"}
|
||||
<thead>
|
||||
<tr>
|
||||
<td class="message">
|
||||
<div class="alert alert-info">
|
||||
{loop type="auth" name="can_create" roles="ADMIN" permissions="admin.categories.create"}
|
||||
{intl l="This category has no sub-categories. To create a new one, click the + button above."}
|
||||
{/loop}
|
||||
|
||||
{elseloop rel="can_create"}
|
||||
{intl l="This category has no sub-categories."}
|
||||
{/elseloop}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</thead>
|
||||
{/elseloop}
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{module_include location='categories_bottom'}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{* Adding a new category *}
|
||||
|
||||
{form name="thelia.admin.category.creation"}
|
||||
|
||||
{* Capture the dialog body, to pass it to the generic dialog *}
|
||||
{capture "category_creation_dialog"}
|
||||
|
||||
{form_hidden_fields form=$form}
|
||||
|
||||
{form_field form=$form field='success_url'}
|
||||
{* on success, redirect to the edition page, _ID_ is replaced with the created object ID, see controller *}
|
||||
<input type="hidden" name="{$name}" value="{url path='/admin/categories/update' category_id='_ID_'}" />
|
||||
{/form_field}
|
||||
{form_field form=$form field='success_url'}
|
||||
{* on success, redirect to the edition page, _ID_ is replaced with the created object ID, see controller *}
|
||||
<input type="hidden" name="{$name}" value="{url path='/admin/categories/update' category_id='_ID_'}" />
|
||||
{/form_field}
|
||||
|
||||
{form_field form=$form field='parent'}
|
||||
<input type="hidden" name="{$name}" value="{$current_category_id}" />
|
||||
{/form_field}
|
||||
{form_field form=$form field='parent'}
|
||||
<input type="hidden" name="{$name}" value="{$category_id}" />
|
||||
{/form_field}
|
||||
|
||||
{form_field form=$form field='title'}
|
||||
<div class="form-group {if $error}has-error{/if}">
|
||||
<label for="{$label_attr.for}" class="control-label">{$label} : </label>
|
||||
{form_field form=$form field='title'}
|
||||
<div class="form-group {if $error}has-error{/if}">
|
||||
<label for="{$label_attr.for}" class="control-label">{$label} : </label>
|
||||
{loop type="lang" name="default-lang" default_only="1"}
|
||||
<div class="input-group">
|
||||
<input type="text" id="{$label_attr.for}" required="required" name="{$name}" class="form-control" value="{$value}" title="{intl l='Currency name'}" placeholder="{intl l='Name'}">
|
||||
<span class="input-group-addon"><img src="{image file="assets/img/flags/{$CODE}.gif"}" alt="$TITLE" /></span>
|
||||
</div>
|
||||
|
||||
{loop type="lang" name="default-lang" default_only="1"}
|
||||
<div class="help-block">{intl l='Enter here the category name in the default language (%title)' title="$TITLE"}</div>
|
||||
|
||||
<div class="input-group">
|
||||
<input type="text" id="{$label_attr.for}" required="required" name="{$name}" class="form-control" value="{$value}" title="{intl l='Currency name'}" placeholder="{intl l='Name'}">
|
||||
<span class="input-group-addon"><img src="{image file="assets/img/flags/{$CODE}.gif"}" alt="$TITLE" /></span>
|
||||
</div>
|
||||
{* Switch edition to the current locale *}
|
||||
<input type="hidden" name="edit_language_id" value="{$ID}" />
|
||||
|
||||
<div class="help-block">{intl l='Enter here the category name in the default language (%title)' title="$TITLE"}</div>
|
||||
{form_field form=$form field='locale'}
|
||||
<input type="hidden" name="{$name}" value="{$LOCALE}" />
|
||||
{/form_field}
|
||||
{/loop}
|
||||
</div>
|
||||
{/form_field}
|
||||
|
||||
{* Switch edition to the current locale *}
|
||||
<input type="hidden" name="edit_language_id" value="{$ID}" />
|
||||
|
||||
{form_field form=$form field='locale'}
|
||||
<input type="hidden" name="{$name}" value="{$LOCALE}" />
|
||||
{/form_field}
|
||||
{/loop}
|
||||
</div>
|
||||
{/form_field}
|
||||
{form_field form=$form field='visible'}
|
||||
<div class="form-group {if $error}has-error{/if}">
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" id="{$label_attr.for}" name="{$name}" value="1" checked="checked">
|
||||
{$label}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
{/form_field}
|
||||
|
||||
{module_include location='category_create_form'}
|
||||
|
||||
{/capture}
|
||||
{/capture}
|
||||
|
||||
{include
|
||||
file = "includes/generic-create-dialog.html"
|
||||
{include
|
||||
file = "includes/generic-create-dialog.html"
|
||||
|
||||
dialog_id = "add_category_dialog"
|
||||
dialog_title = {intl l="Create a new category"}
|
||||
dialog_body = {$smarty.capture.category_creation_dialog nofilter}
|
||||
dialog_id = "category_creation_dialog"
|
||||
dialog_title = {intl l="Create a new category"}
|
||||
dialog_body = {$smarty.capture.category_creation_dialog nofilter}
|
||||
|
||||
dialog_ok_label = {intl l="Create this category"}
|
||||
dialog_cancel_label = {intl l="Cancel"}
|
||||
dialog_ok_label = {intl l="Create this category"}
|
||||
|
||||
form_action = {url path='/admin/categories/create'}
|
||||
form_enctype = {form_enctype form=$form}
|
||||
form_error_message = $form_error_message
|
||||
}
|
||||
form_action = {url path='/admin/categories/create'}
|
||||
form_enctype = {form_enctype form=$form}
|
||||
form_error_message = $form_error_message
|
||||
}
|
||||
{/form}
|
||||
|
||||
{* Delete category confirmation dialog *}
|
||||
|
||||
{* Delete confirmation dialog *}
|
||||
|
||||
{capture "category_delete_dialog"}
|
||||
<input type="hidden" name="current_category_id" value="{$current_category_id}" />
|
||||
<input type="hidden" name="category_id" id="delete_category_id" value"" />
|
||||
<input type="hidden" name="category_id" id="category_delete_id" value="" />
|
||||
|
||||
{module_include location='category_delete_form'}
|
||||
|
||||
@@ -399,9 +263,9 @@
|
||||
{include
|
||||
file = "includes/generic-confirm-dialog.html"
|
||||
|
||||
dialog_id = "delete_category_dialog"
|
||||
dialog_title = {intl l="Delete a category"}
|
||||
dialog_message = {intl l="Do you really want to delete this category, and <strong>all</strong> its contents ?"}
|
||||
dialog_id = "category_delete_dialog"
|
||||
dialog_title = {intl l="Delete category"}
|
||||
dialog_message = {intl l="Do you really want to delete this category and all its content ?"}
|
||||
|
||||
form_action = {url path='/admin/categories/delete'}
|
||||
form_content = {$smarty.capture.category_delete_dialog nofilter}
|
||||
@@ -410,108 +274,76 @@
|
||||
|
||||
{block name="javascript-initialization"}
|
||||
|
||||
{javascripts file='assets/js/bootstrap-switch/bootstrap-switch.js'}
|
||||
<script src="{$asset_url}"></script>
|
||||
{/javascripts}
|
||||
{javascripts file='assets/js/bootstrap-switch/bootstrap-switch.js'}
|
||||
<script src="{$asset_url}"></script>
|
||||
{/javascripts}
|
||||
|
||||
{javascripts file='assets/js/bootstrap-editable/bootstrap-editable.js'}
|
||||
<script src="{$asset_url}"></script>
|
||||
{/javascripts}
|
||||
{javascripts file='assets/js/bootstrap-editable/bootstrap-editable.js'}
|
||||
<script src="{$asset_url}"></script>
|
||||
{/javascripts}
|
||||
|
||||
<script>
|
||||
$(function() {
|
||||
<script>
|
||||
$(function() {
|
||||
|
||||
// JS stuff for category creation form
|
||||
{include
|
||||
file = "includes/generic-js-dialog.html"
|
||||
// Set proper category ID in delete from
|
||||
$('a.category-delete').click(function(ev) {
|
||||
$('#category_delete_id').val($(this).data('id'));
|
||||
});
|
||||
|
||||
dialog_id = "add_category_dialog"
|
||||
form_name = "thelia.admin.category.creation"
|
||||
}
|
||||
// JS stuff for creation form
|
||||
{include
|
||||
file = "includes/generic-js-dialog.html"
|
||||
dialog_id = "category_creation_dialog"
|
||||
form_name = "thelia.admin.category.creation"
|
||||
}
|
||||
|
||||
// JS stuff for product creation form
|
||||
{include
|
||||
file = "includes/generic-js-dialog.html"
|
||||
|
||||
dialog_id = "add_product_dialog"
|
||||
form_name = "thelia.admin.product.creation"
|
||||
}
|
||||
{* Toggle object visibility *}
|
||||
|
||||
|
||||
{* Set the proper ID in the delete confirmation dialog *}
|
||||
$(".categoryVisibleToggle").on('switch-change', function(event, data) {
|
||||
console.log("yaya");
|
||||
$.ajax({
|
||||
url : "{url path='admin/categories/toggle-online'}",
|
||||
data : {
|
||||
category_id : $(this).data('id'),
|
||||
action : 'visibilityToggle'
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$('a.category-delete').click(function(ev) {
|
||||
$('#delete_category_id').val($(this).data('id'));
|
||||
});
|
||||
{* Inline editing of object position using bootstrap-editable *}
|
||||
|
||||
$('a.product-delete').click(function(ev) {
|
||||
$('#delete_product_id').val($(this).data('id'));
|
||||
});
|
||||
$('.categoryPositionChange').editable({
|
||||
type : 'text',
|
||||
title : '{intl l="Enter new category position"}',
|
||||
mode : 'popup',
|
||||
inputclass : 'input-mini',
|
||||
placement : 'left',
|
||||
success : function(response, newValue) {
|
||||
// The URL template
|
||||
var url = "{url path='/admin/categories/update-position' category_id='__ID__' position='__POS__'}";
|
||||
|
||||
// Perform subtitutions
|
||||
url = url.replace('__ID__', $(this).data('id'))
|
||||
.replace('__POS__', newValue);
|
||||
|
||||
{* Toggle object visibility *}
|
||||
// Reload the page
|
||||
location.href = url;
|
||||
}
|
||||
});
|
||||
|
||||
{* Change default status *}
|
||||
|
||||
$('.change-default').change(function(ev) {
|
||||
var url = "{url path='/admin/categories/set-default' category_id='__ID__'}";
|
||||
|
||||
// Perform ID subtitutions
|
||||
url = url.replace('__ID__', $(this).val());
|
||||
|
||||
// Reload the page
|
||||
location.href = url;
|
||||
});
|
||||
|
||||
$(".categoryVisibleToggle").click(function() {
|
||||
$.ajax({
|
||||
url : "{url path='admin/categories/toggle-online'}",
|
||||
data : {
|
||||
category_id : $(this).data('id'),
|
||||
action : 'visibilityToggle'
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$(".productVisibleToggle").click(function() {
|
||||
$.ajax({
|
||||
url : "{url path='admin/products/toggle-online'}",
|
||||
data : {
|
||||
category_id : $(this).data('id'),
|
||||
action : 'visibilityToggle'
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
{* Inline editing of object position using bootstrap-editable *}
|
||||
|
||||
$('.categoryPositionChange').editable({
|
||||
type : 'text',
|
||||
title : '{intl l="Enter new category position"}',
|
||||
mode : 'popup',
|
||||
inputclass : 'input-mini',
|
||||
placement : 'left',
|
||||
success : function(response, newValue) {
|
||||
// The URL template
|
||||
var url = "{url path='/admin/categories/update-position' category_id='__ID__' position='__POS__'}";
|
||||
|
||||
// Perform subtitutions
|
||||
url = url.replace('__ID__', $(this).data('id'))
|
||||
.replace('__POS__', newValue);
|
||||
|
||||
// Reload the page
|
||||
location.href = url;
|
||||
}
|
||||
});
|
||||
|
||||
$('.productPositionChange').editable({
|
||||
type : 'text',
|
||||
title : '{intl l="Enter new product position"}',
|
||||
mode : 'popup',
|
||||
inputclass : 'input-mini',
|
||||
placement : 'left',
|
||||
success : function(response, newValue) {
|
||||
// The URL template
|
||||
var url = "{url path='/admin/products/update-position' product_id='__ID__' position='__POS__'}";
|
||||
|
||||
// Perform subtitutions
|
||||
url = url.replace('__ID__', $(this).data('id'))
|
||||
.replace('__POS__', newValue);
|
||||
|
||||
// Reload the page
|
||||
location.href = url;
|
||||
}
|
||||
});
|
||||
|
||||
})
|
||||
</script>
|
||||
</script>
|
||||
{/block}
|
||||
@@ -8,206 +8,154 @@
|
||||
<div class="catalog edit-category">
|
||||
<div id="wrapper" class="container">
|
||||
|
||||
{include file="includes/catalog-breadcrumb.html"}
|
||||
{include file="includes/catalog-breadcrumb.html" editing_category="true"}
|
||||
|
||||
<div class="row">
|
||||
{loop name="category_edit" type="category" visible="*" id="{$current_category_id}" backend_context="1" lang="$edit_language_id"}
|
||||
{loop name="category_edit" type="category" visible="*" id="{$category_id}" backend_context="1" lang="$edit_language_id"}
|
||||
<div class="col-md-12 general-block-decorator">
|
||||
<div class="row">
|
||||
<div class="col-md-7 title">
|
||||
{intl l='Edit category'}
|
||||
{intl l='Edit category %title' title=$TITLE}
|
||||
</div>
|
||||
|
||||
<div class="col-md-5 actions">
|
||||
<button class="btn btn-default" title="{intl l='Edit previous category'}"><i class="glyphicon glyphicon-arrow-left"></i></button>
|
||||
<button class="btn btn-default" title="{intl l='Preview category page'}"><i class="glyphicon glyphicon-eye-open"></i></button>
|
||||
<button class="btn btn-default" title="{intl l='Edit next category'}"><i class="glyphicon glyphicon-arrow-right"></i></button>
|
||||
|
||||
{if $HAS_PREVIOUS != 0}
|
||||
<a href="{url path='/admin/categories/update' category_id=$PREVIOUS}" class="btn btn-default" title="{intl l='Edit previous category'}"><span class="glyphicon glyphicon-arrow-left"></span></a>
|
||||
{else}
|
||||
<a href="#" disabled="disabled" class="btn btn-default"><span class="glyphicon glyphicon-arrow-left"></span></a>
|
||||
{/if}
|
||||
|
||||
<a href="{$URL}" target="_blank" class="btn btn-default" title="{intl l='Preview category page'}"><span class="glyphicon glyphicon-eye-open"></span></a>
|
||||
|
||||
{if $HAS_NEXT != 0}
|
||||
<a href="{url path='/admin/categories/update' category_id=$NEXT}" class="btn btn-default" title="{intl l='Edit next category'}"><span class="glyphicon glyphicon-arrow-right"></span></a>
|
||||
{else}
|
||||
<a href="#" disabled="disabled" class="btn btn-default"><span class="glyphicon glyphicon-arrow-right"></span></a>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form method="post">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
|
||||
<div class="tabbable">
|
||||
<ul class="nav nav-tabs admin-tabs" id="tabbed_menu">
|
||||
<li class="active">
|
||||
<a href="#general_description">{intl l="General description"}</a>
|
||||
</li>
|
||||
<ul class="nav nav-tabs">
|
||||
<li class="active"><a href="#general_description" data-toggle="tab">{intl l="General description"}</a></li>
|
||||
|
||||
<li>
|
||||
<a href="#details">{intl l="Details"}</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#images">{intl l="Images"}</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#documents">{intl l="Documents"}</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#modules">{intl l="Modules"}</a>
|
||||
</li>
|
||||
</ul>
|
||||
<li><a href="#details" data-toggle="tab">{intl l="Details"}</a></li>
|
||||
<li><a href="#images" data-toggle="tab">{intl l="Images"}</a></li>
|
||||
<li><a href="#documents" data-toggle="tab">{intl l="Documents"}</a></li>
|
||||
<li><a href="#modules" data-toggle="tab">{intl l="Modules"}</a></li>
|
||||
</ul>
|
||||
|
||||
<div class="tab-content">
|
||||
<div class="tab-content">
|
||||
|
||||
<div class="tab-pane active form-container" id="general_description">
|
||||
<div class="form-horizontal col-md-12">
|
||||
<fieldset>
|
||||
<div class="tab-pane fade active in" id="general_description">
|
||||
|
||||
{include file="includes/inner-form-toolbar.html" close_url="{url path='admin/catalog/category/edit' category_id=$current_category_id}"}
|
||||
<div class="form-container">
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="control-group">
|
||||
<label class="control-label">
|
||||
{intl l='Title *'}
|
||||
</label>
|
||||
{form name="thelia.admin.category.modification"}
|
||||
<form method="POST" action="{url path='/admin/categories/save'}" {form_enctype form=$form} class="clearfix">
|
||||
|
||||
<div class="controls">
|
||||
<input type="text" required="required" title="{intl l='Category title'}" placeholder="{intl l='Category title'}" class="input-block-level" value="{$TITLE|htmlspecialchars}">
|
||||
</div>
|
||||
</div>
|
||||
{include file="includes/inner-form-toolbar.html" close_url="{url path='/admin/categories' category_id=$category_id}"}
|
||||
|
||||
<div class="control-group">
|
||||
<label class="control-label">
|
||||
{intl l='Summary'}
|
||||
<span class="label-help-block">{intl l="A short category description, used when a summary or an introduction is required"}</span>
|
||||
</label>
|
||||
{* Be sure to get the category ID, even if the form could not be validated *}
|
||||
<input type="hidden" name="category_id" value="{$category_id}" />
|
||||
|
||||
<div class="controls">
|
||||
<textarea name="summary" rows="3" title="{intl l='Short category description'}" placeholder="{intl l='Short category description'}" class="input-block-level">{$CHAPO|htmlspecialchars}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
{form_hidden_fields form=$form}
|
||||
|
||||
<div class="control-group">
|
||||
<label class="control-label">
|
||||
{intl l='Detailed description'}
|
||||
<span class="label-help-block">{intl l="The détailed category description."}</span>
|
||||
</label>
|
||||
{form_field form=$form field='success_url'}
|
||||
<input type="hidden" name="{$name}" value="{url path='/admin/category' category_id={$category_d}}" />
|
||||
{/form_field}
|
||||
|
||||
<div class="controls">
|
||||
<textarea name="summary" rows="10" class="input-block-level">{$DESCRIPTION|htmlspecialchars}</textarea>
|
||||
{form_field form=$form field='locale'}
|
||||
<input type="hidden" name="{$name}" value="{$edit_language_locale}" />
|
||||
{/form_field}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
{if $form_error}<div class="alert alert-danger">{$form_error_message}</div>{/if}
|
||||
|
||||
<div class="control-group">
|
||||
<label class="control-label">
|
||||
{intl l='Conclusion'}
|
||||
<span class="label-help-block">{intl l="A short post-description information"}</span>
|
||||
</label>
|
||||
{include file="includes/standard-description-form-fields.html"}
|
||||
|
||||
<div class="controls">
|
||||
<textarea name="summary" rows="3" title="{intl l='Short category description'}" placeholder="{intl l='Short category description'}" class="input-block-level">{$POSTSCRIPTUM|htmlspecialchars}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
{form_field form=$form field='url'}
|
||||
<div class="form-group {if $error}has-error{/if}">
|
||||
<label for="{$label_attr.for}" class="control-label">
|
||||
{intl l="{$label}"} :
|
||||
</label>
|
||||
|
||||
<div class="control-group">
|
||||
<label class="control-label">
|
||||
{intl l='Rewriten URL'}
|
||||
</label>
|
||||
<input type="text" id="{$label_attr.for}" required="required" name="{$name}" value="{$value}" title="{intl l='Rewritten URL'}" placeholder="{intl l='Rewriten URL'}" class="form-control">
|
||||
</div>
|
||||
{/form_field}
|
||||
|
||||
<div class="controls">
|
||||
<div class="input-group input-block-level">
|
||||
<input type="text" required="required" title="{intl l='Rewriten URL'}" placeholder="{intl l='Rewriten URL'}" class="input-block-level" value="{$URL|htmlspecialchars}">
|
||||
<a class="btn btn-default input-group-addon use_default_rewriten_url" href="#">{intl l='Use default'}</a>
|
||||
</div>
|
||||
<div class="help-block">{intl l="The rewritten URL to the category page. Click \"Use Default\" button to use the default URL. Use only digits, letters, - and _ characters."}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
{form_field form=$form field='parent'}
|
||||
<div class="form-group {if $error}has-error{/if}">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<label for="{$label_attr.for}" class="control-label">
|
||||
{intl l="{$label}"} :
|
||||
</label>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="control-group">
|
||||
<lablel> </lablel>
|
||||
<div class="controls">
|
||||
<p>{intl l='Category created on %date_create. Last modification: %date_change' date_create="{format_date date=$CREATE_DATE}" date_change="{format_date date=$UPDATE_DATE}"}}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<select id="{$label_attr.for}" required="required" name="{$name}" class="form-control">
|
||||
<option value="0">{intl l="Top level"}</option>
|
||||
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
{$myparent=$PARENT}
|
||||
{loop name="cat-parent" type="category-tree" visible="*" category="0"}
|
||||
<option value="{$ID}" style="padding-left: {3 + $LEVEL * 20}px" {if $myparent == $ID}selected="selected"{/if} {if $category_id == $ID}disabled="disabled"{/if}>{$TITLE}</option>
|
||||
{/loop}
|
||||
|
||||
<div class="tab-pane form-container" id="details">
|
||||
<div class="form-horizontal col-md-12">
|
||||
<fieldset>
|
||||
{include file="includes/inner-form-toolbar.html" close_url="{url path='admin/catalog/category/edit' category_id=$current_category_id}"}
|
||||
</select>
|
||||
</div>
|
||||
{/form_field}
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
{form_field form=$form field='visible'}
|
||||
<div class="form-group {if $error}has-error{/if}">
|
||||
<label for="{$label_attr.for}" class="control-label">{intl l='Visibility'}</label>
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" id="{$label_attr.for}" name="{$name}" value="1" {if $value != 0}checked="checked"{/if}>
|
||||
{$label}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
{/form_field}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="control-group">
|
||||
<label class="control-label">
|
||||
{intl l='Category ID'}
|
||||
</label>
|
||||
|
||||
<div class="controls">
|
||||
<input type="text" name="id" disabled="disabled" value="{$ID}" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<div class="control-group">
|
||||
<label class="control-label">
|
||||
{intl l='Parent category *'}
|
||||
</label>
|
||||
|
||||
<div class="controls">
|
||||
<select required="required" name="parent">
|
||||
<option value="0">{intl l="Top level"}</option>
|
||||
|
||||
{loop name="cat-parent" type="category-tree" visible="*" category="0" exclude="{$current_category_id}"}
|
||||
<option value="{$ID}" style="padding-left: {3 + $LEVEL * 20}px" {if $parent_category_id == $ID}selected="selected"{/if}>{$TITLE}</option>
|
||||
{/loop}
|
||||
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="control-group">
|
||||
<label class="control-label">
|
||||
{intl l='Visibility'}
|
||||
</label>
|
||||
|
||||
<div class="controls">
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" name="visible" {if $VISIBLE}checked="checked"{/if}> {intl l="Display this category on front-office"}
|
||||
</label>
|
||||
<lablel> </lablel>
|
||||
<div class="controls">
|
||||
<p>{intl l='Category created on %date_create. Last modification: %date_change' date_create="{format_date date=$CREATE_DATE}" date_change="{format_date date=$UPDATE_DATE}"}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
{/form}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane" id="images">
|
||||
<p>Images</p>
|
||||
</div>
|
||||
<div class="tab-pane fade" id="details">
|
||||
klljkmk
|
||||
</div>
|
||||
|
||||
<div class="tab-pane" id="documents">
|
||||
<p>Documents</p>
|
||||
</div>
|
||||
<div class="tab-pane fade" id="images">
|
||||
</div>
|
||||
|
||||
<div class="tab-pane" id="modules">
|
||||
<p>Modules</p>
|
||||
</div>
|
||||
<div class="tab-pane fade" id="documents">
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
{/loop}
|
||||
<div class="tab-pane fade" id="modules">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/loop}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -217,10 +165,6 @@
|
||||
<script>
|
||||
|
||||
$(function() {
|
||||
$('#tabbed_menu a').click(function (e) {
|
||||
e.preventDefault();
|
||||
$(this).tab('show');
|
||||
});
|
||||
|
||||
$('.use_default_rewriten_url').click(function(ev) {
|
||||
alert("Not functionnal");
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
{form_field form=$form field='name'}
|
||||
<div class="form-group {if $error}has-error{/if}">
|
||||
<label for="{$label_attr.for}" class="control-label">{intl l="{$label}"} : </label>
|
||||
<input type="text" id="{$label_attr.for}" required="required" name="{$name}" value="{$value|htmlspecialchars}" title="{intl l='Currency name'}" placeholder="{intl l='Currency name'}" class="form-control">
|
||||
<input type="text" id="{$label_attr.for}" required="required" name="{$name}" value="{$value}" title="{intl l='Currency name'}" placeholder="{intl l='Currency name'}" class="form-control">
|
||||
<span class="help-block"> </span>
|
||||
</div>
|
||||
{/form_field}
|
||||
@@ -64,7 +64,7 @@
|
||||
<label for="{$label_attr.for}" class="control-label">
|
||||
{intl l="{$label}"} :
|
||||
</label>
|
||||
<input type="text" id="{$label_attr.for}" required="required" name="{$name}" value="{$value|htmlspecialchars}" title="{intl l='Currency ISO 4217 Code'}" placeholder="{intl l='Code'}" class="form-control">
|
||||
<input type="text" id="{$label_attr.for}" required="required" name="{$name}" value="{$value}" title="{intl l='Currency ISO 4217 Code'}" placeholder="{intl l='Code'}" class="form-control">
|
||||
<span class="help-block">
|
||||
<a title="{intl l='More information about ISO 4217'}" href="http://fr.wikipedia.org/wiki/ISO_4217" target="_blank">List of ISO 4217 code</a>
|
||||
</span>
|
||||
@@ -80,7 +80,7 @@
|
||||
<label for="{$label_attr.for}" class="control-label">
|
||||
{intl l="{$label}"} :
|
||||
</label>
|
||||
<input type="text" id="{$label_attr.for}" required="required" name="{$name}" value="{$value|htmlspecialchars}" title="{intl l='Currency symbol'}" placeholder="{intl l='Symbol'}" class="form-control">
|
||||
<input type="text" id="{$label_attr.for}" required="required" name="{$name}" value="{$value}" title="{intl l='Currency symbol'}" placeholder="{intl l='Symbol'}" class="form-control">
|
||||
<span class="help-block">{intl l='The symbol, such as $, £, €...'}</span>
|
||||
</div>
|
||||
{/form_field}
|
||||
@@ -90,7 +90,7 @@
|
||||
<label for="{$label_attr.for}" class="control-label">
|
||||
{intl l="{$label}"} :
|
||||
</label>
|
||||
<input type="text" id="{$label_attr.for}" required="required" name="{$name}" value="{$value|htmlspecialchars}" title="{intl l='Rate from Euro'}" placeholder="{intl l='Rate'}" class="form-control">
|
||||
<input type="text" id="{$label_attr.for}" required="required" name="{$name}" value="{$value}" title="{intl l='Rate from Euro'}" placeholder="{intl l='Rate'}" class="form-control">
|
||||
<span class="help-block">The rate from Euro: Price in Euro x rate = Price in this currency</span>
|
||||
</div>
|
||||
{/form_field}
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
<div class="general-block-decorator text-center">
|
||||
<h1>{intl l="Oops! An Error Occurred"}</h1>
|
||||
|
||||
{block name="error-message"}<div class="alert alert-danger">{$error_message}</div>{/block}
|
||||
{block name="error-message"}<p>{$error_message}</p>{/block}
|
||||
|
||||
<a href="{url path='/admin/home'}" class="btn btn-default btn-info btn-lg"><span class="glyphicon glyphicon-home"></span> {intl l="Go to administration home"}</a>
|
||||
</div>
|
||||
|
||||
@@ -5,17 +5,17 @@
|
||||
<li><a href="{url path='admin/catalog'}">Catalog</a>
|
||||
|
||||
{ifloop rel="category_path"}</li>
|
||||
{loop name="category_path" type="category-path" visible="*" category=$current_category_id}
|
||||
{if $ID == $current_category_id}
|
||||
{loop name="category_path" type="category-path" visible="*" category=$category_id}
|
||||
{if $ID == $category_id}
|
||||
<li class="active">
|
||||
{if $action == 'edit'}
|
||||
{intl l='Editing %cat' cat="{$TITLE}"}
|
||||
{if $editing_category == true}
|
||||
{intl l='Editing %cat' cat="{$TITLE}"}
|
||||
{else}
|
||||
{$TITLE} <a href="{url path='admin/catalog/category/edit' category_id=$ID}" title="{intl l='Edit this category'}">{intl l="(edit)"}</a>
|
||||
{$TITLE} <a href="{url path='/admin/categories/update' category_id=$ID}" title="{intl l='Edit this category'}">{intl l="(edit)"}</a>
|
||||
{/if}
|
||||
</li>
|
||||
{else}
|
||||
<li><a href="{url path='admin/catalog/category' id=" $ID" action='browse'}">{$TITLE}</a></li>
|
||||
<li><a href="{url path='/admin/categories' category_id=" $ID" action='browse'}">{$TITLE}</a></li>
|
||||
{/if}
|
||||
{/loop}
|
||||
{/ifloop}
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
{/form_field}
|
||||
|
||||
{form_field form=$form field='id'}
|
||||
<input type="hidden" name="{$name}" value="{$value|htmlspecialchars}" />
|
||||
<input type="hidden" name="{$name}" value="{$value}" />
|
||||
{/form_field}
|
||||
|
||||
{form_field form=$form field='locale'}
|
||||
@@ -55,7 +55,7 @@
|
||||
{form_field form=$form field='name'}
|
||||
<div class="form-group {if $error}has-error{/if}">
|
||||
<label for="{$label_attr.for}" class="control-label">{intl l="{$label}"} : </label>
|
||||
<input type="text" id="{$label_attr.for}" required="required" name="{$name}" value="{$value|htmlspecialchars}" title="{intl l='Variable name'}" placeholder="{intl l='Variable name'}" class="form-control">
|
||||
<input type="text" id="{$label_attr.for}" required="required" name="{$name}" value="{$value}" title="{intl l='Variable name'}" placeholder="{intl l='Variable name'}" class="form-control">
|
||||
</div>
|
||||
{/form_field}
|
||||
|
||||
@@ -71,14 +71,14 @@
|
||||
{form_field form=$form field='title'}
|
||||
<div class="form-group {if $error}has-error{/if}">
|
||||
<label for="{$label_attr.for}" class="control-label">{intl l="{$label}"} : </label>
|
||||
<input type="text" id="{$label_attr.for}" name="{$name}" required="required" title="{intl l='Title'}" placeholder="{intl l='Title'}" class="form-control" value="{$value|htmlspecialchars}">
|
||||
<input type="text" id="{$label_attr.for}" name="{$name}" required="required" title="{intl l='Title'}" placeholder="{intl l='Title'}" class="form-control" value="{$value}">
|
||||
</div>
|
||||
{/form_field}
|
||||
|
||||
{form_field form=$form field='subject'}
|
||||
<div class="form-group {if $error}has-error{/if}">
|
||||
<label for="{$label_attr.for}" class="control-label">{intl l="{$label}"} : </label>
|
||||
<input type="text" id="{$label_attr.for}" name="{$name}" required="required" title="{intl l='Subject'}" placeholder="{intl l='Subject'}" class="form-control" value="{$value|htmlspecialchars}">
|
||||
<input type="text" id="{$label_attr.for}" name="{$name}" required="required" title="{intl l='Subject'}" placeholder="{intl l='Subject'}" class="form-control" value="{$value}">
|
||||
</div>
|
||||
{/form_field}
|
||||
|
||||
@@ -88,7 +88,7 @@
|
||||
{intl l="{$label}"} :
|
||||
<span class="label-help-block">{intl l="The mailing template in HTML format."}</span>
|
||||
</label>
|
||||
<textarea name="{$name}" id="{$label_attr.for}" rows="10" class="form-control">{$value|htmlspecialchars}</textarea>
|
||||
<textarea name="{$name}" id="{$label_attr.for}" rows="10" class="form-control">{$value}</textarea>
|
||||
</div>
|
||||
{/form_field}
|
||||
|
||||
@@ -98,7 +98,7 @@
|
||||
{intl l="{$label}"} :
|
||||
<span class="label-help-block">{intl l="The mailing template in text-only format."}</span>
|
||||
</label>
|
||||
<textarea name="{$name}" id="{$label_attr.for}" rows="10" class="form-control">{$value|htmlspecialchars}</textarea>
|
||||
<textarea name="{$name}" id="{$label_attr.for}" rows="10" class="form-control">{$value}</textarea>
|
||||
</div>
|
||||
{/form_field}
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@
|
||||
<div class="btn-group">
|
||||
|
||||
{loop type="auth" name="can_change" roles="ADMIN" permissions="admin.orders.edit"}
|
||||
<a class="btn btn-default btn-xs" title="{intl l='Edit this order'}" href="{url path='/admin/order/update/$ID'}"><span class="glyphicon glyphicon-edit"></span></a>
|
||||
<a class="btn btn-default btn-xs" title="{intl l='Edit this order'}" href="{url path="/admin/order/update/$ID"}"><span class="glyphicon glyphicon-edit"></span></a>
|
||||
{/loop}
|
||||
|
||||
{loop type="auth" name="can_delete" roles="ADMIN" permissions="admin.orders.delete"}
|
||||
@@ -83,7 +83,7 @@
|
||||
<div class="btn-group">
|
||||
|
||||
{loop type="auth" name="can_change" roles="ADMIN" permissions="admin.orders.edit"}
|
||||
<a class="btn btn-default btn-xs" title="{intl l='Edit this order'}" href="{url path='/admin/order/update/$ID'}"><span class="glyphicon glyphicon-edit"></span></a>
|
||||
<a class="btn btn-default btn-xs" title="{intl l='Edit this order'}" href="{url path="/admin/order/update/$ID"}"><span class="glyphicon glyphicon-edit"></span></a>
|
||||
{/loop}
|
||||
|
||||
{loop type="auth" name="can_delete" roles="ADMIN" permissions="admin.orders.delete"}
|
||||
@@ -107,7 +107,7 @@
|
||||
<div class="btn-group">
|
||||
|
||||
{loop type="auth" name="can_change" roles="ADMIN" permissions="admin.orders.edit"}
|
||||
<a class="btn btn-default btn-xs" title="{intl l='Edit this order'}" href="{url path='/admin/order/update/$ID'}"><span class="glyphicon glyphicon-edit"></span></a>
|
||||
<a class="btn btn-default btn-xs" title="{intl l='Edit this order'}" href="{url path="/admin/order/update/$ID"}"><span class="glyphicon glyphicon-edit"></span></a>
|
||||
{/loop}
|
||||
|
||||
{loop type="auth" name="can_delete" roles="ADMIN" permissions="admin.orders.delete"}
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
{* We do not allow creation of hidden variables *}
|
||||
|
||||
{form_field form=$form field='id'}
|
||||
<input type="hidden" name="{$name}" value="{$value|htmlspecialchars}" />
|
||||
<input type="hidden" name="{$name}" value="{$value}" />
|
||||
{/form_field}
|
||||
|
||||
{form_field form=$form field='hidden'}
|
||||
@@ -61,14 +61,14 @@
|
||||
{form_field form=$form field='name'}
|
||||
<div class="form-group {if $error}has-error{/if}">
|
||||
<label for="{$label_attr.for}" class="control-label">{intl l="{$label}"} : </label>
|
||||
<input type="text" id="{$label_attr.for}" required="required" name="{$name}" value="{$value|htmlspecialchars}" title="{intl l='Variable name'}" placeholder="{intl l='Variable name'}" class="form-control">
|
||||
<input type="text" id="{$label_attr.for}" required="required" name="{$name}" value="{$value}" title="{intl l='Variable name'}" placeholder="{intl l='Variable name'}" class="form-control">
|
||||
</div>
|
||||
{/form_field}
|
||||
|
||||
{form_field form=$form field='value'}
|
||||
<div class="form-group {if $error}has-error{/if}">
|
||||
<label for="{$label_attr.for}" class="control-label">{intl l="{$label}"} : </label>
|
||||
<input type="text" id="{$label_attr.for}" name="{$name}" value="{$value|htmlspecialchars}" title="{intl l='Variable value'}" placeholder="{intl l='Variable value'}" class="form-control">
|
||||
<input type="text" id="{$label_attr.for}" name="{$name}" value="{$value}" title="{intl l='Variable value'}" placeholder="{intl l='Variable value'}" class="form-control">
|
||||
</div>
|
||||
{/form_field}
|
||||
|
||||
|
||||
@@ -95,7 +95,7 @@
|
||||
{if $SECURED}
|
||||
{$VALUE}
|
||||
{else}
|
||||
<input id="cancelable_edit_{$ID}" class="js-edit form-control" data-id="{$ID}" type="text" name="variable[{$ID}]" value="{$VALUE|htmlspecialchars}" />
|
||||
<input id="cancelable_edit_{$ID}" class="js-edit form-control" data-id="{$ID}" type="text" name="variable[{$ID}]" value="{$VALUE}" />
|
||||
{/if}
|
||||
</td>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user