From 4aec6f4b3d802e9d5d0fa5e6fea2e96c56ac3f43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Chans=C3=A9aume?= Date: Tue, 15 Jul 2014 11:08:10 +0200 Subject: [PATCH 01/14] Fixed method to display options in the current local and not en_US. --- core/lib/Thelia/Type/ModelValidIdType.php | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/core/lib/Thelia/Type/ModelValidIdType.php b/core/lib/Thelia/Type/ModelValidIdType.php index e3419fdd2..10185fe28 100644 --- a/core/lib/Thelia/Type/ModelValidIdType.php +++ b/core/lib/Thelia/Type/ModelValidIdType.php @@ -13,6 +13,7 @@ namespace Thelia\Type; use Propel\Runtime\ActiveQuery\ModelCriteria; +use Thelia\Core\Translation\Translator; use Thelia\Exception\TypeException; /** @@ -67,8 +68,16 @@ class ModelValidIdType extends BaseType { $queryClass = $this->expectedModelActiveRecordQuery; + $query = $queryClass::create(); + + if (method_exists($query, "joinWithI18n")) { + if (null !== $locale = Translator::getInstance()->getLocale()) { + $query->joinWithI18n($locale); + } + } + $choices = array(); - foreach ($queryClass::create()->find() as $item) { + foreach ($query->find() as $item) { $choices[$item->getId()] = method_exists($item, "getTitle") ? $item->getTitle() : $item->getId(); } From 43517c629a5bad89a025fe898ba805ccd86dabdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Chans=C3=A9aume?= Date: Tue, 15 Jul 2014 14:40:16 +0200 Subject: [PATCH 02/14] Fixed order edit. When a product was deleted, the title was blank. Now we use the order_product title. --- templates/backOffice/default/order-edit.html | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/templates/backOffice/default/order-edit.html b/templates/backOffice/default/order-edit.html index e7a87a5ee..5fe46c037 100644 --- a/templates/backOffice/default/order-edit.html +++ b/templates/backOffice/default/order-edit.html @@ -99,9 +99,15 @@ {/if} - {loop type="product" name="my_product_loop" visible="*" ref="$REF"} - {$TITLE} - {/loop} + {ifloop rel="my_product_loop"} + {loop type="product" name="my_product_loop" visible="*" ref="$REF"} + {$TITLE} + {/loop} + {/ifloop} + {elseloop rel="my_product_loop"} + {* The product doesn't exist anymore *} + {$TITLE} + {/elseloop} {ifloop rel="combinations"}
{loop type="order_product_attribute_combination" name="combinations" order_product=$ID} From a72b6e5ca75c99d2642fe9ca903db11ce61ebacd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Chans=C3=A9aume?= Date: Tue, 15 Jul 2014 14:49:14 +0200 Subject: [PATCH 03/14] Added direct link to the customer page on order detail. --- templates/backOffice/default/order-edit.html | 26 ++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/templates/backOffice/default/order-edit.html b/templates/backOffice/default/order-edit.html index 5fe46c037..0917404e3 100644 --- a/templates/backOffice/default/order-edit.html +++ b/templates/backOffice/default/order-edit.html @@ -168,6 +168,32 @@ + + {loop type="order_address" name="order-invoice-address" id=$INVOICE_ADDRESS} + {assign "orderInvoiceFirstName" $FIRSTNAME} + {assign "orderInvoiceLastName" $LASTNAME} + {assign "orderInvoiceCompany" $COMPANY} + {/loop} +
+ + + + + + + + {if $orderInvoiceCompany} + + + + + {/if} + +
+ {intl l='Customer information'} +
{intl l="Customer"}{$orderInvoiceFirstName|ucwords} {$orderInvoiceLastName|upper}
{intl l="Company"}{$orderInvoiceCompany}
+
+
From 97559fe8c2dea9f1cf88900154fd57d6f0a3b31b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Chans=C3=A9aume?= Date: Tue, 15 Jul 2014 16:39:13 +0200 Subject: [PATCH 04/14] Fixed wrong calculated price when comma is used instead of dot. --- templates/backOffice/default/categories.html | 6 ++++++ templates/backOffice/default/product-edit.html | 8 +++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/templates/backOffice/default/categories.html b/templates/backOffice/default/categories.html index 2cd4e0309..4a8b69d37 100644 --- a/templates/backOffice/default/categories.html +++ b/templates/backOffice/default/categories.html @@ -794,10 +794,16 @@ captureLength: 1, wait : 300, callback : function () { + var price = $(this).val(); + $(this).val(sanitizeFloat(price)); update_price($(this).val(), $(this).data('price-type'), $(this).data('rel-price')); } }); + function sanitizeFloat(numVal) { + return numVal.replace(",", "."); + }; + }); {/block} diff --git a/templates/backOffice/default/product-edit.html b/templates/backOffice/default/product-edit.html index 72df5e344..253b99e60 100644 --- a/templates/backOffice/default/product-edit.html +++ b/templates/backOffice/default/product-edit.html @@ -363,10 +363,16 @@ $(function() { captureLength: 1, wait : 300, callback : function () { - update_price($(this).val(), $(this).data('price-type'), $(this).data('rel-price')); + var price = $(this).val(); + $(this).val(sanitizeFloat(price)); + update_price($(this).val(), $(this).data('price-type'), $(this).data('rel-price')); } }); + function sanitizeFloat(numVal) { + return numVal.replace(",", "."); + }; + // Count generated combinations in real time function countGeneratedCombinations() { From 0310dce719062cf8598aaa215b70513f252cd8c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Chans=C3=A9aume?= Date: Tue, 15 Jul 2014 16:39:51 +0200 Subject: [PATCH 05/14] Revert "Fixed wrong calculated price when comma is used instead of dot." This reverts commit 1a3542ada2dd63f59adeafc0d1f434057d5da927. --- templates/backOffice/default/categories.html | 6 ------ templates/backOffice/default/product-edit.html | 8 +------- 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/templates/backOffice/default/categories.html b/templates/backOffice/default/categories.html index 4a8b69d37..2cd4e0309 100644 --- a/templates/backOffice/default/categories.html +++ b/templates/backOffice/default/categories.html @@ -794,16 +794,10 @@ captureLength: 1, wait : 300, callback : function () { - var price = $(this).val(); - $(this).val(sanitizeFloat(price)); update_price($(this).val(), $(this).data('price-type'), $(this).data('rel-price')); } }); - function sanitizeFloat(numVal) { - return numVal.replace(",", "."); - }; - }); {/block} diff --git a/templates/backOffice/default/product-edit.html b/templates/backOffice/default/product-edit.html index 253b99e60..72df5e344 100644 --- a/templates/backOffice/default/product-edit.html +++ b/templates/backOffice/default/product-edit.html @@ -363,16 +363,10 @@ $(function() { captureLength: 1, wait : 300, callback : function () { - var price = $(this).val(); - $(this).val(sanitizeFloat(price)); - update_price($(this).val(), $(this).data('price-type'), $(this).data('rel-price')); + update_price($(this).val(), $(this).data('price-type'), $(this).data('rel-price')); } }); - function sanitizeFloat(numVal) { - return numVal.replace(",", "."); - }; - // Count generated combinations in real time function countGeneratedCombinations() { From 12e84afdf80af9f31ddb89946a597c3016541035 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Chans=C3=A9aume?= Date: Tue, 15 Jul 2014 16:39:13 +0200 Subject: [PATCH 06/14] Fixed wrong calculated price when comma is used instead of dot. --- templates/backOffice/default/categories.html | 6 ++++++ templates/backOffice/default/product-edit.html | 8 +++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/templates/backOffice/default/categories.html b/templates/backOffice/default/categories.html index 2cd4e0309..4a8b69d37 100644 --- a/templates/backOffice/default/categories.html +++ b/templates/backOffice/default/categories.html @@ -794,10 +794,16 @@ captureLength: 1, wait : 300, callback : function () { + var price = $(this).val(); + $(this).val(sanitizeFloat(price)); update_price($(this).val(), $(this).data('price-type'), $(this).data('rel-price')); } }); + function sanitizeFloat(numVal) { + return numVal.replace(",", "."); + }; + }); {/block} diff --git a/templates/backOffice/default/product-edit.html b/templates/backOffice/default/product-edit.html index 72df5e344..253b99e60 100644 --- a/templates/backOffice/default/product-edit.html +++ b/templates/backOffice/default/product-edit.html @@ -363,10 +363,16 @@ $(function() { captureLength: 1, wait : 300, callback : function () { - update_price($(this).val(), $(this).data('price-type'), $(this).data('rel-price')); + var price = $(this).val(); + $(this).val(sanitizeFloat(price)); + update_price($(this).val(), $(this).data('price-type'), $(this).data('rel-price')); } }); + function sanitizeFloat(numVal) { + return numVal.replace(",", "."); + }; + // Count generated combinations in real time function countGeneratedCombinations() { From 0a99b29015a80ac1ed646026343230b07f8dd33d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Chans=C3=A9aume?= Date: Tue, 15 Jul 2014 17:25:59 +0200 Subject: [PATCH 07/14] added toolbar --- .../default/ajax/product-attributes-tab.html | 10 ++++++++++ .../default/includes/product-prices-tab.html | 14 ++++++++++++-- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/templates/backOffice/default/ajax/product-attributes-tab.html b/templates/backOffice/default/ajax/product-attributes-tab.html index 9e1d56feb..31877f729 100644 --- a/templates/backOffice/default/ajax/product-attributes-tab.html +++ b/templates/backOffice/default/ajax/product-attributes-tab.html @@ -232,6 +232,16 @@ + + {include + file = "includes/inner-form-toolbar.html" + hide_submit_buttons = false + hide_flags = true + + page_url = "{url path='/admin/products/update' product_id=$ID}" + close_url = "{url path='/admin/categories' category_id=$DEFAULT_CATEGORY}" + } + diff --git a/templates/backOffice/default/includes/product-prices-tab.html b/templates/backOffice/default/includes/product-prices-tab.html index 8c55d0fc3..27feea528 100644 --- a/templates/backOffice/default/includes/product-prices-tab.html +++ b/templates/backOffice/default/includes/product-prices-tab.html @@ -242,9 +242,9 @@ {module_include location='product_details_promotion_form'} - + - + {/form} {/if} @@ -454,6 +454,16 @@
{module_include location='product_after_combinations'} + + + {include + file = "includes/inner-form-toolbar.html" + hide_submit_buttons = false + hide_flags = true + page_url = "{url path='/admin/products/update' product_id=$ID}" + close_url = "{url path='/admin/categories' category_id=$DEFAULT_CATEGORY}" + } +
From 3745de8f5781abe83c4c4030e72bca4088bbc4a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Chans=C3=A9aume?= Date: Wed, 16 Jul 2014 09:36:39 +0200 Subject: [PATCH 08/14] fixed position tests on brands --- core/lib/Thelia/Tests/Action/BrandTest.php | 23 ++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/core/lib/Thelia/Tests/Action/BrandTest.php b/core/lib/Thelia/Tests/Action/BrandTest.php index e741e8cb0..5f47feb6b 100644 --- a/core/lib/Thelia/Tests/Action/BrandTest.php +++ b/core/lib/Thelia/Tests/Action/BrandTest.php @@ -155,6 +155,8 @@ class BrandTest extends TestCaseWithURLToolSetup public function testUpdatePositionUp() { + $this->resetBrandPosition(); + $brand = BrandQuery::create() ->filterByPosition(1, Criteria::GREATER_THAN) ->findOne(); @@ -178,6 +180,8 @@ class BrandTest extends TestCaseWithURLToolSetup public function testUpdatePositionDown() { + $this->resetBrandPosition(); + $brand = BrandQuery::create() ->filterByPosition(1) ->findOne(); @@ -201,6 +205,8 @@ class BrandTest extends TestCaseWithURLToolSetup public function testUpdatePositionWithSpecificPosition() { + $this->resetBrandPosition(); + $brand = BrandQuery::create() ->filterByPosition(1, Criteria::GREATER_THAN) ->findOne(); @@ -219,6 +225,23 @@ class BrandTest extends TestCaseWithURLToolSetup $this->assertEquals(1, $updatedBrand->getPosition(),sprintf("new position is 1, new position expected is %d for brand %d", $updatedBrand->getPosition(), $updatedBrand->getId())); } + + /** + * Reorder brand to have proper position + */ + protected function resetBrandPosition() + { + $brands = BrandQuery::create()->find(); + $counter = 1; + + /** @var \Thelia\Model\Brand $brand */ + foreach ($brands as $brand) { + $brand->setPosition($counter); + $brand->save(); + $counter++; + } + } + /** * @return \Thelia\Model\Brand */ From 9900acef84a492c8ce2a590cd18f786254829456 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Chans=C3=A9aume?= Date: Wed, 16 Jul 2014 09:49:43 +0200 Subject: [PATCH 09/14] added ATTRIBUTE_ID and ATTRIBUTE_AVAILABILITY_ID to attribute_combination loop --- core/lib/Thelia/Core/Template/Loop/AttributeCombination.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/lib/Thelia/Core/Template/Loop/AttributeCombination.php b/core/lib/Thelia/Core/Template/Loop/AttributeCombination.php index bae58b38b..dcb7dd050 100644 --- a/core/lib/Thelia/Core/Template/Loop/AttributeCombination.php +++ b/core/lib/Thelia/Core/Template/Loop/AttributeCombination.php @@ -103,6 +103,8 @@ class AttributeCombination extends BaseI18nLoop implements PropelSearchLoopInter $loopResultRow = new LoopResultRow($attributeCombination); $loopResultRow + ->set("ATTRIBUTE_ID", $attributeCombination->getAttributeId()) + ->set("ATTRIBUTE_AVAILABILITY_ID", $attributeCombination->getAttributeAvId()) ->set("LOCALE",$this->locale) ->set("ATTRIBUTE_TITLE", $attributeCombination->getVirtualColumn(AttributeTableMap::TABLE_NAME . '_i18n_TITLE')) ->set("ATTRIBUTE_CHAPO", $attributeCombination->getVirtualColumn(AttributeTableMap::TABLE_NAME . '_i18n_CHAPO')) From bb029cdf35838b65f0703822e53eb6c0de66808c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Chans=C3=A9aume?= Date: Wed, 16 Jul 2014 10:12:02 +0200 Subject: [PATCH 10/14] Fixed the display of the free text value for a feature --- templates/frontOffice/default/product.html | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/templates/frontOffice/default/product.html b/templates/frontOffice/default/product.html index 57289e42c..404291fe2 100644 --- a/templates/frontOffice/default/product.html +++ b/templates/frontOffice/default/product.html @@ -223,7 +223,8 @@
  • {$TITLE} : {loop name="feature_value_info" type="feature_value" feature="{$ID}" product="{product attr="id"}"} - {$TITLE} + {if $LOOP_COUNT > 1}, {else} {/if} + {if $IS_FREE_TEXT == 1}{$FREE_TEXT_VALUE}{else}{$TITLE}{/if} {/loop}
  • {/ifloop} From e9bfe42070f168edf4f8fc52d45d356f129a6d29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Chans=C3=A9aume?= Date: Wed, 16 Jul 2014 12:03:36 +0200 Subject: [PATCH 11/14] fixed sql update script for brands --- setup/update/2.0.3.sql | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/setup/update/2.0.3.sql b/setup/update/2.0.3.sql index d27545f91..ab62ff427 100644 --- a/setup/update/2.0.3.sql +++ b/setup/update/2.0.3.sql @@ -250,9 +250,13 @@ CREATE TABLE `brand_image_i18n` -- Add brand field to product table, and related constraint. -- --------------------------------------------------------- -ALTER TABLE `product` ADD `brand_id` INTEGER DEFAULT 0 AFTER `template_id`; +ALTER TABLE `product` ADD `brand_id` INTEGER AFTER `template_id`; ALTER TABLE `product` ADD CONSTRAINT `fk_product_brand` FOREIGN KEY (`brand_id`) REFERENCES `brand` (`id`) ON DELETE SET NULL; +ALTER TABLE `product_version` ADD `brand_id` INTEGER AFTER `template_id`; +ALTER TABLE `product_version` ADD CONSTRAINT `fk_product_version_brand` FOREIGN KEY (`brand_id`) REFERENCES `brand` (`id`) ON DELETE SET NULL; + + # Add html_output_trim_level config variable # ------------------------------------------ From a1c1f1fb0e7653c4b62f365aca6e57e4e8f670ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Chans=C3=A9aume?= Date: Wed, 16 Jul 2014 15:49:29 +0200 Subject: [PATCH 12/14] Fixed URL::absoluteUrl when an URL contains a target and parameters is not empty --- core/lib/Thelia/Tools/URL.php | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/core/lib/Thelia/Tools/URL.php b/core/lib/Thelia/Tools/URL.php index 330baa178..65a03ed65 100644 --- a/core/lib/Thelia/Tools/URL.php +++ b/core/lib/Thelia/Tools/URL.php @@ -154,6 +154,7 @@ class URL $base = $path; $queryString = ''; + $anchor = ''; if (! is_null($parameters)) { foreach ($parameters as $name => $value) { @@ -167,6 +168,14 @@ class URL if ('' !== $queryString = rtrim($queryString, "&")) { + // url could contain anchor + $pos = strrpos($base, '#'); + if($pos !== false) { + $anchor = substr($base, $pos); + $base = substr($base, 0, $pos); + } + + $base = rtrim($base, "?&"); $sepChar = strstr($base, '?') === false ? '?' : '&'; @@ -174,7 +183,7 @@ class URL $queryString = $sepChar . $queryString; } - return $base . $queryString; + return $base . $queryString . $anchor; } /** From 1d541c0dc28317a1c462f2714b0d641bdeb9edb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Chans=C3=A9aume?= Date: Wed, 16 Jul 2014 16:37:18 +0200 Subject: [PATCH 13/14] Fixed issue on sort order on catalog page. Added price in the product list --- templates/backOffice/default/categories.html | 65 +++++++++++++------- 1 file changed, 44 insertions(+), 21 deletions(-) diff --git a/templates/backOffice/default/categories.html b/templates/backOffice/default/categories.html index 4a8b69d37..13eb59ed5 100644 --- a/templates/backOffice/default/categories.html +++ b/templates/backOffice/default/categories.html @@ -50,7 +50,7 @@ current_order=$category_order order='id' reverse_order='id_reverse' - path={url path='/admin/categories' id_category=$category_id} + path={url path='/admin/catalog' category_id=$category_id} request_parameter_name='category_order' label="{intl l='ID'}" } @@ -63,7 +63,7 @@ current_order=$category_order order='alpha' reverse_order='alpha_reverse' - path={url path='/admin/categories' id_category=$category_id} + path={url path='/admin/catalog' category_id=$category_id} request_parameter_name='category_order' label="{intl l='Category title'}" } @@ -76,7 +76,7 @@ current_order=$category_order order='visible' reverse_order='visible_reverse' - path={url path='/admin/categories' id_category=$category_id} + path={url path='/admin/catalog' category_id=$category_id} request_parameter_name='category_order' label="{intl l='Online'}" } @@ -87,7 +87,7 @@ current_order=$category_order order='manual' reverse_order='manual_reverse' - path={url path='/admin/categories' id_category=$category_id} + path={url path='/admin/catalog' category_id=$category_id} request_parameter_name='category_order' label="{intl l='Position'}" } @@ -187,6 +187,10 @@ {* No product on toplevel category, e.g. id_category = 0 *} + {loop type="currency" name="product-currency" id=$edit_currency_id backend_context="1"} + {$currency_symbol = $SYMBOL} + {/loop} + {if $category_id > 0}
    @@ -218,7 +222,8 @@ current_order=$product_order order='id' reverse_order='id_reverse' - path={url path='/admin/categories' id_category=$category_id target='products'} + path={url path='/admin/catalog' category_id=$category_id target='products'} + request_parameter_name='product_order' label="{intl l='ID'}" } @@ -229,7 +234,8 @@ current_order=$product_order order='ref' reverse_order='ref_reverse' - path={url path='/admin/categories' id_category=$category_id target='products'} + path={url path='/admin/catalog' category_id=$category_id target='products'} + request_parameter_name='product_order' label="{intl l='Reference'}" } @@ -239,28 +245,42 @@ current_order=$product_order order='alpha' reverse_order='alpha_reverse' - path={url path='/admin/categories' id_category=$category_id target='products'} + path={url path='/admin/catalog' category_id=$category_id target='products'} + request_parameter_name='product_order' label="{intl l='Product title'}" } {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='min_price' + reverse_order='max_price' + path={url path='/admin/catalog' category_id=$category_id target='products'} + request_parameter_name='product_order' + label="{intl l='Price'}" + } - + + {admin_sortable_header + current_order=$product_order + order='visible' + reverse_order='visible_reverse' + path={url path='/admin/catalog' category_id=$category_id target='products'} + request_parameter_name='product_order' + 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'} + path={url path='/admin/catalog' category_id=$category_id target='products'} + request_parameter_name='product_order' label="{intl l='Position'}" } @@ -280,6 +300,7 @@ {$TITLE} {/loop} + {$REF} @@ -287,7 +308,9 @@ {module_include location='product_list_row'} - + {format_money number=$BEST_PRICE symbol=$currency_symbol} + + {loop type="auth" name="can_change" role="ADMIN" resource="admin.product" access="UPDATE"}
    @@ -301,7 +324,7 @@ {/elseloop} - + {admin_position_block resource="admin.product" access="UPDATE" @@ -329,13 +352,13 @@ - + {include file = "includes/pagination.html" loop_ref = "product_list" max_page_count = 10 - page_url = "{url path="/admin/categories" category_id=$category_id product_order=$product_order}" + page_url = "{url path="/admin/catalog" category_id=$category_id product_order=$product_order}" } From 800a1c3f5dc584118f5dee12753e21a4189f7380 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Chans=C3=A9aume?= Date: Wed, 16 Jul 2014 16:45:42 +0200 Subject: [PATCH 14/14] Added product reference in the title of the product edit page --- templates/backOffice/default/product-edit.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/backOffice/default/product-edit.html b/templates/backOffice/default/product-edit.html index 253b99e60..715c0545f 100644 --- a/templates/backOffice/default/product-edit.html +++ b/templates/backOffice/default/product-edit.html @@ -26,7 +26,7 @@
    - {intl l='Edit product %title' title={$TITLE}} + {intl l='Edit product %title' title={$TITLE}}{if $REF} ({intl l='ref.:'} {$REF}){/if}