Wed, 13 May 2020 21:10:23 +0200
simplifies menu generation, adds submenus and removes VersionsModule (versions will be part of the ProjectsModule)
--- a/src/main/java/de/uapcore/lightpit/AbstractLightPITServlet.java Wed May 13 18:55:05 2020 +0200 +++ b/src/main/java/de/uapcore/lightpit/AbstractLightPITServlet.java Wed May 13 21:10:23 2020 +0200 @@ -71,6 +71,8 @@ */ private final Map<HttpMethod, Map<String, Method>> mappings = new HashMap<>(); + private final List<MenuEntry> subMenu = new ArrayList<>(); + /** * Gives implementing modules access to the {@link ModuleManager}. * @@ -174,6 +176,14 @@ ); } + final var menuKey = mapping.get().menuKey(); + if (!menuKey.isBlank()) { + subMenu.add(new MenuEntry( + new ResourceKey(moduleInfo.getBundleBaseName(), menuKey), + moduleInfo.getModulePath() + requestPath, + mapping.get().menuSequence())); + } + LOG.debug("{} {} maps to {}::{}", mapping.get().method(), requestPath, @@ -234,13 +244,16 @@ throws IOException, ServletException { req.setAttribute(Constants.REQ_ATTR_MENU, getModuleManager().getMainMenu()); + req.setAttribute(Constants.REQ_ATTR_SUB_MENU, subMenu); req.getRequestDispatcher(SITE_JSP).forward(req, resp); } + private String sanitizeRequestPath(HttpServletRequest req) { + return Optional.ofNullable(req.getPathInfo()).orElse("/"); + } + private Optional<Method> findMapping(HttpMethod method, HttpServletRequest req) { - return Optional.ofNullable(mappings.get(method)) - .map(rm -> rm.get(Optional.ofNullable(req.getPathInfo()).orElse("/")) - ); + return Optional.ofNullable(mappings.get(method)).map(rm -> rm.get(sanitizeRequestPath(req))); } private void forwardAsSpecified(ResponseType type, HttpServletRequest req, HttpServletResponse resp) @@ -275,7 +288,6 @@ // set some internal request attributes req.setAttribute(Constants.REQ_ATTR_PATH, Functions.fullPath(req)); - req.setAttribute(Constants.REQ_ATTR_MODULE_CLASSNAME, this.getClass().getName()); Optional.ofNullable(moduleInfo).ifPresent((proxy) -> req.setAttribute(Constants.REQ_ATTR_MODULE_INFO, proxy)); // obtain a connection and create the data access objects
--- a/src/main/java/de/uapcore/lightpit/Constants.java Wed May 13 18:55:05 2020 +0200 +++ b/src/main/java/de/uapcore/lightpit/Constants.java Wed May 13 21:10:23 2020 +0200 @@ -38,8 +38,6 @@ public final class Constants { public static final String JSP_PATH_PREFIX = "/WEB-INF/jsp/"; - public static final String JSPF_PATH_PREFIX = "/WEB-INF/jspf/"; - public static final String DYN_FRAGMENT_PATH_PREFIX = "/WEB-INF/dynamic_fragments/"; @@ -64,11 +62,6 @@ public static final String CTX_ATTR_DB_DIALECT = "db-dialect"; /** - * Key for the request attribute containing the class name of the currently dispatching module. - */ - public static final String REQ_ATTR_MODULE_CLASSNAME = fqn(AbstractLightPITServlet.class, "moduleClassname"); - - /** * Key for the request attribute containing the {@link LightPITModule} information of the currently dispatching module. */ public static final String REQ_ATTR_MODULE_INFO = fqn(AbstractLightPITServlet.class, "moduleInfo"); @@ -79,6 +72,11 @@ public static final String REQ_ATTR_MENU = fqn(AbstractLightPITServlet.class, "mainMenu"); /** + * Key for the request attribute containing the sub menu list. + */ + public static final String REQ_ATTR_SUB_MENU = fqn(AbstractLightPITServlet.class, "subMenu"); + + /** * Key for the request attribute containing the full path information (servlet path + path info). */ public static final String REQ_ATTR_PATH = fqn(AbstractLightPITServlet.class, "path");
--- a/src/main/java/de/uapcore/lightpit/Functions.java Wed May 13 18:55:05 2020 +0200 +++ b/src/main/java/de/uapcore/lightpit/Functions.java Wed May 13 21:10:23 2020 +0200 @@ -54,10 +54,6 @@ return enforceExt(Constants.JSP_PATH_PREFIX + filename, ".jsp"); } - public static String jspfPath(String filename) { - return enforceExt(Constants.JSPF_PATH_PREFIX + filename, ".jspf"); - } - public static String dynFragmentPath(String filename) { return enforceExt(Constants.DYN_FRAGMENT_PATH_PREFIX + filename, ".jsp"); }
--- a/src/main/java/de/uapcore/lightpit/MenuEntry.java Wed May 13 18:55:05 2020 +0200 +++ b/src/main/java/de/uapcore/lightpit/MenuEntry.java Wed May 13 21:10:23 2020 +0200 @@ -40,11 +40,6 @@ public class MenuEntry implements Comparable<MenuEntry> { /** - * Class name of the module for which this menu is built. - */ - private final String moduleClassName; - - /** * Resource key for the menu label. */ private final ResourceKey resourceKey; @@ -59,17 +54,12 @@ */ private final int sequence; - public MenuEntry(String moduleClassName, ResourceKey resourceKey, String pathName, int sequence) { - this.moduleClassName = moduleClassName; + public MenuEntry(ResourceKey resourceKey, String pathName, int sequence) { this.resourceKey = resourceKey; this.pathName = pathName; this.sequence = sequence; } - public String getModuleClassName() { - return moduleClassName; - } - public ResourceKey getResourceKey() { return resourceKey; }
--- a/src/main/java/de/uapcore/lightpit/ModuleManager.java Wed May 13 18:55:05 2020 +0200 +++ b/src/main/java/de/uapcore/lightpit/ModuleManager.java Wed May 13 21:10:23 2020 +0200 @@ -36,7 +36,10 @@ import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import javax.servlet.annotation.WebListener; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Optional; /** * Scans registered servlets for LightPIT modules. @@ -55,7 +58,7 @@ /** * Maps class names to module information. */ - private final Map<String, LightPITModule> registeredModules = new HashMap<>(); + private final List<LightPITModule> registeredModules = new ArrayList<>(); /** * Contains the menu entries for the loaded modules. @@ -114,7 +117,7 @@ private void handleServletRegistration(String name, Registration reg) { final Optional<LightPITModule> moduleInfo = getModuleInfo(reg); if (moduleInfo.isPresent()) { - registeredModules.put(reg.getClassName(), moduleInfo.get()); + registeredModules.add(moduleInfo.get()); LOG.info("Module detected: {}", name); } else { LOG.debug("Servlet {} is no module, skipping.", name); @@ -145,16 +148,15 @@ */ private void createMainMenu() { mainMenu.clear(); - registeredModules.entrySet() + registeredModules .stream() - .filter(mod -> !mod.getValue().systemModule()) + .filter(mod -> !mod.systemModule()) .map(mod -> new MenuEntry( - mod.getKey(), new ResourceKey( - mod.getValue().bundleBaseName(), - mod.getValue().menuKey()), - mod.getValue().modulePath(), - mod.getValue().defaultPriority())) + mod.bundleBaseName(), + mod.menuKey()), + mod.modulePath(), + mod.defaultPriority())) .sorted() .forEachOrdered(mainMenu::add); }
--- a/src/main/java/de/uapcore/lightpit/RequestMapping.java Wed May 13 18:55:05 2020 +0200 +++ b/src/main/java/de/uapcore/lightpit/RequestMapping.java Wed May 13 21:10:23 2020 +0200 @@ -62,11 +62,19 @@ String requestPath() default ""; /** - * Returns the properties key for the (sub) menu label. + * Specifies the properties key for the sub menu label. + * An empty string (default) means that no sub menu entry shall be created. * <p> * This should only be used for {@link HttpMethod#GET} requests. * * @return the properties key */ String menuKey() default ""; + + /** + * May be changed to control the ordering of menu items. + * + * @return an integer to control the ordering + */ + int menuSequence() default 0; }
--- a/src/main/java/de/uapcore/lightpit/modules/ErrorModule.java Wed May 13 18:55:05 2020 +0200 +++ b/src/main/java/de/uapcore/lightpit/modules/ErrorModule.java Wed May 13 21:10:23 2020 +0200 @@ -59,17 +59,17 @@ return ResponseType.HTML; } - @RequestMapping(requestPath = "404", method = HttpMethod.GET) + @RequestMapping(requestPath = "404.html", method = HttpMethod.GET) public ResponseType handle404(HttpServletRequest req) { return handle(req, 404); } - @RequestMapping(requestPath = "403", method = HttpMethod.GET) + @RequestMapping(requestPath = "403.html", method = HttpMethod.GET) public ResponseType handle403(HttpServletRequest req) { return handle(req, 403); } - @RequestMapping(requestPath = "500", method = HttpMethod.GET) + @RequestMapping(requestPath = "500.html", method = HttpMethod.GET) public ResponseType handle500(HttpServletRequest req) { return handle(req, 500); }
--- a/src/main/java/de/uapcore/lightpit/modules/ProjectsModule.java Wed May 13 18:55:05 2020 +0200 +++ b/src/main/java/de/uapcore/lightpit/modules/ProjectsModule.java Wed May 13 21:10:23 2020 +0200 @@ -47,7 +47,13 @@ public final class ProjectsModule extends AbstractLightPITServlet { @RequestMapping(method = HttpMethod.GET) - public ResponseType handle(HttpServletRequest req, DataAccessObjects dao) { + public ResponseType index(HttpServletRequest req, DataAccessObjects dao) { + + return ResponseType.HTML; + } + + @RequestMapping(method = HttpMethod.GET, requestPath = "versions", menuKey = "menu.versions") + public ResponseType versions(HttpServletRequest req, DataAccessObjects dao) { return ResponseType.HTML; }
--- a/src/main/java/de/uapcore/lightpit/modules/VersionsModule.java Wed May 13 18:55:05 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,53 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2018 Mike Becker. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -package de.uapcore.lightpit.modules; - -import de.uapcore.lightpit.*; -import de.uapcore.lightpit.dao.DataAccessObjects; - -import javax.servlet.annotation.WebServlet; -import javax.servlet.http.HttpServletRequest; - - -@LightPITModule( - bundleBaseName = "localization.versions", - modulePath = "versions", - defaultPriority = 50 -) -@WebServlet( - name = "VersionsModule", - urlPatterns = "/versions/*" -) -public final class VersionsModule extends AbstractLightPITServlet { - @RequestMapping(method = HttpMethod.GET) - public ResponseType handle(HttpServletRequest req, DataAccessObjects dao) { - - return ResponseType.HTML; - } -}
--- a/src/main/resources/localization/projects.properties Wed May 13 18:55:05 2020 +0200 +++ b/src/main/resources/localization/projects.properties Wed May 13 21:10:23 2020 +0200 @@ -23,3 +23,4 @@ name=Project Management description=Allows the configuration of projects. menuLabel=Projects +menu.versions=Versions
--- a/src/main/resources/localization/projects_de.properties Wed May 13 18:55:05 2020 +0200 +++ b/src/main/resources/localization/projects_de.properties Wed May 13 21:10:23 2020 +0200 @@ -23,3 +23,4 @@ name=Projektverwaltung description=Erlaubt die Konfiguration von Projekten. menuLabel=Projekte +menu.versions=Versionen \ No newline at end of file
--- a/src/main/resources/localization/versions.properties Wed May 13 18:55:05 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,26 +0,0 @@ -# Copyright 2018 Mike Becker. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -name = Version Management -description = Allows the configuration of versions and milestones within your project. -menuLabel = Versions
--- a/src/main/resources/localization/versions_de.properties Wed May 13 18:55:05 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,26 +0,0 @@ -# Copyright 2018 Mike Becker. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -name = Versionsverwaltung -description = Erlaubt die Konfiguration von Versionen und Meilensteinen im Projekt. -menuLabel = Versionen
--- a/src/main/webapp/WEB-INF/jsp/site.jsp Wed May 13 18:55:05 2020 +0200 +++ b/src/main/webapp/WEB-INF/jsp/site.jsp Wed May 13 21:10:23 2020 +0200 @@ -30,9 +30,15 @@ <%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> <%@taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %> +<%-- Define an alias for the request path --%> +<c:set scope="page" var="requestPath" value="${requestScope[Constants.REQ_ATTR_PATH]}"/> + <%-- Define an alias for the main menu --%> <c:set scope="page" var="mainMenu" value="${requestScope[Constants.REQ_ATTR_MENU]}"/> +<%-- Define an alias for the sub menu --%> +<c:set scope="page" var="subMenu" value="${requestScope[Constants.REQ_ATTR_SUB_MENU]}"/> + <%-- Define an alias for the fragment name --%> <c:set scope="page" var="fragment" value="${requestScope[Constants.REQ_ATTR_FRAGMENT]}"/> @@ -65,22 +71,16 @@ <body> <div id="mainMenu"> <c:forEach var="menu" items="${mainMenu}"> - <div class="menuEntry" - <c:if test="${requestScope[Constants.REQ_ATTR_MODULE_CLASSNAME] eq menu.moduleClassName}"> - data-active - </c:if> - > - <a href="${menu.pathName}"> - <fmt:bundle basename="${menu.resourceKey.bundle}"> - <fmt:message key="${menu.resourceKey.key}" /> - </fmt:bundle> - </a> - </div> + <%@ include file="../jspf/menu-entry.jspf" %> </c:forEach> </div> - <div id="subMenu"> - - </div> + <c:if test="${not empty subMenu}"> + <div id="subMenu"> + <c:forEach var="menu" items="${subMenu}"> + <%@ include file="../jspf/menu-entry.jspf" %> + </c:forEach> + </div> + </c:if> <div id="content-area"> <c:if test="${not empty fragment}"> <fmt:setBundle scope="request" basename="${moduleInfo.bundleBaseName}"/>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/webapp/WEB-INF/jspf/menu-entry.jspf Wed May 13 21:10:23 2020 +0200 @@ -0,0 +1,38 @@ +<%-- +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + +Copyright 2018 Mike Becker. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--%> +<div class="menuEntry" + <c:set var="menuPath" value="/${menu.pathName}"/> + <c:if test="${fn:startsWith(requestPath, menuPath)}"> + data-active + </c:if> +> + <a href="${menu.pathName}/"> + <fmt:bundle basename="${menu.resourceKey.bundle}"> + <fmt:message key="${menu.resourceKey.key}"/> + </fmt:bundle> + </a> +</div> \ No newline at end of file
--- a/src/main/webapp/WEB-INF/web.xml Wed May 13 18:55:05 2020 +0200 +++ b/src/main/webapp/WEB-INF/web.xml Wed May 13 21:10:23 2020 +0200 @@ -11,14 +11,14 @@ </context-param> <error-page> <error-code>404</error-code> - <location>/error/404</location> + <location>/error/404.html</location> </error-page> <error-page> <error-code>403</error-code> - <location>/error/403</location> + <location>/error/403.html</location> </error-page> <error-page> <error-code>500</error-code> - <location>/error/500</location> + <location>/error/500.html</location> </error-page> </web-app>
--- a/src/main/webapp/index.jsp Wed May 13 18:55:05 2020 +0200 +++ b/src/main/webapp/index.jsp Wed May 13 21:10:23 2020 +0200 @@ -27,6 +27,6 @@ <%@page import="de.uapcore.lightpit.Functions" %> <%@page import="de.uapcore.lightpit.modules.HomeModule" %> <% -response.setStatus(response.SC_MOVED_TEMPORARILY); -response.setHeader("Location", "./"+Functions.modulePathOf(HomeModule.class)); + response.setStatus(response.SC_MOVED_TEMPORARILY); + response.setHeader("Location", Functions.modulePathOf(HomeModule.class)); %>
--- a/src/main/webapp/lightpit.css Wed May 13 18:55:05 2020 +0200 +++ b/src/main/webapp/lightpit.css Wed May 13 21:10:23 2020 +0200 @@ -51,21 +51,22 @@ text-decoration: none; } -#mainMenu { +#mainMenu, #subMenu { width: 100%; display: flex; flex-flow: row wrap; - background: #f0f0f5; + border-image-source: linear-gradient(to right, #606060, rgba(60, 60, 60, .25)); + border-image-slice: 1; + border-bottom-style: solid; + border-bottom-width: 1pt; +} + +#mainMenu { + background: #e0e0e5; } #subMenu { background: #f7f7ff; - border-image-source: linear-gradient(to right, #606060, rgba(60, 60, 60, .25)); - border-image-slice: 1; - border-top-style: solid; - border-top-width: 1pt; - border-bottom-style: solid; - border-bottom-width: 1pt; } .menuEntry { @@ -76,7 +77,7 @@ } #mainMenu .menuEntry[data-active] { - background: #e0e0e5; + background: #d0d0d5; } #subMenu .menuEntry[data-active] {