[ofbiz-framework] branch trunk updated: Implemented: Element link can update partial area (OFBIZ-11810)

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

[ofbiz-framework] branch trunk updated: Implemented: Element link can update partial area (OFBIZ-11810)

nmalin
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 5229d57  Implemented: Element link can update partial area (OFBIZ-11810)
5229d57 is described below

commit 5229d572df72f23601253a32b565c50c72b6bd19
Author: Nicolas Malin <[hidden email]>
AuthorDate: Wed Feb 17 18:11:19 2021 +0100

    Implemented: Element link can update partial area
    (OFBIZ-11810)
   
       Currently when you want to display an information easily on sub-screen
    dynamically (example as related element) is currently not possible without
    write specific js code.
       To simplify it, we introduce a new link type: update-area, to indicate
    to the theme that we want to refresh  a local area with the call result.
   
       Example :
           <link target="ListCategoryProductMembers" link-type="update-area" target-window="detail">
               <parameter param-name="productCategoryId"/>
           </link>
   
    For this example we indicate that the display result of target 'ListCategoryProductMembers' is going
    on the area identify by 'detail'. Pleasure to the theme to manage it.
---
 framework/widget/dtd/widget-common.xsd             |   7 +
 .../widget/renderer/macro/MacroCommonRenderer.java | 146 +++++++++++++++++++++
 .../widget/renderer/macro/MacroFormRenderer.java   | 136 ++-----------------
 .../widget/renderer/macro/MacroMenuRenderer.java   |  27 ++--
 .../widget/renderer/macro/MacroScreenRenderer.java |   7 +-
 .../template/macro/HtmlMenuMacroLibrary.ftl        |   2 +-
 .../template/macro/HtmlScreenMacroLibrary.ftl      |   4 +-
 .../template/macro/HtmlMenuMacroLibrary.ftl        |   2 +-
 8 files changed, 179 insertions(+), 152 deletions(-)

diff --git a/framework/widget/dtd/widget-common.xsd b/framework/widget/dtd/widget-common.xsd
index bd23e32..7b18e40 100644
--- a/framework/widget/dtd/widget-common.xsd
+++ b/framework/widget/dtd/widget-common.xsd
@@ -559,6 +559,13 @@ under the License.
                     <xs:enumeration value="anchor" />
                     <xs:enumeration value="hidden-form" />
                     <xs:enumeration value="layered-modal" />
+                    <xs:enumeration value="update-area">
+                        <xs:annotation>
+                            <xs:documentation>
+                                Specify a specific area to update with a screen injection.
+                            </xs:documentation>
+                        </xs:annotation>
+                    </xs:enumeration>
                 </xs:restriction>
             </xs:simpleType>
         </xs:attribute>
diff --git a/framework/widget/src/main/java/org/apache/ofbiz/widget/renderer/macro/MacroCommonRenderer.java b/framework/widget/src/main/java/org/apache/ofbiz/widget/renderer/macro/MacroCommonRenderer.java
new file mode 100644
index 0000000..8fb85f5
--- /dev/null
+++ b/framework/widget/src/main/java/org/apache/ofbiz/widget/renderer/macro/MacroCommonRenderer.java
@@ -0,0 +1,146 @@
+/*******************************************************************************
+ * 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.widget.renderer.macro;
+
+import java.net.URI;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.apache.ofbiz.base.util.StringUtil;
+import org.apache.ofbiz.base.util.UtilGenerics;
+import org.apache.ofbiz.base.util.UtilHttp;
+import org.apache.ofbiz.base.util.UtilMisc;
+import org.apache.ofbiz.base.util.string.FlexibleStringExpander;
+import org.apache.ofbiz.webapp.control.RequestHandler;
+import org.apache.ofbiz.widget.WidgetWorker;
+import org.apache.ofbiz.widget.model.CommonWidgetModels;
+import org.apache.ofbiz.widget.model.ModelForm;
+
+/**
+ * TODO : Migrate to {@link org.apache.ofbiz.widget.renderer.macro.renderable.RenderableFtl}
+ */
+public class MacroCommonRenderer {
+
+    /**
+     * Create an ajaxXxxx JavaScript CSV string from a list of UpdateArea objects. See
+     * <code>OfbizUtil.js</code>.
+     * @param updateAreas
+     * @param extraParams Renderer-supplied additional target parameters Map
+     * @param anchor
+     * @param context
+     * @return Parameter string or empty string if no UpdateArea objects were found
+     */
+    public static String createAjaxParamsFromUpdateAreas(List<ModelForm.UpdateArea> updateAreas, Map<String, Object> extraParams,
+                                                   String anchor, Map<String, ? extends Object> context) {
+
+        HttpServletRequest request = (HttpServletRequest) context.get("request");
+        HttpServletResponse response = (HttpServletResponse) context.get("response");
+        RequestHandler rh = RequestHandler.from(request);
+
+        StringBuilder sb = new StringBuilder();
+        Iterator<ModelForm.UpdateArea> updateAreaIter = updateAreas.iterator();
+        while (updateAreaIter.hasNext()) {
+            ModelForm.UpdateArea updateArea = updateAreaIter.next();
+            sb.append(updateArea.getAreaId()).append(",");
+            String ajaxTarget = updateArea.getAreaTarget(context);
+            String urlPath = UtilHttp.removeQueryStringFromTarget(ajaxTarget);
+            sb.append(rh.makeLink(request, response, urlPath)).append(",");
+            String queryString = UtilHttp.getQueryStringFromTarget(ajaxTarget).replace("?", "");
+            Map<String, Object> parameters = UtilHttp.getQueryStringOnlyParameterMap(queryString);
+            Map<String, Object> ctx = UtilGenerics.cast(context);
+            Map<String, Object> updateParams = UtilGenerics.cast(updateArea.getParameterMap(ctx));
+            parameters.putAll(updateParams);
+            UtilHttp.canonicalizeParameterMap(parameters);
+            if (extraParams != null) {
+                parameters.putAll(extraParams);
+            }
+            Iterator<Map.Entry<String, Object>> paramIter = parameters.entrySet().iterator();
+            while (paramIter.hasNext()) {
+                Map.Entry<String, Object> entry = paramIter.next();
+                sb.append(entry.getKey()).append("=").append(entry.getValue());
+                if (paramIter.hasNext()) {
+                    sb.append("&");
+                }
+            }
+            if (anchor != null) {
+                sb.append("#").append(anchor);
+            }
+            if (updateAreaIter.hasNext()) {
+                sb.append(",");
+            }
+        }
+        Locale locale = UtilMisc.ensureLocale(context.get("locale"));
+        return FlexibleStringExpander.expandString(sb.toString(), context, locale);
+    }
+
+    /** Create an ajaxXxxx JavaScript CSV string from a list of UpdateArea objects. See
+     * <code>OfbizUtil.js</code>.
+     * @param updateAreas
+     * @param extraParams Renderer-supplied additional target parameters String
+     * @param context
+     * @return Parameter string or empty string if no UpdateArea objects were found
+     */
+    public static String createAjaxParamsFromUpdateAreas(List<ModelForm.UpdateArea> updateAreas, String extraParams,
+                                                         Map<String, ? extends Object> context) {
+        Map<String, Object> extraParamsAsMap = buildParamMapFromString(extraParams);
+        return createAjaxParamsFromUpdateAreas(updateAreas, extraParamsAsMap, null, context);
+    }
+
+    private static Map<String, Object> buildParamMapFromString(String extraParams) {
+        Map<String, Object> extraParamsAsMap = null;
+        if (extraParams != null) {
+            while (extraParams.startsWith("&")) {
+                extraParams = extraParams.replaceFirst("&", "");
+            }
+            extraParamsAsMap = UtilGenerics.cast(StringUtil.strToMap(extraParams, "&", false));
+        }
+        return extraParamsAsMap;
+    }
+
+    /**
+     * Analyze the context against the link type to resolve the url to call
+     * @param context
+     * @return
+     */
+    public static String getLinkUrl(CommonWidgetModels.Link link, Map<String, Object> context) {
+        String linkUrl;
+
+        HttpServletRequest request = (HttpServletRequest) context.get("request");
+        HttpServletResponse response = (HttpServletResponse) context.get("response");
+        switch (link.getLinkType()) {
+        case "update-area":
+            ModelForm.UpdateArea resolveUpdateArea = new ModelForm.UpdateArea("onclick",
+                    WidgetWorker.getScreenStack(context).resolveScreenAreaId(link.getTargetWindow(context)),
+                    link.getTarget(context));
+            linkUrl = createAjaxParamsFromUpdateAreas(UtilMisc.toList(resolveUpdateArea),
+                    UtilHttp.urlEncodeArgs(link.getParameterMap(context)), context);
+            break;
+        default:
+            final URI linkUri = WidgetWorker.buildHyperlinkUri(link.getTarget(context), link.getUrlMode(),
+                    "layered-modal".equals(link.getLinkType()) ? null : link.getParameterMap(context), link.getPrefix(context),
+                    link.getFullPath(), link.getSecure(), link.getEncode(),
+                    request, response);
+            linkUrl = linkUri.toString();
+        }
+        return linkUrl;
+    }
+}
diff --git a/framework/widget/src/main/java/org/apache/ofbiz/widget/renderer/macro/MacroFormRenderer.java b/framework/widget/src/main/java/org/apache/ofbiz/widget/renderer/macro/MacroFormRenderer.java
index 27eb9f1..eca47ff 100644
--- a/framework/widget/src/main/java/org/apache/ofbiz/widget/renderer/macro/MacroFormRenderer.java
+++ b/framework/widget/src/main/java/org/apache/ofbiz/widget/renderer/macro/MacroFormRenderer.java
@@ -138,20 +138,6 @@ public final class MacroFormRenderer implements FormStringRenderer {
         return htmlString.replaceAll("\"", "\\\\\"");
     }
 
-    /**
-     * Extracts parameters from a target URL string, prepares them for an Ajax
-     * JavaScript call. This method is currently set to return a parameter string
-     * suitable for the Prototype.js library.
-     * @param target Target URL string
-     * @return Parameter string
-     */
-    public static String getAjaxParamsFromTarget(String target) {
-        String targetParams = UtilHttp.getQueryStringFromTarget(target);
-        targetParams = targetParams.replace("?", "");
-        targetParams = targetParams.replace("&amp;", "&");
-        return targetParams;
-    }
-
     public boolean getRenderPagination() {
         return this.renderPagination;
     }
@@ -1023,7 +1009,7 @@ public final class MacroFormRenderer implements FormStringRenderer {
                 && this.javaScriptEnabled;
         String ajaxUrl = "";
         if (ajaxEnabled) {
-            ajaxUrl = createAjaxParamsFromUpdateAreas(updateAreas, "", context);
+            ajaxUrl = MacroCommonRenderer.createAjaxParamsFromUpdateAreas(updateAreas, "", context);
         }
         String tabindex = modelFormField.getTabindex();
         StringWriter sr = new StringWriter();
@@ -2118,7 +2104,7 @@ public final class MacroFormRenderer implements FormStringRenderer {
         this.appendContentUrl(imgSrc, "/images/fieldlookup.gif");
         String ajaxUrl = "";
         if (ajaxEnabled) {
-            ajaxUrl = createAjaxParamsFromUpdateAreas(updateAreas, "", context);
+            ajaxUrl = MacroCommonRenderer.createAjaxParamsFromUpdateAreas(updateAreas, "", context);
         }
         String lookupPresentation = lookupField.getLookupPresentation();
         if (UtilValidate.isEmpty(lookupPresentation)) {
@@ -2342,7 +2328,7 @@ public final class MacroFormRenderer implements FormStringRenderer {
         String ajaxLastUrl = "";
         if (viewIndex > 0) {
             if (ajaxEnabled) {
-                ajaxFirstUrl = createAjaxParamsFromUpdateAreas(updateAreas, prepLinkText + 0 + anchor, context);
+                ajaxFirstUrl = MacroCommonRenderer.createAjaxParamsFromUpdateAreas(updateAreas, prepLinkText + 0 + anchor, context);
             } else {
                 linkText = prepLinkText + 0 + anchor;
                 firstUrl = rh.makeLink(this.request, this.response, urlPath + linkText);
@@ -2350,7 +2336,7 @@ public final class MacroFormRenderer implements FormStringRenderer {
         }
         if (viewIndex > 0) {
             if (ajaxEnabled) {
-                ajaxPreviousUrl = createAjaxParamsFromUpdateAreas(updateAreas, prepLinkText + (viewIndex - 1) + anchor, context);
+                ajaxPreviousUrl = MacroCommonRenderer.createAjaxParamsFromUpdateAreas(updateAreas, prepLinkText + (viewIndex - 1) + anchor, context);
             } else {
                 linkText = prepLinkText + (viewIndex - 1) + anchor;
                 previousUrl = rh.makeLink(this.request, this.response, urlPath + linkText);
@@ -2359,7 +2345,7 @@ public final class MacroFormRenderer implements FormStringRenderer {
         // Page select dropdown
         if (listSize > 0 && this.javaScriptEnabled) {
             if (ajaxEnabled) {
-                ajaxSelectUrl = createAjaxParamsFromUpdateAreas(updateAreas, prepLinkText + "' + this.value + '", context);
+                ajaxSelectUrl = MacroCommonRenderer.createAjaxParamsFromUpdateAreas(updateAreas, prepLinkText + "' + this.value + '", context);
             } else {
                 linkText = prepLinkText;
                 if (linkText.startsWith("/")) {
@@ -2371,7 +2357,7 @@ public final class MacroFormRenderer implements FormStringRenderer {
         // Next button
         if (highIndex < listSize) {
             if (ajaxEnabled) {
-                ajaxNextUrl = createAjaxParamsFromUpdateAreas(updateAreas, prepLinkText + (viewIndex + 1) + anchor, context);
+                ajaxNextUrl = MacroCommonRenderer.createAjaxParamsFromUpdateAreas(updateAreas, prepLinkText + (viewIndex + 1) + anchor, context);
             } else {
                 linkText = prepLinkText + (viewIndex + 1) + anchor;
                 nextUrl = rh.makeLink(this.request, this.response, urlPath + linkText);
@@ -2381,7 +2367,7 @@ public final class MacroFormRenderer implements FormStringRenderer {
         if (highIndex < listSize) {
             int lastIndex = UtilMisc.getViewLastIndex(listSize, viewSize);
             if (ajaxEnabled) {
-                ajaxLastUrl = createAjaxParamsFromUpdateAreas(updateAreas, prepLinkText + lastIndex + anchor, context);
+                ajaxLastUrl = MacroCommonRenderer.createAjaxParamsFromUpdateAreas(updateAreas, prepLinkText + lastIndex + anchor, context);
             } else {
                 linkText = prepLinkText + lastIndex + anchor;
                 lastUrl = rh.makeLink(this.request, this.response, urlPath + linkText);
@@ -2390,7 +2376,7 @@ public final class MacroFormRenderer implements FormStringRenderer {
         // Page size select dropdown
         if (listSize > 0 && this.javaScriptEnabled) {
             if (ajaxEnabled) {
-                ajaxSelectSizeUrl = createAjaxParamsFromUpdateAreas(updateAreas, prepLinkSizeText + anchor, context);
+                ajaxSelectSizeUrl = MacroCommonRenderer.createAjaxParamsFromUpdateAreas(updateAreas, prepLinkSizeText + anchor, context);
             } else {
                 linkText = prepLinkSizeText;
                 if (linkText.startsWith("/")) {
@@ -2834,7 +2820,7 @@ public final class MacroFormRenderer implements FormStringRenderer {
         UtilHttp.canonicalizeParameterMap(paramMap);
         String linkUrl = null;
         if (ajaxEnabled) {
-            linkUrl = createAjaxParamsFromUpdateAreas(updateAreas, paramMap, null, context);
+            linkUrl = MacroCommonRenderer.createAjaxParamsFromUpdateAreas(updateAreas, paramMap, null, context);
         } else {
             StringBuilder sb = new StringBuilder("?");
             Iterator<Map.Entry<String, Object>> iter = paramMap.entrySet().iterator();
@@ -2872,110 +2858,6 @@ public final class MacroFormRenderer implements FormStringRenderer {
         executeMacro(writer, sr.toString());
     }
 
-    /**
-     * Create an ajaxXxxx JavaScript CSV string from a list of UpdateArea objects. See
-     * <code>OfbizUtil.js</code>.
-     * @param updateAreas
-     * @param extraParams Renderer-supplied additional target parameters
-     * @param context
-     * @return Parameter string or empty string if no UpdateArea objects were found
-     */
-    private String createAjaxParamsFromUpdateAreas(List<ModelForm.UpdateArea> updateAreas, Map<String, Object> extraParams,
-                                                   String anchor, Map<String, ? extends Object> context) {
-        StringBuilder sb = new StringBuilder();
-        Iterator<ModelForm.UpdateArea> updateAreaIter = updateAreas.iterator();
-        while (updateAreaIter.hasNext()) {
-            ModelForm.UpdateArea updateArea = updateAreaIter.next();
-            sb.append(updateArea.getAreaId()).append(",");
-            String ajaxTarget = updateArea.getAreaTarget(context);
-            String urlPath = UtilHttp.removeQueryStringFromTarget(ajaxTarget);
-            sb.append(this.rh.makeLink(this.request, this.response, urlPath)).append(",");
-            String queryString = UtilHttp.getQueryStringFromTarget(ajaxTarget).replace("?", "");
-            Map<String, Object> parameters = UtilHttp.getQueryStringOnlyParameterMap(queryString);
-            Map<String, Object> ctx = UtilGenerics.cast(context);
-            Map<String, Object> updateParams = UtilGenerics.cast(updateArea.getParameterMap(ctx));
-            parameters.putAll(updateParams);
-            UtilHttp.canonicalizeParameterMap(parameters);
-            parameters.putAll(extraParams);
-            Iterator<Map.Entry<String, Object>> paramIter = parameters.entrySet().iterator();
-            while (paramIter.hasNext()) {
-                Map.Entry<String, Object> entry = paramIter.next();
-                sb.append(entry.getKey()).append("=").append(entry.getValue());
-                if (paramIter.hasNext()) {
-                    sb.append("&");
-                }
-            }
-            if (anchor != null) {
-                sb.append("#").append(anchor);
-            }
-            if (updateAreaIter.hasNext()) {
-                sb.append(",");
-            }
-        }
-        Locale locale = UtilMisc.ensureLocale(context.get("locale"));
-        return FlexibleStringExpander.expandString(sb.toString(), context, locale);
-    }
-
-    /**
-     * Create an ajaxXxxx JavaScript CSV string from a list of UpdateArea objects. See
-     * <code>OfbizUtil.js</code>.
-     * @param updateAreas
-     * @param extraParams Renderer-supplied additional target parameters
-     * @param context
-     * @return Parameter string or empty string if no UpdateArea objects were found
-     */
-    public String createAjaxParamsFromUpdateAreas(List<ModelForm.UpdateArea> updateAreas, String extraParams, Map<String, ? extends Object> context) {
-        //FIXME copy from HtmlFormRenderer.java
-        if (updateAreas == null) {
-            return "";
-        }
-        String ajaxUrl = "";
-        boolean firstLoop = true;
-        for (ModelForm.UpdateArea updateArea : updateAreas) {
-            if (firstLoop) {
-                firstLoop = false;
-            } else {
-                ajaxUrl += ",";
-            }
-            Map<String, Object> ctx = UtilGenerics.cast(context);
-            Map<String, String> parameters = updateArea.getParameterMap(ctx);
-            String targetUrl = updateArea.getAreaTarget(context);
-            String ajaxParams;
-            StringBuffer ajaxParamsBuffer = new StringBuffer();
-            ajaxParamsBuffer.append(getAjaxParamsFromTarget(targetUrl));
-            //add first parameters from updateArea parameters
-            if (UtilValidate.isNotEmpty(parameters)) {
-                for (Map.Entry<String, String> entry : parameters.entrySet()) {
-                    String key = entry.getKey();
-                    String value = entry.getValue();
-                    //test if ajax parameters are not already into extraParams, if so do not add it
-                    if (UtilValidate.isNotEmpty(extraParams) && extraParams.contains(value)) {
-                        continue;
-                    }
-                    if (ajaxParamsBuffer.length() > 0 && ajaxParamsBuffer.indexOf(key) < 0) {
-                        ajaxParamsBuffer.append("&");
-                    }
-                    if (ajaxParamsBuffer.indexOf(key) < 0) {
-                        ajaxParamsBuffer.append(key).append("=").append(value);
-                    }
-                }
-            }
-            //then add parameters from request. Those parameters could end with an anchor so we must set ajax parameters first
-            if (UtilValidate.isNotEmpty(extraParams)) {
-                if (ajaxParamsBuffer.length() > 0 && !extraParams.startsWith("&")) {
-                    ajaxParamsBuffer.append("&");
-                }
-                ajaxParamsBuffer.append(extraParams);
-            }
-            ajaxParams = ajaxParamsBuffer.toString();
-            ajaxUrl += updateArea.getAreaId() + ",";
-            ajaxUrl += this.rh.makeLink(this.request, this.response, UtilHttp.removeQueryStringFromTarget(targetUrl));
-            ajaxUrl += "," + ajaxParams;
-        }
-        Locale locale = UtilMisc.ensureLocale(context.get("locale"));
-        return FlexibleStringExpander.expandString(ajaxUrl, context, locale);
-    }
-
     private void appendTooltip(Appendable writer, Map<String, Object> context, ModelFormField modelFormField) {
         // render the tooltip, in other methods too
         writeFtlElement(writer, renderableFtlFormElementsBuilder.tooltip(context, modelFormField));
diff --git a/framework/widget/src/main/java/org/apache/ofbiz/widget/renderer/macro/MacroMenuRenderer.java b/framework/widget/src/main/java/org/apache/ofbiz/widget/renderer/macro/MacroMenuRenderer.java
index f3b55ee..264c836 100644
--- a/framework/widget/src/main/java/org/apache/ofbiz/widget/renderer/macro/MacroMenuRenderer.java
+++ b/framework/widget/src/main/java/org/apache/ofbiz/widget/renderer/macro/MacroMenuRenderer.java
@@ -200,19 +200,20 @@ public class MacroMenuRenderer implements MenuStringRenderer {
 
     @Override
     public void renderLink(Appendable writer, Map<String, Object> context, MenuLink link) throws IOException {
-        Map<String, Object> parameters = new HashMap<>();
         String target = link.getTarget(context);
         ModelMenuItem menuItem = link.getLinkMenuItem();
         if (isDisableIfEmpty(menuItem, context)) {
             target = null;
         }
-        parameters.put("id", link.getId(context));
-        parameters.put("style", link.getStyle(context));
-        parameters.put("name", link.getName(context));
-        parameters.put("text", link.getText(context));
-        parameters.put("height", link.getHeight());
-        parameters.put("width", link.getWidth());
-        parameters.put("targetWindow", link.getTargetWindow(context));
+        Map<String, Object> parameters = UtilMisc.toMap(
+                        "id", link.getId(context),
+                "style", link.getStyle(context),
+                "name", link.getName(context),
+                "text", link.getText(context),
+                "height", link.getHeight(),
+                "width", link.getWidth(),
+                "targetWindow", link.getTargetWindow(context));
+
         StringBuffer uniqueItemName = new StringBuffer(menuItem.getModelMenu().getName());
         uniqueItemName.append("_").append(menuItem.getName()).append("_LF_").append(UtilMisc.<String>addToBigDecimalInMap(context,
                 "menuUniqueItemIndex", BigDecimal.ONE));
@@ -227,6 +228,7 @@ public class MacroMenuRenderer implements MenuStringRenderer {
             }
         }
         parameters.put("uniqueItemName", uniqueItemName.toString());
+
         String linkType = "";
         if (UtilValidate.isNotEmpty(target)) {
             linkType = WidgetWorker.determineAutoLinkType(link.getLinkType(), target, link.getUrlMode(), request);
@@ -234,6 +236,7 @@ public class MacroMenuRenderer implements MenuStringRenderer {
         parameters.put("linkType", linkType);
         String linkUrl = "";
         String actionUrl = "";
+
         StringBuilder targetParameters = new StringBuilder();
         if ("hidden-form".equals(linkType) || "layered-modal".equals(linkType)) {
             final URI actionUri = WidgetWorker.buildHyperlinkUri(target, link.getUrlMode(), null,
@@ -260,13 +263,7 @@ public class MacroMenuRenderer implements MenuStringRenderer {
             targetParameters.append("\"\"");
         }
         if (UtilValidate.isNotEmpty(target)) {
-            if (!"hidden-form".equals(linkType)) {
-                final URI linkUri = WidgetWorker.buildHyperlinkUri(target, link.getUrlMode(),
-                        "layered-modal".equals(linkType) ? null : link.getParameterMap(context),
-                        link.getPrefix(context), link.getFullPath(), link.getSecure(), link.getEncode(),
-                        request, response);
-                linkUrl = linkUri.toString();
-            }
+            linkUrl = MacroCommonRenderer.getLinkUrl(link.getLink(), context);
         }
         parameters.put("linkUrl", linkUrl);
         parameters.put("actionUrl", actionUrl);
diff --git a/framework/widget/src/main/java/org/apache/ofbiz/widget/renderer/macro/MacroScreenRenderer.java b/framework/widget/src/main/java/org/apache/ofbiz/widget/renderer/macro/MacroScreenRenderer.java
index e4ed5b2..5fdb210 100644
--- a/framework/widget/src/main/java/org/apache/ofbiz/widget/renderer/macro/MacroScreenRenderer.java
+++ b/framework/widget/src/main/java/org/apache/ofbiz/widget/renderer/macro/MacroScreenRenderer.java
@@ -310,12 +310,7 @@ public class MacroScreenRenderer implements ScreenStringRenderer {
         String name = link.getName(context);
         String text = link.getText(context);
         if (UtilValidate.isNotEmpty(target)) {
-            if (!"hidden-form".equals(linkType)) {
-                final URI uri = WidgetWorker.buildHyperlinkUri(target, link.getUrlMode(), link.getParameterMap(context),
-                        link.getPrefix(context), link.getFullPath(), link.getSecure(), link.getEncode(),
-                        request, response);
-                linkUrl = uri.toString();
-            }
+            linkUrl = MacroCommonRenderer.getLinkUrl(link.getLink(), context);
         }
         String imgStr = "";
         ModelScreenWidget.ScreenImage img = link.getImage();
diff --git a/themes/common-theme/template/macro/HtmlMenuMacroLibrary.ftl b/themes/common-theme/template/macro/HtmlMenuMacroLibrary.ftl
index a440739..644f5ef 100644
--- a/themes/common-theme/template/macro/HtmlMenuMacroLibrary.ftl
+++ b/themes/common-theme/template/macro/HtmlMenuMacroLibrary.ftl
@@ -72,7 +72,7 @@ under the License.
     <#if text?has_content>${text}</#if></a>
   <#else>
 <#if (linkType?has_content && "hidden-form" == linkType) || linkUrl?has_content>
-<a<#if id?has_content> id="${id}"</#if><#if style?has_content> class="${style}"</#if><#if name?has_content> name="${name}"</#if><#if targetWindow?has_content> target="${targetWindow}"</#if> href="<#if "hidden-form"==linkType>javascript:document.${uniqueItemName}.submit()<#else>${linkUrl}</#if>"><#rt/>
+<a<#if id?has_content> id="${id}"</#if><#if style?has_content> class="${style}"</#if><#if name?has_content> name="${name}"</#if><#if targetWindow?has_content && "update-area" != linkType> target="${targetWindow}"</#if> href="<#if "hidden-form"==linkType><#if linkUrl?has_content>javascript:ajaxSubmitFormUpdateAreas('${uniqueItemName}', '${linkUrl}')<#else>javascript:document.${uniqueItemName}.submit()</#if><#else><#if "update-area" == linkType>javascript:ajaxUpdateAreas('${linkUrl}')<#els [...]
 </#if>
 <#if imgStr?has_content>${imgStr}</#if> <#if text?has_content>${text}</#if><#rt/>
 <#if (linkType?has_content && "hidden-form" == linkType) || linkUrl?has_content></a><#rt/></#if>
diff --git a/themes/common-theme/template/macro/HtmlScreenMacroLibrary.ftl b/themes/common-theme/template/macro/HtmlScreenMacroLibrary.ftl
index 2d5b65e..1dc0ec1 100644
--- a/themes/common-theme/template/macro/HtmlScreenMacroLibrary.ftl
+++ b/themes/common-theme/template/macro/HtmlScreenMacroLibrary.ftl
@@ -109,8 +109,8 @@ under the License.
             <#if id?has_content>id="${id}"</#if>
             <#if style?has_content>class="${style}"</#if>
             <#if name?has_content>name="${name}"</#if>
-            <#if targetWindow?has_content>target="${targetWindow}"</#if>
-            href="<#if "hidden-form"==linkType>javascript:document.${uniqueItemName}.submit()<#else>${linkUrl}</#if>"><#rt/>
+            <#if targetWindow?has_content && "update-area" != linkType>target="${targetWindow}"</#if>
+            href="<#if "hidden-form"==linkType>javascript:document.${uniqueItemName}.submit()<#else><#if "update-area" == linkType>javascript:ajaxUpdateAreas('${linkUrl}')<#else>${linkUrl}</#if></#if>"><#rt/>
             <#if imgStr?has_content>${imgStr}</#if><#if text?has_content>${text}</#if>
         </a>
     <#else>
diff --git a/themes/rainbowstone/template/macro/HtmlMenuMacroLibrary.ftl b/themes/rainbowstone/template/macro/HtmlMenuMacroLibrary.ftl
index d0233fc..bc58a31 100644
--- a/themes/rainbowstone/template/macro/HtmlMenuMacroLibrary.ftl
+++ b/themes/rainbowstone/template/macro/HtmlMenuMacroLibrary.ftl
@@ -45,7 +45,7 @@ under the License.
         <#if text?has_content>${text}</#if></a>
     <#else>
         <#if (linkType?has_content && "hidden-form" == linkType) || linkUrl?has_content>
-        <a<#if id?has_content> id="${id}"</#if><#if style?has_content> class="${style}"</#if><#if name?has_content> name="${name}"</#if><#if targetWindow?has_content> target="${targetWindow}"</#if> href="<#if "hidden-form"==linkType>javascript:document.${uniqueItemName}.submit()<#else>${linkUrl}</#if>"><#rt/>
+        <a<#if id?has_content> id="${id}"</#if><#if style?has_content> class="${style}"</#if><#if name?has_content> name="${name}"</#if><#if targetWindow?has_content && "update-area" != linkType> target="${targetWindow}"</#if> href="<#if "hidden-form"==linkType><#if linkUrl?has_content>javascript:ajaxSubmitFormUpdateAreas('${uniqueItemName}', '${linkUrl}')<#else>javascript:document.${uniqueItemName}.submit()</#if><#else><#if "update-area" == linkType>javascript:ajaxUpdateAreas('${linkUrl [...]
         </#if>
         <#if imgStr?has_content>${imgStr}</#if><#if text?has_content>${text}</#if><#rt/>
         <#if (linkType?has_content && "hidden-form" == linkType) || linkUrl?has_content></a><#rt/></#if>