Fixed pagination, and factorized pager

This commit is contained in:
Franck Allimant
2014-04-18 19:52:45 +02:00
parent 37c33025d0
commit dcac104b75
6 changed files with 875 additions and 786 deletions

View File

@@ -171,7 +171,6 @@ class CustomerController extends AbstractCrudController
{ {
return $this->render('customers', array_merge(array( return $this->render('customers', array_merge(array(
'customer_order' => $currentOrder, 'customer_order' => $currentOrder,
'display_customer' => 20,
'page' => $this->getRequest()->get('page', 1) 'page' => $this->getRequest()->get('page', 1)
), $customParams) ), $customParams)
); );

View File

@@ -14,10 +14,13 @@ namespace Thelia\Core\Template\Element;
use Propel\Runtime\ActiveQuery\Criteria; use Propel\Runtime\ActiveQuery\Criteria;
use Propel\Runtime\ActiveQuery\ModelCriteria; use Propel\Runtime\ActiveQuery\ModelCriteria;
use Propel\Runtime\Collection\ObjectCollection;
use Propel\Runtime\Util\PropelModelPager;
use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\ContainerInterface;
use Thelia\Core\Security\SecurityContext; use Thelia\Core\Security\SecurityContext;
use Thelia\Core\Template\Element\Exception\LoopException; use Thelia\Core\Template\Element\Exception\LoopException;
use Thelia\Core\Template\Loop\Argument\Argument; use Thelia\Core\Template\Loop\Argument\Argument;
use Thelia\Core\Translation\Translator;
use Thelia\Type\EnumListType; use Thelia\Type\EnumListType;
use Thelia\Type\EnumType; use Thelia\Type\EnumType;
use Thelia\Type\TypeCollection; use Thelia\Type\TypeCollection;
@@ -53,8 +56,11 @@ abstract class BaseLoop
protected $timestampable = false; protected $timestampable = false;
protected $versionable = false; protected $versionable = false;
private static $cacheLoopResult = array(); /** @var Translator */
private static $cacheCount = array(); protected $translator = null;
private static $cacheLoopResult = [];
private static $cacheCount = [];
/** /**
* Create a new Loop * Create a new Loop
@@ -70,6 +76,7 @@ abstract class BaseLoop
$this->request = $container->get('request'); $this->request = $container->get('request');
$this->dispatcher = $container->get('event_dispatcher'); $this->dispatcher = $container->get('event_dispatcher');
$this->securityContext = $container->get('thelia.securityContext'); $this->securityContext = $container->get('thelia.securityContext');
$this->translator = $container->get("thelia.translator");
$this->args = $this->getArgDefinitions()->addArguments($this->getDefaultArgs(), false); $this->args = $this->getArgDefinitions()->addArguments($this->getDefaultArgs(), false);
} }
@@ -81,53 +88,54 @@ abstract class BaseLoop
*/ */
protected function getDefaultArgs() protected function getDefaultArgs()
{ {
$defaultArgs = array( $defaultArgs = [
Argument::createBooleanTypeArgument('backend_context', false), Argument::createBooleanTypeArgument('backend_context', false),
Argument::createBooleanTypeArgument('force_return', false), Argument::createBooleanTypeArgument('force_return', false),
Argument::createAnyTypeArgument('type'), Argument::createAnyTypeArgument('type'),
); ];
if (true === $this->countable) { if (true === $this->countable) {
$defaultArgs = array_merge($defaultArgs, array( $defaultArgs = array_merge($defaultArgs, [
Argument::createIntTypeArgument('offset', 0), Argument::createIntTypeArgument('offset', 0),
Argument::createIntTypeArgument('page'), Argument::createIntTypeArgument('page'),
Argument::createIntTypeArgument('limit', PHP_INT_MAX), Argument::createIntTypeArgument('limit', PHP_INT_MAX),
)); ]);
} }
if ($this instanceof SearchLoopInterface) { if ($this instanceof SearchLoopInterface) {
$defaultArgs = array_merge($defaultArgs, array(
Argument::createAnyTypeArgument('search_term'), $defaultArgs = array_merge($defaultArgs, [
new Argument( Argument::createAnyTypeArgument('search_term'),
'search_in', new Argument(
new TypeCollection( 'search_in',
new EnumListType($this->getSearchIn()) new TypeCollection(
) new EnumListType($this->getSearchIn())
), )
new Argument(
'search_mode',
new TypeCollection(
new EnumType(array(
SearchLoopInterface::MODE_ANY_WORD,
SearchLoopInterface::MODE_SENTENCE,
SearchLoopInterface::MODE_STRICT_SENTENCE,
))
), ),
SearchLoopInterface::MODE_STRICT_SENTENCE new Argument(
) 'search_mode',
)); new TypeCollection(
new EnumType([
SearchLoopInterface::MODE_ANY_WORD,
SearchLoopInterface::MODE_SENTENCE,
SearchLoopInterface::MODE_STRICT_SENTENCE,
])
),
SearchLoopInterface::MODE_STRICT_SENTENCE
)
]);
} }
return $defaultArgs; return $defaultArgs;
} }
/** /**
* Provides a getter to loop parameters * Provides a getter to loop parameter values
* *
* @param string $name the methode name (only getArgname is supported) * @param string $name the method name (only getArgname is supported)
* @param $arguments this parameter is ignored * @param mixed $arguments this parameter is ignored
* *
* @return null * @return mixed the argument value
* @throws \InvalidArgumentException if the parameter is unknown or the method name is not supported. * @throws \InvalidArgumentException if the parameter is unknown or the method name is not supported.
*/ */
public function __call($name, $arguments) public function __call($name, $arguments)
@@ -137,10 +145,12 @@ abstract class BaseLoop
// camelCase to underscore: getNotEmpty -> not_empty // camelCase to underscore: getNotEmpty -> not_empty
$argName = strtolower(preg_replace('/([^A-Z])([A-Z])/', "$1_$2", substr($name, 3))); $argName = strtolower(preg_replace('/([^A-Z])([A-Z])/', "$1_$2", substr($name, 3)));
return $this->getArg($argName)->getValue(); return $this->getArgValue($argName);
} }
throw new \InvalidArgumentException(sprintf("Unsupported magic method %s. only getArgname() is supported.", $name)); throw new \InvalidArgumentException(
$this->translator->trans("Unsupported magic method %name. only getArgname() is supported.", ['%name' => $name])
);
} }
/** /**
@@ -148,12 +158,12 @@ abstract class BaseLoop
* *
* @param array $nameValuePairs a array of name => value pairs. The name is the name of the argument. * @param array $nameValuePairs a array of name => value pairs. The name is the name of the argument.
* *
* @throws \InvalidArgumentException if somùe argument values are missing, or invalid * @throws \InvalidArgumentException if some argument values are missing, or invalid
*/ */
public function initializeArgs(array $nameValuePairs) public function initializeArgs(array $nameValuePairs)
{ {
$faultActor = array(); $faultActor = [];
$faultDetails = array(); $faultDetails = [];
$loopType = isset($nameValuePairs['type']) ? $nameValuePairs['type'] : "undefined"; $loopType = isset($nameValuePairs['type']) ? $nameValuePairs['type'] : "undefined";
$loopName = isset($nameValuePairs['name']) ? $nameValuePairs['name'] : "undefined"; $loopName = isset($nameValuePairs['name']) ? $nameValuePairs['name'] : "undefined";
@@ -167,19 +177,38 @@ abstract class BaseLoop
/* check if mandatory */ /* check if mandatory */
if ($value === null && $argument->mandatory) { if ($value === null && $argument->mandatory) {
$faultActor[] = $argument->name; $faultActor[] = $argument->name;
$faultDetails[] = sprintf('"%s" parameter is missing in loop type: %s, name: %s', $argument->name, $loopType, $loopName); $faultDetails[] = $this->translator->trans(
'"%param" parameter is missing in loop type: %type, name: %name', [
'%param' => $argument->name,
'%type' => $loopType,
'%name' => $loopName
]
);
} else if ($value === '') { } else if ($value === '') {
if (!$argument->empty) { if (!$argument->empty) {
/* check if empty */ /* check if empty */
$faultActor[] = $argument->name; $faultActor[] = $argument->name;
$faultDetails[] = sprintf('"%s" parameter cannot be empty in loop type: %s, name: %s', $argument->name, $loopType, $loopName); $faultDetails[] = $this->translator->trans(
'"%param" parameter cannot be empty in loop type: %type, name: %name', [
'%param' => $argument->name,
'%type' => $loopType,
'%name' => $loopName
]
);
} }
} elseif ($value !== null && !$argument->type->isValid($value)) { } elseif ($value !== null && !$argument->type->isValid($value)) {
/* check type */ /* check type */
$faultActor[] = $argument->name; $faultActor[] = $argument->name;
$faultDetails[] = sprintf('Invalid value "%s" for "%s" argument in loop type: %s, name: %s', $value, $argument->name, $loopType, $loopName); $faultDetails[] = $this->translator->trans(
'Invalid value "%value" for "%param" parameter in loop type: %type, name: %name', [
'%value' => $value,
'%param' => $argument->name,
'%type' => $loopType,
'%name' => $loopName
]
);
} else { } else {
/* set default */ /* set default value */
/* did it as last checking for we consider default value is acceptable no matter type or empty restriction */ /* did it as last checking for we consider default value is acceptable no matter type or empty restriction */
if ($value === null) { if ($value === null) {
$value = $argument->default; $value = $argument->default;
@@ -189,9 +218,9 @@ abstract class BaseLoop
} }
} }
if (!empty($faultActor)) { if (! empty($faultActor)) {
$complement = sprintf('[%s]', implode(', ', $faultDetails)); $complement = sprintf('[%s]', implode(', ', $faultDetails));
throw new \InvalidArgumentException($complement); throw new \InvalidArgumentException($complement);
} }
} }
@@ -201,15 +230,17 @@ abstract class BaseLoop
* *
* @param string $argumentName the argument name * @param string $argumentName the argument name
* *
* @return Argument the loop argument.
* @throws \InvalidArgumentException if argument is not found in loop argument list * @throws \InvalidArgumentException if argument is not found in loop argument list
* @return Argument the loop argument.
*/ */
protected function getArg($argumentName) protected function getArg($argumentName)
{ {
$arg = $this->args->get($argumentName); $arg = $this->args->get($argumentName);
if ($arg === null) if ($arg === null)
throw new \InvalidArgumentException("Undefined loop argument '$argumentName'"); throw new \InvalidArgumentException(
$this->translator->trans('Undefined loop argument "%name"', ['%name' => $argumentName])
);
return $arg; return $arg;
} }
@@ -220,7 +251,7 @@ abstract class BaseLoop
* @param string $argumentName the argument name * @param string $argumentName the argument name
* *
* @throws \InvalidArgumentException if argument is not found in loop argument list * @throws \InvalidArgumentException if argument is not found in loop argument list
* @return Argument the loop argument. * @return mixed the loop argument value
*/ */
protected function getArgValue($argumentName) protected function getArgValue($argumentName)
{ {
@@ -228,20 +259,24 @@ abstract class BaseLoop
} }
/** /**
* @param ModelCriteria $search * @param ModelCriteria $search the search request
* @param null $pagination * @param PropelModelPager $pagination the pagination part
* *
* @return array|mixed|\PropelModelPager|\PropelObjectCollection * @return array|PropelModelPager|ObjectCollection
* @throws \InvalidArgumentException if the search mode is undefined.
*/ */
protected function search(ModelCriteria $search, &$pagination = null) protected function search(ModelCriteria $search, &$pagination = null)
{ {
if (false === $this->countable) { if (false === $this->countable) {
return $search->find(); return $search->find();
} }
if ($this instanceof SearchLoopInterface) { if ($this instanceof SearchLoopInterface) {
$searchTerm = $this->getSearch_term();
$searchIn = $this->getSearch_in(); $searchTerm = $this->getArgValue('search_term');
$searchMode = $this->getSearch_mode(); $searchIn = $this->getArgValue('search_in');
$searchMode = $this->getArgValue('search_mode');
if (null !== $searchTerm && null !== $searchIn) { if (null !== $searchTerm && null !== $searchIn) {
switch ($searchMode) { switch ($searchMode) {
@@ -256,6 +291,10 @@ abstract class BaseLoop
case SearchLoopInterface::MODE_STRICT_SENTENCE: case SearchLoopInterface::MODE_STRICT_SENTENCE:
$searchCriteria = Criteria::EQUAL; $searchCriteria = Criteria::EQUAL;
break; break;
default:
throw new \InvalidArgumentException(
$this->translator->trans("Undefined search mode '%mode'", ['%mode' => $searchMode])
);
} }
$this->doSearch($search, $searchTerm, $searchIn, $searchCriteria); $this->doSearch($search, $searchTerm, $searchIn, $searchCriteria);
@@ -269,24 +308,31 @@ abstract class BaseLoop
} }
} }
protected function searchArray(array $search, &$pagination = null) protected function searchArray(array $search)
{ {
if (false === $this->countable) { if (false === $this->countable) {
return $search; return $search;
} }
$limit = intval($this->getArgValue('limit'));
$offset = intval($this->getArgValue('offset'));
if ($this->getArgValue('page') !== null) { if ($this->getArgValue('page') !== null) {
$nbPage = ceil(count($search)/$this->getArgValue('limit')); $pageNum = intval($this->getArgValue('page'));
if ($this->getArgValue('page') > $nbPage || $this->getArgValue('page') <= 0) {
return array(); $totalPageCount = ceil(count($search) / $limit);
if ($pageNum > $totalPageCount || $pageNum <= 0) {
return [];
} }
$firstItem = ($this->getArgValue('page')-1) * $this->getArgValue('limit') + 1; $firstItem = ($pageNum - 1) * $limit + 1;
return array_slice($search, $firstItem, $firstItem + $this->getArgValue('limit'), false); return array_slice($search, $firstItem, $firstItem + $limit, false);
} else { } else {
return array_slice($search, $this->getArgValue('offset'), $this->getArgValue('limit'), false); return array_slice($search, $offset, $limit, false);
} }
} }
@@ -294,30 +340,36 @@ abstract class BaseLoop
/** /**
* @param ModelCriteria $search * @param ModelCriteria $search
* *
* @return array|mixed|\PropelObjectCollection * @return ObjectCollection
*/ */
protected function searchWithOffset(ModelCriteria $search) protected function searchWithOffset(ModelCriteria $search)
{ {
if ($this->getArgValue('limit') >= 0) { $limit = intval($this->getArgValue('limit'));
$search->limit($this->getArgValue('limit'));
if ($limit >= 0) {
$search->limit($limit);
} }
$search->offset($this->getArgValue('offset'));
$search->offset(intval($this->getArgValue('offset')));
return $search->find(); return $search->find();
} }
/** /**
* @param ModelCriteria $search * @param ModelCriteria $search
* @param $pagination * @param PropelModelPager $pagination
* *
* @return array|\Propel\Runtime\Util\PropelModelPager * @return array|PropelModelPager
*/ */
protected function searchWithPagination(ModelCriteria $search, &$pagination) protected function searchWithPagination(ModelCriteria $search, &$pagination)
{ {
$pagination = $search->paginate($this->getArgValue('page'), $this->getArgValue('limit')); $page = intval($this->getArgValue('page'));
$limit = intval($this->getArgValue('limit'));
if ($this->getArgValue('page') > $pagination->getLastPage()) { $pagination = $search->paginate($page, $limit);
return array();
if ($page > $pagination->getLastPage()) {
return [];
} else { } else {
return $pagination; return $pagination;
} }
@@ -350,18 +402,21 @@ abstract class BaseLoop
} }
/** /**
* @param $pagination * @param PropelModelPager $pagination
*
* @return LoopResult * @return LoopResult
*/ */
public function exec(&$pagination) public function exec(&$pagination)
{ {
$hash = $this->args->getHash(); $hash = $this->args->getHash();
if (false === isset(self::$cacheLoopResult[$hash])) { if (false === isset(self::$cacheLoopResult[$hash])) {
$results = [];
if ($this instanceof PropelSearchLoopInterface) { if ($this instanceof PropelSearchLoopInterface) {
$searchModelCriteria = $this->buildModelCriteria(); $searchModelCriteria = $this->buildModelCriteria();
if (null === $searchModelCriteria) {
$results = array(); if (null !== $searchModelCriteria) {
} else {
$results = $this->search( $results = $this->search(
$searchModelCriteria, $searchModelCriteria,
$pagination $pagination
@@ -369,13 +424,9 @@ abstract class BaseLoop
} }
} elseif ($this instanceof ArraySearchLoopInterface) { } elseif ($this instanceof ArraySearchLoopInterface) {
$searchArray = $this->buildArray(); $searchArray = $this->buildArray();
if (null === $searchArray) {
$results = array(); if (null !== $searchArray) {
} else { $results = $this->searchArray($searchArray);
$results = $this->searchArray(
$searchArray,
$pagination
);
} }
} }
@@ -405,30 +456,49 @@ abstract class BaseLoop
* - ArraySearchLoopInterface * - ArraySearchLoopInterface
*/ */
$searchInterface = false; $searchInterface = false;
if ($this instanceof PropelSearchLoopInterface) { if ($this instanceof PropelSearchLoopInterface) {
if (true === $searchInterface) { if (true === $searchInterface) {
throw new LoopException('Loop cannot implements multiple Search Interfaces : `PropelSearchLoopInterface`, `ArraySearchLoopInterface`', LoopException::MULTIPLE_SEARCH_INTERFACE); throw new LoopException(
$this->translator->trans(
'Loop cannot implements multiple Search Interfaces : `PropelSearchLoopInterface`, `ArraySearchLoopInterface`'
),
LoopException::MULTIPLE_SEARCH_INTERFACE);
} }
$searchInterface = true; $searchInterface = true;
} }
if ($this instanceof ArraySearchLoopInterface) { if ($this instanceof ArraySearchLoopInterface) {
if (true === $searchInterface) { if (true === $searchInterface) {
throw new LoopException('Loop cannot implements multiple Search Interfaces : `PropelSearchLoopInterface`, `ArraySearchLoopInterface`', LoopException::MULTIPLE_SEARCH_INTERFACE); throw new LoopException(
$this->translator->trans(
'Loop cannot implements multiple Search Interfaces : `PropelSearchLoopInterface`, `ArraySearchLoopInterface`'
),
LoopException::MULTIPLE_SEARCH_INTERFACE);
} }
$searchInterface = true; $searchInterface = true;
} }
if (false === $searchInterface) { if (false === $searchInterface) {
throw new LoopException('Loop must implements one of the following interfaces : `PropelSearchLoopInterface`, `ArraySearchLoopInterface`', LoopException::SEARCH_INTERFACE_NOT_FOUND); throw new LoopException(
$this->translator->trans(
'Loop must implements one of the following interfaces : `PropelSearchLoopInterface`, `ArraySearchLoopInterface`'
),
LoopException::SEARCH_INTERFACE_NOT_FOUND);
} }
/* Only PropelSearch allows timestamp and version */ /* Only PropelSearch allows timestamp and version */
if (!$this instanceof PropelSearchLoopInterface) { if (!$this instanceof PropelSearchLoopInterface) {
if (true === $this->timestampable) { if (true === $this->timestampable) {
throw new LoopException("Loop must implements 'PropelSearchLoopInterface' to be timestampable", LoopException::NOT_TIMESTAMPED); throw new LoopException(
$this->translator->trans("Loop must implements 'PropelSearchLoopInterface' to be timestampable"),
LoopException::NOT_TIMESTAMPED);
} }
if (true === $this->versionable) { if (true === $this->versionable) {
throw new LoopException("Loop must implements 'PropelSearchLoopInterface' to be versionable", LoopException::NOT_VERSIONED); throw new LoopException(
$this->translator->trans("Loop must implements 'PropelSearchLoopInterface' to be versionable"),
LoopException::NOT_VERSIONED);
} }
} }
} }
@@ -441,15 +511,14 @@ abstract class BaseLoop
abstract public function parseResults(LoopResult $loopResult); abstract public function parseResults(LoopResult $loopResult);
/** /**
* * Definition of loop arguments
* define all args used in your loop
*
* *
* example : * example :
* *
* public function getArgDefinitions() * public function getArgDefinitions()
* { * {
* return new ArgumentCollection( * return new ArgumentCollection(
*
* Argument::createIntListTypeArgument('id'), * Argument::createIntListTypeArgument('id'),
* new Argument( * new Argument(
* 'ref', * 'ref',
@@ -459,14 +528,7 @@ abstract class BaseLoop
* ), * ),
* Argument::createIntListTypeArgument('category'), * Argument::createIntListTypeArgument('category'),
* Argument::createBooleanTypeArgument('new'), * Argument::createBooleanTypeArgument('new'),
* Argument::createBooleanTypeArgument('promo'), * ...
* Argument::createFloatTypeArgument('min_price'),
* Argument::createFloatTypeArgument('max_price'),
* Argument::createIntTypeArgument('min_stock'),
* Argument::createFloatTypeArgument('min_weight'),
* Argument::createFloatTypeArgument('max_weight'),
* Argument::createBooleanTypeArgument('current'),
*
* ); * );
* } * }
* *
@@ -474,4 +536,4 @@ abstract class BaseLoop
*/ */
abstract protected function getArgDefinitions(); abstract protected function getArgDefinitions();
} }

View File

@@ -12,16 +12,20 @@
namespace Thelia\Core\Template\Smarty\Plugins; namespace Thelia\Core\Template\Smarty\Plugins;
use Propel\Runtime\Util\PropelModelPager;
use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\ContainerInterface;
use Thelia\Core\Template\Element\BaseLoop; use Thelia\Core\Template\Element\BaseLoop;
use Thelia\Core\Template\Element\LoopResult;
use Thelia\Core\Template\Smarty\AbstractSmartyPlugin; use Thelia\Core\Template\Smarty\AbstractSmartyPlugin;
use Thelia\Core\Template\Smarty\SmartyPluginDescriptor; use Thelia\Core\Template\Smarty\SmartyPluginDescriptor;
use Thelia\Core\Template\Element\Exception\ElementNotFoundException; use Thelia\Core\Template\Element\Exception\ElementNotFoundException;
use Thelia\Core\Template\Element\Exception\InvalidElementException; use Thelia\Core\Template\Element\Exception\InvalidElementException;
use Thelia\Core\Translation\Translator;
class TheliaLoop extends AbstractSmartyPlugin class TheliaLoop extends AbstractSmartyPlugin
{ {
/** @var PropelModelPager[] */
protected static $pagination = null; protected static $pagination = null;
protected $loopDefinition = array(); protected $loopDefinition = array();
@@ -30,10 +34,15 @@ class TheliaLoop extends AbstractSmartyPlugin
protected $dispatcher; protected $dispatcher;
protected $securityContext; protected $securityContext;
/** @var Translator */
protected $translator;
/** @var ContainerInterface Service Container */ /** @var ContainerInterface Service Container */
protected $container = null; protected $container = null;
/** @var LoopResult[] */
protected $loopstack = array(); protected $loopstack = array();
protected $varstack = array(); protected $varstack = array();
/** /**
@@ -46,31 +55,43 @@ class TheliaLoop extends AbstractSmartyPlugin
$this->request = $container->get('request'); $this->request = $container->get('request');
$this->dispatcher = $container->get('event_dispatcher'); $this->dispatcher = $container->get('event_dispatcher');
$this->securityContext = $container->get('thelia.securityContext'); $this->securityContext = $container->get('thelia.securityContext');
$this->translator = $container->get("thelia.translator");
} }
/** /**
* @param $loopId * @param string $loopName
* * @return PropelModelPager
* @return \PropelModelPager * @throws \InvalidArgumentException if no pagination was found for loop
*/ */
public static function getPagination($loopName) public static function getPagination($loopName)
{ {
if (array_key_exists($loopName, self::$pagination)) { if (array_key_exists($loopName, self::$pagination)) {
return self::$pagination[$loopName]; return self::$pagination[$loopName];
} else { } else {
throw new \InvalidArgumentException("Loop $loopName is not defined"); throw new \InvalidArgumentException(
Translator::getInstance()->trans("No pagination currently defined for loop name '%name'", ['%name' => $loopName ])
);
} }
} }
/** /**
* Process the count function: executes a loop and return the number of items found * Process the count function: executes a loop and return the number of items found
*
* @param array $params parameters array
* @param \Smarty_Internal_Template $template
*
* @return int the item count
* @throws \InvalidArgumentException if a parameter is missing
*
*/ */
public function theliaCount($params, $template) public function theliaCount($params, /** @noinspection PhpUnusedParameterInspection */ $template)
{ {
$type = $this->getParam($params, 'type'); $type = $this->getParam($params, 'type');
if (null == $type) { if (null == $type) {
throw new \InvalidArgumentException("Missing 'type' parameter in count arguments"); throw new \InvalidArgumentException(
$this->translator->trans("Missing 'type' parameter in {count} loop arguments")
);
} }
$loop = $this->createLoopInstance($params); $loop = $this->createLoopInstance($params);
@@ -83,31 +104,39 @@ class TheliaLoop extends AbstractSmartyPlugin
/** /**
* Process {loop name="loop name" type="loop type" ... } ... {/loop} block * Process {loop name="loop name" type="loop type" ... } ... {/loop} block
* *
* @param unknown $params * @param array $params
* @param unknown $content * @param string $content
* @param unknown $template * @param \Smarty_Internal_Template $template
* @param unknown $repeat * @param boolean $repeat
*
* @throws \InvalidArgumentException * @throws \InvalidArgumentException
* @return string *
* @return void|string
*/ */
public function theliaLoop($params, $content, $template, &$repeat) public function theliaLoop($params, $content, $template, &$repeat)
{ {
$name = $this->getParam($params, 'name'); $name = $this->getParam($params, 'name');
if (null == $name) { if (null == $name) {
throw new \InvalidArgumentException("Missing 'name' parameter in loop arguments"); throw new \InvalidArgumentException(
$this->translator->trans("Missing 'name' parameter in loop arguments")
);
} }
$type = $this->getParam($params, 'type'); $type = $this->getParam($params, 'type');
if (null == $type) { if (null == $type) {
throw new \InvalidArgumentException("Missing 'type' parameter in loop arguments"); throw new \InvalidArgumentException(
$this->translator->trans("Missing 'type' parameter in loop arguments")
);
} }
if ($content === null) { if ($content === null) {
// Check if a loop with the same name exists in the current scope, and abort if it's the case. // Check if a loop with the same name exists in the current scope, and abort if it's the case.
if (array_key_exists($name, $this->varstack)) { if (array_key_exists($name, $this->varstack)) {
throw new \InvalidArgumentException("A loop named '$name' already exists in the current scope."); throw new \InvalidArgumentException(
$this->translator->trans("A loop named '%name' already exists in the current scope.", ['%name' => $name])
);
} }
$loop = $this->createLoopInstance($params); $loop = $this->createLoopInstance($params);
@@ -120,7 +149,7 @@ class TheliaLoop extends AbstractSmartyPlugin
$this->loopstack[$name] = $loopResults; $this->loopstack[$name] = $loopResults;
// Pas de résultat ? la boucle est terminée, ne pas évaluer le contenu. // No results ? The loop is terminated, do not evaluate loop text.
if ($loopResults->isEmpty()) $repeat = false; if ($loopResults->isEmpty()) $repeat = false;
} else { } else {
@@ -173,52 +202,56 @@ class TheliaLoop extends AbstractSmartyPlugin
return $content; return $content;
} }
return '';
} }
/** /**
* Process {elseloop rel="loopname"} ... {/elseloop} block * Process {elseloop rel="loopname"} ... {/elseloop} block
* *
* @param unknown $params * @param array $params loop parameters
* @param unknown $content * @param string $content loop text content
* @param unknown $template * @param \Smarty_Internal_Template $template the Smarty object
* @param unknown $repeat * @param boolean $repeat repeat indicator (see Smarty doc.)
* @return Ambigous <string, unknown> * @return string the loop output
*/ */
public function theliaElseloop($params, $content, $template, &$repeat) public function theliaElseloop($params, $content, /** @noinspection PhpUnusedParameterInspection */ $template, &$repeat)
{ {
// When encountering close tag, check if loop has results.
// When encoutering close tag, check if loop has results.
if ($repeat === false) { if ($repeat === false) {
return $this->checkEmptyLoop($params, $template) ? $content : ''; return $this->checkEmptyLoop($params) ? $content : '';
} }
return '';
} }
/** /**
* Process {ifloop rel="loopname"} ... {/ifloop} block * Process {ifloop rel="loopname"} ... {/ifloop} block
* *
* @param unknown $params * @param array $params loop parameters
* @param unknown $content * @param string $content loop text content
* @param unknown $template * @param \Smarty_Internal_Template $template the Smarty object
* @param unknown $repeat * @param boolean $repeat repeat indicator (see Smarty doc.)
* @return Ambigous <string, unknown> * @return string the loop output
*/ */
public function theliaIfLoop($params, $content, $template, &$repeat) public function theliaIfLoop($params, $content, /** @noinspection PhpUnusedParameterInspection */ $template, &$repeat)
{ {
// When encountering close tag, check if loop has results. // When encountering close tag, check if loop has results.
if ($repeat === false) { if ($repeat === false) {
return $this->checkEmptyLoop($params, $template) ? '' : $content; return $this->checkEmptyLoop($params) ? '' : $content;
} }
return '';
} }
/** /**
* Process {pageloop rel="loopname"} ... {/pageloop} block * Process {pageloop rel="loopname"} ... {/pageloop} block
* *
* @param $params * @param array $params loop parameters
* @param $content * @param string $content loop text content
* @param $template * @param \Smarty_Internal_Template $template the Smarty object
* @param $repeat * @param boolean $repeat repeat indicator (see Smarty doc.)
* * @return string the loop output
* @return string
* @throws \InvalidArgumentException * @throws \InvalidArgumentException
*/ */
public function theliaPageLoop($params, $content, $template, &$repeat) public function theliaPageLoop($params, $content, $template, &$repeat)
@@ -226,73 +259,102 @@ class TheliaLoop extends AbstractSmartyPlugin
$loopName = $this->getParam($params, 'rel'); $loopName = $this->getParam($params, 'rel');
if (null == $loopName) if (null == $loopName)
throw new \InvalidArgumentException("Missing 'rel' parameter in page loop"); throw new \InvalidArgumentException($this->translator->trans("Missing 'rel' parameter in page loop"));
// Find pagination // Find pagination
$pagination = self::getPagination($loopName); $pagination = self::getPagination($loopName);
if ($pagination === null) { // loop has no result
if ($pagination === null || $pagination->getNbResults() == 0) {
// No need to paginate
return ''; return '';
} }
if ($pagination->getNbResults() == 0) { $startPage = intval($this->getParam($params, 'start-page', 1));
return ''; $displayedPageCount = intval($this->getParam($params, 'limit', 10));
}
$nbPage = $this->getParam($params, 'numPage', 10); if (intval($displayedPageCount) == 0) $displayedPageCount = PHP_INT_MAX;
$maxPage = $pagination->getLastPage();
$totalPageCount = $pagination->getLastPage();
if ($content === null) { if ($content === null) {
$page = $pagination->getPage();
if ($maxPage > ($page + $nbPage)) { // The current page
$end = $page + $nbPage; $currentPage = $pagination->getPage();
} else {
$end = $maxPage; // Get the start page.
if ($totalPageCount > $displayedPageCount) {
$startPage = $currentPage - round($displayedPageCount / 2);
if ($startPage < 0) $startPage = 1;
} }
$template->assign('PREV', $page > 1 ? $page-1: $page);
$template->assign('NEXT', $page < $maxPage ? $page+1 : $maxPage); // This is the iterative page number, the one we're going to increment in this loop
$template->assign('END', $end); $iterationPage = $startPage;
$template->assign('LAST', $pagination->getLastPage());
// The last displayed page number
$endPage = $startPage + $displayedPageCount - 1;
if ($endPage > $totalPageCount) {
$endPage = $totalPageCount;
}
// The first displayed page number
$template->assign('START' , $startPage);
// The previous page number
$template->assign('PREV' , $currentPage > 1 ? $currentPage-1 : $currentPage);
// The next page number
$template->assign('NEXT' , $currentPage < $totalPageCount ? $currentPage+1 : $totalPageCount);
// The last displayed page number
$template->assign('END' , $endPage);
// The overall last page
$template->assign('LAST' , $totalPageCount);
} else { } else {
$page = $template->getTemplateVars('PAGE'); $iterationPage = $template->getTemplateVars('PAGE');
$page++;
$iterationPage++;
} }
if ($iterationPage <= $template->getTemplateVars('END')) {
// The iterative page number
$template->assign('PAGE', $iterationPage);
if ($page <= $template->getTemplateVars('END')) { // The overall current page number
$template->assign('PAGE', $page);
$template->assign('CURRENT', $pagination->getPage()); $template->assign('CURRENT', $pagination->getPage());
$repeat = true; $repeat = true;
} }
if ($content !== null) { if ($content !== null) {
return $content; return $content;
} }
return '';
} }
/** /**
* Check if a loop has returned results. The loop shoud have been executed before, or an * Check if a loop has returned results. The loop shoud have been executed before, or an
* InvalidArgumentException is thrown * InvalidArgumentException is thrown
* *
* @param unknown $params * @param array $params
* @param unknown $template *
* @return boolean true if the loop is empty
* @throws \InvalidArgumentException * @throws \InvalidArgumentException
*/ */
protected function checkEmptyLoop($params, $template) protected function checkEmptyLoop($params)
{ {
$loopName = $this->getParam($params, 'rel'); $loopName = $this->getParam($params, 'rel');
if (null == $loopName) if (null == $loopName)
throw new \InvalidArgumentException("Missing 'rel' parameter in ifloop/elseloop arguments"); throw new \InvalidArgumentException(
$this->translator->trans("Missing 'rel' parameter in ifloop/elseloop arguments")
);
if (! isset($this->loopstack[$loopName])) { if (! isset($this->loopstack[$loopName]))
throw new \InvalidArgumentException("Loop $loopName is not defined."); throw new \InvalidArgumentException(
} $this->translator->trans("Related loop name '%name'' is not defined.", ['%name' => $loopName])
);
return $this->loopstack[$loopName]->isEmpty(); return $this->loopstack[$loopName]->isEmpty();
} }
@@ -309,14 +371,16 @@ class TheliaLoop extends AbstractSmartyPlugin
$type = strtolower($smartyParams['type']); $type = strtolower($smartyParams['type']);
if (! isset($this->loopDefinition[$type])) { if (! isset($this->loopDefinition[$type])) {
throw new ElementNotFoundException(sprintf("'%s' loop type does not exists", $type)); throw new ElementNotFoundException(
$this->translator->trans("Loop type '%type' is not defined.", ['%type' => $type]));
} }
$class = new \ReflectionClass($this->loopDefinition[$type]); $class = new \ReflectionClass($this->loopDefinition[$type]);
if ($class->isSubclassOf("Thelia\Core\Template\Element\BaseLoop") === false) { if ($class->isSubclassOf("Thelia\Core\Template\Element\BaseLoop") === false) {
throw new InvalidElementException(sprintf("'%s' Loop class should extends Thelia\Core\Template\Element\BaseLoop", throw new InvalidElementException(
$type)); $this->translator->trans("'%type' loop class should extends Thelia\Core\Template\Element\BaseLoop", ['%type' => $type])
);
} }
$loop = $class->newInstance( $loop = $class->newInstance(
@@ -350,7 +414,12 @@ class TheliaLoop extends AbstractSmartyPlugin
{ {
foreach ($loopDefinition as $name => $className) { foreach ($loopDefinition as $name => $className) {
if (array_key_exists($name, $this->loopDefinition)) { if (array_key_exists($name, $this->loopDefinition)) {
throw new \InvalidArgumentException(sprintf("%s loop name already exists for %s class name", $name, $className)); throw new \InvalidArgumentException(
$this->translator->trans("The loop name '%name' is already defined in %className class", [
'%name' => $name,
'%className' => $className
])
);
} }
$this->loopDefinition[$name] = $className; $this->loopDefinition[$name] = $className;
@@ -360,7 +429,7 @@ class TheliaLoop extends AbstractSmartyPlugin
/** /**
* Defines the various smarty plugins hendled by this class * Defines the various smarty plugins hendled by this class
* *
* @return an array of smarty plugin descriptors * @return SmartyPluginDescriptor[] smarty plugin descriptors
*/ */
public function getPluginDescriptors() public function getPluginDescriptors()
{ {

View File

@@ -1,5 +1,6 @@
#order #order
max_displayed_orders = 20 max_displayed_orders = 20
max_displayed_customers = 20
#order status #order status
order_not_paid = 'warning' order_not_paid = 'warning'

View File

@@ -1,390 +1,368 @@
{extends file="admin-layout.tpl"} {extends file="admin-layout.tpl"}
{block name="page-title"}{intl l='Customer'}{/block} {block name="page-title"}{intl l='Customer'}{/block}
{block name="check-resource"}admin.customer{/block} {block name="check-resource"}admin.customer{/block}
{block name="check-access"}view{/block} {block name="check-access"}view{/block}
{block name="main-content"} {block name="main-content"}
<div class="customer"> <div class="customer">
<div id="wrapper" class="container"> <div id="wrapper" class="container">
<ul class="breadcrumb"> <ul class="breadcrumb">
<li><a href="{url path='/admin/home'}">{intl l="Home"}</a></li> <li><a href="{url path='/admin/home'}">{intl l="Home"}</a></li>
<li><a href="{url path='/admin/customers'}">{intl l="Customers"}</a></li> <li><a href="{url path='/admin/customers'}">{intl l="Customers"}</a></li>
</ul> </ul>
{module_include location='customer_top'} {module_include location='customer_top'}
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
<div class="general-block-decorator"> <div class="general-block-decorator">
{if $removal_error } {if $removal_error }
<div class="alert alert-danger fade in"> <div class="alert alert-danger fade in">
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button> <button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>
<h4 class="modal-title">{$error_message}</h4> <h4 class="modal-title">{$error_message}</h4>
</div> </div>
{/if} {/if}
<div class="table-responsive"> <div class="table-responsive">
<table class="table table-striped table-condensed" id="customer_list"> <table class="table table-striped table-condensed" id="customer_list">
<caption> <caption>
{intl l="Customers list"} {intl l="Customers list"}
{module_include location='customer_list_caption'} {module_include location='customer_list_caption'}
{loop type="auth" name="can_create" role="ADMIN" resource="admin.customer" access="CREATE"} {loop type="auth" name="can_create" role="ADMIN" resource="admin.customer" access="CREATE"}
<a class="btn btn-default btn-primary action-btn" title="{intl l='Add a new Customer'}" href="#add_customer_dialog" data-toggle="modal"> <a class="btn btn-default btn-primary action-btn" title="{intl l='Add a new Customer'}" href="#add_customer_dialog" data-toggle="modal">
<span class="glyphicon glyphicon-plus-sign"></span> <span class="glyphicon glyphicon-plus-sign"></span>
</a> </a>
{/loop} {/loop}
</caption> </caption>
{ifloop rel="customer_list"} {ifloop rel="customer_list"}
<thead> <thead>
<tr> <tr>
<th class="object-title"> <th class="object-title">
{admin_sortable_header {admin_sortable_header
current_order=$customer_order current_order=$customer_order
order='reference' order='reference'
reverse_order='reference_reverse' reverse_order='reference_reverse'
path={url path='/admin/customers'} path={url path='/admin/customers'}
request_parameter_name='customer_order' request_parameter_name='customer_order'
label="{intl l='Reference'}" label="{intl l='Reference'}"
} }
</th> </th>
{module_include location='customer_list_header'} {module_include location='customer_list_header'}
<th class="object-title"> <th class="object-title">
{admin_sortable_header {admin_sortable_header
current_order=$customer_order current_order=$customer_order
order='lastname' order='lastname'
reverse_order='lastname_reverse' reverse_order='lastname_reverse'
path={url path='/admin/customers'} path={url path='/admin/customers'}
request_parameter_name='customer_order' request_parameter_name='customer_order'
label="{intl l='Last name'}" label="{intl l='Last name'}"
} }
</th> </th>
<th class="object-title"> <th class="object-title">
{admin_sortable_header {admin_sortable_header
current_order=$customer_order current_order=$customer_order
order='firstname' order='firstname'
reverse_order='firstname_reverse' reverse_order='firstname_reverse'
path={url path='/admin/customers'} path={url path='/admin/customers'}
request_parameter_name='customer_order' request_parameter_name='customer_order'
label="{intl l='First name'}" label="{intl l='First name'}"
} }
</th> </th>
<th class="object-title"> <th class="object-title">
{admin_sortable_header {admin_sortable_header
current_order=$customer_order current_order=$customer_order
order='registration_date' order='registration_date'
reverse_order='registration_date_reverse' reverse_order='registration_date_reverse'
path={url path='/admin/customers'} path={url path='/admin/customers'}
request_parameter_name='customer_order' request_parameter_name='customer_order'
label="{intl l='Registration date'}" label="{intl l='Registration date'}"
} }
</th> </th>
<th class="object-title">{intl l='Date of last order'}</th> <th class="object-title">{intl l='Date of last order'}</th>
<th class="object-title">{intl l='Last order amount'}</th> <th class="object-title">{intl l='Last order amount'}</th>
{* TODO {* TODO
<th class="object-title"> <th class="object-title">
{admin_sortable_header {admin_sortable_header
current_order=$customer_order current_order=$customer_order
order='last_order' order='last_order'
reverse_order='last_order_reverse' reverse_order='last_order_reverse'
path={url path='/admin/customers'} path={url path='/admin/customers'}
request_parameter_name='customer_order' request_parameter_name='customer_order'
label="{intl l='Date of last order'}" label="{intl l='Date of last order'}"
} }
</th> </th>
<th class="object-title"> <th class="object-title">
{admin_sortable_header {admin_sortable_header
current_order=$customer_order current_order=$customer_order
order='order_amount' order='order_amount'
reverse_order='order_amount_reverse' reverse_order='order_amount_reverse'
path={url path='/admin/customers'} path={url path='/admin/customers'}
request_parameter_name='customer_order' request_parameter_name='customer_order'
label="{intl l='Last order amount'}" label="{intl l='Last order amount'}"
} }
</th> </th>
*} *}
<th class="actions">{intl l='Actions'}</th> <th class="actions">{intl l='Actions'}</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{loop name="customer_list" type="customer" current="false" visible="*" order=$customer_order backend_context="1" page=$page limit=$display_customer} {loop name="customer_list" type="customer" current="false" visible="*" order=$customer_order backend_context="1" page=$page limit=#max_displayed_customers#}
{assign "lastOrderDate" ''} {assign "lastOrderDate" ''}
{assign "lastOrderAmount" ''} {assign "lastOrderAmount" ''}
{assign "lastOrderCurrency" ''} {assign "lastOrderCurrency" ''}
{loop type="order" name="last-order" customer=$ID order="create-date-reverse" limit="1"} {loop type="order" name="last-order" customer=$ID order="create-date-reverse" limit="1"}
{assign "lastOrderDate" "{format_date date=$CREATE_DATE}"} {assign "lastOrderDate" "{format_date date=$CREATE_DATE}"}
{assign "lastOrderAmount" "{format_number number=$TOTAL_TAXED_AMOUNT}"} {assign "lastOrderAmount" "{format_number number=$TOTAL_TAXED_AMOUNT}"}
{loop type="currency" name="order-currency" id=$CURRENCY} {loop type="currency" name="order-currency" id=$CURRENCY}
{assign "lastOrderCurrency" $SYMBOL} {assign "lastOrderCurrency" $SYMBOL}
{/loop} {/loop}
{/loop} {/loop}
<tr> <tr>
<td><a href="{url path="/admin/customer/update" customer_id=$ID page=$page}">{$REF}</a></td> <td><a href="{url path="/admin/customer/update" customer_id=$ID page=$page}">{$REF}</a></td>
<td class="object-title"> <td class="object-title">
{$LASTNAME} {$LASTNAME}
</td> </td>
<td class="object-title"> <td class="object-title">
{$FIRSTNAME} {$FIRSTNAME}
</td> </td>
{module_include location='customer_list_row'} {module_include location='customer_list_row'}
<td> <td>
{format_date date=$CREATE_DATE} {format_date date=$CREATE_DATE}
</td> </td>
<td> <td>
{$lastOrderDate} {$lastOrderDate}
</td> </td>
<td> <td>
{$lastOrderCurrency} {$lastOrderAmount} {$lastOrderCurrency} {$lastOrderAmount}
</td> </td>
<td class="actions"> <td class="actions">
<div class="btn-group"> <div class="btn-group">
{loop type="auth" name="can_change" role="ADMIN" resource="admin.customer" access="UPDATE"} {loop type="auth" name="can_change" role="ADMIN" resource="admin.customer" access="UPDATE"}
<a class="btn btn-default btn-xs" title="{intl l='Edit this customer'}" href="{url path='/admin/customer/update' customer_id=$ID page=$page}"><i class="glyphicon glyphicon-edit"></i></a> <a class="btn btn-default btn-xs" title="{intl l='Edit this customer'}" href="{url path='/admin/customer/update' customer_id=$ID page=$page}"><i class="glyphicon glyphicon-edit"></i></a>
{/loop} {/loop}
{loop type="auth" name="can_send_mail" role="ADMIN" resource="admin.customer" access="VIEW"} {loop type="auth" name="can_send_mail" role="ADMIN" resource="admin.customer" access="VIEW"}
<a class="btn btn-default btn-xs" title="{intl l="Send a mail to this customer"}" href="mailto:{$EMAIL}"><span class="glyphicon glyphicon-envelope"></span></a> <a class="btn btn-default btn-xs" title="{intl l="Send a mail to this customer"}" href="mailto:{$EMAIL}"><span class="glyphicon glyphicon-envelope"></span></a>
{/loop} {/loop}
{loop type="auth" name="can_delete" role="ADMIN" resource="admin.customer" access="DELETE"} {loop type="auth" name="can_delete" role="ADMIN" resource="admin.customer" access="DELETE"}
<a class="btn btn-default btn-xs customer-delete" title="{intl l='Delete this customer and all his orders'}" href="#delete_customer_dialog" data-id="{$ID}" data-toggle="modal"><span class="glyphicon glyphicon-trash"></span></a> <a class="btn btn-default btn-xs customer-delete" title="{intl l='Delete this customer and all his orders'}" href="#delete_customer_dialog" data-id="{$ID}" data-toggle="modal"><span class="glyphicon glyphicon-trash"></span></a>
{/loop} {/loop}
</div> </div>
</td> </td>
</tr> </tr>
{/loop} {/loop}
</tbody> </tbody>
<tfoot> <tfoot>
<tr> <tr>
<td colspan="6"> <td colspan="6">
{include
<div class="text-center"> file = "includes/pagination.html"
<ul class="pagination pagination-centered">
loop_ref = "customer_list"
{if $page != 1} max_page_count = 10
<li><a href="{url path="/admin/customers" page="1"}">&laquo;</a></li> page_url = "{url path="/admin/customers" customer_order=$customer_order}"
{/if} }
{pageloop rel="customer_list" numPage="20"} </td>
{if $PAGE == $CURRENT && $PAGE > 2} </tr>
<li><a href="{url path="/admin/customers" page=$PREV}">&lsaquo;</a></li> </tfoot>
{/if} {/ifloop}
</table>
{if $PAGE != $CURRENT} </div>
<li><a href="{url path="/admin/customers" page="{$PAGE}"}">{$PAGE}</a></li> </div>
</div>
{else} </div>
<li class="active"><a href="#">{$PAGE}</a></li>
{/if}
{module_include location='customer_bottom'}
{if $PAGE == $END && $PAGE < $LAST}
<li><a href="{url path="/admin/customers" page=$NEXT}">&rsaquo;</a></li> {* Adding a new Category *}
{/if}
{/pageloop}
{if $LAST > $CURRENT} {form name="thelia.admin.customer.create"}
<li><a href="{url path="/admin/customers" page="$LAST"}">&raquo;</a></li>
{/if} {* Capture the dialog body, to pass it to the generic dialog *}
</ul> {capture "customer_creation_dialog"}
</div>
{form_hidden_fields form=$form}
</td>
</tr> {form_field form=$form field='success_url'}
</tfoot> {* on success, redirect to the edition page, _ID_ is replaced with the created object ID, see controller *}
{/ifloop} <input type="hidden" name="{$name}" value="{url path='/admin/customer/update' customer_id='_ID_'}" />
</table> {/form_field}
</div>
</div> {form_field form=$form field="password"}
</div> <input type="hidden" name="{$name}" value="random" />
</div> {/form_field}
{form_field form=$form field="password_confirm"}
{module_include location='customer_bottom'} <input type="hidden" name="{$name}" value="random" />
{/form_field}
{* Adding a new Category *}
{form_field form=$form field="agreed"}
<input type="hidden" name="{$name}" value="on" />
{form name="thelia.admin.customer.create"} {/form_field}
{* Capture the dialog body, to pass it to the generic dialog *} {form_field form=$form field='company'}
{capture "customer_creation_dialog"} <div class="form-group {if $error}has-error{/if}">
<label for="{$label_attr.for}" class="control-label">{$label} : </label>
{form_hidden_fields form=$form} <input type="text" id="{$label_attr.for}" name="{$name}" class="form-control" value="{$value}" title="{$label}" placeholder="{intl l='Company'}">
</div>
{form_field form=$form field='success_url'} {/form_field}
{* 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/customer/update' customer_id='_ID_'}" /> {form_field form=$form field='title'}
{/form_field} <div class="form-group {if $error}has-error{/if}">
<label for="{$label_attr.for}" class="control-label">{$label} : </label>
{form_field form=$form field="password"}
<input type="hidden" name="{$name}" value="random" /> <select name="{$name}" id="{$label_attr.for}" class="form-control">
{/form_field} {loop type="title" name="title1"}
<option value="{$ID}">{$LONG}</option>
{form_field form=$form field="password_confirm"} {/loop}
<input type="hidden" name="{$name}" value="random" /> </select>
{/form_field} </div>
{/form_field}
{form_field form=$form field="agreed"}
<input type="hidden" name="{$name}" value="on" /> {form_field form=$form field='firstname'}
{/form_field} <div class="form-group {if $error}has-error{/if}">
<label for="{$label_attr.for}" class="control-label">{$label} : </label>
{form_field form=$form field='company'} <input type="text" id="{$label_attr.for}" name="{$name}" class="form-control" value="{$value}" title="{$label}" placeholder="{intl l='Firstname'}">
<div class="form-group {if $error}has-error{/if}"> </div>
<label for="{$label_attr.for}" class="control-label">{$label} : </label> {/form_field}
<input type="text" id="{$label_attr.for}" name="{$name}" class="form-control" value="{$value}" title="{$label}" placeholder="{intl l='Company'}">
</div> {form_field form=$form field='lastname'}
{/form_field} <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'} <input type="text" id="{$label_attr.for}" name="{$name}" class="form-control" value="{$value}" title="{$label}" placeholder="{intl l='Lastname'}">
<div class="form-group {if $error}has-error{/if}"> </div>
<label for="{$label_attr.for}" class="control-label">{$label} : </label> {/form_field}
<select name="{$name}" id="{$label_attr.for}" class="form-control"> {form_field form=$form field='address1'}
{loop type="title" name="title1"} <div class="form-group {if $error}has-error{/if}">
<option value="{$ID}">{$LONG}</option> <label for="{$label_attr.for}" class="control-label">{$label} : </label>
{/loop} <input type="text" id="{$label_attr.for}" name="{$name}" class="form-control" value="{$value}" title="{$label}" placeholder="{intl l='Address'}">
</select> </div>
</div>
{/form_field} <div class="form-group">
{form_field form=$form field='address2'}
{form_field form=$form field='firstname'} <input type="text" id="{$label_attr.for}" name="{$name}" class="form-control" value="{$value}" title="{$label}" placeholder="{intl l='Additional address'}">
<div class="form-group {if $error}has-error{/if}"> {/form_field}
<label for="{$label_attr.for}" class="control-label">{$label} : </label> </div>
<input type="text" id="{$label_attr.for}" name="{$name}" class="form-control" value="{$value}" title="{$label}" placeholder="{intl l='Firstname'}">
</div> <div class="form-group">
{/form_field} {form_field form=$form field='address3'}
<input type="text" id="{$label_attr.for}" name="{$name}" class="form-control" value="{$value}" title="{$label}" placeholder="{intl l='Additional address'}">
{form_field form=$form field='lastname'} {/form_field}
<div class="form-group {if $error}has-error{/if}"> </div>
<label for="{$label_attr.for}" class="control-label">{$label} : </label> {/form_field}
<input type="text" id="{$label_attr.for}" name="{$name}" class="form-control" value="{$value}" title="{$label}" placeholder="{intl l='Lastname'}">
</div> {form_field form=$form field='zipcode'}
{/form_field} <div class="form-group {if $error}has-error{/if}">
<label for="{$label_attr.for}" class="control-label">{$label} : </label>
{form_field form=$form field='address1'} <input type="text" id="{$label_attr.for}" name="{$name}" class="form-control" value="{$value}" title="{$label}" placeholder="{intl l='Zip code'}">
<div class="form-group {if $error}has-error{/if}"> </div>
<label for="{$label_attr.for}" class="control-label">{$label} : </label> {/form_field}
<input type="text" id="{$label_attr.for}" name="{$name}" class="form-control" value="{$value}" title="{$label}" placeholder="{intl l='Address'}">
</div> {form_field form=$form field='city'}
<div class="form-group {if $error}has-error{/if}">
<div class="form-group"> <label for="{$label_attr.for}" class="control-label">{$label} : </label>
{form_field form=$form field='address2'} <input type="text" id="{$label_attr.for}" name="{$name}" class="form-control" value="{$value}" title="{$label}" placeholder="{intl l='City'}">
<input type="text" id="{$label_attr.for}" name="{$name}" class="form-control" value="{$value}" title="{$label}" placeholder="{intl l='Additional address'}"> </div>
{/form_field} {/form_field}
</div>
{form_field form=$form field='country'}
<div class="form-group"> <div class="form-group {if $error}has-error{/if}">
{form_field form=$form field='address3'} <label for="{$label_attr.for}" class="control-label">{$label} : </label>
<input type="text" id="{$label_attr.for}" name="{$name}" class="form-control" value="{$value}" title="{$label}" placeholder="{intl l='Additional address'}"> <select name="{$name}" id="{$label_attr.for}" class="form-control">
{/form_field} {loop type="country" name="country1"}
</div> <option value="{$ID}">{$TITLE}</option>
{/form_field} {/loop}
</select>
{form_field form=$form field='zipcode'} </div>
<div class="form-group {if $error}has-error{/if}"> {/form_field}
<label for="{$label_attr.for}" class="control-label">{$label} : </label>
<input type="text" id="{$label_attr.for}" name="{$name}" class="form-control" value="{$value}" title="{$label}" placeholder="{intl l='Zip code'}"> {form_field form=$form field='email'}
</div> <div class="form-group {if $error}has-error{/if}">
{/form_field} <label for="{$label_attr.for}" class="control-label">{$label} : </label>
<input type="text" id="{$label_attr.for}" name="{$name}" class="form-control" value="{$value}" title="{$label}" placeholder="{intl l='Email address'}">
{form_field form=$form field='city'} </div>
<div class="form-group {if $error}has-error{/if}"> {/form_field}
<label for="{$label_attr.for}" class="control-label">{$label} : </label>
<input type="text" id="{$label_attr.for}" name="{$name}" class="form-control" value="{$value}" title="{$label}" placeholder="{intl l='City'}"> {module_include location='customer_create_form'}
</div>
{/form_field} {/capture}
{form_field form=$form field='country'} {include
<div class="form-group {if $error}has-error{/if}"> file = "includes/generic-create-dialog.html"
<label for="{$label_attr.for}" class="control-label">{$label} : </label>
<select name="{$name}" id="{$label_attr.for}" class="form-control"> dialog_id = "add_customer_dialog"
{loop type="country" name="country1"} dialog_title = {intl l="Create a new customer"}
<option value="{$ID}">{$TITLE}</option> dialog_body = {$smarty.capture.customer_creation_dialog nofilter}
{/loop}
</select> dialog_ok_label = {intl l="Create this customer"}
</div> dialog_cancel_label = {intl l="Cancel"}
{/form_field}
form_action = {url path='/admin/customer/create'}
{form_field form=$form field='email'} form_enctype = {form_enctype form=$form}
<div class="form-group {if $error}has-error{/if}"> form_error_message = $form_error_message
<label for="{$label_attr.for}" class="control-label">{$label} : </label> }
<input type="text" id="{$label_attr.for}" name="{$name}" class="form-control" value="{$value}" title="{$label}" placeholder="{intl l='Email address'}"> {/form}
</div>
{/form_field} {* Delete confirmation dialog *}
{module_include location='customer_create_form'} {capture "delete_customer_dialog"}
<input type="hidden" name="page" value="{$page}">
{/capture} <input type="hidden" name="customer_id" id="delete_customer_id">
{include {module_include location='customer_delete_form'}
file = "includes/generic-create-dialog.html" {/capture}
dialog_id = "add_customer_dialog" {include
dialog_title = {intl l="Create a new customer"} file = "includes/generic-confirm-dialog.html"
dialog_body = {$smarty.capture.customer_creation_dialog nofilter}
dialog_id = "delete_customer_dialog"
dialog_ok_label = {intl l="Create this customer"} dialog_title = {intl l="Delete customer"}
dialog_cancel_label = {intl l="Cancel"} dialog_message = {intl l="Do you really want to delete this customer ?"}
form_action = {url path='/admin/customer/create'} form_action = {url path='/admin/customer/delete'}
form_enctype = {form_enctype form=$form} form_content = {$smarty.capture.delete_customer_dialog nofilter}
form_error_message = $form_error_message form_id = "form_delete_customer"
} }
{/form}
{/block}
{* Delete confirmation dialog *}
{block name="javascript-initialization"}
{capture "delete_customer_dialog"}
<input type="hidden" name="page" value="{$page}"> <script type="text/javascript">
<input type="hidden" name="customer_id" id="delete_customer_id"> $(".customer-delete").click(function(){
$("#delete_customer_id").val($(this).attr("data-id"));
{module_include location='customer_delete_form'} });
{/capture} </script>
{include {/block}
file = "includes/generic-confirm-dialog.html"
{block name="javascript-last-call"}
dialog_id = "delete_customer_dialog" {module_include location='customers-js'}
dialog_title = {intl l="Delete customer"}
dialog_message = {intl l="Do you really want to delete this customer ?"}
form_action = {url path='/admin/customer/delete'}
form_content = {$smarty.capture.delete_customer_dialog nofilter}
form_id = "form_delete_customer"
}
{/block}
{block name="javascript-initialization"}
<script type="text/javascript">
$(".customer-delete").click(function(){
$("#delete_customer_id").val($(this).attr("data-id"));
});
</script>
{/block}
{block name="javascript-last-call"}
{module_include location='customers-js'}
{/block} {/block}

View File

@@ -1,231 +1,211 @@
{extends file="admin-layout.tpl"} {extends file="admin-layout.tpl"}
{block name="page-title"}{intl l='Orders'}{/block} {block name="page-title"}{intl l='Orders'}{/block}
{block name="check-resource"}admin.order{/block} {block name="check-resource"}admin.order{/block}
{block name="check-access"}view{/block} {block name="check-access"}view{/block}
{block name="main-content"} {block name="main-content"}
{assign order_page {$smarty.get.page|default:1}} {assign order_page {$smarty.get.page|default:1}}
{assign status_filter {$smarty.get.status|assertType:'IntListType'}} {assign status_filter {$smarty.get.status|assertType:'IntListType'}}
<div class="orders"> <div class="orders">
<div id="wrapper" class="container"> <div id="wrapper" class="container">
<ul class="breadcrumb"> <ul class="breadcrumb">
<li><a href="{url path='/admin/home'}">{intl l="Home"}</a></li> <li><a href="{url path='/admin/home'}">{intl l="Home"}</a></li>
<li><a href="#">{intl l="Orders"}</a></li> <li><a href="#">{intl l="Orders"}</a></li>
</ul> </ul>
{module_include location='orders_top'} {module_include location='orders_top'}
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
<div class="general-block-decorator"> <div class="general-block-decorator">
<div class="table-responsive"> <div class="table-responsive">
<table class="table table-striped table-condensed table-left-aligned"> <table class="table table-striped table-condensed table-left-aligned">
<caption class="clearfix"> <caption class="clearfix">
{intl l='Orders'} {intl l='Orders'}
</caption> </caption>
{ifloop rel="order-list"} {ifloop rel="order-list"}
<thead> <thead>
<tr> <tr>
<th class="object-title"> <th class="object-title">
{admin_sortable_header {admin_sortable_header
current_order=$orders_order current_order=$orders_order
order='id' order='id'
reverse_order='id-reverse' reverse_order='id-reverse'
path={url path='/admin/orders'} path={url path='/admin/orders'}
request_parameter_name='orders_order' request_parameter_name='orders_order'
label="{intl l='ID'}" label="{intl l='ID'}"
} </th> } </th>
<th class="object-title"> <th class="object-title">
{admin_sortable_header {admin_sortable_header
current_order=$orders_order current_order=$orders_order
order='reference' order='reference'
reverse_order='reference-reverse' reverse_order='reference-reverse'
path={url path='/admin/orders'} path={url path='/admin/orders'}
request_parameter_name='orders_order' request_parameter_name='orders_order'
label="{intl l='Reference'}" label="{intl l='Reference'}"
} </th> } </th>
<th class="object-title"> <th class="object-title">
{admin_sortable_header {admin_sortable_header
current_order=$orders_order current_order=$orders_order
order='create-date' order='create-date'
reverse_order='create-date-reverse' reverse_order='create-date-reverse'
path={url path='/admin/orders'} path={url path='/admin/orders'}
request_parameter_name='orders_order' request_parameter_name='orders_order'
label="{intl l='Date & Hour'}" label="{intl l='Date & Hour'}"
} </th> } </th>
<th class="object-title"> <th class="object-title">
{admin_sortable_header {admin_sortable_header
current_order=$orders_order current_order=$orders_order
order='company' order='company'
reverse_order='company-reverse' reverse_order='company-reverse'
path={url path='/admin/orders'} path={url path='/admin/orders'}
request_parameter_name='orders_order' request_parameter_name='orders_order'
label="{intl l='Company'}" label="{intl l='Company'}"
} </th> } </th>
<th class="object-title"> <th class="object-title">
{admin_sortable_header {admin_sortable_header
current_order=$orders_order current_order=$orders_order
order='customer-name' order='customer-name'
reverse_order='customer-name-reverse' reverse_order='customer-name-reverse'
path={url path='/admin/orders'} path={url path='/admin/orders'}
request_parameter_name='orders_order' request_parameter_name='orders_order'
label="{intl l='Cutomer Name'}" label="{intl l='Cutomer Name'}"
} </th> } </th>
<th class="object-title text-right">{intl l='Amount'}</th> <th class="object-title text-right">{intl l='Amount'}</th>
<th class="object-title text-center"> <th class="object-title text-center">
{admin_sortable_header {admin_sortable_header
current_order=$orders_order current_order=$orders_order
order='status' order='status'
reverse_order='status-reverse' reverse_order='status-reverse'
path={url path='/admin/orders'} path={url path='/admin/orders'}
request_parameter_name='orders_order' request_parameter_name='orders_order'
label="{intl l='Status'}" label="{intl l='Status'}"
} </th> } </th>
{module_include location='orders_table_header'} {module_include location='orders_table_header'}
<th class="actions">{intl l="Actions"}</th> <th class="actions">{intl l="Actions"}</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{loop type="order" name="order-list" customer="*" order=$orders_order backend_context="1" page={$order_page} limit={#max_displayed_orders#} status=$status_filter|default:'*'} {loop type="order" name="order-list" customer="*" order=$orders_order backend_context="1" page={$order_page} limit={#max_displayed_orders#} status=$status_filter|default:'*'}
{loop type="currency" name="order-currency" id=$CURRENCY} {loop type="currency" name="order-currency" id=$CURRENCY}
{$orderCurrency=$SYMBOL} {$orderCurrency=$SYMBOL}
{/loop} {/loop}
{loop type="order_address" name="order-invoice-address" id=$INVOICE_ADDRESS} {loop type="order_address" name="order-invoice-address" id=$INVOICE_ADDRESS}
{assign "orderInvoiceFirstName" $FIRSTNAME} {assign "orderInvoiceFirstName" $FIRSTNAME}
{assign "orderInvoiceLastName" $LASTNAME} {assign "orderInvoiceLastName" $LASTNAME}
{assign "orderInvoiceCompany" $COMPANY} {assign "orderInvoiceCompany" $COMPANY}
{/loop} {/loop}
{loop type="order-status" name="order-status" id=$STATUS} {loop type="order-status" name="order-status" id=$STATUS}
{assign "orderStatus" $TITLE} {assign "orderStatus" $TITLE}
{assign "orderStatusLabel" "order_$CODE"} {assign "orderStatusLabel" "order_$CODE"}
{/loop} {/loop}
<tr> <tr>
<td><a href="{url path="/admin/order/update/$ID"}">{$ID}</a></td> <td><a href="{url path="/admin/order/update/$ID"}">{$ID}</a></td>
<td><a href="{url path="/admin/order/update/$ID"}">{$REF}</a></td> <td><a href="{url path="/admin/order/update/$ID"}">{$REF}</a></td>
<td>{format_date date=$CREATE_DATE}</td> <td>{format_date date=$CREATE_DATE}</td>
<td>{$orderInvoiceCompany}</td> <td>{$orderInvoiceCompany}</td>
<td><a href="{url path='/admin/customer/update' customer_id=$CUSTOMER}">{$orderInvoiceFirstName|ucwords} {$orderInvoiceLastName|upper}</a></td> <td><a href="{url path='/admin/customer/update' customer_id=$CUSTOMER}">{$orderInvoiceFirstName|ucwords} {$orderInvoiceLastName|upper}</a></td>
<td class="text-right">{format_money number=$TOTAL_TAXED_AMOUNT symbol=$orderCurrency}</td> <td class="text-right">{format_money number=$TOTAL_TAXED_AMOUNT symbol=$orderCurrency}</td>
<td class="text-center"><span class="label label-{#$orderStatusLabel#}">{$orderStatus}</span></td> <td class="text-center"><span class="label label-{#$orderStatusLabel#}">{$orderStatus}</span></td>
{module_include location='orders_table_row'} {module_include location='orders_table_row'}
<td> <td>
<div class="btn-group"> <div class="btn-group">
{loop type="auth" name="can_change" role="ADMIN" resource="admin.order" access="UPDATE"} {loop type="auth" name="can_change" role="ADMIN" resource="admin.order" access="UPDATE"}
<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>
{if $STATUS !== 5} {if $STATUS !== 5}
<a class="btn btn-default btn-xs order-cancel" title="{intl l='Cancel this order'}" href="#cancel_order_dialog" data-id="{$ID}" data-toggle="modal"><span class="glyphicon glyphicon-remove-sign"></span></a> <a class="btn btn-default btn-xs order-cancel" title="{intl l='Cancel this order'}" href="#cancel_order_dialog" data-id="{$ID}" data-toggle="modal"><span class="glyphicon glyphicon-remove-sign"></span></a>
{/if} {/if}
{/loop} {/loop}
</div> </div>
</td> </td>
</tr> </tr>
{/loop} {/loop}
</tbody> </tbody>
<tfoot> <tfoot>
<tr> <tr>
<td colspan="7"> <td colspan="7">
{include
<div class="text-center"> file = "includes/pagination.html"
<ul class="pagination pagination-centered">
{if $order_page != 1} loop_ref = "order-list"
<li><a href="{url path="/admin/orders" page=1 orders_order=$orders_order}">&laquo;</a></li> max_page_count = 10
{else} page_url = "{url path="/admin/orders" orders_order=$orders_order}"
<li class="disabled"><a href="#">&laquo;</a></li> }
{/if} </td>
</tr>
{pageloop rel="order-list"} </tfoot>
{if $PAGE != $CURRENT}
<li><a href="{url path="/admin/orders" page=$PAGE orders_order=$orders_order}">{$PAGE}</a></li> {/ifloop}
{else} </table>
<li class="active"><a href="#">{$PAGE}</a></li> </div>
{/if} </div>
</div>
</div>
{/pageloop}
{if $PAGE == $LAST && $LAST != $CURRENT} {module_include location='orders_bottom'}
<li><a href="{url path="/admin/orders" page=$PAGE orders_order=$orders_order}">&raquo;</a></li>
{else} </div>
<li class="disabled"><a href="#">&raquo;</a></li> </div>
{/if}
</ul> {* Cancel order confirmation dialog *}
</div>
{capture "cancel_order_dialog"}
</td> <input type="hidden" name="order_page" value="{$order_page}">
</tr> <input type="hidden" name="order_id" id="cancel_order_id" />
</tfoot> <input type="hidden" name="status_id" value="5" />
{/capture}
{/ifloop}
{include
</table> file = "includes/generic-confirm-dialog.html"
</div>
</div> dialog_id = "cancel_order_dialog"
</div> dialog_title = {intl l="Delete an order"}
</div> dialog_message = {intl l="Do you really want to cancel this order ?"}
{module_include location='orders_bottom'} form_action = {url path='/admin/order/update/status'}
form_content = {$smarty.capture.cancel_order_dialog nofilter}
</div> form_id = "cancel-order-form"
</div> }
{* Cancel order confirmation dialog *} {/block}
{capture "cancel_order_dialog"} {block name="javascript-initialization"}
<input type="hidden" name="order_page" value="{$order_page}">
<input type="hidden" name="order_id" id="cancel_order_id" /> <script type="text/javascript">
<input type="hidden" name="status_id" value="5" /> $(".order-cancel").click(function(){
{/capture} $("#cancel_order_id").val($(this).attr("data-id"));
});
{include </script>
file = "includes/generic-confirm-dialog.html"
{/block}
dialog_id = "cancel_order_dialog"
dialog_title = {intl l="Delete an order"} {block name="javascript-last-call"}
dialog_message = {intl l="Do you really want to cancel this order ?"} {module_include location='orders-js'}
form_action = {url path='/admin/order/update/status'}
form_content = {$smarty.capture.cancel_order_dialog nofilter}
form_id = "cancel-order-form"
}
{/block}
{block name="javascript-initialization"}
<script type="text/javascript">
$(".order-cancel").click(function(){
$("#cancel_order_id").val($(this).attr("data-id"));
});
</script>
{/block}
{block name="javascript-last-call"}
{module_include location='orders-js'}
{/block} {/block}