This is an automated email from the ASF dual-hosted git repository.

nmalin pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/ofbiz-framework.git


The following commit(s) were added to refs/heads/trunk by this push:
     new d0a5c8e690 Improved: Add uri shortener function (OFBIZ-13154) (#841)
d0a5c8e690 is described below

commit d0a5c8e690c5d74ead735d3f518d365d289416d3
Author: Nicolas Malin <nicolas.ma...@nereide.fr>
AuthorDate: Tue Nov 5 15:56:48 2024 +0100

    Improved: Add uri shortener function (OFBIZ-13154) (#841)
    
    This improvement offer the possibility to use a shortener url to call ofbiz 
when it render a url.
    
    The origin requirement is to send by email a url to contact OFBiz without 
any information on technical or functionnal context like JWToken, userLogin, 
orderId, partyId and so on. OFBiz forward only a short reference that match the 
actual uri wanted.
    
    Example :
      * ecommerce/myaccount/order/ORD10034 -> s/tiozerzaze
      * 
ecommerce/myaccount?token=JWT[more..than..100]axdr&userLoginId=m...@ofbiz.org 
-> s/epsserlner
    
    When a request arrive in OFBiz with the pattern s/{shortener}, the request 
handler forward to matched uri.
    
    To generate a shortener on freemarker template just use it like it :
    
       <@ofbizUrl pathShortener="true">${MyBigUriToSecure}</@ofbizUrl>
    
    For email template it's ugly recommand to use webSiteId and fullPath
    
       <@ofbizUrl webSiteId="MyWebSite" fullPath="true" 
pathShortener="true">${MyBigUriToSecure}</@ofbizUrl>
    
    With this you can have a url like this : 
https://mywebsite.mydomain/s/rytedqzdfd
    
    At this time only <@ofbizUrl macro freemarker support it.
---
 .../product/category/ftl/UrlRegexpTransform.java   |   5 +-
 .../common/webcommon/WEB-INF/common-controller.xml |   4 +
 framework/security/config/security.properties      |   3 +
 framework/webapp/dtd/site-conf.xsd                 |  10 +-
 framework/webapp/entitydef/entitymodel.xml         |  14 ++
 framework/webapp/ofbiz-component.xml               |   2 +-
 .../webapp/test/OfbizPathShortenerTests.groovy     |  54 +++++++
 .../apache/ofbiz/webapp/OfbizPathShortener.java    | 176 +++++++++++++++++++++
 .../org/apache/ofbiz/webapp/OfbizUrlBuilder.java   |  49 +++++-
 .../ofbiz/webapp/control/RequestHandler.java       |  32 +++-
 .../apache/ofbiz/webapp/ftl/OfbizUrlTransform.java |   6 +-
 framework/webapp/testdef/webapptests.xml           |   2 +-
 12 files changed, 341 insertions(+), 16 deletions(-)

diff --git 
a/applications/product/src/main/java/org/apache/ofbiz/product/category/ftl/UrlRegexpTransform.java
 
b/applications/product/src/main/java/org/apache/ofbiz/product/category/ftl/UrlRegexpTransform.java
index ec008c7c4e..2d57b98b72 100644
--- 
a/applications/product/src/main/java/org/apache/ofbiz/product/category/ftl/UrlRegexpTransform.java
+++ 
b/applications/product/src/main/java/org/apache/ofbiz/product/category/ftl/UrlRegexpTransform.java
@@ -103,6 +103,7 @@ public class UrlRegexpTransform implements 
TemplateTransformModel {
         final StringBuffer buf = new StringBuffer();
         final boolean fullPath = checkArg(args, "fullPath", false);
         final boolean secure = checkArg(args, "secure", false);
+        final boolean shortener = checkArg(args, "pathShortener", false);
         final boolean encode = checkArg(args, "encode", true);
         final String controlPath = convertToString(args.get("controlPath"));
         final String webSiteId = convertToString(args.get("webSiteId"));
@@ -142,7 +143,7 @@ public class UrlRegexpTransform implements 
TemplateTransformModel {
 
                         RequestHandler rh = RequestHandler.from(request);
                         String seoUrl = seoUrl(rh.makeLink(request, response, 
buf.toString(), fullPath,
-                                secure || request.isSecure(), encode, 
controlPath), userLogin == null);
+                                secure || request.isSecure(), encode, 
controlPath, shortener), userLogin == null);
                         String requestURI = buf.toString();
 
                         // add / update csrf token to link when required
@@ -159,7 +160,7 @@ public class UrlRegexpTransform implements 
TemplateTransformModel {
                         ComponentConfig.WebappInfo webAppInfo = 
WebAppUtil.getWebappInfoFromWebsiteId(webSiteId);
                         StringBuilder newUrlBuff = new StringBuilder(250);
                         OfbizUrlBuilder builder = 
OfbizUrlBuilder.from(webAppInfo, delegator);
-                        builder.buildFullUrl(newUrlBuff, buf.toString(), 
secure);
+                        builder.buildFullUrl(newUrlBuff, buf.toString(), 
secure, shortener);
                         String newUrl = newUrlBuff.toString();
                         if (encode) {
                             newUrl = URLEncoder.encode(newUrl, "UTF-8");
diff --git a/framework/common/webcommon/WEB-INF/common-controller.xml 
b/framework/common/webcommon/WEB-INF/common-controller.xml
index fa7f592437..b6ef970a15 100644
--- a/framework/common/webcommon/WEB-INF/common-controller.xml
+++ b/framework/common/webcommon/WEB-INF/common-controller.xml
@@ -109,6 +109,10 @@ under the License.
         <security https="true" auth="false"/>
         <response name="success" type="request" value="main"/>
     </request-map>
+    <request-map uri="s/{shortener}">
+        <security https="true" auth="false"/>
+        <response name="success" type="shortener" value="main"/>
+    </request-map>
 
     <!-- Common Mappings used for locales and timezones -->
     <request-map uri="ListLocales"><security https="true" 
auth="false"/><response name="success" type="view" value="ListLocales" 
save-last-view="true"/></request-map>
diff --git a/framework/security/config/security.properties 
b/framework/security/config/security.properties
index c7effe8d3f..be3bdf052a 100644
--- a/framework/security/config/security.properties
+++ b/framework/security/config/security.properties
@@ -323,3 +323,6 @@ Content-Security-Policy=Content-Security-Policy-Report-Only
 
 #-- Define policy directives, see 
https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP
 PolicyDirectives=default-src 'self'
+
+#-- Give the size of shortener path when the functionality to shorter the url 
is used
+path.shortener.size=10
\ No newline at end of file
diff --git a/framework/webapp/dtd/site-conf.xsd 
b/framework/webapp/dtd/site-conf.xsd
index 8f2927a337..cbf2cfde81 100644
--- a/framework/webapp/dtd/site-conf.xsd
+++ b/framework/webapp/dtd/site-conf.xsd
@@ -530,7 +530,8 @@ under the License.
                     request-redirect-noparam,
                     url,
                     url-redirect,
-                    cross-redirect
+                    cross-redirect,
+                    shortener
                 </xs:documentation>
             </xs:annotation>
             <xs:simpleType>
@@ -625,6 +626,13 @@ under the License.
                             </xs:documentation>
                         </xs:annotation>
                     </xs:enumeration>
+                    <xs:enumeration value="shortener">
+                        <xs:annotation>
+                            <xs:documentation>
+                                Call ofbiz shortener to resolve the next uri 
to call
+                            </xs:documentation>
+                        </xs:annotation>
+                    </xs:enumeration>
                 </xs:restriction>
             </xs:simpleType>
         </xs:attribute>
diff --git a/framework/webapp/entitydef/entitymodel.xml 
b/framework/webapp/entitydef/entitymodel.xml
index 010969815b..f8403d7a62 100644
--- a/framework/webapp/entitydef/entitymodel.xml
+++ b/framework/webapp/entitydef/entitymodel.xml
@@ -30,10 +30,24 @@ under the License.
   <!-- ========================================================= -->
   <!-- ======================== Data Model ===================== -->
   <!-- The modules in this file are as follows:                  -->
+  <!--  - org.apache.ofbiz.webapp.path -->
   <!--  - org.apache.ofbiz.webapp.visit -->
   <!--  - org.apache.ofbiz.webapp.website -->
   <!-- ========================================================= -->
 
+    <entity entity-name="ShortenedPath"
+            package-name="org.apache.ofbiz.webapp.path"
+            title="Shortened Path">
+      <field name="shortenedPath" type="id"/>
+      <field name="originalPathHash" type="id-vlong"/>
+      <field name="originalPath" type="very-long"/>
+      <field name="createdDate" type="date-time"/>
+      <field name="createdByUserLogin" type="id-vlong"/>
+      <prim-key field="shortenedPath"/>
+      <index name="PATH_SHOR_HASH">
+          <index-field name="originalPathHash"/>
+      </index>
+    </entity>
 
   <!-- ========================================================= -->
   <!-- org.apache.ofbiz.webapp.visit -->
diff --git a/framework/webapp/ofbiz-component.xml 
b/framework/webapp/ofbiz-component.xml
index 54e4b39280..152f9310e7 100644
--- a/framework/webapp/ofbiz-component.xml
+++ b/framework/webapp/ofbiz-component.xml
@@ -31,5 +31,5 @@ under the License.
     <entity-resource type="group" reader-name="main" loader="main" 
location="entitydef/entitygroup.xml"/>
     -->
 
-<!--<test-suite loader="main" location="testdef/webapptests.xml"/>-->
+    <test-suite loader="main" location="testdef/webapptests.xml"/>
 </ofbiz-component>
diff --git 
a/framework/webapp/src/main/groovy/org/apache/ofbiz/webapp/test/OfbizPathShortenerTests.groovy
 
b/framework/webapp/src/main/groovy/org/apache/ofbiz/webapp/test/OfbizPathShortenerTests.groovy
new file mode 100644
index 0000000000..824e3b30cb
--- /dev/null
+++ 
b/framework/webapp/src/main/groovy/org/apache/ofbiz/webapp/test/OfbizPathShortenerTests.groovy
@@ -0,0 +1,54 @@
+/*******************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License") you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ 
*******************************************************************************/
+package org.apache.ofbiz.webapp.test
+
+import org.apache.ofbiz.service.testtools.OFBizTestCase
+import org.apache.ofbiz.webapp.OfbizPathShortener
+
+class OfbizPathShortenerTests extends OFBizTestCase {
+
+    OfbizPathShortenerTests(String name) {
+        super(name)
+    }
+    void testComputeLongUrlToShortUrl() {
+        String longUri = 
"passwordChange?USERNAME=admin&TOKEN=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxML" +
+                
"LL.eyJ1c2VyTG9naW5JZCI6Imx1Y2lsZS5wZWxsZXRpZXJAZWRsbi5vcmciLCJpc3MiOiJBcGFjaGVPRkJpeiIsImV4cCI6MTcyNTU"
 +
+                
"0MjM0OSwiaWF0IjoxNzI1NTQwNTQLLL.Rycl_L-u4ZeWkx82pWWGu7gycfsHQxIxE8zu1nQ5oueGDBeOXALL-SJzMuvSARbpxCwF9A"
 +
+                "jl4rTxgoEYuRMoHg&JavaScriptEnabled=Y&Albert=Yoda"
+        assert OfbizPathShortener.resolveShortenedPath(this.getDelegator(), 
longUri).length() < 11
+    }
+    void testResolveLongUrlComputedFromShort() {
+        String longUri = 
"passwordChange?USERNAME=admin&TOKEN=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxML" +
+                
"LL.eyJ1c2VyTG9naW5JZCI6Imx1Y2lsZS5wZWxsZXRpZXJAZWRsbi5vcmciLCJpc3MiOiJBcGFjaGVPRkJpeiIsImV4cCI6MTcyNTU"
 +
+                
"0MjM0OSwiaWF0IjoxNzI1NTQwNTQLLL.Rycl_L-u4ZeWkx82pWWGu7gycfsHQxIxE8zu1nQ5oueGDBeOXALL-SJzMuvSARbpxCwF9A"
 +
+                "jl4rTxgoEYuRMoHg&JavaScriptEnabled=Y"
+        String shortUri = 
OfbizPathShortener.resolveShortenedPath(this.getDelegator(), longUri)
+        assert longUri == 
OfbizPathShortener.resolveOriginalPathFromShortened(this.getDelegator(), 
shortUri)
+    }
+    void testResolveLongUrlComputedFromShortAlreadyStored() {
+        String longUri = 
"passwordChange?USERNAME=admin&TOKEN=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxML" +
+                
"LL.eyJ1c2VyTG9naW5JZCI6Imx1Y2lsZS5wZWxsZXRpZXJAZWRsbi5vcmciLCJpc3MiOiJBcGFjaGVPRkJpeiIsImV4cCI6MTcyNTU"
 +
+                
"0MjM0OSwiaWF0IjoxNzI1NTQwNTQLLL.Rycl_L-u4ZeWkx82pWWGu7gycfsHQxIxE8zu1nQ5oueGDBeOXALL-SJzMuvSARbpxCwF9A"
 +
+                "jl4rTxgoEYuRMoHg&JavaScriptEnabled=Y&And=Again"
+        String shortUriFirst = 
OfbizPathShortener.resolveShortenedPath(this.getDelegator(),longUri)
+        String shortUriSecond = 
OfbizPathShortener.resolveShortenedPath(this.getDelegator(),longUri)
+        assert shortUriSecond == shortUriFirst
+    }
+
+}
diff --git 
a/framework/webapp/src/main/java/org/apache/ofbiz/webapp/OfbizPathShortener.java
 
b/framework/webapp/src/main/java/org/apache/ofbiz/webapp/OfbizPathShortener.java
new file mode 100644
index 0000000000..81f485d559
--- /dev/null
+++ 
b/framework/webapp/src/main/java/org/apache/ofbiz/webapp/OfbizPathShortener.java
@@ -0,0 +1,176 @@
+/*******************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ 
*******************************************************************************/
+package org.apache.ofbiz.webapp;
+
+import java.util.Map;
+import javax.transaction.Transaction;
+import org.apache.commons.lang.RandomStringUtils;
+import org.apache.ofbiz.base.crypto.HashCrypt;
+import org.apache.ofbiz.base.util.UtilDateTime;
+import org.apache.ofbiz.base.util.UtilProperties;
+import org.apache.ofbiz.entity.Delegator;
+import org.apache.ofbiz.entity.GenericEntityException;
+import org.apache.ofbiz.entity.GenericValue;
+import org.apache.ofbiz.entity.transaction.GenericTransactionException;
+import org.apache.ofbiz.entity.transaction.TransactionUtil;
+import org.apache.ofbiz.entity.util.EntityQuery;
+
+public class OfbizPathShortener {
+    public static final String SHORTENED_PATH = "s/";
+    public static final String RESTORE_PATH = "../";
+
+    /**
+     * For an ofbiz path, return a shortened url that will be linked to the 
given path
+     * example : orderview?orderId=HA1023 -> s/izapnreiis
+     * @param delegator
+     * @param path to shorten
+     * @return a shortened key corresponding to the path
+     * @throws GenericEntityException
+     */
+    public static String shortenPath(Delegator delegator, String path) throws 
GenericEntityException {
+        return SHORTENED_PATH + resolveShortenedPath(delegator, path);
+    }
+
+    /**
+     * For the given path, check if a shortened path already exists otherwise 
generate a new one
+     * @param delegator
+     * @param path
+     * @return a shortened path corresponding to the given path
+     * @throws GenericEntityException
+     */
+    public static String resolveShortenedPath(Delegator delegator, String 
path) throws GenericEntityException {
+        String shortenedPath = resolveExistingShortenedPath(delegator, path);
+        int nbLoop = 0;
+        if (shortenedPath == null) {
+            do {
+                shortenedPath = generate();
+                nbLoop++;
+            } while (!recordPathMapping(delegator, path, shortenedPath) || 
nbLoop > 10);
+        }
+        return shortenedPath;
+    }
+
+    /**
+     * Try to resolve the original path, if failed, return to the webapp root. 
Use views request for that define on common-controller
+     * @param delegator
+     * @param shortenedPath
+     * @return the origin path corresponding to the given shortened path, 
webapp root otherwise
+     * @throws GenericEntityException
+     */
+    public static String restoreOriginalPath(Delegator delegator, String 
shortenedPath) throws GenericEntityException {
+        String originalPath = resolveOriginalPathFromShortened(delegator, 
shortenedPath);
+        return RESTORE_PATH + (originalPath != null ? originalPath : "views");
+    }
+
+    /**
+     * From a shortened path, resolve the origin path
+     * @param delegator
+     * @param shortenedPath path
+     * @return the original path corresponding to the shortened path
+     * @throws GenericEntityException
+     */
+    public static String resolveOriginalPathFromShortened(Delegator delegator, 
String shortenedPath) throws GenericEntityException {
+        return readPathMapping(delegator, shortenedPath);
+    }
+
+    /**
+     * For a path, function tried to resolve if it already presents in database
+     * For performance issue the function use the hash to resolve it
+     * @param delegator
+     * @param path
+     * @return the shortened path if found, null otherwise
+     * @throws GenericEntityException
+     */
+    private static String resolveExistingShortenedPath(Delegator delegator, 
String path) throws GenericEntityException {
+        GenericValue existingPath = EntityQuery.use(delegator)
+                .from("ShortenedPath")
+                .where("originalPathHash", generateHash(path))
+                .cache()
+                .queryFirst();
+        return existingPath != null ? existingPath.getString("shortenedPath") 
: null;
+    }
+
+    /**
+     * generate a random shortened path, the size can be set on property : 
security.
+     * @return shortened path
+     */
+    private static String generate() {
+        int shortenerSize = UtilProperties.getPropertyAsInteger("security", 
"path.shortener.size", 10);
+        return RandomStringUtils.randomAlphabetic(shortenerSize);
+    }
+
+    /**
+     * Create the mapping between an origin map and the shortened path
+     * This will be executed on dedicate transaction to be sure to not 
rollback it after.
+     * @param delegator
+     * @param path
+     * @param shortenedPath
+     * @return true if it's create with success
+     */
+    private static boolean recordPathMapping(Delegator delegator, String path, 
String shortenedPath) {
+        Transaction trans = null;
+        try {
+            try {
+                trans = TransactionUtil.suspend();
+                TransactionUtil.begin();
+                delegator.create("ShortenedPath", Map.of("shortenedPath", 
shortenedPath,
+                        "originalPath", path,
+                        "originalPathHash", generateHash(path),
+                        "createdDate", UtilDateTime.nowTimestamp(),
+                        "createdByUserLogin", "system"));
+                TransactionUtil.commit();
+            } catch (GenericEntityException e) {
+                TransactionUtil.rollback();
+                return false;
+            } finally {
+                TransactionUtil.resume(trans);
+            }
+        } catch (GenericTransactionException e) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * @param path
+     * @return a hash of the given path
+     */
+    private static String generateHash(String path) {
+        return HashCrypt.digestHash("SHA", path.getBytes());
+    }
+
+    /**
+     * Find the origin path corresponding to the shorter in database
+     * @param delegator
+     * @param shortenedPath
+     * @return the original path corresponding to shortened path given, null 
if not found
+     * @throws GenericEntityException
+     */
+    private static String readPathMapping(Delegator delegator, String 
shortenedPath) throws GenericEntityException {
+        GenericValue existingPath = EntityQuery.use(delegator)
+                .from("ShortenedPath")
+                .where("shortenedPath", shortenedPath)
+                .cache()
+                .queryOne();
+        return existingPath != null
+                ? existingPath.getString("originalPath")
+                : null;
+    }
+
+}
diff --git 
a/framework/webapp/src/main/java/org/apache/ofbiz/webapp/OfbizUrlBuilder.java 
b/framework/webapp/src/main/java/org/apache/ofbiz/webapp/OfbizUrlBuilder.java
index cfb80dfccf..05c05cda40 100644
--- 
a/framework/webapp/src/main/java/org/apache/ofbiz/webapp/OfbizUrlBuilder.java
+++ 
b/framework/webapp/src/main/java/org/apache/ofbiz/webapp/OfbizUrlBuilder.java
@@ -57,7 +57,8 @@ public final class OfbizUrlBuilder {
             URL url = 
ConfigXMLReader.getControllerConfigURL(request.getServletContext());
             ControllerConfig config = ConfigXMLReader.getControllerConfig(url);
             String servletPath = (String) 
request.getAttribute("_CONTROL_PATH_");
-            builder = new OfbizUrlBuilder(config, webSiteProps, servletPath);
+            Delegator delegator = (Delegator) 
request.getAttribute("delegator");
+            builder = new OfbizUrlBuilder(delegator, config, webSiteProps, 
servletPath);
             request.setAttribute("_OFBIZ_URL_BUILDER_", builder);
         }
         return builder;
@@ -95,14 +96,16 @@ public final class OfbizUrlBuilder {
         if (webSiteProps == null) {
             webSiteProps = WebSiteProperties.defaults(delegator);
         }
-        return new OfbizUrlBuilder(config, webSiteProps, servletPath);
+        return new OfbizUrlBuilder(delegator, config, webSiteProps, 
servletPath);
     }
 
+    private final Delegator delegator;
     private final ControllerConfig config;
     private final WebSiteProperties webSiteProps;
     private final String servletPath;
 
-    private OfbizUrlBuilder(ControllerConfig config, WebSiteProperties 
webSiteProps, String servletPath) {
+    private OfbizUrlBuilder(Delegator delegator, ControllerConfig config, 
WebSiteProperties webSiteProps, String servletPath) {
+        this.delegator = delegator;
         this.config = config;
         this.webSiteProps = webSiteProps;
         this.servletPath = servletPath;
@@ -118,9 +121,26 @@ public final class OfbizUrlBuilder {
      * @throws WebAppConfigurationException
      * @throws IOException
      */
-    public boolean buildFullUrl(Appendable buffer, String url, boolean useSSL) 
throws WebAppConfigurationException, IOException {
+    public boolean buildFullUrl(Appendable buffer, String url, boolean useSSL)
+            throws WebAppConfigurationException, IOException, 
GenericEntityException {
+        return buildFullUrl(buffer, url, useSSL, false);
+    }
+
+    /**
+     * Builds a full URL - including scheme, host, servlet path and resource.
+     * @param buffer
+     * @param url
+     * @param useSSL Default value to use - will be replaced by request-map 
setting
+     * if one is found.
+     * @param pathShortener
+     * @return <code>true</code> if the URL uses https
+     * @throws WebAppConfigurationException
+     * @throws IOException
+     */
+    public boolean buildFullUrl(Appendable buffer, String url, boolean useSSL, 
boolean pathShortener)
+            throws WebAppConfigurationException, IOException, 
GenericEntityException {
         boolean makeSecure = buildHostPart(buffer, url, useSSL);
-        buildPathPart(buffer, url);
+        buildPathPart(buffer, url, pathShortener);
         return makeSecure;
     }
 
@@ -183,7 +203,20 @@ public final class OfbizUrlBuilder {
      * @throws WebAppConfigurationException
      * @throws IOException
      */
-    public void buildPathPart(Appendable buffer, String url) throws 
WebAppConfigurationException, IOException {
+    public void buildPathPart(Appendable buffer, String url)
+            throws WebAppConfigurationException, IOException, 
GenericEntityException {
+        buildPathPart(buffer, url, false);
+    }
+    /**
+     * Builds a partial URL - including the servlet path and resource, but not 
the scheme or host.
+     * @param buffer
+     * @param url
+     * @param pathShortener
+     * @throws WebAppConfigurationException
+     * @throws IOException
+     */
+    public void buildPathPart(Appendable buffer, String url, boolean 
pathShortener)
+            throws WebAppConfigurationException, IOException, 
GenericEntityException {
         if (servletPath == null) {
             throw new IllegalStateException("Servlet path is unknown");
         }
@@ -191,6 +224,8 @@ public final class OfbizUrlBuilder {
         if (!url.startsWith("/")) {
             buffer.append("/");
         }
-        buffer.append(url);
+        buffer.append(pathShortener
+                ? OfbizPathShortener.shortenPath(delegator, url)
+                : url);
     }
 }
diff --git 
a/framework/webapp/src/main/java/org/apache/ofbiz/webapp/control/RequestHandler.java
 
b/framework/webapp/src/main/java/org/apache/ofbiz/webapp/control/RequestHandler.java
index 020bc83f91..f02efae40f 100644
--- 
a/framework/webapp/src/main/java/org/apache/ofbiz/webapp/control/RequestHandler.java
+++ 
b/framework/webapp/src/main/java/org/apache/ofbiz/webapp/control/RequestHandler.java
@@ -64,6 +64,7 @@ import org.apache.ofbiz.entity.util.EntityQuery;
 import org.apache.ofbiz.entity.util.EntityUtilProperties;
 import org.apache.ofbiz.security.CsrfUtil;
 import org.apache.ofbiz.webapp.OfbizUrlBuilder;
+import org.apache.ofbiz.webapp.OfbizPathShortener;
 import org.apache.ofbiz.webapp.control.ConfigXMLReader.ControllerConfig;
 import org.apache.ofbiz.webapp.control.ConfigXMLReader.RequestMap;
 import org.apache.ofbiz.webapp.event.EventFactory;
@@ -901,6 +902,18 @@ public final class RequestHandler {
                 }
                 String url = nextRequestResponse.getValue().startsWith("/") ? 
nextRequestResponse.getValue() : "/" + nextRequestResponse.getValue();
                 callRedirect(url + this.makeQueryString(request, 
nextRequestResponse), response, request, redirectSC);
+            } else if ("shortener".equals(nextRequestResponse.getType())) {
+                // check for a shortener
+                if (Debug.verboseOn()) {
+                    Debug.logVerbose("[RequestHandler.doRequest]: Response is 
a shortener redirect." + showSessionId(request), MODULE);
+                }
+                String url = null;
+                try {
+                    url = OfbizPathShortener.restoreOriginalPath(delegator, 
(String) request.getAttribute("shortener"));
+                } catch (GenericEntityException e) {
+                    throw new RuntimeException(e);
+                }
+                callRedirect(url, response, request, redirectSC);
             } else if 
("request-redirect".equals(nextRequestResponse.getType())) {
                 if (Debug.verboseOn()) {
                     Debug.logVerbose("[RequestHandler.doRequest]: Response is 
a Request redirect." + showSessionId(request), MODULE);
@@ -1359,6 +1372,10 @@ public final class RequestHandler {
 
     public String makeLink(HttpServletRequest request, HttpServletResponse 
response, String url, boolean fullPath, boolean secure, boolean encode,
                            String targetControlPath) {
+        return makeLink(request, response, url, fullPath, secure, encode, "", 
false);
+    }
+    public String makeLink(HttpServletRequest request, HttpServletResponse 
response, String url, boolean fullPath, boolean secure, boolean encode,
+                           String targetControlPath, boolean pathShortener) {
         WebSiteProperties webSiteProps = null;
         try {
             webSiteProps = WebSiteProperties.from(request);
@@ -1458,8 +1475,19 @@ public final class RequestHandler {
         }
 
         // now add the actual passed url, but if it doesn't start with a / add 
one first
-        if (url != null && !url.startsWith("/")) {
-            newURL.append("/");
+        if (url != null) {
+            if (!url.startsWith("/")) {
+                newURL.append("/");
+            }
+            if (pathShortener) {
+                try {
+                    url = OfbizPathShortener.shortenPath(delegator, url);
+                } catch (GenericEntityException e) {
+                    // If the entity engine is throwing exceptions, then there 
is no point in continuing.
+                    Debug.logError(e, "Exception thrown while getting the path 
shortener: ", MODULE);
+                    return null;
+                }
+            }
         }
         newURL.append(url == null ? "" : url);
 
diff --git 
a/framework/webapp/src/main/java/org/apache/ofbiz/webapp/ftl/OfbizUrlTransform.java
 
b/framework/webapp/src/main/java/org/apache/ofbiz/webapp/ftl/OfbizUrlTransform.java
index 763b4f087f..829a7392ef 100644
--- 
a/framework/webapp/src/main/java/org/apache/ofbiz/webapp/ftl/OfbizUrlTransform.java
+++ 
b/framework/webapp/src/main/java/org/apache/ofbiz/webapp/ftl/OfbizUrlTransform.java
@@ -47,6 +47,7 @@ import freemarker.template.TemplateTransformModel;
  * <li><b>fullPath</b> (true/false) - generate a full URL including scheme and 
host, defaults to false.</li>
  * <li><b>secure</b> (true/false) - generate a secure (https) URL, defaults to 
false. Server settings will
  * override this argument.</li>
+ * <li><b>pathShortener</b> (true/false) - generate a shortened URL. Defaults 
to false.</li>
  * <li><b>encode</b> (true/false) - encode the URL, defaults to true. Encoding 
is UTF-8.</li>
  * <li><b>webSiteId</b> - generate a full URL using the web site settings 
found in the WebSite entity.</li>
  * <li><b>controlPath</b> - override the default control path.</li>
@@ -96,6 +97,7 @@ public class OfbizUrlTransform implements 
TemplateTransformModel {
         final StringBuilder buf = new StringBuilder();
         final boolean fullPath = checkBooleanArg(args, "fullPath", false);
         final boolean secure = checkBooleanArg(args, "secure", false);
+        final boolean pathShortener = checkBooleanArg(args, "pathShortener", 
false);
         final boolean encode = checkBooleanArg(args, "encode", true);
         final String webSiteId = convertToString(args.get("webSiteId"));
         final String controlPath = convertToString(args.get("controlPath"));
@@ -133,7 +135,7 @@ public class OfbizUrlTransform implements 
TemplateTransformModel {
                         WebappInfo webAppInfo = 
WebAppUtil.getWebappInfoFromWebsiteId(webSiteId);
                         StringBuilder newUrlBuff = new StringBuilder(250);
                         OfbizUrlBuilder builder = 
OfbizUrlBuilder.from(webAppInfo, delegator);
-                        builder.buildFullUrl(newUrlBuff, buf.toString(), 
secure);
+                        builder.buildFullUrl(newUrlBuff, buf.toString(), 
secure, pathShortener);
                         String newUrl = newUrlBuff.toString();
                         if (encode) {
                             newUrl = URLEncoder.encode(newUrl, "UTF-8");
@@ -145,7 +147,7 @@ public class OfbizUrlTransform implements 
TemplateTransformModel {
                         HttpServletResponse response = 
FreeMarkerWorker.unwrap(env.getVariable("response"));
                         String requestUrl = buf.toString();
                         RequestHandler rh = RequestHandler.from(request);
-                        String link = (rh.makeLink(request, response, 
requestUrl, fullPath, secure, encode, controlPath));
+                        String link = (rh.makeLink(request, response, 
requestUrl, fullPath, secure, encode, controlPath, pathShortener));
                         out.write(link);
                     } else {
                         out.write(buf.toString());
diff --git a/framework/webapp/testdef/webapptests.xml 
b/framework/webapp/testdef/webapptests.xml
index f33f498994..692e5640fa 100644
--- a/framework/webapp/testdef/webapptests.xml
+++ b/framework/webapp/testdef/webapptests.xml
@@ -20,5 +20,5 @@
 <test-suite suite-name="webapptests"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
         
xsi:noNamespaceSchemaLocation="https://ofbiz.apache.org/dtds/test-suite.xsd";>
-
+    <test-case case-name="PathShortener-tests"><junit-test-suite 
class-name="org.apache.ofbiz.webapp.test.OfbizPathShortenerTests"/></test-case>
 </test-suite>

Reply via email to