diff --git a/core/lib/Thelia/Action/PositionManagementTrait.php b/core/lib/Thelia/Action/PositionManagementTrait.php new file mode 100644 index 000000000..70f91d995 --- /dev/null +++ b/core/lib/Thelia/Action/PositionManagementTrait.php @@ -0,0 +1,157 @@ +. */ +/* */ +/*************************************************************************************/ + +namespace Thelia\Action; + +use Thelia\Core\Event\BaseChangePositionEvent; + +trait PositionManagementTrait { + + const POSITION_UP + /** + * Changes object position, selecting absolute ou relative change. + * + * @param BaseChangePositionEvent $event + */ + public function changePosition(BaseChangePositionEvent $event) + { + if ($event->getMode() == BaseChangePositionEvent::POSITION_ABSOLUTE) + return $this->changeAbsolutePosition($event); + else + return $this->exchangePosition($event); + } + + /** + * Move up or down a object + * + * @param BaseChangePositionEvent $event + */ + protected function exchangePosition(BaseChangePositionEvent $event) + { + $object = CategoryQuery::create()->findPk($event->getCategoryId()); + + if ($object !== null) { + + // The current position of the object + $my_position = $object->getPosition(); + + // Find object to exchange position with + $search = CategoryQuery::create() + ->filterByParent($object->getParent()); + + // Up or down ? + if ($event->getMode() == BaseChangePositionEvent::POSITION_UP) { + // Find the object immediately before me + $search->filterByPosition(array('max' => $my_position-1))->orderByPosition(Criteria::DESC); + } elseif ($event->getMode() == BaseChangePositionEvent::POSITION_DOWN) { + // Find the object immediately after me + $search->filterByPosition(array('min' => $my_position+1))->orderByPosition(Criteria::ASC); + } else + + return; + + $result = $search->findOne(); + + // If we found the proper object, exchange their positions + if ($result) { + + $cnx = Propel::getWriteConnection(CategoryTableMap::DATABASE_NAME); + + $cnx->beginTransaction(); + + try { + $object + ->setDispatcher($this->getDispatcher()) + ->setPosition($result->getPosition()) + ->save() + ; + + $result->setPosition($my_position)->save(); + + $cnx->commit(); + } catch (Exception $e) { + $cnx->rollback(); + } + } + } + } + + /** + * Changes object position + * + * @param BaseChangePositionEvent $event + */ + protected function changeAbsolutePosition(BaseChangePositionEvent $event) + { + $object = CategoryQuery::create()->findPk($event->getCategoryId()); + + if ($object !== null) { + + // The required position + $new_position = $event->getPosition(); + + // The current position + $current_position = $object->getPosition(); + + if ($new_position != null && $new_position > 0 && $new_position != $current_position) { + + // Find categories to offset + $search = CategoryQuery::create()->filterByParent($object->getParent()); + + if ($new_position > $current_position) { + // The new position is after the current position -> we will offset + 1 all categories located between us and the new position + $search->filterByPosition(array('min' => 1+$current_position, 'max' => $new_position)); + + $delta = -1; + } else { + // The new position is brefore the current position -> we will offset - 1 all categories located between us and the new position + $search->filterByPosition(array('min' => $new_position, 'max' => $current_position - 1)); + + $delta = 1; + } + + $results = $search->find(); + + $cnx = Propel::getWriteConnection(CategoryTableMap::DATABASE_NAME); + + $cnx->beginTransaction(); + + try { + foreach ($results as $result) { + $result->setPosition($result->getPosition() + $delta)->save($cnx); + } + + $object + ->setDispatcher($this->getDispatcher()) + ->setPosition($new_position) + ->save($cnx) + ; + + $cnx->commit(); + } catch (Exception $e) { + $cnx->rollback(); + } + } + } + } +} \ No newline at end of file diff --git a/core/lib/Thelia/Config/Resources/routing/admin.xml b/core/lib/Thelia/Config/Resources/routing/admin.xml index 9f56548ed..ac652017c 100755 --- a/core/lib/Thelia/Config/Resources/routing/admin.xml +++ b/core/lib/Thelia/Config/Resources/routing/admin.xml @@ -53,7 +53,7 @@ Thelia\Controller\Admin\ConfigController::changeAction - + Thelia\Controller\Admin\ConfigController::saveChangeAction @@ -75,7 +75,7 @@ Thelia\Controller\Admin\MessageController::changeAction - + Thelia\Controller\Admin\MessageController::saveChangeAction @@ -97,7 +97,7 @@ Thelia\Controller\Admin\CurrencyController::changeAction - + Thelia\Controller\Admin\CurrencyController::saveChangeAction diff --git a/core/lib/Thelia/Controller/Admin/ConfigController.php b/core/lib/Thelia/Controller/Admin/ConfigController.php index b84368c2f..a67ecbaaa 100644 --- a/core/lib/Thelia/Controller/Admin/ConfigController.php +++ b/core/lib/Thelia/Controller/Admin/ConfigController.php @@ -241,7 +241,7 @@ class ConfigController extends BaseAdminController if ($this->getRequest()->get('save_mode') == 'stay') { $this->redirectToRoute( - "admin.configuration.variables.update", + "admin.configuration.variables.change", array('variable_id' => $variable_id) ); } diff --git a/core/lib/Thelia/Core/Event/BaseUpdatePositionEvent.php b/core/lib/Thelia/Core/Event/BaseUpdatePositionEvent.php new file mode 100644 index 000000000..3587a0757 --- /dev/null +++ b/core/lib/Thelia/Core/Event/BaseUpdatePositionEvent.php @@ -0,0 +1,87 @@ +. */ +/* */ +/*************************************************************************************/ + +namespace Thelia\Core\Event; + +class BaseUpdatePositionEvent extends ActionEvent +{ + const POSITION_UP = 1; + const POSITION_DOWN = 2; + const POSITION_ABSOLUTE = 3; + + protected $object_id; + protected $mode; + protected $position; + + protected $object; + + public function __construct($object_id, $mode, $position = null) + { + $this->object_id = $object_id; + $this->mode = $mode; + $this->position = $position; + } + + public function getMode() + { + return $this->mode; + } + + public function setMode($mode) + { + $this->mode = $mode; + return $this; + } + + public function getPosition() + { + return $this->position; + } + + public function setPosition($position) + { + $this->position = $position; + return $this; + } + + public function getObjectId() + { + return $this->object_id; + } + + public function setObjectId($object_id) + { + $this->object_id = $object_id; + return $this; + } + + public function getObjectId() + { + return $this->object_id; + } + + public function setObjectId($object_id) + { + $this->object_id = $object_id; + } +} diff --git a/core/lib/Thelia/Core/Event/CurrencyUpdatePositionEvent.php b/core/lib/Thelia/Core/Event/CurrencyUpdatePositionEvent.php new file mode 100644 index 000000000..655e24c9a --- /dev/null +++ b/core/lib/Thelia/Core/Event/CurrencyUpdatePositionEvent.php @@ -0,0 +1,28 @@ +. */ +/* */ +/*************************************************************************************/ + +namespace Thelia\Core\Event; + +class CategoryChangePositionEvent extends BaseChangePositionEvent +{ +} \ No newline at end of file diff --git a/core/lib/Thelia/Core/Template/Smarty/Plugins/AdminUtilities.php b/core/lib/Thelia/Core/Template/Smarty/Plugins/AdminUtilities.php index 4e2c065fd..fcea9ffd6 100644 --- a/core/lib/Thelia/Core/Template/Smarty/Plugins/AdminUtilities.php +++ b/core/lib/Thelia/Core/Template/Smarty/Plugins/AdminUtilities.php @@ -70,7 +70,7 @@ class AdminUtilities extends AbstractSmartyPlugin if ($permissions == null || $this->securityContext->isGranted("ADMIN", array($permission))) { return sprintf( - '%s', + '%s', URL::getInstance()->absoluteUrl("$path/positionUp", array($url_parameter => $id)), $in_place_edit_class, $id, diff --git a/core/lib/Thelia/Core/Template/Smarty/Plugins/TheliaLoop.php b/core/lib/Thelia/Core/Template/Smarty/Plugins/TheliaLoop.php index e7ee5e995..a65e15a8e 100755 --- a/core/lib/Thelia/Core/Template/Smarty/Plugins/TheliaLoop.php +++ b/core/lib/Thelia/Core/Template/Smarty/Plugins/TheliaLoop.php @@ -136,8 +136,23 @@ class TheliaLoop extends AbstractSmartyPlugin } if ($loopResults->valid()) { + $loopResultRow = $loopResults->current(); + // On first iteration, save variables that may be overwritten by this loop + if (! isset($this->varstack[$name])) { + + $saved_vars = array(); + + $varlist = $loopResultRow->getVars(); + + foreach ($varlist as $var) { + $saved_vars[$var] = $template->getTemplateVars($var); + } + + $this->varstack[$name] = $saved_vars; + } + foreach ($loopResultRow->getVarVal() as $var => $val) { $template->assign($var, $val); } diff --git a/core/lib/Thelia/Core/TheliaHttpKernel.php b/core/lib/Thelia/Core/TheliaHttpKernel.php index 950f08832..20197d9c6 100755 --- a/core/lib/Thelia/Core/TheliaHttpKernel.php +++ b/core/lib/Thelia/Core/TheliaHttpKernel.php @@ -122,7 +122,9 @@ class TheliaHttpKernel extends HttpKernel */ protected function initParam(Request $request) { - // Ensure an instaciation of URL service + // Ensure an instaciation of URL service, which is accessed as a pseudo-singleton + // in the rest of the application. + // See Thelia\Tools\URL class. $this->container->get('thelia.url.manager'); $lang = $this->detectLang($request); diff --git a/core/lib/Thelia/Model/Tools/PositionManagementTrait.php b/core/lib/Thelia/Model/Tools/PositionManagementTrait.php new file mode 100644 index 000000000..eccbb00dd --- /dev/null +++ b/core/lib/Thelia/Model/Tools/PositionManagementTrait.php @@ -0,0 +1,176 @@ +. */ +/* */ +/*************************************************************************************/ + +namespace Thelia\Model\Tools; + +use Propel\Runtime\ActiveQuery\PropelQuery; +use Propel\Runtime\ActiveQuery\Criteria; + +trait PositionManagementTrait { + + /** + * Create an instancer of this object query + */ + private function createQuery() { + return PropelQuery::from(__CLASS__); + } + + /** + * Return the database name from this object's table map. + */ + private function getDatabaseNameFromMap() { + $class = new \ReflectionClass(self::TABLE_MAP); + + return $class->getConstant('DATABASE_NAME'); + } + + + /** + * Get the position of the next inserted object + */ + public function getNextPosition($parent) { + + $last = $this->createQuery() + ->filterByParent($parent) + ->orderByPosition(Criteria::DESC) + ->limit(1) + ->findOne() + ; + + return $last != null ? $last->getPosition() + 1 : 1; + } + + /** + * Move up a object + */ + protected function movePositionUp() { + $this->movePositionUpOrDown(true); + } + + /** + * Move down a object + */ + protected function movePositionDown() { + $this->movePositionUpOrDown(false); + } + + /** + * Move up or down a object + * + * @param the exchange mode: go up (POSITION_UP) or go down (POSITION_DOWN) + */ + protected function movePositionUpOrDown($up = true) + { + // The current position of the object + $my_position = $this->getPosition(); + + // Find object to exchange position with + $search = $this->createQuery() + ->filterByParent($this->getParent()); + + // Up or down ? + if ($up === true) { + // Find the object immediately before me + $search->filterByPosition(array('max' => $my_position-1))->orderByPosition(Criteria::DESC); + } + else { + // Find the object immediately after me + $search->filterByPosition(array('min' => $my_position+1))->orderByPosition(Criteria::ASC); + } + + $result = $search->findOne(); + + // If we found the proper object, exchange their positions + if ($result) { + + $cnx = Propel::getWriteConnection(CategoryTableMap::DATABASE_NAME); + + $cnx->beginTransaction(); + + try { + $this + ->setDispatcher($this->getDispatcher()) + ->setPosition($result->getPosition()) + ->save() + ; + + $result->setPosition($my_position)->save(); + + $cnx->commit(); + } catch (Exception $e) { + $cnx->rollback(); + } + } + } + + /** + * Changes object position + * + * @param newPosition + */ + protected function changeAbsolutePosition($newPosition) + { + // The current position + $current_position = $this->getPosition(); + + if ($newPosition != null && $newPosition > 0 && $newPosition != $current_position) { + + // Find categories to offset + $search = $this->createQuery()->filterByParent($this->getParent()); + + if ($newPosition > $current_position) { + // The new position is after the current position -> we will offset + 1 all categories located between us and the new position + $search->filterByPosition(array('min' => 1+$current_position, 'max' => $newPosition)); + + $delta = -1; + } else { + // The new position is brefore the current position -> we will offset - 1 all categories located between us and the new position + $search->filterByPosition(array('min' => $newPosition, 'max' => $current_position - 1)); + + $delta = 1; + } + + $results = $search->find(); + + $cnx = Propel::getWriteConnection(CategoryTableMap::DATABASE_NAME); + + $cnx->beginTransaction(); + + try { + foreach ($results as $result) { + $result->setPosition($result->getPosition() + $delta)->save($cnx); + } + + $this + ->setDispatcher($this->getDispatcher()) + ->setPosition($newPosition) + ->save($cnx) + ; + + $cnx->commit(); + } catch (Exception $e) { + $cnx->rollback(); + } + } + } +} \ No newline at end of file diff --git a/core/lib/Thelia/Tools/URL.php b/core/lib/Thelia/Tools/URL.php index b1fa6cd03..84d35b457 100755 --- a/core/lib/Thelia/Tools/URL.php +++ b/core/lib/Thelia/Tools/URL.php @@ -48,6 +48,8 @@ class URL public function __construct(ContainerInterface $container, $environment) { // Allow singleton style calls once intanciated. + // For this to work, the URL service has to be instanciated very early. This is done manually + // in TheliaHttpKernel, by calling $this->container->get('thelia.url.manager'); self::$instance = $this; $this->container = $container; diff --git a/templates/admin/default/categories.html b/templates/admin/default/categories.html index d4f753f91..fdf2ab093 100755 --- a/templates/admin/default/categories.html +++ b/templates/admin/default/categories.html @@ -85,17 +85,17 @@ - {loop name="category_list" type="category" visible="*" parent="{$current_category_id}" order="$category_order"} + {loop name="category_list" type="category" visible="*" parent=$current_category_id order=$category_order backend_context="1" lang=$lang_id} - {loop type="image" name="cat_image" source="category" source_id="$ID" limit="1" width="50" height="50" resize_mode="crop" backend_context="1"} + 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"} #TITLE {/loop} - {$TITLE} + {$ID} p={$POSITION} {$TITLE} diff --git a/templates/admin/default/currencies.html b/templates/admin/default/currencies.html index 98fd36272..d446de776 100644 --- a/templates/admin/default/currencies.html +++ b/templates/admin/default/currencies.html @@ -155,7 +155,7 @@
{loop type="auth" name="can_change" roles="ADMIN" permissions="admin.configuration.currencies.change"} - + {/loop} {loop type="auth" name="can_delete" roles="ADMIN" permissions="admin.configuration.currencies.delete"} diff --git a/templates/admin/default/currency-edit.html b/templates/admin/default/currency-edit.html index 4ac8bb7c5..173876ede 100644 --- a/templates/admin/default/currency-edit.html +++ b/templates/admin/default/currency-edit.html @@ -29,7 +29,7 @@
{form name="thelia.admin.currency.modification"} -
+
{* Be sure to get the currency ID, even if the form could not be validated *} diff --git a/templates/admin/default/message-edit.html b/templates/admin/default/message-edit.html index 71570f69a..92d707b45 100644 --- a/templates/admin/default/message-edit.html +++ b/templates/admin/default/message-edit.html @@ -29,7 +29,7 @@
{form name="thelia.admin.message.modification"} - +
{* Be sure to get the message ID, even if the form could not be validated *} diff --git a/templates/admin/default/messages.html b/templates/admin/default/messages.html index 257e64c0e..75ee742bf 100644 --- a/templates/admin/default/messages.html +++ b/templates/admin/default/messages.html @@ -19,7 +19,7 @@
- +
@@ -64,7 +64,7 @@ {if ! $SECURED}
{loop type="auth" name="can_change" roles="ADMIN" permissions="admin.configuration.messages.change"} - + {/loop} {loop type="auth" name="can_delete" roles="ADMIN" permissions="admin.configuration.messages.delete"} diff --git a/templates/admin/default/variable-edit.html b/templates/admin/default/variable-edit.html index 7c89ec378..80410c444 100644 --- a/templates/admin/default/variable-edit.html +++ b/templates/admin/default/variable-edit.html @@ -29,7 +29,7 @@
{form name="thelia.admin.config.modification"} - +
{* Be sure to get the variable ID, even if the form could not be validated *} diff --git a/templates/admin/default/variables.html b/templates/admin/default/variables.html index 307ee85d5..bbab0ae72 100644 --- a/templates/admin/default/variables.html +++ b/templates/admin/default/variables.html @@ -19,7 +19,7 @@
- +
@@ -102,7 +102,7 @@ {loop type="auth" name="can_change" roles="ADMIN" permissions="admin.configuration.variables.change"} - + {/loop} {loop type="auth" name="can_delete" roles="ADMIN" permissions="admin.configuration.variables.delete"}