/******************************************************************************* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. *******************************************************************************/ package org.apache.ofbiz.product.category.ftl; import java.io.IOException; import java.io.Writer; import java.util.Collection; import java.util.Hashtable; import java.util.LinkedList; import java.util.List; import java.util.Map; import javax.servlet.RequestDispatcher; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.oro.text.regex.MalformedPatternException; import org.apache.oro.text.regex.Pattern; import org.apache.oro.text.regex.Perl5Compiler; import org.apache.oro.text.regex.Perl5Matcher; import org.apache.ofbiz.base.util.Debug; import org.apache.ofbiz.base.util.StringUtil; import org.apache.ofbiz.base.util.StringUtil.StringWrapper; import org.apache.ofbiz.base.util.UtilMisc; import org.apache.ofbiz.base.util.UtilValidate; import org.apache.ofbiz.common.UrlServletHelper; import org.apache.ofbiz.entity.Delegator; import org.apache.ofbiz.entity.GenericEntityException; import org.apache.ofbiz.entity.GenericValue; import org.apache.ofbiz.entity.condition.EntityCondition; import org.apache.ofbiz.entity.condition.EntityExpr; import org.apache.ofbiz.entity.condition.EntityOperator; import org.apache.ofbiz.product.category.CatalogUrlServlet; import org.apache.ofbiz.product.category.CategoryContentWrapper; import org.apache.ofbiz.product.category.CategoryWorker; import org.apache.ofbiz.product.category.SeoConfigUtil; import org.apache.ofbiz.product.category.SeoUrlUtil; import org.apache.ofbiz.product.product.ProductContentWrapper; import freemarker.core.Environment; import freemarker.ext.beans.BeanModel; import freemarker.ext.beans.StringModel; import freemarker.template.SimpleScalar; import freemarker.template.TemplateModelException; import freemarker.template.TemplateTransformModel; public class CatalogUrlSeoTransform implements TemplateTransformModel { public final static String module = CatalogUrlSeoTransform.class.getName(); private static Map<String, String> categoryNameIdMap = null; private static Map<String, String> categoryIdNameMap = null; private static boolean categoryMapInitialed = false; private static final String asciiRegexp = "^[0-9-_a-zA-Z]*$"; private static Pattern asciiPattern = null; public static final String URL_HYPHEN = "-"; static { if (!SeoConfigUtil.isInitialed()) { SeoConfigUtil.init(); } try { Perl5Compiler perlCompiler = new Perl5Compiler(); asciiPattern = perlCompiler.compile(asciiRegexp, Perl5Compiler.READ_ONLY_MASK); } catch (MalformedPatternException e1) { Debug.logWarning(e1, module); } } public String getStringArg(Map args, String key) { Object o = args.get(key); if (o instanceof SimpleScalar) { return ((SimpleScalar) o).getAsString(); } else if (o instanceof StringModel) { return ((StringModel) o).getAsString(); } return null; } @Override public Writer getWriter(final Writer out, final Map args) throws TemplateModelException, IOException { final StringBuilder buf = new StringBuilder(); return new Writer(out) { @Override public void write(char[] cbuf, int off, int len) throws IOException { buf.append(cbuf, off, len); } @Override public void flush() throws IOException { out.flush(); } @Override public void close() throws IOException { try { Environment env = Environment.getCurrentEnvironment(); BeanModel req = (BeanModel) env.getVariable("request"); if (req != null) { String productId = getStringArg(args, "productId"); String currentCategoryId = getStringArg(args, "currentCategoryId"); String previousCategoryId = getStringArg(args, "previousCategoryId"); HttpServletRequest request = (HttpServletRequest) req.getWrappedObject(); if (!isCategoryMapInitialed()) { initCategoryMap(request); } String catalogUrl = ""; if (SeoConfigUtil.isCategoryUrlEnabled(request.getContextPath())) { if (UtilValidate.isEmpty(productId)) { catalogUrl = makeCategoryUrl(request, currentCategoryId, previousCategoryId, null, null, null, null); } else { catalogUrl = makeProductUrl(request, productId, currentCategoryId, previousCategoryId); } } else { catalogUrl = CatalogUrlServlet.makeCatalogUrl(request, productId, currentCategoryId, previousCategoryId); } out.write(catalogUrl); } } catch (TemplateModelException e) { throw new IOException(e.getMessage()); } } }; } /** * Check whether the category map is initialed. * * @return a boolean value to indicate whether the category map has been initialized. */ public static boolean isCategoryMapInitialed() { return categoryMapInitialed; } /** * Get the category name/id map. * * @return the category name/id map */ public static Map<String, String> getCategoryNameIdMap() { return categoryNameIdMap; } /** * Get the category id/name map. * * @return the category id/name map */ public static Map<String, String> getCategoryIdNameMap() { return categoryIdNameMap; } /** * Initial category-name/category-id map. * Note: as a key, the category-name should be: * 1. ascii * 2. lower cased and use hyphen between the words. * If not, the category id will be used. * */ public static synchronized void initCategoryMap(HttpServletRequest request) { Delegator delegator = (Delegator) request.getAttribute("delegator"); initCategoryMap(request, delegator); } public static synchronized void initCategoryMap(HttpServletRequest request, Delegator delegator) { if (SeoConfigUtil.checkCategoryUrl()) { categoryNameIdMap = new Hashtable<String, String>(); categoryIdNameMap = new Hashtable<String, String>(); Perl5Matcher matcher = new Perl5Matcher(); try { Collection<GenericValue> allCategories = delegator.findList("ProductCategory", null, UtilMisc.toSet("productCategoryId", "categoryName"), null, null, false); for (GenericValue category : allCategories) { String categoryName = category.getString("categoryName"); String categoryNameId = null; String categoryIdName = null; String categoryId = category.getString("productCategoryId"); if (UtilValidate.isNotEmpty(categoryName)) { categoryName = SeoUrlUtil.replaceSpecialCharsUrl(categoryName.trim()); if (matcher.matches(categoryName, asciiPattern)) { categoryIdName = categoryName.replaceAll(" ", URL_HYPHEN); categoryNameId = categoryIdName + URL_HYPHEN + categoryId.trim().replaceAll(" ", URL_HYPHEN); } else { categoryIdName = categoryId.trim().replaceAll(" ", URL_HYPHEN); categoryNameId = categoryIdName; } } else { GenericValue productCategory = delegator.findOne("ProductCategory", UtilMisc.toMap("productCategoryId", categoryId), true); CategoryContentWrapper wrapper = new CategoryContentWrapper(productCategory, request); StringWrapper alternativeUrl = wrapper.get("ALTERNATIVE_URL", "url"); if (UtilValidate.isNotEmpty(alternativeUrl) && UtilValidate.isNotEmpty(alternativeUrl.toString())) { categoryIdName = SeoUrlUtil.replaceSpecialCharsUrl(alternativeUrl.toString()); categoryNameId = categoryIdName + URL_HYPHEN + categoryId.trim().replaceAll(" ", URL_HYPHEN); } else { categoryNameId = categoryId.trim().replaceAll(" ", URL_HYPHEN); categoryIdName = categoryNameId; } } if (categoryNameIdMap.containsKey(categoryNameId)) { categoryNameId = categoryId.trim().replaceAll(" ", URL_HYPHEN); categoryIdName = categoryNameId; } if (!matcher.matches(categoryNameId, asciiPattern) || categoryNameIdMap.containsKey(categoryNameId)) { continue; } categoryNameIdMap.put(categoryNameId, categoryId); categoryIdNameMap.put(categoryId, categoryIdName); } } catch (GenericEntityException e) { Debug.logError(e, module); } } categoryMapInitialed = true; } /** * Make product url according to the configurations. * * @return String a catalog url */ public static String makeProductUrl(HttpServletRequest request, String productId, String currentCategoryId, String previousCategoryId) { Delegator delegator = (Delegator) request.getAttribute("delegator"); if (!isCategoryMapInitialed()) { initCategoryMap(request); } String contextPath = request.getContextPath(); StringBuilder urlBuilder = new StringBuilder(); GenericValue product = null; urlBuilder.append((request.getSession().getServletContext()).getContextPath()); if (urlBuilder.length() == 0 || urlBuilder.charAt(urlBuilder.length() - 1) != '/') { urlBuilder.append("/"); } if (UtilValidate.isNotEmpty(productId)) { try { product = delegator.findOne("Product", UtilMisc.toMap("productId", productId), true); } catch (GenericEntityException e) { Debug.logError(e, "Error looking up product info for productId [" + productId + "]: " + e.toString(), module); } } if (product != null) { urlBuilder.append(CatalogUrlServlet.PRODUCT_REQUEST + "/"); } if (UtilValidate.isNotEmpty(currentCategoryId)) { List<String> trail = CategoryWorker.getTrail(request); trail = CategoryWorker.adjustTrail(trail, currentCategoryId, previousCategoryId); if (!SeoConfigUtil.isCategoryUrlEnabled(contextPath)) { for (String trailCategoryId: trail) { if ("TOP".equals(trailCategoryId)) continue; urlBuilder.append("/"); urlBuilder.append(trailCategoryId); } } else { if (trail.size() > 1) { String lastCategoryId = trail.get(trail.size() - 1); if (!"TOP".equals(lastCategoryId)) { if (SeoConfigUtil.isCategoryNameEnabled()) { String categoryName = CatalogUrlSeoTransform.getCategoryIdNameMap().get(lastCategoryId); if (UtilValidate.isNotEmpty(categoryName)) { urlBuilder.append(categoryName); if (product != null) { urlBuilder.append(URL_HYPHEN); } } } } } } } if (UtilValidate.isNotEmpty(productId)) { if (product != null) { String productName = product.getString("productName"); productName = SeoUrlUtil.replaceSpecialCharsUrl(productName); if (UtilValidate.isNotEmpty(productName)) { urlBuilder.append(productName + URL_HYPHEN); } else { ProductContentWrapper wrapper = new ProductContentWrapper(product, request); StringWrapper alternativeUrl = wrapper.get("ALTERNATIVE_URL", "url"); if (UtilValidate.isNotEmpty(alternativeUrl) && UtilValidate.isNotEmpty(alternativeUrl.toString())) { productName = SeoUrlUtil.replaceSpecialCharsUrl(alternativeUrl.toString()); if (UtilValidate.isNotEmpty(productName)) { urlBuilder.append(productName + URL_HYPHEN); } } } } try { urlBuilder.append(productId); } catch (Exception e) { urlBuilder.append(productId); } } if (!urlBuilder.toString().endsWith("/") && UtilValidate.isNotEmpty(SeoConfigUtil.getCategoryUrlSuffix())) { urlBuilder.append(SeoConfigUtil.getCategoryUrlSuffix()); } return urlBuilder.toString(); } /** * Make category url according to the configurations. * * @return String a category url */ public static String makeCategoryUrl(HttpServletRequest request, String currentCategoryId, String previousCategoryId, String viewSize, String viewIndex, String viewSort, String searchString) { if (!isCategoryMapInitialed()) { initCategoryMap(request); } StringBuilder urlBuilder = new StringBuilder(); urlBuilder.append((request.getSession().getServletContext()).getContextPath()); if (urlBuilder.length() == 0 || urlBuilder.charAt(urlBuilder.length() - 1) != '/') { urlBuilder.append("/"); } urlBuilder.append(CatalogUrlServlet.CATEGORY_REQUEST + "/"); if (UtilValidate.isNotEmpty(currentCategoryId)) { List<String> trail = CategoryWorker.getTrail(request); trail = CategoryWorker.adjustTrail(trail, currentCategoryId, previousCategoryId); if (trail.size() > 1) { String lastCategoryId = trail.get(trail.size() - 1); if (!"TOP".equals(lastCategoryId)) { String categoryName = CatalogUrlSeoTransform.getCategoryIdNameMap().get(lastCategoryId); if (UtilValidate.isNotEmpty(categoryName)) { urlBuilder.append(categoryName); urlBuilder.append(URL_HYPHEN); urlBuilder.append(lastCategoryId.trim().replaceAll(" ", URL_HYPHEN)); } else { urlBuilder.append(lastCategoryId.trim().replaceAll(" ", URL_HYPHEN)); } } } } if (!urlBuilder.toString().endsWith("/") && UtilValidate.isNotEmpty(SeoConfigUtil.getCategoryUrlSuffix())) { urlBuilder.append(SeoConfigUtil.getCategoryUrlSuffix()); } // append view index if (UtilValidate.isNotEmpty(viewIndex)) { if (!urlBuilder.toString().endsWith("?") && !urlBuilder.toString().endsWith("&")) { urlBuilder.append("?"); } urlBuilder.append("viewIndex=" + viewIndex + "&"); } // append view size if (UtilValidate.isNotEmpty(viewSize)) { if (!urlBuilder.toString().endsWith("?") && !urlBuilder.toString().endsWith("&")) { urlBuilder.append("?"); } urlBuilder.append("viewSize=" + viewSize + "&"); } // append view sort if (UtilValidate.isNotEmpty(viewSort)) { if (!urlBuilder.toString().endsWith("?") && !urlBuilder.toString().endsWith("&")) { urlBuilder.append("?"); } urlBuilder.append("viewSort=" + viewSort + "&"); } // append search string if (UtilValidate.isNotEmpty(searchString)) { if (!urlBuilder.toString().endsWith("?") && !urlBuilder.toString().endsWith("&")) { urlBuilder.append("?"); } urlBuilder.append("searchString=" + searchString + "&"); } if (urlBuilder.toString().endsWith("&")) { return urlBuilder.toString().substring(0, urlBuilder.toString().length()-1); } return urlBuilder.toString(); } /** * Make product url according to the configurations. * * @return String a catalog url */ public static String makeProductUrl(String contextPath, List<String> trail, String productId, String productName, String currentCategoryId, String previousCategoryId) { StringBuilder urlBuilder = new StringBuilder(); urlBuilder.append(contextPath); if (urlBuilder.charAt(urlBuilder.length() - 1) != '/') { urlBuilder.append("/"); } if (!SeoConfigUtil.isCategoryUrlEnabled(contextPath)) { urlBuilder.append(CatalogUrlServlet.CATALOG_URL_MOUNT_POINT); } else { urlBuilder.append(CatalogUrlServlet.PRODUCT_REQUEST + "/"); } if (UtilValidate.isNotEmpty(currentCategoryId)) { trail = CategoryWorker.adjustTrail(trail, currentCategoryId, previousCategoryId); if (!SeoConfigUtil.isCategoryUrlEnabled(contextPath)) { for (String trailCategoryId: trail) { if ("TOP".equals(trailCategoryId)) continue; urlBuilder.append("/"); urlBuilder.append(trailCategoryId); } } else { if (trail.size() > 1) { String lastCategoryId = trail.get(trail.size() - 1); if (!"TOP".equals(lastCategoryId)) { if (SeoConfigUtil.isCategoryNameEnabled()) { String categoryName = CatalogUrlSeoTransform.getCategoryIdNameMap().get(lastCategoryId); if (UtilValidate.isNotEmpty(categoryName)) { urlBuilder.append(categoryName + URL_HYPHEN); } } } } } } if (UtilValidate.isNotEmpty(productId)) { if (!SeoConfigUtil.isCategoryUrlEnabled(contextPath)) { urlBuilder.append("/p_"); } else { productName = SeoUrlUtil.replaceSpecialCharsUrl(productName); if (UtilValidate.isNotEmpty(productName)) { urlBuilder.append(productName + URL_HYPHEN); } } urlBuilder.append(productId); } if (!urlBuilder.toString().endsWith("/") && UtilValidate.isNotEmpty(SeoConfigUtil.getCategoryUrlSuffix())) { urlBuilder.append(SeoConfigUtil.getCategoryUrlSuffix()); } return urlBuilder.toString(); } /** * Get a string lower cased and hyphen connected. * * @param name a String to be transformed * @return String nice name */ protected static String getNiceName(String name) { Perl5Matcher matcher = new Perl5Matcher(); String niceName = null; if (UtilValidate.isNotEmpty(name)) { name = name.trim().replaceAll(" ", URL_HYPHEN); if (UtilValidate.isNotEmpty(name) && matcher.matches(name, asciiPattern)) { niceName = name; } } return niceName; } public static boolean forwardProductUri(HttpServletRequest request, HttpServletResponse response, Delegator delegator) throws ServletException, IOException { return forwardProductUri(request, response, delegator, null); } public static boolean forwardProductUri(HttpServletRequest request, HttpServletResponse response, Delegator delegator, String controlServlet) throws ServletException, IOException { return forwardUri(request, response, delegator, controlServlet); } /** * Forward a uri according to forward pattern regular expressions. * @param request * @param response * @param delegator * @param controlServlet * @return boolean to indicate whether the uri is forwarded. * @throws ServletException * @throws IOException */ public static boolean forwardUri(HttpServletRequest request, HttpServletResponse response, Delegator delegator, String controlServlet) throws ServletException, IOException { String pathInfo = request.getRequestURI(); String contextPath = request.getContextPath(); if (!isCategoryMapInitialed()) { initCategoryMap(request, delegator); } if (!SeoConfigUtil.isCategoryUrlEnabled(contextPath)) { return false; } List<String> pathElements = StringUtil.split(pathInfo, "/"); if (UtilValidate.isEmpty(pathElements)) { return false; } // remove context path pathInfo = SeoUrlUtil.removeContextPath(pathInfo, contextPath); // remove servlet path pathInfo = SeoUrlUtil.removeContextPath(pathInfo, request.getServletPath()); if (pathInfo.startsWith("/" + CatalogUrlServlet.CATEGORY_REQUEST + "/")) { return forwardCategoryUri(request, response, delegator, controlServlet); } String lastPathElement = pathElements.get(pathElements.size() - 1); String categoryId = null; String productId = null; if (UtilValidate.isNotEmpty(lastPathElement)) { if (UtilValidate.isNotEmpty(SeoConfigUtil.getCategoryUrlSuffix())) { if (lastPathElement.endsWith(SeoConfigUtil.getCategoryUrlSuffix())) { lastPathElement = lastPathElement.substring(0, lastPathElement.length() - SeoConfigUtil.getCategoryUrlSuffix().length()); } else { return false; } } if (SeoConfigUtil.isCategoryNameEnabled() || pathInfo.startsWith("/" + CatalogUrlServlet.CATEGORY_REQUEST + "/")) { for (String categoryName : categoryNameIdMap.keySet()) { if (lastPathElement.startsWith(categoryName)) { categoryId = categoryNameIdMap.get(categoryName); if (!lastPathElement.equals(categoryName)) { lastPathElement = lastPathElement.substring(categoryName.length() + URL_HYPHEN.length()); } break; } } if (UtilValidate.isEmpty(categoryId)) { categoryId = lastPathElement; } } if (UtilValidate.isNotEmpty(lastPathElement)) { List<String> urlElements = StringUtil.split(lastPathElement, URL_HYPHEN); if (UtilValidate.isEmpty(urlElements)) { try { if (delegator.findOne("Product", UtilMisc.toMap("productId", lastPathElement), true) != null) { productId = lastPathElement; } } catch (GenericEntityException e) { Debug.logError(e, "Error looking up product info for ProductUrl with path info [" + pathInfo + "]: " + e.toString(), module); } } else { int i = urlElements.size() - 1; String tempProductId = urlElements.get(i); while (i >= 0) { try { List<EntityExpr> exprs = new LinkedList<EntityExpr>(); exprs.add(EntityCondition.makeCondition("productId", EntityOperator.EQUALS, lastPathElement)); exprs.add(EntityCondition.makeCondition("productId", EntityOperator.EQUALS, tempProductId)); List<GenericValue> products = delegator.findList("Product", EntityCondition.makeCondition(exprs, EntityOperator.OR), UtilMisc.toSet("productId", "productName"), null, null, true); if (products != null && products.size() > 0) { if (products.size() == 1) { productId = products.get(0).getString("productId"); break; } else { productId = tempProductId; break; } } else if (i > 0) { tempProductId = urlElements.get(i - 1) + URL_HYPHEN + tempProductId; } } catch (GenericEntityException e) { Debug.logError(e, "Error looking up product info for ProductUrl with path info [" + pathInfo + "]: " + e.toString(), module); } i--; } } } } if (UtilValidate.isNotEmpty(productId) || UtilValidate.isNotEmpty(categoryId)) { if (categoryId != null) { request.setAttribute("productCategoryId", categoryId); } if (productId != null) { request.setAttribute("product_id", productId); request.setAttribute("productId", productId); } StringBuilder urlBuilder = new StringBuilder(); if (UtilValidate.isNotEmpty(controlServlet)) { urlBuilder.append("/" + controlServlet); } urlBuilder.append("/" + (productId != null ? CatalogUrlServlet.PRODUCT_REQUEST : CatalogUrlServlet.CATEGORY_REQUEST)); UrlServletHelper.setViewQueryParameters(request, urlBuilder); Debug.logInfo("[Filtered request]: " + pathInfo + " (" + urlBuilder + ")", module); RequestDispatcher rd = request.getRequestDispatcher(urlBuilder.toString()); rd.forward(request, response); return true; } return false; } /** * Forward a category uri according to forward pattern regular expressions. * @param request * @param response * @param delegator * @param controlServlet * @return * @throws ServletException * @throws IOException */ public static boolean forwardCategoryUri(HttpServletRequest request, HttpServletResponse response, Delegator delegator, String controlServlet) throws ServletException, IOException { String pathInfo = request.getRequestURI(); String contextPath = request.getContextPath(); if (!isCategoryMapInitialed()) { initCategoryMap(request); } if (!SeoConfigUtil.isCategoryUrlEnabled(contextPath)) { return false; } List<String> pathElements = StringUtil.split(pathInfo, "/"); if (UtilValidate.isEmpty(pathElements)) { return false; } String lastPathElement = pathElements.get(pathElements.size() - 1); String categoryId = null; if (UtilValidate.isNotEmpty(lastPathElement)) { if (UtilValidate.isNotEmpty(SeoConfigUtil.getCategoryUrlSuffix())) { if (lastPathElement.endsWith(SeoConfigUtil.getCategoryUrlSuffix())) { lastPathElement = lastPathElement.substring(0, lastPathElement.length() - SeoConfigUtil.getCategoryUrlSuffix().length()); } else { return false; } } for (String categoryName : categoryNameIdMap.keySet()) { if (lastPathElement.startsWith(categoryName)) { categoryId = categoryNameIdMap.get(categoryName); break; } } if (UtilValidate.isEmpty(categoryId)) { categoryId = lastPathElement.trim(); } } if (UtilValidate.isNotEmpty(categoryId)) { request.setAttribute("productCategoryId", categoryId); StringBuilder urlBuilder = new StringBuilder(); if (UtilValidate.isNotEmpty(controlServlet)) { urlBuilder.append("/" + controlServlet); } urlBuilder.append("/" + CatalogUrlServlet.CATEGORY_REQUEST); UrlServletHelper.setViewQueryParameters(request, urlBuilder); Debug.logInfo("[Filtered request]: " + pathInfo + " (" + urlBuilder + ")", module); RequestDispatcher rd = request.getRequestDispatcher(urlBuilder.toString()); rd.forward(request, response); return true; } return false; } /** * This is used when building product url in services. * * @param delegator * @param wrapper * @param prefix * @param contextPath * @param currentCategoryId * @param previousCategoryId * @param productId * @return */ public static String makeProductUrl(Delegator delegator, ProductContentWrapper wrapper, String prefix, String contextPath, String currentCategoryId, String previousCategoryId, String productId) { StringBuilder urlBuilder = new StringBuilder(); GenericValue product = null; urlBuilder.append(prefix); if (urlBuilder.charAt(urlBuilder.length() - 1) != '/') { urlBuilder.append("/"); } if (UtilValidate.isNotEmpty(productId)) { try { product = delegator.findOne("Product", UtilMisc.toMap("productId", productId), true); } catch (GenericEntityException e) { Debug.logError(e, "Error looking up product info for productId [" + productId + "]: " + e.toString(), module); } } if (product != null) { urlBuilder.append(CatalogUrlServlet.PRODUCT_REQUEST + "/"); } if (UtilValidate.isNotEmpty(currentCategoryId)) { List<String> trail = null; trail = CategoryWorker.adjustTrail(trail, currentCategoryId, previousCategoryId); if (!SeoConfigUtil.isCategoryUrlEnabled(contextPath)) { for (String trailCategoryId: trail) { if ("TOP".equals(trailCategoryId)) continue; urlBuilder.append("/"); urlBuilder.append(trailCategoryId); } } else { if (trail != null && trail.size() > 1) { String lastCategoryId = trail.get(trail.size() - 1); if (!"TOP".equals(lastCategoryId)) { if (SeoConfigUtil.isCategoryNameEnabled()) { String categoryName = CatalogUrlSeoTransform.getCategoryIdNameMap().get(lastCategoryId); if (UtilValidate.isNotEmpty(categoryName)) { urlBuilder.append(categoryName); if (product != null) { urlBuilder.append(URL_HYPHEN); } } } } } } } if (UtilValidate.isNotEmpty(productId)) { if (product != null) { String productName = product.getString("productName"); productName = SeoUrlUtil.replaceSpecialCharsUrl(productName); if (UtilValidate.isNotEmpty(productName)) { urlBuilder.append(productName + URL_HYPHEN); } else { StringWrapper alternativeUrl = wrapper.get("ALTERNATIVE_URL", "url"); if (UtilValidate.isNotEmpty(alternativeUrl) && UtilValidate.isNotEmpty(alternativeUrl.toString())) { productName = SeoUrlUtil.replaceSpecialCharsUrl(alternativeUrl.toString()); if (UtilValidate.isNotEmpty(productName)) { urlBuilder.append(productName + URL_HYPHEN); } } } } try { urlBuilder.append(productId); } catch (Exception e) { urlBuilder.append(productId); } } if (!urlBuilder.toString().endsWith("/") && UtilValidate.isNotEmpty(SeoConfigUtil.getCategoryUrlSuffix())) { urlBuilder.append(SeoConfigUtil.getCategoryUrlSuffix()); } return urlBuilder.toString(); } /** * This is used when building category url in services. * * @param delegator * @param wrapper * @param prefix * @param currentCategoryId * @param previousCategoryId * @param productId * @param viewSize * @param viewIndex * @param viewSort * @param searchString * @return */ public static String makeCategoryUrl(Delegator delegator, CategoryContentWrapper wrapper, String prefix, String currentCategoryId, String previousCategoryId, String productId, String viewSize, String viewIndex, String viewSort, String searchString) { StringBuilder urlBuilder = new StringBuilder(); urlBuilder.append(prefix); if (urlBuilder.charAt(urlBuilder.length() - 1) != '/') { urlBuilder.append("/"); } urlBuilder.append(CatalogUrlServlet.CATEGORY_REQUEST + "/"); if (UtilValidate.isNotEmpty(currentCategoryId)) { List<String> trail = null; trail = CategoryWorker.adjustTrail(trail, currentCategoryId, previousCategoryId); if (trail != null && trail.size() > 1) { String lastCategoryId = trail.get(trail.size() - 1); if (!"TOP".equals(lastCategoryId)) { String categoryName = CatalogUrlSeoTransform.getCategoryIdNameMap().get(lastCategoryId); if (UtilValidate.isNotEmpty(categoryName)) { urlBuilder.append(categoryName); urlBuilder.append(URL_HYPHEN); urlBuilder.append(lastCategoryId.trim().replaceAll(" ", URL_HYPHEN)); } else { urlBuilder.append(lastCategoryId.trim().replaceAll(" ", URL_HYPHEN)); } } } } if (!urlBuilder.toString().endsWith("/") && UtilValidate.isNotEmpty(SeoConfigUtil.getCategoryUrlSuffix())) { urlBuilder.append(SeoConfigUtil.getCategoryUrlSuffix()); } // append view index if (UtilValidate.isNotEmpty(viewIndex)) { if (!urlBuilder.toString().endsWith("?") && !urlBuilder.toString().endsWith("&")) { urlBuilder.append("?"); } urlBuilder.append("viewIndex=" + viewIndex + "&"); } // append view size if (UtilValidate.isNotEmpty(viewSize)) { if (!urlBuilder.toString().endsWith("?") && !urlBuilder.toString().endsWith("&")) { urlBuilder.append("?"); } urlBuilder.append("viewSize=" + viewSize + "&"); } // append view sort if (UtilValidate.isNotEmpty(viewSort)) { if (!urlBuilder.toString().endsWith("?") && !urlBuilder.toString().endsWith("&")) { urlBuilder.append("?"); } urlBuilder.append("viewSort=" + viewSort + "&"); } // append search string if (UtilValidate.isNotEmpty(searchString)) { if (!urlBuilder.toString().endsWith("?") && !urlBuilder.toString().endsWith("&")) { urlBuilder.append("?"); } urlBuilder.append("searchString=" + searchString + "&"); } if (urlBuilder.toString().endsWith("&")) { return urlBuilder.toString().substring(0, urlBuilder.toString().length()-1); } return urlBuilder.toString(); } }