Categories refactoring

This commit is contained in:
franck
2013-09-06 15:56:06 +02:00
parent 7b3cd46261
commit 6fdf60b960
18 changed files with 634 additions and 423 deletions

View File

@@ -79,7 +79,6 @@
<service id="thelia.url.manager" class="Thelia\Tools\URL">
<argument type="service" id="service_container" />
<argument >%kernel.environment%</argument>
</service>
<service id="event_dispatcher" class="Symfony\Component\EventDispatcher\ContainerAwareEventDispatcher">
@@ -98,7 +97,7 @@
<!-- Translation and internationalisation -->
<service id="thelia.translator" class="Thelia\Core\Translation\Translator">
<argument type="string" id="en_US"></argument>
<argument type="string" id="en_UK"></argument>
</service>
<!-- Security context for front and back office -->

View File

@@ -25,14 +25,36 @@
</route>
<!-- Route to the catalog controller (process category browsing) -->
<!-- Route to the catalog controller -->
<route id="admin.catalog" path="/admin/catalog">
<default key="_controller">Thelia\Controller\Admin\CategoryController::indexAction</default>
<default key="_controller">Thelia\Controller\Admin\CategoryController::defaultAction</default>
</route>
<route id="admin.category" path="/admin/catalog/category">
<default key="_controller">Thelia\Controller\Admin\CategoryController::processAction</default>
<!-- Categories management -->
<route id="admin.configuration.categories.create" path="/admin/categories/create">
<default key="_controller">Thelia\Controller\Admin\CategoryController::createAction</default>
</route>
<route id="admin.configuration.categories.update" path="/admin/categories/update">
<default key="_controller">Thelia\Controller\Admin\CategoryController::changeAction</default>
</route>
<route id="admin.configuration.categories.save" path="/admin/categories/save">
<default key="_controller">Thelia\Controller\Admin\CategoryController::saveChangeAction</default>
</route>
<route id="admin.configuration.categories.set-default" path="/admin/categories/toggle-online">
<default key="_controller">Thelia\Controller\Admin\CategoryController::toggleOnlineAction</default>
</route>
<route id="admin.configuration.categories.delete" path="/admin/categories/delete">
<default key="_controller">Thelia\Controller\Admin\CategoryController::deleteAction</default>
</route>
<route id="admin.configuration.categories.update-position" path="/admin/categories/update-position">
<default key="_controller">Thelia\Controller\Admin\CategoryController::updatePositionAction</default>
</route>
<!-- Routes to the Config (system variables) controller -->

View File

@@ -34,6 +34,8 @@ use Thelia\Core\Security\SecurityContext;
use Thelia\Model\AdminLog;
use Thelia\Model\Lang;
use Thelia\Model\LangQuery;
use Thelia\Form\BaseForm;
use Thelia\Form\Exception\FormValidationException;
class BaseAdminController extends BaseController
{
@@ -126,6 +128,55 @@ class BaseAdminController extends BaseController
return $response->setContent($this->errorPage("Sorry, you're not allowed to perform this action"));
}
/*
* Create the standard message displayed to the user when the form cannot be validated.
*/
protected function createStandardFormValidationErrorMessage(FormValidationException $exception) {
return Translator::getInstance()->trans(
"Please check your input: %error",
array(
'%error' => $exception->getMessage()
)
);
}
/**
* Setup the error context when an error occurs in a action method.
*
* @param string $action the action that caused the error (category modification, variable creation, currency update, etc.)
* @param BaseForm $form the form where the error occured, or null if no form was involved
* @param string $error_message the error message
* @param Exception $exception the exception or null if no exception
*/
protected function setupFormErrorContext($action, $error_message, BaseForm $form = null, \Exception $exception = null) {
if ($error_message !== false) {
// Log the error message
Tlog::getInstance()->error(
Translator::getInstance()->trans(
"Error during %action process : %error. Exception was %exc",
array(
'%action' => $action,
'%error' => $error_message,
'%exc' => $exception != null ? $exception->getMessage() : 'no exception'
)
)
);
if ($fom != null) {
// Mark the form as errored
$form->setErrorMessage($error_message);
// Pass it to the parser context
$this->getParserContext()->addForm($form);
}
// Pass the error message to the parser.
$this->getParserContext()->setGeneralError($error_message);
}
}
/**
* @return a ParserInterface instance parser
*/

View File

@@ -37,223 +37,330 @@ use Thelia\Model\Lang;
class CategoryController extends BaseAdminController
{
protected function createNewCategory($args)
{
try {
$categoryCreationForm = new CategoryCreationForm($this->getRequest());
/**
* Render the categories list, ensuring the sort order is set.
*
* @return Symfony\Component\HttpFoundation\Response the response
*/
protected function renderList() {
$form = $this->validateForm($categoryCreationForm, "POST");
$args = $this->setupArgs();
return $this->render('categories', $args);
}
protected function setupArgs() {
// Get the category ID
$id = $this->getRequest()->get('category_id', 0);
// Find the current category order
$category_order = $this->getRequest()->get(
'order',
$this->getSession()->get('admin.category_order', 'manual')
);
$args = array(
'current_category_id' => $id,
'category_order' => $category_order,
);
// Store the current sort order in session
$this->getSession()->set('admin.category_order', $category_order);
return $args;
}
/**
* The default action is displaying the categories list.
*
* @return Symfony\Component\HttpFoundation\Response the response
*/
public function defaultAction() {
if (null !== $response = $this->checkAuth("admin.categories.view")) return $response;
return $this->renderList();
}
protected function createAction($args)
{
// Check current user authorization
if (null !== $response = $this->checkAuth("admin.categories.create")) return $response;
$error_msg = false;
// Create the Creation Form
$creationForm = new CategoryCreationForm($this->getRequest());
try {
// Validate the form, create the CategoryCreation event and dispatch it.
$form = $this->validateForm($creationForm, "POST");
$data = $form->getData();
$createEvent = new CategoryCreateEvent();
$categoryCreateEvent = new CategoryCreateEvent(
$data["title"],
$data["parent"],
$data["locale"]
);
$this->dispatch(TheliaEvents::CATEGORY_CREATE, $categoryCreateEvent);
$this->dispatch(TheliaEvents::CATEGORY_CREATE, $createEvent);
$category = $categoryCreateEvent->getCreatedCategory();
$createdObject = $createEvent->getCategory();
$this->adminLogAppend(sprintf("Category %s (ID %s) created", $category->getTitle(), $category->getId()));
// Log currency creation
$this->adminLogAppend(sprintf("Category %s (ID %s) created", $createdObject->getName(), $createdObject->getId()));
// Substitute _ID_ in the URL with the ID of the created category
$successUrl = str_replace('_ID_', $category->getId(), $categoryCreationForm->getSuccessUrl());
// Substitute _ID_ in the URL with the ID of the created object
$successUrl = str_replace('_ID_', $createdObject->getId(), $creationForm->getSuccessUrl());
// Redirect to the success URL
$this->redirect($successUrl);
}
catch (FormValidationException $e) {
$categoryCreationForm->setErrorMessage($e->getMessage());
$this->getParserContext()->addForm($categoryCreationForm);
catch (FormValidationException $ex) {
// Form cannot be validated
$error_msg = sprintf("Please check your input: %s", $ex->getMessage());
}
catch (Exception $e) {
Tlog::getInstance()->error(sprintf("Failed to create category: %s", $e->getMessage()));
$this->getParserContext()->setGeneralError($e->getMessage());
catch (\Exception $ex) {
// Any other error
$error_msg = $ex;
}
if ($error_msg !== false) {
// An error has been detected: log it
Tlog::getInstance()->error(sprintf("Error during category creation process : %s. Exception was %s", $error_msg, $ex->getMessage()));
// Mark the form as errored
$creationForm->setErrorMessage($error_msg);
// Pass it to the parser, along with the error currency
$this->getParserContext()
->addForm($creationForm)
->setGeneralError($error_msg)
;
}
// At this point, the form has error, and should be redisplayed.
return $this->render('categories', $args);
return $this->renderList();
}
protected function editCategory($args)
{
if (null !== $response = $this->checkAuth("admin.category.edit")) return $response;
/**
* Load a currency object for modification, and display the edit template.
*
* @return Symfony\Component\HttpFoundation\Response the response
*/
public function changeAction() {
return $this->render('edit_category', $args);
// Check current user authorization
if (null !== $response = $this->checkAuth("admin.categories.update")) return $response;
// Load the currency object
$currency = CategoryQuery::create()
->joinWithI18n($this->getCurrentEditionLocale())
->findOneById($this->getRequest()->get('currency_id'));
if ($currency != null) {
// Prepare the data that will hydrate the form
$data = array(
'id' => $currency->getId(),
'name' => $currency->getName(),
'locale' => $currency->getLocale(),
'code' => $currency->getCode(),
'symbol' => $currency->getSymbol(),
'rate' => $currency->getRate()
);
// Setup the object form
$changeForm = new CategoryModificationForm($this->getRequest(), "form", $data);
// Pass it to the parser
$this->getParserContext()->addForm($changeForm);
}
// Render the edition template.
return $this->render('currency-edit', array('currency_id' => $this->getRequest()->get('currency_id')));
}
protected function deleteCategory($args)
{
/**
* Save changes on a modified currency object, and either go back to the currency list, or stay on the edition page.
*
* @return Symfony\Component\HttpFoundation\Response the response
*/
public function saveChangeAction() {
// Check current user authorization
if (null !== $response = $this->checkAuth("admin.categories.update")) return $response;
$error_msg = false;
// Create the form from the request
$changeForm = new CategoryModificationForm($this->getRequest());
// Get the currency ID
$currency_id = $this->getRequest()->get('currency_id');
try {
$categoryDeletionForm = new CategoryDeletionForm($this->getRequest());
$data = $this->validateForm($categoryDeletionForm, "POST")->getData();
// Check the form against constraints violations
$form = $this->validateForm($changeForm, "POST");
$categoryDeleteEvent = new CategoryDeleteEvent($data['category_id']);
// Get the form field values
$data = $form->getData();
$this->dispatch(TheliaEvents::CATEGORY_DELETE, $categoryDeleteEvent);
$changeEvent = new CategoryUpdateEvent($data['id']);
$category = $categoryDeleteEvent->getDeletedCategory();
// Create and dispatch the change event
$changeEvent
->setCategoryName($data['name'])
->setLocale($data["locale"])
->setSymbol($data['symbol'])
->setCode($data['code'])
->setRate($data['rate'])
;
$this->adminLogAppend(sprintf("Category %s (ID %s) deleted", $category->getTitle(), $category->getId()));
$this->dispatch(TheliaEvents::CATEGORY_UPDATE, $changeEvent);
// Substitute _ID_ in the URL with the ID of the created category
$successUrl = str_replace('_ID_', $categoryDeleteEvent->getDeletedCategory()->getParent(), $categoryDeletionForm->getSuccessUrl());
// Log currency modification
$changedObject = $changeEvent->getCategory();
$this->adminLogAppend(sprintf("Category %s (ID %s) modified", $changedObject->getName(), $changedObject->getId()));
// If we have to stay on the same page, do not redirect to the succesUrl,
// just redirect to the edit page again.
if ($this->getRequest()->get('save_mode') == 'stay') {
$this->redirectToRoute(
"admin.categories.update",
array('currency_id' => $currency_id)
);
}
// Redirect to the success URL
$this->redirect($successUrl);
$this->redirect($changeForm->getSuccessUrl());
}
catch (FormValidationException $e) {
$categoryDeletionForm->setErrorMessage($e->getMessage());
$this->getParserContext()->addForm($categoryDeletionForm);
catch (FormValidationException $ex) {
// Invalid data entered
$error_msg = sprintf("Please check your input: %s", $ex->getMessage());
}
catch (Exception $e) {
Tlog::getInstance()->error(sprintf("Failed to delete category: %s", $e->getMessage()));
$this->getParserContext()->setGeneralError($e->getMessage());
catch (\Exception $ex) {
// Any other error
$error_msg = $ex;
}
// At this point, something was wrong, category was not deleted. Display parent category list
return $this->render('categories', $args);
if ($error_msg !== false) {
// Log error currency
Tlog::getInstance()->error(sprintf("Error during currency modification process : %s. Exception was %s", $error_msg, $ex->getMessage()));
// Mark the form as errored
$changeForm->setErrorMessage($error_msg);
// Pas the form and the error to the parser
$this->getParserContext()
->addForm($changeForm)
->setGeneralError($error_msg)
;
}
// At this point, the form has errors, and should be redisplayed.
return $this->render('currency-edit', array('currency_id' => $currency_id));
}
protected function browseCategory($args)
{
if (null !== $response = $this->checkAuth("admin.catalog.view")) return $response;
/**
* Sets the default currency
*/
public function setDefaultAction() {
// Check current user authorization
if (null !== $response = $this->checkAuth("admin.categories.update")) return $response;
return $this->render('categories', $args);
}
$changeEvent = new CategoryUpdateEvent($this->getRequest()->get('currency_id', 0));
protected function visibilityToggle($args)
{
$event = new CategoryToggleVisibilityEvent($this->getRequest()->get('category_id', 0));
$this->dispatch(TheliaEvents::CATEGORY_TOGGLE_VISIBILITY, $event);
return $this->nullResponse();
}
protected function changePosition($args)
{
$request = $this->getRequest();
$event = new CategoryChangePositionEvent(
$request->get('category_id', 0),
CategoryChangePositionEvent::POSITION_ABSOLUTE,
$request->get('position', null)
);
$this->dispatch(TheliaEvents::CATEGORY_CHANGE_POSITION, $event);
return $this->render('categories', $args);
}
protected function positionDown($args)
{
$event = new CategoryChangePositionEvent(
$this->getRequest()->get('category_id', 0),
CategoryChangePositionEvent::POSITION_DOWN
);
$this->dispatch(TheliaEvents::CATEGORY_CHANGE_POSITION, $event);
return $this->render('categories', $args);
}
protected function positionUp($args)
{
$event = new CategoryChangePositionEvent(
$this->getRequest()->get('category_id', 0),
CategoryChangePositionEvent::POSITION_UP
);
$this->dispatch(TheliaEvents::CATEGORY_CHANGE_POSITION, $event);
return $this->render('categories', $args);
}
public function indexAction()
{
return $this->processAction();
}
public function processAction()
{
// Get the current action
$action = $this->getRequest()->get('action', 'browse');
// Get the category ID
$id = $this->getRequest()->get('id', 0);
// Find the current order
$category_order = $this->getRequest()->get(
'order',
$this->getSession()->get('admin.category_order', 'manual')
);
// Find the current edit language ID
$edition_language = $this->getRequest()->get(
'edition_language',
$this->getSession()->get('admin.edition_language', Lang::getDefaultLanguage()->getId())
);
$args = array(
'action' => $action,
'current_category_id' => $id,
'category_order' => $category_order,
'edition_language' => $edition_language,
);
// Store the current sort order in session
$this->getSession()->set('admin.category_order', $category_order);
// Store the current edition language in session
$this->getSession()->set('admin.edition_language', $edition_language);
// Create and dispatch the change event
$changeEvent->setIsDefault(true);
try {
switch ($action) {
case 'browse' : // Browse categories
return $this->browseCategory($args);
case 'create' : // Create a new category
return $this->createNewCategory($args);
case 'edit' : // Edit an existing category
return $this->editCategory($args);
case 'delete' : // Delete an existing category
return $this->deleteCategory($args);
case 'visibilityToggle' : // Toggle visibility
return $this->visibilityToggle($id);
case 'changePosition' : // Change position
return $this->changePosition($args);
case 'positionUp' : // Move up category
return $this->positionUp($args);
case 'positionDown' : // Move down category
return $this->positionDown($args);
}
$this->dispatch(TheliaEvents::CATEGORY_SET_DEFAULT, $changeEvent);
}
catch (AuthorizationException $ex) {
return $this->errorPage($ex->getMessage());
}
catch (AuthenticationException $ex) {
return $this->errorPage($ex->getMessage());
catch (\Exception $ex) {
// Any error
return $this->errorPage($ex);
}
// We did not recognized the action -> return a 404 page
return $this->pageNotFound();
$this->redirectToRoute('admin.categories.default');
}
/**
* Update categories rates
*/
public function updateRatesAction() {
// Check current user authorization
if (null !== $response = $this->checkAuth("admin.categories.update")) return $response;
try {
$this->dispatch(TheliaEvents::CATEGORY_UPDATE_RATES);
}
catch (\Exception $ex) {
// Any error
return $this->errorPage($ex);
}
$this->redirectToRoute('admin.categories.default');
}
/**
* Update currencyposition
*/
public function updatePositionAction() {
// Check current user authorization
if (null !== $response = $this->checkAuth("admin.categories.update")) return $response;
try {
$mode = $this->getRequest()->get('mode', null);
if ($mode == 'up')
$mode = CategoryUpdatePositionEvent::POSITION_UP;
else if ($mode == 'down')
$mode = CategoryUpdatePositionEvent::POSITION_DOWN;
else
$mode = CategoryUpdatePositionEvent::POSITION_ABSOLUTE;
$position = $this->getRequest()->get('position', null);
$event = new CategoryUpdatePositionEvent(
$this->getRequest()->get('currency_id', null),
$mode,
$this->getRequest()->get('position', null)
);
$this->dispatch(TheliaEvents::CATEGORY_UPDATE_POSITION, $event);
}
catch (\Exception $ex) {
// Any error
return $this->errorPage($ex);
}
$this->redirectToRoute('admin.categories.default');
}
/**
* Delete a currency object
*
* @return Symfony\Component\HttpFoundation\Response the response
*/
public function deleteAction() {
// Check current user authorization
if (null !== $response = $this->checkAuth("admin.categories.delete")) return $response;
// Get the currency id, and dispatch the delet request
$event = new CategoryDeleteEvent($this->getRequest()->get('currency_id'));
$this->dispatch(TheliaEvents::CATEGORY_DELETE, $event);
$this->redirectToRoute('admin.categories.default');
}
}

View File

@@ -121,26 +121,14 @@ class ConfigController extends BaseAdminController
}
catch (FormValidationException $ex) {
// Form cannot be validated
$message = sprintf("Please check your input: %s", $ex->getMessage());
$message = $this->createStandardFormValidationErrorMessage($ex);
}
catch (\Exception $ex) {
// Any other error
$message = sprintf("Sorry, an error occured: %s", $ex->getMessage());
$message = $ex->getMessage();
}
if ($message !== false) {
// An error has been detected: log it
Tlog::getInstance()->error(sprintf("Error during variable creation process : %s. Exception was %s", $message, $ex->getMessage()));
// Mark the form as errored
$creationForm->setErrorMessage($message);
// Pass it to the parser, along with the error message
$this->getParserContext()
->addForm($creationForm)
->setGeneralError($message)
;
}
$this->setupFormErrorContext("variable creation", $message, $creationForm, $ex);
// At this point, the form has error, and should be redisplayed.
return $this->renderList();
@@ -250,27 +238,15 @@ class ConfigController extends BaseAdminController
$this->redirect($changeForm->getSuccessUrl());
}
catch (FormValidationException $ex) {
// Invalid data entered
$message = sprintf("Please check your input: %s", $ex->getMessage());
// Form cannot be validated
$message = $this->createStandardFormValidationErrorMessage($ex);
}
catch (\Exception $ex) {
// Any other error
$message = sprintf("Sorry, an error occured: %s", $ex->getMessage());
$message = $ex->getMessage();
}
if ($message !== false) {
// Log error message
Tlog::getInstance()->error(sprintf("Error during variable modification process : %s. Exception was %s", $message, $ex->getMessage()));
// Mark the form as errored
$changeForm->setErrorMessage($message);
// Pas the form and the error to the parser
$this->getParserContext()
->addForm($changeForm)
->setGeneralError($message)
;
}
$this->setupFormErrorContext("variable edition", $message, $creationForm, $ex);
// At this point, the form has errors, and should be redisplayed.
return $this->render('variable-edit', array('variable_id' => $variable_id));

View File

@@ -111,7 +111,7 @@ class CurrencyController extends BaseAdminController
$createdObject = $createEvent->getCurrency();
// Log currency creation
$this->adminLogAppend(sprintf("Variable %s (ID %s) created", $createdObject->getName(), $createdObject->getId()));
$this->adminLogAppend(sprintf("Currency %s (ID %s) created", $createdObject->getName(), $createdObject->getId()));
// Substitute _ID_ in the URL with the ID of the created object
$successUrl = str_replace('_ID_', $createdObject->getId(), $creationForm->getSuccessUrl());
@@ -226,7 +226,7 @@ class CurrencyController extends BaseAdminController
// Log currency modification
$changedObject = $changeEvent->getCurrency();
$this->adminLogAppend(sprintf("Variable %s (ID %s) modified", $changedObject->getName(), $changedObject->getId()));
$this->adminLogAppend(sprintf("Currency %s (ID %s) modified", $changedObject->getName(), $changedObject->getId()));
// If we have to stay on the same page, do not redirect to the succesUrl,
// just redirect to the edit page again.

View File

@@ -25,12 +25,11 @@ namespace Thelia\Core\Event;
use Thelia\Model\Category;
class CategoryCreateEvent extends ActionEvent
class CategoryCreateEvent extends CategoryEvent
{
protected $title;
protected $parent;
protected $locale;
protected $created_category;
public function __construct($title, $parent, $locale)
{
@@ -68,14 +67,4 @@ class CategoryCreateEvent extends ActionEvent
{
$this->locale = $locale;
}
public function getCreatedCategory()
{
return $this->created_category;
}
public function setCreatedCategory(Category $created_category)
{
$this->created_category = $created_category;
}
}

View File

@@ -22,13 +22,11 @@
/*************************************************************************************/
namespace Thelia\Core\Event;
use Thelia\Model\Category;
class CategoryDeleteEvent extends ActionEvent
class CategoryDeleteEvent extends CategoryEvent
{
protected $category_id;
protected $deleted_category;
public function __construct($category_id)
{
$this->category_id = $category_id;
@@ -43,14 +41,4 @@ class CategoryDeleteEvent extends ActionEvent
{
$this->category_id = $category_id;
}
public function getDeletedCategory()
{
return $this->deleted_category;
}
public function setDeletedCategory(Category $deleted_category)
{
$this->deleted_category = $deleted_category;
}
}
}

View File

@@ -35,13 +35,15 @@ class CategoryEvent extends ActionEvent
$this->category = $category;
}
/**
* @return \Thelia\Model\Category
*/
public function getCategory()
{
return $this->category;
}
public function setCategory(Category $category)
{
$this->category = $category;
return $this;
}
}

View File

@@ -23,6 +23,6 @@
namespace Thelia\Core\Event;
class CurrencyUpdatePositionEvent extends BaseUpdatePositionEvent
class CategoryUpdatePositionEvent extends BaseUpdatePositionEvent
{
}

View File

@@ -127,6 +127,9 @@ class TheliaHttpKernel extends HttpKernel
// See Thelia\Tools\URL class.
$this->container->get('thelia.url.manager');
// Same thing for the Translator service.
$this->container->get('thelia.translator');
$lang = $this->detectLang($request);
if ($lang) {

View File

@@ -5,6 +5,29 @@ use Symfony\Component\Translation\Translator as BaseTranslator;
class Translator extends BaseTranslator
{
protected static $instance = null;
public function __construct()
{
// Allow singleton style calls once intanciated.
// For this to work, the Translator service has to be instanciated very early. This is done manually
// in TheliaHttpKernel, by calling $this->container->get('thelia.translator');
self::$instance = $this;
}
/**
* Return this class instance, only once instanciated.
*
* @throws \RuntimeException if the class has not been instanciated.
* @return Thelia\Core\Translation\Translator the instance.
*/
public static function getInstance() {
if (self::$instance == null) throw new \RuntimeException("Translator instance is not initialized.");
return self::$instance;
}
/**
* {@inheritdoc}
*
@@ -21,7 +44,7 @@ class Translator extends BaseTranslator
}
if ($this->catalogues[$locale]->has((string) $id, $domain))
return parent::trans($id, $parameters, $domain = 'messages', $locale = null);
return parent::trans($id, $parameters, $domain, $locale);
else
return strtr($id, $parameters);
}

View File

@@ -8,9 +8,7 @@
<div class="catalog">
<div id="wrapper" class="container">
<ul class="breadcrumb">
{include file="includes/category_breadcrumb.html"}
</ul>
{include file="includes/catalog-breadcrumb.html"}
{module_include location='catalog_top'}
@@ -20,7 +18,7 @@
<table class="table table-striped table-condensed" id="category_list">
<caption>
{* display parent category name, and get current cat ID *}
{loop name="category_title" type="category" visible="*" 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}
@@ -30,7 +28,7 @@
{module_include location='category_list_caption'}
{loop type="auth" name="can_create" roles="ADMIN" permissions="admin.category.create"}
{loop type="auth" name="can_create" roles="ADMIN" permissions="admin.categories.create"}
<a class="btn btn-default btn-primary action-btn" title="{intl l='Add a new category'}" href="#add_category_dialog" data-toggle="modal">
<span class="glyphicon glyphicon-plus-sign"></span>
</a>
@@ -40,6 +38,16 @@
{ifloop rel="category_list"}
<thead>
<tr>
<th class="object-title">
{admin_sortable_header
current_order=$category_order
order='id'
reverse_order='id_reverse'
path={url path='/admin/catalog' id_category=$current_category_id}
label="{intl l='ID'}"
}
</th>
<th class="object-image">&nbsp;</th>
<th class="object-title">
@@ -47,8 +55,8 @@
current_order=$category_order
order='alpha'
reverse_order='alpha_reverse'
path={url path='/admin/catalog/category' id="{$current_category_id}"}
label={intl l='Category title'}
path={url path='/admin/catalog' id_category=$current_category_id}
label="{intl l='Category title'}"
}
</th>
@@ -59,8 +67,8 @@
current_order=$category_order
order='visible'
reverse_order='visible_reverse'
path={url path='/admin/catalog/category' id="{$current_category_id}"}
label={intl l='Online'}
path={url path='/admin/catalog' id_category=$current_category_id}
label="{intl l='Online'}"
}
</th>
@@ -69,8 +77,8 @@
current_order=$category_order
order='manual'
reverse_order='manual_reverse'
path={url path='/admin/catalog/category' id="{$current_category_id}"}
label={intl l='Position'}
path={url path='/admin/catalog' id_category=$current_category_id}
label="{intl l='Position'}"
}
</th>
@@ -81,22 +89,24 @@
<tbody>
{loop name="category_list" type="category" visible="*" parent=$current_category_id order=$category_order backend_context="1" lang=$lang_id}
<tr>
<td>{$ID}</td>
<td>
i={$ID} {loop type="image" name="cat_image" source="category" source_id="$ID" limit="1" width="50" height="50" resize_mode="crop" backend_context="1"}
<a href="{url path='admin/catalog/category' id="$ID" action='browse'}" title="{intl l='Browse this category'}"><img class="img-thumbnail" src="#IMAGE_URL" alt="#TITLE" /></a>
{loop type="image" name="cat_image" source="category" source_id="$ID" limit="1" width="50" height="50" resize_mode="crop" backend_context="1"}
<a href="{url path='admin/catalog' category_id=$ID}" title="{intl l='Browse this category'}"><img class="img-thumbnail" src="#IMAGE_URL" alt="#TITLE" /></a>
{/loop}
</td>
<td class="object-title">
<a href="{url path='admin/catalog/category' id="$ID" action='browse'}" title="{intl l='Browse this category'}">
{$ID} p={$POSITION} {$TITLE}
<a href="{url path='admin/catalog' category_id=$ID}" title="{intl l='Browse this category'}">
{$TITLE}
</a>
</td>
{module_include location='category_list_row'}
<td>
{loop type="auth" name="can_change" roles="ADMIN" permissions="admin.category.edit"}
{loop type="auth" name="can_change" roles="ADMIN" permissions="admin.categories.edit"}
<div class="make-switch switch-small" data-on="success" data-off="danger" data-on-label="<i class='glyphicon glyphicon-ok'></i>" data-off-label="<i class='glyphicon glyphicon-remove'></i>">
<input type="checkbox" data-id="{$ID}" class="categoryVisibleToggle" {if $VISIBLE == 1}checked="checked"{/if}>
</div>
@@ -111,24 +121,24 @@
<td>
{admin_position_block
permission="admin.category.edit"
path={url path='admin/catalog/category' category_id="{$ID}"}
permission="admin.categories.edit"
path={url path='admin/category/update-position' category_id=$ID}
url_parameter="category_id"
in_place_edit_class="categoryPositionChange"
position="$POSITION"
id="$ID"
position=$POSITION
id=$ID
}
</td>
<td>
<div class="btn-group">
<a class="btn btn-default btn-xs" title="{intl l='Browse this category'}" href="{url path='admin/catalog/category' id="$ID" action='browse'}"><i class="glyphicon glyphicon-folder-open"></i></a>
<a class="btn btn-default btn-xs" title="{intl l='Browse this category'}" href="{url path='admin/category' category_id=$ID}"><i class="glyphicon glyphicon-folder-open"></i></a>
{loop type="auth" name="can_change" roles="ADMIN" permissions="admin.category.edit"}
<a class="btn btn-default btn-xs" title="{intl l='Edit this category'}" href="{url path='admin/catalog/category' id="$ID" action='edit'}"><i class="glyphicon glyphicon-edit"></i></a>
{loop type="auth" name="can_change" roles="ADMIN" permissions="admin.categories.edit"}
<a class="btn btn-default btn-xs" title="{intl l='Edit this category'}" href="{url path='/admin/categories/update' category_id=$ID}"><i class="glyphicon glyphicon-edit"></i></a>
{/loop}
{loop type="auth" name="can_delete" roles="ADMIN" permissions="admin.category.delete"}
{loop type="auth" name="can_delete" roles="ADMIN" permissions="admin.categories.delete"}
<a class="btn btn-default btn-xs category-delete" title="{intl l='Delete this category and all its contents'}" href="#delete_category_dialog" data-id="{$ID}" data-toggle="modal"><i class="glyphicon glyphicon-trash"></i></a>
{/loop}
</div>
@@ -143,7 +153,7 @@
<tr>
<td class="message">
<div class="alert alert-info">
{loop type="auth" name="can_create" roles="ADMIN" permissions="admin.category.create"}
{loop type="auth" name="can_create" roles="ADMIN" permissions="admin.categories.create"}
{intl l="This category has no sub-categories. To create a new one, click the + button above."}
{/loop}
@@ -166,9 +176,10 @@
<table class="table table-striped table-condensed">
<caption>
{* display parent category name *}
{loop name="category_title" type="category" visible="*" 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"}
{intl l="Top level Products"}
{/elseloop}
@@ -183,15 +194,34 @@
{ifloop rel="product_list"}
<thead>
<tr>
<th class="object-title">
{admin_sortable_header
current_order=$product_order
order='id'
reverse_order='id_reverse'
path={url path='/admin/product' category_id=$current_category_id}
label="{intl l='ID'}"
}
<th>&nbsp;</th>
<th class="object-title">
{admin_sortable_header
current_order=$product_order
order='ref'
reverse_order='ref_reverse'
path={url path='/admin/product' category_id=$current_category_id}
label="{intl l='Reference'}"
}
</th>
<th class="object-title">
{admin_sortable_header
current_order=$product_order
order='alpha'
reverse_order='alpha_reverse'
path={url path='/admin/catalog/product' id="{$current_category_id}"}
label={intl l='Product title'}
path={url path='/admin/product' category_id=$current_category_id}
label="{intl l='Product title'}"
}
{module_include location='product_list_header'}
@@ -201,8 +231,8 @@
current_order=$product_order
order='visible'
reverse_order='visible_reverse'
path={url path='/admin/catalog/product' id="{$current_category_id}"}
label={intl l='Online'}
path={url path='/admin/product' category_id=$current_category_id}
label="{intl l='Online'}"
}
</th>
@@ -211,8 +241,8 @@
current_order=$product_order
order='manual'
reverse_order='manual_reverse'
path={url path='/admin/catalog/product' id="{$current_category_id}"}
label={intl l='Position'}
path={url path='/admin/product' category_id=$current_category_id}
label="{intl l='Position'}"
}
</th>
@@ -221,37 +251,58 @@
</thead>
<tbody>
{loop name="product_list" type="product" category="{$current_category_id}" order="manual"}
{loop name="product_list" type="product" category=$current_category_id order="manual"}
<tr>
<td>{$ID}</td>
<td>
{loop type="image" name="cat_image" source="product" source_id="$ID" limit="1" width="50" height="50" resize_mode="crop" backend_context="1"}
<a href="{url path='admin/catalog/product' id="$ID" action='edit'}" title="{intl l='Edit this product'}">
<a href="{url path='admin/product/edit' id=$ID}" title="{intl l='Edit this product'}">
<img src="#IMAGE_URL" alt="#TITLE" />
</a>
{/loop}
<td class="object-title"><a href="{url path='admin/catalog/product' id="$ID" action='edit'}" title="{intl l='Edit this product'}">{$TITLE}</a></td>
<td class="object-title"><a href="{url path='admin/product/edit' id=$ID}" title="{intl l='Edit this product'}">{$REF}</a></td>
<td class="object-title"><a href="{url path='admin/product/edit' id=$ID}" title="{intl l='Edit this product'}">{$TITLE}</a></td>
{module_include location='product_list_row'}
<td>
<input type="checkbox" data-id="{$ID}" class="displayToggle" {if $VISIBLE == 1}checked="checked"{/if}>
{loop type="auth" name="can_change" roles="ADMIN" permissions="admin.products.edit"}
<div class="make-switch switch-small" data-on="success" data-off="danger" data-on-label="<i class='glyphicon glyphicon-ok'></i>" data-off-label="<i class='glyphicon glyphicon-remove'></i>">
<input type="checkbox" data-id="{$ID}" class="productVisibleToggle" {if $VISIBLE == 1}checked="checked"{/if}>
</div>
{/loop}
{elseloop rel="can_change"}
<div class="make-switch switch-small" data-on="success" data-off="danger" data-on-label="<i class='glyphicon glyphicon-ok'></i>" data-off-label="<i class='glyphicon glyphicon-remove'></i>">
<input type="checkbox" class="disabled" disabled="disabled" {if $VISIBLE == 1}checked="checked"{/if}>
</div>
{/elseloop}
</td>
<td>
{admin_position_block
permission="admin.product.edit"
path={url path='admin/catalog/product' category_id="{$ID}"}
path={url path='admin/product' category_id=$ID}
url_parameter="product_id"
in_place_edit_class="productPositionChange"
position="$POSITION"
id="$ID"
position=$POSITION
id=$ID
}
</td>
<td>
<a class="btn btn-default btn-xs" title="{intl l='Edit this product'}" href="{url path='admin/catalog/product' id="$ID" action='edit'}"><i class="glyphicon glyphicon-edit"></i></a>
<a class="btn btn-default btn-xs product-delete" title="{intl l='Delete this product'}" href="{url path='admin/catalog/product' id="$ID" action='delete'}"><i class="glyphicon glyphicon-trash"></i></a>
<div class="btn-group">
{loop type="auth" name="can_change" roles="ADMIN" permissions="admin.product.edit"}
<a class="btn btn-default btn-xs" title="{intl l='Edit this product'}" href="{url path='admin/product/edit' product_id=$ID}"><i class="glyphicon glyphicon-edit"></i></a>
{/loop}
{loop type="auth" name="can_change" roles="ADMIN" permissions="admin.product.delete"}
<a class="btn btn-default btn-xs product-delete" title="{intl l='Delete this product'}" href="{url path='admin/product/delete' id=$ID}"><i class="glyphicon glyphicon-trash"></i></a>
{/loop}
</div>
</td>
</tr>
{/loop}
@@ -274,8 +325,105 @@
</div>
</div>
{include file="includes/add-category-dialog.html"}
{include file="includes/delete-category-dialog.html"}
{* Adding a new Category *}
<div class="modal fade" id="add_category_dialog" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</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="form-group">
<label class="control-label">
{intl l='Category Title *'}
</label>
{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-group 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="form-control input-block-level">
</span>
{/form_field}
<span class="input-group-addon"><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 class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal" aria-hidden="true">{intl l="Cancel"}</button>
<button type="submit" class="btn btn-default btn-primary">{intl l="Create this category"}</button>
</div>
</form>
{/form}
</div>
</div>
</div>
{* Delete category confirmation dialog *}
<div class="modal fade" id="delete_category_dialog" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h3>{intl l="Delete a category"}</h3>
</div>
<div class="modal-body">
<p>{intl l="Do you really want to delete this category ?"}</p>
</div>
<form method="post" action="{url path='/admin/categories/delete'}">
<input type="hidden" name="current_category_id" value="{$current_category_id}" />
<input type="hidden" name="category_id" id="delete_category_id" value="" />
<div class="modal-footer">
<button type="submit" class="btn btn-default btn-primary"><span class="glyphicon glyphicon-check"></span> {intl l="Yes"}</button>
<button type="button" class="btn btn-default" data-dismiss="modal" aria-hidden="true"><span class="glyphicon glyphicon-remove"></span> {intl l="No"}</button>
</div>
</form>
</div>
</div>
</div>
{/block}
{block name="javascript-initialization"}
@@ -314,14 +462,25 @@ $(function() {
{* Set the proper category ID in the delete confirmation dialog *}
$(document).on("click", ".category-delete", function () {
$('#'+'delete-category-id').val($(this).data('id'));
$('a.category-delete').click(function(ev) {
$('#delete_category_id').val($(this).data('id'));
});
// Toggle category visibility
$(".categoryVisibleToggle").click(function() {
$.ajax({
url : "{url path='admin/catalog/category'}",
url : "{url path='admin/categories/toggle-online'}",
data : {
category_id : $(this).data('id'),
action : 'visibilityToggle'
}
});
});
// Toggle product visibility
$(".productVisibleToggle").click(function() {
$.ajax({
url : "{url path='admin/products/toggle-online'}",
data : {
category_id : $(this).data('id'),
action : 'visibilityToggle'
@@ -338,15 +497,34 @@ $(function() {
inputclass : 'input-mini',
placement : 'left',
success : function(response, newValue) {
// The URL template
var url = "{url path='admin/catalog/category' action='changePosition' category_id='__ID__' position='__POS__'}";
// The URL template
var url = "{url path='/admin/categories/update-position' category_id='__ID__' position='__POS__'}";
// Perform subtitutions
// Perform subtitutions
url = url.replace('__ID__', $(this).data('id'))
.replace('__POS__', newValue);
.replace('__POS__', newValue);
// Reload the page
location.href = url;
// Reload the page
location.href = url;
}
});
$('.productPositionChange').editable({
type : 'text',
title : '{intl l="Enter new product position"}',
mode : 'popup',
inputclass : 'input-mini',
placement : 'left',
success : function(response, newValue) {
// The URL template
var url = "{url path='/admin/products/update-position' product_id='__ID__' position='__POS__'}";
// Perform subtitutions
url = url.replace('__ID__', $(this).data('id'))
.replace('__POS__', newValue);
// Reload the page
location.href = url;
}
});

View File

@@ -7,9 +7,8 @@
{block name="main-content"}
<div class="catalog edit-category">
<div id="wrapper" class="container">
<ul class="breadcrumb">
{include file="includes/category_breadcrumb.html"}
</ul>
{include file="includes/categories-breadcrumb.html"}
<div class="row">
{loop name="category_edit" type="category" visible="*" id="{$current_category_id}" backend_context="1" lang="$edit_language_id"}

View File

@@ -122,7 +122,7 @@
<td>
{loop type="auth" name="can_change" roles="ADMIN" permissions="admin.configuration.currencies.change"}
<a title="{intl l='Change this currency'}" href="{url path='/admin/configuration/currencies/update' currency_id="$ID"}">{$NAME}</a>
<a title="{intl l='Change this currency'}" href="{url path='/admin/configuration/currencies/update' currency_id=$ID}">{$NAME}</a>
{/loop}
{elseloop rel="can_change"}
{$NAME}

View File

@@ -1,71 +0,0 @@
{* Adding a new Category *}
<div class="modal fade" id="add_category_dialog" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</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="form-group">
<label class="control-label">
{intl l='Category Title *'}
</label>
{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-group 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="form-control input-block-level">
</span>
{/form_field}
<span class="input-group-addon"><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 class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal" aria-hidden="true">{intl l="Cancel"}</button>
<button type="submit" class="btn btn-default btn-primary">{intl l="Create this category"}</button>
</div>
</form>
{/form}
</div>
</div>
</div>

View File

@@ -1,13 +0,0 @@
{* Breadcrumb for categories browsing and editing *}
<li><a href="{url path='admin/home'}">Home</a></li>
<li><a href="{url path='admin/catalog'}">Catalog</a> {ifloop rel="category_path"}</li>
{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' id=" $ID" action='edit' }" title="{intl l='Edit this category'}">{intl l="(edit)"}</a> {/if}
</li>
{else}
<li><a href="{url path='admin/catalog/category' id=" $ID" action='browse'}">{$TITLE}</a></li>
{/if} {/loop} {/ifloop} {elseloop rel="category_path"}
</li>
{/elseloop}

View File

@@ -1,42 +0,0 @@
{* 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">&times;</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='category_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>