diff --git a/core/lib/Thelia/Action/Product.php b/core/lib/Thelia/Action/Product.php index d2deb7688..67aa5e431 100644 --- a/core/lib/Thelia/Action/Product.php +++ b/core/lib/Thelia/Action/Product.php @@ -40,6 +40,7 @@ use Thelia\Core\Event\ProductAddContentEvent; use Thelia\Core\Event\ProductDeleteContentEvent; use Thelia\Model\ProductAssociatedContent; use Thelia\Model\ProductAssociatedContentQuery; +use Thelia\Model\ProductCategory; class Product extends BaseAction implements EventSubscriberInterface { @@ -55,13 +56,13 @@ class Product extends BaseAction implements EventSubscriberInterface $product ->setDispatcher($this->getDispatcher()) - ->setLocale($event->getLocale()) + ->setRef($event->getRef()) ->setTitle($event->getTitle()) - ->setParent($event->getParent()) + ->setLocale($event->getLocale()) ->setVisible($event->getVisible()) - ->save() - ; + ->create($event->getDefaultCategory()) + ; $event->setProduct($product); } diff --git a/core/lib/Thelia/Controller/Admin/CategoryController.php b/core/lib/Thelia/Controller/Admin/CategoryController.php index 8c74c31ec..4d0d15ef1 100755 --- a/core/lib/Thelia/Controller/Admin/CategoryController.php +++ b/core/lib/Thelia/Controller/Admin/CategoryController.php @@ -133,7 +133,7 @@ class CategoryController extends AbstractCrudController 'description' => $object->getDescription(), 'postscriptum' => $object->getPostscriptum(), 'visible' => $object->getVisible(), - 'url' => $object->getRewritenUrl($this->getCurrentEditionLocale()), + 'url' => $object->getRewrittenUrl($this->getCurrentEditionLocale()), 'parent' => $object->getParent() ); diff --git a/core/lib/Thelia/Controller/Admin/ProductController.php b/core/lib/Thelia/Controller/Admin/ProductController.php index e1b651e60..ae58ca908 100644 --- a/core/lib/Thelia/Controller/Admin/ProductController.php +++ b/core/lib/Thelia/Controller/Admin/ProductController.php @@ -83,9 +83,10 @@ class ProductController extends AbstractCrudController $createEvent = new ProductCreateEvent(); $createEvent + ->setRef($formData['ref']) ->setTitle($formData['title']) - ->setLocale($formData["locale"]) - ->setParent($formData['parent']) + ->setLocale($formData['locale']) + ->setDefaultCategory($formData['default_category']) ->setVisible($formData['visible']) ; @@ -134,15 +135,15 @@ class ProductController extends AbstractCrudController { // Prepare the data that will hydrate the form $data = array( - 'id' => $object->getId(), - 'locale' => $object->getLocale(), - 'title' => $object->getTitle(), - 'chapo' => $object->getChapo(), - 'description' => $object->getDescription(), - 'postscriptum' => $object->getPostscriptum(), - 'visible' => $object->getVisible(), - 'url' => $object->getRewritenUrl($this->getCurrentEditionLocale()), - 'parent' => $object->getParent() + 'id' => $object->getId(), + 'locale' => $object->getLocale(), + 'title' => $object->getTitle(), + 'chapo' => $object->getChapo(), + 'description' => $object->getDescription(), + 'postscriptum' => $object->getPostscriptum(), + 'visible' => $object->getVisible(), + 'url' => $object->getRewrittenUrl($this->getCurrentEditionLocale()), + 'default_category' => $object->getDefaultCategoryId() ); // Setup the object form @@ -174,12 +175,26 @@ class ProductController extends AbstractCrudController protected function getEditionArguments() { return array( - 'product_id' => $this->getRequest()->get('product_id', 0), - 'folder_id' => $this->getRequest()->get('folder_id', 0), + 'category_id' => $this->getCategoryId(), + 'product_id' => $this->getRequest()->get('product_id', 0), + 'folder_id' => $this->getRequest()->get('folder_id', 0), 'current_tab' => $this->getRequest()->get('current_tab', 'general') ); } + protected function getCategoryId() { + // Trouver le category_id, soit depuis la reques, souit depuis le produit courant + $category_id = $this->getRequest()->get('category_id', null); + + if ($category_id == null) { + $product = $this->getExistingObject(); + + if ($product !== null) $category_id = $product->getDefaultCategoryId(); + } + + return $category_id != null ? $category_id : 0; + } + protected function renderListTemplate($currentOrder) { $this->getListOrderFromSession('product', 'product_order', 'manual'); @@ -187,18 +202,15 @@ class ProductController extends AbstractCrudController return $this->render('categories', array( 'product_order' => $currentOrder, - 'product_id' => $this->getRequest()->get('product_id', 0) + 'category_id' => $this->getCategoryId() )); } protected function redirectToListTemplate() { - // Redirect to the product default category list - $product = $this->getExistingObject(); - $this->redirectToRoute( 'admin.products.default', - array('category_id' => $product != null ? $product->getDefaultCategory() : 0) + array('category_id' => $this->getCategoryId()) ); } @@ -238,7 +250,7 @@ class ProductController extends AbstractCrudController // Redirect to parent product list $this->redirectToRoute( 'admin.products.default', - array('category_id' => $deleteEvent->getProduct()->getDefaultCategory()) + array('category_id' => $deleteEvent->getProduct()->getDefaultCategoryId()) ); } @@ -249,24 +261,18 @@ class ProductController extends AbstractCrudController // Redirect to parent product list $this->redirectToRoute( 'admin.categories.default', - array('category_id' => $product->getDefaultCategory()) + array('category_id' => $updateEvent->getProduct()->getDefaultCategoryId()) ); } } - protected function performAdditionalUpdatePositionAction($event) + protected function performAdditionalUpdatePositionAction($positionEvent) { - $product = ProductQuery::create()->findPk($event->getObjectId()); - - if ($product != null) { - // Redirect to parent product list - $this->redirectToRoute( - 'admin.categories.default', - array('category_id' => $product->getDefaultCategory()) - ); - } - - return null; + // Redirect to parent product list + $this->redirectToRoute( + 'admin.categories.default', + array('category_id' => $positionEvent->getProduct()->getDefaultCategoryId()) + ); } public function getAvailableRelatedContentAction($productId, $folderId) diff --git a/core/lib/Thelia/Core/Event/ProductCreateEvent.php b/core/lib/Thelia/Core/Event/ProductCreateEvent.php index 51be86e25..d2d30a11a 100644 --- a/core/lib/Thelia/Core/Event/ProductCreateEvent.php +++ b/core/lib/Thelia/Core/Event/ProductCreateEvent.php @@ -25,11 +25,23 @@ namespace Thelia\Core\Event; class ProductCreateEvent extends ProductEvent { + protected $ref; protected $title; - protected $parent; protected $locale; + protected $default_category; protected $visible; + public function getRef() + { + return $this->ref; + } + + public function setRef($ref) + { + $this->ref = $ref; + return $this; + } + public function getTitle() { return $this->title; @@ -38,19 +50,6 @@ class ProductCreateEvent extends ProductEvent public function setTitle($title) { $this->title = $title; - - return $this; - } - - public function getParent() - { - return $this->parent; - } - - public function setParent($parent) - { - $this->parent = $parent; - return $this; } @@ -62,7 +61,17 @@ class ProductCreateEvent extends ProductEvent public function setLocale($locale) { $this->locale = $locale; + return $this; + } + public function getDefaultCategory() + { + return $this->default_category; + } + + public function setDefaultCategory($default_category) + { + $this->default_category = $default_category; return $this; } @@ -74,7 +83,6 @@ class ProductCreateEvent extends ProductEvent public function setVisible($visible) { $this->visible = $visible; - return $this; } } diff --git a/core/lib/Thelia/Core/Template/Loop/Product.php b/core/lib/Thelia/Core/Template/Loop/Product.php index 59f83f8fc..4a401d464 100755 --- a/core/lib/Thelia/Core/Template/Loop/Product.php +++ b/core/lib/Thelia/Core/Template/Loop/Product.php @@ -598,31 +598,59 @@ class Product extends BaseI18nLoop $loopResult = new LoopResult($products); foreach ($products as $product) { + $loopResultRow = new LoopResultRow($loopResult, $product, $this->versionable, $this->timestampable, $this->countable); $price = $product->getRealLowestPrice(); + $taxedPrice = $product->getTaxedPrice( CountryQuery::create()->findOneById(64) // @TODO : make it magic ); + // Find previous and next product, in the default category. + $default_category_id = $product->getDefaultCategoryId(); - $loopResultRow->set("ID", $product->getId()) - ->set("REF",$product->getRef()) - ->set("IS_TRANSLATED",$product->getVirtualColumn('IS_TRANSLATED')) - ->set("LOCALE",$locale) - ->set("TITLE",$product->getVirtualColumn('i18n_TITLE')) - ->set("CHAPO", $product->getVirtualColumn('i18n_CHAPO')) - ->set("DESCRIPTION", $product->getVirtualColumn('i18n_DESCRIPTION')) - ->set("POSTSCRIPTUM", $product->getVirtualColumn('i18n_POSTSCRIPTUM')) - ->set("URL", $product->getUrl($locale)) - ->set("BEST_PRICE", $price) - ->set("BEST_PRICE_TAX", $taxedPrice - $price) - ->set("BEST_TAXED_PRICE", $taxedPrice) - ->set("IS_PROMO", $product->getVirtualColumn('main_product_is_promo')) - ->set("IS_NEW", $product->getVirtualColumn('main_product_is_new')) - ->set("POSITION", $product->getPosition()) + $previous = ProductQuery::create() + ->joinProductCategory() + ->where('ProductCategory.category_id = ?', $default_category_id) + ->filterByPosition($product->getPosition(), Criteria::LESS_THAN) + ->orderByPosition(Criteria::DESC) + ->findOne() ; + $next = ProductQuery::create() + ->joinProductCategory() + ->where('ProductCategory.category_id = ?', $default_category_id) + ->filterByPosition($product->getPosition(), Criteria::GREATER_THAN) + ->orderByPosition(Criteria::ASC) + ->findOne() + ; + + $loopResultRow + ->set("ID" , $product->getId()) + ->set("REF" , $product->getRef()) + ->set("IS_TRANSLATED" , $product->getVirtualColumn('IS_TRANSLATED')) + ->set("LOCALE" , $locale) + ->set("TITLE" , $product->getVirtualColumn('i18n_TITLE')) + ->set("CHAPO" , $product->getVirtualColumn('i18n_CHAPO')) + ->set("DESCRIPTION" , $product->getVirtualColumn('i18n_DESCRIPTION')) + ->set("POSTSCRIPTUM" , $product->getVirtualColumn('i18n_POSTSCRIPTUM')) + ->set("URL" , $product->getUrl($locale)) + ->set("BEST_PRICE" , $price) + ->set("BEST_PRICE_TAX" , $taxedPrice - $price) + ->set("BEST_TAXED_PRICE" , $taxedPrice) + ->set("IS_PROMO" , $product->getVirtualColumn('main_product_is_promo')) + ->set("IS_NEW" , $product->getVirtualColumn('main_product_is_new')) + ->set("POSITION" , $product->getPosition()) + ->set("VISIBLE" , $product->getVisible() ? "1" : "0") + ->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) + ->set("DEFAULT_CATEGORY" , $default_category_id) + + ; + $loopResult->addRow($loopResultRow); } diff --git a/core/lib/Thelia/Form/ProductCreationForm.php b/core/lib/Thelia/Form/ProductCreationForm.php index 49ab76fb7..a4ffdde32 100644 --- a/core/lib/Thelia/Form/ProductCreationForm.php +++ b/core/lib/Thelia/Form/ProductCreationForm.php @@ -23,16 +23,26 @@ namespace Thelia\Form; use Symfony\Component\Validator\Constraints\NotBlank; +use Thelia\Core\Translation\Translator; +use Thelia\Model\ProductQuery; +use Symfony\Component\Validator\Constraints\Callback; +use Symfony\Component\Validator\ExecutionContextInterface; class ProductCreationForm extends BaseForm { - protected function buildForm() + protected function buildForm($change_mode = false) { + $ref_constraints = array(new NotBlank()); + + if (! $change_mode) { + $ref_constraints[] = new Callback(array( + "methods" => array(array($this, "checkDuplicateRef")) + )); + } + $this->formBuilder ->add("ref", "text", array( - "constraints" => array( - new NotBlank() - ), + "constraints" => $ref_constraints, "label" => "Product reference *", "label_attr" => array( "for" => "ref" @@ -64,6 +74,19 @@ class ProductCreationForm extends BaseForm ; } + public function checkDuplicateRef($value, ExecutionContextInterface $context) + { + $count = ProductQuery::create()->filterByRef($value)->count(); + + if ($count > 0) { + $context->addViolation( + Translator::getInstance()->trans( + "A product with reference %ref already exists. Please choose another reference.", + array('%ref' => $value) + )); + } + } + public function getName() { return "thelia_product_creation"; diff --git a/core/lib/Thelia/Model/Base/ProductQuery.php b/core/lib/Thelia/Model/Base/ProductQuery.php index 9b1710f03..9c3b0759c 100644 --- a/core/lib/Thelia/Model/Base/ProductQuery.php +++ b/core/lib/Thelia/Model/Base/ProductQuery.php @@ -857,7 +857,7 @@ abstract class ProductQuery extends ModelCriteria * * @return ChildProductQuery The current query, for fluid interface */ - public function joinTemplate($relationAlias = null, $joinType = Criteria::INNER_JOIN) + public function joinTemplate($relationAlias = null, $joinType = Criteria::LEFT_JOIN) { $tableMap = $this->getTableMap(); $relationMap = $tableMap->getRelation('Template'); @@ -892,7 +892,7 @@ abstract class ProductQuery extends ModelCriteria * * @return \Thelia\Model\TemplateQuery A secondary query class using the current class as primary query */ - public function useTemplateQuery($relationAlias = null, $joinType = Criteria::INNER_JOIN) + public function useTemplateQuery($relationAlias = null, $joinType = Criteria::LEFT_JOIN) { return $this ->joinTemplate($relationAlias, $joinType) diff --git a/core/lib/Thelia/Model/Base/Tax.php b/core/lib/Thelia/Model/Base/Tax.php index da674b5cf..636ee3558 100644 --- a/core/lib/Thelia/Model/Base/Tax.php +++ b/core/lib/Thelia/Model/Base/Tax.php @@ -77,6 +77,13 @@ abstract class Tax implements ActiveRecordInterface */ protected $serialized_requirements; + /** + * The value for the is_default field. + * Note: this column has a database default value of: false + * @var boolean + */ + protected $is_default; + /** * The value for the created_at field. * @var string @@ -135,11 +142,24 @@ abstract class Tax implements ActiveRecordInterface */ protected $taxI18nsScheduledForDeletion = null; + /** + * Applies default values to this object. + * This method should be called from the object's constructor (or + * equivalent initialization method). + * @see __construct() + */ + public function applyDefaultValues() + { + $this->is_default = false; + } + /** * Initializes internal state of Thelia\Model\Base\Tax object. + * @see applyDefaults() */ public function __construct() { + $this->applyDefaultValues(); } /** @@ -422,6 +442,17 @@ abstract class Tax implements ActiveRecordInterface return $this->serialized_requirements; } + /** + * Get the [is_default] column value. + * + * @return boolean + */ + public function getIsDefault() + { + + return $this->is_default; + } + /** * Get the [optionally formatted] temporal [created_at] column value. * @@ -525,6 +556,35 @@ abstract class Tax implements ActiveRecordInterface return $this; } // setSerializedRequirements() + /** + * Sets the value of the [is_default] column. + * Non-boolean arguments are converted using the following rules: + * * 1, '1', 'true', 'on', and 'yes' are converted to boolean true + * * 0, '0', 'false', 'off', and 'no' are converted to boolean false + * Check on string values is case insensitive (so 'FaLsE' is seen as 'false'). + * + * @param boolean|integer|string $v The new value + * @return \Thelia\Model\Tax The current object (for fluent API support) + */ + public function setIsDefault($v) + { + if ($v !== null) { + if (is_string($v)) { + $v = in_array(strtolower($v), array('false', 'off', '-', 'no', 'n', '0', '')) ? false : true; + } else { + $v = (boolean) $v; + } + } + + if ($this->is_default !== $v) { + $this->is_default = $v; + $this->modifiedColumns[] = TaxTableMap::IS_DEFAULT; + } + + + return $this; + } // setIsDefault() + /** * Sets the value of [created_at] column to a normalized version of the date/time value specified. * @@ -577,6 +637,10 @@ abstract class Tax implements ActiveRecordInterface */ public function hasOnlyDefaultValues() { + if ($this->is_default !== false) { + return false; + } + // otherwise, everything was equal, so return TRUE return true; } // hasOnlyDefaultValues() @@ -613,13 +677,16 @@ abstract class Tax implements ActiveRecordInterface $col = $row[TableMap::TYPE_NUM == $indexType ? 2 + $startcol : TaxTableMap::translateFieldName('SerializedRequirements', TableMap::TYPE_PHPNAME, $indexType)]; $this->serialized_requirements = (null !== $col) ? (string) $col : null; - $col = $row[TableMap::TYPE_NUM == $indexType ? 3 + $startcol : TaxTableMap::translateFieldName('CreatedAt', TableMap::TYPE_PHPNAME, $indexType)]; + $col = $row[TableMap::TYPE_NUM == $indexType ? 3 + $startcol : TaxTableMap::translateFieldName('IsDefault', TableMap::TYPE_PHPNAME, $indexType)]; + $this->is_default = (null !== $col) ? (boolean) $col : null; + + $col = $row[TableMap::TYPE_NUM == $indexType ? 4 + $startcol : TaxTableMap::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 ? 4 + $startcol : TaxTableMap::translateFieldName('UpdatedAt', TableMap::TYPE_PHPNAME, $indexType)]; + $col = $row[TableMap::TYPE_NUM == $indexType ? 5 + $startcol : TaxTableMap::translateFieldName('UpdatedAt', TableMap::TYPE_PHPNAME, $indexType)]; if ($col === '0000-00-00 00:00:00') { $col = null; } @@ -632,7 +699,7 @@ abstract class Tax implements ActiveRecordInterface $this->ensureConsistency(); } - return $startcol + 5; // 5 = TaxTableMap::NUM_HYDRATE_COLUMNS. + return $startcol + 6; // 6 = TaxTableMap::NUM_HYDRATE_COLUMNS. } catch (Exception $e) { throw new PropelException("Error populating \Thelia\Model\Tax object", 0, $e); @@ -899,6 +966,9 @@ abstract class Tax implements ActiveRecordInterface if ($this->isColumnModified(TaxTableMap::SERIALIZED_REQUIREMENTS)) { $modifiedColumns[':p' . $index++] = 'SERIALIZED_REQUIREMENTS'; } + if ($this->isColumnModified(TaxTableMap::IS_DEFAULT)) { + $modifiedColumns[':p' . $index++] = 'IS_DEFAULT'; + } if ($this->isColumnModified(TaxTableMap::CREATED_AT)) { $modifiedColumns[':p' . $index++] = 'CREATED_AT'; } @@ -925,6 +995,9 @@ abstract class Tax implements ActiveRecordInterface case 'SERIALIZED_REQUIREMENTS': $stmt->bindValue($identifier, $this->serialized_requirements, PDO::PARAM_STR); break; + case 'IS_DEFAULT': + $stmt->bindValue($identifier, (int) $this->is_default, 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; @@ -1003,9 +1076,12 @@ abstract class Tax implements ActiveRecordInterface return $this->getSerializedRequirements(); break; case 3: - return $this->getCreatedAt(); + return $this->getIsDefault(); break; case 4: + return $this->getCreatedAt(); + break; + case 5: return $this->getUpdatedAt(); break; default: @@ -1040,8 +1116,9 @@ abstract class Tax implements ActiveRecordInterface $keys[0] => $this->getId(), $keys[1] => $this->getType(), $keys[2] => $this->getSerializedRequirements(), - $keys[3] => $this->getCreatedAt(), - $keys[4] => $this->getUpdatedAt(), + $keys[3] => $this->getIsDefault(), + $keys[4] => $this->getCreatedAt(), + $keys[5] => $this->getUpdatedAt(), ); $virtualColumns = $this->virtualColumns; foreach($virtualColumns as $key => $virtualColumn) @@ -1100,9 +1177,12 @@ abstract class Tax implements ActiveRecordInterface $this->setSerializedRequirements($value); break; case 3: - $this->setCreatedAt($value); + $this->setIsDefault($value); break; case 4: + $this->setCreatedAt($value); + break; + case 5: $this->setUpdatedAt($value); break; } // switch() @@ -1132,8 +1212,9 @@ abstract class Tax implements ActiveRecordInterface if (array_key_exists($keys[0], $arr)) $this->setId($arr[$keys[0]]); if (array_key_exists($keys[1], $arr)) $this->setType($arr[$keys[1]]); if (array_key_exists($keys[2], $arr)) $this->setSerializedRequirements($arr[$keys[2]]); - if (array_key_exists($keys[3], $arr)) $this->setCreatedAt($arr[$keys[3]]); - if (array_key_exists($keys[4], $arr)) $this->setUpdatedAt($arr[$keys[4]]); + if (array_key_exists($keys[3], $arr)) $this->setIsDefault($arr[$keys[3]]); + if (array_key_exists($keys[4], $arr)) $this->setCreatedAt($arr[$keys[4]]); + if (array_key_exists($keys[5], $arr)) $this->setUpdatedAt($arr[$keys[5]]); } /** @@ -1148,6 +1229,7 @@ abstract class Tax implements ActiveRecordInterface if ($this->isColumnModified(TaxTableMap::ID)) $criteria->add(TaxTableMap::ID, $this->id); if ($this->isColumnModified(TaxTableMap::TYPE)) $criteria->add(TaxTableMap::TYPE, $this->type); if ($this->isColumnModified(TaxTableMap::SERIALIZED_REQUIREMENTS)) $criteria->add(TaxTableMap::SERIALIZED_REQUIREMENTS, $this->serialized_requirements); + if ($this->isColumnModified(TaxTableMap::IS_DEFAULT)) $criteria->add(TaxTableMap::IS_DEFAULT, $this->is_default); if ($this->isColumnModified(TaxTableMap::CREATED_AT)) $criteria->add(TaxTableMap::CREATED_AT, $this->created_at); if ($this->isColumnModified(TaxTableMap::UPDATED_AT)) $criteria->add(TaxTableMap::UPDATED_AT, $this->updated_at); @@ -1215,6 +1297,7 @@ abstract class Tax implements ActiveRecordInterface { $copyObj->setType($this->getType()); $copyObj->setSerializedRequirements($this->getSerializedRequirements()); + $copyObj->setIsDefault($this->getIsDefault()); $copyObj->setCreatedAt($this->getCreatedAt()); $copyObj->setUpdatedAt($this->getUpdatedAt()); @@ -1788,10 +1871,12 @@ abstract class Tax implements ActiveRecordInterface $this->id = null; $this->type = null; $this->serialized_requirements = null; + $this->is_default = null; $this->created_at = null; $this->updated_at = null; $this->alreadyInSave = false; $this->clearAllReferences(); + $this->applyDefaultValues(); $this->resetModified(); $this->setNew(true); $this->setDeleted(false); diff --git a/core/lib/Thelia/Model/Base/TaxQuery.php b/core/lib/Thelia/Model/Base/TaxQuery.php index 93da10f53..927542e48 100644 --- a/core/lib/Thelia/Model/Base/TaxQuery.php +++ b/core/lib/Thelia/Model/Base/TaxQuery.php @@ -25,12 +25,14 @@ use Thelia\Model\Map\TaxTableMap; * @method ChildTaxQuery orderById($order = Criteria::ASC) Order by the id column * @method ChildTaxQuery orderByType($order = Criteria::ASC) Order by the type column * @method ChildTaxQuery orderBySerializedRequirements($order = Criteria::ASC) Order by the serialized_requirements column + * @method ChildTaxQuery orderByIsDefault($order = Criteria::ASC) Order by the is_default column * @method ChildTaxQuery orderByCreatedAt($order = Criteria::ASC) Order by the created_at column * @method ChildTaxQuery orderByUpdatedAt($order = Criteria::ASC) Order by the updated_at column * * @method ChildTaxQuery groupById() Group by the id column * @method ChildTaxQuery groupByType() Group by the type column * @method ChildTaxQuery groupBySerializedRequirements() Group by the serialized_requirements column + * @method ChildTaxQuery groupByIsDefault() Group by the is_default column * @method ChildTaxQuery groupByCreatedAt() Group by the created_at column * @method ChildTaxQuery groupByUpdatedAt() Group by the updated_at column * @@ -52,12 +54,14 @@ use Thelia\Model\Map\TaxTableMap; * @method ChildTax findOneById(int $id) Return the first ChildTax filtered by the id column * @method ChildTax findOneByType(string $type) Return the first ChildTax filtered by the type column * @method ChildTax findOneBySerializedRequirements(string $serialized_requirements) Return the first ChildTax filtered by the serialized_requirements column + * @method ChildTax findOneByIsDefault(boolean $is_default) Return the first ChildTax filtered by the is_default column * @method ChildTax findOneByCreatedAt(string $created_at) Return the first ChildTax filtered by the created_at column * @method ChildTax findOneByUpdatedAt(string $updated_at) Return the first ChildTax filtered by the updated_at column * * @method array findById(int $id) Return ChildTax objects filtered by the id column * @method array findByType(string $type) Return ChildTax objects filtered by the type column * @method array findBySerializedRequirements(string $serialized_requirements) Return ChildTax objects filtered by the serialized_requirements column + * @method array findByIsDefault(boolean $is_default) Return ChildTax objects filtered by the is_default column * @method array findByCreatedAt(string $created_at) Return ChildTax objects filtered by the created_at column * @method array findByUpdatedAt(string $updated_at) Return ChildTax objects filtered by the updated_at column * @@ -148,7 +152,7 @@ abstract class TaxQuery extends ModelCriteria */ protected function findPkSimple($key, $con) { - $sql = 'SELECT ID, TYPE, SERIALIZED_REQUIREMENTS, CREATED_AT, UPDATED_AT FROM tax WHERE ID = :p0'; + $sql = 'SELECT ID, TYPE, SERIALIZED_REQUIREMENTS, IS_DEFAULT, CREATED_AT, UPDATED_AT FROM tax WHERE ID = :p0'; try { $stmt = $con->prepare($sql); $stmt->bindValue(':p0', $key, PDO::PARAM_INT); @@ -336,6 +340,33 @@ abstract class TaxQuery extends ModelCriteria return $this->addUsingAlias(TaxTableMap::SERIALIZED_REQUIREMENTS, $serializedRequirements, $comparison); } + /** + * Filter the query on the is_default column + * + * Example usage: + * + * $query->filterByIsDefault(true); // WHERE is_default = true + * $query->filterByIsDefault('yes'); // WHERE is_default = true + * + * + * @param boolean|string $isDefault The value to use as filter. + * Non-boolean arguments are converted using the following rules: + * * 1, '1', 'true', 'on', and 'yes' are converted to boolean true + * * 0, '0', 'false', 'off', and 'no' are converted to boolean false + * Check on string values is case insensitive (so 'FaLsE' is seen as 'false'). + * @param string $comparison Operator to use for the column comparison, defaults to Criteria::EQUAL + * + * @return ChildTaxQuery The current query, for fluid interface + */ + public function filterByIsDefault($isDefault = null, $comparison = null) + { + if (is_string($isDefault)) { + $is_default = in_array(strtolower($isDefault), array('false', 'off', '-', 'no', 'n', '0', '')) ? false : true; + } + + return $this->addUsingAlias(TaxTableMap::IS_DEFAULT, $isDefault, $comparison); + } + /** * Filter the query on the created_at column * diff --git a/core/lib/Thelia/Model/Base/Template.php b/core/lib/Thelia/Model/Base/Template.php index 77efa7ba0..08bb0f3a0 100644 --- a/core/lib/Thelia/Model/Base/Template.php +++ b/core/lib/Thelia/Model/Base/Template.php @@ -864,9 +864,10 @@ abstract class Template implements ActiveRecordInterface if ($this->productsScheduledForDeletion !== null) { if (!$this->productsScheduledForDeletion->isEmpty()) { - \Thelia\Model\ProductQuery::create() - ->filterByPrimaryKeys($this->productsScheduledForDeletion->getPrimaryKeys(false)) - ->delete($con); + foreach ($this->productsScheduledForDeletion as $product) { + // need to save related object because we set the relation to null + $product->save($con); + } $this->productsScheduledForDeletion = null; } } @@ -1553,7 +1554,7 @@ abstract class Template implements ActiveRecordInterface $this->productsScheduledForDeletion = clone $this->collProducts; $this->productsScheduledForDeletion->clear(); } - $this->productsScheduledForDeletion[]= clone $product; + $this->productsScheduledForDeletion[]= $product; $product->setTemplate(null); } diff --git a/core/lib/Thelia/Model/Base/TemplateQuery.php b/core/lib/Thelia/Model/Base/TemplateQuery.php index 506d7d943..98c588dd8 100644 --- a/core/lib/Thelia/Model/Base/TemplateQuery.php +++ b/core/lib/Thelia/Model/Base/TemplateQuery.php @@ -395,7 +395,7 @@ abstract class TemplateQuery extends ModelCriteria * * @return ChildTemplateQuery The current query, for fluid interface */ - public function joinProduct($relationAlias = null, $joinType = Criteria::INNER_JOIN) + public function joinProduct($relationAlias = null, $joinType = Criteria::LEFT_JOIN) { $tableMap = $this->getTableMap(); $relationMap = $tableMap->getRelation('Product'); @@ -430,7 +430,7 @@ abstract class TemplateQuery extends ModelCriteria * * @return \Thelia\Model\ProductQuery A secondary query class using the current class as primary query */ - public function useProductQuery($relationAlias = null, $joinType = Criteria::INNER_JOIN) + public function useProductQuery($relationAlias = null, $joinType = Criteria::LEFT_JOIN) { return $this ->joinProduct($relationAlias, $joinType) diff --git a/core/lib/Thelia/Model/Category.php b/core/lib/Thelia/Model/Category.php index 042864de0..204896674 100755 --- a/core/lib/Thelia/Model/Category.php +++ b/core/lib/Thelia/Model/Category.php @@ -28,7 +28,7 @@ class Category extends BaseCategory /** * {@inheritDoc} */ - protected function getRewritenUrlViewName() { + protected function getRewrittenUrlViewName() { return 'category'; } @@ -69,8 +69,6 @@ class Category extends BaseCategory { $this->setPosition($this->getNextPosition()); - $this->generateRewritenUrl($this->getLocale()); - $this->dispatchEvent(TheliaEvents::BEFORE_CREATECATEGORY, new CategoryEvent($this)); return true; @@ -81,6 +79,7 @@ class Category extends BaseCategory */ public function postInsert(ConnectionInterface $con = null) { + //$this->generateRewrittenUrl($this->getLocale()); $this->dispatchEvent(TheliaEvents::AFTER_CREATECATEGORY, new CategoryEvent($this)); } diff --git a/core/lib/Thelia/Model/Content.php b/core/lib/Thelia/Model/Content.php index 79660f93a..4d9cf9f3a 100755 --- a/core/lib/Thelia/Model/Content.php +++ b/core/lib/Thelia/Model/Content.php @@ -17,7 +17,7 @@ class Content extends BaseContent /** * {@inheritDoc} */ - protected function getRewritenUrlViewName() { + protected function getRewrittenUrlViewName() { return 'content'; } @@ -37,8 +37,11 @@ class Content extends BaseContent { $this->setPosition($this->getNextPosition()); - $this->generateRewritenUrl($this->getLocale()); - return true; } + + public function postInsert(ConnectionInterface $con = null) + { + //$this->generateRewrittenUrl($this->getLocale()); + } } diff --git a/core/lib/Thelia/Model/Folder.php b/core/lib/Thelia/Model/Folder.php index 1e73e7e23..e32505d9d 100755 --- a/core/lib/Thelia/Model/Folder.php +++ b/core/lib/Thelia/Model/Folder.php @@ -17,7 +17,7 @@ class Folder extends BaseFolder /** * {@inheritDoc} */ - protected function getRewritenUrlViewName() { + protected function getRewrittenUrlViewName() { return 'folder'; } @@ -67,8 +67,12 @@ class Folder extends BaseFolder { $this->setPosition($this->getNextPosition()); - $this->generateRewritenUrl($this->getLocale()); return true; } + + public function postInsert(ConnectionInterface $con = null) + { + //$this->generateRewrittenUrl($this->getLocale()); + } } diff --git a/core/lib/Thelia/Model/Map/ProductTableMap.php b/core/lib/Thelia/Model/Map/ProductTableMap.php index 17c4585f0..59ac236ac 100644 --- a/core/lib/Thelia/Model/Map/ProductTableMap.php +++ b/core/lib/Thelia/Model/Map/ProductTableMap.php @@ -189,7 +189,7 @@ class ProductTableMap extends TableMap $this->addColumn('REF', 'Ref', 'VARCHAR', true, 255, null); $this->addColumn('VISIBLE', 'Visible', 'TINYINT', true, null, 0); $this->addColumn('POSITION', 'Position', 'INTEGER', true, null, null); - $this->addForeignKey('TEMPLATE_ID', 'TemplateId', 'INTEGER', 'template', 'ID', true, null, null); + $this->addForeignKey('TEMPLATE_ID', 'TemplateId', 'INTEGER', 'template', 'ID', false, null, null); $this->addColumn('CREATED_AT', 'CreatedAt', 'TIMESTAMP', false, null, null); $this->addColumn('UPDATED_AT', 'UpdatedAt', 'TIMESTAMP', false, null, null); $this->addColumn('VERSION', 'Version', 'INTEGER', false, null, 0); diff --git a/core/lib/Thelia/Model/Map/ProductVersionTableMap.php b/core/lib/Thelia/Model/Map/ProductVersionTableMap.php index 4e84b4db8..14d38a764 100644 --- a/core/lib/Thelia/Model/Map/ProductVersionTableMap.php +++ b/core/lib/Thelia/Model/Map/ProductVersionTableMap.php @@ -180,7 +180,7 @@ class ProductVersionTableMap extends TableMap $this->addColumn('REF', 'Ref', 'VARCHAR', true, 255, null); $this->addColumn('VISIBLE', 'Visible', 'TINYINT', true, null, 0); $this->addColumn('POSITION', 'Position', 'INTEGER', true, null, null); - $this->addColumn('TEMPLATE_ID', 'TemplateId', 'INTEGER', true, null, null); + $this->addColumn('TEMPLATE_ID', 'TemplateId', 'INTEGER', false, null, null); $this->addColumn('CREATED_AT', 'CreatedAt', 'TIMESTAMP', false, null, null); $this->addColumn('UPDATED_AT', 'UpdatedAt', 'TIMESTAMP', false, null, null); $this->addPrimaryKey('VERSION', 'Version', 'INTEGER', true, null, 0); diff --git a/core/lib/Thelia/Model/Map/TaxTableMap.php b/core/lib/Thelia/Model/Map/TaxTableMap.php index 6ca89ae85..eebde0ca0 100644 --- a/core/lib/Thelia/Model/Map/TaxTableMap.php +++ b/core/lib/Thelia/Model/Map/TaxTableMap.php @@ -57,7 +57,7 @@ class TaxTableMap extends TableMap /** * The total number of columns */ - const NUM_COLUMNS = 5; + const NUM_COLUMNS = 6; /** * The number of lazy-loaded columns @@ -67,7 +67,7 @@ class TaxTableMap extends TableMap /** * The number of columns to hydrate (NUM_COLUMNS - NUM_LAZY_LOAD_COLUMNS) */ - const NUM_HYDRATE_COLUMNS = 5; + const NUM_HYDRATE_COLUMNS = 6; /** * the column name for the ID field @@ -84,6 +84,11 @@ class TaxTableMap extends TableMap */ const SERIALIZED_REQUIREMENTS = 'tax.SERIALIZED_REQUIREMENTS'; + /** + * the column name for the IS_DEFAULT field + */ + const IS_DEFAULT = 'tax.IS_DEFAULT'; + /** * the column name for the CREATED_AT field */ @@ -115,12 +120,12 @@ class TaxTableMap extends TableMap * e.g. self::$fieldNames[self::TYPE_PHPNAME][0] = 'Id' */ protected static $fieldNames = array ( - self::TYPE_PHPNAME => array('Id', 'Type', 'SerializedRequirements', 'CreatedAt', 'UpdatedAt', ), - self::TYPE_STUDLYPHPNAME => array('id', 'type', 'serializedRequirements', 'createdAt', 'updatedAt', ), - self::TYPE_COLNAME => array(TaxTableMap::ID, TaxTableMap::TYPE, TaxTableMap::SERIALIZED_REQUIREMENTS, TaxTableMap::CREATED_AT, TaxTableMap::UPDATED_AT, ), - self::TYPE_RAW_COLNAME => array('ID', 'TYPE', 'SERIALIZED_REQUIREMENTS', 'CREATED_AT', 'UPDATED_AT', ), - self::TYPE_FIELDNAME => array('id', 'type', 'serialized_requirements', 'created_at', 'updated_at', ), - self::TYPE_NUM => array(0, 1, 2, 3, 4, ) + self::TYPE_PHPNAME => array('Id', 'Type', 'SerializedRequirements', 'IsDefault', 'CreatedAt', 'UpdatedAt', ), + self::TYPE_STUDLYPHPNAME => array('id', 'type', 'serializedRequirements', 'isDefault', 'createdAt', 'updatedAt', ), + self::TYPE_COLNAME => array(TaxTableMap::ID, TaxTableMap::TYPE, TaxTableMap::SERIALIZED_REQUIREMENTS, TaxTableMap::IS_DEFAULT, TaxTableMap::CREATED_AT, TaxTableMap::UPDATED_AT, ), + self::TYPE_RAW_COLNAME => array('ID', 'TYPE', 'SERIALIZED_REQUIREMENTS', 'IS_DEFAULT', 'CREATED_AT', 'UPDATED_AT', ), + self::TYPE_FIELDNAME => array('id', 'type', 'serialized_requirements', 'is_default', 'created_at', 'updated_at', ), + self::TYPE_NUM => array(0, 1, 2, 3, 4, 5, ) ); /** @@ -130,12 +135,12 @@ class TaxTableMap extends TableMap * e.g. self::$fieldKeys[self::TYPE_PHPNAME]['Id'] = 0 */ protected static $fieldKeys = array ( - self::TYPE_PHPNAME => array('Id' => 0, 'Type' => 1, 'SerializedRequirements' => 2, 'CreatedAt' => 3, 'UpdatedAt' => 4, ), - self::TYPE_STUDLYPHPNAME => array('id' => 0, 'type' => 1, 'serializedRequirements' => 2, 'createdAt' => 3, 'updatedAt' => 4, ), - self::TYPE_COLNAME => array(TaxTableMap::ID => 0, TaxTableMap::TYPE => 1, TaxTableMap::SERIALIZED_REQUIREMENTS => 2, TaxTableMap::CREATED_AT => 3, TaxTableMap::UPDATED_AT => 4, ), - self::TYPE_RAW_COLNAME => array('ID' => 0, 'TYPE' => 1, 'SERIALIZED_REQUIREMENTS' => 2, 'CREATED_AT' => 3, 'UPDATED_AT' => 4, ), - self::TYPE_FIELDNAME => array('id' => 0, 'type' => 1, 'serialized_requirements' => 2, 'created_at' => 3, 'updated_at' => 4, ), - self::TYPE_NUM => array(0, 1, 2, 3, 4, ) + self::TYPE_PHPNAME => array('Id' => 0, 'Type' => 1, 'SerializedRequirements' => 2, 'IsDefault' => 3, 'CreatedAt' => 4, 'UpdatedAt' => 5, ), + self::TYPE_STUDLYPHPNAME => array('id' => 0, 'type' => 1, 'serializedRequirements' => 2, 'isDefault' => 3, 'createdAt' => 4, 'updatedAt' => 5, ), + self::TYPE_COLNAME => array(TaxTableMap::ID => 0, TaxTableMap::TYPE => 1, TaxTableMap::SERIALIZED_REQUIREMENTS => 2, TaxTableMap::IS_DEFAULT => 3, TaxTableMap::CREATED_AT => 4, TaxTableMap::UPDATED_AT => 5, ), + self::TYPE_RAW_COLNAME => array('ID' => 0, 'TYPE' => 1, 'SERIALIZED_REQUIREMENTS' => 2, 'IS_DEFAULT' => 3, 'CREATED_AT' => 4, 'UPDATED_AT' => 5, ), + self::TYPE_FIELDNAME => array('id' => 0, 'type' => 1, 'serialized_requirements' => 2, 'is_default' => 3, 'created_at' => 4, 'updated_at' => 5, ), + self::TYPE_NUM => array(0, 1, 2, 3, 4, 5, ) ); /** @@ -157,6 +162,7 @@ class TaxTableMap extends TableMap $this->addPrimaryKey('ID', 'Id', 'INTEGER', true, null, null); $this->addColumn('TYPE', 'Type', 'VARCHAR', true, 255, null); $this->addColumn('SERIALIZED_REQUIREMENTS', 'SerializedRequirements', 'LONGVARCHAR', true, null, null); + $this->addColumn('IS_DEFAULT', 'IsDefault', 'BOOLEAN', false, 1, false); $this->addColumn('CREATED_AT', 'CreatedAt', 'TIMESTAMP', false, null, null); $this->addColumn('UPDATED_AT', 'UpdatedAt', 'TIMESTAMP', false, null, null); } // initialize() @@ -335,12 +341,14 @@ class TaxTableMap extends TableMap $criteria->addSelectColumn(TaxTableMap::ID); $criteria->addSelectColumn(TaxTableMap::TYPE); $criteria->addSelectColumn(TaxTableMap::SERIALIZED_REQUIREMENTS); + $criteria->addSelectColumn(TaxTableMap::IS_DEFAULT); $criteria->addSelectColumn(TaxTableMap::CREATED_AT); $criteria->addSelectColumn(TaxTableMap::UPDATED_AT); } else { $criteria->addSelectColumn($alias . '.ID'); $criteria->addSelectColumn($alias . '.TYPE'); $criteria->addSelectColumn($alias . '.SERIALIZED_REQUIREMENTS'); + $criteria->addSelectColumn($alias . '.IS_DEFAULT'); $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 ef000e820..13600351a 100755 --- a/core/lib/Thelia/Model/Product.php +++ b/core/lib/Thelia/Model/Product.php @@ -21,12 +21,10 @@ class Product extends BaseProduct use \Thelia\Model\Tools\UrlRewritingTrait; - protected $defaultCategory = null; - /** * {@inheritDoc} */ - protected function getRewritenUrlViewName() { + protected function getRewrittenUrlViewName() { return 'product'; } @@ -45,7 +43,8 @@ class Product extends BaseProduct public function getTaxedPrice(Country $country) { $taxCalculator = new Calculator(); - return round($taxCalculator->load($this, $country)->getTaxedPrice($this->getRealLowestPrice()), 2); + + return $taxCalculator->load($this, $country)->getTaxedPrice($this->getRealLowestPrice()); } /** @@ -99,10 +98,12 @@ class Product extends BaseProduct $con->beginTransaction(); + $this->dispatchEvent(TheliaEvents::BEFORE_CREATEPRODUCT, new ProductEvent($this)); + try { // Create the product $this->save($con); - +echo "1"; // Add the default category $pc = new ProductCategory(); @@ -113,13 +114,44 @@ class Product extends BaseProduct ->save($con) ; + // Set the position + $this->setPosition($this->getNextPosition())->save($con); + echo "2"; + // Create an empty product sale element + $sale_elements = new ProductSaleElements(); + + $sale_elements + ->setProduct($this) + ->setRef('') + ->setPromo(0) + ->setNewness(0) + ->setWeight(0) + ->save($con) + ; + echo "3"; + // Create an empty product price in the default currency + $product_price = new ProductPrice(); + + $product_price + ->setProductSaleElements($sale_elements) + ->setPromoPrice(0) + ->setPrice(0) + ->setCurrency(CurrencyQuery::create()->findByByDefault(true)) + ->save($con) + ; + echo "4"; // Store all the stuff ! $con->commit(); +echo "commited !!!: ".$this->getId(); + + + $this->dispatchEvent(TheliaEvents::AFTER_CREATEPRODUCT, new ProductEvent($this)); } catch(PropelException $ex) { $con->rollback(); - +echo("error !"); +exit; throw $ex; } } @@ -140,30 +172,6 @@ class Product extends BaseProduct if ($produits != null) $query->filterById($produits, Criteria::IN); } - /** - * {@inheritDoc} - */ - public function preInsert(ConnectionInterface $con = null) - { - $this->generateRewritenUrl($this->getLocale()); - - $this->dispatchEvent(TheliaEvents::BEFORE_CREATEPRODUCT, new ProductEvent($this)); - - return true; - } - - /** - * {@inheritDoc} - */ - public function postInsert(ConnectionInterface $con = null) - { - $this->setPosition($this->getNextPosition()); - - $this->save(); - - $this->dispatchEvent(TheliaEvents::AFTER_CREATEPRODUCT, new ProductEvent($this)); - } - /** * {@inheritDoc} */ @@ -199,5 +207,4 @@ class Product extends BaseProduct { $this->dispatchEvent(TheliaEvents::AFTER_DELETEPRODUCT, new ProductEvent($this)); } - } diff --git a/core/lib/Thelia/Model/Rewriting.php b/core/lib/Thelia/Model/Rewriting.php deleted file mode 100644 index 8d6f75fab..000000000 --- a/core/lib/Thelia/Model/Rewriting.php +++ /dev/null @@ -1,9 +0,0 @@ -getRedirected()) { + //check if rewriting url alredy exists and put redirect to the new one + RewritingUrlQuery::create() + ->filterByView($this->getView()) + ->filterByViewId($this->getViewId()) + ->filterByViewLocale($this->getViewLocale()) + ->filterByRedirected($this->getId(), Criteria::NOT_IN) + ->update(array( + "Redirected" => $this->getId() + )); + } + } } diff --git a/core/lib/Thelia/Model/Tools/UrlRewritingTrait.php b/core/lib/Thelia/Model/Tools/UrlRewritingTrait.php index 6ab24fbc4..1b9087626 100644 --- a/core/lib/Thelia/Model/Tools/UrlRewritingTrait.php +++ b/core/lib/Thelia/Model/Tools/UrlRewritingTrait.php @@ -23,6 +23,9 @@ namespace Thelia\Model\Tools; +use Thelia\Exception\UrlRewritingException; +use Thelia\Model\RewritingUrlQuery; +use Thelia\Model\RewritingUrl; use Thelia\Tools\URL; /** * A trait for managing Rewriten URLs from model classes @@ -32,7 +35,7 @@ trait UrlRewritingTrait { /** * @returns string the view name of the rewriten object (e.g., 'category', 'product') */ - protected abstract function getRewritenUrlViewName(); + protected abstract function getRewrittenUrlViewName(); /** * Get the object URL for the given locale, rewriten if rewriting is enabled. @@ -41,7 +44,7 @@ trait UrlRewritingTrait { */ public function getUrl($locale) { - return URL::getInstance()->retrieve($this->getRewritenUrlViewName(), $this->getId(), $locale)->toString(); + return URL::getInstance()->retrieve($this->getRewrittenUrlViewName(), $this->getId(), $locale)->toString(); } /** @@ -49,27 +52,79 @@ trait UrlRewritingTrait { * * @param string $locale a valid locale (e.g. en_US) */ - public function generateRewritenUrl($locale) + public function generateRewrittenUrl($locale) { - URL::getInstance()->generateRewritenUrl($this->getRewritenUrlViewName(), $this->getId(), $locale, $this->getTitle()); + if ($this->isNew()) { + throw new \RuntimeException(sprintf('Object %s must be saved before generating url', $this->getRewrittenUrlViewName())); + } + // Borrowed from http://stackoverflow.com/questions/2668854/sanitizing-strings-to-make-them-url-and-filename-safe + + $this->setLocale($locale); + + $title = $this->getTitle() ?: $this->getRef(); + // Replace all weird characters with dashes + $string = preg_replace('/[^\w\-~_\.]+/u', '-', $title); + + // 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 + try{ + $i=0; + while(URL::getInstance()->resolve($urlFilePart)) { + $i++; + $urlFilePart = sprintf("%s-%d.html",$cleanString, $i); + } + } catch (UrlRewritingException $e) { + $rewritingUrl = new RewritingUrl(); + $rewritingUrl->setUrl($urlFilePart) + ->setView($this->getRewrittenUrlViewName()) + ->setViewId($this->getId()) + ->setViewLocale($locale) + ->save() + ; + } + + return $urlFilePart; + } /** * return the rewriten URL for the given locale * * @param string $locale a valid locale (e.g. en_US) + * @return null */ - public function getRewritenUrl($locale) + public function getRewrittenUrl($locale) { - return "fake url - TODO"; + $rewritingUrl = RewritingUrlQuery::create() + ->filterByViewLocale($locale) + ->filterByView($this->getRewrittenUrlViewName()) + ->filterByViewId($this->getId()) + ->filterByRedirected(0) + ->findOne() + ; + + if($rewritingUrl) { + $url = $rewritingUrl->getUrl(); + } else { + $url = null; + } + + return $url; } /** * Set the rewriten URL for the given locale * * @param string $locale a valid locale (e.g. en_US) + * @param $url the wanted url + * @return $this */ - public function setRewritenUrl($locale, $url) + public function setRewrittenUrl($locale, $url) { // TODO - code me ! diff --git a/core/lib/Thelia/Tests/Rewriting/ProductRewriteTest.php b/core/lib/Thelia/Tests/Rewriting/ProductRewriteTest.php new file mode 100644 index 000000000..c38773814 --- /dev/null +++ b/core/lib/Thelia/Tests/Rewriting/ProductRewriteTest.php @@ -0,0 +1,88 @@ +. */ +/* */ +/*************************************************************************************/ + +namespace Thelia\Tests\Rewriting; +use Thelia\Model\Product; +use Thelia\Model\ProductQuery; + + +/** + * Class ProductRewriteTest + * @package Thelia\Tests\Rewriting + * @author Manuel Raynaud + */ +class ProductRewriteTest extends \PHPUnit_Framework_TestCase +{ + protected static $productId; + + public static function setUpBeforeClass() + { + $product = new Product(); + $product->setRef(sprintf("TestRewrittenProduct%s",uniqid())) + ->setPosition(1) + ->setVisible(1) + ->setLocale('en_US') + ->setTitle('My english super Title') + ->setLocale('fr_FR') + ->setTitle('Mon super titre en français') + ->save(); + + self::$productId = $product->getId(); + } + + /** + * @covers Thelia\Model\Tools\UrlRewritingTrait::generateRewrittenUrl + */ + public function testFrenchRewrittenUrl() + { + $product = ProductQuery::create()->findPk(self::$productId); + + $rewrittenUrl = $product->generateRewrittenUrl('fr_FR'); + $this->assertNotNull($rewrittenUrl, "rewritten url can not be null"); + $this->assertRegExp('/^mon-super-titre-en-français(-[0-9]+)?\.html$/', $rewrittenUrl); + //mon-super-titre-en-français-2.html + } + + /** + * @covers Thelia\Model\Tools\UrlRewritingTrait::generateRewrittenUrl + */ + public function testEnglishRewrittenUrl() + { + $product = ProductQuery::create()->findPk(self::$productId); + + $rewrittenUrl = $product->generateRewrittenUrl('en_US'); + $this->assertNotNull($rewrittenUrl, "rewritten url can not be null"); + $this->assertRegExp('/^my-english-super-title(-[0-9]+)?\.html$/', $rewrittenUrl); + } + + /** + * @expectedException \RuntimeException + * @expectedExceptionMessage Object product must be saved before generating url + */ + public function testOnNotSavedProduct() + { + $product = new Product(); + + $product->generateRewrittenUrl('fr_FR'); + } +} \ No newline at end of file diff --git a/install/insert.sql b/install/insert.sql index c7287089f..8b77278d3 100755 --- a/install/insert.sql +++ b/install/insert.sql @@ -1128,9 +1128,9 @@ INSERT INTO `country_i18n` (`id`, `locale`, `title`, `description`, `chapo`, `po (268, 'es_ES', 'USA - Alabama', '', '', ''), (268, 'fr_FR', 'USA - Alabama', '', '', ''); -INSERT INTO `tax` (`id`, `type`, `serialized_requirements`, `created_at`, `updated_at`) +INSERT INTO `tax` (`id`, `type`, `serialized_requirements`, `is_default`, `created_at`, `updated_at`) VALUES - (1, 'PricePercentTaxType', 'eyJwZXJjZW50IjoxOS42fQ==', NOW(), NOW()); + (1, 'PricePercentTaxType', 'eyJwZXJjZW50IjoxOS42fQ==', 1, NOW(), NOW()); INSERT INTO `tax_i18n` (`id`, `locale`, `title`) VALUES diff --git a/install/thelia.sql b/install/thelia.sql index 3dc5d0454..f26967e35 100755 --- a/install/thelia.sql +++ b/install/thelia.sql @@ -36,7 +36,7 @@ CREATE TABLE `product` `ref` VARCHAR(255) NOT NULL, `visible` TINYINT DEFAULT 0 NOT NULL, `position` INTEGER NOT NULL, - `template_id` INTEGER NOT NULL, + `template_id` INTEGER, `created_at` DATETIME, `updated_at` DATETIME, `version` INTEGER DEFAULT 0, @@ -121,6 +121,7 @@ CREATE TABLE `tax` `id` INTEGER NOT NULL AUTO_INCREMENT, `type` VARCHAR(255) NOT NULL, `serialized_requirements` TEXT NOT NULL, + `is_default` TINYINT(1) DEFAULT 0, `created_at` DATETIME, `updated_at` DATETIME, PRIMARY KEY (`id`) @@ -2167,7 +2168,7 @@ CREATE TABLE `product_version` `ref` VARCHAR(255) NOT NULL, `visible` TINYINT DEFAULT 0 NOT NULL, `position` INTEGER NOT NULL, - `template_id` INTEGER NOT NULL, + `template_id` INTEGER, `created_at` DATETIME, `updated_at` DATETIME, `version` INTEGER DEFAULT 0 NOT NULL, diff --git a/local/config/schema.xml b/local/config/schema.xml index a43cfda56..61611202f 100755 --- a/local/config/schema.xml +++ b/local/config/schema.xml @@ -28,7 +28,7 @@ - + @@ -102,6 +102,7 @@ + diff --git a/templates/admin/default/categories.html b/templates/admin/default/categories.html index b2ca8440e..51bfc59f6 100755 --- a/templates/admin/default/categories.html +++ b/templates/admin/default/categories.html @@ -5,452 +5,533 @@ {block name="check-permissions"}admin.categories.view{/block} {block name="main-content"} -
+
-
+
- {include file="includes/catalog-breadcrumb.html"} + {include file="includes/catalog-breadcrumb.html"} - {module_include location='categories_top'} + {module_include location='categories_top'} -
-
-
+
+
+
- -
- {* display parent category name, and get current cat ID *} - {loop name="category_title" type="category" visible="*" id=$category_id} - {intl l="Categories in %cat" cat=$TITLE} - {$cat_id = $ID} - {/loop} - {elseloop rel="category_title"} - {intl l="Top level categories"} - {/elseloop} + + + {loop type="auth" name="can_create" roles="ADMIN" permissions="admin.categories.create"} + + + + {/loop} + - {ifloop rel="category_list"} - - - + {ifloop rel="category_list"} + + + - + - + - {module_include location='category_list_header'} + {module_include location='category_list_header'} - + - + - - - + + + - - {loop name="category_list" type="category" visible="*" parent=$category_id order=$category_order backend_context="1" lang=$lang_id} - - + + {loop name="category_list" type="category" visible="*" parent=$category_id order=$category_order backend_context="1" lang=$lang_id} + + - + - + - {module_include location='category_list_row'} + {module_include location='category_list_row'} - + {elseloop rel="can_change"} +
+ +
+ {/elseloop} + - + - - - {/loop} - - {/ifloop} + {loop type="auth" name="can_delete" roles="ADMIN" permissions="admin.categories.delete"} + + {/loop} + + + + {/loop} + + {/ifloop} - {elseloop rel="category_list"} - - - + + - - - {/elseloop} -
+ {* display parent category name, and get current cat ID *} + {loop name="category_title" type="category" visible="*" id=$category_id} + {intl l="Categories in %cat" cat=$TITLE} + {$cat_id = $ID} + {/loop} + {elseloop rel="category_title"} + {intl l="Top level categories"} + {/elseloop} - {module_include location='category_list_caption'} + {module_include location='category_list_caption'} - {loop type="auth" name="can_create" roles="ADMIN" permissions="admin.categories.create"} - - - - {/loop} -
- {admin_sortable_header - current_order=$category_order - order='id' - reverse_order='id_reverse' - path={url path='/admin/categories' id_category=$category_id} - request_parameter_name='category_order' - label="{intl l='ID'}" - } -
+ {admin_sortable_header + current_order=$category_order + order='id' + reverse_order='id_reverse' + path={url path='/admin/categories' id_category=$category_id} + request_parameter_name='category_order' + label="{intl l='ID'}" + } +    - {admin_sortable_header - current_order=$category_order - order='alpha' - reverse_order='alpha_reverse' - path={url path='/admin/categories' id_category=$category_id} - request_parameter_name='category_order' - label="{intl l='Category title'}" - } - + {admin_sortable_header + current_order=$category_order + order='alpha' + reverse_order='alpha_reverse' + path={url path='/admin/categories' id_category=$category_id} + request_parameter_name='category_order' + label="{intl l='Category title'}" + } + - {admin_sortable_header - current_order=$category_order - order='visible' - reverse_order='visible_reverse' - path={url path='/admin/categories' id_category=$category_id} - request_parameter_name='category_order' - label="{intl l='Online'}" - } - + {admin_sortable_header + current_order=$category_order + order='visible' + reverse_order='visible_reverse' + path={url path='/admin/categories' id_category=$category_id} + request_parameter_name='category_order' + label="{intl l='Online'}" + } + - {admin_sortable_header - current_order=$category_order - order='manual' - reverse_order='manual_reverse' - path={url path='/admin/categories' id_category=$category_id} - request_parameter_name='category_order' - label="{intl l='Position'}" - } - + {admin_sortable_header + current_order=$category_order + order='manual' + reverse_order='manual_reverse' + path={url path='/admin/categories' id_category=$category_id} + request_parameter_name='category_order' + label="{intl l='Position'}" + } + {intl l='Actions'}
{intl l='Actions'}
{$ID}
{$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} - + {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} - - + + {$TITLE} + + - {loop type="auth" name="can_change" roles="ADMIN" permissions="admin.categories.edit"} -
- -
- {/loop} +
+ {loop type="auth" name="can_change" roles="ADMIN" permissions="admin.categories.edit"} +
+ +
+ {/loop} - {elseloop rel="can_change"} -
- -
- {/elseloop} -
- {admin_position_block - permission="admin.categories.edit" - path={url path='admin/categories/update-position' category_id=$ID} - url_parameter="category_id" - in_place_edit_class="categoryPositionChange" - position=$POSITION - id=$ID - } - + {admin_position_block + permission="admin.categories.edit" + path={url path='admin/categories/update-position' category_id=$ID} + url_parameter="category_id" + in_place_edit_class="categoryPositionChange" + position=$POSITION + id=$ID + } + -
- +
+
+ - {loop type="auth" name="can_change" roles="ADMIN" permissions="admin.categories.edit"} - - {/loop} + {loop type="auth" name="can_change" roles="ADMIN" permissions="admin.categories.edit"} + + {/loop} - {loop type="auth" name="can_delete" roles="ADMIN" permissions="admin.categories.delete"} - - {/loop} -
-
-
- {loop type="auth" name="can_create" roles="ADMIN" permissions="admin.categories.create"} - {intl l="This category has no sub-categories. To create a new one, click the + button above."} - {/loop} + {elseloop rel="category_list"} +
+
+ {loop type="auth" name="can_create" roles="ADMIN" permissions="admin.categories.create"} + {intl l="This category has no sub-categories. To create a new one, click the + button above."} + {/loop} - {elseloop rel="can_create"} - {intl l="This category has no sub-categories."} - {/elseloop} -
-
- + {elseloop rel="can_create"} + {intl l="This category has no sub-categories."} + {/elseloop} + + + + + {/elseloop} +
+
{* -- PRODUCT MANAGEMENT ---------------------------------------------------- *} -
-
-
+
+
+
- - + + + + + {/elseloop} +
- {* display parent category name *} - {loop name="category_title" type="category" visible="*" id=$category_id} - {intl l="Products in %cat" cat=$TITLE} - {/loop} + + + + + + - {ifloop rel="product_list"} - - - + + + - + - + - + - - - + + + - - {loop name="product_list" type="product" visible="*" category_default=$category_id order=$product_order} - - + + {loop name="product_list" type="product" visible="*" category_default=$category_id order=$product_order} + + - + - + - {module_include location='product_list_row'} + {module_include location='product_list_row'} - + {elseloop rel="can_change"} +
+ +
+ {/elseloop} + - + - - - {/loop} - - {/ifloop} + {loop type="auth" name="can_delete" roles="ADMIN" permissions="admin.product.delete"} + + {/loop} + + + + {/loop} + + {/ifloop} - {elseloop rel="product_list"} - - - - - - {/elseloop} -
+ {* display parent category name *} + {loop name="category_title" type="category" visible="*" id=$category_id} + {intl l="Products in %cat" cat=$TITLE} + {/loop} - {elseloop rel="category_title"} - {intl l="Top level Products"} - {/elseloop} + {elseloop rel="category_title"} + {intl l="Top level Products"} + {/elseloop} - {module_include location='product_list_caption'} + {module_include location='product_list_caption'} - - - -
- {admin_sortable_header - current_order=$product_order - order='id' - reverse_order='id_reverse' - path={url path='/admin/categories' id_category=$category_id target='products'} - label="{intl l='ID'}" - } + {ifloop rel="product_list"} +
+ {admin_sortable_header + current_order=$product_order + order='id' + reverse_order='id_reverse' + path={url path='/admin/categories' id_category=$category_id target='products'} + label="{intl l='ID'}" + } -    - {admin_sortable_header - current_order=$product_order - order='ref' - reverse_order='ref_reverse' - path={url path='/admin/categories' id_category=$category_id target='products'} - label="{intl l='Reference'}" - } - + {admin_sortable_header + current_order=$product_order + order='ref' + reverse_order='ref_reverse' + path={url path='/admin/categories' id_category=$category_id target='products'} + label="{intl l='Reference'}" + } + - {admin_sortable_header - current_order=$product_order - order='alpha' - reverse_order='alpha_reverse' - path={url path='/admin/categories' id_category=$category_id target='products'} - label="{intl l='Product title'}" - } + + {admin_sortable_header + current_order=$product_order + order='alpha' + reverse_order='alpha_reverse' + path={url path='/admin/categories' id_category=$category_id target='products'} + label="{intl l='Product title'}" + } - {module_include location='product_list_header'} + {module_include location='product_list_header'} - - {admin_sortable_header - current_order=$product_order - order='visible' - reverse_order='visible_reverse' - path={url path='/admin/categories' id_category=$category_id target='products'} - label="{intl l='Online'}" - } - + {admin_sortable_header + current_order=$product_order + order='visible' + reverse_order='visible_reverse' + path={url path='/admin/categories' id_category=$category_id target='products'} + label="{intl l='Online'}" + } + - {admin_sortable_header - current_order=$product_order - order='manual' - reverse_order='manual_reverse' - path={url path='/admin/categories' id_category=$category_id target='products'} - label="{intl l='Position'}" - } - + {admin_sortable_header + current_order=$product_order + order='manual' + reverse_order='manual_reverse' + path={url path='/admin/categories' id_category=$category_id target='products'} + label="{intl l='Position'}" + } +  
 
{$ID}
{$ID} - {loop type="image" name="cat_image" source="product" source_id="$ID" limit="1" width="50" height="50" resize_mode="crop" backend_context="1"} - - {$TITLE} - - {/loop} + + {loop type="image" name="cat_image" source="product" source_id="$ID" limit="1" width="50" height="50" resize_mode="crop" backend_context="1"} + + {$TITLE} + + {/loop} - {$REF}{$REF}{$TITLE}{$TITLE} - {loop type="auth" name="can_change" roles="ADMIN" permissions="admin.products.edit"} -
- -
- {/loop} +
+ {loop type="auth" name="can_change" roles="ADMIN" permissions="admin.products.edit"} +
+ +
+ {/loop} - {elseloop rel="can_delete"} -
- -
- {/elseloop} -
- {admin_position_block - permission="admin.product.edit" - path={url path='admin/product' category_id=$ID} - url_parameter="product_id" - in_place_edit_class="productPositionChange" - position=$POSITION - id=$ID - } - + {admin_position_block + permission="admin.product.edit" + path={url path='/admin/products/update-position' product_id=$ID} + url_parameter="product_id" + in_place_edit_class="productPositionChange" + position=$POSITION + id=$ID + } + -
- {loop type="auth" name="can_change" roles="ADMIN" permissions="admin.product.edit"} - - {/loop} +
+
+ {loop type="auth" name="can_change" roles="ADMIN" permissions="admin.product.edit"} + + {/loop} - {loop type="auth" name="can_delete" roles="ADMIN" permissions="admin.product.delete"} - - {/loop} -
-
{intl l="This category doesn't contains any products. To add a new product, click the + button above."}
+ {elseloop rel="product_list"} +
{intl l="This category doesn't contains any products. To add a new product, click the + button above."}
-
-
+
- - {module_include location='categories_bottom'} -
- {module_include location='catalog_bottom'} -
+ {module_include location='categories_bottom'}
+ {module_include location='catalog_bottom'} +
- {* Adding a new category *} - {form name="thelia.admin.category.creation"} +{* -- Adding a new category ------------------------------------------------- *} - {* Capture the dialog body, to pass it to the generic dialog *} - {capture "category_creation_dialog"} +{form name="thelia.admin.category.creation"} - {form_hidden_fields form=$form} + {* Capture the dialog body, to pass it to the generic dialog *} + {capture "category_creation_dialog"} - {form_field form=$form field='success_url'} - {* on success, redirect to the edition page, _ID_ is replaced with the created object ID, see controller *} - - {/form_field} + {form_hidden_fields form=$form} - {form_field form=$form field='parent'} - - {/form_field} + {form_field form=$form field='success_url'} + {* on success, redirect to the edition page, _ID_ is replaced with the created object ID, see controller *} + + {/form_field} - {form_field form=$form field='title'} -
- - {loop type="lang" name="default-lang" default_only="1"} -
- - $TITLE -
+ {form_field form=$form field='parent'} + + {/form_field} -
{intl l='Enter here the category name in the default language (%title)' title="$TITLE"}
- - {* Switch edition to the current locale *} - - - {form_field form=$form field='locale'} - - {/form_field} - {/loop} -
- {/form_field} - - {form_field form=$form field='visible'} -
-
- + {form_field form=$form field='title'} +
+ + {loop type="lang" name="default-lang" default_only="1"} +
+ + $TITLE
+ +
{intl l='Enter here the category name in the default language (%title)' title="$TITLE"}
+ + {* Switch edition to the current locale *} + + + {form_field form=$form field='locale'} + + {/form_field} + {/loop} +
+ {/form_field} + + {form_field form=$form field='visible'} +
+
+
- {/form_field} +
+ {/form_field} - {module_include location='category_create_form'} - - {/capture} - - {include - file = "includes/generic-create-dialog.html" - - dialog_id = "category_creation_dialog" - dialog_title = {intl l="Create a new category"} - dialog_body = {$smarty.capture.category_creation_dialog nofilter} - - dialog_ok_label = {intl l="Create this category"} - - form_action = {url path='/admin/categories/create'} - form_enctype = {form_enctype form=$form} - form_error_message = $form_error_message - } - {/form} - - - {* Delete category confirmation dialog *} - - {capture "category_delete_dialog"} - - - {module_include location='category_delete_form'} + {module_include location='category_create_form'} {/capture} {include - file = "includes/generic-confirm-dialog.html" + file = "includes/generic-create-dialog.html" - dialog_id = "category_delete_dialog" - dialog_title = {intl l="Delete category"} - dialog_message = {intl l="Do you really want to delete this category and all its content ?"} + dialog_id = "category_creation_dialog" + dialog_title = {intl l="Create a new category"} + dialog_body = {$smarty.capture.category_creation_dialog nofilter} - form_action = {url path='/admin/categories/delete'} - form_content = {$smarty.capture.category_delete_dialog nofilter} + dialog_ok_label = {intl l="Create this category"} + + form_action = {url path='/admin/categories/create'} + form_enctype = {form_enctype form=$form} + form_error_message = $form_error_message } +{/form} - {* Delete product confirmation dialog *} +{* -- Adding a new product -------------------------------------------------- *} - {capture "product_delete_dialog"} - +{form name="thelia.admin.product.creation"} - {module_include location='product_delete_form'} + {* Capture the dialog body, to pass it to the generic dialog *} + {capture "product_creation_dialog"} + + {form_hidden_fields form=$form} + + {* Be sure to get the category_id, even if the form could not be validated *} + + + {form_field form=$form field='success_url'} + {* on success, redirect to the edition page, _ID_ is replaced with the created object ID, see controller *} + + {/form_field} + + {form_field form=$form field='default_category'} + + {/form_field} + + {form_field form=$form field='ref'} +
+ + +
+ +
+ +
{intl l='Enter here the product reference'}
+
+ {/form_field} + + {form_field form=$form field='title'} +
+ + {loop type="lang" name="default-lang" default_only="1"} +
+ + $TITLE +
+ +
{intl l='Enter here the product name in the default language (%title)' title="$TITLE"}
+ + {* Switch edition to the current locale *} + + + {form_field form=$form field='locale'} + + {/form_field} + {/loop} +
+ {/form_field} + + {form_field form=$form field='visible'} +
+
+ +
+
+ {/form_field} + + {module_include location='product_create_form'} {/capture} {include - file = "includes/generic-confirm-dialog.html" + file = "includes/generic-create-dialog.html" - dialog_id = "product_delete_dialog" - dialog_title = {intl l="Delete product"} - dialog_message = {intl l="Do you really want to delete this product ?"} + dialog_id = "product_creation_dialog" + dialog_title = {intl l="Create a new product"} + dialog_body = {$smarty.capture.product_creation_dialog nofilter} - form_action = {url path='/admin/products/delete'} - form_content = {$smarty.capture.product_delete_dialog nofilter} + dialog_ok_label = {intl l="Create this product"} + + form_action = {url path='/admin/products/create'} + form_enctype = {form_enctype form=$form} + form_error_message = $form_error_message } +{/form} + +{* -- Delete category confirmation dialog ----------------------------------- *} + +{capture "category_delete_dialog"} + + + {module_include location='category_delete_form'} + +{/capture} + +{include + file = "includes/generic-confirm-dialog.html" + + dialog_id = "category_delete_dialog" + dialog_title = {intl l="Delete category"} + dialog_message = {intl l="Do you really want to delete this category and all its content ?"} + + form_action = {url path='/admin/categories/delete'} + form_content = {$smarty.capture.category_delete_dialog nofilter} +} + +{* -- Delete product confirmation dialog ------------------------------------ *} + +{capture "product_delete_dialog"} + + + {module_include location='product_delete_form'} + +{/capture} + +{include + file = "includes/generic-confirm-dialog.html" + + dialog_id = "product_delete_dialog" + dialog_title = {intl l="Delete product"} + dialog_message = {intl l="Do you really want to delete this product ?"} + + form_action = {url path='/admin/products/delete'} + form_content = {$smarty.capture.product_delete_dialog nofilter} +} {/block} {block name="javascript-initialization"} @@ -483,11 +564,15 @@ form_name = "thelia.admin.category.creation" } + {include + file = "includes/generic-js-dialog.html" + dialog_id = "product_creation_dialog" + form_name = "thelia.admin.product.creation" + } + {* Toggle object visibility *} - $(".categoryVisibleToggle").on('switch-change', function(event, data) { - console.log("yaya"); $.ajax({ url : "{url path='admin/categories/toggle-online'}", data : { @@ -497,6 +582,17 @@ }); }); + + $(".productVisibleToggle").on('switch-change', function(event, data) { + $.ajax({ + url : "{url path='admin/products/toggle-online'}", + data : { + product_id : $(this).data('id'), + action : 'visibilityToggle' + } + }); + }); + {* Inline editing of object position using bootstrap-editable *} $('.categoryPositionChange').editable({ @@ -518,6 +614,25 @@ } }); + $('.productPositionChange').editable({ + type : 'text', + title : '{intl l="Enter new product position"}', + mode : 'popup', + inputclass : 'input-mini', + placement : 'left', + success : function(response, newValue) { + // The URL template + var url = "{url path='/admin/products/update-position' product_id='__ID__' position='__POS__'}"; + + // Perform subtitutions + url = url.replace('__ID__', $(this).data('id')) + .replace('__POS__', newValue); + + // Reload the page + location.href = url; + } + }); + {* Change default status *} $('.change-default').change(function(ev) {