Added ThekliaLoop smarty plugin, and reorganized code a bit

This commit is contained in:
franck
2013-06-19 16:03:28 +02:00
parent 3f1b2d6b92
commit cff9d3cb96
15 changed files with 755 additions and 787 deletions

View File

@@ -36,17 +36,29 @@
<argument type="service" id="service_container"/>
</service>
<service id="thelia.parser" class="Thelia\Core\Template\SmartyParser" scope="request">
<!-- Parser configuration -->
<service id="thelia.parser" class="Thelia\Core\Template\Smarty\SmartyParser" scope="request">
<argument type="service" id="request" />
<argument type="service" id="event_dispatcher"/>
</service>
<!-- Smarty parser plugins -->
<service id="smarty.plugin.assetic" class="Thelia\Core\Template\Smarty\Plugins\Assetic" >
<tag name="thelia.parser.register_plugin"/>
</service>
<service id="smarty.plugin.thelialoop" class="Thelia\Core\Template\Smarty\Plugins\TheliaLoop" scope="request">
<tag name="thelia.parser.register_plugin"/>
<argument type="service" id="request" />
<argument type="service" id="event_dispatcher"/>
<call method="setLoopList">
<argument>%tpex.loop%</argument>
</call>
</service>
</service>
<service id="smarty.plugin.assetic" class="Thelia\Core\Template\Smarty\Plugins\Assetic" >
<tag name="smarty.register_plugin"/>
</service>
<service id="http_kernel" class="Thelia\Core\TheliaHttpKernel">
<argument type="service" id="event_dispatcher" />

View File

@@ -4,7 +4,7 @@
/* Thelia */
/* */
/* Copyright (c) OpenStudio */
/* email : info@thelia.net */
/* email : info@thelia.net */
/* web : http://www.thelia.net */
/* */
/* This program is free software; you can redistribute it and/or modify */
@@ -17,7 +17,7 @@
/* 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/>. */
/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* */
/*************************************************************************************/
namespace Thelia\Core\Bundle;
@@ -28,7 +28,7 @@ use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\Scope;
use Thelia\Core\DependencyInjection\Compiler\RegisterListenersPass;
use Thelia\Core\DependencyInjection\Compiler\RegisterSmartyPluginPass;
use Thelia\Core\DependencyInjection\Compiler\RegisterParserPluginPass;
/**
* First Bundle use in Thelia
@@ -57,6 +57,6 @@ class TheliaBundle extends Bundle
$container->addScope(new Scope('request'));
$container->addCompilerPass(new RegisterListenersPass());
$container->addCompilerPass(new RegisterSmartyPluginPass());
$container->addCompilerPass(new RegisterParserPluginPass());
}
}

View File

@@ -0,0 +1,61 @@
<?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\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
/**
* Register parser plugins. These plugins shouild be tagged thelia.parser.register_plugin
* in the configuration.
*
* @author Franck Allimant <franck@cqfdev.fr>
*
*/
class RegisterParserPluginPass implements CompilerPassInterface {
/**
* You can modify the container here before it is dumped to PHP code.
*
* @param ContainerBuilder $container
*
* @api
*/
public function process(ContainerBuilder $container)
{
if (!$container->hasDefinition("thelia.parser")) {
return;
}
$smarty = $container->getDefinition("thelia.parser");
foreach ($container->findTaggedServiceIds("thelia.parser.register_plugin") as $id => $plugin) {
$smarty->addMethodCall("addPlugins", array(new Reference($id)));
}
$smarty->addMethodCall("registerPlugins");
}
}

View File

@@ -1,66 +0,0 @@
<?php
/**
* Created by JetBrains PhpStorm.
* User: manu
* Date: 18/06/13
* Time: 21:55
* To change this template use File | Settings | File Templates.
*/
namespace Thelia\Core\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
class RegisterSmartyPluginPass implements CompilerPassInterface {
/**
* You can modify the container here before it is dumped to PHP code.
*
* @param ContainerBuilder $container
*
* @api
*/
public function process(ContainerBuilder $container)
{
if (!$container->hasDefinition("thelia.parser")) {
return;
}
$smarty = $container->getDefinition("thelia.parser");
foreach ($container->findTaggedServiceIds("smarty.register_plugin") as $id => $plugin) {
$smarty->addMethodCall("addPlugins", array(new Reference($id)));
/*$register_plugin = $container->get($id);
$reflectionObject = new \ReflectionObject($register_plugin);
$interface = "Thelia\Core\Template\Smarty\SmartyPluginInterface";
if (!$reflectionObject->implementsInterface($interface)) {
throw new \RuntimeException(sprintf("%s class must implement %s interface",$reflectionObject->getName(), $interface));
}
$plugins = $register_plugin->registerPlugins();
if(!is_array($plugins)) {
$plugins = array($plugins);
}
foreach($plugins as $plugin) {
$smarty->addMethodCall("registerPlugin", array(
$plugin->type,
$plugin->name,
array(
$plugin->class,
$plugin->method
)
));
}*/
}
$smarty->addMethodCall("registerPlugins");
}
}

View File

@@ -1,24 +1,24 @@
<?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/>. */
/* */
/* */
/* 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\Assets;
@@ -32,14 +32,12 @@ use Assetic\AssetWriter;
use Assetic\Asset\AssetCache;
use Assetic\Cache\FilesystemCache;
class AsseticManager {
protected $options;
public function __construct($_options = array()) {
$this->options = $_options;
}
/**
* This class is a simple helper for generating assets using Assetic.
*
* @author Franck Allimant <franck@cqfdev.fr>
*/
class AsseticHelper {
/**
* Generates assets from $asset_path in $output_path, using $filters.
@@ -50,7 +48,7 @@ class AsseticManager {
* @param unknown $asset_type the asset type: css, js, ... The generated files will have this extension. Pass an empty string to use the asset source extension.
* @param unknown $filters a list of filters, as defined below (see switch($filter_name) ...)
* @param unknown $debug true / false
* @throws \Exception
* @throws \InvalidArgumentException if an invalid filter name is found
* @return string The URL to the generated asset file.
*/
public function asseticize($asset_path, $output_path, $output_url, $asset_type, $filters, $debug) {
@@ -90,7 +88,7 @@ class AsseticManager {
break;
default :
throw new \Exception("Unsupported Assetic filter: '$filter_name'");
throw new \InvalidArgumentException("Unsupported Assetic filter: '$filter_name'");
break;
}
}

View File

@@ -1,171 +0,0 @@
<?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;
use Symfony\Component\HttpFoundation\Response;
use Thelia\Core\Template\ParserInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Exception\ResourceNotFoundException;
use Symfony\Component\Config\ConfigCache;
use Thelia\Tpex\Tpex;
use Thelia\Log\Tlog;
/**
*
* Master class of Thelia's parser. The loop mechanism depends of this parser
*
* From this class all the parser is lunch
*
*
* @author Manuel Raynaud <mraynaud@openstudio.fr>
*/
class Parser implements ParserInterface
{
const PREFIXE = 'prx';
const SHOW_TIME = true;
const ALLOW_DEBUG = true;
const USE_CACHE = true;
/**
*
* @var Symfony\Component\DependencyInjection\ContainerInterface
*/
protected $container;
protected $content;
protected $status = 200;
/**
*
* @var Thelia\Tpex\Tpex
*/
protected $tpex;
protected $template = "default";
/**
*
* @param type $container
*
* public function __construct(ContainerBuilder $container)
*/
public function __construct(ContainerInterface $container)
{
$this->container = $container;
}
/**
*
* @return Symfony\Component\HttpFoundation\Request
*/
public function getRequest()
{
return $this->container->get('request');
}
/**
*
* @return Symfony\Component\EventDispatcher\EventDispatcher
*/
public function getDispatcher()
{
return $this->container->get('dispatcher');
}
/**
*
* This method must return a Symfony\Component\HttpFoudation\Response instance or the content of the response
*
*/
public function getContent()
{
$this->loadParser();
return $this->content;
}
/**
*
* set $content with the body of the response or the Response object directly
*
* @param string|Symfony\Component\HttpFoundation\Response $content
*/
public function setContent($content)
{
$this->content = $content;
}
/**
*
* @return type the status of the response
*/
public function getStatus()
{
return $this->status;
}
/**
*
* status HTTP of the response
*
* @param int $status
*/
public function setStatus($status)
{
$this->status = $status;
}
/**
* Main parser function, load the parser
*/
public function loadParser()
{
$content = $this->openFile($this->getRequest());
$tpex = $this->container->get("template");
$tpex->setBaseDir(THELIA_TEMPLATE_DIR . rtrim($this->template, "/") . "/");
$tpex->setContent($content);
$this->setContent($tpex->execute());
}
protected function openFile(Request $request)
{
$file = $request->attributes->get('_view');
$fileName = THELIA_TEMPLATE_DIR . rtrim($this->template, "/") . "/" . $file . ".html";
if (file_exists($fileName)) {
$content = file_get_contents($fileName);
} else {
throw new ResourceNotFoundException(sprintf("%s file not found in %s template", $file, $this->template));
}
return $content;
}
}

View File

@@ -23,8 +23,6 @@
namespace Thelia\Core\Template;
/**
*
*
*
* @author Manuel Raynaud <mraynaud@openstudio.fr>
*

View File

@@ -17,13 +17,13 @@
/* 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/>. */
/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* */
/*************************************************************************************/
namespace Thelia\Core\Template\Assets;
namespace Thelia\Core\Template\Smarty\Assets;
use Thelia\Core\Template\Assets\AsseticManager;
use Thelia\Core\Template\Assets\AsseticHelper;
class SmartyAssetsManager {
@@ -35,6 +35,7 @@ class SmartyAssetsManager {
private $path_relative_to_web_root;
/**
* Creates a new SmartyAssetsManager instance
*
* @param string $web_root the disk path to the web root
* @param string $path_relative_to_web_root the path (relative to web root) where the assets will be generated
@@ -44,7 +45,7 @@ class SmartyAssetsManager {
$this->web_root = $web_root;
$this->path_relative_to_web_root = $path_relative_to_web_root;
$this->assetic_manager = new AsseticManager();
$this->assetic_manager = new AsseticHelper();
}
public function processSmartyPluginCall($assetType, $params, $content, \Smarty_Internal_Template $template, &$repeat) {

View File

@@ -1,18 +1,32 @@
<?php
/**
* Created by JetBrains PhpStorm.
* User: manu
* Date: 18/06/13
* Time: 22:44
* To change this template use File | Settings | File Templates.
*/
/*************************************************************************************/
/* */
/* 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\RegisterSmartyPlugin;
use Thelia\Core\Template\Smarty\SmartyPluginDescriptor;
use Thelia\Core\Template\Smarty\SmartyPluginInterface;
use Thelia\Core\Template\Assets\SmartyAssetsManager;
use Thelia\Core\Template\Smarty\Assets\SmartyAssetsManager;
class Assetic implements SmartyPluginInterface {
@@ -45,14 +59,16 @@ class Assetic implements SmartyPluginInterface {
}
/**
* @return mixed
* Define the various smarty plugins hendled by this class
*
* @return an array of smarty plugin descriptors
*/
public function registerPlugins()
public function getPluginDescriptors()
{
return array(
new RegisterSmartyPlugin('block', 'stylesheets', $this, 'theliaBlockStylesheets'),
new RegisterSmartyPlugin('block', 'javascripts', $this, 'theliaBlockJavascripts'),
new RegisterSmartyPlugin('block', 'images', $this, 'theliaBlockImages')
new SmartyPluginDescriptor('block', 'stylesheets', $this, 'theliaBlockStylesheets'),
new SmartyPluginDescriptor('block', 'javascripts', $this, 'theliaBlockJavascripts'),
new SmartyPluginDescriptor('block', 'images' , $this, 'theliaBlockImages')
);
}
}

View File

@@ -0,0 +1,291 @@
<?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\SmartyPluginInterface;
use Thelia\Core\Template\Smarty\SmartyPluginDescriptor;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
class TheliaLoop implements SmartyPluginInterface {
protected $loopDefinition = array();
protected $request;
protected $dispatcher;
public function __construct(Request $request, EventDispatcherInterface $dispatcher) {
$this->request = $request;
$this->dispatcher = $dispatcher;
}
/**
* Process {loop name="loop name" type="loop type" ... } ... {/loop} block
*
* @param unknown $params
* @param unknown $content
* @param unknown $template
* @param unknown $repeat
* @throws \InvalidArgumentException
* @return string
*/
public function theliaLoop($params, $content, $template, &$repeat) {
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) {
$loop = $this->createLoopInstance(strtolower($params['type']));
$this->getLoopArgument($loop, $params);
$loopResults = $loop->exec();
$template->assignByRef($name, $loopResults);
}
else {
$loopResults = $template->getTemplateVars($name);
$loopResults->next();
}
if ($loopResults->valid()) {
$loopResultRow = $loopResults->current();
foreach($loopResultRow->getVarVal() as $var => $val) {
$template->assign(substr($var, 1), $val);
$template->assign('__COUNT__', 1 + $loopResults->key());
$template->assign('__TOTAL__', $loopResults->getCount());
}
$repeat = $loopResults->valid();
}
if ($content !== null) {
if ($loopResults->isEmpty()) $content = "";
return $content;
}
}
/**
* Process {elseloop rel="loopname"} ... {/elseloop} block
*
* @param unknown $params
* @param unknown $content
* @param unknown $template
* @param unknown $repeat
* @return Ambigous <string, unknown>
*/
public function theliaElseloop($params, $content, $template, &$repeat) {
// When encoutering close tag, check if loop has results.
if ($repeat === false) {
return $this->checkEmptyLoop($params, $template) ? $content : '';
}
}
/**
* Process {ifloop rel="loopname"} ... {/ifloop} block
*
* @param unknown $params
* @param unknown $content
* @param unknown $template
* @param unknown $repeat
* @return Ambigous <string, unknown>
*/
public function theliaIfLoop($params, $content, $template, &$repeat) {
// When encountering close tag, check if loop has results.
if ($repeat === false) {
return $this->checkEmptyLoop($params, $template) ? '' : $content;
}
}
/**
* Check if a loop has returned results. The loop shoud have been executed before, or an
* InvalidArgumentException is thrown
*
* @param unknown $params
* @param unknown $template
* @throws \InvalidArgumentException
*/
protected function checkEmptyLoop($params, $template) {
if (empty($params['rel']))
throw new \InvalidArgumentException("Missing 'rel' parameter in ifloop/elseloop arguments");
$loopName = $params['rel'];
// Find loop results in the current template vars
$loopResults = $template->getTemplateVars($loopName);
if (empty($loopResults)) {
throw new \InvalidArgumentException("Loop $loopName is not defined.");
}
return $loopResults->isEmpty();
}
/**
*
* find the loop class with his name and construct an instance of this class
*
* @param string $name
* @return \Thelia\Tpex\Element\Loop\BaseLoop
* @throws \Thelia\Tpex\Exception\InvalidElementException
* @throws \Thelia\Tpex\Exception\ElementNotFoundException
*/
protected function createLoopInstance($name)
{
if (! isset($this->loopDefinition[$name])) {
throw new ElementNotFoundException(sprintf("%s loop does not exists", $name));
}
$class = new \ReflectionClass($this->loopDefinition[$name]);
if ($class->isSubclassOf("Thelia\Tpex\Element\Loop\BaseLoop") === false) {
throw new InvalidElementException(sprintf("%s Loop class have to extends Thelia\Tpex\Element\Loop\BaseLoop",
$name));
}
return $class->newInstance(
$this->request,
$this->dispatcher
);
}
/**
* Returns the value of a loop argument.
*
* @param unknown $loop a BaseLoop instance
* @param unknown $smartyParam
* @throws \InvalidArgumentException
*/
protected function getLoopArgument($loop, $smartyParam)
{
$defaultItemsParams = array('required' => true);
$shortcutItemParams = array('optional' => array('required' => false));
$errorCode = 0;
$faultActor = array();
$faultDetails = array();
foreach($loop->defineArgs() as $name => $param){
if(is_integer($name)){
$name = $param;
$param = $defaultItemsParams;
}
if(is_string($param) && array_key_exists($param, $shortcutItemParams)){
$param = $shortcutItemParams[$param];
}
if(!is_array($param)){
$param = array('default' => $param);
}
$value = isset($smartyParam[$name]) ? $smartyParam[$name] : null;
if($value == null){
if(isset($param['default'])){
$value = $param['default'];
}
else if($param['required'] === true){
$faultActor[] = $name;
$faultDetails[] = sprintf('"%s" parameter is missing', $name);
continue;
}
}
$loop->{$name} = $value;
}
if(!empty($faultActor)){
$complement = sprintf('[%s]', implode(', ', $faultDetails));
throw new \InvalidArgumentException($complement);
}
}
/**
*
* Injects an associative array containing information for loop execution
*
* key is loop name
* value is the class implementing/extending base loop classes
*
* ex :
*
* $loop = array(
* "product" => "Thelia\Loop\Product",
* "category" => "Thelia\Loop\Category",
* "myLoop" => "My\Own\Loop"
* );
*
* @param array $loops
* @throws \InvalidArgumentException if loop name already exists
*/
public function setLoopList(array $loopDefinition)
{
foreach ($loopDefinition as $name => $className) {
if (array_key_exists($name, $this->loopDefinition)) {
throw new \InvalidArgumentException(sprintf("%s loop name already exists for %s class name", $name, $className));
}
$this->loopDefinition[$name] = $className;
}
}
/**
* Defines the various smarty plugins hendled by this class
*
* @return an array of smarty plugin descriptors
*/
public function getPluginDescriptors()
{
return array(
new SmartyPluginDescriptor('block', 'loop' , $this, 'theliaLoop'),
new SmartyPluginDescriptor('block', 'elseloop' , $this, 'theliaElseloop'),
new SmartyPluginDescriptor('block', 'ifloop' , $this, 'theliaIfLoop')
);
}
}

View File

@@ -1,26 +0,0 @@
<?php
/**
* Created by JetBrains PhpStorm.
* User: manu
* Date: 18/06/13
* Time: 22:41
* To change this template use File | Settings | File Templates.
*/
namespace Thelia\Core\Template\Smarty;
class RegisterSmartyPlugin {
public $type;
public $name;
public $class;
public $method;
public function __construct($type, $name, $class, $method)
{
$this->type = $type;
$this->name = $name;
$this->class = $class;
$this->method = $method;
}
}

View File

@@ -0,0 +1,210 @@
<?php
namespace Thelia\Core\Template\Smarty;
use \Symfony\Component\HttpFoundation\Request;
use \Symfony\Component\EventDispatcher\EventDispatcherInterface;
use \Smarty;
use Thelia\Core\Template\ParserInterface;
use Thelia\Core\Template\Loop\Category;
use Thelia\Core\Template\Smarty\SmartyPluginInterface;
use Thelia\Core\Template\Smarty\Assets\SmartyAssetsManager;
/**
*
* @author Franck Allimant <franck@cqfdev.fr>
*/
class SmartyParser extends Smarty implements ParserInterface {
public $plugins = array();
protected $request, $dispatcher;
protected $template = "";
protected $status = 200;
/**
* @param \Symfony\Component\HttpFoundation\Request $request
* @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher
*/
public function __construct(Request $request, EventDispatcherInterface $dispatcher, $template = false)
{
parent::__construct();
$this->request = $request;
$this->dispatcher = $dispatcher;
// Configure basic Smarty parameters
$compile_dir = THELIA_ROOT . 'cache/smarty/compile';
if (! is_dir($compile_dir)) @mkdir($compile_dir, 0777, true);
$cache_dir = THELIA_ROOT . 'cache/smarty/cache';
if (! is_dir($cache_dir)) @mkdir($cache_dir, 0777, true);
$this->setTemplate($template != false ? $template : 'smarty-sample');
$this->setCompileDir($compile_dir);
$this->setCacheDir($cache_dir);
// Prevent smarty ErrorException: Notice: Undefined index bla bla bla...
$this->error_reporting = E_ALL ^ E_NOTICE;
// Activer le cache, avec une lifetime de 15mn, et en vérifiant que les templates sources n'ont pas été modifiés.
$this->caching = 1;
$this->cache_lifetime = 300;
$this->compile_check = true;
// The default HTTP status
$this->status = 200;
// Register translation function 'intl'
$this->registerPlugin('function', 'intl', array($this, 'theliaTranslate'));
// Register Thelia modules inclusion function 'thelia_module'
$this->registerPlugin('function', 'thelia_module', array($this, 'theliaModule'));
}
public function setTemplate($template_path_from_template_base) {
$this->template = $template_path_from_template_base;
$this->setTemplateDir(THELIA_TEMPLATE_DIR.$this->template);
}
public function getTemplate() {
return $this->template;
}
/**
* Return a rendered template file
*
* @param string $realTemplateName the template name (from the template directory)
* @param array $parameters an associative array of names / value pairs
* @return string the rendered template text
*/
public function render($realTemplateName, array $parameters) {
$this->assign($parameters);
return $this->fetch($realTemplateName);
}
/**
* Process translate function
*
* @param unknown $params
* @param unknown $smarty
* @return string
*/
public function theliaTranslate($params, &$smarty)
{
if (isset($params['l'])) {
$string = str_replace('\'', '\\\'', $params['l']);
}
else {
$string = '';
}
// TODO
return "[$string]";
}
/**
* Process theliaModule template inclusion function
*
* @param unknown $params
* @param unknown $smarty
* @return string
*/
public function theliaModule($params, &$smarty)
{
// TODO
return "";
}
/**
*
* This method must return a Symfony\Component\HttpFoudation\Response instance or the content of the response
*
*/
public function getContent()
{
return $this->fetch($this->getTemplateFilePath());
}
/**
*
* set $content with the body of the response or the Response object directly
*
* @param string|Symfony\Component\HttpFoundation\Response $content
*/
public function setContent($content)
{
$this->content = $content;
}
/**
*
* @return type the status of the response
*/
public function getStatus()
{
return $this->status;
}
/**
*
* status HTTP of the response
*
* @param int $status
*/
public function setStatus($status)
{
$this->status = $status;
}
public function addPlugins(SmartyPluginInterface $plugin)
{
$this->plugins[] = $plugin;
}
public function registerPlugins()
{
foreach ($this->plugins as $register_plugin) {
$plugins = $register_plugin->getPluginDescriptors();
if(!is_array($plugins)) {
$plugins = array($plugins);
}
foreach ($plugins as $plugin) {
$this->registerPlugin(
$plugin->getType(),
$plugin->getName(),
array(
$plugin->getClass(),
$plugin->getMethod()
)
);
}
}
}
protected function getTemplateFilePath()
{
$file = $this->request->attributes->get('_view');
$fileName = THELIA_TEMPLATE_DIR . rtrim($this->template, "/") . "/" . $file . ".html";
if (file_exists($fileName)) return $fileName;
throw new ResourceNotFoundException(sprintf("%s file not found in %s template", $file, $this->template));
}
}

View File

@@ -0,0 +1,87 @@
<?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;
class SmartyPluginDescriptor {
/**
* @var string Smarty plugin type (block, function, etc.)
*/
protected $type;
/**
* @var string Smarty plugin name. This name will be used in Smarty templates.
*/
protected $name;
/**
* @var SmartyPluginInterface plugin implmentation class
*/
protected $class;
/**
* @var string plugin implmentation method in $class
*/
protected $method;
public function __construct($type, $name, $class, $method)
{
$this->type = $type;
$this->name = $name;
$this->class = $class;
$this->method = $method;
}
public function setType($type) {
$this->type = $type;
}
public function getType() {
return $this->type;
}
public function setName($name) {
$this->name = $name;
}
public function getName() {
return $this->name;
}
public function setClass($class) {
$this->class = $class;
}
public function getClass() {
return $this->class;
}
public function setMethod($method) {
$this->method = $method;
}
public function getMethod() {
return $this->method;
}
}

View File

@@ -1,19 +1,31 @@
<?php
/**
* Created by JetBrains PhpStorm.
* User: manu
* Date: 18/06/13
* Time: 22:38
* To change this template use File | Settings | File Templates.
*/
/*************************************************************************************/
/* */
/* 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;
interface SmartyPluginInterface {
/**
* @return mixed
* @return an array of SmartyPluginDescriptor
*/
public function registerPlugins();
public function getPluginDescriptors();
}

View File

@@ -1,455 +0,0 @@
<?php
namespace Thelia\Core\Template;
use \Symfony\Component\HttpFoundation\Request;
use \Symfony\Component\EventDispatcher\EventDispatcherInterface;
use \Smarty;
use Thelia\Core\Template\ParserInterface;
use Thelia\Core\Template\Loop\Category;
use Thelia\Core\Template\Smarty\SmartyPluginInterface;
/**
*
* @author Franck Allimant <franck@cqfdev.fr>
*/
class SmartyParser extends Smarty implements ParserInterface {
public $plugins = array();
protected $request, $dispatcher;
protected $template = "";
protected $status = 200;
protected $loopDefinition = array();
protected $asset_manager = null; // Lazy loading
/**
* @param \Symfony\Component\HttpFoundation\Request $request
* @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher
*/
public function __construct(Request $request, EventDispatcherInterface $dispatcher, $template = false)
{
parent::__construct();
$this->request = $request;
$this->dispatcher = $dispatcher;
// Configure basic Smarty parameters
$compile_dir = THELIA_ROOT . 'cache/smarty/compile';
if (! is_dir($compile_dir)) @mkdir($compile_dir, 0777, true);
$cache_dir = THELIA_ROOT . 'cache/smarty/cache';
if (! is_dir($cache_dir)) @mkdir($cache_dir, 0777, true);
$this->setTemplate($template != false ? $template : 'smarty-sample');
$this->setCompileDir($compile_dir);
$this->setCacheDir($cache_dir);
// Prevent smarty ErrorException: Notice: Undefined index bla bla bla...
$this->error_reporting = E_ALL ^ E_NOTICE;
// The default HTTP status
$this->status = 200;
// Register Thelia base block plugins
$this->registerPlugin('block', 'loop' , array($this, 'theliaLoop'));
$this->registerPlugin('block', 'elseloop' , array($this, 'theliaElseloop'));
$this->registerPlugin('block', 'ifloop' , array($this, 'theliaIfLoop'));
// Register translation function 'intl'
$this->registerPlugin('function', 'intl', array($this, 'theliaTranslate'));
// Register Thelia modules inclusion function 'thelia_module'
$this->registerPlugin('function', 'thelia_module', array($this, 'theliaModule'));
}
public function setTemplate($template_path_from_template_base) {
$this->template = $template_path_from_template_base;
$this->setTemplateDir(THELIA_TEMPLATE_DIR.$this->template);
}
public function getTemplate() {
return $this->template;
}
/**
* Return a rendered template file
*
* @param string $realTemplateName the template name (from the template directory)
* @param array $parameters an associative array of names / value pairs
* @return string the rendered template text
*/
public function render($realTemplateName, array $parameters) {
$this->assign($parameters);
return $this->fetch($realTemplateName);
}
/**
* Process {loop name="loop name" type="loop type" ... } ... {/loop} block
*
* @param unknown $params
* @param unknown $content
* @param unknown $template
* @param unknown $repeat
* @throws \InvalidArgumentException
* @return string
*/
public function theliaLoop($params, $content, $template, &$repeat) {
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) {
$loop = $this->createLoopInstance(strtolower($params['type']));
$this->getLoopArgument($loop, $params);
$loopResults = $loop->exec();
$template->assignByRef($name, $loopResults);
}
else {
$loopResults = $template->getTemplateVars($name);
$loopResults->next();
}
if ($loopResults->valid()) {
$loopResultRow = $loopResults->current();
foreach($loopResultRow->getVarVal() as $var => $val) {
$template->assign(substr($var, 1), $val);
$template->assign('__COUNT__', 1 + $loopResults->key());
$template->assign('__TOTAL__', $loopResults->getCount());
}
$repeat = $loopResults->valid();
}
if ($content !== null) {
if ($loopResults->isEmpty()) $content = "";
return $content;
}
}
/**
* Process {elseloop rel="loopname"} ... {/elseloop} block
*
* @param unknown $params
* @param unknown $content
* @param unknown $template
* @param unknown $repeat
* @return Ambigous <string, unknown>
*/
public function theliaElseloop($params, $content, $template, &$repeat) {
// When encoutering close tag, check if loop has results.
if ($repeat === false) {
return $this->checkEmptyLoop($params, $template) ? $content : '';
}
}
/**
* Process {ifloop rel="loopname"} ... {/ifloop} block
*
* @param unknown $params
* @param unknown $content
* @param unknown $template
* @param unknown $repeat
* @return Ambigous <string, unknown>
*/
public function theliaIfLoop($params, $content, $template, &$repeat) {
// When encountering close tag, check if loop has results.
if ($repeat === false) {
return $this->checkEmptyLoop($params, $template) ? '' : $content;
}
}
/**
* Process translate function
*
* @param unknown $params
* @param unknown $smarty
* @return string
*/
public function theliaTranslate($params, &$smarty)
{
if (isset($params['l'])) {
$string = str_replace('\'', '\\\'', $params['l']);
}
else {
$string = '';
}
// TODO
return "[$string]";
}
/**
* Process theliaModule template inclusion function
*
* @param unknown $params
* @param unknown $smarty
* @return string
*/
public function theliaModule($params, &$smarty)
{
// TODO
return "";
}
/**
*
* This method must return a Symfony\Component\HttpFoudation\Response instance or the content of the response
*
*/
public function getContent()
{
return $this->fetch($this->getTemplateFilePath());
}
/**
*
* set $content with the body of the response or the Response object directly
*
* @param string|Symfony\Component\HttpFoundation\Response $content
*/
public function setContent($content)
{
$this->content = $content;
}
/**
*
* @return type the status of the response
*/
public function getStatus()
{
return $this->status;
}
/**
*
* status HTTP of the response
*
* @param int $status
*/
public function setStatus($status)
{
$this->status = $status;
}
/**
* Check if a loop has returned results. The loop shoud have been executed before, or an
* InvalidArgumentException is thrown
*
* @param unknown $params
* @param unknown $template
* @throws \InvalidArgumentException
*/
protected function checkEmptyLoop($params, $template) {
if (empty($params['rel']))
throw new \InvalidArgumentException("Missing 'rel' parameter in ifloop/elseloop arguments");
$loopName = $params['rel'];
// Find loop results in the current template vars
$loopResults = $template->getTemplateVars($loopName);
if (empty($loopResults)) {
throw new \InvalidArgumentException("Loop $loopName is not defined.");
}
return $loopResults->isEmpty();
}
/**
*
* Injects an associative array containing information for loop execution
*
* key is loop name
* value is the class implementing/extending base loop classes
*
* ex :
*
* $loop = array(
* "product" => "Thelia\Loop\Product",
* "category" => "Thelia\Loop\Category",
* "myLoop" => "My\Own\Loop"
* );
*
* @param array $loops
* @throws \InvalidArgumentException if loop name already exists
*/
public function setLoopList(array $loopDefinition)
{
foreach ($loopDefinition as $name => $className) {
if (array_key_exists($name, $this->loopDefinition)) {
throw new \InvalidArgumentException(sprintf("%s loop name already exists for %s class name", $name, $className));
}
$this->loopDefinition[$name] = $className;
}
}
public function addPlugins(SmartyPluginInterface $plugin)
{
$this->plugins[] = $plugin;
}
public function registerPlugins()
{
foreach ($this->plugins as $register_plugin) {
$plugins = $register_plugin->registerPlugins();
if(!is_array($plugins)) {
$plugins = array($plugins);
}
foreach ($plugins as $plugin) {
$this->registerPlugin(
$plugin->type,
$plugin->name,
array(
$plugin->class,
$plugin->method
)
);
}
}
}
/**
* Returns the value of a loop argument.
*
* @param unknown $loop a BaseLoop instance
* @param unknown $smartyParam
* @throws \InvalidArgumentException
*/
protected function getLoopArgument($loop, $smartyParam)
{
$defaultItemsParams = array('required' => true);
$shortcutItemParams = array('optional' => array('required' => false));
$errorCode = 0;
$faultActor = array();
$faultDetails = array();
foreach($loop->defineArgs() as $name => $param){
if(is_integer($name)){
$name = $param;
$param = $defaultItemsParams;
}
if(is_string($param) && array_key_exists($param, $shortcutItemParams)){
$param = $shortcutItemParams[$param];
}
if(!is_array($param)){
$param = array('default' => $param);
}
$value = isset($smartyParam[$name]) ? $smartyParam[$name] : null;
if($value == null){
if(isset($param['default'])){
$value = $param['default'];
}
else if($param['required'] === true){
$faultActor[] = $name;
$faultDetails[] = sprintf('"%s" parameter is missing', $name);
continue;
}
}
$loop->{$name} = $value;
}
if(!empty($faultActor)){
$complement = sprintf('[%s]', implode(', ', $faultDetails));
throw new \InvalidArgumentException($complement);
}
}
/**
*
* find the loop class with his name and construct an instance of this class
*
* @param string $name
* @return \Thelia\Tpex\Element\Loop\BaseLoop
* @throws \Thelia\Tpex\Exception\InvalidElementException
* @throws \Thelia\Tpex\Exception\ElementNotFoundException
*/
protected function createLoopInstance($name)
{
if (! isset($this->loopDefinition[$name])) {
throw new ElementNotFoundException(sprintf("%s loop does not exists", $name));
}
$class = new \ReflectionClass($this->loopDefinition[$name]);
if ($class->isSubclassOf("Thelia\Tpex\Element\Loop\BaseLoop") === false) {
throw new InvalidElementException(sprintf("%s Loop class have to extends Thelia\Tpex\Element\Loop\BaseLoop",
$name));
}
return $class->newInstance(
$this->request,
$this->dispatcher
);
}
protected function getTemplateFilePath()
{
$file = $this->request->attributes->get('_view');
$fileName = THELIA_TEMPLATE_DIR . rtrim($this->template, "/") . "/" . $file . ".html";
if (file_exists($fileName)) return $fileName;
throw new ResourceNotFoundException(sprintf("%s file not found in %s template", $file, $this->template));
}
protected function getAssetManager() {
if ($this->asset_manager == null)
$this->asset_manager = new SmartyAssetsManager(THELIA_WEB_DIR, "assets/$this->template");
return $this->asset_manager;
}
}