From c10086f6c9cfbb27cc47834219784df6f39fdc93 Mon Sep 17 00:00:00 2001 From: franck Date: Mon, 1 Jul 2013 16:50:19 +0200 Subject: [PATCH 01/17] Introduction du package Thelia Security --- .../AuthenticationProviderInterface.php | 60 +++++++ .../UsernamePasswordAuthenticator.php | 67 ++++++++ .../Encoder/PasswordEncoderInterface.php | 52 ++++++ .../Security/Encoder/PasswordHashEncoder.php | 66 ++++++++ .../AuthenticationTokenNotFoundException.php | 28 ++++ .../Security/Security/SecurityManager.php | 81 ++++++++++ .../Security/Security/Token/AbstractToken.php | 148 ++++++++++++++++++ .../Security/Token/TokenInterface.php | 81 ++++++++++ .../Security/Token/UsernamePasswordToken.php | 75 +++++++++ .../Security/User/AdminUserProvider.php | 18 +++ .../Security/User/CustomerUserProvider.php | 20 +++ .../Security/Security/User/UserInterface.php | 42 +++++ .../Security/User/UserProviderInterface.php | 14 ++ 13 files changed, 752 insertions(+) create mode 100644 core/lib/Thelia/Core/Security/Security/Authentication/AuthenticationProviderInterface.php create mode 100644 core/lib/Thelia/Core/Security/Security/Authentication/UsernamePasswordAuthenticator.php create mode 100644 core/lib/Thelia/Core/Security/Security/Encoder/PasswordEncoderInterface.php create mode 100644 core/lib/Thelia/Core/Security/Security/Encoder/PasswordHashEncoder.php create mode 100644 core/lib/Thelia/Core/Security/Security/Exception/AuthenticationTokenNotFoundException.php create mode 100644 core/lib/Thelia/Core/Security/Security/SecurityManager.php create mode 100644 core/lib/Thelia/Core/Security/Security/Token/AbstractToken.php create mode 100644 core/lib/Thelia/Core/Security/Security/Token/TokenInterface.php create mode 100644 core/lib/Thelia/Core/Security/Security/Token/UsernamePasswordToken.php create mode 100644 core/lib/Thelia/Core/Security/Security/User/AdminUserProvider.php create mode 100644 core/lib/Thelia/Core/Security/Security/User/CustomerUserProvider.php create mode 100644 core/lib/Thelia/Core/Security/Security/User/UserInterface.php create mode 100644 core/lib/Thelia/Core/Security/Security/User/UserProviderInterface.php diff --git a/core/lib/Thelia/Core/Security/Security/Authentication/AuthenticationProviderInterface.php b/core/lib/Thelia/Core/Security/Security/Authentication/AuthenticationProviderInterface.php new file mode 100644 index 000000000..eb60f0482 --- /dev/null +++ b/core/lib/Thelia/Core/Security/Security/Authentication/AuthenticationProviderInterface.php @@ -0,0 +1,60 @@ +. */ +/* */ +/*************************************************************************************/ + +namespace Thelia\Core\Security\Authentication; + +use Thelia\Core\Security\UserNotFoundException; +use Thelia\Core\Security\IncorrectPasswordException; + +/** + * Aunthentication providers are in charge or retrieving users, and check their + * credentials. + * + * @author Franck + * + */ +interface AuthenticationProviderInterface { + + /** + * Set the authentication token + * + * @param TokenInterface $token the authentication token + */ + public function setToken(TokenInterface $token); + + + /** + * Set the authentication token + * + * @param unknown $key + */ + public function supportsToken(TokenInterface $token); + + /** + * Authenticate the token + * + *@throws Exception if authentication was not successful + */ + public function authenticate(); +} +?> \ No newline at end of file diff --git a/core/lib/Thelia/Core/Security/Security/Authentication/UsernamePasswordAuthenticator.php b/core/lib/Thelia/Core/Security/Security/Authentication/UsernamePasswordAuthenticator.php new file mode 100644 index 000000000..f6161f038 --- /dev/null +++ b/core/lib/Thelia/Core/Security/Security/Authentication/UsernamePasswordAuthenticator.php @@ -0,0 +1,67 @@ +. */ +/* */ +/*************************************************************************************/ + +namespace Thelia\Core\Security\Authentication; + +use Thelia\Core\Security\Authentication\AuthenticationProviderInterface; +use Thelia\Core\Security\Encoder\PasswordEncoderInterface; +use Thelia\Core\Security\User\UserProviderInterface; +use Thelia\Security\Token\TokenInterface; +use Thelia\Core\Security\Exception\IncorrectPasswordException; +use Thelia\Core\Security\Token\UsernamePasswordToken; + +class UsernamePasswordAuthenticator implements AuthenticationProviderInterface { + + protected $userProvider; + protected $encoder; + + private $token; + + public function __construct(UserProviderInterface $userProvider, PasswordEncoderInterface $encoder) { + $this->userProvider = $userProvider; + $this->encoder = $encoder; + } + + public function supportsToken(TokenInterface $token) { + + return $token instanceof UsernamePasswordToken; + } + + public function authenticate($token) { + + if (!$this->supports($token)) { + return null; + } + + // Retreive user + $user = $this->userProvider->getUser($this->token->getUsername()); + + // Check password + $authOk = $this->encoder->isEqual($password, $user->getPassword(), $user->getAlgo(), $user->getSalt()) === true; + + $authenticatedToken = new UsernamePasswordToken($user, $token->getCredentials(), $authOk); + + return $authenticatedToken; + } +} \ No newline at end of file diff --git a/core/lib/Thelia/Core/Security/Security/Encoder/PasswordEncoderInterface.php b/core/lib/Thelia/Core/Security/Security/Encoder/PasswordEncoderInterface.php new file mode 100644 index 000000000..3353969b8 --- /dev/null +++ b/core/lib/Thelia/Core/Security/Security/Encoder/PasswordEncoderInterface.php @@ -0,0 +1,52 @@ +. */ +/* */ +/*************************************************************************************/ + +namespace Thelia\Core\Security\Encoder; + +/** + * This interface defines a password encoder. + * + * @author Franck Allimant + * + */ +interface PasswordEncoderInterface { + + /** + * Encode a string. + * + * @param string $password the password to encode + * @param string $algorithm the hash() algorithm + * @return string $salt the salt + */ + public function encode($password, $algorithm, $salt); + + /** + * Check a string against an encoded password. + * + * @param string $string the string to compare against password + * @param string $password the encoded password + * @param string $algorithm the hash() algorithm + * @return string $salt the salt + */ + public function isEqual($string, $password, $algorithm, $salt); +} diff --git a/core/lib/Thelia/Core/Security/Security/Encoder/PasswordHashEncoder.php b/core/lib/Thelia/Core/Security/Security/Encoder/PasswordHashEncoder.php new file mode 100644 index 000000000..7a66812f5 --- /dev/null +++ b/core/lib/Thelia/Core/Security/Security/Encoder/PasswordHashEncoder.php @@ -0,0 +1,66 @@ +. */ +/* */ +/*************************************************************************************/ + +namespace Thelia\Core\Security\Encoder; + +/** + * This interface defines a hash based password encoder. + * + * @author Franck Allimant + */ + +class PasswordHashEncoder implements PasswordEncoderInterface { + + /** + * {@inheritdoc} + */ + public function encode($password, $algorithm, $salt) + { + if (!in_array($algorithm, hash_algos(), true)) { + throw new \LogicException(sprintf('The algorithm "%s" is not supported.', $algorithm)); + } + + // Salt the string + $salted = $password.$salt; + + // Create the hash + $digest = hash($algorithm, $salted, true); + + // "stretch" hash + for ($i = 1; $i < 5000; $i++) { + $digest = hash($algorithm, $digest.$salted, true); + } + + return base64_encode($digest); + } + + /** + * {@inheritdoc} + */ + public function isEqual($string, $password, $algorithm, $salt) + { + $encoded = $this->encode($password, $algorithm, $salt); + + return $encoded == $string; + } +} \ No newline at end of file diff --git a/core/lib/Thelia/Core/Security/Security/Exception/AuthenticationTokenNotFoundException.php b/core/lib/Thelia/Core/Security/Security/Exception/AuthenticationTokenNotFoundException.php new file mode 100644 index 000000000..061b43257 --- /dev/null +++ b/core/lib/Thelia/Core/Security/Security/Exception/AuthenticationTokenNotFoundException.php @@ -0,0 +1,28 @@ +. */ +/* */ +/*************************************************************************************/ + +namespace Thelia\Core\Security\Exception; + +class AuthenticationTokenNotFoundException extends \Exception +{ +} diff --git a/core/lib/Thelia/Core/Security/Security/SecurityManager.php b/core/lib/Thelia/Core/Security/Security/SecurityManager.php new file mode 100644 index 000000000..82c45a304 --- /dev/null +++ b/core/lib/Thelia/Core/Security/Security/SecurityManager.php @@ -0,0 +1,81 @@ +. */ +/* */ +/*************************************************************************************/ + +namespace Thelia\Core\Security; + +use Thelia\Core\Security\Authentication\AuthenticationProviderInterface; +use Thelia\Core\Security\Exception\AuthenticationTokenNotFoundException; + +/** + * A simple security manager, in charge of authenticating users using various authentication systems. + * + * @author Franck Allimant + */ +class SecurityManager { + + protected $authProvider; + + public function __construct(AuthenticationProviderInterface $authProvider) { + $this->authProvider = $authProvider; + } + + /** + * Checks if the current token is authenticated + * + * @throws AuthenticationCredentialsNotFoundException when the security context has no authentication token. + * + * @return Boolean + */ + final public function isGranted() + { + if (null === $this->token) { + throw new AuthenticationTokenNotFoundException('The security context contains no authentication token.'); + } + + if (!$this->token->isAuthenticated()) { + $this->token = $this->authProvider->authenticate($this->token); + } + + return $this->token->isAuthenticated(); + } + + /** + * Gets the currently authenticated token. + * + * @return TokenInterface|null A TokenInterface instance or null if no authentication information is available + */ + public function getToken() + { + return $this->token; + } + + /** + * Sets the token. + * + * @param TokenInterface $token A TokenInterface token, or null if no further authentication information should be stored + */ + public function setToken(TokenInterface $token = null) + { + $this->token = $token; + } +} \ No newline at end of file diff --git a/core/lib/Thelia/Core/Security/Security/Token/AbstractToken.php b/core/lib/Thelia/Core/Security/Security/Token/AbstractToken.php new file mode 100644 index 000000000..be1f8cb8b --- /dev/null +++ b/core/lib/Thelia/Core/Security/Security/Token/AbstractToken.php @@ -0,0 +1,148 @@ + +* @author Johannes M. Schmitt +*/ +abstract class AbstractToken implements TokenInterface +{ + private $user; + private $authenticated; + + /** + * Constructor. + * + * @param RoleInterface[] $roles An array of roles + * + * @throws \InvalidArgumentException + */ + public function __construct() + { + $this->authenticated = false; + } + + /** + * {@inheritdoc} + */ + public function getUsername() + { + if ($this->user instanceof UserInterface) { + return $this->user->getUsername(); + } + + return (string) $this->user; + } + + public function getUser() + { + return $this->user; + } + + /** + * Sets the user in the token. + * + * The user can be a UserInterface instance, or an object implementing + * a __toString method or the username as a regular string. + * + * @param mixed $user The user + * @throws \InvalidArgumentException + */ + public function setUser($user) + { + if (!($user instanceof UserInterface || is_string($user))) { + throw new \InvalidArgumentException('$user must be an instanceof UserInterface, or a primitive string.'); + } + + if (null === $this->user) { + $changed = false; + } elseif ($this->user instanceof UserInterface) { + if (!$user instanceof UserInterface) { + $changed = true; + } else { + $changed = $this->hasUserChanged($user); + } + } elseif ($user instanceof UserInterface) { + $changed = true; + } else { + $changed = (string) $this->user !== (string) $user; + } + + if ($changed) { + $this->setAuthenticated(false); + } + + $this->user = $user; + } + + /** + * {@inheritdoc} + */ + public function isAuthenticated() + { + return $this->authenticated; + } + + /** + * {@inheritdoc} + */ + public function setAuthenticated($authenticated) + { + $this->authenticated = (Boolean) $authenticated; + } + + /** + * {@inheritdoc} + */ + public function eraseCredentials() + { + if ($this->getUser() instanceof UserInterface) { + $this->getUser()->eraseCredentials(); + } + } + + /** + * {@inheritdoc} + */ + public function serialize() + { + return serialize(array($this->user, $this->authenticated)); + } + + /** + * {@inheritdoc} + */ + public function unserialize($serialized) + { + list($this->user, $this->authenticated) = unserialize($serialized); + } + + private function hasUserChanged(UserInterface $user) + { + if (!($this->user instanceof UserInterface)) { + throw new \BadMethodCallException('Method "hasUserChanged" should be called when current user class is instance of "UserInterface".'); + } + + if ($this->user instanceof EquatableInterface) { + return ! (Boolean) $this->user->isEqualTo($user); + } + + if ($this->user->getPassword() !== $user->getPassword()) { + return true; + } + + if ($this->user->getSalt() !== $user->getSalt()) { + return true; + } + + if ($this->user->getUsername() !== $user->getUsername()) { + return true; + } + + return false; + } +} \ No newline at end of file diff --git a/core/lib/Thelia/Core/Security/Security/Token/TokenInterface.php b/core/lib/Thelia/Core/Security/Security/Token/TokenInterface.php new file mode 100644 index 000000000..d70c8eeea --- /dev/null +++ b/core/lib/Thelia/Core/Security/Security/Token/TokenInterface.php @@ -0,0 +1,81 @@ +. */ +/* */ +/*************************************************************************************/ + +namespace Thelia\Core\Security\Token; + +/** + * TokenInterface is the interface for the user authentication information. + * + * Parts borrowed from Symfony Security Framework (Fabien Potencier / Johannes M. Schmitt ) + */ + +interface TokenInterface extends \Serializable +{ + /** + * Returns the user credentials. + * + * @return mixed The user credentials + */ + public function getCredentials(); + + /** + * Returns a user representation. + * + * @return mixed either returns an object which implements __toString(), or + * a primitive string is returned. + */ + public function getUser(); + + /** + * Sets a user instance + * + * @param mixed $user + */ + public function setUser($user); + + /** + * Returns the username. + * + * @return string + */ + public function getUsername(); + + /** + * Returns whether the user is authenticated or not. + * + * @return Boolean true if the token has been authenticated, false otherwise + */ + public function isAuthenticated(); + + /** + * Sets the authenticated flag. + * + * @param Boolean $isAuthenticated The authenticated flag + */ + public function setAuthenticated($isAuthenticated); + + /** + * Removes sensitive information from the token. + */ + public function eraseCredentials(); +} \ No newline at end of file diff --git a/core/lib/Thelia/Core/Security/Security/Token/UsernamePasswordToken.php b/core/lib/Thelia/Core/Security/Security/Token/UsernamePasswordToken.php new file mode 100644 index 000000000..f605d1767 --- /dev/null +++ b/core/lib/Thelia/Core/Security/Security/Token/UsernamePasswordToken.php @@ -0,0 +1,75 @@ + +*/ +class UsernamePasswordToken extends AbstractToken +{ + private $credentials; + + /** + * Constructor. + * + * @param string $user The username (like a nickname, email address, etc.), or a UserInterface instance or an object implementing a __toString method. + * @param string $password The password of the user + * + * @throws \InvalidArgumentException + */ + public function __construct($username, $password, $authenticated = false) + { + $this->setUser($username); + $this->credentials = $password; + + parent::setAuthenticated($authenticated); + } + + /** + * {@inheritdoc} + */ + public function setAuthenticated($isAuthenticated) + { + if ($isAuthenticated) { + throw new \LogicException('Cannot set this token to trusted after instantiation.'); + } + + parent::setAuthenticated(false); + } + + public function getCredentials() + { + return $this->credentials; + } + + /** + * {@inheritdoc} + */ + public function eraseCredentials() + { + parent::eraseCredentials(); + + $this->credentials = null; + } + + /** + * {@inheritdoc} + */ + public function serialize() + { + return serialize(array($this->credentials, $this->providerKey, parent::serialize())); + } + + /** + * {@inheritdoc} + */ + public function unserialize($serialized) + { + list($this->credentials, $this->providerKey, $parentStr) = unserialize($serialized); + parent::unserialize($parentStr); + } +} \ No newline at end of file diff --git a/core/lib/Thelia/Core/Security/Security/User/AdminUserProvider.php b/core/lib/Thelia/Core/Security/Security/User/AdminUserProvider.php new file mode 100644 index 000000000..9df5ee484 --- /dev/null +++ b/core/lib/Thelia/Core/Security/Security/User/AdminUserProvider.php @@ -0,0 +1,18 @@ +filterByLogin($key) + ->findOne(); + + return $admin; + } +} \ No newline at end of file diff --git a/core/lib/Thelia/Core/Security/Security/User/CustomerUserProvider.php b/core/lib/Thelia/Core/Security/Security/User/CustomerUserProvider.php new file mode 100644 index 000000000..824d9e774 --- /dev/null +++ b/core/lib/Thelia/Core/Security/Security/User/CustomerUserProvider.php @@ -0,0 +1,20 @@ +filterByEmail($key) + ->findOne(); + + return $customer; + } +} \ No newline at end of file diff --git a/core/lib/Thelia/Core/Security/Security/User/UserInterface.php b/core/lib/Thelia/Core/Security/Security/User/UserInterface.php new file mode 100644 index 000000000..9057504ca --- /dev/null +++ b/core/lib/Thelia/Core/Security/Security/User/UserInterface.php @@ -0,0 +1,42 @@ + + * + */ +interface UserInterface { + + /** + * Return the user unique name + */ + public function getUsername(); + + /** + * Return the user encoded password + */ + public function getPassword(); + + /** + * return the salt used to calculate the user password + */ + public function getSalt(); + + /** + * return the algorithm used to calculate the user password + */ + public function getAlgo(); + + /** + * Removes sensitive data from the user. + * + * This is important if, at any given point, sensitive information like + * the plain-text password is stored on this object. + * + * @return void + */ + public function eraseCredentials(); +} \ No newline at end of file diff --git a/core/lib/Thelia/Core/Security/Security/User/UserProviderInterface.php b/core/lib/Thelia/Core/Security/Security/User/UserProviderInterface.php new file mode 100644 index 000000000..217e693c1 --- /dev/null +++ b/core/lib/Thelia/Core/Security/Security/User/UserProviderInterface.php @@ -0,0 +1,14 @@ + \ No newline at end of file From 7382159685b120b91c5833a464a8a460b04791d2 Mon Sep 17 00:00:00 2001 From: Etienne Roudeix Date: Mon, 1 Jul 2013 17:02:15 +0200 Subject: [PATCH 02/17] create type formatter create product loop (not functional yet) --- .../Thelia/Core/Template/Element/BaseLoop.php | 3 + .../Core/Template/Loop/Argument/Argument.php | 13 ++ .../Thelia/Core/Template/Loop/Category.php | 1 + .../lib/Thelia/Core/Template/Loop/Product.php | 186 ++++++++++++++++++ .../Template/Smarty/Plugins/TheliaLoop.php | 2 +- .../lib/Thelia/Tests/Type/BooleanTypeTest.php | 55 ++++++ .../lib/Thelia/Tests/Type/IntListTypeTest.php | 7 + core/lib/Thelia/Tests/Type/JsonTypeTest.php | 7 + core/lib/Thelia/Type/AnyType.php | 5 + core/lib/Thelia/Type/BooleanType.php | 60 ++++++ core/lib/Thelia/Type/EnumType.php | 5 + core/lib/Thelia/Type/FloatType.php | 5 + core/lib/Thelia/Type/IntListType.php | 5 + core/lib/Thelia/Type/IntType.php | 5 + core/lib/Thelia/Type/JsonType.php | 7 +- core/lib/Thelia/Type/TypeCollection.php | 10 + core/lib/Thelia/Type/TypeInterface.php | 2 + 17 files changed, 376 insertions(+), 2 deletions(-) create mode 100755 core/lib/Thelia/Core/Template/Loop/Product.php create mode 100755 core/lib/Thelia/Tests/Type/BooleanTypeTest.php create mode 100755 core/lib/Thelia/Type/BooleanType.php diff --git a/core/lib/Thelia/Core/Template/Element/BaseLoop.php b/core/lib/Thelia/Core/Template/Element/BaseLoop.php index 6863f1a09..0ee3872de 100755 --- a/core/lib/Thelia/Core/Template/Element/BaseLoop.php +++ b/core/lib/Thelia/Core/Template/Element/BaseLoop.php @@ -67,6 +67,9 @@ abstract class BaseLoop $this->dispatcher = $dispatcher; } + /** + * @return \Thelia\Core\Template\Loop\Argument\ArgumentCollection + */ public function getArgs() { return $this->defineArgs()->addArguments($this->getDefaultArgs()); diff --git a/core/lib/Thelia/Core/Template/Loop/Argument/Argument.php b/core/lib/Thelia/Core/Template/Loop/Argument/Argument.php index 9fb72bff0..e8c44da03 100755 --- a/core/lib/Thelia/Core/Template/Loop/Argument/Argument.php +++ b/core/lib/Thelia/Core/Template/Loop/Argument/Argument.php @@ -72,6 +72,19 @@ class Argument ); } + public static function createBooleanTypeArgument($name, $default=null, $mandatory=false, $empty=true) + { + return new Argument( + $name, + new TypeCollection( + new Type\BoolType() + ), + $default, + $mandatory, + $empty + ); + } + public static function createIntListTypeArgument($name, $default=null, $mandatory=false, $empty=true) { return new Argument( diff --git a/core/lib/Thelia/Core/Template/Loop/Category.php b/core/lib/Thelia/Core/Template/Loop/Category.php index 55be4d69a..0d3426dfa 100755 --- a/core/lib/Thelia/Core/Template/Loop/Category.php +++ b/core/lib/Thelia/Core/Template/Loop/Category.php @@ -62,6 +62,7 @@ use Thelia\Type; * Class Category * @package Thelia\Core\Template\Loop * @author Manuel Raynaud + * @author Etienne Roudeix */ class Category extends BaseLoop { diff --git a/core/lib/Thelia/Core/Template/Loop/Product.php b/core/lib/Thelia/Core/Template/Loop/Product.php new file mode 100755 index 000000000..55be4d69a --- /dev/null +++ b/core/lib/Thelia/Core/Template/Loop/Product.php @@ -0,0 +1,186 @@ +. */ +/* */ +/*************************************************************************************/ + +namespace Thelia\Core\Template\Loop; + +use Thelia\Core\Template\Element\BaseLoop; +use Thelia\Core\Template\Element\LoopResult; +use Thelia\Core\Template\Element\LoopResultRow; + +use Thelia\Core\Template\Loop\Argument\ArgumentCollection; +use Thelia\Core\Template\Loop\Argument\Argument; +use Thelia\Log\Tlog; + +use Thelia\Model\CategoryQuery; +use Thelia\Type\TypeCollection; +use Thelia\Type; + +/** + * + * Category loop, all params available : + * + * - id : can be an id (eq : 3) or a "string list" (eg: 3, 4, 5) + * - parent : categories having this parent id + * - current : current id is used if you are on a category page + * - not_empty : if value is 1, category and subcategories must have at least 1 product + * - visible : default 1, if you want category not visible put 0 + * - order : all value available : + * * alpha : sorting by title alphabetical order + * * alpha_reverse : sorting by title alphabetical reverse order + * * reverse : sorting by position descending + * * by default results are sorting by position ascending + * - random : if 1, random results. Default value is 0 + * - exclude : all category id you want to exclude (as for id, an integer or a "string list" can be used) + * + * example : + * + * + * #TITLE : #ID + * + * + * + * Class Category + * @package Thelia\Core\Template\Loop + * @author Manuel Raynaud + */ +class Category extends BaseLoop +{ + public $id; + public $parent; + public $current; + public $not_empty; + public $visible; + public $link; + public $order; + public $random; + public $exclude; + + /** + * @return ArgumentCollection + */ + protected function defineArgs() + { + return new ArgumentCollection( + Argument::createIntListTypeArgument('id'), + Argument::createIntTypeArgument('parent'), + Argument::createIntTypeArgument('current'), + Argument::createIntTypeArgument('not_empty', 0), + Argument::createIntTypeArgument('visible', 1), + Argument::createAnyTypeArgument('link'), + new Argument( + 'order', + new TypeCollection( + new Type\EnumType('alpha', 'alpha_reverse', 'reverse') + ) + ), + Argument::createIntTypeArgument('random', 0), + Argument::createIntListTypeArgument('exclude') + ); + } + + /** + * @param $pagination + * + * @return \Thelia\Core\Template\Element\LoopResult + */ + public function exec(&$pagination) + { + $search = CategoryQuery::create(); + + if (!is_null($this->id)) { + $search->filterById(explode(',', $this->id), \Criteria::IN); + } + + if (!is_null($this->parent)) { + $search->filterByParent($this->parent); + } + + if ($this->current == 1) { + $search->filterById($this->request->get("category_id")); + } elseif (null !== $this->current && $this->current == 0) { + $search->filterById($this->request->get("category_id"), \Criteria::NOT_IN); + } + + if (!is_null($this->exclude)) { + $search->filterById(explode(",", $this->exclude), \Criteria::NOT_IN); + } + + if (!is_null($this->link)) { + $search->filterByLink($this->link); + } + + $search->filterByVisible($this->visible); + + switch ($this->order) { + case "alpha": + $search->addAscendingOrderByColumn(\Thelia\Model\CategoryI18nPeer::TITLE); + break; + case "alpha_reverse": + $search->addDescendingOrderByColumn(\Thelia\Model\CategoryI18nPeer::TITLE); + break; + case "reverse": + $search->orderByPosition(\Criteria::DESC); + break; + default: + $search->orderByPosition(); + break; + } + + if ($this->random == 1) { + $search->clearOrderByColumns(); + $search->addAscendingOrderByColumn('RAND()'); + } + + /** + * \Criteria::INNER_JOIN in second parameter for joinWithI18n exclude query without translation. + * + * @todo : verify here if we want results for row without translations. + */ + $search->joinWithI18n($this->request->getSession()->get('locale', 'en_US'), \Criteria::INNER_JOIN); + + $categories = $this->search($search, $pagination); + + $loopResult = new LoopResult(); + + foreach ($categories as $category) { + + if ($this->not_empty && $category->countAllProducts() == 0) continue; + + $loopResultRow = new LoopResultRow(); + $loopResultRow->set("TITLE",$category->getTitle()); + $loopResultRow->set("CHAPO", $category->getChapo()); + $loopResultRow->set("DESCRIPTION", $category->getDescription()); + $loopResultRow->set("POSTSCRIPTUM", $category->getPostscriptum()); + $loopResultRow->set("PARENT", $category->getParent()); + $loopResultRow->set("ID", $category->getId()); + $loopResultRow->set("URL", $category->getUrl()); + $loopResultRow->set("LINK", $category->getLink()); + $loopResultRow->set("NB_CHILD", $category->countChild()); + + $loopResult->addRow($loopResultRow); + } + + return $loopResult; + } + +} diff --git a/core/lib/Thelia/Core/Template/Smarty/Plugins/TheliaLoop.php b/core/lib/Thelia/Core/Template/Smarty/Plugins/TheliaLoop.php index 4d2ea7d57..3bc019c59 100755 --- a/core/lib/Thelia/Core/Template/Smarty/Plugins/TheliaLoop.php +++ b/core/lib/Thelia/Core/Template/Smarty/Plugins/TheliaLoop.php @@ -308,7 +308,7 @@ class TheliaLoop implements SmartyPluginInterface $value = $argument->default; } - $loop->{$argument->name} = $value; + $loop->{$argument->name} = $value === null ? null : $argument->type->getFormatedValue($value); } if (!empty($faultActor)) { diff --git a/core/lib/Thelia/Tests/Type/BooleanTypeTest.php b/core/lib/Thelia/Tests/Type/BooleanTypeTest.php new file mode 100755 index 000000000..84889ff40 --- /dev/null +++ b/core/lib/Thelia/Tests/Type/BooleanTypeTest.php @@ -0,0 +1,55 @@ +. */ +/* */ +/*************************************************************************************/ + +namespace Thelia\Tests\Type; + +use Thelia\Type\BooleanType; + +/** + * + * @author Etienne Roudeix + * + */ +class BooleanTypeTest extends \PHPUnit_Framework_TestCase +{ + public function testBooleanType() + { + $booleanType = new BooleanType(); + $this->assertTrue($booleanType->isValid('y')); + $this->assertTrue($booleanType->isValid('yes')); + $this->assertTrue($booleanType->isValid('true')); + $this->assertTrue($booleanType->isValid('no')); + $this->assertTrue($booleanType->isValid('n')); + $this->assertTrue($booleanType->isValid('false')); + $this->assertFalse($booleanType->isValid('foo')); + $this->assertFalse($booleanType->isValid(5)); + } + + public function testFormatBooleanType() + { + $booleanType = new BooleanType(); + $this->assertTrue($booleanType->getFormatedValue('yes')); + $this->assertFalse($booleanType->getFormatedValue('no')); + $this->assertNull($booleanType->getFormatedValue('foo')); + } +} diff --git a/core/lib/Thelia/Tests/Type/IntListTypeTest.php b/core/lib/Thelia/Tests/Type/IntListTypeTest.php index 68b628a5c..bbe410bd1 100755 --- a/core/lib/Thelia/Tests/Type/IntListTypeTest.php +++ b/core/lib/Thelia/Tests/Type/IntListTypeTest.php @@ -39,4 +39,11 @@ class IntListTypeTest extends \PHPUnit_Framework_TestCase $this->assertTrue($intListType->isValid('1,2,3')); $this->assertFalse($intListType->isValid('1,2,3.3')); } + + public function testFormatJsonType() + { + $intListType = new IntListType(); + $this->assertTrue(is_array($intListType->getFormatedValue('1,2,3'))); + $this->assertNull($intListType->getFormatedValue('foo')); + } } diff --git a/core/lib/Thelia/Tests/Type/JsonTypeTest.php b/core/lib/Thelia/Tests/Type/JsonTypeTest.php index 276a867d2..2dc15a2f5 100755 --- a/core/lib/Thelia/Tests/Type/JsonTypeTest.php +++ b/core/lib/Thelia/Tests/Type/JsonTypeTest.php @@ -38,4 +38,11 @@ class JsonTypeTest extends \PHPUnit_Framework_TestCase $this->assertTrue($jsonType->isValid('{"k0":"v0","k1":"v1","k2":"v2"}')); $this->assertFalse($jsonType->isValid('1,2,3')); } + + public function testFormatJsonType() + { + $jsonType = new JsonType(); + $this->assertTrue(is_array($jsonType->getFormatedValue('{"k0":"v0","k1":"v1","k2":"v2"}'))); + $this->assertNull($jsonType->getFormatedValue('foo')); + } } diff --git a/core/lib/Thelia/Type/AnyType.php b/core/lib/Thelia/Type/AnyType.php index 5b00a5aeb..8e22d44eb 100755 --- a/core/lib/Thelia/Type/AnyType.php +++ b/core/lib/Thelia/Type/AnyType.php @@ -39,4 +39,9 @@ class AnyType implements TypeInterface { return true; } + + public function getFormatedValue($value) + { + return $value; + } } diff --git a/core/lib/Thelia/Type/BooleanType.php b/core/lib/Thelia/Type/BooleanType.php new file mode 100755 index 000000000..939ff0d79 --- /dev/null +++ b/core/lib/Thelia/Type/BooleanType.php @@ -0,0 +1,60 @@ +. */ +/* */ +/*************************************************************************************/ +namespace Thelia\Type; + +/** + * + * @author Etienne Roudeix + * + */ + +class BooleanType implements TypeInterface +{ + protected $trueValuesArray = array( + '1', + 'true', + 'yes', + 'y', + ); + protected $falseValuesArray = array( + '0', + 'false', + 'no', + 'n', + ); + + public function getType() + { + return 'Boolean type'; + } + + public function isValid($value) + { + return in_array($value, $this->trueValuesArray) || in_array($value, $this->falseValuesArray); + } + + public function getFormatedValue($value) + { + return $this->isValid($value) ? ( in_array($value, $this->trueValuesArray) ) : null; + } +} diff --git a/core/lib/Thelia/Type/EnumType.php b/core/lib/Thelia/Type/EnumType.php index d0a1c9b89..0aff0ec88 100755 --- a/core/lib/Thelia/Type/EnumType.php +++ b/core/lib/Thelia/Type/EnumType.php @@ -47,4 +47,9 @@ class EnumType implements TypeInterface { return in_array($value, $this->values); } + + public function getFormatedValue($value) + { + return $this->isValid($value) ? $value : null; + } } diff --git a/core/lib/Thelia/Type/FloatType.php b/core/lib/Thelia/Type/FloatType.php index e341b540f..83d3561a4 100755 --- a/core/lib/Thelia/Type/FloatType.php +++ b/core/lib/Thelia/Type/FloatType.php @@ -39,4 +39,9 @@ class FloatType implements TypeInterface { return filter_var($value, FILTER_VALIDATE_FLOAT) === false ? false : true; } + + public function getFormatedValue($value) + { + return $this->isValid($value) ? $value : null; + } } diff --git a/core/lib/Thelia/Type/IntListType.php b/core/lib/Thelia/Type/IntListType.php index 74b0a6790..b023173f7 100755 --- a/core/lib/Thelia/Type/IntListType.php +++ b/core/lib/Thelia/Type/IntListType.php @@ -44,4 +44,9 @@ class IntListType implements TypeInterface return true; } + + public function getFormatedValue($values) + { + return $this->isValid($values) ? explode(',', $values) : null; + } } diff --git a/core/lib/Thelia/Type/IntType.php b/core/lib/Thelia/Type/IntType.php index d077a6c89..4adc2e10e 100755 --- a/core/lib/Thelia/Type/IntType.php +++ b/core/lib/Thelia/Type/IntType.php @@ -39,4 +39,9 @@ class IntType implements TypeInterface { return filter_var($value, FILTER_VALIDATE_INT) === false ? false : true; } + + public function getFormatedValue($value) + { + return $this->isValid($value) ? $value : null; + } } diff --git a/core/lib/Thelia/Type/JsonType.php b/core/lib/Thelia/Type/JsonType.php index 805977a32..397967df9 100755 --- a/core/lib/Thelia/Type/JsonType.php +++ b/core/lib/Thelia/Type/JsonType.php @@ -37,7 +37,12 @@ class JsonType implements TypeInterface public function isValid($value) { - json_decode($value); + json_decode($value, true); return (json_last_error() == JSON_ERROR_NONE); } + + public function getFormatedValue($value) + { + return $this->isValid($value) ? json_decode($value, true) : null; + } } diff --git a/core/lib/Thelia/Type/TypeCollection.php b/core/lib/Thelia/Type/TypeCollection.php index 4430be5df..bff384f92 100755 --- a/core/lib/Thelia/Type/TypeCollection.php +++ b/core/lib/Thelia/Type/TypeCollection.php @@ -132,4 +132,14 @@ class TypeCollection implements \Iterator return false; } + + public function getFormatedValue($value) { + foreach($this as $type) { + if($type->isValid($value)) { + return $type->getFormatedValue($value); + } + } + + return null; + } } diff --git a/core/lib/Thelia/Type/TypeInterface.php b/core/lib/Thelia/Type/TypeInterface.php index ae626f0b9..9268df7d5 100755 --- a/core/lib/Thelia/Type/TypeInterface.php +++ b/core/lib/Thelia/Type/TypeInterface.php @@ -33,4 +33,6 @@ interface TypeInterface public function getType(); public function isValid($value); + + public function getFormatedValue($value); } From 23aaa4951be982ad15cfaa4ed8bdd04299900ffd Mon Sep 17 00:00:00 2001 From: Etienne Roudeix Date: Mon, 1 Jul 2013 17:03:48 +0200 Subject: [PATCH 03/17] product loop (not functional yet) --- .../lib/Thelia/Core/Template/Loop/Product.php | 39 ++++++++++++++----- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/core/lib/Thelia/Core/Template/Loop/Product.php b/core/lib/Thelia/Core/Template/Loop/Product.php index 55be4d69a..bdafb2794 100755 --- a/core/lib/Thelia/Core/Template/Loop/Product.php +++ b/core/lib/Thelia/Core/Template/Loop/Product.php @@ -31,7 +31,7 @@ use Thelia\Core\Template\Loop\Argument\ArgumentCollection; use Thelia\Core\Template\Loop\Argument\Argument; use Thelia\Log\Tlog; -use Thelia\Model\CategoryQuery; +use Thelia\Model\ProductQuery; use Thelia\Type\TypeCollection; use Thelia\Type; @@ -59,18 +59,22 @@ use Thelia\Type; * * * - * Class Category + * Class Product * @package Thelia\Core\Template\Loop - * @author Manuel Raynaud + * @author Etienne Roudeix */ -class Category extends BaseLoop +class Product extends BaseLoop { public $id; - public $parent; - public $current; - public $not_empty; + public $ref; + public $category; + public $price; + public $price2; + public $promo; + public $newness; public $visible; - public $link; + public $weight; + public $current; public $order; public $random; public $exclude; @@ -82,15 +86,30 @@ class Category extends BaseLoop { return new ArgumentCollection( Argument::createIntListTypeArgument('id'), - Argument::createIntTypeArgument('parent'), + new Argument( + 'ref', + new TypeCollection( + new Type\JsonType() + ) + ), + Argument::createIntListTypeArgument('category'), + Argument::createBooleanTypeArgument('new'), + Argument::createIntTypeArgument('promo'), + Argument::createIntTypeArgument('max_prix'), + Argument::createIntTypeArgument('min_price'), + Argument::createIntTypeArgument('min_stock'), + Argument::createIntTypeArgument('min_weight'), + Argument::createIntTypeArgument('max_weight'), Argument::createIntTypeArgument('current'), + Argument::createIntTypeArgument('current_category'), + Argument::createIntTypeArgument('depth'), Argument::createIntTypeArgument('not_empty', 0), Argument::createIntTypeArgument('visible', 1), Argument::createAnyTypeArgument('link'), new Argument( 'order', new TypeCollection( - new Type\EnumType('alpha', 'alpha_reverse', 'reverse') + new Type\EnumType('title_alpha', 'title_alpha_reverse', 'reverse', 'min_price', 'max_price', 'category', 'manual', 'ref', 'promo') ) ), Argument::createIntTypeArgument('random', 0), From 97f948e647b6647a0405634fb945277578e6f93e Mon Sep 17 00:00:00 2001 From: franck Date: Tue, 2 Jul 2013 15:49:29 +0200 Subject: [PATCH 04/17] Added security --- .../Admin/Controller/AdminController.php | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/core/lib/Thelia/Admin/Controller/AdminController.php b/core/lib/Thelia/Admin/Controller/AdminController.php index bb5536c7e..b11be13c4 100755 --- a/core/lib/Thelia/Admin/Controller/AdminController.php +++ b/core/lib/Thelia/Admin/Controller/AdminController.php @@ -28,17 +28,23 @@ use Thelia\Form\AdminLogin; class AdminController extends BaseAdminController { - public function indexAction() + public function loginAction() { - $form = $this->getLoginForm(); $request = $this->getRequest(); if($request->isMethod("POST")) { + $form->bind($request); - if($form->isValid()) { + if ($form->isValid()) { + + $this->container->get('request')->authenticate( + $form->get('username')->getData(), + $form->get('password')->getData() + ); + echo "valid"; exit; } } @@ -48,6 +54,15 @@ class AdminController extends BaseAdminController { )); } + public function indexAction() + { + $form = $this->getLoginForm(); + + return $this->render("login.html", array( + "form" => $form->createView() + )); + } + protected function getLoginForm() { $form = $this->getFormBuilder(); From b54fec5243865f4a3a66344bc41c59dc95893420 Mon Sep 17 00:00:00 2001 From: franck Date: Tue, 2 Jul 2013 15:50:30 +0200 Subject: [PATCH 05/17] Added "remember me" field --- core/lib/Thelia/Form/AdminLogin.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/core/lib/Thelia/Form/AdminLogin.php b/core/lib/Thelia/Form/AdminLogin.php index 2bd2debac..4c9ed5437 100644 --- a/core/lib/Thelia/Form/AdminLogin.php +++ b/core/lib/Thelia/Form/AdminLogin.php @@ -27,6 +27,7 @@ use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Validator\Constraints\Length; use Symfony\Component\Validator\Constraints\NotBlank; +use Symfony\Component\Validator\Constraints\Choice; class AdminLogin extends AbstractType { @@ -39,7 +40,12 @@ class AdminLogin extends AbstractType { new Length(array("min" => 3)) ) )) - ->add("password", "password"); + ->add("password", "password", array( + "constraints" => array( + new NotBlank() + ) + )) + ->add("remember_me", "checkbox"); } /** From 40c0da5553f493384c853eff4046b397064bef60 Mon Sep 17 00:00:00 2001 From: Etienne Roudeix Date: Tue, 2 Jul 2013 16:33:43 +0200 Subject: [PATCH 06/17] loop product --- core/lib/Thelia/Config/Resources/config.xml | 1 + .../Core/Template/Loop/Argument/Argument.php | 15 +- .../Thelia/Core/Template/Loop/Category.php | 20 +-- .../lib/Thelia/Core/Template/Loop/Product.php | 154 +++++++++++++----- .../Template/Smarty/Plugins/TheliaLoop.php | 11 +- core/lib/Thelia/Model/ProductPeer.php | 4 + core/lib/Thelia/Model/ProductQuery.php | 11 ++ templates/smarty-sample/category.html | 8 + 8 files changed, 164 insertions(+), 60 deletions(-) create mode 100755 templates/smarty-sample/category.html diff --git a/core/lib/Thelia/Config/Resources/config.xml b/core/lib/Thelia/Config/Resources/config.xml index d1e6569fa..c8d0ef53f 100755 --- a/core/lib/Thelia/Config/Resources/config.xml +++ b/core/lib/Thelia/Config/Resources/config.xml @@ -6,6 +6,7 @@ + diff --git a/core/lib/Thelia/Core/Template/Loop/Argument/Argument.php b/core/lib/Thelia/Core/Template/Loop/Argument/Argument.php index e8c44da03..86c11edfb 100755 --- a/core/lib/Thelia/Core/Template/Loop/Argument/Argument.php +++ b/core/lib/Thelia/Core/Template/Loop/Argument/Argument.php @@ -72,12 +72,25 @@ class Argument ); } + public static function createFloatTypeArgument($name, $default=null, $mandatory=false, $empty=true) + { + return new Argument( + $name, + new TypeCollection( + new Type\FloatType() + ), + $default, + $mandatory, + $empty + ); + } + public static function createBooleanTypeArgument($name, $default=null, $mandatory=false, $empty=true) { return new Argument( $name, new TypeCollection( - new Type\BoolType() + new Type\BooleanType() ), $default, $mandatory, diff --git a/core/lib/Thelia/Core/Template/Loop/Category.php b/core/lib/Thelia/Core/Template/Loop/Category.php index 0d3426dfa..161727f51 100755 --- a/core/lib/Thelia/Core/Template/Loop/Category.php +++ b/core/lib/Thelia/Core/Template/Loop/Category.php @@ -84,9 +84,9 @@ class Category extends BaseLoop return new ArgumentCollection( Argument::createIntListTypeArgument('id'), Argument::createIntTypeArgument('parent'), - Argument::createIntTypeArgument('current'), - Argument::createIntTypeArgument('not_empty', 0), - Argument::createIntTypeArgument('visible', 1), + Argument::createBooleanTypeArgument('current'), + Argument::createBooleanTypeArgument('not_empty', 0), + Argument::createBooleanTypeArgument('visible', 1), Argument::createAnyTypeArgument('link'), new Argument( 'order', @@ -94,7 +94,7 @@ class Category extends BaseLoop new Type\EnumType('alpha', 'alpha_reverse', 'reverse') ) ), - Argument::createIntTypeArgument('random', 0), + Argument::createBooleanTypeArgument('random', 0), Argument::createIntListTypeArgument('exclude') ); } @@ -109,28 +109,28 @@ class Category extends BaseLoop $search = CategoryQuery::create(); if (!is_null($this->id)) { - $search->filterById(explode(',', $this->id), \Criteria::IN); + $search->filterById($this->id, \Criteria::IN); } if (!is_null($this->parent)) { $search->filterByParent($this->parent); } - if ($this->current == 1) { + if ($this->current === true) { $search->filterById($this->request->get("category_id")); - } elseif (null !== $this->current && $this->current == 0) { + } elseif ($this->current === false) { $search->filterById($this->request->get("category_id"), \Criteria::NOT_IN); } if (!is_null($this->exclude)) { - $search->filterById(explode(",", $this->exclude), \Criteria::NOT_IN); + $search->filterById($this->exclude, \Criteria::NOT_IN); } if (!is_null($this->link)) { $search->filterByLink($this->link); } - $search->filterByVisible($this->visible); + $search->filterByVisible($this->visible ? 1 : 0); switch ($this->order) { case "alpha": @@ -147,7 +147,7 @@ class Category extends BaseLoop break; } - if ($this->random == 1) { + if ($this->random === true) { $search->clearOrderByColumns(); $search->addAscendingOrderByColumn('RAND()'); } diff --git a/core/lib/Thelia/Core/Template/Loop/Product.php b/core/lib/Thelia/Core/Template/Loop/Product.php index bdafb2794..38d6d9b75 100755 --- a/core/lib/Thelia/Core/Template/Loop/Product.php +++ b/core/lib/Thelia/Core/Template/Loop/Product.php @@ -31,6 +31,8 @@ use Thelia\Core\Template\Loop\Argument\ArgumentCollection; use Thelia\Core\Template\Loop\Argument\Argument; use Thelia\Log\Tlog; +use Thelia\Model\CategoryQuery; +use Thelia\Model\ProductCategoryQuery; use Thelia\Model\ProductQuery; use Thelia\Type\TypeCollection; use Thelia\Type; @@ -68,13 +70,17 @@ class Product extends BaseLoop public $id; public $ref; public $category; - public $price; - public $price2; + public $new; public $promo; - public $newness; - public $visible; - public $weight; + public $min_price; + public $max_price; + public $min_stock; + public $min_weight; + public $max_weight; public $current; + public $current_category; + public $depth; + public $visible; public $order; public $random; public $exclude; @@ -94,25 +100,23 @@ class Product extends BaseLoop ), Argument::createIntListTypeArgument('category'), Argument::createBooleanTypeArgument('new'), - Argument::createIntTypeArgument('promo'), - Argument::createIntTypeArgument('max_prix'), - Argument::createIntTypeArgument('min_price'), + Argument::createBooleanTypeArgument('promo'), + Argument::createFloatTypeArgument('min_price'), + Argument::createFloatTypeArgument('max_prix'), Argument::createIntTypeArgument('min_stock'), - Argument::createIntTypeArgument('min_weight'), - Argument::createIntTypeArgument('max_weight'), - Argument::createIntTypeArgument('current'), - Argument::createIntTypeArgument('current_category'), + Argument::createFloatTypeArgument('min_weight'), + Argument::createFloatTypeArgument('max_weight'), + Argument::createBooleanTypeArgument('current'), + Argument::createBooleanTypeArgument('current_category'), Argument::createIntTypeArgument('depth'), - Argument::createIntTypeArgument('not_empty', 0), - Argument::createIntTypeArgument('visible', 1), - Argument::createAnyTypeArgument('link'), + Argument::createBooleanTypeArgument('visible', 1), new Argument( 'order', new TypeCollection( - new Type\EnumType('title_alpha', 'title_alpha_reverse', 'reverse', 'min_price', 'max_price', 'category', 'manual', 'ref', 'promo') + new Type\EnumType('alpha', 'alpha_reverse', 'reverse', 'min_price', 'max_price', 'category', 'manual', 'manual_reverse', 'ref', 'promo', 'new') ) ), - Argument::createIntTypeArgument('random', 0), + Argument::createBooleanTypeArgument('random', 0), Argument::createIntListTypeArgument('exclude') ); } @@ -124,28 +128,65 @@ class Product extends BaseLoop */ public function exec(&$pagination) { - $search = CategoryQuery::create(); + $search = ProductQuery::create(); if (!is_null($this->id)) { - $search->filterById(explode(',', $this->id), \Criteria::IN); + $search->filterById($this->id, \Criteria::IN); } - if (!is_null($this->parent)) { - $search->filterByParent($this->parent); + if (!is_null($this->ref)) { + $search->filterByRef($this->ref, \Criteria::IN); } - if ($this->current == 1) { - $search->filterById($this->request->get("category_id")); - } elseif (null !== $this->current && $this->current == 0) { - $search->filterById($this->request->get("category_id"), \Criteria::NOT_IN); + if (!is_null($this->category)) { + $search->filterByCategory( + CategoryQuery::create()->filterById($this->category, \Criteria::IN)->find(), + \Criteria::IN + ); } - if (!is_null($this->exclude)) { - $search->filterById(explode(",", $this->exclude), \Criteria::NOT_IN); + if ($this->new === true) { + $search->filterByNewness(1, \Criteria::EQUAL); + } else if($this->new === false) { + $search->filterByNewness(0, \Criteria::EQUAL); } - if (!is_null($this->link)) { - $search->filterByLink($this->link); + if ($this->promo === true) { + $search->filterByPromo(1, \Criteria::EQUAL); + } else if($this->promo === false) { + $search->filterByNewness(0, \Criteria::EQUAL); + } + + $search->filterByPriceDependingOnPromo($this->min_price, $this->max_price); //@todo review + + if ($this->current === true) { + $search->filterById($this->request->get("product_id")); + } elseif($this->current === false) { + $search->filterById($this->request->get("product_id"), \Criteria::NOT_IN); + } + + if ($this->current_category === true) { + $search->filterByCategory( + CategoryQuery::create()->filterByProduct( + ProductCategoryQuery::create()->filterByProductId( + $this->request->get("product_id"), + \Criteria::EQUAL + )->find(), + \Criteria::IN + )->find(), + \Criteria::IN + ); + } elseif($this->current_category === false) { + $search->filterByCategory( + CategoryQuery::create()->filterByProduct( + ProductCategoryQuery::create()->filterByProductId( + $this->request->get("product_id"), + \Criteria::EQUAL + )->find(), + \Criteria::IN + )->find(), + \Criteria::NOT_IN + ); } $search->filterByVisible($this->visible); @@ -160,16 +201,44 @@ class Product extends BaseLoop case "reverse": $search->orderByPosition(\Criteria::DESC); break; + /*case "min_price": + $search->orderByPosition(\Criteria::DESC); + break; + case "max_price": + $search->orderByPosition(\Criteria::DESC); + break; + case "category": + $search->orderByPosition(\Criteria::DESC); + break;*/ + case "manual": + $search->addAscendingOrderByColumn(\Thelia\Model\ProductPeer::POSITION); + break; + case "manual_reverse": + $search->addDescendingOrderByColumn(\Thelia\Model\ProductPeer::POSITION); + break; + case "ref": + $search->addAscendingOrderByColumn(\Thelia\Model\ProductPeer::REF); + break; + case "promo": + $search->addDescendingOrderByColumn(\Thelia\Model\ProductPeer::PROMO); + break; + case "new": + $search->addDescendingOrderByColumn(\Thelia\Model\ProductPeer::NEWNESS); + break; default: $search->orderByPosition(); break; } - if ($this->random == 1) { + if ($this->random === true) { $search->clearOrderByColumns(); $search->addAscendingOrderByColumn('RAND()'); } + if (!is_null($this->exclude)) { + $search->filterById($this->exclude, \Criteria::NOT_IN); + } + /** * \Criteria::INNER_JOIN in second parameter for joinWithI18n exclude query without translation. * @@ -177,24 +246,21 @@ class Product extends BaseLoop */ $search->joinWithI18n($this->request->getSession()->get('locale', 'en_US'), \Criteria::INNER_JOIN); - $categories = $this->search($search, $pagination); + $products = $this->search($search, $pagination); $loopResult = new LoopResult(); - foreach ($categories as $category) { - - if ($this->not_empty && $category->countAllProducts() == 0) continue; - + foreach ($products as $product) { $loopResultRow = new LoopResultRow(); - $loopResultRow->set("TITLE",$category->getTitle()); - $loopResultRow->set("CHAPO", $category->getChapo()); - $loopResultRow->set("DESCRIPTION", $category->getDescription()); - $loopResultRow->set("POSTSCRIPTUM", $category->getPostscriptum()); - $loopResultRow->set("PARENT", $category->getParent()); - $loopResultRow->set("ID", $category->getId()); - $loopResultRow->set("URL", $category->getUrl()); - $loopResultRow->set("LINK", $category->getLink()); - $loopResultRow->set("NB_CHILD", $category->countChild()); + $loopResultRow->set("ID", $product->getId()); + $loopResultRow->set("REF",$product->getRef()); + $loopResultRow->set("TITLE",$product->getTitle()); + $loopResultRow->set("CHAPO", $product->getChapo()); + $loopResultRow->set("DESCRIPTION", $product->getDescription()); + $loopResultRow->set("POSTSCRIPTUM", $product->getPostscriptum()); + //$loopResultRow->set("CATEGORY", $product->getCategory()); + + //$loopResultRow->set("URL", $product->getUrl()); $loopResult->addRow($loopResultRow); } diff --git a/core/lib/Thelia/Core/Template/Smarty/Plugins/TheliaLoop.php b/core/lib/Thelia/Core/Template/Smarty/Plugins/TheliaLoop.php index 3bc019c59..e02db2726 100755 --- a/core/lib/Thelia/Core/Template/Smarty/Plugins/TheliaLoop.php +++ b/core/lib/Thelia/Core/Template/Smarty/Plugins/TheliaLoop.php @@ -78,7 +78,6 @@ class TheliaLoop implements SmartyPluginInterface if (empty($params['name'])) throw new \InvalidArgumentException("Missing 'name' parameter in loop arguments"); - if (empty($params['type'])) throw new \InvalidArgumentException("Missing 'type' parameter in loop arguments"); @@ -279,7 +278,7 @@ class TheliaLoop implements SmartyPluginInterface $argumentsCollection = $loop->getArgs(); foreach( $argumentsCollection as $argument ) { - $value = isset($smartyParam[$argument->name]) ? $smartyParam[$argument->name] : null; + $value = isset($smartyParam[$argument->name]) ? (string)$smartyParam[$argument->name] : null; /* check if mandatory */ if($value === null && $argument->mandatory) { @@ -304,11 +303,13 @@ class TheliaLoop implements SmartyPluginInterface /* set default */ /* did it as last checking for we consider default value is acceptable no matter type or empty restriction */ - if($value === null) { - $value = $argument->default; + if($value === null && $argument->default !== null) { + $value = (string)$argument->default; } - $loop->{$argument->name} = $value === null ? null : $argument->type->getFormatedValue($value); + $test = $argument->type->getFormatedValue($value); + + $loop->{$argument->name} = $value === null ? null : $test; } if (!empty($faultActor)) { diff --git a/core/lib/Thelia/Model/ProductPeer.php b/core/lib/Thelia/Model/ProductPeer.php index 63e4f6970..b8b356d3c 100755 --- a/core/lib/Thelia/Model/ProductPeer.php +++ b/core/lib/Thelia/Model/ProductPeer.php @@ -18,4 +18,8 @@ use Thelia\Model\om\BaseProductPeer; */ class ProductPeer extends BaseProductPeer { + public static function getPriceDependingOnPromoExpression() + { + return 'IF(' . self::PROMO . '=1, ' . self::PRICE2 . ', ' . self::PRICE . ')'; + } } diff --git a/core/lib/Thelia/Model/ProductQuery.php b/core/lib/Thelia/Model/ProductQuery.php index 23c807592..cbd9893ae 100755 --- a/core/lib/Thelia/Model/ProductQuery.php +++ b/core/lib/Thelia/Model/ProductQuery.php @@ -18,4 +18,15 @@ use Thelia\Model\om\BaseProductQuery; */ class ProductQuery extends BaseProductQuery { + public function filterByPriceDependingOnPromo($minPrice = null, $maxPrice = null) + { + if ($minPrice !== null) { + $this->where(ProductPeer::getPriceDependingOnPromoExpression() . ' ' . \Criteria::GREATER_EQUAL . ' ?', $minPrice); + } + if ($maxPrice !== null) { + $this->where(ProductPeer::getPriceDependingOnPromoExpression() . ' ' . \Criteria::LESS_EQUAL . ' ?', $maxPrice); + } + + return $this; + } } diff --git a/templates/smarty-sample/category.html b/templates/smarty-sample/category.html new file mode 100755 index 000000000..c6c70c69a --- /dev/null +++ b/templates/smarty-sample/category.html @@ -0,0 +1,8 @@ +{loop name="category0" type="category" parent="0"} +

1 - CATEGORY : #TITLE

+

+ {loop name="category1" type="category" parent="#ID"} +

2 - SUBCATEGORY : #TITLE

+ {/loop} +

1bis - CATEGORY : #TITLE

+{/loop} \ No newline at end of file From 63e9707cc7ccf49c6b9e42c6208ef4ddcb7a7ed6 Mon Sep 17 00:00:00 2001 From: Etienne Roudeix Date: Tue, 2 Jul 2013 16:35:08 +0200 Subject: [PATCH 07/17] parser trouble sample --- templates/smarty-sample/category.html | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 templates/smarty-sample/category.html diff --git a/templates/smarty-sample/category.html b/templates/smarty-sample/category.html new file mode 100644 index 000000000..bc72f9c38 --- /dev/null +++ b/templates/smarty-sample/category.html @@ -0,0 +1,8 @@ +{loop name="category0" type="category" parent="0"} +

1 - CATEGORY : #TITLE

+

+{loop name="category1" type="category" parent="#ID"} +

2 - SUBCATEGORY : #TITLE

+{/loop} +

1bis - CATEGORY : #TITLE

+{/loop} \ No newline at end of file From 18e49e7ebe531b71f5e3770c43760392cde56fe0 Mon Sep 17 00:00:00 2001 From: franck Date: Tue, 2 Jul 2013 19:18:41 +0200 Subject: [PATCH 08/17] Introduction of loop scopes. --- .gitignore | 1 + composer.json | 2 - .../Admin/Controller/BaseAdminController.php | 7 +- .../Core/Template/Element/LoopResultRow.php | 9 ++- .../Core/Template/Smarty/Plugins/Assetic.php | 2 +- .../Template/Smarty/Plugins/TheliaLoop.php | 58 +++++++++++--- .../Template/Smarty/Plugins/Translation.php | 2 +- core/lib/Thelia/Model/Admin.php | 25 ++++++- core/lib/Thelia/Model/Customer.php | 27 ++++++- .../Encoder/PasswordHashEncoderTest.php | 75 +++++++++++++++++++ .../Core/Security/SecurityManagerTest.php | 49 ++++++++++++ .../Token/UsernamePasswordTokenTest.php | 38 ++++++++++ templates/admin/default/login.html | 6 +- templates/smarty-sample/category.html | 43 +++++++++-- templates/smarty-sample/included.html | 12 +++ 15 files changed, 326 insertions(+), 30 deletions(-) create mode 100644 core/lib/Thelia/Tests/Core/Security/Encoder/PasswordHashEncoderTest.php create mode 100644 core/lib/Thelia/Tests/Core/Security/SecurityManagerTest.php create mode 100644 core/lib/Thelia/Tests/Core/Security/Token/UsernamePasswordTokenTest.php create mode 100644 templates/smarty-sample/included.html diff --git a/.gitignore b/.gitignore index b2dc20145..ae4975b08 100755 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,4 @@ coverage local/cache/* composer.lock web/assets/* +web/.htaccess diff --git a/composer.json b/composer.json index 9cc776517..81055efd8 100755 --- a/composer.json +++ b/composer.json @@ -27,8 +27,6 @@ "symfony/form": "2.2.*", "symfony/validator": "2.2.*", - "symfony/security": "2.2.*", - "symfony/templating": "2.2.*", "smarty/smarty": "v3.1.13", "kriswallsmith/assetic": "1.2.*@dev", diff --git a/core/lib/Thelia/Admin/Controller/BaseAdminController.php b/core/lib/Thelia/Admin/Controller/BaseAdminController.php index 3aa0e9281..f63f87d21 100755 --- a/core/lib/Thelia/Admin/Controller/BaseAdminController.php +++ b/core/lib/Thelia/Admin/Controller/BaseAdminController.php @@ -48,7 +48,7 @@ class BaseAdminController extends ContainerAware */ public function render($templateName, $args = array()) { - $args = array_merge($args, array('lang' => 'fr')); + $args = array_merge($args, array('lang' => 'fr')); // FIXME $response = new Response(); @@ -57,7 +57,7 @@ class BaseAdminController extends ContainerAware public function renderRaw($templateName, $args = array()) { - $args = array_merge($args, array('lang' => 'fr')); + $args = array_merge($args, array('lang' => 'fr')); // FIXME return $this->getParser()->render($templateName, $args); } @@ -90,5 +90,6 @@ class BaseAdminController extends ContainerAware return $this->getFormFactory()->createBuilder("form"); } - + protected function isGranted() { + } } \ No newline at end of file diff --git a/core/lib/Thelia/Core/Template/Element/LoopResultRow.php b/core/lib/Thelia/Core/Template/Element/LoopResultRow.php index 4a14521c7..73d2f937a 100755 --- a/core/lib/Thelia/Core/Template/Element/LoopResultRow.php +++ b/core/lib/Thelia/Core/Template/Element/LoopResultRow.php @@ -29,12 +29,12 @@ class LoopResultRow public function set($key, $value) { - $this->substitution["#".$key] = $value; + $this->substitution[$key] = $value; } public function get($key) { - return $this->substitution["#".$key]; + return $this->substitution[$key]; } public function getVarVal() @@ -42,4 +42,9 @@ class LoopResultRow return $this->substitution; } + public function getVars() + { + return array_keys($this->substitution); + } + } diff --git a/core/lib/Thelia/Core/Template/Smarty/Plugins/Assetic.php b/core/lib/Thelia/Core/Template/Smarty/Plugins/Assetic.php index f79f98a1a..4418bd50c 100755 --- a/core/lib/Thelia/Core/Template/Smarty/Plugins/Assetic.php +++ b/core/lib/Thelia/Core/Template/Smarty/Plugins/Assetic.php @@ -35,7 +35,7 @@ class Assetic implements SmartyPluginInterface { $web_root = THELIA_WEB_DIR; - $asset_dir_from_web_root = '/assets/admin/default'; // FIXME + $asset_dir_from_web_root = 'assets/admin/default'; // FIXME $this->asset_manager = new SmartyAssetsManager($web_root, $asset_dir_from_web_root); } diff --git a/core/lib/Thelia/Core/Template/Smarty/Plugins/TheliaLoop.php b/core/lib/Thelia/Core/Template/Smarty/Plugins/TheliaLoop.php index aad155eae..6c04ef4ba 100755 --- a/core/lib/Thelia/Core/Template/Smarty/Plugins/TheliaLoop.php +++ b/core/lib/Thelia/Core/Template/Smarty/Plugins/TheliaLoop.php @@ -42,6 +42,9 @@ class TheliaLoop implements SmartyPluginInterface protected $dispatcher; + protected $loopstack = array(); + protected $varstack = array(); + public function __construct(Request $request, EventDispatcherInterface $dispatcher) { $this->request = $request; @@ -71,37 +74,75 @@ class TheliaLoop implements SmartyPluginInterface if ($content === null) { + // 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)) { + throw new \InvalidArgumentException("A loop named '$name' already exists in the current scope."); + } + $loop = $this->createLoopInstance(strtolower($params['type'])); $this->getLoopArgument($loop, $params); $loopResults = $loop->exec(); - $template->assignByRef($name, $loopResults); + $this->loopstack[$name] = $loopResults; + } else { - $loopResults = $template->getTemplateVars($name); + $loopResults = $this->loopstack[$name]; $loopResults->next(); } if ($loopResults->valid()) { + $loopResultRow = $loopResults->current(); + // On first iteration, save variables that may be overwritten by this loop + if (! isset($this->varstack[$name])) { + + $saved_vars = array(); + + $varlist = $loopResultRow->getVars(); + $varlist[] = 'LOOP_COUNT'; + $varlist[] = 'LOOP_TOTAL'; + + foreach($varlist as $var) { + $saved_vars[$var] = $template->getTemplateVars($var); + } + + $this->varstack[$name] = $saved_vars; + } + foreach($loopResultRow->getVarVal() as $var => $val) { - $template->assign(substr($var, 1), $val); + $template->assign($var, $val); } - $template->assign('__COUNT__', 1 + $loopResults->key()); - $template->assign('__TOTAL__', $loopResults->getCount()); + // Assign meta information + $template->assign('LOOP_COUNT', 1 + $loopResults->key()); + $template->assign('LOOP_TOTAL', $loopResults->getCount()); $repeat = $loopResults->valid(); } + // Loop is terminated. Cleanup. + if (! $repeat) { + + // Restore previous variables values before terminating + if (isset($this->varstack[$name])) { + + foreach($this->varstack[$name] as $var => $value) { + $template->assign($var, $value); + } + + unset($this->varstack[$name]); + } + } if ($content !== null) { if ($loopResults->isEmpty()) $content = ""; + return $content; } } @@ -158,14 +199,11 @@ class TheliaLoop implements SmartyPluginInterface $loopName = $params['rel']; - // Find loop results in the current template vars - $loopResults = $template->getTemplateVars($loopName); - - if (empty($loopResults)) { + if (! isset($this->loopstack[$loopName])) { throw new \InvalidArgumentException("Loop $loopName is not defined."); } - return $loopResults->isEmpty(); + return $this->loopstack[$loopName]->isEmpty(); } /** diff --git a/core/lib/Thelia/Core/Template/Smarty/Plugins/Translation.php b/core/lib/Thelia/Core/Template/Smarty/Plugins/Translation.php index 403cfac80..ec984613e 100755 --- a/core/lib/Thelia/Core/Template/Smarty/Plugins/Translation.php +++ b/core/lib/Thelia/Core/Template/Smarty/Plugins/Translation.php @@ -44,7 +44,7 @@ class Translation implements SmartyPluginInterface } // TODO - return "[$string]"; + return "$string"; } /** diff --git a/core/lib/Thelia/Model/Admin.php b/core/lib/Thelia/Model/Admin.php index 854ccb068..68b151193 100755 --- a/core/lib/Thelia/Model/Admin.php +++ b/core/lib/Thelia/Model/Admin.php @@ -3,7 +3,7 @@ namespace Thelia\Model; use Thelia\Model\om\BaseAdmin; - +use Symfony\Component\Security\Core\User\UserInterface; /** * Skeleton subclass for representing a row from the 'admin' table. @@ -16,6 +16,27 @@ use Thelia\Model\om\BaseAdmin; * * @package propel.generator.Thelia.Model */ -class Admin extends BaseAdmin +class Admin extends BaseAdmin implements UserInterface { + /** + * {@inheritDoc} + */ + public function getUsername() { + return $this->getLogin(); + } + + /** + * {@inheritDoc} + */ + public function eraseCredentials() { + $this->setPassword(null); + } + + /** + * {@inheritDoc} + */ + public function getRoles() { + return array(new Role('USER_CUSTOMER')); + } + } diff --git a/core/lib/Thelia/Model/Customer.php b/core/lib/Thelia/Model/Customer.php index 835f91e59..1002d8d94 100755 --- a/core/lib/Thelia/Model/Customer.php +++ b/core/lib/Thelia/Model/Customer.php @@ -3,6 +3,8 @@ namespace Thelia\Model; use Thelia\Model\om\BaseCustomer; +use Symfony\Component\Security\Core\User\UserInterface; +use Symfony\Component\Security\Core\Role\Role; /** @@ -16,6 +18,29 @@ use Thelia\Model\om\BaseCustomer; * * @package propel.generator.Thelia.Model */ -class Customer extends BaseCustomer +class Customer extends BaseCustomer implements UserInterface { + /** + * {@inheritDoc} + */ + + public function getUsername() { + return $this->getEmail(); + } + + /** + * {@inheritDoc} + */ + public function eraseCredentials() { + $this->setPassword(null); + } + + /** + * {@inheritDoc} + */ + public function getRoles() { + return array(new Role('USER_CUSTOMER')); + } } + + diff --git a/core/lib/Thelia/Tests/Core/Security/Encoder/PasswordHashEncoderTest.php b/core/lib/Thelia/Tests/Core/Security/Encoder/PasswordHashEncoderTest.php new file mode 100644 index 000000000..7590b3788 --- /dev/null +++ b/core/lib/Thelia/Tests/Core/Security/Encoder/PasswordHashEncoderTest.php @@ -0,0 +1,75 @@ +encode('password', 'sha512', 'a simple salt'); + + // echo "PASS=\{$pass\}"; + + $this->assertEquals("L3f/gGy4nBVhi8WSsC1a7E9JM8U+rtk6ZT+NiqX8M1UDJv6mahQEZ1z2cN/y9pixH+hgWbkBitONMiXWscomoQ==", $pass, "Expected password not found."); + } + + public function testIsEqual() + { + $encoder = new PasswordHashEncoder(); + + $exp = "L3f/gGy4nBVhi8WSsC1a7E9JM8U+rtk6ZT+NiqX8M1UDJv6mahQEZ1z2cN/y9pixH+hgWbkBitONMiXWscomoQ=="; + + $this->assertTrue($encoder->isEqual($exp, 'password', 'sha512', 'a simple salt')); + } + + public function testWrongPass() + { + $encoder = new PasswordHashEncoder(); + + $exp = "L3f/gGy4nBVhi8WSsC1a7E9JM8U+rtk6ZT+NiqX8M1UDJv6mahQEZ1z2cN/y9pixH+hgWbkBitONMiXWscomoQ=="; + + $this->assertFalse($encoder->isEqual($exp, 'grongron', 'sha512', 'a simple salt')); + } + + public function testWrongSalt() + { + $encoder = new PasswordHashEncoder(); + + $exp = "L3f/gGy4nBVhi8WSsC1a7E9JM8U+rtk6ZT+NiqX8M1UDJv6mahQEZ1z2cN/y9pixH+hgWbkBitONMiXWscomoQ=="; + + $this->assertFalse($encoder->isEqual($exp, 'password', 'sha512', 'another salt')); + } + + public function testWrongAlgo() + { + $encoder = new PasswordHashEncoder(); + + $exp = "L3f/gGy4nBVhi8WSsC1a7E9JM8U+rtk6ZT+NiqX8M1UDJv6mahQEZ1z2cN/y9pixH+hgWbkBitONMiXWscomoQ=="; + + $this->assertFalse($encoder->isEqual($exp, 'password', 'md5', 'another salt')); + } + + /** + * @expectedException LogicException + */ + public function testUnsupportedAlgo() + { + $encoder = new PasswordHashEncoder(); + + $exp = "L3f/gGy4nBVhi8WSsC1a7E9JM8U+rtk6ZT+NiqX8M1UDJv6mahQEZ1z2cN/y9pixH+hgWbkBitONMiXWscomoQ=="; + + $encoder->isEqual($exp, 'password', 'sbonk', 'another salt'); + } + + /** + * @expectedException LogicException + */ + public function testEncodeWrongAlgorithm() + { + $encoder = new PasswordHashEncoder(); + + $encoder->encode('password', 'pouët', 'a simple salt'); + } +} \ No newline at end of file diff --git a/core/lib/Thelia/Tests/Core/Security/SecurityManagerTest.php b/core/lib/Thelia/Tests/Core/Security/SecurityManagerTest.php new file mode 100644 index 000000000..84cb0d1ef --- /dev/null +++ b/core/lib/Thelia/Tests/Core/Security/SecurityManagerTest.php @@ -0,0 +1,49 @@ +. */ +/* */ +/*************************************************************************************/ + +namespace Thelia\Tests\Security; + +use Thelia\Core\Security\SecurityManager; +/** + * + * @author Franck Allimant + * + */ +class SecurityManagerTest extends \PHPUnit_Framework_TestCase +{ + public function testGetSetToken() + { + /* + $context = new SecurityManager($authProvider)( + $this->getMock('AuthenticationProviderInterface'), + $this->getMock('Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface') + ); + $this->assertNull($context->getToken()); + + $context->setToken($token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface')); + $this->assertSame($token, $context->getToken()); + */ + // $this->assertFalse(1==1, "faux !"); + } +} diff --git a/core/lib/Thelia/Tests/Core/Security/Token/UsernamePasswordTokenTest.php b/core/lib/Thelia/Tests/Core/Security/Token/UsernamePasswordTokenTest.php new file mode 100644 index 000000000..147c5b816 --- /dev/null +++ b/core/lib/Thelia/Tests/Core/Security/Token/UsernamePasswordTokenTest.php @@ -0,0 +1,38 @@ +assertFalse($token->isAuthenticated()); + + $token = new UsernamePasswordToken('username', 'password', true); + $this->assertTrue($token->isAuthenticated()); + } + + /** + * @expectedException LogicException + */ + public function testSetAuthenticatedToTrue() + { + $token = new UsernamePasswordToken('foo', 'bar', true); + $token->setAuthenticated(true); + } + + public function testSetAuthenticatedToFalse() + { + $token = new UsernamePasswordToken('foo', 'bar', true); + $token->setAuthenticated(false); + $this->assertFalse($token->isAuthenticated()); + } + + public function testEraseCredentials() + { + $token = new UsernamePasswordToken('foo', 'bar', true); + $token->eraseCredentials(); + $this->assertEquals('', $token->getCredentials()); + } +} \ No newline at end of file diff --git a/templates/admin/default/login.html b/templates/admin/default/login.html index 5c33d742e..20fadb19b 100755 --- a/templates/admin/default/login.html +++ b/templates/admin/default/login.html @@ -2,7 +2,7 @@ {include file='includes/header.inc.html'}
- + {{intl l='abcd'}|capitalize} @@ -15,7 +15,7 @@

{intl l='Thelia Back Office'}

-
+ {form_field_hidden form=$form} {form_field form=$form.username} {form_error form=$form.username} @@ -27,7 +27,9 @@ {/form_field} + {form_field form=$form.remember_me} + {/form_field}
diff --git a/templates/smarty-sample/category.html b/templates/smarty-sample/category.html index bc72f9c38..14c4757d6 100644 --- a/templates/smarty-sample/category.html +++ b/templates/smarty-sample/category.html @@ -1,8 +1,39 @@ +{include file="included.html"} + {loop name="category0" type="category" parent="0"} -

1 - CATEGORY : #TITLE

-

-{loop name="category1" type="category" parent="#ID"} -

2 - SUBCATEGORY : #TITLE

+

Out before - CATEGORY : #TITLE (#LOOP_COUNT / #LOOP_TOTAL)

+ {loop name="category1" type="category" parent="#ID"} +

Inner - SUBCATEGORY : #TITLE (#LOOP_COUNT / #LOOP_TOTAL)

+ {/loop} + + {#myid=#ID} + + {loop name="category2" type="category" parent="#ID"} +

Inner 2 before - SUBCATEGORY : #TITLE (#LOOP_COUNT / #LOOP_TOTAL)

+ + {loop name="category3" type="category" parent="#myid"} +

Inner inner 2 - SUBCATEGORY : #TITLE (#LOOP_COUNT / #LOOP_TOTAL)

+ {/loop} + +

Inner 2 after - SUBCATEGORY : #TITLE (#LOOP_COUNT / #LOOP_TOTAL)

+ {/loop} + +

Out after - CATEGORY : #TITLE (#LOOP_COUNT / #LOOP_TOTAL)

+
+ + {ifloop rel="category2"} +

Hey, y'a d'la categorie 2 !

+ {/ifloop} + + {elseloop rel="category2"} +

Hey, y'a PAS de categorie 2 !

+ {/elseloop} + + {loop name="category2" type="category" parent="#myid"} +

Exter 2 - SUBCATEGORY : #TITLE (#LOOP_COUNT / #LOOP_TOTAL)

+ {/loop} +{/loop} + +{loop name="category2" type="category" parent="1"} +

Final Exter 2 - SUBCATEGORY : #TITLE (#LOOP_COUNT / #LOOP_TOTAL)

{/loop} -

1bis - CATEGORY : #TITLE

-{/loop} \ No newline at end of file diff --git a/templates/smarty-sample/included.html b/templates/smarty-sample/included.html new file mode 100644 index 000000000..93581ff7f --- /dev/null +++ b/templates/smarty-sample/included.html @@ -0,0 +1,12 @@ +{loop name="included0" type="category" parent="0"} +

Out before - CATEGORY : #TITLE (#LOOP_COUNT / #LOOP_TOTAL)

+ {loop name="category1" type="category" parent="#ID"} +

Inner - SUBCATEGORY : #TITLE (#LOOP_COUNT / #LOOP_TOTAL)

+ {/loop} + + {loop name="category2" type="category" parent="#ID"} +

Inner 2 - SUBCATEGORY : #TITLE (#LOOP_COUNT / #LOOP_TOTAL)

+ {/loop} +

Out after - CATEGORY : #TITLE (#LOOP_COUNT / #LOOP_TOTAL)

+
+{/loop} \ No newline at end of file From 34ffdd12f5db39fd830c951e3e40f35edcad0216 Mon Sep 17 00:00:00 2001 From: franck Date: Wed, 3 Jul 2013 10:48:00 +0200 Subject: [PATCH 09/17] Moved Security package to the proper location --- .../Authentication/AuthenticationProviderInterface.php | 0 .../Authentication/UsernamePasswordAuthenticator.php | 0 .../Security/{Security => }/Encoder/PasswordEncoderInterface.php | 0 .../Core/Security/{Security => }/Encoder/PasswordHashEncoder.php | 0 .../Exception/AuthenticationTokenNotFoundException.php | 0 core/lib/Thelia/Core/Security/{Security => }/SecurityManager.php | 0 .../Thelia/Core/Security/{Security => }/Token/AbstractToken.php | 0 .../Thelia/Core/Security/{Security => }/Token/TokenInterface.php | 0 .../Core/Security/{Security => }/Token/UsernamePasswordToken.php | 0 .../Core/Security/{Security => }/User/AdminUserProvider.php | 0 .../Core/Security/{Security => }/User/CustomerUserProvider.php | 0 .../Thelia/Core/Security/{Security => }/User/UserInterface.php | 0 .../Core/Security/{Security => }/User/UserProviderInterface.php | 0 13 files changed, 0 insertions(+), 0 deletions(-) rename core/lib/Thelia/Core/Security/{Security => }/Authentication/AuthenticationProviderInterface.php (100%) rename core/lib/Thelia/Core/Security/{Security => }/Authentication/UsernamePasswordAuthenticator.php (100%) rename core/lib/Thelia/Core/Security/{Security => }/Encoder/PasswordEncoderInterface.php (100%) rename core/lib/Thelia/Core/Security/{Security => }/Encoder/PasswordHashEncoder.php (100%) rename core/lib/Thelia/Core/Security/{Security => }/Exception/AuthenticationTokenNotFoundException.php (100%) rename core/lib/Thelia/Core/Security/{Security => }/SecurityManager.php (100%) rename core/lib/Thelia/Core/Security/{Security => }/Token/AbstractToken.php (100%) rename core/lib/Thelia/Core/Security/{Security => }/Token/TokenInterface.php (100%) rename core/lib/Thelia/Core/Security/{Security => }/Token/UsernamePasswordToken.php (100%) rename core/lib/Thelia/Core/Security/{Security => }/User/AdminUserProvider.php (100%) rename core/lib/Thelia/Core/Security/{Security => }/User/CustomerUserProvider.php (100%) rename core/lib/Thelia/Core/Security/{Security => }/User/UserInterface.php (100%) rename core/lib/Thelia/Core/Security/{Security => }/User/UserProviderInterface.php (100%) diff --git a/core/lib/Thelia/Core/Security/Security/Authentication/AuthenticationProviderInterface.php b/core/lib/Thelia/Core/Security/Authentication/AuthenticationProviderInterface.php similarity index 100% rename from core/lib/Thelia/Core/Security/Security/Authentication/AuthenticationProviderInterface.php rename to core/lib/Thelia/Core/Security/Authentication/AuthenticationProviderInterface.php diff --git a/core/lib/Thelia/Core/Security/Security/Authentication/UsernamePasswordAuthenticator.php b/core/lib/Thelia/Core/Security/Authentication/UsernamePasswordAuthenticator.php similarity index 100% rename from core/lib/Thelia/Core/Security/Security/Authentication/UsernamePasswordAuthenticator.php rename to core/lib/Thelia/Core/Security/Authentication/UsernamePasswordAuthenticator.php diff --git a/core/lib/Thelia/Core/Security/Security/Encoder/PasswordEncoderInterface.php b/core/lib/Thelia/Core/Security/Encoder/PasswordEncoderInterface.php similarity index 100% rename from core/lib/Thelia/Core/Security/Security/Encoder/PasswordEncoderInterface.php rename to core/lib/Thelia/Core/Security/Encoder/PasswordEncoderInterface.php diff --git a/core/lib/Thelia/Core/Security/Security/Encoder/PasswordHashEncoder.php b/core/lib/Thelia/Core/Security/Encoder/PasswordHashEncoder.php similarity index 100% rename from core/lib/Thelia/Core/Security/Security/Encoder/PasswordHashEncoder.php rename to core/lib/Thelia/Core/Security/Encoder/PasswordHashEncoder.php diff --git a/core/lib/Thelia/Core/Security/Security/Exception/AuthenticationTokenNotFoundException.php b/core/lib/Thelia/Core/Security/Exception/AuthenticationTokenNotFoundException.php similarity index 100% rename from core/lib/Thelia/Core/Security/Security/Exception/AuthenticationTokenNotFoundException.php rename to core/lib/Thelia/Core/Security/Exception/AuthenticationTokenNotFoundException.php diff --git a/core/lib/Thelia/Core/Security/Security/SecurityManager.php b/core/lib/Thelia/Core/Security/SecurityManager.php similarity index 100% rename from core/lib/Thelia/Core/Security/Security/SecurityManager.php rename to core/lib/Thelia/Core/Security/SecurityManager.php diff --git a/core/lib/Thelia/Core/Security/Security/Token/AbstractToken.php b/core/lib/Thelia/Core/Security/Token/AbstractToken.php similarity index 100% rename from core/lib/Thelia/Core/Security/Security/Token/AbstractToken.php rename to core/lib/Thelia/Core/Security/Token/AbstractToken.php diff --git a/core/lib/Thelia/Core/Security/Security/Token/TokenInterface.php b/core/lib/Thelia/Core/Security/Token/TokenInterface.php similarity index 100% rename from core/lib/Thelia/Core/Security/Security/Token/TokenInterface.php rename to core/lib/Thelia/Core/Security/Token/TokenInterface.php diff --git a/core/lib/Thelia/Core/Security/Security/Token/UsernamePasswordToken.php b/core/lib/Thelia/Core/Security/Token/UsernamePasswordToken.php similarity index 100% rename from core/lib/Thelia/Core/Security/Security/Token/UsernamePasswordToken.php rename to core/lib/Thelia/Core/Security/Token/UsernamePasswordToken.php diff --git a/core/lib/Thelia/Core/Security/Security/User/AdminUserProvider.php b/core/lib/Thelia/Core/Security/User/AdminUserProvider.php similarity index 100% rename from core/lib/Thelia/Core/Security/Security/User/AdminUserProvider.php rename to core/lib/Thelia/Core/Security/User/AdminUserProvider.php diff --git a/core/lib/Thelia/Core/Security/Security/User/CustomerUserProvider.php b/core/lib/Thelia/Core/Security/User/CustomerUserProvider.php similarity index 100% rename from core/lib/Thelia/Core/Security/Security/User/CustomerUserProvider.php rename to core/lib/Thelia/Core/Security/User/CustomerUserProvider.php diff --git a/core/lib/Thelia/Core/Security/Security/User/UserInterface.php b/core/lib/Thelia/Core/Security/User/UserInterface.php similarity index 100% rename from core/lib/Thelia/Core/Security/Security/User/UserInterface.php rename to core/lib/Thelia/Core/Security/User/UserInterface.php diff --git a/core/lib/Thelia/Core/Security/Security/User/UserProviderInterface.php b/core/lib/Thelia/Core/Security/User/UserProviderInterface.php similarity index 100% rename from core/lib/Thelia/Core/Security/Security/User/UserProviderInterface.php rename to core/lib/Thelia/Core/Security/User/UserProviderInterface.php From 92d15d44c7d056d448531a0d07b3e74950d56ecc Mon Sep 17 00:00:00 2001 From: Etienne Roudeix Date: Wed, 3 Jul 2013 10:52:24 +0200 Subject: [PATCH 10/17] new types --- .../lib/Thelia/Core/Template/Loop/Product.php | 6 +++ .../Tests/Type/AlphaNumStringListTypeTest.php | 48 +++++++++++++++++ .../Tests/Type/AlphaNumStringTypeTest.php | 43 +++++++++++++++ .../lib/Thelia/Tests/Type/IntListTypeTest.php | 2 +- .../Thelia/Type/AlphaNumStringListType.php | 52 +++++++++++++++++++ core/lib/Thelia/Type/AlphaNumStringType.php | 47 +++++++++++++++++ templates/smarty-sample/category.html | 48 ++++++----------- 7 files changed, 214 insertions(+), 32 deletions(-) create mode 100755 core/lib/Thelia/Tests/Type/AlphaNumStringListTypeTest.php create mode 100755 core/lib/Thelia/Tests/Type/AlphaNumStringTypeTest.php create mode 100755 core/lib/Thelia/Type/AlphaNumStringListType.php create mode 100755 core/lib/Thelia/Type/AlphaNumStringType.php diff --git a/core/lib/Thelia/Core/Template/Loop/Product.php b/core/lib/Thelia/Core/Template/Loop/Product.php index 38d6d9b75..c4447d4c6 100755 --- a/core/lib/Thelia/Core/Template/Loop/Product.php +++ b/core/lib/Thelia/Core/Template/Loop/Product.php @@ -95,6 +95,7 @@ class Product extends BaseLoop new Argument( 'ref', new TypeCollection( + new Type\AlphaNumStringType(), new Type\JsonType() ) ), @@ -139,6 +140,11 @@ class Product extends BaseLoop } if (!is_null($this->category)) { + + if(null !== $this->depth) { + + } + $search->filterByCategory( CategoryQuery::create()->filterById($this->category, \Criteria::IN)->find(), \Criteria::IN diff --git a/core/lib/Thelia/Tests/Type/AlphaNumStringListTypeTest.php b/core/lib/Thelia/Tests/Type/AlphaNumStringListTypeTest.php new file mode 100755 index 000000000..22d1c52ce --- /dev/null +++ b/core/lib/Thelia/Tests/Type/AlphaNumStringListTypeTest.php @@ -0,0 +1,48 @@ +. */ +/* */ +/*************************************************************************************/ + +namespace Thelia\Tests\Type; + +use Thelia\Type\AlphaNumStringListType; + +/** + * + * @author Etienne Roudeix + * + */ +class AlphaNumStringListTypeTest extends \PHPUnit_Framework_TestCase +{ + public function testAlphaNumStringListType() + { + $type = new AlphaNumStringListType(); + $this->assertTrue($type->isValid('FOO1,FOO_2,FOO-3')); + $this->assertFalse($type->isValid('FOO.1,FOO$_2,FOO-3')); + } + + public function testFormatAlphaNumStringListType() + { + $type = new AlphaNumStringListType(); + $this->assertTrue(is_array($type->getFormatedValue('FOO1,FOO_2,FOO-3'))); + $this->assertNull($type->getFormatedValue('5€')); + } +} diff --git a/core/lib/Thelia/Tests/Type/AlphaNumStringTypeTest.php b/core/lib/Thelia/Tests/Type/AlphaNumStringTypeTest.php new file mode 100755 index 000000000..46526715b --- /dev/null +++ b/core/lib/Thelia/Tests/Type/AlphaNumStringTypeTest.php @@ -0,0 +1,43 @@ +. */ +/* */ +/*************************************************************************************/ + +namespace Thelia\Tests\Type; + +use Thelia\Type\AlphaNumStringType; + +/** + * + * @author Etienne Roudeix + * + */ +class AlphaNumStringTypeTest extends \PHPUnit_Framework_TestCase +{ + public function testAlphaNumStringType() + { + $type = new AlphaNumStringType(); + $this->assertTrue($type->isValid('azs_qs-0-9ds')); + $this->assertFalse($type->isValid('3.3')); + $this->assertFalse($type->isValid('3 3')); + $this->assertFalse($type->isValid('3€3')); + } +} diff --git a/core/lib/Thelia/Tests/Type/IntListTypeTest.php b/core/lib/Thelia/Tests/Type/IntListTypeTest.php index bbe410bd1..6f9cc1e99 100755 --- a/core/lib/Thelia/Tests/Type/IntListTypeTest.php +++ b/core/lib/Thelia/Tests/Type/IntListTypeTest.php @@ -40,7 +40,7 @@ class IntListTypeTest extends \PHPUnit_Framework_TestCase $this->assertFalse($intListType->isValid('1,2,3.3')); } - public function testFormatJsonType() + public function testFormatIntListType() { $intListType = new IntListType(); $this->assertTrue(is_array($intListType->getFormatedValue('1,2,3'))); diff --git a/core/lib/Thelia/Type/AlphaNumStringListType.php b/core/lib/Thelia/Type/AlphaNumStringListType.php new file mode 100755 index 000000000..1fa210cba --- /dev/null +++ b/core/lib/Thelia/Type/AlphaNumStringListType.php @@ -0,0 +1,52 @@ +. */ +/* */ +/*************************************************************************************/ +namespace Thelia\Type; + +/** + * + * @author Etienne Roudeix + * + */ + +class AlphaNumStringListType implements TypeInterface +{ + public function getType() + { + return 'Alphanumeric string list type'; + } + + public function isValid($values) + { + foreach(explode(',', $values) as $value) { + if(!preg_match('#^[a-zA-Z0-9\-_]+$#', $value)) + return false; + } + + return true; + } + + public function getFormatedValue($values) + { + return $this->isValid($values) ? explode(',', $values) : null; + } +} diff --git a/core/lib/Thelia/Type/AlphaNumStringType.php b/core/lib/Thelia/Type/AlphaNumStringType.php new file mode 100755 index 000000000..b893f8a46 --- /dev/null +++ b/core/lib/Thelia/Type/AlphaNumStringType.php @@ -0,0 +1,47 @@ +. */ +/* */ +/*************************************************************************************/ +namespace Thelia\Type; + +/** + * + * @author Etienne Roudeix + * + */ + +class AlphaNumStringType implements TypeInterface +{ + public function getType() + { + return 'Alphanumeric string type'; + } + + public function isValid($value) + { + return preg_match('#^[a-zA-Z0-9\-_]+$#', $value) ? true : false; + } + + public function getFormatedValue($value) + { + return $this->isValid($value) ? $value : null; + } +} diff --git a/templates/smarty-sample/category.html b/templates/smarty-sample/category.html index 14c4757d6..0055dee99 100755 --- a/templates/smarty-sample/category.html +++ b/templates/smarty-sample/category.html @@ -1,39 +1,25 @@ -{include file="included.html"} - {loop name="category0" type="category" parent="0"} -

Out before - CATEGORY : #TITLE (#LOOP_COUNT / #LOOP_TOTAL)

+

CATEGORY : #TITLE

{loop name="category1" type="category" parent="#ID"} -

Inner - SUBCATEGORY : #TITLE (#LOOP_COUNT / #LOOP_TOTAL)

+
+

SUBCATEGORY : #TITLE (#LOOP_COUNT / #LOOP_TOTAL)

+ {loop name="product" type="product" category="#ID"} +

PRODUCT : #REF / #TITLE

+ #PRICE € + {/loop} +
+
{/loop} - {#myid=#ID} - - {loop name="category2" type="category" parent="#ID"} -

Inner 2 before - SUBCATEGORY : #TITLE (#LOOP_COUNT / #LOOP_TOTAL)

- - {loop name="category3" type="category" parent="#myid"} -

Inner inner 2 - SUBCATEGORY : #TITLE (#LOOP_COUNT / #LOOP_TOTAL)

- {/loop} - -

Inner 2 after - SUBCATEGORY : #TITLE (#LOOP_COUNT / #LOOP_TOTAL)

+ {loop name="product" type="product" category="#ID"} +

PRODUCT : #REF / #TITLE

+ #PRICE € {/loop} - -

Out after - CATEGORY : #TITLE (#LOOP_COUNT / #LOOP_TOTAL)


- - {ifloop rel="category2"} -

Hey, y'a d'la categorie 2 !

- {/ifloop} - - {elseloop rel="category2"} -

Hey, y'a PAS de categorie 2 !

- {/elseloop} - - {loop name="category2" type="category" parent="#myid"} -

Exter 2 - SUBCATEGORY : #TITLE (#LOOP_COUNT / #LOOP_TOTAL)

- {/loop} +
{/loop} - -{loop name="category2" type="category" parent="1"} -

Final Exter 2 - SUBCATEGORY : #TITLE (#LOOP_COUNT / #LOOP_TOTAL)

+

PRODUCTS selected by ref

+{loop name="product" type="product" ref='REF1,REF2'} +

PRODUCT : #REF / #TITLE

+ #PRICE € {/loop} From b4a2862131d44164dbb357a1d1be1b474bba2473 Mon Sep 17 00:00:00 2001 From: Etienne Roudeix Date: Wed, 3 Jul 2013 13:20:55 +0200 Subject: [PATCH 11/17] product loop --- .../lib/Thelia/Core/Template/Loop/Product.php | 60 ++++++++++++++++--- .../Core/Template/Smarty/SmartyParser.php | 4 +- core/lib/Thelia/Model/CategoryQuery.php | 26 +++++--- core/lib/Thelia/Model/ProductPeer.php | 4 -- core/lib/Thelia/Model/ProductQuery.php | 11 ---- templates/smarty-sample/category.html | 12 ++-- templates/smarty-sample/index.html | 14 ++--- 7 files changed, 85 insertions(+), 46 deletions(-) diff --git a/core/lib/Thelia/Core/Template/Loop/Product.php b/core/lib/Thelia/Core/Template/Loop/Product.php index c4447d4c6..4a3510196 100755 --- a/core/lib/Thelia/Core/Template/Loop/Product.php +++ b/core/lib/Thelia/Core/Template/Loop/Product.php @@ -33,6 +33,7 @@ use Thelia\Log\Tlog; use Thelia\Model\CategoryQuery; use Thelia\Model\ProductCategoryQuery; +use Thelia\Model\ProductPeer; use Thelia\Model\ProductQuery; use Thelia\Type\TypeCollection; use Thelia\Type; @@ -95,15 +96,14 @@ class Product extends BaseLoop new Argument( 'ref', new TypeCollection( - new Type\AlphaNumStringType(), - new Type\JsonType() + new Type\AlphaNumStringListType() ) ), Argument::createIntListTypeArgument('category'), Argument::createBooleanTypeArgument('new'), Argument::createBooleanTypeArgument('promo'), Argument::createFloatTypeArgument('min_price'), - Argument::createFloatTypeArgument('max_prix'), + Argument::createFloatTypeArgument('max_price'), Argument::createIntTypeArgument('min_stock'), Argument::createFloatTypeArgument('min_weight'), Argument::createFloatTypeArgument('max_weight'), @@ -114,7 +114,7 @@ class Product extends BaseLoop new Argument( 'order', new TypeCollection( - new Type\EnumType('alpha', 'alpha_reverse', 'reverse', 'min_price', 'max_price', 'category', 'manual', 'manual_reverse', 'ref', 'promo', 'new') + new Type\EnumType(array('alpha', 'alpha_reverse', 'reverse', 'min_price', 'max_price', 'category', 'manual', 'manual_reverse', 'ref', 'promo', 'new')) ) ), Argument::createBooleanTypeArgument('random', 0), @@ -140,13 +140,16 @@ class Product extends BaseLoop } if (!is_null($this->category)) { + $categories = CategoryQuery::create()->filterById($this->category, \Criteria::IN)->find(); if(null !== $this->depth) { - + foreach(CategoryQuery::findAllChild($this->category, $this->depth) as $subCategory) { + $categories->prepend($subCategory); + } } $search->filterByCategory( - CategoryQuery::create()->filterById($this->category, \Criteria::IN)->find(), + $categories, \Criteria::IN ); } @@ -163,7 +166,45 @@ class Product extends BaseLoop $search->filterByNewness(0, \Criteria::EQUAL); } - $search->filterByPriceDependingOnPromo($this->min_price, $this->max_price); //@todo review + if (null != $this->min_stock) { + $search->filterByQuantity($this->min_stock, \Criteria::GREATER_EQUAL); + } + + if(null !== $this->min_price) { + $search->condition('in_promo', ProductPeer::PROMO . \Criteria::EQUAL . '1') + ->condition('not_in_promo', ProductPeer::PROMO . \Criteria::NOT_EQUAL . '1') + ->condition('min_price2', ProductPeer::PRICE2 . \Criteria::GREATER_EQUAL . '?', $this->min_price) + ->condition('min_price', ProductPeer::PRICE . \Criteria::GREATER_EQUAL . '?', $this->min_price) + ->combine(array('in_promo', 'min_price2'), \Criteria::LOGICAL_AND, 'in_promo_min_price') + ->combine(array('not_in_promo', 'min_price'), \Criteria::LOGICAL_AND, 'not_in_promo_min_price') + ->where(array('not_in_promo_min_price', 'in_promo_min_price'), \Criteria::LOGICAL_OR); + } + + if(null !== $this->max_price) { + $search->condition('in_promo', ProductPeer::PROMO . \Criteria::EQUAL . '1') + ->condition('not_in_promo', ProductPeer::PROMO . \Criteria::NOT_EQUAL . '1') + ->condition('max_price2', ProductPeer::PRICE2 . \Criteria::LESS_EQUAL . '?', $this->max_price) + ->condition('max_price', ProductPeer::PRICE . \Criteria::LESS_EQUAL . '?', $this->max_price) + ->combine(array('in_promo', 'max_price2'), \Criteria::LOGICAL_AND, 'in_promo_max_price') + ->combine(array('not_in_promo', 'max_price'), \Criteria::LOGICAL_AND, 'not_in_promo_max_price') + ->where(array('not_in_promo_max_price', 'in_promo_max_price'), \Criteria::LOGICAL_OR); + } + + /*if(null !== $this->min_weight) { + $search->condition('min_price2', ProductPeer::PRICE2 . \Criteria::GREATER_EQUAL . '?', $this->min_price) + ->condition('min_price', ProductPeer::PRICE . \Criteria::GREATER_EQUAL . '?', $this->min_price) + ->combine(array('in_promo', 'min_price2'), \Criteria::LOGICAL_AND, 'in_promo_min_price') + ->combine(array('not_in_promo', 'min_price'), \Criteria::LOGICAL_AND, 'not_in_promo_min_price') + ->where(array('not_in_promo_min_price', 'in_promo_min_price'), \Criteria::LOGICAL_OR); + } + + if(null !== $this->max_weight) { + $search->condition('min_price2', ProductPeer::PRICE2 . \Criteria::GREATER_EQUAL . '?', $this->min_price) + ->condition('min_price', ProductPeer::PRICE . \Criteria::GREATER_EQUAL . '?', $this->min_price) + ->combine(array('in_promo', 'min_price2'), \Criteria::LOGICAL_AND, 'in_promo_min_price') + ->combine(array('not_in_promo', 'min_price'), \Criteria::LOGICAL_AND, 'not_in_promo_min_price') + ->where(array('not_in_promo_min_price', 'in_promo_min_price'), \Criteria::LOGICAL_OR); + }*/ if ($this->current === true) { $search->filterById($this->request->get("product_id")); @@ -264,7 +305,10 @@ class Product extends BaseLoop $loopResultRow->set("CHAPO", $product->getChapo()); $loopResultRow->set("DESCRIPTION", $product->getDescription()); $loopResultRow->set("POSTSCRIPTUM", $product->getPostscriptum()); - //$loopResultRow->set("CATEGORY", $product->getCategory()); + $loopResultRow->set("PRICE", $product->getPrice()); + $loopResultRow->set("PROMO_PRICE", $product->getPrice2() ? : 0); + $loopResultRow->set("PROMO", $product->getPromo()); + $loopResultRow->set("NEW", $product->getNewness()); //$loopResultRow->set("URL", $product->getUrl()); diff --git a/core/lib/Thelia/Core/Template/Smarty/SmartyParser.php b/core/lib/Thelia/Core/Template/Smarty/SmartyParser.php index 42a79a6d4..ac83bf57e 100755 --- a/core/lib/Thelia/Core/Template/Smarty/SmartyParser.php +++ b/core/lib/Thelia/Core/Template/Smarty/SmartyParser.php @@ -72,10 +72,10 @@ class SmartyParser extends Smarty implements ParserInterface // The default HTTP status $this->status = 200; - $this->registerFilter('pre', array($this, "pretest")); + $this->registerFilter('pre', array($this, "preThelia")); } - public function pretest($tpl_source, \Smarty_Internal_Template $template) + public function preThelia($tpl_source, \Smarty_Internal_Template $template) { $new_source = preg_replace('`{#([a-zA-Z][a-zA-Z0-9\-_]*)(.*)}`', '{\$$1$2}', $tpl_source); $new_source = preg_replace('`#([a-zA-Z][a-zA-Z0-9\-_]*)`', '{\$$1|default:\'#$1\'}', $new_source); diff --git a/core/lib/Thelia/Model/CategoryQuery.php b/core/lib/Thelia/Model/CategoryQuery.php index 61ff52dfb..ae8f984eb 100755 --- a/core/lib/Thelia/Model/CategoryQuery.php +++ b/core/lib/Thelia/Model/CategoryQuery.php @@ -35,7 +35,7 @@ class CategoryQuery extends BaseCategoryQuery * * find all category children for a given category. an array of \Thelia\Model\Category is return * - * @param $categoryId the category id + * @param $categoryId the category id or an array of id * @param int $depth max depth you want to search * @param int $currentPos don't change this param, it is used for recursion * @return \Thelia\Model\Category[] @@ -43,17 +43,25 @@ class CategoryQuery extends BaseCategoryQuery public static function findAllChild($categoryId, $depth = 0, $currentPos = 0) { $result = array(); - $currentPos++; - if($depth == $currentPos && $depth != 0) return; + if(is_array($categoryId)) { + foreach($categoryId as $categorySingleId) { + $result = array_merge($result, (array) self::findAllChild($categorySingleId, $depth, $currentPos)); + } + } else { + $currentPos++; - $categories = self::create() - ->filterByParent($categoryId) - ->find(); + if($depth == $currentPos && $depth != 0) return; - foreach ($categories as $category) { - array_push($result, $category); - $result = array_merge($result, (array) self::findAllChild($category->getId(), $depth, $currentPos)); + $categories = self::create() + ->filterByParent($categoryId) + ->find(); + + + foreach ($categories as $category) { + array_push($result, $category); + $result = array_merge($result, (array) self::findAllChild($category->getId(), $depth, $currentPos)); + } } return $result; diff --git a/core/lib/Thelia/Model/ProductPeer.php b/core/lib/Thelia/Model/ProductPeer.php index b8b356d3c..63e4f6970 100755 --- a/core/lib/Thelia/Model/ProductPeer.php +++ b/core/lib/Thelia/Model/ProductPeer.php @@ -18,8 +18,4 @@ use Thelia\Model\om\BaseProductPeer; */ class ProductPeer extends BaseProductPeer { - public static function getPriceDependingOnPromoExpression() - { - return 'IF(' . self::PROMO . '=1, ' . self::PRICE2 . ', ' . self::PRICE . ')'; - } } diff --git a/core/lib/Thelia/Model/ProductQuery.php b/core/lib/Thelia/Model/ProductQuery.php index cbd9893ae..23c807592 100755 --- a/core/lib/Thelia/Model/ProductQuery.php +++ b/core/lib/Thelia/Model/ProductQuery.php @@ -18,15 +18,4 @@ use Thelia\Model\om\BaseProductQuery; */ class ProductQuery extends BaseProductQuery { - public function filterByPriceDependingOnPromo($minPrice = null, $maxPrice = null) - { - if ($minPrice !== null) { - $this->where(ProductPeer::getPriceDependingOnPromoExpression() . ' ' . \Criteria::GREATER_EQUAL . ' ?', $minPrice); - } - if ($maxPrice !== null) { - $this->where(ProductPeer::getPriceDependingOnPromoExpression() . ' ' . \Criteria::LESS_EQUAL . ' ?', $maxPrice); - } - - return $this; - } } diff --git a/templates/smarty-sample/category.html b/templates/smarty-sample/category.html index 0055dee99..a4b704b68 100755 --- a/templates/smarty-sample/category.html +++ b/templates/smarty-sample/category.html @@ -1,4 +1,4 @@ -{loop name="category0" type="category" parent="0"} +{*loop name="category0" type="category" parent="0"}

CATEGORY : #TITLE

{loop name="category1" type="category" parent="#ID"}
@@ -17,9 +17,11 @@ {/loop}

-{/loop} -

PRODUCTS selected by ref

-{loop name="product" type="product" ref='REF1,REF2'} +{/loop*} +

PRODUCTS

+{loop name="product" type="product" min_price="200" max_price="1000"}

PRODUCT : #REF / #TITLE

- #PRICE € + price : #PRICE €
+ promo price : #PROMO_PRICE €
+ is promo : #PROMO
{/loop} diff --git a/templates/smarty-sample/index.html b/templates/smarty-sample/index.html index 24f964510..d24efa89c 100755 --- a/templates/smarty-sample/index.html +++ b/templates/smarty-sample/index.html @@ -32,7 +32,7 @@ An image from asset directory :

Category loop example

    {loop type="category" name="catloop1"} -
  • {$__COUNT__}/{$__TOTAL__} : {$ID} {$TITLE}, children: {$NB_CHILD} +
  • {$LOOP_COUNT}/{$LOOP_TOTAL} : {$ID} {$TITLE}, children: {$NB_CHILD} {ifloop rel="inner1"}
      {loop type="category" name="inner1" parent="{$ID}"} @@ -52,7 +52,7 @@ An image from asset directory : Hey ! Loop catloop2 is not empty:
        {loop type="category" name="catloop2" parent="12"} -
      • {$__COUNT__}/{$__TOTAL__} : {$ID} {$TITLE}
      • +
      • {$LOOP_COUNT}/{$LOOP_TOTAL} : {$ID} {$TITLE}
      • {/loop}
      {/ifloop} @@ -69,7 +69,7 @@ An image from asset directory : Loop catloop3 is not empty:
        {loop type="category" name="catloop3" parent="0"} -
      • {$__COUNT__}/{$__TOTAL__} : {$ID} {$TITLE}
      • +
      • {$LOOP_COUNT}/{$LOOP_TOTAL} : {$ID} {$TITLE}
      • {/loop}
      {/ifloop} @@ -105,20 +105,20 @@ An image from asset directory :

      PAGE 1

        {loop type="category" name="catloopwithpagination1" limit="2" page="1"} -
      • {$__COUNT__}/{$__TOTAL__} : {$ID} {$TITLE}
      • +
      • {$LOOP_COUNT}/{$LOOP_TOTAL} : {$ID} {$TITLE}
      • {/loop}

      PAGE 2

        {loop type="category" name="catloopwithpagination2" limit="2" page="2"} -
      • {$__COUNT__}/{$__TOTAL__} : {$ID} {$TITLE}
      • +
      • {$LOOP_COUNT}/{$LOOP_TOTAL} : {$ID} {$TITLE}
      • {/loop}

      PAGE 1000

        {loop type="category" name="catloopwithpagination1000" limit="2" page="1000"} -
      • {$__COUNT__}/{$__TOTAL__} : {$ID} {$TITLE}
      • +
      • {$LOOP_COUNT}/{$LOOP_TOTAL} : {$ID} {$TITLE}
      • {/loop} {elseloop rel="catloopwithpagination1000"} @@ -133,7 +133,7 @@ An image from asset directory :

        PAGE {$current_page} :

          {loop type="category" name="catloopwithpaginationchoice" limit="2" page="{$current_page}"} -
        • {$__COUNT__}/{$__TOTAL__} : {$ID} {$TITLE}
        • +
        • {$LOOP_COUNT}/{$LOOP_TOTAL} : {$ID} {$TITLE}
        • {/loop}

        page choice

        From b5d9f69212e3d6a51cf5fd1ec1b4c2341c440be2 Mon Sep 17 00:00:00 2001 From: Etienne Roudeix Date: Wed, 3 Jul 2013 15:07:49 +0200 Subject: [PATCH 12/17] thelia loop --- .../lib/Thelia/Core/Template/Loop/Product.php | 19 ++++++------------- templates/smarty-sample/category.html | 3 ++- 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/core/lib/Thelia/Core/Template/Loop/Product.php b/core/lib/Thelia/Core/Template/Loop/Product.php index 4a3510196..7387a3699 100755 --- a/core/lib/Thelia/Core/Template/Loop/Product.php +++ b/core/lib/Thelia/Core/Template/Loop/Product.php @@ -190,21 +190,13 @@ class Product extends BaseLoop ->where(array('not_in_promo_max_price', 'in_promo_max_price'), \Criteria::LOGICAL_OR); } - /*if(null !== $this->min_weight) { - $search->condition('min_price2', ProductPeer::PRICE2 . \Criteria::GREATER_EQUAL . '?', $this->min_price) - ->condition('min_price', ProductPeer::PRICE . \Criteria::GREATER_EQUAL . '?', $this->min_price) - ->combine(array('in_promo', 'min_price2'), \Criteria::LOGICAL_AND, 'in_promo_min_price') - ->combine(array('not_in_promo', 'min_price'), \Criteria::LOGICAL_AND, 'not_in_promo_min_price') - ->where(array('not_in_promo_min_price', 'in_promo_min_price'), \Criteria::LOGICAL_OR); + if(null !== $this->min_weight) { + $search->filterByWeight($this->min_weight, \Criteria::GREATER_EQUAL); } if(null !== $this->max_weight) { - $search->condition('min_price2', ProductPeer::PRICE2 . \Criteria::GREATER_EQUAL . '?', $this->min_price) - ->condition('min_price', ProductPeer::PRICE . \Criteria::GREATER_EQUAL . '?', $this->min_price) - ->combine(array('in_promo', 'min_price2'), \Criteria::LOGICAL_AND, 'in_promo_min_price') - ->combine(array('not_in_promo', 'min_price'), \Criteria::LOGICAL_AND, 'not_in_promo_min_price') - ->where(array('not_in_promo_min_price', 'in_promo_min_price'), \Criteria::LOGICAL_OR); - }*/ + $search->filterByWeight($this->max_weight, \Criteria::LESS_EQUAL); + } if ($this->current === true) { $search->filterById($this->request->get("product_id")); @@ -306,7 +298,8 @@ class Product extends BaseLoop $loopResultRow->set("DESCRIPTION", $product->getDescription()); $loopResultRow->set("POSTSCRIPTUM", $product->getPostscriptum()); $loopResultRow->set("PRICE", $product->getPrice()); - $loopResultRow->set("PROMO_PRICE", $product->getPrice2() ? : 0); + $loopResultRow->set("PROMO_PRICE", $product->getPrice2()); + $loopResultRow->set("WEIGHT", $product->getWeight()); $loopResultRow->set("PROMO", $product->getPromo()); $loopResultRow->set("NEW", $product->getNewness()); diff --git a/templates/smarty-sample/category.html b/templates/smarty-sample/category.html index a4b704b68..8a0dc2dba 100755 --- a/templates/smarty-sample/category.html +++ b/templates/smarty-sample/category.html @@ -19,9 +19,10 @@
        {/loop*}

        PRODUCTS

        -{loop name="product" type="product" min_price="200" max_price="1000"} +{loop name="product" type="product" min_weight="2.5" max_weight="10"}

        PRODUCT : #REF / #TITLE

        price : #PRICE €
        promo price : #PROMO_PRICE €
        is promo : #PROMO
        + weight : #WEIGHT
        {/loop} From 3073f9a43cf7ecdaacc7a94227aeccbe3a2fa06b Mon Sep 17 00:00:00 2001 From: Etienne Roudeix Date: Wed, 3 Jul 2013 16:07:18 +0200 Subject: [PATCH 13/17] fix thelia diese problems --- core/lib/Thelia/Config/Resources/config.xml | 4 ++ .../Core/Template/Element/LoopResultRow.php | 2 +- .../Template/Smarty/Plugins/TheliaSyntax.php | 54 +++++++++++++++++++ .../Core/Template/Smarty/SmartyParser.php | 11 ++-- templates/smarty-sample/category.html | 3 +- 5 files changed, 67 insertions(+), 7 deletions(-) create mode 100644 core/lib/Thelia/Core/Template/Smarty/Plugins/TheliaSyntax.php diff --git a/core/lib/Thelia/Config/Resources/config.xml b/core/lib/Thelia/Config/Resources/config.xml index c8d0ef53f..b163f1983 100755 --- a/core/lib/Thelia/Config/Resources/config.xml +++ b/core/lib/Thelia/Config/Resources/config.xml @@ -67,6 +67,10 @@ + + + + diff --git a/core/lib/Thelia/Core/Template/Element/LoopResultRow.php b/core/lib/Thelia/Core/Template/Element/LoopResultRow.php index 73d2f937a..3ac05fe5c 100755 --- a/core/lib/Thelia/Core/Template/Element/LoopResultRow.php +++ b/core/lib/Thelia/Core/Template/Element/LoopResultRow.php @@ -29,7 +29,7 @@ class LoopResultRow public function set($key, $value) { - $this->substitution[$key] = $value; + $this->substitution[$key] = $value === null ? '' : $value; } public function get($key) diff --git a/core/lib/Thelia/Core/Template/Smarty/Plugins/TheliaSyntax.php b/core/lib/Thelia/Core/Template/Smarty/Plugins/TheliaSyntax.php new file mode 100644 index 000000000..9b508cf3e --- /dev/null +++ b/core/lib/Thelia/Core/Template/Smarty/Plugins/TheliaSyntax.php @@ -0,0 +1,54 @@ +. */ +/* */ +/*************************************************************************************/ +namespace Thelia\Core\Template\Smarty\Plugins; + +use Thelia\Core\Template\Smarty\SmartyPluginDescriptor; +use Thelia\Core\Template\Smarty\SmartyPluginInterface; + +/** + * Class TheliaSyntax + * @package Thelia\Core\Template\Smarty\Plugins + * + * @author Etienne Roudeix + */ +class TheliaSyntax implements SmartyPluginInterface +{ + public function dieseCancel($value, $diese) + { + if($value === null) { + return $diese; + } + + return $value; + } + + /** + * @return SmartyPluginDescriptor[] + */ + public function getPluginDescriptors() + { + return array( + new SmartyPluginDescriptor("modifier", "dieseCanceller", $this, "dieseCancel") + ); + } +} diff --git a/core/lib/Thelia/Core/Template/Smarty/SmartyParser.php b/core/lib/Thelia/Core/Template/Smarty/SmartyParser.php index ac83bf57e..8e34a89ed 100755 --- a/core/lib/Thelia/Core/Template/Smarty/SmartyParser.php +++ b/core/lib/Thelia/Core/Template/Smarty/SmartyParser.php @@ -29,10 +29,11 @@ class SmartyParser extends Smarty implements ParserInterface protected $status = 200; /** - * @param \Symfony\Component\HttpFoundation\Request $request - * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher - * @param bool $template - * @param string $env Environment define for the kernel application. Used for the cache directory + * @param Request $request + * @param EventDispatcherInterface $dispatcher + * @param bool $template + * @param string $env + * @param bool $debug */ public function __construct(Request $request, EventDispatcherInterface $dispatcher, $template = false, $env = "prod", $debug = false) { @@ -78,7 +79,7 @@ class SmartyParser extends Smarty implements ParserInterface public function preThelia($tpl_source, \Smarty_Internal_Template $template) { $new_source = preg_replace('`{#([a-zA-Z][a-zA-Z0-9\-_]*)(.*)}`', '{\$$1$2}', $tpl_source); - $new_source = preg_replace('`#([a-zA-Z][a-zA-Z0-9\-_]*)`', '{\$$1|default:\'#$1\'}', $new_source); + $new_source = preg_replace('`#([a-zA-Z][a-zA-Z0-9\-_]*)`', '{\$$1|dieseCanceller:\'#$1\'}', $new_source); return $new_source; } diff --git a/templates/smarty-sample/category.html b/templates/smarty-sample/category.html index 8a0dc2dba..b99390cb6 100755 --- a/templates/smarty-sample/category.html +++ b/templates/smarty-sample/category.html @@ -19,10 +19,11 @@
        {/loop*}

        PRODUCTS

        -{loop name="product" type="product" min_weight="2.5" max_weight="10"} +{loop name="product" type="product"}

        PRODUCT : #REF / #TITLE

        price : #PRICE €
        promo price : #PROMO_PRICE €
        is promo : #PROMO
        + is new : #NEW
        weight : #WEIGHT
        {/loop} From a7a9688d415069d61d00d72a8a8fef83aba58546 Mon Sep 17 00:00:00 2001 From: Etienne Roudeix Date: Fri, 5 Jul 2013 11:12:51 +0200 Subject: [PATCH 14/17] product lopp --- core/lib/Thelia/Core/Template/Loop/Product.php | 7 ++++--- templates/smarty-sample/category.html | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/core/lib/Thelia/Core/Template/Loop/Product.php b/core/lib/Thelia/Core/Template/Loop/Product.php index 7387a3699..29294e709 100755 --- a/core/lib/Thelia/Core/Template/Loop/Product.php +++ b/core/lib/Thelia/Core/Template/Loop/Product.php @@ -240,10 +240,11 @@ class Product extends BaseLoop case "reverse": $search->orderByPosition(\Criteria::DESC); break; - /*case "min_price": - $search->orderByPosition(\Criteria::DESC); + case "min_price": + //$search->order + //$search->orderByPosition(\Criteria::DESC); break; - case "max_price": + /*case "max_price": $search->orderByPosition(\Criteria::DESC); break; case "category": diff --git a/templates/smarty-sample/category.html b/templates/smarty-sample/category.html index b99390cb6..594be9cc6 100755 --- a/templates/smarty-sample/category.html +++ b/templates/smarty-sample/category.html @@ -19,7 +19,7 @@
        {/loop*}

        PRODUCTS

        -{loop name="product" type="product"} +{loop name="product" type="product" order="min_price"}

        PRODUCT : #REF / #TITLE

        price : #PRICE €
        promo price : #PROMO_PRICE €
        From faf2c1de77be796ee4dfca6770bc1844f73bb5d6 Mon Sep 17 00:00:00 2001 From: Etienne Roudeix Date: Fri, 5 Jul 2013 11:54:26 +0200 Subject: [PATCH 15/17] product loop --- .../lib/Thelia/Core/Template/Loop/Product.php | 80 ++++++++++--------- 1 file changed, 41 insertions(+), 39 deletions(-) diff --git a/core/lib/Thelia/Core/Template/Loop/Product.php b/core/lib/Thelia/Core/Template/Loop/Product.php index 29294e709..a869a8c51 100755 --- a/core/lib/Thelia/Core/Template/Loop/Product.php +++ b/core/lib/Thelia/Core/Template/Loop/Product.php @@ -23,6 +23,8 @@ namespace Thelia\Core\Template\Loop; +use Propel\Runtime\ActiveQuery\Criteria; + use Thelia\Core\Template\Element\BaseLoop; use Thelia\Core\Template\Element\LoopResult; use Thelia\Core\Template\Element\LoopResultRow; @@ -132,15 +134,15 @@ class Product extends BaseLoop $search = ProductQuery::create(); if (!is_null($this->id)) { - $search->filterById($this->id, \Criteria::IN); + $search->filterById($this->id, Criteria::IN); } if (!is_null($this->ref)) { - $search->filterByRef($this->ref, \Criteria::IN); + $search->filterByRef($this->ref, Criteria::IN); } if (!is_null($this->category)) { - $categories = CategoryQuery::create()->filterById($this->category, \Criteria::IN)->find(); + $categories = CategoryQuery::create()->filterById($this->category, Criteria::IN)->find(); if(null !== $this->depth) { foreach(CategoryQuery::findAllChild($this->category, $this->depth) as $subCategory) { @@ -150,58 +152,58 @@ class Product extends BaseLoop $search->filterByCategory( $categories, - \Criteria::IN + Criteria::IN ); } if ($this->new === true) { - $search->filterByNewness(1, \Criteria::EQUAL); + $search->filterByNewness(1, Criteria::EQUAL); } else if($this->new === false) { - $search->filterByNewness(0, \Criteria::EQUAL); + $search->filterByNewness(0, Criteria::EQUAL); } if ($this->promo === true) { - $search->filterByPromo(1, \Criteria::EQUAL); + $search->filterByPromo(1, Criteria::EQUAL); } else if($this->promo === false) { - $search->filterByNewness(0, \Criteria::EQUAL); + $search->filterByNewness(0, Criteria::EQUAL); } if (null != $this->min_stock) { - $search->filterByQuantity($this->min_stock, \Criteria::GREATER_EQUAL); + $search->filterByQuantity($this->min_stock, Criteria::GREATER_EQUAL); } if(null !== $this->min_price) { - $search->condition('in_promo', ProductPeer::PROMO . \Criteria::EQUAL . '1') - ->condition('not_in_promo', ProductPeer::PROMO . \Criteria::NOT_EQUAL . '1') - ->condition('min_price2', ProductPeer::PRICE2 . \Criteria::GREATER_EQUAL . '?', $this->min_price) - ->condition('min_price', ProductPeer::PRICE . \Criteria::GREATER_EQUAL . '?', $this->min_price) - ->combine(array('in_promo', 'min_price2'), \Criteria::LOGICAL_AND, 'in_promo_min_price') - ->combine(array('not_in_promo', 'min_price'), \Criteria::LOGICAL_AND, 'not_in_promo_min_price') - ->where(array('not_in_promo_min_price', 'in_promo_min_price'), \Criteria::LOGICAL_OR); + $search->condition('in_promo', ProductPeer::PROMO . Criteria::EQUAL . '1') + ->condition('not_in_promo', ProductPeer::PROMO . Criteria::NOT_EQUAL . '1') + ->condition('min_price2', ProductPeer::PRICE2 . Criteria::GREATER_EQUAL . '?', $this->min_price) + ->condition('min_price', ProductPeer::PRICE . Criteria::GREATER_EQUAL . '?', $this->min_price) + ->combine(array('in_promo', 'min_price2'), Criteria::LOGICAL_AND, 'in_promo_min_price') + ->combine(array('not_in_promo', 'min_price'), Criteria::LOGICAL_AND, 'not_in_promo_min_price') + ->where(array('not_in_promo_min_price', 'in_promo_min_price'), Criteria::LOGICAL_OR); } if(null !== $this->max_price) { - $search->condition('in_promo', ProductPeer::PROMO . \Criteria::EQUAL . '1') - ->condition('not_in_promo', ProductPeer::PROMO . \Criteria::NOT_EQUAL . '1') - ->condition('max_price2', ProductPeer::PRICE2 . \Criteria::LESS_EQUAL . '?', $this->max_price) - ->condition('max_price', ProductPeer::PRICE . \Criteria::LESS_EQUAL . '?', $this->max_price) - ->combine(array('in_promo', 'max_price2'), \Criteria::LOGICAL_AND, 'in_promo_max_price') - ->combine(array('not_in_promo', 'max_price'), \Criteria::LOGICAL_AND, 'not_in_promo_max_price') - ->where(array('not_in_promo_max_price', 'in_promo_max_price'), \Criteria::LOGICAL_OR); + $search->condition('in_promo', ProductPeer::PROMO . Criteria::EQUAL . '1') + ->condition('not_in_promo', ProductPeer::PROMO . Criteria::NOT_EQUAL . '1') + ->condition('max_price2', ProductPeer::PRICE2 . Criteria::LESS_EQUAL . '?', $this->max_price) + ->condition('max_price', ProductPeer::PRICE . Criteria::LESS_EQUAL . '?', $this->max_price) + ->combine(array('in_promo', 'max_price2'), Criteria::LOGICAL_AND, 'in_promo_max_price') + ->combine(array('not_in_promo', 'max_price'), Criteria::LOGICAL_AND, 'not_in_promo_max_price') + ->where(array('not_in_promo_max_price', 'in_promo_max_price'), Criteria::LOGICAL_OR); } if(null !== $this->min_weight) { - $search->filterByWeight($this->min_weight, \Criteria::GREATER_EQUAL); + $search->filterByWeight($this->min_weight, Criteria::GREATER_EQUAL); } if(null !== $this->max_weight) { - $search->filterByWeight($this->max_weight, \Criteria::LESS_EQUAL); + $search->filterByWeight($this->max_weight, Criteria::LESS_EQUAL); } if ($this->current === true) { $search->filterById($this->request->get("product_id")); } elseif($this->current === false) { - $search->filterById($this->request->get("product_id"), \Criteria::NOT_IN); + $search->filterById($this->request->get("product_id"), Criteria::NOT_IN); } if ($this->current_category === true) { @@ -209,22 +211,22 @@ class Product extends BaseLoop CategoryQuery::create()->filterByProduct( ProductCategoryQuery::create()->filterByProductId( $this->request->get("product_id"), - \Criteria::EQUAL + Criteria::EQUAL )->find(), - \Criteria::IN + Criteria::IN )->find(), - \Criteria::IN + Criteria::IN ); } elseif($this->current_category === false) { $search->filterByCategory( CategoryQuery::create()->filterByProduct( ProductCategoryQuery::create()->filterByProductId( $this->request->get("product_id"), - \Criteria::EQUAL + Criteria::EQUAL )->find(), - \Criteria::IN + Criteria::IN )->find(), - \Criteria::NOT_IN + Criteria::NOT_IN ); } @@ -238,17 +240,17 @@ class Product extends BaseLoop $search->addDescendingOrderByColumn(\Thelia\Model\CategoryI18nPeer::TITLE); break; case "reverse": - $search->orderByPosition(\Criteria::DESC); + $search->orderByPosition(Criteria::DESC); break; case "min_price": //$search->order - //$search->orderByPosition(\Criteria::DESC); + //$search->orderByPosition(Criteria::DESC); break; /*case "max_price": - $search->orderByPosition(\Criteria::DESC); + $search->orderByPosition(Criteria::DESC); break; case "category": - $search->orderByPosition(\Criteria::DESC); + $search->orderByPosition(Criteria::DESC); break;*/ case "manual": $search->addAscendingOrderByColumn(\Thelia\Model\ProductPeer::POSITION); @@ -276,15 +278,15 @@ class Product extends BaseLoop } if (!is_null($this->exclude)) { - $search->filterById($this->exclude, \Criteria::NOT_IN); + $search->filterById($this->exclude, Criteria::NOT_IN); } /** - * \Criteria::INNER_JOIN in second parameter for joinWithI18n exclude query without translation. + * Criteria::INNER_JOIN in second parameter for joinWithI18n exclude query without translation. * * @todo : verify here if we want results for row without translations. */ - $search->joinWithI18n($this->request->getSession()->get('locale', 'en_US'), \Criteria::INNER_JOIN); + $search->joinWithI18n($this->request->getSession()->get('locale', 'en_US'), Criteria::INNER_JOIN); $products = $this->search($search, $pagination); From c27c62f3f361c237a70f332e7c237407fc08c9d3 Mon Sep 17 00:00:00 2001 From: Etienne Roudeix Date: Fri, 5 Jul 2013 12:04:13 +0200 Subject: [PATCH 16/17] use propel 2 --- .../lib/Thelia/Core/Template/Loop/Product.php | 28 +++++-------------- 1 file changed, 7 insertions(+), 21 deletions(-) diff --git a/core/lib/Thelia/Core/Template/Loop/Product.php b/core/lib/Thelia/Core/Template/Loop/Product.php index a869a8c51..c80a47ade 100755 --- a/core/lib/Thelia/Core/Template/Loop/Product.php +++ b/core/lib/Thelia/Core/Template/Loop/Product.php @@ -37,31 +37,13 @@ use Thelia\Model\CategoryQuery; use Thelia\Model\ProductCategoryQuery; use Thelia\Model\ProductPeer; use Thelia\Model\ProductQuery; +use Thelia\Model\ConfigQuery; use Thelia\Type\TypeCollection; use Thelia\Type; /** * - * Category loop, all params available : - * - * - id : can be an id (eq : 3) or a "string list" (eg: 3, 4, 5) - * - parent : categories having this parent id - * - current : current id is used if you are on a category page - * - not_empty : if value is 1, category and subcategories must have at least 1 product - * - visible : default 1, if you want category not visible put 0 - * - order : all value available : - * * alpha : sorting by title alphabetical order - * * alpha_reverse : sorting by title alphabetical reverse order - * * reverse : sorting by position descending - * * by default results are sorting by position ascending - * - random : if 1, random results. Default value is 0 - * - exclude : all category id you want to exclude (as for id, an integer or a "string list" can be used) - * - * example : - * - * - * #TITLE : #ID - * + * Product loop * * * Class Product @@ -286,7 +268,11 @@ class Product extends BaseLoop * * @todo : verify here if we want results for row without translations. */ - $search->joinWithI18n($this->request->getSession()->get('locale', 'en_US'), Criteria::INNER_JOIN); + + $search->joinWithI18n( + $this->request->getSession()->get('locale', 'en_US'), + (ConfigQuery::read("default_lang_without_translation", 1)) ? Criteria::LEFT_JOIN : Criteria::INNER_JOIN + ); $products = $this->search($search, $pagination); From 7f0dd5c13b5677fe26333000304fa2f9fe5f94a5 Mon Sep 17 00:00:00 2001 From: Etienne Roudeix Date: Fri, 5 Jul 2013 17:15:22 +0200 Subject: [PATCH 17/17] product loop --- .../Core/Template/Smarty/Plugins/TheliaLoop.php | 10 +++++----- templates/smarty-sample/index.html | 14 +++++++------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/core/lib/Thelia/Core/Template/Smarty/Plugins/TheliaLoop.php b/core/lib/Thelia/Core/Template/Smarty/Plugins/TheliaLoop.php index 18ae783f0..86a29da6d 100755 --- a/core/lib/Thelia/Core/Template/Smarty/Plugins/TheliaLoop.php +++ b/core/lib/Thelia/Core/Template/Smarty/Plugins/TheliaLoop.php @@ -130,13 +130,13 @@ class TheliaLoop implements SmartyPluginInterface $template->assign($var, $val); } - // Assign meta information - $template->assign('LOOP_COUNT', 1 + $loopResults->key()); - $template->assign('LOOP_TOTAL', $loopResults->getCount()); - - $repeat = true; + $repeat = true; } + // Assign meta information + $template->assign('LOOP_COUNT', 1 + $loopResults->key()); + $template->assign('LOOP_TOTAL', $loopResults->getCount()); + // Loop is terminated. Cleanup. if (! $repeat) { // Restore previous variables values before terminating diff --git a/templates/smarty-sample/index.html b/templates/smarty-sample/index.html index 5a73519b0..c185ae5f8 100755 --- a/templates/smarty-sample/index.html +++ b/templates/smarty-sample/index.html @@ -17,7 +17,7 @@ An image from asset directory :

        Category loop example

          {loop type="category" name="catloop1"} -
        • {$__COUNT__}/{$__TOTAL__} : {$ID} {$TITLE}, children: {$NB_CHILD} +
        • {$LOOP_COUNT}/{$LOOP_TOTAL} : {$ID} {$TITLE}, children: {$NB_CHILD} {ifloop rel="inner1"}
            {loop type="category" name="inner1" parent="{$ID}"} @@ -40,7 +40,7 @@ An image from asset directory : Hey ! Loop catloop2 is not empty:
              {loop type="category" name="catloop2" parent="12"} -
            • {$__COUNT__}/{$__TOTAL__} : {$ID} {$TITLE}
            • +
            • {$LOOP_COUNT}/{$LOOP_TOTAL} : {$ID} {$TITLE}
            • {/loop}
            {/ifloop} @@ -57,7 +57,7 @@ An image from asset directory : Loop catloop3 is not empty:
              {loop type="category" name="catloop3" parent="0"} -
            • {$__COUNT__}/{$__TOTAL__} : {$ID} {$TITLE}
            • +
            • {$LOOP_COUNT}/{$LOOP_TOTAL} : {$ID} {$TITLE}
            • {/loop}
            {/ifloop} @@ -86,20 +86,20 @@ An image from asset directory :

            PAGE 1

              {loop type="category" name="catloopwithpagination1" limit="2" page="1"} -
            • {$__COUNT__}/{$__TOTAL__} : {$ID} {$TITLE}
            • +
            • {$LOOP_COUNT}/{$LOOP_TOTAL} : {$ID} {$TITLE}
            • {/loop}

            PAGE 2

              {loop type="category" name="catloopwithpagination2" limit="2" page="2"} -
            • {$__COUNT__}/{$__TOTAL__} : {$ID} {$TITLE}
            • +
            • {$LOOP_COUNT}/{$LOOP_TOTAL} : {$ID} {$TITLE}
            • {/loop}

            PAGE 1000

              {loop type="category" name="catloopwithpagination1000" limit="2" page="1000"} -
            • {$__COUNT__}/{$__TOTAL__} : {$ID} {$TITLE}
            • +
            • {$LOOP_COUNT}/{$LOOP_TOTAL} : {$ID} {$TITLE}
            • {/loop} {elseloop rel="catloopwithpagination1000"} @@ -114,7 +114,7 @@ An image from asset directory :

              PAGE {$current_page} :

                {loop type="category" name="catloopwithpaginationchoice" limit="2" page="{$current_page}"} -
              • {$__COUNT__}/{$__TOTAL__} : {$ID} {$TITLE}
              • +
              • {$LOOP_COUNT}/{$LOOP_TOTAL} : {$ID} {$TITLE}
              • {/loop}

              page choice