Introduction of loop scopes.
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -17,3 +17,4 @@ coverage
|
|||||||
local/cache/*
|
local/cache/*
|
||||||
composer.lock
|
composer.lock
|
||||||
web/assets/*
|
web/assets/*
|
||||||
|
web/.htaccess
|
||||||
|
|||||||
@@ -27,8 +27,6 @@
|
|||||||
|
|
||||||
"symfony/form": "2.2.*",
|
"symfony/form": "2.2.*",
|
||||||
"symfony/validator": "2.2.*",
|
"symfony/validator": "2.2.*",
|
||||||
"symfony/security": "2.2.*",
|
|
||||||
"symfony/templating": "2.2.*",
|
|
||||||
|
|
||||||
"smarty/smarty": "v3.1.13",
|
"smarty/smarty": "v3.1.13",
|
||||||
"kriswallsmith/assetic": "1.2.*@dev",
|
"kriswallsmith/assetic": "1.2.*@dev",
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ class BaseAdminController extends ContainerAware
|
|||||||
*/
|
*/
|
||||||
public function render($templateName, $args = array())
|
public function render($templateName, $args = array())
|
||||||
{
|
{
|
||||||
$args = array_merge($args, array('lang' => 'fr'));
|
$args = array_merge($args, array('lang' => 'fr')); // FIXME
|
||||||
|
|
||||||
$response = new Response();
|
$response = new Response();
|
||||||
|
|
||||||
@@ -57,7 +57,7 @@ class BaseAdminController extends ContainerAware
|
|||||||
|
|
||||||
public function renderRaw($templateName, $args = array())
|
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);
|
return $this->getParser()->render($templateName, $args);
|
||||||
}
|
}
|
||||||
@@ -90,5 +90,6 @@ class BaseAdminController extends ContainerAware
|
|||||||
return $this->getFormFactory()->createBuilder("form");
|
return $this->getFormFactory()->createBuilder("form");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function isGranted() {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -29,12 +29,12 @@ class LoopResultRow
|
|||||||
|
|
||||||
public function set($key, $value)
|
public function set($key, $value)
|
||||||
{
|
{
|
||||||
$this->substitution["#".$key] = $value;
|
$this->substitution[$key] = $value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function get($key)
|
public function get($key)
|
||||||
{
|
{
|
||||||
return $this->substitution["#".$key];
|
return $this->substitution[$key];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getVarVal()
|
public function getVarVal()
|
||||||
@@ -42,4 +42,9 @@ class LoopResultRow
|
|||||||
return $this->substitution;
|
return $this->substitution;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getVars()
|
||||||
|
{
|
||||||
|
return array_keys($this->substitution);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ class Assetic implements SmartyPluginInterface
|
|||||||
{
|
{
|
||||||
$web_root = THELIA_WEB_DIR;
|
$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);
|
$this->asset_manager = new SmartyAssetsManager($web_root, $asset_dir_from_web_root);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,6 +42,9 @@ class TheliaLoop implements SmartyPluginInterface
|
|||||||
|
|
||||||
protected $dispatcher;
|
protected $dispatcher;
|
||||||
|
|
||||||
|
protected $loopstack = array();
|
||||||
|
protected $varstack = array();
|
||||||
|
|
||||||
public function __construct(Request $request, EventDispatcherInterface $dispatcher)
|
public function __construct(Request $request, EventDispatcherInterface $dispatcher)
|
||||||
{
|
{
|
||||||
$this->request = $request;
|
$this->request = $request;
|
||||||
@@ -71,37 +74,75 @@ class TheliaLoop implements SmartyPluginInterface
|
|||||||
|
|
||||||
if ($content === null) {
|
if ($content === null) {
|
||||||
|
|
||||||
|
// Check if a loop with the same name exists in the current scope, and abort if it's the case.
|
||||||
|
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']));
|
$loop = $this->createLoopInstance(strtolower($params['type']));
|
||||||
|
|
||||||
$this->getLoopArgument($loop, $params);
|
$this->getLoopArgument($loop, $params);
|
||||||
|
|
||||||
$loopResults = $loop->exec();
|
$loopResults = $loop->exec();
|
||||||
|
|
||||||
$template->assignByRef($name, $loopResults);
|
$this->loopstack[$name] = $loopResults;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
$loopResults = $template->getTemplateVars($name);
|
$loopResults = $this->loopstack[$name];
|
||||||
|
|
||||||
$loopResults->next();
|
$loopResults->next();
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($loopResults->valid()) {
|
if ($loopResults->valid()) {
|
||||||
|
|
||||||
$loopResultRow = $loopResults->current();
|
$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) {
|
foreach($loopResultRow->getVarVal() as $var => $val) {
|
||||||
$template->assign(substr($var, 1), $val);
|
$template->assign($var, $val);
|
||||||
}
|
}
|
||||||
|
|
||||||
$template->assign('__COUNT__', 1 + $loopResults->key());
|
// Assign meta information
|
||||||
$template->assign('__TOTAL__', $loopResults->getCount());
|
$template->assign('LOOP_COUNT', 1 + $loopResults->key());
|
||||||
|
$template->assign('LOOP_TOTAL', $loopResults->getCount());
|
||||||
|
|
||||||
$repeat = $loopResults->valid();
|
$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 ($content !== null) {
|
||||||
|
|
||||||
if ($loopResults->isEmpty()) $content = "";
|
if ($loopResults->isEmpty()) $content = "";
|
||||||
|
|
||||||
return $content;
|
return $content;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -158,14 +199,11 @@ class TheliaLoop implements SmartyPluginInterface
|
|||||||
|
|
||||||
$loopName = $params['rel'];
|
$loopName = $params['rel'];
|
||||||
|
|
||||||
// Find loop results in the current template vars
|
if (! isset($this->loopstack[$loopName])) {
|
||||||
$loopResults = $template->getTemplateVars($loopName);
|
|
||||||
|
|
||||||
if (empty($loopResults)) {
|
|
||||||
throw new \InvalidArgumentException("Loop $loopName is not defined.");
|
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
|
// TODO
|
||||||
return "[$string]";
|
return "$string";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
namespace Thelia\Model;
|
namespace Thelia\Model;
|
||||||
|
|
||||||
use Thelia\Model\om\BaseAdmin;
|
use Thelia\Model\om\BaseAdmin;
|
||||||
|
use Symfony\Component\Security\Core\User\UserInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Skeleton subclass for representing a row from the 'admin' table.
|
* Skeleton subclass for representing a row from the 'admin' table.
|
||||||
@@ -16,6 +16,27 @@ use Thelia\Model\om\BaseAdmin;
|
|||||||
*
|
*
|
||||||
* @package propel.generator.Thelia.Model
|
* @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;
|
namespace Thelia\Model;
|
||||||
|
|
||||||
use Thelia\Model\om\BaseCustomer;
|
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
|
* @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'}
|
{include file='includes/header.inc.html'}
|
||||||
|
|
||||||
<div class="loginpage">
|
<div class="loginpage">
|
||||||
|
{{intl l='abcd'}|capitalize}
|
||||||
<div class="brandbar container">
|
<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>
|
<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>
|
</div>
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
<div class="hero-unit">
|
<div class="hero-unit">
|
||||||
<h1>{intl l='Thelia Back Office'}</h1>
|
<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_hidden form=$form}
|
||||||
{form_field form=$form.username}
|
{form_field form=$form.username}
|
||||||
{form_error form=$form.username}
|
{form_error form=$form.username}
|
||||||
@@ -27,7 +27,9 @@
|
|||||||
<input type="password" class="input" placeholder="{intl l='Password'}" name="{$name}" />
|
<input type="password" class="input" placeholder="{intl l='Password'}" name="{$name}" />
|
||||||
{/form_field}
|
{/form_field}
|
||||||
|
|
||||||
|
{form_field form=$form.remember_me}
|
||||||
<label class="checkbox"> <input type="checkbox" name="remember" value="yes"> {intl l='Remember me'}</label>
|
<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>
|
<button type="submit" class="btn btn-primary">{intl l='Login'} <i class="icon-play"></i></button>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -1,8 +1,39 @@
|
|||||||
|
{include file="included.html"}
|
||||||
|
|
||||||
{loop name="category0" type="category" parent="0"}
|
{loop name="category0" type="category" parent="0"}
|
||||||
<h2>1 - CATEGORY : #TITLE</h2>
|
<h2>Out before - CATEGORY : #TITLE (#LOOP_COUNT / #LOOP_TOTAL)</h2>
|
||||||
<hr /><hr />
|
{loop name="category1" type="category" parent="#ID"}
|
||||||
{loop name="category1" type="category" parent="#ID"}
|
<h3>Inner - SUBCATEGORY : #TITLE (#LOOP_COUNT / #LOOP_TOTAL)</h3>
|
||||||
<h3>2 - SUBCATEGORY : #TITLE</h3>
|
{/loop}
|
||||||
|
|
||||||
|
{#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}
|
||||||
<h2>1bis - CATEGORY : #TITLE</h2>
|
|
||||||
|
{loop name="category2" type="category" parent="1"}
|
||||||
|
<h3>Final Exter 2 - SUBCATEGORY : #TITLE (#LOOP_COUNT / #LOOP_TOTAL)</h3>
|
||||||
{/loop}
|
{/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