From f7a635dbf72a37477e053e10f36213d386322f78 Mon Sep 17 00:00:00 2001 From: franck Date: Thu, 25 Jul 2013 22:59:46 +0200 Subject: [PATCH] Added DataAccess plugin, admin header completed --- composer.lock | 16 +-- core/lib/Thelia/Action/Customer.php | 14 +- .../Admin/Controller/BaseAdminController.php | 27 ++-- .../Admin/Controller/SessionController.php | 5 + core/lib/Thelia/Config/Resources/config.xml | 8 ++ core/lib/Thelia/Core/Event/TheliaEvents.php | 40 +++++- .../Thelia/Core/Security/SecurityContext.php | 4 +- core/lib/Thelia/Core/Template/Loop/Order.php | 59 +++++++++ .../Thelia/Core/Template/Loop/OrderStatus.php | 59 +++++++++ .../Thelia/Core/Template/ParserContext.php | 1 + ...Interface.php => AbstractSmartyPlugin.php} | 27 +++- .../Smarty/Assets/SmartyAssetsManager.php | 15 ++- .../Core/Template/Smarty/Plugins/Assetic.php | 32 +++-- .../Smarty/Plugins/DataAccessFunctions.php | 112 ++++++++++++++++ .../Core/Template/Smarty/Plugins/Form.php | 4 +- .../Core/Template/Smarty/Plugins/Module.php | 4 +- .../Core/Template/Smarty/Plugins/Security.php | 17 +-- .../Template/Smarty/Plugins/TheliaLoop.php | 30 ++++- .../Template/Smarty/Plugins/TheliaSyntax.php | 4 +- .../Template/Smarty/Plugins/Translation.php | 14 +- .../Template/Smarty/Plugins/UrlGenerator.php | 4 +- .../Core/Template/Smarty/SmartyParser.php | 4 +- .../Smarty/SmartyPluginDescriptor.php | 2 +- install/INSTALL-TODO.txt | 1 + templates/admin/default/assets/css/admin.less | 81 ++++++++++++ .../default/assets/css/img/search-icon.png | Bin 0 -> 3320 bytes .../admin/default/assets/css/img/search.png | Bin 0 -> 3919 bytes .../default/assets/css/img/top-bar-logo.png | Bin 0 -> 5696 bytes .../admin/default/assets/css/img/top.jpg | Bin 0 -> 13200 bytes templates/admin/default/home.html | 8 +- .../admin/default/includes/header.inc.html | 125 +++++++++++++++++- 31 files changed, 620 insertions(+), 97 deletions(-) create mode 100644 core/lib/Thelia/Core/Template/Loop/Order.php create mode 100644 core/lib/Thelia/Core/Template/Loop/OrderStatus.php rename core/lib/Thelia/Core/Template/Smarty/{SmartyPluginInterface.php => AbstractSmartyPlugin.php} (78%) create mode 100644 core/lib/Thelia/Core/Template/Smarty/Plugins/DataAccessFunctions.php create mode 100644 templates/admin/default/assets/css/img/search-icon.png create mode 100644 templates/admin/default/assets/css/img/search.png create mode 100644 templates/admin/default/assets/css/img/top-bar-logo.png create mode 100644 templates/admin/default/assets/css/img/top.jpg diff --git a/composer.lock b/composer.lock index b49c21e6f..88c9d3b5a 100755 --- a/composer.lock +++ b/composer.lock @@ -11,12 +11,12 @@ "source": { "type": "git", "url": "https://github.com/ezyang/htmlpurifier.git", - "reference": "107b3055a10eb824701463602f77fbc8129180a2" + "reference": "af7107e8304e0ac0ad6fc2dfbecc9062d90e0b17" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ezyang/htmlpurifier/zipball/107b3055a10eb824701463602f77fbc8129180a2", - "reference": "107b3055a10eb824701463602f77fbc8129180a2", + "url": "https://api.github.com/repos/ezyang/htmlpurifier/zipball/af7107e8304e0ac0ad6fc2dfbecc9062d90e0b17", + "reference": "af7107e8304e0ac0ad6fc2dfbecc9062d90e0b17", "shasum": "" }, "require": { @@ -47,7 +47,7 @@ "keywords": [ "html" ], - "time": "2013-07-17 04:56:29" + "time": "2013-07-18 17:22:45" }, { "name": "ircmaxell/password-compat", @@ -304,12 +304,12 @@ "source": { "type": "git", "url": "https://github.com/krichprollsch/phpCssEmbed.git", - "reference": "v1.0.1" + "reference": "v1.0.2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/krichprollsch/phpCssEmbed/zipball/v1.0.1", - "reference": "v1.0.1", + "url": "https://api.github.com/repos/krichprollsch/phpCssEmbed/zipball/v1.0.2", + "reference": "v1.0.2", "shasum": "" }, "require": { @@ -337,7 +337,7 @@ "css", "url" ], - "time": "2013-04-29 19:58:29" + "time": "2013-07-22 20:01:48" }, { "name": "simplepie/simplepie", diff --git a/core/lib/Thelia/Action/Customer.php b/core/lib/Thelia/Action/Customer.php index fe1379aed..3a39a4bf2 100755 --- a/core/lib/Thelia/Action/Customer.php +++ b/core/lib/Thelia/Action/Customer.php @@ -56,8 +56,6 @@ class Customer implements EventSubscriberInterface public function create(ActionEvent $event) { - $event->getDispatcher()->dispatch(TheliaEvents::BEFORE_CREATECUSTOMER, $event); - $request = $event->getRequest(); $customerCreationForm = new CustomerCreation($request); @@ -71,8 +69,11 @@ class Customer implements EventSubscriberInterface if ($form->isValid()) { $data = $form->getData(); $customer = new CustomerModel(); + try { - $customer->createOrUpdate( + $event->getDispatcher()->dispatch(TheliaEvents::BEFORE_CREATECUSTOMER, $event); + + $customer->createOrUpdate( $data["title"], $data["firstname"], $data["lastname"], @@ -120,8 +121,6 @@ class Customer implements EventSubscriberInterface public function modify(ActionEvent $event) { - $event->getDispatcher()->dispatch(TheliaEvents::BEFORE_CHANGECUSTOMER, $event); - $request = $event->getRequest(); $customerModification = new CustomerModification($request); @@ -137,7 +136,10 @@ class Customer implements EventSubscriberInterface $customer = CustomerQuery::create()->findPk(1); try { - $data = $form->getData(); + $event->getDispatcher()->dispatch(TheliaEvents::BEFORE_CHANGECUSTOMER, $event); + + $data = $form->getData(); + $customer->createOrUpdate( $data["title"], $data["firstname"], diff --git a/core/lib/Thelia/Admin/Controller/BaseAdminController.php b/core/lib/Thelia/Admin/Controller/BaseAdminController.php index b5fe2a71b..742db4304 100755 --- a/core/lib/Thelia/Admin/Controller/BaseAdminController.php +++ b/core/lib/Thelia/Admin/Controller/BaseAdminController.php @@ -37,6 +37,7 @@ use Thelia\Core\Security\SecurityContext; use Thelia\Tools\URL; use Thelia\Tools\Redirect; use Thelia\Core\Template\ParserContext; +use Thelia\Core\Event\ActionEvent; /** * @@ -133,10 +134,21 @@ class BaseAdminController extends ContainerAware return $this->container->get('request'); } + /** + * Dispatch a Thelia event to modules + * + * @param string $eventName a TheliaEvent name, as defined in TheliaEvents class + * @param ActionEvent $event the event + */ + protected function dispatch($eventName, ActionEvent $event = null) { + + $this->container->get("event_dispatcher")->dispatch($eventName, $event); + } + /** * Returns the session from the current request * - * @return Ambigous + * @return \Symfony\Component\HttpFoundation\Session\SessionInterface */ protected function getSession() { @@ -146,8 +158,7 @@ class BaseAdminController extends ContainerAware } /** - * - * @return a ParserInterface instance parser, as configured. + * @return a ParserInterface instance parser */ protected function getParser() { @@ -159,16 +170,6 @@ class BaseAdminController extends ContainerAware return $parser; } - protected function getFormFactory() - { - return BaseForm::getFormFactory($this->getRequest(), ConfigQuery::read("form.secret.admin", md5(__DIR__))); - } - - protected function getFormBuilder() - { - return $this->getFormFactory()->createBuilder("form"); - } - /** * Forwards the request to another controller. * diff --git a/core/lib/Thelia/Admin/Controller/SessionController.php b/core/lib/Thelia/Admin/Controller/SessionController.php index 71a3c0106..9928a5a01 100755 --- a/core/lib/Thelia/Admin/Controller/SessionController.php +++ b/core/lib/Thelia/Admin/Controller/SessionController.php @@ -31,6 +31,7 @@ use Thelia\Core\Security\Exception\AuthenticationException; use Symfony\Component\Validator\Exception\ValidatorException; use Thelia\Tools\URL; use Thelia\Tools\Redirect; +use Thelia\Core\Event\TheliaEvents; class SessionController extends BaseAdminController { @@ -41,6 +42,8 @@ class SessionController extends BaseAdminController { public function checkLogoutAction() { + $this->dispatch(TheliaEvents::ADMIN_LOGOUT); + $this->getSecurityContext()->clear(); // Go back to login page. @@ -64,6 +67,8 @@ class SessionController extends BaseAdminController { // Log authentication success AdminLog::append("Authentication successuful", $request, $user); + $this->dispatch(TheliaEvents::ADMIN_LOGIN); + // Redirect to the success URL return Redirect::exec($adminLoginForm->getSuccessUrl()); } diff --git a/core/lib/Thelia/Config/Resources/config.xml b/core/lib/Thelia/Config/Resources/config.xml index a8cf23d8a..94ec1855d 100755 --- a/core/lib/Thelia/Config/Resources/config.xml +++ b/core/lib/Thelia/Config/Resources/config.xml @@ -10,6 +10,8 @@ + + @@ -130,6 +132,12 @@ + + + + + + diff --git a/core/lib/Thelia/Core/Event/TheliaEvents.php b/core/lib/Thelia/Core/Event/TheliaEvents.php index d570c22a1..c086f4fa9 100755 --- a/core/lib/Thelia/Core/Event/TheliaEvents.php +++ b/core/lib/Thelia/Core/Event/TheliaEvents.php @@ -25,8 +25,7 @@ namespace Thelia\Core\Event; /** * - * Class containing all Thelia events name using in Thelia Core - * + * This class contains all Thelia events identifiers used by Thelia Core * * @author Manuel Raynaud */ @@ -37,25 +36,56 @@ final class TheliaEvents /** * ACTION event * - * Send if no action are already present in Thelia action process ( see Thelia\Routing\Matcher\ActionMatcher) + * Sent if no action are already present in Thelia action process ( see Thelia\Routing\Matcher\ActionMatcher) */ const ACTION = "thelia.action"; /** * INCLUDE event * - * Send before starting thelia inclusion + * Sent before starting thelia inclusion */ const INCLUSION = "thelia.include"; + /** + * Sent before the logout of the customer. + */ const CUSTOMER_LOGOUT = "action.customer_logout"; + /** + * Sent once the customer is successfully logged in. + */ const CUSTOMER_LOGIN = "action.customer_login"; + /** + * Sent before the logout of the administrator. + */ + const ADMIN_LOGOUT = "action.admin_logout"; + /** + * Sent once the administrator is successfully logged in. + */ + const ADMIN_LOGIN = "action.admin_login"; + + /** + * Sent once the customer creation form has been successfully validated, and before customer insertion in the database. + */ const BEFORE_CREATECUSTOMER = "action.before_createcustomer"; + + /** + * Sent just after a successful insert of a new customer in the database. + */ const AFTER_CREATECUSTOMER = "action.after_createcustomer"; + /** + * Sent once the customer change form has been successfully validated, and before customer update in the database. + */ const BEFORE_CHANGECUSTOMER = "action.before_changecustomer"; + /** + * Sent just after a successful update of a customer in the database. + */ const AFTER_CHANGECUSTOMER = "action.after_changecustomer"; + /** + * Sent before customer insertion, to allow modules to create a custom customer reference. + */ const CREATECUSTOMER_CUSTOMREF = "customer.creation.customref"; -} +} \ No newline at end of file diff --git a/core/lib/Thelia/Core/Security/SecurityContext.php b/core/lib/Thelia/Core/Security/SecurityContext.php index a4409afd8..b881282b9 100755 --- a/core/lib/Thelia/Core/Security/SecurityContext.php +++ b/core/lib/Thelia/Core/Security/SecurityContext.php @@ -55,6 +55,8 @@ class SecurityContext { } $this->context = $context; + + return $this; } public function getContext($exception_if_context_undefined = false) { @@ -133,7 +135,7 @@ class SecurityContext { // Get permissions from profile // $userPermissions = $user->getPermissions(); - echo "TODO: Finalize permissions system !"; + // TODO: Finalize permissions system !; $userPermissions = array('*'); // FIXME ! diff --git a/core/lib/Thelia/Core/Template/Loop/Order.php b/core/lib/Thelia/Core/Template/Loop/Order.php new file mode 100644 index 000000000..112a1b0f3 --- /dev/null +++ b/core/lib/Thelia/Core/Template/Loop/Order.php @@ -0,0 +1,59 @@ +. */ +/* */ +/*************************************************************************************/ + +namespace Thelia\Core\Template\Loop; + +use Thelia\Core\Template\Element\BaseLoop; +use Thelia\Core\Template\Element\LoopResult; +use Thelia\Core\Template\Element\LoopResultRow; + +use Thelia\Core\Template\Loop\Argument\ArgumentCollection; +use Thelia\Core\Template\Loop\Argument\Argument; + +use Thelia\Type\TypeCollection; +use Thelia\Type; + +/** + * + * @package Thelia\Core\Template\Loop + * + * @author Franck Allimant + */ +class Order extends BaseLoop +{ + public function getArgDefinitions() + { + return new ArgumentCollection(); + } + + /** + * + * + * @return \Thelia\Core\Template\Element\LoopResult + */ + public function exec(&$pagination) + { + // TODO : a coder ! + return new LoopResult(); + } +} \ No newline at end of file diff --git a/core/lib/Thelia/Core/Template/Loop/OrderStatus.php b/core/lib/Thelia/Core/Template/Loop/OrderStatus.php new file mode 100644 index 000000000..e6787f003 --- /dev/null +++ b/core/lib/Thelia/Core/Template/Loop/OrderStatus.php @@ -0,0 +1,59 @@ +. */ +/* */ +/*************************************************************************************/ + +namespace Thelia\Core\Template\Loop; + +use Thelia\Core\Template\Element\BaseLoop; +use Thelia\Core\Template\Element\LoopResult; +use Thelia\Core\Template\Element\LoopResultRow; + +use Thelia\Core\Template\Loop\Argument\ArgumentCollection; +use Thelia\Core\Template\Loop\Argument\Argument; + +use Thelia\Type\TypeCollection; +use Thelia\Type; + +/** + * + * @package Thelia\Core\Template\Loop + * + * @author Franck Allimant + */ +class OrderStatus extends BaseLoop +{ + public function getArgDefinitions() + { + return new ArgumentCollection(); + } + + /** + * + * + * @return \Thelia\Core\Template\Element\LoopResult + */ + public function exec(&$pagination) + { + // TODO : a coder ! + return new LoopResult(); + } +} \ No newline at end of file diff --git a/core/lib/Thelia/Core/Template/ParserContext.php b/core/lib/Thelia/Core/Template/ParserContext.php index e5c682e37..85b2b73b9 100644 --- a/core/lib/Thelia/Core/Template/ParserContext.php +++ b/core/lib/Thelia/Core/Template/ParserContext.php @@ -44,6 +44,7 @@ class ParserContext implements \IteratorAggregate ->set('BASE_URL' , ConfigQuery::read('base_url', '/')) ->set('INDEX_PAGE' , URL::getIndexPage()) ->set('RETURN_TO_URL' , URL::absoluteUrl($request->getSession()->getReturnToUrl())) + ->set('THELIA_VERSION' , ConfigQuery::read('thelia_version', 'undefined')) ; } diff --git a/core/lib/Thelia/Core/Template/Smarty/SmartyPluginInterface.php b/core/lib/Thelia/Core/Template/Smarty/AbstractSmartyPlugin.php similarity index 78% rename from core/lib/Thelia/Core/Template/Smarty/SmartyPluginInterface.php rename to core/lib/Thelia/Core/Template/Smarty/AbstractSmartyPlugin.php index e1228c7b1..0015dbb0b 100755 --- a/core/lib/Thelia/Core/Template/Smarty/SmartyPluginInterface.php +++ b/core/lib/Thelia/Core/Template/Smarty/AbstractSmartyPlugin.php @@ -25,15 +25,34 @@ namespace Thelia\Core\Template\Smarty; /** * - * this interface must be implements when you want to add plugin to smarty using config process + * The class all Smarty Thelia plugin shoud extend * - * Interface SmartyPluginInterface + * Class AbstractSmartyPlugin * @package Thelia\Core\Template\Smarty */ -interface SmartyPluginInterface +abstract class AbstractSmartyPlugin { + /** + * Explode a comma separated list in a array, trimming all array elements + * + * @param unknown $commaSeparatedValues + * @return multitype: + */ + protected function _explode($commaSeparatedValues) + { + $array = explode(',', $commaSeparatedValues); + + if (array_walk($array, function(&$item) { + $item = strtoupper(trim($item)); + })) { + return $array; + } + + return array(); + } + /** * @return an array of SmartyPluginDescriptor */ - public function getPluginDescriptors(); + public abstract function getPluginDescriptors(); } diff --git a/core/lib/Thelia/Core/Template/Smarty/Assets/SmartyAssetsManager.php b/core/lib/Thelia/Core/Template/Smarty/Assets/SmartyAssetsManager.php index 1f8a3cb2e..1e594ae32 100755 --- a/core/lib/Thelia/Core/Template/Smarty/Assets/SmartyAssetsManager.php +++ b/core/lib/Thelia/Core/Template/Smarty/Assets/SmartyAssetsManager.php @@ -50,10 +50,7 @@ class SmartyAssetsManager $this->assetic_manager = new AsseticHelper(); } - public function processSmartyPluginCall($assetType, $params, $content, \Smarty_Internal_Template $template, &$repeat) - { - // Opening tag (first call only) - if ($repeat) { + public function computeAssetUrl($assetType, $params, \Smarty_Internal_Template $template) { $file = $params['file']; $filters = isset($params['filters']) ? $params['filters'] : ''; $debug = isset($params['debug']) ? trim(strtolower($params['debug'])) == 'true' : false; @@ -79,7 +76,17 @@ class SmartyAssetsManager $debug ); + return $url; + } + + public function processSmartyPluginCall($assetType, $params, $content, \Smarty_Internal_Template $template, &$repeat) + { + // Opening tag (first call only) + if ($repeat) { + $url = $this->computeAssetUrl($assetType, $params, $template); + $template->assign('asset_url', $url); + } elseif (isset($content)) { return $content; } diff --git a/core/lib/Thelia/Core/Template/Smarty/Plugins/Assetic.php b/core/lib/Thelia/Core/Template/Smarty/Plugins/Assetic.php index 4418bd50c..7fca50151 100755 --- a/core/lib/Thelia/Core/Template/Smarty/Plugins/Assetic.php +++ b/core/lib/Thelia/Core/Template/Smarty/Plugins/Assetic.php @@ -24,12 +24,12 @@ namespace Thelia\Core\Template\Smarty\Plugins; use Thelia\Core\Template\Smarty\SmartyPluginDescriptor; -use Thelia\Core\Template\Smarty\SmartyPluginInterface; +use Thelia\Core\Template\Smarty\AbstractSmartyPlugin; use Thelia\Core\Template\Smarty\Assets\SmartyAssetsManager; -class Assetic implements SmartyPluginInterface +class Assetic extends AbstractSmartyPlugin { - public $asset_manager; + public $assetManager; public function __construct() { @@ -37,22 +37,27 @@ class Assetic implements SmartyPluginInterface $asset_dir_from_web_root = 'assets/admin/default'; // FIXME - $this->asset_manager = new SmartyAssetsManager($web_root, $asset_dir_from_web_root); + $this->assetManager = new SmartyAssetsManager($web_root, $asset_dir_from_web_root); } - public function theliaBlockJavascripts($params, $content, \Smarty_Internal_Template $template, &$repeat) + public function blockJavascripts($params, $content, \Smarty_Internal_Template $template, &$repeat) { - return $this->asset_manager->processSmartyPluginCall('js', $params, $content, $template, $repeat); + return $this->assetManager->processSmartyPluginCall('js', $params, $content, $template, $repeat); } - public function theliaBlockImages($params, $content, \Smarty_Internal_Template $template, &$repeat) + public function blockImages($params, $content, \Smarty_Internal_Template $template, &$repeat) { - return $this->asset_manager->processSmartyPluginCall(SmartyAssetsManager::ASSET_TYPE_AUTO, $params, $content, $template, $repeat); + return $this->assetManager->processSmartyPluginCall(SmartyAssetsManager::ASSET_TYPE_AUTO, $params, $content, $template, $repeat); } - public function theliaBlockStylesheets($params, $content, \Smarty_Internal_Template $template, &$repeat) + public function blockStylesheets($params, $content, \Smarty_Internal_Template $template, &$repeat) { - return $this->asset_manager->processSmartyPluginCall('css', $params, $content, $template, $repeat); + return $this->assetManager->processSmartyPluginCall('css', $params, $content, $template, $repeat); + } + + public function functionImage($params, \Smarty_Internal_Template $template) + { + return $this->assetManager->computeAssetUrl(SmartyAssetsManager::ASSET_TYPE_AUTO, $params, $template); } /** @@ -63,9 +68,10 @@ class Assetic implements SmartyPluginInterface public function getPluginDescriptors() { return array( - new SmartyPluginDescriptor('block', 'stylesheets', $this, 'theliaBlockStylesheets'), - new SmartyPluginDescriptor('block', 'javascripts', $this, 'theliaBlockJavascripts'), - new SmartyPluginDescriptor('block', 'images' , $this, 'theliaBlockImages') + new SmartyPluginDescriptor('block' , 'stylesheets', $this, 'blockStylesheets'), + new SmartyPluginDescriptor('block' , 'javascripts', $this, 'blockJavascripts'), + new SmartyPluginDescriptor('block' , 'images' , $this, 'blockImages'), + new SmartyPluginDescriptor('function', 'image' , $this, 'functionImage') ); } } diff --git a/core/lib/Thelia/Core/Template/Smarty/Plugins/DataAccessFunctions.php b/core/lib/Thelia/Core/Template/Smarty/Plugins/DataAccessFunctions.php new file mode 100644 index 000000000..7fcbdfbc7 --- /dev/null +++ b/core/lib/Thelia/Core/Template/Smarty/Plugins/DataAccessFunctions.php @@ -0,0 +1,112 @@ +. */ +/* */ +/*************************************************************************************/ + +namespace Thelia\Core\Template\Smarty\Plugins; + +use Thelia\Core\Template\Smarty\AbstractSmartyPlugin; +use Thelia\Core\Security\SecurityContext; +use Thelia\Core\Template\ParserContext; +use Thelia\Core\Template\Smarty\SmartyPluginDescriptor; +/** + * Implementation of data access to main Thelia objects (users, cart, etc.) + * + * @author Franck Allimant + * + */ +class DataAccessFunctions extends AbstractSmartyPlugin +{ + private $securityContext; + protected $parserContext; + + public function __construct(SecurityContext $securityContext, ParserContext $parserContext) + { + $this->securityContext = $securityContext; + } + + /** + * Provides access to the current logged administrator attributes using the accessors. + * + * @param array $params + * @param unknown $smarty + * @return string the value of the requested attribute + */ + public function adminDataAccess($params, &$smarty) + { + return $this->userDataAccess("Admin User", SecurityContext::CONTEXT_BACK_OFFICE, $params); + } + + /** + * Provides access to the current logged customer attributes throught the accessor + * + * @param array $params + * @param unknown $smarty + * @return string the value of the requested attribute + */ + public function customerDataAccess($params, &$smarty) + { + return $this->userDataAccess("Customer User", SecurityContext::CONTEXT_FRONT_OFFICE, $params); + } + + + /** + * Provides access to user attributes using the accessors. + * + * @param array $params + * @param unknown $smarty + * @return string the value of the requested attribute + * @throws InvalidArgumentException if the object does not have the requested attribute. + */ + protected function userDataAccess($objectLabel, $context, $params) + { + $attribute = $params['attr']; + + if (! empty($attribute)) { + $user = $this->securityContext->setContext($context)->getUser(); + + if (null != $user) { + $getter = sprintf("get%s", ucfirst(strtolower($attribute))); + + if (method_exists($user, $getter)) { + return $user->$getter(); + } + + throw new \InvalidArgumentException(sprintf("%s has no '%s' attribute", $objectLabel, $attribute)); + + } + } + + return ''; + } + /** + * Define the various smarty plugins hendled by this class + * + * @return an array of smarty plugin descriptors + */ + public function getPluginDescriptors() + { + return array( + new SmartyPluginDescriptor('function', 'admin', $this, 'adminDataAccess'), + new SmartyPluginDescriptor('function', 'customer', $this, 'customerDataAccess') + ); + } +} diff --git a/core/lib/Thelia/Core/Template/Smarty/Plugins/Form.php b/core/lib/Thelia/Core/Template/Smarty/Plugins/Form.php index cdabebb13..7ddb96104 100755 --- a/core/lib/Thelia/Core/Template/Smarty/Plugins/Form.php +++ b/core/lib/Thelia/Core/Template/Smarty/Plugins/Form.php @@ -27,7 +27,7 @@ use Thelia\Form\BaseForm; use Thelia\Core\Template\Element\Exception\ElementNotFoundException; use Symfony\Component\HttpFoundation\Request; use Thelia\Core\Template\Smarty\SmartyPluginDescriptor; -use Thelia\Core\Template\Smarty\SmartyPluginInterface; +use Thelia\Core\Template\Smarty\AbstractSmartyPlugin; use Thelia\Log\Tlog; use Thelia\Core\Template\ParserContext; @@ -55,7 +55,7 @@ use Thelia\Core\Template\ParserContext; * Class Form * @package Thelia\Core\Template\Smarty\Plugins */ -class Form implements SmartyPluginInterface +class Form extends AbstractSmartyPlugin { protected $request; diff --git a/core/lib/Thelia/Core/Template/Smarty/Plugins/Module.php b/core/lib/Thelia/Core/Template/Smarty/Plugins/Module.php index 99080ce70..52feae096 100755 --- a/core/lib/Thelia/Core/Template/Smarty/Plugins/Module.php +++ b/core/lib/Thelia/Core/Template/Smarty/Plugins/Module.php @@ -24,9 +24,9 @@ namespace Thelia\Core\Template\Smarty\Plugins; use Thelia\Core\Template\Smarty\SmartyPluginDescriptor; -use Thelia\Core\Template\Smarty\SmartyPluginInterface; +use Thelia\Core\Template\Smarty\AbstractSmartyPlugin; -class Module implements SmartyPluginInterface +class Module extends AbstractSmartyPlugin { /** * Process theliaModule template inclusion function diff --git a/core/lib/Thelia/Core/Template/Smarty/Plugins/Security.php b/core/lib/Thelia/Core/Template/Smarty/Plugins/Security.php index cd19576b1..51bb0d1ef 100755 --- a/core/lib/Thelia/Core/Template/Smarty/Plugins/Security.php +++ b/core/lib/Thelia/Core/Template/Smarty/Plugins/Security.php @@ -24,12 +24,12 @@ namespace Thelia\Core\Template\Smarty\Plugins; use Thelia\Core\Template\Smarty\SmartyPluginDescriptor; -use Thelia\Core\Template\Smarty\SmartyPluginInterface; +use Thelia\Core\Template\Smarty\AbstractSmartyPlugin; use Thelia\Core\Template\Smarty\Assets\SmartyAssetsManager; use Thelia\Core\Security\SecurityContext; use Thelia\Core\Security\Exception\AuthenticationException; -class Security implements SmartyPluginInterface +class Security extends AbstractSmartyPlugin { private $securityContext; @@ -38,19 +38,6 @@ class Security implements SmartyPluginInterface $this->securityContext = $securityContext; } - private function _explode($commaSeparatedValues) - { - $array = explode(',', $commaSeparatedValues); - - if (array_walk($array, function(&$item) { - $item = strtoupper(trim($item)); - })) { - return $array; - } - - return array(); - } - /** * Process security check function * diff --git a/core/lib/Thelia/Core/Template/Smarty/Plugins/TheliaLoop.php b/core/lib/Thelia/Core/Template/Smarty/Plugins/TheliaLoop.php index 3d5496741..ddb0eca4a 100755 --- a/core/lib/Thelia/Core/Template/Smarty/Plugins/TheliaLoop.php +++ b/core/lib/Thelia/Core/Template/Smarty/Plugins/TheliaLoop.php @@ -24,7 +24,7 @@ namespace Thelia\Core\Template\Smarty\Plugins; use Thelia\Core\Template\Element\BaseLoop; -use Thelia\Core\Template\Smarty\SmartyPluginInterface; +use Thelia\Core\Template\Smarty\AbstractSmartyPlugin; use Thelia\Core\Template\Smarty\SmartyPluginDescriptor; use Thelia\Core\Template\Element\Exception\ElementNotFoundException; @@ -34,7 +34,7 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Thelia\Core\Security\SecurityContext; -class TheliaLoop implements SmartyPluginInterface +class TheliaLoop extends AbstractSmartyPlugin { protected static $pagination = null; @@ -68,6 +68,23 @@ class TheliaLoop implements SmartyPluginInterface } } + /** + * Process the count function: executes a loop and return the number of items found + */ + public function theliaCount($params, $template) { + + if (empty($params['type'])) + throw new \InvalidArgumentException("Missing 'type' parameter in count arguments"); + + $loop = $this->createLoopInstance($params); + + $dummy = null; + + $loopResults = $loop->exec($dummy); + + return $loopResults->valid() ? $loopResults->getCount() : 0; + } + /** * Process {loop name="loop name" type="loop type" ... } ... {/loop} block * @@ -343,10 +360,11 @@ class TheliaLoop implements SmartyPluginInterface { return array( - new SmartyPluginDescriptor('block', 'loop' , $this, 'theliaLoop'), - new SmartyPluginDescriptor('block', 'elseloop' , $this, 'theliaElseloop'), - new SmartyPluginDescriptor('block', 'ifloop' , $this, 'theliaIfLoop'), - new SmartyPluginDescriptor('block', 'pageloop' , $this, 'theliaPageLoop'), + new SmartyPluginDescriptor('function', 'count' , $this, 'theliaCount'), + new SmartyPluginDescriptor('block' , 'loop' , $this, 'theliaLoop'), + new SmartyPluginDescriptor('block' , 'elseloop' , $this, 'theliaElseloop'), + new SmartyPluginDescriptor('block' , 'ifloop' , $this, 'theliaIfLoop'), + new SmartyPluginDescriptor('block' , 'pageloop' , $this, 'theliaPageLoop'), ); } } diff --git a/core/lib/Thelia/Core/Template/Smarty/Plugins/TheliaSyntax.php b/core/lib/Thelia/Core/Template/Smarty/Plugins/TheliaSyntax.php index 9b508cf3e..7881941ac 100755 --- a/core/lib/Thelia/Core/Template/Smarty/Plugins/TheliaSyntax.php +++ b/core/lib/Thelia/Core/Template/Smarty/Plugins/TheliaSyntax.php @@ -23,7 +23,7 @@ namespace Thelia\Core\Template\Smarty\Plugins; use Thelia\Core\Template\Smarty\SmartyPluginDescriptor; -use Thelia\Core\Template\Smarty\SmartyPluginInterface; +use Thelia\Core\Template\Smarty\AbstractSmartyPlugin; /** * Class TheliaSyntax @@ -31,7 +31,7 @@ use Thelia\Core\Template\Smarty\SmartyPluginInterface; * * @author Etienne Roudeix */ -class TheliaSyntax implements SmartyPluginInterface +class TheliaSyntax extends AbstractSmartyPlugin { public function dieseCancel($value, $diese) { diff --git a/core/lib/Thelia/Core/Template/Smarty/Plugins/Translation.php b/core/lib/Thelia/Core/Template/Smarty/Plugins/Translation.php index f03c34a7e..6015ca618 100755 --- a/core/lib/Thelia/Core/Template/Smarty/Plugins/Translation.php +++ b/core/lib/Thelia/Core/Template/Smarty/Plugins/Translation.php @@ -24,10 +24,10 @@ namespace Thelia\Core\Template\Smarty\Plugins; use Thelia\Core\Template\Smarty\SmartyPluginDescriptor; -use Thelia\Core\Template\Smarty\SmartyPluginInterface; +use Thelia\Core\Template\Smarty\AbstractSmartyPlugin; use Symfony\Component\Translation\TranslatorInterface; -class Translation implements SmartyPluginInterface +class Translation extends AbstractSmartyPlugin { protected $translator; @@ -44,7 +44,15 @@ class Translation implements SmartyPluginInterface */ public function translate($params, &$smarty) { - return $this->translator->trans($params['l'], isset($params['p']) ? $params['p'] : array()); + // All parameters other than 'l' are supposed to be variables. Build an array of var => value pairs + // and pass it to the translator + $vars = array(); + + foreach($params as $name => $value) { + $vars["%$name"] = $value; + } + + return $this->translator->trans($params['l'], $vars); } /** diff --git a/core/lib/Thelia/Core/Template/Smarty/Plugins/UrlGenerator.php b/core/lib/Thelia/Core/Template/Smarty/Plugins/UrlGenerator.php index c2c75dffc..9d63080be 100644 --- a/core/lib/Thelia/Core/Template/Smarty/Plugins/UrlGenerator.php +++ b/core/lib/Thelia/Core/Template/Smarty/Plugins/UrlGenerator.php @@ -24,11 +24,11 @@ namespace Thelia\Core\Template\Smarty\Plugins; use Thelia\Core\Template\Smarty\SmartyPluginDescriptor; -use Thelia\Core\Template\Smarty\SmartyPluginInterface; +use Thelia\Core\Template\Smarty\AbstractSmartyPlugin; use Thelia\Tools\URL; use Thelia\Core\HttpFoundation\Request; -class UrlGenerator implements SmartyPluginInterface +class UrlGenerator extends AbstractSmartyPlugin { protected $request; diff --git a/core/lib/Thelia/Core/Template/Smarty/SmartyParser.php b/core/lib/Thelia/Core/Template/Smarty/SmartyParser.php index a1e63dbf8..65b82d88f 100755 --- a/core/lib/Thelia/Core/Template/Smarty/SmartyParser.php +++ b/core/lib/Thelia/Core/Template/Smarty/SmartyParser.php @@ -10,7 +10,7 @@ use \Smarty; use Symfony\Component\HttpFoundation\Response; use Thelia\Core\Template\ParserInterface; -use Thelia\Core\Template\Smarty\SmartyPluginInterface; +use Thelia\Core\Template\Smarty\AbstractSmartyPlugin; use Thelia\Core\Template\Exception\ResourceNotFoundException; use Thelia\Core\Template\ParserContext; use Thelia\Model\ConfigQuery; @@ -177,7 +177,7 @@ class SmartyParser extends Smarty implements ParserInterface $this->status = $status; } - public function addPlugins(SmartyPluginInterface $plugin) + public function addPlugins(AbstractSmartyPlugin $plugin) { $this->plugins[] = $plugin; } diff --git a/core/lib/Thelia/Core/Template/Smarty/SmartyPluginDescriptor.php b/core/lib/Thelia/Core/Template/Smarty/SmartyPluginDescriptor.php index b22f99577..f3bdbb203 100755 --- a/core/lib/Thelia/Core/Template/Smarty/SmartyPluginDescriptor.php +++ b/core/lib/Thelia/Core/Template/Smarty/SmartyPluginDescriptor.php @@ -42,7 +42,7 @@ class SmartyPluginDescriptor protected $name; /** - * @var SmartyPluginInterface plugin implmentation class + * @var AbstractSmartyPlugin plugin implmentation class */ protected $class; diff --git a/install/INSTALL-TODO.txt b/install/INSTALL-TODO.txt index 772946a7c..748a1788c 100644 --- a/install/INSTALL-TODO.txt +++ b/install/INSTALL-TODO.txt @@ -8,3 +8,4 @@ Variables Config à initialiser: - default_locale : la locale par défaut (ex. en_US), à utiliser pour les fichiers de traduction - asset_dir_from_web_root : le chemin relatif à /web du repertoires des assets (ex. assets) - active_template: chemin du template front relatif au repertoire template (ex. default) +- thelia_version: la version de Thelia (ex. 2.0.0 alpha) diff --git a/templates/admin/default/assets/css/admin.less b/templates/admin/default/assets/css/admin.less index c19e623f6..0489c9348 100755 --- a/templates/admin/default/assets/css/admin.less +++ b/templates/admin/default/assets/css/admin.less @@ -112,6 +112,87 @@ hr { text-align: left; } +// -- Top bar ----------------------------------------------------------------- + +.topbar { + + @top-bar-height: 50px; + + background: url("img/top.jpg") repeat-x; + color: #6d737b; + font-size: 13px; + font-weight: bold; + text-shadow: 0px 1px 1px black; + + .form-search { + + position: relative; + float: right; + margin: 0px; + + input.search-query { + background: url("img/search.png") no-repeat; + width: 212px; + height: 20px; + outline: none; + border: none; + padding: 5px 20px 5px 20px; + border-radius: 0px; + font-size: 12px; + color: #eee; + + &:focus { + box-shadow: none; + color: white; + } + } + + button.btn { + position: absolute; + right: 5px; + text-indent: -13337px; + background: url("img/search-icon.png") no-repeat; + width: 16px; + height: 15px; + display: block; + cursor: pointer; + outline: none; + border: none; + top: 17px; + box-shadow: none; + border-radius: 0px; + z-index: 1337; + } + } + + .container { + line-height: @top-bar-height; + } + + .version-info { + float: left; + line-height: @top-bar-height; + background: url("img/top-bar-logo.png") left -3px no-repeat; + padding-left: 100px; + + } + + .user-info { + float: right; + line-height: @top-bar-height; + margin-left: 20px; + color: #fff; + + a.logout { + text-indent: -13337px; + display: inline-block; + background: url("img/logout.png") left center no-repeat; + width: 23px; + height: @top-bar-height; + } + } +} + // -- Brandbar ---------------------------------------------------------------- .loginpage { diff --git a/templates/admin/default/assets/css/img/search-icon.png b/templates/admin/default/assets/css/img/search-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..66178cb8f5cda0ded6073e48169acdce12493256 GIT binary patch literal 3320 zcmV5r00009a7bBm000XU z000XU0RWnu7ytkYPiaF#P*7-ZbZ>KLZ*U+IBfRsybQWXdwQbLP>6pAqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uhf59&ghTmgWD0l;*T zI709Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_IfqH8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 zfg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vFIcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya? z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0 z?2xS?_ve_-kiKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$ z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4 z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i& z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01 z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3| zawq-H%e&ckC+@AhPrP6BKT#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot2z=0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F} z0006bNklCfWw19fZ;}NQqSG=Hg&$aVT^UJB1RuNQ74*oda3S zDoN?sr4$?lO*>U^k``1lbr2IuEj&>y!B^6lgeG~-^X0Y2B~PBQ4*qc9a_&7J=bZl{ zBDewg3x&e$@$qr4)oSfmtJUqp!^70v++5K6?vEPx5Rb=)N~O|sxj-i;C;MAlTekq; zg#aL^>-r0~U~_ZxM>3iGmd$3r*Xwm#M5Nhl8uRn>qk!t^8sE~=(xl@!j)+Jik@yU( z0Z)MCv9YnYwOXwsBC@lyvkdh20tPlVHnJij2L}hIKpMCWTmxT}{*8 zxh|Ndd1M&It6HsA^xA2fW_x{oeF5n60u(R;ENYteu~MmYyhUecXXm@SyT5atPRGvY z^ACZ5_a6j+>%ikcAnKLZ*U+IBfRsybQWXdwQbLP>6pAqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uhf59&ghTmgWD0l;*T zI709Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_IfqH8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 zfg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vFIcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya? z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0 z?2xS?_ve_-kiKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$ z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4 z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i& z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01 z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3| zawq-H%e&ckC+@AhPrP6BKT#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot2z=0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F} z000DfNklX$^6OKP8x|su?5jZ2y|yRUDQPp3W6(ffD&b5$7Bt z4iVWhN^Ith`QPd0O;u6#c<)(jw`i?4QPs1+2M-(gI&db=6D@9|kiqp?IMS(^kt0cP(P_&RW=I8d4>l`A+GAa{kG&ATyX2gF}> z^RB9>_jsStTxrl+ZB7Coudnt0HSk{*2TBv=N)_jVdn&szW~YaLv(o#FmB#W$=-=G< zOt^K;9_R8C<;oyI(5viDWNYtkjM>3N1X0~A(&fq^S+_fpBx?)6M_ViWs5tlg-uf_6 z6!ogRh25ClTI%ttc%PA`DYxclQSYAwW;QDP$$^0qVHDy53%fD9ywu|qkbn?nV4y^E zrEwB?SAc?uwDt{;5Uy8uTeBO}A8UH=GrDP)x!FHa)nW)7%jFBWFhtg$${S<)WK9pa zP|4*BthHM&g}||VzJPNM=fpaBWBQ15f~q6m3;c^AFdoNwL>#?mc$Ro$`m54A;y4d5 z9s(m_6yluVterQe@7j7$!YBk734v-5gm>PPH^%f`g%5%dpz45Suij?Gm_DYxj5kBz zrq8mf#P$lUG5y-sXITd8FWy}5bdu)^#i9k?n7#}=bUF#Z^$@t+Zna*iRBNc(Za8E5 ztCd$&q1|c$Tn>SY?ba%)9+AMlhc~7V*YEAtD!|1MXvT4LZh2|(?Z@`k?>gkd7_+OR zj;gY}v`8FB=aM983P3KGdpb?KH%6b@PZZ~@tv9Am+WEATF!S3DqA1*-B+0Lv$u5`6 zpL^xK;gQkJgD;FRyEp9KduDIW;Dj$$R#x6;YwxbAu2!pkEkX3!-ukeebYsje4!f%M z_Q7ZA`NhS>H$=q4mJ@G8#2-F<`1JJjwA{WmcYIHMc;ojrgU0NpumiVm&4KS;7#|-$ zy%F}iD*T2cM~*yu?b@|dX*WB*@9`%H!^lE!%q|Igx7%gzuRn;w;KIR!2hU!)a^Y%FLcaOU~Wbg&Z0fqGkeF0#b=Z)Du9>{<$&;}9_ d(FYCsX8>61k;1&JGkpL6002ovPDHLkV1kl{aiRbK literal 0 HcmV?d00001 diff --git a/templates/admin/default/assets/css/img/top-bar-logo.png b/templates/admin/default/assets/css/img/top-bar-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..e1f3b87869481ae928dcffd034fe91aeae493f1f GIT binary patch literal 5696 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 t*?IZdr5SgWbL+ - +
-
- - {module_include location='index_top'} + {module_include location='home_top'} welcome home ! diff --git a/templates/admin/default/includes/header.inc.html b/templates/admin/default/includes/header.inc.html index 712a0a600..e0dd1997c 100755 --- a/templates/admin/default/includes/header.inc.html +++ b/templates/admin/default/includes/header.inc.html @@ -19,6 +19,127 @@ {/stylesheets} - {* TODO allow modules to include CSS here *} + {module_include location='head_css'} - \ No newline at end of file + + +{* display top bar once admin is connected *} + +{loop name="top-bar-auth" type="auth" context="admin" roles="ADMIN"} + +{module_include location='before_topbar'} + +
+
+ +
{intl l='Version %ver' ver="{$THELIA_VERSION}"}
+ + {module_include location='inside_topbar'} + + + + {loop name="top-bar-search" type="auth" context="admin" roles="ADMIN" permissions="admin.search"} + + {/loop} +
+
+ +{module_include location='after_topbar'} + + +{module_include location='before_top_menu'} + + + +{module_include location='after_top_menu'} + +{/loop} \ No newline at end of file