diff --git a/core/lib/Thelia/Controller/Admin/CategoryController.php b/core/lib/Thelia/Controller/Admin/CategoryController.php index 15f620736..83d90c86e 100755 --- a/core/lib/Thelia/Controller/Admin/CategoryController.php +++ b/core/lib/Thelia/Controller/Admin/CategoryController.php @@ -126,7 +126,7 @@ class CategoryController extends AbstractCrudController 'description' => $object->getDescription(), 'postscriptum' => $object->getPostscriptum(), 'visible' => $object->getVisible(), - 'url' => $object->getUrl($this->getCurrentEditionLocale()), + 'url' => $object->getRewritenUrl($this->getCurrentEditionLocale()), 'parent' => $object->getParent() ); diff --git a/core/lib/Thelia/Core/Template/Loop/Category.php b/core/lib/Thelia/Core/Template/Loop/Category.php index 2b1156b98..bd1c32de2 100755 --- a/core/lib/Thelia/Core/Template/Loop/Category.php +++ b/core/lib/Thelia/Core/Template/Loop/Category.php @@ -173,6 +173,22 @@ class Category extends BaseI18nLoop $loopResult = new LoopResult($categories); foreach ($categories as $category) { + + // Find previous and next category + $previous = CategoryQuery::create() + ->filterByParent($category->getParent()) + ->filterByPosition($category->getPosition(), Criteria::LESS_THAN) + ->orderByPosition(Criteria::DESC) + ->findOne() + ; + + $next = CategoryQuery::create() + ->filterByParent($category->getParent()) + ->filterByPosition($category->getPosition(), Criteria::GREATER_THAN) + ->orderByPosition(Criteria::ASC) + ->findOne() + ; + /* * no cause pagination lost : * if ($this->getNotEmpty() && $category->countAllProducts() == 0) continue; @@ -193,7 +209,13 @@ class Category extends BaseI18nLoop ->set("PRODUCT_COUNT", $category->countChild()) ->set("VISIBLE", $category->getVisible() ? "1" : "0") ->set("POSITION", $category->getPosition()) - ; + + ->set("HAS_PREVIOUS", $previous != null ? 1 : 0) + ->set("HAS_NEXT" , $next != null ? 1 : 0) + + ->set("PREVIOUS", $previous != null ? $previous->getId() : -1) + ->set("NEXT" , $next != null ? $next->getId() : -1) + ; $loopResult->addRow($loopResultRow); } diff --git a/core/lib/Thelia/Model/Category.php b/core/lib/Thelia/Model/Category.php index 5533eaf42..042864de0 100755 --- a/core/lib/Thelia/Model/Category.php +++ b/core/lib/Thelia/Model/Category.php @@ -15,6 +15,8 @@ class Category extends BaseCategory use \Thelia\Model\Tools\PositionManagementTrait; + use \Thelia\Model\Tools\UrlRewritingTrait; + /** * @return int number of child for the current category */ @@ -23,9 +25,11 @@ class Category extends BaseCategory return CategoryQuery::countChild($this->getId()); } - public function getUrl($locale) - { - return URL::getInstance()->retrieve('category', $this->getId(), $locale)->toString(); + /** + * {@inheritDoc} + */ + protected function getRewritenUrlViewName() { + return 'category'; } /** @@ -51,20 +55,38 @@ class Category extends BaseCategory return $countProduct; } + /** + * Calculate next position relative to our parent + */ + protected function addCriteriaToPositionQuery($query) { + $query->filterByParent($this->getParent()); + } + + /** + * {@inheritDoc} + */ public function preInsert(ConnectionInterface $con = null) { - $this->setPosition($this->getNextPosition($this->parent)); + $this->setPosition($this->getNextPosition()); + + $this->generateRewritenUrl($this->getLocale()); $this->dispatchEvent(TheliaEvents::BEFORE_CREATECATEGORY, new CategoryEvent($this)); return true; } + /** + * {@inheritDoc} + */ public function postInsert(ConnectionInterface $con = null) { $this->dispatchEvent(TheliaEvents::AFTER_CREATECATEGORY, new CategoryEvent($this)); } + /** + * {@inheritDoc} + */ public function preUpdate(ConnectionInterface $con = null) { $this->dispatchEvent(TheliaEvents::BEFORE_UPDATECATEGORY, new CategoryEvent($this)); @@ -72,11 +94,17 @@ class Category extends BaseCategory return true; } + /** + * {@inheritDoc} + */ public function postUpdate(ConnectionInterface $con = null) { $this->dispatchEvent(TheliaEvents::AFTER_UPDATECATEGORY, new CategoryEvent($this)); } + /** + * {@inheritDoc} + */ public function preDelete(ConnectionInterface $con = null) { $this->dispatchEvent(TheliaEvents::BEFORE_DELETECATEGORY, new CategoryEvent($this)); @@ -84,6 +112,9 @@ class Category extends BaseCategory return true; } + /** + * {@inheritDoc} + */ public function postDelete(ConnectionInterface $con = null) { $this->dispatchEvent(TheliaEvents::AFTER_DELETECATEGORY, new CategoryEvent($this)); diff --git a/core/lib/Thelia/Model/Content.php b/core/lib/Thelia/Model/Content.php index 5bc0ba6b2..79660f93a 100755 --- a/core/lib/Thelia/Model/Content.php +++ b/core/lib/Thelia/Model/Content.php @@ -4,11 +4,41 @@ namespace Thelia\Model; use Thelia\Model\Base\Content as BaseContent; use Thelia\Tools\URL; +use Propel\Runtime\Connection\ConnectionInterface; class Content extends BaseContent { - public function getUrl($locale) + use \Thelia\Model\Tools\ModelEventDispatcherTrait; + + use \Thelia\Model\Tools\PositionManagementTrait; + + use \Thelia\Model\Tools\UrlRewritingTrait; + + /** + * {@inheritDoc} + */ + protected function getRewritenUrlViewName() { + return 'content'; + } + + /** + * Calculate next position relative to our parent + */ + protected function addCriteriaToPositionQuery($query) { + + // TODO: Find the default folder for this content, + // and generate the position relative to this folder + } + + /** + * {@inheritDoc} + */ + public function preInsert(ConnectionInterface $con = null) { - return URL::getInstance()->retrieve('content', $this->getId(), $locale)->toString(); + $this->setPosition($this->getNextPosition()); + + $this->generateRewritenUrl($this->getLocale()); + + return true; } } diff --git a/core/lib/Thelia/Model/Folder.php b/core/lib/Thelia/Model/Folder.php index 8151dae0e..31c8a1e3c 100755 --- a/core/lib/Thelia/Model/Folder.php +++ b/core/lib/Thelia/Model/Folder.php @@ -7,6 +7,19 @@ use Thelia\Tools\URL; class Folder extends BaseFolder { + use \Thelia\Model\Tools\ModelEventDispatcherTrait; + + use \Thelia\Model\Tools\PositionManagementTrait; + + use \Thelia\Model\Tools\UrlRewritingTrait; + + /** + * {@inheritDoc} + */ + protected function getRewritenUrlViewName() { + return 'folder'; + } + /** * @return int number of contents for the folder */ @@ -15,11 +28,6 @@ class Folder extends BaseFolder return FolderQuery::countChild($this->getId()); } - public function getUrl($locale) - { - return URL::getInstance()->retrieve('folder', $this->getId(), $locale)->toString(); - } - /** * * count all products for current category and sub categories @@ -43,4 +51,23 @@ class Folder extends BaseFolder return $contentsCount; } + + /** + * Calculate next position relative to our parent + */ + protected function addCriteriaToPositionQuery($query) { + $query->filterByParent($this->getParent()); + } + + /** + * {@inheritDoc} + */ + public function preInsert(ConnectionInterface $con = null) + { + $this->setPosition($this->getNextPosition()); + + $this->generateRewritenUrl($this->getLocale()); + + return true; + } } diff --git a/core/lib/Thelia/Model/Product.php b/core/lib/Thelia/Model/Product.php index 06c4640d0..014d534d6 100755 --- a/core/lib/Thelia/Model/Product.php +++ b/core/lib/Thelia/Model/Product.php @@ -9,16 +9,25 @@ use Thelia\TaxEngine\Calculator; class Product extends BaseProduct { - public function getUrl($locale) - { - return URL::getInstance()->retrieve('product', $this->getId(), $locale)->toString(); + use \Thelia\Model\Tools\ModelEventDispatcherTrait; + + use \Thelia\Model\Tools\PositionManagementTrait; + + use \Thelia\Model\Tools\UrlRewritingTrait; + + /** + * {@inheritDoc} + */ + protected function getRewritenUrlViewName() { + return 'product'; } public function getRealLowestPrice($virtualColumnName = 'real_lowest_price') { try { $amount = $this->getVirtualColumn($virtualColumnName); - } catch(PropelException $e) { + } + catch(PropelException $e) { throw new PropelException("Virtual column `$virtualColumnName` does not exist in Product::getRealLowestPrice"); } @@ -30,4 +39,26 @@ class Product extends BaseProduct $taxCalculator = new Calculator(); return $taxCalculator->load($this, $country)->getTaxedPrice($this->getRealLowestPrice()); } + + /** + * Calculate next position relative to our default category + */ + protected function addCriteriaToPositionQuery($query) { + + // TODO: Find the default category for this product, + // and generate the position relative to this category + + } + + /** + * {@inheritDoc} + */ + public function preInsert(ConnectionInterface $con = null) + { + $this->setPosition($this->getNextPosition()); + + $this->generateRewritenUrl($this->getLocale()); + + return true; + } } diff --git a/core/lib/Thelia/Model/Tools/UrlRewritingTrait.php b/core/lib/Thelia/Model/Tools/UrlRewritingTrait.php new file mode 100644 index 000000000..6ab24fbc4 --- /dev/null +++ b/core/lib/Thelia/Model/Tools/UrlRewritingTrait.php @@ -0,0 +1,78 @@ +. */ +/* */ +/*************************************************************************************/ + +namespace Thelia\Model\Tools; + +use Thelia\Tools\URL; +/** + * A trait for managing Rewriten URLs from model classes + */ +trait UrlRewritingTrait { + + /** + * @returns string the view name of the rewriten object (e.g., 'category', 'product') + */ + protected abstract function getRewritenUrlViewName(); + + /** + * Get the object URL for the given locale, rewriten if rewriting is enabled. + * + * @param string $locale a valid locale (e.g. en_US) + */ + public function getUrl($locale) + { + return URL::getInstance()->retrieve($this->getRewritenUrlViewName(), $this->getId(), $locale)->toString(); + } + + /** + * Generate a rewriten URL from the object title, and store it in the rewriting table + * + * @param string $locale a valid locale (e.g. en_US) + */ + public function generateRewritenUrl($locale) + { + URL::getInstance()->generateRewritenUrl($this->getRewritenUrlViewName(), $this->getId(), $locale, $this->getTitle()); + } + + /** + * return the rewriten URL for the given locale + * + * @param string $locale a valid locale (e.g. en_US) + */ + public function getRewritenUrl($locale) + { + return "fake url - TODO"; + } + + /** + * Set the rewriten URL for the given locale + * + * @param string $locale a valid locale (e.g. en_US) + */ + public function setRewritenUrl($locale, $url) + { + // TODO - code me ! + + return $this; + } +} \ No newline at end of file diff --git a/core/lib/Thelia/Tools/URL.php b/core/lib/Thelia/Tools/URL.php index 32c1aadb5..495250f99 100755 --- a/core/lib/Thelia/Tools/URL.php +++ b/core/lib/Thelia/Tools/URL.php @@ -183,6 +183,7 @@ class URL return $this->absoluteUrl($path, $parameters); } + /** * Retrieve a rewritten URL from a view, a view id and a locale * @@ -261,4 +262,50 @@ class URL return $this->resolver; } + + protected function sanitize($string, $force_lowercase = true, $alphabetic_only = false) + { + static $strip = array("~", "`", "!", "@", "#", "$", "%", "^", "&", "*", "(", ")", "_", "=", "+", "[", "{", "]", + "}", "\\", "|", ";", ":", "\"", "'", "‘", "’", "“", "”", "–", "—", + "—", "–", ",", "<", ".", ">", "/", "?"); + + $clean = trim(str_replace($strip, "", strip_tags($string))); + + $clean = preg_replace('/\s+/', "-", $clean); + + $clean = ($alphabetic_only) ? preg_replace("/[^a-zA-Z0-9]/", "", $clean) : $clean ; + + return ($force_lowercase) ? + (function_exists('mb_strtolower')) ? + mb_strtolower($clean, 'UTF-8') : + strtolower($clean) : + $clean; + } + + /** + * Genenerate the file part of a rewriten URL from a given baseString, using a view, a view id and a locale + * + * @param $view + * @param $viewId + * @param $viewLocale + * @param $baseString the string to be converted in a valid URL + * + * @return A valid file part URL. + */ + public function generateRewritenUrl($view, $viewId, $viewLocale, $baseString) + { + // Borrowed from http://stackoverflow.com/questions/2668854/sanitizing-strings-to-make-them-url-and-filename-safe + + // Replace all weird characters with dashes + $string = preg_replace('/[^\w\-~_\.]+/u', '-', $baseString); + + // Only allow one dash separator at a time (and make string lowercase) + $cleanString = mb_strtolower(preg_replace('/--+/u', '-', $string), 'UTF-8'); + + $urlFilePart = $cleanString . ".html"; + + // TODO : + // check if URL url already exists, and add a numeric suffix, or the like + // insert the URL in the rewriting table + } } diff --git a/templates/admin/default/category-edit.html b/templates/admin/default/category-edit.html index 9fa9e18e4..626114d2a 100755 --- a/templates/admin/default/category-edit.html +++ b/templates/admin/default/category-edit.html @@ -8,20 +8,31 @@
- {include file="includes/catalog-breadcrumb.html"} + {include file="includes/catalog-breadcrumb.html" editing_category="true"}
{loop name="category_edit" type="category" visible="*" id="{$category_id}" backend_context="1" lang="$edit_language_id"}
- {intl l='Edit category'} + {intl l='Edit category %title' title=$TITLE}
- - - + + {if $HAS_PREVIOUS != 0} + + {else} + + {/if} + + + + {if $HAS_NEXT != 0} + + {else} + + {/if}
diff --git a/templates/admin/default/includes/catalog-breadcrumb.html b/templates/admin/default/includes/catalog-breadcrumb.html index 52f40223e..8bbe69088 100644 --- a/templates/admin/default/includes/catalog-breadcrumb.html +++ b/templates/admin/default/includes/catalog-breadcrumb.html @@ -8,14 +8,14 @@ {loop name="category_path" type="category-path" visible="*" category=$category_id} {if $ID == $category_id}
  • - {if $action == 'edit'} - {intl l='Editing %cat' cat="{$TITLE}"} + {if $editing_category == true} + {intl l='Editing %cat' cat="{$TITLE}"} {else} - {$TITLE} {intl l="(edit)"} + {$TITLE} {intl l="(edit)"} {/if}
  • {else} -
  • {$TITLE}
  • +
  • {$TITLE}
  • {/if} {/loop} {/ifloop}