Added category creation and deletion
This commit is contained in:
266
core/lib/Thelia/Action/Category.php
Normal file
266
core/lib/Thelia/Action/Category.php
Normal file
@@ -0,0 +1,266 @@
|
||||
<?php
|
||||
/*************************************************************************************/
|
||||
/* */
|
||||
/* Thelia */
|
||||
/* */
|
||||
/* Copyright (c) OpenStudio */
|
||||
/* email : info@thelia.net */
|
||||
/* web : http://www.thelia.net */
|
||||
/* */
|
||||
/* This program is free software; you can redistribute it and/or modify */
|
||||
/* it under the terms of the GNU General Public License as published by */
|
||||
/* the Free Software Foundation; either version 3 of the License */
|
||||
/* */
|
||||
/* This program is distributed in the hope that it will be useful, */
|
||||
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
|
||||
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
|
||||
/* GNU General Public License for more details. */
|
||||
/* */
|
||||
/* You should have received a copy of the GNU General Public License */
|
||||
/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
/* */
|
||||
/*************************************************************************************/
|
||||
|
||||
namespace Thelia\Action;
|
||||
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Thelia\Core\Event\ActionEvent;
|
||||
use Thelia\Core\Event\TheliaEvents;
|
||||
use Thelia\Model\Category as CategoryModel;
|
||||
use Thelia\Form\CategoryCreationForm;
|
||||
use Thelia\Core\Event\CategoryEvent;
|
||||
use Thelia\Tools\Redirect;
|
||||
use Thelia\Model\CategoryQuery;
|
||||
use Thelia\Model\AdminLog;
|
||||
use Thelia\Form\CategoryDeletionForm;
|
||||
|
||||
class Category implements EventSubscriberInterface
|
||||
{
|
||||
public function create(ActionEvent $event)
|
||||
{
|
||||
$request = $event->getRequest();
|
||||
|
||||
$categoryCreationForm = new CategoryCreationForm($request);
|
||||
|
||||
$form = $categoryCreationForm->getForm();
|
||||
|
||||
if ($request->isMethod("post")) {
|
||||
|
||||
$form->bind($request);
|
||||
|
||||
if ($form->isValid()) {
|
||||
|
||||
$data = $form->getData();
|
||||
|
||||
$category = new CategoryModel();
|
||||
|
||||
try {
|
||||
$event->getDispatcher()->dispatch(TheliaEvents::BEFORE_CREATECATEGORY, $event);
|
||||
|
||||
$category->create(
|
||||
$data["title"],
|
||||
$data["parent"],
|
||||
$data["locale"]
|
||||
);
|
||||
|
||||
AdminLog::append(sprintf("Category %s (ID %s) created", $category->getTitle(), $category->getId()), $request, $request->getSession()->getAdminUser());
|
||||
|
||||
$categoryEvent = new CategoryEvent($category);
|
||||
|
||||
$event->getDispatcher()->dispatch(TheliaEvents::AFTER_CREATECATEGORY, $categoryEvent);
|
||||
|
||||
// Substitute _ID_ in the URL with the ID of the created category
|
||||
$successUrl = str_replace('_ID_', $category->getId(), $categoryCreationForm->getSuccessUrl());
|
||||
|
||||
// Redirect to the success URL
|
||||
Redirect::exec($successUrl);
|
||||
|
||||
} catch (Exception $e) {
|
||||
Tlog::getInstance()->error(sprintf('error during creating customer on action/createCustomer with message "%s"', $e->getMessage()));
|
||||
|
||||
$message = "Failed to create this category, please try again.";
|
||||
}
|
||||
}
|
||||
else {
|
||||
$message = "Missing or invalid data";
|
||||
}
|
||||
}
|
||||
else {
|
||||
$message = "Wrong form method !";
|
||||
}
|
||||
|
||||
// The form has an error
|
||||
$categoryCreationForm->setError(true);
|
||||
$categoryCreationForm->setErrorMessage($message);
|
||||
|
||||
// Store the form in the parser context
|
||||
$event->setErrorForm($categoryCreationForm);
|
||||
|
||||
// Stop event propagation
|
||||
$event->stopPropagation();
|
||||
}
|
||||
|
||||
public function modify(ActionEvent $event)
|
||||
{
|
||||
/*
|
||||
$request = $event->getRequest();
|
||||
|
||||
$customerModification = new CustomerModification($request);
|
||||
|
||||
$form = $customerModification->getForm();
|
||||
|
||||
if ($request->isMethod("post")) {
|
||||
|
||||
$form->bind($request);
|
||||
|
||||
if ($form->isValid()) {
|
||||
$data = $form->getData();
|
||||
|
||||
$customer = CustomerQuery::create()->findPk(1);
|
||||
try {
|
||||
$customerEvent = new CustomerEvent($customer);
|
||||
$event->getDispatcher()->dispatch(TheliaEvents::BEFORE_CHANGECUSTOMER, $customerEvent);
|
||||
|
||||
$data = $form->getData();
|
||||
|
||||
$customer->createOrUpdate(
|
||||
$data["title"],
|
||||
$data["firstname"],
|
||||
$data["lastname"],
|
||||
$data["address1"],
|
||||
$data["address2"],
|
||||
$data["address3"],
|
||||
$data["phone"],
|
||||
$data["cellphone"],
|
||||
$data["zipcode"],
|
||||
$data["country"]
|
||||
);
|
||||
|
||||
$customerEvent->customer = $customer;
|
||||
$event->getDispatcher()->dispatch(TheliaEvents::AFTER_CHANGECUSTOMER, $customerEvent);
|
||||
|
||||
// Update the logged-in user, and redirect to the success URL (exits)
|
||||
// We don-t send the login event, as the customer si already logged.
|
||||
$this->processSuccessfulLogin($event, $customer, $customerModification);
|
||||
}
|
||||
catch(PropelException $e) {
|
||||
|
||||
Tlog::getInstance()->error(sprintf('error during modifying customer on action/modifyCustomer with message "%s"', $e->getMessage()));
|
||||
|
||||
$message = "Failed to change your account, please try again.";
|
||||
}
|
||||
}
|
||||
else {
|
||||
$message = "Missing or invalid data";
|
||||
}
|
||||
}
|
||||
else {
|
||||
$message = "Wrong form method !";
|
||||
}
|
||||
|
||||
// The form has an error
|
||||
$customerModification->setError(true);
|
||||
$customerModification->setErrorMessage($message);
|
||||
|
||||
// Dispatch the errored form
|
||||
$event->setErrorForm($customerModification);
|
||||
*/
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a category
|
||||
*
|
||||
* @param ActionEvent $event
|
||||
*/
|
||||
public function delete(ActionEvent $event)
|
||||
{
|
||||
$request = $event->getRequest();
|
||||
|
||||
$categoryDeletionForm = new CategoryDeletionForm($request);
|
||||
|
||||
$form = $categoryDeletionForm->getForm();
|
||||
|
||||
if ($request->isMethod("post")) {
|
||||
|
||||
$form->bind($request);
|
||||
|
||||
if ($form->isValid()) {
|
||||
|
||||
$data = $form->getData();
|
||||
|
||||
try {
|
||||
$category = CategoryQuery::create()->findPk($data['id']);
|
||||
|
||||
$categoryEvent = new CategoryEvent($category);
|
||||
|
||||
$event->getDispatcher()->dispatch(TheliaEvents::BEFORE_DELETECATEGORY, $categoryEvent);
|
||||
|
||||
$category->delete();
|
||||
|
||||
AdminLog::append(sprintf("Category %s (ID %s) deleted", $category->getTitle(), $category->getId()), $request, $request->getSession()->getAdminUser());
|
||||
|
||||
$categoryEvent->category = $category;
|
||||
|
||||
$event->getDispatcher()->dispatch(TheliaEvents::AFTER_DELETECATEGORY, $categoryEvent);
|
||||
|
||||
// Substitute _ID_ in the URL with the ID of the created category
|
||||
$successUrl = str_replace('_ID_', $category->getParent(), $categoryDeletionForm->getSuccessUrl());
|
||||
|
||||
// Redirect to the success URL
|
||||
Redirect::exec($successUrl);
|
||||
}
|
||||
catch(PropelException $e) {
|
||||
|
||||
Tlog::getInstance()->error(sprintf('error during deleting category ID=%s on action/modifyCustomer with message "%s"', $data['id'], $e->getMessage()));
|
||||
|
||||
$message = "Failed to change your account, please try again.";
|
||||
}
|
||||
}
|
||||
else {
|
||||
$message = "Missing or invalid data";
|
||||
}
|
||||
}
|
||||
else {
|
||||
$message = "Wrong form method !";
|
||||
}
|
||||
|
||||
// The form has an error
|
||||
$categoryDeletionForm->setError(true);
|
||||
$categoryDeletionForm->setErrorMessage($message);
|
||||
|
||||
// Store the form in the parser context
|
||||
$event->setErrorForm($categoryDeletionForm);
|
||||
|
||||
// Stop event propagation
|
||||
$event->stopPropagation();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of event names this subscriber listens to.
|
||||
*
|
||||
* The array keys are event names and the value can be:
|
||||
*
|
||||
* * The method name to call (priority defaults to 0)
|
||||
* * An array composed of the method name to call and the priority
|
||||
* * An array of arrays composed of the method names to call and respective
|
||||
* priorities, or 0 if unset
|
||||
*
|
||||
* For instance:
|
||||
*
|
||||
* * array('eventName' => 'methodName')
|
||||
* * array('eventName' => array('methodName', $priority))
|
||||
* * array('eventName' => array(array('methodName1', $priority), array('methodName2'))
|
||||
*
|
||||
* @return array The event names to listen to
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
"action.createCategory" => array("create", 128),
|
||||
"action.modifyCategory" => array("modify", 128),
|
||||
"action.deleteCategory" => array("delete", 128),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -38,6 +38,9 @@ use Thelia\Tools\URL;
|
||||
use Thelia\Tools\Redirect;
|
||||
use Thelia\Core\Template\ParserContext;
|
||||
use Thelia\Core\Event\ActionEvent;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcher;
|
||||
use Thelia\Core\Factory\ActionEventFactory;
|
||||
use Thelia\Core\Security\Exception\AuthorizationException;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -70,6 +73,19 @@ class BaseAdminController extends ContainerAware
|
||||
return new Response($this->renderRaw(self::TEMPLATE_404), 404);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check current admin user authorisations. An ADMIN role is assumed.
|
||||
*
|
||||
* @param unknown $permissions a single permission or an array of permissions.
|
||||
*
|
||||
* @throws AuthenticationException if permissions are not granted ti the current user.
|
||||
*/
|
||||
protected function checkAuth($permissions) {
|
||||
|
||||
if (! $this->getSecurityContext()->isGranted(array("ADMIN"), is_array($permissions) ? $permissions : array($permissions))) {
|
||||
throw new AuthorizationException("Sorry, you're not allowed to perform this action");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the givent template, and returns the result as an Http Response.
|
||||
@@ -118,12 +134,43 @@ class BaseAdminController extends ContainerAware
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an action event,
|
||||
*
|
||||
* @return EventDispatcher
|
||||
*/
|
||||
protected function dispatchEvent($action)
|
||||
{
|
||||
// Create the
|
||||
$eventFactory = new ActionEventFactory($this->getRequest(), $action, $this->container->getParameter("thelia.actionEvent"));
|
||||
|
||||
$actionEvent = $eventFactory->createActionEvent();
|
||||
|
||||
$this->getDispatcher()->dispatch("action.$action", $actionEvent);
|
||||
|
||||
if ($actionEvent->hasErrorForm()) {
|
||||
$this->getParserContext()->setErrorForm($actionEvent->getErrorForm());
|
||||
}
|
||||
|
||||
return $actionEvent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the event dispatcher,
|
||||
*
|
||||
* @return EventDispatcherInterface
|
||||
*/
|
||||
protected function getDispatcher()
|
||||
{
|
||||
return $this->container->get('event_dispatcher');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the parser context,
|
||||
*
|
||||
* @return ParserContext
|
||||
*/
|
||||
protected function getParserContext($context = false)
|
||||
protected function getParserContext()
|
||||
{
|
||||
return $this->container->get('thelia.parser.context');
|
||||
}
|
||||
@@ -150,17 +197,6 @@ 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
|
||||
*
|
||||
|
||||
@@ -23,6 +23,8 @@
|
||||
|
||||
namespace Thelia\Admin\Controller;
|
||||
|
||||
use Thelia\Model\CategoryQuery;
|
||||
use Thelia\Core\Security\Exception\AuthenticationException;
|
||||
class CategoryController extends BaseAdminController {
|
||||
|
||||
public function indexAction()
|
||||
@@ -33,31 +35,83 @@ class CategoryController extends BaseAdminController {
|
||||
'current_category_id' => 0
|
||||
);
|
||||
|
||||
return $this->browseCategory($args);
|
||||
}
|
||||
|
||||
public function createNewCategory($args) {
|
||||
|
||||
$this->checkAuth("ADMIN", "admin.category.create");
|
||||
|
||||
$this->dispatchEvent("createCategory");
|
||||
|
||||
// At this point, the form has error, and should be redisplayed.
|
||||
return $this->render('categories', $args);
|
||||
}
|
||||
|
||||
public function processAction($action)
|
||||
public function editCategory($args) {
|
||||
|
||||
$this->checkAuth("AMIN", "admin.category.edit");
|
||||
|
||||
return $this->render('edit_category', $args);
|
||||
}
|
||||
|
||||
public function deleteCategory($category_id) {
|
||||
|
||||
$this->checkAuth("AMIN", "admin.category.delete");
|
||||
|
||||
$category = CategoryQuery::create()->findPk($category_id);
|
||||
|
||||
$this->dispatchEvent("deleteCategory");
|
||||
|
||||
// Something was wrong, category was not deleted. Display parent category list
|
||||
return $this->render(
|
||||
'categories',
|
||||
array('current_category_id' => $category->getParent())
|
||||
);
|
||||
}
|
||||
|
||||
public function browseCategory($args) {
|
||||
|
||||
$this->checkAuth("AMIN", "admin.catalog.view");
|
||||
|
||||
return $this->render('categories', $args);
|
||||
}
|
||||
|
||||
public function processAction()
|
||||
{
|
||||
list($action, $id) = explode('/', $action);
|
||||
// Get the current action
|
||||
$action = $this->getRequest()->get('action', 'browse');
|
||||
|
||||
// Get the category ID
|
||||
$id = $this->getRequest()->get('id', 0);
|
||||
|
||||
$args = array(
|
||||
'action' => $action,
|
||||
'current_category_id' => $id
|
||||
);
|
||||
|
||||
// Browe categories
|
||||
if ($action == 'browse') {
|
||||
return $this->render('categories', $args);
|
||||
try {
|
||||
// Browse categories
|
||||
if ($action == 'browse') {
|
||||
return $this->browseCategory($args);
|
||||
}
|
||||
// Create a new category
|
||||
else if ($action == 'create') {
|
||||
return $this->createNewCategory($args);
|
||||
}
|
||||
// Edit an existing category
|
||||
else if ($action == 'edit') {
|
||||
return $this->editCategory($args);
|
||||
}
|
||||
// Delete an existing category
|
||||
else if ($action == 'delete') {
|
||||
return $this->deleteCategory($id);
|
||||
}
|
||||
}
|
||||
// Create a new category
|
||||
else if ($action = 'create') {
|
||||
return $this->render('edit_category', $args);
|
||||
catch(AuthenticationException $ex) {
|
||||
return $this->render('general_error', array(
|
||||
"error_message" => $ex->getMessage())
|
||||
);
|
||||
}
|
||||
// Edit an existing category
|
||||
else if ($action = 'edit') {
|
||||
return $this->render('edit_category', $args);
|
||||
}
|
||||
|
||||
//return $this->render("categories");
|
||||
}
|
||||
}
|
||||
@@ -65,7 +65,7 @@ class SessionController extends BaseAdminController {
|
||||
$this->getSecurityContext()->setUser($user);
|
||||
|
||||
// Log authentication success
|
||||
AdminLog::append("Authentication successuful", $request, $user);
|
||||
AdminLog::append("Authentication successful", $request, $user);
|
||||
|
||||
$this->dispatch(TheliaEvents::ADMIN_LOGIN);
|
||||
|
||||
|
||||
@@ -22,6 +22,10 @@
|
||||
<argument type="service" id="thelia.securityContext"/>
|
||||
</service>
|
||||
|
||||
<service id="thelia.action.category" class="Thelia\Action\Category">
|
||||
<tag name="kernel.event_subscriber"/>
|
||||
</service>
|
||||
|
||||
</services>
|
||||
|
||||
</config>
|
||||
@@ -22,13 +22,21 @@
|
||||
<loop class="Thelia\Core\Template\Loop\Product" name="product"/>
|
||||
<loop class="Thelia\Core\Template\Loop\Feed" name="feed"/>
|
||||
<loop class="Thelia\Core\Template\Loop\Title" name="title"/>
|
||||
<loop class="Thelia\Core\Template\Loop\Lang" name="lang"/>
|
||||
<loop class="Thelia\Core\Template\Loop\CategoryTree" name="category-tree"/>
|
||||
|
||||
</loops>
|
||||
|
||||
<forms>
|
||||
<form name="thelia.customer.creation" class="Thelia\Form\CustomerCreation"/>
|
||||
<form name="thelia.customer.modification" class="Thelia\Form\CustomerModification"/>
|
||||
|
||||
<form name="thelia.customer.login" class="Thelia\Form\CustomerLogin"/>
|
||||
<form name="thelia.admin.login" class="Thelia\Form\AdminLogin"/>
|
||||
|
||||
<form name="thelia.admin.category.creation" class="Thelia\Form\CategoryCreationForm"/>
|
||||
<form name="thelia.admin.category.deletion" class="Thelia\Form\CategoryDeletionForm"/>
|
||||
|
||||
</forms>
|
||||
|
||||
|
||||
@@ -84,7 +92,7 @@
|
||||
<argument type="service" id="request" />
|
||||
<argument type="service" id="event_dispatcher"/>
|
||||
<argument type="service" id="thelia.parser.context"/>
|
||||
<argument >true</argument> <!-- debug mode -->
|
||||
<argument >false</argument> <!-- Template name, or false -->
|
||||
<argument >%kernel.environment%</argument>
|
||||
<argument >%kernel.debug%</argument>
|
||||
</service>
|
||||
|
||||
@@ -31,9 +31,8 @@
|
||||
<default key="_controller">Thelia\Admin\Controller\CategoryController::indexAction</default>
|
||||
</route>
|
||||
|
||||
<route id="admin.category" path="/admin/catalog/category/{action}">
|
||||
<route id="admin.category" path="/admin/catalog/category">
|
||||
<default key="_controller">Thelia\Admin\Controller\CategoryController::processAction</default>
|
||||
<requirement key="action">.*</requirement>
|
||||
</route>
|
||||
|
||||
<!-- The default route, to display a template -->
|
||||
|
||||
@@ -59,7 +59,7 @@ abstract class ActionEvent extends Event
|
||||
public function __construct(Request $request, $action)
|
||||
{
|
||||
$this->request = $request;
|
||||
$this->action = $action;
|
||||
$this->action = $action;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
37
core/lib/Thelia/Core/Event/CategoryEvent.php
Normal file
37
core/lib/Thelia/Core/Event/CategoryEvent.php
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
/*************************************************************************************/
|
||||
/* */
|
||||
/* Thelia */
|
||||
/* */
|
||||
/* Copyright (c) OpenStudio */
|
||||
/* email : info@thelia.net */
|
||||
/* web : http://www.thelia.net */
|
||||
/* */
|
||||
/* This program is free software; you can redistribute it and/or modify */
|
||||
/* it under the terms of the GNU General Public License as published by */
|
||||
/* the Free Software Foundation; either version 3 of the License */
|
||||
/* */
|
||||
/* This program is distributed in the hope that it will be useful, */
|
||||
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
|
||||
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
|
||||
/* GNU General Public License for more details. */
|
||||
/* */
|
||||
/* You should have received a copy of the GNU General Public License */
|
||||
/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
/* */
|
||||
/*************************************************************************************/
|
||||
|
||||
namespace Thelia\Core\Event;
|
||||
|
||||
|
||||
use Thelia\Model\Category;
|
||||
|
||||
class CategoryEvent extends InternalEvent {
|
||||
|
||||
public $category;
|
||||
|
||||
public function __construct(Category $category)
|
||||
{
|
||||
$this->category = $category;
|
||||
}
|
||||
}
|
||||
@@ -88,4 +88,25 @@ final class TheliaEvents
|
||||
* Sent before customer insertion, to allow modules to create a custom customer reference.
|
||||
*/
|
||||
const CREATECUSTOMER_CUSTOMREF = "customer.creation.customref";
|
||||
|
||||
|
||||
/**
|
||||
* Sent once the category creation form has been successfully validated, and before category insertion in the database.
|
||||
*/
|
||||
const BEFORE_CREATECATEGORY = "action.before_createcategory";
|
||||
|
||||
/**
|
||||
* Sent just after a successful insert of a new category in the database.
|
||||
*/
|
||||
const AFTER_CREATECATEGORY = "action.after_createcategory";
|
||||
/**
|
||||
* Sent befonre deleting a category
|
||||
*/
|
||||
const BEFORE_DELETECATEGORY = "action.before_deletecategory";
|
||||
|
||||
/**
|
||||
* Sent just after a successful delete of a category from the database.
|
||||
*/
|
||||
const AFTER_DELETECATEGORY = "action.after_deletecategory";
|
||||
|
||||
}
|
||||
@@ -60,7 +60,6 @@ class ActionEventFactory
|
||||
{
|
||||
if (array_key_exists($this->action, $this->className)) {
|
||||
$class = new \ReflectionClass($this->className[$this->action]);
|
||||
|
||||
// return $class->newInstance($this->request, $this->action);
|
||||
} else {
|
||||
$class = new \ReflectionClass($this->defaultClassName);
|
||||
|
||||
@@ -78,26 +78,6 @@ class Session extends BaseSession
|
||||
return $this->remove('admin_user');
|
||||
}
|
||||
|
||||
// -- Error form -----------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @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');
|
||||
}
|
||||
|
||||
// -- Return page ----------------------------------------------------------
|
||||
|
||||
public function setReturnToUrl($url)
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
/*************************************************************************************/
|
||||
/* */
|
||||
/* Thelia */
|
||||
/* */
|
||||
/* Copyright (c) OpenStudio */
|
||||
/* email : info@thelia.net */
|
||||
/* web : http://www.thelia.net */
|
||||
/* */
|
||||
/* This program is free software; you can redistribute it and/or modify */
|
||||
/* it under the terms of the GNU General Public License as published by */
|
||||
/* the Free Software Foundation; either version 3 of the License */
|
||||
/* */
|
||||
/* This program is distributed in the hope that it will be useful, */
|
||||
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
|
||||
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
|
||||
/* GNU General Public License for more details. */
|
||||
/* */
|
||||
/* You should have received a copy of the GNU General Public License */
|
||||
/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
/* */
|
||||
/*************************************************************************************/
|
||||
|
||||
namespace Thelia\Core\Security\Exception;
|
||||
|
||||
class AuthorizationException extends \Exception
|
||||
{
|
||||
}
|
||||
@@ -107,7 +107,7 @@ class SecurityContext {
|
||||
*
|
||||
* @return Boolean
|
||||
*/
|
||||
final public function isGranted($roles, $permissions)
|
||||
final public function isGranted(array $roles, array $permissions)
|
||||
{
|
||||
if ($this->isAuthenticated() === true) {
|
||||
|
||||
|
||||
@@ -80,7 +80,7 @@ abstract class BaseLoop
|
||||
return array(
|
||||
Argument::createIntTypeArgument('offset', 0),
|
||||
Argument::createIntTypeArgument('page'),
|
||||
Argument::createIntTypeArgument('limit', 10),
|
||||
Argument::createIntTypeArgument('limit', PHP_INT_MAX),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -111,6 +111,19 @@ class Argument
|
||||
);
|
||||
}
|
||||
|
||||
public static function createBooleanOrBothTypeArgument($name, $default=null, $mandatory=false, $empty=true)
|
||||
{
|
||||
return new Argument(
|
||||
$name,
|
||||
new TypeCollection(
|
||||
new Type\BooleanOrBothType()
|
||||
),
|
||||
$default,
|
||||
$mandatory,
|
||||
$empty
|
||||
);
|
||||
}
|
||||
|
||||
public static function createIntListTypeArgument($name, $default=null, $mandatory=false, $empty=true)
|
||||
{
|
||||
return new Argument(
|
||||
|
||||
@@ -36,6 +36,7 @@ use Thelia\Model\CategoryQuery;
|
||||
use Thelia\Model\ConfigQuery;
|
||||
use Thelia\Type\TypeCollection;
|
||||
use Thelia\Type;
|
||||
use Thelia\Type\BooleanOrBothType;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -122,7 +123,7 @@ class Category extends BaseLoop
|
||||
$search->filterById($exclude, Criteria::NOT_IN);
|
||||
}
|
||||
|
||||
if ($this->getVisible() != '*')
|
||||
if ($this->getVisible() != BooleanOrBothType::ANY)
|
||||
$search->filterByVisible($this->getVisible() ? 1 : 0);
|
||||
|
||||
$orders = $this->getOrder();
|
||||
|
||||
@@ -36,6 +36,7 @@ use Thelia\Model\CategoryQuery;
|
||||
use Thelia\Model\ConfigQuery;
|
||||
use Thelia\Type\TypeCollection;
|
||||
use Thelia\Type;
|
||||
use Thelia\Type\BooleanOrBothType;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -68,7 +69,7 @@ class CategoryPath extends BaseLoop
|
||||
Argument::createIntTypeArgument('category', null, true),
|
||||
Argument::createIntTypeArgument('depth'),
|
||||
Argument::createIntTypeArgument('level'),
|
||||
Argument::createBooleanTypeArgument('visible', true, false)
|
||||
Argument::createBooleanOrBothTypeArgument('visible', true, false)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -84,10 +85,12 @@ class CategoryPath extends BaseLoop
|
||||
|
||||
$search = CategoryQuery::create();
|
||||
$search->filterById($id);
|
||||
if ($visible == true) $search->filterByVisible($visible);
|
||||
if ($visible != BooleanOrBothType::ANY) $search->filterByVisible($visible);
|
||||
|
||||
$results = array();
|
||||
|
||||
$ids = array();
|
||||
|
||||
do {
|
||||
$category = $search->findOne();
|
||||
|
||||
@@ -106,6 +109,14 @@ class CategoryPath extends BaseLoop
|
||||
$parent = $category->getParent();
|
||||
|
||||
if ($parent > 0) {
|
||||
|
||||
// Prevent circular refererences
|
||||
if (in_array($parent, $ids)) {
|
||||
throw new \LogicException(sprintf("Circular reference detected in category ID=%d hierarchy (category ID=%d appears more than one times in path)", $id, $parent));
|
||||
}
|
||||
|
||||
$ids[] = $parent;
|
||||
|
||||
$search = CategoryQuery::create();
|
||||
$search->filterById($parent);
|
||||
if ($visible == true) $search->filterByVisible($visible);
|
||||
|
||||
123
core/lib/Thelia/Core/Template/Loop/CategoryTree.php
Normal file
123
core/lib/Thelia/Core/Template/Loop/CategoryTree.php
Normal file
@@ -0,0 +1,123 @@
|
||||
<?php
|
||||
/*************************************************************************************/
|
||||
/* */
|
||||
/* Thelia */
|
||||
/* */
|
||||
/* Copyright (c) OpenStudio */
|
||||
/* email : info@thelia.net */
|
||||
/* web : http://www.thelia.net */
|
||||
/* */
|
||||
/* This program is free software; you can redistribute it and/or modify */
|
||||
/* it under the terms of the GNU General Public License as published by */
|
||||
/* the Free Software Foundation; either version 3 of the License */
|
||||
/* */
|
||||
/* This program is distributed in the hope that it will be useful, */
|
||||
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
|
||||
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
|
||||
/* GNU General Public License for more details. */
|
||||
/* */
|
||||
/* You should have received a copy of the GNU General Public License */
|
||||
/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
/* */
|
||||
/*************************************************************************************/
|
||||
|
||||
namespace Thelia\Core\Template\Loop;
|
||||
|
||||
use Propel\Runtime\ActiveQuery\Criteria;
|
||||
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\Log\Tlog;
|
||||
|
||||
use Thelia\Model\CategoryQuery;
|
||||
use Thelia\Model\ConfigQuery;
|
||||
use Thelia\Type\TypeCollection;
|
||||
use Thelia\Type;
|
||||
use Thelia\Type\BooleanOrBothType;
|
||||
|
||||
/**
|
||||
*
|
||||
* Category tree loop, to get a category tree from a given category to a given depth.
|
||||
*
|
||||
* - category is the category id
|
||||
* - depth is the maximum depth to go, default unlimited
|
||||
* - visible if true or missing, only visible categories will be displayed. If false, all categories (visible or not) are returned.
|
||||
*
|
||||
* @package Thelia\Core\Template\Loop
|
||||
* @author Franck Allimant <franck@cqfdev.fr>
|
||||
*/
|
||||
class CategoryTree extends BaseLoop
|
||||
{
|
||||
/**
|
||||
* @return ArgumentCollection
|
||||
*/
|
||||
protected function getArgDefinitions()
|
||||
{
|
||||
return new ArgumentCollection(
|
||||
Argument::createIntTypeArgument('category', null, true),
|
||||
Argument::createIntTypeArgument('depth', PHP_INT_MAX),
|
||||
Argument::createBooleanOrBothTypeArgument('visible', true, false),
|
||||
Argument::createIntListTypeArgument('exclude', array())
|
||||
);
|
||||
}
|
||||
|
||||
// changement de rubrique
|
||||
protected function buildCategoryTree($parent, $visible, $level, $max_level, array $exclude, LoopResult &$loopResult) {
|
||||
|
||||
if ($level > $max_level) return;
|
||||
|
||||
$search = CategoryQuery::create();
|
||||
|
||||
$search->filterByParent($parent);
|
||||
|
||||
if ($visible != BooleanOrBothType::ANY) $search->filterByVisible($visible);
|
||||
|
||||
$search->filterById($exclude, Criteria::NOT_IN);
|
||||
|
||||
$search->orderByPosition(Criteria::ASC);
|
||||
|
||||
$results = $search->find();
|
||||
|
||||
foreach($results as $result) {
|
||||
|
||||
$loopResultRow = new LoopResultRow();
|
||||
|
||||
$loopResultRow
|
||||
->set("ID", $result->getId())
|
||||
->set("TITLE",$result->getTitle())
|
||||
->set("PARENT", $result->getParent())
|
||||
->set("URL", $result->getUrl())
|
||||
->set("VISIBLE", $result->getVisible() ? "1" : "0")
|
||||
->set("LEVEL", $level)
|
||||
;
|
||||
|
||||
$loopResult->addRow($loopResultRow);
|
||||
|
||||
$this->buildCategoryTree($result->getId(), $visible, 1 + $level, $max_level, $exclude, $loopResult);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $pagination (ignored)
|
||||
*
|
||||
* @return \Thelia\Core\Template\Element\LoopResult
|
||||
*/
|
||||
public function exec(&$pagination)
|
||||
{
|
||||
$id = $this->getCategory();
|
||||
$depth = $this->getDepth();
|
||||
$visible = $this->getVisible();
|
||||
$exclude = $this->getExclude();
|
||||
|
||||
//echo "exclude=".print_r($exclude);
|
||||
|
||||
$loopResult = new LoopResult();
|
||||
|
||||
$this->buildCategoryTree($id, $visible, 0, $depth, $exclude, $loopResult);
|
||||
|
||||
return $loopResult;
|
||||
}
|
||||
}
|
||||
@@ -41,6 +41,7 @@ use Thelia\Model\ContentQuery;
|
||||
use Thelia\Model\ConfigQuery;
|
||||
use Thelia\Type\TypeCollection;
|
||||
use Thelia\Type;
|
||||
use Thelia\Type\BooleanOrBothType;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -64,7 +65,7 @@ class Content extends BaseLoop
|
||||
Argument::createBooleanTypeArgument('current'),
|
||||
Argument::createBooleanTypeArgument('current_folder'),
|
||||
Argument::createIntTypeArgument('depth', 1),
|
||||
Argument::createBooleanTypeArgument('visible', 1),
|
||||
Argument::createBooleanOrBothTypeArgument('visible', 1),
|
||||
new Argument(
|
||||
'order',
|
||||
new TypeCollection(
|
||||
@@ -148,7 +149,7 @@ class Content extends BaseLoop
|
||||
|
||||
$visible = $this->getVisible();
|
||||
|
||||
$search->filterByVisible($visible);
|
||||
if ($visible != BooleanOrBothType::ANY) $search->filterByVisible($visible);
|
||||
|
||||
$orders = $this->getOrder();
|
||||
|
||||
|
||||
@@ -40,6 +40,7 @@ use Thelia\Model\ConfigQuery;
|
||||
use Thelia\Model\Map\ProductCategoryTableMap;
|
||||
use Thelia\Type\TypeCollection;
|
||||
use Thelia\Type;
|
||||
use Thelia\Type\BooleanOrBothType;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -61,7 +62,7 @@ class Feature extends BaseLoop
|
||||
Argument::createIntListTypeArgument('id'),
|
||||
Argument::createIntListTypeArgument('product'),
|
||||
Argument::createIntListTypeArgument('category'),
|
||||
Argument::createBooleanTypeArgument('visible', 1),
|
||||
Argument::createBooleanOrBothTypeArgument('visible', 1),
|
||||
Argument::createIntListTypeArgument('exclude'),
|
||||
new Argument(
|
||||
'order',
|
||||
@@ -96,7 +97,7 @@ class Feature extends BaseLoop
|
||||
|
||||
$visible = $this->getVisible();
|
||||
|
||||
$search->filterByVisible($visible);
|
||||
if ($visible != BooleanOrBothType::ANY) $search->filterByVisible($visible);
|
||||
|
||||
$product = $this->getProduct();
|
||||
$category = $this->getCategory();
|
||||
|
||||
@@ -36,6 +36,7 @@ use Thelia\Model\FolderQuery;
|
||||
use Thelia\Model\ConfigQuery;
|
||||
use Thelia\Type\TypeCollection;
|
||||
use Thelia\Type;
|
||||
use Thelia\Type\BooleanOrBothType;
|
||||
|
||||
/**
|
||||
* Class Folder
|
||||
@@ -55,7 +56,7 @@ class Folder extends BaseLoop
|
||||
Argument::createIntTypeArgument('parent'),
|
||||
Argument::createBooleanTypeArgument('current'),
|
||||
Argument::createBooleanTypeArgument('not_empty', 0),
|
||||
Argument::createBooleanTypeArgument('visible', 1),
|
||||
Argument::createBooleanOrBothTypeArgument('visible', 1),
|
||||
new Argument(
|
||||
'order',
|
||||
new TypeCollection(
|
||||
@@ -104,7 +105,9 @@ class Folder extends BaseLoop
|
||||
$search->filterById($exclude, Criteria::NOT_IN);
|
||||
}
|
||||
|
||||
$search->filterByVisible($this->getVisible() ? 1 : 0);
|
||||
$visible = $this->getVisible();
|
||||
|
||||
if ($visible != BooleanOrBothType::ANY) $search->filterByVisible($visible ? 1 : 0);
|
||||
|
||||
$orders = $this->getOrder();
|
||||
|
||||
|
||||
114
core/lib/Thelia/Core/Template/Loop/Lang.php
Normal file
114
core/lib/Thelia/Core/Template/Loop/Lang.php
Normal file
@@ -0,0 +1,114 @@
|
||||
<?php
|
||||
/*************************************************************************************/
|
||||
/* */
|
||||
/* Thelia */
|
||||
/* */
|
||||
/* Copyright (c) OpenStudio */
|
||||
/* email : info@thelia.net */
|
||||
/* web : http://www.thelia.net */
|
||||
/* */
|
||||
/* This program is free software; you can redistribute it and/or modify */
|
||||
/* it under the terms of the GNU General Public License as published by */
|
||||
/* the Free Software Foundation; either version 3 of the License */
|
||||
/* */
|
||||
/* This program is distributed in the hope that it will be useful, */
|
||||
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
|
||||
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
|
||||
/* GNU General Public License for more details. */
|
||||
/* */
|
||||
/* You should have received a copy of the GNU General Public License */
|
||||
/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
/* */
|
||||
/*************************************************************************************/
|
||||
|
||||
namespace Thelia\Core\Template\Loop;
|
||||
|
||||
use Propel\Runtime\ActiveQuery\Criteria;
|
||||
use Thelia\Core\Template\Element\BaseLoop;
|
||||
use Thelia\Core\Template\Element\LoopResult;
|
||||
use Thelia\Core\Template\Element\LoopResultRow;
|
||||
|
||||
use Thelia\Core\Template\Loop\Argument\Argument;
|
||||
|
||||
use Thelia\Type\TypeCollection;
|
||||
use Thelia\Type;
|
||||
use Thelia\Model\LangQuery;
|
||||
use Thelia\Core\Template\Loop\Argument\ArgumentCollection;
|
||||
|
||||
/**
|
||||
* Language loop, to get a list of available languages
|
||||
*
|
||||
* - id is the language id
|
||||
* - exclude is a comma separated list of lang IDs that will be excluded from output
|
||||
* - default if 1, the loop return only default lang. If 0, return all but the default language
|
||||
*
|
||||
* @package Thelia\Core\Template\Loop
|
||||
* @author Franck Allimant <franck@cqfdev.fr>
|
||||
*/
|
||||
class Lang extends BaseLoop
|
||||
{
|
||||
/**
|
||||
* @return ArgumentCollection
|
||||
*/
|
||||
protected function getArgDefinitions()
|
||||
{
|
||||
return new ArgumentCollection(
|
||||
Argument::createIntTypeArgument('id', null),
|
||||
Argument::createIntListTypeArgument('exclude'),
|
||||
Argument::createBooleanTypeArgument('default_only', false)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $pagination (ignored)
|
||||
*
|
||||
* @return \Thelia\Core\Template\Element\LoopResult
|
||||
*/
|
||||
public function exec(&$pagination)
|
||||
{
|
||||
$id = $this->getId();
|
||||
$exclude = $this->getExclude();
|
||||
$default_only = $this->getDefaultOnly();
|
||||
|
||||
$search = LangQuery::create();
|
||||
|
||||
if (! is_null($id))
|
||||
$search->filterById($id);
|
||||
|
||||
if ($default_only)
|
||||
$search->filterByByDefault(true);
|
||||
|
||||
if (! is_null($exclude)) {
|
||||
$search->filterById($exclude, Criteria::NOT_IN);
|
||||
}
|
||||
|
||||
$search->orderByPosition(Criteria::ASC);
|
||||
|
||||
$results = $this->search($search, $pagination);
|
||||
|
||||
$loopResult = new LoopResult();
|
||||
|
||||
foreach ($results as $result) {
|
||||
|
||||
$loopResultRow = new LoopResultRow();
|
||||
|
||||
$loopResultRow
|
||||
->set("ID", $result->getId())
|
||||
->set("TITLE",$result->getTitle())
|
||||
->set("CODE", $result->getCode())
|
||||
->set("LOCALE", $result->getLocale())
|
||||
->set("URL", $result->getUrl())
|
||||
->set("IS_DEFAULT", $result->getByDefault())
|
||||
->set("URL", $result->getUrl())
|
||||
->set("POSITION", $result->getPosition())
|
||||
|
||||
->set("CREATE_DATE", $result->getCreatedAt())
|
||||
->set("UPDATE_DATE", $result->getUpdatedAt())
|
||||
;
|
||||
|
||||
$loopResult->addRow($loopResultRow);
|
||||
}
|
||||
|
||||
return $loopResult;
|
||||
}
|
||||
}
|
||||
@@ -44,6 +44,7 @@ use Thelia\Model\ProductQuery;
|
||||
use Thelia\Model\ConfigQuery;
|
||||
use Thelia\Type\TypeCollection;
|
||||
use Thelia\Type;
|
||||
use Thelia\Type\BooleanOrBothType;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -80,7 +81,7 @@ class Product extends BaseLoop
|
||||
Argument::createBooleanTypeArgument('current'),
|
||||
Argument::createBooleanTypeArgument('current_category'),
|
||||
Argument::createIntTypeArgument('depth', 1),
|
||||
Argument::createBooleanTypeArgument('visible', 1),
|
||||
Argument::createBooleanOrBothTypeArgument('visible', 1),
|
||||
new Argument(
|
||||
'order',
|
||||
new TypeCollection(
|
||||
@@ -252,11 +253,10 @@ class Product extends BaseLoop
|
||||
|
||||
$visible = $this->getVisible();
|
||||
|
||||
$search->filterByVisible($visible);
|
||||
if ($visible != BooleanOrBothType::ANY) $search->filterByVisible($visible ? 1 : 0);
|
||||
|
||||
$orders = $this->getOrder();
|
||||
|
||||
|
||||
foreach($orders as $order) {
|
||||
switch ($order) {
|
||||
case "alpha":
|
||||
|
||||
@@ -26,6 +26,7 @@ namespace Thelia\Core\Template\Smarty\Plugins;
|
||||
use Thelia\Core\Template\Smarty\SmartyPluginDescriptor;
|
||||
use Thelia\Core\Template\Smarty\AbstractSmartyPlugin;
|
||||
use Thelia\Core\Template\Smarty\Assets\SmartyAssetsManager;
|
||||
use Thelia\Model\ConfigQuery;
|
||||
|
||||
class Assetic extends AbstractSmartyPlugin
|
||||
{
|
||||
@@ -35,7 +36,7 @@ class Assetic extends AbstractSmartyPlugin
|
||||
{
|
||||
$web_root = THELIA_WEB_DIR;
|
||||
|
||||
$asset_dir_from_web_root = 'assets/admin/default'; // FIXME
|
||||
$asset_dir_from_web_root = ConfigQuery::read('asset_dir_from_web_root', 'assets');
|
||||
|
||||
$this->assetManager = new SmartyAssetsManager($web_root, $asset_dir_from_web_root);
|
||||
}
|
||||
|
||||
@@ -101,7 +101,8 @@ class Form extends AbstractSmartyPlugin
|
||||
// Re-use the errored form
|
||||
$instance = $errorForm;
|
||||
|
||||
$this->parserContext->clearErrorForm();
|
||||
// Don't do that, as we may want to use this form firther in the template code
|
||||
//$this->parserContext->clearErrorForm();
|
||||
}
|
||||
|
||||
$instance->createView();
|
||||
|
||||
@@ -49,7 +49,7 @@ class UrlGenerator extends AbstractSmartyPlugin
|
||||
// the path to process
|
||||
$path = $this->getParam($params, 'path');
|
||||
|
||||
return URL::absoluteUrl($path, $this->getArgsFromParam($params));
|
||||
return URL::absoluteUrl($path, $this->getArgsFromParam($params, array('path')));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -84,7 +84,7 @@ class UrlGenerator extends AbstractSmartyPlugin
|
||||
// the related action (optionale)
|
||||
$action = $this->getParam($params, 'action');
|
||||
|
||||
$args = $this->getArgsFromParam($params);
|
||||
$args = $this->getArgsFromParam($params, array('view', 'action'));
|
||||
|
||||
if (! empty($action)) $args['action'] = $action;
|
||||
|
||||
@@ -92,17 +92,23 @@ class UrlGenerator extends AbstractSmartyPlugin
|
||||
}
|
||||
|
||||
/**
|
||||
* Get URL parameters array from a comma separated list or arguments in the
|
||||
* parameters.
|
||||
* Get URL parameters array from parameters.
|
||||
*
|
||||
* @param array $params Smarty function params
|
||||
* @return array the parameters array (either emply, of valued)
|
||||
*/
|
||||
private function getArgsFromParam($params) {
|
||||
private function getArgsFromParam($params, $exclude = array()) {
|
||||
|
||||
$args = $this->getParam($params, array('arguments', 'args'));
|
||||
$pairs = array();
|
||||
|
||||
return $args !== null ? explode($args, ',') : array();
|
||||
foreach($params as $name => $value) {
|
||||
|
||||
if (in_array($name, $exclude)) continue;
|
||||
|
||||
$pairs[$name] = $value;
|
||||
}
|
||||
|
||||
return $pairs;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
55
core/lib/Thelia/Form/CategoryCreationForm.php
Normal file
55
core/lib/Thelia/Form/CategoryCreationForm.php
Normal file
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
/*************************************************************************************/
|
||||
/* */
|
||||
/* Thelia */
|
||||
/* */
|
||||
/* Copyright (c) OpenStudio */
|
||||
/* email : info@thelia.net */
|
||||
/* web : http://www.thelia.net */
|
||||
/* */
|
||||
/* This program is free software; you can redistribute it and/or modify */
|
||||
/* it under the terms of the GNU General Public License as published by */
|
||||
/* the Free Software Foundation; either version 3 of the License */
|
||||
/* */
|
||||
/* This program is distributed in the hope that it will be useful, */
|
||||
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
|
||||
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
|
||||
/* GNU General Public License for more details. */
|
||||
/* */
|
||||
/* You should have received a copy of the GNU General Public License */
|
||||
/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
/* */
|
||||
/*************************************************************************************/
|
||||
namespace Thelia\Form;
|
||||
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Validator\Constraints\NotBlank;
|
||||
|
||||
class CategoryCreationForm extends BaseForm {
|
||||
|
||||
protected function buildForm()
|
||||
{
|
||||
$this->formBuilder
|
||||
->add("title", "text", array(
|
||||
"constraints" => array(
|
||||
new NotBlank()
|
||||
)
|
||||
))
|
||||
->add("parent", "integer", array(
|
||||
"constraints" => array(
|
||||
new NotBlank()
|
||||
)
|
||||
))
|
||||
->add("locale", "text", array(
|
||||
"constraints" => array(
|
||||
new NotBlank()
|
||||
)
|
||||
))
|
||||
;
|
||||
}
|
||||
|
||||
public function getName()
|
||||
{
|
||||
return "thelia_category_creation";
|
||||
}
|
||||
}
|
||||
45
core/lib/Thelia/Form/CategoryDeletionForm.php
Normal file
45
core/lib/Thelia/Form/CategoryDeletionForm.php
Normal file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
/*************************************************************************************/
|
||||
/* */
|
||||
/* Thelia */
|
||||
/* */
|
||||
/* Copyright (c) OpenStudio */
|
||||
/* email : info@thelia.net */
|
||||
/* web : http://www.thelia.net */
|
||||
/* */
|
||||
/* This program is free software; you can redistribute it and/or modify */
|
||||
/* it under the terms of the GNU General Public License as published by */
|
||||
/* the Free Software Foundation; either version 3 of the License */
|
||||
/* */
|
||||
/* This program is distributed in the hope that it will be useful, */
|
||||
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
|
||||
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
|
||||
/* GNU General Public License for more details. */
|
||||
/* */
|
||||
/* You should have received a copy of the GNU General Public License */
|
||||
/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
/* */
|
||||
/*************************************************************************************/
|
||||
namespace Thelia\Form;
|
||||
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Validator\Constraints\NotBlank;
|
||||
|
||||
class CategoryDeletionForm extends BaseForm {
|
||||
|
||||
protected function buildForm()
|
||||
{
|
||||
$this->formBuilder
|
||||
->add("id", "integer", array(
|
||||
"constraints" => array(
|
||||
new NotBlank()
|
||||
)
|
||||
))
|
||||
;
|
||||
}
|
||||
|
||||
public function getName()
|
||||
{
|
||||
return "thelia_category_deletion";
|
||||
}
|
||||
}
|
||||
@@ -93,6 +93,12 @@ abstract class Currency implements ActiveRecordInterface
|
||||
*/
|
||||
protected $by_default;
|
||||
|
||||
/**
|
||||
* The value for the position field.
|
||||
* @var int
|
||||
*/
|
||||
protected $position;
|
||||
|
||||
/**
|
||||
* The value for the created_at field.
|
||||
* @var string
|
||||
@@ -484,6 +490,17 @@ abstract class Currency implements ActiveRecordInterface
|
||||
return $this->by_default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the [position] column value.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getPosition()
|
||||
{
|
||||
|
||||
return $this->position;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the [optionally formatted] temporal [created_at] column value.
|
||||
*
|
||||
@@ -629,6 +646,27 @@ abstract class Currency implements ActiveRecordInterface
|
||||
return $this;
|
||||
} // setByDefault()
|
||||
|
||||
/**
|
||||
* Set the value of [position] column.
|
||||
*
|
||||
* @param int $v new value
|
||||
* @return \Thelia\Model\Currency The current object (for fluent API support)
|
||||
*/
|
||||
public function setPosition($v)
|
||||
{
|
||||
if ($v !== null) {
|
||||
$v = (int) $v;
|
||||
}
|
||||
|
||||
if ($this->position !== $v) {
|
||||
$this->position = $v;
|
||||
$this->modifiedColumns[] = CurrencyTableMap::POSITION;
|
||||
}
|
||||
|
||||
|
||||
return $this;
|
||||
} // setPosition()
|
||||
|
||||
/**
|
||||
* Sets the value of [created_at] column to a normalized version of the date/time value specified.
|
||||
*
|
||||
@@ -723,13 +761,16 @@ abstract class Currency implements ActiveRecordInterface
|
||||
$col = $row[TableMap::TYPE_NUM == $indexType ? 4 + $startcol : CurrencyTableMap::translateFieldName('ByDefault', TableMap::TYPE_PHPNAME, $indexType)];
|
||||
$this->by_default = (null !== $col) ? (int) $col : null;
|
||||
|
||||
$col = $row[TableMap::TYPE_NUM == $indexType ? 5 + $startcol : CurrencyTableMap::translateFieldName('CreatedAt', TableMap::TYPE_PHPNAME, $indexType)];
|
||||
$col = $row[TableMap::TYPE_NUM == $indexType ? 5 + $startcol : CurrencyTableMap::translateFieldName('Position', TableMap::TYPE_PHPNAME, $indexType)];
|
||||
$this->position = (null !== $col) ? (int) $col : null;
|
||||
|
||||
$col = $row[TableMap::TYPE_NUM == $indexType ? 6 + $startcol : CurrencyTableMap::translateFieldName('CreatedAt', TableMap::TYPE_PHPNAME, $indexType)];
|
||||
if ($col === '0000-00-00 00:00:00') {
|
||||
$col = null;
|
||||
}
|
||||
$this->created_at = (null !== $col) ? PropelDateTime::newInstance($col, null, '\DateTime') : null;
|
||||
|
||||
$col = $row[TableMap::TYPE_NUM == $indexType ? 6 + $startcol : CurrencyTableMap::translateFieldName('UpdatedAt', TableMap::TYPE_PHPNAME, $indexType)];
|
||||
$col = $row[TableMap::TYPE_NUM == $indexType ? 7 + $startcol : CurrencyTableMap::translateFieldName('UpdatedAt', TableMap::TYPE_PHPNAME, $indexType)];
|
||||
if ($col === '0000-00-00 00:00:00') {
|
||||
$col = null;
|
||||
}
|
||||
@@ -742,7 +783,7 @@ abstract class Currency implements ActiveRecordInterface
|
||||
$this->ensureConsistency();
|
||||
}
|
||||
|
||||
return $startcol + 7; // 7 = CurrencyTableMap::NUM_HYDRATE_COLUMNS.
|
||||
return $startcol + 8; // 8 = CurrencyTableMap::NUM_HYDRATE_COLUMNS.
|
||||
|
||||
} catch (Exception $e) {
|
||||
throw new PropelException("Error populating \Thelia\Model\Currency object", 0, $e);
|
||||
@@ -1055,6 +1096,9 @@ abstract class Currency implements ActiveRecordInterface
|
||||
if ($this->isColumnModified(CurrencyTableMap::BY_DEFAULT)) {
|
||||
$modifiedColumns[':p' . $index++] = 'BY_DEFAULT';
|
||||
}
|
||||
if ($this->isColumnModified(CurrencyTableMap::POSITION)) {
|
||||
$modifiedColumns[':p' . $index++] = 'POSITION';
|
||||
}
|
||||
if ($this->isColumnModified(CurrencyTableMap::CREATED_AT)) {
|
||||
$modifiedColumns[':p' . $index++] = 'CREATED_AT';
|
||||
}
|
||||
@@ -1087,6 +1131,9 @@ abstract class Currency implements ActiveRecordInterface
|
||||
case 'BY_DEFAULT':
|
||||
$stmt->bindValue($identifier, $this->by_default, PDO::PARAM_INT);
|
||||
break;
|
||||
case 'POSITION':
|
||||
$stmt->bindValue($identifier, $this->position, PDO::PARAM_INT);
|
||||
break;
|
||||
case 'CREATED_AT':
|
||||
$stmt->bindValue($identifier, $this->created_at ? $this->created_at->format("Y-m-d H:i:s") : null, PDO::PARAM_STR);
|
||||
break;
|
||||
@@ -1171,9 +1218,12 @@ abstract class Currency implements ActiveRecordInterface
|
||||
return $this->getByDefault();
|
||||
break;
|
||||
case 5:
|
||||
return $this->getCreatedAt();
|
||||
return $this->getPosition();
|
||||
break;
|
||||
case 6:
|
||||
return $this->getCreatedAt();
|
||||
break;
|
||||
case 7:
|
||||
return $this->getUpdatedAt();
|
||||
break;
|
||||
default:
|
||||
@@ -1210,8 +1260,9 @@ abstract class Currency implements ActiveRecordInterface
|
||||
$keys[2] => $this->getSymbol(),
|
||||
$keys[3] => $this->getRate(),
|
||||
$keys[4] => $this->getByDefault(),
|
||||
$keys[5] => $this->getCreatedAt(),
|
||||
$keys[6] => $this->getUpdatedAt(),
|
||||
$keys[5] => $this->getPosition(),
|
||||
$keys[6] => $this->getCreatedAt(),
|
||||
$keys[7] => $this->getUpdatedAt(),
|
||||
);
|
||||
$virtualColumns = $this->virtualColumns;
|
||||
foreach($virtualColumns as $key => $virtualColumn)
|
||||
@@ -1282,9 +1333,12 @@ abstract class Currency implements ActiveRecordInterface
|
||||
$this->setByDefault($value);
|
||||
break;
|
||||
case 5:
|
||||
$this->setCreatedAt($value);
|
||||
$this->setPosition($value);
|
||||
break;
|
||||
case 6:
|
||||
$this->setCreatedAt($value);
|
||||
break;
|
||||
case 7:
|
||||
$this->setUpdatedAt($value);
|
||||
break;
|
||||
} // switch()
|
||||
@@ -1316,8 +1370,9 @@ abstract class Currency implements ActiveRecordInterface
|
||||
if (array_key_exists($keys[2], $arr)) $this->setSymbol($arr[$keys[2]]);
|
||||
if (array_key_exists($keys[3], $arr)) $this->setRate($arr[$keys[3]]);
|
||||
if (array_key_exists($keys[4], $arr)) $this->setByDefault($arr[$keys[4]]);
|
||||
if (array_key_exists($keys[5], $arr)) $this->setCreatedAt($arr[$keys[5]]);
|
||||
if (array_key_exists($keys[6], $arr)) $this->setUpdatedAt($arr[$keys[6]]);
|
||||
if (array_key_exists($keys[5], $arr)) $this->setPosition($arr[$keys[5]]);
|
||||
if (array_key_exists($keys[6], $arr)) $this->setCreatedAt($arr[$keys[6]]);
|
||||
if (array_key_exists($keys[7], $arr)) $this->setUpdatedAt($arr[$keys[7]]);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1334,6 +1389,7 @@ abstract class Currency implements ActiveRecordInterface
|
||||
if ($this->isColumnModified(CurrencyTableMap::SYMBOL)) $criteria->add(CurrencyTableMap::SYMBOL, $this->symbol);
|
||||
if ($this->isColumnModified(CurrencyTableMap::RATE)) $criteria->add(CurrencyTableMap::RATE, $this->rate);
|
||||
if ($this->isColumnModified(CurrencyTableMap::BY_DEFAULT)) $criteria->add(CurrencyTableMap::BY_DEFAULT, $this->by_default);
|
||||
if ($this->isColumnModified(CurrencyTableMap::POSITION)) $criteria->add(CurrencyTableMap::POSITION, $this->position);
|
||||
if ($this->isColumnModified(CurrencyTableMap::CREATED_AT)) $criteria->add(CurrencyTableMap::CREATED_AT, $this->created_at);
|
||||
if ($this->isColumnModified(CurrencyTableMap::UPDATED_AT)) $criteria->add(CurrencyTableMap::UPDATED_AT, $this->updated_at);
|
||||
|
||||
@@ -1403,6 +1459,7 @@ abstract class Currency implements ActiveRecordInterface
|
||||
$copyObj->setSymbol($this->getSymbol());
|
||||
$copyObj->setRate($this->getRate());
|
||||
$copyObj->setByDefault($this->getByDefault());
|
||||
$copyObj->setPosition($this->getPosition());
|
||||
$copyObj->setCreatedAt($this->getCreatedAt());
|
||||
$copyObj->setUpdatedAt($this->getUpdatedAt());
|
||||
|
||||
@@ -2579,6 +2636,7 @@ abstract class Currency implements ActiveRecordInterface
|
||||
$this->symbol = null;
|
||||
$this->rate = null;
|
||||
$this->by_default = null;
|
||||
$this->position = null;
|
||||
$this->created_at = null;
|
||||
$this->updated_at = null;
|
||||
$this->alreadyInSave = false;
|
||||
|
||||
@@ -27,6 +27,7 @@ use Thelia\Model\Map\CurrencyTableMap;
|
||||
* @method ChildCurrencyQuery orderBySymbol($order = Criteria::ASC) Order by the symbol column
|
||||
* @method ChildCurrencyQuery orderByRate($order = Criteria::ASC) Order by the rate column
|
||||
* @method ChildCurrencyQuery orderByByDefault($order = Criteria::ASC) Order by the by_default column
|
||||
* @method ChildCurrencyQuery orderByPosition($order = Criteria::ASC) Order by the position column
|
||||
* @method ChildCurrencyQuery orderByCreatedAt($order = Criteria::ASC) Order by the created_at column
|
||||
* @method ChildCurrencyQuery orderByUpdatedAt($order = Criteria::ASC) Order by the updated_at column
|
||||
*
|
||||
@@ -35,6 +36,7 @@ use Thelia\Model\Map\CurrencyTableMap;
|
||||
* @method ChildCurrencyQuery groupBySymbol() Group by the symbol column
|
||||
* @method ChildCurrencyQuery groupByRate() Group by the rate column
|
||||
* @method ChildCurrencyQuery groupByByDefault() Group by the by_default column
|
||||
* @method ChildCurrencyQuery groupByPosition() Group by the position column
|
||||
* @method ChildCurrencyQuery groupByCreatedAt() Group by the created_at column
|
||||
* @method ChildCurrencyQuery groupByUpdatedAt() Group by the updated_at column
|
||||
*
|
||||
@@ -66,6 +68,7 @@ use Thelia\Model\Map\CurrencyTableMap;
|
||||
* @method ChildCurrency findOneBySymbol(string $symbol) Return the first ChildCurrency filtered by the symbol column
|
||||
* @method ChildCurrency findOneByRate(double $rate) Return the first ChildCurrency filtered by the rate column
|
||||
* @method ChildCurrency findOneByByDefault(int $by_default) Return the first ChildCurrency filtered by the by_default column
|
||||
* @method ChildCurrency findOneByPosition(int $position) Return the first ChildCurrency filtered by the position column
|
||||
* @method ChildCurrency findOneByCreatedAt(string $created_at) Return the first ChildCurrency filtered by the created_at column
|
||||
* @method ChildCurrency findOneByUpdatedAt(string $updated_at) Return the first ChildCurrency filtered by the updated_at column
|
||||
*
|
||||
@@ -74,6 +77,7 @@ use Thelia\Model\Map\CurrencyTableMap;
|
||||
* @method array findBySymbol(string $symbol) Return ChildCurrency objects filtered by the symbol column
|
||||
* @method array findByRate(double $rate) Return ChildCurrency objects filtered by the rate column
|
||||
* @method array findByByDefault(int $by_default) Return ChildCurrency objects filtered by the by_default column
|
||||
* @method array findByPosition(int $position) Return ChildCurrency objects filtered by the position column
|
||||
* @method array findByCreatedAt(string $created_at) Return ChildCurrency objects filtered by the created_at column
|
||||
* @method array findByUpdatedAt(string $updated_at) Return ChildCurrency objects filtered by the updated_at column
|
||||
*
|
||||
@@ -164,7 +168,7 @@ abstract class CurrencyQuery extends ModelCriteria
|
||||
*/
|
||||
protected function findPkSimple($key, $con)
|
||||
{
|
||||
$sql = 'SELECT ID, CODE, SYMBOL, RATE, BY_DEFAULT, CREATED_AT, UPDATED_AT FROM currency WHERE ID = :p0';
|
||||
$sql = 'SELECT ID, CODE, SYMBOL, RATE, BY_DEFAULT, POSITION, CREATED_AT, UPDATED_AT FROM currency WHERE ID = :p0';
|
||||
try {
|
||||
$stmt = $con->prepare($sql);
|
||||
$stmt->bindValue(':p0', $key, PDO::PARAM_INT);
|
||||
@@ -434,6 +438,47 @@ abstract class CurrencyQuery extends ModelCriteria
|
||||
return $this->addUsingAlias(CurrencyTableMap::BY_DEFAULT, $byDefault, $comparison);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter the query on the position column
|
||||
*
|
||||
* Example usage:
|
||||
* <code>
|
||||
* $query->filterByPosition(1234); // WHERE position = 1234
|
||||
* $query->filterByPosition(array(12, 34)); // WHERE position IN (12, 34)
|
||||
* $query->filterByPosition(array('min' => 12)); // WHERE position > 12
|
||||
* </code>
|
||||
*
|
||||
* @param mixed $position The value to use as filter.
|
||||
* Use scalar values for equality.
|
||||
* Use array values for in_array() equivalent.
|
||||
* Use associative array('min' => $minValue, 'max' => $maxValue) for intervals.
|
||||
* @param string $comparison Operator to use for the column comparison, defaults to Criteria::EQUAL
|
||||
*
|
||||
* @return ChildCurrencyQuery The current query, for fluid interface
|
||||
*/
|
||||
public function filterByPosition($position = null, $comparison = null)
|
||||
{
|
||||
if (is_array($position)) {
|
||||
$useMinMax = false;
|
||||
if (isset($position['min'])) {
|
||||
$this->addUsingAlias(CurrencyTableMap::POSITION, $position['min'], Criteria::GREATER_EQUAL);
|
||||
$useMinMax = true;
|
||||
}
|
||||
if (isset($position['max'])) {
|
||||
$this->addUsingAlias(CurrencyTableMap::POSITION, $position['max'], Criteria::LESS_EQUAL);
|
||||
$useMinMax = true;
|
||||
}
|
||||
if ($useMinMax) {
|
||||
return $this;
|
||||
}
|
||||
if (null === $comparison) {
|
||||
$comparison = Criteria::IN;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->addUsingAlias(CurrencyTableMap::POSITION, $position, $comparison);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter the query on the created_at column
|
||||
*
|
||||
|
||||
@@ -90,6 +90,12 @@ abstract class Lang implements ActiveRecordInterface
|
||||
*/
|
||||
protected $by_default;
|
||||
|
||||
/**
|
||||
* The value for the position field.
|
||||
* @var int
|
||||
*/
|
||||
protected $position;
|
||||
|
||||
/**
|
||||
* The value for the created_at field.
|
||||
* @var string
|
||||
@@ -430,6 +436,17 @@ abstract class Lang implements ActiveRecordInterface
|
||||
return $this->by_default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the [position] column value.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getPosition()
|
||||
{
|
||||
|
||||
return $this->position;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the [optionally formatted] temporal [created_at] column value.
|
||||
*
|
||||
@@ -596,6 +613,27 @@ abstract class Lang implements ActiveRecordInterface
|
||||
return $this;
|
||||
} // setByDefault()
|
||||
|
||||
/**
|
||||
* Set the value of [position] column.
|
||||
*
|
||||
* @param int $v new value
|
||||
* @return \Thelia\Model\Lang The current object (for fluent API support)
|
||||
*/
|
||||
public function setPosition($v)
|
||||
{
|
||||
if ($v !== null) {
|
||||
$v = (int) $v;
|
||||
}
|
||||
|
||||
if ($this->position !== $v) {
|
||||
$this->position = $v;
|
||||
$this->modifiedColumns[] = LangTableMap::POSITION;
|
||||
}
|
||||
|
||||
|
||||
return $this;
|
||||
} // setPosition()
|
||||
|
||||
/**
|
||||
* Sets the value of [created_at] column to a normalized version of the date/time value specified.
|
||||
*
|
||||
@@ -693,13 +731,16 @@ abstract class Lang implements ActiveRecordInterface
|
||||
$col = $row[TableMap::TYPE_NUM == $indexType ? 5 + $startcol : LangTableMap::translateFieldName('ByDefault', TableMap::TYPE_PHPNAME, $indexType)];
|
||||
$this->by_default = (null !== $col) ? (int) $col : null;
|
||||
|
||||
$col = $row[TableMap::TYPE_NUM == $indexType ? 6 + $startcol : LangTableMap::translateFieldName('CreatedAt', TableMap::TYPE_PHPNAME, $indexType)];
|
||||
$col = $row[TableMap::TYPE_NUM == $indexType ? 6 + $startcol : LangTableMap::translateFieldName('Position', TableMap::TYPE_PHPNAME, $indexType)];
|
||||
$this->position = (null !== $col) ? (int) $col : null;
|
||||
|
||||
$col = $row[TableMap::TYPE_NUM == $indexType ? 7 + $startcol : LangTableMap::translateFieldName('CreatedAt', TableMap::TYPE_PHPNAME, $indexType)];
|
||||
if ($col === '0000-00-00 00:00:00') {
|
||||
$col = null;
|
||||
}
|
||||
$this->created_at = (null !== $col) ? PropelDateTime::newInstance($col, null, '\DateTime') : null;
|
||||
|
||||
$col = $row[TableMap::TYPE_NUM == $indexType ? 7 + $startcol : LangTableMap::translateFieldName('UpdatedAt', TableMap::TYPE_PHPNAME, $indexType)];
|
||||
$col = $row[TableMap::TYPE_NUM == $indexType ? 8 + $startcol : LangTableMap::translateFieldName('UpdatedAt', TableMap::TYPE_PHPNAME, $indexType)];
|
||||
if ($col === '0000-00-00 00:00:00') {
|
||||
$col = null;
|
||||
}
|
||||
@@ -712,7 +753,7 @@ abstract class Lang implements ActiveRecordInterface
|
||||
$this->ensureConsistency();
|
||||
}
|
||||
|
||||
return $startcol + 8; // 8 = LangTableMap::NUM_HYDRATE_COLUMNS.
|
||||
return $startcol + 9; // 9 = LangTableMap::NUM_HYDRATE_COLUMNS.
|
||||
|
||||
} catch (Exception $e) {
|
||||
throw new PropelException("Error populating \Thelia\Model\Lang object", 0, $e);
|
||||
@@ -950,6 +991,9 @@ abstract class Lang implements ActiveRecordInterface
|
||||
if ($this->isColumnModified(LangTableMap::BY_DEFAULT)) {
|
||||
$modifiedColumns[':p' . $index++] = 'BY_DEFAULT';
|
||||
}
|
||||
if ($this->isColumnModified(LangTableMap::POSITION)) {
|
||||
$modifiedColumns[':p' . $index++] = 'POSITION';
|
||||
}
|
||||
if ($this->isColumnModified(LangTableMap::CREATED_AT)) {
|
||||
$modifiedColumns[':p' . $index++] = 'CREATED_AT';
|
||||
}
|
||||
@@ -985,6 +1029,9 @@ abstract class Lang implements ActiveRecordInterface
|
||||
case 'BY_DEFAULT':
|
||||
$stmt->bindValue($identifier, $this->by_default, PDO::PARAM_INT);
|
||||
break;
|
||||
case 'POSITION':
|
||||
$stmt->bindValue($identifier, $this->position, PDO::PARAM_INT);
|
||||
break;
|
||||
case 'CREATED_AT':
|
||||
$stmt->bindValue($identifier, $this->created_at ? $this->created_at->format("Y-m-d H:i:s") : null, PDO::PARAM_STR);
|
||||
break;
|
||||
@@ -1072,9 +1119,12 @@ abstract class Lang implements ActiveRecordInterface
|
||||
return $this->getByDefault();
|
||||
break;
|
||||
case 6:
|
||||
return $this->getCreatedAt();
|
||||
return $this->getPosition();
|
||||
break;
|
||||
case 7:
|
||||
return $this->getCreatedAt();
|
||||
break;
|
||||
case 8:
|
||||
return $this->getUpdatedAt();
|
||||
break;
|
||||
default:
|
||||
@@ -1111,8 +1161,9 @@ abstract class Lang implements ActiveRecordInterface
|
||||
$keys[3] => $this->getLocale(),
|
||||
$keys[4] => $this->getUrl(),
|
||||
$keys[5] => $this->getByDefault(),
|
||||
$keys[6] => $this->getCreatedAt(),
|
||||
$keys[7] => $this->getUpdatedAt(),
|
||||
$keys[6] => $this->getPosition(),
|
||||
$keys[7] => $this->getCreatedAt(),
|
||||
$keys[8] => $this->getUpdatedAt(),
|
||||
);
|
||||
$virtualColumns = $this->virtualColumns;
|
||||
foreach($virtualColumns as $key => $virtualColumn)
|
||||
@@ -1172,9 +1223,12 @@ abstract class Lang implements ActiveRecordInterface
|
||||
$this->setByDefault($value);
|
||||
break;
|
||||
case 6:
|
||||
$this->setCreatedAt($value);
|
||||
$this->setPosition($value);
|
||||
break;
|
||||
case 7:
|
||||
$this->setCreatedAt($value);
|
||||
break;
|
||||
case 8:
|
||||
$this->setUpdatedAt($value);
|
||||
break;
|
||||
} // switch()
|
||||
@@ -1207,8 +1261,9 @@ abstract class Lang implements ActiveRecordInterface
|
||||
if (array_key_exists($keys[3], $arr)) $this->setLocale($arr[$keys[3]]);
|
||||
if (array_key_exists($keys[4], $arr)) $this->setUrl($arr[$keys[4]]);
|
||||
if (array_key_exists($keys[5], $arr)) $this->setByDefault($arr[$keys[5]]);
|
||||
if (array_key_exists($keys[6], $arr)) $this->setCreatedAt($arr[$keys[6]]);
|
||||
if (array_key_exists($keys[7], $arr)) $this->setUpdatedAt($arr[$keys[7]]);
|
||||
if (array_key_exists($keys[6], $arr)) $this->setPosition($arr[$keys[6]]);
|
||||
if (array_key_exists($keys[7], $arr)) $this->setCreatedAt($arr[$keys[7]]);
|
||||
if (array_key_exists($keys[8], $arr)) $this->setUpdatedAt($arr[$keys[8]]);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1226,6 +1281,7 @@ abstract class Lang implements ActiveRecordInterface
|
||||
if ($this->isColumnModified(LangTableMap::LOCALE)) $criteria->add(LangTableMap::LOCALE, $this->locale);
|
||||
if ($this->isColumnModified(LangTableMap::URL)) $criteria->add(LangTableMap::URL, $this->url);
|
||||
if ($this->isColumnModified(LangTableMap::BY_DEFAULT)) $criteria->add(LangTableMap::BY_DEFAULT, $this->by_default);
|
||||
if ($this->isColumnModified(LangTableMap::POSITION)) $criteria->add(LangTableMap::POSITION, $this->position);
|
||||
if ($this->isColumnModified(LangTableMap::CREATED_AT)) $criteria->add(LangTableMap::CREATED_AT, $this->created_at);
|
||||
if ($this->isColumnModified(LangTableMap::UPDATED_AT)) $criteria->add(LangTableMap::UPDATED_AT, $this->updated_at);
|
||||
|
||||
@@ -1296,6 +1352,7 @@ abstract class Lang implements ActiveRecordInterface
|
||||
$copyObj->setLocale($this->getLocale());
|
||||
$copyObj->setUrl($this->getUrl());
|
||||
$copyObj->setByDefault($this->getByDefault());
|
||||
$copyObj->setPosition($this->getPosition());
|
||||
$copyObj->setCreatedAt($this->getCreatedAt());
|
||||
$copyObj->setUpdatedAt($this->getUpdatedAt());
|
||||
if ($makeNew) {
|
||||
@@ -1337,6 +1394,7 @@ abstract class Lang implements ActiveRecordInterface
|
||||
$this->locale = null;
|
||||
$this->url = null;
|
||||
$this->by_default = null;
|
||||
$this->position = null;
|
||||
$this->created_at = null;
|
||||
$this->updated_at = null;
|
||||
$this->alreadyInSave = false;
|
||||
|
||||
@@ -24,6 +24,7 @@ use Thelia\Model\Map\LangTableMap;
|
||||
* @method ChildLangQuery orderByLocale($order = Criteria::ASC) Order by the locale column
|
||||
* @method ChildLangQuery orderByUrl($order = Criteria::ASC) Order by the url column
|
||||
* @method ChildLangQuery orderByByDefault($order = Criteria::ASC) Order by the by_default column
|
||||
* @method ChildLangQuery orderByPosition($order = Criteria::ASC) Order by the position column
|
||||
* @method ChildLangQuery orderByCreatedAt($order = Criteria::ASC) Order by the created_at column
|
||||
* @method ChildLangQuery orderByUpdatedAt($order = Criteria::ASC) Order by the updated_at column
|
||||
*
|
||||
@@ -33,6 +34,7 @@ use Thelia\Model\Map\LangTableMap;
|
||||
* @method ChildLangQuery groupByLocale() Group by the locale column
|
||||
* @method ChildLangQuery groupByUrl() Group by the url column
|
||||
* @method ChildLangQuery groupByByDefault() Group by the by_default column
|
||||
* @method ChildLangQuery groupByPosition() Group by the position column
|
||||
* @method ChildLangQuery groupByCreatedAt() Group by the created_at column
|
||||
* @method ChildLangQuery groupByUpdatedAt() Group by the updated_at column
|
||||
*
|
||||
@@ -49,6 +51,7 @@ use Thelia\Model\Map\LangTableMap;
|
||||
* @method ChildLang findOneByLocale(string $locale) Return the first ChildLang filtered by the locale column
|
||||
* @method ChildLang findOneByUrl(string $url) Return the first ChildLang filtered by the url column
|
||||
* @method ChildLang findOneByByDefault(int $by_default) Return the first ChildLang filtered by the by_default column
|
||||
* @method ChildLang findOneByPosition(int $position) Return the first ChildLang filtered by the position column
|
||||
* @method ChildLang findOneByCreatedAt(string $created_at) Return the first ChildLang filtered by the created_at column
|
||||
* @method ChildLang findOneByUpdatedAt(string $updated_at) Return the first ChildLang filtered by the updated_at column
|
||||
*
|
||||
@@ -58,6 +61,7 @@ use Thelia\Model\Map\LangTableMap;
|
||||
* @method array findByLocale(string $locale) Return ChildLang objects filtered by the locale column
|
||||
* @method array findByUrl(string $url) Return ChildLang objects filtered by the url column
|
||||
* @method array findByByDefault(int $by_default) Return ChildLang objects filtered by the by_default column
|
||||
* @method array findByPosition(int $position) Return ChildLang objects filtered by the position column
|
||||
* @method array findByCreatedAt(string $created_at) Return ChildLang objects filtered by the created_at column
|
||||
* @method array findByUpdatedAt(string $updated_at) Return ChildLang objects filtered by the updated_at column
|
||||
*
|
||||
@@ -148,7 +152,7 @@ abstract class LangQuery extends ModelCriteria
|
||||
*/
|
||||
protected function findPkSimple($key, $con)
|
||||
{
|
||||
$sql = 'SELECT ID, TITLE, CODE, LOCALE, URL, BY_DEFAULT, CREATED_AT, UPDATED_AT FROM lang WHERE ID = :p0';
|
||||
$sql = 'SELECT ID, TITLE, CODE, LOCALE, URL, BY_DEFAULT, POSITION, CREATED_AT, UPDATED_AT FROM lang WHERE ID = :p0';
|
||||
try {
|
||||
$stmt = $con->prepare($sql);
|
||||
$stmt->bindValue(':p0', $key, PDO::PARAM_INT);
|
||||
@@ -435,6 +439,47 @@ abstract class LangQuery extends ModelCriteria
|
||||
return $this->addUsingAlias(LangTableMap::BY_DEFAULT, $byDefault, $comparison);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter the query on the position column
|
||||
*
|
||||
* Example usage:
|
||||
* <code>
|
||||
* $query->filterByPosition(1234); // WHERE position = 1234
|
||||
* $query->filterByPosition(array(12, 34)); // WHERE position IN (12, 34)
|
||||
* $query->filterByPosition(array('min' => 12)); // WHERE position > 12
|
||||
* </code>
|
||||
*
|
||||
* @param mixed $position The value to use as filter.
|
||||
* Use scalar values for equality.
|
||||
* Use array values for in_array() equivalent.
|
||||
* Use associative array('min' => $minValue, 'max' => $maxValue) for intervals.
|
||||
* @param string $comparison Operator to use for the column comparison, defaults to Criteria::EQUAL
|
||||
*
|
||||
* @return ChildLangQuery The current query, for fluid interface
|
||||
*/
|
||||
public function filterByPosition($position = null, $comparison = null)
|
||||
{
|
||||
if (is_array($position)) {
|
||||
$useMinMax = false;
|
||||
if (isset($position['min'])) {
|
||||
$this->addUsingAlias(LangTableMap::POSITION, $position['min'], Criteria::GREATER_EQUAL);
|
||||
$useMinMax = true;
|
||||
}
|
||||
if (isset($position['max'])) {
|
||||
$this->addUsingAlias(LangTableMap::POSITION, $position['max'], Criteria::LESS_EQUAL);
|
||||
$useMinMax = true;
|
||||
}
|
||||
if ($useMinMax) {
|
||||
return $this;
|
||||
}
|
||||
if (null === $comparison) {
|
||||
$comparison = Criteria::IN;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->addUsingAlias(LangTableMap::POSITION, $position, $comparison);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter the query on the created_at column
|
||||
*
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace Thelia\Model;
|
||||
|
||||
use Thelia\Model\Base\Category as BaseCategory;
|
||||
use Propel\Runtime\ActiveQuery\Criteria;
|
||||
|
||||
class Category extends BaseCategory {
|
||||
/**
|
||||
@@ -17,6 +18,37 @@ class Category extends BaseCategory {
|
||||
{
|
||||
|
||||
}
|
||||
/**
|
||||
* Create a new category.
|
||||
*
|
||||
* @param string $title the category title
|
||||
* @param int $parent the ID of the parent category
|
||||
* @param string $locale the locale of the title
|
||||
*/
|
||||
public function create($title, $parent, $locale)
|
||||
{
|
||||
$this
|
||||
->setLocale($locale)
|
||||
->setTitle($title)
|
||||
->setParent($parent)
|
||||
->setVisible(1)
|
||||
->setPosition($this->getNextPosition($parent))
|
||||
;
|
||||
|
||||
$this->save();
|
||||
}
|
||||
|
||||
public function getNextPosition($parent) {
|
||||
|
||||
$last = CategoryQuery::create()
|
||||
->filterByParent($parent)
|
||||
->orderByPosition(Criteria::DESC)
|
||||
->limit(1)
|
||||
->findOne()
|
||||
;
|
||||
|
||||
return $last->getPosition() + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
|
||||
@@ -57,7 +57,7 @@ class CurrencyTableMap extends TableMap
|
||||
/**
|
||||
* The total number of columns
|
||||
*/
|
||||
const NUM_COLUMNS = 7;
|
||||
const NUM_COLUMNS = 8;
|
||||
|
||||
/**
|
||||
* The number of lazy-loaded columns
|
||||
@@ -67,7 +67,7 @@ class CurrencyTableMap extends TableMap
|
||||
/**
|
||||
* The number of columns to hydrate (NUM_COLUMNS - NUM_LAZY_LOAD_COLUMNS)
|
||||
*/
|
||||
const NUM_HYDRATE_COLUMNS = 7;
|
||||
const NUM_HYDRATE_COLUMNS = 8;
|
||||
|
||||
/**
|
||||
* the column name for the ID field
|
||||
@@ -94,6 +94,11 @@ class CurrencyTableMap extends TableMap
|
||||
*/
|
||||
const BY_DEFAULT = 'currency.BY_DEFAULT';
|
||||
|
||||
/**
|
||||
* the column name for the POSITION field
|
||||
*/
|
||||
const POSITION = 'currency.POSITION';
|
||||
|
||||
/**
|
||||
* the column name for the CREATED_AT field
|
||||
*/
|
||||
@@ -125,12 +130,12 @@ class CurrencyTableMap extends TableMap
|
||||
* e.g. self::$fieldNames[self::TYPE_PHPNAME][0] = 'Id'
|
||||
*/
|
||||
protected static $fieldNames = array (
|
||||
self::TYPE_PHPNAME => array('Id', 'Code', 'Symbol', 'Rate', 'ByDefault', 'CreatedAt', 'UpdatedAt', ),
|
||||
self::TYPE_STUDLYPHPNAME => array('id', 'code', 'symbol', 'rate', 'byDefault', 'createdAt', 'updatedAt', ),
|
||||
self::TYPE_COLNAME => array(CurrencyTableMap::ID, CurrencyTableMap::CODE, CurrencyTableMap::SYMBOL, CurrencyTableMap::RATE, CurrencyTableMap::BY_DEFAULT, CurrencyTableMap::CREATED_AT, CurrencyTableMap::UPDATED_AT, ),
|
||||
self::TYPE_RAW_COLNAME => array('ID', 'CODE', 'SYMBOL', 'RATE', 'BY_DEFAULT', 'CREATED_AT', 'UPDATED_AT', ),
|
||||
self::TYPE_FIELDNAME => array('id', 'code', 'symbol', 'rate', 'by_default', 'created_at', 'updated_at', ),
|
||||
self::TYPE_NUM => array(0, 1, 2, 3, 4, 5, 6, )
|
||||
self::TYPE_PHPNAME => array('Id', 'Code', 'Symbol', 'Rate', 'ByDefault', 'Position', 'CreatedAt', 'UpdatedAt', ),
|
||||
self::TYPE_STUDLYPHPNAME => array('id', 'code', 'symbol', 'rate', 'byDefault', 'position', 'createdAt', 'updatedAt', ),
|
||||
self::TYPE_COLNAME => array(CurrencyTableMap::ID, CurrencyTableMap::CODE, CurrencyTableMap::SYMBOL, CurrencyTableMap::RATE, CurrencyTableMap::BY_DEFAULT, CurrencyTableMap::POSITION, CurrencyTableMap::CREATED_AT, CurrencyTableMap::UPDATED_AT, ),
|
||||
self::TYPE_RAW_COLNAME => array('ID', 'CODE', 'SYMBOL', 'RATE', 'BY_DEFAULT', 'POSITION', 'CREATED_AT', 'UPDATED_AT', ),
|
||||
self::TYPE_FIELDNAME => array('id', 'code', 'symbol', 'rate', 'by_default', 'position', 'created_at', 'updated_at', ),
|
||||
self::TYPE_NUM => array(0, 1, 2, 3, 4, 5, 6, 7, )
|
||||
);
|
||||
|
||||
/**
|
||||
@@ -140,12 +145,12 @@ class CurrencyTableMap extends TableMap
|
||||
* e.g. self::$fieldKeys[self::TYPE_PHPNAME]['Id'] = 0
|
||||
*/
|
||||
protected static $fieldKeys = array (
|
||||
self::TYPE_PHPNAME => array('Id' => 0, 'Code' => 1, 'Symbol' => 2, 'Rate' => 3, 'ByDefault' => 4, 'CreatedAt' => 5, 'UpdatedAt' => 6, ),
|
||||
self::TYPE_STUDLYPHPNAME => array('id' => 0, 'code' => 1, 'symbol' => 2, 'rate' => 3, 'byDefault' => 4, 'createdAt' => 5, 'updatedAt' => 6, ),
|
||||
self::TYPE_COLNAME => array(CurrencyTableMap::ID => 0, CurrencyTableMap::CODE => 1, CurrencyTableMap::SYMBOL => 2, CurrencyTableMap::RATE => 3, CurrencyTableMap::BY_DEFAULT => 4, CurrencyTableMap::CREATED_AT => 5, CurrencyTableMap::UPDATED_AT => 6, ),
|
||||
self::TYPE_RAW_COLNAME => array('ID' => 0, 'CODE' => 1, 'SYMBOL' => 2, 'RATE' => 3, 'BY_DEFAULT' => 4, 'CREATED_AT' => 5, 'UPDATED_AT' => 6, ),
|
||||
self::TYPE_FIELDNAME => array('id' => 0, 'code' => 1, 'symbol' => 2, 'rate' => 3, 'by_default' => 4, 'created_at' => 5, 'updated_at' => 6, ),
|
||||
self::TYPE_NUM => array(0, 1, 2, 3, 4, 5, 6, )
|
||||
self::TYPE_PHPNAME => array('Id' => 0, 'Code' => 1, 'Symbol' => 2, 'Rate' => 3, 'ByDefault' => 4, 'Position' => 5, 'CreatedAt' => 6, 'UpdatedAt' => 7, ),
|
||||
self::TYPE_STUDLYPHPNAME => array('id' => 0, 'code' => 1, 'symbol' => 2, 'rate' => 3, 'byDefault' => 4, 'position' => 5, 'createdAt' => 6, 'updatedAt' => 7, ),
|
||||
self::TYPE_COLNAME => array(CurrencyTableMap::ID => 0, CurrencyTableMap::CODE => 1, CurrencyTableMap::SYMBOL => 2, CurrencyTableMap::RATE => 3, CurrencyTableMap::BY_DEFAULT => 4, CurrencyTableMap::POSITION => 5, CurrencyTableMap::CREATED_AT => 6, CurrencyTableMap::UPDATED_AT => 7, ),
|
||||
self::TYPE_RAW_COLNAME => array('ID' => 0, 'CODE' => 1, 'SYMBOL' => 2, 'RATE' => 3, 'BY_DEFAULT' => 4, 'POSITION' => 5, 'CREATED_AT' => 6, 'UPDATED_AT' => 7, ),
|
||||
self::TYPE_FIELDNAME => array('id' => 0, 'code' => 1, 'symbol' => 2, 'rate' => 3, 'by_default' => 4, 'position' => 5, 'created_at' => 6, 'updated_at' => 7, ),
|
||||
self::TYPE_NUM => array(0, 1, 2, 3, 4, 5, 6, 7, )
|
||||
);
|
||||
|
||||
/**
|
||||
@@ -169,6 +174,7 @@ class CurrencyTableMap extends TableMap
|
||||
$this->addColumn('SYMBOL', 'Symbol', 'VARCHAR', false, 45, null);
|
||||
$this->addColumn('RATE', 'Rate', 'FLOAT', false, null, null);
|
||||
$this->addColumn('BY_DEFAULT', 'ByDefault', 'TINYINT', false, null, null);
|
||||
$this->addColumn('POSITION', 'Position', 'INTEGER', false, null, null);
|
||||
$this->addColumn('CREATED_AT', 'CreatedAt', 'TIMESTAMP', false, null, null);
|
||||
$this->addColumn('UPDATED_AT', 'UpdatedAt', 'TIMESTAMP', false, null, null);
|
||||
} // initialize()
|
||||
@@ -352,6 +358,7 @@ class CurrencyTableMap extends TableMap
|
||||
$criteria->addSelectColumn(CurrencyTableMap::SYMBOL);
|
||||
$criteria->addSelectColumn(CurrencyTableMap::RATE);
|
||||
$criteria->addSelectColumn(CurrencyTableMap::BY_DEFAULT);
|
||||
$criteria->addSelectColumn(CurrencyTableMap::POSITION);
|
||||
$criteria->addSelectColumn(CurrencyTableMap::CREATED_AT);
|
||||
$criteria->addSelectColumn(CurrencyTableMap::UPDATED_AT);
|
||||
} else {
|
||||
@@ -360,6 +367,7 @@ class CurrencyTableMap extends TableMap
|
||||
$criteria->addSelectColumn($alias . '.SYMBOL');
|
||||
$criteria->addSelectColumn($alias . '.RATE');
|
||||
$criteria->addSelectColumn($alias . '.BY_DEFAULT');
|
||||
$criteria->addSelectColumn($alias . '.POSITION');
|
||||
$criteria->addSelectColumn($alias . '.CREATED_AT');
|
||||
$criteria->addSelectColumn($alias . '.UPDATED_AT');
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ class LangTableMap extends TableMap
|
||||
/**
|
||||
* The total number of columns
|
||||
*/
|
||||
const NUM_COLUMNS = 8;
|
||||
const NUM_COLUMNS = 9;
|
||||
|
||||
/**
|
||||
* The number of lazy-loaded columns
|
||||
@@ -67,7 +67,7 @@ class LangTableMap extends TableMap
|
||||
/**
|
||||
* The number of columns to hydrate (NUM_COLUMNS - NUM_LAZY_LOAD_COLUMNS)
|
||||
*/
|
||||
const NUM_HYDRATE_COLUMNS = 8;
|
||||
const NUM_HYDRATE_COLUMNS = 9;
|
||||
|
||||
/**
|
||||
* the column name for the ID field
|
||||
@@ -99,6 +99,11 @@ class LangTableMap extends TableMap
|
||||
*/
|
||||
const BY_DEFAULT = 'lang.BY_DEFAULT';
|
||||
|
||||
/**
|
||||
* the column name for the POSITION field
|
||||
*/
|
||||
const POSITION = 'lang.POSITION';
|
||||
|
||||
/**
|
||||
* the column name for the CREATED_AT field
|
||||
*/
|
||||
@@ -121,12 +126,12 @@ class LangTableMap extends TableMap
|
||||
* e.g. self::$fieldNames[self::TYPE_PHPNAME][0] = 'Id'
|
||||
*/
|
||||
protected static $fieldNames = array (
|
||||
self::TYPE_PHPNAME => array('Id', 'Title', 'Code', 'Locale', 'Url', 'ByDefault', 'CreatedAt', 'UpdatedAt', ),
|
||||
self::TYPE_STUDLYPHPNAME => array('id', 'title', 'code', 'locale', 'url', 'byDefault', 'createdAt', 'updatedAt', ),
|
||||
self::TYPE_COLNAME => array(LangTableMap::ID, LangTableMap::TITLE, LangTableMap::CODE, LangTableMap::LOCALE, LangTableMap::URL, LangTableMap::BY_DEFAULT, LangTableMap::CREATED_AT, LangTableMap::UPDATED_AT, ),
|
||||
self::TYPE_RAW_COLNAME => array('ID', 'TITLE', 'CODE', 'LOCALE', 'URL', 'BY_DEFAULT', 'CREATED_AT', 'UPDATED_AT', ),
|
||||
self::TYPE_FIELDNAME => array('id', 'title', 'code', 'locale', 'url', 'by_default', 'created_at', 'updated_at', ),
|
||||
self::TYPE_NUM => array(0, 1, 2, 3, 4, 5, 6, 7, )
|
||||
self::TYPE_PHPNAME => array('Id', 'Title', 'Code', 'Locale', 'Url', 'ByDefault', 'Position', 'CreatedAt', 'UpdatedAt', ),
|
||||
self::TYPE_STUDLYPHPNAME => array('id', 'title', 'code', 'locale', 'url', 'byDefault', 'position', 'createdAt', 'updatedAt', ),
|
||||
self::TYPE_COLNAME => array(LangTableMap::ID, LangTableMap::TITLE, LangTableMap::CODE, LangTableMap::LOCALE, LangTableMap::URL, LangTableMap::BY_DEFAULT, LangTableMap::POSITION, LangTableMap::CREATED_AT, LangTableMap::UPDATED_AT, ),
|
||||
self::TYPE_RAW_COLNAME => array('ID', 'TITLE', 'CODE', 'LOCALE', 'URL', 'BY_DEFAULT', 'POSITION', 'CREATED_AT', 'UPDATED_AT', ),
|
||||
self::TYPE_FIELDNAME => array('id', 'title', 'code', 'locale', 'url', 'by_default', 'position', 'created_at', 'updated_at', ),
|
||||
self::TYPE_NUM => array(0, 1, 2, 3, 4, 5, 6, 7, 8, )
|
||||
);
|
||||
|
||||
/**
|
||||
@@ -136,12 +141,12 @@ class LangTableMap extends TableMap
|
||||
* e.g. self::$fieldKeys[self::TYPE_PHPNAME]['Id'] = 0
|
||||
*/
|
||||
protected static $fieldKeys = array (
|
||||
self::TYPE_PHPNAME => array('Id' => 0, 'Title' => 1, 'Code' => 2, 'Locale' => 3, 'Url' => 4, 'ByDefault' => 5, 'CreatedAt' => 6, 'UpdatedAt' => 7, ),
|
||||
self::TYPE_STUDLYPHPNAME => array('id' => 0, 'title' => 1, 'code' => 2, 'locale' => 3, 'url' => 4, 'byDefault' => 5, 'createdAt' => 6, 'updatedAt' => 7, ),
|
||||
self::TYPE_COLNAME => array(LangTableMap::ID => 0, LangTableMap::TITLE => 1, LangTableMap::CODE => 2, LangTableMap::LOCALE => 3, LangTableMap::URL => 4, LangTableMap::BY_DEFAULT => 5, LangTableMap::CREATED_AT => 6, LangTableMap::UPDATED_AT => 7, ),
|
||||
self::TYPE_RAW_COLNAME => array('ID' => 0, 'TITLE' => 1, 'CODE' => 2, 'LOCALE' => 3, 'URL' => 4, 'BY_DEFAULT' => 5, 'CREATED_AT' => 6, 'UPDATED_AT' => 7, ),
|
||||
self::TYPE_FIELDNAME => array('id' => 0, 'title' => 1, 'code' => 2, 'locale' => 3, 'url' => 4, 'by_default' => 5, 'created_at' => 6, 'updated_at' => 7, ),
|
||||
self::TYPE_NUM => array(0, 1, 2, 3, 4, 5, 6, 7, )
|
||||
self::TYPE_PHPNAME => array('Id' => 0, 'Title' => 1, 'Code' => 2, 'Locale' => 3, 'Url' => 4, 'ByDefault' => 5, 'Position' => 6, 'CreatedAt' => 7, 'UpdatedAt' => 8, ),
|
||||
self::TYPE_STUDLYPHPNAME => array('id' => 0, 'title' => 1, 'code' => 2, 'locale' => 3, 'url' => 4, 'byDefault' => 5, 'position' => 6, 'createdAt' => 7, 'updatedAt' => 8, ),
|
||||
self::TYPE_COLNAME => array(LangTableMap::ID => 0, LangTableMap::TITLE => 1, LangTableMap::CODE => 2, LangTableMap::LOCALE => 3, LangTableMap::URL => 4, LangTableMap::BY_DEFAULT => 5, LangTableMap::POSITION => 6, LangTableMap::CREATED_AT => 7, LangTableMap::UPDATED_AT => 8, ),
|
||||
self::TYPE_RAW_COLNAME => array('ID' => 0, 'TITLE' => 1, 'CODE' => 2, 'LOCALE' => 3, 'URL' => 4, 'BY_DEFAULT' => 5, 'POSITION' => 6, 'CREATED_AT' => 7, 'UPDATED_AT' => 8, ),
|
||||
self::TYPE_FIELDNAME => array('id' => 0, 'title' => 1, 'code' => 2, 'locale' => 3, 'url' => 4, 'by_default' => 5, 'position' => 6, 'created_at' => 7, 'updated_at' => 8, ),
|
||||
self::TYPE_NUM => array(0, 1, 2, 3, 4, 5, 6, 7, 8, )
|
||||
);
|
||||
|
||||
/**
|
||||
@@ -166,6 +171,7 @@ class LangTableMap extends TableMap
|
||||
$this->addColumn('LOCALE', 'Locale', 'VARCHAR', false, 45, null);
|
||||
$this->addColumn('URL', 'Url', 'VARCHAR', false, 255, null);
|
||||
$this->addColumn('BY_DEFAULT', 'ByDefault', 'TINYINT', false, null, null);
|
||||
$this->addColumn('POSITION', 'Position', 'INTEGER', false, null, null);
|
||||
$this->addColumn('CREATED_AT', 'CreatedAt', 'TIMESTAMP', false, null, null);
|
||||
$this->addColumn('UPDATED_AT', 'UpdatedAt', 'TIMESTAMP', false, null, null);
|
||||
} // initialize()
|
||||
@@ -334,6 +340,7 @@ class LangTableMap extends TableMap
|
||||
$criteria->addSelectColumn(LangTableMap::LOCALE);
|
||||
$criteria->addSelectColumn(LangTableMap::URL);
|
||||
$criteria->addSelectColumn(LangTableMap::BY_DEFAULT);
|
||||
$criteria->addSelectColumn(LangTableMap::POSITION);
|
||||
$criteria->addSelectColumn(LangTableMap::CREATED_AT);
|
||||
$criteria->addSelectColumn(LangTableMap::UPDATED_AT);
|
||||
} else {
|
||||
@@ -343,6 +350,7 @@ class LangTableMap extends TableMap
|
||||
$criteria->addSelectColumn($alias . '.LOCALE');
|
||||
$criteria->addSelectColumn($alias . '.URL');
|
||||
$criteria->addSelectColumn($alias . '.BY_DEFAULT');
|
||||
$criteria->addSelectColumn($alias . '.POSITION');
|
||||
$criteria->addSelectColumn($alias . '.CREATED_AT');
|
||||
$criteria->addSelectColumn($alias . '.UPDATED_AT');
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ class URL
|
||||
*/
|
||||
public static function absoluteUrl($path, array $parameters = array(), $path_only = false)
|
||||
{
|
||||
// Already absolute ?
|
||||
// Already absolute ?
|
||||
if (substr($path, 0, 4) != 'http') {
|
||||
|
||||
$root = $path_only ? ConfigQuery::read('base_url', '/') : self::getIndexPage();
|
||||
@@ -57,7 +57,7 @@ class URL
|
||||
$queryString = '';
|
||||
|
||||
foreach($parameters as $name => $value) {
|
||||
$queryString = sprintf("%s=%s&", urlencode($name), urlencode($value));
|
||||
$queryString .= sprintf("%s=%s&", urlencode($name), urlencode($value));
|
||||
}
|
||||
|
||||
$sepChar = strstr($base, '?') === false ? '?' : '&';
|
||||
@@ -77,7 +77,7 @@ class URL
|
||||
*/
|
||||
public static function adminViewUrl($viewName, array $parameters = array()) {
|
||||
|
||||
$path = sprintf("%s/admin/%s", self::getIndexPage(), $viewName); // FIXME ! view= should not be necessaray, check routing parameters
|
||||
$path = sprintf("%s/admin/%s", self::getIndexPage(), $viewName); // FIXME ! view= should not be required, check routing parameters
|
||||
|
||||
return self::absoluteUrl($path, $parameters);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
INSERT INTO `lang`(`id`,`title`,`code`,`locale`,`url`,`by_default`,`created_at`,`updated_at`)VALUES
|
||||
(1, 'Français', 'fr', 'fr_FR', '','1', NOW(), NOW()),
|
||||
(2, 'English', 'en', 'en_EN', '', '0', NOW(), NOW()),
|
||||
(3, 'Espanol', 'es', 'es_ES', '', '0', NOW(), NOW()),
|
||||
(4, 'Italiano', 'it', 'it_IT', '','0', NOW(), NOW());
|
||||
INSERT INTO `lang`(`id`,`title`,`code`,`locale`,`url`,`by_default`,`position`,`created_at`,`updated_at`)VALUES
|
||||
(1, 'Français', 'fr', 'fr_FR', '','1', '1', NOW(), NOW()),
|
||||
(2, 'English', 'en', 'en_EN', '', '0', '2', NOW(), NOW()),
|
||||
(3, 'Espanol', 'es', 'es_ES', '', '0', '3', NOW(), NOW()),
|
||||
(4, 'Italiano', 'it', 'it_IT', '','0', '4', NOW(), NOW());
|
||||
|
||||
INSERT INTO `config` (`name`, `value`, `secured`, `hidden`, `created_at`, `updated_at`) VALUES
|
||||
('session_config.default', '1', 1, 1, NOW(), NOW()),
|
||||
@@ -23,11 +23,11 @@ INSERT INTO `customer_title_i18n` (`id`, `locale`, `short`, `long`) VALUES
|
||||
(3, 'en_US', 'Miss', 'Miss'),
|
||||
(3, 'fr_FR', 'Mlle', 'Madamemoiselle');
|
||||
|
||||
INSERT INTO `currency` (`id` ,`code` ,`symbol` ,`rate` ,`by_default` ,`created_at` ,`updated_at`)
|
||||
INSERT INTO `currency` (`id` ,`code` ,`symbol` ,`rate` ,`by_default`, `position` ,`created_at` ,`updated_at`)
|
||||
VALUES
|
||||
(1, 'EUR', '€', '1', '1', NOW() , NOW()),
|
||||
(2, 'USD', '$', '1.26', '0', NOW(), NOW()),
|
||||
(3, 'GBP', '£', '0.89', '0', NOW(), NOW());
|
||||
(1, 'EUR', '€', '1', '1', '1', NOW() , NOW()),
|
||||
(2, 'USD', '$', '1.26', '0', '2', NOW(), NOW()),
|
||||
(3, 'GBP', '£', '0.89', '0', '3', NOW(), NOW());
|
||||
|
||||
INSERT INTO `currency_i18n` (`id` ,`locale` ,`name`)
|
||||
VALUES
|
||||
|
||||
@@ -522,6 +522,7 @@ CREATE TABLE `lang`
|
||||
`locale` VARCHAR(45),
|
||||
`url` VARCHAR(255),
|
||||
`by_default` TINYINT,
|
||||
`position` INTEGER,
|
||||
`created_at` DATETIME,
|
||||
`updated_at` DATETIME,
|
||||
PRIMARY KEY (`id`)
|
||||
@@ -763,6 +764,7 @@ CREATE TABLE `currency`
|
||||
`symbol` VARCHAR(45),
|
||||
`rate` FLOAT,
|
||||
`by_default` TINYINT,
|
||||
`position` INTEGER,
|
||||
`created_at` DATETIME,
|
||||
`updated_at` DATETIME,
|
||||
PRIMARY KEY (`id`)
|
||||
|
||||
@@ -589,7 +589,8 @@
|
||||
<column name="code" size="45" type="VARCHAR" />
|
||||
<column name="symbol" size="45" type="VARCHAR" />
|
||||
<column name="rate" type="FLOAT" />
|
||||
<column name="by_default" type="TINYINT" />
|
||||
<column name="by_default" type="TINYINT" />
|
||||
<column name="position" type="INTEGER" />
|
||||
<behavior name="timestampable" />
|
||||
<behavior name="i18n">
|
||||
<parameter name="i18n_columns" value="name" />
|
||||
|
||||
@@ -124,7 +124,7 @@ hr {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.modal-footer {
|
||||
.footer {
|
||||
background: none repeat scroll 0 0 transparent;
|
||||
border: medium none;
|
||||
box-shadow: none;
|
||||
@@ -375,6 +375,7 @@ hr {
|
||||
padding: 10px 20px;
|
||||
}
|
||||
}
|
||||
|
||||
// -- Login form --------------------------------------------------------------
|
||||
|
||||
.form-signin {
|
||||
@@ -394,157 +395,157 @@ textarea:focus, input[type="text"]:focus, input[type="password"]:focus, input[ty
|
||||
|
||||
// -- Allow inline forms validation states ------------------------------------
|
||||
|
||||
.form-inline .warning .control-label,
|
||||
.form-inline .warning .help-block,
|
||||
.form-inline .warning .help-inline {
|
||||
form .warning .control-label,
|
||||
form .warning .help-block,
|
||||
form .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 {
|
||||
form .warning .checkbox,
|
||||
form .warning .radio,
|
||||
form .warning input,
|
||||
form .warning select,
|
||||
form .warning textarea {
|
||||
color: #c09853;
|
||||
}
|
||||
|
||||
.form-inline .warning input,
|
||||
.form-inline .warning select,
|
||||
.form-inline .warning textarea {
|
||||
form .warning input,
|
||||
form .warning select,
|
||||
form .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 {
|
||||
form .warning input:focus,
|
||||
form .warning select:focus,
|
||||
form .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 {
|
||||
form .warning .input-prepend .add-on,
|
||||
form .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 {
|
||||
form .error .control-label,
|
||||
form .error .help-block,
|
||||
form .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 {
|
||||
form .error .checkbox,
|
||||
form .error .radio,
|
||||
form .error input,
|
||||
form .error select,
|
||||
form .error textarea {
|
||||
color: #b94a48;
|
||||
}
|
||||
|
||||
.form-inline .error input,
|
||||
.form-inline .error select,
|
||||
.form-inline .error textarea {
|
||||
form .error input,
|
||||
form .error select,
|
||||
form .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 {
|
||||
form .error input:focus,
|
||||
form .error select:focus,
|
||||
form .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 {
|
||||
form .error .input-prepend .add-on,
|
||||
form .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 {
|
||||
form .success .control-label,
|
||||
form .success .help-block,
|
||||
form .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 {
|
||||
form .success .checkbox,
|
||||
form .success .radio,
|
||||
form .success input,
|
||||
form .success select,
|
||||
form .success textarea {
|
||||
color: #468847;
|
||||
}
|
||||
|
||||
.form-inline .success input,
|
||||
.form-inline .success select,
|
||||
.form-inline .success textarea {
|
||||
form .success input,
|
||||
form .success select,
|
||||
form .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 {
|
||||
form .success input:focus,
|
||||
form .success select:focus,
|
||||
form .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 {
|
||||
form .success .input-prepend .add-on,
|
||||
form .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 {
|
||||
form .info .control-label,
|
||||
form .info .help-block,
|
||||
form .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 {
|
||||
form .info .checkbox,
|
||||
form .info .radio,
|
||||
form .info input,
|
||||
form .info select,
|
||||
form .info textarea {
|
||||
color: #3a87ad;
|
||||
}
|
||||
|
||||
.form-inline .info input,
|
||||
.form-inline .info select,
|
||||
.form-inline .info textarea {
|
||||
form .info input,
|
||||
form .info select,
|
||||
form .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 {
|
||||
form .info input:focus,
|
||||
form .info select:focus,
|
||||
form .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 {
|
||||
form .info .input-prepend .add-on,
|
||||
form .info .input-append .add-on {
|
||||
color: #3a87ad;
|
||||
background-color: #d9edf7;
|
||||
border-color: #3a87ad;
|
||||
@@ -560,6 +561,7 @@ textarea:focus, input[type="text"]:focus, input[type="password"]:focus, input[ty
|
||||
padding: 1em;
|
||||
margin-bottom: 20px;
|
||||
|
||||
// The block title
|
||||
.title {
|
||||
color: #5A6876;
|
||||
text-transform: uppercase;
|
||||
@@ -569,11 +571,53 @@ textarea:focus, input[type="text"]:focus, input[type="password"]:focus, input[ty
|
||||
line-height: 30px;
|
||||
}
|
||||
|
||||
// The action bar on the right
|
||||
.actions {
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
|
||||
// Reduce bottom margin of admin tabs.
|
||||
.admin-tabs {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
// The overall form container
|
||||
.form-container {
|
||||
|
||||
// The inner toolbar (flags & save buttons)
|
||||
.inner-toolbar {
|
||||
|
||||
line-height: 30px;
|
||||
margin-bottom: 1em;
|
||||
|
||||
.inner-actions {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.nav-pills {
|
||||
margin-bottom: 0;
|
||||
|
||||
li a {
|
||||
padding: 4px;
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
li.active a {
|
||||
opacity: 1;
|
||||
background-color: #E7E7E7;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Modal dialog tweaking ------------------------------------------------------
|
||||
|
||||
.modal {
|
||||
form {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
// -- Admin forms tweaking ----------------------------------------------------
|
||||
|
||||
label {
|
||||
@@ -584,6 +628,10 @@ label {
|
||||
}
|
||||
}
|
||||
|
||||
.input-append.input-block-level .add-on img {
|
||||
max-height: 18px;
|
||||
}
|
||||
|
||||
// Information in field label
|
||||
.label-help-block, .help-block {
|
||||
color: lighten(#595959, 20);
|
||||
|
||||
BIN
templates/admin/default/assets/img/flags/en.gif
Normal file
BIN
templates/admin/default/assets/img/flags/en.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.2 KiB |
BIN
templates/admin/default/assets/img/flags/es.gif
Normal file
BIN
templates/admin/default/assets/img/flags/es.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.2 KiB |
BIN
templates/admin/default/assets/img/flags/fr.gif
Normal file
BIN
templates/admin/default/assets/img/flags/fr.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.2 KiB |
BIN
templates/admin/default/assets/img/flags/it.gif
Normal file
BIN
templates/admin/default/assets/img/flags/it.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.2 KiB |
@@ -16,10 +16,10 @@
|
||||
<div class="row-fluid">
|
||||
<div class="span12">
|
||||
<div class="general-block-decorator">
|
||||
<table class="table table-striped">
|
||||
<table class="table table-striped table-condensed">
|
||||
<caption>
|
||||
{* display parent category name, and get current cat ID *}
|
||||
{loop name="category_title" type="category" id="{$current_category_id}"}
|
||||
{loop name="category_title" type="category" visible="*" id="{$current_category_id}"}
|
||||
{intl l="Categories in %cat" cat=$TITLE}
|
||||
{$cat_id = $ID}
|
||||
{/loop}
|
||||
@@ -29,9 +29,11 @@
|
||||
|
||||
{module_include location='category_list_caption'}
|
||||
|
||||
<a class="btn btn-primary btn-add-item" title="{intl l='Add a new category'}" href="{url path="admin/catalog/category/create/{$cat_id|default:0}"}">
|
||||
{loop type="auth" name="can_create" context="admin" roles="ADMIN" permissions="admin.category.create"}
|
||||
<a class="btn btn-primary btn-add-item" title="{intl l='Add a new category'}" href="#add_category_dialog" data-toggle="modal">
|
||||
<i class="icon-plus-sign icon-white"></i>
|
||||
</a>
|
||||
{/loop}
|
||||
</caption>
|
||||
|
||||
{ifloop rel="category_list"}
|
||||
@@ -48,9 +50,9 @@
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
{loop name="category_list" type="category" parent="{$current_category_id}" order="manual"}
|
||||
{loop name="category_list" type="category" visible="*" parent="{$current_category_id}" order="manual"}
|
||||
<tr>
|
||||
<td class="object-title"><a href="{url path="admin/catalog/category/browse/$ID"}" title="{intl l='Browse this category'}">{$TITLE}</a></td>
|
||||
<td class="object-title"><a href="{url path='admin/catalog/category' id="$ID" action='browse'}" title="{intl l='Browse this category'}">{$TITLE}</a></td>
|
||||
|
||||
{module_include location='category_list_row'}
|
||||
|
||||
@@ -59,15 +61,27 @@
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<a href="{url path='admin/catalog/category/positionUp/{$ID}'}"><i class="icon-arrow-up"></i></a>
|
||||
<span class="object_classement_editable" data-action="changeCategoryPosition" data-name="category_id" data-id="{$ID}">{$POSITION}</span>
|
||||
<a href="{url path='admin/catalog/category/positionDown/{$ID}'}"><i class="icon-arrow-down"></i></a>
|
||||
{loop type="auth" name="can_change" context="admin" roles="ADMIN" permissions="admin.category.edit"}
|
||||
<a href="{url path='admin/catalog/category' id="{$ID}" action='positionUp'}"><i class="icon-arrow-up"></i></a>
|
||||
<span class="object_classement_editable" data-action="changeCategoryPosition" data-name="category_id" data-id="{$ID}">{$POSITION}</span>
|
||||
<a href="{url path='admin/catalog/category' id="{$ID}" action='positionDown'}"><i class="icon-arrow-down"></i></a>
|
||||
{/loop}
|
||||
|
||||
{elseloop rel="can_change"}
|
||||
{$POSITION}
|
||||
{/elseloop}
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<a class="btn btn-mini" title="{intl l='Browse this category'}" href="{url path="admin/catalog/category/browse/{$ID}"}"><i class="icon-folder-open"></i></a>
|
||||
<a class="btn btn-mini" title="{intl l='Edit this category'}" href="{url path="admin/catalog/category/edit/{$ID}"}"><i class="icon-edit"></i></a>
|
||||
<a class="btn btn-mini category-delete" title="{intl l='Delete this category and all its contents'}" href="{url path='admin/catalog/category/delete/{$ID}'}"><i class="icon-trash"></i></a>
|
||||
<a class="btn btn-mini" title="{intl l='Browse this category'}" href="{url path='admin/catalog/category' id="$ID" action='browse'}"><i class="icon-folder-open"></i></a>
|
||||
|
||||
{loop type="auth" name="can_change" context="admin" roles="ADMIN" permissions="admin.category.edit"}
|
||||
<a class="btn btn-mini" title="{intl l='Edit this category'}" href="{url path='admin/catalog/category' id="$ID" action='edit'}"><i class="icon-edit"></i></a>
|
||||
{/loop}
|
||||
|
||||
{loop type="auth" name="can_delete" context="admin" roles="ADMIN" permissions="admin.category.delete"}
|
||||
<a class="btn btn-mini category-delete" title="{intl l='Delete this category and all its contents'}" href="#delete_category_dialog" data-id="{$ID}" data-toggle="modal"><i class="icon-trash"></i></a>
|
||||
{/loop}
|
||||
</td>
|
||||
</tr>
|
||||
{/loop}
|
||||
@@ -77,7 +91,17 @@
|
||||
{elseloop rel="category_list"}
|
||||
<thead>
|
||||
<tr>
|
||||
<td class="message"><div class="alert alert-info">{intl l="This category has no sub-categories. To create a new one, click the + button above."}</div></td>
|
||||
<td class="message">
|
||||
<div class="alert alert-info">
|
||||
{loop type="auth" name="can_create" context="admin" roles="ADMIN" permissions="admin.category.create"}
|
||||
{intl l="This category has no sub-categories. To create a new one, click the + button above."}
|
||||
{/loop}
|
||||
|
||||
{elseloop rel="can_create"}
|
||||
{intl l="This category has no sub-categories."}
|
||||
{/elseloop}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</thead>
|
||||
{/elseloop}
|
||||
@@ -89,10 +113,10 @@
|
||||
<div class="row-fluid">
|
||||
<div class="span12">
|
||||
<div class="general-block-decorator">
|
||||
<table class="table table-striped">
|
||||
<table class="table table-striped table-condensed">
|
||||
<caption>
|
||||
{* display parent category name *}
|
||||
{loop name="category_title" type="category" id="{$current_category_id}"}
|
||||
{loop name="category_title" type="category" visible="*" id="{$current_category_id}"}
|
||||
{intl l="Products in %cat" cat=$TITLE}
|
||||
{/loop}
|
||||
{elseloop rel="category_title"}
|
||||
@@ -124,9 +148,9 @@
|
||||
<tbody>
|
||||
{loop name="product_list" type="product" category="{$current_category_id}" order="manual"}
|
||||
<tr>
|
||||
<td><a href="{url path="admin/catalog/product/edit/$ID"}" title="{intl l='Edit this product'}">Image !</a></td>
|
||||
<td><a href="{url path='admin/catalog/product' id="$ID" action='edit'}" title="{intl l='Edit this product'}">Image !</a></td>
|
||||
|
||||
<td class="object-title"><a href="{url path="admin/catalog/product/edit/$ID"}" title="{intl l='Edit this product'}">{$TITLE}</a></td>
|
||||
<td class="object-title"><a href="{url path='admin/catalog/product' id="$ID" action='edit'}" title="{intl l='Edit this product'}">{$TITLE}</a></td>
|
||||
|
||||
{module_include location='product_list_row'}
|
||||
|
||||
@@ -135,14 +159,14 @@
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<a href="{url path='admin/catalog/product/positionUp/{$ID}'}"><i class="icon-arrow-up"></i></a>
|
||||
<a href="{url path='admin/catalog/product' id="$ID" action='positionUn'}"><i class="icon-arrow-up"></i></a>
|
||||
<span class="object_classement_editable" data-action="changeProductPosition" data-name="product_id" data-id="{$ID}">{$POSITION}</span>
|
||||
<a href="{url path='admin/catalog/product/positionDown/{$ID}'}"><i class="icon-arrow-down"></i></a>
|
||||
<a href="{url path='admin/catalog/product' id="$ID" action='positionDown'}"><i class="icon-arrow-down"></i></a>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<a class="btn btn-mini" title="{intl l='Edit this category'}" href="{url path="admin/catalog/product/edit/{$ID}"}"><i class="icon-edit"></i></a>
|
||||
<a class="btn btn-mini product-delete" title="{intl l='Delete this product'}" href="{url path='admin/catalog/product/delete/{$ID}'}"><i class="icon-trash"></i></a>
|
||||
<a class="btn btn-mini" title="{intl l='Edit this category'}" href="{url path='admin/catalog/product' id="$ID" action='edit'}"><i class="icon-edit"></i></a>
|
||||
<a class="btn btn-mini product-delete" title="{intl l='Delete this product'}" href="{url path='admin/catalog/product' id="$ID" action='delete'}"><i class="icon-trash"></i></a>
|
||||
</td>
|
||||
</tr>
|
||||
{/loop}
|
||||
@@ -165,6 +189,40 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{include file="includes/add-category-dialog.html"}
|
||||
{include file="includes/delete-category-dialog.html"}
|
||||
|
||||
{include file='includes/js.inc.html'}
|
||||
|
||||
<script>
|
||||
$(function() {
|
||||
|
||||
{* display the form creation dialog if it contains errors *}
|
||||
|
||||
{form name="thelia.admin.category.creation"}
|
||||
{if #form_error}
|
||||
$('#add_category_dialog').modal();
|
||||
{/if}
|
||||
{/form}
|
||||
|
||||
{* Always reset dialog on close *}
|
||||
$('#add_category_dialog').on('hidden',function() {
|
||||
// Hide error message
|
||||
$('#add_category_dialog_error').remove();
|
||||
|
||||
// Clear error status
|
||||
$("#add_category_dialog .error").removeClass('error');
|
||||
|
||||
// Empty field values
|
||||
$("#add_category_dialog input[type=text]").val('');
|
||||
});
|
||||
|
||||
{* Set the proper category ID in the delete confirmation dialog *}
|
||||
$(document).on("click", ".category-delete", function () {
|
||||
$('#'+'delete-category-id').val($(this).data('id'));
|
||||
});
|
||||
|
||||
})
|
||||
</script>
|
||||
|
||||
{include file='includes/footer.inc.html'}
|
||||
@@ -1,10 +1,6 @@
|
||||
{check_auth context="admin" roles="ADMIN" permissions="admin.catalog.view" login_tpl="/admin/login"}
|
||||
|
||||
{if $action == 'create'}
|
||||
{$page_title={intl l='Create a new category'}}
|
||||
{else}
|
||||
{$page_title={intl l='Edit category'}}
|
||||
{/if}
|
||||
{$page_title={intl l='Edit category'}}
|
||||
|
||||
{include file='includes/header.inc.html'}
|
||||
|
||||
@@ -19,29 +15,22 @@
|
||||
<div class="span12 general-block-decorator">
|
||||
<div class="row-fluid">
|
||||
<div class="span7 title">
|
||||
{if $action == 'create'}
|
||||
{intl l='Create a new category'}
|
||||
{else}
|
||||
{intl l='Edit category'}
|
||||
{/if}
|
||||
{intl l='Edit category'}
|
||||
</div>
|
||||
|
||||
<div class="span5 actions">
|
||||
<button class="btn btn-primary" title="{intl l='Save and stay on this page'}">{intl l='Save'} <i class="icon icon-white icon-ok"></i></button>
|
||||
<button class="btn btn-info" title="{intl l='Save and close'}">{intl l='Save'} <i class="icon icon-remove icon-white"></i></button>
|
||||
{if $action != 'create'}
|
||||
<button class="btn" title="{intl l='Edit previous category'}"><i class="icon icon-arrow-left"></i></button>
|
||||
<button class="btn" title="{intl l='Preview category page'}"><i class="icon icon-eye-open"></i></button>
|
||||
<button class="btn" title="{intl l='Edit next category'}"><i class="icon icon-arrow-right"></i></button>
|
||||
{/if}
|
||||
<button class="btn" title="{intl l='Edit previous category'}"><i class="icon icon-arrow-left"></i></button>
|
||||
<button class="btn" title="{intl l='Preview category page'}"><i class="icon icon-eye-open"></i></button>
|
||||
<button class="btn" title="{intl l='Edit next category'}"><i class="icon icon-arrow-right"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tabbable">
|
||||
<ul class="nav nav-tabs" id="tabbed_menu">
|
||||
<ul class="nav nav-tabs admin-tabs" id="tabbed_menu">
|
||||
<li class="active">
|
||||
<a href="#general_description">{intl l="General description"}</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a href="#details">{intl l="Details"}</a>
|
||||
</li>
|
||||
@@ -57,9 +46,13 @@
|
||||
</ul>
|
||||
|
||||
<div class="tab-content">
|
||||
<div class="tab-pane active" id="general_description">
|
||||
|
||||
<div class="tab-pane active form-container" id="general_description">
|
||||
<form class="form-horizontal span12">
|
||||
<fieldset>
|
||||
|
||||
{include file="includes/inner-form-toolbar.html"}
|
||||
|
||||
<div class="row-fluid">
|
||||
<div class="span12">
|
||||
<div class="control-group">
|
||||
@@ -133,7 +126,12 @@
|
||||
|
||||
<div class="controls">
|
||||
<select required="required" name="parent" class="input-block-level">
|
||||
<option value="">{intl l="Choose a category"}</option>
|
||||
<option value="0">{intl l="Top level"}</option>
|
||||
|
||||
{loop name="cat-parent" type="category-tree" visible="*" category="0" exclude="{$current_category_id}"}
|
||||
<option value="#ID" style="padding-left: {3 + $LEVEL * 20}px" {if $parent_category_id == $ID}selected="selected"{/if}>{$TITLE}</option>
|
||||
{/loop}
|
||||
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
@@ -160,19 +158,22 @@
|
||||
</div>
|
||||
|
||||
<div class="tab-pane" id="details">
|
||||
<p>Infos générales</p>
|
||||
<p>Détails divers</p>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane" id="images">
|
||||
<p>Images</p>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane" id="documents">
|
||||
<p>Documents</p>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane" id="modules">
|
||||
<p>Modules</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
10
templates/admin/default/general_error.html
Normal file
10
templates/admin/default/general_error.html
Normal file
@@ -0,0 +1,10 @@
|
||||
{$page_title={intl l='AN error occured'}}
|
||||
|
||||
{include file='includes/header.inc.html'}
|
||||
|
||||
<div id="wrapper" class="container">
|
||||
<h1>{intl l="Oops! An Error Occurred"}</h1>
|
||||
<p>{$error_message}</p>
|
||||
</div>
|
||||
|
||||
{include file='includes/footer.inc.html'}
|
||||
66
templates/admin/default/includes/add-category-dialog.html
Normal file
66
templates/admin/default/includes/add-category-dialog.html
Normal file
@@ -0,0 +1,66 @@
|
||||
|
||||
{* Adding a new Category *}
|
||||
|
||||
<div class="modal hide fade" id="add_category_dialog" tabindex="-1" role="dialog" aria-hidden="true">
|
||||
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h3>{intl l="Create a new category"}</h3>
|
||||
</div>
|
||||
|
||||
{form name="thelia.admin.category.creation"}
|
||||
<form method="POST" action="{url path='/admin/catalog/category'}" {form_enctype form=$form}>
|
||||
|
||||
{* the action processed by the controller *}
|
||||
<input type="hidden" name="action" value="create" />
|
||||
|
||||
{form_hidden_fields form=$form}
|
||||
|
||||
{form_field form=$form field='parent'}
|
||||
<input type="hidden" name="{$name}" value="{$current_category_id}" />
|
||||
{/form_field}
|
||||
|
||||
{form_field form=$form field='success_url'}
|
||||
{* on success, redirect to category change page. _ID_ is replaced with the ID of the created category (see Thelia\Action\Category.php) *}
|
||||
<input type="hidden" name="{$name}" value="{url path='admin/catalog/category' id="_ID_" action='edit'}" />
|
||||
{/form_field}
|
||||
|
||||
<div class="modal-body">
|
||||
|
||||
{if #form_error}<div class="alert alert-block alert-error" id="add_category_dialog_error">#form_error_message</div>{/if}
|
||||
|
||||
<div class="control-group">
|
||||
<label class="control-label">
|
||||
{intl l='Category Title *'}
|
||||
</label>
|
||||
|
||||
<div class="controls">
|
||||
{loop type="lang" name="default-lang" default_only="1"}
|
||||
{form_field form=$form field='locale'}
|
||||
<input type="hidden" name="{$name}" value="{$LOCALE}" />
|
||||
{/form_field}
|
||||
|
||||
<div class="input-append input-block-level">
|
||||
{form_field form=$form field='title'}
|
||||
<span {if $error}class="error"{/if}>
|
||||
<input type="text" required="required" name="{$name}" value="{$value}" title="{intl l='Category title'}" placeholder="{intl l='Category title'}" class="input-block-level">
|
||||
</span>
|
||||
{/form_field}
|
||||
<span class="add-on"><img src="{image file="../assets/img/flags/{$CODE}.gif"}" alt="{intl l=$TITLE}" /></span>
|
||||
</div>
|
||||
|
||||
<div class="help-block">{intl l="Enter here the category title in the default language ($TITLE)"}</div>
|
||||
{/loop}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn" data-dismiss="modal" aria-hidden="true">{intl l="Cancel"}</button>
|
||||
<button type="submit" class="btn btn-primary">{intl l="Create this category"}</button>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
{/form}
|
||||
</div>
|
||||
@@ -1,16 +1,18 @@
|
||||
{* Breadcrumb for categories browsing and editing *}
|
||||
|
||||
<li><a href="{url path='admin/home'}">Home</a> <span class="divider">/</span></li>
|
||||
<li><a href="{url path='admin/catalog'}">Catalog</a>
|
||||
|
||||
{ifloop rel="category_path"}
|
||||
<span class="divider">/</span></li>
|
||||
|
||||
{loop name="category_path" type="category-path" category="{$current_category_id}"}
|
||||
{loop name="category_path" type="category-path" visible="*" category="{$current_category_id}"}
|
||||
{if $ID == $current_category_id}
|
||||
<li class="active">
|
||||
{if $action == 'edit'}
|
||||
{intl l='Editing %cat' cat="{$TITLE}"}
|
||||
{else}
|
||||
{$TITLE} <a href="{url path="admin/catalog/category/edit/$ID"}" title="{intl l='Edit this category'}">{intl l="(edit)"}</a>
|
||||
{$TITLE} <a href="{url path="admin/catalog/category/edit/$ID"}" title="{intl l='Edit this category'}">{intl l="(edit)"}</a>
|
||||
{/if}
|
||||
</li>
|
||||
{else}
|
||||
@@ -18,11 +20,7 @@
|
||||
{/if}
|
||||
{/loop}
|
||||
{/ifloop}
|
||||
|
||||
{elseloop rel="category_path"}
|
||||
{if $action == 'create'}<span class="divider">/</span>{/if}</li>
|
||||
{/elseloop}
|
||||
|
||||
{if $action == 'create'}
|
||||
<li class="active">{intl l='Create a new category'}</li>
|
||||
{/if}
|
||||
|
||||
</li>
|
||||
{/elseloop}
|
||||
42
templates/admin/default/includes/delete-category-dialog.html
Normal file
42
templates/admin/default/includes/delete-category-dialog.html
Normal file
@@ -0,0 +1,42 @@
|
||||
|
||||
{* Adding a new Category *}
|
||||
|
||||
<div class="modal hide fade" id="delete_category_dialog" tabindex="-1" role="dialog" aria-hidden="true">
|
||||
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h3>{intl l="Delete a category"}</h3>
|
||||
</div>
|
||||
|
||||
{form name="thelia.admin.category.deletion"}
|
||||
<form method="POST" action="{url path='/admin/catalog/category'}" {form_enctype form=$form}>
|
||||
|
||||
{* the action processed by the controller *}
|
||||
<input type="hidden" name="action" value="delete" />
|
||||
|
||||
{form_hidden_fields form=$form}
|
||||
|
||||
{form_field form=$form field='id'}
|
||||
<input type="hidden" name="{$name}" id="delete-category-id" value="" />
|
||||
{/form_field}
|
||||
|
||||
{form_field form=$form field='success_url'}
|
||||
{* on success, redirect to catalog. _ID_ is replaced with the ID of the deleted category parent id (see Thelia\Action\Category.php) *}
|
||||
<input type="hidden" name="{$name}" value="{url path='admin/catalog/category' id="_ID_" action='browse'}" />
|
||||
{/form_field}
|
||||
|
||||
<div class="modal-body">
|
||||
|
||||
{if #form_error}<div class="alert alert-block alert-error" id="add_category_dialog_error">#form_error_message</div>{/if}
|
||||
|
||||
<p>{intl l="Delete this category and all its contents ?"}</p>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn" data-dismiss="modal" aria-hidden="true">{intl l="No"}</button>
|
||||
<button type="submit" class="btn btn-primary">{intl l="Yes"}</button>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
{/form}
|
||||
</div>
|
||||
@@ -1,7 +1,7 @@
|
||||
{module_include location='before_footer'}
|
||||
|
||||
<hr />
|
||||
<footer class="modal-footer">
|
||||
<footer class="footer">
|
||||
<div class="container">
|
||||
<p>{intl l='© Thelia 2013'}
|
||||
- <a href="http://www.openstudio.fr/" target="_blank">{intl l='Édité par OpenStudio'}</a>
|
||||
|
||||
27
templates/admin/default/includes/inner-form-toolbar.html
Normal file
27
templates/admin/default/includes/inner-form-toolbar.html
Normal file
@@ -0,0 +1,27 @@
|
||||
<div class="row-fluid inner-toolbar">
|
||||
<div class="span6 inner-actions">
|
||||
{* Display the top form toolbar, with edition flags, and save buttons *}
|
||||
|
||||
{* When creating a new object, only default language is displayed *}
|
||||
{if $action == 'create'}
|
||||
{$default_only = 1}
|
||||
{else}
|
||||
{$default_only = 0}
|
||||
{/if}
|
||||
|
||||
<ul class="nav nav-pills">
|
||||
{loop name="lang_list" type="lang" default_only={$default_only}}
|
||||
<li {if $IS_DEFAULT}class="active"{/if}>
|
||||
<a href="#" title="{intl l="Edit information for %lng" lng=$TITLE}">
|
||||
<img src="{image file="../assets/img/flags/{$CODE}.gif"}" alt="{intl l=$TITLE}" />
|
||||
</a>
|
||||
</li>
|
||||
{/loop}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="span6 inner-actions">
|
||||
<button class="btn btn-primary" title="{intl l='Save'}">{intl l='Save'} <i class="icon icon-white icon-ok"></i></button>
|
||||
<button class="btn btn-info" title="{intl l='Save and close'}">{intl l='Save and close'} <i class="icon icon-remove icon-white"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -13,7 +13,7 @@
|
||||
<div class="hero-unit">
|
||||
<h1>{intl l='Thelia Back Office'}</h1>
|
||||
|
||||
{form name="thelia.admin.login" success_url="home" error_url="login"}
|
||||
{form name="thelia.admin.login"}
|
||||
<form action="{url path='/admin/checklogin'}" method="post" class="well form-inline" {form_enctype form=$form}>
|
||||
|
||||
{if #form_error}<div class="alert alert-error">#form_error_message</div>{/if}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
{* We use $INDEX_PAGE as form action to avoid mixing post and get data *}
|
||||
<form action="{$INDEX_PAGE}" method="post" {form_enctype form=$form}>
|
||||
{*
|
||||
The two fields below are not par of the Login form, they are here to defines
|
||||
The two fields below are not par of the form, they are here to defines
|
||||
the action to process, and the view to render once the form is submited
|
||||
*}
|
||||
<input type="hidden" name="action" value="createCustomer" /> {* the action triggered by this form *}
|
||||
|
||||
Reference in New Issue
Block a user