Introduction of loop scopes.
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -17,3 +17,4 @@ coverage
|
||||
local/cache/*
|
||||
composer.lock
|
||||
web/assets/*
|
||||
web/.htaccess
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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() {
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
foreach($loopResultRow->getVarVal() as $var => $val) {
|
||||
$template->assign(substr($var, 1), $val);
|
||||
// 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);
|
||||
}
|
||||
|
||||
$template->assign('__COUNT__', 1 + $loopResults->key());
|
||||
$template->assign('__TOTAL__', $loopResults->getCount());
|
||||
$this->varstack[$name] = $saved_vars;
|
||||
}
|
||||
|
||||
foreach($loopResultRow->getVarVal() as $var => $val) {
|
||||
$template->assign($var, $val);
|
||||
}
|
||||
|
||||
// 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();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -44,7 +44,7 @@ class Translation implements SmartyPluginInterface
|
||||
}
|
||||
|
||||
// TODO
|
||||
return "[$string]";
|
||||
return "$string";
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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'));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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'));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
use Thelia\Core\Security\Encoder\PasswordHashEncoder;
|
||||
|
||||
class PasswordHashEncoderTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function testEncode()
|
||||
{
|
||||
$encoder = new PasswordHashEncoder();
|
||||
|
||||
$pass = $encoder->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');
|
||||
}
|
||||
}
|
||||
49
core/lib/Thelia/Tests/Core/Security/SecurityManagerTest.php
Normal file
49
core/lib/Thelia/Tests/Core/Security/SecurityManagerTest.php
Normal file
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
/*************************************************************************************/
|
||||
/* */
|
||||
/* Thelia */
|
||||
/* */
|
||||
/* Copyright (c) OpenStudio */
|
||||
/* email : info@thelia.net */
|
||||
/* web : http://www.thelia.net */
|
||||
/* */
|
||||
/* This program is free software; you can redistribute it and/or modify */
|
||||
/* it under the terms of the GNU General Public License as published by */
|
||||
/* the Free Software Foundation; either version 3 of the License */
|
||||
/* */
|
||||
/* This program is distributed in the hope that it will be useful, */
|
||||
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
|
||||
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
|
||||
/* GNU General Public License for more details. */
|
||||
/* */
|
||||
/* You should have received a copy of the GNU General Public License */
|
||||
/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
/* */
|
||||
/*************************************************************************************/
|
||||
|
||||
namespace Thelia\Tests\Security;
|
||||
|
||||
use Thelia\Core\Security\SecurityManager;
|
||||
/**
|
||||
*
|
||||
* @author Franck Allimant <franck@cqfdev.fr>
|
||||
*
|
||||
*/
|
||||
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 !");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
use Thelia\Core\Security\Token\UsernamePasswordToken;
|
||||
|
||||
class UsernamePasswordTokenTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function testConstructor()
|
||||
{
|
||||
$token = new UsernamePasswordToken('username', 'password');
|
||||
|
||||
$this->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());
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
{include file='includes/header.inc.html'}
|
||||
|
||||
<div class="loginpage">
|
||||
|
||||
{{intl l='abcd'}|capitalize}
|
||||
<div class="brandbar container">
|
||||
<a class="brand" href="index.php">{images file='assets/img/logo-thelia-34px.png'}<img src="{$asset_url}" alt="{intl l='Thelia, solution e-commerce libre'}" />{/images}</a>
|
||||
</div>
|
||||
@@ -15,7 +15,7 @@
|
||||
<div class="hero-unit">
|
||||
<h1>{intl l='Thelia Back Office'}</h1>
|
||||
|
||||
<form action="/admin" method="post" class="well form-inline" {form_enctype form=$form}>
|
||||
<form action="admin/login" method="post" class="well form-inline" {form_enctype form=$form}>
|
||||
{form_field_hidden form=$form}
|
||||
{form_field form=$form.username}
|
||||
{form_error form=$form.username}
|
||||
@@ -27,7 +27,9 @@
|
||||
<input type="password" class="input" placeholder="{intl l='Password'}" name="{$name}" />
|
||||
{/form_field}
|
||||
|
||||
{form_field form=$form.remember_me}
|
||||
<label class="checkbox"> <input type="checkbox" name="remember" value="yes"> {intl l='Remember me'}</label>
|
||||
{/form_field}
|
||||
|
||||
<button type="submit" class="btn btn-primary">{intl l='Login'} <i class="icon-play"></i></button>
|
||||
</form>
|
||||
|
||||
@@ -1,8 +1,39 @@
|
||||
{include file="included.html"}
|
||||
|
||||
{loop name="category0" type="category" parent="0"}
|
||||
<h2>1 - CATEGORY : #TITLE</h2>
|
||||
<hr /><hr />
|
||||
<h2>Out before - CATEGORY : #TITLE (#LOOP_COUNT / #LOOP_TOTAL)</h2>
|
||||
{loop name="category1" type="category" parent="#ID"}
|
||||
<h3>2 - SUBCATEGORY : #TITLE</h3>
|
||||
<h3>Inner - SUBCATEGORY : #TITLE (#LOOP_COUNT / #LOOP_TOTAL)</h3>
|
||||
{/loop}
|
||||
<h2>1bis - CATEGORY : #TITLE</h2>
|
||||
|
||||
{#myid=#ID}
|
||||
|
||||
{loop name="category2" type="category" parent="#ID"}
|
||||
<h3>Inner 2 before - SUBCATEGORY : #TITLE (#LOOP_COUNT / #LOOP_TOTAL)</h3>
|
||||
|
||||
{loop name="category3" type="category" parent="#myid"}
|
||||
<h3>Inner inner 2 - SUBCATEGORY : #TITLE (#LOOP_COUNT / #LOOP_TOTAL)</h3>
|
||||
{/loop}
|
||||
|
||||
<h3>Inner 2 after - SUBCATEGORY : #TITLE (#LOOP_COUNT / #LOOP_TOTAL)</h3>
|
||||
{/loop}
|
||||
|
||||
<h2>Out after - CATEGORY : #TITLE (#LOOP_COUNT / #LOOP_TOTAL)</h2>
|
||||
<hr />
|
||||
|
||||
{ifloop rel="category2"}
|
||||
<p>Hey, y'a d'la categorie 2 !</p>
|
||||
{/ifloop}
|
||||
|
||||
{elseloop rel="category2"}
|
||||
<p>Hey, y'a PAS de categorie 2 !</p>
|
||||
{/elseloop}
|
||||
|
||||
{loop name="category2" type="category" parent="#myid"}
|
||||
<h3>Exter 2 - SUBCATEGORY : #TITLE (#LOOP_COUNT / #LOOP_TOTAL)</h3>
|
||||
{/loop}
|
||||
{/loop}
|
||||
|
||||
{loop name="category2" type="category" parent="1"}
|
||||
<h3>Final Exter 2 - SUBCATEGORY : #TITLE (#LOOP_COUNT / #LOOP_TOTAL)</h3>
|
||||
{/loop}
|
||||
12
templates/smarty-sample/included.html
Normal file
12
templates/smarty-sample/included.html
Normal file
@@ -0,0 +1,12 @@
|
||||
{loop name="included0" type="category" parent="0"}
|
||||
<h2>Out before - CATEGORY : #TITLE (#LOOP_COUNT / #LOOP_TOTAL)</h2>
|
||||
{loop name="category1" type="category" parent="#ID"}
|
||||
<h3>Inner - SUBCATEGORY : #TITLE (#LOOP_COUNT / #LOOP_TOTAL)</h3>
|
||||
{/loop}
|
||||
|
||||
{loop name="category2" type="category" parent="#ID"}
|
||||
<h3>Inner 2 - SUBCATEGORY : #TITLE (#LOOP_COUNT / #LOOP_TOTAL)</h3>
|
||||
{/loop}
|
||||
<h2>Out after - CATEGORY : #TITLE (#LOOP_COUNT / #LOOP_TOTAL)</h2>
|
||||
<hr />
|
||||
{/loop}
|
||||
Reference in New Issue
Block a user