Added: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/UrlRegexpContextFilter.java
URL: http://svn.apache.org/viewvc/ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/UrlRegexpContextFilter.java?rev=1535171&view=auto ============================================================================== --- ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/UrlRegexpContextFilter.java (added) +++ ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/UrlRegexpContextFilter.java Wed Oct 23 20:48:36 2013 @@ -0,0 +1,325 @@ +/******************************************************************************* + * 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.ofbiz.product.category; + +import static org.ofbiz.base.util.UtilGenerics.checkMap; + +import java.io.IOException; +import java.net.URL; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.servlet.FilterChain; +import javax.servlet.RequestDispatcher; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.ofbiz.base.util.Debug; +import org.ofbiz.base.util.GeneralException; +import org.ofbiz.base.util.StringUtil; +import org.ofbiz.base.util.UtilHttp; +import org.ofbiz.base.util.UtilMisc; +import org.ofbiz.base.util.UtilObject; +import org.ofbiz.base.util.UtilProperties; +import org.ofbiz.base.util.UtilValidate; +import org.ofbiz.entity.Delegator; +import org.ofbiz.entity.DelegatorFactory; +import org.ofbiz.entity.GenericEntityException; +import org.ofbiz.entity.GenericValue; +import org.ofbiz.entity.condition.EntityCondition; +import org.ofbiz.entity.util.EntityUtil; +import org.ofbiz.product.category.ftl.UrlRegexpTransform; +import org.ofbiz.security.Security; +import org.ofbiz.service.LocalDispatcher; +import org.ofbiz.webapp.control.ConfigXMLReader; +import org.ofbiz.webapp.control.ConfigXMLReader.ControllerConfig; +import org.ofbiz.webapp.control.ContextFilter; +import org.ofbiz.webapp.control.WebAppConfigurationException; + +/** + * UrlRegexpContextFilter - Restricts access to raw files and configures servlet objects. + */ +public class UrlRegexpContextFilter extends ContextFilter { + + public static final String module = UrlRegexpContextFilter.class.getName(); + + /** + * @throws GeneralException + * @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain) + */ + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { + HttpServletRequest httpRequest = (HttpServletRequest) request; + HttpServletResponse httpResponse = (HttpServletResponse) response; + + String uri = httpRequest.getRequestURI(); + boolean forwarded = UrlRegexpTransform.forwardUri(httpResponse, uri); + if (forwarded) { + return; + } + + URL controllerConfigURL = ConfigXMLReader.getControllerConfigURL(config.getServletContext()); + ControllerConfig controllerConfig = null; + Map<String, ConfigXMLReader.RequestMap> requestMaps = null; + try { + controllerConfig = ConfigXMLReader.getControllerConfig(controllerConfigURL); + requestMaps = controllerConfig.getRequestMapMap(); + } catch (WebAppConfigurationException e) { + Debug.logError(e, "Exception thrown while parsing controller.xml file: ", module); + throw new ServletException(e); + } + Set<String> uris = requestMaps.keySet(); + + // NOTE: the following part is copied from org.ofbiz.webapp.control.ContextFilter.doFilter method, please update this if framework is updated. + // Debug.logInfo("Running ContextFilter.doFilter", module); + + // ----- Servlet Object Setup ----- + // set the cached class loader for more speedy running in this thread + String disableCachedClassloader = config.getInitParameter("disableCachedClassloader"); + if (disableCachedClassloader == null || !"Y".equalsIgnoreCase(disableCachedClassloader)) { + Thread.currentThread().setContextClassLoader(localCachedClassLoader); + } + + // set the ServletContext in the request for future use + httpRequest.setAttribute("servletContext", config.getServletContext()); + + // set the webSiteId in the session + if (UtilValidate.isEmpty(httpRequest.getSession().getAttribute("webSiteId"))) { + httpRequest.getSession().setAttribute("webSiteId", config.getServletContext().getAttribute("webSiteId")); + } + + // set the filesystem path of context root. + httpRequest.setAttribute("_CONTEXT_ROOT_", config.getServletContext().getRealPath("/")); + + // set the server root url + StringBuffer serverRootUrl = UtilHttp.getServerRootUrl(httpRequest); + httpRequest.setAttribute("_SERVER_ROOT_URL_", serverRootUrl.toString()); + + // request attributes from redirect call + String reqAttrMapHex = (String) httpRequest.getSession().getAttribute("_REQ_ATTR_MAP_"); + if (UtilValidate.isNotEmpty(reqAttrMapHex)) { + byte[] reqAttrMapBytes = StringUtil.fromHexString(reqAttrMapHex); + Map<String, Object> reqAttrMap = checkMap(UtilObject.getObject(reqAttrMapBytes), String.class, Object.class); + if (reqAttrMap != null) { + for (Map.Entry<String, Object> entry : reqAttrMap.entrySet()) { + httpRequest.setAttribute(entry.getKey(), entry.getValue()); + } + } + httpRequest.getSession().removeAttribute("_REQ_ATTR_MAP_"); + } + + // ----- Context Security ----- + // check if we are disabled + String disableSecurity = config.getInitParameter("disableContextSecurity"); + if (disableSecurity != null && "Y".equalsIgnoreCase(disableSecurity)) { + chain.doFilter(httpRequest, httpResponse); + return; + } + + // check if we are told to redirect everthing + String redirectAllTo = config.getInitParameter("forceRedirectAll"); + if (UtilValidate.isNotEmpty(redirectAllTo)) { + // little trick here so we don't loop on ourself + if (httpRequest.getSession().getAttribute("_FORCE_REDIRECT_") == null) { + httpRequest.getSession().setAttribute("_FORCE_REDIRECT_", "true"); + Debug.logWarning("Redirecting user to: " + redirectAllTo, module); + + if (!redirectAllTo.toLowerCase().startsWith("http")) { + redirectAllTo = httpRequest.getContextPath() + redirectAllTo; + } + httpResponse.sendRedirect(redirectAllTo); + return; + } else { + httpRequest.getSession().removeAttribute("_FORCE_REDIRECT_"); + chain.doFilter(httpRequest, httpResponse); + return; + } + } + + // test to see if we have come through the control servlet already, if not do the processing + String requestPath = null; + String contextUri = null; + if (httpRequest.getAttribute(ContextFilter.FORWARDED_FROM_SERVLET) == null) { + if (debug) Debug.log("[Domain]: " + httpRequest.getServerName() + " [Request]: " + httpRequest.getRequestURI(), module); + + requestPath = httpRequest.getServletPath(); + if (requestPath == null) requestPath = ""; + if (requestPath.lastIndexOf("/") > 0) { + if (requestPath.indexOf("/") == 0) { + requestPath = "/" + requestPath.substring(1, requestPath.indexOf("/", 1)); + } else { + requestPath = requestPath.substring(1, requestPath.indexOf("/")); + } + } + + String requestInfo = httpRequest.getServletPath(); + if (requestInfo == null) requestInfo = ""; + if (requestInfo.lastIndexOf("/") >= 0) { + requestInfo = requestInfo.substring(0, requestInfo.lastIndexOf("/")) + "/*"; + } + + StringBuilder contextUriBuffer = new StringBuilder(); + if (httpRequest.getContextPath() != null) { + contextUriBuffer.append(httpRequest.getContextPath()); + } + if (httpRequest.getServletPath() != null) { + contextUriBuffer.append(httpRequest.getServletPath()); + } + if (httpRequest.getPathInfo() != null) { + contextUriBuffer.append(httpRequest.getPathInfo()); + } + contextUri = contextUriBuffer.toString(); + + List<String> pathItemList = StringUtil.split(httpRequest.getPathInfo(), "/"); + String viewName = ""; + if (pathItemList != null) { + viewName = pathItemList.get(0); + } + + // Debug.logInfo("In ContextFilter.doFilter, FORWARDED_FROM_SERVLET is NOT set", module); + String allowedPath = config.getInitParameter("allowedPaths"); + String redirectPath = config.getInitParameter("redirectPath"); + String errorCode = config.getInitParameter("errorCode"); + + List<String> allowList = StringUtil.split(allowedPath, ":"); + allowList.add("/"); // No path is allowed. + if (UtilValidate.isNotEmpty(httpRequest.getServletPath())) { + allowList.add(""); // No path is allowed if servlet path is not empty. + } + + // Verbose Debugging + if (Debug.verboseOn()) { + for (String allow : allowList) { + Debug.logVerbose("[Allow]: " + allow, module); + } + Debug.logVerbose("[Request path]: " + requestPath, module); + Debug.logVerbose("[Request info]: " + requestInfo, module); + Debug.logVerbose("[Servlet path]: " + httpRequest.getServletPath(), module); + Debug.logVerbose("[View name]: " + viewName, module); + Debug.logVerbose("[Not In AllowList]: " + (!allowList.contains(requestPath) && !allowList.contains(requestInfo) && !allowList.contains(httpRequest.getServletPath())), module); + Debug.logVerbose("[Not In controller]: " + (UtilValidate.isEmpty(requestPath) && UtilValidate.isEmpty(httpRequest.getServletPath()) && !uris.contains(viewName)), module); + } + + // check to make sure the requested url is allowed + if (!allowList.contains(requestPath) && !allowList.contains(requestInfo) && !allowList.contains(httpRequest.getServletPath()) + && (UtilValidate.isEmpty(requestPath) && UtilValidate.isEmpty(httpRequest.getServletPath()) && !uris.contains(viewName))) { + String filterMessage = "[Filtered request]: " + contextUri; + + if (redirectPath == null) { + int error = 404; + if (UtilValidate.isNotEmpty(errorCode)) { + try { + error = Integer.parseInt(errorCode); + } catch (NumberFormatException nfe) { + Debug.logWarning(nfe, "Error code specified would not parse to Integer : " + errorCode, module); + } + } + filterMessage = filterMessage + " (" + error + ")"; + httpResponse.sendError(error, contextUri); + } else { + filterMessage = filterMessage + " (" + redirectPath + ")"; + if (!redirectPath.toLowerCase().startsWith("http")) { + redirectPath = httpRequest.getContextPath() + redirectPath; + } + // httpResponse.sendRedirect(redirectPath); + if (uri.equals("") || uri.equals("/")) { + // redirect without any url change in browser + RequestDispatcher rd = request.getRequestDispatcher(redirectPath); + rd.forward(request, response); + } else { + // redirect with url change in browser + httpResponse.setStatus(UrlRegexpConfigUtil.DEFAULT_RESPONSECODE); + httpResponse.setHeader("Location", redirectPath); + } + } + Debug.logWarning(filterMessage, module); + return; + } + } + + // check if multi tenant is enabled + String useMultitenant = UtilProperties.getPropertyValue("general.properties", "multitenant"); + if ("Y".equals(useMultitenant)) { + // get tenant delegator by domain name + String serverName = httpRequest.getServerName(); + try { + // if tenant was specified, replace delegator with the new per-tenant delegator and set tenantId to session attribute + Delegator delegator = getDelegator(config.getServletContext()); + List<GenericValue> tenants = delegator.findList("Tenant", EntityCondition.makeCondition("domainName", serverName), null, UtilMisc.toList("-createdStamp"), null, false); + if (UtilValidate.isNotEmpty(tenants)) { + GenericValue tenant = EntityUtil.getFirst(tenants); + String tenantId = tenant.getString("tenantId"); + + // if the request path is a root mount then redirect to the initial path + if (UtilValidate.isNotEmpty(requestPath) && requestPath.equals(contextUri)) { + String initialPath = tenant.getString("initialPath"); + if (UtilValidate.isNotEmpty(initialPath) && !"/".equals(initialPath)) { + ((HttpServletResponse) response).sendRedirect(initialPath); + return; + } + } + + // make that tenant active, setup a new delegator and a new dispatcher + String tenantDelegatorName = delegator.getDelegatorBaseName() + "#" + tenantId; + httpRequest.getSession().setAttribute("delegatorName", tenantDelegatorName); + + // after this line the delegator is replaced with the new per-tenant delegator + delegator = DelegatorFactory.getDelegator(tenantDelegatorName); + config.getServletContext().setAttribute("delegator", delegator); + + // clear web context objects + // config.getServletContext().setAttribute("authorization", null); + config.getServletContext().setAttribute("security", null); + config.getServletContext().setAttribute("dispatcher", null); + + // initialize authorizer + // getAuthz(); + // initialize security + Security security = getSecurity(); + // initialize the services dispatcher + LocalDispatcher dispatcher = getDispatcher(config.getServletContext()); + + // set web context objects + httpRequest.getSession().setAttribute("dispatcher", dispatcher); + httpRequest.getSession().setAttribute("security", security); + + httpRequest.setAttribute("tenantId", tenantId); + } + + // NOTE DEJ20101130: do NOT always put the delegator name in the user's session because the user may + // have logged in and specified a tenant, and even if no Tenant record with a matching domainName field + // is found this will change the user's delegator back to the base one instead of the one for the + // tenant specified on login + // httpRequest.getSession().setAttribute("delegatorName", delegator.getDelegatorName()); + } catch (GenericEntityException e) { + Debug.logWarning(e, "Unable to get Tenant", module); + } + } + + // we're done checking; continue on + chain.doFilter(httpRequest, httpResponse); + + // reset thread local security + // AbstractAuthorization.clearThreadLocal(); + } + +} Propchange: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/UrlRegexpContextFilter.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/UrlRegexpContextFilter.java ------------------------------------------------------------------------------ svn:keywords = Date Rev Author URL Id Propchange: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/UrlRegexpContextFilter.java ------------------------------------------------------------------------------ svn:mime-type = text/plain Added: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/UrlUtil.java URL: http://svn.apache.org/viewvc/ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/UrlUtil.java?rev=1535171&view=auto ============================================================================== --- ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/UrlUtil.java (added) +++ ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/UrlUtil.java Wed Oct 23 20:48:36 2013 @@ -0,0 +1,15 @@ +package org.ofbiz.product.category; + +import org.ofbiz.base.util.UtilValidate; + +public class UrlUtil { + public static String replaceSpecialCharsUrl(String url) { + if (UtilValidate.isEmpty(url)) { + url = ""; + } + for (String characterPattern : UrlRegexpConfigUtil.getNameFilters().keySet()) { + url = url.replaceAll(characterPattern, UrlRegexpConfigUtil.getNameFilters().get(characterPattern)); + } + return url; + } +} Propchange: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/UrlUtil.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/UrlUtil.java ------------------------------------------------------------------------------ svn:keywords = Date Rev Author URL Id Propchange: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/UrlUtil.java ------------------------------------------------------------------------------ svn:mime-type = text/plain Added: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/ftl/CatalogAltUrlSeoTransform.java URL: http://svn.apache.org/viewvc/ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/ftl/CatalogAltUrlSeoTransform.java?rev=1535171&view=auto ============================================================================== --- ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/ftl/CatalogAltUrlSeoTransform.java (added) +++ ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/ftl/CatalogAltUrlSeoTransform.java Wed Oct 23 20:48:36 2013 @@ -0,0 +1,160 @@ +/******************************************************************************* + * 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.ofbiz.product.category.ftl; + +import java.io.IOException; +import java.io.Writer; +import java.util.Locale; +import java.util.Map; + +import javax.servlet.http.HttpServletRequest; + +import org.ofbiz.base.util.UtilMisc; +import org.ofbiz.base.util.UtilValidate; +import org.ofbiz.base.util.template.FreeMarkerWorker; +import org.ofbiz.entity.Delegator; +import org.ofbiz.entity.GenericEntityException; +import org.ofbiz.entity.GenericValue; +import org.ofbiz.product.category.CatalogUrlFilter; +import org.ofbiz.product.category.CategoryContentWrapper; +import org.ofbiz.product.category.UrlRegexpConfigUtil; +import org.ofbiz.product.product.ProductContentWrapper; +import org.ofbiz.service.LocalDispatcher; +import org.ofbiz.webapp.control.RequestHandler; + +import freemarker.core.Environment; +import freemarker.ext.beans.BeanModel; +import freemarker.ext.beans.NumberModel; +import freemarker.ext.beans.StringModel; +import freemarker.template.SimpleNumber; +import freemarker.template.SimpleScalar; +import freemarker.template.TemplateModelException; +import freemarker.template.TemplateTransformModel; + +public class CatalogAltUrlSeoTransform implements TemplateTransformModel { + public final static String module = CatalogUrlSeoTransform.class.getName(); + + 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(); + } else if (o instanceof SimpleNumber) { + return ((SimpleNumber) o).getAsNumber().toString(); + } else if (o instanceof NumberModel) { + return ((NumberModel) o).getAsNumber().toString(); + } + return null; + } + + public boolean checkArg(Map args, String key, boolean defaultValue) { + if (!args.containsKey(key)) { + return defaultValue; + } else { + Object o = args.get(key); + if (o instanceof SimpleScalar) { + SimpleScalar s = (SimpleScalar) o; + return "true".equalsIgnoreCase(s.getAsString()); + } + return defaultValue; + } + } + + @Override + public Writer getWriter(final Writer out, final Map args) throws TemplateModelException, IOException { + final StringBuilder buf = new StringBuilder(); + final boolean fullPath = checkArg(args, "fullPath", false); + final boolean secure = checkArg(args, "secure", false); + + return new Writer(out) { + + public void write(char[] cbuf, int off, int len) throws IOException { + buf.append(cbuf, off, len); + } + + public void flush() throws IOException { + out.flush(); + } + + public void close() throws IOException { + try { + Environment env = Environment.getCurrentEnvironment(); + BeanModel req = (BeanModel) env.getVariable("request"); + String previousCategoryId = getStringArg(args, "previousCategoryId"); + String productCategoryId = getStringArg(args, "productCategoryId"); + String productId = getStringArg(args, "productId"); + String url = ""; + + Object prefix = env.getVariable("urlPrefix"); + String viewSize = getStringArg(args, "viewSize"); + String viewIndex = getStringArg(args, "viewIndex"); + String viewSort = getStringArg(args, "viewSort"); + String searchString = getStringArg(args, "searchString"); + if (req != null) { + HttpServletRequest request = (HttpServletRequest) req.getWrappedObject(); + StringBuilder newURL = new StringBuilder(); + if (UtilValidate.isNotEmpty(productId)) { + if (UrlRegexpConfigUtil.isCategoryUrlEnabled(request.getContextPath())) { + url = CatalogUrlSeoTransform.makeProductUrl(request, productId, productCategoryId, previousCategoryId); + } else { + url = CatalogUrlFilter.makeProductUrl(request, previousCategoryId, productCategoryId, productId); + } + } else { + if (UrlRegexpConfigUtil.isCategoryUrlEnabled(request.getContextPath())) { + url = CatalogUrlSeoTransform.makeCategoryUrl(request, productCategoryId, previousCategoryId, viewSize, viewIndex, viewSort, searchString); + } else { + url = CatalogUrlFilter.makeCategoryUrl(request, previousCategoryId, productCategoryId, productId, viewSize, viewIndex, viewSort, searchString); + } + } + // make the link + if (fullPath) { + String serverRootUrl = RequestHandler.getDefaultServerRootUrl(request, secure); + newURL.append(serverRootUrl); + } + newURL.append(url); + out.write(newURL.toString()); + } else if (prefix != null) { + Delegator delegator = FreeMarkerWorker.getWrappedObject("delegator", env); + LocalDispatcher dispatcher = FreeMarkerWorker.getWrappedObject("dispatcher", env); + Locale locale = (Locale) args.get("locale"); + if (UtilValidate.isNotEmpty(productId)) { + GenericValue product = delegator.findOne("Product", UtilMisc.toMap("productId", productId), false); + ProductContentWrapper wrapper = new ProductContentWrapper(dispatcher, product, locale, "text/html"); + url = CatalogUrlFilter.makeProductUrl(delegator, wrapper, null, ((StringModel) prefix).getAsString(), previousCategoryId, productCategoryId, + productId); + } else { + GenericValue productCategory = delegator.findOne("ProductCategory", UtilMisc.toMap("productCategoryId", productCategoryId), false); + CategoryContentWrapper wrapper = new CategoryContentWrapper(dispatcher, productCategory, locale, "text/html"); + url = CatalogUrlFilter.makeCategoryUrl(delegator, wrapper, null, ((StringModel) prefix).getAsString(), previousCategoryId, productCategoryId, + productId, viewSize, viewIndex, viewSort, searchString); + } + out.write(url.toString()); + } else { + out.write(buf.toString()); + } + } catch (TemplateModelException e) { + throw new IOException(e.getMessage()); + } catch (GenericEntityException e) { + throw new IOException(e.getMessage()); + } + } + }; + } +} Propchange: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/ftl/CatalogAltUrlSeoTransform.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/ftl/CatalogAltUrlSeoTransform.java ------------------------------------------------------------------------------ svn:keywords = Date Rev Author URL Id Propchange: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/ftl/CatalogAltUrlSeoTransform.java ------------------------------------------------------------------------------ svn:mime-type = text/plain Added: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/ftl/CatalogUrlSeoTransform.java URL: http://svn.apache.org/viewvc/ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/ftl/CatalogUrlSeoTransform.java?rev=1535171&view=auto ============================================================================== --- ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/ftl/CatalogUrlSeoTransform.java (added) +++ ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/ftl/CatalogUrlSeoTransform.java Wed Oct 23 20:48:36 2013 @@ -0,0 +1,668 @@ +/******************************************************************************* + * 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.ofbiz.product.category.ftl; + +import java.io.IOException; +import java.io.Writer; +import java.util.Collection; +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 javolution.util.FastList; +import javolution.util.FastMap; + +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.ofbiz.base.util.Debug; +import org.ofbiz.base.util.StringUtil; +import org.ofbiz.base.util.StringUtil.StringWrapper; +import org.ofbiz.base.util.UtilMisc; +import org.ofbiz.base.util.UtilValidate; +import org.ofbiz.common.UrlServletHelper; +import org.ofbiz.entity.Delegator; +import org.ofbiz.entity.GenericEntityException; +import org.ofbiz.entity.GenericValue; +import org.ofbiz.entity.condition.EntityCondition; +import org.ofbiz.entity.condition.EntityExpr; +import org.ofbiz.entity.condition.EntityOperator; +import org.ofbiz.product.category.CatalogUrlServlet; +import org.ofbiz.product.category.CategoryContentWrapper; +import org.ofbiz.product.category.CategoryWorker; +import org.ofbiz.product.category.UrlRegexpConfigUtil; +import org.ofbiz.product.category.UrlUtil; +import org.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> m_categoryNameIdMap = null; + private static Map<String, String> m_categoryIdNameMap = null; + private static boolean m_categoryMapInitialed = false; + private static final String m_asciiRegexp = "^[0-9-_a-zA-Z]*$"; + private static Perl5Compiler m_perlCompiler = new Perl5Compiler(); + private static Pattern m_asciiPattern = null; + public static final String URL_HYPHEN = "-"; + + static { + if (!UrlRegexpConfigUtil.isInitialed()) { + UrlRegexpConfigUtil.init(); + } + try { + m_asciiPattern = m_perlCompiler.compile(m_asciiRegexp, Perl5Compiler.DEFAULT_MASK); + } catch (MalformedPatternException e1) { + // do nothing + } + } + + @SuppressWarnings("unchecked") + 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 + @SuppressWarnings("unchecked") + 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 (UrlRegexpConfigUtil.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 m_categoryMapInitialed; + } + + /** + * Get the category name/id map. + * + * @return the category name/id map + */ + public static Map<String, String> getCategoryNameIdMap() { + return m_categoryNameIdMap; + } + + /** + * Get the category id/name map. + * + * @return the category id/name map + */ + public static Map<String, String> getCategoryIdNameMap() { + return m_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 (UrlRegexpConfigUtil.checkCategoryUrl()) { + m_categoryNameIdMap = FastMap.newInstance(); + m_categoryIdNameMap = FastMap.newInstance(); + 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 = UrlUtil.replaceSpecialCharsUrl(categoryName.trim().toLowerCase()); + if (matcher.matches(categoryName, m_asciiPattern)) { + categoryIdName = categoryName.toLowerCase().replaceAll(" ", URL_HYPHEN); + categoryNameId = categoryIdName + URL_HYPHEN + categoryId.trim().toLowerCase().replaceAll(" ", URL_HYPHEN); + } else { + categoryIdName = categoryId.trim().toLowerCase().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"); + if (UtilValidate.isNotEmpty(alternativeUrl) && UtilValidate.isNotEmpty(alternativeUrl.toString())) { + categoryIdName = UrlUtil.replaceSpecialCharsUrl(alternativeUrl.toString()); + categoryNameId = categoryIdName + URL_HYPHEN + categoryId.trim().toLowerCase().replaceAll(" ", URL_HYPHEN); + } else { + categoryNameId = categoryId.trim().toLowerCase().replaceAll(" ", URL_HYPHEN); + categoryIdName = categoryNameId; + } + } + if (m_categoryNameIdMap.containsKey(categoryNameId)) { + categoryNameId = categoryId.trim().toLowerCase().replaceAll(" ", URL_HYPHEN); + categoryIdName = categoryNameId; + } + if (!matcher.matches(categoryNameId, m_asciiPattern) || m_categoryNameIdMap.containsKey(categoryNameId)) { + continue; + } + m_categoryNameIdMap.put(categoryNameId, categoryId); + m_categoryIdNameMap.put(categoryId, categoryIdName); + } + } catch (GenericEntityException e) { + Debug.logError(e, module); + } + } + m_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.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 (!UrlRegexpConfigUtil.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 (UrlRegexpConfigUtil.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 = UrlUtil.replaceSpecialCharsUrl(productName); + if (UtilValidate.isNotEmpty(productName)) { + urlBuilder.append(productName + URL_HYPHEN); + } else { + ProductContentWrapper wrapper = new ProductContentWrapper(product, request); + StringWrapper alternativeUrl = wrapper.get("ALTERNATIVE_URL"); + if (UtilValidate.isNotEmpty(alternativeUrl) && UtilValidate.isNotEmpty(alternativeUrl.toString())) { + productName = UrlUtil.replaceSpecialCharsUrl(alternativeUrl.toString()); + if (UtilValidate.isNotEmpty(productName)) { + urlBuilder.append(productName + URL_HYPHEN); + } + } + } + } + try { + UrlRegexpConfigUtil.addSpecialProductId(productId); + urlBuilder.append(productId.toLowerCase()); + } catch (Exception e) { + urlBuilder.append(productId); + } + } + + if (!urlBuilder.toString().endsWith("/") && UtilValidate.isNotEmpty(UrlRegexpConfigUtil.getCategoryUrlSuffix())) { + urlBuilder.append(UrlRegexpConfigUtil.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.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().toLowerCase().replaceAll(" ", URL_HYPHEN)); + } else { + urlBuilder.append(lastCategoryId.trim().toLowerCase().replaceAll(" ", URL_HYPHEN)); + } + } + } + } + + if (!urlBuilder.toString().endsWith("/") && UtilValidate.isNotEmpty(UrlRegexpConfigUtil.getCategoryUrlSuffix())) { + urlBuilder.append(UrlRegexpConfigUtil.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 (!UrlRegexpConfigUtil.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 (!UrlRegexpConfigUtil.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 (UrlRegexpConfigUtil.isCategoryNameEnabled()) { + String categoryName = CatalogUrlSeoTransform.getCategoryIdNameMap().get(lastCategoryId); + if (UtilValidate.isNotEmpty(categoryName)) { + urlBuilder.append(categoryName + URL_HYPHEN); + } + } + } + } + } + } + + if (UtilValidate.isNotEmpty(productId)) { + if (!UrlRegexpConfigUtil.isCategoryUrlEnabled(contextPath)) { + urlBuilder.append("/p_"); + } else { + productName = UrlUtil.replaceSpecialCharsUrl(productName); + if (UtilValidate.isNotEmpty(productName)) { + urlBuilder.append(productName + URL_HYPHEN); + } + } + urlBuilder.append(productId); + } + + if (!urlBuilder.toString().endsWith("/") && UtilValidate.isNotEmpty(UrlRegexpConfigUtil.getCategoryUrlSuffix())) { + urlBuilder.append(UrlRegexpConfigUtil.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().toLowerCase().replaceAll(" ", URL_HYPHEN); + if (UtilValidate.isNotEmpty(name) && matcher.matches(name, m_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 uri + * String to reverse transform + * @return boolean to indicate whether the uri is forwarded. + * @throws IOException + * @throws ServletException + */ + 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 (!UrlRegexpConfigUtil.isCategoryUrlEnabled(contextPath)) { + return false; + } + List<String> pathElements = StringUtil.split(pathInfo, "/"); + if (UtilValidate.isEmpty(pathElements)) { + return false; + } + 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(UrlRegexpConfigUtil.getCategoryUrlSuffix())) { + if (lastPathElement.endsWith(UrlRegexpConfigUtil.getCategoryUrlSuffix())) { + lastPathElement = lastPathElement.substring(0, lastPathElement.length() - UrlRegexpConfigUtil.getCategoryUrlSuffix().length()); + } else { + return false; + } + } + if (UrlRegexpConfigUtil.isCategoryNameEnabled() || pathInfo.startsWith("/" + CatalogUrlServlet.CATEGORY_REQUEST + "/")) { + for (String categoryName : m_categoryNameIdMap.keySet()) { + if (lastPathElement.startsWith(categoryName)) { + categoryId = m_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 = FastList.newInstance(); + exprs.add(EntityCondition.makeCondition("productId", EntityOperator.EQUALS, lastPathElement)); + if (UrlRegexpConfigUtil.isSpecialProductId(tempProductId)) { + exprs.add(EntityCondition.makeCondition("productId", EntityOperator.EQUALS, UrlRegexpConfigUtil.getSpecialProductId(tempProductId))); + } else { + exprs.add(EntityCondition.makeCondition("productId", EntityOperator.EQUALS, tempProductId.toUpperCase())); + } + 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 uri + * String to reverse transform + * @return String + * @throws IOException + * @throws ServletException + */ + 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 (!UrlRegexpConfigUtil.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(UrlRegexpConfigUtil.getCategoryUrlSuffix())) { + if (lastPathElement.endsWith(UrlRegexpConfigUtil.getCategoryUrlSuffix())) { + lastPathElement = lastPathElement.substring(0, lastPathElement.length() - UrlRegexpConfigUtil.getCategoryUrlSuffix().length()); + } else { + return false; + } + } + for (String categoryName : m_categoryNameIdMap.keySet()) { + if (lastPathElement.startsWith(categoryName)) { + categoryId = m_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; + } + +} \ No newline at end of file Propchange: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/ftl/CatalogUrlSeoTransform.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/ftl/CatalogUrlSeoTransform.java ------------------------------------------------------------------------------ svn:keywords = Date Rev Author URL Id Propchange: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/ftl/CatalogUrlSeoTransform.java ------------------------------------------------------------------------------ svn:mime-type = text/plain Added: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/ftl/OfbizCatalogAltUrlTransform.java URL: http://svn.apache.org/viewvc/ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/ftl/OfbizCatalogAltUrlTransform.java?rev=1535171&view=auto ============================================================================== --- ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/ftl/OfbizCatalogAltUrlTransform.java (added) +++ ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/ftl/OfbizCatalogAltUrlTransform.java Wed Oct 23 20:48:36 2013 @@ -0,0 +1,153 @@ +/******************************************************************************* + * 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.ofbiz.product.category.ftl; + +import java.io.IOException; +import java.io.Writer; +import java.util.Locale; +import java.util.Map; + +import javax.servlet.http.HttpServletRequest; + +import org.ofbiz.base.util.UtilMisc; +import org.ofbiz.base.util.UtilValidate; +import org.ofbiz.base.util.template.FreeMarkerWorker; +import org.ofbiz.entity.Delegator; +import org.ofbiz.entity.GenericEntityException; +import org.ofbiz.entity.GenericValue; +import org.ofbiz.product.category.CatalogUrlFilter; +import org.ofbiz.product.category.CategoryContentWrapper; +import org.ofbiz.product.product.ProductContentWrapper; +import org.ofbiz.service.LocalDispatcher; +import org.ofbiz.webapp.control.RequestHandler; + +import freemarker.core.Environment; +import freemarker.ext.beans.BeanModel; +import freemarker.ext.beans.NumberModel; +import freemarker.ext.beans.StringModel; +import freemarker.template.SimpleNumber; +import freemarker.template.SimpleScalar; +import freemarker.template.TemplateModelException; +import freemarker.template.TemplateTransformModel; + +public class OfbizCatalogAltUrlTransform implements TemplateTransformModel { + public final static String module = OfbizCatalogUrlTransform.class.getName(); + + 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(); + } else if (o instanceof SimpleNumber) { + return ((SimpleNumber) o).getAsNumber().toString(); + } else if (o instanceof NumberModel) { + return ((NumberModel) o).getAsNumber().toString(); + } + return null; + } + + public boolean checkArg(Map args, String key, boolean defaultValue) { + if (!args.containsKey(key)) { + return defaultValue; + } else { + Object o = args.get(key); + if (o instanceof SimpleScalar) { + SimpleScalar s = (SimpleScalar) o; + return "true".equalsIgnoreCase(s.getAsString()); + } + return defaultValue; + } + } + + @Override + public Writer getWriter(final Writer out, final Map args) + throws TemplateModelException, IOException { + final StringBuilder buf = new StringBuilder(); + final boolean fullPath = checkArg(args, "fullPath", false); + final boolean secure = checkArg(args, "secure", false); + + 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"); + String previousCategoryId = getStringArg(args, "previousCategoryId"); + String productCategoryId = getStringArg(args, "productCategoryId"); + String productId = getStringArg(args, "productId"); + String url = ""; + + Object prefix = env.getVariable("urlPrefix"); + String viewSize = getStringArg(args, "viewSize"); + String viewIndex = getStringArg(args, "viewIndex"); + String viewSort = getStringArg(args, "viewSort"); + String searchString = getStringArg(args, "searchString"); + if (req != null) { + HttpServletRequest request = (HttpServletRequest) req.getWrappedObject(); + StringBuilder newURL = new StringBuilder(); + if (UtilValidate.isNotEmpty(productId)) { + url = CatalogUrlFilter.makeProductUrl(request, previousCategoryId, productCategoryId, productId); + } else { + url = CatalogUrlFilter.makeCategoryUrl(request, previousCategoryId, productCategoryId, productId, viewSize, viewIndex, viewSort, searchString); + } + // make the link + if (fullPath){ + String serverRootUrl = RequestHandler.getDefaultServerRootUrl(request, secure); + newURL.append(serverRootUrl); + } + newURL.append(url); + out.write(newURL.toString()); + } else if (prefix != null) { + Delegator delegator = FreeMarkerWorker.getWrappedObject("delegator", env); + LocalDispatcher dispatcher = FreeMarkerWorker.getWrappedObject("dispatcher", env); + Locale locale = (Locale) args.get("locale"); + if (UtilValidate.isNotEmpty(productId)) { + GenericValue product = delegator.findOne("Product", UtilMisc.toMap("productId", productId), false); + ProductContentWrapper wrapper = new ProductContentWrapper(dispatcher, product, locale, "text/html"); + url = CatalogUrlFilter.makeProductUrl(delegator, wrapper, null, ((StringModel) prefix).getAsString(), previousCategoryId, productCategoryId, productId); + } else { + GenericValue productCategory = delegator.findOne("ProductCategory", UtilMisc.toMap("productCategoryId", productCategoryId), false); + CategoryContentWrapper wrapper = new CategoryContentWrapper(dispatcher, productCategory, locale, "text/html"); + url = CatalogUrlFilter.makeCategoryUrl(delegator, wrapper, null, ((StringModel) prefix).getAsString(), previousCategoryId, productCategoryId, productId, viewSize, viewIndex, viewSort, searchString); + } + out.write(url.toString()); + } else { + out.write(buf.toString()); + } + } catch (TemplateModelException e) { + throw new IOException(e.getMessage()); + } catch (GenericEntityException e) { + throw new IOException(e.getMessage()); + } + } + }; + } +} Propchange: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/ftl/OfbizCatalogAltUrlTransform.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/ftl/OfbizCatalogAltUrlTransform.java ------------------------------------------------------------------------------ svn:keywords = Date Rev Author URL Id Propchange: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/ftl/OfbizCatalogAltUrlTransform.java ------------------------------------------------------------------------------ svn:mime-type = text/plain Added: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/ftl/OfbizCatalogUrlTransform.java URL: http://svn.apache.org/viewvc/ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/ftl/OfbizCatalogUrlTransform.java?rev=1535171&view=auto ============================================================================== --- ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/ftl/OfbizCatalogUrlTransform.java (added) +++ ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/ftl/OfbizCatalogUrlTransform.java Wed Oct 23 20:48:36 2013 @@ -0,0 +1,83 @@ +/******************************************************************************* + * 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.ofbiz.product.category.ftl; + +import java.io.IOException; +import java.io.Writer; +import java.util.Map; + +import javax.servlet.http.HttpServletRequest; + +import org.ofbiz.product.category.CatalogUrlServlet; + +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 OfbizCatalogUrlTransform implements TemplateTransformModel { + public final static String module = OfbizCatalogUrlTransform.class.getName(); + + 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(); + String catalogUrl = CatalogUrlServlet.makeCatalogUrl(request, productId, currentCategoryId, previousCategoryId); + out.write(catalogUrl); + } + } catch (TemplateModelException e) { + throw new IOException(e.getMessage()); + } + } + }; + } +} \ No newline at end of file Propchange: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/ftl/OfbizCatalogUrlTransform.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/ftl/OfbizCatalogUrlTransform.java ------------------------------------------------------------------------------ svn:keywords = Date Rev Author URL Id Propchange: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/ftl/OfbizCatalogUrlTransform.java ------------------------------------------------------------------------------ svn:mime-type = text/plain Added: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/ftl/SeoTransform.java URL: http://svn.apache.org/viewvc/ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/ftl/SeoTransform.java?rev=1535171&view=auto ============================================================================== --- ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/ftl/SeoTransform.java (added) +++ ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/ftl/SeoTransform.java Wed Oct 23 20:48:36 2013 @@ -0,0 +1,189 @@ +/******************************************************************************* + * 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.ofbiz.product.category.ftl; + +import java.io.IOException; +import java.io.Writer; +import java.util.Iterator; +import java.util.Map; + +import javax.servlet.ServletContext; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; + +import org.apache.oro.text.regex.Pattern; +import org.apache.oro.text.regex.Perl5Matcher; +import org.ofbiz.base.util.Debug; +import org.ofbiz.entity.GenericValue; +import org.ofbiz.product.category.SeoConfigUtil; +import org.ofbiz.webapp.control.RequestHandler; + +import freemarker.core.Environment; +import freemarker.ext.beans.BeanModel; +import freemarker.template.SimpleScalar; +import freemarker.template.TemplateScalarModel; +import freemarker.template.TemplateTransformModel; + +/** + * SeoTransform - Freemarker Transform for URLs (links) + * + */ +public class SeoTransform implements TemplateTransformModel { + + private static final String module = SeoTransform.class.getName(); + + public boolean checkArg(Map args, String key, boolean defaultValue) { + if (!args.containsKey(key)) { + return defaultValue; + } else { + Object o = args.get(key); + if (o instanceof SimpleScalar) { + SimpleScalar s = (SimpleScalar) o; + return "true".equalsIgnoreCase(s.getAsString()); + } + return defaultValue; + } + } + + public Writer getWriter(final Writer out, Map args) { + final StringBuffer buf = new StringBuffer(); + final boolean fullPath = checkArg(args, "fullPath", false); + final boolean secure = checkArg(args, "secure", false); + final boolean encode = checkArg(args, "encode", true); + + return new Writer(out) { + + public void write(char cbuf[], int off, int len) { + buf.append(cbuf, off, len); + } + + public void flush() throws IOException { + out.flush(); + } + + public void close() throws IOException { + try { + Environment env = Environment.getCurrentEnvironment(); + BeanModel req = (BeanModel) env.getVariable("request"); + BeanModel res = (BeanModel) env.getVariable("response"); + Object prefix = env.getVariable("urlPrefix"); + if (req != null) { + HttpServletRequest request = (HttpServletRequest) req.getWrappedObject(); + ServletContext ctx = (ServletContext) request.getAttribute("servletContext"); + HttpServletResponse response = null; + if (res != null) { + response = (HttpServletResponse) res.getWrappedObject(); + } + HttpSession session = request.getSession(); + GenericValue userLogin = (GenericValue) session.getAttribute("userLogin"); + + // anonymous shoppers are not logged in + if (userLogin != null && "anonymous".equals(userLogin.getString("userLoginId"))) { + userLogin = null; + } + + RequestHandler rh = (RequestHandler) ctx.getAttribute("_REQUEST_HANDLER_"); + out.write(seoUrl(rh.makeLink(request, response, buf.toString(), fullPath, secure, encode), userLogin == null)); + } else if (prefix != null) { + if (prefix instanceof TemplateScalarModel) { + TemplateScalarModel s = (TemplateScalarModel) prefix; + String prefixString = s.getAsString(); + String bufString = buf.toString(); + boolean prefixSlash = prefixString.endsWith("/"); + boolean bufSlash = bufString.startsWith("/"); + if (prefixSlash && bufSlash) { + bufString = bufString.substring(1); + } else if (!prefixSlash && !bufSlash) { + bufString = "/" + bufString; + } + out.write(prefixString + bufString); + } + } else { + out.write(buf.toString()); + } + } catch (Exception e) { + throw new IOException(e.getMessage()); + } + } + }; + } + + /** + * Transform a url according to seo pattern regular expressions. + * + * @param url , String to do the seo transform + * @param isAnon , boolean to indicate whether it's an anonymous visit. + * + * @return String, the transformed url. + */ + public static String seoUrl(String url, boolean isAnon) { + Perl5Matcher matcher = new Perl5Matcher(); + if (SeoConfigUtil.checkUseUrlRegexp() && matcher.matches(url, SeoConfigUtil.getGeneralRegexpPattern())) { + Iterator<String> keys = SeoConfigUtil.getSeoPatterns().keySet().iterator(); + boolean foundMatch = false; + while (keys.hasNext()) { + String key = keys.next(); + Pattern pattern = SeoConfigUtil.getSeoPatterns().get(key); + if (pattern.getPattern().contains(";jsessionid=")) { + if (isAnon) { + if (SeoConfigUtil.isJSessionIdAnonEnabled()) { + continue; + } + } else { + if (SeoConfigUtil.isJSessionIdUserEnabled()) { + continue; + } else { + boolean foundException = false; + for (int i = 0; i < SeoConfigUtil.getUserExceptionPatterns().size(); i++) { + if (matcher.matches(url, SeoConfigUtil.getUserExceptionPatterns().get(i))) { + foundException = true; + break; + } + } + if (foundException) { + continue; + } + } + } + } + String replacement = SeoConfigUtil.getSeoReplacements().get(key); + if (matcher.matches(url, pattern)) { + for (int i = 1; i < matcher.getMatch().groups(); i++) { + replacement = replacement.replaceAll("\\$" + i, matcher.getMatch().group(i)); + } + // break if found any matcher + url = replacement; + foundMatch = true; + break; + } + } + if (!foundMatch && SeoConfigUtil.isDebugEnabled()) { + Debug.logInfo("Can NOT find a seo transform pattern for this url: " + url, module); + } + } + return url; + } + + static { + if (!SeoConfigUtil.isInitialed()) { + SeoConfigUtil.init(); + } + } +} Propchange: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/ftl/SeoTransform.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/ftl/SeoTransform.java ------------------------------------------------------------------------------ svn:keywords = Date Rev Author URL Id Propchange: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/ftl/SeoTransform.java ------------------------------------------------------------------------------ svn:mime-type = text/plain |
Free forum by Nabble | Edit this page |