diff --git a/core/lib/Thelia/Action/Cart.php b/core/lib/Thelia/Action/Cart.php index 019911265..9203dc00d 100755 --- a/core/lib/Thelia/Action/Cart.php +++ b/core/lib/Thelia/Action/Cart.php @@ -143,7 +143,7 @@ class Cart extends BaseAction implements EventSubscriberInterface return array( "action.addArticle" => array("addItem", 128), "action.deleteArticle" => array("deleteItem", 128), - "action.changeArticle" => array("changeItem", 128), + "action.updateArticle" => array("changeItem", 128), ); } diff --git a/core/lib/Thelia/Action/Category.php b/core/lib/Thelia/Action/Category.php index 28ca2fc9e..1b0568cbe 100755 --- a/core/lib/Thelia/Action/Category.php +++ b/core/lib/Thelia/Action/Category.php @@ -248,9 +248,9 @@ class Category extends BaseAction implements EventSubscriberInterface TheliaEvents::CATEGORY_TOGGLE_VISIBILITY => array("toggleVisibility", 128), TheliaEvents::CATEGORY_CHANGE_POSITION => array("changePosition", 128), - "action.changeCategoryPositionU" => array("changePositionUp", 128), - "action.changeCategoryPositionDown" => array("changePositionDown", 128), - "action.changeCategoryPosition" => array("changePosition", 128), + "action.updateCategoryPositionU" => array("changePositionUp", 128), + "action.updateCategoryPositionDown" => array("changePositionDown", 128), + "action.updateCategoryPosition" => array("changePosition", 128), ); } } diff --git a/core/lib/Thelia/Action/Config.php b/core/lib/Thelia/Action/Config.php index c7b016c63..e0a62f69d 100644 --- a/core/lib/Thelia/Action/Config.php +++ b/core/lib/Thelia/Action/Config.php @@ -31,7 +31,7 @@ use Thelia\Model\Config as ConfigModel; use Thelia\Core\Event\TheliaEvents; -use Thelia\Core\Event\ConfigChangeEvent; +use Thelia\Core\Event\ConfigUpdateEvent; use Thelia\Core\Event\ConfigCreateEvent; use Thelia\Core\Event\ConfigDeleteEvent; @@ -65,9 +65,9 @@ class Config extends BaseAction implements EventSubscriberInterface /** * Change a configuration entry value * - * @param ConfigChangeEvent $event + * @param ConfigUpdateEvent $event */ - public function setValue(ConfigChangeEvent $event) + public function setValue(ConfigUpdateEvent $event) { $search = ConfigQuery::create(); @@ -90,9 +90,9 @@ class Config extends BaseAction implements EventSubscriberInterface /** * Change a configuration entry * - * @param ConfigChangeEvent $event + * @param ConfigUpdateEvent $event */ - public function modify(ConfigChangeEvent $event) + public function modify(ConfigUpdateEvent $event) { $search = ConfigQuery::create(); diff --git a/core/lib/Thelia/Action/Currency.php b/core/lib/Thelia/Action/Currency.php index c0ca41d60..7f9c3281f 100644 --- a/core/lib/Thelia/Action/Currency.php +++ b/core/lib/Thelia/Action/Currency.php @@ -31,11 +31,11 @@ use Thelia\Model\Currency as CurrencyModel; use Thelia\Core\Event\TheliaEvents; -use Thelia\Core\Event\CurrencyChangeEvent; +use Thelia\Core\Event\CurrencyUpdateEvent; use Thelia\Core\Event\CurrencyCreateEvent; use Thelia\Core\Event\CurrencyDeleteEvent; -use Thelia\Model\Map\CurrencyTableMap; use Thelia\Model\ConfigQuery; +use Thelia\Core\Event\CurrencyUpdatePositionEvent; class Currency extends BaseAction implements EventSubscriberInterface { @@ -67,9 +67,9 @@ class Currency extends BaseAction implements EventSubscriberInterface /** * Change a currency * - * @param CurrencyChangeEvent $event + * @param CurrencyUpdateEvent $event */ - public function update(CurrencyChangeEvent $event) + public function update(CurrencyUpdateEvent $event) { $search = CurrencyQuery::create(); @@ -93,9 +93,9 @@ class Currency extends BaseAction implements EventSubscriberInterface /** * Set the default currency * - * @param CurrencyChangeEvent $event + * @param CurrencyUpdateEvent $event */ - public function setDefault(CurrencyChangeEvent $event) + public function setDefault(CurrencyUpdateEvent $event) { $search = CurrencyQuery::create(); @@ -107,6 +107,7 @@ class Currency extends BaseAction implements EventSubscriberInterface CurrencyQuery::create()->filterByByDefault(true)->update(array('ByDefault' => false)); $currency + ->setDispatcher($this->getDispatcher()) ->setByDefault($event->getIsDefault()) ->save() ; @@ -139,7 +140,7 @@ class Currency extends BaseAction implements EventSubscriberInterface $rates_url = ConfigQuery::read('currency_rate_update_url', 'http://www.ecb.int/stats/eurofxref/eurofxref-daily.xml'); - $rate_data = file_get_contents($rates_url); + $rate_data = @file_get_contents($rates_url); if ($rate_data && $sxe = new \SimpleXMLElement($rate_data)) { @@ -149,12 +150,38 @@ class Currency extends BaseAction implements EventSubscriberInterface $rate = floatval($last['rate']); if (null !== $currency = CurrencyQuery::create()->findOneByCode($code)) { - $currency->setRate($rate)->save(); + $currency + ->setDispatcher($this->getDispatcher()) + ->setRate($rate) + ->save() + ; } } } else { - throw new \RuntimeException(sprintf("Failed to get currency rates data from URL %s", $url)); + throw new \RuntimeException(sprintf("Failed to get currency rates data from URL %s", $rates_url)); + } + } + + /** + * Changes position, selecting absolute ou relative change. + * + * @param CategoryChangePositionEvent $event + */ + public function updatePosition(CurrencyUpdatePositionEvent $event) + { + if (null !== $currency = CurrencyQuery::create()->findOneById($event->getObjectId())) { + + $currency->setDispatcher($this->getDispatcher()); + + $mode = $event->getMode(); + + if ($mode == CurrencyUpdatePositionEvent::POSITION_ABSOLUTE) + return $currency->changeAbsolutePosition($event->getPosition()); + else if ($mode == CurrencyUpdatePositionEvent::POSITION_UP) + return $currency->movePositionUp(); + else if ($mode == CurrencyUpdatePositionEvent::POSITION_DOWN) + return $currency->movePositionDown(); } } @@ -164,12 +191,12 @@ class Currency extends BaseAction implements EventSubscriberInterface public static function getSubscribedEvents() { return array( - TheliaEvents::CURRENCY_CREATE => array("create", 128), - TheliaEvents::CURRENCY_UPDATE => array("update", 128), - TheliaEvents::CURRENCY_DELETE => array("delete", 128), - TheliaEvents::CURRENCY_SET_DEFAULT => array("setDefault", 128), - TheliaEvents::CURRENCY_UPDATE_RATES => array("updateRates", 128), - + TheliaEvents::CURRENCY_CREATE => array("create", 128), + TheliaEvents::CURRENCY_UPDATE => array("update", 128), + TheliaEvents::CURRENCY_DELETE => array("delete", 128), + TheliaEvents::CURRENCY_SET_DEFAULT => array("setDefault", 128), + TheliaEvents::CURRENCY_UPDATE_RATES => array("updateRates", 128), + TheliaEvents::CURRENCY_UPDATE_POSITION => array("updatePosition", 128) ); } } diff --git a/core/lib/Thelia/Action/Image.php b/core/lib/Thelia/Action/Image.php index eabf03b0a..103633165 100755 --- a/core/lib/Thelia/Action/Image.php +++ b/core/lib/Thelia/Action/Image.php @@ -269,8 +269,8 @@ class Image extends BaseAction implements EventSubscriberInterface $event->setCacheFilepath($cacheFilePath); $event->setCacheOriginalFilepath($originalImagePathInCache); - $event->setFileUrl(URL::absoluteUrl($processed_image_url, null, URL::PATH_TO_FILE)); - $event->setOriginalFileUrl(URL::absoluteUrl($original_image_url, null, URL::PATH_TO_FILE)); + $event->setFileUrl(URL::getInstance()->absoluteUrl($processed_image_url, null, URL::PATH_TO_FILE)); + $event->setOriginalFileUrl(URL::getInstance()->absoluteUrl($original_image_url, null, URL::PATH_TO_FILE)); } /** @@ -382,7 +382,7 @@ class Image extends BaseAction implements EventSubscriberInterface { $path = $this->getCachePathFromWebRoot($subdir); - return URL::absoluteUrl(sprintf("%s/%s", $path, $safe_filename), null, URL::PATH_TO_FILE); + return URL::getInstance()->absoluteUrl(sprintf("%s/%s", $path, $safe_filename), null, URL::PATH_TO_FILE); } /** @@ -494,4 +494,4 @@ class Image extends BaseAction implements EventSubscriberInterface TheliaEvents::IMAGE_CLEAR_CACHE => array("clearCache", 128), ); } -} +} \ No newline at end of file diff --git a/core/lib/Thelia/Action/Message.php b/core/lib/Thelia/Action/Message.php index f2335bd82..5299efc99 100644 --- a/core/lib/Thelia/Action/Message.php +++ b/core/lib/Thelia/Action/Message.php @@ -31,7 +31,7 @@ use Thelia\Model\Message as MessageModel; use Thelia\Core\Event\TheliaEvents; -use Thelia\Core\Event\MessageChangeEvent; +use Thelia\Core\Event\MessageUpdateEvent; use Thelia\Core\Event\MessageCreateEvent; use Thelia\Core\Event\MessageDeleteEvent; @@ -65,9 +65,9 @@ class Message extends BaseAction implements EventSubscriberInterface /** * Change a message * - * @param MessageChangeEvent $event + * @param MessageUpdateEvent $event */ - public function modify(MessageChangeEvent $event) + public function modify(MessageUpdateEvent $event) { $search = MessageQuery::create(); 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/Command/CacheClear.php b/core/lib/Thelia/Command/CacheClear.php index 82a7cbd7f..37e853f05 100755 --- a/core/lib/Thelia/Command/CacheClear.php +++ b/core/lib/Thelia/Command/CacheClear.php @@ -24,6 +24,7 @@ namespace Thelia\Command; use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Filesystem\Filesystem; use Symfony\Component\Filesystem\Exception\IOException; @@ -44,7 +45,14 @@ class CacheClear extends ContainerAwareCommand { $this ->setName("cache:clear") - ->setDescription("Invalidate all caches"); + ->setDescription("Invalidate all caches") + ->addOption( + "without-assets", + null, + InputOption::VALUE_NONE, + "remove cache assets" + ) + ; } protected function execute(InputInterface $input, OutputInterface $output) @@ -52,20 +60,28 @@ class CacheClear extends ContainerAwareCommand $cacheDir = $this->getContainer()->getParameter("kernel.cache_dir"); - if (!is_writable($cacheDir)) { - throw new \RuntimeException(sprintf('Unable to write in the "%s" directory', $cacheDir)); - } - - $output->writeln(sprintf("Clearing cache in %s directory", $cacheDir)); - - $fs = new Filesystem(); - try { - $fs->remove($cacheDir); - - $output->writeln("cache cleared successfully"); - } catch (IOException $e) { - $output->writeln(sprintf("error during clearing cache : %s", $e->getMessage())); + $this->clearCache($cacheDir, $output); + if(!$input->getOption("without-assets")) { + $this->clearCache(THELIA_WEB_DIR . "/assets", $output); } } + + protected function clearCache($dir, OutputInterface $output) + { + if (!is_writable($dir)) { + throw new \RuntimeException(sprintf('Unable to write in the "%s" directory', $dir)); + } + + $output->writeln(sprintf("Clearing cache in %s directory", $dir)); + + $fs = new Filesystem(); + try { + $fs->remove($dir); + + $output->writeln(sprintf("%s cache dir cleared successfully", $dir)); + } catch (IOException $e) { + $output->writeln(sprintf("error during clearing cache : %s", $e->getMessage())); + } + } } diff --git a/core/lib/Thelia/Config/Resources/action.xml b/core/lib/Thelia/Config/Resources/action.xml index 66c189ec6..dd43bbb91 100755 --- a/core/lib/Thelia/Config/Resources/action.xml +++ b/core/lib/Thelia/Config/Resources/action.xml @@ -22,6 +22,11 @@ + + + + + diff --git a/core/lib/Thelia/Config/Resources/config.xml b/core/lib/Thelia/Config/Resources/config.xml index e94fbf1fe..70eeb6ba8 100755 --- a/core/lib/Thelia/Config/Resources/config.xml +++ b/core/lib/Thelia/Config/Resources/config.xml @@ -44,6 +44,9 @@
+ + + @@ -75,6 +78,13 @@ + + + + + %kernel.environment% + + @@ -122,6 +132,7 @@ + %kernel.environment% diff --git a/core/lib/Thelia/Config/Resources/routing.xml b/core/lib/Thelia/Config/Resources/routing.xml index 5200aefc1..a8ad18abf 100755 --- a/core/lib/Thelia/Config/Resources/routing.xml +++ b/core/lib/Thelia/Config/Resources/routing.xml @@ -75,6 +75,10 @@ %kernel.debug% + + + + diff --git a/core/lib/Thelia/Config/Resources/routing/admin.xml b/core/lib/Thelia/Config/Resources/routing/admin.xml index e32865f15..3510a6b87 100755 --- a/core/lib/Thelia/Config/Resources/routing/admin.xml +++ b/core/lib/Thelia/Config/Resources/routing/admin.xml @@ -70,7 +70,7 @@ Thelia\Controller\Admin\ConfigController::defaultAction - + Thelia\Controller\Admin\ConfigController::changeValuesAction @@ -78,11 +78,11 @@ Thelia\Controller\Admin\ConfigController::createAction - + Thelia\Controller\Admin\ConfigController::changeAction - + Thelia\Controller\Admin\ConfigController::saveChangeAction @@ -100,11 +100,11 @@ Thelia\Controller\Admin\MessageController::createAction - + Thelia\Controller\Admin\MessageController::changeAction - + Thelia\Controller\Admin\MessageController::saveChangeAction @@ -122,11 +122,11 @@ Thelia\Controller\Admin\CurrencyController::createAction - + Thelia\Controller\Admin\CurrencyController::changeAction - + Thelia\Controller\Admin\CurrencyController::saveChangeAction @@ -142,6 +142,10 @@ Thelia\Controller\Admin\CurrencyController::deleteAction + + Thelia\Controller\Admin\CurrencyController::updatePositionAction + + diff --git a/core/lib/Thelia/Config/Resources/routing/front.xml b/core/lib/Thelia/Config/Resources/routing/front.xml index d696a3411..f8b156946 100755 --- a/core/lib/Thelia/Config/Resources/routing/front.xml +++ b/core/lib/Thelia/Config/Resources/routing/front.xml @@ -29,8 +29,18 @@ - - Thelia\Controller\Front\CustomerAddressController::createAction + + Thelia\Controller\Front\AddressController::createAction + address + + + + Thelia\Controller\Front\DefaultController::noAction + address_edit + + + + Thelia\Controller\Front\AddressController::updateAction @@ -40,7 +50,7 @@ cart - + Thelia\Controller\Front\CartController::deleteItem cart @@ -50,9 +60,4 @@ cart - - - - - diff --git a/core/lib/Thelia/Controller/Admin/BaseAdminController.php b/core/lib/Thelia/Controller/Admin/BaseAdminController.php index 949344685..eab57394b 100755 --- a/core/lib/Thelia/Controller/Admin/BaseAdminController.php +++ b/core/lib/Thelia/Controller/Admin/BaseAdminController.php @@ -85,10 +85,16 @@ class BaseAdminController extends BaseController /** * Return a general error page * + * @param mixed $message a message string, or an exception instance + * * @return \Symfony\Component\HttpFoundation\Response */ protected function errorPage($message) { + if ($message instanceof \Exception) { + $message = sprintf("Sorry, an error occured: %s", $message->getMessage()); + } + return $this->render('general_error', array( "error_message" => $message) ); @@ -168,24 +174,31 @@ class BaseAdminController extends BaseController * @param unknown $urlParameters the URL parametrs, as a var/value pair array */ public function redirectToRoute($routeId, $urlParameters = array()) { - $this->redirect(URL::absoluteUrl($this->getRoute($routeId), $urlParameters)); + $this->redirect(URL::getInstance()->absoluteUrl($this->getRoute($routeId), $urlParameters)); } /** - * Get the current edition lang ID, checking if a change was requested in the current request + * Get the current edition lang ID, checking if a change was requested in the current request. */ - protected function getCurrentEditionLangId() { - return $this->getRequest()->get( - 'edit_language_id', - $this->getSession()->getAdminEditionLangId() - ); + protected function getCurrentEditionLang() { + + // Return the new language if a change is required. + if (null !== $edit_language_id = $this->getRequest()->get('edit_language_id', null)) { + + if (null !== $edit_language = LangQuery::create()->findOneById($edit_language_id)) { + return $edit_language; + } + } + + // Otherwise return the lang stored in session. + return $this->getSession()->getAdminEditionLang(); } /** - * A simple helper to get the current edition locale, from the session edition language ID + * A simple helper to get the current edition locale. */ protected function getCurrentEditionLocale() { - return LangQuery::create()->findOneById($this->getCurrentEditionLangId())->getLocale(); + return $this->getCurrentEditionLang()->getLocale(); } /** @@ -217,23 +230,14 @@ class BaseAdminController extends BaseController $session = $this->getSession(); - $edition_language = $this->getCurrentEditionLangId(); - - // Current back-office (not edition) language - $current_lang = LangQuery::create()->findOneById($session->getLangId()); - // Find the current edit language ID - $edition_language = LangQuery::create()->findOneById($this->getCurrentEditionLangId()); + $edition_language = $this->getCurrentEditionLang(); // Prepare common template variables $args = array_merge($args, array( - 'locale' => $session->getLocale(), - 'lang_code' => $session->getLang(), - 'lang_id' => $session->getLangId(), - - 'datetime_format' => $current_lang->getDateTimeFormat(), - 'date_format' => $current_lang->getDateFormat(), - 'time_format' => $current_lang->getTimeFormat(), + 'locale' => $session->getLang()->getLocale(), + 'lang_code' => $session->getLang()->getCode(), + 'lang_id' => $session->getLang()->getId(), 'edit_language_id' => $edition_language->getId(), 'edit_language_locale' => $edition_language->getLocale(), @@ -242,7 +246,7 @@ class BaseAdminController extends BaseController )); // Update the current edition language in session - $this->getSession()->setAdminEditionLangId($edition_language->getId()); + $this->getSession()->setAdminEditionLang($edition_language); // Render the template. try { @@ -253,7 +257,7 @@ class BaseAdminController extends BaseController catch (AuthenticationException $ex) { // User is not authenticated, and templates requires authentication -> redirect to login page // We user login_tpl as a path, not a template. - Redirect::exec(URL::absoluteUrl($ex->getLoginTemplate())); + Redirect::exec(URL::getInstance()->absoluteUrl($ex->getLoginTemplate())); } catch (AuthorizationException $ex) { // User is not allowed to perform the required action. Return the error page instead of the requested page. diff --git a/core/lib/Thelia/Controller/Admin/ConfigController.php b/core/lib/Thelia/Controller/Admin/ConfigController.php index d6bcc09ab..a67ecbaaa 100644 --- a/core/lib/Thelia/Controller/Admin/ConfigController.php +++ b/core/lib/Thelia/Controller/Admin/ConfigController.php @@ -26,7 +26,7 @@ namespace Thelia\Controller\Admin; use Thelia\Core\Event\ConfigDeleteEvent; use Thelia\Core\Event\TheliaEvents; use Thelia\Tools\URL; -use Thelia\Core\Event\ConfigChangeEvent; +use Thelia\Core\Event\ConfigUpdateEvent; use Thelia\Core\Event\ConfigCreateEvent; use Thelia\Log\Tlog; use Thelia\Form\Exception\FormValidationException; @@ -154,7 +154,7 @@ class ConfigController extends BaseAdminController public function changeAction() { // Check current user authorization - if (null !== $response = $this->checkAuth("admin.configuration.variables.change")) return $response; + if (null !== $response = $this->checkAuth("admin.configuration.variables.update")) return $response; // Load the config object $config = ConfigQuery::create() @@ -196,7 +196,7 @@ class ConfigController extends BaseAdminController public function saveChangeAction() { // Check current user authorization - if (null !== $response = $this->checkAuth("admin.configuration.variables.change")) return $response; + if (null !== $response = $this->checkAuth("admin.configuration.variables.update")) return $response; $message = false; @@ -214,7 +214,7 @@ class ConfigController extends BaseAdminController // Get the form field values $data = $form->getData(); - $changeEvent = new ConfigChangeEvent($data['id']); + $changeEvent = new ConfigUpdateEvent($data['id']); // Create and dispatch the change event $changeEvent @@ -284,13 +284,13 @@ class ConfigController extends BaseAdminController public function changeValuesAction() { // Check current user authorization - if (null !== $response = $this->checkAuth("admin.configuration.variables.change")) return $response; + if (null !== $response = $this->checkAuth("admin.configuration.variables.update")) return $response; $variables = $this->getRequest()->get('variable', array()); // Process all changed variables foreach($variables as $id => $value) { - $event = new ConfigChangeEvent($id); + $event = new ConfigUpdateEvent($id); $event->setValue($value); $this->dispatch(TheliaEvents::CONFIG_SETVALUE, $event); diff --git a/core/lib/Thelia/Controller/Admin/CurrencyController.php b/core/lib/Thelia/Controller/Admin/CurrencyController.php index 71814d05b..56acfb89d 100644 --- a/core/lib/Thelia/Controller/Admin/CurrencyController.php +++ b/core/lib/Thelia/Controller/Admin/CurrencyController.php @@ -26,7 +26,7 @@ namespace Thelia\Controller\Admin; use Thelia\Core\Event\CurrencyDeleteEvent; use Thelia\Core\Event\TheliaEvents; use Thelia\Tools\URL; -use Thelia\Core\Event\CurrencyChangeEvent; +use Thelia\Core\Event\CurrencyUpdateEvent; use Thelia\Core\Event\CurrencyCreateEvent; use Thelia\Log\Tlog; use Thelia\Form\Exception\FormValidationException; @@ -34,6 +34,7 @@ use Thelia\Core\Security\Exception\AuthorizationException; use Thelia\Model\CurrencyQuery; use Thelia\Form\CurrencyModificationForm; use Thelia\Form\CurrencyCreationForm; +use Thelia\Core\Event\CurrencyUpdatePositionEvent; /** * Manages currencies sent by mail @@ -124,7 +125,7 @@ class CurrencyController extends BaseAdminController } catch (\Exception $ex) { // Any other error - $error_msg = sprintf("Sorry, an error occured: %s", $ex->getMessage()); + $error_msg = $ex; } if ($error_msg !== false) { @@ -153,7 +154,7 @@ class CurrencyController extends BaseAdminController public function changeAction() { // Check current user authorization - if (null !== $response = $this->checkAuth("admin.configuration.currencies.change")) return $response; + if (null !== $response = $this->checkAuth("admin.configuration.currencies.update")) return $response; // Load the currency object $currency = CurrencyQuery::create() @@ -191,7 +192,7 @@ class CurrencyController extends BaseAdminController public function saveChangeAction() { // Check current user authorization - if (null !== $response = $this->checkAuth("admin.configuration.currencies.change")) return $response; + if (null !== $response = $this->checkAuth("admin.configuration.currencies.update")) return $response; $error_msg = false; @@ -209,7 +210,7 @@ class CurrencyController extends BaseAdminController // Get the form field values $data = $form->getData(); - $changeEvent = new CurrencyChangeEvent($data['id']); + $changeEvent = new CurrencyUpdateEvent($data['id']); // Create and dispatch the change event $changeEvent @@ -231,7 +232,7 @@ class CurrencyController extends BaseAdminController // just redirect to the edit page again. if ($this->getRequest()->get('save_mode') == 'stay') { $this->redirectToRoute( - "admin.configuration.currencies.change", + "admin.configuration.currencies.update", array('currency_id' => $currency_id) ); } @@ -245,7 +246,7 @@ class CurrencyController extends BaseAdminController } catch (\Exception $ex) { // Any other error - $error_msg = sprintf("Sorry, an error occured: %s", $ex->getMessage()); + $error_msg = $ex; } if ($error_msg !== false) { @@ -271,9 +272,9 @@ class CurrencyController extends BaseAdminController */ public function setDefaultAction() { // Check current user authorization - if (null !== $response = $this->checkAuth("admin.configuration.currencies.change")) return $response; + if (null !== $response = $this->checkAuth("admin.configuration.currencies.update")) return $response; - $changeEvent = new CurrencyChangeEvent($this->getRequest()->get('currency_id', 0)); + $changeEvent = new CurrencyUpdateEvent($this->getRequest()->get('currency_id', 0)); // Create and dispatch the change event $changeEvent->setIsDefault(true); @@ -283,7 +284,7 @@ class CurrencyController extends BaseAdminController } catch (\Exception $ex) { // Any error - return $this->errorPage(sprintf("Sorry, an error occured: %s", $ex->getMessage())); + return $this->errorPage($ex); } $this->redirectToRoute('admin.configuration.currencies.default'); @@ -294,19 +295,55 @@ class CurrencyController extends BaseAdminController */ public function updateRatesAction() { // Check current user authorization - if (null !== $response = $this->checkAuth("admin.configuration.currencies.change")) return $response; + if (null !== $response = $this->checkAuth("admin.configuration.currencies.update")) return $response; try { $this->dispatch(TheliaEvents::CURRENCY_UPDATE_RATES); } catch (\Exception $ex) { // Any error - return $this->errorPage(sprintf("Sorry, an error occured: %s", $ex->getMessage())); + return $this->errorPage($ex); } $this->redirectToRoute('admin.configuration.currencies.default'); } + /** + * Update currencyposition + */ + public function updatePositionAction() { + // Check current user authorization + if (null !== $response = $this->checkAuth("admin.configuration.currencies.update")) return $response; + + try { + $mode = $this->getRequest()->get('mode', null); + + if ($mode == 'up') + $mode = CurrencyUpdatePositionEvent::POSITION_UP; + else if ($mode == 'down') + $mode = CurrencyUpdatePositionEvent::POSITION_DOWN; + else + $mode = CurrencyUpdatePositionEvent::POSITION_ABSOLUTE; + + $position = $this->getRequest()->get('position', null); + + $event = new CurrencyUpdatePositionEvent( + $this->getRequest()->get('currency_id', null), + $mode, + $this->getRequest()->get('position', null) + ); + + $this->dispatch(TheliaEvents::CURRENCY_UPDATE_POSITION, $event); + } + catch (\Exception $ex) { + // Any error + return $this->errorPage($ex); + } + + $this->redirectToRoute('admin.configuration.currencies.default'); + } + + /** * Delete a currency object * diff --git a/core/lib/Thelia/Controller/Admin/MessageController.php b/core/lib/Thelia/Controller/Admin/MessageController.php index 5cc3d2734..c024a5d34 100644 --- a/core/lib/Thelia/Controller/Admin/MessageController.php +++ b/core/lib/Thelia/Controller/Admin/MessageController.php @@ -26,7 +26,7 @@ namespace Thelia\Controller\Admin; use Thelia\Core\Event\MessageDeleteEvent; use Thelia\Core\Event\TheliaEvents; use Thelia\Tools\URL; -use Thelia\Core\Event\MessageChangeEvent; +use Thelia\Core\Event\MessageUpdateEvent; use Thelia\Core\Event\MessageCreateEvent; use Thelia\Log\Tlog; use Thelia\Form\Exception\FormValidationException; @@ -133,7 +133,7 @@ class MessageController extends BaseAdminController public function changeAction() { // Check current user authorization - if (null !== $response = $this->checkAuth("admin.configuration.messages.change")) return $response; + if (null !== $response = $this->checkAuth("admin.configuration.messages.update")) return $response; // Load the message object $message = MessageQuery::create() @@ -173,7 +173,7 @@ class MessageController extends BaseAdminController public function saveChangeAction() { // Check current user authorization - if (null !== $response = $this->checkAuth("admin.configuration.messages.change")) return $response; + if (null !== $response = $this->checkAuth("admin.configuration.messages.update")) return $response; $message = false; @@ -191,7 +191,7 @@ class MessageController extends BaseAdminController // Get the form field values $data = $form->getData(); - $changeEvent = new MessageChangeEvent($data['id']); + $changeEvent = new MessageUpdateEvent($data['id']); // Create and dispatch the change event $changeEvent @@ -215,7 +215,7 @@ class MessageController extends BaseAdminController // just redirect to the edit page again. if ($this->getRequest()->get('save_mode') == 'stay') { $this->redirectToRoute( - "admin.configuration.messages.change", + "admin.configuration.messages.update", array('message_id' => $message_id) ); } @@ -265,6 +265,6 @@ class MessageController extends BaseAdminController $this->dispatch(TheliaEvents::MESSAGE_DELETE, $event); - $this->redirect(URL::adminViewUrl('messages')); + $this->redirect(URL::getInstance()->adminViewUrl('messages')); } } \ No newline at end of file diff --git a/core/lib/Thelia/Controller/Front/CustomerAddressController.php b/core/lib/Thelia/Controller/Front/AddressController.php similarity index 53% rename from core/lib/Thelia/Controller/Front/CustomerAddressController.php rename to core/lib/Thelia/Controller/Front/AddressController.php index 4f44ce1d1..08e803556 100644 --- a/core/lib/Thelia/Controller/Front/CustomerAddressController.php +++ b/core/lib/Thelia/Controller/Front/AddressController.php @@ -24,33 +24,42 @@ namespace Thelia\Controller\Front; use Thelia\Core\Event\AddressCreateOrUpdateEvent; use Thelia\Core\Event\TheliaEvents; -use Thelia\Form\AddressForm; +use Thelia\Form\AddressCreateForm; +use Thelia\Form\AddressUpdateForm; use Thelia\Form\Exception\FormValidationException; +use Thelia\Model\Base\AddressQuery; use Thelia\Model\Customer; use Thelia\Tools\URL; /** - * Class CustomerAddressController + * Class AddressController * @package Thelia\Controller\Front * @author Manuel Raynaud */ -class CustomerAddressController extends BaseFrontController +class AddressController extends BaseFrontController { + /** + * Create controller. + * Check if customer is logged in + * + * Dispatch TheliaEvents::ADDRESS_CREATE event + */ public function createAction() { if ($this->getSecurityContext()->hasCustomerUser() === false) { - $this->redirect(URL::getIndexPage()); + $this->redirect(URL::getInstance()->getIndexPage()); } - $addressCreate = new AddressForm($this->getRequest()); + $addressCreate = new AddressCreateForm($this->getRequest()); try { $customer = $this->getSecurityContext()->getCustomerUser(); $form = $this->validateForm($addressCreate, "post"); - $event = $this->createAddressEvent($form->getData(), $customer); + $event = $this->createAddressEvent($form); + $event->setCustomer($customer); $this->dispatch(TheliaEvents::ADDRESS_CREATE, $event); $this->redirectSuccess($addressCreate); @@ -74,23 +83,76 @@ class CustomerAddressController extends BaseFrontController } } - protected function createAddressEvent($data, Customer $customer) + public function updateAction() + { + $request = $this->getRequest(); + + if ($this->getSecurityContext()->hasCustomerUser() === false) { + $this->redirectToRoute("home"); + } + + if(null === $address_id = $request->get("address_id")) { + $this->redirectToRoute("home"); + } + + $addressUpdate = new AddressUpdateForm($request); + + try { + $customer = $this->getSecurityContext()->getCustomerUser(); + + $form = $this->validateForm($addressUpdate); + + $address = AddressQuery::create()->findPk($address_id); + + if (null === $address) { + $this->redirectToRoute("home"); + } + + if($address->getCustomer()->getId() != $customer->getId()) { + $this->redirectToRoute("home"); + } + + $event = $this->createAddressEvent($form); + $event->setAddress($address); + + $this->dispatch(TheliaEvents::ADDRESS_UPDATE, $event); + + $this->redirectSuccess($addressUpdate); + }catch (FormValidationException $e) { + $message = sprintf("Please check your input: %s", $e->getMessage()); + } + catch (\Exception $e) { + $message = sprintf("Sorry, an error occured: %s", $e->getMessage()); + } + + if ($message !== false) { + \Thelia\Log\Tlog::getInstance()->error(sprintf("Error during address creation process : %s", $message)); + + $addressUpdate->setErrorMessage($message); + + $this->getParserContext() + ->addForm($addressUpdate) + ->setGeneralError($message) + ; + } + } + + protected function createAddressEvent($form) { return new AddressCreateOrUpdateEvent( - $data["label"], - $data["title"], - $data["firstname"], - $data["lastname"], - $data["address1"], - $data["address2"], - $data["address3"], - $data["zipcode"], - $data["city"], - $data["country"], - $data["cellpone"], - $data["phone"], - $data["company"], - $customer + $form->get("label")->getData(), + $form->get("title")->getData(), + $form->get("firstname")->getData(), + $form->get("lastname")->getData(), + $form->get("address1")->getData(), + $form->get("address2")->getData(), + $form->get("address3")->getData(), + $form->get("zipcode")->getData(), + $form->get("city")->getData(), + $form->get("country")->getData(), + $form->get("cellphone")->getData(), + $form->get("phone")->getData(), + $form->get("company")->getData() ); } } \ No newline at end of file diff --git a/core/lib/Thelia/Controller/Front/BaseFrontController.php b/core/lib/Thelia/Controller/Front/BaseFrontController.php index b6d4a6f93..e92387a83 100755 --- a/core/lib/Thelia/Controller/Front/BaseFrontController.php +++ b/core/lib/Thelia/Controller/Front/BaseFrontController.php @@ -23,6 +23,7 @@ namespace Thelia\Controller\Front; use Thelia\Controller\BaseController; +use Thelia\Tools\URL; class BaseFrontController extends BaseController { @@ -44,6 +45,6 @@ class BaseFrontController extends BaseController * @param unknown $urlParameters the URL parametrs, as a var/value pair array */ public function redirectToRoute($routeId, $urlParameters = array()) { - $this->redirect(URL::absoluteUrl($this->getRoute($routeId), $urlParameters)); + $this->redirect(URL::getInstance()->absoluteUrl($this->getRoute($routeId), $urlParameters)); } } diff --git a/core/lib/Thelia/Controller/Front/CustomerController.php b/core/lib/Thelia/Controller/Front/CustomerController.php index 88e15396e..d753510a6 100755 --- a/core/lib/Thelia/Controller/Front/CustomerController.php +++ b/core/lib/Thelia/Controller/Front/CustomerController.php @@ -202,7 +202,7 @@ class CustomerController extends BaseFrontController } // Redirect to home page - $this->redirect(URL::getIndexPage()); + $this->redirect(URL::getInstance()->getIndexPage()); } /** diff --git a/core/lib/Thelia/Controller/Front/DefaultController.php b/core/lib/Thelia/Controller/Front/DefaultController.php index d9c5a681a..fe7e1aede 100755 --- a/core/lib/Thelia/Controller/Front/DefaultController.php +++ b/core/lib/Thelia/Controller/Front/DefaultController.php @@ -46,23 +46,30 @@ class DefaultController extends BaseFrontController */ public function noAction(Request $request) { - if(ConfigQuery::isRewritingEnable()) { - - /* Does the query GET parameters match a rewritten URL ? */ - $rewrittenUrl = URL::init()->retrieveCurrent($request); - if($rewrittenUrl->rewrittenUrl !== null) { - /* 301 redirection to rewritten URL */ - $this->redirect($rewrittenUrl->rewrittenUrl, 301); - } - } + $view = null; if (! $view = $request->query->get('view')) { - $view = "index"; if ($request->request->has('view')) { $view = $request->request->get('view'); } } + if(null !== $view) { + $request->attributes->set('_view', $view); + } - $request->attributes->set('_view', $view); + if (null === $view && null === $request->attributes->get("_view")) { + $request->attributes->set("_view", "index"); + } + + if(ConfigQuery::isRewritingEnable()) { + if($request->attributes->get('_rewritten', false) === false) { + /* Does the query GET parameters match a rewritten URL ? */ + $rewrittenUrl = URL::getInstance()->retrieveCurrent($request); + if($rewrittenUrl->rewrittenUrl !== null) { + /* 301 redirection to rewritten URL */ + $this->redirect($rewrittenUrl->rewrittenUrl, 301); + } + } + } } } diff --git a/core/lib/Thelia/Controller/Front/UrlRewritingController.php b/core/lib/Thelia/Controller/Front/UrlRewritingController.php deleted file mode 100755 index df2479969..000000000 --- a/core/lib/Thelia/Controller/Front/UrlRewritingController.php +++ /dev/null @@ -1,81 +0,0 @@ -. */ -/* */ -/*************************************************************************************/ -namespace Thelia\Controller\Front; - -use Thelia\Core\HttpFoundation\Request; -use Thelia\Exception\UrlRewritingException; -use Thelia\Model\ConfigQuery; -use Thelia\Tools\URL; - -class UrlRewritingController extends BaseFrontController -{ - public function check(Request $request, $rewritten_url) - { - if(ConfigQuery::isRewritingEnable()) { - try { - $rewrittenUrlData = URL::init()->resolve($rewritten_url); - } catch(UrlRewritingException $e) { - switch($e->getCode()) { - case UrlRewritingException::URL_NOT_FOUND : - return $this->pageNotFound(); - break; - default: - throw $e; - } - } - - /* is the URL redirected ? */ - - if(null !== $rewrittenUrlData->redirectedToUrl) { - $this->redirect($rewrittenUrlData->redirectedToUrl, 301); - } - - /* define GET arguments in request */ - - if(null !== $rewrittenUrlData->view) { - $request->query->set('view', $rewrittenUrlData->view); - if(null !== $rewrittenUrlData->viewId) { - $request->query->set($rewrittenUrlData->view . '_id', $rewrittenUrlData->viewId); - } - } - if(null !== $rewrittenUrlData->locale) { - $request->query->set('locale', $rewrittenUrlData->locale); - } - - foreach($rewrittenUrlData->otherParameters as $parameter => $value) { - $request->query->set($parameter, $value); - } - } - - if (! $view = $request->query->get('view')) { - $view = "index"; - if ($request->request->has('view')) { - $view = $request->request->get('view'); - } - } - - $request->attributes->set('_view', $view); - - } - -} diff --git a/core/lib/Thelia/Core/Event/AddressCreateOrUpdateEvent.php b/core/lib/Thelia/Core/Event/AddressCreateOrUpdateEvent.php index 1eee2acc8..d6dd6e3ed 100644 --- a/core/lib/Thelia/Core/Event/AddressCreateOrUpdateEvent.php +++ b/core/lib/Thelia/Core/Event/AddressCreateOrUpdateEvent.php @@ -32,7 +32,7 @@ use Thelia\Model\Customer; * @package Thelia\Core\Event * @author Manuel Raynaud */ -class AddressCreateOrUpdateEvent extends Event +class AddressCreateOrUpdateEvent extends ActionEvent { /** * @var string address label diff --git a/core/lib/Thelia/Core/Event/AddressEvent.php b/core/lib/Thelia/Core/Event/AddressEvent.php new file mode 100644 index 000000000..5167108fd --- /dev/null +++ b/core/lib/Thelia/Core/Event/AddressEvent.php @@ -0,0 +1,54 @@ +. */ +/* */ +/*************************************************************************************/ + +namespace Thelia\Core\Event; +use Symfony\Component\EventDispatcher\Event; +use Thelia\Model\Address; + + +/** + * Class AddressEvent + * @package Thelia\Core\Event + * @author Manuel Raynaud + */ +class AddressEvent extends ActionEvent +{ + /** + * @var \Thelia\Model\Address + */ + protected $address; + + function __construct(Address $address) + { + $this->address = $address; + } + + + /** + * @return \Thelia\Model\Address + */ + public function getAddress() + { + return $this->address; + } +} \ No newline at end of file diff --git a/core/lib/Thelia/Core/Event/BaseUpdatePositionEvent.php b/core/lib/Thelia/Core/Event/BaseUpdatePositionEvent.php new file mode 100644 index 000000000..41907c29c --- /dev/null +++ b/core/lib/Thelia/Core/Event/BaseUpdatePositionEvent.php @@ -0,0 +1,77 @@ +. */ +/* */ +/*************************************************************************************/ + +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; + } +} diff --git a/core/lib/Thelia/Core/Event/CartEvent.php b/core/lib/Thelia/Core/Event/CartEvent.php index 59e67e2bf..61d58e9d2 100755 --- a/core/lib/Thelia/Core/Event/CartEvent.php +++ b/core/lib/Thelia/Core/Event/CartEvent.php @@ -26,7 +26,7 @@ namespace Thelia\Core\Event; use Symfony\Component\EventDispatcher\Event; use Thelia\Model\Cart; -class CartEvent extends Event +class CartEvent extends ActionEvent { protected $cart; protected $quantity; diff --git a/core/lib/Thelia/Core/Event/CategoryChangePositionEvent.php b/core/lib/Thelia/Core/Event/CategoryChangePositionEvent.php index 94f131626..3a3dbb18f 100644 --- a/core/lib/Thelia/Core/Event/CategoryChangePositionEvent.php +++ b/core/lib/Thelia/Core/Event/CategoryChangePositionEvent.php @@ -22,64 +22,7 @@ /*************************************************************************************/ namespace Thelia\Core\Event; -use Thelia\Model\Category; -class CategoryChangePositionEvent extends ActionEvent +class CurrencyUpdatePositionEvent extends BaseUpdatePositionEvent { - const POSITION_UP = 1; - const POSITION_DOWN = 2; - const POSITION_ABSOLUTE = 3; - - protected $category_id; - protected $mode; - protected $position; - protected $category; - - public function __construct($category_id, $mode, $position = null) - { - $this->category_id = $category_id; - $this->mode = $mode; - $this->position = $position; - } - - public function getMode() - { - return $this->mode; - } - - public function setMode($mode) - { - $this->mode = $mode; - } - - public function getPosition() - { - return $this->position; - } - - public function setPosition($position) - { - $this->position = $position; - } - - public function getCategory() - { - return $this->category; - } - - public function setCategory($category) - { - $this->category = $category; - } - - public function getCategoryId() - { - return $this->category_id; - } - - public function setCategoryId($category_id) - { - $this->category_id = $category_id; - } - -} +} \ No newline at end of file diff --git a/core/lib/Thelia/Core/Event/CategoryChangeEvent.php b/core/lib/Thelia/Core/Event/CategoryUpdateEvent.php similarity index 97% rename from core/lib/Thelia/Core/Event/CategoryChangeEvent.php rename to core/lib/Thelia/Core/Event/CategoryUpdateEvent.php index e8e183b3f..8103864c5 100644 --- a/core/lib/Thelia/Core/Event/CategoryChangeEvent.php +++ b/core/lib/Thelia/Core/Event/CategoryUpdateEvent.php @@ -25,7 +25,7 @@ namespace Thelia\Core\Event; use Thelia\Model\Category; -class CategoryCreateEvent extends ActionEvent +class CategoryUpdateEvent extends ActionEvent { protected $category_id; protected $locale; diff --git a/core/lib/Thelia/Core/Event/ConfigChangeEvent.php b/core/lib/Thelia/Core/Event/ConfigUpdateEvent.php similarity index 98% rename from core/lib/Thelia/Core/Event/ConfigChangeEvent.php rename to core/lib/Thelia/Core/Event/ConfigUpdateEvent.php index e7da059ee..a73613699 100644 --- a/core/lib/Thelia/Core/Event/ConfigChangeEvent.php +++ b/core/lib/Thelia/Core/Event/ConfigUpdateEvent.php @@ -25,7 +25,7 @@ namespace Thelia\Core\Event; use Thelia\Model\Config; -class ConfigChangeEvent extends ConfigCreateEvent +class ConfigUpdateEvent extends ConfigCreateEvent { protected $config_id; diff --git a/core/lib/Thelia/Core/Event/CurrencyChangeEvent.php b/core/lib/Thelia/Core/Event/CurrencyUpdateEvent.php similarity index 97% rename from core/lib/Thelia/Core/Event/CurrencyChangeEvent.php rename to core/lib/Thelia/Core/Event/CurrencyUpdateEvent.php index 1e7677fee..044a93baa 100644 --- a/core/lib/Thelia/Core/Event/CurrencyChangeEvent.php +++ b/core/lib/Thelia/Core/Event/CurrencyUpdateEvent.php @@ -24,7 +24,7 @@ namespace Thelia\Core\Event; use Thelia\Model\Currency; -class CurrencyChangeEvent extends CurrencyCreateEvent +class CurrencyUpdateEvent extends CurrencyCreateEvent { protected $currency_id; protected $is_default; diff --git a/core/lib/Thelia/Core/Event/CurrencyUpdatePositionEvent.php b/core/lib/Thelia/Core/Event/CurrencyUpdatePositionEvent.php new file mode 100644 index 000000000..3a3dbb18f --- /dev/null +++ b/core/lib/Thelia/Core/Event/CurrencyUpdatePositionEvent.php @@ -0,0 +1,28 @@ +. */ +/* */ +/*************************************************************************************/ + +namespace Thelia\Core\Event; + +class CurrencyUpdatePositionEvent extends BaseUpdatePositionEvent +{ +} \ No newline at end of file diff --git a/core/lib/Thelia/Core/Event/CustomerCreateOrUpdateEvent.php b/core/lib/Thelia/Core/Event/CustomerCreateOrUpdateEvent.php index e7cf83b5c..9bee163ae 100755 --- a/core/lib/Thelia/Core/Event/CustomerCreateOrUpdateEvent.php +++ b/core/lib/Thelia/Core/Event/CustomerCreateOrUpdateEvent.php @@ -13,7 +13,7 @@ namespace Thelia\Core\Event; use Symfony\Component\EventDispatcher\Event; use Thelia\Model\Customer; -class CustomerCreateOrUpdateEvent extends Event { +class CustomerCreateOrUpdateEvent extends ActionEvent { //base parameters for creating new customer protected $title; diff --git a/core/lib/Thelia/Core/Event/MessageChangeEvent.php b/core/lib/Thelia/Core/Event/MessageUpdateEvent.php similarity index 98% rename from core/lib/Thelia/Core/Event/MessageChangeEvent.php rename to core/lib/Thelia/Core/Event/MessageUpdateEvent.php index 1d36f1d30..1b3712266 100644 --- a/core/lib/Thelia/Core/Event/MessageChangeEvent.php +++ b/core/lib/Thelia/Core/Event/MessageUpdateEvent.php @@ -25,7 +25,7 @@ namespace Thelia\Core\Event; use Thelia\Model\Message; -class MessageChangeEvent extends MessageCreateEvent +class MessageUpdateEvent extends MessageCreateEvent { protected $message_id; diff --git a/core/lib/Thelia/Core/Event/TheliaEvents.php b/core/lib/Thelia/Core/Event/TheliaEvents.php index 18ca58be1..de978c182 100755 --- a/core/lib/Thelia/Core/Event/TheliaEvents.php +++ b/core/lib/Thelia/Core/Event/TheliaEvents.php @@ -88,12 +88,13 @@ final class TheliaEvents /** * Sent once the customer change form has been successfully validated, and before customer update in the database. */ - const BEFORE_CHANGECUSTOMER = "action.before_updateCustomer"; + const BEFORE_UPDATECUSTOMER = "action.before_updateCustomer"; /** * Sent just after a successful update of a customer in the database. */ - const AFTER_CHANGECUSTOMER = "action.after_updateCustomer"; + const AFTER_UPDATECUSTOMER = "action.after_updateCustomer"; + // -- ADDRESS MANAGEMENT --------------------------------------------------------- /** * sent for address creation */ @@ -104,6 +105,17 @@ final class TheliaEvents */ const ADDRESS_UPDATE = "action.updateAddress"; + const BEFORE_CREATEADDRESS = "action.before_createAddress"; + const AFTER_CREATEADDRESS = "action.after_createAddress"; + + const BEFORE_UPDATEADDRESS = "action.before_updateAddress"; + const AFTER_UPDATEADDRESS = "action.after_updateAddress"; + + const BEFORE_DELETEADDRESS = "action.before_deleteAddress"; + const AFTER_DELETEADDRESS = "action.after_deleteAddress"; + + // -- END ADDRESS MANAGEMENT --------------------------------------------------------- + /** * Sent once the category creation form has been successfully validated, and before category insertion in the database. */ @@ -124,7 +136,7 @@ final class TheliaEvents /** * Change category position */ - const CATEGORY_CHANGE_POSITION = "action.changeCategoryPosition"; + const CATEGORY_CHANGE_POSITION = "action.updateCategoryPosition"; /** * Sent just after a successful insert of a new category in the database. @@ -344,12 +356,12 @@ final class TheliaEvents // -- Currencies management --------------------------------------------- - const CURRENCY_CREATE = "action.createCurrency"; - const CURRENCY_UPDATE = "action.updateCurrency"; - const CURRENCY_DELETE = "action.deleteCurrency"; - const CURRENCY_SET_DEFAULT = "action.setDefaultCurrency"; - const CURRENCY_UPDATE_RATES = "action.updateCurrencyRates"; - + const CURRENCY_CREATE = "action.createCurrency"; + const CURRENCY_UPDATE = "action.updateCurrency"; + const CURRENCY_DELETE = "action.deleteCurrency"; + const CURRENCY_SET_DEFAULT = "action.setDefaultCurrency"; + const CURRENCY_UPDATE_RATES = "action.updateCurrencyRates"; + const CURRENCY_UPDATE_POSITION = "action.updateCurrencyPosition"; const BEFORE_CREATECURRENCY = "action.before_createCurrency"; const AFTER_CREATECURRENCY = "action.after_createCurrency"; diff --git a/core/lib/Thelia/Core/EventListener/ViewListener.php b/core/lib/Thelia/Core/EventListener/ViewListener.php index d55826d56..3bd3147a4 100755 --- a/core/lib/Thelia/Core/EventListener/ViewListener.php +++ b/core/lib/Thelia/Core/EventListener/ViewListener.php @@ -86,7 +86,7 @@ class ViewListener implements EventSubscriberInterface } catch (AuthenticationException $ex) { // Redirect to the login template - Redirect::exec(URL::viewUrl($ex->getLoginTemplate())); + Redirect::exec($this->container->get('thelia.url.manager')->viewUrl($ex->getLoginTemplate())); } } diff --git a/core/lib/Thelia/Core/HttpFoundation/Session/Session.php b/core/lib/Thelia/Core/HttpFoundation/Session/Session.php index 5f311ad0e..4a486e488 100755 --- a/core/lib/Thelia/Core/HttpFoundation/Session/Session.php +++ b/core/lib/Thelia/Core/HttpFoundation/Session/Session.php @@ -41,55 +41,29 @@ use Thelia\Model\Lang; */ class Session extends BaseSession { - // -- Language ------------------------------------------------------------ - - public function getLocale() - { - return $this->get("locale", "en_US"); - } - - public function setLocale($locale) - { - $this->set("locale", $locale); - - return $this; - } - /** * @return \Thelia\Model\Lang|null */ public function getLang() { - return $this->get("lang"); + return $this->get("thelia.current.lang", Lang::getDefaultLanguage()); } public function setLang(Lang $lang) { - $this->set("lang", $lang); + $this->set("thelia.current.lang", $lang); return $this; } - public function getLangId() + public function getAdminEditionLang() { - return $this->get("lang_id", Lang::getDefaultLanguage()->getId()); + return $this->get('thelia.admin.edition.lang', Lang::getDefaultLanguage()); } - public function setLangId($langId) + public function setAdminEditionLang($langId) { - $this->set("lang_id", $langId); - - return $this; - } - - public function getAdminEditionLangId() - { - return $this->get('admin.edition_language', Lang::getDefaultLanguage()->getId()); - } - - public function setAdminEditionLangId($langId) - { - $this->set('admin.edition_language', $langId); + $this->set('thelia.admin.edition.lang', $langId); return $this; } @@ -98,43 +72,43 @@ class Session extends BaseSession public function setCustomerUser(UserInterface $user) { - $this->set('customer_user', $user); + $this->set('thelia.customer_user', $user); return $this; } public function getCustomerUser() { - return $this->get('customer_user'); + return $this->get('thelia.customer_user'); } public function clearCustomerUser() { - return $this->remove('customer_user'); + return $this->remove('thelia.customer_user'); } // -- Admin user ----------------------------------------------------------- public function setAdminUser(UserInterface $user) { - $this->set('admin_user', $user); + $this->set('thelia.admin_user', $user); return $this; } public function getAdminUser() { - return $this->get('admin_user'); + return $this->get('thelia.admin_user'); } public function clearAdminUser() { - return $this->remove('admin_user'); + return $this->remove('thelia.admin_user'); } // -- Return page ---------------------------------------------------------- public function setReturnToUrl($url) { - $this->set('return_to_url', $url); + $this->set('thelia.return_to_url', $url); return $this; } @@ -144,7 +118,7 @@ class Session extends BaseSession */ public function getReturnToUrl() { - return $this->get('return_to_url', URL::getIndexPage()); + return $this->get('thelia.return_to_url', URL::getInstance()->getIndexPage()); } // -- Cart ------------------------------------------------------------------ @@ -156,7 +130,7 @@ class Session extends BaseSession */ public function getCart() { - $cart_id = $this->get("cart_id"); + $cart_id = $this->get("thelia.cart_id"); $cart = null; if ($cart_id) { $cart = CartQuery::create()->findPk($cart_id); @@ -193,7 +167,7 @@ class Session extends BaseSession */ public function setCart($cart_id) { - $this->set("cart_id", $cart_id); + $this->set("thelia.cart_id", $cart_id); return $this; } -} +} \ No newline at end of file diff --git a/core/lib/Thelia/Core/Routing/RewritingRouter.php b/core/lib/Thelia/Core/Routing/RewritingRouter.php new file mode 100644 index 000000000..79d5dc80b --- /dev/null +++ b/core/lib/Thelia/Core/Routing/RewritingRouter.php @@ -0,0 +1,219 @@ +. */ +/* */ +/*************************************************************************************/ + +namespace Thelia\Core\Routing; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\Routing\Exception\InvalidParameterException; +use Symfony\Component\Routing\Exception\MethodNotAllowedException; +use Symfony\Component\Routing\Exception\MissingMandatoryParametersException; +use Symfony\Component\Routing\Exception\ResourceNotFoundException; +use Symfony\Component\Routing\Exception\RouteNotFoundException; +use Symfony\Component\Routing\Matcher\RequestMatcherInterface; +use Symfony\Component\Routing\RequestContext; +use Symfony\Component\Routing\RouteCollection; +use Symfony\Component\Routing\RouterInterface; +use Thelia\Exception\RedirectException; +use Thelia\Exception\UrlRewritingException; +use Thelia\Model\ConfigQuery; +use Thelia\Tools\Redirect; +use Thelia\Tools\URL; + + +/** + * Class RewritingRouter + * @package Thelia\Core\Routing + * @author Manuel Raynaud + * @author Etienne Roudeix + */ +class RewritingRouter implements RouterInterface, RequestMatcherInterface +{ + + /** + * @var RequestContext The context + */ + protected $context; + + /** + * @var options, don't use for now but mandatory + */ + protected $options; + + /** + * Sets the request context. + * + * @param RequestContext $context The context + * + * @api + */ + public function setContext(RequestContext $context) + { + // TODO: Implement setContext() method. + $this->context = $context; + } + + /** + * Gets the request context. + * + * @return RequestContext The context + * + * @api + */ + public function getContext() + { + // TODO: Implement getContext() method. + return $this->context; + } + + public function setOption($key, $value) + { + //NOTHING TO DO FOR NOW + } + + /** + * Gets the RouteCollection instance associated with this Router. + * + * @return RouteCollection A RouteCollection instance + */ + public function getRouteCollection() + { + return new RouteCollection(); + } + + /** + * Generates a URL or path for a specific route based on the given parameters. + * + * Parameters that reference placeholders in the route pattern will substitute them in the + * path or host. Extra params are added as query string to the URL. + * + * When the passed reference type cannot be generated for the route because it requires a different + * host or scheme than the current one, the method will return a more comprehensive reference + * that includes the required params. For example, when you call this method with $referenceType = ABSOLUTE_PATH + * but the route requires the https scheme whereas the current scheme is http, it will instead return an + * ABSOLUTE_URL with the https scheme and the current host. This makes sure the generated URL matches + * the route in any case. + * + * If there is no route with the given name, the generator must throw the RouteNotFoundException. + * + * @param string $name The name of the route + * @param mixed $parameters An array of parameters + * @param Boolean|string $referenceType The type of reference to be generated (one of the constants) + * + * @return string The generated URL + * + * @throws RouteNotFoundException If the named route doesn't exist + * @throws MissingMandatoryParametersException When some parameters are missing that are mandatory for the route + * @throws InvalidParameterException When a parameter value for a placeholder is not correct because + * it does not match the requirement + * + * @api + */ + public function generate($name, $parameters = array(), $referenceType = self::ABSOLUTE_PATH) + { + // TODO: Implement generate() method. + } + + /** + * Tries to match a URL path with a set of routes. + * + * If the matcher can not find information, it must throw one of the exceptions documented + * below. + * + * @param string $pathinfo The path info to be parsed (raw format, i.e. not urldecoded) + * + * @return array An array of parameters + * + * @throws ResourceNotFoundException If the resource could not be found + * @throws MethodNotAllowedException If the resource was found but the request method is not allowed + * + * @api + */ + public function match($pathinfo) + { + throw new ResourceNotFoundException("impossible to find route with this method, please use matchRequest method"); + } + + /** + * Tries to match a request with a set of routes. + * + * If the matcher can not find information, it must throw one of the exceptions documented + * below. + * + * @param Request $request The request to match + * + * @throws \Exception|\Thelia\Exception\UrlRewritingException + * @throws \Symfony\Component\Routing\Exception\ResourceNotFoundException + * @throws \Thelia\Exception\RedirectException + * @return array An array of parameters + * + */ + public function matchRequest(Request $request) + { + if(ConfigQuery::isRewritingEnable()) { + try { + $rewrittenUrlData = URL::getInstance()->resolve($request->getPathInfo()); + } catch(UrlRewritingException $e) { + switch($e->getCode()) { + case UrlRewritingException::URL_NOT_FOUND : + throw new ResourceNotFoundException(); + break; + default: + throw $e; + } + } + + /* is the URL redirected ? */ + + if(null !== $rewrittenUrlData->redirectedToUrl) { + $this->redirect($rewrittenUrlData->redirectedToUrl, 301); + } + + /* define GET arguments in request */ + + if(null !== $rewrittenUrlData->view) { + $request->attributes->set('_view', $rewrittenUrlData->view); + if(null !== $rewrittenUrlData->viewId) { + $request->query->set($rewrittenUrlData->view . '_id', $rewrittenUrlData->viewId); + } + } + if(null !== $rewrittenUrlData->locale) { + $request->query->set('locale', $rewrittenUrlData->locale); + } + + foreach($rewrittenUrlData->otherParameters as $parameter => $value) { + $request->query->set($parameter, $value); + } + + return array ( + '_controller' => 'Thelia\\Controller\\Front\\DefaultController::noAction', + '_route' => 'rewrite', + '_rewritten' => true, + ); + } + throw new ResourceNotFoundException(); + } + + protected function redirect($url, $status = 302) + { + Redirect::exec($url, $status); + } +} \ No newline at end of file diff --git a/core/lib/Thelia/Core/Template/Assets/AsseticHelper.php b/core/lib/Thelia/Core/Template/Assets/AsseticHelper.php index c5995e3de..cd7f06ac8 100755 --- a/core/lib/Thelia/Core/Template/Assets/AsseticHelper.php +++ b/core/lib/Thelia/Core/Template/Assets/AsseticHelper.php @@ -43,15 +43,16 @@ class AsseticHelper * Generates assets from $asset_path in $output_path, using $filters. * * @param string $asset_path the full path to the asset file (or file collection) - * @param unknown $output_path the full disk path to the output directory (shoud be visible to web server) - * @param unknown $output_url the URL to the generated asset directory - * @param unknown $asset_type the asset type: css, js, ... The generated files will have this extension. Pass an empty string to use the asset source extension. - * @param unknown $filters a list of filters, as defined below (see switch($filter_name) ...) - * @param unknown $debug true / false + * @param string $output_path the full disk path to the output directory (shoud be visible to web server) + * @param string $output_url the URL to the generated asset directory + * @param string $asset_type the asset type: css, js, ... The generated files will have this extension. Pass an empty string to use the asset source extension. + * @param array $filters a list of filters, as defined below (see switch($filter_name) ...) + * @param boolean $debug true / false + * @param boolean $dev_mode true / false. If true, assets are not cached and always compiled. * @throws \InvalidArgumentException if an invalid filter name is found * @return string The URL to the generated asset file. */ - public function asseticize($asset_path, $output_path, $output_url, $asset_type, $filters, $debug) + public function asseticize($asset_path, $output_path, $output_url, $asset_type, $filters, $debug, $dev_mode = false) { $asset_name = basename($asset_path); $asset_dir = dirname($asset_path); @@ -106,17 +107,49 @@ class AsseticHelper $factory->setDebug($debug); - $factory->addWorker(new CacheBustingWorker()); + $factory->addWorker(new CacheBustingWorker('-')); - // Prepare the assets writer - $writer = new AssetWriter($output_path); + // We do not pass the filter list here, juste to get the asset file name + $asset = $factory->createAsset($asset_name); - $asset = $factory->createAsset($asset_name, $filter_list); + $asset_target_path = $asset->getTargetPath(); - $cache = new AssetCache($asset, new FilesystemCache($output_path)); + $target_file = sprintf("%s/%s", $output_path, $asset_target_path); - $writer->writeAsset($cache); + // As it seems that assetic cannot handle a real file cache, let's do the job ourselves. + // It works only if the CacheBustingWorker is used, as a new file name is generated for each version. + // + // the previous version of the file is deleted, by getting the first part of the ouput file name + // (the one before '-'), and delete aby file beginning with the same string. Example: + // old name: 3bc974a-dfacc1f.css + // new name: 3bc974a-ad3ef47.css + // + // before generating 3bc974a-ad3ef47.css, delete 3bc974a-* files. + // + if ($dev_mode == true || ! file_exists($target_file)) { - return rtrim($output_url, '/').'/'.$asset->getTargetPath(); + // Delete previous version of the file + list($commonPart, $dummy) = explode('-', $asset_target_path); + + foreach (glob("$output_path/$commonPart-*") as $filename) { + @unlink($filename); + } + + // Apply filters now + foreach ($filter_list as $filter) { + if ('?' != $filter[0]) { + $asset->ensureFilter($fm->get($filter)); + } + elseif (!$debug) { + $asset->ensureFilter($fm->get(substr($filter, 1))); + } + } + + $writer = new AssetWriter($output_path); + + $writer->writeAsset($asset); + } + + return rtrim($output_url, '/').'/'.$asset_target_path; } } diff --git a/core/lib/Thelia/Core/Template/Element/BaseI18nLoop.php b/core/lib/Thelia/Core/Template/Element/BaseI18nLoop.php index ef51d51b8..18eadce3c 100644 --- a/core/lib/Thelia/Core/Template/Element/BaseI18nLoop.php +++ b/core/lib/Thelia/Core/Template/Element/BaseI18nLoop.php @@ -69,7 +69,7 @@ abstract class BaseI18nLoop extends BaseLoop $this->getBackend_context(), $this->getLang(), $search, - $this->request->getSession()->getLocale(), + $this->request->getSession()->getLang()->getLocale(), $columns, $foreignTable, $foreignKey, diff --git a/core/lib/Thelia/Core/Template/Element/BaseLoop.php b/core/lib/Thelia/Core/Template/Element/BaseLoop.php index 8961af6a3..98ed8af33 100755 --- a/core/lib/Thelia/Core/Template/Element/BaseLoop.php +++ b/core/lib/Thelia/Core/Template/Element/BaseLoop.php @@ -239,31 +239,6 @@ abstract class BaseLoop } } - /** - * Setup ModelCriteria for proper i18n processing - * - * @param ModelCriteria $search the Propel Criteria to configure - * @param array $columns the i18n columns - * @param string $foreignTable the specified table (default to criteria table) - * @param string $foreignKey the foreign key in this table (default to criteria table) - * - * @return mixed the locale - */ - protected function configureI18nProcessing(ModelCriteria $search, $columns = array('TITLE', 'CHAPO', 'DESCRIPTION', 'POSTSCRIPTUM'), $foreignTable = null, $foreignKey = 'ID', $forceReturn = false) { - - /* manage translations */ - return ModelCriteriaTools::getI18n( - $this->getBackend_context(), - $this->getLang(), - $search, - $this->request->getSession()->getLocale(), - $columns, - $foreignTable, - $foreignKey, - $forceReturn - ); - } - /** * * this function have to be implement in your own loop class. diff --git a/core/lib/Thelia/Core/Template/Loop/Address.php b/core/lib/Thelia/Core/Template/Loop/Address.php index fab153969..757cc11b8 100755 --- a/core/lib/Thelia/Core/Template/Loop/Address.php +++ b/core/lib/Thelia/Core/Template/Loop/Address.php @@ -64,7 +64,7 @@ class Address extends BaseLoop ), 'current' ), - Argument::createBooleanTypeArgument('default'), + Argument::createBooleanTypeArgument('default', false), Argument::createIntListTypeArgument('exclude') ); } @@ -99,10 +99,9 @@ class Address extends BaseLoop $default = $this->getDefault(); + if ($default === true) { $search->filterByIsDefault(1, Criteria::EQUAL); - } elseif ($default === false) { - $search->filterByIsDefault(1, Criteria::NOT_EQUAL); } $exclude = $this->getExclude(); @@ -119,7 +118,7 @@ class Address extends BaseLoop $loopResultRow = new LoopResultRow($loopResult, $address, $this->versionable, $this->timestampable, $this->countable); $loopResultRow ->set("ID", $address->getId()) - ->set("NAME", $address->getName()) + ->set("LABEL", $address->getLabel()) ->set("CUSTOMER", $address->getCustomerId()) ->set("TITLE", $address->getTitleId()) ->set("COMPANY", $address->getCompany()) diff --git a/core/lib/Thelia/Core/Template/Loop/Cart.php b/core/lib/Thelia/Core/Template/Loop/Cart.php index f9e5c00aa..8e4d78683 100755 --- a/core/lib/Thelia/Core/Template/Loop/Cart.php +++ b/core/lib/Thelia/Core/Template/Loop/Cart.php @@ -71,10 +71,12 @@ class Cart extends BaseLoop */ public function exec(&$pagination) { - $cartItems = $cart->getCartItems(); - $result = new LoopResult($cartItems); + + $cart = $this->getCart($this->request); + $cartItems = $cart->getCartItems(); + $result = new LoopResult($cartItems); if ($cart === null) { return $result; diff --git a/core/lib/Thelia/Core/Template/Loop/Config.php b/core/lib/Thelia/Core/Template/Loop/Config.php index 37f098d77..8e03de636 100644 --- a/core/lib/Thelia/Core/Template/Loop/Config.php +++ b/core/lib/Thelia/Core/Template/Loop/Config.php @@ -166,7 +166,7 @@ class Config extends BaseI18nLoop ->set("POSTSCRIPTUM" , $result->getVirtualColumn('i18n_POSTSCRIPTUM')) ->set("HIDDEN" , $result->getHidden()) ->set("SECURED" , $result->getSecured()) - ; + ; $loopResult->addRow($loopResultRow); } diff --git a/core/lib/Thelia/Core/Template/Smarty/Assets/SmartyAssetsManager.php b/core/lib/Thelia/Core/Template/Smarty/Assets/SmartyAssetsManager.php index a07eba565..f19eded95 100755 --- a/core/lib/Thelia/Core/Template/Smarty/Assets/SmartyAssetsManager.php +++ b/core/lib/Thelia/Core/Template/Smarty/Assets/SmartyAssetsManager.php @@ -34,18 +34,22 @@ class SmartyAssetsManager private $web_root; private $path_relative_to_web_root; + private $developmentMode; /** * Creates a new SmartyAssetsManager instance * - * @param string $web_root the disk path to the web root - * @param string $path_relative_to_web_root the path (relative to web root) where the assets will be generated + * @param string $web_root the disk path to the web root + * @param string $path_relative_to_web_root the path (relative to web root) where the assets will be generated + * @param boolean $developmentMode true / false. If true, assets are not cached, and always generated. */ - public function __construct($web_root, $path_relative_to_web_root) + public function __construct($web_root, $path_relative_to_web_root, $developmentMode) { $this->web_root = $web_root; $this->path_relative_to_web_root = $path_relative_to_web_root; + $this->developmentMode = $developmentMode; + $this->assetic_manager = new AsseticHelper(); } @@ -70,10 +74,11 @@ class SmartyAssetsManager $url = $this->assetic_manager->asseticize( $asset_dir.'/'.$asset_file, $this->web_root."/".$this->path_relative_to_web_root, - URL::absoluteUrl($this->path_relative_to_web_root, null, URL::PATH_TO_FILE /* path only */), + URL::getInstance()->absoluteUrl($this->path_relative_to_web_root, null, URL::PATH_TO_FILE /* path only */), $assetType, $filters, - $debug + $debug, + $this->developmentMode ); return $url; diff --git a/core/lib/Thelia/Core/Template/Smarty/Plugins/AdminUtilities.php b/core/lib/Thelia/Core/Template/Smarty/Plugins/AdminUtilities.php index aab63177a..bb9770520 100644 --- a/core/lib/Thelia/Core/Template/Smarty/Plugins/AdminUtilities.php +++ b/core/lib/Thelia/Core/Template/Smarty/Plugins/AdminUtilities.php @@ -70,12 +70,12 @@ class AdminUtilities extends AbstractSmartyPlugin if ($permissions == null || $this->securityContext->isGranted("ADMIN", array($permission))) { return sprintf( - '%s', - URL::absoluteUrl("$path/positionUp", array($url_parameter => $id)), + '%s', + URL::getInstance()->absoluteUrl($path, array('mode' => 'up', $url_parameter => $id)), $in_place_edit_class, $id, $position, - URL::absoluteUrl("$path/positionDown", array($url_parameter => $id)) + URL::getInstance()->absoluteUrl($path, array('mode' => 'down', $url_parameter => $id)) ); } else { @@ -121,11 +121,11 @@ class AdminUtilities extends AbstractSmartyPlugin } if (! empty($icon)) - $output = sprintf(' ', $icon); + $output = sprintf(' ', $icon); else $output = ''; - return sprintf('%s%s', $output, URL::absoluteUrl($path, array('order' => $order_change)), $label); + return sprintf('%s%s', $output, URL::getInstance()->absoluteUrl($path, array('order' => $order_change)), $label); } diff --git a/core/lib/Thelia/Core/Template/Smarty/Plugins/Assetic.php b/core/lib/Thelia/Core/Template/Smarty/Plugins/Assetic.php index f8ea1c2ef..b7bb95b83 100755 --- a/core/lib/Thelia/Core/Template/Smarty/Plugins/Assetic.php +++ b/core/lib/Thelia/Core/Template/Smarty/Plugins/Assetic.php @@ -32,13 +32,13 @@ class Assetic extends AbstractSmartyPlugin { public $assetManager; - public function __construct() + public function __construct($developmentMode) { $web_root = THELIA_WEB_DIR; $asset_dir_from_web_root = ConfigQuery::read('asset_dir_from_web_root', 'assets'); - $this->assetManager = new SmartyAssetsManager($web_root, $asset_dir_from_web_root); + $this->assetManager = new SmartyAssetsManager($web_root, $asset_dir_from_web_root, $developmentMode == 'dev'); } public function blockJavascripts($params, $content, \Smarty_Internal_Template $template, &$repeat) diff --git a/core/lib/Thelia/Core/Template/Smarty/Plugins/DataAccessFunctions.php b/core/lib/Thelia/Core/Template/Smarty/Plugins/DataAccessFunctions.php index ee65f34b3..a2fd10c15 100755 --- a/core/lib/Thelia/Core/Template/Smarty/Plugins/DataAccessFunctions.php +++ b/core/lib/Thelia/Core/Template/Smarty/Plugins/DataAccessFunctions.php @@ -68,14 +68,14 @@ class DataAccessFunctions extends AbstractSmartyPlugin } /** - * Provides access to user attributes using the accessors. + * @param $objectLabel + * @param $user + * @param $params * - * @param array $params - * @param unknown $smarty - * @return string the value of the requested attribute - * @throws InvalidArgumentException if the object does not have the requested attribute. + * @return string + * @throws \InvalidArgumentException */ - protected function userDataAccess($objectLabel, $user, $params) + protected function userDataAccess($objectLabel, $user, $params) { $attribute = $this->getNormalizedParam($params, array('attribute', 'attrib', 'attr')); diff --git a/core/lib/Thelia/Core/Template/Smarty/Plugins/Form.php b/core/lib/Thelia/Core/Template/Smarty/Plugins/Form.php index 4d63ccdc2..625c89e1f 100755 --- a/core/lib/Thelia/Core/Template/Smarty/Plugins/Form.php +++ b/core/lib/Thelia/Core/Template/Smarty/Plugins/Form.php @@ -130,6 +130,7 @@ class Form extends AbstractSmartyPlugin } $template->assign("label", $formFieldView->vars["label"]); + $template->assign("label_attr", $formFieldView->vars["label_attr"]); $errors = $formFieldView->vars["errors"]; 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/Template/Smarty/Plugins/UrlGenerator.php b/core/lib/Thelia/Core/Template/Smarty/Plugins/UrlGenerator.php index 1d18488f8..f6c20ecfb 100755 --- a/core/lib/Thelia/Core/Template/Smarty/Plugins/UrlGenerator.php +++ b/core/lib/Thelia/Core/Template/Smarty/Plugins/UrlGenerator.php @@ -51,7 +51,7 @@ class UrlGenerator extends AbstractSmartyPlugin $target = $this->getParam($params, 'target', null); - $url = URL::absoluteUrl($path, $this->getArgsFromParam($params, array('path', 'target'))); + $url = URL::getInstance()->absoluteUrl($path, $this->getArgsFromParam($params, array('path', 'target'))); if ($target != null) $url .= '#'.$target; @@ -102,7 +102,7 @@ class UrlGenerator extends AbstractSmartyPlugin $args = $this->getArgsFromParam($params, array('view', 'action', 'target')); if (! empty($action)) $args['action'] = $action; - return $forAdmin ? URL::adminViewUrl($view, $args) : URL::viewUrl($view, $args); + return $forAdmin ? URL::getInstance()->adminViewUrl($view, $args) : URL::getInstance()->viewUrl($view, $args); } /** @@ -169,16 +169,16 @@ class UrlGenerator extends AbstractSmartyPlugin protected function getCurrentUrl() { - return URL::init()->retrieveCurrent($this->request)->toString(); + return URL::getInstance()->retrieveCurrent($this->request)->toString(); } protected function getReturnToUrl() { - return URL::absoluteUrl($this->request->getSession()->getReturnToUrl()); + return URL::getInstance()->absoluteUrl($this->request->getSession()->getReturnToUrl()); } protected function getIndexUrl() { - return Url::getIndexPage(); + return URL::getInstance()->getIndexPage(); } } diff --git a/core/lib/Thelia/Core/Template/Smarty/SmartyParser.php b/core/lib/Thelia/Core/Template/Smarty/SmartyParser.php index 89c7eb457..85619bc8a 100755 --- a/core/lib/Thelia/Core/Template/Smarty/SmartyParser.php +++ b/core/lib/Thelia/Core/Template/Smarty/SmartyParser.php @@ -66,6 +66,8 @@ class SmartyParser extends Smarty implements ParserInterface $this->debugging = $debug; + $this->escape_html = true; + // Prevent smarty ErrorException: Notice: Undefined index bla bla bla... $this->error_reporting = E_ALL ^ E_NOTICE; diff --git a/core/lib/Thelia/Core/TheliaHttpKernel.php b/core/lib/Thelia/Core/TheliaHttpKernel.php index 7109f5ba2..20197d9c6 100755 --- a/core/lib/Thelia/Core/TheliaHttpKernel.php +++ b/core/lib/Thelia/Core/TheliaHttpKernel.php @@ -122,6 +122,11 @@ class TheliaHttpKernel extends HttpKernel */ protected function initParam(Request $request) { + // 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); if ($lang) { @@ -139,6 +144,7 @@ class TheliaHttpKernel extends HttpKernel protected function detectLang(Request $request) { $lang = null; + //first priority => lang parameter present in request (get or post) if ($request->query->has("lang")) { $lang = Model\LangQuery::create()->findOneByCode($request->query->get("lang")); diff --git a/core/lib/Thelia/Form/AddressForm.php b/core/lib/Thelia/Form/AddressCreateForm.php similarity index 97% rename from core/lib/Thelia/Form/AddressForm.php rename to core/lib/Thelia/Form/AddressCreateForm.php index b98577927..3f953fe38 100644 --- a/core/lib/Thelia/Form/AddressForm.php +++ b/core/lib/Thelia/Form/AddressCreateForm.php @@ -26,11 +26,11 @@ use Symfony\Component\Validator\Constraints\NotBlank; /** - * Class AddressForm + * Class AddressCreateForm * @package Thelia\Form * @author Manuel Raynaud */ -class AddressForm extends BaseForm +class AddressCreateForm extends BaseForm { /** @@ -60,7 +60,8 @@ class AddressForm extends BaseForm "constraints" => array( new NotBlank() ), - "label" => "address name" + "label" => "address name", + "required" => true )) ->add("title", "text", array( "constraints" => array( diff --git a/core/lib/Thelia/Form/AddressUpdateForm.php b/core/lib/Thelia/Form/AddressUpdateForm.php new file mode 100644 index 000000000..aa969831b --- /dev/null +++ b/core/lib/Thelia/Form/AddressUpdateForm.php @@ -0,0 +1,69 @@ +. */ +/* */ +/*************************************************************************************/ + +namespace Thelia\Form; +use Symfony\Component\Validator\Constraints\NotBlank; + + +/** + * Class AddressUpdateForm + * @package Thelia\Form + * @author Manuel Raynaud + */ +class AddressUpdateForm extends AddressCreateForm { + + /** + * + * in this function you add all the fields you need for your Form. + * Form this you have to call add method on $this->formBuilder attribute : + * + * $this->formBuilder->add("name", "text") + * ->add("email", "email", array( + * "attr" => array( + * "class" => "field" + * ), + * "label" => "email", + * "constraints" => array( + * new \Symfony\Component\Validator\Constraints\NotBlank() + * ) + * ) + * ) + * ->add('age', 'integer'); + * + * @return null + */ + protected function buildForm() + { + parent::buildForm(); + + + } + + /** + * @return string the name of you form. This name must be unique + */ + public function getName() + { + return "thelia_address_update"; + } +} \ No newline at end of file diff --git a/core/lib/Thelia/Form/BaseForm.php b/core/lib/Thelia/Form/BaseForm.php index edb51ad2a..870f94505 100755 --- a/core/lib/Thelia/Form/BaseForm.php +++ b/core/lib/Thelia/Form/BaseForm.php @@ -135,7 +135,7 @@ abstract class BaseForm $successUrl = $default; } - return URL::absoluteUrl($successUrl); + return URL::getInstance()->absoluteUrl($successUrl); } public function createView() diff --git a/core/lib/Thelia/Form/CartAdd.php b/core/lib/Thelia/Form/CartAdd.php index fb6347cd9..25bde3cdc 100755 --- a/core/lib/Thelia/Form/CartAdd.php +++ b/core/lib/Thelia/Form/CartAdd.php @@ -67,6 +67,10 @@ class CartAdd extends BaseForm new Constraints\Callback(array("methods" => array( array($this, "checkProduct") ))) + ), + "label" => "product", + "label_attr" => array( + "for" => "cart_product" ) )) ->add("product_sale_elements_id", "text", array( diff --git a/core/lib/Thelia/Form/CategoryCreationForm.php b/core/lib/Thelia/Form/CategoryCreationForm.php index 9935eec19..a125ad090 100755 --- a/core/lib/Thelia/Form/CategoryCreationForm.php +++ b/core/lib/Thelia/Form/CategoryCreationForm.php @@ -32,6 +32,10 @@ class CategoryCreationForm extends BaseForm ->add("title", "text", array( "constraints" => array( new NotBlank() + ), + "label" => "Category title *", + "label_attr" => array( + "for" => "title" ) )) ->add("parent", "integer", array( diff --git a/core/lib/Thelia/Form/CurrencyCreationForm.php b/core/lib/Thelia/Form/CurrencyCreationForm.php index a4d858ed6..3328cd342 100644 --- a/core/lib/Thelia/Form/CurrencyCreationForm.php +++ b/core/lib/Thelia/Form/CurrencyCreationForm.php @@ -40,11 +40,47 @@ class CurrencyCreationForm extends BaseForm } $this->formBuilder - ->add("name" , "text" , array("constraints" => array(new NotBlank()))) - ->add("locale" , "text" , array("constraints" => array(new NotBlank()))) - ->add("symbol" , "text" , array("constraints" => array(new NotBlank()))) - ->add("rate" , "text" , array("constraints" => array(new NotBlank()))) - ->add("code" , "text" , array("constraints" => $code_constraints)) + ->add("name" , "text" , array( + "constraints" => array( + new NotBlank() + ), + "label" => "Name *", + "label_attr" => array( + "for" => "name" + )) + ) + ->add("locale" , "text" , array( + "constraints" => array( + new NotBlank() + )) + ) + ->add("symbol" , "text" , array( + "constraints" => array( + new NotBlank() + ), + "label" => "Symbol *", + "label_attr" => array( + "for" => "symbol" + )) + ) + ->add("rate" , "text" , array( + "constraints" => array( + new NotBlank() + ), + "label" => "Rate from € *", + "label_attr" => array( + "for" => "rate" + )) + ) + ->add("code" , "text" , array( + "constraints" => array( + new NotBlank() + ), + "label" => "ISO 4217 code *", + "label_attr" => array( + "for" => "iso_4217_code" + )) + ) ; } diff --git a/core/lib/Thelia/Model/Address.php b/core/lib/Thelia/Model/Address.php index 58e6285ca..413797eeb 100755 --- a/core/lib/Thelia/Model/Address.php +++ b/core/lib/Thelia/Model/Address.php @@ -2,21 +2,73 @@ namespace Thelia\Model; +use Propel\Runtime\Connection\ConnectionInterface; use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Thelia\Core\Event\AddressEvent; +use Thelia\Core\Event\TheliaEvents; use Thelia\Model\Base\Address as BaseAddress; class Address extends BaseAddress { + use \Thelia\Model\Tools\ModelEventDispatcherTrait; - protected $dispatcher; - - public function setDispatcher(EventDispatcherInterface $dispatcher) + /** + * Code to be run before inserting to database + * @param ConnectionInterface $con + * @return boolean + */ + public function preInsert(ConnectionInterface $con = null) { - $this->dispatcher = $dispatcher; + $this->dispatchEvent(TheliaEvents::BEFORE_CREATEADDRESS, new AddressEvent($this)); + return true; } - public function getDispatcher() + /** + * Code to be run after inserting to database + * @param ConnectionInterface $con + */ + public function postInsert(ConnectionInterface $con = null) { - return $this->dispatcher; + $this->dispatchEvent(TheliaEvents::AFTER_CREATEADDRESS, new AddressEvent($this)); + } + + /** + * Code to be run before updating the object in database + * @param ConnectionInterface $con + * @return boolean + */ + public function preUpdate(ConnectionInterface $con = null) + { + $this->dispatchEvent(TheliaEvents::BEFORE_UPDATEADDRESS, new AddressEvent($this)); + return true; + } + + /** + * Code to be run after updating the object in database + * @param ConnectionInterface $con + */ + public function postUpdate(ConnectionInterface $con = null) + { + $this->dispatchEvent(TheliaEvents::AFTER_UPDATEADDRESS, new AddressEvent($this)); + } + + /** + * Code to be run before deleting the object in database + * @param ConnectionInterface $con + * @return boolean + */ + public function preDelete(ConnectionInterface $con = null) + { + $this->dispatchEvent(TheliaEvents::BEFORE_DELETEADDRESS, new AddressEvent($this)); + return true; + } + + /** + * Code to be run after deleting the object in database + * @param ConnectionInterface $con + */ + public function postDelete(ConnectionInterface $con = null) + { + $this->dispatchEvent(TheliaEvents::AFTER_DELETEADDRESS, new AddressEvent($this)); } } diff --git a/core/lib/Thelia/Model/Base/Lang.php b/core/lib/Thelia/Model/Base/Lang.php index 293b0f3a7..1c11103af 100644 --- a/core/lib/Thelia/Model/Base/Lang.php +++ b/core/lib/Thelia/Model/Base/Lang.php @@ -85,10 +85,40 @@ abstract class Lang implements ActiveRecordInterface protected $url; /** - * The value for the position field. - * @var int + * The value for the date_format field. + * @var string */ - protected $position; + protected $date_format; + + /** + * The value for the time_format field. + * @var string + */ + protected $time_format; + + /** + * The value for the datetime_format field. + * @var string + */ + protected $datetime_format; + + /** + * The value for the decimal_separator field. + * @var string + */ + protected $decimal_separator; + + /** + * The value for the thousands_separator field. + * @var string + */ + protected $thousands_separator; + + /** + * The value for the decimals field. + * @var string + */ + protected $decimals; /** * The value for the by_default field. @@ -96,6 +126,12 @@ abstract class Lang implements ActiveRecordInterface */ protected $by_default; + /** + * The value for the position field. + * @var int + */ + protected $position; + /** * The value for the created_at field. * @var string @@ -426,14 +462,69 @@ abstract class Lang implements ActiveRecordInterface } /** - * Get the [position] column value. + * Get the [date_format] column value. * - * @return int + * @return string */ - public function getPosition() + public function getDateFormat() { - return $this->position; + return $this->date_format; + } + + /** + * Get the [time_format] column value. + * + * @return string + */ + public function getTimeFormat() + { + + return $this->time_format; + } + + /** + * Get the [datetime_format] column value. + * + * @return string + */ + public function getDatetimeFormat() + { + + return $this->datetime_format; + } + + /** + * Get the [decimal_separator] column value. + * + * @return string + */ + public function getDecimalSeparator() + { + + return $this->decimal_separator; + } + + /** + * Get the [thousands_separator] column value. + * + * @return string + */ + public function getThousandsSeparator() + { + + return $this->thousands_separator; + } + + /** + * Get the [decimals] column value. + * + * @return string + */ + public function getDecimals() + { + + return $this->decimals; } /** @@ -447,6 +538,17 @@ abstract class Lang implements ActiveRecordInterface return $this->by_default; } + /** + * Get the [position] column value. + * + * @return int + */ + public function getPosition() + { + + return $this->position; + } + /** * Get the [optionally formatted] temporal [created_at] column value. * @@ -593,25 +695,130 @@ abstract class Lang implements ActiveRecordInterface } // setUrl() /** - * Set the value of [position] column. + * Set the value of [date_format] column. * - * @param int $v new value + * @param string $v new value * @return \Thelia\Model\Lang The current object (for fluent API support) */ - public function setPosition($v) + public function setDateFormat($v) { if ($v !== null) { - $v = (int) $v; + $v = (string) $v; } - if ($this->position !== $v) { - $this->position = $v; - $this->modifiedColumns[] = LangTableMap::POSITION; + if ($this->date_format !== $v) { + $this->date_format = $v; + $this->modifiedColumns[] = LangTableMap::DATE_FORMAT; } return $this; - } // setPosition() + } // setDateFormat() + + /** + * Set the value of [time_format] column. + * + * @param string $v new value + * @return \Thelia\Model\Lang The current object (for fluent API support) + */ + public function setTimeFormat($v) + { + if ($v !== null) { + $v = (string) $v; + } + + if ($this->time_format !== $v) { + $this->time_format = $v; + $this->modifiedColumns[] = LangTableMap::TIME_FORMAT; + } + + + return $this; + } // setTimeFormat() + + /** + * Set the value of [datetime_format] column. + * + * @param string $v new value + * @return \Thelia\Model\Lang The current object (for fluent API support) + */ + public function setDatetimeFormat($v) + { + if ($v !== null) { + $v = (string) $v; + } + + if ($this->datetime_format !== $v) { + $this->datetime_format = $v; + $this->modifiedColumns[] = LangTableMap::DATETIME_FORMAT; + } + + + return $this; + } // setDatetimeFormat() + + /** + * Set the value of [decimal_separator] column. + * + * @param string $v new value + * @return \Thelia\Model\Lang The current object (for fluent API support) + */ + public function setDecimalSeparator($v) + { + if ($v !== null) { + $v = (string) $v; + } + + if ($this->decimal_separator !== $v) { + $this->decimal_separator = $v; + $this->modifiedColumns[] = LangTableMap::DECIMAL_SEPARATOR; + } + + + return $this; + } // setDecimalSeparator() + + /** + * Set the value of [thousands_separator] column. + * + * @param string $v new value + * @return \Thelia\Model\Lang The current object (for fluent API support) + */ + public function setThousandsSeparator($v) + { + if ($v !== null) { + $v = (string) $v; + } + + if ($this->thousands_separator !== $v) { + $this->thousands_separator = $v; + $this->modifiedColumns[] = LangTableMap::THOUSANDS_SEPARATOR; + } + + + return $this; + } // setThousandsSeparator() + + /** + * Set the value of [decimals] column. + * + * @param string $v new value + * @return \Thelia\Model\Lang The current object (for fluent API support) + */ + public function setDecimals($v) + { + if ($v !== null) { + $v = (string) $v; + } + + if ($this->decimals !== $v) { + $this->decimals = $v; + $this->modifiedColumns[] = LangTableMap::DECIMALS; + } + + + return $this; + } // setDecimals() /** * Set the value of [by_default] column. @@ -634,6 +841,27 @@ abstract class Lang implements ActiveRecordInterface return $this; } // setByDefault() + /** + * Set the value of [position] column. + * + * @param int $v new value + * @return \Thelia\Model\Lang The current object (for fluent API support) + */ + public function setPosition($v) + { + if ($v !== null) { + $v = (int) $v; + } + + if ($this->position !== $v) { + $this->position = $v; + $this->modifiedColumns[] = LangTableMap::POSITION; + } + + + return $this; + } // setPosition() + /** * Sets the value of [created_at] column to a normalized version of the date/time value specified. * @@ -728,19 +956,37 @@ abstract class Lang implements ActiveRecordInterface $col = $row[TableMap::TYPE_NUM == $indexType ? 4 + $startcol : LangTableMap::translateFieldName('Url', TableMap::TYPE_PHPNAME, $indexType)]; $this->url = (null !== $col) ? (string) $col : null; - $col = $row[TableMap::TYPE_NUM == $indexType ? 5 + $startcol : LangTableMap::translateFieldName('Position', TableMap::TYPE_PHPNAME, $indexType)]; - $this->position = (null !== $col) ? (int) $col : null; + $col = $row[TableMap::TYPE_NUM == $indexType ? 5 + $startcol : LangTableMap::translateFieldName('DateFormat', TableMap::TYPE_PHPNAME, $indexType)]; + $this->date_format = (null !== $col) ? (string) $col : null; - $col = $row[TableMap::TYPE_NUM == $indexType ? 6 + $startcol : LangTableMap::translateFieldName('ByDefault', TableMap::TYPE_PHPNAME, $indexType)]; + $col = $row[TableMap::TYPE_NUM == $indexType ? 6 + $startcol : LangTableMap::translateFieldName('TimeFormat', TableMap::TYPE_PHPNAME, $indexType)]; + $this->time_format = (null !== $col) ? (string) $col : null; + + $col = $row[TableMap::TYPE_NUM == $indexType ? 7 + $startcol : LangTableMap::translateFieldName('DatetimeFormat', TableMap::TYPE_PHPNAME, $indexType)]; + $this->datetime_format = (null !== $col) ? (string) $col : null; + + $col = $row[TableMap::TYPE_NUM == $indexType ? 8 + $startcol : LangTableMap::translateFieldName('DecimalSeparator', TableMap::TYPE_PHPNAME, $indexType)]; + $this->decimal_separator = (null !== $col) ? (string) $col : null; + + $col = $row[TableMap::TYPE_NUM == $indexType ? 9 + $startcol : LangTableMap::translateFieldName('ThousandsSeparator', TableMap::TYPE_PHPNAME, $indexType)]; + $this->thousands_separator = (null !== $col) ? (string) $col : null; + + $col = $row[TableMap::TYPE_NUM == $indexType ? 10 + $startcol : LangTableMap::translateFieldName('Decimals', TableMap::TYPE_PHPNAME, $indexType)]; + $this->decimals = (null !== $col) ? (string) $col : null; + + $col = $row[TableMap::TYPE_NUM == $indexType ? 11 + $startcol : LangTableMap::translateFieldName('ByDefault', TableMap::TYPE_PHPNAME, $indexType)]; $this->by_default = (null !== $col) ? (int) $col : null; - $col = $row[TableMap::TYPE_NUM == $indexType ? 7 + $startcol : LangTableMap::translateFieldName('CreatedAt', TableMap::TYPE_PHPNAME, $indexType)]; + $col = $row[TableMap::TYPE_NUM == $indexType ? 12 + $startcol : LangTableMap::translateFieldName('Position', TableMap::TYPE_PHPNAME, $indexType)]; + $this->position = (null !== $col) ? (int) $col : null; + + $col = $row[TableMap::TYPE_NUM == $indexType ? 13 + $startcol : LangTableMap::translateFieldName('CreatedAt', TableMap::TYPE_PHPNAME, $indexType)]; if ($col === '0000-00-00 00:00:00') { $col = null; } $this->created_at = (null !== $col) ? PropelDateTime::newInstance($col, null, '\DateTime') : null; - $col = $row[TableMap::TYPE_NUM == $indexType ? 8 + $startcol : LangTableMap::translateFieldName('UpdatedAt', TableMap::TYPE_PHPNAME, $indexType)]; + $col = $row[TableMap::TYPE_NUM == $indexType ? 14 + $startcol : LangTableMap::translateFieldName('UpdatedAt', TableMap::TYPE_PHPNAME, $indexType)]; if ($col === '0000-00-00 00:00:00') { $col = null; } @@ -753,7 +999,7 @@ abstract class Lang implements ActiveRecordInterface $this->ensureConsistency(); } - return $startcol + 9; // 9 = LangTableMap::NUM_HYDRATE_COLUMNS. + return $startcol + 15; // 15 = LangTableMap::NUM_HYDRATE_COLUMNS. } catch (Exception $e) { throw new PropelException("Error populating \Thelia\Model\Lang object", 0, $e); @@ -988,12 +1234,30 @@ abstract class Lang implements ActiveRecordInterface if ($this->isColumnModified(LangTableMap::URL)) { $modifiedColumns[':p' . $index++] = 'URL'; } - if ($this->isColumnModified(LangTableMap::POSITION)) { - $modifiedColumns[':p' . $index++] = 'POSITION'; + if ($this->isColumnModified(LangTableMap::DATE_FORMAT)) { + $modifiedColumns[':p' . $index++] = 'DATE_FORMAT'; + } + if ($this->isColumnModified(LangTableMap::TIME_FORMAT)) { + $modifiedColumns[':p' . $index++] = 'TIME_FORMAT'; + } + if ($this->isColumnModified(LangTableMap::DATETIME_FORMAT)) { + $modifiedColumns[':p' . $index++] = 'DATETIME_FORMAT'; + } + if ($this->isColumnModified(LangTableMap::DECIMAL_SEPARATOR)) { + $modifiedColumns[':p' . $index++] = 'DECIMAL_SEPARATOR'; + } + if ($this->isColumnModified(LangTableMap::THOUSANDS_SEPARATOR)) { + $modifiedColumns[':p' . $index++] = 'THOUSANDS_SEPARATOR'; + } + if ($this->isColumnModified(LangTableMap::DECIMALS)) { + $modifiedColumns[':p' . $index++] = 'DECIMALS'; } if ($this->isColumnModified(LangTableMap::BY_DEFAULT)) { $modifiedColumns[':p' . $index++] = 'BY_DEFAULT'; } + if ($this->isColumnModified(LangTableMap::POSITION)) { + $modifiedColumns[':p' . $index++] = 'POSITION'; + } if ($this->isColumnModified(LangTableMap::CREATED_AT)) { $modifiedColumns[':p' . $index++] = 'CREATED_AT'; } @@ -1026,12 +1290,30 @@ abstract class Lang implements ActiveRecordInterface case 'URL': $stmt->bindValue($identifier, $this->url, PDO::PARAM_STR); break; - case 'POSITION': - $stmt->bindValue($identifier, $this->position, PDO::PARAM_INT); + case 'DATE_FORMAT': + $stmt->bindValue($identifier, $this->date_format, PDO::PARAM_STR); + break; + case 'TIME_FORMAT': + $stmt->bindValue($identifier, $this->time_format, PDO::PARAM_STR); + break; + case 'DATETIME_FORMAT': + $stmt->bindValue($identifier, $this->datetime_format, PDO::PARAM_STR); + break; + case 'DECIMAL_SEPARATOR': + $stmt->bindValue($identifier, $this->decimal_separator, PDO::PARAM_STR); + break; + case 'THOUSANDS_SEPARATOR': + $stmt->bindValue($identifier, $this->thousands_separator, PDO::PARAM_STR); + break; + case 'DECIMALS': + $stmt->bindValue($identifier, $this->decimals, PDO::PARAM_STR); break; case 'BY_DEFAULT': $stmt->bindValue($identifier, $this->by_default, PDO::PARAM_INT); break; + case 'POSITION': + $stmt->bindValue($identifier, $this->position, PDO::PARAM_INT); + break; case 'CREATED_AT': $stmt->bindValue($identifier, $this->created_at ? $this->created_at->format("Y-m-d H:i:s") : null, PDO::PARAM_STR); break; @@ -1116,15 +1398,33 @@ abstract class Lang implements ActiveRecordInterface return $this->getUrl(); break; case 5: - return $this->getPosition(); + return $this->getDateFormat(); break; case 6: - return $this->getByDefault(); + return $this->getTimeFormat(); break; case 7: - return $this->getCreatedAt(); + return $this->getDatetimeFormat(); break; case 8: + return $this->getDecimalSeparator(); + break; + case 9: + return $this->getThousandsSeparator(); + break; + case 10: + return $this->getDecimals(); + break; + case 11: + return $this->getByDefault(); + break; + case 12: + return $this->getPosition(); + break; + case 13: + return $this->getCreatedAt(); + break; + case 14: return $this->getUpdatedAt(); break; default: @@ -1160,10 +1460,16 @@ abstract class Lang implements ActiveRecordInterface $keys[2] => $this->getCode(), $keys[3] => $this->getLocale(), $keys[4] => $this->getUrl(), - $keys[5] => $this->getPosition(), - $keys[6] => $this->getByDefault(), - $keys[7] => $this->getCreatedAt(), - $keys[8] => $this->getUpdatedAt(), + $keys[5] => $this->getDateFormat(), + $keys[6] => $this->getTimeFormat(), + $keys[7] => $this->getDatetimeFormat(), + $keys[8] => $this->getDecimalSeparator(), + $keys[9] => $this->getThousandsSeparator(), + $keys[10] => $this->getDecimals(), + $keys[11] => $this->getByDefault(), + $keys[12] => $this->getPosition(), + $keys[13] => $this->getCreatedAt(), + $keys[14] => $this->getUpdatedAt(), ); $virtualColumns = $this->virtualColumns; foreach($virtualColumns as $key => $virtualColumn) @@ -1220,15 +1526,33 @@ abstract class Lang implements ActiveRecordInterface $this->setUrl($value); break; case 5: - $this->setPosition($value); + $this->setDateFormat($value); break; case 6: - $this->setByDefault($value); + $this->setTimeFormat($value); break; case 7: - $this->setCreatedAt($value); + $this->setDatetimeFormat($value); break; case 8: + $this->setDecimalSeparator($value); + break; + case 9: + $this->setThousandsSeparator($value); + break; + case 10: + $this->setDecimals($value); + break; + case 11: + $this->setByDefault($value); + break; + case 12: + $this->setPosition($value); + break; + case 13: + $this->setCreatedAt($value); + break; + case 14: $this->setUpdatedAt($value); break; } // switch() @@ -1260,10 +1584,16 @@ abstract class Lang implements ActiveRecordInterface if (array_key_exists($keys[2], $arr)) $this->setCode($arr[$keys[2]]); if (array_key_exists($keys[3], $arr)) $this->setLocale($arr[$keys[3]]); if (array_key_exists($keys[4], $arr)) $this->setUrl($arr[$keys[4]]); - if (array_key_exists($keys[5], $arr)) $this->setPosition($arr[$keys[5]]); - if (array_key_exists($keys[6], $arr)) $this->setByDefault($arr[$keys[6]]); - if (array_key_exists($keys[7], $arr)) $this->setCreatedAt($arr[$keys[7]]); - if (array_key_exists($keys[8], $arr)) $this->setUpdatedAt($arr[$keys[8]]); + if (array_key_exists($keys[5], $arr)) $this->setDateFormat($arr[$keys[5]]); + if (array_key_exists($keys[6], $arr)) $this->setTimeFormat($arr[$keys[6]]); + if (array_key_exists($keys[7], $arr)) $this->setDatetimeFormat($arr[$keys[7]]); + if (array_key_exists($keys[8], $arr)) $this->setDecimalSeparator($arr[$keys[8]]); + if (array_key_exists($keys[9], $arr)) $this->setThousandsSeparator($arr[$keys[9]]); + if (array_key_exists($keys[10], $arr)) $this->setDecimals($arr[$keys[10]]); + if (array_key_exists($keys[11], $arr)) $this->setByDefault($arr[$keys[11]]); + if (array_key_exists($keys[12], $arr)) $this->setPosition($arr[$keys[12]]); + if (array_key_exists($keys[13], $arr)) $this->setCreatedAt($arr[$keys[13]]); + if (array_key_exists($keys[14], $arr)) $this->setUpdatedAt($arr[$keys[14]]); } /** @@ -1280,8 +1610,14 @@ abstract class Lang implements ActiveRecordInterface if ($this->isColumnModified(LangTableMap::CODE)) $criteria->add(LangTableMap::CODE, $this->code); if ($this->isColumnModified(LangTableMap::LOCALE)) $criteria->add(LangTableMap::LOCALE, $this->locale); if ($this->isColumnModified(LangTableMap::URL)) $criteria->add(LangTableMap::URL, $this->url); - if ($this->isColumnModified(LangTableMap::POSITION)) $criteria->add(LangTableMap::POSITION, $this->position); + if ($this->isColumnModified(LangTableMap::DATE_FORMAT)) $criteria->add(LangTableMap::DATE_FORMAT, $this->date_format); + if ($this->isColumnModified(LangTableMap::TIME_FORMAT)) $criteria->add(LangTableMap::TIME_FORMAT, $this->time_format); + if ($this->isColumnModified(LangTableMap::DATETIME_FORMAT)) $criteria->add(LangTableMap::DATETIME_FORMAT, $this->datetime_format); + if ($this->isColumnModified(LangTableMap::DECIMAL_SEPARATOR)) $criteria->add(LangTableMap::DECIMAL_SEPARATOR, $this->decimal_separator); + if ($this->isColumnModified(LangTableMap::THOUSANDS_SEPARATOR)) $criteria->add(LangTableMap::THOUSANDS_SEPARATOR, $this->thousands_separator); + if ($this->isColumnModified(LangTableMap::DECIMALS)) $criteria->add(LangTableMap::DECIMALS, $this->decimals); if ($this->isColumnModified(LangTableMap::BY_DEFAULT)) $criteria->add(LangTableMap::BY_DEFAULT, $this->by_default); + if ($this->isColumnModified(LangTableMap::POSITION)) $criteria->add(LangTableMap::POSITION, $this->position); if ($this->isColumnModified(LangTableMap::CREATED_AT)) $criteria->add(LangTableMap::CREATED_AT, $this->created_at); if ($this->isColumnModified(LangTableMap::UPDATED_AT)) $criteria->add(LangTableMap::UPDATED_AT, $this->updated_at); @@ -1351,8 +1687,14 @@ abstract class Lang implements ActiveRecordInterface $copyObj->setCode($this->getCode()); $copyObj->setLocale($this->getLocale()); $copyObj->setUrl($this->getUrl()); - $copyObj->setPosition($this->getPosition()); + $copyObj->setDateFormat($this->getDateFormat()); + $copyObj->setTimeFormat($this->getTimeFormat()); + $copyObj->setDatetimeFormat($this->getDatetimeFormat()); + $copyObj->setDecimalSeparator($this->getDecimalSeparator()); + $copyObj->setThousandsSeparator($this->getThousandsSeparator()); + $copyObj->setDecimals($this->getDecimals()); $copyObj->setByDefault($this->getByDefault()); + $copyObj->setPosition($this->getPosition()); $copyObj->setCreatedAt($this->getCreatedAt()); $copyObj->setUpdatedAt($this->getUpdatedAt()); if ($makeNew) { @@ -1393,8 +1735,14 @@ abstract class Lang implements ActiveRecordInterface $this->code = null; $this->locale = null; $this->url = null; - $this->position = null; + $this->date_format = null; + $this->time_format = null; + $this->datetime_format = null; + $this->decimal_separator = null; + $this->thousands_separator = null; + $this->decimals = null; $this->by_default = null; + $this->position = null; $this->created_at = null; $this->updated_at = null; $this->alreadyInSave = false; diff --git a/core/lib/Thelia/Model/Base/LangQuery.php b/core/lib/Thelia/Model/Base/LangQuery.php index a5100c5f8..2fabec9ee 100644 --- a/core/lib/Thelia/Model/Base/LangQuery.php +++ b/core/lib/Thelia/Model/Base/LangQuery.php @@ -23,8 +23,14 @@ use Thelia\Model\Map\LangTableMap; * @method ChildLangQuery orderByCode($order = Criteria::ASC) Order by the code column * @method ChildLangQuery orderByLocale($order = Criteria::ASC) Order by the locale column * @method ChildLangQuery orderByUrl($order = Criteria::ASC) Order by the url column - * @method ChildLangQuery orderByPosition($order = Criteria::ASC) Order by the position column + * @method ChildLangQuery orderByDateFormat($order = Criteria::ASC) Order by the date_format column + * @method ChildLangQuery orderByTimeFormat($order = Criteria::ASC) Order by the time_format column + * @method ChildLangQuery orderByDatetimeFormat($order = Criteria::ASC) Order by the datetime_format column + * @method ChildLangQuery orderByDecimalSeparator($order = Criteria::ASC) Order by the decimal_separator column + * @method ChildLangQuery orderByThousandsSeparator($order = Criteria::ASC) Order by the thousands_separator column + * @method ChildLangQuery orderByDecimals($order = Criteria::ASC) Order by the decimals column * @method ChildLangQuery orderByByDefault($order = Criteria::ASC) Order by the by_default column + * @method ChildLangQuery orderByPosition($order = Criteria::ASC) Order by the position column * @method ChildLangQuery orderByCreatedAt($order = Criteria::ASC) Order by the created_at column * @method ChildLangQuery orderByUpdatedAt($order = Criteria::ASC) Order by the updated_at column * @@ -33,8 +39,14 @@ use Thelia\Model\Map\LangTableMap; * @method ChildLangQuery groupByCode() Group by the code column * @method ChildLangQuery groupByLocale() Group by the locale column * @method ChildLangQuery groupByUrl() Group by the url column - * @method ChildLangQuery groupByPosition() Group by the position column + * @method ChildLangQuery groupByDateFormat() Group by the date_format column + * @method ChildLangQuery groupByTimeFormat() Group by the time_format column + * @method ChildLangQuery groupByDatetimeFormat() Group by the datetime_format column + * @method ChildLangQuery groupByDecimalSeparator() Group by the decimal_separator column + * @method ChildLangQuery groupByThousandsSeparator() Group by the thousands_separator column + * @method ChildLangQuery groupByDecimals() Group by the decimals column * @method ChildLangQuery groupByByDefault() Group by the by_default column + * @method ChildLangQuery groupByPosition() Group by the position column * @method ChildLangQuery groupByCreatedAt() Group by the created_at column * @method ChildLangQuery groupByUpdatedAt() Group by the updated_at column * @@ -50,8 +62,14 @@ use Thelia\Model\Map\LangTableMap; * @method ChildLang findOneByCode(string $code) Return the first ChildLang filtered by the code column * @method ChildLang findOneByLocale(string $locale) Return the first ChildLang filtered by the locale column * @method ChildLang findOneByUrl(string $url) Return the first ChildLang filtered by the url column - * @method ChildLang findOneByPosition(int $position) Return the first ChildLang filtered by the position column + * @method ChildLang findOneByDateFormat(string $date_format) Return the first ChildLang filtered by the date_format column + * @method ChildLang findOneByTimeFormat(string $time_format) Return the first ChildLang filtered by the time_format column + * @method ChildLang findOneByDatetimeFormat(string $datetime_format) Return the first ChildLang filtered by the datetime_format column + * @method ChildLang findOneByDecimalSeparator(string $decimal_separator) Return the first ChildLang filtered by the decimal_separator column + * @method ChildLang findOneByThousandsSeparator(string $thousands_separator) Return the first ChildLang filtered by the thousands_separator column + * @method ChildLang findOneByDecimals(string $decimals) Return the first ChildLang filtered by the decimals column * @method ChildLang findOneByByDefault(int $by_default) Return the first ChildLang filtered by the by_default column + * @method ChildLang findOneByPosition(int $position) Return the first ChildLang filtered by the position column * @method ChildLang findOneByCreatedAt(string $created_at) Return the first ChildLang filtered by the created_at column * @method ChildLang findOneByUpdatedAt(string $updated_at) Return the first ChildLang filtered by the updated_at column * @@ -60,8 +78,14 @@ use Thelia\Model\Map\LangTableMap; * @method array findByCode(string $code) Return ChildLang objects filtered by the code column * @method array findByLocale(string $locale) Return ChildLang objects filtered by the locale column * @method array findByUrl(string $url) Return ChildLang objects filtered by the url column - * @method array findByPosition(int $position) Return ChildLang objects filtered by the position column + * @method array findByDateFormat(string $date_format) Return ChildLang objects filtered by the date_format column + * @method array findByTimeFormat(string $time_format) Return ChildLang objects filtered by the time_format column + * @method array findByDatetimeFormat(string $datetime_format) Return ChildLang objects filtered by the datetime_format column + * @method array findByDecimalSeparator(string $decimal_separator) Return ChildLang objects filtered by the decimal_separator column + * @method array findByThousandsSeparator(string $thousands_separator) Return ChildLang objects filtered by the thousands_separator column + * @method array findByDecimals(string $decimals) Return ChildLang objects filtered by the decimals column * @method array findByByDefault(int $by_default) Return ChildLang objects filtered by the by_default column + * @method array findByPosition(int $position) Return ChildLang objects filtered by the position column * @method array findByCreatedAt(string $created_at) Return ChildLang objects filtered by the created_at column * @method array findByUpdatedAt(string $updated_at) Return ChildLang objects filtered by the updated_at column * @@ -152,7 +176,7 @@ abstract class LangQuery extends ModelCriteria */ protected function findPkSimple($key, $con) { - $sql = 'SELECT ID, TITLE, CODE, LOCALE, URL, POSITION, BY_DEFAULT, CREATED_AT, UPDATED_AT FROM lang WHERE ID = :p0'; + $sql = 'SELECT ID, TITLE, CODE, LOCALE, URL, DATE_FORMAT, TIME_FORMAT, DATETIME_FORMAT, DECIMAL_SEPARATOR, THOUSANDS_SEPARATOR, DECIMALS, BY_DEFAULT, POSITION, CREATED_AT, UPDATED_AT FROM lang WHERE ID = :p0'; try { $stmt = $con->prepare($sql); $stmt->bindValue(':p0', $key, PDO::PARAM_INT); @@ -399,44 +423,177 @@ abstract class LangQuery extends ModelCriteria } /** - * Filter the query on the position column + * Filter the query on the date_format column * * Example usage: * - * $query->filterByPosition(1234); // WHERE position = 1234 - * $query->filterByPosition(array(12, 34)); // WHERE position IN (12, 34) - * $query->filterByPosition(array('min' => 12)); // WHERE position > 12 + * $query->filterByDateFormat('fooValue'); // WHERE date_format = 'fooValue' + * $query->filterByDateFormat('%fooValue%'); // WHERE date_format LIKE '%fooValue%' * * - * @param mixed $position The value to use as filter. - * Use scalar values for equality. - * Use array values for in_array() equivalent. - * Use associative array('min' => $minValue, 'max' => $maxValue) for intervals. + * @param string $dateFormat The value to use as filter. + * Accepts wildcards (* and % trigger a LIKE) * @param string $comparison Operator to use for the column comparison, defaults to Criteria::EQUAL * * @return ChildLangQuery The current query, for fluid interface */ - public function filterByPosition($position = null, $comparison = null) + public function filterByDateFormat($dateFormat = null, $comparison = null) { - if (is_array($position)) { - $useMinMax = false; - if (isset($position['min'])) { - $this->addUsingAlias(LangTableMap::POSITION, $position['min'], Criteria::GREATER_EQUAL); - $useMinMax = true; - } - if (isset($position['max'])) { - $this->addUsingAlias(LangTableMap::POSITION, $position['max'], Criteria::LESS_EQUAL); - $useMinMax = true; - } - if ($useMinMax) { - return $this; - } - if (null === $comparison) { + if (null === $comparison) { + if (is_array($dateFormat)) { $comparison = Criteria::IN; + } elseif (preg_match('/[\%\*]/', $dateFormat)) { + $dateFormat = str_replace('*', '%', $dateFormat); + $comparison = Criteria::LIKE; } } - return $this->addUsingAlias(LangTableMap::POSITION, $position, $comparison); + return $this->addUsingAlias(LangTableMap::DATE_FORMAT, $dateFormat, $comparison); + } + + /** + * Filter the query on the time_format column + * + * Example usage: + * + * $query->filterByTimeFormat('fooValue'); // WHERE time_format = 'fooValue' + * $query->filterByTimeFormat('%fooValue%'); // WHERE time_format LIKE '%fooValue%' + * + * + * @param string $timeFormat The value to use as filter. + * Accepts wildcards (* and % trigger a LIKE) + * @param string $comparison Operator to use for the column comparison, defaults to Criteria::EQUAL + * + * @return ChildLangQuery The current query, for fluid interface + */ + public function filterByTimeFormat($timeFormat = null, $comparison = null) + { + if (null === $comparison) { + if (is_array($timeFormat)) { + $comparison = Criteria::IN; + } elseif (preg_match('/[\%\*]/', $timeFormat)) { + $timeFormat = str_replace('*', '%', $timeFormat); + $comparison = Criteria::LIKE; + } + } + + return $this->addUsingAlias(LangTableMap::TIME_FORMAT, $timeFormat, $comparison); + } + + /** + * Filter the query on the datetime_format column + * + * Example usage: + * + * $query->filterByDatetimeFormat('fooValue'); // WHERE datetime_format = 'fooValue' + * $query->filterByDatetimeFormat('%fooValue%'); // WHERE datetime_format LIKE '%fooValue%' + * + * + * @param string $datetimeFormat The value to use as filter. + * Accepts wildcards (* and % trigger a LIKE) + * @param string $comparison Operator to use for the column comparison, defaults to Criteria::EQUAL + * + * @return ChildLangQuery The current query, for fluid interface + */ + public function filterByDatetimeFormat($datetimeFormat = null, $comparison = null) + { + if (null === $comparison) { + if (is_array($datetimeFormat)) { + $comparison = Criteria::IN; + } elseif (preg_match('/[\%\*]/', $datetimeFormat)) { + $datetimeFormat = str_replace('*', '%', $datetimeFormat); + $comparison = Criteria::LIKE; + } + } + + return $this->addUsingAlias(LangTableMap::DATETIME_FORMAT, $datetimeFormat, $comparison); + } + + /** + * Filter the query on the decimal_separator column + * + * Example usage: + * + * $query->filterByDecimalSeparator('fooValue'); // WHERE decimal_separator = 'fooValue' + * $query->filterByDecimalSeparator('%fooValue%'); // WHERE decimal_separator LIKE '%fooValue%' + * + * + * @param string $decimalSeparator The value to use as filter. + * Accepts wildcards (* and % trigger a LIKE) + * @param string $comparison Operator to use for the column comparison, defaults to Criteria::EQUAL + * + * @return ChildLangQuery The current query, for fluid interface + */ + public function filterByDecimalSeparator($decimalSeparator = null, $comparison = null) + { + if (null === $comparison) { + if (is_array($decimalSeparator)) { + $comparison = Criteria::IN; + } elseif (preg_match('/[\%\*]/', $decimalSeparator)) { + $decimalSeparator = str_replace('*', '%', $decimalSeparator); + $comparison = Criteria::LIKE; + } + } + + return $this->addUsingAlias(LangTableMap::DECIMAL_SEPARATOR, $decimalSeparator, $comparison); + } + + /** + * Filter the query on the thousands_separator column + * + * Example usage: + * + * $query->filterByThousandsSeparator('fooValue'); // WHERE thousands_separator = 'fooValue' + * $query->filterByThousandsSeparator('%fooValue%'); // WHERE thousands_separator LIKE '%fooValue%' + * + * + * @param string $thousandsSeparator The value to use as filter. + * Accepts wildcards (* and % trigger a LIKE) + * @param string $comparison Operator to use for the column comparison, defaults to Criteria::EQUAL + * + * @return ChildLangQuery The current query, for fluid interface + */ + public function filterByThousandsSeparator($thousandsSeparator = null, $comparison = null) + { + if (null === $comparison) { + if (is_array($thousandsSeparator)) { + $comparison = Criteria::IN; + } elseif (preg_match('/[\%\*]/', $thousandsSeparator)) { + $thousandsSeparator = str_replace('*', '%', $thousandsSeparator); + $comparison = Criteria::LIKE; + } + } + + return $this->addUsingAlias(LangTableMap::THOUSANDS_SEPARATOR, $thousandsSeparator, $comparison); + } + + /** + * Filter the query on the decimals column + * + * Example usage: + * + * $query->filterByDecimals('fooValue'); // WHERE decimals = 'fooValue' + * $query->filterByDecimals('%fooValue%'); // WHERE decimals LIKE '%fooValue%' + * + * + * @param string $decimals The value to use as filter. + * Accepts wildcards (* and % trigger a LIKE) + * @param string $comparison Operator to use for the column comparison, defaults to Criteria::EQUAL + * + * @return ChildLangQuery The current query, for fluid interface + */ + public function filterByDecimals($decimals = null, $comparison = null) + { + if (null === $comparison) { + if (is_array($decimals)) { + $comparison = Criteria::IN; + } elseif (preg_match('/[\%\*]/', $decimals)) { + $decimals = str_replace('*', '%', $decimals); + $comparison = Criteria::LIKE; + } + } + + return $this->addUsingAlias(LangTableMap::DECIMALS, $decimals, $comparison); } /** @@ -480,6 +637,47 @@ abstract class LangQuery extends ModelCriteria return $this->addUsingAlias(LangTableMap::BY_DEFAULT, $byDefault, $comparison); } + /** + * Filter the query on the position column + * + * Example usage: + * + * $query->filterByPosition(1234); // WHERE position = 1234 + * $query->filterByPosition(array(12, 34)); // WHERE position IN (12, 34) + * $query->filterByPosition(array('min' => 12)); // WHERE position > 12 + * + * + * @param mixed $position The value to use as filter. + * Use scalar values for equality. + * Use array values for in_array() equivalent. + * Use associative array('min' => $minValue, 'max' => $maxValue) for intervals. + * @param string $comparison Operator to use for the column comparison, defaults to Criteria::EQUAL + * + * @return ChildLangQuery The current query, for fluid interface + */ + public function filterByPosition($position = null, $comparison = null) + { + if (is_array($position)) { + $useMinMax = false; + if (isset($position['min'])) { + $this->addUsingAlias(LangTableMap::POSITION, $position['min'], Criteria::GREATER_EQUAL); + $useMinMax = true; + } + if (isset($position['max'])) { + $this->addUsingAlias(LangTableMap::POSITION, $position['max'], Criteria::LESS_EQUAL); + $useMinMax = true; + } + if ($useMinMax) { + return $this; + } + if (null === $comparison) { + $comparison = Criteria::IN; + } + } + + return $this->addUsingAlias(LangTableMap::POSITION, $position, $comparison); + } + /** * Filter the query on the created_at column * diff --git a/core/lib/Thelia/Model/Category.php b/core/lib/Thelia/Model/Category.php index 590064a12..ced10c94b 100755 --- a/core/lib/Thelia/Model/Category.php +++ b/core/lib/Thelia/Model/Category.php @@ -13,6 +13,8 @@ class Category extends BaseCategory { use \Thelia\Model\Tools\ModelEventDispatcherTrait; + use \Thelia\Model\Tools\PositionManagementTrait; + /** * @return int number of child for the current category */ @@ -23,7 +25,7 @@ class Category extends BaseCategory public function getUrl($locale) { - return URL::init()->retrieve('category', $this->getId(), $locale)->toString(); + return URL::getInstance()->retrieve('category', $this->getId(), $locale)->toString(); } /** @@ -46,18 +48,6 @@ class Category extends BaseCategory $this->save(); } - public function getNextPosition($parent) { - - $last = CategoryQuery::create() - ->filterByParent($parent) - ->orderByPosition(Criteria::DESC) - ->limit(1) - ->findOne() - ; - - return $last != null ? $last->getPosition() + 1 : 1; - } - /** * * count all products for current category and sub categories diff --git a/core/lib/Thelia/Model/Content.php b/core/lib/Thelia/Model/Content.php index 6522951fe..5bc0ba6b2 100755 --- a/core/lib/Thelia/Model/Content.php +++ b/core/lib/Thelia/Model/Content.php @@ -9,6 +9,6 @@ class Content extends BaseContent { public function getUrl($locale) { - return URL::init()->retrieve('content', $this->getId(), $locale)->toString(); + return URL::getInstance()->retrieve('content', $this->getId(), $locale)->toString(); } } diff --git a/core/lib/Thelia/Model/Currency.php b/core/lib/Thelia/Model/Currency.php index fc1c86b67..ef8d73735 100755 --- a/core/lib/Thelia/Model/Currency.php +++ b/core/lib/Thelia/Model/Currency.php @@ -11,6 +11,8 @@ class Currency extends BaseCurrency { use \Thelia\Model\Tools\ModelEventDispatcherTrait; + use \Thelia\Model\Tools\PositionManagementTrait; + /** * {@inheritDoc} */ @@ -18,6 +20,9 @@ class Currency extends BaseCurrency { { $this->dispatchEvent(TheliaEvents::BEFORE_CREATECURRENCY, new CurrencyEvent($this)); + // Set the current position for the new object + $this->setPosition($this->getNextPosition()); + return true; } diff --git a/core/lib/Thelia/Model/Customer.php b/core/lib/Thelia/Model/Customer.php index 9572b6441..080c02ed6 100755 --- a/core/lib/Thelia/Model/Customer.php +++ b/core/lib/Thelia/Model/Customer.php @@ -2,7 +2,7 @@ namespace Thelia\Model; -use Symfony\Component\Config\Definition\Exception\Exception; +use Propel\Runtime\Exception\PropelException; use Thelia\Model\AddressQuery; use Thelia\Model\Base\Customer as BaseCustomer; @@ -115,7 +115,7 @@ class Customer extends BaseCustomer implements UserInterface $con->commit(); - } catch(Exception $e) { + } catch(PropelException $e) { $con->rollback(); throw $e; } @@ -225,7 +225,7 @@ class Customer extends BaseCustomer implements UserInterface */ public function preUpdate(ConnectionInterface $con = null) { - $this->dispatchEvent(TheliaEvents::BEFORE_CHANGECUSTOMER, new CustomerEvent($this)); + $this->dispatchEvent(TheliaEvents::BEFORE_UPDATECUSTOMER, new CustomerEvent($this)); return true; } @@ -234,7 +234,7 @@ class Customer extends BaseCustomer implements UserInterface */ public function postUpdate(ConnectionInterface $con = null) { - $this->dispatchEvent(TheliaEvents::AFTER_CHANGECUSTOMER, new CustomerEvent($this)); + $this->dispatchEvent(TheliaEvents::AFTER_UPDATECUSTOMER, new CustomerEvent($this)); } /** diff --git a/core/lib/Thelia/Model/Folder.php b/core/lib/Thelia/Model/Folder.php index 5461ba847..8151dae0e 100755 --- a/core/lib/Thelia/Model/Folder.php +++ b/core/lib/Thelia/Model/Folder.php @@ -17,7 +17,7 @@ class Folder extends BaseFolder public function getUrl($locale) { - return URL::init()->retrieve('folder', $this->getId(), $locale)->toString(); + return URL::getInstance()->retrieve('folder', $this->getId(), $locale)->toString(); } /** diff --git a/core/lib/Thelia/Model/Lang.php b/core/lib/Thelia/Model/Lang.php index 654c89719..3c45ade33 100755 --- a/core/lib/Thelia/Model/Lang.php +++ b/core/lib/Thelia/Model/Lang.php @@ -9,48 +9,15 @@ class Lang extends BaseLang { /** * Return the default language object, using a local variable to cache it. * - * @throws RuntimeException + * @throws \RuntimeException */ - private static $default_lang = null; - public static function getDefaultLanguage() { - if (self::$default_lang == null) { - $default_lang = LangQuery::create()->findOneByByDefault(true); - if ($default_lang == null) throw new RuntimeException("No default language is defined. Please define one."); - } + $default_lang = LangQuery::create()->findOneByByDefault(1); + + if ($default_lang == null) throw new \RuntimeException("No default language is defined. Please define one."); return $default_lang; } - - public function getDateFormat() - { - return "d/m/Y"; - } - - public function getTimeFormat() - { - return "H:i:s"; - } - - public function getDateTimeFormat() - { - return "d/m/Y H:i:s"; - } - - public function getDecimalSeparator() - { - return "."; - } - - public function getThousandsSeparator() - { - return " "; - } - - public function getDecimals() - { - return 2; - } } diff --git a/core/lib/Thelia/Model/Map/LangTableMap.php b/core/lib/Thelia/Model/Map/LangTableMap.php index 9eb864388..6d153b869 100644 --- a/core/lib/Thelia/Model/Map/LangTableMap.php +++ b/core/lib/Thelia/Model/Map/LangTableMap.php @@ -57,7 +57,7 @@ class LangTableMap extends TableMap /** * The total number of columns */ - const NUM_COLUMNS = 9; + const NUM_COLUMNS = 15; /** * The number of lazy-loaded columns @@ -67,7 +67,7 @@ class LangTableMap extends TableMap /** * The number of columns to hydrate (NUM_COLUMNS - NUM_LAZY_LOAD_COLUMNS) */ - const NUM_HYDRATE_COLUMNS = 9; + const NUM_HYDRATE_COLUMNS = 15; /** * the column name for the ID field @@ -95,15 +95,45 @@ class LangTableMap extends TableMap const URL = 'lang.URL'; /** - * the column name for the POSITION field + * the column name for the DATE_FORMAT field */ - const POSITION = 'lang.POSITION'; + const DATE_FORMAT = 'lang.DATE_FORMAT'; + + /** + * the column name for the TIME_FORMAT field + */ + const TIME_FORMAT = 'lang.TIME_FORMAT'; + + /** + * the column name for the DATETIME_FORMAT field + */ + const DATETIME_FORMAT = 'lang.DATETIME_FORMAT'; + + /** + * the column name for the DECIMAL_SEPARATOR field + */ + const DECIMAL_SEPARATOR = 'lang.DECIMAL_SEPARATOR'; + + /** + * the column name for the THOUSANDS_SEPARATOR field + */ + const THOUSANDS_SEPARATOR = 'lang.THOUSANDS_SEPARATOR'; + + /** + * the column name for the DECIMALS field + */ + const DECIMALS = 'lang.DECIMALS'; /** * the column name for the BY_DEFAULT field */ const BY_DEFAULT = 'lang.BY_DEFAULT'; + /** + * the column name for the POSITION field + */ + const POSITION = 'lang.POSITION'; + /** * the column name for the CREATED_AT field */ @@ -126,12 +156,12 @@ class LangTableMap extends TableMap * e.g. self::$fieldNames[self::TYPE_PHPNAME][0] = 'Id' */ protected static $fieldNames = array ( - self::TYPE_PHPNAME => array('Id', 'Title', 'Code', 'Locale', 'Url', 'Position', 'ByDefault', 'CreatedAt', 'UpdatedAt', ), - self::TYPE_STUDLYPHPNAME => array('id', 'title', 'code', 'locale', 'url', 'position', 'byDefault', 'createdAt', 'updatedAt', ), - self::TYPE_COLNAME => array(LangTableMap::ID, LangTableMap::TITLE, LangTableMap::CODE, LangTableMap::LOCALE, LangTableMap::URL, LangTableMap::POSITION, LangTableMap::BY_DEFAULT, LangTableMap::CREATED_AT, LangTableMap::UPDATED_AT, ), - self::TYPE_RAW_COLNAME => array('ID', 'TITLE', 'CODE', 'LOCALE', 'URL', 'POSITION', 'BY_DEFAULT', 'CREATED_AT', 'UPDATED_AT', ), - self::TYPE_FIELDNAME => array('id', 'title', 'code', 'locale', 'url', 'position', 'by_default', 'created_at', 'updated_at', ), - self::TYPE_NUM => array(0, 1, 2, 3, 4, 5, 6, 7, 8, ) + self::TYPE_PHPNAME => array('Id', 'Title', 'Code', 'Locale', 'Url', 'DateFormat', 'TimeFormat', 'DatetimeFormat', 'DecimalSeparator', 'ThousandsSeparator', 'Decimals', 'ByDefault', 'Position', 'CreatedAt', 'UpdatedAt', ), + self::TYPE_STUDLYPHPNAME => array('id', 'title', 'code', 'locale', 'url', 'dateFormat', 'timeFormat', 'datetimeFormat', 'decimalSeparator', 'thousandsSeparator', 'decimals', 'byDefault', 'position', 'createdAt', 'updatedAt', ), + self::TYPE_COLNAME => array(LangTableMap::ID, LangTableMap::TITLE, LangTableMap::CODE, LangTableMap::LOCALE, LangTableMap::URL, LangTableMap::DATE_FORMAT, LangTableMap::TIME_FORMAT, LangTableMap::DATETIME_FORMAT, LangTableMap::DECIMAL_SEPARATOR, LangTableMap::THOUSANDS_SEPARATOR, LangTableMap::DECIMALS, LangTableMap::BY_DEFAULT, LangTableMap::POSITION, LangTableMap::CREATED_AT, LangTableMap::UPDATED_AT, ), + self::TYPE_RAW_COLNAME => array('ID', 'TITLE', 'CODE', 'LOCALE', 'URL', 'DATE_FORMAT', 'TIME_FORMAT', 'DATETIME_FORMAT', 'DECIMAL_SEPARATOR', 'THOUSANDS_SEPARATOR', 'DECIMALS', 'BY_DEFAULT', 'POSITION', 'CREATED_AT', 'UPDATED_AT', ), + self::TYPE_FIELDNAME => array('id', 'title', 'code', 'locale', 'url', 'date_format', 'time_format', 'datetime_format', 'decimal_separator', 'thousands_separator', 'decimals', 'by_default', 'position', 'created_at', 'updated_at', ), + self::TYPE_NUM => array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, ) ); /** @@ -141,12 +171,12 @@ class LangTableMap extends TableMap * e.g. self::$fieldKeys[self::TYPE_PHPNAME]['Id'] = 0 */ protected static $fieldKeys = array ( - self::TYPE_PHPNAME => array('Id' => 0, 'Title' => 1, 'Code' => 2, 'Locale' => 3, 'Url' => 4, 'Position' => 5, 'ByDefault' => 6, 'CreatedAt' => 7, 'UpdatedAt' => 8, ), - self::TYPE_STUDLYPHPNAME => array('id' => 0, 'title' => 1, 'code' => 2, 'locale' => 3, 'url' => 4, 'position' => 5, 'byDefault' => 6, 'createdAt' => 7, 'updatedAt' => 8, ), - self::TYPE_COLNAME => array(LangTableMap::ID => 0, LangTableMap::TITLE => 1, LangTableMap::CODE => 2, LangTableMap::LOCALE => 3, LangTableMap::URL => 4, LangTableMap::POSITION => 5, LangTableMap::BY_DEFAULT => 6, LangTableMap::CREATED_AT => 7, LangTableMap::UPDATED_AT => 8, ), - self::TYPE_RAW_COLNAME => array('ID' => 0, 'TITLE' => 1, 'CODE' => 2, 'LOCALE' => 3, 'URL' => 4, 'POSITION' => 5, 'BY_DEFAULT' => 6, 'CREATED_AT' => 7, 'UPDATED_AT' => 8, ), - self::TYPE_FIELDNAME => array('id' => 0, 'title' => 1, 'code' => 2, 'locale' => 3, 'url' => 4, 'position' => 5, 'by_default' => 6, 'created_at' => 7, 'updated_at' => 8, ), - self::TYPE_NUM => array(0, 1, 2, 3, 4, 5, 6, 7, 8, ) + self::TYPE_PHPNAME => array('Id' => 0, 'Title' => 1, 'Code' => 2, 'Locale' => 3, 'Url' => 4, 'DateFormat' => 5, 'TimeFormat' => 6, 'DatetimeFormat' => 7, 'DecimalSeparator' => 8, 'ThousandsSeparator' => 9, 'Decimals' => 10, 'ByDefault' => 11, 'Position' => 12, 'CreatedAt' => 13, 'UpdatedAt' => 14, ), + self::TYPE_STUDLYPHPNAME => array('id' => 0, 'title' => 1, 'code' => 2, 'locale' => 3, 'url' => 4, 'dateFormat' => 5, 'timeFormat' => 6, 'datetimeFormat' => 7, 'decimalSeparator' => 8, 'thousandsSeparator' => 9, 'decimals' => 10, 'byDefault' => 11, 'position' => 12, 'createdAt' => 13, 'updatedAt' => 14, ), + self::TYPE_COLNAME => array(LangTableMap::ID => 0, LangTableMap::TITLE => 1, LangTableMap::CODE => 2, LangTableMap::LOCALE => 3, LangTableMap::URL => 4, LangTableMap::DATE_FORMAT => 5, LangTableMap::TIME_FORMAT => 6, LangTableMap::DATETIME_FORMAT => 7, LangTableMap::DECIMAL_SEPARATOR => 8, LangTableMap::THOUSANDS_SEPARATOR => 9, LangTableMap::DECIMALS => 10, LangTableMap::BY_DEFAULT => 11, LangTableMap::POSITION => 12, LangTableMap::CREATED_AT => 13, LangTableMap::UPDATED_AT => 14, ), + self::TYPE_RAW_COLNAME => array('ID' => 0, 'TITLE' => 1, 'CODE' => 2, 'LOCALE' => 3, 'URL' => 4, 'DATE_FORMAT' => 5, 'TIME_FORMAT' => 6, 'DATETIME_FORMAT' => 7, 'DECIMAL_SEPARATOR' => 8, 'THOUSANDS_SEPARATOR' => 9, 'DECIMALS' => 10, 'BY_DEFAULT' => 11, 'POSITION' => 12, 'CREATED_AT' => 13, 'UPDATED_AT' => 14, ), + self::TYPE_FIELDNAME => array('id' => 0, 'title' => 1, 'code' => 2, 'locale' => 3, 'url' => 4, 'date_format' => 5, 'time_format' => 6, 'datetime_format' => 7, 'decimal_separator' => 8, 'thousands_separator' => 9, 'decimals' => 10, 'by_default' => 11, 'position' => 12, 'created_at' => 13, 'updated_at' => 14, ), + self::TYPE_NUM => array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, ) ); /** @@ -170,8 +200,14 @@ class LangTableMap extends TableMap $this->addColumn('CODE', 'Code', 'VARCHAR', false, 10, null); $this->addColumn('LOCALE', 'Locale', 'VARCHAR', false, 45, null); $this->addColumn('URL', 'Url', 'VARCHAR', false, 255, null); - $this->addColumn('POSITION', 'Position', 'INTEGER', false, null, null); + $this->addColumn('DATE_FORMAT', 'DateFormat', 'VARCHAR', false, 45, null); + $this->addColumn('TIME_FORMAT', 'TimeFormat', 'VARCHAR', false, 45, null); + $this->addColumn('DATETIME_FORMAT', 'DatetimeFormat', 'VARCHAR', false, 45, null); + $this->addColumn('DECIMAL_SEPARATOR', 'DecimalSeparator', 'VARCHAR', false, 45, null); + $this->addColumn('THOUSANDS_SEPARATOR', 'ThousandsSeparator', 'VARCHAR', false, 45, null); + $this->addColumn('DECIMALS', 'Decimals', 'VARCHAR', false, 45, null); $this->addColumn('BY_DEFAULT', 'ByDefault', 'TINYINT', false, null, null); + $this->addColumn('POSITION', 'Position', 'INTEGER', false, null, null); $this->addColumn('CREATED_AT', 'CreatedAt', 'TIMESTAMP', false, null, null); $this->addColumn('UPDATED_AT', 'UpdatedAt', 'TIMESTAMP', false, null, null); } // initialize() @@ -339,8 +375,14 @@ class LangTableMap extends TableMap $criteria->addSelectColumn(LangTableMap::CODE); $criteria->addSelectColumn(LangTableMap::LOCALE); $criteria->addSelectColumn(LangTableMap::URL); - $criteria->addSelectColumn(LangTableMap::POSITION); + $criteria->addSelectColumn(LangTableMap::DATE_FORMAT); + $criteria->addSelectColumn(LangTableMap::TIME_FORMAT); + $criteria->addSelectColumn(LangTableMap::DATETIME_FORMAT); + $criteria->addSelectColumn(LangTableMap::DECIMAL_SEPARATOR); + $criteria->addSelectColumn(LangTableMap::THOUSANDS_SEPARATOR); + $criteria->addSelectColumn(LangTableMap::DECIMALS); $criteria->addSelectColumn(LangTableMap::BY_DEFAULT); + $criteria->addSelectColumn(LangTableMap::POSITION); $criteria->addSelectColumn(LangTableMap::CREATED_AT); $criteria->addSelectColumn(LangTableMap::UPDATED_AT); } else { @@ -349,8 +391,14 @@ class LangTableMap extends TableMap $criteria->addSelectColumn($alias . '.CODE'); $criteria->addSelectColumn($alias . '.LOCALE'); $criteria->addSelectColumn($alias . '.URL'); - $criteria->addSelectColumn($alias . '.POSITION'); + $criteria->addSelectColumn($alias . '.DATE_FORMAT'); + $criteria->addSelectColumn($alias . '.TIME_FORMAT'); + $criteria->addSelectColumn($alias . '.DATETIME_FORMAT'); + $criteria->addSelectColumn($alias . '.DECIMAL_SEPARATOR'); + $criteria->addSelectColumn($alias . '.THOUSANDS_SEPARATOR'); + $criteria->addSelectColumn($alias . '.DECIMALS'); $criteria->addSelectColumn($alias . '.BY_DEFAULT'); + $criteria->addSelectColumn($alias . '.POSITION'); $criteria->addSelectColumn($alias . '.CREATED_AT'); $criteria->addSelectColumn($alias . '.UPDATED_AT'); } diff --git a/core/lib/Thelia/Model/Product.php b/core/lib/Thelia/Model/Product.php index 606a2f3db..6c7ffbe44 100755 --- a/core/lib/Thelia/Model/Product.php +++ b/core/lib/Thelia/Model/Product.php @@ -9,6 +9,6 @@ class Product extends BaseProduct { public function getUrl($locale) { - return URL::init()->retrieve('product', $this->getId(), $locale)->toString(); + return URL::getInstance()->retrieve('product', $this->getId(), $locale)->toString(); } } diff --git a/core/lib/Thelia/Model/Tools/ModelEventDispatcherTrait.php b/core/lib/Thelia/Model/Tools/ModelEventDispatcherTrait.php index 7e2e34d83..17af1e44d 100644 --- a/core/lib/Thelia/Model/Tools/ModelEventDispatcherTrait.php +++ b/core/lib/Thelia/Model/Tools/ModelEventDispatcherTrait.php @@ -44,6 +44,11 @@ trait ModelEventDispatcherTrait { return $this; } + public function getDispatcher() + { + return $this->dispatcher; + } + protected function dispatchEvent($eventName, ActionEvent $event) { if (!is_null($this->dispatcher)) { diff --git a/core/lib/Thelia/Model/Tools/PositionManagementTrait.php b/core/lib/Thelia/Model/Tools/PositionManagementTrait.php new file mode 100644 index 000000000..9e0b1f35c --- /dev/null +++ b/core/lib/Thelia/Model/Tools/PositionManagementTrait.php @@ -0,0 +1,190 @@ +. */ +/* */ +/*************************************************************************************/ + +namespace Thelia\Model\Tools; + +use Propel\Runtime\ActiveQuery\PropelQuery; +use Propel\Runtime\ActiveQuery\Criteria; +use Propel\Runtime\Propel; + +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 = null) { + + $query = $this->createQuery() + ->orderByPosition(Criteria::DESC) + ->limit(1); + + if ($parent !== null) $last->filterByParent($parent); + + $last = $query->findOne() + ; + + return $last != null ? $last->getPosition() + 1 : 1; + } + + /** + * Move up a object + */ + public function movePositionUp() { + $this->movePositionUpOrDown(true); + } + + /** + * Move down a object + */ + public 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(); + + if (method_exists($this, 'getParent')) $search->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($this->getDatabaseName()); + + $cnx->beginTransaction(); + + try { + $this + ->setPosition($result->getPosition()) + ->save() + ; + + $result->setDispatcher($this->getDispatcher())->setPosition($my_position)->save(); + + $cnx->commit(); + } catch (Exception $e) { + $cnx->rollback(); + } + } + } + + /** + * Simply return the database name, from the constant in the MAP class. + */ + protected function getDatabaseName() { + // Find DATABASE_NAME constant + $mapClassName = self::TABLE_MAP; + + return $mapClassName::DATABASE_NAME; + } + + /** + * Changes object position + * + * @param newPosition + */ + public 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(); + + if (method_exists($this, 'getParent')) $search->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($this->getDatabaseName()); + + $cnx->beginTransaction(); + + try { + foreach ($results as $result) { + $result->setDispatcher($this->getDispatcher())->setPosition($result->getPosition() + $delta)->save($cnx); + } + + $this + ->setPosition($newPosition) + ->save($cnx) + ; + + $cnx->commit(); + } catch (Exception $e) { + $cnx->rollback(); + } + } + } +} \ No newline at end of file diff --git a/core/lib/Thelia/Rewriting/RewritingResolver.php b/core/lib/Thelia/Rewriting/RewritingResolver.php index 900442db9..4c8bcba40 100755 --- a/core/lib/Thelia/Rewriting/RewritingResolver.php +++ b/core/lib/Thelia/Rewriting/RewritingResolver.php @@ -58,6 +58,8 @@ class RewritingResolver public function load($rewrittenUrl) { + $rewrittenUrl = ltrim($rewrittenUrl, '/'); + $this->search = $this->rewritingUrlQuery->getResolverSearch($rewrittenUrl); if($this->search->count() == 0) { diff --git a/core/lib/Thelia/Rewriting/RewritingRetriever.php b/core/lib/Thelia/Rewriting/RewritingRetriever.php index 917028bf4..8289942f2 100755 --- a/core/lib/Thelia/Rewriting/RewritingRetriever.php +++ b/core/lib/Thelia/Rewriting/RewritingRetriever.php @@ -66,7 +66,8 @@ class RewritingRetriever $allParametersWithoutView[$view . '_id'] = $viewId; } - $this->url = URL::viewUrl($view, $allParametersWithoutView); + $this->rewrittenUrl = null; + $this->url = URL::getInstance()->viewUrl($view, $allParametersWithoutView); if($this->search !== null) { $this->rewrittenUrl = $this->search->getUrl(); } @@ -93,7 +94,8 @@ class RewritingRetriever $allParametersWithoutView[$view . '_id'] = $viewId; } - $this->url = URL::viewUrl($view, $allParametersWithoutView); + $this->rewrittenUrl = null; + $this->url = URL::getInstance()->viewUrl($view, $allParametersWithoutView); if($this->search !== null) { $this->rewrittenUrl = $this->search->getUrl(); } diff --git a/core/lib/Thelia/Tests/Action/ImageTest.php b/core/lib/Thelia/Tests/Action/ImageTest.php index bde077481..8cc6e5644 100755 --- a/core/lib/Thelia/Tests/Action/ImageTest.php +++ b/core/lib/Thelia/Tests/Action/ImageTest.php @@ -30,13 +30,14 @@ use Thelia\Core\HttpFoundation\Session\Session; use Thelia\Action\Image; use Thelia\Core\Event\ImageEvent; use Thelia\Model\ConfigQuery; +use Thelia\Tools\URL; /** * Class ImageTest * * @package Thelia\Tests\Action\ImageTest */ -class ImageTest extends \PHPUnit_Framework_TestCase +class ImageTest extends \Thelia\Tests\TestCaseWithURLToolSetup { protected $request; @@ -50,6 +51,11 @@ class ImageTest extends \PHPUnit_Framework_TestCase $container->set("event_dispatcher", $dispatcher); + $request = new Request(); + $request->setSession($this->session); + + $container->set("request", $request); + return $container; } diff --git a/core/lib/Thelia/Tests/Command/CacheClearTest.php b/core/lib/Thelia/Tests/Command/CacheClearTest.php index ed2e512b6..741fad299 100755 --- a/core/lib/Thelia/Tests/Command/CacheClearTest.php +++ b/core/lib/Thelia/Tests/Command/CacheClearTest.php @@ -47,6 +47,7 @@ class CacheClearTest extends \PHPUnit_Framework_TestCase $fs = new Filesystem(); $fs->mkdir($this->cache_dir); + $fs->mkdir(THELIA_WEB_DIR . "/assets"); } public function testCacheClear() diff --git a/core/lib/Thelia/Tests/Controller/DefaultControllerTest.php b/core/lib/Thelia/Tests/Controller/DefaultControllerTest.php index fda569435..177802078 100755 --- a/core/lib/Thelia/Tests/Controller/DefaultControllerTest.php +++ b/core/lib/Thelia/Tests/Controller/DefaultControllerTest.php @@ -23,7 +23,7 @@ namespace Thelia\Tests\Controller; -use Symfony\Component\HttpFoundation\Request; +use Thelia\Core\HttpFoundation\Request; use Thelia\Controller\Front\DefaultController; /** @@ -43,7 +43,7 @@ class DefaultControllerTest extends \PHPUnit_Framework_TestCase $this->assertEquals($request->attributes->get('_view'), "index"); } - public function testNoActionWithQuery() + public function testNoActionWithGetParam() { $defaultController = new DefaultController(); $request = new Request(array( @@ -55,15 +55,59 @@ class DefaultControllerTest extends \PHPUnit_Framework_TestCase $this->assertEquals($request->attributes->get('_view'), 'foo'); } - public function testNoActionWithRequest() + public function testNoActionWithPostParam() { $defaultController = new DefaultController(); - $request = new Request(array(), array( - "view" => "foo" - )); + $request = new Request( + array(), + array("view" => "foo") + ); $defaultController->noAction($request); $this->assertEquals($request->attributes->get('_view'), 'foo'); } + + + public function testNoActionWithAttribute() + { + $defaultController = new DefaultController(); + $request = new Request( + array(), + array(), + array("_view" => "foo") + ); + + $defaultController->noAction($request); + + $this->assertEquals($request->attributes->get('_view'), 'foo'); + } + + public function testNoActionWithAttributeAndQuery() + { + $defaultController = new DefaultController(); + $request = new Request( + array("view" => "bar"), + array(), + array("_view" => "foo") + ); + + $defaultController->noAction($request); + + $this->assertEquals($request->attributes->get('_view'), 'bar'); + } + + public function testNoActionWithAttributeAndRequest() + { + $defaultController = new DefaultController(); + $request = new Request( + array(), + array("view" => "bar"), + array("_view" => "foo") + ); + + $defaultController->noAction($request); + + $this->assertEquals($request->attributes->get('_view'), 'bar'); + } } diff --git a/core/lib/Thelia/Tests/Core/Template/Element/BaseLoopTestor.php b/core/lib/Thelia/Tests/Core/Template/Element/BaseLoopTestor.php index c4b0192bc..7d14fbbc5 100755 --- a/core/lib/Thelia/Tests/Core/Template/Element/BaseLoopTestor.php +++ b/core/lib/Thelia/Tests/Core/Template/Element/BaseLoopTestor.php @@ -34,7 +34,7 @@ use Thelia\Core\HttpFoundation\Session\Session; * @author Etienne Roudeix * */ -abstract class BaseLoopTestor extends \PHPUnit_Framework_TestCase +abstract class BaseLoopTestor extends \Thelia\Tests\TestCaseWithURLToolSetup { protected $request; protected $dispatcher; diff --git a/core/lib/Thelia/Tests/Rewriting/RewritingRetrieverTest.php b/core/lib/Thelia/Tests/Rewriting/RewritingRetrieverTest.php index 9a5036adc..8723e0096 100755 --- a/core/lib/Thelia/Tests/Rewriting/RewritingRetrieverTest.php +++ b/core/lib/Thelia/Tests/Rewriting/RewritingRetrieverTest.php @@ -71,7 +71,7 @@ class RewritingRetrieverTest extends \PHPUnit_Framework_TestCase $retriever->loadViewUrl('view', 'fr_FR', 1); $this->assertEquals('foo.html', $retriever->rewrittenUrl); - $this->assertEquals(URL::viewUrl('view', array('locale' => 'fr_FR', 'view_id' => 1)), $retriever->url); + $this->assertEquals(URL::getInstance()->viewUrl('view', array('locale' => 'fr_FR', 'view_id' => 1)), $retriever->url); } public function testLoadSpecificUrl() @@ -93,6 +93,6 @@ class RewritingRetrieverTest extends \PHPUnit_Framework_TestCase $retriever->loadSpecificUrl('view', 'fr_FR', 1, array('foo0' => 'bar0', 'foo1' => 'bar1')); $this->assertEquals('foo.html', $retriever->rewrittenUrl); - $this->assertEquals(URL::viewUrl('view', array('foo0' => 'bar0', 'foo1' => 'bar1', 'locale' => 'fr_FR', 'view_id' => 1)), $retriever->url); + $this->assertEquals(URL::getInstance()->viewUrl('view', array('foo0' => 'bar0', 'foo1' => 'bar1', 'locale' => 'fr_FR', 'view_id' => 1)), $retriever->url); } } diff --git a/core/lib/Thelia/Tests/TestCaseWithURLToolSetup.php b/core/lib/Thelia/Tests/TestCaseWithURLToolSetup.php new file mode 100644 index 000000000..927f7cf62 --- /dev/null +++ b/core/lib/Thelia/Tests/TestCaseWithURLToolSetup.php @@ -0,0 +1,64 @@ +. */ +/* */ +/*************************************************************************************/ + +namespace Thelia\Tests; + +/** + * This class provides URL Tool class initialisation + * + * @package Thelia\Tests\TestCaseWithURLSetup + */ +class TestCaseWithURLToolSetup extends \PHPUnit_Framework_TestCase { + + + public function __construct() { + $this->setupURLTool(); + } + + protected function setupURLTool() { + + $container = new \Symfony\Component\DependencyInjection\ContainerBuilder(); + + $context = new \Symfony\Component\Routing\RequestContext( + '/thelia/index.php', + 'GET', + 'localhost', + 'http', + 80, + 443, + '/path/to/action' + ); + + $router = $this->getMockBuilder("Symfony\Component\Routing\Router") + ->disableOriginalConstructor() + ->getMock(); + + $router->expects($this->any()) + ->method('getContext') + ->will($this->returnValue($context)); + + $container->set("router.admin", $router); + + new \Thelia\Tools\URL($container); + } +} \ No newline at end of file diff --git a/core/lib/Thelia/Tests/Tools/URLTest.php b/core/lib/Thelia/Tests/Tools/URLTest.php index 0c99651b4..86804f5c0 100755 --- a/core/lib/Thelia/Tests/Tools/URLTest.php +++ b/core/lib/Thelia/Tests/Tools/URLTest.php @@ -28,10 +28,208 @@ use Thelia\Tools\URL; /** * * @author Etienne Roudeix + * @author Franck Allimant * */ class URLTest extends \PHPUnit_Framework_TestCase { + protected $context; + + public function setUp() + { + $container = new \Symfony\Component\DependencyInjection\ContainerBuilder(); + + $router = $this->getMockBuilder("Symfony\Component\Routing\Router") + ->disableOriginalConstructor() + ->getMock(); + + $this->context = new \Symfony\Component\Routing\RequestContext( + '/thelia/index.php', + 'GET', + 'localhost', + 'http', + 80, + 443, + '/path/to/action' + ); + + $router->expects($this->any()) + ->method('getContext') + ->will($this->returnValue($this->context)); + + $container->set("router.admin", $router); + + new \Thelia\Tools\URL($container); + } + + public function testGetIndexPage() { + + $this->context->setBaseUrl('/thelia/index.php'); + $url = \Thelia\Tools\URL::getInstance()->getIndexPage(); + $this->assertEquals('http://localhost/thelia/index.php', $url); + + $this->context->setBaseUrl('/thelia/'); + $url = \Thelia\Tools\URL::getInstance()->getIndexPage(); + $this->assertEquals('http://localhost/thelia/', $url); + + $this->context->setBaseUrl('/thelia'); + $url = \Thelia\Tools\URL::getInstance()->getIndexPage(); + $this->assertEquals('http://localhost/thelia', $url); + + $this->context->setBaseUrl('/'); + $url = \Thelia\Tools\URL::getInstance()->getIndexPage(); + $this->assertEquals('http://localhost/', $url); + } + + public function testGetBaseUrl() { + $this->context->setBaseUrl('/thelia/index.php'); + $url = \Thelia\Tools\URL::getInstance()->getBaseUrl(); + $this->assertEquals('http://localhost/thelia/index.php', $url); + + $this->context->setBaseUrl('/thelia/'); + $url = \Thelia\Tools\URL::getInstance()->getBaseUrl(); + $this->assertEquals('http://localhost/thelia/', $url); + + $this->context->setBaseUrl('/'); + $url = \Thelia\Tools\URL::getInstance()->getBaseUrl(); + $this->assertEquals('http://localhost/', $url); + } + + public function testAbsoluteUrl() { + $this->context->setBaseUrl('/'); + $url = \Thelia\Tools\URL::getInstance()->absoluteUrl('/path/to/action'); + $this->assertEquals('http://localhost/path/to/action', $url); + + $this->context->setBaseUrl('/thelia/'); + $url = \Thelia\Tools\URL::getInstance()->absoluteUrl('/path/to/action'); + $this->assertEquals('http://localhost/thelia/path/to/action', $url); + + $this->context->setBaseUrl('/thelia'); + $url = \Thelia\Tools\URL::getInstance()->absoluteUrl('/path/to/action'); + $this->assertEquals('http://localhost/thelia/path/to/action', $url); + + $this->context->setBaseUrl('/thelia/index.php'); + $url = \Thelia\Tools\URL::getInstance()->absoluteUrl('/path/to/action'); + $this->assertEquals('http://localhost/thelia/index.php/path/to/action', $url); + } + + public function testAbsoluteUrlOnAbsolutePath() { + + $this->context->setBaseUrl('/thelia/index.php'); + $url = \Thelia\Tools\URL::getInstance()->absoluteUrl('http://myhost/path/to/action'); + $this->assertEquals('http://myhost/path/to/action', $url); + + $this->context->setBaseUrl('/thelia/'); + $url = \Thelia\Tools\URL::getInstance()->absoluteUrl('http://myhost/path/to/action'); + $this->assertEquals('http://myhost/path/to/action', $url); + + $this->context->setBaseUrl('/'); + $url = \Thelia\Tools\URL::getInstance()->absoluteUrl('http://myhost/path/to/action'); + $this->assertEquals('http://myhost/path/to/action', $url); + } + + public function testAbsoluteUrlOnAbsolutePathWithParameters() { + + $this->context->setBaseUrl('/thelia/index.php'); + $url = \Thelia\Tools\URL::getInstance()->absoluteUrl('http://myhost/path/to/action', array("p1" => "v1", "p2" => "v2")); + $this->assertEquals('http://myhost/path/to/action?p1=v1&p2=v2', $url); + + $this->context->setBaseUrl('/thelia/'); + $url = \Thelia\Tools\URL::getInstance()->absoluteUrl('http://myhost/path/to/action', array("p1" => "v1", "p2" => "v2")); + $this->assertEquals('http://myhost/path/to/action?p1=v1&p2=v2', $url); + + $this->context->setBaseUrl('/'); + $url = \Thelia\Tools\URL::getInstance()->absoluteUrl('http://myhost/path/to/action', array("p1" => "v1", "p2" => "v2")); + $this->assertEquals('http://myhost/path/to/action?p1=v1&p2=v2', $url); + } + + public function testAbsoluteUrlOnAbsolutePathWithParametersAddParameters() { + $this->context->setBaseUrl('/thelia/index.php'); + $url = \Thelia\Tools\URL::getInstance()->absoluteUrl('http://myhost/path/to/action?p0=v0', array("p1" => "v1", "p2" => "v2")); + $this->assertEquals('http://myhost/path/to/action?p0=v0&p1=v1&p2=v2', $url); + + $this->context->setBaseUrl('/thelia/'); + $url = \Thelia\Tools\URL::getInstance()->absoluteUrl('http://myhost/path/to/action?p0=v0', array("p1" => "v1", "p2" => "v2")); + $this->assertEquals('http://myhost/path/to/action?p0=v0&p1=v1&p2=v2', $url); + + $this->context->setBaseUrl('/'); + $url = \Thelia\Tools\URL::getInstance()->absoluteUrl('http://myhost/path/to/action?p0=v0', array("p1" => "v1", "p2" => "v2")); + $this->assertEquals('http://myhost/path/to/action?p0=v0&p1=v1&p2=v2', $url); + } + + public function testAbsoluteUrlWithParameters() { + $this->context->setBaseUrl('/thelia/index.php'); + $url = \Thelia\Tools\URL::getInstance()->absoluteUrl('/path/to/action', array("p1" => "v1", "p2" => "v2")); + $this->assertEquals('http://localhost/thelia/index.php/path/to/action?p1=v1&p2=v2', $url); + + $this->context->setBaseUrl('/thelia/'); + $url = \Thelia\Tools\URL::getInstance()->absoluteUrl('/path/to/action', array("p1" => "v1", "p2" => "v2")); + $this->assertEquals('http://localhost/thelia/path/to/action?p1=v1&p2=v2', $url); + + $this->context->setBaseUrl('/thelia'); + $url = \Thelia\Tools\URL::getInstance()->absoluteUrl('/path/to/action', array("p1" => "v1", "p2" => "v2")); + $this->assertEquals('http://localhost/thelia/path/to/action?p1=v1&p2=v2', $url); + + $this->context->setBaseUrl('/thelia'); + $url = \Thelia\Tools\URL::getInstance()->absoluteUrl('path/to/action', array("p1" => "v1", "p2" => "v2")); + $this->assertEquals('http://localhost/thelia/path/to/action?p1=v1&p2=v2', $url); + + $this->context->setBaseUrl('/'); + $url = \Thelia\Tools\URL::getInstance()->absoluteUrl('/path/to/action', array("p1" => "v1", "p2" => "v2")); + $this->assertEquals('http://localhost/path/to/action?p1=v1&p2=v2', $url); + } + + public function testAbsoluteUrlPathOnly() { + $this->context->setBaseUrl('/thelia/index.php'); + $url = \Thelia\Tools\URL::getInstance()->absoluteUrl('/path/to/action', array(), URL::PATH_TO_FILE); + $this->assertEquals('http://localhost/thelia/path/to/action', $url); + } + + public function testAbsoluteUrlPathOnlyWithParameters() { + $this->context->setBaseUrl('/thelia/index.php'); + $url = \Thelia\Tools\URL::getInstance()->absoluteUrl('/path/to/action', array("p1" => "v1", "p2" => "v2"), URL::PATH_TO_FILE); + $this->assertEquals('http://localhost/thelia/path/to/action?p1=v1&p2=v2', $url); + + $this->context->setBaseUrl('/thelia/'); + $url = \Thelia\Tools\URL::getInstance()->absoluteUrl('/path/to/action', array("p1" => "v1", "p2" => "v2"), URL::PATH_TO_FILE); + $this->assertEquals('http://localhost/thelia/path/to/action?p1=v1&p2=v2', $url); + + $this->context->setBaseUrl('/'); + $url = \Thelia\Tools\URL::getInstance()->absoluteUrl('/path/to/action', array("p1" => "v1", "p2" => "v2"), URL::PATH_TO_FILE); + $this->assertEquals('http://localhost/path/to/action?p1=v1&p2=v2', $url); + + } + + public function testAbsoluteUrlFromIndexWithParameters() { + $this->context->setBaseUrl('/thelia/index.php'); + $url = \Thelia\Tools\URL::getInstance()->absoluteUrl('/', array("p1" => "v1", "p2" => "v2")); + $this->assertEquals('http://localhost/thelia/index.php/?p1=v1&p2=v2', $url); + + $this->context->setBaseUrl('/thelia/'); + $url = \Thelia\Tools\URL::getInstance()->absoluteUrl('/', array("p1" => "v1", "p2" => "v2")); + $this->assertEquals('http://localhost/thelia/?p1=v1&p2=v2', $url); + + $this->context->setBaseUrl('/'); + $url = \Thelia\Tools\URL::getInstance()->absoluteUrl('/', array("p1" => "v1", "p2" => "v2")); + $this->assertEquals('http://localhost/?p1=v1&p2=v2', $url); + + } + + public function testAbsoluteUrlPathOnlyFromIndexWithParameters() { + $this->context->setBaseUrl('/thelia/index.php'); + $url = \Thelia\Tools\URL::getInstance()->absoluteUrl('/', array("p1" => "v1", "p2" => "v2"), URL::PATH_TO_FILE); + $this->assertEquals('http://localhost/thelia/?p1=v1&p2=v2', $url); + + $this->context->setBaseUrl('/thelia/'); + $url = \Thelia\Tools\URL::getInstance()->absoluteUrl('/', array("p1" => "v1", "p2" => "v2"), URL::PATH_TO_FILE); + $this->assertEquals('http://localhost/thelia/?p1=v1&p2=v2', $url); + + $this->context->setBaseUrl('/'); + $url = \Thelia\Tools\URL::getInstance()->absoluteUrl('/', array("p1" => "v1", "p2" => "v2"), URL::PATH_TO_FILE); + $this->assertEquals('http://localhost/?p1=v1&p2=v2', $url); + + } + public function testRetrieve() { diff --git a/core/lib/Thelia/Tools/URL.php b/core/lib/Thelia/Tools/URL.php index 190f7541c..161175bbf 100755 --- a/core/lib/Thelia/Tools/URL.php +++ b/core/lib/Thelia/Tools/URL.php @@ -23,38 +23,89 @@ namespace Thelia\Tools; -use Symfony\Component\HttpFoundation\Request; use Thelia\Model\ConfigQuery; use Thelia\Rewriting\RewritingResolver; use Thelia\Rewriting\RewritingRetriever; +use Thelia\Core\HttpFoundation\Request; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\Routing\RequestContext; + class URL { protected $resolver = null; protected $retriever = null; + protected $requestContext; + const PATH_TO_FILE = true; const WITH_INDEX_PAGE = false; - public function __construct() + protected static $instance = null; + + public function __construct(ContainerInterface $container) { + // 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->requestContext = $container->get('router.admin')->getContext(); + $this->retriever = new RewritingRetriever(); $this->resolver = new RewritingResolver(); } - public static function getIndexPage() - { - return ConfigQuery::read('base_url', '/') . "index_dev.php"; // FIXME ! + /** + * Return this class instance, only once instanciated. + * + * @throws \RuntimeException if the class has not been instanciated. + * @return \Thelia\Tools\URL the instance. + */ + public static function getInstance() { + if (self::$instance == null) throw new \RuntimeException("URL instance is not initialized."); + + return self::$instance; } - public static function init() + /** + * Return the base URL, either the base_url defined in Config, or the URL + * of the current language, if 'one_domain_foreach_lang' is enabled. + * + * @return string the base URL, with a trailing '/' + */ + public function getBaseUrl() { - return new URL(); + if ($host = $this->requestContext->getHost()) { + + $scheme = $this->requestContext->getScheme(); + + $port = ''; + + if ('http' === $scheme && 80 != $this->requestContext->getHttpPort()) { + $port = ':'.$this->requestContext->getHttpPort(); + } elseif ('https' === $scheme && 443 != $this->requestContext->getHttpsPort()) { + $port = ':'.$this->requestContext->getHttpsPort(); + } + + $schemeAuthority = "$scheme://$host"."$port"; + } + + return $schemeAuthority.$this->requestContext->getBaseUrl(); + } + + /** + * @return string the index page, which is in fact the base URL. + */ + public function getIndexPage() + { + // The index page is the base URL :) + return $this->getBaseUrl(); } /** * Returns the Absolute URL for a given path relative to web root. By default, - * the index.php (or index_dev.php) script name is added to the URL, use + * the script name (index_dev.php) is added to the URL in dev_environment, use * $path_only = true to get a path without the index script. * * @param string $path the relative path @@ -63,19 +114,21 @@ class URL * * @return string The generated URL */ - public static function absoluteUrl($path, array $parameters = null, $path_only = self::WITH_INDEX_PAGE) + public function absoluteUrl($path, array $parameters = null, $path_only = self::WITH_INDEX_PAGE) { // Already absolute ? if (substr($path, 0, 4) != 'http') { - /** - * @etienne : can't be done here for it's already done in ::viewUrl / ::adminViewUrl - * @franck : should be done, as absoluteUrl() is sometimes called directly (see UrlGenerator::generateUrlFunction()) - */ - $root = $path_only == self::PATH_TO_FILE ? ConfigQuery::read('base_url', '/') : self::getIndexPage(); - //$root = $path_only == self::PATH_TO_FILE ? ConfigQuery::read('base_url', '/') : ''; + $base_url = $this->getBaseUrl(); - $base = rtrim($root, '/') . '/' . ltrim($path, '/'); + // If only a path is requested, be sure to remove the script name (index.php or index_dev.php), if any. + if ($path_only == self::PATH_TO_FILE) { + // As the base_url always ends with '/', if we don't find / at the end, we have a script. + if (substr($base_url, -1) != '/') $base_url = dirname($base_url); + } + + // Normalize the given path + $base = rtrim($base_url, '/') . '/' . ltrim($path, '/'); } else $base = $path; @@ -90,6 +143,7 @@ class URL $sepChar = strstr($base, '?') === false ? '?' : '&'; if ('' !== $queryString = rtrim($queryString, "&")) $queryString = $sepChar . $queryString; + return $base . $queryString; } @@ -101,11 +155,11 @@ class URL * * @return string The generated URL */ - public static function adminViewUrl($viewName, array $parameters = array()) + public function adminViewUrl($viewName, array $parameters = array()) { - $path = sprintf("%s/admin/%s", self::getIndexPage(), $viewName); // FIXME ! view= should not be required, check routing parameters + $path = sprintf("%s/admin/%s", $this->getIndexPage(), $viewName); - return self::absoluteUrl($path, $parameters); + return $this->absoluteUrl($path, $parameters); } /** @@ -116,72 +170,71 @@ class URL * * @return string The generated URL */ - public static function viewUrl($viewName, array $parameters = array()) + public function viewUrl($viewName, array $parameters = array()) { $path = sprintf("?view=%s", $viewName); - return self::absoluteUrl($path, $parameters); + return $this->absoluteUrl($path, $parameters); + } + /** + * Retrieve a rewritten URL from a view, a view id and a locale + * + * @param $view + * @param $viewId + * @param $viewLocale + * + * @return RewritingRetriever You can access $url and $rewrittenUrl properties + */ + public function retrieve($view, $viewId, $viewLocale) + { + if(ConfigQuery::isRewritingEnable()) { + $this->retriever->loadViewUrl($view, $viewLocale, $viewId); + } + + return $this->retriever; } - /** - * Retrieve a rewritten URL from a view, a view id and a locale - * - * @param $view - * @param $viewId - * @param $viewLocale - * - * @return RewritingRetriever You can access $url and $rewrittenUrl properties - */ - public function retrieve($view, $viewId, $viewLocale) - { - if(ConfigQuery::isRewritingEnable()) { - $this->retriever->loadViewUrl($view, $viewLocale, $viewId); - } + /** + * Retrieve a rewritten URL from the current GET parameters + * + * @param Request $request + * + * @return RewritingRetriever You can access $url and $rewrittenUrl properties or use toString method + */ + public function retrieveCurrent(Request $request) + { + if(ConfigQuery::isRewritingEnable()) { + $view = $request->attributes->get('_view', null); + $viewLocale = $request->query->get('locale', null); + $viewId = $view === null ? null : $request->query->get($view . '_id', null); - return $this->retriever; - } + $allOtherParameters = $request->query->all(); + if($view !== null) { + unset($allOtherParameters['view']); + if($viewId !== null) { + unset($allOtherParameters[$view . '_id']); + } + } + if($viewLocale !== null) { + unset($allOtherParameters['locale']); + } - /** - * Retrieve a rewritten URL from the current GET parameters - * - * @param Request $request - * - * @return RewritingRetriever You can access $url and $rewrittenUrl properties or use toString method - */ - public function retrieveCurrent(Request $request) - { - if(ConfigQuery::isRewritingEnable()) { - $view = $request->query->get('view', null); - $viewLocale = $request->query->get('locale', null); - $viewId = $view === null ? null : $request->query->get($view . '_id', null); + $this->retriever->loadSpecificUrl($view, $viewLocale, $viewId, $allOtherParameters); + } - $allOtherParameters = $request->query->all(); - if($view !== null) { - unset($allOtherParameters['view']); - } - if($viewLocale !== null) { - unset($allOtherParameters['locale']); - } - if($viewId !== null) { - unset($allOtherParameters[$view . '_id']); - } + return $this->retriever; + } - $this->retriever->loadSpecificUrl($view, $viewLocale, $viewId, $allOtherParameters); - } - - return $this->retriever; - } - - /** - * Retrieve a rewritten URL from the current GET parameters or use toString method - * - * @param $url - * - * @return RewritingResolver - */ - public function resolve($url) - { - $this->resolver->load($url); - return $this->resolver; - } -} + /** + * Retrieve a rewritten URL from the current GET parameters or use toString method + * + * @param $url + * + * @return RewritingResolver + */ + public function resolve($url) + { + $this->resolver->load($url); + return $this->resolver; + } +} \ No newline at end of file diff --git a/documentation/api/classes/Thelia.Core.Event.TheliaEvents.html b/documentation/api/classes/Thelia.Core.Event.TheliaEvents.html index fe9f532a1..2d4072603 100755 --- a/documentation/api/classes/Thelia.Core.Event.TheliaEvents.html +++ b/documentation/api/classes/Thelia.Core.Event.TheliaEvents.html @@ -1631,8 +1631,8 @@ ADMIN_LOGIN
BEFORE_CREATECUSTOMER
AFTER_CREATECUSTOMER
- BEFORE_CHANGECUSTOMER
- AFTER_CHANGECUSTOMER
+ BEFORE_UPDATECUSTOMER
+ AFTER_UPDATECUSTOMER
BEFORE_CREATECATEGORY
AFTER_CREATECATEGORY
BEFORE_DELETECATEGORY
@@ -1891,8 +1891,8 @@
-

BEFORE_CHANGECUSTOMER

-
BEFORE_CHANGECUSTOMER
+

BEFORE_UPDATECUSTOMER

+
BEFORE_UPDATECUSTOMER

Sent once the customer change form has been successfully validated, and before customer update in the database.

@@ -1913,8 +1913,8 @@
-

AFTER_CHANGECUSTOMER

-
AFTER_CHANGECUSTOMER
+

AFTER_UPDATECUSTOMER

+
AFTER_UPDATECUSTOMER

Sent just after a successful update of a customer in the database.

diff --git a/documentation/api/files/Action/Category.php.txt b/documentation/api/files/Action/Category.php.txt index b62357b08..ddd3362a5 100755 --- a/documentation/api/files/Action/Category.php.txt +++ b/documentation/api/files/Action/Category.php.txt @@ -109,7 +109,7 @@ class Category extends BaseAction implements EventSubscriberInterface $customer = CustomerQuery::create()->findPk(1); try { $customerEvent = new CustomerEvent($customer); - $event->getDispatcher()->dispatch(TheliaEvents::BEFORE_CHANGECUSTOMER, $customerEvent); + $event->getDispatcher()->dispatch(TheliaEvents::BEFORE_UPDATECUSTOMER, $customerEvent); $data = $form->getData(); @@ -127,7 +127,7 @@ class Category extends BaseAction implements EventSubscriberInterface ); $customerEvent->customer = $customer; - $event->getDispatcher()->dispatch(TheliaEvents::AFTER_CHANGECUSTOMER, $customerEvent); + $event->getDispatcher()->dispatch(TheliaEvents::AFTER_UPDATECUSTOMER, $customerEvent); // Update the logged-in user, and redirect to the success URL (exits) // We don-t send the login event, as the customer si already logged. diff --git a/documentation/api/files/Core/Event/TheliaEvents.php.txt b/documentation/api/files/Core/Event/TheliaEvents.php.txt index e06abc3c7..ca95903a8 100755 --- a/documentation/api/files/Core/Event/TheliaEvents.php.txt +++ b/documentation/api/files/Core/Event/TheliaEvents.php.txt @@ -78,11 +78,11 @@ final class TheliaEvents /** * Sent once the customer change form has been successfully validated, and before customer update in the database. */ - const BEFORE_CHANGECUSTOMER = "action.before_changecustomer"; + const BEFORE_UPDATECUSTOMER = "action.before_changecustomer"; /** * Sent just after a successful update of a customer in the database. */ - const AFTER_CHANGECUSTOMER = "action.after_changecustomer"; + const AFTER_UPDATECUSTOMER = "action.after_changecustomer"; /** * Sent once the category creation form has been successfully validated, and before category insertion in the database. diff --git a/documentation/api/files/Model/Customer.php.txt b/documentation/api/files/Model/Customer.php.txt index cbd507bf4..a220fd0c8 100755 --- a/documentation/api/files/Model/Customer.php.txt +++ b/documentation/api/files/Model/Customer.php.txt @@ -125,7 +125,7 @@ class Customer extends BaseCustomer implements UserInterface public function preUpdate(ConnectionInterface $con = null) { $customerEvent = new CustomerEvent($this); - $this->dispatchEvent(TheliaEvents::BEFORE_CHANGECUSTOMER, $customerEvent); + $this->dispatchEvent(TheliaEvents::BEFORE_UPDATECUSTOMER, $customerEvent); return true; } @@ -133,7 +133,7 @@ class Customer extends BaseCustomer implements UserInterface public function postUpdate(ConnectionInterface $con = null) { $customerEvent = new CustomerEvent($this); - $this->dispatchEvent(TheliaEvents::AFTER_CHANGECUSTOMER, $customerEvent); + $this->dispatchEvent(TheliaEvents::AFTER_UPDATECUSTOMER, $customerEvent); } protected function dispatchEvent($eventName, CustomerEvent $customerEvent) diff --git a/install/insert.sql b/install/insert.sql index a4b7d3c1b..a2e5868e4 100755 --- a/install/insert.sql +++ b/install/insert.sql @@ -1,8 +1,8 @@ -INSERT INTO `lang`(`id`,`title`,`code`,`locale`,`url`,`by_default`,`created_at`,`updated_at`)VALUES -(1, 'Français', 'fr', 'fr_FR', '','1', NOW(), NOW()), -(2, 'English', 'en', 'en_EN', '', '0', NOW(), NOW()), -(3, 'Espanol', 'es', 'es_ES', '', '0', NOW(), NOW()), -(4, 'Italiano', 'it', 'it_IT', '','0', NOW(), NOW()); +INSERT INTO `lang`(`id`,`title`,`code`,`locale`,`url`,`date_format`,`time_format`,`datetime_format`,`decimal_separator`,`thousands_separator`,`decimals`,`by_default`,`created_at`,`updated_at`)VALUES +(1, 'Français', 'fr', 'fr_FR', '', 'd/m/Y', 'H:i:s', 'd/m/y H:i:s', ',', ' ', '2', '1', NOW(), NOW()), +(2, 'English', 'en', 'en_EN', '', 'm-d-Y', 'h:i:s', 'm-d-Y h:i:s', '.', ' ', '2', '0', NOW(), NOW()), +(3, 'castellano', 'es', 'es_ES', '', 'm-d-Y', 'h:i:s', 'm-d-Y h:i:s', ',', '.', '2', '0', NOW(), NOW()), +(4, 'Italiano', 'it', 'it_IT', '', 'd/m/Y', 'H:i:s', 'd/m/y H:i:s', ',', ' ', '2', '0', NOW(), NOW()); INSERT INTO `config` (`name`, `value`, `secured`, `hidden`, `created_at`, `updated_at`) VALUES ('session_config.default', '1', 1, 1, NOW(), NOW()), diff --git a/install/thelia.sql b/install/thelia.sql index 85c00b369..879f323b9 100755 --- a/install/thelia.sql +++ b/install/thelia.sql @@ -522,8 +522,14 @@ CREATE TABLE `lang` `code` VARCHAR(10), `locale` VARCHAR(45), `url` VARCHAR(255), - `position` INTEGER, + `date_format` VARCHAR(45), + `time_format` VARCHAR(45), + `datetime_format` VARCHAR(45), + `decimal_separator` VARCHAR(45), + `thousands_separator` VARCHAR(45), + `decimals` VARCHAR(45), `by_default` TINYINT, + `position` INTEGER, `created_at` DATETIME, `updated_at` DATETIME, PRIMARY KEY (`id`) diff --git a/local/config/schema.xml b/local/config/schema.xml index ea2b108fb..d4fb9213f 100755 --- a/local/config/schema.xml +++ b/local/config/schema.xml @@ -389,8 +389,14 @@ - + + + + + + + diff --git a/templates/admin/default/admin-layout.tpl b/templates/admin/default/admin-layout.tpl index d392919ad..7ab676532 100644 --- a/templates/admin/default/admin-layout.tpl +++ b/templates/admin/default/admin-layout.tpl @@ -210,9 +210,7 @@ {block name="before-javascript-include"}{/block} - {javascripts file='assets/js/jquery.min.js'} - - {/javascripts} + {block name="after-javascript-include"}{/block} diff --git a/templates/admin/default/assets/bootstrap-editable/css/bootstrap-editable.css b/templates/admin/default/assets/bootstrap-editable/css/bootstrap-editable.css deleted file mode 100755 index ed3d651ae..000000000 --- a/templates/admin/default/assets/bootstrap-editable/css/bootstrap-editable.css +++ /dev/null @@ -1,205 +0,0 @@ -/*! X-editable - v1.4.6 -* In-place editing with Twitter Bootstrap, jQuery UI or pure jQuery -* http://github.com/vitalets/x-editable -* Copyright (c) 2013 Vitaliy Potapov; Licensed MIT */ - -.editableform { - margin-bottom: 0; /* overwrites bootstrap margin */ -} - -.editableform .control-group { - margin-bottom: 0; /* overwrites bootstrap margin */ - white-space: nowrap; /* prevent wrapping buttons on new line */ - line-height: 20px; /* overwriting bootstrap line-height. See #133 */ -} - -.editable-buttons { - display: inline-block; /* should be inline to take effect of parent's white-space: nowrap */ - vertical-align: top; - margin-left: 7px; - /* inline-block emulation for IE7*/ - zoom: 1; - *display: inline; -} - -.editable-buttons.editable-buttons-bottom { - display: block; - margin-top: 7px; - margin-left: 0; -} - -.editable-input { - vertical-align: top; - display: inline-block; /* should be inline to take effect of parent's white-space: nowrap */ - width: auto; /* bootstrap-responsive has width: 100% that breakes layout */ - white-space: normal; /* reset white-space decalred in parent*/ - /* display-inline emulation for IE7*/ - zoom: 1; - *display: inline; -} - -.editable-buttons .editable-cancel { - margin-left: 7px; -} - -/*for jquery-ui buttons need set height to look more pretty*/ -.editable-buttons button.ui-button-icon-only { - height: 24px; - width: 30px; -} - -.editableform-loading { - background: url('../img/loading.gif') center center no-repeat; - height: 25px; - width: auto; - min-width: 25px; -} - -.editable-inline .editableform-loading { - background-position: left 5px; -} - - .editable-error-block { - max-width: 300px; - margin: 5px 0 0 0; - width: auto; - white-space: normal; -} - -/*add padding for jquery ui*/ -.editable-error-block.ui-state-error { - padding: 3px; -} - -.editable-error { - color: red; -} - -/* ---- For specific types ---- */ - -.editableform .editable-date { - padding: 0; - margin: 0; - float: left; -} - -/* move datepicker icon to center of add-on button. See https://github.com/vitalets/x-editable/issues/183 */ -.editable-inline .add-on .icon-th { - margin-top: 3px; - margin-left: 1px; -} - - -/* checklist vertical alignment */ -.editable-checklist label input[type="checkbox"], -.editable-checklist label span { - vertical-align: middle; - margin: 0; -} - -.editable-checklist label { - white-space: nowrap; -} - -/* set exact width of textarea to fit buttons toolbar */ -.editable-wysihtml5 { - width: 566px; - height: 250px; -} - -/* clear button shown as link in date inputs */ -.editable-clear { - clear: both; - font-size: 0.9em; - text-decoration: none; - text-align: right; -} - -/* IOS-style clear button for text inputs */ -.editable-clear-x { - background: url('../img/clear.png') center center no-repeat; - display: block; - width: 13px; - height: 13px; - position: absolute; - opacity: 0.6; - z-index: 100; - - top: 50%; - right: 6px; - margin-top: -6px; - -} - -.editable-clear-x:hover { - opacity: 1; -} - -.editable-pre-wrapped { - white-space: pre-wrap; -} -.editable-container.editable-popup { - max-width: none !important; /* without this rule poshytip/tooltip does not stretch */ -} - -.editable-container.popover { - width: auto; /* without this rule popover does not stretch */ -} - -.editable-container.editable-inline { - display: inline-block; - vertical-align: middle; - width: auto; - /* inline-block emulation for IE7*/ - zoom: 1; - *display: inline; -} - -.editable-container.ui-widget { - font-size: inherit; /* jqueryui widget font 1.1em too big, overwrite it */ - z-index: 9990; /* should be less than select2 dropdown z-index to close dropdown first when click */ -} -.editable-click, -a.editable-click, -a.editable-click:hover { - text-decoration: none; - border-bottom: dotted 1px #0088cc; -} - -.editable-click.editable-disabled, -a.editable-click.editable-disabled, -a.editable-click.editable-disabled:hover { - color: #585858; - cursor: default; - border-bottom: none; -} - -.editable-empty, .editable-empty:hover, .editable-empty:focus{ - font-style: italic; - color: #DD1144; - /* border-bottom: none; */ - text-decoration: none; -} - -.editable-unsaved { - font-weight: bold; -} - -.editable-unsaved:after { -/* content: '*'*/ -} - -.editable-bg-transition { - -webkit-transition: background-color 1400ms ease-out; - -moz-transition: background-color 1400ms ease-out; - -o-transition: background-color 1400ms ease-out; - -ms-transition: background-color 1400ms ease-out; - transition: background-color 1400ms ease-out; -} - -/*see https://github.com/vitalets/x-editable/issues/139 */ -.form-horizontal .editable -{ - padding-top: 5px; - display:inline-block; -} \ No newline at end of file diff --git a/templates/admin/default/assets/bootstrap-editable/js/bootstrap-editable.min.js b/templates/admin/default/assets/bootstrap-editable/js/bootstrap-editable.min.js deleted file mode 100755 index 13df29e24..000000000 --- a/templates/admin/default/assets/bootstrap-editable/js/bootstrap-editable.min.js +++ /dev/null @@ -1,5 +0,0 @@ -/*! X-editable - v1.4.6 -* In-place editing with Twitter Bootstrap, jQuery UI or pure jQuery -* http://github.com/vitalets/x-editable -* Copyright (c) 2013 Vitaliy Potapov; Licensed MIT */ -(function(e){"use strict";var t=function(t,n){this.options=e.extend({},e.fn.editableform.defaults,n),this.$div=e(t),this.options.scope||(this.options.scope=this)};t.prototype={constructor:t,initInput:function(){this.input=this.options.input,this.value=this.input.str2value(this.options.value)},initTemplate:function(){this.$form=e(e.fn.editableform.template)},initButtons:function(){var t=this.$form.find(".editable-buttons");t.append(e.fn.editableform.buttons),this.options.showbuttons==="bottom"&&t.addClass("editable-buttons-bottom")},render:function(){this.$loading=e(e.fn.editableform.loading),this.$div.empty().append(this.$loading),this.initTemplate(),this.options.showbuttons?this.initButtons():this.$form.find(".editable-buttons").remove(),this.showLoading(),this.isSaving=!1,this.$div.triggerHandler("rendering"),this.initInput(),this.input.prerender(),this.$form.find("div.editable-input").append(this.input.$tpl),this.$div.append(this.$form),e.when(this.input.render()).then(e.proxy(function(){this.options.showbuttons||this.input.autosubmit(),this.$form.find(".editable-cancel").click(e.proxy(this.cancel,this));if(this.input.error)this.error(this.input.error),this.$form.find(".editable-submit").attr("disabled",!0),this.input.$input.attr("disabled",!0),this.$form.submit(function(e){e.preventDefault()});else{this.error(!1),this.input.$input.removeAttr("disabled"),this.$form.find(".editable-submit").removeAttr("disabled");var t=this.value===null||this.value===undefined||this.value===""?this.options.defaultValue:this.value;this.input.value2input(t),this.$form.submit(e.proxy(this.submit,this))}this.$div.triggerHandler("rendered"),this.showForm(),this.input.postrender&&this.input.postrender()},this))},cancel:function(){this.$div.triggerHandler("cancel")},showLoading:function(){var e,t;this.$form?(e=this.$form.outerWidth(),t=this.$form.outerHeight(),e&&this.$loading.width(e),t&&this.$loading.height(t),this.$form.hide()):(e=this.$loading.parent().width(),e&&this.$loading.width(e)),this.$loading.show()},showForm:function(e){this.$loading.hide(),this.$form.show(),e!==!1&&this.input.activate(),this.$div.triggerHandler("show")},error:function(t){var n=this.$form.find(".control-group"),r=this.$form.find(".editable-error-block"),i;if(t===!1)n.removeClass(e.fn.editableform.errorGroupClass),r.removeClass(e.fn.editableform.errorBlockClass).empty().hide();else{if(t){i=t.split("\n");for(var s=0;s").text(i[s]).html();t=i.join("
")}n.addClass(e.fn.editableform.errorGroupClass),r.addClass(e.fn.editableform.errorBlockClass).html(t).show()}},submit:function(t){t.stopPropagation(),t.preventDefault();var n,r=this.input.input2value();if(n=this.validate(r)){this.error(n),this.showForm();return}if(!this.options.savenochange&&this.input.value2str(r)==this.input.value2str(this.value)){this.$div.triggerHandler("nochange");return}var i=this.input.value2submit(r);this.isSaving=!0,e.when(this.save(i)).done(e.proxy(function(e){this.isSaving=!1;var t=typeof this.options.success=="function"?this.options.success.call(this.options.scope,e,r):null;if(t===!1){this.error(!1),this.showForm(!1);return}if(typeof t=="string"){this.error(t),this.showForm();return}t&&typeof t=="object"&&t.hasOwnProperty("newValue")&&(r=t.newValue),this.error(!1),this.value=r,this.$div.triggerHandler("save",{newValue:r,submitValue:i,response:e})},this)).fail(e.proxy(function(e){this.isSaving=!1;var t;typeof this.options.error=="function"?t=this.options.error.call(this.options.scope,e,r):t=typeof e=="string"?e:e.responseText||e.statusText||"Unknown error!",this.error(t),this.showForm()},this))},save:function(t){this.options.pk=e.fn.editableutils.tryParseJson(this.options.pk,!0);var n=typeof this.options.pk=="function"?this.options.pk.call(this.options.scope):this.options.pk,r=!!(typeof this.options.url=="function"||this.options.url&&(this.options.send==="always"||this.options.send==="auto"&&n!==null&&n!==undefined)),i;if(r)return this.showLoading(),i={name:this.options.name||"",value:t,pk:n},typeof this.options.params=="function"?i=this.options.params.call(this.options.scope,i):(this.options.params=e.fn.editableutils.tryParseJson(this.options.params,!0),e.extend(i,this.options.params)),typeof this.options.url=="function"?this.options.url.call(this.options.scope,i):e.ajax(e.extend({url:this.options.url,data:i,type:"POST"},this.options.ajaxOptions))},validate:function(e){e===undefined&&(e=this.value);if(typeof this.options.validate=="function")return this.options.validate.call(this.options.scope,e)},option:function(e,t){e in this.options&&(this.options[e]=t),e==="value"&&this.setValue(t)},setValue:function(e,t){t?this.value=this.input.str2value(e):this.value=e,this.$form&&this.$form.is(":visible")&&this.input.value2input(this.value)}},e.fn.editableform=function(n){var r=arguments;return this.each(function(){var i=e(this),s=i.data("editableform"),o=typeof n=="object"&&n;s||i.data("editableform",s=new t(this,o)),typeof n=="string"&&s[n].apply(s,Array.prototype.slice.call(r,1))})},e.fn.editableform.Constructor=t,e.fn.editableform.defaults={type:"text",url:null,params:null,name:null,pk:null,value:null,defaultValue:null,send:"auto",validate:null,success:null,error:null,ajaxOptions:null,showbuttons:!0,scope:null,savenochange:!1},e.fn.editableform.template='
',e.fn.editableform.loading='
',e.fn.editableform.buttons='',e.fn.editableform.errorGroupClass=null,e.fn.editableform.errorBlockClass="editable-error"})(window.jQuery),function(e){"use strict";e.fn.editableutils={inherit:function(e,t){var n=function(){};n.prototype=t.prototype,e.prototype=new n,e.prototype.constructor=e,e.superclass=t.prototype},setCursorPosition:function(e,t){if(e.setSelectionRange)e.setSelectionRange(t,t);else if(e.createTextRange){var n=e.createTextRange();n.collapse(!0),n.moveEnd("character",t),n.moveStart("character",t),n.select()}},tryParseJson:function(e,t){if(typeof e=="string"&&e.length&&e.match(/^[\{\[].*[\}\]]$/))if(t)try{e=(new Function("return "+e))()}catch(n){}finally{return e}else e=(new Function("return "+e))();return e},sliceObj:function(t,n,r){var i,s,o={};if(!e.isArray(n)||!n.length)return o;for(var u=0;u").text(t).html()},itemsByValue:function(t,n,r){if(!n||t===null)return[];if(typeof r!="function"){var i=r||"value";r=function(e){return e[i]}}var s=e.isArray(t),o=[],u=this;return e.each(n,function(n,i){if(i.children)o=o.concat(u.itemsByValue(t,i.children,r));else if(s)e.grep(t,function(e){return e==(i&&typeof i==="object"?r(i):i)}).length&&o.push(i);else{var a=i&&typeof i=="object"?r(i):i;t==a&&o.push(i)}}),o},createInput:function(t){var n,r,i,s=t.type;return s==="date"&&(t.mode==="inline"?e.fn.editabletypes.datefield?s="datefield":e.fn.editabletypes.dateuifield&&(s="dateuifield"):e.fn.editabletypes.date?s="date":e.fn.editabletypes.dateui&&(s="dateui"),s==="date"&&!e.fn.editabletypes.date&&(s="combodate")),s==="datetime"&&t.mode==="inline"&&(s="datetimefield"),s==="wysihtml5"&&!e.fn.editabletypes[s]&&(s="textarea"),typeof e.fn.editabletypes[s]=="function"?(n=e.fn.editabletypes[s],r=this.sliceObj(t,this.objectKeys(n.defaults)),i=new n(r),i):(e.error("Unknown type: "+s),!1)},supportsTransitions:function(){var e=document.body||document.documentElement,t=e.style,n="transition",r=["Moz","Webkit","Khtml","O","ms"];if(typeof t[n]=="string")return!0;n=n.charAt(0).toUpperCase()+n.substr(1);for(var i=0;i"),this.tip().is(this.innerCss)?this.tip().append(this.$form):this.tip().find(this.innerCss).append(this.$form),this.renderForm()},hide:function(e){if(!this.tip()||!this.tip().is(":visible")||!this.$element.hasClass("editable-open"))return;if(this.$form.data("editableform").isSaving){this.delayedHide={reason:e};return}this.delayedHide=!1,this.$element.removeClass("editable-open"),this.innerHide(),this.$element.triggerHandler("hidden",e||"manual")},innerShow:function(){},innerHide:function(){},toggle:function(e){this.container()&&this.tip()&&this.tip().is(":visible")?this.hide():this.show(e)},setPosition:function(){},save:function(e,t){this.$element.triggerHandler("save",t),this.hide("save")},option:function(e,t){this.options[e]=t,e in this.containerOptions?(this.containerOptions[e]=t,this.setContainerOption(e,t)):(this.formOptions[e]=t,this.$form&&this.$form.editableform("option",e,t))},setContainerOption:function(e,t){this.call("option",e,t)},destroy:function(){this.hide(),this.innerDestroy(),this.$element.off("destroyed"),this.$element.removeData("editableContainer")},innerDestroy:function(){},closeOthers:function(t){e(".editable-open").each(function(n,r){if(r===t||e(r).find(t).length)return;var i=e(r),s=i.data("editableContainer");if(!s)return;s.options.onblur==="cancel"?i.data("editableContainer").hide("onblur"):s.options.onblur==="submit"&&i.data("editableContainer").tip().find("form").submit()})},activate:function(){this.tip&&this.tip().is(":visible")&&this.$form&&this.$form.data("editableform").input.activate()}},e.fn.editableContainer=function(r){var i=arguments;return this.each(function(){var s=e(this),o="editableContainer",u=s.data(o),a=typeof r=="object"&&r,f=a.mode==="inline"?n:t;u||s.data(o,u=new f(this,a)),typeof r=="string"&&u[r].apply(u,Array.prototype.slice.call(i,1))})},e.fn.editableContainer.Popup=t,e.fn.editableContainer.Inline=n,e.fn.editableContainer.defaults={value:null,placement:"top",autohide:!0,onblur:"cancel",anim:!1,mode:"popup"},jQuery.event.special.destroyed={remove:function(e){e.handler&&e.handler()}}}(window.jQuery),function(e){"use strict";e.extend(e.fn.editableContainer.Inline.prototype,e.fn.editableContainer.Popup.prototype,{containerName:"editableform",innerCss:".editable-inline",containerClass:"editable-container editable-inline",initContainer:function(){this.$tip=e(""),this.options.anim||(this.options.anim=0)},splitOptions:function(){this.containerOptions={},this.formOptions=this.options},tip:function(){return this.$tip},innerShow:function(){this.$element.hide(),this.tip().insertAfter(this.$element).show()},innerHide:function(){this.$tip.hide(this.options.anim,e.proxy(function(){this.$element.show(),this.innerDestroy()},this))},innerDestroy:function(){this.tip()&&this.tip().empty().remove()}})}(window.jQuery),function(e){"use strict";var t=function(t,n){this.$element=e(t),this.options=e.extend({},e.fn.editable.defaults,n,e.fn.editableutils.getConfigData(this.$element)),this.options.selector?this.initLive():this.init(),this.options.highlight&&!e.fn.editableutils.supportsTransitions()&&(this.options.highlight=!1)};t.prototype={constructor:t,init:function(){var t=!1,n,r;this.options.name=this.options.name||this.$element.attr("id"),this.options.scope=this.$element[0],this.input=e.fn.editableutils.createInput(this.options);if(!this.input)return;this.options.value===undefined||this.options.value===null?(this.value=this.input.html2value(e.trim(this.$element.html())),t=!0):(this.options.value=e.fn.editableutils.tryParseJson(this.options.value,!0),typeof this.options.value=="string"?this.value=this.input.str2value(this.options.value):this.value=this.options.value),this.$element.addClass("editable"),this.input.type==="textarea"&&this.$element.addClass("editable-pre-wrapped"),this.options.toggle!=="manual"?(this.$element.addClass("editable-click"),this.$element.on(this.options.toggle+".editable",e.proxy(function(e){this.options.disabled||e.preventDefault();if(this.options.toggle==="mouseenter")this.show();else{var t=this.options.toggle!=="click";this.toggle(t)}},this))):this.$element.attr("tabindex",-1),typeof this.options.display=="function"&&(this.options.autotext="always");switch(this.options.autotext){case"always":n=!0;break;case"auto":n=!e.trim(this.$element.text()).length&&this.value!==null&&this.value!==undefined&&!t;break;default:n=!1}e.when(n?this.render():!0).then(e.proxy(function(){this.options.disabled?this.disable():this.enable(),this.$element.triggerHandler("init",this)},this))},initLive:function(){var t=this.options.selector;this.options.selector=!1,this.options.autotext="never",this.$element.on(this.options.toggle+".editable",t,e.proxy(function(t){var n=e(t.target);n.data("editable")||(n.hasClass(this.options.emptyclass)&&n.empty(),n.editable(this.options).trigger(t))},this))},render:function(e){if(this.options.display===!1)return;return this.input.value2htmlFinal?this.input.value2html(this.value,this.$element[0],this.options.display,e):typeof this.options.display=="function"?this.options.display.call(this.$element[0],this.value,e):this.input.value2html(this.value,this.$element[0])},enable:function(){this.options.disabled=!1,this.$element.removeClass("editable-disabled"),this.handleEmpty(this.isEmpty),this.options.toggle!=="manual"&&this.$element.attr("tabindex")==="-1"&&this.$element.removeAttr("tabindex")},disable:function(){this.options.disabled=!0,this.hide(),this.$element.addClass("editable-disabled"),this.handleEmpty(this.isEmpty),this.$element.attr("tabindex",-1)},toggleDisabled:function(){this.options.disabled?this.enable():this.disable()},option:function(t,n){if(t&&typeof t=="object"){e.each(t,e.proxy(function(t,n){this.option(e.trim(t),n)},this));return}this.options[t]=n;if(t==="disabled")return n?this.disable():this.enable();t==="value"&&this.setValue(n),this.container&&this.container.option(t,n),this.input.option&&this.input.option(t,n)},handleEmpty:function(t){if(this.options.display===!1)return;t!==undefined?this.isEmpty=t:e.trim(this.$element.html())===""?this.isEmpty=!0:e.trim(this.$element.text())!==""?this.isEmpty=!1:this.isEmpty=!this.$element.height()||!this.$element.width(),this.options.disabled?this.isEmpty&&(this.$element.empty(),this.options.emptyclass&&this.$element.removeClass(this.options.emptyclass)):this.isEmpty?(this.$element.html(this.options.emptytext),this.options.emptyclass&&this.$element.addClass(this.options.emptyclass)):this.options.emptyclass&&this.$element.removeClass(this.options.emptyclass)},show:function(t){if(this.options.disabled)return;if(!this.container){var n=e.extend({},this.options,{value:this.value,input:this.input});this.$element.editableContainer(n),this.$element.on("save.internal",e.proxy(this.save,this)),this.container=this.$element.data("editableContainer")}else if(this.container.tip().is(":visible"))return;this.container.show(t)},hide:function(){this.container&&this.container.hide()},toggle:function(e){this.container&&this.container.tip().is(":visible")?this.hide():this.show(e)},save:function(e,t){if(this.options.unsavedclass){var n=!1;n=n||typeof this.options.url=="function",n=n||this.options.display===!1,n=n||t.response!==undefined,n=n||this.options.savenochange&&this.input.value2str(this.value)!==this.input.value2str(t.newValue),n?this.$element.removeClass(this.options.unsavedclass):this.$element.addClass(this.options.unsavedclass)}if(this.options.highlight){var r=this.$element,i=r.css("background-color");r.css("background-color",this.options.highlight),setTimeout(function(){i==="transparent"&&(i=""),r.css("background-color",i),r.addClass("editable-bg-transition"),setTimeout(function(){r.removeClass("editable-bg-transition")},1700)},10)}this.setValue(t.newValue,!1,t.response)},validate:function(){if(typeof this.options.validate=="function")return this.options.validate.call(this,this.value)},setValue:function(t,n,r){n?this.value=this.input.str2value(t):this.value=t,this.container&&this.container.option("value",this.value),e.when(this.render(r)).then(e.proxy(function(){this.handleEmpty()},this))},activate:function(){this.container&&this.container.activate()},destroy:function(){this.disable(),this.container&&this.container.destroy(),this.input.destroy(),this.options.toggle!=="manual"&&(this.$element.removeClass("editable-click"),this.$element.off(this.options.toggle+".editable")),this.$element.off("save.internal"),this.$element.removeClass("editable editable-open editable-disabled"),this.$element.removeData("editable")}},e.fn.editable=function(n){var r={},i=arguments,s="editable";switch(n){case"validate":return this.each(function(){var t=e(this),n=t.data(s),i;n&&(i=n.validate())&&(r[n.options.name]=i)}),r;case"getValue":return arguments.length===2&&arguments[1]===!0?r=this.eq(0).data(s).value:this.each(function(){var t=e(this),n=t.data(s);n&&n.value!==undefined&&n.value!==null&&(r[n.options.name]=n.input.value2submit(n.value))}),r;case"submit":var o=arguments[1]||{},u=this,a=this.editable("validate"),f;return e.isEmptyObject(a)?(f=this.editable("getValue"),o.data&&e.extend(f,o.data),e.ajax(e.extend({url:o.url,data:f,type:"POST"},o.ajaxOptions)).success(function(e){typeof o.success=="function"&&o.success.call(u,e,o)}).error(function(){typeof o.error=="function"&&o.error.apply(u,arguments)})):typeof o.error=="function"&&o.error.call(u,a),this}return this.each(function(){var r=e(this),o=r.data(s),u=typeof n=="object"&&n;if(u&&u.selector){o=new t(this,u);return}o||r.data(s,o=new t(this,u)),typeof n=="string"&&o[n].apply(o,Array.prototype.slice.call(i,1))})},e.fn.editable.defaults={type:"text",disabled:!1,toggle:"click",emptytext:"Empty",autotext:"auto",value:null,display:null,emptyclass:"editable-empty",unsavedclass:"editable-unsaved",selector:null,highlight:"#FFFF80"}}(window.jQuery),function(e){"use strict";e.fn.editabletypes={};var t=function(){};t.prototype={init:function(t,n,r){this.type=t,this.options=e.extend({},r,n)},prerender:function(){this.$tpl=e(this.options.tpl),this.$input=this.$tpl,this.$clear=null,this.error=null},render:function(){},value2html:function(t,n){e(n).text(e.trim(t))},html2value:function(t){return e("
").html(t).text()},value2str:function(e){return e},str2value:function(e){return e},value2submit:function(e){return e},value2input:function(e){this.$input.val(e)},input2value:function(){return this.$input.val()},activate:function(){this.$input.is(":visible")&&this.$input.focus()},clear:function(){this.$input.val(null)},escape:function(t){return e("
").text(t).html()},autosubmit:function(){},destroy:function(){},setClass:function(){this.options.inputclass&&this.$input.addClass(this.options.inputclass)},setAttr:function(e){this.options[e]!==undefined&&this.options[e]!==null&&this.$input.attr(e,this.options[e])},option:function(e,t){this.options[e]=t}},t.defaults={tpl:"",inputclass:"input-medium",scope:null,showbuttons:!0},e.extend(e.fn.editabletypes,{abstractinput:t})}(window.jQuery),function(e){"use strict";var t=function(e){};e.fn.editableutils.inherit(t,e.fn.editabletypes.abstractinput),e.extend(t.prototype,{render:function(){var t=e.Deferred();return this.error=null,this.onSourceReady(function(){this.renderList(),t.resolve()},function(){this.error=this.options.sourceError,t.resolve()}),t.promise()},html2value:function(e){return null},value2html:function(t,n,r,i){var s=e.Deferred(),o=function(){typeof r=="function"?r.call(n,t,this.sourceData,i):this.value2htmlFinal(t,n),s.resolve()};return t===null?o.call(this):this.onSourceReady(o,function(){s.resolve()}),s.promise()},onSourceReady:function(t,n){var r;e.isFunction(this.options.source)?(r=this.options.source.call(this.options.scope),this.sourceData=null):r=this.options.source;if(this.options.sourceCache&&e.isArray(this.sourceData)){t.call(this);return}try{r=e.fn.editableutils.tryParseJson(r,!1)}catch(i){n.call(this);return}if(typeof r=="string"){if(this.options.sourceCache){var s=r,o;e(document).data(s)||e(document).data(s,{}),o=e(document).data(s);if(o.loading===!1&&o.sourceData){this.sourceData=o.sourceData,this.doPrepend(),t.call(this);return}if(o.loading===!0){o.callbacks.push(e.proxy(function(){this.sourceData=o.sourceData,this.doPrepend(),t.call(this)},this)),o.err_callbacks.push(e.proxy(n,this));return}o.loading=!0,o.callbacks=[],o.err_callbacks=[]}e.ajax({url:r,type:"get",cache:!1,dataType:"json",success:e.proxy(function(r){o&&(o.loading=!1),this.sourceData=this.makeArray(r),e.isArray(this.sourceData)?(o&&(o.sourceData=this.sourceData,e.each(o.callbacks,function(){this.call()})),this.doPrepend(),t.call(this)):(n.call(this),o&&e.each(o.err_callbacks,function(){this.call()}))},this),error:e.proxy(function(){n.call(this),o&&(o.loading=!1,e.each(o.err_callbacks,function(){this.call()}))},this)})}else this.sourceData=this.makeArray(r),e.isArray(this.sourceData)?(this.doPrepend(),t.call(this)):n.call(this)},doPrepend:function(){if(this.options.prepend===null||this.options.prepend===undefined)return;e.isArray(this.prependData)||(e.isFunction(this.options.prepend)&&(this.options.prepend=this.options.prepend.call(this.options.scope)),this.options.prepend=e.fn.editableutils.tryParseJson(this.options.prepend,!0),typeof this.options.prepend=="string"&&(this.options.prepend={"":this.options.prepend}),this.prependData=this.makeArray(this.options.prepend)),e.isArray(this.prependData)&&e.isArray(this.sourceData)&&(this.sourceData=this.prependData.concat(this.sourceData))},renderList:function(){},value2htmlFinal:function(e,t){},makeArray:function(t){var n,r,i=[],s,o;if(!t||typeof t=="string")return null;if(e.isArray(t)){o=function(e,t){r={value:e,text:t};if(n++>=2)return!1};for(var u=0;u1&&(s.children&&(s.children=this.makeArray(s.children)),i.push(s))):i.push({value:s,text:s})}else e.each(t,function(e,t){i.push({value:e,text:t})});return i},option:function(e,t){this.options[e]=t,e==="source"&&(this.sourceData=null),e==="prepend"&&(this.prependData=null)}}),t.defaults=e.extend({},e.fn.editabletypes.abstractinput.defaults,{source:null,prepend:!1,sourceError:"Error when loading list",sourceCache:!0}),e.fn.editabletypes.list=t}(window.jQuery),function(e){"use strict";var t=function(e){this.init("text",e,t.defaults)};e.fn.editableutils.inherit(t,e.fn.editabletypes.abstractinput),e.extend(t.prototype,{render:function(){this.renderClear(),this.setClass(),this.setAttr("placeholder")},activate:function(){this.$input.is(":visible")&&(this.$input.focus(),e.fn.editableutils.setCursorPosition(this.$input.get(0),this.$input.val().length),this.toggleClear&&this.toggleClear())},renderClear:function(){this.options.clear&&(this.$clear=e(''),this.$input.after(this.$clear).css("padding-right",24).keyup(e.proxy(function(t){if(~e.inArray(t.keyCode,[40,38,9,13,27]))return;clearTimeout(this.t);var n=this;this.t=setTimeout(function(){n.toggleClear(t)},100)},this)).parent().css("position","relative"),this.$clear.click(e.proxy(this.clear,this)))},postrender:function(){},toggleClear:function(e){if(!this.$clear)return;var t=this.$input.val().length,n=this.$clear.is(":visible");t&&!n&&this.$clear.show(),!t&&n&&this.$clear.hide()},clear:function(){this.$clear.hide(),this.$input.val("").focus()}}),t.defaults=e.extend({},e.fn.editabletypes.abstractinput.defaults,{tpl:'',placeholder:null,clear:!0}),e.fn.editabletypes.text=t}(window.jQuery),function(e){"use strict";var t=function(e){this.init("textarea",e,t.defaults)};e.fn.editableutils.inherit(t,e.fn.editabletypes.abstractinput),e.extend(t.prototype,{render:function(){this.setClass(),this.setAttr("placeholder"),this.setAttr("rows"),this.$input.keydown(function(t){t.ctrlKey&&t.which===13&&e(this).closest("form").submit()})},activate:function(){e.fn.editabletypes.text.prototype.activate.call(this)}}),t.defaults=e.extend({},e.fn.editabletypes.abstractinput.defaults,{tpl:"",inputclass:"input-large",placeholder:null,rows:7}),e.fn.editabletypes.textarea=t}(window.jQuery),function(e){"use strict";var t=function(e){this.init("select",e,t.defaults)};e.fn.editableutils.inherit(t,e.fn.editabletypes.list),e.extend(t.prototype,{renderList:function(){this.$input.empty();var t=function(n,r){var i;if(e.isArray(r))for(var s=0;s",i),r[s].children))):(i.value=r[s].value,r[s].disabled&&(i.disabled=!0),n.append(e("
";if(this.o.calendarWeeks){var n='';t+=n,this.picker.find(".datepicker-days thead tr:first-child").prepend(n)}while(e'+c[this.o.language].daysMin[e++%7]+"";t+="",this.picker.find(".datepicker-days thead").append(t)},fillMonths:function(){var e="",t=0;while(t<12)e+=''+c[this.o.language].monthsShort[t++]+"";this.picker.find(".datepicker-months td").html(e)},setRange:function(t){!t||!t.length?delete this.range:this.range=e.map(t,function(e){return e.valueOf()}),this.fill()},getClassNames:function(t){var n=[],r=this.viewDate.getUTCFullYear(),i=this.viewDate.getUTCMonth(),s=this.date.valueOf(),o=new Date;return t.getUTCFullYear()r||t.getUTCFullYear()==r&&t.getUTCMonth()>i)&&n.push("new"),this.o.todayHighlight&&t.getUTCFullYear()==o.getFullYear()&&t.getUTCMonth()==o.getMonth()&&t.getUTCDate()==o.getDate()&&n.push("today"),s&&t.valueOf()==s&&n.push("active"),(t.valueOf()this.o.endDate||e.inArray(t.getUTCDay(),this.o.daysOfWeekDisabled)!==-1)&&n.push("disabled"),this.range&&(t>this.range[0]&&t");if(this.o.calendarWeeks){var y=new Date(+p+(this.o.weekStart-p.getUTCDay()-7)%7*864e5),b=new Date(+y+(11-y.getUTCDay())%7*864e5),w=new Date(+(w=t(b.getUTCFullYear(),0,1))+(11-w.getUTCDay())%7*864e5),E=(b-w)/864e5/7+1;m.push('")}}g=this.getClassNames(p),g.push("day");var S=this.o.beforeShowDay(p);S===undefined?S={}:typeof S=="boolean"?S={enabled:S}:typeof S=="string"&&(S={classes:S}),S.enabled===!1&&g.push("disabled"),S.classes&&(g=g.concat(S.classes.split(/\s+/))),S.tooltip&&(l=S.tooltip),g=e.unique(g),m.push('"),p.getUTCDay()==this.o.weekEnd&&m.push(""),p.setUTCDate(p.getUTCDate()+1)}this.picker.find(".datepicker-days tbody").empty().append(m.join(""));var x=this.date&&this.date.getUTCFullYear(),T=this.picker.find(".datepicker-months").find("th:eq(1)").text(r).end().find("span").removeClass("active");x&&x==r&&T.eq(this.date.getUTCMonth()).addClass("active"),(ru)&&T.addClass("disabled"),r==s&&T.slice(0,o).addClass("disabled"),r==u&&T.slice(a+1).addClass("disabled"),m="",r=parseInt(r/10,10)*10;var N=this.picker.find(".datepicker-years").find("th:eq(1)").text(r+"-"+(r+9)).end().find("td");r-=1;for(var C=-1;C<11;C++)m+='u?" disabled":"")+'">'+r+"",r+=1;N.html(m)},updateNavArrows:function(){if(!this._allow_update)return;var e=new Date(this.viewDate),t=e.getUTCFullYear(),n=e.getUTCMonth();switch(this.viewMode){case 0:this.o.startDate!==-Infinity&&t<=this.o.startDate.getUTCFullYear()&&n<=this.o.startDate.getUTCMonth()?this.picker.find(".prev").css({visibility:"hidden"}):this.picker.find(".prev").css({visibility:"visible"}),this.o.endDate!==Infinity&&t>=this.o.endDate.getUTCFullYear()&&n>=this.o.endDate.getUTCMonth()?this.picker.find(".next").css({visibility:"hidden"}):this.picker.find(".next").css({visibility:"visible"});break;case 1:case 2:this.o.startDate!==-Infinity&&t<=this.o.startDate.getUTCFullYear()?this.picker.find(".prev").css({visibility:"hidden"}):this.picker.find(".prev").css({visibility:"visible"}),this.o.endDate!==Infinity&&t>=this.o.endDate.getUTCFullYear()?this.picker.find(".next").css({visibility:"hidden"}):this.picker.find(".next").css({visibility:"visible"})}},click:function(n){n.preventDefault();var r=e(n.target).closest("span, td, th");if(r.length==1)switch(r[0].nodeName.toLowerCase()){case"th":switch(r[0].className){case"datepicker-switch":this.showMode(1);break;case"prev":case"next":var i=h.modes[this.viewMode].navStep*(r[0].className=="prev"?-1:1);switch(this.viewMode){case 0:this.viewDate=this.moveMonth(this.viewDate,i);break;case 1:case 2:this.viewDate=this.moveYear(this.viewDate,i)}this.fill();break;case"today":var s=new Date;s=t(s.getFullYear(),s.getMonth(),s.getDate(),0,0,0),this.showMode(-2);var o=this.o.todayBtn=="linked"?null:"view";this._setDate(s,o);break;case"clear":var u;this.isInput?u=this.element:this.component&&(u=this.element.find("input")),u&&u.val("").change(),this._trigger("changeDate"),this.update(),this.o.autoclose&&this.hide()}break;case"span":if(!r.is(".disabled")){this.viewDate.setUTCDate(1);if(r.is(".month")){var a=1,f=r.parent().find("span").index(r),l=this.viewDate.getUTCFullYear();this.viewDate.setUTCMonth(f),this._trigger("changeMonth",this.viewDate),this.o.minViewMode===1&&this._setDate(t(l,f,a,0,0,0,0))}else{var l=parseInt(r.text(),10)||0,a=1,f=0;this.viewDate.setUTCFullYear(l),this._trigger("changeYear",this.viewDate),this.o.minViewMode===2&&this._setDate(t(l,f,a,0,0,0,0))}this.showMode(-1),this.fill()}break;case"td":if(r.is(".day")&&!r.is(".disabled")){var a=parseInt(r.text(),10)||1,l=this.viewDate.getUTCFullYear(),f=this.viewDate.getUTCMonth();r.is(".old")?f===0?(f=11,l-=1):f-=1:r.is(".new")&&(f==11?(f=0,l+=1):f+=1),this._setDate(t(l,f,a,0,0,0,0))}}},_setDate:function(e,t){if(!t||t=="date")this.date=new Date(e);if(!t||t=="view")this.viewDate=new Date(e);this.fill(),this.setValue(),this._trigger("changeDate");var n;this.isInput?n=this.element:this.component&&(n=this.element.find("input")),n&&(n.change(),this.o.autoclose&&(!t||t=="date")&&this.hide())},moveMonth:function(e,t){if(!t)return e;var n=new Date(e.valueOf()),r=n.getUTCDate(),i=n.getUTCMonth(),s=Math.abs(t),o,u;t=t>0?1:-1;if(s==1){u=t==-1?function(){return n.getUTCMonth()==i}:function(){return n.getUTCMonth()!=o},o=i+t,n.setUTCMonth(o);if(o<0||o>11)o=(o+12)%12}else{for(var a=0;a=this.o.startDate&&e<=this.o.endDate},keydown:function(e){if(this.picker.is(":not(:visible)")){e.keyCode==27&&this.show();return}var t=!1,n,r,i,s,o;switch(e.keyCode){case 27:this.hide(),e.preventDefault();break;case 37:case 39:if(!this.o.keyboardNavigation)break;n=e.keyCode==37?-1:1,e.ctrlKey?(s=this.moveYear(this.date,n),o=this.moveYear(this.viewDate,n)):e.shiftKey?(s=this.moveMonth(this.date,n),o=this.moveMonth(this.viewDate,n)):(s=new Date(this.date),s.setUTCDate(this.date.getUTCDate()+n),o=new Date(this.viewDate),o.setUTCDate(this.viewDate.getUTCDate()+n)),this.dateWithinRange(s)&&(this.date=s,this.viewDate=o,this.setValue(),this.update(),e.preventDefault(),t=!0);break;case 38:case 40:if(!this.o.keyboardNavigation)break;n=e.keyCode==38?-1:1,e.ctrlKey?(s=this.moveYear(this.date,n),o=this.moveYear(this.viewDate,n)):e.shiftKey?(s=this.moveMonth(this.date,n),o=this.moveMonth(this.viewDate,n)):(s=new Date(this.date),s.setUTCDate(this.date.getUTCDate()+n*7),o=new Date(this.viewDate),o.setUTCDate(this.viewDate.getUTCDate()+n*7)),this.dateWithinRange(s)&&(this.date=s,this.viewDate=o,this.setValue(),this.update(),e.preventDefault(),t=!0);break;case 13:this.hide(),e.preventDefault();break;case 9:this.hide()}if(t){this._trigger("changeDate");var u;this.isInput?u=this.element:this.component&&(u=this.element.find("input")),u&&u.change()}},showMode:function(e){e&&(this.viewMode=Math.max(this.o.minViewMode,Math.min(2,this.viewMode+e))),this.picker.find(">div").hide().filter(".datepicker-"+h.modes[this.viewMode].clsName).css("display","block"),this.updateNavArrows()}};var i=function(t,n){this.element=e(t),this.inputs=e.map(n.inputs,function(e){return e.jquery?e[0]:e}),delete n.inputs,e(this.inputs).datepicker(n).bind("changeDate",e.proxy(this.dateUpdated,this)),this.pickers=e.map(this.inputs,function(t){return e(t).data("datepicker")}),this.updateDates()};i.prototype={updateDates:function(){this.dates=e.map(this.pickers,function(e){return e.date}),this.updateRanges()},updateRanges:function(){var t=e.map(this.dates,function(e){return e.valueOf()});e.each(this.pickers,function(e,n){n.setRange(t)})},dateUpdated:function(t){var n=e(t.target).data("datepicker"),r=n.getUTCDate(),i=e.inArray(t.target,this.inputs),s=this.inputs.length;if(i==-1)return;if(r=0&&rthis.dates[i])while(ithis.dates[i])this.pickers[i++].setUTCDate(r);this.updateDates()},remove:function(){e.map(this.pickers,function(e){e.remove()}),delete this.element.data().datepicker}};var u=e.fn.datepicker,a=e.fn.datepicker=function(t){var n=Array.apply(null,arguments);n.shift();var u,a;return this.each(function(){var a=e(this),l=a.data("datepicker"),c=typeof t=="object"&&t;if(!l){var h=s(this,"date"),p=e.extend({},f,h,c),d=o(p.language),v=e.extend({},f,d,h,c);if(a.is(".input-daterange")||v.inputs){var m={inputs:v.inputs||a.find("input").toArray()};a.data("datepicker",l=new i(this,e.extend(v,m)))}else a.data("datepicker",l=new r(this,v))}if(typeof t=="string"&&typeof l[t]=="function"){u=l[t].apply(l,n);if(u!==undefined)return!1}}),u!==undefined?u:this},f=e.fn.datepicker.defaults={autoclose:!1,beforeShowDay:e.noop,calendarWeeks:!1,clearBtn:!1,daysOfWeekDisabled:[],endDate:Infinity,forceParse:!0,format:"mm/dd/yyyy",keyboardNavigation:!0,language:"en",minViewMode:0,rtl:!1,startDate:-Infinity,startView:0,todayBtn:!1,todayHighlight:!1,weekStart:0},l=e.fn.datepicker.locale_opts=["format","rtl","weekStart"];e.fn.datepicker.Constructor=r;var c=e.fn.datepicker.dates={en:{days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday"],daysShort:["Sun","Mon","Tue","Wed","Thu","Fri","Sat","Sun"],daysMin:["Su","Mo","Tu","We","Th","Fr","Sa","Su"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],monthsShort:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],today:"Today",clear:"Clear"}},h={modes:[{clsName:"days",navFnc:"Month",navStep:1},{clsName:"months",navFnc:"FullYear",navStep:1},{clsName:"years",navFnc:"FullYear",navStep:10}],isLeapYear:function(e){return e%4===0&&e%100!==0||e%400===0},getDaysInMonth:function(e,t){return[31,h.isLeapYear(e)?29:28,31,30,31,30,31,31,30,31,30,31][t]},validParts:/dd?|DD?|mm?|MM?|yy(?:yy)?/g,nonpunctuation:/[^ -\/:-@\[\u3400-\u9fff-`{-~\t\n\r]+/g,parseFormat:function(e){var t=e.replace(this.validParts,"\0").split("\0"),n=e.match(this.validParts);if(!t||!t.length||!n||n.length===0)throw new Error("Invalid date format.");return{separators:t,parts:n}},parseDate:function(n,i,s){if(n instanceof Date)return n;typeof i=="string"&&(i=h.parseFormat(i));if(/^[\-+]\d+[dmwy]([\s,]+[\-+]\d+[dmwy])*$/.test(n)){var o=/([\-+]\d+)([dmwy])/,u=n.match(/([\-+]\d+)([dmwy])/g),a,f;n=new Date;for(var l=0;l',contTemplate:'',footTemplate:''};h.template='
 
'+E+""+p.getUTCDate()+"
'+h.headTemplate+""+h.footTemplate+"
"+"
"+'
'+''+h.headTemplate+h.contTemplate+h.footTemplate+"
"+"
"+'
'+''+h.headTemplate+h.contTemplate+h.footTemplate+"
"+"
"+"
",e.fn.datepicker.DPGlobal=h,e.fn.datepicker.noConflict=function(){return e.fn.datepicker=u,this},e(document).on("focus.datepicker.data-api click.datepicker.data-api",'[data-provide="datepicker"]',function(t){var n=e(this);if(n.data("datepicker"))return;t.preventDefault(),a.call(n,"show")}),e(function(){a.call(e('[data-provide="datepicker-inline"]'))})}(window.jQuery),function(e){"use strict";e.fn.bdatepicker=e.fn.datepicker.noConflict(),e.fn.datepicker||(e.fn.datepicker=e.fn.bdatepicker);var t=function(e){this.init("date",e,t.defaults),this.initPicker(e,t.defaults)};e.fn.editableutils.inherit(t,e.fn.editabletypes.abstractinput),e.extend(t.prototype,{initPicker:function(t,n){this.options.viewformat||(this.options.viewformat=this.options.format),t.datepicker=e.fn.editableutils.tryParseJson(t.datepicker,!0),this.options.datepicker=e.extend({},n.datepicker,t.datepicker,{format:this.options.viewformat}),this.options.datepicker.language=this.options.datepicker.language||"en",this.dpg=e.fn.bdatepicker.DPGlobal,this.parsedFormat=this.dpg.parseFormat(this.options.format),this.parsedViewFormat=this.dpg.parseFormat(this.options.viewformat)},render:function(){this.$input.bdatepicker(this.options.datepicker),this.options.clear&&(this.$clear=e('').html(this.options.clear).click(e.proxy(function(e){e.preventDefault(),e.stopPropagation(),this.clear()},this)),this.$tpl.parent().append(e('
').append(this.$clear)))},value2html:function(e,n){var r=e?this.dpg.formatDate(e,this.parsedViewFormat,this.options.datepicker.language):"";t.superclass.value2html(r,n)},html2value:function(e){return this.parseDate(e,this.parsedViewFormat)},value2str:function(e){return e?this.dpg.formatDate(e,this.parsedFormat,this.options.datepicker.language):""},str2value:function(e){return this.parseDate(e,this.parsedFormat)},value2submit:function(e){return this.value2str(e)},value2input:function(e){this.$input.bdatepicker("update",e)},input2value:function(){return this.$input.data("datepicker").date},activate:function(){},clear:function(){this.$input.data("datepicker").date=null,this.$input.find(".active").removeClass("active"),this.options.showbuttons||this.$input.closest("form").submit()},autosubmit:function(){this.$input.on("mouseup",".day",function(t){if(e(t.currentTarget).is(".old")||e(t.currentTarget).is(".new"))return;var n=e(this).closest("form");setTimeout(function(){n.submit()},200)})},parseDate:function(e,t){var n=null,r;return e&&(n=this.dpg.parseDate(e,t,this.options.datepicker.language),typeof e=="string"&&(r=this.dpg.formatDate(n,t,this.options.datepicker.language),e!==r&&(n=null))),n}}),t.defaults=e.extend({},e.fn.editabletypes.abstractinput.defaults,{tpl:'
',inputclass:null,format:"yyyy-mm-dd",viewformat:null,datepicker:{weekStart:0,startView:0,minViewMode:0,autoclose:!1},clear:"× clear"}),e.fn.editabletypes.date=t}(window.jQuery),function(e){"use strict";var t=function(e){this.init("datefield",e,t.defaults),this.initPicker(e,t.defaults)};e.fn.editableutils.inherit(t,e.fn.editabletypes.date),e.extend(t.prototype,{render:function(){this.$input=this.$tpl.find("input"),this.setClass(),this.setAttr("placeholder"),this.$tpl.bdatepicker(this.options.datepicker),this.$input.off("focus keydown"),this.$input.keyup(e.proxy(function(){this.$tpl.removeData("date"),this.$tpl.bdatepicker("update")},this))},value2input:function(e){this.$input.val(e?this.dpg.formatDate(e,this.parsedViewFormat,this.options.datepicker.language):""),this.$tpl.bdatepicker("update")},input2value:function(){return this.html2value(this.$input.val())},activate:function(){e.fn.editabletypes.text.prototype.activate.call(this)},autosubmit:function(){}}),t.defaults=e.extend({},e.fn.editabletypes.date.defaults,{tpl:'
',inputclass:"input-small",datepicker:{weekStart:0,startView:0,minViewMode:0,autoclose:!0}}),e.fn.editabletypes.datefield=t}(window.jQuery),function(e){"use strict";var t=function(e){this.init("datetime",e,t.defaults),this.initPicker(e,t.defaults)};e.fn.editableutils.inherit(t,e.fn.editabletypes.abstractinput),e.extend(t.prototype,{initPicker:function(t,n){this.options.viewformat||(this.options.viewformat=this.options.format),t.datetimepicker=e.fn.editableutils.tryParseJson(t.datetimepicker,!0),this.options.datetimepicker=e.extend({},n.datetimepicker,t.datetimepicker,{format:this.options.viewformat}),this.options.datetimepicker.language=this.options.datetimepicker.language||"en",this.dpg=e.fn.datetimepicker.DPGlobal,this.parsedFormat=this.dpg.parseFormat(this.options.format,this.options.formatType),this.parsedViewFormat=this.dpg.parseFormat(this.options.viewformat,this.options.formatType)},render:function(){this.$input.datetimepicker(this.options.datetimepicker),this.$input.on("changeMode",function(t){var n=e(this).closest("form").parent();setTimeout(function(){n.triggerHandler("resize")},0)}),this.options.clear&&(this.$clear=e('').html(this.options.clear).click(e.proxy(function(e){e.preventDefault(),e.stopPropagation(),this.clear()},this)),this.$tpl.parent().append(e('
').append(this.$clear)))},value2html:function(e,n){var r=e?this.dpg.formatDate(this.toUTC(e),this.parsedViewFormat,this.options.datetimepicker.language,this.options.formatType):"";if(!n)return r;t.superclass.value2html(r,n)},html2value:function(e){var t=this.parseDate(e,this.parsedViewFormat);return t?this.fromUTC(t):null},value2str:function(e){return e?this.dpg.formatDate(this.toUTC(e),this.parsedFormat,this.options.datetimepicker.language,this.options.formatType):""},str2value:function(e){var t=this.parseDate(e,this.parsedFormat);return t?this.fromUTC(t):null},value2submit:function(e){return this.value2str(e)},value2input:function(e){e&&this.$input.data("datetimepicker").setDate(e)},input2value:function(){var e=this.$input.data("datetimepicker");return e.date?e.getDate():null},activate:function(){},clear:function(){this.$input.data("datetimepicker").date=null,this.$input.find(".active").removeClass("active"),this.options.showbuttons||this.$input.closest("form").submit()},autosubmit:function(){this.$input.on("mouseup",".minute",function(t){var n=e(this).closest("form");setTimeout(function(){n.submit()},200)})},toUTC:function(e){return e?new Date(e.valueOf()-e.getTimezoneOffset()*6e4):e},fromUTC:function(e){return e?new Date(e.valueOf()+e.getTimezoneOffset()*6e4):e},parseDate:function(e,t){var n=null,r;return e&&(n=this.dpg.parseDate(e,t,this.options.datetimepicker.language,this.options.formatType),typeof e=="string"&&(r=this.dpg.formatDate(n,t,this.options.datetimepicker.language,this.options.formatType),e!==r&&(n=null))),n}}),t.defaults=e.extend({},e.fn.editabletypes.abstractinput.defaults,{tpl:'
',inputclass:null,format:"yyyy-mm-dd hh:ii",formatType:"standard",viewformat:null,datetimepicker:{todayHighlight:!1,autoclose:!1},clear:"× clear"}),e.fn.editabletypes.datetime=t}(window.jQuery),function(e){"use strict";var t=function(e){this.init("datetimefield",e,t.defaults),this.initPicker(e,t.defaults)};e.fn.editableutils.inherit(t,e.fn.editabletypes.datetime),e.extend(t.prototype,{render:function(){this.$input=this.$tpl.find("input"),this.setClass(),this.setAttr("placeholder"),this.$tpl.datetimepicker(this.options.datetimepicker),this.$input.off("focus keydown"),this.$input.keyup(e.proxy(function(){this.$tpl.removeData("date"),this.$tpl.datetimepicker("update")},this))},value2input:function(e){this.$input.val(this.value2html(e)),this.$tpl.datetimepicker("update")},input2value:function(){return this.html2value(this.$input.val())},activate:function(){e.fn.editabletypes.text.prototype.activate.call(this)},autosubmit:function(){}}),t.defaults=e.extend({},e.fn.editabletypes.datetime.defaults,{tpl:'
',inputclass:"input-medium",datetimepicker:{todayHighlight:!1,autoclose:!0}}),e.fn.editabletypes.datetimefield=t}(window.jQuery),function(e){"use strict";var t=function(n){this.init("typeahead",n,t.defaults),this.options.typeahead=e.extend({},t.defaults.typeahead,{matcher:this.matcher,sorter:this.sorter,highlighter:this.highlighter,updater:this.updater},n.typeahead)};e.fn.editableutils.inherit(t,e.fn.editabletypes.list),e.extend(t.prototype,{renderList:function(){this.$input=this.$tpl.is("input")?this.$tpl:this.$tpl.find('input[type="text"]'),this.options.typeahead.source=this.sourceData,this.$input.typeahead(this.options.typeahead);var t=this.$input.data("typeahead");t.render=e.proxy(this.typeaheadRender,t),t.select=e.proxy(this.typeaheadSelect,t),t.move=e.proxy(this.typeaheadMove,t),this.renderClear(),this.setClass(),this.setAttr("placeholder")},value2htmlFinal:function(t,n){if(this.getIsObjects()){var r=e.fn.editableutils.itemsByValue(t,this.sourceData);e(n).text(r.length?r[0].text:"")}else e(n).text(t)},html2value:function(e){return e?e:null},value2input:function(t){if(this.getIsObjects()){var n=e.fn.editableutils.itemsByValue(t,this.sourceData);this.$input.data("value",t).val(n.length?n[0].text:"")}else this.$input.val(t)},input2value:function(){if(this.getIsObjects()){var t=this.$input.data("value"),n=e.fn.editableutils.itemsByValue(t,this.sourceData);return n.length&&n[0].text.toLowerCase()===this.$input.val().toLowerCase()?t:null}return this.$input.val()},getIsObjects:function(){if(this.isObjects===undefined){this.isObjects=!1;for(var e=0;e',typeahead:null,clear:!0}),e.fn.editabletypes.typeahead=t}(window.jQuery); \ No newline at end of file diff --git a/templates/admin/default/assets/bootstrap-editable/img/clear.png b/templates/admin/default/assets/img/clear.png similarity index 100% rename from templates/admin/default/assets/bootstrap-editable/img/clear.png rename to templates/admin/default/assets/img/clear.png diff --git a/templates/admin/default/assets/img/flags/en.gif b/templates/admin/default/assets/img/flags/en.gif index 3a7661fdb..91b2b0090 100755 Binary files a/templates/admin/default/assets/img/flags/en.gif and b/templates/admin/default/assets/img/flags/en.gif differ diff --git a/templates/admin/default/assets/img/flags/es.gif b/templates/admin/default/assets/img/flags/es.gif index 9ac64ad37..bdf09f8f7 100755 Binary files a/templates/admin/default/assets/img/flags/es.gif and b/templates/admin/default/assets/img/flags/es.gif differ diff --git a/templates/admin/default/assets/img/flags/fr.gif b/templates/admin/default/assets/img/flags/fr.gif index 46f4d122d..2f6cf9ea3 100755 Binary files a/templates/admin/default/assets/img/flags/fr.gif and b/templates/admin/default/assets/img/flags/fr.gif differ diff --git a/templates/admin/default/assets/img/flags/it.gif b/templates/admin/default/assets/img/flags/it.gif index fddb02499..cdd750a54 100755 Binary files a/templates/admin/default/assets/img/flags/it.gif and b/templates/admin/default/assets/img/flags/it.gif differ diff --git a/templates/admin/default/assets/bootstrap-editable/img/loading.gif b/templates/admin/default/assets/img/loading.gif similarity index 100% rename from templates/admin/default/assets/bootstrap-editable/img/loading.gif rename to templates/admin/default/assets/img/loading.gif diff --git a/templates/admin/default/assets/bootstrap-editable/js/bootstrap-editable.js b/templates/admin/default/assets/js/bootstrap-editable/bootstrap-editable.js old mode 100755 new mode 100644 similarity index 95% rename from templates/admin/default/assets/bootstrap-editable/js/bootstrap-editable.js rename to templates/admin/default/assets/js/bootstrap-editable/bootstrap-editable.js index b70ea20bb..26280b37f --- a/templates/admin/default/assets/bootstrap-editable/js/bootstrap-editable.js +++ b/templates/admin/default/assets/js/bootstrap-editable/bootstrap-editable.js @@ -1,8 +1,7 @@ -/*! X-editable - v1.4.6 -* In-place editing with Twitter Bootstrap, jQuery UI or pure jQuery -* http://github.com/vitalets/x-editable -* Copyright (c) 2013 Vitaliy Potapov; Licensed MIT */ - +/*! X-editable - v1.4.7 +* In-place editing with Twitter Bootstrap, jQuery UI or pure jQuery +* http://github.com/vitalets/x-editable +* Copyright (c) 2013 Vitaliy Potapov; Licensed MIT */ /** Form with single input element, two buttons and two states: normal/loading. Applied as jQuery method to DIV tag (not to form tag!). This is because form can be in loading state when spinner shown. @@ -33,6 +32,9 @@ Editableform is linked with one of input types, e.g. 'text', 'select' etc. //set initial value //todo: may be add check: typeof str === 'string' ? this.value = this.input.str2value(this.options.value); + + //prerender: get input.$input + this.input.prerender(); }, initTemplate: function() { this.$form = $($.fn.editableform.template); @@ -80,9 +82,8 @@ Editableform is linked with one of input types, e.g. 'text', 'select' etc. this.initInput(); //append input to form - this.input.prerender(); this.$form.find('div.editable-input').append(this.input.$tpl); - + //append form to container this.$div.append(this.$form); @@ -620,8 +621,11 @@ Editableform is linked with one of input types, e.g. 'text', 'select' etc. //error class attached to editable-error-block $.fn.editableform.errorBlockClass = 'editable-error'; + + //engine + $.fn.editableform.engine = 'jqeury'; }(window.jQuery)); - + /** * EditableForm utilites */ @@ -871,7 +875,7 @@ Editableform is linked with one of input types, e.g. 'text', 'select' etc. }; }(window.jQuery)); - + /** Attaches stand-alone container with editable-form to HTML element. Element is used only for positioning, value is not stored anywhere.
This method applied internally in $().editable(). You should subscribe on it's events (save / cancel) to get profit of it.
@@ -898,6 +902,8 @@ Applied as jQuery method. containerDataName: null, //object name in element's .data() innerCss: null, //tbd in child class containerClass: 'editable-container editable-popup', //css class applied to container element + defaults: {}, //container itself defaults + init: function(element, options) { this.$element = $(element); //since 1.4.1 container do not use data-* directly as they already merged into options. @@ -975,10 +981,9 @@ Applied as jQuery method. throw new Error(this.containerName + ' not found. Have you included corresponding js file?'); } - var cDef = $.fn[this.containerName].defaults; //keys defined in container defaults go to container, others go to form for(var k in this.options) { - if(k in cDef) { + if(k in this.defaults) { this.containerOptions[k] = this.options[k]; } else { this.formOptions[k] = this.options[k]; @@ -1386,7 +1391,7 @@ Applied as jQuery method. }; }(window.jQuery)); - + /** * Editable Inline * --------------------- @@ -1440,7 +1445,7 @@ Applied as jQuery method. } }); -}(window.jQuery)); +}(window.jQuery)); /** Makes editable any HTML element on the page. Applied as jQuery method. @@ -2249,11 +2254,11 @@ Makes editable any HTML element on the page. Applied as jQuery method. @since 1.4.5 @default #FFFF80 **/ - highlight: '#FFFF80' + highlight: '#FFFF80' }; }(window.jQuery)); - + /** AbstractInput - base class for all editable inputs. It defines interface to be implemented by any input type. @@ -2415,7 +2420,7 @@ To create your own input you can inherit from this class. }, // -------- helper functions -------- - setClass: function() { + setClass: function() { if(this.options.inputclass) { this.$input.addClass(this.options.inputclass); } @@ -2447,9 +2452,9 @@ To create your own input you can inherit from this class. @property inputclass @type string - @default input-medium + @default null **/ - inputclass: 'input-medium', + inputclass: null, //scope for external methods (e.g. source defined as function) //for internal use only scope: null, @@ -2461,7 +2466,7 @@ To create your own input you can inherit from this class. $.extend($.fn.editabletypes, {abstractinput: AbstractInput}); }(window.jQuery)); - + /** List - abstract class for inputs that have source option loaded from js array or via ajax @@ -2783,7 +2788,7 @@ List - abstract class for inputs that have source option loaded from js array or $.fn.editabletypes.list = List; }(window.jQuery)); - + /** Text input @@ -2918,7 +2923,7 @@ $(function(){ $.fn.editabletypes.text = Text; }(window.jQuery)); - + /** Textarea input @@ -3030,7 +3035,7 @@ $(function(){ $.fn.editabletypes.textarea = Textarea; }(window.jQuery)); - + /** Select (dropdown) @@ -3126,7 +3131,7 @@ $(function(){ $.fn.editabletypes.select = Select; }(window.jQuery)); - + /** List of checkboxes. Internally value stored as javascript array of values. @@ -3279,7 +3284,7 @@ $(function(){ $.fn.editabletypes.checklist = Checklist; }(window.jQuery)); - + /** HTML5 input types. Following types are supported: @@ -3498,7 +3503,7 @@ Time }); $.fn.editabletypes.time = Time; }(window.jQuery)); - + /** Select2 input. Based on amazing work of Igor Vaynberg https://github.com/ivaynberg/select2. Please see [original select2 docs](http://ivaynberg.github.com/select2) for detailed description and options. @@ -3817,13 +3822,13 @@ $(function(){ @type string @default ', ' **/ - viewseparator: ', ' + viewseparator: ', ' }); $.fn.editabletypes.select2 = Constructor; }(window.jQuery)); - + /** * Combodate - 1.0.4 * Dropdown date and time picker. @@ -4276,7 +4281,7 @@ $(function(){ roundTime: true //whether to round minutes and seconds if step > 1 }; -}(window.jQuery)); +}(window.jQuery)); /** Combodate input - dropdown date and time picker. Based on [combodate](http://vitalets.github.com/combodate) plugin (included). To use it you should manually include [momentjs](http://momentjs.com). @@ -4343,7 +4348,14 @@ $(function(){ $.extend(Constructor.prototype, { render: function () { this.$input.combodate(this.options.combodate); + + if($.fn.editableform.engine === 'bs3') { + this.$input.siblings().find('select').addClass('form-control'); + } + if(this.options.inputclass) { + this.$input.siblings().find('select').addClass(this.options.inputclass); + } //"clear" link /* if(this.options.clear) { @@ -4466,31 +4478,73 @@ $(function(){ $.fn.editabletypes.combodate = Constructor; }(window.jQuery)); - + /* -Editableform based on Twitter Bootstrap +Editableform based on Twitter Bootstrap 3 */ (function ($) { "use strict"; + //store parent methods + var pInitInput = $.fn.editableform.Constructor.prototype.initInput; + $.extend($.fn.editableform.Constructor.prototype, { - initTemplate: function() { + initTemplate: function() { this.$form = $($.fn.editableform.template); + this.$form.find('.control-group').addClass('form-group'); this.$form.find('.editable-error-block').addClass('help-block'); - } + }, + initInput: function() { + pInitInput.apply(this); + + //for bs3 set default class `input-sm` to standard inputs + var emptyInputClass = this.input.options.inputclass === null || this.input.options.inputclass === false; + var defaultClass = 'input-sm'; + + //bs3 add `form-control` class to standard inputs + var stdtypes = 'text,select,textarea,password,email,url,tel,number,range,time'.split(','); + if(~$.inArray(this.input.type, stdtypes)) { + this.input.$input.addClass('form-control'); + if(emptyInputClass) { + this.input.options.inputclass = defaultClass; + this.input.$input.addClass(defaultClass); + } + } + + //apply bs3 size class also to buttons (to fit size of control) + var $btn = this.$form.find('.editable-buttons'); + var classes = emptyInputClass ? [defaultClass] : this.input.options.inputclass.split(' '); + for(var i=0; i'; + $.fn.editableform.buttons = + ''+ + ''; //error classes - $.fn.editableform.errorGroupClass = 'error'; - $.fn.editableform.errorBlockClass = null; - -}(window.jQuery)); + $.fn.editableform.errorGroupClass = 'has-error'; + $.fn.editableform.errorBlockClass = null; + //engine + $.fn.editableform.engine = 'bs3'; +}(window.jQuery)); /** -* Editable Popover +* Editable Popover3 (for Bootstrap 3) * --------------------- * requires bootstrap-popover.js */ @@ -4500,15 +4554,16 @@ Editableform based on Twitter Bootstrap //extend methods $.extend($.fn.editableContainer.Popup.prototype, { containerName: 'popover', - //for compatibility with bootstrap <= 2.2.1 (content inserted into

instead of directly .popover-content) - innerCss: $.fn.popover && $($.fn.popover.defaults.template).find('p').length ? '.popover-content p' : '.popover-content', + containerDataName: 'bs.popover', + innerCss: '.popover-content', + defaults: $.fn.popover.Constructor.DEFAULTS, initContainer: function(){ $.extend(this.containerOptions, { trigger: 'manual', selector: false, content: ' ', - template: $.fn.popover.defaults.template + template: this.defaults.template }); //as template property is used in inputs, hide it from popover @@ -4548,10 +4603,11 @@ Editableform based on Twitter Bootstrap /** * move popover to new position. This function mainly copied from bootstrap-popover. */ - /*jshint laxcomma: true*/ + /*jshint laxcomma: true, eqeqeq: false*/ setPosition: function () { - (function() { + (function() { + /* var $tip = this.tip() , inside , pos @@ -4661,14 +4717,32 @@ Editableform based on Twitter Bootstrap .offset(tp) .addClass(placement) .addClass('in'); + */ + + + var $tip = this.tip(); + + var placement = typeof this.options.placement == 'function' ? + this.options.placement.call(this, $tip[0], this.$element[0]) : + this.options.placement; + + + var pos = this.getPosition(); + var actualWidth = $tip[0].offsetWidth; + var actualHeight = $tip[0].offsetHeight; + var calculatedOffset = this.getCalculatedOffset(placement, pos, actualWidth, actualHeight); + + this.applyPlacement(calculatedOffset, placement); + + }).call(this.container()); - /*jshint laxcomma: false*/ + /*jshint laxcomma: false, eqeqeq: true*/ } }); }(window.jQuery)); - + /* ========================================================= * bootstrap-datepicker.js * http://www.eyecon.ro/bootstrap-datepicker @@ -5923,7 +5997,7 @@ Editableform based on Twitter Bootstrap }); }( window.jQuery )); - + /** Bootstrap-datepicker. Description and examples: https://github.com/eternicode/bootstrap-datepicker. @@ -6153,7 +6227,7 @@ $(function(){ $.fn.editabletypes.date = Date; }(window.jQuery)); - + /** Bootstrap datefield input - modification for inline mode. Shows normal and binds popup datepicker. @@ -6234,7 +6308,7 @@ Automatically shown in inline mode. $.fn.editabletypes.datefield = DateField; -}(window.jQuery)); +}(window.jQuery)); /** Bootstrap-datetimepicker. Based on [smalot bootstrap-datetimepicker plugin](https://github.com/smalot/bootstrap-datetimepicker). @@ -6478,7 +6552,7 @@ $(function(){ $.fn.editabletypes.datetime = DateTime; -}(window.jQuery)); +}(window.jQuery)); /** Bootstrap datetimefield input - datetime input for inline mode. Shows normal and binds popup datetimepicker. @@ -6555,273 +6629,4 @@ Automatically shown in inline mode. $.fn.editabletypes.datetimefield = DateTimeField; -}(window.jQuery)); -/** -Typeahead input (bootstrap only). Based on Twitter Bootstrap [typeahead](http://twitter.github.com/bootstrap/javascript.html#typeahead). -Depending on `source` format typeahead operates in two modes: - -* **strings**: - When `source` defined as array of strings, e.g. `['text1', 'text2', 'text3' ...]`. - User can submit one of these strings or any text entered in input (even if it is not matching source). - -* **objects**: - When `source` defined as array of objects, e.g. `[{value: 1, text: "text1"}, {value: 2, text: "text2"}, ...]`. - User can submit only values that are in source (otherwise `null` is submitted). This is more like *dropdown* behavior. - -@class typeahead -@extends list -@since 1.4.1 -@final -@example - - -**/ -(function ($) { - "use strict"; - - var Constructor = function (options) { - this.init('typeahead', options, Constructor.defaults); - - //overriding objects in config (as by default jQuery extend() is not recursive) - this.options.typeahead = $.extend({}, Constructor.defaults.typeahead, { - //set default methods for typeahead to work with objects - matcher: this.matcher, - sorter: this.sorter, - highlighter: this.highlighter, - updater: this.updater - }, options.typeahead); - }; - - $.fn.editableutils.inherit(Constructor, $.fn.editabletypes.list); - - $.extend(Constructor.prototype, { - renderList: function() { - this.$input = this.$tpl.is('input') ? this.$tpl : this.$tpl.find('input[type="text"]'); - - //set source of typeahead - this.options.typeahead.source = this.sourceData; - - //apply typeahead - this.$input.typeahead(this.options.typeahead); - - //patch some methods in typeahead - var ta = this.$input.data('typeahead'); - ta.render = $.proxy(this.typeaheadRender, ta); - ta.select = $.proxy(this.typeaheadSelect, ta); - ta.move = $.proxy(this.typeaheadMove, ta); - - this.renderClear(); - this.setClass(); - this.setAttr('placeholder'); - }, - - value2htmlFinal: function(value, element) { - if(this.getIsObjects()) { - var items = $.fn.editableutils.itemsByValue(value, this.sourceData); - $(element).text(items.length ? items[0].text : ''); - } else { - $(element).text(value); - } - }, - - html2value: function (html) { - return html ? html : null; - }, - - value2input: function(value) { - if(this.getIsObjects()) { - var items = $.fn.editableutils.itemsByValue(value, this.sourceData); - this.$input.data('value', value).val(items.length ? items[0].text : ''); - } else { - this.$input.val(value); - } - }, - - input2value: function() { - if(this.getIsObjects()) { - var value = this.$input.data('value'), - items = $.fn.editableutils.itemsByValue(value, this.sourceData); - - if(items.length && items[0].text.toLowerCase() === this.$input.val().toLowerCase()) { - return value; - } else { - return null; //entered string not found in source - } - } else { - return this.$input.val(); - } - }, - - /* - if in sourceData values <> texts, typeahead in "objects" mode: - user must pick some value from list, otherwise `null` returned. - if all values == texts put typeahead in "strings" mode: - anything what entered is submited. - */ - getIsObjects: function() { - if(this.isObjects === undefined) { - this.isObjects = false; - for(var i=0; i - **/ - tpl:'', - /** - Configuration of typeahead. [Full list of options](http://twitter.github.com/bootstrap/javascript.html#typeahead). - - @property typeahead - @type object - @default null - **/ - typeahead: null, - /** - Whether to show `clear` button - - @property clear - @type boolean - @default true - **/ - clear: true - }); - - $.fn.editabletypes.typeahead = Constructor; - }(window.jQuery)); \ No newline at end of file diff --git a/templates/admin/default/assets/js/bootstrap-switch/bootstrap-switch.js b/templates/admin/default/assets/js/bootstrap-switch/bootstrap-switch.js new file mode 100644 index 000000000..1538f9df1 --- /dev/null +++ b/templates/admin/default/assets/js/bootstrap-switch/bootstrap-switch.js @@ -0,0 +1,382 @@ +/*! ============================================================ + * bootstrapSwitch v1.8 by Larentis Mattia @SpiritualGuru + * http://www.larentis.eu/ + * + * Enhanced for radiobuttons by Stein, Peter @BdMdesigN + * http://www.bdmdesign.org/ + * + * Project site: + * http://www.larentis.eu/switch/ + * ============================================================ + * Licensed under the Apache License, Version 2.0 + * http://www.apache.org/licenses/LICENSE-2.0 + * ============================================================ */ + +!function ($) { + "use strict"; + + $.fn['bootstrapSwitch'] = function (method) { + var inputSelector = 'input[type!="hidden"]'; + var methods = { + init: function () { + return this.each(function () { + var $element = $(this) + , $div + , $switchLeft + , $switchRight + , $label + , $form = $element.closest('form') + , myClasses = "" + , classes = $element.attr('class') + , color + , moving + , onLabel = "ON" + , offLabel = "OFF" + , icon = false + , textLabel = false; + + $.each(['switch-mini', 'switch-small', 'switch-large'], function (i, el) { + if (classes.indexOf(el) >= 0) + myClasses = el; + }); + + $element.addClass('has-switch'); + + if ($element.data('on') !== undefined) + color = "switch-" + $element.data('on'); + + if ($element.data('on-label') !== undefined) + onLabel = $element.data('on-label'); + + if ($element.data('off-label') !== undefined) + offLabel = $element.data('off-label'); + + if ($element.data('label-icon') !== undefined) + icon = $element.data('label-icon'); + + if ($element.data('text-label') !== undefined) + textLabel = $element.data('text-label'); + + $switchLeft = $('') + .addClass("switch-left") + .addClass(myClasses) + .addClass(color) + .html(onLabel); + + color = ''; + if ($element.data('off') !== undefined) + color = "switch-" + $element.data('off'); + + $switchRight = $('') + .addClass("switch-right") + .addClass(myClasses) + .addClass(color) + .html(offLabel); + + $label = $('

')).parent().data('animated', false); + + if ($element.data('animated') !== false) + $div.addClass('switch-animate').data('animated', true); + + $div + .append($switchLeft) + .append($label) + .append($switchRight); + + $element.find('>div').addClass( + $element.find(inputSelector).is(':checked') ? 'switch-on' : 'switch-off' + ); + + if ($element.find(inputSelector).is(':disabled')) + $(this).addClass('deactivate'); + + var changeStatus = function ($this) { + if ($element.parent('label').is('.label-change-switch')) { + + } else { + $this.siblings('label').trigger('mousedown').trigger('mouseup').trigger('click'); + } + }; + + $element.on('keydown', function (e) { + if (e.keyCode === 32) { + e.stopImmediatePropagation(); + e.preventDefault(); + changeStatus($(e.target).find('span:first')); + } + }); + + $switchLeft.on('click', function (e) { + changeStatus($(this)); + }); + + $switchRight.on('click', function (e) { + changeStatus($(this)); + }); + + $element.find(inputSelector).on('change', function (e, skipOnChange) { + var $this = $(this) + , $element = $this.parent() + , thisState = $this.is(':checked') + , state = $element.is('.switch-off'); + + e.preventDefault(); + + $element.css('left', ''); + + if (state === thisState) { + + if (thisState) + $element.removeClass('switch-off').addClass('switch-on'); + else $element.removeClass('switch-on').addClass('switch-off'); + + if ($element.data('animated') !== false) + $element.addClass("switch-animate"); + + if (typeof skipOnChange === 'boolean' && skipOnChange) + return; + + $element.parent().trigger('switch-change', {'el': $this, 'value': thisState}) + } + }); + + $element.find('label').on('mousedown touchstart', function (e) { + var $this = $(this); + moving = false; + + e.preventDefault(); + e.stopImmediatePropagation(); + + $this.closest('div').removeClass('switch-animate'); + + if ($this.closest('.has-switch').is('.deactivate')) { + $this.unbind('click'); + } else if ($this.closest('.switch-on').parent().is('.radio-no-uncheck')) { + $this.unbind('click'); + } else { + $this.on('mousemove touchmove', function (e) { + var $element = $(this).closest('.make-switch') + , relativeX = (e.pageX || e.originalEvent.targetTouches[0].pageX) - $element.offset().left + , percent = (relativeX / $element.width()) * 100 + , left = 25 + , right = 75; + + moving = true; + + if (percent < left) + percent = left; + else if (percent > right) + percent = right; + + $element.find('>div').css('left', (percent - right) + "%") + }); + + $this.on('click touchend', function (e) { + var $this = $(this) + , $myInputBox = $this.siblings('input'); + + e.stopImmediatePropagation(); + e.preventDefault(); + + $this.unbind('mouseleave'); + + if (moving) + $myInputBox.prop('checked', !(parseInt($this.parent().css('left')) < -25)); + else + $myInputBox.prop("checked", !$myInputBox.is(":checked")); + + moving = false; + $myInputBox.trigger('change'); + }); + + $this.on('mouseleave', function (e) { + var $this = $(this) + , $myInputBox = $this.siblings('input'); + + e.preventDefault(); + e.stopImmediatePropagation(); + + $this.unbind('mouseleave mousemove'); + $this.trigger('mouseup'); + + $myInputBox.prop('checked', !(parseInt($this.parent().css('left')) < -25)).trigger('change'); + }); + + $this.on('mouseup', function (e) { + e.stopImmediatePropagation(); + e.preventDefault(); + + $(this).trigger('mouseleave'); + }); + } + }); + + if ($form.data('bootstrapSwitch') !== 'injected') { + $form.bind('reset', function () { + setTimeout(function () { + $form.find('.make-switch').each(function () { + var $input = $(this).find(inputSelector); + + $input.prop('checked', $input.is(':checked')).trigger('change'); + }); + }, 1); + }); + $form.data('bootstrapSwitch', 'injected'); + } + } + ); + }, + toggleActivation: function () { + var $this = $(this); + + $this.toggleClass('deactivate'); + $this.find(inputSelector).prop('disabled', $this.is('.deactivate')); + }, + isActive: function () { + return !$(this).hasClass('deactivate'); + }, + setActive: function (active) { + var $this = $(this); + + if (active) { + $this.removeClass('deactivate'); + $this.find(inputSelector).removeAttr('disabled'); + } + else { + $this.addClass('deactivate'); + $this.find(inputSelector).attr('disabled', 'disabled'); + } + }, + toggleState: function (skipOnChange) { + var $input = $(this).find(':checkbox'); + $input.prop('checked', !$input.is(':checked')).trigger('change', skipOnChange); + }, + toggleRadioState: function (skipOnChange) { + var $radioinput = $(this).find(':radio'); + $radioinput.not(':checked').prop('checked', !$radioinput.is(':checked')).trigger('change', skipOnChange); + }, + toggleRadioStateAllowUncheck: function (uncheck, skipOnChange) { + var $radioinput = $(this).find(':radio'); + if (uncheck) { + $radioinput.not(':checked').trigger('change', skipOnChange); + } + else { + $radioinput.not(':checked').prop('checked', !$radioinput.is(':checked')).trigger('change', skipOnChange); + } + }, + setState: function (value, skipOnChange) { + $(this).find(inputSelector).prop('checked', value).trigger('change', skipOnChange); + }, + setOnLabel: function (value) { + var $switchLeft = $(this).find(".switch-left"); + $switchLeft.html(value); + }, + setOffLabel: function (value) { + var $switchRight = $(this).find(".switch-right"); + $switchRight.html(value); + }, + setOnClass: function (value) { + var $switchLeft = $(this).find(".switch-left"); + var color = ''; + if (value !== undefined) { + if ($(this).attr('data-on') !== undefined) { + color = "switch-" + $(this).attr('data-on') + } + $switchLeft.removeClass(color); + color = "switch-" + value; + $switchLeft.addClass(color); + } + }, + setOffClass: function (value) { + var $switchRight = $(this).find(".switch-right"); + var color = ''; + if (value !== undefined) { + if ($(this).attr('data-off') !== undefined) { + color = "switch-" + $(this).attr('data-off') + } + $switchRight.removeClass(color); + color = "switch-" + value; + $switchRight.addClass(color); + } + }, + setAnimated: function (value) { + var $element = $(this).find(inputSelector).parent(); + if (value === undefined) value = false; + $element.data('animated', value); + $element.attr('data-animated', value); + + if ($element.data('animated') !== false) { + $element.addClass("switch-animate"); + } else { + $element.removeClass("switch-animate"); + } + }, + setSizeClass: function (value) { + var $element = $(this); + var $switchLeft = $element.find(".switch-left"); + var $switchRight = $element.find(".switch-right"); + var $label = $element.find("label"); + $.each(['switch-mini', 'switch-small', 'switch-large'], function (i, el) { + if (el !== value) { + $switchLeft.removeClass(el); + $switchRight.removeClass(el); + $label.removeClass(el); + } else { + $switchLeft.addClass(el); + $switchRight.addClass(el); + $label.addClass(el); + } + }); + }, + status: function () { + return $(this).find(inputSelector).is(':checked'); + }, + destroy: function () { + var $element = $(this) + , $div = $element.find('div') + , $form = $element.closest('form') + , $inputbox; + + $div.find(':not(input)').remove(); + + $inputbox = $div.children(); + $inputbox.unwrap().unwrap(); + + $inputbox.unbind('change'); + + if ($form) { + $form.unbind('reset'); + $form.removeData('bootstrapSwitch'); + } + + return $inputbox; + } + }; + + if (methods[method]) + return methods[method].apply(this, Array.prototype.slice.call(arguments, 1)); + else if (typeof method === 'object' || !method) + return methods.init.apply(this, arguments); + else + $.error('Method ' + method + ' does not exist!'); + }; +}(jQuery); + +(function ($) { + $(function () { + $('.make-switch')['bootstrapSwitch'](); + }); +})(jQuery); \ No newline at end of file diff --git a/templates/admin/default/assets/less/thelia/bootstrap-editable.less b/templates/admin/default/assets/less/thelia/bootstrap-editable.less new file mode 100644 index 000000000..05454fc1f --- /dev/null +++ b/templates/admin/default/assets/less/thelia/bootstrap-editable.less @@ -0,0 +1,655 @@ +/*! X-editable - v1.4.7 +* In-place editing with Twitter Bootstrap, jQuery UI or pure jQuery +* http://github.com/vitalets/x-editable +* Copyright (c) 2013 Vitaliy Potapov; Licensed MIT */ +.editableform { + margin-bottom: 0; /* overwrites bootstrap margin */ +} + +.editableform .control-group { + margin-bottom: 0; /* overwrites bootstrap margin */ + white-space: nowrap; /* prevent wrapping buttons on new line */ + line-height: 20px; /* overwriting bootstrap line-height. See #133 */ +} + +.editable-buttons { + display: inline-block; /* should be inline to take effect of parent's white-space: nowrap */ + vertical-align: top; + margin-left: 7px; + /* inline-block emulation for IE7*/ + zoom: 1; + *display: inline; +} + +.editable-buttons.editable-buttons-bottom { + display: block; + margin-top: 7px; + margin-left: 0; +} + +.editable-input { + vertical-align: top; + display: inline-block; /* should be inline to take effect of parent's white-space: nowrap */ + width: auto; /* bootstrap-responsive has width: 100% that breakes layout */ + white-space: normal; /* reset white-space decalred in parent*/ + /* display-inline emulation for IE7*/ + zoom: 1; + *display: inline; +} + +.editable-buttons .editable-cancel { + margin-left: 7px; +} + +/*for jquery-ui buttons need set height to look more pretty*/ +.editable-buttons button.ui-button-icon-only { + height: 24px; + width: 30px; +} + +.editableform-loading { + background: url('@{imgDir}/loading.gif') center center no-repeat; + height: 25px; + width: auto; + min-width: 25px; +} + +.editable-inline .editableform-loading { + background-position: left 5px; +} + + .editable-error-block { + max-width: 300px; + margin: 5px 0 0 0; + width: auto; + white-space: normal; +} + +/*add padding for jquery ui*/ +.editable-error-block.ui-state-error { + padding: 3px; +} + +.editable-error { + color: red; +} + +/* ---- For specific types ---- */ + +.editableform .editable-date { + padding: 0; + margin: 0; + float: left; +} + +/* move datepicker icon to center of add-on button. See https://github.com/vitalets/x-editable/issues/183 */ +.editable-inline .add-on .icon-th { + margin-top: 3px; + margin-left: 1px; +} + + +/* checklist vertical alignment */ +.editable-checklist label input[type="checkbox"], +.editable-checklist label span { + vertical-align: middle; + margin: 0; +} + +.editable-checklist label { + white-space: nowrap; +} + +/* set exact width of textarea to fit buttons toolbar */ +.editable-wysihtml5 { + width: 566px; + height: 250px; +} + +/* clear button shown as link in date inputs */ +.editable-clear { + clear: both; + font-size: 0.9em; + text-decoration: none; + text-align: right; +} + +/* IOS-style clear button for text inputs */ +.editable-clear-x { + background: url('@{imgDir}/clear.png') center center no-repeat; + display: block; + width: 13px; + height: 13px; + position: absolute; + opacity: 0.6; + z-index: 100; + + top: 50%; + right: 6px; + margin-top: -6px; + +} + +.editable-clear-x:hover { + opacity: 1; +} + +.editable-pre-wrapped { + white-space: pre-wrap; +} +.editable-container.editable-popup { + max-width: none !important; /* without this rule poshytip/tooltip does not stretch */ +} + +.editable-container.popover { + width: auto; /* without this rule popover does not stretch */ +} + +.editable-container.editable-inline { + display: inline-block; + vertical-align: middle; + width: auto; + /* inline-block emulation for IE7*/ + zoom: 1; + *display: inline; +} + +.editable-container.ui-widget { + font-size: inherit; /* jqueryui widget font 1.1em too big, overwrite it */ + z-index: 9990; /* should be less than select2 dropdown z-index to close dropdown first when click */ +} +.editable-click, +a.editable-click, +a.editable-click:hover { + text-decoration: none; + border-bottom: dashed 1px #0088cc; +} + +.editable-click.editable-disabled, +a.editable-click.editable-disabled, +a.editable-click.editable-disabled:hover { + color: #585858; + cursor: default; + border-bottom: none; +} + +.editable-empty, .editable-empty:hover, .editable-empty:focus{ + font-style: italic; + color: #DD1144; + /* border-bottom: none; */ + text-decoration: none; +} + +.editable-unsaved { + font-weight: bold; +} + +.editable-unsaved:after { +/* content: '*'*/ +} + +.editable-bg-transition { + -webkit-transition: background-color 1400ms ease-out; + -moz-transition: background-color 1400ms ease-out; + -o-transition: background-color 1400ms ease-out; + -ms-transition: background-color 1400ms ease-out; + transition: background-color 1400ms ease-out; +} + +/*see https://github.com/vitalets/x-editable/issues/139 */ +.form-horizontal .editable +{ + padding-top: 5px; + display:inline-block; +} + + +/*! + * Datepicker for Bootstrap + * + * Copyright 2012 Stefan Petre + * Improvements by Andrew Rowls + * Licensed under the Apache License v2.0 + * http://www.apache.org/licenses/LICENSE-2.0 + * + */ +.datepicker { + padding: 4px; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + direction: ltr; + /*.dow { + border-top: 1px solid #ddd !important; + }*/ + +} +.datepicker-inline { + width: 220px; +} +.datepicker.datepicker-rtl { + direction: rtl; +} +.datepicker.datepicker-rtl table tr td span { + float: right; +} +.datepicker-dropdown { + top: 0; + left: 0; +} +.datepicker-dropdown:before { + content: ''; + display: inline-block; + border-left: 7px solid transparent; + border-right: 7px solid transparent; + border-bottom: 7px solid #ccc; + border-bottom-color: rgba(0, 0, 0, 0.2); + position: absolute; + top: -7px; + left: 6px; +} +.datepicker-dropdown:after { + content: ''; + display: inline-block; + border-left: 6px solid transparent; + border-right: 6px solid transparent; + border-bottom: 6px solid #ffffff; + position: absolute; + top: -6px; + left: 7px; +} +.datepicker > div { + display: none; +} +.datepicker.days div.datepicker-days { + display: block; +} +.datepicker.months div.datepicker-months { + display: block; +} +.datepicker.years div.datepicker-years { + display: block; +} +.datepicker table { + margin: 0; +} +.datepicker td, +.datepicker th { + text-align: center; + width: 20px; + height: 20px; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + border: none; +} +.table-striped .datepicker table tr td, +.table-striped .datepicker table tr th { + background-color: transparent; +} +.datepicker table tr td.day:hover { + background: #eeeeee; + cursor: pointer; +} +.datepicker table tr td.old, +.datepicker table tr td.new { + color: #999999; +} +.datepicker table tr td.disabled, +.datepicker table tr td.disabled:hover { + background: none; + color: #999999; + cursor: default; +} +.datepicker table tr td.today, +.datepicker table tr td.today:hover, +.datepicker table tr td.today.disabled, +.datepicker table tr td.today.disabled:hover { + background-color: #fde19a; + background-image: -moz-linear-gradient(top, #fdd49a, #fdf59a); + background-image: -ms-linear-gradient(top, #fdd49a, #fdf59a); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fdd49a), to(#fdf59a)); + background-image: -webkit-linear-gradient(top, #fdd49a, #fdf59a); + background-image: -o-linear-gradient(top, #fdd49a, #fdf59a); + background-image: linear-gradient(top, #fdd49a, #fdf59a); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fdd49a', endColorstr='#fdf59a', GradientType=0); + border-color: #fdf59a #fdf59a #fbed50; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); + color: #000; +} +.datepicker table tr td.today:hover, +.datepicker table tr td.today:hover:hover, +.datepicker table tr td.today.disabled:hover, +.datepicker table tr td.today.disabled:hover:hover, +.datepicker table tr td.today:active, +.datepicker table tr td.today:hover:active, +.datepicker table tr td.today.disabled:active, +.datepicker table tr td.today.disabled:hover:active, +.datepicker table tr td.today.active, +.datepicker table tr td.today:hover.active, +.datepicker table tr td.today.disabled.active, +.datepicker table tr td.today.disabled:hover.active, +.datepicker table tr td.today.disabled, +.datepicker table tr td.today:hover.disabled, +.datepicker table tr td.today.disabled.disabled, +.datepicker table tr td.today.disabled:hover.disabled, +.datepicker table tr td.today[disabled], +.datepicker table tr td.today:hover[disabled], +.datepicker table tr td.today.disabled[disabled], +.datepicker table tr td.today.disabled:hover[disabled] { + background-color: #fdf59a; +} +.datepicker table tr td.today:active, +.datepicker table tr td.today:hover:active, +.datepicker table tr td.today.disabled:active, +.datepicker table tr td.today.disabled:hover:active, +.datepicker table tr td.today.active, +.datepicker table tr td.today:hover.active, +.datepicker table tr td.today.disabled.active, +.datepicker table tr td.today.disabled:hover.active { + background-color: #fbf069 \9; +} +.datepicker table tr td.today:hover:hover { + color: #000; +} +.datepicker table tr td.today.active:hover { + color: #fff; +} +.datepicker table tr td.range, +.datepicker table tr td.range:hover, +.datepicker table tr td.range.disabled, +.datepicker table tr td.range.disabled:hover { + background: #eeeeee; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} +.datepicker table tr td.range.today, +.datepicker table tr td.range.today:hover, +.datepicker table tr td.range.today.disabled, +.datepicker table tr td.range.today.disabled:hover { + background-color: #f3d17a; + background-image: -moz-linear-gradient(top, #f3c17a, #f3e97a); + background-image: -ms-linear-gradient(top, #f3c17a, #f3e97a); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f3c17a), to(#f3e97a)); + background-image: -webkit-linear-gradient(top, #f3c17a, #f3e97a); + background-image: -o-linear-gradient(top, #f3c17a, #f3e97a); + background-image: linear-gradient(top, #f3c17a, #f3e97a); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#f3c17a', endColorstr='#f3e97a', GradientType=0); + border-color: #f3e97a #f3e97a #edde34; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} +.datepicker table tr td.range.today:hover, +.datepicker table tr td.range.today:hover:hover, +.datepicker table tr td.range.today.disabled:hover, +.datepicker table tr td.range.today.disabled:hover:hover, +.datepicker table tr td.range.today:active, +.datepicker table tr td.range.today:hover:active, +.datepicker table tr td.range.today.disabled:active, +.datepicker table tr td.range.today.disabled:hover:active, +.datepicker table tr td.range.today.active, +.datepicker table tr td.range.today:hover.active, +.datepicker table tr td.range.today.disabled.active, +.datepicker table tr td.range.today.disabled:hover.active, +.datepicker table tr td.range.today.disabled, +.datepicker table tr td.range.today:hover.disabled, +.datepicker table tr td.range.today.disabled.disabled, +.datepicker table tr td.range.today.disabled:hover.disabled, +.datepicker table tr td.range.today[disabled], +.datepicker table tr td.range.today:hover[disabled], +.datepicker table tr td.range.today.disabled[disabled], +.datepicker table tr td.range.today.disabled:hover[disabled] { + background-color: #f3e97a; +} +.datepicker table tr td.range.today:active, +.datepicker table tr td.range.today:hover:active, +.datepicker table tr td.range.today.disabled:active, +.datepicker table tr td.range.today.disabled:hover:active, +.datepicker table tr td.range.today.active, +.datepicker table tr td.range.today:hover.active, +.datepicker table tr td.range.today.disabled.active, +.datepicker table tr td.range.today.disabled:hover.active { + background-color: #efe24b \9; +} +.datepicker table tr td.selected, +.datepicker table tr td.selected:hover, +.datepicker table tr td.selected.disabled, +.datepicker table tr td.selected.disabled:hover { + background-color: #9e9e9e; + background-image: -moz-linear-gradient(top, #b3b3b3, #808080); + background-image: -ms-linear-gradient(top, #b3b3b3, #808080); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#b3b3b3), to(#808080)); + background-image: -webkit-linear-gradient(top, #b3b3b3, #808080); + background-image: -o-linear-gradient(top, #b3b3b3, #808080); + background-image: linear-gradient(top, #b3b3b3, #808080); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#b3b3b3', endColorstr='#808080', GradientType=0); + border-color: #808080 #808080 #595959; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); + color: #fff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); +} +.datepicker table tr td.selected:hover, +.datepicker table tr td.selected:hover:hover, +.datepicker table tr td.selected.disabled:hover, +.datepicker table tr td.selected.disabled:hover:hover, +.datepicker table tr td.selected:active, +.datepicker table tr td.selected:hover:active, +.datepicker table tr td.selected.disabled:active, +.datepicker table tr td.selected.disabled:hover:active, +.datepicker table tr td.selected.active, +.datepicker table tr td.selected:hover.active, +.datepicker table tr td.selected.disabled.active, +.datepicker table tr td.selected.disabled:hover.active, +.datepicker table tr td.selected.disabled, +.datepicker table tr td.selected:hover.disabled, +.datepicker table tr td.selected.disabled.disabled, +.datepicker table tr td.selected.disabled:hover.disabled, +.datepicker table tr td.selected[disabled], +.datepicker table tr td.selected:hover[disabled], +.datepicker table tr td.selected.disabled[disabled], +.datepicker table tr td.selected.disabled:hover[disabled] { + background-color: #808080; +} +.datepicker table tr td.selected:active, +.datepicker table tr td.selected:hover:active, +.datepicker table tr td.selected.disabled:active, +.datepicker table tr td.selected.disabled:hover:active, +.datepicker table tr td.selected.active, +.datepicker table tr td.selected:hover.active, +.datepicker table tr td.selected.disabled.active, +.datepicker table tr td.selected.disabled:hover.active { + background-color: #666666 \9; +} +.datepicker table tr td.active, +.datepicker table tr td.active:hover, +.datepicker table tr td.active.disabled, +.datepicker table tr td.active.disabled:hover { + background-color: #006dcc; + background-image: -moz-linear-gradient(top, #0088cc, #0044cc); + background-image: -ms-linear-gradient(top, #0088cc, #0044cc); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0044cc)); + background-image: -webkit-linear-gradient(top, #0088cc, #0044cc); + background-image: -o-linear-gradient(top, #0088cc, #0044cc); + background-image: linear-gradient(top, #0088cc, #0044cc); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#0088cc', endColorstr='#0044cc', GradientType=0); + border-color: #0044cc #0044cc #002a80; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); + color: #fff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); +} +.datepicker table tr td.active:hover, +.datepicker table tr td.active:hover:hover, +.datepicker table tr td.active.disabled:hover, +.datepicker table tr td.active.disabled:hover:hover, +.datepicker table tr td.active:active, +.datepicker table tr td.active:hover:active, +.datepicker table tr td.active.disabled:active, +.datepicker table tr td.active.disabled:hover:active, +.datepicker table tr td.active.active, +.datepicker table tr td.active:hover.active, +.datepicker table tr td.active.disabled.active, +.datepicker table tr td.active.disabled:hover.active, +.datepicker table tr td.active.disabled, +.datepicker table tr td.active:hover.disabled, +.datepicker table tr td.active.disabled.disabled, +.datepicker table tr td.active.disabled:hover.disabled, +.datepicker table tr td.active[disabled], +.datepicker table tr td.active:hover[disabled], +.datepicker table tr td.active.disabled[disabled], +.datepicker table tr td.active.disabled:hover[disabled] { + background-color: #0044cc; +} +.datepicker table tr td.active:active, +.datepicker table tr td.active:hover:active, +.datepicker table tr td.active.disabled:active, +.datepicker table tr td.active.disabled:hover:active, +.datepicker table tr td.active.active, +.datepicker table tr td.active:hover.active, +.datepicker table tr td.active.disabled.active, +.datepicker table tr td.active.disabled:hover.active { + background-color: #003399 \9; +} +.datepicker table tr td span { + display: block; + width: 23%; + height: 54px; + line-height: 54px; + float: left; + margin: 1%; + cursor: pointer; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} +.datepicker table tr td span:hover { + background: #eeeeee; +} +.datepicker table tr td span.disabled, +.datepicker table tr td span.disabled:hover { + background: none; + color: #999999; + cursor: default; +} +.datepicker table tr td span.active, +.datepicker table tr td span.active:hover, +.datepicker table tr td span.active.disabled, +.datepicker table tr td span.active.disabled:hover { + background-color: #006dcc; + background-image: -moz-linear-gradient(top, #0088cc, #0044cc); + background-image: -ms-linear-gradient(top, #0088cc, #0044cc); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0044cc)); + background-image: -webkit-linear-gradient(top, #0088cc, #0044cc); + background-image: -o-linear-gradient(top, #0088cc, #0044cc); + background-image: linear-gradient(top, #0088cc, #0044cc); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#0088cc', endColorstr='#0044cc', GradientType=0); + border-color: #0044cc #0044cc #002a80; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); + color: #fff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); +} +.datepicker table tr td span.active:hover, +.datepicker table tr td span.active:hover:hover, +.datepicker table tr td span.active.disabled:hover, +.datepicker table tr td span.active.disabled:hover:hover, +.datepicker table tr td span.active:active, +.datepicker table tr td span.active:hover:active, +.datepicker table tr td span.active.disabled:active, +.datepicker table tr td span.active.disabled:hover:active, +.datepicker table tr td span.active.active, +.datepicker table tr td span.active:hover.active, +.datepicker table tr td span.active.disabled.active, +.datepicker table tr td span.active.disabled:hover.active, +.datepicker table tr td span.active.disabled, +.datepicker table tr td span.active:hover.disabled, +.datepicker table tr td span.active.disabled.disabled, +.datepicker table tr td span.active.disabled:hover.disabled, +.datepicker table tr td span.active[disabled], +.datepicker table tr td span.active:hover[disabled], +.datepicker table tr td span.active.disabled[disabled], +.datepicker table tr td span.active.disabled:hover[disabled] { + background-color: #0044cc; +} +.datepicker table tr td span.active:active, +.datepicker table tr td span.active:hover:active, +.datepicker table tr td span.active.disabled:active, +.datepicker table tr td span.active.disabled:hover:active, +.datepicker table tr td span.active.active, +.datepicker table tr td span.active:hover.active, +.datepicker table tr td span.active.disabled.active, +.datepicker table tr td span.active.disabled:hover.active { + background-color: #003399 \9; +} +.datepicker table tr td span.old, +.datepicker table tr td span.new { + color: #999999; +} +.datepicker th.datepicker-switch { + width: 145px; +} +.datepicker thead tr:first-child th, +.datepicker tfoot tr th { + cursor: pointer; +} +.datepicker thead tr:first-child th:hover, +.datepicker tfoot tr th:hover { + background: #eeeeee; +} +.datepicker .cw { + font-size: 10px; + width: 12px; + padding: 0 2px 0 5px; + vertical-align: middle; +} +.datepicker thead tr:first-child th.cw { + cursor: default; + background-color: transparent; +} +.input-append.date .add-on i, +.input-prepend.date .add-on i { + display: block; + cursor: pointer; + width: 16px; + height: 16px; +} +.input-daterange input { + text-align: center; +} +.input-daterange input:first-child { + -webkit-border-radius: 3px 0 0 3px; + -moz-border-radius: 3px 0 0 3px; + border-radius: 3px 0 0 3px; +} +.input-daterange input:last-child { + -webkit-border-radius: 0 3px 3px 0; + -moz-border-radius: 0 3px 3px 0; + border-radius: 0 3px 3px 0; +} +.input-daterange .add-on { + display: inline-block; + width: auto; + min-width: 16px; + height: 18px; + padding: 4px 5px; + font-weight: normal; + line-height: 18px; + text-align: center; + text-shadow: 0 1px 0 #ffffff; + vertical-align: middle; + background-color: #eeeeee; + border: 1px solid #ccc; + margin-left: -5px; + margin-right: -5px; +} diff --git a/templates/admin/default/assets/less/thelia/bootstrap-switch.less b/templates/admin/default/assets/less/thelia/bootstrap-switch.less new file mode 100644 index 000000000..84a297f0b --- /dev/null +++ b/templates/admin/default/assets/less/thelia/bootstrap-switch.less @@ -0,0 +1,160 @@ +.has-switch { + display: inline-block; + cursor: pointer; + border-radius: @input-border-radius; + border: 1px solid; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + position: relative; + text-align: left; + overflow: hidden; + line-height: 8px; + .user-select(none); + vertical-align: middle; + + min-width: 100px; + + &.switch-mini { + min-width: 72px; + } + &.switch-mini i.switch-mini-icons { + height: 1.20em; + line-height: 9px; + vertical-align: text-top; + text-align: center; + transform: scale(0.6); + margin-top: -1px; + margin-bottom: -1px; + } + &.switch-small { + min-width: 80px; + } + + &.switch-large { + min-width: 120px; + } + + &.deactivate { + .opacity(50); + cursor: default !important; + label, span { + cursor: default !important; + } + } + > div { + display: inline-block; + width: 150%; + position: relative; + top: 0; + + &.switch-animate { + .transition(left 0.5s); + } + &.switch-off { + left: -50%; + } + &.switch-on { + left: 0%; + } + } + input[type=radio], + input[type=checkbox] { + //debug + display: none; + //position: absolute; + //margin-left: 60%; + //z-index: 123; + } + + span, label { + .box-sizing(border-box); + + cursor: pointer; + position: relative; + display: inline-block; + height: 100%; + + padding-bottom: 4px; + padding-top: 4px; + font-size: 14px; + line-height: 20px; + + &.switch-mini { + padding-bottom: 4px; + padding-top: 4px; + font-size: 10px; + line-height: 9px; + } + + &.switch-small { + padding-bottom: 3px; + padding-top: 3px; + font-size: 12px; + line-height: 18px; + } + + &.switch-large { + padding-bottom: 9px; + padding-top: 9px; + font-size: 16px; + line-height: normal; + } + } + + label { + text-align: center; + margin-top: -1px; + margin-bottom: -1px; + z-index: 100; + width: 34%; + border-left: 1px solid @btn-default-border; + border-right: 1px solid @btn-default-border; + + .button-variant(@btn-default-color, @btn-default-bg, @btn-default-border); + + i { + color: #000; + text-shadow: 0 1px 0 #fff; + line-height: 18px; + pointer-events: none; + } + } + + span { + text-align: center; + z-index: 1; + width: 33%; + + &.switch-left { + .border-left-radius(@input-border-radius); + } + + &.switch-right { + .button-variant(@btn-default-color, @btn-default-bg, @btn-default-border); + .border-right-radius(@input-border-radius); + } + + &.switch-primary, &.switch-left { + .button-variant(@btn-primary-color, @btn-primary-bg, @btn-primary-border); + } + + &.switch-info { + .button-variant(@btn-info-color, @btn-info-bg, @btn-info-border); + } + + &.switch-success { + .button-variant(@btn-success-color, @btn-success-bg, @btn-success-border); + } + + &.switch-warning { + .button-variant(@btn-warning-color, @btn-warning-bg, @btn-warning-border); + } + + &.switch-danger { + .button-variant(@btn-danger-color, @btn-danger-bg, @btn-danger-border); + } + + &.switch-default { + .button-variant(@btn-default-color, @btn-default-bg, @btn-default-border); + } + } +} \ No newline at end of file diff --git a/templates/admin/default/assets/less/thelia/tables.less b/templates/admin/default/assets/less/thelia/tables.less index 060979487..d8b3a9e4e 100755 --- a/templates/admin/default/assets/less/thelia/tables.less +++ b/templates/admin/default/assets/less/thelia/tables.less @@ -17,9 +17,9 @@ } } - td, th { - text-align: center; - } + // td, th { + // text-align: center; + // } td.object-title, th.object-title { text-align: left; diff --git a/templates/admin/default/assets/less/thelia/thelia.less b/templates/admin/default/assets/less/thelia/thelia.less index 0d1074e5b..3883c7067 100644 --- a/templates/admin/default/assets/less/thelia/thelia.less +++ b/templates/admin/default/assets/less/thelia/thelia.less @@ -8,6 +8,8 @@ @import "modals.less"; @import "tables.less"; @import "tablesorter.less"; +@import "bootstrap-editable.less"; +@import "bootstrap-switch.less"; // -- Base styling ------------------------------------------------------------ diff --git a/templates/admin/default/categories.html b/templates/admin/default/categories.html index 8e7c4a07d..a3097f722 100755 --- a/templates/admin/default/categories.html +++ b/templates/admin/default/categories.html @@ -4,12 +4,6 @@ {block name="check-permissions"}admin.catalog.view{/block} -{block name="after-admin-css"} - {stylesheets file='assets/bootstrap-editable/css/bootstrap-editable.css' filters='cssembed'} - - {/stylesheets} -{/block} - {block name="main-content"}
@@ -80,22 +74,22 @@ } -   + {intl l='Actions'} - {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"} - #TITLE + 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} @@ -103,11 +97,15 @@ {loop type="auth" name="can_change" roles="ADMIN" permissions="admin.category.edit"} - +
+ +
{/loop} {elseloop rel="can_change"} - +
+ +
{/elseloop} @@ -123,15 +121,17 @@ - +
+ - {loop type="auth" name="can_change" roles="ADMIN" permissions="admin.category.edit"} - - {/loop} + {loop type="auth" name="can_change" roles="ADMIN" permissions="admin.category.edit"} + + {/loop} - {loop type="auth" name="can_delete" roles="ADMIN" permissions="admin.category.delete"} - - {/loop} + {loop type="auth" name="can_delete" roles="ADMIN" permissions="admin.category.delete"} + + {/loop} +
{/loop} @@ -235,7 +235,9 @@ {module_include location='product_list_row'} - +
+ +
@@ -261,7 +263,7 @@ {elseloop rel="product_list"} -
{intl l="This category doesn't have any products. To add a new product, click the + button above."}
+
{intl l="This category doesn't have any products. To add a new product, click the + button above."}
{/elseloop} @@ -278,75 +280,78 @@ {include file="includes/delete-category-dialog.html"} {/block} -{block name="after-javascript-include"} - {javascripts file='assets/bootstrap-editable/js/bootstrap-editable.js'} +{block name="javascript-initialization"} + + {javascripts file='assets/js/bootstrap-switch/bootstrap-switch.js'} {/javascripts} -{/block} -{block name="javascript-initialization"} - + {/javascripts} - {* display the form creation dialog if it contains errors *} + -{/block} + // Reload the page + location.href = url; + } + }); + + }); + +{/block} \ No newline at end of file diff --git a/templates/admin/default/currencies.html b/templates/admin/default/currencies.html index 4ed1f6ae9..29bd582a2 100644 --- a/templates/admin/default/currencies.html +++ b/templates/admin/default/currencies.html @@ -4,396 +4,386 @@ {block name="check-permissions"}admin.configuration.currencies.view{/block} -{block name="after-admin-css"} - {stylesheets file='assets/bootstrap-editable/css/bootstrap-editable.css' filters='cssembed'} - - {/stylesheets} -{/block} - {block name="main-content"} -
+
-
+
- + - {module_include location='currencies_top'} + {module_include location='currencies_top'} -
+
+
-
-
-
- - - - +
-
+
- {intl l='Currencies'} - {loop type="auth" name="can_create" roles="ADMIN" permissions="admin.configuration.currencies.create"} - - - - - {/loop} + -
- {admin_sortable_header - current_order=$order - order='id' - reverse_order='id_reverse' - path='/admin/configuration/currencies' - label="{intl l='ID'}" - } - - {admin_sortable_header - current_order=$order - order='alpha' - reverse_order='alpha_reverse' - path='/admin/configuration/currencies' - label="{intl l='Name'}" - } -
+ + + + - + - + - + - + - + - {module_include location='currencies_table_header'} + - - + {module_include location='currencies_table_header'} - {loop name="currencies" type="currency" backend_context="1" lang=$lang_id order=$order} - - + + + - + + {loop name="currencies" type="currency" backend_context="1" lang=$lang_id order=$order} + + - + - + - + - + - + - {module_include location='currencies_table_row'} + - - - {/loop} + - - - {/elseloop} -
+ {intl l='Currencies'} + {loop type="auth" name="can_create" roles="ADMIN" permissions="admin.configuration.currencies.create"} + + + + + + + {/loop} +
+ {admin_sortable_header + current_order=$order + order='id' + reverse_order='id_reverse' + path='/admin/configuration/currencies' + label="{intl l='ID'}" + } + - {admin_sortable_header - current_order=$order - order='code' - reverse_order='code_reverse' - path='/admin/configuration/currencies' - label="{intl l="ISO 4217 Code"}" - } - - + {admin_sortable_header + current_order=$order + order='name' + reverse_order='name_reverse' + path='/admin/configuration/currencies' + label="{intl l='Name'}" + } + - {admin_sortable_header - current_order=$order - order='symbol' - reverse_order='symbol_reverse' - path='/admin/configuration/currencies' - label="{intl l="Symbol"}" - } - + {admin_sortable_header + current_order=$order + order='code' + reverse_order='code_reverse' + path='/admin/configuration/currencies' + label="{intl l="ISO 4217 Code"}" + } + + - {admin_sortable_header - current_order=$order - order='rate' - reverse_order='rate_reverse' - path='/admin/configuration/currencies' - label="{intl l="Rate in €"}" - } - + {admin_sortable_header + current_order=$order + order='symbol' + reverse_order='symbol_reverse' + path='/admin/configuration/currencies' + label="{intl l="Symbol"}" + } + - {admin_sortable_header - current_order=$order - order='manual' - reverse_order='manual_reverse' - path='/admin/configuration/currencies' - label="{intl l="Position"}" - } - + {admin_sortable_header + current_order=$order + order='rate' + reverse_order='rate_reverse' + path='/admin/configuration/currencies' + label="{intl l="Rate in €"}" + } + - {admin_sortable_header - current_order=$order - order='is_default' - reverse_order='is_default_reverse' - path='/admin/configuration/currencies' - label="{intl l="Default"}" - } - + {admin_sortable_header + current_order=$order + order='manual' + reverse_order='manual_reverse' + path='/admin/configuration/currencies' + label="{intl l="Position"}" + } + + {admin_sortable_header + current_order=$order + order='is_default' + reverse_order='is_default_reverse' + path='/admin/configuration/currencies' + label="{intl l="Default"}" + } +  
{$ID}{intl l='Actions'}
- {loop type="auth" name="can_change" roles="ADMIN" permissions="admin.configuration.currencies.change"} - {$NAME} - {/loop} - {elseloop rel="can_change"} - {$NAME} - {/elseloop} -
{$ID}{$ISOCODE} + {loop type="auth" name="can_change" roles="ADMIN" permissions="admin.configuration.currencies.change"} + {$NAME} + {/loop} + {elseloop rel="can_change"} + {$NAME} + {/elseloop} + {$SYMBOL}{$ISOCODE}{format_number number="$RATE" decimals="4"}{$SYMBOL} - {admin_position_block - permission="admin.currencies.edit" - path="/admin/configuration/currencies" - url_parameter="currency_id" - in_place_edit_class="currencyPositionChange" - position="$POSITION" - id="$ID" - } - {format_number number="$RATE" decimals="4"} - - + {admin_position_block + permission="admin.currencies.edit" + path="/admin/configuration/currencies/update-position" + url_parameter="currency_id" + in_place_edit_class="currencyPositionChange" + position="$POSITION" + id="$ID" + } + +
+ +
+
-
- {loop type="auth" name="can_change" roles="ADMIN" permissions="admin.configuration.currencies.change"} - - {/loop} + {module_include location='currencies_table_row'} - {loop type="auth" name="can_delete" roles="ADMIN" permissions="admin.configuration.currencies.delete"} - - {/loop} -
-
+
+ {loop type="auth" name="can_change" roles="ADMIN" permissions="admin.configuration.currencies.change"} + + + + {/loop} - {elseloop rel="currencies"} -
-
- {intl l="No currency has been created yet. Click the + button to create one."} -
-
-
-
-
-
+ {loop type="auth" name="can_delete" roles="ADMIN" permissions="admin.configuration.currencies.delete"} + + + + {/loop} +
+ + + {/loop} + {elseloop rel="currencies"} + + +
+ {intl l="No currency has been created yet. Click the + button to create one."} +
+ + + {/elseloop} + + - {module_include location='currencies_bottom'} - -
-
- - -{* Adding a new currency *} - -