Merge branch 'master' of github.com:thelia/thelia

This commit is contained in:
Manuel Raynaud
2013-09-07 16:40:14 +02:00
45 changed files with 1600 additions and 1019 deletions

View File

@@ -51,6 +51,8 @@ class Category extends BaseAction implements EventSubscriberInterface
$event->getParent(),
$event->getLocale()
);
$event->setCategory($category);
}
public function update(CategoryChangeEvent $event)

View File

@@ -48,7 +48,10 @@
<form name="thelia.address.update" class="Thelia\Form\AddressUpdateForm" />
<form name="thelia.admin.category.creation" class="Thelia\Form\CategoryCreationForm"/>
<form name="thelia.admin.category.deletion" class="Thelia\Form\CategoryDeletionForm"/>
<form name="thelia.admin.category.deletion" class="Thelia\Form\CategoryModificationForm"/>
<form name="thelia.admin.product.creation" class="Thelia\Form\ProductCreationForm"/>
<form name="thelia.admin.product.deletion" class="Thelia\Form\ProductModificationForm"/>
<form name="thelia.cart.add" class="Thelia\Form\CartAdd"/>
@@ -80,7 +83,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">
@@ -99,7 +101,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,40 @@
</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.categories.default" path="/admin/categories">
<default key="_controller">Thelia\Controller\Admin\CategoryController::defaultAction</default>
</route>
<route id="admin.categories.create" path="/admin/categories/create">
<default key="_controller">Thelia\Controller\Admin\CategoryController::createAction</default>
</route>
<route id="admin.categories.update" path="/admin/categories/update">
<default key="_controller">Thelia\Controller\Admin\CategoryController::changeAction</default>
</route>
<route id="admin.categories.save" path="/admin/categories/save">
<default key="_controller">Thelia\Controller\Admin\CategoryController::saveChangeAction</default>
</route>
<route id="admin.categories.set-default" path="/admin/categories/toggle-online">
<default key="_controller">Thelia\Controller\Admin\CategoryController::toggleOnlineAction</default>
</route>
<route id="admin.categories.delete" path="/admin/categories/delete">
<default key="_controller">Thelia\Controller\Admin\CategoryController::deleteAction</default>
</route>
<route id="admin.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 -->
@@ -105,6 +131,10 @@
<default key="_controller">Thelia\Controller\Admin\CurrencyController::setDefaultAction</default>
</route>
<route id="admin.configuration.currencies.update-position" path="/admin/configuration/currencies/update-position">
<default key="_controller">Thelia\Controller\Admin\CurrencyController::updatePositionAction</default>
</route>
<route id="admin.configuration.currencies.update-rates" path="/admin/configuration/currencies/update-rates">
<default key="_controller">Thelia\Controller\Admin\CurrencyController::updateRatesAction</default>
</route>
@@ -113,17 +143,20 @@
<default key="_controller">Thelia\Controller\Admin\CurrencyController::deleteAction</default>
</route>
<route id="admin.configuration.currencies.update-position" path="/admin/configuration/currencies/update-position">
<default key="_controller">Thelia\Controller\Admin\CurrencyController::updatePositionAction</default>
</route>
<!-- attribute and feature routes management -->
<route id="admin.configuration.attribute" path="/admin/configuration/product_attributes">
<default key="_controller">Thelia\Controller\Admin\AttributeController::defaultAction</default>
</route>
<!-- attribute and feature routes management -->
<route id="admin.configuration.currencies.update-position" path="/admin/configuration/product_attributes">
<default key="_controller">Thelia\Controller\Admin\CurrencyController::updatePositionAction</default>
<route id="admin.configuration.attribute.edit" path="/admin/configuration/product_attributes/update">
<default key="_controller">Thelia\Controller\Admin\AttributeController::updateAction</default>
</route>
<!-- end attribute and feature routes management -->
<!-- The default route, to display a template -->

View File

@@ -43,7 +43,7 @@ use Thelia\Form\MessageCreationForm;
class AttributeController extends BaseAdminController
{
/**
* The default action is displaying the messages list.
* The default action is displaying the attributes list.
*
* @return Symfony\Component\HttpFoundation\Response the response
*/
@@ -51,7 +51,13 @@ class AttributeController extends BaseAdminController
if (null !== $response = $this->checkAuth("admin.configuration.attributes.view")) return $response;
return $this->render('product_attributes');
return $this->render('product-attributes');
}
public function updateAction() {
if (null !== $response = $this->checkAuth("admin.configuration.attributes.update")) return $response;
return $this->render('product-attributes-edit');
}
}

View File

@@ -34,6 +34,9 @@ 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;
use Thelia\Log\Tlog;
class BaseAdminController extends BaseController
{
@@ -66,7 +69,7 @@ class BaseAdminController extends BaseController
}
}
catch (\Exception $ex) {
return new Response($this->errorPage($ex->getMessage()));
return $this->errorPage($ex->getMessage());
}
return $this->pageNotFound();
@@ -92,7 +95,7 @@ class BaseAdminController extends BaseController
protected function errorPage($message)
{
if ($message instanceof \Exception) {
$message = sprintf("Sorry, an error occured: %s", $message->getMessage());
$message = sprintf($this->getTranslator()->trans("Sorry, an error occured: %msg"), array('msg' => $message->getMessage()));
}
return $this->render('general_error', array(
@@ -123,7 +126,56 @@ class BaseAdminController extends BaseController
// Generate the proper response
$response = new Response();
return $response->setContent($this->errorPage("Sorry, you're not allowed to perform this action"));
return $this->errorPage($this->getTranslator()->trans("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 $this->getTranslator()->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(
$this->getTranslator()->trans(
"Error during %action process : %error. Exception was %exc",
array(
'%action' => $action,
'%error' => $error_message,
'%exc' => $exception != null ? $exception->getMessage() : 'no exception'
)
)
);
if ($form != 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);
}
}
/**
@@ -261,7 +313,7 @@ class BaseAdminController extends BaseController
}
catch (AuthorizationException $ex) {
// User is not allowed to perform the required action. Return the error page instead of the requested page.
return $this->errorPage("Sorry, you are not allowed to perform this action.");
return $this->errorPage($this->getTranslator()->trans("Sorry, you are not allowed to perform this action."));
}
}
}

View File

@@ -34,226 +34,305 @@ use Thelia\Core\Event\CategoryToggleVisibilityEvent;
use Thelia\Core\Event\CategoryChangePositionEvent;
use Thelia\Form\CategoryDeletionForm;
use Thelia\Model\Lang;
use Thelia\Core\Translation\Translator;
use Thelia\Core\Event\CategoryUpdatePositionEvent;
use Thelia\Model\CategoryQuery;
use Thelia\Form\CategoryModificationForm;
class CategoryController extends BaseAdminController
{
protected function createNewCategory($args)
{
try {
$categoryCreationForm = new CategoryCreationForm($this->getRequest());
$form = $this->validateForm($categoryCreationForm, "POST");
$data = $form->getData();
$categoryCreateEvent = new CategoryCreateEvent(
$data["title"],
$data["parent"],
$data["locale"]
);
$this->dispatch(TheliaEvents::CATEGORY_CREATE, $categoryCreateEvent);
$category = $categoryCreateEvent->getCreatedCategory();
$this->adminLogAppend(sprintf("Category %s (ID %s) created", $category->getTitle(), $category->getId()));
// 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
$this->redirect($successUrl);
}
catch (FormValidationException $e) {
$categoryCreationForm->setErrorMessage($e->getMessage());
$this->getParserContext()->addForm($categoryCreationForm);
}
catch (Exception $e) {
Tlog::getInstance()->error(sprintf("Failed to create category: %s", $e->getMessage()));
$this->getParserContext()->setGeneralError($e->getMessage());
}
// At this point, the form has error, and should be redisplayed.
return $this->render('categories', $args);
/**
* Render the categories list, ensuring the sort order is set.
*
* @return Symfony\Component\HttpFoundation\Response the response
*/
protected function renderList() {
return $this->render('categories', $this->getTemplateArgs());
}
protected function editCategory($args)
{
if (null !== $response = $this->checkAuth("admin.category.edit")) return $response;
return $this->render('edit_category', $args);
}
protected function deleteCategory($args)
{
try {
$categoryDeletionForm = new CategoryDeletionForm($this->getRequest());
$data = $this->validateForm($categoryDeletionForm, "POST")->getData();
$categoryDeleteEvent = new CategoryDeleteEvent($data['category_id']);
$this->dispatch(TheliaEvents::CATEGORY_DELETE, $categoryDeleteEvent);
$category = $categoryDeleteEvent->getDeletedCategory();
$this->adminLogAppend(sprintf("Category %s (ID %s) deleted", $category->getTitle(), $category->getId()));
// Substitute _ID_ in the URL with the ID of the created category
$successUrl = str_replace('_ID_', $categoryDeleteEvent->getDeletedCategory()->getParent(), $categoryDeletionForm->getSuccessUrl());
// Redirect to the success URL
$this->redirect($successUrl);
}
catch (FormValidationException $e) {
$categoryDeletionForm->setErrorMessage($e->getMessage());
$this->getParserContext()->addForm($categoryDeletionForm);
}
catch (Exception $e) {
Tlog::getInstance()->error(sprintf("Failed to delete category: %s", $e->getMessage()));
$this->getParserContext()->setGeneralError($e->getMessage());
}
// At this point, something was wrong, category was not deleted. Display parent category list
return $this->render('categories', $args);
}
protected function browseCategory($args)
{
if (null !== $response = $this->checkAuth("admin.catalog.view")) return $response;
return $this->render('categories', $args);
}
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');
protected function getTemplateArgs() {
// Get the category ID
$id = $this->getRequest()->get('id', 0);
$category_id = $this->getRequest()->get('category_id', 0);
// Find the current order
// Find the current category 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,
'current_category_id' => $category_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);
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();
}
/**
* Create a new category object
*
* @return Symfony\Component\HttpFoundation\Response the response
*/
public function createAction() {
// 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 {
switch ($action) {
case 'browse' : // Browse categories
return $this->browseCategory($args);
// Validate the form, create the CategoryCreation event and dispatch it.
$form = $this->validateForm($creationForm, "POST");
case 'create' : // Create a new category
$data = $form->getData();
return $this->createNewCategory($args);
$createEvent = new CategoryCreateEvent(
$data["title"],
$data["parent"],
$data["locale"]
);
case 'edit' : // Edit an existing category
$this->dispatch(TheliaEvents::CATEGORY_CREATE, $createEvent);
return $this->editCategory($args);
if (! $createEvent->hasCategory()) throw new \LogicException($this->getTranslator()->trans("No category was created."));
case 'delete' : // Delete an existing category
$createdObject = $createEvent->getCategory();
return $this->deleteCategory($args);
// Log category creation
$this->adminLogAppend(sprintf("Category %s (ID %s) created", $createdObject->getTitle(), $createdObject->getId()));
case 'visibilityToggle' : // Toggle visibility
// Substitute _ID_ in the URL with the ID of the created object
$successUrl = str_replace('_ID_', $createdObject->getId(), $creationForm->getSuccessUrl());
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);
}
// Redirect to the success URL
$this->redirect($successUrl);
}
catch (AuthorizationException $ex) {
return $this->errorPage($ex->getMessage());
catch (FormValidationException $ex) {
// Form cannot be validated
$error_msg = $this->createStandardFormValidationErrorMessage($ex);
}
catch (AuthenticationException $ex) {
return $this->errorPage($ex->getMessage());
catch (\Exception $ex) {
// Any other error
$error_msg = $ex->getMessage();
}
// We did not recognized the action -> return a 404 page
return $this->pageNotFound();
$this->setupFormErrorContext("category creation", $error_msg, $creationForm, $ex);
// At this point, the form has error, and should be redisplayed.
return $this->renderList();
}
}
/**
* Load a category object for modification, and display the edit template.
*
* @return Symfony\Component\HttpFoundation\Response the response
*/
public function changeAction() {
// Check current user authorization
if (null !== $response = $this->checkAuth("admin.categories.update")) return $response;
// Load the category object
$category = CategoryQuery::create()
->joinWithI18n($this->getCurrentEditionLocale())
->findOneById($this->getRequest()->get('category_id'));
if ($category != null) {
// Prepare the data that will hydrate the form
$data = array(
'id' => $category->getId(),
'locale' => $category->getLocale(),
'title' => $category->getTitle(),
'chapo' => $category->getChapo(),
'description' => $category->getDescription(),
'postscriptum' => $category->getPostscriptum(),
'parent' => $category->getParent(),
'visible' => $category->getVisible() ? true : false,
'url' => $category->getUrl($this->getCurrentEditionLocale())
// tbc !!!
);
// 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('category-edit', $this->getTemplateArgs());
}
/**
* Save changes on a modified category object, and either go back to the category 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 category ID
$category_id = $this->getRequest()->get('category_id');
try {
// Check the form against constraints violations
$form = $this->validateForm($changeForm, "POST");
// Get the form field values
$data = $form->getData();
$changeEvent = new CategoryUpdateEvent($data['id']);
// Create and dispatch the change event
$changeEvent
->setCategoryName($data['name'])
->setLocale($data["locale"])
->setSymbol($data['symbol'])
->setCode($data['code'])
->setRate($data['rate'])
;
$this->dispatch(TheliaEvents::CATEGORY_UPDATE, $changeEvent);
if (! $createEvent->hasCategory()) throw new \LogicException($this->getTranslator()->trans("No category was updated."));
// Log category modification
$changedObject = $changeEvent->getCategory();
$this->adminLogAppend(sprintf("Category %s (ID %s) modified", $changedObject->getTitle(), $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('category_id' => $category_id)
);
}
// Redirect to the success URL
$this->redirect($changeForm->getSuccessUrl());
}
catch (FormValidationException $ex) {
// Form cannot be validated
$error_msg = $this->createStandardFormValidationErrorMessage($ex);
}
catch (\Exception $ex) {
// Any other error
$error_msg = $ex->getMessage();
}
$this->setupFormErrorContext("category modification", $error_msg, $changeForm, $ex);
// At this point, the form has errors, and should be redisplayed.
return $this->render('category-edit', array('category_id' => $category_id));
}
/**
* Online status toggle category
*/
public function setToggleVisibilityAction() {
// Check current user authorization
if (null !== $response = $this->checkAuth("admin.categories.update")) return $response;
$changeEvent = new CategoryUpdateEvent($this->getRequest()->get('category_id', 0));
// Create and dispatch the change event
$changeEvent->setIsDefault(true);
try {
$this->dispatch(TheliaEvents::CATEGORY_SET_DEFAULT, $changeEvent);
}
catch (\Exception $ex) {
// Any error
return $this->errorPage($ex);
}
$this->redirectToRoute('admin.categories.default');
}
/**
* Update categoryposition
*/
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('category_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 category 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 category id, and dispatch the deleted request
$event = new CategoryDeleteEvent($this->getRequest()->get('category_id'));
$this->dispatch(TheliaEvents::CATEGORY_DELETE, $event);
if ($event->hasCategory())
$this->adminLogAppend(sprintf("Category %s (ID %s) deleted", $event->getCategory()->getTitle(), $event->getCategory()->getId()));
$this->redirectToRoute('admin.categories.default');
}
}

View File

@@ -108,6 +108,8 @@ class ConfigController extends BaseAdminController
$this->dispatch(TheliaEvents::CONFIG_CREATE, $createEvent);
if (! $createEvent->hasConfig()) throw new \LogicException($this->getTranslator()->trans("No variable was created."));
$createdObject = $createEvent->getConfig();
// Log config creation
@@ -121,26 +123,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();
@@ -231,6 +221,8 @@ class ConfigController extends BaseAdminController
$this->dispatch(TheliaEvents::CONFIG_UPDATE, $changeEvent);
if (! $changeEvent->hasConfig()) throw new \LogicException($this->getTranslator()->trans("No variable was updated."));
// Log config modification
$changedObject = $changeEvent->getConfig();
@@ -250,27 +242,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, $changeForm, $ex);
// At this point, the form has errors, and should be redisplayed.
return $this->render('variable-edit', array('variable_id' => $variable_id));
@@ -314,6 +294,9 @@ class ConfigController extends BaseAdminController
$this->dispatch(TheliaEvents::CONFIG_DELETE, $event);
if ($event->hasConfig())
$this->adminLogAppend(sprintf("Variable %s (ID %s) modified", $event->getConfig()->getName(), $event->getConfig()->getId()));
$this->redirectToRoute('admin.configuration.variables.default');
}
}

View File

@@ -108,10 +108,12 @@ class CurrencyController extends BaseAdminController
$this->dispatch(TheliaEvents::CURRENCY_CREATE, $createEvent);
if (! $createEvent->hasCurrency()) throw new \LogicException($this->getTranslator()->trans("No currency was created."));
$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());
@@ -121,26 +123,14 @@ class CurrencyController extends BaseAdminController
}
catch (FormValidationException $ex) {
// Form cannot be validated
$error_msg = sprintf("Please check your input: %s", $ex->getMessage());
$error_msg = $this->createStandardFormValidationErrorMessage($ex);
}
catch (\Exception $ex) {
// Any other error
$error_msg = $ex;
$error_msg = $ex->getMessage();
}
if ($error_msg !== false) {
// An error has been detected: log it
Tlog::getInstance()->error(sprintf("Error during currency 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)
;
}
$this->setupFormErrorContext("currency creation", $error_msg, $creationForm, $ex);
// At this point, the form has error, and should be redisplayed.
return $this->renderList();
@@ -223,10 +213,12 @@ class CurrencyController extends BaseAdminController
$this->dispatch(TheliaEvents::CURRENCY_UPDATE, $changeEvent);
if (! $changeEvent->hasCurrency()) throw new \LogicException($this->getTranslator()->trans("No currency was updated."));
// 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.
@@ -241,27 +233,15 @@ class CurrencyController extends BaseAdminController
$this->redirect($changeForm->getSuccessUrl());
}
catch (FormValidationException $ex) {
// Invalid data entered
$error_msg = sprintf("Please check your input: %s", $ex->getMessage());
// Form cannot be validated
$error_msg = $this->createStandardFormValidationErrorMessage($ex);
}
catch (\Exception $ex) {
// Any other error
$error_msg = $ex;
$error_msg = $ex->getMessage();
}
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)
;
}
$this->setupFormErrorContext("currency modification", $error_msg, $changeForm, $ex);
// At this point, the form has errors, and should be redisplayed.
return $this->render('currency-edit', array('currency_id' => $currency_id));
@@ -359,6 +339,9 @@ class CurrencyController extends BaseAdminController
$this->dispatch(TheliaEvents::CURRENCY_DELETE, $event);
if ($event->hasCurrency())
$this->adminLogAppend(sprintf("Currency %s (ID %s) modified", $event->getCurrency()->getName(), $event->getCurrency()->getId()));
$this->redirectToRoute('admin.configuration.currencies.default');
}
}

View File

@@ -42,6 +42,15 @@ use Thelia\Form\MessageCreationForm;
*/
class MessageController extends BaseAdminController
{
/**
* Render the messages list
*
* @return Symfony\Component\HttpFoundation\Response the response
*/
protected function renderList() {
return $this->render('messages');
}
/**
* The default action is displaying the messages list.
*
@@ -51,7 +60,7 @@ class MessageController extends BaseAdminController
if (null !== $response = $this->checkAuth("admin.configuration.messages.view")) return $response;
return $this->render('messages');
return $this->renderList();
}
/**
@@ -66,7 +75,7 @@ class MessageController extends BaseAdminController
$message = false;
// Create the Creation Form
// Create the creation Form
$creationForm = new MessageCreationForm($this->getRequest());
try {
@@ -87,10 +96,11 @@ class MessageController extends BaseAdminController
$this->dispatch(TheliaEvents::MESSAGE_CREATE, $createEvent);
if (! $createEvent->hasMessage()) throw new \LogicException($this->getTranslator()->trans("No message was created."));
$createdObject = $createEvent->getMessage();
// Log message creation
$this->adminLogAppend(sprintf("Variable %s (ID %s) created", $createdObject->getName(), $createdObject->getId()));
$this->adminLogAppend(sprintf("Message %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());
@@ -100,26 +110,14 @@ class MessageController 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 message 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("message modification", $message, $creationForm, $ex);
// At this point, the form has error, and should be redisplayed.
return $this->render('messages');
@@ -206,7 +204,8 @@ class MessageController extends BaseAdminController
$this->dispatch(TheliaEvents::MESSAGE_UPDATE, $changeEvent);
// Log message modification
if (! $changeEvent->hasMessage()) throw new \LogicException($this->getTranslator()->trans("No message was updated."));
$changedObject = $changeEvent->getMessage();
$this->adminLogAppend(sprintf("Variable %s (ID %s) modified", $changedObject->getName(), $changedObject->getId()));
@@ -224,27 +223,15 @@ class MessageController 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 message 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("message modification", $message, $changeForm, $ex);
// At this point, the form has errors, and should be redisplayed.
return $this->render('message-edit', array('message_id' => $message_id));
@@ -265,6 +252,9 @@ class MessageController extends BaseAdminController
$this->dispatch(TheliaEvents::MESSAGE_DELETE, $event);
$this->redirect(URL::getInstance()->adminViewUrl('messages'));
if ($event->hasMessage())
$this->adminLogAppend(sprintf("Message %s (ID %s) modified", $event->getMessage()->getName(), $event->getMessage()->getId()));
$this->redirectToRoute('admin.configuration.messages.default');
}
}

View File

@@ -73,32 +73,34 @@ class SessionController extends BaseAdminController
// Redirect to the success URL
return Redirect::exec($adminLoginForm->getSuccessUrl());
} catch (ValidatorException $ex) {
}
catch (FormValidationException $ex) {
// Validation problem
$message = "Missing or invalid information. Please check your input.";
} catch (AuthenticationException $ex) {
$message = $this->createStandardFormValidationErrorMessage($ex);
}
catch (AuthenticationException $ex) {
// Log authentication failure
AdminLog::append(sprintf("Authentication failure for username '%s'", $authenticator->getUsername()), $request);
$message = "Login failed. Please check your username and password.";
} catch (\Exception $ex) {
$message = $this->getTranslator()->trans("Login failed. Please check your username and password.");
}
catch (\Exception $ex) {
// Log authentication failure
AdminLog::append(sprintf("Undefined error: %s", $ex->getMessage()), $request);
$message = "Unable to process your request. Please try again.".$ex->getMessage();
$message = $this->getTranslator()->trans(
"Unable to process your request. Please try again (%err).",
array("%err" => $ex->getMessage())
);
}
// Store error information in the form
$adminLoginForm->setError(true);
$adminLoginForm->setErrorMessage($message);
// Store the form name in session (see Form Smarty plugin to find usage of this parameter)
$this->getParserContext()->addForm($adminLoginForm);
$this->setupFormErrorContext("Login process", $message, $adminLoginForm, $ex);
// Display the login form again
return $this->render("login");
}
}
}

View File

@@ -20,25 +20,29 @@
/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* */
/*************************************************************************************/
namespace Thelia\Form;
use Symfony\Component\Validator\Constraints\NotBlank;
namespace Thelia\Core\Event;
class CategoryDeletionForm extends BaseForm
class BaseToggleVisibilityEvent extends ActionEvent
{
protected function buildForm()
protected $object_id;
protected $object;
public function __construct($object_id)
{
$this->formBuilder
->add("category_id", "integer", array(
"constraints" => array(
new NotBlank()
)
))
;
$this->object_id = $object_id;
}
public function getName()
public function getObjectId()
{
return "thelia_category_deletion";
return $this->object_id;
}
}
public function setObjectId($object_id)
{
$this->object_id = $object_id;
return $this;
}
}

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)
{
@@ -47,6 +46,7 @@ class CategoryCreateEvent extends ActionEvent
public function setTitle($title)
{
$this->title = $title;
return $this;
}
public function getParent()
@@ -57,6 +57,7 @@ class CategoryCreateEvent extends ActionEvent
public function setParent($parent)
{
$this->parent = $parent;
return $this;
}
public function getLocale()
@@ -67,15 +68,6 @@ class CategoryCreateEvent extends ActionEvent
public function setLocale($locale)
{
$this->locale = $locale;
return $this;
}
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;
@@ -42,15 +40,6 @@ class CategoryDeleteEvent extends ActionEvent
public function setCategoryId($category_id)
{
$this->category_id = $category_id;
return $this;
}
public function getDeletedCategory()
{
return $this->deleted_category;
}
public function setDeletedCategory(Category $deleted_category)
{
$this->deleted_category = $deleted_category;
}
}
}

View File

@@ -28,20 +28,26 @@ use Thelia\Core\Event\ActionEvent;
class CategoryEvent extends ActionEvent
{
public $category;
public $category = null;
public function __construct(Category $category)
public function __construct(Category $category = null)
{
$this->category = $category;
}
/**
* @return \Thelia\Model\Category
*/
public function hasCategory() {
return ! is_null($this->category);
}
public function getCategory()
{
return $this->category;
}
public function setCategory(Category $category)
{
$this->category = $category;
return $this;
}
}

View File

@@ -22,35 +22,7 @@
/*************************************************************************************/
namespace Thelia\Core\Event;
use Thelia\Model\Category;
class CategoryToggleVisibilityEvent extends ActionEvent
class CategoryToggleVisibilityEvent extends BaseToggleVisibilityEvent
{
protected $category_id;
protected $category;
public function __construct($category_id)
{
$this->category_id = $category_id;
}
public function getCategoryId()
{
return $this->category_id;
}
public function setCategoryId($category_id)
{
$this->category_id = $category_id;
}
public function getCategory()
{
return $this->category;
}
public function setCategory(Category $category)
{
$this->category = $category;
}
}

View File

@@ -22,17 +22,16 @@
/*************************************************************************************/
namespace Thelia\Core\Event;
use Thelia\Model\Category;
class CategoryUpdateEvent extends ActionEvent
class CategoryUpdateEvent extends CategoryCreateEvent
{
protected $category_id;
protected $locale;
protected $title;
protected $chapo;
protected $description;
protected $postscriptum;
protected $url;
protected $visibility;
protected $parent;
@@ -41,4 +40,81 @@ class CategoryUpdateEvent extends ActionEvent
{
$this->category_id = $category_id;
}
public function getCategoryId()
{
return $this->category_id;
}
public function setCategoryId($category_id)
{
$this->category_id = $category_id;
return $this;
}
public function getChapo()
{
return $this->chapo;
}
public function setChapo($chapo)
{
$this->chapo = $chapo;
return $this;
}
public function getDescription()
{
return $this->description;
}
public function setDescription($description)
{
$this->description = $description;
return $this;
}
public function getPostscriptum()
{
return $this->postscriptum;
}
public function setPostscriptum($postscriptum)
{
$this->postscriptum = $postscriptum;
return $this;
}
public function getUrl()
{
return $this->url;
}
public function setUrl($url)
{
$this->url = $url;
return $this;
}
public function getVisibility()
{
return $this->visibility;
}
public function setVisibility($visibility)
{
$this->visibility = $visibility;
return $this;
}
public function getParent()
{
return $this->parent;
}
public function setParent($parent)
{
$this->parent = $parent;
return $this;
}
}

View File

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

View File

@@ -26,13 +26,17 @@ use Thelia\Model\Config;
class ConfigEvent extends ActionEvent
{
protected $config;
protected $config = null;
public function __construct(Config $config = null)
{
$this->config = $config;
}
public function hasConfig() {
return ! is_null($this->config);
}
public function getConfig()
{
return $this->config;

View File

@@ -26,13 +26,17 @@ use Thelia\Model\Currency;
class CurrencyEvent extends ActionEvent
{
protected $currency;
protected $currency = null;
public function __construct(Currency $currency = null)
{
$this->currency = $currency;
}
public function hasCurrency() {
return ! is_null($this->currency);
}
public function getCurrency()
{
return $this->currency;

View File

@@ -26,13 +26,17 @@ use Thelia\Model\Message;
class MessageEvent extends ActionEvent
{
protected $message;
protected $message = null;
public function __construct(Message $message = null)
{
$this->message = $message;
}
public function hasMessage() {
return ! is_null($this->message);
}
public function getMessage()
{
return $this->message;

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

@@ -141,6 +141,8 @@ abstract class BaseForm
public function createView()
{
$this->view = $this->form->createView();
return $this;
}
public function getView()
@@ -159,6 +161,8 @@ abstract class BaseForm
public function setError($has_error = true)
{
$this->has_error = $has_error;
return $this;
}
/**
@@ -180,6 +184,8 @@ abstract class BaseForm
{
$this->setError(true);
$this->error_message = $message;
return $this;
}
/**

View File

@@ -23,6 +23,7 @@
namespace Thelia\Form;
use Symfony\Component\Validator\Constraints\NotBlank;
use Thelia\Core\Translation\Translator;
class CategoryCreationForm extends BaseForm
{
@@ -33,7 +34,7 @@ class CategoryCreationForm extends BaseForm
"constraints" => array(
new NotBlank()
),
"label" => "Category title *",
"label" => Translator::getInstance()->trans("Category title *"),
"label_attr" => array(
"for" => "title"
)

View 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\Validator\Constraints\NotBlank;
use Thelia\Model\LangQuery;
use Propel\Runtime\ActiveQuery\Criteria;
use Symfony\Component\Validator\Constraints\GreaterThan;
use Thelia\Core\Translation\Translator;
class CategoryModificationForm extends CategoryCreationForm
{
use StandardDescriptionFieldsTrait;
protected function buildForm()
{
parent::buildForm(true);
$this->formBuilder
->add("id", "hidden", array("constraints" => array(new GreaterThan(array('value' => 0)))))
->add("visible", "checkbox", array(
"label" => Translator::getInstance()->trans("This category is online on the front office.")
))
;
// Add standard description fields
$this->addStandardDescFields(array('title', 'locale'));
}
public function getName()
{
return "thelia_category_modification";
}
}

View File

@@ -27,12 +27,12 @@ use Thelia\Model\LangQuery;
use Propel\Runtime\ActiveQuery\Criteria;
use Symfony\Component\Validator\Constraints\GreaterThan;
class ConfigModificationForm extends BaseDescForm
class ConfigModificationForm extends BaseForm
{
use StandardDescriptionFieldsTrait;
protected function buildForm()
{
parent::buildForm(true);
$this->formBuilder
->add("id", "hidden", array(
"constraints" => array(
@@ -61,6 +61,9 @@ class ConfigModificationForm extends BaseDescForm
"label" => "Prevent variable modification or deletion, except for super-admin"
))
;
// Add standard description fields
$this->addStandardDescFields();
}
public function getName()

View File

@@ -24,50 +24,44 @@ namespace Thelia\Form;
use Symfony\Component\Validator\Constraints\NotBlank;
/**
* A base form for all objects with standard contents.
*
* @author Franck Allimant <franck@cqfdev.fr>
*/
abstract class BaseDescForm extends BaseForm
class ProductCreationForm extends BaseForm
{
protected function buildForm()
{
$this->formBuilder
->add("locale", "hidden", array(
"constraints" => array(
new NotBlank()
)
->add("ref", "text", array(
"constraints" => array(
new NotBlank()
),
"label" => "Product reference *",
"label_attr" => array(
"for" => "ref"
)
)
))
->add("title", "text", array(
"constraints" => array(
new NotBlank()
),
"label" => "Title",
"label_attr" => array(
"for" => "title"
)
)
)
->add("chapo", "text", array(
"label" => "Summary",
"constraints" => array(
new NotBlank()
),
"label" => "Product title *",
"label_attr" => array(
"for" => "summary"
"for" => "title"
)
))
->add("description", "text", array(
"label" => "Detailed description",
"label_attr" => array(
"for" => "detailed_description"
->add("parent", "integer", array(
"constraints" => array(
new NotBlank()
)
))
->add("postscriptum", "text", array(
"label" => "Conclusion",
"label_attr" => array(
"for" => "conclusion"
->add("locale", "text", array(
"constraints" => array(
new NotBlank()
)
))
;
}
}
}
public function getName()
{
return "thelia_product_creation";
}
}

View File

@@ -0,0 +1,92 @@
<?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\Validator\Constraints\NotBlank;
use Thelia\Core\Translation\Translator;
/**
* A trait to add standard localized description fields to a form.
*
* @author Franck Allimant <franck@cqfdev.fr>
*/
trait StandardDescriptionFieldsTrait
{
/**
* Add standard description fields + locale tot the form
*
* @param array $exclude name of the fields that should not be added to the form
*/
protected function addStandardDescFields($exclude = array())
{
if (! in_array('locale', $exclude))
$this->formBuilder
->add("locale", "hidden", array(
"constraints" => array(
new NotBlank()
)
)
);
if (! in_array('title', $exclude))
$this->formBuilder
->add("title", "text", array(
"constraints" => array(
new NotBlank()
),
"label" => Translator::getInstance()->trans("Title"),
"label_attr" => array(
"for" => "title"
)
)
);
if (! in_array('chapo', $exclude))
$this->formBuilder
->add("chapo", "text", array(
"label" => Translator::getInstance()->trans("Summary"),
"label_attr" => array(
"for" => "summary"
)
));
if (! in_array('description', $exclude))
$this->formBuilder
->add("description", "text", array(
"label" => Translator::getInstance()->trans("Detailed description"),
"label_attr" => array(
"for" => "detailed_description"
)
));
if (! in_array('postscriptum', $exclude))
$this->formBuilder
->add("postscriptum", "text", array(
"label" => Translator::getInstance()->trans("Conclusion"),
"label_attr" => array(
"for" => "conclusion"
)
));
}
}

View File

@@ -55,7 +55,7 @@ trait PositionManagementTrait {
->orderByPosition(Criteria::DESC)
->limit(1);
if ($parent !== null) $last->filterByParent($parent);
if ($parent !== null) $query->filterByParent($parent);
$last = $query->findOne()
;