Hi Adrian,
This commit "introduced" (actually new file) a NPE in MacroMenuRenderer.java
due to
if (menuItem.getDisabled() || isDisableIfEmpty(menuItem, context)) {
target = null;
}
in case you have a <condition disabled-style="disabled"> in a menu item, like
in (this is the only case)
<menu-item name="createOrder" title="${uiLabelMap.OrderCreateOrder}">
<condition disabled-style="disabled">
<and>
<not><if-empty field="quote"/></not>
<if-compare operator="equals" value="QUO_APPROVED"
field="quote.statusId"/>
</and>
</condition>
<link target="loadCartFromQuote">
<parameter param-name="quoteId" from-field="quote.quoteId"/>
<parameter param-name="finalizeMode" value="init"/>
</link>
</menu-item>
A simple temporary workaround is to remove "disabled-style="disabled" in the
condition.
I will commit this temporary workaround, to be removed with you fix.
BTW I believe the style associated with "disabled-style="disabled" should be
upated
Thanks
Jacques
[email protected] wrote:
> Author: adrianc
> Date: Sun Aug 25 18:00:43 2013
> New Revision: 1517353
>
> URL: http://svn.apache.org/r1517353
> Log:
> Converted menu widget HTML renderer to a macro renderer.
>
> Added:
>
> ofbiz/trunk/framework/widget/src/org/ofbiz/widget/menu/MacroMenuRenderer.java
> ofbiz/trunk/framework/widget/templates/htmlMenuMacroLibrary.ftl
> Modified:
>
> ofbiz/trunk/framework/widget/src/org/ofbiz/widget/screen/MacroScreenViewHandler.java
>
> Added:
> ofbiz/trunk/framework/widget/src/org/ofbiz/widget/menu/MacroMenuRenderer.java
> URL:
> http://svn.apache.org/viewvc/ofbiz/trunk/framework/widget/src/org/ofbiz/widget/menu/MacroMenuRenderer.java?rev=1517353&view=auto
> ==============================================================================
> ---
> ofbiz/trunk/framework/widget/src/org/ofbiz/widget/menu/MacroMenuRenderer.java
> (added) +++
> ofbiz/trunk/framework/widget/src/org/ofbiz/widget/menu/MacroMenuRenderer.java
> Sun Aug 25 18:00:43 2013 @@ -0,0 +1,347 @@
> +/*******************************************************************************
> + * 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.widget.menu;
> +
> +import java.io.IOException;
> +import java.io.Reader;
> +import java.io.StringReader;
> +import java.io.StringWriter;
> +import java.math.BigDecimal;
> +import java.util.HashMap;
> +import java.util.List;
> +import java.util.Map;
> +
> +import javax.servlet.ServletContext;
> +import javax.servlet.http.HttpServletRequest;
> +import javax.servlet.http.HttpServletResponse;
> +
> +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.base.util.template.FreeMarkerWorker;
> +import org.ofbiz.webapp.control.RequestHandler;
> +import org.ofbiz.webapp.taglib.ContentUrlTag;
> +import org.ofbiz.widget.ModelWidget;
> +import org.ofbiz.widget.WidgetWorker;
> +import org.ofbiz.widget.menu.ModelMenuItem.Image;
> +import org.ofbiz.widget.menu.ModelMenuItem.Link;
> +
> +import freemarker.core.Environment;
> +import freemarker.template.Template;
> +import freemarker.template.TemplateException;
> +
> +public class MacroMenuRenderer implements MenuStringRenderer {
> +
> + public static final String module = MacroMenuRenderer.class.getName();
> + private int macroCount = 999;
> + private final Map<Appendable, Environment> environments = new
> HashMap<Appendable, Environment>();
> + private final Template macroLibrary;
> + private final HttpServletRequest request;
> + private final HttpServletResponse response;
> +
> + public MacroMenuRenderer(String macroLibraryPath, HttpServletRequest
> request, HttpServletResponse response) throws
> TemplateException, IOException { + this.macroLibrary =
> FreeMarkerWorker.getTemplate(macroLibraryPath);
> + this.request = request;
> + this.response = response;
> + }
> +
> + // Made this a separate method so it can be externalized and reused.
> + private Map<String, Object> createImageParameters(Map<String, Object>
> context, Image image) {
> + Map<String, Object> parameters = new HashMap<String, Object>();
> + parameters.put("id", image.getId(context));
> + parameters.put("style", image.getStyle(context));
> + parameters.put("width", image.getWidth(context));
> + parameters.put("height", image.getHeight(context));
> + parameters.put("border", image.getBorder(context));
> + String src = image.getSrc(context);
> + if (UtilValidate.isNotEmpty(src) && request != null && response !=
> null) {
> + String urlMode = image.getUrlMode();
> + if ("ofbiz".equalsIgnoreCase(urlMode)) {
> + boolean fullPath = false;
> + boolean secure = false;
> + boolean encode = false;
> + ServletContext ctx = (ServletContext)
> request.getAttribute("servletContext");
> + RequestHandler rh = (RequestHandler)
> ctx.getAttribute("_REQUEST_HANDLER_");
> + src = rh.makeLink(request, response, src, fullPath, secure,
> encode);
> + } else if ("content".equalsIgnoreCase(urlMode)) {
> + StringBuilder newURL = new StringBuilder();
> + ContentUrlTag.appendContentPrefix(request, newURL);
> + newURL.append(src);
> + src = newURL.toString();
> + }
> + }
> + parameters.put("src", src);
> + return parameters;
> + }
> +
> + private void executeMacro(Appendable writer, String macro) throws
> IOException, TemplateException {
> + Environment environment = getEnvironment(writer);
> + Reader templateReader = new StringReader(macro);
> + macroCount++;
> + String templateName = toString().concat("_") + macroCount;
> + Template template = new Template(templateName, templateReader,
> FreeMarkerWorker.getDefaultOfbizConfig());
> + templateReader.close();
> + environment.include(template);
> + }
> +
> + private void executeMacro(Appendable writer, String macroName,
> Map<String, Object> macroParameters) throws IOException,
> TemplateException { + StringBuilder sb = new StringBuilder("<@");
> + sb.append(macroName);
> + if (macroParameters != null) {
> + for (Map.Entry<String, Object> parameter :
> macroParameters.entrySet()) {
> + sb.append(' ');
> + sb.append(parameter.getKey());
> + sb.append("=");
> + Object value = parameter.getValue();
> + if (value instanceof String) {
> + sb.append('"');
> + sb.append(((String) value).replaceAll("\"", "\\\\\""));
> + sb.append('"');
> + } else {
> + sb.append(value);
> + }
> + }
> + }
> + sb.append(" />");
> + if (Debug.verboseOn()) {
> + Debug.logVerbose("Executing macro: " + sb, module);
> + }
> + executeMacro(writer, sb.toString());
> + }
> +
> + private Environment getEnvironment(Appendable writer) throws
> TemplateException, IOException {
> + Environment environment = environments.get(writer);
> + if (environment == null) {
> + Map<String, Object> input = UtilMisc.toMap("key", null);
> + environment = FreeMarkerWorker.renderTemplate(macroLibrary,
> input, writer);
> + environments.put(writer, environment);
> + }
> + return environment;
> + }
> +
> + private boolean isDisableIfEmpty(ModelMenuItem menuItem, Map<String,
> Object> context) {
> + boolean disabled = false;
> + String disableIfEmpty = menuItem.getDisableIfEmpty();
> + if (UtilValidate.isNotEmpty(disableIfEmpty)) {
> + List<String> keys = StringUtil.split(disableIfEmpty, "|");
> + for (String key : keys) {
> + Object obj = context.get(key);
> + if (obj == null) {
> + disabled = true;
> + break;
> + }
> + }
> + }
> + return disabled;
> + }
> +
> + private boolean isHideIfSelected(ModelMenuItem menuItem, Map<String,
> Object> context) {
> + ModelMenu menu = menuItem.getModelMenu();
> + String currentMenuItemName =
> menu.getSelectedMenuItemContextFieldName(context);
> + String currentItemName = menuItem.getName();
> + Boolean hideIfSelected = menuItem.getHideIfSelected();
> + return (hideIfSelected != null && hideIfSelected.booleanValue() &&
> currentMenuItemName != null &&
> currentMenuItemName.equals(currentItemName)); + }
> +
> + @Override
> + public void renderFormatSimpleWrapperClose(Appendable writer,
> Map<String, Object> context, ModelMenu menu) throws
> IOException { + // Nothing to do.
> + }
> +
> + @Override
> + public void renderFormatSimpleWrapperOpen(Appendable writer, Map<String,
> Object> context, ModelMenu menu) throws IOException
> { + // Nothing to do.
> + }
> +
> + @Override
> + public void renderFormatSimpleWrapperRows(Appendable writer, Map<String,
> Object> context, Object menu) throws IOException {
> + List<ModelMenuItem> menuItemList = ((ModelMenu)
> menu).getMenuItemList();
> + for (ModelMenuItem currentMenuItem : menuItemList) {
> + renderMenuItem(writer, context, currentMenuItem);
> + }
> + }
> +
> + @Override
> + public void renderImage(Appendable writer, Map<String, Object> context,
> Image image) throws IOException {
> + Map<String, Object> parameters = createImageParameters(context,
> image);
> + try {
> + executeMacro(writer, "renderImage", parameters);
> + } catch (TemplateException e) {
> + throw new IOException(e);
> + }
> + }
> +
> + @Override
> + public void renderLink(Appendable writer, Map<String, Object> context,
> Link link) throws IOException {
> + Map<String, Object> parameters = new HashMap<String, Object>();
> + String target = link.getTarget(context);
> + ModelMenuItem menuItem = link.getLinkMenuItem();
> + if (menuItem.getDisabled() || 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("targetWindow", link.getTargetWindow(context));
> + String uniqueItemName = menuItem.getModelMenu().getName() + "_" +
> menuItem.getName() + "_LF_" + UtilMisc.<String>
> addToBigDecimalInMap(context, "menuUniqueItemIndex", BigDecimal.ONE); +
> parameters.put("uniqueItemName", uniqueItemName);
> + String linkType =
> WidgetWorker.determineAutoLinkType(link.getLinkType(), target,
> link.getUrlMode(), request);
> + parameters.put("linkType", linkType);
> + String linkUrl = "";
> + String actionUrl = "";
> + StringBuilder targetParameters = new StringBuilder();
> + if ("hidden-form".equals(linkType) ||
> "ajax-window".equals(linkType)) {
> + StringBuilder sb = new StringBuilder();
> + WidgetWorker.buildHyperlinkUrl(sb, target, link.getUrlMode(),
> null, link.getPrefix(context), link.getFullPath(),
> link.getSecure(), link.getEncode(), request, response, context); +
> actionUrl = sb.toString();
> + targetParameters.append("[");
> + for (Map.Entry<String, String> parameter :
> link.getParameterMap(context).entrySet()) {
> + if (targetParameters.length() > 1) {
> + targetParameters.append(",");
> + }
> + targetParameters.append("{'name':'");
> + targetParameters.append(parameter.getKey());
> + targetParameters.append("'");
> + targetParameters.append(",'value':'");
> + targetParameters.append(parameter.getValue());
> + targetParameters.append("'}");
> + }
> + targetParameters.append("]");
> +
> + }
> + if (UtilValidate.isNotEmpty(target)) {
> + if (!"hidden-form".equals(linkType)) {
> + StringBuilder sb = new StringBuilder();
> + WidgetWorker.buildHyperlinkUrl(sb, target,
> link.getUrlMode(), link.getParameterMap(context),
> link.getPrefix(context), link.getFullPath(), link.getSecure(),
> link.getEncode(), request, response, context); +
> linkUrl = sb.toString(); + }
> + }
> + parameters.put("linkUrl", linkUrl);
> + parameters.put("actionUrl", actionUrl);
> + parameters.put("parameterList", targetParameters.toString());
> + String imgStr = "";
> + Image img = link.getImage();
> + if (img != null) {
> + StringWriter sw = new StringWriter();
> + renderImage(sw, context, img);
> + imgStr = sw.toString();
> + }
> + parameters.put("imgStr", imgStr);
> + try {
> + executeMacro(writer, "renderLink", parameters);
> + } catch (TemplateException e) {
> + throw new IOException(e);
> + }
> + }
> +
> + @Override
> + public void renderMenuClose(Appendable writer, Map<String, Object>
> context, ModelMenu menu) throws IOException {
> + Map<String, Object> parameters = null;
> + if (ModelWidget.widgetBoundaryCommentsEnabled(context)) {
> + parameters = new HashMap<String, Object>();
> + StringBuilder sb = new StringBuilder("End Menu Widget ");
> + sb.append(menu.getBoundaryCommentName());
> + parameters.put("boundaryComment", sb.toString());
> + }
> + try {
> + executeMacro(writer, "renderMenuEnd", parameters);
> + } catch (TemplateException e) {
> + throw new IOException(e);
> + }
> + }
> +
> + @Override
> + public void renderMenuItem(Appendable writer, Map<String, Object>
> context, ModelMenuItem menuItem) throws IOException {
> + if (isHideIfSelected(menuItem, context))
> + return;
> + Map<String, Object> parameters = new HashMap<String, Object>();
> + String style = menuItem.getWidgetStyle();
> + if (menuItem.isSelected(context)) {
> + style = menuItem.getSelectedStyle();
> + if (UtilValidate.isEmpty(style)) {
> + style = "selected";
> + }
> + }
> + if (menuItem.getDisabled() || this.isDisableIfEmpty(menuItem,
> context)) {
> + style = menuItem.getDisabledTitleStyle();
> + }
> + if (style == null) {
> + style = "";
> + }
> + String alignStyle = menuItem.getAlignStyle();
> + if (UtilValidate.isNotEmpty(alignStyle)) {
> + style = style.concat(" ").concat(alignStyle);
> + }
> + parameters.put("style", style);
> + parameters.put("toolTip", menuItem.getTooltip(context));
> + String linkStr = "";
> + Link link = menuItem.getLink();
> + if (link != null) {
> + StringWriter sw = new StringWriter();
> + renderLink(sw, context, link);
> + linkStr = sw.toString();
> + } else {
> + linkStr = menuItem.getTitle(context);
> + StringUtil.SimpleEncoder simpleEncoder =
> (StringUtil.SimpleEncoder) context.get("simpleEncoder");
> + if (simpleEncoder != null) {
> + linkStr = simpleEncoder.encode(linkStr);
> + }
> + }
> + parameters.put("linkStr", linkStr);
> + boolean containsNestedMenus = !menuItem.getMenuItemList().isEmpty();
> + parameters.put("containsNestedMenus", containsNestedMenus);
> + try {
> + executeMacro(writer, "renderMenuItemBegin", parameters);
> + } catch (TemplateException e) {
> + throw new IOException(e);
> + }
> + if (containsNestedMenus) {
> + for (ModelMenuItem childMenuItem : menuItem.getMenuItemList()) {
> + childMenuItem.renderMenuItemString(writer, context, this);
> + }
> + }
> + parameters.clear();
> + parameters.put("containsNestedMenus", containsNestedMenus);
> + try {
> + executeMacro(writer, "renderMenuItemEnd", parameters);
> + } catch (TemplateException e) {
> + throw new IOException(e);
> + }
> + }
> +
> + @Override
> + public void renderMenuOpen(Appendable writer, Map<String, Object>
> context, ModelMenu menu) throws IOException {
> + Map<String, Object> parameters = new HashMap<String, Object>();
> + if (ModelWidget.widgetBoundaryCommentsEnabled(context)) {
> + StringBuilder sb = new StringBuilder("Begin Menu Widget ");
> + sb.append(menu.getBoundaryCommentName());
> + parameters.put("boundaryComment", sb.toString());
> + }
> + parameters.put("id", menu.getId());
> + parameters.put("style", menu.getMenuContainerStyle(context));
> + parameters.put("title", menu.getTitle(context));
> + try {
> + executeMacro(writer, "renderMenuBegin", parameters);
> + } catch (TemplateException e) {
> + throw new IOException(e);
> + }
> + }
> +}
>
> Modified:
> ofbiz/trunk/framework/widget/src/org/ofbiz/widget/screen/MacroScreenViewHandler.java
> URL:
> http://svn.apache.org/viewvc/ofbiz/trunk/framework/widget/src/org/ofbiz/widget/screen/MacroScreenViewHandler.java?rev=1517353&r1=1517352&r2=1517353&view=diff
> ==============================================================================
> ---
> ofbiz/trunk/framework/widget/src/org/ofbiz/widget/screen/MacroScreenViewHandler.java
> (original) +++
> ofbiz/trunk/framework/widget/src/org/ofbiz/widget/screen/MacroScreenViewHandler.java
> Sun Aug 25 18:00:43 2013 @@ -38,8 +38,10 @@
> import org.ofbiz.webapp.view.AbstractVie
> import org.ofbiz.webapp.view.ViewHandlerException;
> import org.ofbiz.widget.form.FormStringRenderer;
> import org.ofbiz.widget.form.MacroFormRenderer;
> -import org.ofbiz.widget.tree.TreeStringRenderer;
> +import org.ofbiz.widget.menu.MacroMenuRenderer;
> +import org.ofbiz.widget.menu.MenuStringRenderer;
> import org.ofbiz.widget.tree.MacroTreeRenderer;
> +import org.ofbiz.widget.tree.TreeStringRenderer;
> import org.xml.sax.SAXException;
>
> import freemarker.template.TemplateException;
> @@ -90,15 +92,13 @@ public class MacroScreenViewHandler exte
> ScreenStringRenderer screenStringRenderer = new
> MacroScreenRenderer(UtilProperties.getPropertyValue("widget",
> getName() + ".name"), UtilProperties.getPropertyValue("widget",
> getName() + ".screenrenderer")); FormStringRenderer
> formStringRenderer = new
> MacroFormRenderer(UtilProperties.getPropertyValue("widget", getName() +
> ".formrenderer"),
> request, response); TreeStringRenderer treeStringRenderer = new
> MacroTreeRenderer(UtilProperties.getPropertyValue("widget",
> getName() + ".treerenderer"), writer);
> - // TODO: uncomment these lines when the renderers are implemented
> - //MenuStringRenderer menuStringRenderer = new
> MacroMenuRenderer(UtilProperties.getPropertyValue("widget", getName()
> + ".menurenderer"), writer); + MenuStringRenderer
> menuStringRenderer = new
> MacroMenuRenderer(UtilProperties.getPropertyValue("widget", getName() +
> ".menurenderer"), request, response);
>
> ScreenRenderer screens = new ScreenRenderer(writer, null,
> screenStringRenderer);
> screens.populateContextForRequest(request, response,
> servletContext);
> - // this is the object used to render forms from their definitions
> screens.getContext().put("formStringRenderer",
> formStringRenderer);
> screens.getContext().put("treeStringRenderer",
> treeStringRenderer);
> - //screens.getContext().put("menuStringRenderer",
> menuStringRenderer);
> + screens.getContext().put("menuStringRenderer",
> menuStringRenderer);
> screens.getContext().put("simpleEncoder",
> StringUtil.getEncoder(UtilProperties.getPropertyValue("widget", getName() +
> ".encoder"))); screenStringRenderer.renderScreenBegin(writer,
> screens.getContext());
> screens.render(page);
>
> Added: ofbiz/trunk/framework/widget/templates/htmlMenuMacroLibrary.ftl
> URL:
> http://svn.apache.org/viewvc/ofbiz/trunk/framework/widget/templates/htmlMenuMacroLibrary.ftl?rev=1517353&view=auto
> ==============================================================================
> --- ofbiz/trunk/framework/widget/templates/htmlMenuMacroLibrary.ftl (added)
> +++ ofbiz/trunk/framework/widget/templates/htmlMenuMacroLibrary.ftl Sun Aug
> 25 18:00:43 2013
> @@ -0,0 +1,72 @@
> +<#--
> +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.
> +-->
> +
> +<#macro renderMenuBegin boundaryComment id style title>
> + <#if boundaryComment?has_content>
> +<!-- ${boundaryComment} -->
> + </#if>
> + <div<#if id?has_content> id="${id}"</#if><#if style?has_content>
> class="${style}"</#if>>
> + <#if title?has_content>
> + <h2>${title}</h2>
> + </#if>
> + <ul>
> + <li>
> + <ul>
> +</#macro>
> +
> +<#macro renderMenuEnd boundaryComment>
> + </ul>
> + </li>
> + </ul>
> + <br class="clear"/>
> + </div>
> + <#if boundaryComment?has_content>
> +<!-- ${boundaryComment} -->
> + </#if>
> +</#macro>
> +
> +<#macro renderImage src id style width height border>
> + <img src="${src}"<#if id?has_content> id="${id}"</#if><#if
> style?has_content> class="${style}"</#if><#if width?has_content>
> width="${width}"</#if><#if height?has_content> height="${height}"</#if><#if
> border?has_content> border="${border}"</#if> />
> +</#macro> +
> +<#macro renderLink linkType linkUrl parameterList targetWindow
> uniqueItemName actionUrl id="" style="" name="" height=""
> width="" text="" imgStr=""> + <#if "hidden-form" == linkType>
> + <form method="post" action="${actionUrl}"<#if targetWindow?has_content>
> target="${targetWindow}"</#if>
> onsubmit="javascript:submitFormDisableSubmits(this)"
> name="${uniqueItemName}"><#rt/> + <#list parameterList as parameter>
> + <input name="${parameter.name}" value="${parameter.value}"
> type="hidden"/><#rt/>
> + </#list>
> + </form><#rt/>
> + </#if>
> + <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>"><#if
> imgStr?has_content>${imgStr}</#if><#if text?has_content>${text}</#if></a>
> +</#macro> + +<#macro renderMenuItemBegin style toolTip
> linkStr containsNestedMenus> + <li<#if style?has_content>
> class="${style}"</#if><#if toolTip?has_content> title="${title}"</#if>>
> + <#if linkStr?has_content>${linkStr}</#if>
> + <#if containsNestedMenus>
> + <ul>
> + </#if>
> +</#macro>
> +
> +<#macro renderMenuItemEnd containsNestedMenus>
> + <#if containsNestedMenus>
> + </ul>
> + </#if>
> + </li>
> +</#macro>