Merge branch 'loops'

This commit is contained in:
Etienne Roudeix
2013-07-05 17:16:10 +02:00
52 changed files with 1844 additions and 69 deletions

View File

@@ -0,0 +1,60 @@
<?php
/*************************************************************************************/
/* */
/* Thelia */
/* */
/* Copyright (c) OpenStudio */
/* email : info@thelia.net */
/* web : http://www.thelia.net */
/* */
/* This program is free software; you can redistribute it and/or modify */
/* it under the terms of the GNU General Public License as published by */
/* the Free Software Foundation; either version 3 of the License */
/* */
/* This program is distributed in the hope that it will be useful, */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
/* GNU General Public License for more details. */
/* */
/* You should have received a copy of the GNU General Public License */
/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* */
/*************************************************************************************/
namespace Thelia\Core\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();
}
?>

View File

@@ -0,0 +1,67 @@
<?php
/*************************************************************************************/
/* */
/* Thelia */
/* */
/* Copyright (c) OpenStudio */
/* email : info@thelia.net */
/* web : http://www.thelia.net */
/* */
/* This program is free software; you can redistribute it and/or modify */
/* it under the terms of the GNU General Public License as published by */
/* the Free Software Foundation; either version 3 of the License */
/* */
/* This program is distributed in the hope that it will be useful, */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
/* GNU General Public License for more details. */
/* */
/* You should have received a copy of the GNU General Public License */
/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* */
/*************************************************************************************/
namespace Thelia\Core\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;
}
}

View File

@@ -0,0 +1,52 @@
<?php
/*************************************************************************************/
/* */
/* Thelia */
/* */
/* Copyright (c) OpenStudio */
/* email : info@thelia.net */
/* web : http://www.thelia.net */
/* */
/* This program is free software; you can redistribute it and/or modify */
/* it under the terms of the GNU General Public License as published by */
/* the Free Software Foundation; either version 3 of the License */
/* */
/* This program is distributed in the hope that it will be useful, */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
/* GNU General Public License for more details. */
/* */
/* You should have received a copy of the GNU General Public License */
/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* */
/*************************************************************************************/
namespace Thelia\Core\Security\Encoder;
/**
* This interface defines a password encoder.
*
* @author Franck Allimant <franck@cqfdev.fr>
*
*/
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);
}

View File

@@ -0,0 +1,66 @@
<?php
/*************************************************************************************/
/* */
/* Thelia */
/* */
/* Copyright (c) OpenStudio */
/* email : info@thelia.net */
/* web : http://www.thelia.net */
/* */
/* This program is free software; you can redistribute it and/or modify */
/* it under the terms of the GNU General Public License as published by */
/* the Free Software Foundation; either version 3 of the License */
/* */
/* This program is distributed in the hope that it will be useful, */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
/* GNU General Public License for more details. */
/* */
/* You should have received a copy of the GNU General Public License */
/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* */
/*************************************************************************************/
namespace Thelia\Core\Security\Encoder;
/**
* This interface defines a hash based password encoder.
*
* @author Franck Allimant <franck@cqfdev.fr>
*/
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;
}
}

View File

@@ -0,0 +1,28 @@
<?php
/*************************************************************************************/
/* */
/* Thelia */
/* */
/* Copyright (c) OpenStudio */
/* email : info@thelia.net */
/* web : http://www.thelia.net */
/* */
/* This program is free software; you can redistribute it and/or modify */
/* it under the terms of the GNU General Public License as published by */
/* the Free Software Foundation; either version 3 of the License */
/* */
/* This program is distributed in the hope that it will be useful, */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
/* GNU General Public License for more details. */
/* */
/* You should have received a copy of the GNU General Public License */
/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* */
/*************************************************************************************/
namespace Thelia\Core\Security\Exception;
class AuthenticationTokenNotFoundException extends \Exception
{
}

View File

@@ -0,0 +1,81 @@
<?php
/*************************************************************************************/
/* */
/* Thelia */
/* */
/* Copyright (c) OpenStudio */
/* email : info@thelia.net */
/* web : http://www.thelia.net */
/* */
/* This program is free software; you can redistribute it and/or modify */
/* it under the terms of the GNU General Public License as published by */
/* the Free Software Foundation; either version 3 of the License */
/* */
/* This program is distributed in the hope that it will be useful, */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
/* GNU General Public License for more details. */
/* */
/* You should have received a copy of the GNU General Public License */
/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* */
/*************************************************************************************/
namespace Thelia\Core\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 <franck@cqfdev.fr>
*/
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;
}
}

View File

@@ -0,0 +1,148 @@
<?php
namespace Thelia\Core\Security\Token;
use Thelia\Core\Security\User\UserInterface;
/**
* Base class for Token instances.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
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;
}
}

View File

@@ -0,0 +1,81 @@
<?php
/*************************************************************************************/
/* */
/* Thelia */
/* */
/* Copyright (c) OpenStudio */
/* email : info@thelia.net */
/* web : http://www.thelia.net */
/* */
/* This program is free software; you can redistribute it and/or modify */
/* it under the terms of the GNU General Public License as published by */
/* the Free Software Foundation; either version 3 of the License */
/* */
/* This program is distributed in the hope that it will be useful, */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
/* GNU General Public License for more details. */
/* */
/* You should have received a copy of the GNU General Public License */
/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* */
/*************************************************************************************/
namespace Thelia\Core\Security\Token;
/**
* TokenInterface is the interface for the user authentication information.
*
* Parts borrowed from Symfony Security Framework (Fabien Potencier <fabien@symfony.com> / Johannes M. Schmitt <schmittjoh@gmail.com>)
*/
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();
}

View File

@@ -0,0 +1,75 @@
<?php
namespace Thelia\Core\Security\Token;
use Thelia\Core\Security\User\UserInterface;
/**
* UsernamePasswordToken implements a username and password token.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
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);
}
}

View File

@@ -0,0 +1,18 @@
<?php
use Thelia\Core\Security\User\UserProviderInterface;
use Thelia\Model\Admin;
use Thelia\Core\Security\Encoder\PasswordEncoderInterface;
class AdminUserProvider implements UserProviderInterface {
public function getUser($key) {
$admin = new Admin();
$admin = AdminQuery::create()
->filterByLogin($key)
->findOne();
return $admin;
}
}

View File

@@ -0,0 +1,20 @@
<?php
use Thelia\Core\Security\User\UserProviderInterface;
use Thelia\Model\Customer;
use Thelia\Model\CustomerQuery;
use Thelia\Core\Security\UserNotFoundException;
use Thelia\Core\Security\Encoder\PasswordEncoderInterface;
class CustomerUserProvider implements UserProviderInterface {
public function getUser($key) {
$customer = new Customer();
$customer = CustomerQuery::create()
->filterByEmail($key)
->findOne();
return $customer;
}
}

View File

@@ -0,0 +1,42 @@
<?php
namespace Thelia\Core\Security\User;
/**
* This interface should be implemented by user classes
*
* @author Franck Allimant <franck@cqfdev.fr>
*
*/
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();
}

View File

@@ -0,0 +1,14 @@
<?php
namespace Thelia\Core\Security\User;
interface UserProviderInterface {
/**
* Returns a UserInterface instance
*
* @param $key the unique user key (username, email address, etc.)
* @return a UserInterface instance, or null if none was found.
*/
public function getUser($key);
}
?>

View File

@@ -68,6 +68,9 @@ abstract class BaseLoop
$this->dispatcher = $dispatcher;
}
/**
* @return \Thelia\Core\Template\Loop\Argument\ArgumentCollection
*/
public function getArgs()
{
return $this->defineArgs()->addArguments($this->getDefaultArgs());

View File

@@ -29,12 +29,12 @@ class LoopResultRow
public function set($key, $value)
{
$this->substitution["#".$key] = $value;
$this->substitution[$key] = $value === null ? '' : $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);
}
}

View File

@@ -72,6 +72,32 @@ 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\BooleanType()
),
$default,
$mandatory,
$empty
);
}
public static function createIntListTypeArgument($name, $default=null, $mandatory=false, $empty=true)
{
return new Argument(

View File

@@ -64,6 +64,7 @@ use Thelia\Type;
* Class Category
* @package Thelia\Core\Template\Loop
* @author Manuel Raynaud <mraynaud@openstudio.fr>
* @author Etienne Roudeix <eroudeix@openstudio.fr>
*/
class Category extends BaseLoop
{
@@ -85,9 +86,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',
@@ -95,7 +96,7 @@ class Category extends BaseLoop
new Type\EnumType('alpha', 'alpha_reverse', 'reverse')
)
),
Argument::createIntTypeArgument('random', 0),
Argument::createBooleanTypeArgument('random', 0),
Argument::createIntListTypeArgument('exclude')
);
}
@@ -110,28 +111,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":
@@ -148,7 +149,7 @@ class Category extends BaseLoop
break;
}
if ($this->random == 1) {
if ($this->random === true) {
$search->clearOrderByColumns();
$search->addAscendingOrderByColumn('RAND()');
}

View File

@@ -0,0 +1,303 @@
<?php
/*************************************************************************************/
/* */
/* Thelia */
/* */
/* Copyright (c) OpenStudio */
/* email : info@thelia.net */
/* web : http://www.thelia.net */
/* */
/* This program is free software; you can redistribute it and/or modify */
/* it under the terms of the GNU General Public License as published by */
/* the Free Software Foundation; either version 3 of the License */
/* */
/* This program is distributed in the hope that it will be useful, */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
/* GNU General Public License for more details. */
/* */
/* You should have received a copy of the GNU General Public License */
/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* */
/*************************************************************************************/
namespace Thelia\Core\Template\Loop;
use Propel\Runtime\ActiveQuery\Criteria;
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\Model\ProductCategoryQuery;
use Thelia\Model\ProductPeer;
use Thelia\Model\ProductQuery;
use Thelia\Model\ConfigQuery;
use Thelia\Type\TypeCollection;
use Thelia\Type;
/**
*
* Product loop
*
*
* Class Product
* @package Thelia\Core\Template\Loop
* @author Etienne Roudeix <eroudeix@openstudio.fr>
*/
class Product extends BaseLoop
{
public $id;
public $ref;
public $category;
public $new;
public $promo;
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;
/**
* @return ArgumentCollection
*/
protected function defineArgs()
{
return new ArgumentCollection(
Argument::createIntListTypeArgument('id'),
new Argument(
'ref',
new TypeCollection(
new Type\AlphaNumStringListType()
)
),
Argument::createIntListTypeArgument('category'),
Argument::createBooleanTypeArgument('new'),
Argument::createBooleanTypeArgument('promo'),
Argument::createFloatTypeArgument('min_price'),
Argument::createFloatTypeArgument('max_price'),
Argument::createIntTypeArgument('min_stock'),
Argument::createFloatTypeArgument('min_weight'),
Argument::createFloatTypeArgument('max_weight'),
Argument::createBooleanTypeArgument('current'),
Argument::createBooleanTypeArgument('current_category'),
Argument::createIntTypeArgument('depth'),
Argument::createBooleanTypeArgument('visible', 1),
new Argument(
'order',
new TypeCollection(
new Type\EnumType(array('alpha', 'alpha_reverse', 'reverse', 'min_price', 'max_price', 'category', 'manual', 'manual_reverse', 'ref', 'promo', 'new'))
)
),
Argument::createBooleanTypeArgument('random', 0),
Argument::createIntListTypeArgument('exclude')
);
}
/**
* @param $pagination
*
* @return \Thelia\Core\Template\Element\LoopResult
*/
public function exec(&$pagination)
{
$search = ProductQuery::create();
if (!is_null($this->id)) {
$search->filterById($this->id, Criteria::IN);
}
if (!is_null($this->ref)) {
$search->filterByRef($this->ref, Criteria::IN);
}
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(
$categories,
Criteria::IN
);
}
if ($this->new === true) {
$search->filterByNewness(1, Criteria::EQUAL);
} else if($this->new === false) {
$search->filterByNewness(0, Criteria::EQUAL);
}
if ($this->promo === true) {
$search->filterByPromo(1, Criteria::EQUAL);
} else if($this->promo === false) {
$search->filterByNewness(0, Criteria::EQUAL);
}
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->filterByWeight($this->min_weight, Criteria::GREATER_EQUAL);
}
if(null !== $this->max_weight) {
$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);
}
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);
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;
case "min_price":
//$search->order
//$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 === 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.
*
* @todo : verify here if we want results for row without translations.
*/
$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);
$loopResult = new LoopResult();
foreach ($products as $product) {
$loopResultRow = new LoopResultRow();
$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("PRICE", $product->getPrice());
$loopResultRow->set("PROMO_PRICE", $product->getPrice2());
$loopResultRow->set("WEIGHT", $product->getWeight());
$loopResultRow->set("PROMO", $product->getPromo());
$loopResultRow->set("NEW", $product->getNewness());
//$loopResultRow->set("URL", $product->getUrl());
$loopResult->addRow($loopResultRow);
}
return $loopResult;
}
}

View File

@@ -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);
}

View File

@@ -43,6 +43,9 @@ class TheliaLoop implements SmartyPluginInterface
protected $dispatcher;
protected $loopstack = array();
protected $varstack = array();
public function __construct(Request $request, EventDispatcherInterface $dispatcher)
{
$this->request = $request;
@@ -78,13 +81,16 @@ 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");
$name = $params['name'];
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']));
@@ -93,11 +99,10 @@ class TheliaLoop implements SmartyPluginInterface
self::$pagination[$name] = null;
$loopResults = $loop->exec(self::$pagination[$name]);
$this->loopstack[$name] = $loopResults;
$template->assignByRef($name, $loopResults);
} else {
$loopResults = $template->getTemplateVars($name);
} else {
$loopResults = $this->loopstack[$name];
$loopResults->next();
}
@@ -105,21 +110,50 @@ class TheliaLoop implements SmartyPluginInterface
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());
//$repeat = $loopResults->valid();
$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
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 = "";
}
if ($loopResults->isEmpty()) $content = "";
return $content;
}
}
@@ -178,10 +212,10 @@ class TheliaLoop implements SmartyPluginInterface
$loopName = $params['rel'];
// Find loop results in the current template vars
$loopResults = $template->getTemplateVars($loopName);
/* $loopResults = $template->getTemplateVars($loopName);
if (empty($loopResults)) {
throw new \InvalidArgumentException("Loop $loopName is not defined.");
}
}*/
// Find pagination
$pagination = self::getPagination($loopName);
@@ -225,14 +259,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();
}
/**
@@ -279,7 +310,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 +335,11 @@ 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;
$loop->{$argument->name} = $value === null ? null : $argument->type->getFormatedValue($value);
}
if (!empty($faultActor)) {

View File

@@ -0,0 +1,54 @@
<?php
/*************************************************************************************/
/* */
/* Thelia */
/* */
/* Copyright (c) OpenStudio */
/* email : info@thelia.net */
/* web : http://www.thelia.net */
/* */
/* This program is free software; you can redistribute it and/or modify */
/* it under the terms of the GNU General Public License as published by */
/* the Free Software Foundation; either version 3 of the License */
/* */
/* This program is distributed in the hope that it will be useful, */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
/* GNU General Public License for more details. */
/* */
/* You should have received a copy of the GNU General Public License */
/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* */
/*************************************************************************************/
namespace Thelia\Core\Template\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 <eroudeix@openstudio.fr>
*/
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")
);
}
}

View File

@@ -44,7 +44,7 @@ class Translation implements SmartyPluginInterface
}
// TODO
return "[$string]";
return "$string";
}
/**

View File

@@ -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)
{
@@ -72,13 +73,13 @@ 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);
$new_source = preg_replace('`#([a-zA-Z][a-zA-Z0-9\-_]*)`', '{\$$1|dieseCanceller:\'#$1\'}', $new_source);
return $new_source;
}