Introduction of loop scopes.

This commit is contained in:
franck
2013-07-02 19:18:41 +02:00
parent 63e9707cc7
commit 18e49e7ebe
15 changed files with 326 additions and 30 deletions

1
.gitignore vendored
View File

@@ -17,3 +17,4 @@ coverage
local/cache/*
composer.lock
web/assets/*
web/.htaccess

View File

@@ -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",

View File

@@ -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() {
}
}

View File

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

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

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

View File

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

View File

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

View File

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

View File

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

View 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 !");
}
}

View File

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

View File

@@ -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>

View File

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

View 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}