You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ofbiz.apache.org by de...@apache.org on 2017/12/30 09:47:02 UTC

svn commit: r1819581 - in /ofbiz/ofbiz-plugins/branches/release17.12: ./ ecommerce/template/catalog/ConfigProductDetail.ftl

Author: deepak
Date: Sat Dec 30 09:47:02 2017
New Revision: 1819581

URL: http://svn.apache.org/viewvc?rev=1819581&view=rev
Log:
Merge from trunk revision:1819580

Improved: Update markup of Configure product page according to standard markup given by Bootstrap v4.0.0 (OFBIZ-10110)
Thanks Nitish Mishra for your contribution

Modified:
    ofbiz/ofbiz-plugins/branches/release17.12/   (props changed)
    ofbiz/ofbiz-plugins/branches/release17.12/ecommerce/template/catalog/ConfigProductDetail.ftl

Propchange: ofbiz/ofbiz-plugins/branches/release17.12/
------------------------------------------------------------------------------
--- svn:mergeinfo (original)
+++ svn:mergeinfo Sat Dec 30 09:47:02 2017
@@ -10,4 +10,4 @@
 /ofbiz/branches/json-integration-refactoring/plugins:1634077-1635900
 /ofbiz/branches/multitenant20100310/plugins:921280-927264
 /ofbiz/branches/release13.07/plugins:1547657
-/ofbiz/ofbiz-plugins/trunk:1819576
+/ofbiz/ofbiz-plugins/trunk:1819576,1819580

Modified: ofbiz/ofbiz-plugins/branches/release17.12/ecommerce/template/catalog/ConfigProductDetail.ftl
URL: http://svn.apache.org/viewvc/ofbiz/ofbiz-plugins/branches/release17.12/ecommerce/template/catalog/ConfigProductDetail.ftl?rev=1819581&r1=1819580&r2=1819581&view=diff
==============================================================================
--- ofbiz/ofbiz-plugins/branches/release17.12/ecommerce/template/catalog/ConfigProductDetail.ftl (original)
+++ ofbiz/ofbiz-plugins/branches/release17.12/ecommerce/template/catalog/ConfigProductDetail.ftl Sat Dec 30 09:47:02 2017
@@ -160,407 +160,483 @@ ${virtualJavaScript!}
   -->
 </script>
 
-<div id="productdetail">
-<table>
-<#-- Category next/previous -->
-<#if category??>
-  <tr>
-    <td colspan="2" align="right">
-      <#if previousProductId??>
-        <a href='<@o...@ofbizUrl>'
-           class="buttontext">${uiLabelMap.CommonPrevious}</a>&nbsp;|&nbsp;
-      </#if>
-      <a href="<@o...@ofbizUrl>" class="buttontext">
-      ${(category.categoryName)?default(category.description)!}
-      </a>
-      <#if nextProductId??>&nbsp;|&nbsp;
-        <a href='<@o...@ofbizUrl>'
-           class="buttontext">${uiLabelMap.CommonNext}</a>
-      </#if>
-    </td>
-  </tr>
-</#if>
-  <tr>
-    <td colspan="2"></td>
-  </tr>
-<#-- Product image/name/price -->
-  <tr>
-    <td valign="top" width="0">
-    <#assign productLargeImageUrl = productContentWrapper.get("LARGE_IMAGE_URL", "url")!>
-    <#-- remove the next two lines to always display the virtual image first (virtual images must exist) -->
-    <#if firstLargeImage?has_content>
-      <#assign productLargeImageUrl = firstLargeImage>
-    </#if>
-    <#if productLargeImageUrl?string?has_content>
-      <a href="javascript:popupDetail();">
-        <img src='<@o...@ofbizContentUrl>'
-             name='mainImage' vspace='5' hspace='5' class='cssImgXLarge' align='left' alt=""/>
-      </a>
-    </#if>
-    </td>
-    <td align="right" valign="top">
-      <h2>${productContentWrapper.get("PRODUCT_NAME", "html")!}</h2>
-      <div>${productContentWrapper.get("DESCRIPTION", "html")!}</div>
-      <div><b>${product.productId!}</b></div>
-    <#-- example of showing a certain type of feature with the product -->
-    <#if sizeProductFeatureAndAppls?has_content>
-      <div>
-        <#if (sizeProductFeatureAndAppls?size == 1)>
-        <#-- TODO : i18n -->
-          Size:
-        <#else>
-          Sizes Available:
+<div id="productdetail" class="card">
+  <table class="table table-responsive-sm">
+  <#-- Category next/previous -->
+  <#if category??>
+    <tr>
+      <td colspan="2" align="right">
+        <#if previousProductId??>
+          <a href='<@o...@ofbizUrl>'
+             class="buttontext">${uiLabelMap.CommonPrevious}</a>&nbsp;|&nbsp;
         </#if>
-        <#list sizeProductFeatureAndAppls as sizeProductFeatureAndAppl>
-        ${sizeProductFeatureAndAppl.description?default(sizeProductFeatureAndAppl.abbrev?default(
-        sizeProductFeatureAndAppl.productFeatureId))}
-          <#if sizeProductFeatureAndAppl_has_next>,</#if>
-        </#list>
-      </div>
-    </#if>
-    <#-- for prices:
-            - if totalPrice is present, use it (totalPrice is the price calculated from the parts)
-            - if price < competitivePrice, show competitive or "Compare At" price
-            - if price < listPrice, show list price
-            - if price < defaultPrice and defaultPrice < listPrice, show default
-            - if isSale show price with salePrice style and print "On Sale!"
-    -->
-    <#if totalPrice??>
-      <div>${uiLabelMap.ProductAggregatedPrice}:
-        <span id='totalPrice' class='basePrice'>
-          <@ofbizCurrency amount=totalPrice isoCode=totalPrice.currencyUsed/>
-        </span>
-      </div>
-    <#else>
-      <#if price.competitivePrice?? && price.price?? && price.price < price.competitivePrice>
-        <div>${uiLabelMap.ProductCompareAtPrice}:
-          <span class='basePrice'>
-            <@ofbizCurrency amount=price.competitivePrice isoCode=price.currencyUsed/>
-          </span>
+        <a href="<@o...@ofbizUrl>" class="buttontext">
+        ${(category.categoryName)?default(category.description)!}
+        </a>
+        <#if nextProductId??>&nbsp;|&nbsp;
+          <a href='<@o...@ofbizUrl>'
+             class="buttontext">${uiLabelMap.CommonNext}</a>
+        </#if>
+      </td>
+    </tr>
+  </#if>
+  <#-- Product image/name/price -->
+    <tr>
+      <td>
+      <#assign productLargeImageUrl = productContentWrapper.get("LARGE_IMAGE_URL", "url")!>
+      <#-- remove the next two lines to always display the virtual image first (virtual images must exist) -->
+      <#if firstLargeImage?has_content>
+        <#assign productLargeImageUrl = firstLargeImage>
+      </#if>
+      <#if productLargeImageUrl?string?has_content>
+        <a href="javascript:popupDetail();">
+          <img src='<@o...@ofbizContentUrl>' alt=""/>
+        </a>
+      </#if>
+      </td>
+      <td>
+        <h2>${productContentWrapper.get("PRODUCT_NAME", "html")!}</h2>
+        <div>${productContentWrapper.get("DESCRIPTION", "html")!}</div>
+        <div><b>${product.productId!}</b></div>
+      <#-- example of showing a certain type of feature with the product -->
+      <#if sizeProductFeatureAndAppls?has_content>
+        <div>
+          <#if (sizeProductFeatureAndAppls?size == 1)>
+          <#-- TODO : i18n -->
+            Size:
+          <#else>
+            Sizes Available:
+          </#if>
+          <#list sizeProductFeatureAndAppls as sizeProductFeatureAndAppl>
+          ${sizeProductFeatureAndAppl.description?default(sizeProductFeatureAndAppl.abbrev?default(
+          sizeProductFeatureAndAppl.productFeatureId))}
+            <#if sizeProductFeatureAndAppl_has_next>,</#if>
+          </#list>
         </div>
       </#if>
-      <#if price.listPrice?? && price.price?? && price.price < price.listPrice>
-        <div>${uiLabelMap.ProductListPrice}:
-          <span class='basePrice'>
-            <@ofbizCurrency amount=price.listPrice isoCode=price.currencyUsed/>
+      <#-- for prices:
+              - if totalPrice is present, use it (totalPrice is the price calculated from the parts)
+              - if price < competitivePrice, show competitive or "Compare At" price
+              - if price < listPrice, show list price
+              - if price < defaultPrice and defaultPrice < listPrice, show default
+              - if isSale show price with salePrice style and print "On Sale!"
+      -->
+      <#if totalPrice??>
+        <div>${uiLabelMap.ProductAggregatedPrice}:
+          <span id='totalPrice' class='basePrice'>
+            <@ofbizCurrency amount=totalPrice isoCode=totalPrice.currencyUsed/>
           </span>
         </div>
+      <#else>
+        <#if price.competitivePrice?? && price.price?? && price.price < price.competitivePrice>
+          <div>${uiLabelMap.ProductCompareAtPrice}:
+            <span class='basePrice'>
+              <@ofbizCurrency amount=price.competitivePrice isoCode=price.currencyUsed/>
+            </span>
+          </div>
+        </#if>
+        <#if price.listPrice?? && price.price?? && price.price < price.listPrice>
+          <div>${uiLabelMap.ProductListPrice}:
+            <span class='basePrice'>
+              <@ofbizCurrency amount=price.listPrice isoCode=price.currencyUsed/>
+            </span>
+          </div>
+        </#if>
+        <#if price.listPrice?? && price.defaultPrice?? && price.price?? &&
+        price.price < price.defaultPrice && price.defaultPrice < price.listPrice>
+          <div>${uiLabelMap.ProductRegularPrice}:
+            <span class='basePrice'>
+              <@ofbizCurrency amount=price.defaultPrice isoCode=price.currencyUsed/>
+            </span>
+          </div>
+        </#if>
+        <div>
+          <#if price.isSale?? && price.isSale>
+            <span class='salePrice'>${uiLabelMap.OrderOnSale}!</span>
+            <#assign priceStyle = "salePrice">
+          <#else>
+            <#assign priceStyle = "regularPrice">
+          </#if>
+        ${uiLabelMap.OrderYourPrice}:
+          <#if "Y" = product.isVirtual!> from </#if>
+            <span class='${priceStyle}'>
+              <@ofbizCurrency amount=price.price isoCode=price.currencyUsed/>
+            </span>
+        </div>
+        <#if price.listPrice?? && price.price?? && price.price < price.listPrice>
+          <#assign priceSaved = price.listPrice - price.price>
+          <#assign percentSaved = (priceSaved / price.listPrice) * 100>
+          <div>${uiLabelMap.OrderSave}:
+            <span class="basePrice">
+              <@ofbizCurrency amount=priceSaved isoCode=price.currencyUsed/> (${percentSaved?int}%)
+            </span>
+          </div>
+        </#if>
       </#if>
-      <#if price.listPrice?? && price.defaultPrice?? && price.price?? &&
-      price.price < price.defaultPrice && price.defaultPrice < price.listPrice>
-        <div>${uiLabelMap.ProductRegularPrice}:
-          <span class='basePrice'>
-            <@ofbizCurrency amount=price.defaultPrice isoCode=price.currencyUsed/>
-          </span>
+      <#-- Included quantities/pieces -->
+      <#if product.quantityIncluded?? && product.quantityIncluded != 0>
+        <div>
+          ${uiLabelMap.OrderIncludes}:${product.quantityIncluded!}${product.quantityUomId!}
         </div>
       </#if>
-      <div>
-        <#if price.isSale?? && price.isSale>
-          <span class='salePrice'>${uiLabelMap.OrderOnSale}!</span>
-          <#assign priceStyle = "salePrice">
-        <#else>
-          <#assign priceStyle = "regularPrice">
-        </#if>
-      ${uiLabelMap.OrderYourPrice}:
-        <#if "Y" = product.isVirtual!> from </#if>
-          <span class='${priceStyle}'>
-            <@ofbizCurrency amount=price.price isoCode=price.currencyUsed/>
-          </span>
-      </div>
-      <#if price.listPrice?? && price.price?? && price.price < price.listPrice>
-        <#assign priceSaved = price.listPrice - price.price>
-        <#assign percentSaved = (priceSaved / price.listPrice) * 100>
-        <div>${uiLabelMap.OrderSave}:
-          <span class="basePrice">
-            <@ofbizCurrency amount=priceSaved isoCode=price.currencyUsed/> (${percentSaved?int}%)
-          </span>
+      <#if product.piecesIncluded?? && product.piecesIncluded?long != 0>
+        <div>
+          ${uiLabelMap.OrderPieces}:${product.piecesIncluded}
+        </div>
+      </#if>
+      <#if daysToShip??>
+        <div>
+          ${uiLabelMap.ProductUsuallyShipsIn} ${daysToShip} ${uiLabelMap.CommonDays}
         </div>
       </#if>
-    </#if>
-    <#-- Included quantities/pieces -->
-    <#if product.quantityIncluded?? && product.quantityIncluded != 0>
-      <div>
-        ${uiLabelMap.OrderIncludes}:${product.quantityIncluded!}${product.quantityUomId!}
-      </div>
-    </#if>
-    <#if product.piecesIncluded?? && product.piecesIncluded?long != 0>
-      <div>
-        ${uiLabelMap.OrderPieces}:${product.piecesIncluded}
-      </div>
-    </#if>
-    <#if daysToShip??>
-      <div>
-        ${uiLabelMap.ProductUsuallyShipsIn} ${daysToShip} ${uiLabelMap.CommonDays}
-      </div>
-    </#if>
-
-    <#-- show tell a friend details only in ecommerce application -->
-      <div>&nbsp;</div>
-      <div>
-        <a href="javascript:popUpSmall('<@o...@ofbizUrl>','tellafriend');"
-            class="buttontext">${uiLabelMap.CommonTellAFriend}</a>
-      </div>
 
-    <#if disFeatureList?? && 0 < disFeatureList.size()>
-      <p>&nbsp;</p>
-      <#list disFeatureList as currentFeature>
+      <#-- show tell a friend details only in ecommerce application -->
+        <div>&nbsp;</div>
         <div>
-        ${currentFeature.productFeatureTypeId}:&nbsp;${currentFeature.description}
+          <a href="javascript:popUpSmall('<@o...@ofbizUrl>','tellafriend');">${uiLabelMap.CommonTellAFriend}</a>
         </div>
-      </#list>
-      <div>&nbsp;</div>
-    </#if>
 
-      <form method="post"
-          action="<@ofbizUrl>additem<#if requestAttributes._CURRENT_VIEW_??>/${requestAttributes._CURRENT_VIEW_}</#...@ofbizUrl>"
-          name="addform" style='margin: 0;'>
-      <#assign inStock = true>
-      <#-- Variant Selection -->
-      <#if product.isVirtual?? && "Y" == product.isVirtual?upper_case>
-        <#if variantTree?? && 0 < variantTree.size()>
-          <#list featureSet as currentType>
-            <div>
-              <select name="FT${currentType}" onchange="javascript:getList(this.name, (this.selectedIndex-1), 1);">
-                <option>${featureTypes.get(currentType)}</option>
-              </select>
-            </div>
-          </#list>
-          <input type='hidden' name="product_id" value='${product.productId}'/>
-          <input type='hidden' name="add_product_id" value='NULL'/>
+      <#if disFeatureList?? && 0 < disFeatureList.size()>
+        <p>&nbsp;</p>
+        <#list disFeatureList as currentFeature>
+          <div>
+          ${currentFeature.productFeatureTypeId}:&nbsp;${currentFeature.description}
+          </div>
+        </#list>
+        <div>&nbsp;</div>
+      </#if>
+
+        <form method="post"
+            action="<@ofbizUrl>additem<#if requestAttributes._CURRENT_VIEW_??>/${requestAttributes._CURRENT_VIEW_}</#...@ofbizUrl>"
+            name="addform">
+        <#assign inStock = true>
+        <#-- Variant Selection -->
+        <#if product.isVirtual?? && "Y" == product.isVirtual?upper_case>
+          <#if variantTree?? && 0 < variantTree.size()>
+            <#list featureSet as currentType>
+              <div>
+                <select name="FT${currentType}" onchange="javascript:getList(this.name, (this.selectedIndex-1), 1);">
+                  <option>${featureTypes.get(currentType)}</option>
+                </select>
+              </div>
+            </#list>
+            <input type='hidden' name="product_id" value='${product.productId}'/>
+            <input type='hidden' name="add_product_id" value='NULL'/>
+          <#else>
+            <input type='hidden' name="product_id" value='${product.productId}'/>
+            <input type='hidden' name="add_product_id" value='NULL'/>
+            <div class='tabletext'><b>${uiLabelMap.ProductItemOutOfStock}.</b></div>
+            <#assign inStock = false>
+          </#if>
         <#else>
           <input type='hidden' name="product_id" value='${product.productId}'/>
-          <input type='hidden' name="add_product_id" value='NULL'/>
-          <div class='tabletext'><b>${uiLabelMap.ProductItemOutOfStock}.</b></div>
-          <#assign inStock = false>
+          <input type='hidden' name="add_product_id" value='${product.productId}'/>
+          <#if productNotAvailable??>
+            <#assign isStoreInventoryRequired =
+            Static["org.apache.ofbiz.product.store.ProductStoreWorker"]
+            .isStoreInventoryRequired(request, product)>
+            <#if isStoreInventoryRequired>
+              <div class='tabletext'><b>${uiLabelMap.ProductItemOutOfStock}.</b></div>
+              <#assign inStock = false>
+            <#else>
+              <div class='tabletext'><b>${product.inventoryMessage!}</b></div>
+            </#if>
+          </#if>
         </#if>
+      </td>
+    </tr>
+    <tr>
+      <td>
+      <#-- check to see if introductionDate hasn't passed yet -->
+      <#if product.introductionDate?? && nowTimestamp.before(product.introductionDate)>
+        <p>&nbsp;</p>
+        <div class='tabletext' style='color: red;'>${uiLabelMap.ProductProductNotYetMadeAvailable}.</div>
+      <#-- check to see if salesDiscontinuationDate has passed -->
+      <#elseif product.salesDiscontinuationDate?? && nowTimestamp.after(product.salesDiscontinuationDate)>
+        <div class='tabletext' style='color: red;'>${uiLabelMap.ProductProductNoLongerAvailable}.</div>
+      <#-- check to see if the product requires inventory check and has inventory -->
       <#else>
-        <input type='hidden' name="product_id" value='${product.productId}'/>
-        <input type='hidden' name="add_product_id" value='${product.productId}'/>
-        <#if productNotAvailable??>
-          <#assign isStoreInventoryRequired =
-          Static["org.apache.ofbiz.product.store.ProductStoreWorker"]
-          .isStoreInventoryRequired(request, product)>
-          <#if isStoreInventoryRequired>
-            <div class='tabletext'><b>${uiLabelMap.ProductItemOutOfStock}.</b></div>
-            <#assign inStock = false>
+        <#if inStock>
+          <#if "Y" == product.requireAmount?default("N")>
+            <#assign hiddenStyle = "visible">
           <#else>
-            <div class='tabletext'><b>${product.inventoryMessage!}</b></div>
+            <#assign hiddenStyle = "hidden">
           </#if>
-        </#if>
-      </#if>
-    </td>
-  </tr>
-  <tr>
-    <td colspan="2" align="right">
-    <#-- check to see if introductionDate hasn't passed yet -->
-    <#if product.introductionDate?? && nowTimestamp.before(product.introductionDate)>
-      <p>&nbsp;</p>
-      <div class='tabletext' style='color: red;'>${uiLabelMap.ProductProductNotYetMadeAvailable}.</div>
-    <#-- check to see if salesDiscontinuationDate has passed -->
-    <#elseif product.salesDiscontinuationDate?? && nowTimestamp.after(product.salesDiscontinuationDate)>
-      <div class='tabletext' style='color: red;'>${uiLabelMap.ProductProductNoLongerAvailable}.</div>
-    <#-- check to see if the product requires inventory check and has inventory -->
-    <#else>
-      <#if inStock>
-        <#if "Y" == product.requireAmount?default("N")>
-          <#assign hiddenStyle = "visible">
-        <#else>
-          <#assign hiddenStyle = "hidden">
-        </#if>
-        <div id="add_amount" class="${hiddenStyle}">
-          <span style="white-space: nowrap;"><b>Amount:</b></span>&nbsp;
-          <input type="text" size="5" name="add_amount" value=""/>
-        </div>
-        <#if !configwrapper.isCompleted()>
-          <div>[${uiLabelMap.EcommerceProductNotConfigured}]&nbsp;
-            <input type="text" size="5" name="quantity" value="0" disabled="disabled"/></div>
-        <#else>
-          <a href="javascript:addItem()" class="buttontext">
-                <span style="white-space: nowrap;">
-                ${uiLabelMap.OrderAddToCart}
-                </span>
-          </a>&nbsp;
-          <input type="text" size="5" name="quantity" value="1"/>
-          <#if minimumQuantity?? &&  minimumQuantity &gt; 0>
-            Minimum order quantity is ${minimumQuantity}.
+          <div id="add_amount" class="${hiddenStyle}">
+            <span style="white-space: nowrap;"><b>Amount:</b></span>&nbsp;
+            <input type="text" size="5" name="add_amount" value=""/>
+          </div>
+          <#if !configwrapper.isCompleted()>
+            <div>[${uiLabelMap.EcommerceProductNotConfigured}]&nbsp;
+              <input type="text" size="5" name="quantity" value="0" disabled="disabled"/></div>
+          <#else>
+            <div class="input-group"><input type="text" size="5" name="quantity" value="1" class="form-control form-control-sm"/>
+            <span class="input-group-button">
+            <a href="javascript:addItem()" class="btn btn-outline-secondary btn-sm">
+                  ${uiLabelMap.OrderAddToCart}
+            </a>
+            </span>
+            </div>
+            <#if minimumQuantity?? &&  minimumQuantity &gt; 0>
+              Minimum order quantity is ${minimumQuantity}.
+            </#if>
           </#if>
         </#if>
+        <#if requestParameters.category_id??>
+          <input type='hidden' name='category_id' value='${requestParameters.category_id}'/>
+        </#if>
       </#if>
-      <#if requestParameters.category_id??>
-        <input type='hidden' name='category_id' value='${requestParameters.category_id}'/>
-      </#if>
-    </#if>
-      </form>
-      <div>
-      <#if sessionAttributes.userLogin?has_content && sessionAttributes.userLogin.userLoginId != "anonymous">
-        <form name="addToShoppingList" method="post"
-              action="<@ofbizUrl>addItemToShoppingList<#if requestAttributes._CURRENT_VIEW_??>/${requestAttributes._CURRENT_VIEW_}</#...@ofbizUrl>">
-          <input type="hidden" name="productId" value="${product.productId}"/>
-          <input type="hidden" name="product_id" value="${product.productId}"/>
-          <input type="hidden" name="configId" value="${configId!}"/>
-          <select name="shoppingListId">
-            <#if shoppingLists?has_content>
-              <#list shoppingLists as shoppingList>
-                <option value="${shoppingList.shoppingListId}">${shoppingList.listName}</option>
-              </#list>
-            </#if>
-            <option value="">---</option>
-            <option value="">${uiLabelMap.OrderNewShoppingList}</option>
-          </select>
-          &nbsp;&nbsp;
-          <input type="text" size="5" name="quantity" value="1"/>
-          <a href="javascript:document.addToShoppingList.submit();" class="buttontext">
-            [${uiLabelMap.OrderAddToShoppingList}]
-          </a>
         </form>
-      <#else> <br/>
-      ${uiLabelMap.OrderYouMust}
-        <a href="<@o...@ofbizUrl>" class="buttontext">${uiLabelMap.CommonBeLogged}</a>
-      ${uiLabelMap.OrderToAddSelectedItemsToShoppingList}.&nbsp;
-      </#if>
-      </div>
-    <#-- Prefill first select box (virtual products only) -->
-    <#if variantTree?? && 0 < variantTree.size()>
-      <script language="JavaScript" type="text/javascript">eval("list" + "${featureOrderFirst}" + "()");</script>
-    </#if>
-
-    <#-- Swatches (virtual products only) -->
-    <#if variantSample?? && 0 < variantSample.size()>
-      <#assign imageKeys = variantSample.keySet()>
-      <#assign imageMap = variantSample>
-      <p>&nbsp;</p>
-      <table cellspacing="0" cellpadding="0">
+        </td>
+        </tr>
         <tr>
-          <#assign maxIndex = 7>
-          <#assign indexer = 0>
-          <#list imageKeys as key>
-            <#assign swatchProduct = imageMap.get(key)>
-            <#if swatchProduct?has_content && indexer < maxIndex>
-              <#assign imageUrl =
-              Static["org.apache.ofbiz.product.product.ProductContentWrapper"]
-              .getProductContentAsText(swatchProduct, "SMALL_IMAGE_URL", request, "url")!>
-              <#if !imageUrl?string?has_content>
-                <#assign imageUrl = productContentWrapper.get("SMALL_IMAGE_URL", "url")!>
-              </#if>
-              <#if !imageUrl?string?has_content>
-                <#assign imageUrl = "/images/defaultImage.jpg">
-              </#if>
-              <td align="center" valign="bottom">
-                <a href="javascript:getList('FT${featureOrderFirst}','${indexer}',1);">
-                  <img src="<@o...@ofbizContentUrl>"
-                       class='cssImgSmall' alt=""/>
-                </a>
-                <br/>
-                <a href="javascript:getList('FT${featureOrderFirst}','${indexer}',1);" class="buttontext">
-                ${key}
-                </a>
-              </td>
+          <td>
+            <#if sessionAttributes.userLogin?has_content && sessionAttributes.userLogin.userLoginId != "anonymous">
+              <form name="addToShoppingList" method="post"
+                    action="<@ofbizUrl>addItemToShoppingList<#if requestAttributes._CURRENT_VIEW_??>/${requestAttributes._CURRENT_VIEW_}</#...@ofbizUrl>">
+                <input type="hidden" name="productId" value="${product.productId}"/>
+                <input type="hidden" name="product_id" value="${product.productId}"/>
+                <input type="hidden" name="configId" value="${configId!}"/>
+                <div class="row">
+                  <div class="col-5">
+                    <select name="shoppingListId" class="form-control form-control-sm">
+                      <#if shoppingLists?has_content>
+                        <#list shoppingLists as shoppingList>
+                          <option value="${shoppingList.shoppingListId}">${shoppingList.listName}</option>
+                        </#list>
+                      </#if>
+                      <option value="">---</option>
+                      <option value="">${uiLabelMap.OrderNewShoppingList}</option>
+                    </select>
+                  </div>
+                  <div class="col-5">
+                    <input type="text" size="5" name="quantity" value="1" class="form-control form-control-sm"/>
+                  </div>
+                  <div class="col-2">
+                    <a href="javascript:document.addToShoppingList.submit();" class="btn btn-outline-secondary btn-sm">
+                      [${uiLabelMap.OrderAddToShoppingList}]
+                    </a>
+                  </div>
+                </div>
+              </form>
+            <#else>
+            ${uiLabelMap.OrderYouMust}
+              <a href="<@o...@ofbizUrl>" class="buttontext">${uiLabelMap.CommonBeLogged}</a>
+            ${uiLabelMap.OrderToAddSelectedItemsToShoppingList}.&nbsp;
             </#if>
-            <#assign indexer = indexer + 1>
-          </#list>
-          <#if (indexer > maxIndex)>
-            <div><b>${uiLabelMap.OrderMoreOptionsAvailable}.</b></div>
-          </#if>
+          </td>
         </tr>
-      </table>
-    </#if>
-    </td>
-  </tr>
-<#-- Long description of product -->
-  <tr>
-    <td colspan="2">
-      <div>${productContentWrapper.get("LONG_DESCRIPTION", "html")!}</div>
-    </td>
-  </tr>
-  <tr>
-    <td colspan="2">
-      <hr class='sepbar'/>
-    </td>
-  </tr>
-<#-- Any attributes/etc may go here -->
-<#-- Product Configurator -->
-  <tr>
-    <td colspan="2">
-      <form name="configform" id="configFormId" method="post"
-            action="<@ofbizUrl>product<#if requestAttributes._CURRENT_VIEW_??>/${requestAttributes._CURRENT_VIEW_}</#...@ofbizUrl>">
-        <input type='hidden' name='add_product_id' value='${product.productId}'/>
-        <input type='hidden' name='add_category_id' value=''/>
-        <input type='hidden' name='quantity' value='1'/>
-
-        <input type='hidden' name='product_id' value='${product.productId}'/>
-        <table>
-          <tr>
-            <td>
-              <div>
-                <a href="javascript:verifyConfig();" class="buttontext">${uiLabelMap.OrderVerifyConfiguration}</a>
-              </div>
-            </td>
-          </tr>
+        <tr>
+        <td>
+      <#-- Prefill first select box (virtual products only) -->
+      <#if variantTree?? && 0 < variantTree.size()>
+        <script language="JavaScript" type="text/javascript">eval("list" + "${featureOrderFirst}" + "()");</script>
+      </#if>
 
-        <#assign counter = 0>
-        <#assign questions = configwrapper.questions>
-        <#list questions as question>
+      <#-- Swatches (virtual products only) -->
+      <#if variantSample?? && 0 < variantSample.size()>
+        <#assign imageKeys = variantSample.keySet()>
+        <#assign imageMap = variantSample>
+        <p>&nbsp;</p>
+        <table class="table">
           <tr>
-            <td>
-              <div>${question.question}</div>
-              <#if question.isFirst()>
-                <a name='#${question.getConfigItem().getString("configItemId")}'></a>
-                <div>${StringUtil.wrapString(question.description!)}</div>
-                <#assign instructions = question.content.get("INSTRUCTIONS", "html")!>
-                <#if instructions?has_content>
-                  <a href="javascript:showErrorAlert("${uiLabelMap.CommonErrorMessage2}","${instructions}");"
-                  class="buttontext">Instructions</a>
+            <#assign maxIndex = 7>
+            <#assign indexer = 0>
+            <#list imageKeys as key>
+              <#assign swatchProduct = imageMap.get(key)>
+              <#if swatchProduct?has_content && indexer < maxIndex>
+                <#assign imageUrl =
+                Static["org.apache.ofbiz.product.product.ProductContentWrapper"]
+                .getProductContentAsText(swatchProduct, "SMALL_IMAGE_URL", request, "url")!>
+                <#if !imageUrl?string?has_content>
+                  <#assign imageUrl = productContentWrapper.get("SMALL_IMAGE_URL", "url")!>
                 </#if>
-                <#assign image = question.content.get("IMAGE_URL", "url")!>
-                <#if image?string?has_content>
-                  <img src='<@o...@ofbizContentUrl>'
-                       vspace='5' hspace='5' class='cssImgXLarge' align='left' alt=""/>
+                <#if !imageUrl?string?has_content>
+                  <#assign imageUrl = "/images/defaultImage.jpg">
                 </#if>
-              <#else>
-                <div>
-                  <a href='#${question.getConfigItem().getString("configItemId")}' class="buttontext">Details</a>
-                </div>
+                <td align="center" valign="bottom">
+                  <a href="javascript:getList('FT${featureOrderFirst}','${indexer}',1);">
+                    <img src="<@o...@ofbizContentUrl>"
+                         class='cssImgSmall' alt=""/>
+                  </a>
+                  <br/>
+                  <a href="javascript:getList('FT${featureOrderFirst}','${indexer}',1);" class="buttontext">
+                  ${key}
+                  </a>
+                </td>
               </#if>
-            </td>
+              <#assign indexer = indexer + 1>
+            </#list>
+            <#if (indexer > maxIndex)>
+              <div><b>${uiLabelMap.OrderMoreOptionsAvailable}.</b></div>
+            </#if>
           </tr>
-          <tr>
-            <td>
-              <#if question.isStandard()>
-              <#-- Standard item: all the options are always included -->
-                <#assign options = question.options>
-                <#list options as option>
-                  <div>${option.description} <#if !option.isAvailable()> (*)</#if></div>
-                </#list>
-              <#else>
-                <#if question.isSingleChoice()>
-                <#-- Single choice question -->
-                  <#assign options = question.options>
-                  <#assign selectedOption = question.getSelected()!>
-                  <#assign selectedPrice = 0.0>
-                  <#if selectedOption?has_content>
-                    <#assign selectedPrice = selectedOption.getPrice()>
+        </table>
+      </#if>
+      </td>
+    </tr>
+  <#-- Long description of product -->
+    <tr>
+      <td colspan="2">
+        <div>${productContentWrapper.get("LONG_DESCRIPTION", "html")!}</div>
+      </td>
+    </tr>
+    <tr>
+      <td colspan="2">
+        <hr class='sepbar'/>
+      </td>
+    </tr>
+  <#-- Any attributes/etc may go here -->
+  <#-- Product Configurator -->
+    <tr>
+      <td colspan="2">
+        <form name="configform" id="configFormId" method="post"
+              action="<@ofbizUrl>product<#if requestAttributes._CURRENT_VIEW_??>/${requestAttributes._CURRENT_VIEW_}</#...@ofbizUrl>">
+          <input type='hidden' name='add_product_id' value='${product.productId}'/>
+          <input type='hidden' name='add_category_id' value=''/>
+          <input type='hidden' name='quantity' value='1'/>
+
+          <input type='hidden' name='product_id' value='${product.productId}'/>
+          <table>
+            <tr>
+              <td>
+                <div>
+                  <a href="javascript:verifyConfig();" class="btn btn-outline-secondary btn-sm">${uiLabelMap.OrderVerifyConfiguration}</a>
+                </div>
+              </td>
+            </tr>
+
+          <#assign counter = 0>
+          <#assign questions = configwrapper.questions>
+          <#list questions as question>
+            <tr>
+              <td>
+                <div>${question.question}</div>
+                <#if question.isFirst()>
+                  <a name='#${question.getConfigItem().getString("configItemId")}'></a>
+                  <div>${StringUtil.wrapString(question.description!)}</div>
+                  <#assign instructions = question.content.get("INSTRUCTIONS", "html")!>
+                  <#if instructions?has_content>
+                    <a href="javascript:showErrorAlert('${uiLabelMap.CommonErrorMessage2}','${instructions}');"
+                    class="btn btn-outline-secondary btn-sm">Instructions</a>
+                  </#if>
+                  <#assign image = question.content.get("IMAGE_URL", "url")!>
+                  <#if image?string?has_content>
+                    <img src='<@o...@ofbizContentUrl>'
+                         vspace='5' hspace='5' class='cssImgXLarge' align='left' alt=""/>
                   </#if>
-                <#-- The single choice input can be implemented with radio buttons or a select field -->
-                  <#if renderSingleChoiceWithRadioButtons?? && "Y" == renderSingleChoiceWithRadioButtons>
-                  <#-- This is the radio button implementation -->
-                    <#if !question.isMandatory()>
-                      <div>
-                        <input type="radio" name='${counter}' value='<#if !question.isSelected()>checked</#if>'/>
-                        No option
-                      </div>
+                <#else>
+                  <div>
+                    <a href='#${question.getConfigItem().getString("configItemId")}' class="btn btn-outline-secondary btn-sm">Details</a>
+                  </div>
+                </#if>
+              </td>
+            </tr>
+            <tr>
+              <td>
+                <#if question.isStandard()>
+                <#-- Standard item: all the options are always included -->
+                  <#assign options = question.options>
+                  <#list options as option>
+                    <div>${option.description} <#if !option.isAvailable()> (*)</#if></div>
+                  </#list>
+                <#else>
+                  <#if question.isSingleChoice()>
+                  <#-- Single choice question -->
+                    <#assign options = question.options>
+                    <#assign selectedOption = question.getSelected()!>
+                    <#assign selectedPrice = 0.0>
+                    <#if selectedOption?has_content>
+                      <#assign selectedPrice = selectedOption.getPrice()>
+                    </#if>
+                  <#-- The single choice input can be implemented with radio buttons or a select field -->
+                    <#if renderSingleChoiceWithRadioButtons?? && "Y" == renderSingleChoiceWithRadioButtons>
+                    <#-- This is the radio button implementation -->
+                      <#if !question.isMandatory()>
+                        <div>
+                          <input type="radio" name='${counter}' value='<#if !question.isSelected()>checked</#if>'/>
+                          No option
+                        </div>
+                      </#if>
+                      <#assign optionCounter = 0>
+                      <#list options as option>
+                        <#assign componentCounter = 0>
+                        <#if showOffsetPrice?? && "Y" == showOffsetPrice>
+                          <#assign shownPrice = option.price - selectedPrice>
+                        <#else>
+                          <#assign shownPrice = option.price>
+                        </#if>
+                      <#-- Render virtual compoennts -->
+                        <#if option.hasVirtualComponent()>
+                          <div>
+                            <input type='radio' name='${counter}' id="${counter}_${optionCounter}"
+                                   value='${optionCounter}'
+                                   onclick="javascript:checkOptionVariants('${counter}_${optionCounter}');"/>
+                          ${option.description} <#if !option.isAvailable()> (*)</#if>
+                            <#assign components = option.getComponents()>
+                            <#list components as component>
+                              <#if (option.isVirtualComponent(component))>
+                              ${setRequestAttribute("inlineProductId", component.productId)}
+                              ${setRequestAttribute("inlineCounter", counter+ "_" +optionCounter + "_"+componentCounter)}
+                              ${setRequestAttribute("addJavaScript", componentCounter)}
+                              ${screens.render(inlineProductDetailScreen)}
+                                <#assign componentCounter = componentCounter + 1>
+                              </#if>
+                            </#list>
+                          </div>
+                        <#else>
+                          <div>
+                            <input type="radio" name='${counter}' value='${optionCounter}'
+                                   <#if option.isSelected() || (!question.isSelected() && optionCounter == 0 && question.isMandatory())>checked="checked"</#if>/>
+                          ${option.description}&nbsp;
+                            <#if (shownPrice > 0)>+<@ofbizCurrency amount=shownPrice isoCode=price.currencyUsed/>
+                              &nbsp;</#if>
+                            <#if (shownPrice < 0)>-<@ofbizCurrency amount=(-1*shownPrice) isoCode=price.currencyUsed/>
+                              &nbsp;</#if>
+                            <#if !option.isAvailable()>(*)</#if>
+                          </div>
+                        </#if>
+                        <#assign optionCounter = optionCounter + 1>
+                      </#list>
+                    <#else>
+                    <#-- And this is the select box implementation -->
+                      <select name='${counter}' class="form-control">
+                        <#if !question.isMandatory()>
+                          <option value=''>---</option>
+                        </#if>
+                        <#assign options = question.options>
+                        <#assign optionCounter = 0>
+                        <#list options as option>
+                          <#if showOffsetPrice?? && "Y" == showOffsetPrice>
+                            <#assign shownPrice = option.price - selectedPrice>
+                          <#else>
+                            <#assign shownPrice = option.price>
+                          </#if>
+                          <#if option.isSelected()>
+                            <#assign optionCounter = optionCounter + 1>
+                          </#if>
+                          <option value='${optionCounter}' <#if option.isSelected()>selected="selected"</#if>>
+                          ${option.description}&nbsp;
+                            <#if (shownPrice > 0)>+<@ofbizCurrency amount=shownPrice isoCode=price.currencyUsed/>
+                              &nbsp;</#if>
+                            <#if (shownPrice < 0)>-<@ofbizCurrency amount=(-1*shownPrice) isoCode=price.currencyUsed/>
+                              &nbsp;</#if>
+                            <#if !option.isAvailable()> (*)</#if>
+                          </option>
+                          <#assign optionCounter = optionCounter + 1>
+                        </#list>
+                      </select>
                     </#if>
+                  <#else>
+                  <#-- Multi choice question -->
+                    <#assign options = question.options>
                     <#assign optionCounter = 0>
                     <#list options as option>
                       <#assign componentCounter = 0>
-                      <#if showOffsetPrice?? && "Y" == showOffsetPrice>
-                        <#assign shownPrice = option.price - selectedPrice>
-                      <#else>
-                        <#assign shownPrice = option.price>
-                      </#if>
                     <#-- Render virtual compoennts -->
                       <#if option.hasVirtualComponent()>
                         <div>
-                          <input type='radio' name='${counter}' id="${counter}_${optionCounter}"
+                          <input type='CHECKBOX' name='${counter}' id="${counter}_${optionCounter}"
                                  value='${optionCounter}'
                                  onclick="javascript:checkOptionVariants('${counter}_${optionCounter}');"/>
                         ${option.description} <#if !option.isAvailable()> (*)</#if>
@@ -577,248 +653,180 @@ ${virtualJavaScript!}
                         </div>
                       <#else>
                         <div>
-                          <input type="radio" name='${counter}' value='${optionCounter}'
-                                 <#if option.isSelected() || (!question.isSelected() && optionCounter == 0 && question.isMandatory())>checked="checked"</#if>/>
-                        ${option.description}&nbsp;
-                          <#if (shownPrice > 0)>+<@ofbizCurrency amount=shownPrice isoCode=price.currencyUsed/>
-                            &nbsp;</#if>
-                          <#if (shownPrice < 0)>-<@ofbizCurrency amount=(-1*shownPrice) isoCode=price.currencyUsed/>
-                            &nbsp;</#if>
-                          <#if !option.isAvailable()>(*)</#if>
+                          <input type='CHECKBOX' name='${counter}'
+                                 value='${optionCounter}' <#if option.isSelected()>checked="checked"</#if>/>
+                        ${option.description} +<@ofbizCurrency amount=option.price isoCode=price.currencyUsed/>
+                          <#if !option.isAvailable()> (*)</#if>
                         </div>
                       </#if>
                       <#assign optionCounter = optionCounter + 1>
                     </#list>
-                  <#else>
-                  <#-- And this is the select box implementation -->
-                    <select name='${counter}'>
-                      <#if !question.isMandatory()>
-                        <option value=''>---</option>
-                      </#if>
-                      <#assign options = question.options>
-                      <#assign optionCounter = 0>
-                      <#list options as option>
-                        <#if showOffsetPrice?? && "Y" == showOffsetPrice>
-                          <#assign shownPrice = option.price - selectedPrice>
-                        <#else>
-                          <#assign shownPrice = option.price>
-                        </#if>
-                        <#if option.isSelected()>
-                          <#assign optionCounter = optionCounter + 1>
-                        </#if>
-                        <option value='${optionCounter}' <#if option.isSelected()>selected="selected"</#if>>
-                        ${option.description}&nbsp;
-                          <#if (shownPrice > 0)>+<@ofbizCurrency amount=shownPrice isoCode=price.currencyUsed/>
-                            &nbsp;</#if>
-                          <#if (shownPrice < 0)>-<@ofbizCurrency amount=(-1*shownPrice) isoCode=price.currencyUsed/>
-                            &nbsp;</#if>
-                          <#if !option.isAvailable()> (*)</#if>
-                        </option>
-                        <#assign optionCounter = optionCounter + 1>
-                      </#list>
-                    </select>
                   </#if>
-                <#else>
-                <#-- Multi choice question -->
-                  <#assign options = question.options>
-                  <#assign optionCounter = 0>
-                  <#list options as option>
-                    <#assign componentCounter = 0>
-                  <#-- Render virtual compoennts -->
-                    <#if option.hasVirtualComponent()>
-                      <div>
-                        <input type='CHECKBOX' name='${counter}' id="${counter}_${optionCounter}"
-                               value='${optionCounter}'
-                               onclick="javascript:checkOptionVariants('${counter}_${optionCounter}');"/>
-                      ${option.description} <#if !option.isAvailable()> (*)</#if>
-                        <#assign components = option.getComponents()>
-                        <#list components as component>
-                          <#if (option.isVirtualComponent(component))>
-                          ${setRequestAttribute("inlineProductId", component.productId)}
-                          ${setRequestAttribute("inlineCounter", counter+ "_" +optionCounter + "_"+componentCounter)}
-                          ${setRequestAttribute("addJavaScript", componentCounter)}
-                          ${screens.render(inlineProductDetailScreen)}
-                            <#assign componentCounter = componentCounter + 1>
-                          </#if>
-                        </#list>
-                      </div>
-                    <#else>
-                      <div>
-                        <input type='CHECKBOX' name='${counter}'
-                               value='${optionCounter}' <#if option.isSelected()>checked="checked"</#if>/>
-                      ${option.description} +<@ofbizCurrency amount=option.price isoCode=price.currencyUsed/>
-                        <#if !option.isAvailable()> (*)</#if>
-                      </div>
-                    </#if>
-                    <#assign optionCounter = optionCounter + 1>
-                  </#list>
                 </#if>
-              </#if>
-            </td>
-          </tr>
-          <#assign counter = counter + 1>
-        </#list>
-        </table>
-      </form>
-    </td>
-  </tr>
-<#-- Product Reviews -->
-  <tr>
-    <td colspan="2">
-      <div>${uiLabelMap.OrderCustomerReviews}:</div>
-    <#if averageRating?? && (averageRating > 0) && numRatings?? && (numRatings > 1)>
-      <div>${uiLabelMap.OrderAverageRating}: ${averageRating}
-        <#if numRatings??>(${uiLabelMap.CommonFrom} ${numRatings} ${uiLabelMap.OrderRatings})</#if>
-      </div>
-    </#if>
-    </td>
-  </tr>
-  <tr>
-    <td colspan="2">
-      <hr class='sepbar'/>
-    </td>
-  </tr>
-<#if productReviews?has_content>
-  <#list productReviews as productReview>
-    <#assign postedUserLogin = productReview.getRelatedOne("UserLogin", false)>
-    <#assign postedPerson = postedUserLogin.getRelatedOne("Person", false)!>
+              </td>
+            </tr>
+            <#assign counter = counter + 1>
+          </#list>
+          </table>
+        </form>
+      </td>
+    </tr>
+  <#-- Product Reviews -->
     <tr>
       <td colspan="2">
-        <table border="0" cellpadding="0" cellspacing='0'>
-          <tr>
-            <td>
-              <div>${uiLabelMap.CommonBy}:
-                <#if "Y" == productReview.postedAnonymous?default("N")>
-                ${uiLabelMap.OrderAnonymous}
-                <#else>
-                ${postedPerson.firstName} ${postedPerson.lastName}
-                </#if>
-              </div>
-            </td>
-            <td>
-              <div>${uiLabelMap.CommonOn}: ${productReview.postedDateTime!}</div>
-            </td>
-            <td>
-              <div>${uiLabelMap.OrderRanking}: ${productReview.productRating!?string}</div>
-            </td>
-          </tr>
-          <tr>
-            <td colspan="3">
-              <div>&nbsp;</div>
-            </td>
-          </tr>
-          <tr>
-            <td colspan="3">
-              <div>${productReview.productReview!}</div>
-            </td>
-          </tr>
-        </table>
+        <div>${uiLabelMap.OrderCustomerReviews}:</div>
+      <#if averageRating?? && (averageRating > 0) && numRatings?? && (numRatings > 1)>
+        <div>${uiLabelMap.OrderAverageRating}: ${averageRating}
+          <#if numRatings??>(${uiLabelMap.CommonFrom} ${numRatings} ${uiLabelMap.OrderRatings})</#if>
+        </div>
+      </#if>
       </td>
     </tr>
-  </#list>
-  <tr>
-    <td colspan="2">
-      <a href="<@o...@ofbizUrl>"
-         class="buttontext">${uiLabelMap.ProductReviewThisProduct}!</a>
-    </td>
-  </tr>
-<#else>
-  <tr>
-    <td colspan="2">
-      <div>${uiLabelMap.ProductProductNotReviewedYet}.</div>
-    </td>
-  </tr>
-  <tr>
-    <td colspan="2">
-      <a href="<@o...@ofbizUrl>"
-         class="buttontext">${uiLabelMap.ProductBeTheFirstToReviewThisProduct}</a>
-    </td>
-  </tr>
-</table>
-</#if>
-
-<#-- Upgrades/Up-Sell/Cross-Sell -->
-<#macro associated assocProducts beforeName showName afterName formNamePrefix targetRequestName>
-  <#assign targetRequest = "product">
-  <#if targetRequestName?has_content>
-    <#assign targetRequest = targetRequestName>
-  </#if>
-  <#if assocProducts?has_content>
     <tr>
-      <td>&nbsp;</td>
+      <td colspan="2">
+        <hr class='sepbar'/>
+      </td>
     </tr>
+  <#if productReviews?has_content>
+    <#list productReviews as productReview>
+      <#assign postedUserLogin = productReview.getRelatedOne("UserLogin", false)>
+      <#assign postedPerson = postedUserLogin.getRelatedOne("Person", false)!>
+      <tr>
+        <td colspan="2">
+          <table border="0" cellpadding="0" cellspacing='0'>
+            <tr>
+              <td>
+                <div>${uiLabelMap.CommonBy}:
+                  <#if "Y" == productReview.postedAnonymous?default("N")>
+                  ${uiLabelMap.OrderAnonymous}
+                  <#else>
+                  ${postedPerson.firstName} ${postedPerson.lastName}
+                  </#if>
+                </div>
+              </td>
+              <td>
+                <div>${uiLabelMap.CommonOn}: ${productReview.postedDateTime!}</div>
+              </td>
+              <td>
+                <div>${uiLabelMap.OrderRanking}: ${productReview.productRating!?string}</div>
+              </td>
+            </tr>
+            <tr>
+              <td colspan="3">
+                <div>&nbsp;</div>
+              </td>
+            </tr>
+            <tr>
+              <td colspan="3">
+                <div>${productReview.productReview!}</div>
+              </td>
+            </tr>
+          </table>
+        </td>
+      </tr>
+    </#list>
     <tr>
       <td colspan="2">
-        <h2>${beforeName!}<#if "Y" == showName>${productContentWrapper.get("PRODUCT_NAME", "html")!}</#if>${afterName!}</h2>
+        <a href="<@o...@ofbizUrl>"
+           class="buttontext">${uiLabelMap.ProductReviewThisProduct}!</a>
       </td>
     </tr>
+  <#else>
+    <tr>
+      <td colspan="2">
+        <div>${uiLabelMap.ProductProductNotReviewedYet}.</div>
+      </td>
+    </tr>
+    <tr>
+      <td colspan="2">
+        <a href="<@o...@ofbizUrl>">${uiLabelMap.ProductBeTheFirstToReviewThisProduct}</a>
+      </td>
+    </tr>
+  </table>
+  </#if>
 
-    <#list assocProducts as productAssoc>
+  <#-- Upgrades/Up-Sell/Cross-Sell -->
+  <#macro associated assocProducts beforeName showName afterName formNamePrefix targetRequestName>
+    <#assign targetRequest = "product">
+    <#if targetRequestName?has_content>
+      <#assign targetRequest = targetRequestName>
+    </#if>
+    <#if assocProducts?has_content>
       <tr>
-        <td>
-          <div>
-            <a href='<@ofbizUrl>${targetRequest}/<#if categoryId??>~category_id=${categoryId}/</#...@ofbizUrl>'
-               class="buttontext">
-            ${productAssoc.productIdTo!}
-            </a>
-            - ${productAssoc.reason!}
-          </div>
-        </td>
+        <td></td>
       </tr>
-    ${setRequestAttribute("optProductId", productAssoc.productIdTo)}
-    ${setRequestAttribute("listIndex", listIndex)}
-    ${setRequestAttribute("formNamePrefix", formNamePrefix)}
-      <#if targetRequestName?has_content>
-      ${setRequestAttribute("targetRequestName", targetRequestName)}
-      </#if>
       <tr>
-        <td>
-        ${screens.render(productsummaryScreen)}
+        <td colspan="2">
+          <h2>${beforeName!}<#if "Y" == showName>${productContentWrapper.get("PRODUCT_NAME", "html")!}</#if>${afterName!}</h2>
         </td>
       </tr>
-      <#local listIndex = listIndex + 1>
 
-    </#list>
-  ${setRequestAttribute("optProductId", "")}
-  ${setRequestAttribute("formNamePrefix", "")}
-  ${setRequestAttribute("targetRequestName", "")}
-  </#if>
-</#macro>
-<#assign productValue = product>
-<#assign listIndex = 1>
-${setRequestAttribute("productValue", productValue)}
-
-  <table>
-  <#-- obsolete -->
-  <@associated assocProducts=obsoleteProducts beforeName="" showName="Y"
-      afterName=" is made obsolete by these products:" formNamePrefix="obs" targetRequestName=""/>
-  <#-- cross sell -->
-  <@associated assocProducts=crossSellProducts beforeName="" showName="N"
-      afterName="You might be interested in these as well:" formNamePrefix="cssl" targetRequestName="crosssell"/>
-  <#-- up sell -->
-  <@associated assocProducts=upSellProducts beforeName="Try these instead of " showName="Y" afterName=":"
-      formNamePrefix="upsl" targetRequestName="upsell"/>
-  <#-- obsolescence -->
-  <@associated assocProducts=obsolenscenseProducts beforeName="" showName="Y"
-      afterName=" makes these products obsolete:" formNamePrefix="obce" targetRequestName=""/>
-  </table>
+      <#list assocProducts as productAssoc>
+        <tr>
+          <td>
+            <div>
+              <a href='<@ofbizUrl>${targetRequest}/<#if categoryId??>~category_id=${categoryId}/</#...@ofbizUrl>'
+                 class="buttontext">
+              ${productAssoc.productIdTo!}
+              </a>
+              - ${productAssoc.reason!}
+            </div>
+          </td>
+        </tr>
+      ${setRequestAttribute("optProductId", productAssoc.productIdTo)}
+      ${setRequestAttribute("listIndex", listIndex)}
+      ${setRequestAttribute("formNamePrefix", formNamePrefix)}
+        <#if targetRequestName?has_content>
+        ${setRequestAttribute("targetRequestName", targetRequestName)}
+        </#if>
+        <tr>
+          <td>
+          ${screens.render(productsummaryScreen)}
+          </td>
+        </tr>
+        <#local listIndex = listIndex + 1>
 
-<#-- special cross/up-sell area using commonFeatureResultIds (from common feature product search) -->
-<#if commonFeatureResultIds?has_content>
-  <h2>Similar Products That Might Interest You...</h2>
+      </#list>
+    ${setRequestAttribute("optProductId", "")}
+    ${setRequestAttribute("formNamePrefix", "")}
+    ${setRequestAttribute("targetRequestName", "")}
+    </#if>
+  </#macro>
+  <#assign productValue = product>
+  <#assign listIndex = 1>
+  ${setRequestAttribute("productValue", productValue)}
+
+    <table class="table">
+    <#-- obsolete -->
+    <@associated assocProducts=obsoleteProducts beforeName="" showName="Y"
+        afterName=" is made obsolete by these products:" formNamePrefix="obs" targetRequestName=""/>
+    <#-- cross sell -->
+    <@associated assocProducts=crossSellProducts beforeName="" showName="N"
+        afterName="You might be interested in these as well:" formNamePrefix="cssl" targetRequestName="crosssell"/>
+    <#-- up sell -->
+    <@associated assocProducts=upSellProducts beforeName="Try these instead of " showName="Y" afterName=":"
+        formNamePrefix="upsl" targetRequestName="upsell"/>
+    <#-- obsolescence -->
+    <@associated assocProducts=obsolenscenseProducts beforeName="" showName="Y"
+        afterName=" makes these products obsolete:" formNamePrefix="obce" targetRequestName=""/>
+    </table>
+
+  <#-- special cross/up-sell area using commonFeatureResultIds (from common feature product search) -->
+  <#if commonFeatureResultIds?has_content>
+    <h2>Similar Products That Might Interest You...</h2>
 
 
-  <#list commonFeatureResultIds as commonFeatureResultId>
-    <div>
-    ${setRequestAttribute("optProductId", commonFeatureResultId)}
-      ${setRequestAttribute("listIndex", commonFeatureResultId_index)}
-      ${setRequestAttribute("formNamePrefix", "cfeatcssl")}
-      <#-- ${setRequestAttribute("targetRequestName", targetRequestName)} -->
-      ${screens.render(productsummaryScreen)}
-    </div>
-    <#if commonFeatureResultId_has_next>
-      <hr/>
-    </#if>
-  </#list>
-</#if>
+    <#list commonFeatureResultIds as commonFeatureResultId>
+      <div>
+      ${setRequestAttribute("optProductId", commonFeatureResultId)}
+        ${setRequestAttribute("listIndex", commonFeatureResultId_index)}
+        ${setRequestAttribute("formNamePrefix", "cfeatcssl")}
+        <#-- ${setRequestAttribute("targetRequestName", targetRequestName)} -->
+        ${screens.render(productsummaryScreen)}
+      </div>
+      <#if commonFeatureResultId_has_next>
+        <hr/>
+      </#if>
+    </#list>
+  </#if>
 </div>