svn commit: r1535171 [1/4] - in /ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23: ./ applications/product/ applications/product/config/ applications/product/dtd/ applications/product/src/org/ofbiz/product/category/ applications/product/src/org...

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
|

svn commit: r1535171 [1/4] - in /ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23: ./ applications/product/ applications/product/config/ applications/product/dtd/ applications/product/src/org/ofbiz/product/category/ applications/product/src/org...

jleroux@apache.org
Author: jleroux
Date: Wed Oct 23 20:48:36 2013
New Revision: 1535171

URL: http://svn.apache.org/r1535171
Log:
Create a branch for easier exchanges on OFBIZ-5312

Added:
    ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/   (props changed)
      - copied from r1535157, ofbiz/trunk/
    ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/config/SeoConfig.xml   (with props)
    ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/config/urlregexp.xml   (with props)
    ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/dtd/
    ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/dtd/SeoConfig.xsd   (with props)
    ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/dtd/urlregexp.xsd   (with props)
    ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/CatalogUrlSeoFilter.java   (with props)
    ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/CategoryUrlServlet.java   (with props)
    ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/ControlServlet.java   (with props)
    ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/ProductUrlServlet.java   (with props)
    ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/SeoCatalogUrlFilter.java   (with props)
    ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/SeoCatalogUrlServlet.java   (with props)
    ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/SeoConfigUtil.java   (with props)
    ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/SeoContentUrlFilter.java   (with props)
    ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/SeoContextFilter.java   (with props)
    ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/SeoControlServlet.java   (with props)
    ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/SeoUrlUtil.java   (with props)
    ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/UrlRegexpConfigUtil.java   (with props)
    ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/UrlRegexpContextFilter.java   (with props)
    ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/UrlUtil.java   (with props)
    ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/ftl/
    ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/ftl/CatalogAltUrlSeoTransform.java   (with props)
    ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/ftl/CatalogUrlSeoTransform.java   (with props)
    ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/ftl/OfbizCatalogAltUrlTransform.java   (with props)
    ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/ftl/OfbizCatalogUrlTransform.java   (with props)
    ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/ftl/SeoTransform.java   (with props)
    ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/ftl/UrlRegexpTransform.java   (with props)
Removed:
    ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/OfbizCatalogAltUrlTransform.java
    ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/OfbizCatalogUrlTransform.java
Modified:
    ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/build.xml
    ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/config/freemarkerTransforms.properties
    ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/framework/webapp/src/org/ofbiz/webapp/control/RequestHandler.java
    ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/specialpurpose/ecommerce/webapp/ecommerce/WEB-INF/web.xml

Propchange: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/
------------------------------------------------------------------------------
--- svn:ignore (added)
+++ svn:ignore Wed Oct 23 20:48:36 2013
@@ -0,0 +1,12 @@
+ofbiz.jar
+bin
+*.patch
+*.iml
+*.ipr
+*.iws
+*.time
+.settings
+.project
+changelog
+.idea
+dependencyCheck.groovy

Propchange: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/
------------------------------------------------------------------------------
--- svn:mergeinfo (added)
+++ svn:mergeinfo Wed Oct 23 20:48:36 2013
@@ -0,0 +1,6 @@
+/ofbiz/branches/2013_RemoveJavolution:1462755
+/ofbiz/branches/addbirt:831210-885099,885686-886087
+/ofbiz/branches/dojo1.4:951708-952957
+/ofbiz/branches/jackrabbit20100709:962442-1231517
+/ofbiz/branches/jquery:952958-1044489
+/ofbiz/branches/multitenant20100310:921280-927264

Modified: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/build.xml
URL: http://svn.apache.org/viewvc/ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/build.xml?rev=1535171&r1=1535157&r2=1535171&view=diff
==============================================================================
--- ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/build.xml (original)
+++ ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/build.xml Wed Oct 23 20:48:36 2013
@@ -44,6 +44,7 @@ under the License.
         <fileset dir="../../framework/webapp/lib" includes="*.jar"/>
         <fileset dir="../../framework/webapp/build/lib" includes="*.jar"/>
         <fileset dir="../../framework/common/build/lib" includes="*.jar"/>
+        <fileset dir="../../framework/base/lib/scripting" includes="*.jar"/>
         <fileset dir="../content/lib" includes="*.jar"/>
         <fileset dir="../content/build/lib" includes="*.jar"/>
         <fileset dir="../party/build/lib" includes="*.jar"/>

Added: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/config/SeoConfig.xml
URL: http://svn.apache.org/viewvc/ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/config/SeoConfig.xml?rev=1535171&view=auto
==============================================================================
--- ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/config/SeoConfig.xml (added)
+++ ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/config/SeoConfig.xml Wed Oct 23 20:48:36 2013
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<seo-config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../dtd/SeoConfig.xsd">
+    <regexpifmatch>^.*/.*$</regexpifmatch>
+    
+    <debug>false</debug>
+    
+    <config>
+        <description>sample: remove jsessionid</description>
+        <seo>
+            <url-pattern>^(.*);jsessionid=(.*)jvm[1-9](.*)$</url-pattern>
+            <replacement>$1$3</replacement>
+        </seo>
+        <forward>
+            <url-pattern>^(.*);jsessionid=(.*)jvm[1-9](.*)$</url-pattern>
+            <replacement>$1$3</replacement>
+            <responsecode>301</responsecode>
+        </forward>
+    </config>
+    
+    <config>
+        <description>sample: remove /ecommerce/main</description>
+        <seo>
+            <url-pattern>^/ecommerce/main$</url-pattern>
+            <replacement>/ecommerce/</replacement>
+        </seo>
+        <forward>
+            <url-pattern>^/ecommerce/main$</url-pattern>
+            <replacement>/ecommerce/</replacement>
+            <responsecode>301</responsecode>
+        </forward>
+    </config>
+
+    <config>
+        <description>sample: remove /main</description>
+        <seo>
+            <url-pattern>^/main$</url-pattern>
+            <replacement>/</replacement>
+        </seo>
+        <forward>
+            <url-pattern>^/main$</url-pattern>
+            <replacement>/</replacement>
+            <responsecode>301</responsecode>
+        </forward>
+    </config>
+
+</seo-config>

Propchange: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/config/SeoConfig.xml
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/config/SeoConfig.xml
------------------------------------------------------------------------------
    svn:keywords = Date Rev Author URL Id

Propchange: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/config/SeoConfig.xml
------------------------------------------------------------------------------
    svn:mime-type = text/xml

Modified: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/config/freemarkerTransforms.properties
URL: http://svn.apache.org/viewvc/ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/config/freemarkerTransforms.properties?rev=1535171&r1=1535157&r2=1535171&view=diff
==============================================================================
--- ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/config/freemarkerTransforms.properties (original)
+++ ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/config/freemarkerTransforms.properties Wed Oct 23 20:48:36 2013
@@ -21,6 +21,8 @@
 
 # entries are in the form: key=transform name, property=transform class name
 
-ofbizCatalogAltUrl=org.ofbiz.product.category.OfbizCatalogAltUrlTransform
-ofbizCatalogUrl=org.ofbiz.product.category.CatalogUrlDirective
+#ofbizCatalogAltUrl=org.ofbiz.product.category.ftl.OfbizCatalogAltUrlTransform
+#ofbizCatalogUrl=org.ofbiz.product.category.ftl.CatalogUrlDirective
+ofbizCatalogAltUrl=org.ofbiz.product.category.ftl.CatalogAltUrlSeoTransform
+ofbizCatalogUrl=org.ofbiz.product.category.ftl.CatalogUrlSeoTransform
 

Added: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/config/urlregexp.xml
URL: http://svn.apache.org/viewvc/ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/config/urlregexp.xml?rev=1535171&view=auto
==============================================================================
--- ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/config/urlregexp.xml (added)
+++ ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/config/urlregexp.xml Wed Oct 23 20:48:36 2013
@@ -0,0 +1,123 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<urlregexp xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../dtd/urlregexp.xsd">
+    <regexpifmatch>^.*/.*$</regexpifmatch>
+    
+    <debug>false</debug>
+    
+    <category-url>
+        <value>enable</value><!-- enable/disable
+                                  1. if enable, the product seo url will be /product-name-product-id.html or /category-name-product-name-product-id.html
+                                  2. if disable, the CatalogUrlServlet.makeCatalogUrl will be used -->
+        <allowed-context-paths>/:/ecommerce</allowed-context-paths><!-- if category-url is enabled,
+                                                                      only context paths listed here will be allowed to use the new seo transformers -->
+        <category-name>disable</category-name><!-- enable/disable
+                                                  1. if enable, the product seo url will be /category-name-product-name-product-id.html
+                                                  2. if disable, the product seo url will be /product-name-product-id.html -->
+        <category-url-suffix>.html</category-url-suffix><!-- suffix to add to the product seo url -->
+    </category-url>
+
+    <jsessionid>
+        <anonymous>
+            <value>disable</value><!-- enable/disable
+                                      1. when disable, the seo url patterns that contains jsessionid will be applied;
+                                      2. when enable, the seo url patterns that contains jsessionid will be skipped -->
+        </anonymous>
+        <user>
+            <value>disable</value><!-- enable/disable
+                                      1. when disable, the url-patterns under exceptions will be used to skip the seo url patterns that contains jsessionid;
+                                      2. when enable, the seo url patterns that contains jsessionid will be skipped -->
+            <exceptions>
+                <url-pattern>^.*/(keywordsearch|logout).*$</url-pattern><!-- sample: ^.*/(keywordsearch|logout).*$ -->
+            </exceptions>
+        </user>
+    </jsessionid>
+
+    <name-filters>
+        <filter>
+            <character-pattern>\u00fc</character-pattern>
+            <replacement>ue</replacement>
+        </filter>
+        <filter>
+            <character-pattern>\u00e4</character-pattern>
+            <replacement>ae</replacement>
+        </filter>
+        <filter>
+            <character-pattern>\u00f6</character-pattern>
+            <replacement>oe</replacement>
+        </filter>
+        <filter>
+            <character-pattern>\u00df</character-pattern>
+            <replacement>ss</replacement>
+        </filter>
+        <filter>
+            <character-pattern>\\+</character-pattern>
+            <replacement>und</replacement>
+        </filter>
+        <filter>
+            <character-pattern>\u0026</character-pattern>
+            <replacement>und</replacement>
+        </filter>
+        <filter>
+            <character-pattern>è</character-pattern>
+            <replacement>e</replacement>
+        </filter>
+        <!-- please keep the following 2 filters, don't remove them -->
+        <filter>
+            <character-pattern>[^A-Za-z0-9+-]</character-pattern>
+            <replacement>-</replacement>
+        </filter>
+        <filter>
+            <character-pattern>-{2,}</character-pattern>
+            <replacement>-</replacement>
+        </filter>
+    </name-filters>
+<!--
+    <config>
+        <description>sample: remove /control/main</description>
+        <seo>
+            <url-pattern>^/control/main$</url-pattern>
+            <replacement>/</replacement>
+        </seo>
+        <forward>
+            <url-pattern>^/control/main$</url-pattern>
+            <replacement>/</replacement>
+            <responsecode>301</responsecode>
+        </forward>
+    </config>
+ -->
+
+    <config>
+        <description>sample: remove /main</description>
+        <seo>
+            <url-pattern>^/main$</url-pattern>
+            <replacement>/</replacement>
+        </seo>
+        <forward>
+            <url-pattern>^/main$</url-pattern>
+            <replacement>/</replacement>
+            <responsecode>301</responsecode>
+        </forward>
+    </config>
+
+    <!-- <config>
+        <description>sample: remove all controller urls</description>
+        <seo>
+            <url-pattern>^/control/(.*)$</url-pattern>
+            <replacement>/$1</replacement>
+        </seo>
+        <forward>
+            <url-pattern>^/control/(.*)$</url-pattern>
+            <replacement>/$1</replacement>
+            <responsecode>301</responsecode>
+        </forward>
+    </config> -->
+
+    <config>
+        <description>sample: remove jsessionid</description>
+        <seo>
+            <url-pattern>^(.*);jsessionid=(.*)jvm[1-9](.*)$</url-pattern>
+            <replacement>$1$3</replacement>
+        </seo>
+    </config>
+    
+</urlregexp>

Propchange: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/config/urlregexp.xml
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/config/urlregexp.xml
------------------------------------------------------------------------------
    svn:keywords = Date Rev Author URL Id

Propchange: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/config/urlregexp.xml
------------------------------------------------------------------------------
    svn:mime-type = text/xml

Added: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/dtd/SeoConfig.xsd
URL: http://svn.apache.org/viewvc/ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/dtd/SeoConfig.xsd?rev=1535171&view=auto
==============================================================================
--- ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/dtd/SeoConfig.xsd (added)
+++ ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/dtd/SeoConfig.xsd Wed Oct 23 20:48:36 2013
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
+    <xs:element name="seo-config">
+        <xs:complexType>
+            <xs:sequence>
+                <xs:element minOccurs="0" maxOccurs="1" ref="regexpifmatch"/>
+                <xs:element minOccurs="0" maxOccurs="1" ref="debug"/>
+                <xs:element minOccurs="0" maxOccurs="unbounded" ref="config"/>
+            </xs:sequence>
+        </xs:complexType>
+    </xs:element>
+    <xs:element name="regexpifmatch"/>
+    <xs:element name="debug"/>
+    <xs:element name="config">
+        <xs:complexType>
+            <xs:sequence>
+                <xs:element minOccurs="0" maxOccurs="1" ref="description"/>
+                <xs:element minOccurs="1" maxOccurs="1" ref="seo"/>
+                <xs:element minOccurs="0" maxOccurs="1" ref="forward"/>
+            </xs:sequence>
+        </xs:complexType>
+    </xs:element>
+    <xs:element name="description"/>
+    <xs:element name="seo">
+        <xs:complexType>
+            <xs:sequence>
+                <xs:element minOccurs="1" maxOccurs="1" ref="url-pattern"/>
+                <xs:element minOccurs="1" maxOccurs="1" ref="replacement"/>
+            </xs:sequence>
+        </xs:complexType>
+    </xs:element>
+    <xs:element name="forward">
+        <xs:complexType>
+            <xs:sequence>
+                <xs:element minOccurs="1" maxOccurs="1" ref="url-pattern"/>
+                <xs:element minOccurs="1" maxOccurs="1" ref="replacement"/>
+                <xs:element minOccurs="0" maxOccurs="1" ref="responsecode"/>
+            </xs:sequence>
+        </xs:complexType>
+    </xs:element>
+    <xs:element name="url-pattern"/>
+    <xs:element name="replacement"/>
+    <xs:element name="responsecode" default="301"/>
+</xs:schema>

Propchange: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/dtd/SeoConfig.xsd
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/dtd/SeoConfig.xsd
------------------------------------------------------------------------------
    svn:keywords = Date Rev Author URL Id

Propchange: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/dtd/SeoConfig.xsd
------------------------------------------------------------------------------
    svn:mime-type = text/xsd

Added: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/dtd/urlregexp.xsd
URL: http://svn.apache.org/viewvc/ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/dtd/urlregexp.xsd?rev=1535171&view=auto
==============================================================================
--- ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/dtd/urlregexp.xsd (added)
+++ ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/dtd/urlregexp.xsd Wed Oct 23 20:48:36 2013
@@ -0,0 +1,108 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
+    <xs:element name="urlregexp">
+        <xs:complexType>
+            <xs:sequence>
+                <xs:element minOccurs="0" maxOccurs="1" ref="regexpifmatch"/>
+                <xs:element minOccurs="0" maxOccurs="1" ref="debug"/>
+                <xs:element minOccurs="0" maxOccurs="1" ref="category-url"/>
+                <xs:element minOccurs="0" maxOccurs="1" ref="jsessionid"/>
+                <xs:element minOccurs="0" maxOccurs="1" ref="name-filters"/>
+                <xs:element minOccurs="0" maxOccurs="unbounded" ref="config"/>
+            </xs:sequence>
+        </xs:complexType>
+    </xs:element>
+    <xs:element name="regexpifmatch"/>
+    <xs:element name="debug"/>
+    <xs:element name="category-url">
+        <xs:complexType>
+            <xs:sequence>
+                <xs:element minOccurs="1" maxOccurs="1" ref="value"/>
+                <xs:element minOccurs="0" maxOccurs="1" ref="allowed-context-paths"/>
+                <xs:element minOccurs="0" maxOccurs="1" ref="category-name"/>
+                <xs:element minOccurs="0" maxOccurs="1" ref="category-url-suffix"/>
+            </xs:sequence>
+        </xs:complexType>
+    </xs:element>
+    <xs:element name="value" default="disable"/>
+    <xs:element name="allowed-context-paths"/>
+    <xs:element name="category-name" default="disable"/>
+    <xs:element name="category-url-suffix" default=".html"/>
+    <xs:element name="jsessionid">
+        <xs:complexType>
+            <xs:sequence>
+                <xs:element minOccurs="0" maxOccurs="1" ref="anonymous"/>
+                <xs:element minOccurs="0" maxOccurs="1" ref="user"/>
+            </xs:sequence>
+        </xs:complexType>
+    </xs:element>
+    <xs:element name="anonymous">
+        <xs:complexType>
+            <xs:sequence>
+                <xs:element minOccurs="0" maxOccurs="1" ref="value"/>
+            </xs:sequence>
+        </xs:complexType>
+    </xs:element>
+    <xs:element name="user">
+        <xs:complexType>
+            <xs:sequence>
+                <xs:element minOccurs="0" maxOccurs="1" ref="value"/>
+                <xs:element minOccurs="0" maxOccurs="1" ref="exceptions"/>
+            </xs:sequence>
+        </xs:complexType>
+    </xs:element>
+    <xs:element name="name-filters">
+        <xs:complexType>
+            <xs:sequence>
+                <xs:element minOccurs="0" maxOccurs="unbounded" ref="filter"/>
+            </xs:sequence>
+        </xs:complexType>
+    </xs:element>
+    <xs:element name="filter">
+        <xs:complexType>
+            <xs:sequence>
+                <xs:element minOccurs="1" maxOccurs="1" ref="character-pattern"/>
+                <xs:element minOccurs="1" maxOccurs="1" ref="replacement"/>
+            </xs:sequence>
+        </xs:complexType>
+    </xs:element>
+    <xs:element name="character-pattern"/>
+    <xs:element name="exceptions">
+        <xs:complexType>
+            <xs:sequence>
+                <xs:element minOccurs="0" maxOccurs="unbounded" ref="url-pattern"/>
+            </xs:sequence>
+        </xs:complexType>
+    </xs:element>
+    <xs:element name="config">
+        <xs:complexType>
+            <xs:sequence>
+                <xs:element minOccurs="0" maxOccurs="1" ref="description"/>
+                <xs:element minOccurs="1" maxOccurs="1" ref="seo"/>
+                <xs:element minOccurs="0" maxOccurs="1" ref="forward"/>
+            </xs:sequence>
+        </xs:complexType>
+    </xs:element>
+    <xs:element name="description"/>
+    <xs:element name="seo">
+        <xs:complexType>
+            <xs:sequence>
+                <xs:element minOccurs="1" maxOccurs="1" ref="url-pattern"/>
+                <xs:element minOccurs="1" maxOccurs="1" ref="replacement"/>
+            </xs:sequence>
+        </xs:complexType>
+    </xs:element>
+    <xs:element name="forward">
+        <xs:complexType>
+            <xs:sequence>
+                <xs:element minOccurs="1" maxOccurs="1" ref="url-pattern"/>
+                <xs:element minOccurs="1" maxOccurs="1" ref="replacement"/>
+                <xs:element minOccurs="0" maxOccurs="1" ref="responsecode"/>
+            </xs:sequence>
+        </xs:complexType>
+    </xs:element>
+    <xs:element name="url-pattern"/>
+    <xs:element name="replacement"/>
+    <xs:element name="responsecode" default="301"/>
+</xs:schema>

Propchange: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/dtd/urlregexp.xsd
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/dtd/urlregexp.xsd
------------------------------------------------------------------------------
    svn:keywords = Date Rev Author URL Id

Propchange: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/dtd/urlregexp.xsd
------------------------------------------------------------------------------
    svn:mime-type = text/xsd

Added: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/CatalogUrlSeoFilter.java
URL: http://svn.apache.org/viewvc/ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/CatalogUrlSeoFilter.java?rev=1535171&view=auto
==============================================================================
--- ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/CatalogUrlSeoFilter.java (added)
+++ ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/CatalogUrlSeoFilter.java Wed Oct 23 20:48:36 2013
@@ -0,0 +1,71 @@
+/*******************************************************************************
+ * 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 java.io.IOException;
+
+import javax.servlet.FilterChain;
+import javax.servlet.ServletContext;
+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.UtilValidate;
+import org.ofbiz.common.UrlServletHelper;
+import org.ofbiz.entity.Delegator;
+import org.ofbiz.product.category.ftl.CatalogUrlSeoTransform;
+
+public class CatalogUrlSeoFilter extends CatalogUrlFilter {
+
+    public final static String module = CatalogUrlSeoFilter.class.getName();
+
+    protected static String defaultLocaleString = null;
+    protected static String redirectUrl = null;
+
+    /**
+     * @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;
+        Delegator delegator = (Delegator) httpRequest.getSession().getServletContext().getAttribute("delegator");
+
+        // Get ServletContext
+        ServletContext servletContext = config.getServletContext();
+
+        // Set request attribute and session
+        UrlServletHelper.setRequestAttributes(request, delegator, servletContext);
+
+        // set initial parameters
+        String initDefaultLocalesString = config.getInitParameter("defaultLocaleString");
+        String initRedirectUrl = config.getInitParameter("redirectUrl");
+        defaultLocaleString = UtilValidate.isNotEmpty(initDefaultLocalesString) ? initDefaultLocalesString : "";
+        redirectUrl = UtilValidate.isNotEmpty(initRedirectUrl) ? initRedirectUrl : "";
+
+        // set the ServletContext in the request for future use
+        httpRequest.setAttribute("servletContext", config.getServletContext());
+        if (CatalogUrlSeoTransform.forwardUri(httpRequest, httpResponse, delegator, ControlServlet.controlServlet)) {
+            return;
+        }
+        super.doFilter(httpRequest, httpResponse, chain);
+    }
+
+}

Propchange: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/CatalogUrlSeoFilter.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/CatalogUrlSeoFilter.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/CatalogUrlSeoFilter.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/CategoryUrlServlet.java
URL: http://svn.apache.org/viewvc/ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/CategoryUrlServlet.java?rev=1535171&view=auto
==============================================================================
--- ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/CategoryUrlServlet.java (added)
+++ ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/CategoryUrlServlet.java Wed Oct 23 20:48:36 2013
@@ -0,0 +1,78 @@
+/*******************************************************************************
+ * 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 java.io.IOException;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.ofbiz.entity.Delegator;
+import org.ofbiz.product.category.ftl.CatalogUrlSeoTransform;
+
+/**
+ * ControlServlet.java - Master servlet for the web application.
+ */
+@SuppressWarnings("serial")
+public class CategoryUrlServlet extends HttpServlet {
+
+    public static final String module = CategoryUrlServlet.class.getName();
+    public static final String CATEGORY_URL_MOUNT_POINT = "category";
+
+    public CategoryUrlServlet() {
+        super();
+    }
+
+    /**
+     * @see javax.servlet.Servlet#init(javax.servlet.ServletConfig)
+     */
+    @Override
+    public void init(ServletConfig config) throws ServletException {
+        super.init(config);
+    }
+
+    /**
+     * @see javax.servlet.http.HttpServlet#doPost(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
+     */
+    @Override
+    public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
+        doGet(request, response);
+    }
+
+    /**
+     * @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
+     */
+    @Override
+    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
+        Delegator delegator = (Delegator) getServletContext().getAttribute("delegator");
+        CatalogUrlSeoTransform.forwardCategoryUri(request, response, delegator, null);
+    }
+
+    /**
+     * @see javax.servlet.Servlet#destroy()
+     */
+    @Override
+    public void destroy() {
+        super.destroy();
+    }
+
+}

Propchange: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/CategoryUrlServlet.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/CategoryUrlServlet.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/CategoryUrlServlet.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/ControlServlet.java
URL: http://svn.apache.org/viewvc/ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/ControlServlet.java?rev=1535171&view=auto
==============================================================================
--- ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/ControlServlet.java (added)
+++ ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/ControlServlet.java Wed Oct 23 20:48:36 2013
@@ -0,0 +1,68 @@
+/*******************************************************************************
+ * 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 javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+
+import org.ofbiz.base.util.UtilValidate;
+
+/**
+ * ControlServlet.java - Master servlet for the web application.
+ */
+@SuppressWarnings("serial")
+public class ControlServlet extends org.ofbiz.webapp.control.ControlServlet {
+
+    public static final String module = ControlServlet.class.getName();
+
+    protected static String defaultPage = null;
+    protected static String pageNotFound = null;
+    protected static String controlServlet = null;
+
+    public ControlServlet() {
+        super();
+    }
+
+    /**
+     * @see javax.servlet.Servlet#init(javax.servlet.ServletConfig)
+     */
+    public void init(ServletConfig config) throws ServletException {
+        super.init(config);
+
+        ServletContext context = this.getServletContext();
+        if (UtilValidate.isEmpty(defaultPage)) {
+            defaultPage = context.getInitParameter("defaultPage");
+        }
+        if (UtilValidate.isEmpty(defaultPage)) {
+            defaultPage = "/main";
+        }
+        if (UtilValidate.isEmpty(pageNotFound)) {
+            pageNotFound = context.getInitParameter("pageNotFound");
+        }
+        if (UtilValidate.isEmpty(pageNotFound)) {
+            pageNotFound = "/pagenotfound";
+        }
+
+        if (defaultPage.startsWith("/") && defaultPage.lastIndexOf("/") > 0) {
+            controlServlet = defaultPage.substring(1);
+            controlServlet = controlServlet.substring(0, controlServlet.indexOf("/"));
+        }
+    }
+}

Propchange: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/ControlServlet.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/ControlServlet.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/ControlServlet.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/ProductUrlServlet.java
URL: http://svn.apache.org/viewvc/ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/ProductUrlServlet.java?rev=1535171&view=auto
==============================================================================
--- ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/ProductUrlServlet.java (added)
+++ ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/ProductUrlServlet.java Wed Oct 23 20:48:36 2013
@@ -0,0 +1,78 @@
+/*******************************************************************************
+ * 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 java.io.IOException;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.ofbiz.entity.Delegator;
+import org.ofbiz.product.category.ftl.CatalogUrlSeoTransform;
+
+/**
+ * ControlServlet.java - Master servlet for the web application.
+ */
+@SuppressWarnings("serial")
+public class ProductUrlServlet extends HttpServlet {
+
+    public static final String module = ProductUrlServlet.class.getName();    
+    public static final String PRODUCT_URL_MOUNT_POINT = "product";
+
+    public ProductUrlServlet() {
+        super();
+    }
+
+    /**
+     * @see javax.servlet.Servlet#init(javax.servlet.ServletConfig)
+     */
+    @Override
+    public void init(ServletConfig config) throws ServletException {
+        super.init(config);
+    }
+
+    /**
+     * @see javax.servlet.http.HttpServlet#doPost(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
+     */
+    @Override
+    public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
+        doGet(request, response);
+    }
+
+    /**
+     * @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
+     */
+    @Override
+    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
+        Delegator delegator = (Delegator) getServletContext().getAttribute("delegator");
+        CatalogUrlSeoTransform.forwardProductUri(request, response, delegator);
+    }
+
+    /**
+     * @see javax.servlet.Servlet#destroy()
+     */
+    @Override
+    public void destroy() {
+        super.destroy();
+    }
+
+}

Propchange: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/ProductUrlServlet.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/ProductUrlServlet.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/ProductUrlServlet.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/SeoCatalogUrlFilter.java
URL: http://svn.apache.org/viewvc/ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/SeoCatalogUrlFilter.java?rev=1535171&view=auto
==============================================================================
--- ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/SeoCatalogUrlFilter.java (added)
+++ ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/SeoCatalogUrlFilter.java Wed Oct 23 20:48:36 2013
@@ -0,0 +1,474 @@
+/*******************************************************************************
+ * 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 java.io.IOException;
+import java.util.Collections;
+import java.util.List;
+
+import javax.servlet.FilterChain;
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import javolution.util.FastList;
+
+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.util.EntityUtil;
+import org.ofbiz.product.product.ProductContentWrapper;
+import org.ofbiz.webapp.control.ContextFilter;
+
+public class SeoCatalogUrlFilter extends ContextFilter {
+
+    public final static String module = SeoCatalogUrlFilter.class.getName();
+    public static final String PRODUCT_REQUEST = "product";
+    public static final String CATEGORY_REQUEST = "category";
+
+    protected static String defaultLocaleString = null;
+    protected static String redirectUrl = null;
+
+    @Override
+    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
+        HttpServletRequest httpRequest = (HttpServletRequest) request;
+        HttpServletResponse httpResponse = (HttpServletResponse) response;
+        Delegator delegator = (Delegator) httpRequest.getSession().getServletContext().getAttribute("delegator");
+
+        // Get ServletContext
+        ServletContext servletContext = config.getServletContext();
+
+        // Set request attribute and session
+        UrlServletHelper.setRequestAttributes(request, delegator, servletContext);
+
+        // set initial parameters
+        String initDefaultLocalesString = config.getInitParameter("defaultLocaleString");
+        String initRedirectUrl = config.getInitParameter("redirectUrl");
+        defaultLocaleString = UtilValidate.isNotEmpty(initDefaultLocalesString) ? initDefaultLocalesString : "";
+        redirectUrl = UtilValidate.isNotEmpty(initRedirectUrl) ? initRedirectUrl : "";
+
+        String pathInfo = httpRequest.getServletPath();
+        String alternativeUrl = "";
+        if (UtilValidate.isEmpty(pathInfo)) {
+            pathInfo = SeoUrlUtil.removeContextPath(httpRequest.getRequestURI(), httpRequest.getContextPath());
+        }
+        if (UtilValidate.isNotEmpty(pathInfo)) {
+            List<String> pathElements = StringUtil.split(pathInfo, "/");
+            if (UtilValidate.isNotEmpty(pathElements)) {
+                alternativeUrl = pathElements.get(0);
+            }
+        }
+        if (UtilValidate.isNotEmpty(alternativeUrl)) {
+            String productId = null;
+            String productCategoryId = null;
+            String urlContentId = null;
+            try {
+                // look for productId
+                if (alternativeUrl.endsWith("-p")) {
+                    List<EntityCondition> productContentConds = FastList.newInstance();
+                    productContentConds.add(EntityCondition.makeCondition("productContentTypeId", "ALTERNATIVE_URL"));
+                    productContentConds.add(EntityUtil.getFilterByDateExpr());
+                    List<GenericValue> productContentInfos = delegator.findList("ProductContentAndInfo", EntityCondition.makeCondition(productContentConds), null,
+                            UtilMisc.toList("-fromDate"), null, true);
+                    if (UtilValidate.isNotEmpty(productContentInfos)) {
+                        for (GenericValue productContentInfo : productContentInfos) {
+                            String contentId = (String) productContentInfo.get("contentId");
+                            List<GenericValue> ContentAssocDataResourceViewTos = delegator.findByAnd("ContentAssocDataResourceViewTo",
+                                    UtilMisc.toMap("contentIdStart", contentId, "caContentAssocTypeId", "ALTERNATE_LOCALE", "drDataResourceTypeId", "ELECTRONIC_TEXT"), null, true);
+                            if (UtilValidate.isNotEmpty(ContentAssocDataResourceViewTos)) {
+                                for (GenericValue ContentAssocDataResourceViewTo : ContentAssocDataResourceViewTos) {
+                                    GenericValue ElectronicText = ContentAssocDataResourceViewTo.getRelatedOne("ElectronicText", true);
+                                    if (UtilValidate.isNotEmpty(ElectronicText)) {
+                                        String textData = (String) ElectronicText.get("textData");
+                                        textData = UrlServletHelper.invalidCharacter(textData);
+                                        if (alternativeUrl.matches(textData + ".+$")) {
+                                            String productIdStr = null;
+                                            productIdStr = alternativeUrl.replace(textData + "-", "");
+                                            productIdStr = productIdStr.replace("-p", "");
+                                            String checkProductId = (String) productContentInfo.get("productId");
+                                            if (productIdStr.equalsIgnoreCase(checkProductId)) {
+                                                productId = checkProductId;
+                                                break;
+                                            }
+                                        }
+                                    }
+                                }
+                            }
+                            if (UtilValidate.isEmpty(productId)) {
+                                List<GenericValue> contentDataResourceViews = delegator.findByAnd("ContentDataResourceView",
+                                        UtilMisc.toMap("contentId", contentId, "drDataResourceTypeId", "ELECTRONIC_TEXT"), null, true);
+                                for (GenericValue contentDataResourceView : contentDataResourceViews) {
+                                    GenericValue ElectronicText = contentDataResourceView.getRelatedOne("ElectronicText", true);
+                                    if (UtilValidate.isNotEmpty(ElectronicText)) {
+                                        String textData = (String) ElectronicText.get("textData");
+                                        if (UtilValidate.isNotEmpty(textData)) {
+                                            textData = UrlServletHelper.invalidCharacter(textData);
+                                            if (alternativeUrl.matches(textData + ".+$")) {
+                                                String productIdStr = null;
+                                                productIdStr = alternativeUrl.replace(textData + "-", "");
+                                                productIdStr = productIdStr.replace("-p", "");
+                                                String checkProductId = (String) productContentInfo.get("productId");
+                                                if (productIdStr.equalsIgnoreCase(checkProductId)) {
+                                                    productId = checkProductId;
+                                                    break;
+                                                }
+                                            }
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+
+                // look for productCategoryId
+                if (alternativeUrl.endsWith("-c")) {
+                    List<EntityCondition> productCategoryContentConds = FastList.newInstance();
+                    productCategoryContentConds.add(EntityCondition.makeCondition("prodCatContentTypeId", "ALTERNATIVE_URL"));
+                    productCategoryContentConds.add(EntityUtil.getFilterByDateExpr());
+                    List<GenericValue> productCategoryContentInfos = delegator.findList("ProductCategoryContentAndInfo",
+                            EntityCondition.makeCondition(productCategoryContentConds), null, UtilMisc.toList("-fromDate"), null, true);
+                    if (UtilValidate.isNotEmpty(productCategoryContentInfos)) {
+                        for (GenericValue productCategoryContentInfo : productCategoryContentInfos) {
+                            String contentId = (String) productCategoryContentInfo.get("contentId");
+                            List<GenericValue> ContentAssocDataResourceViewTos = delegator.findByAnd("ContentAssocDataResourceViewTo",
+                                    UtilMisc.toMap("contentIdStart", contentId, "caContentAssocTypeId", "ALTERNATE_LOCALE", "drDataResourceTypeId", "ELECTRONIC_TEXT"), null, true);
+                            if (UtilValidate.isNotEmpty(ContentAssocDataResourceViewTos)) {
+                                for (GenericValue ContentAssocDataResourceViewTo : ContentAssocDataResourceViewTos) {
+                                    GenericValue ElectronicText = ContentAssocDataResourceViewTo.getRelatedOne("ElectronicText", true);
+                                    if (UtilValidate.isNotEmpty(ElectronicText)) {
+                                        String textData = (String) ElectronicText.get("textData");
+                                        if (UtilValidate.isNotEmpty(textData)) {
+                                            textData = UrlServletHelper.invalidCharacter(textData);
+                                            if (alternativeUrl.matches(textData + ".+$")) {
+                                                String productCategoryStr = null;
+                                                productCategoryStr = alternativeUrl.replace(textData + "-", "");
+                                                productCategoryStr = productCategoryStr.replace("-c", "");
+                                                String checkProductCategoryId = (String) productCategoryContentInfo.get("productCategoryId");
+                                                if (productCategoryStr.equalsIgnoreCase(checkProductCategoryId)) {
+                                                    productCategoryId = checkProductCategoryId;
+                                                    break;
+                                                }
+                                            }
+                                        }
+                                    }
+                                }
+                            }
+                            if (UtilValidate.isEmpty(productCategoryId)) {
+                                List<GenericValue> contentDataResourceViews = delegator.findByAnd("ContentDataResourceView",
+                                        UtilMisc.toMap("contentId", contentId, "drDataResourceTypeId", "ELECTRONIC_TEXT"), null, true);
+                                for (GenericValue contentDataResourceView : contentDataResourceViews) {
+                                    GenericValue ElectronicText = contentDataResourceView.getRelatedOne("ElectronicText", true);
+                                    if (UtilValidate.isNotEmpty(ElectronicText)) {
+                                        String textData = (String) ElectronicText.get("textData");
+                                        if (UtilValidate.isNotEmpty(textData)) {
+                                            textData = UrlServletHelper.invalidCharacter(textData);
+                                            if (alternativeUrl.matches(textData + ".+$")) {
+                                                String productCategoryStr = null;
+                                                productCategoryStr = alternativeUrl.replace(textData + "-", "");
+                                                productCategoryStr = productCategoryStr.replace("-c", "");
+                                                String checkProductCategoryId = (String) productCategoryContentInfo.get("productCategoryId");
+                                                if (productCategoryStr.equalsIgnoreCase(checkProductCategoryId)) {
+                                                    productCategoryId = checkProductCategoryId;
+                                                    break;
+                                                }
+                                            }
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+
+            } catch (GenericEntityException e) {
+                Debug.logWarning("Cannot look for product and product category", module);
+            }
+
+            // generate forward URL
+            StringBuilder urlBuilder = new StringBuilder();
+            if (UtilValidate.isNotEmpty(SeoControlServlet.controlServlet)) {
+                urlBuilder.append("/" + SeoControlServlet.controlServlet);
+            }
+
+            if (UtilValidate.isNotEmpty(productId)) {
+                try {
+                    List<EntityCondition> conds = FastList.newInstance();
+                    conds.add(EntityCondition.makeCondition("productId", productId));
+                    conds.add(EntityUtil.getFilterByDateExpr());
+                    List<GenericValue> productCategoryMembers = delegator.findList("ProductCategoryMember", EntityCondition.makeCondition(conds),
+                            UtilMisc.toSet("productCategoryId"), UtilMisc.toList("-fromDate"), null, true);
+                    if (UtilValidate.isNotEmpty(productCategoryMembers)) {
+                        GenericValue productCategoryMember = EntityUtil.getFirst(productCategoryMembers);
+                        productCategoryId = productCategoryMember.getString("productCategoryId");
+                    }
+                } catch (GenericEntityException e) {
+                    Debug.logError(e, "Cannot find product category for product: " + productId, module);
+                }
+                urlBuilder.append("/" + PRODUCT_REQUEST);
+
+            } else {
+                urlBuilder.append("/" + CATEGORY_REQUEST);
+            }
+
+            // generate trail belong to a top category
+            String topCategoryId = CategoryWorker.getCatalogTopCategory(httpRequest, null);
+            List<GenericValue> trailCategories = CategoryWorker.getRelatedCategoriesRet(httpRequest, "trailCategories", topCategoryId, false, false, true);
+            List<String> trailCategoryIds = EntityUtil.getFieldListFromEntityList(trailCategories, "productCategoryId", true);
+
+            // look for productCategoryId from productId
+            if (UtilValidate.isNotEmpty(productId)) {
+                try {
+                    List<EntityCondition> rolllupConds = FastList.newInstance();
+                    rolllupConds.add(EntityCondition.makeCondition("productId", productId));
+                    rolllupConds.add(EntityUtil.getFilterByDateExpr());
+                    List<GenericValue> productCategoryMembers = delegator.findList("ProductCategoryMember", EntityCondition.makeCondition(rolllupConds), null,
+                            UtilMisc.toList("-fromDate"), null, true);
+                    for (GenericValue productCategoryMember : productCategoryMembers) {
+                        String trailCategoryId = productCategoryMember.getString("productCategoryId");
+                        if (trailCategoryIds.contains(trailCategoryId)) {
+                            productCategoryId = trailCategoryId;
+                            break;
+                        }
+                    }
+                } catch (GenericEntityException e) {
+                    Debug.logError(e, "Cannot generate trail from product category", module);
+                }
+            }
+
+            // generate trail elements from productCategoryId
+            if (UtilValidate.isNotEmpty(productCategoryId)) {
+                List<String> trailElements = FastList.newInstance();
+                trailElements.add(productCategoryId);
+                String parentProductCategoryId = productCategoryId;
+                while (UtilValidate.isNotEmpty(parentProductCategoryId)) {
+                    // find product category rollup
+                    try {
+                        List<EntityCondition> rolllupConds = FastList.newInstance();
+                        rolllupConds.add(EntityCondition.makeCondition("productCategoryId", parentProductCategoryId));
+                        rolllupConds.add(EntityUtil.getFilterByDateExpr());
+                        List<GenericValue> productCategoryRollups = delegator.findList("ProductCategoryRollup", EntityCondition.makeCondition(rolllupConds), null,
+                                UtilMisc.toList("-fromDate"), null, true);
+                        if (UtilValidate.isNotEmpty(productCategoryRollups)) {
+                            // add only categories that belong to the top category to trail
+                            for (GenericValue productCategoryRollup : productCategoryRollups) {
+                                String trailCategoryId = productCategoryRollup.getString("parentProductCategoryId");
+                                parentProductCategoryId = trailCategoryId;
+                                if (trailCategoryIds.contains(trailCategoryId)) {
+                                    trailElements.add(trailCategoryId);
+                                    break;
+                                }
+                            }
+                        } else {
+                            parentProductCategoryId = null;
+                        }
+                    } catch (GenericEntityException e) {
+                        Debug.logError(e, "Cannot generate trail from product category", module);
+                    }
+                }
+                Collections.reverse(trailElements);
+
+                List<String> trail = CategoryWorker.getTrail(httpRequest);
+                if (trail == null) {
+                    trail = FastList.newInstance();
+                }
+
+                // adjust trail
+                String previousCategoryId = null;
+                if (trail.size() > 0) {
+                    previousCategoryId = trail.get(trail.size() - 1);
+                }
+                trail = CategoryWorker.adjustTrail(trail, productCategoryId, previousCategoryId);
+
+                if (trailElements.size() == 1) {
+                    CategoryWorker.setTrail(request, trailElements.get(0), null);
+                } else if (trailElements.size() == 2) {
+                    CategoryWorker.setTrail(request, trailElements.get(1), trailElements.get(0));
+                } else if (trailElements.size() > 2) {
+                    if (trail.contains(trailElements.get(0))) {
+                        // first category is in the trail, so remove it everything after that and fill it in with the list from the pathInfo
+                        int firstElementIndex = trail.indexOf(trailElements.get(0));
+                        while (trail.size() > firstElementIndex) {
+                            trail.remove(firstElementIndex);
+                        }
+                        trail.addAll(trailElements);
+                    } else {
+                        // first category is NOT in the trail, so clear out the trail and use the trailElements list
+                        trail.clear();
+                        trail.addAll(trailElements);
+                    }
+                    CategoryWorker.setTrail(request, trail);
+                }
+
+                request.setAttribute("productCategoryId", productCategoryId);
+
+                if (productId != null) {
+                    request.setAttribute("product_id", productId);
+                    request.setAttribute("productId", productId);
+                }
+            }
+
+            // Set view query parameters
+            UrlServletHelper.setViewQueryParameters(request, urlBuilder);
+            if (UtilValidate.isNotEmpty(productId) || UtilValidate.isNotEmpty(productCategoryId) || UtilValidate.isNotEmpty(urlContentId)) {
+                Debug.logInfo("[Filtered request]: " + pathInfo + " (" + urlBuilder + ")", module);
+                RequestDispatcher dispatch = request.getRequestDispatcher(urlBuilder.toString());
+                dispatch.forward(request, response);
+                return;
+            }
+
+            // Check path alias
+            UrlServletHelper.checkPathAlias(request, httpResponse, delegator, pathInfo);
+        }
+
+        // we're done checking; continue on
+        chain.doFilter(request, response);
+    }
+
+    public static String makeCategoryUrl(HttpServletRequest request, String previousCategoryId, String productCategoryId, String productId, String viewSize, String viewIndex,
+            String viewSort, String searchString) {
+        Delegator delegator = (Delegator) request.getAttribute("delegator");
+        try {
+            GenericValue productCategory = delegator.findOne("ProductCategory", UtilMisc.toMap("productCategoryId", productCategoryId), true);
+            CategoryContentWrapper wrapper = new CategoryContentWrapper(productCategory, request);
+            List<String> trail = CategoryWorker.getTrail(request);
+            return makeCategoryUrl(delegator, wrapper, trail, request.getSession().getServletContext().getContextPath(), previousCategoryId, productCategoryId, productId,
+                    viewSize, viewIndex, viewSort, searchString);
+        } catch (GenericEntityException e) {
+            Debug.logWarning(e, "Cannot create category's URL for: " + productCategoryId, module);
+            return redirectUrl;
+        }
+    }
+
+    public static String makeCategoryUrl(Delegator delegator, CategoryContentWrapper wrapper, List<String> trail, String contextPath, String previousCategoryId,
+            String productCategoryId, String productId, String viewSize, String viewIndex, String viewSort, String searchString) {
+        String url = "";
+        StringWrapper alternativeUrl = wrapper.get("ALTERNATIVE_URL");
+
+        if (UtilValidate.isNotEmpty(alternativeUrl) && UtilValidate.isNotEmpty(alternativeUrl.toString())) {
+            StringBuilder urlBuilder = new StringBuilder();
+            urlBuilder.append(contextPath);
+            if (urlBuilder.charAt(urlBuilder.length() - 1) != '/') {
+                urlBuilder.append("/");
+            }
+            // append alternative URL
+            url = UrlServletHelper.invalidCharacter(alternativeUrl.toString());
+            urlBuilder.append(url);
+            if (UtilValidate.isNotEmpty(productCategoryId)) {
+                urlBuilder.append("-");
+                urlBuilder.append(productCategoryId);
+                urlBuilder.append("-c");
+            }
+            // 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);
+            }
+
+            url = urlBuilder.toString();
+        } else {
+            if (UtilValidate.isEmpty(trail)) {
+                trail = FastList.newInstance();
+            }
+            url = SeoCatalogUrlServlet.makeCatalogUrl(contextPath, trail, productId, productCategoryId, previousCategoryId);
+        }
+
+        return url;
+    }
+
+    public static String makeProductUrl(HttpServletRequest request, String previousCategoryId, String productCategoryId, String productId) {
+        Delegator delegator = (Delegator) request.getAttribute("delegator");
+        String url = null;
+        try {
+            GenericValue product = delegator.findOne("Product", UtilMisc.toMap("productId", productId), true);
+            ProductContentWrapper wrapper = new ProductContentWrapper(product, request);
+            List<String> trail = CategoryWorker.getTrail(request);
+            url = makeProductUrl(delegator, wrapper, trail, request.getSession().getServletContext().getContextPath(), previousCategoryId, productCategoryId, productId);
+        } catch (GenericEntityException e) {
+            Debug.logWarning(e, "Cannot create product's URL for: " + productId, module);
+            return redirectUrl;
+        }
+        return url;
+    }
+
+    public static String makeProductUrl(Delegator delegator, ProductContentWrapper wrapper, List<String> trail, String contextPath, String previousCategoryId,
+            String productCategoryId, String productId) {
+        String url = "";
+        StringWrapper alternativeUrl = wrapper.get("ALTERNATIVE_URL");
+        if (UtilValidate.isNotEmpty(alternativeUrl) && UtilValidate.isNotEmpty(alternativeUrl.toString())) {
+            StringBuilder urlBuilder = new StringBuilder();
+            urlBuilder.append(contextPath);
+            if (urlBuilder.charAt(urlBuilder.length() - 1) != '/') {
+                urlBuilder.append("/");
+            }
+            // append alternative URL
+            url = UrlServletHelper.invalidCharacter(alternativeUrl.toString());
+            urlBuilder.append(url);
+            if (UtilValidate.isNotEmpty(productId)) {
+                urlBuilder.append("-");
+                urlBuilder.append(productId);
+                urlBuilder.append("-p");
+            }
+            url = urlBuilder.toString();
+        } else {
+            if (UtilValidate.isEmpty(trail)) {
+                trail = FastList.newInstance();
+            }
+            url = SeoCatalogUrlServlet.makeCatalogUrl(contextPath, trail, productId, productCategoryId, previousCategoryId);
+        }
+        return url;
+    }
+}

Propchange: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/SeoCatalogUrlFilter.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/SeoCatalogUrlFilter.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/SeoCatalogUrlFilter.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/SeoCatalogUrlServlet.java
URL: http://svn.apache.org/viewvc/ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/SeoCatalogUrlServlet.java?rev=1535171&view=auto
==============================================================================
--- ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/SeoCatalogUrlServlet.java (added)
+++ ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/SeoCatalogUrlServlet.java Wed Oct 23 20:48:36 2013
@@ -0,0 +1,206 @@
+/*******************************************************************************
+ * 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 java.io.IOException;
+import java.util.List;
+
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import javolution.util.FastList;
+
+import org.ofbiz.base.util.Debug;
+import org.ofbiz.base.util.StringUtil;
+import org.ofbiz.base.util.UtilMisc;
+import org.ofbiz.base.util.UtilValidate;
+import org.ofbiz.entity.Delegator;
+import org.ofbiz.entity.GenericEntityException;
+
+/**
+ * SeoCatalogUrlServlet.java
+ */
+@SuppressWarnings("serial")
+public class SeoCatalogUrlServlet extends HttpServlet {
+
+    public static final String module = SeoCatalogUrlServlet.class.getName();
+    public static final String CATALOG_URL_MOUNT_POINT = "products";
+    public static final String PRODUCT_REQUEST = "product";
+    public static final String CATEGORY_REQUEST = "category";
+
+    public SeoCatalogUrlServlet() {
+        super();
+    }
+
+    /**
+     * @see javax.servlet.http.HttpServlet#init(javax.servlet.ServletConfig)
+     */
+    @Override
+    public void init(ServletConfig config) throws ServletException {
+        super.init(config);
+    }
+
+    /**
+     * @see javax.servlet.http.HttpServlet#doPost(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
+     */
+    @Override
+    public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
+        doGet(request, response);
+    }
+
+    /**
+     * @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
+     */
+    @Override
+    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
+        Delegator delegator = (Delegator) getServletContext().getAttribute("delegator");
+
+        String pathInfo = request.getPathInfo();
+        List<String> pathElements = StringUtil.split(pathInfo, "/");
+
+        // look for productId
+        String productId = null;
+        try {
+            String lastPathElement = pathElements.get(pathElements.size() - 1);
+            if (lastPathElement.startsWith("p_") || delegator.findOne("Product", UtilMisc.toMap("productId", lastPathElement), true) != null) {
+                if (lastPathElement.startsWith("p_")) {
+                    productId = lastPathElement.substring(2);
+                } else {
+                    productId = lastPathElement;
+                }
+                pathElements.remove(pathElements.size() - 1);
+            }
+        } catch (GenericEntityException e) {
+            Debug.logError(e, "Error looking up product info for ProductUrl with path info [" + pathInfo + "]: " + e.toString(), module);
+        }
+
+        // get category info going with the IDs that remain
+        String categoryId = null;
+        if (pathElements.size() == 1) {
+            CategoryWorker.setTrail(request, pathElements.get(0), null);
+            categoryId = pathElements.get(0);
+        } else if (pathElements.size() == 2) {
+            CategoryWorker.setTrail(request, pathElements.get(1), pathElements.get(0));
+            categoryId = pathElements.get(1);
+        } else if (pathElements.size() > 2) {
+            List<String> trail = CategoryWorker.getTrail(request);
+            if (trail == null) {
+                trail = FastList.newInstance();
+            }
+
+            if (trail.contains(pathElements.get(0))) {
+                // first category is in the trail, so remove it everything after that and fill it in with the list from the pathInfo
+                int firstElementIndex = trail.indexOf(pathElements.get(0));
+                while (trail.size() > firstElementIndex) {
+                    trail.remove(firstElementIndex);
+                }
+                trail.addAll(pathElements);
+            } else {
+                // first category is NOT in the trail, so clear out the trail and use the pathElements list
+                trail.clear();
+                trail.addAll(pathElements);
+            }
+            CategoryWorker.setTrail(request, trail);
+            categoryId = pathElements.get(pathElements.size() - 1);
+        }
+        if (categoryId != null) {
+            request.setAttribute("productCategoryId", categoryId);
+        }
+
+        String rootCategoryId = null;
+        if (pathElements.size() >= 1) {
+            rootCategoryId = pathElements.get(0);
+        }
+        if (rootCategoryId != null) {
+            request.setAttribute("rootCategoryId", rootCategoryId);
+        }
+
+        if (productId != null) {
+            request.setAttribute("product_id", productId);
+            request.setAttribute("productId", productId);
+        }
+
+        RequestDispatcher rd = request.getRequestDispatcher("/" + (UtilValidate.isEmpty(SeoControlServlet.controlServlet) ? "" : (SeoControlServlet.controlServlet + "/"))
+                + (productId != null ? PRODUCT_REQUEST : CATEGORY_REQUEST));
+        rd.forward(request, response);
+    }
+
+    /**
+     * @see javax.servlet.http.HttpServlet#destroy()
+     */
+    @Override
+    public void destroy() {
+        super.destroy();
+    }
+
+    public static String makeCatalogUrl(HttpServletRequest request, String productId, String currentCategoryId, String previousCategoryId) {
+        StringBuilder urlBuilder = new StringBuilder();
+        urlBuilder.append(request.getSession().getServletContext().getContextPath());
+        if (urlBuilder.charAt(urlBuilder.length() - 1) != '/') {
+            urlBuilder.append("/");
+        }
+        urlBuilder.append(CATALOG_URL_MOUNT_POINT);
+
+        if (UtilValidate.isNotEmpty(currentCategoryId)) {
+            List<String> trail = CategoryWorker.getTrail(request);
+            trail = CategoryWorker.adjustTrail(trail, currentCategoryId, previousCategoryId);
+            for (String trailCategoryId : trail) {
+                if ("TOP".equals(trailCategoryId)) continue;
+                urlBuilder.append("/");
+                urlBuilder.append(trailCategoryId);
+            }
+        }
+
+        if (UtilValidate.isNotEmpty(productId)) {
+            urlBuilder.append("/p_");
+            urlBuilder.append(productId);
+        }
+
+        return urlBuilder.toString();
+    }
+
+    public static String makeCatalogUrl(String contextPath, List<String> crumb, String productId, String currentCategoryId, String previousCategoryId) {
+        StringBuilder urlBuilder = new StringBuilder();
+        urlBuilder.append(contextPath);
+        if (urlBuilder.charAt(urlBuilder.length() - 1) != '/') {
+            urlBuilder.append("/");
+        }
+        urlBuilder.append(CATALOG_URL_MOUNT_POINT);
+
+        if (UtilValidate.isNotEmpty(currentCategoryId)) {
+            crumb = CategoryWorker.adjustTrail(crumb, currentCategoryId, previousCategoryId);
+            for (String trailCategoryId : crumb) {
+                if ("TOP".equals(trailCategoryId)) continue;
+                urlBuilder.append("/");
+                urlBuilder.append(trailCategoryId);
+            }
+        }
+
+        if (UtilValidate.isNotEmpty(productId)) {
+            urlBuilder.append("/p_");
+            urlBuilder.append(productId);
+        }
+
+        return urlBuilder.toString();
+    }
+}

Propchange: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/SeoCatalogUrlServlet.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/applications/product/src/org/ofbiz/product/category/SeoCatalogUrlServlet.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/SeoCatalogUrlServlet.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain