diff --git a/core/lib/Thelia/Config/Resources/config.xml b/core/lib/Thelia/Config/Resources/config.xml index bd17daa8d..4199ce349 100644 --- a/core/lib/Thelia/Config/Resources/config.xml +++ b/core/lib/Thelia/Config/Resources/config.xml @@ -36,17 +36,29 @@ - + + + + + + + + + + + + + + + + %tpex.loop% - + - - - diff --git a/core/lib/Thelia/Core/Bundle/TheliaBundle.php b/core/lib/Thelia/Core/Bundle/TheliaBundle.php index 76c3cabaa..a016dc193 100644 --- a/core/lib/Thelia/Core/Bundle/TheliaBundle.php +++ b/core/lib/Thelia/Core/Bundle/TheliaBundle.php @@ -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 . */ +/* along with this program. If not, see . */ /* */ /*************************************************************************************/ 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()); } } diff --git a/core/lib/Thelia/Core/DependencyInjection/Compiler/RegisterSmartyPluginPass.php b/core/lib/Thelia/Core/DependencyInjection/Compiler/RegisterParserPluginPass.php similarity index 89% rename from core/lib/Thelia/Core/DependencyInjection/Compiler/RegisterSmartyPluginPass.php rename to core/lib/Thelia/Core/DependencyInjection/Compiler/RegisterParserPluginPass.php index 0de6ab1bb..3c1d0f35f 100644 --- a/core/lib/Thelia/Core/DependencyInjection/Compiler/RegisterSmartyPluginPass.php +++ b/core/lib/Thelia/Core/DependencyInjection/Compiler/RegisterParserPluginPass.php @@ -29,14 +29,12 @@ 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. * - * This compiler allow adding plugins in smarty parser using config files * - * Class RegisterSmartyPluginPass - * @package Thelia\Core\DependencyInjection\Compiler */ - -class RegisterSmartyPluginPass implements CompilerPassInterface { +class RegisterParserPluginPass implements CompilerPassInterface { /** * You can modify the container here before it is dumped to PHP code. @@ -53,10 +51,8 @@ class RegisterSmartyPluginPass implements CompilerPassInterface { $smarty = $container->getDefinition("thelia.parser"); - foreach ($container->findTaggedServiceIds("smarty.register_plugin") as $id => $plugin) { - + foreach ($container->findTaggedServiceIds("thelia.parser.register_plugin") as $id => $plugin) { $smarty->addMethodCall("addPlugins", array(new Reference($id))); - } $smarty->addMethodCall("registerPlugins"); diff --git a/core/lib/Thelia/Core/Template/Assets/AsseticManager.php b/core/lib/Thelia/Core/Template/Assets/AsseticHelper.php similarity index 93% rename from core/lib/Thelia/Core/Template/Assets/AsseticManager.php rename to core/lib/Thelia/Core/Template/Assets/AsseticHelper.php index 8f8df9698..6826e373d 100644 --- a/core/lib/Thelia/Core/Template/Assets/AsseticManager.php +++ b/core/lib/Thelia/Core/Template/Assets/AsseticHelper.php @@ -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 . */ +/* along with this program. If not, see . */ /* */ /*************************************************************************************/ @@ -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 + */ +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; } } diff --git a/core/lib/Thelia/Core/Template/Parser.php b/core/lib/Thelia/Core/Template/Parser.php deleted file mode 100644 index bf47caa40..000000000 --- a/core/lib/Thelia/Core/Template/Parser.php +++ /dev/null @@ -1,171 +0,0 @@ -. */ -/* */ -/*************************************************************************************/ -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 - */ - - -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; - } -} diff --git a/core/lib/Thelia/Core/Template/ParserInterface.php b/core/lib/Thelia/Core/Template/ParserInterface.php index 4b3102fbf..08308659c 100644 --- a/core/lib/Thelia/Core/Template/ParserInterface.php +++ b/core/lib/Thelia/Core/Template/ParserInterface.php @@ -23,8 +23,6 @@ namespace Thelia\Core\Template; /** - * - * * * @author Manuel Raynaud * diff --git a/core/lib/Thelia/Core/Template/Assets/SmartyAssetsManager.php b/core/lib/Thelia/Core/Template/Smarty/Assets/SmartyAssetsManager.php similarity index 94% rename from core/lib/Thelia/Core/Template/Assets/SmartyAssetsManager.php rename to core/lib/Thelia/Core/Template/Smarty/Assets/SmartyAssetsManager.php index 50a5e3ae2..69a611b3e 100644 --- a/core/lib/Thelia/Core/Template/Assets/SmartyAssetsManager.php +++ b/core/lib/Thelia/Core/Template/Smarty/Assets/SmartyAssetsManager.php @@ -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 . */ +/* along with this program. If not, see . */ /* */ /*************************************************************************************/ -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) { diff --git a/core/lib/Thelia/Core/Template/Smarty/Plugins/Assetic.php b/core/lib/Thelia/Core/Template/Smarty/Plugins/Assetic.php index d083e083b..7b24b78be 100644 --- a/core/lib/Thelia/Core/Template/Smarty/Plugins/Assetic.php +++ b/core/lib/Thelia/Core/Template/Smarty/Plugins/Assetic.php @@ -1,18 +1,32 @@ . */ +/* */ +/*************************************************************************************/ 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') ); } } \ No newline at end of file diff --git a/core/lib/Thelia/Core/Template/Smarty/Plugins/TheliaLoop.php b/core/lib/Thelia/Core/Template/Smarty/Plugins/TheliaLoop.php new file mode 100644 index 000000000..162f261b5 --- /dev/null +++ b/core/lib/Thelia/Core/Template/Smarty/Plugins/TheliaLoop.php @@ -0,0 +1,291 @@ +. */ +/* */ +/*************************************************************************************/ + +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 + */ + 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 + */ + 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') + ); + } +} \ No newline at end of file diff --git a/core/lib/Thelia/Core/Template/Smarty/RegisterSmartyPlugin.php b/core/lib/Thelia/Core/Template/Smarty/RegisterSmartyPlugin.php deleted file mode 100644 index f48d05cd9..000000000 --- a/core/lib/Thelia/Core/Template/Smarty/RegisterSmartyPlugin.php +++ /dev/null @@ -1,26 +0,0 @@ -type = $type; - $this->name = $name; - $this->class = $class; - $this->method = $method; - } -} \ No newline at end of file diff --git a/core/lib/Thelia/Core/Template/Smarty/SmartyParser.php b/core/lib/Thelia/Core/Template/Smarty/SmartyParser.php new file mode 100644 index 000000000..ecf5b352c --- /dev/null +++ b/core/lib/Thelia/Core/Template/Smarty/SmartyParser.php @@ -0,0 +1,210 @@ + + */ +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)); + } +} \ No newline at end of file diff --git a/core/lib/Thelia/Core/Template/Smarty/SmartyPluginDescriptor.php b/core/lib/Thelia/Core/Template/Smarty/SmartyPluginDescriptor.php new file mode 100644 index 000000000..3e8fec8ad --- /dev/null +++ b/core/lib/Thelia/Core/Template/Smarty/SmartyPluginDescriptor.php @@ -0,0 +1,87 @@ +. */ +/* */ +/*************************************************************************************/ + +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; + } +} \ No newline at end of file diff --git a/core/lib/Thelia/Core/Template/Smarty/SmartyPluginInterface.php b/core/lib/Thelia/Core/Template/Smarty/SmartyPluginInterface.php index 3c6c55a14..87705c467 100644 --- a/core/lib/Thelia/Core/Template/Smarty/SmartyPluginInterface.php +++ b/core/lib/Thelia/Core/Template/Smarty/SmartyPluginInterface.php @@ -1,19 +1,31 @@ . */ +/* */ +/*************************************************************************************/ namespace Thelia\Core\Template\Smarty; - interface SmartyPluginInterface { /** - * @return mixed + * @return an array of SmartyPluginDescriptor */ - public function registerPlugins(); - + public function getPluginDescriptors(); } \ No newline at end of file diff --git a/core/lib/Thelia/Core/Template/SmartyParser.php b/core/lib/Thelia/Core/Template/SmartyParser.php deleted file mode 100644 index d0dcab7d0..000000000 --- a/core/lib/Thelia/Core/Template/SmartyParser.php +++ /dev/null @@ -1,455 +0,0 @@ - - */ -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 - */ - 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 - */ - 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; - } -} diff --git a/core/lib/Thelia/Tpex b/core/lib/Thelia/Tpex index 8561f3516..b1c517281 160000 --- a/core/lib/Thelia/Tpex +++ b/core/lib/Thelia/Tpex @@ -1 +1 @@ -Subproject commit 8561f3516ea03a20e397c4c3aba530fbc6adc364 +Subproject commit b1c517281eb3c9b50260b0cf2b5e41ffa94808e0 diff --git a/web/.htaccess b/web/.htaccess index 1c2d2bbe7..4336ad269 100755 --- a/web/.htaccess +++ b/web/.htaccess @@ -8,7 +8,5 @@ AddDefaultCharset UTF-8 RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d - RewriteBase /thelia2 - RewriteRule ^admin(/.*)?$ index_dev.php/admin/$1 [L,QSA] \ No newline at end of file