实现基于FTL模板的菜单之一
有前面我们提到过,在OFBIZ中form和screen以及树形目录,都是可以通过xml来定义,再通过ftl的模板来生成的,但是菜单却不可以这样。虽然我们也可以在xml文件中配置我们的菜单。但是转换为html时是在HtmlMenuRenderer通过在JAVA代码在硬编码实现的。从OFBIZ代码我们可以知道在未来,OFBIZ会支持FTL模板,但是到到目前为止(12.04版本)还没有实现功能。那么我们可不可以自己动手来实现这个模板呢?答案是肯定的。 思路是通过在java 代码中生成对ftl宏的调用。然后在ftl模板中定义这些宏即可。最后让OFBIZ加载我们的菜单模板即可。 首先们需要创建一个建一个新的类并且实现MenuStringRenderer。这个类就叫作 MacroMenuRenderer 吧。 实现的代码可以是如下。 /** * Widget Library - Form Renderer implementation based on Freemarker macros * */ public class MacroMenuRenderer implements MenuStringRenderer { public static final String module = MacroTreeRenderer.class.getName(); protected HttpServletRequest request; protected String userLoginIdAtPermGrant; protected boolean userLoginIdHasChanged = true; private Template macroLibrary; private WeakHashMap<Appendable, Environment> environments = new WeakHashMap<Appendable, Environment>(); protected boolean widgetCommentsEnabled = false; public MacroMenuRenderer(String macroLibraryPath, Appendable writer,HttpServletRequest request) throws TemplateException, IOException { this.macroLibrary = FreeMarkerWorker.getTemplate(macroLibraryPath); this.request = request; } private void executeMacro(Appendable writer, String macro) throws IOException { try { Environment environment = getEnvironment(writer); Reader templateReader = new StringReader(macro); // FIXME: I am using a Date as an hack to provide a unique name for the template... Template template = new Template((new java.util.Date()).toString(), templateReader, FreeMarkerWorker.getDefaultGorun8Config()); templateReader.close(); environment.include(template); } catch (TemplateException e) { Debug.logError(e, "Error rendering screen macro [" + macro + "] thru ftl", module); } catch (IOException e) { Debug.logError(e, "Error rendering screen macro [" + macro + "] thru ftl", module); } } private void executeMacro(Appendable writer, String macroName, Map<String, Object> parameters) throws IOException { StringBuilder sb = new StringBuilder("<@"); sb.append(macroName); if (parameters != null) { for (Map.Entry<String, Object> parameter : parameters.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(" />"); 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; } public void renderFormatSimpleWrapperRows(Appendable writer, Map<String, Object> context, Object menuObj) throws IOException { List<ModelMenuItem> menuItemList = ((ModelMenu) menuObj).getMenuItemList(); for (ModelMenuItem currentMenuItem: menuItemList) { renderMenuItem(writer, context, currentMenuItem); } } public void renderMenuItem(Appendable writer, Map<String, Object> context, ModelMenuItem menuItem) throws IOException { //Debug.logInfo("in renderMenuItem, menuItem:" + menuItem.getName() + " context:" + context ,""); boolean hideThisItem = isHideIfSelected(menuItem, context); if (hideThisItem) { return; } String style = menuItem.getWidgetStyle(); if (menuItem.isSelected(context)) { style = menuItem.getSelectedStyle(); if (UtilValidate.isEmpty(style)) { style = "selected"; } } String name = menuItem.getName(); if (menuItem.getDisabled() || this.isDisableIfEmpty(menuItem, context)) { style = menuItem.getDisabledTitleStyle(); } String alignStyle = menuItem.getAlignStyle(); String toolTip = menuItem.getTooltip(context); Link link = menuItem.getLink(); StringWriter sr = new StringWriter(); sr.append("<@renderMenuItemOpen "); sr.append(" style=\""); sr.append(style); sr.append("\" alignStyle=\""); sr.append(alignStyle); sr.append("\" toolTip=\""); sr.append(toolTip); sr.append("\" name=\""); sr.append(name); sr.append("\" />"); // executeMacro(writer, sr.toString()); if (link != null) { renderLink(writer, context, link); } else { String txt = menuItem.getTitle(context); StringUtil.SimpleEncoder simpleEncoder = (StringUtil.SimpleEncoder) context.get("simpleEncoder"); if (simpleEncoder != null) { txt = simpleEncoder.encode(txt); } //sr = new StringWriter(); sr.append("<@renderMenuItemText "); sr.append(" text=\""); sr.append(txt); sr.append("\" />"); //executeMacro(writer, sr.toString()); } if (!menuItem.getMenuItemList().isEmpty()) { //sr = new StringWriter(); sr.append("<@renderSubMenuOpen/> "); // executeMacro(writer, sr.toString()); for (ModelMenuItem childMenuItem : menuItem.getMenuItemList()) { childMenuItem.renderMenuItemString(writer, context, this); } //sr = new StringWriter(); sr.append("<@renderSubMenuClose/>"); //executeMacro(writer, sr.toString()); } //sr = new StringWriter(); sr.append("<@renderMenuItemClose/>"); executeMacro(writer, sr.toString()); } public 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; } /** * Renders the beginning boundary comment string. * @param writer The writer to write to * @param widgetType The widget type: "Screen Widget", "Form Widget", etc. * @param modelWidget The widget */ public void renderBeginningBoundaryComment(Appendable writer, String widgetType, ModelWidget modelWidget) throws IOException { if (this.widgetCommentsEnabled) { StringWriter sr = new StringWriter(); sr.append("<@formatBoundaryComment "); sr.append(" boundaryType=\""); sr.append("Begin"); sr.append("\" widgetType=\""); sr.append(widgetType); sr.append("\" widgetName=\""); sr.append(modelWidget.getBoundaryCommentName()); sr.append("\" />"); executeMacro(writer, sr.toString()); } } /** * Renders the ending boundary comment string. * @param writer The writer to write to * @param widgetType The widget type: "Screen Widget", "Form Widget", etc. * @param modelWidget The widget */ public void renderEndingBoundaryComment(Appendable writer, String widgetType, ModelWidget modelWidget) throws IOException { if (this.widgetCommentsEnabled) { StringWriter sr = new StringWriter(); sr.append("<@formatBoundaryComment "); sr.append(" boundaryType=\""); sr.append("End"); sr.append("\" widgetType=\""); sr.append(widgetType); sr.append("\" widgetName=\""); sr.append(modelWidget.getBoundaryCommentName()); sr.append("\" />"); executeMacro(writer, sr.toString()); } } public void renderMenuOpen(Appendable writer, Map<String, Object> context, ModelMenu modelMenu) throws IOException { if (!userLoginIdHasChanged) { userLoginIdHasChanged = userLoginIdHasChanged(); } this.widgetCommentsEnabled = ModelWidget.widgetBoundaryCommentsEnabled(context); renderBeginningBoundaryComment(writer, "Menu Widget", modelMenu); String menuId = modelMenu.getId(); String menuContainerStyle = modelMenu.getMenuContainerStyle(context); String menuWidth = modelMenu.getMenuWidth(); String menuTitle = modelMenu.getTitle(context); int menuItemCount = modelMenu.renderedMenuItemCount(context); StringWriter sr = new StringWriter(); sr.append("<@renderMenuOpen "); sr.append(" menuId=\""); sr.append(menuId); sr.append("\" containerStyle=\""); sr.append(menuContainerStyle); sr.append("\" menuWidth=\""); sr.append(menuWidth); sr.append("\" menuTitle=\""); sr.append(menuTitle); sr.append("\" menuItemCount=\""); sr.append(String.valueOf(menuItemCount)); sr.append("\" />"); executeMacro(writer, sr.toString()); } /* (non-Javadoc) * @see cn.gorun8.widget.menu.MenuStringRenderer#renderMenuClose(java.io.Writer, java.util.Map, cn.gorun8.widget.menu.ModelMenu) */ public void renderMenuClose(Appendable writer, Map<String, Object> context, ModelMenu modelMenu) throws IOException { // TODO: div can't be directly inside an UL String fillStyle = modelMenu.getFillStyle(); int menuItemCount = modelMenu.renderedMenuItemCount(context); renderEndingBoundaryComment(writer, "Menu Widget", modelMenu); StringWriter sr = new StringWriter(); sr.append("<@renderMenuClose "); sr.append(" fillStyle=\""); sr.append(fillStyle); sr.append("\" menuItemCount=\""); sr.append(String.valueOf(menuItemCount)); sr.append("\" />"); executeMacro(writer, sr.toString()); userLoginIdHasChanged = userLoginIdHasChanged(); GenericValue userLogin = (GenericValue)request.getSession().getAttribute("userLogin"); if (userLogin != null) { String userLoginId = userLogin.getString("userLoginId"); //request.getSession().setAttribute("userLoginIdAtPermGrant", userLoginId); setUserLoginIdAtPermGrant(userLoginId); //Debug.logInfo("in HtmlMenuRenderer, userLoginId(Close):" + userLoginId + " userLoginIdAtPermGrant:" + request.getSession().getAttribute("userLoginIdAtPermGrant"),""); } else { request.getSession().setAttribute("userLoginIdAtPermGrant", null); } } public void renderFormatSimpleWrapperOpen(Appendable writer, Map<String, Object> context, ModelMenu modelMenu) throws IOException { //appendWhitespace(writer); } public void renderFormatSimpleWrapperClose(Appendable writer, Map<String, Object> context, ModelMenu modelMenu) throws IOException { //appendWhitespace(writer); } /** * @param string */ public void setUserLoginIdAtPermGrant(String string) { //Debug.logInfo("in HtmlMenuRenderer, userLoginIdAtPermGrant(setUserLoginIdAtPermGrant):" + string,""); this.userLoginIdAtPermGrant = string; } public String getUserLoginIdAtPermGrant() { return this.userLoginIdAtPermGrant; } public 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(); //Debug.logInfo("in HtmlMenuRenderer, currentMenuItemName:" + currentMenuItemName + " currentItemName:" + currentItemName + " hideIfSelected:" + hideIfSelected,""); return (hideIfSelected != null && hideIfSelected.booleanValue() && currentMenuItemName != null && currentMenuItemName.equals(currentItemName)); } public boolean userLoginIdHasChanged() { boolean hasChanged = false; GenericValue userLogin = (GenericValue)request.getSession().getAttribute("userLogin"); userLoginIdAtPermGrant = getUserLoginIdAtPermGrant(); //userLoginIdAtPermGrant = (String)request.getSession().getAttribute("userLoginIdAtPermGrant"); String userLoginId = null; if (userLogin != null) userLoginId = userLogin.getString("userLoginId"); //Debug.logInfo("in HtmlMenuRenderer, userLoginId:" + userLoginId + " userLoginIdAtPermGrant:" + userLoginIdAtPermGrant ,""); if ((userLoginId == null && userLoginIdAtPermGrant != null) || (userLoginId != null && userLoginIdAtPermGrant == null) || ((userLoginId != null && userLoginIdAtPermGrant != null) && !userLoginId.equals(userLoginIdAtPermGrant))) { hasChanged = true; } else { if (userLoginIdAtPermGrant != null) hasChanged = true; else hasChanged = false; userLoginIdAtPermGrant = null; } return hasChanged; } public void setUserLoginIdHasChanged(boolean b) { userLoginIdHasChanged = b; } public String getTitle(ModelMenuItem menuItem, Map<String, Object> context) { String title = null; title = menuItem.getTitle(context); return title; } public void renderLink(Appendable writer, Map<String, Object> context, ModelMenuItem.Link link) throws IOException { String target = link.getTarget(context); ModelMenuItem menuItem = link.getLinkMenuItem(); if (menuItem.getDisabled() || isDisableIfEmpty(menuItem, context)) { target = null; } HttpServletResponse response = (HttpServletResponse) context.get("response"); //HttpServletRequest request = (HttpServletRequest) context.get("request"); String targetWindow = link.getTargetWindow(context); String uniqueItemName = menuItem.getModelMenu().getName() + "_" + menuItem.getName() + "_LF_" + UtilMisc.<String>addToBigDecimalInMap(context, "menuUniqueItemIndex", BigDecimal.ONE); String linkType = WidgetWorker.determineAutoLinkType(link.getLinkType(), target, link.getUrlMode(), request); String id = link.getId(context); String style = link.getStyle(context); String name = link.getName(context); String confirmationMsg = link.getConfirmation(context); String text = link.getText(context); String imgid =""; String imgstyle =""; String imgwid = ""; String imghgt = ""; String imgborder = ""; String imgsrc = ""; StringBuffer tmpbuf = new StringBuffer(); StringBufferWriter strWriter = new StringBufferWriter(tmpbuf); WidgetWorker.buildHyperlinkUrl(strWriter, target, link.getUrlMode(), null, link.getPrefix(context), link.getFullPath(), link.getSecure(), link.getEncode(), request, response, context); String hyperlinkUrl = strWriter.toString(); Image image = link.getImage(); if (image != null) { imgid = image.getId(context); imgstyle = image.getStyle(context); imgwid = image.getWidth(context); imghgt = image.getHeight(context); imgborder = image.getBorder(context); imgsrc = image.getSrc(context); imgid = UtilValidate.isNotEmpty(imgid)?imgid:""; imgstyle = UtilValidate.isNotEmpty(imgstyle)?imgstyle:""; imgwid = UtilValidate.isNotEmpty(imgwid)?imgwid:""; imghgt = UtilValidate.isNotEmpty(imghgt)?imghgt:""; imgborder = UtilValidate.isNotEmpty(imgborder)?imgborder:""; imgsrc = UtilValidate.isNotEmpty(imgsrc)?imgsrc:""; } StringWriter sr = new StringWriter(); sr.append("<@renderLink "); sr.append(" targetWindow=\""); sr.append( targetWindow); sr.append("\" uniqueItemName=\""); sr.append( uniqueItemName); sr.append("\" linkType=\""); sr.append( linkType); sr.append("\" id=\""); sr.append( id); sr.append("\" style=\""); sr.append( style); sr.append("\" name=\""); sr.append( name); sr.append("\" confirmationMsg=\""); sr.append( confirmationMsg); sr.append("\" imgid=\""); sr.append( imgid); sr.append("\" imgstyle=\""); sr.append( imgstyle); sr.append("\" imgwid=\""); sr.append( imgwid); sr.append("\" imghgt=\""); sr.append( imghgt); sr.append("\" imgborder=\""); sr.append( imgborder); sr.append("\" imgsrc=\""); sr.append( imgsrc); sr.append("\" text=\""); sr.append( text); sr.append("\" hyperlinkUrl=\""); sr.append( hyperlinkUrl); sr.append("\" />"); executeMacro(writer, sr.toString()); } public void renderImage(Appendable writer, Map<String, Object> context, ModelMenuItem.Image image) throws IOException { // open tag writer.append("<img "); String id = image.getId(context); if (UtilValidate.isNotEmpty(id)) { writer.append(" id=\""); writer.append(id); writer.append("\""); } String style = image.getStyle(context); if (UtilValidate.isNotEmpty(style)) { writer.append(" class=\""); writer.append(style); writer.append("\""); } String wid = image.getWidth(context); if (UtilValidate.isNotEmpty(wid)) { writer.append(" width=\""); writer.append(wid); writer.append("\""); } String hgt = image.getHeight(context); if (UtilValidate.isNotEmpty(hgt)) { writer.append(" height=\""); writer.append(hgt); writer.append("\""); } String border = image.getBorder(context); if (UtilValidate.isNotEmpty(border)) { writer.append(" border=\""); writer.append(border); writer.append("\""); } String src = image.getSrc(context); if (UtilValidate.isNotEmpty(src)) { writer.append(" src=\""); String urlMode = image.getUrlMode(); boolean fullPath = false; boolean secure = false; boolean encode = false; HttpServletResponse response = (HttpServletResponse) context.get("response"); HttpServletRequest request = (HttpServletRequest) context.get("request"); if (urlMode != null && urlMode.equalsIgnoreCase("gorun8")) { if (request != null && response != null) { ServletContext ctx = (ServletContext) request.getAttribute("servletContext"); RequestHandler rh = (RequestHandler) ctx.getAttribute("_REQUEST_HANDLER_"); String urlString = rh.makeLink(request, response, src, fullPath, secure, encode); writer.append(urlString); } else { writer.append(src); } } else if (urlMode != null && urlMode.equalsIgnoreCase("content")) { if (request != null && response != null) { StringBuilder newURL = new StringBuilder(); ContentUrlTag.appendContentPrefix(request, newURL); newURL.append(src); writer.append(newURL.toString()); } } else { writer.append(src); } writer.append("\""); } writer.append("/>"); } } 明天我们将讲如何定义FTL模板。 |