From e8bf47b92968c122dae4f36e651ed624d1a9401f Mon Sep 17 00:00:00 2001 From: franck Date: Tue, 16 Jul 2013 16:34:47 +0200 Subject: [PATCH] Refactored Form system to get simple error handling and a clearer interface --- core/lib/Thelia/Action/Customer.php | 15 +- .../Admin/Controller/BaseAdminController.php | 29 +--- .../Admin/Controller/SessionController.php | 46 ++--- core/lib/Thelia/Config/Resources/config.xml | 6 +- core/lib/Thelia/Core/Event/ActionEvent.php | 20 ++- .../Core/EventListener/ControllerListener.php | 4 +- .../Core/HttpFoundation/Session/Session.php | 37 +++- ...AdminUsernamePasswordFormAuthenticator.php | 8 +- ...tomerUsernamePasswordFormAuthenticator.php | 7 +- .../UsernamePasswordFormAuthenticator.php | 44 ++--- .../Thelia/Core/Security/SecurityContext.php | 47 +++++- core/lib/Thelia/Core/Template/Loop/Auth.php | 13 +- .../Smarty/Assets/SmartyAssetsManager.php | 3 +- .../Core/Template/Smarty/Plugins/Form.php | 159 +++++++++++------- .../Template/Smarty/Plugins/UrlGenerator.php | 59 +++++++ core/lib/Thelia/Form/AdminLogin.php | 10 +- core/lib/Thelia/Form/BaseForm.php | 25 ++- core/lib/Thelia/Form/CustomerCreation.php | 2 +- core/lib/Thelia/Form/CustomerLogin.php | 2 +- core/lib/Thelia/Form/CustomerModification.php | 2 +- core/lib/Thelia/Tools/URL.php | 57 +++++++ templates/admin/default/assets/css/admin.less | 158 +++++++++++++++++ templates/admin/default/home.html | 2 +- templates/admin/default/login.html | 26 ++- templates/smarty-sample/connexion.html | 62 +++---- templates/smarty-sample/index.html | 3 +- 26 files changed, 626 insertions(+), 220 deletions(-) create mode 100644 core/lib/Thelia/Core/Template/Smarty/Plugins/UrlGenerator.php create mode 100644 core/lib/Thelia/Tools/URL.php diff --git a/core/lib/Thelia/Action/Customer.php b/core/lib/Thelia/Action/Customer.php index 9241fd89f..09c3246fd 100755 --- a/core/lib/Thelia/Action/Customer.php +++ b/core/lib/Thelia/Action/Customer.php @@ -43,10 +43,9 @@ class Customer implements EventSubscriberInterface $request = $event->getRequest(); - $customerForm = new CustomerCreation($request); - - $form = $customerForm->getForm(); + $customerCreation = new CustomerCreation($request); + $form = $customerCreation->getForm(); if ($request->isMethod("post")) { $form->bind($request); @@ -68,18 +67,18 @@ class Customer implements EventSubscriberInterface $data["country"], $data["email"], $data["password"], - $request->getSession()->get("lang") + $request->getSession()->getLang() ); } catch (\PropelException $e) { Tlog::getInstance()->error(sprintf('error during creating customer on action/createCustomer with message "%s"', $e->getMessage())); - $event->setFormError($form); + $event->setFormError($customerCreation); } //Customer is create, he is automatically connected } else { - $event->setFormError($form); + $event->setFormError($customerCreation); } } @@ -117,10 +116,10 @@ class Customer implements EventSubscriberInterface ); } catch(\PropelException $e) { Tlog::getInstance()->error(sprintf('error during modifying customer on action/modifyCustomer with message "%s"', $e->getMessage())); - $event->setFormError($form); + $event->setFormError($customerModification); } } else { - $event->setFormError($form); + $event->setFormError($customerModification); } } diff --git a/core/lib/Thelia/Admin/Controller/BaseAdminController.php b/core/lib/Thelia/Admin/Controller/BaseAdminController.php index 47258480b..15a9753f3 100755 --- a/core/lib/Thelia/Admin/Controller/BaseAdminController.php +++ b/core/lib/Thelia/Admin/Controller/BaseAdminController.php @@ -34,6 +34,7 @@ use Thelia\Core\Security\Exception\AuthenticationTokenNotFoundException; use Thelia\Model\ConfigQuery; use Thelia\Core\Security\Exception\AuthenticationException; use Thelia\Core\Security\SecurityContext; +use Thelia\Tools\URL; /** * @@ -93,7 +94,7 @@ class BaseAdminController extends ContainerAware // User is not authenticated, and templates requires authentication -> redirect to login page // (see Thelia\Core\Template\Smarty\Plugins\Security) - return new RedirectResponse($this->generateUrl('admin/login')); // FIXME shoud be a parameter + return new RedirectResponse(URL::absoluteUrl('admin/login')); // FIXME shoud be a parameter } } @@ -155,32 +156,6 @@ class BaseAdminController extends ContainerAware return $this->getFormFactory()->createBuilder("form"); } - /** - * Generates a URL from the given parameters. - * - * @param string $route The name of the route - * @param mixed $parameters An array of parameters - * @param Boolean|string $referenceType The type of reference (one of the constants in UrlGeneratorInterface) - * - * @return string The generated URL - * - * @see UrlGeneratorInterface - */ - protected function generateUrl($path, array $parameters = array()) - { - $base = ConfigQuery::read("base_url", "/").$path; //FIXME - - $queryString = ''; - - foreach($parameters as $name => $value) { - $queryString = sprintf("%s=%s&", urlencode($name), urlencode($value)); - } - - if ('' !== $queryString = rtrim($queryString, "&")) $queryString = '?' . $queryString; - - return $base . $queryString; - } - /** * 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 e56af2d1b..457b6798e 100755 --- a/core/lib/Thelia/Admin/Controller/SessionController.php +++ b/core/lib/Thelia/Admin/Controller/SessionController.php @@ -32,16 +32,14 @@ use Thelia\Core\Security\Token\TokenInterface; use Thelia\Core\Security\Authentication\AdminUsernamePasswordFormAuthenticator; use Thelia\Model\AdminLog; use Thelia\Core\Security\Exception\AuthenticationException; +use Symfony\Component\Validator\Exception\ValidatorException; +use Thelia\Tools\URL; class SessionController extends BaseAdminController { public function showLoginAction() { - $form = $this->getLoginForm(); - - return $this->render("login.html", array( - "form" => $form->createView() - )); + return $this->render("login.html"); } public function checkLogoutAction() @@ -49,12 +47,12 @@ class SessionController extends BaseAdminController { $this->getSecurityContext()->clear(); // Go back to login page. - return $this->redirect($this->generateUrl('admin/login')); + return $this->redirect(URL::absoluteUrl('admin/login')); } public function checkLoginAction() { - $form = $this->getLoginForm(); + $form = new AdminLogin($this->getRequest()); $request = $this->getRequest(); @@ -69,8 +67,17 @@ class SessionController extends BaseAdminController { // Log authentication success AdminLog::append("Authentication successufull", $request, $user); + // Get the success URL to redirect the user to + $successUrl = $form->getForm()->get('success_url')->getData(); + + if (null == $successUrl) $successUrl = 'admin/home'; + // Redirect to home page - FIXME: requested URL, if we come from another page ? - return $this->redirect($this->generateUrl('admin')); + return $this->redirect(URL::absoluteUrl($successUrl)); + } + catch (ValidatorException $ex) { + + $message = "Missing or invalid information. Please check your input."; } catch (AuthenticationException $ex) { @@ -80,17 +87,18 @@ class SessionController extends BaseAdminController { $message = "Login failed. Please check your username and password."; } - // Display the login form again - return $this->render("login.html", array( - "form" => $authenticator->getLoginForm()->createView(), - "message" => $message - )); - } + // Store the form in session (see Form Smlarty plugin to find usage of this parameter) + $request->getSession()->setErrorFormName($form->getName()); - protected function getLoginForm() - { - $adminLogin = new AdminLogin($this->getRequest()); - - return $adminLogin->getForm(); + if (empty($failureUrl)) { + // Display the login form again, with an error message if required + return $this->render("login.html", array( + "message" => $message + )); + } + else { + // Redirect to the provided URL + return $this->redirect(URL::absoluteUrl($failureUrl)); + } } } \ No newline at end of file diff --git a/core/lib/Thelia/Config/Resources/config.xml b/core/lib/Thelia/Config/Resources/config.xml index ce63ace02..bb680aeac 100755 --- a/core/lib/Thelia/Config/Resources/config.xml +++ b/core/lib/Thelia/Config/Resources/config.xml @@ -27,7 +27,7 @@
- + @@ -120,6 +120,10 @@ + + + + diff --git a/core/lib/Thelia/Core/Event/ActionEvent.php b/core/lib/Thelia/Core/Event/ActionEvent.php index 32074c559..fc5822a93 100755 --- a/core/lib/Thelia/Core/Event/ActionEvent.php +++ b/core/lib/Thelia/Core/Event/ActionEvent.php @@ -25,6 +25,7 @@ namespace Thelia\Core\Event; use Symfony\Component\EventDispatcher\Event; use Symfony\Component\HttpFoundation\Request; +use Thelia\Form\BaseForm; /** * * Class thrown on Thelia.action event @@ -47,7 +48,7 @@ abstract class ActionEvent extends Event */ protected $action; - protected $form; + protected $form = null; /** * @@ -78,17 +79,30 @@ abstract class ActionEvent extends Event return $this->request; } - public function setFormError($form) + /** + * Store a form taht contains error, to pass it to the current session. + * + * @param BaseForm $form + */ + public function setFormError(BaseForm $form) { $this->form = $form; $this->stopPropagation(); } - public function getForm() + /** + * @return BaseForm the errored form, or null + */ + public function getFormError() { return $this->form; } + /** + * Check if theis event contains a form with errors + * + * @return boolean + */ public function hasFormError() { return $this->form !== null; diff --git a/core/lib/Thelia/Core/EventListener/ControllerListener.php b/core/lib/Thelia/Core/EventListener/ControllerListener.php index 93c827c09..96d249e69 100755 --- a/core/lib/Thelia/Core/EventListener/ControllerListener.php +++ b/core/lib/Thelia/Core/EventListener/ControllerListener.php @@ -50,9 +50,7 @@ class ControllerListener implements EventSubscriberInterface $dispatcher->dispatch("action.".$action, $actionEvent); if ($actionEvent->hasFormError()) { - $request->getSession()->set("form_error", true); - $request->getSession()->set("form_name", $actionEvent->getForm()->createView() - ->vars["attr"]["thelia_name"]); + $request->getSession()->setErrorFormName($actionEvent->getFormError()->getName()); } } diff --git a/core/lib/Thelia/Core/HttpFoundation/Session/Session.php b/core/lib/Thelia/Core/HttpFoundation/Session/Session.php index 63e07b2a7..564c4b1c6 100755 --- a/core/lib/Thelia/Core/HttpFoundation/Session/Session.php +++ b/core/lib/Thelia/Core/HttpFoundation/Session/Session.php @@ -25,6 +25,7 @@ namespace Thelia\Core\HttpFoundation\Session; use Symfony\Component\HttpFoundation\Session\Session as BaseSession; use Thelia\Core\Security\User\UserInterface; +use Thelia\Form\BaseForm; class Session extends BaseSession { @@ -39,28 +40,52 @@ class Session extends BaseSession { return substr($this->getLocale(), 0, 2); } - public function setCustomerUser(UserInterface $user) { + public function setCustomerUser(UserInterface $user) + { $this->set('customer_user', $user); } - public function getCustomerUser() { + public function getCustomerUser() + { return $this->get('customer_user'); } - public function clearCustomerUser() { + public function clearCustomerUser() + { return $this->remove('customer_user'); } - public function setAdminUser(UserInterface $user) { + public function setAdminUser(UserInterface $user) + { $this->set('admin_user', $user); } - public function getAdminUser() { + public function getAdminUser() + { return $this->get('admin_user'); } - public function clearAdminUser() { + public function clearAdminUser() + { return $this->remove('admin_user'); } + + /** + * @param string $formName the form name + */ + public function setErrorFormName($formName) + { + $this->set('error_form', $formName); + } + + public function getErrorFormName() + { + return $this->get('error_form', null); + } + + public function clearErrorFormName() + { + return $this->remove('error_form'); + } } diff --git a/core/lib/Thelia/Core/Security/Authentication/AdminUsernamePasswordFormAuthenticator.php b/core/lib/Thelia/Core/Security/Authentication/AdminUsernamePasswordFormAuthenticator.php index 81db900d8..b1427f0fd 100644 --- a/core/lib/Thelia/Core/Security/Authentication/AdminUsernamePasswordFormAuthenticator.php +++ b/core/lib/Thelia/Core/Security/Authentication/AdminUsernamePasswordFormAuthenticator.php @@ -29,17 +29,15 @@ use Symfony\Component\Form\Form; use Thelia\Core\Security\UserProvider\AdminUserProvider; use Thelia\Core\Security\Authentication\UsernamePasswordFormAuthenticator; +use Thelia\Form\AdminLogin; class AdminUsernamePasswordFormAuthenticator extends UsernamePasswordFormAuthenticator { - public function __construct(Request $request, Form $loginForm) { + public function __construct(Request $request, AdminLogin $loginForm) { parent::__construct( $request, $loginForm, - new AdminUserProvider(), - array( - 'username_field_name', 'email' - ) + new AdminUserProvider() ); } } \ No newline at end of file diff --git a/core/lib/Thelia/Core/Security/Authentication/CustomerUsernamePasswordFormAuthenticator.php b/core/lib/Thelia/Core/Security/Authentication/CustomerUsernamePasswordFormAuthenticator.php index 483894b93..8f3836119 100644 --- a/core/lib/Thelia/Core/Security/Authentication/CustomerUsernamePasswordFormAuthenticator.php +++ b/core/lib/Thelia/Core/Security/Authentication/CustomerUsernamePasswordFormAuthenticator.php @@ -30,11 +30,14 @@ use Thelia\Form\CustomerLogin; class CustomerUsernamePasswordFormAuthenticator extends UsernamePasswordFormAuthenticator { - public function __construct(Request $request, AdminLogin $loginForm) { + public function __construct(Request $request, CustomerLogin $loginForm) { parent::__construct( $request, $loginForm, - new AdminUserProvider(), + new CustomerUserProvider(), + array( + 'username_field_name', 'email' + ) ); } } \ No newline at end of file diff --git a/core/lib/Thelia/Core/Security/Authentication/UsernamePasswordFormAuthenticator.php b/core/lib/Thelia/Core/Security/Authentication/UsernamePasswordFormAuthenticator.php index 37156786c..77d54d682 100644 --- a/core/lib/Thelia/Core/Security/Authentication/UsernamePasswordFormAuthenticator.php +++ b/core/lib/Thelia/Core/Security/Authentication/UsernamePasswordFormAuthenticator.php @@ -29,6 +29,8 @@ use Thelia\Core\Security\UserProvider\UserProviderInterface; use Symfony\Component\Form\Form; use Thelia\Core\Security\Exception\WrongPasswordException; use Thelia\Core\Security\Exception\UsernameNotFoundException; +use Symfony\Component\Validator\Exception\ValidatorException; +use Thelia\Form\BaseForm; class UsernamePasswordFormAuthenticator implements AuthenticatorInterface { @@ -37,10 +39,12 @@ class UsernamePasswordFormAuthenticator implements AuthenticatorInterface { protected $userProvider; protected $options; + protected $baseLoginForm; - public function __construct(Request $request, Form $loginForm, UserProviderInterface $userProvider, array $options = array()) { + public function __construct(Request $request, BaseForm $loginForm, UserProviderInterface $userProvider, array $options = array()) { $this->request = $request; - $this->loginForm = $loginForm; + $this->baseLoginForm = $loginForm; + $this->loginForm = $this->baseLoginForm->getForm(); $this->userProvider = $userProvider; $defaults = array( @@ -54,38 +58,38 @@ class UsernamePasswordFormAuthenticator implements AuthenticatorInterface { $this->loginForm->bind($this->request); } - public function getLoginForm() { - return $this->loginForm; - } - + /** + * @return string the username value + */ public function getUsername() { return $this->loginForm->get($this->options['username_field_name'])->getData(); } + /** + * @see \Thelia\Core\Security\Authentication\AuthenticatorInterface::getAuthentifiedUser() + */ public function getAuthentifiedUser() { if ($this->request->isMethod($this->options['required_method'])) { - if ($this->loginForm->isValid()) { + if (! $this->loginForm->isValid()) throw new ValidatorException("Form is not valid."); - // Retreive user - $username = $this->getUsername(); - $password = $this->loginForm->get($this->options['password_field_name'])->getData(); + // Retreive user + $username = $this->getUsername(); + $password = $this->loginForm->get($this->options['password_field_name'])->getData(); - $user = $this->userProvider->getUser($username); + $user = $this->userProvider->getUser($username); - if ($user === null) throw new UsernameNotFoundException(sprintf("Username '%s' was not found.", $username)); + if ($user === null) throw new UsernameNotFoundException(sprintf("Username '%s' was not found.", $username)); - // Check user password - $authOk = $user->checkPassword($password) === true; + // Check user password + $authOk = $user->checkPassword($password) === true; - if ($authOk !== true) throw new WrongPasswordException(sprintf("Wrong password for user '%s'.", $username)); + if ($authOk !== true) throw new WrongPasswordException(sprintf("Wrong password for user '%s'.", $username)); - return $user; - } - } - else { - throw new \RuntimeException("Invalid method."); + return $user; } + + throw new \RuntimeException("Invalid method."); } } \ 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 645d9a196..a4409afd8 100755 --- a/core/lib/Thelia/Core/Security/SecurityContext.php +++ b/core/lib/Thelia/Core/Security/SecurityContext.php @@ -109,10 +109,51 @@ class SecurityContext { { if ($this->isAuthenticated() === true) { - echo "TODO: check roles and permissions !"; + $user = $this->getUser(); - // TODO : check roles and permissions - return true; + // Check if user's roles matches required roles + $userRoles = $user->getRoles(); + + $roleFound = false; + + foreach($userRoles as $role) { + if (in_array($role, $roles)) { + $roleFound = true; + + break; + } + } + + if ($roleFound) { + + if (empty($permissions)) { + return true; + } + + // Get permissions from profile + // $userPermissions = $user->getPermissions(); + + echo "TODO: Finalize permissions system !"; + + $userPermissions = array('*'); // FIXME ! + + $permissionsFound = true; + + // User have all permissions ? + if (in_array('*', $userPermissions)) + return true; + + // Check that user's permissions matches required permissions + foreach($permissions as $permission) { + if (! in_array($permission, $userPermissions)) { + $permissionsFound = false; + + break; + } + } + + return $permissionsFound; + } } return false; diff --git a/core/lib/Thelia/Core/Template/Loop/Auth.php b/core/lib/Thelia/Core/Template/Loop/Auth.php index d0c477ef9..5adaf8286 100755 --- a/core/lib/Thelia/Core/Template/Loop/Auth.php +++ b/core/lib/Thelia/Core/Template/Loop/Auth.php @@ -47,7 +47,8 @@ class Auth extends BaseLoop { return new ArgumentCollection( Argument::createAnyTypeArgument('roles', null, true), - Argument::createAnyTypeArgument('permissions') + Argument::createAnyTypeArgument('permissions'), + Argument::createAnyTypeArgument('context', 'front', false) ); } @@ -72,16 +73,20 @@ class Auth extends BaseLoop */ public function exec(&$pagination) { + $context = $this->getContext(); $roles = $this->_explode($this->getRoles()); $permissions = $this->_explode($this->getPermissions()); $loopResult = new LoopResult(); try { - $this->securityContext->isGranted($roles, $permissions == null ? array() : $permissions); + $this->securityContext->setContext($context); - // Create an empty row: loop is no longer empty :) - $loopResult->addRow(new LoopResultRow()); + if (true === $this->securityContext->isGranted($roles, $permissions == null ? array() : $permissions)) { + + // Create an empty row: loop is no longer empty :) + $loopResult->addRow(new LoopResultRow()); + } } catch (\Exception $ex) { // Not granted, loop is empty diff --git a/core/lib/Thelia/Core/Template/Smarty/Assets/SmartyAssetsManager.php b/core/lib/Thelia/Core/Template/Smarty/Assets/SmartyAssetsManager.php index 63a9a90e4..5610785d8 100755 --- a/core/lib/Thelia/Core/Template/Smarty/Assets/SmartyAssetsManager.php +++ b/core/lib/Thelia/Core/Template/Smarty/Assets/SmartyAssetsManager.php @@ -25,6 +25,7 @@ namespace Thelia\Core\Template\Smarty\Assets; use Thelia\Core\Template\Assets\AsseticHelper; use Thelia\Model\ConfigQuery; +use Thelia\Tools\URL; class SmartyAssetsManager { @@ -72,7 +73,7 @@ class SmartyAssetsManager $url = $this->assetic_manager->asseticize( $asset_dir.'/'.$asset_file, $this->web_root."/".$this->path_relative_to_web_root, - ConfigQuery::read('base_url', '/') . $this->path_relative_to_web_root, + URL::absoluteUrl($this->path_relative_to_web_root), $assetType, $filters, $debug diff --git a/core/lib/Thelia/Core/Template/Smarty/Plugins/Form.php b/core/lib/Thelia/Core/Template/Smarty/Plugins/Form.php index 2ff6be8e3..db71484ed 100755 --- a/core/lib/Thelia/Core/Template/Smarty/Plugins/Form.php +++ b/core/lib/Thelia/Core/Template/Smarty/Plugins/Form.php @@ -58,7 +58,6 @@ class Form implements SmartyPluginInterface { protected $request; - protected $form; protected $formDefinition = array(); public function __construct(Request $request) @@ -87,67 +86,74 @@ class Form implements SmartyPluginInterface throw new \InvalidArgumentException("Missing 'name' parameter in form arguments"); } - $instance = $this->getInstance($params['name']); - $form = $instance->getForm(); + $instance = $this->createInstance($params['name']); - if ( - true === $this->request->getSession()->get("form_error", false) && - $this->request->getSession()->get("form_name") == $instance->getName()) - { - $form->bind($this->request); - $this->request->getSession()->set("form_error", false); + // Check if session contains our form + $errorForm = $this->request->getSession()->getErrorFormName(); + + if ($errorForm == $instance->getName()) { + + // Bind form with current request to get error messages and field values. + $instance->getForm()->bind($this->request); + + // Remove the form from the session + $this->request->getSession()->clearErrorFormName(); } - $template->assign("form", $form->createView()); - } else { + $instance->createView(); + + $template->assign("form", $instance); + } + else { return $content; } } - public function formRender($params, $content, \Smarty_Internal_Template $template, &$repeat) + public function renderFormField($params, $content, \Smarty_Internal_Template $template, &$repeat) { if ($repeat) { - $form = $params["form"]; + $formFieldView = $this->getFormFieldView($params); - if (! $form instanceof \Symfony\Component\Form\FormView) { - throw new \InvalidArgumentException("form parameter in form_field block must be an instance of - Symfony\Component\Form\FormView"); + $template->assign("options", $formFieldView->vars); + $template->assign("name", $formFieldView->vars["full_name"]); + $template->assign("value", $formFieldView->vars["value"]); + $template->assign("label", $formFieldView->vars["label"]); + + $errors = $formFieldView->vars["errors"]; + + $template->assign("error", empty($errors) ? false : true); + + if (! empty($errors)) { + $this->assignFieldErrorVars($template, $errors); } - - $template->assign("options", $form->vars); - $template->assign("name", $form->vars["full_name"]); - $template->assign("value", $form->vars["value"]); - $template->assign("label", $form->vars["label"]); - $template->assign("error", empty($form->vars["errors"]) ? false : true); $attr = array(); - foreach ($form->vars["attr"] as $key => $value) { + + foreach ($formFieldView->vars["attr"] as $key => $value) { $attr[] = sprintf('%s="%s"', $key, $value); } + $template->assign("attr", implode(" ", $attr)); - $form->setRendered(); - - } else { + $formFieldView->setRendered(); + } + else { return $content; } } - public function formRenderHidden($params, \Smarty_Internal_Template $template) + public function renderHiddenFormField($params, \Smarty_Internal_Template $template) { - $form = $params["form"]; - $field = ''; - if (! $form instanceof \Symfony\Component\Form\FormView) { - throw new \InvalidArgumentException("form parameter in form_field_hidden function must be an instance of - Symfony\Component\Form\FormView"); - } + $instance = $this->getInstanceFromParams($params); + + $formView = $instance->getView(); $return = ""; - foreach ($form->getIterator() as $row) { + foreach ($formView->getIterator() as $row) { if ($this->isHidden($row) && $row->isRendered() === false) { $return .= sprintf($field, $row->vars["full_name"], $row->vars["value"]); } @@ -156,53 +162,79 @@ class Form implements SmartyPluginInterface return $return; } - protected function isHidden(FormView $formView) - { - return array_search("hidden", $formView->vars["block_prefixes"]); - } - public function formEnctype($params, \Smarty_Internal_Template $template) { - $form = $params["form"]; + $instance = $this->getInstanceFromParams($params); - if (! $form instanceof \Symfony\Component\Form\FormView) { - throw new \InvalidArgumentException("form parameter in form_enctype function must be an instance of - Symfony\Component\Form\FormView"); - } + $formView = $instance->getForm(); - if ($form->vars["multipart"]) { + if ($formView->vars["multipart"]) { return sprintf('%s="%s"',"enctype", "multipart/form-data"); } } public function formError($params, $content, \Smarty_Internal_Template $template, &$repeat) { + $formFieldView = $this->getFormFieldView($params); - $form = $params["form"]; - if (! $form instanceof \Symfony\Component\Form\FormView) { - throw new \InvalidArgumentException("form parameter in form_error block must be an instance of - Symfony\Component\Form\FormView"); - } + $errors = $formFieldView->vars["errors"]; - if (empty($form->vars["errors"])) { + if (empty($errors)) { return ""; } if ($repeat) { - - $error = $form->vars["errors"]; - - $template->assign("message", $error[0]->getMessage()); - $template->assign("parameters", $error[0]->getMessageParameters()); - $template->assign("pluralization", $error[0]->getMessagePluralization()); - - - } else { + $this->assignFieldErrorVars($template, $errors); + } + else { return $content; } } - public function getInstance($name) + + protected function assignFieldErrorVars(\Smarty_Internal_Template $template, array $errors) + { + $template->assign("message", $errors[0]->getMessage()); + $template->assign("parameters", $errors[0]->getMessageParameters()); + $template->assign("pluralization", $errors[0]->getMessagePluralization()); + } + + protected function isHidden(FormView $formView) + { + return array_search("hidden", $formView->vars["block_prefixes"]); + } + + protected function getFormFieldView($params) { + $instance = $this->getInstanceFromParams($params); + + if (! isset($params['field'])) + throw new \InvalidArgumentException("'field' parameter is missing"); + + $fieldName = $params['field']; + + if (empty($instance->getView()[$fieldName])) + throw new \InvalidArgumentException(sprintf("Field name '%s' not found in form %s", $fieldName, $instance->getName())); + + return $instance->getView()[$fieldName]; + } + + protected function getInstanceFromParams($params) { + + if (empty($params['form'])) { + throw new \InvalidArgumentException("Missing 'form' parameter in form arguments"); + } + + $instance = $params["form"]; + + if (! $instance instanceof \Thelia\Form\BaseForm) { + throw new \InvalidArgumentException(sprintf("form parameter in form_field block must be an instance of + \Thelia\Form\BaseForm, instance of %s found", get_class($instance))); + } + + return $instance; + } + + protected function createInstance($name) { if (!isset($this->formDefinition[$name])) { throw new ElementNotFoundException(sprintf("%s form does not exists", $name)); @@ -210,7 +242,6 @@ class Form implements SmartyPluginInterface $class = new \ReflectionClass($this->formDefinition[$name]); - return $class->newInstance( $this->request, "form" @@ -224,8 +255,8 @@ class Form implements SmartyPluginInterface { return array( new SmartyPluginDescriptor("block", "form", $this, "generateForm"), - new SmartyPluginDescriptor("block", "form_field", $this, "formRender"), - new SmartyPluginDescriptor("function", "form_field_hidden", $this, "formRenderHidden"), + new SmartyPluginDescriptor("block", "form_field", $this, "renderFormField"), + new SmartyPluginDescriptor("function", "form_hidden_fields", $this, "renderHiddenFormField"), new SmartyPluginDescriptor("function", "form_enctype", $this, "formEnctype"), new SmartyPluginDescriptor("block", "form_error", $this, "formError") ); diff --git a/core/lib/Thelia/Core/Template/Smarty/Plugins/UrlGenerator.php b/core/lib/Thelia/Core/Template/Smarty/Plugins/UrlGenerator.php new file mode 100644 index 000000000..adcee9579 --- /dev/null +++ b/core/lib/Thelia/Core/Template/Smarty/Plugins/UrlGenerator.php @@ -0,0 +1,59 @@ +. */ +/* */ +/*************************************************************************************/ + +namespace Thelia\Core\Template\Smarty\Plugins; + +use Thelia\Core\Template\Smarty\SmartyPluginDescriptor; +use Thelia\Core\Template\Smarty\SmartyPluginInterface; +use Thelia\Tools\URL; + +class UrlGenerator implements SmartyPluginInterface +{ + /** + * Process url generator function + * + * @param array $params + * @param unknown $smarty + * @return string no text is returned. + */ + public function generateUrlFunction($params, &$smarty) + { + // the path to process + $path =trim($params['path']); + + return URL::absoluteUrl($path); + } + + /** + * Define the various smarty plugins hendled by this class + * + * @return an array of smarty plugin descriptors + */ + public function getPluginDescriptors() + { + return array( + new SmartyPluginDescriptor('function', 'url', $this, 'generateUrlFunction') + ); + } +} diff --git a/core/lib/Thelia/Form/AdminLogin.php b/core/lib/Thelia/Form/AdminLogin.php index 6ba27830e..b67e788fc 100755 --- a/core/lib/Thelia/Form/AdminLogin.php +++ b/core/lib/Thelia/Form/AdminLogin.php @@ -23,7 +23,6 @@ namespace Thelia\Form; -use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Validator\Constraints\Length; use Symfony\Component\Validator\Constraints\NotBlank; use Symfony\Component\Validator\Constraints\Choice; @@ -32,7 +31,7 @@ class AdminLogin extends BaseForm { protected function buildForm() { - $this->form + $this->formBuilder ->add("username", "text", array( "constraints" => array( new NotBlank(), @@ -44,12 +43,15 @@ class AdminLogin extends BaseForm { new NotBlank() ) )) - ->add("remember_me", "checkbox") + ->add("remember_me", "checkbox", array( + 'value' => 'yes' + )) + ->add("success_url", "text") ; } public function getName() { - return "admin_login"; + return "adminLogin"; } } diff --git a/core/lib/Thelia/Form/BaseForm.php b/core/lib/Thelia/Form/BaseForm.php index b53747a5d..a3220fa8b 100755 --- a/core/lib/Thelia/Form/BaseForm.php +++ b/core/lib/Thelia/Form/BaseForm.php @@ -36,9 +36,14 @@ abstract class BaseForm { /** * @var \Symfony\Component\Form\FormFactoryInterface */ + protected $formBuilder; + + /** + * @var \Symfony\Component\Form\Form + */ protected $form; - public $name; + private $view = null; public function __construct(Request $request, $type= "form", $data = array(), $options = array()) { @@ -48,7 +53,7 @@ abstract class BaseForm { $options["attr"]["thelia_name"] = $this->getName(); } - $this->form = Forms::createFormFactoryBuilder() + $this->formBuilder = Forms::createFormFactoryBuilder() ->addExtension(new HttpFoundationExtension()) ->addExtension( new CsrfExtension( @@ -63,9 +68,19 @@ abstract class BaseForm { ->createNamedBuilder($this->getName(), $type, $data, $options); ; + $this->buildForm(); + $this->form = $this->formBuilder->getForm(); + } - $this->buildForm(); + public function createView() { + $this->view = $this->form->createView(); + } + + public function getView() { + if ($this->view === null) throw new \LogicException("View was not created. Please call BaseForm::createView() first."); + + return $this->view; } /** @@ -73,13 +88,13 @@ abstract class BaseForm { */ public function getForm() { - return $this->form->getForm(); + return $this->form; } /** * * in this function you add all the fields you need for your Form. - * Form this you have to call add method on $this->form attribute : + * Form this you have to call add method on $this->formBuilder attribute : * * $this->form->add("name", "text") * ->add("email", "email", array( diff --git a/core/lib/Thelia/Form/CustomerCreation.php b/core/lib/Thelia/Form/CustomerCreation.php index 2ba80f362..9f675e74e 100755 --- a/core/lib/Thelia/Form/CustomerCreation.php +++ b/core/lib/Thelia/Form/CustomerCreation.php @@ -33,7 +33,7 @@ class CustomerCreation extends BaseForm protected function buildForm() { - $this->form + $this->formBuilder ->add("firstname", "text", array( "constraints" => array( new Constraints\NotBlank() diff --git a/core/lib/Thelia/Form/CustomerLogin.php b/core/lib/Thelia/Form/CustomerLogin.php index e18947d1b..e1096f9b4 100644 --- a/core/lib/Thelia/Form/CustomerLogin.php +++ b/core/lib/Thelia/Form/CustomerLogin.php @@ -32,7 +32,7 @@ class CustomerLogin extends BaseForm { protected function buildForm() { - $this->form + $this->formBuilder ->add("username", "text", array( "constraints" => array( new NotBlank(), diff --git a/core/lib/Thelia/Form/CustomerModification.php b/core/lib/Thelia/Form/CustomerModification.php index cb21be2fa..c87e5fece 100755 --- a/core/lib/Thelia/Form/CustomerModification.php +++ b/core/lib/Thelia/Form/CustomerModification.php @@ -52,7 +52,7 @@ class CustomerModification extends BaseForm { protected function buildForm() { - $this->form + $this->formBuilder ->add("firstname", "text", array( "constraints" => array( new Constraints\NotBlank() diff --git a/core/lib/Thelia/Tools/URL.php b/core/lib/Thelia/Tools/URL.php new file mode 100644 index 000000000..b1c8964d4 --- /dev/null +++ b/core/lib/Thelia/Tools/URL.php @@ -0,0 +1,57 @@ +. */ +/* */ +/*************************************************************************************/ + +namespace Thelia\Tools; + +use Thelia\Model\ConfigQuery; + +class URL +{ + /** + * Returns the Absolute URL for a given path relative to web root + * + * @param string $path the relative path + * @param mixed $parameters An array of parameters + * @param Boolean|string $referenceType The type of reference (one of the constants in UrlGeneratorInterface) + * + * @return string The generated URL + */ + public static function absoluteUrl($path, array $parameters = array()) + { + // Already absolute ? + if (substr($path, 0, 4) != 'http') + $base = ConfigQuery::read('base_url', '/') . ltrim($path, '/'); + else + $base = $path; + + $queryString = ''; + + foreach($parameters as $name => $value) { + $queryString = sprintf("%s=%s&", urlencode($name), urlencode($value)); + } + + if ('' !== $queryString = rtrim($queryString, "&")) $queryString = '?' . $queryString; + + return $base . $queryString; + } +} diff --git a/templates/admin/default/assets/css/admin.less b/templates/admin/default/assets/css/admin.less index fb3b8f8b7..c19e623f6 100755 --- a/templates/admin/default/assets/css/admin.less +++ b/templates/admin/default/assets/css/admin.less @@ -235,4 +235,162 @@ hr { border: 1px solid #e5e5e5; .border-radius; .box-shadow; +} + +// -- Allow inline forms validation states ------------------------------------ + +.form-inline .warning .control-label, +.form-inline .warning .help-block, +.form-inline .warning .help-inline { + color: #c09853; +} + +.form-inline .warning .checkbox, +.form-inline .warning .radio, +.form-inline .warning input, +.form-inline .warning select, +.form-inline .warning textarea { + color: #c09853; +} + +.form-inline .warning input, +.form-inline .warning select, +.form-inline .warning textarea { + border-color: #c09853; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); +} + +.form-inline .warning input:focus, +.form-inline .warning select:focus, +.form-inline .warning textarea:focus { + border-color: #a47e3c; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #dbc59e; + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #dbc59e; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #dbc59e; +} + +.form-inline .warning .input-prepend .add-on, +.form-inline .warning .input-append .add-on { + color: #c09853; + background-color: #fcf8e3; + border-color: #c09853; +} + +.form-inline .error .control-label, +.form-inline .error .help-block, +.form-inline .error .help-inline { + color: #b94a48; +} + +.form-inline .error .checkbox, +.form-inline .error .radio, +.form-inline .error input, +.form-inline .error select, +.form-inline .error textarea { + color: #b94a48; +} + +.form-inline .error input, +.form-inline .error select, +.form-inline .error textarea { + border-color: #b94a48; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); +} + +.form-inline .error input:focus, +.form-inline .error select:focus, +.form-inline .error textarea:focus { + border-color: #953b39; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #d59392; + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #d59392; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #d59392; +} + +.form-inline .error .input-prepend .add-on, +.form-inline .error .input-append .add-on { + color: #b94a48; + background-color: #f2dede; + border-color: #b94a48; +} + +.form-inline .success .control-label, +.form-inline .success .help-block, +.form-inline .success .help-inline { + color: #468847; +} + +.form-inline .success .checkbox, +.form-inline .success .radio, +.form-inline .success input, +.form-inline .success select, +.form-inline .success textarea { + color: #468847; +} + +.form-inline .success input, +.form-inline .success select, +.form-inline .success textarea { + border-color: #468847; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); +} + +.form-inline .success input:focus, +.form-inline .success select:focus, +.form-inline .success textarea:focus { + border-color: #356635; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7aba7b; + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7aba7b; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7aba7b; +} + +.form-inline .success .input-prepend .add-on, +.form-inline .success .input-append .add-on { + color: #468847; + background-color: #dff0d8; + border-color: #468847; +} + +.form-inline .info .control-label, +.form-inline .info .help-block, +.form-inline .info .help-inline { + color: #3a87ad; +} + +.form-inline .info .checkbox, +.form-inline .info .radio, +.form-inline .info input, +.form-inline .info select, +.form-inline .info textarea { + color: #3a87ad; +} + +.form-inline .info input, +.form-inline .info select, +.form-inline .info textarea { + border-color: #3a87ad; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); +} + +.form-inline .info input:focus, +.form-inline .info select:focus, +.form-inline .info textarea:focus { + border-color: #2d6987; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7ab5d3; + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7ab5d3; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7ab5d3; +} + +.form-inline .info .input-prepend .add-on, +.form-inline .info .input-append .add-on { + color: #3a87ad; + background-color: #d9edf7; + border-color: #3a87ad; } \ No newline at end of file diff --git a/templates/admin/default/home.html b/templates/admin/default/home.html index 53f0a5b6b..f49172464 100755 --- a/templates/admin/default/home.html +++ b/templates/admin/default/home.html @@ -1,4 +1,4 @@ -{check_auth context="admin" roles="ADMIN"} +{check_auth context="admin" roles="ADMIN" login_url="login"} {$page_title={intl l='Home'}} {include file='includes/header.inc.html'} diff --git a/templates/admin/default/login.html b/templates/admin/default/login.html index 55cc345b7..b674a1dc5 100755 --- a/templates/admin/default/login.html +++ b/templates/admin/default/login.html @@ -13,28 +13,36 @@

{intl l='Thelia Back Office'}

+ {form name="thelia.admin.login" success_url="home" error_url="login"} {if isset($message)}
{$message}
{/if} - {form_field_hidden form=$form} + {form_hidden_fields form=$form} - {form_field form=$form.username} - {form_error form=$form.username}{$message}{/form_error} - + {form_field form=$form field='success_url'} + {* redirect to /admin *} + {/form_field} + + {form_field form=$form field='username'} + + + {/form_field} - {form_field form=$form.password} - {form_error form=$form.password}{$message}{/form_error} - + {form_field form=$form field='password'} + + + {/form_field} - {form_field form=$form.remember_me} - + {form_field form=$form field='remember_me'} + {/form_field} + {/form}
{module_include location='index_middle'} diff --git a/templates/smarty-sample/connexion.html b/templates/smarty-sample/connexion.html index 44b13386e..a2f9120dd 100755 --- a/templates/smarty-sample/connexion.html +++ b/templates/smarty-sample/connexion.html @@ -3,10 +3,10 @@ {form name="thelia.customer.creation"}
- {form_field_hidden form=$form} + {form_hidden_fields form=$form} - {form_field form=$form.title} - {form_error form=$form.title} + {form_field form=$form field="title"} + {form_error form=$form field="title"} {$message} {/form_error} @@ -19,64 +19,64 @@
{/form_field} - {form_field form=$form.firstname} - {form_error form=$form.firstname} + {form_field form=$form field="firstname"} + {form_error form=$form field="firstname"} {$message} {/form_error}
{/form_field} - {form_field form=$form.lastname} - {form_error form=$form.lastname} + {form_field form=$form field="lastname"} + {form_error form=$form field="lastname"} {$message} {/form_error}
{/form_field} - {form_field form=$form.address1} - {form_error form=$form.address1} + {form_field form=$form field="address1"} + {form_error form=$form field="address1"} {$message} {/form_error}
{/form_field} - {form_field form=$form.address2} - {form_error form=$form.address2} + {form_field form=$form field="address2"} + {form_error form=$form field="address2"} {$message} {/form_error}
{/form_field} - {form_field form=$form.address3} - {form_error form=$form.address3} + {form_field form=$form field="address3"} + {form_error form=$form field="address3"} {$message} {/form_error}
{/form_field} - {form_field form=$form.zipcode} - {form_error form=$form.zipcode} + {form_field form=$form field="zipcode"} + {form_error form=$form field="zipcode"} {$message} {/form_error}
{/form_field} - {form_field form=$form.city} - {form_error form=$form.city} + {form_field form=$form field="city"} + {form_error form=$form field="city"} {$message} {/form_error}
{/form_field} - {form_field form=$form.country} - {form_error form=$form.country} + {form_field form=$form field="country"} + {form_error form=$form field="country"} {$message} {/form_error} @@ -89,43 +89,43 @@
{/form_field} - {form_field form=$form.phone} - {form_error form=$form.phone} + {form_field form=$form field="phone"} + {form_error form=$form field="phone"} {$message} {/form_error}
{/form_field} - {form_field form=$form.cellphone} - {form_error form=$form.cellphone} + {form_field form=$form field="cellphone"} + {form_error form=$form field="cellphone"} {$message} {/form_error}
{/form_field} - {form_field form=$form.email} - {form_error form=$form.email} + {form_field form=$form field="email"} + {form_error form=$form field="email"} {#message} {/form_error}
{/form_field} - {form_field form=$form.email_confirm} - {form_error form=$form.email_confirm} + {form_field form=$form field="email_confirm"} + {form_error form=$form field="email_confirm"} {#message} {/form_error}
{/form_field} - {form_field form=$form.password} - {form_error form=$form.password} + {form_field form=$form field="password"} + {form_error form=$form field="password"} {#message} {/form_error}
{/form_field} - {form_field form=$form.password_confirm} - {form_error form=$form.password_confirm} + {form_field form=$form field="password_confirm"} + {form_error form=$form field="password_confirm"} {#message} {/form_error}
diff --git a/templates/smarty-sample/index.html b/templates/smarty-sample/index.html index 87efeeb4d..53834febf 100755 --- a/templates/smarty-sample/index.html +++ b/templates/smarty-sample/index.html @@ -1,7 +1,8 @@ {include file="includes/header.html"}
-{loop type="auth" name="auth_test" roles="CUSTOMER"} +{loop type="auth" name="auth_test" context="front" roles="CUSTOMER"} +

Customer is authentified :-)

{/loop}