/* This file is part of Cyclos (www.cyclos.org). A project of the Social Trade Organisation (www.socialtrade.org). Cyclos is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Cyclos is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Cyclos; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package nl.strohalm.cyclos.taglibs; import java.io.IOException; import java.util.ArrayList; import java.util.List; import javax.servlet.jsp.JspException; import javax.servlet.jsp.JspWriter; import javax.servlet.jsp.PageContext; import javax.servlet.jsp.tagext.TagSupport; import nl.strohalm.cyclos.access.Module; import nl.strohalm.cyclos.access.Permission; import nl.strohalm.cyclos.services.permissions.PermissionService; import nl.strohalm.cyclos.utils.MessageHelper; import nl.strohalm.cyclos.utils.SpringHelper; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang.ObjectUtils; import org.apache.commons.lang.StringEscapeUtils; import org.apache.commons.lang.StringUtils; /** * Taglib for menu rendering * @author luis */ public class MenuTag extends TagSupport { /** * A menu item bean, containing data to render a menu * @author luis */ private static class Menu { private final String url; private final String label; private final String confirmationKey; private Menu parent; private List<Menu> children; public Menu(final String label, final String url, final String confirmationKey) { this.label = label; this.url = url; this.confirmationKey = confirmationKey; } public void addChild(final Menu child) { if (child != null) { child.parent = this; if (children == null) { children = new ArrayList<Menu>(); } children.add(child); } } public List<Menu> getChildren() { return children; } public String getConfirmationKey() { return confirmationKey; } public String getLabel() { return label; } public Menu getParent() { return parent; } public String getUrl() { return url; } public boolean hasContent() { return url != null || children != null; } public boolean isNested() { return parent != null; } } private static final long serialVersionUID = 1447606721801461983L; private String confirmationKey; private String key; private String url; private Module module; private Permission permission; private String label; private PermissionService permissionService; private Menu menu; private MessageHelper messageHelper; @Override public int doEndTag() throws JspException { try { // Rendering is done by the topmost tag. If this is nested, do nothing // Also, render something only if there is some content if (menu == null || menu.isNested() || !menu.hasContent()) { return EVAL_PAGE; } final JspWriter out = pageContext.getOut(); // Render this parent menu final int index = index(); final String divId = divId(menu, index); renderDiv(menu, index, divId); // Render each submenu final List<Menu> subMenus = menu.getChildren(); if (CollectionUtils.isNotEmpty(subMenus)) { out.print("<ul id='subMenuContainer" + index + "' class='subMenuContainer' style='display:none'>"); final int subMenuCount = subMenus.size(); for (int i = 0; i < subMenuCount; i++) { final Menu subMenu = subMenus.get(i); final String subMenuId = divId(subMenu, i); renderDiv(subMenu, i, subMenuId); if (i == 0) { out.println("<script>$('" + subMenuId + "').addClassName('firstSubMenu');</script>"); } else if (i == subMenuCount - 1) { out.println("<script>$('" + subMenuId + "').addClassName('lastSubMenu');</script>"); } } out.println("</ul></li>"); } out.println(); out.println("<script>allMenus.push($('" + divId + "'));</script>"); return EVAL_PAGE; } catch (final IOException e) { throw new JspException(e); } finally { release(); } } @Override public int doStartTag() throws JspException { // If there is a permission check, verify it boolean granted = true; if (permission != null) { granted = permissionService.hasPermission(permission); } else if (module != null) { granted = permissionService.hasPermission(module); } // If the permission is not granted, don't eval the tag if (!granted) { return EVAL_PAGE; } // Build the menu bean if (StringUtils.isEmpty(label) && StringUtils.isNotEmpty(key)) { label = messageHelper.message(key); } menu = new Menu(label, url, confirmationKey); // Find the parent tag, if any final MenuTag parentTag = (MenuTag) findAncestorWithClass(this, MenuTag.class); if (parentTag != null) { // Nest the menu beans final Menu parentMenu = parentTag.getMenu(); if (parentMenu != null) { parentMenu.addChild(menu); } else { // The parent tag does not have permission - ignore this menu too menu = null; } } return EVAL_BODY_INCLUDE; } public String getConfirmationKey() { return confirmationKey; } public String getKey() { return key; } public String getLabel() { return label; } public Menu getMenu() { return menu; } public Module getModule() { return module; } public Permission getPermission() { return permission; } public String getUrl() { return url; } @Override public void release() { label = null; key = null; url = null; module = null; permission = null; confirmationKey = null; menu = null; super.release(); } public void setConfirmationKey(final String confirmationKey) { this.confirmationKey = confirmationKey; } public void setKey(final String key) { this.key = key; } public void setLabel(final String displayName) { label = displayName; } public void setModule(final Module module) { this.module = module; } @Override public void setPageContext(final PageContext pageContext) { super.setPageContext(pageContext); permissionService = SpringHelper.bean(pageContext.getServletContext(), PermissionService.class); messageHelper = SpringHelper.bean(pageContext.getServletContext(), MessageHelper.class); } public void setPermission(final Permission permission) { this.permission = permission; } public void setUrl(final String url) { this.url = url; } private String divId(final Menu menu, final int index) { if (menu.isNested()) { return "submenu" + pageContext.getAttribute("menuIndex") + "." + index; } else { return "menu" + index; } } private String getClassName(final Menu menu) { return (menu.isNested() ? "subMenu" : "menu"); } private int index() { int index = (Integer) ObjectUtils.defaultIfNull(pageContext.getAttribute("menuIndex"), -1); index++; pageContext.setAttribute("menuIndex", index); return index; } /** * Render the current div */ private void renderDiv(final Menu menu, final int index, final String divId) throws IOException, JspException { final JspWriter out = pageContext.getOut(); final String className = getClassName(menu); out.print("<li id='" + divId + "'class='" + className + "'"); String url = menu.getUrl(); if (StringUtils.isNotEmpty(url)) { url = StringEscapeUtils.escapeHtml(url); if (url.contains("?")) { url += "&"; } else { url += "?"; } url += "fromMenu=true"; out.print(" linkURL=\"" + url + "\""); final String confirmationKey = menu.getConfirmationKey(); if (StringUtils.isNotEmpty(confirmationKey)) { out.print(" confirmationMessage=\"" + StringEscapeUtils.escapeHtml(messageHelper.message(confirmationKey)) + "\""); } } out.print(">"); out.print("<span class=\"" + className + "Bullet\"></span>"); out.print("<span class=\"" + className + "Text\">"); final String displayName = menu.getLabel(); out.print(EscapeHTMLTag.escape(displayName, true)); out.println("</span>"); if (menu.getParent() == null) { out.println("<script>menuCount++;</script>"); } } }