/******************************************************************************* * 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.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.apache.ofbiz.base.util.Debug; import org.apache.ofbiz.base.util.StringUtil; import org.apache.ofbiz.base.util.UtilCodec; import org.apache.ofbiz.base.util.UtilMisc; import org.apache.ofbiz.base.util.UtilValidate; import org.apache.ofbiz.base.util.template.FreeMarkerWorker; import org.apache.ofbiz.webapp.control.RequestHandler; import org.apache.ofbiz.webapp.taglib.ContentUrlTag; import org.apache.ofbiz.widget.WidgetWorker; import org.apache.ofbiz.widget.model.CommonWidgetModels.Image; import org.apache.ofbiz.widget.model.ModelMenu; import org.apache.ofbiz.widget.model.ModelMenuItem; import org.apache.ofbiz.widget.model.ModelMenuItem.MenuLink; import org.apache.ofbiz.widget.model.ModelWidget; import org.apache.ofbiz.widget.renderer.MenuStringRenderer; 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, MenuLink link) throws IOException { Map<String, Object> parameters = new HashMap<String, Object>(); 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)); StringBuffer uniqueItemName = new StringBuffer(menuItem.getModelMenu().getName()); uniqueItemName.append("_").append(menuItem.getName()).append("_LF_").append(UtilMisc.<String> addToBigDecimalInMap(context, "menuUniqueItemIndex", BigDecimal.ONE)); if (menuItem.getModelMenu().getExtraIndex(context) != null) { uniqueItemName.append("_").append(menuItem.getModelMenu().getExtraIndex(context)); } if (context.containsKey("itemIndex")) { if (context.containsKey("parentItemIndex")) { uniqueItemName.append(context.get("parentItemIndex")).append("_").append(context.get("itemIndex")); } else { uniqueItemName.append("_").append(context.get("itemIndex")); } } parameters.put("uniqueItemName", uniqueItemName.toString()); String linkType = ""; if (UtilValidate.isNotEmpty(target)) { 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) || "layered-modal".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 (targetParameters.length() == 0) { targetParameters.append("\"\""); } if (UtilValidate.isNotEmpty(target)) { if (!"hidden-form".equals(linkType)) { StringBuilder sb = new StringBuilder(); WidgetWorker.buildHyperlinkUrl(sb, target, link.getUrlMode(), "layered-modal".equals(linkType)?null: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); 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)) { String selectedStyle = menuItem.getSelectedStyle(); if (UtilValidate.isEmpty(selectedStyle)) { selectedStyle = "selected"; } if (UtilValidate.isNotEmpty(style)) { style += " " ; } style += selectedStyle ; } if (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 = ""; MenuLink link = menuItem.getLink(); if (link != null) { StringWriter sw = new StringWriter(); renderLink(sw, context, link); linkStr = sw.toString(); } else { linkStr = menuItem.getTitle(context); UtilCodec.SimpleEncoder simpleEncoder = (UtilCodec.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); } } }