simplifies menu generation, adds submenus and removes VersionsModule (versions will be part of the ProjectsModule)

Wed, 13 May 2020 21:10:23 +0200

author
Mike Becker <universe@uap-core.de>
date
Wed, 13 May 2020 21:10:23 +0200
changeset 45
cc7f082c5ef3
parent 44
835dd169642a
child 46
1574965c7dc7

simplifies menu generation, adds submenus and removes VersionsModule (versions will be part of the ProjectsModule)

src/main/java/de/uapcore/lightpit/AbstractLightPITServlet.java file | annotate | diff | comparison | revisions
src/main/java/de/uapcore/lightpit/Constants.java file | annotate | diff | comparison | revisions
src/main/java/de/uapcore/lightpit/Functions.java file | annotate | diff | comparison | revisions
src/main/java/de/uapcore/lightpit/MenuEntry.java file | annotate | diff | comparison | revisions
src/main/java/de/uapcore/lightpit/ModuleManager.java file | annotate | diff | comparison | revisions
src/main/java/de/uapcore/lightpit/RequestMapping.java file | annotate | diff | comparison | revisions
src/main/java/de/uapcore/lightpit/modules/ErrorModule.java file | annotate | diff | comparison | revisions
src/main/java/de/uapcore/lightpit/modules/ProjectsModule.java file | annotate | diff | comparison | revisions
src/main/java/de/uapcore/lightpit/modules/VersionsModule.java file | annotate | diff | comparison | revisions
src/main/resources/localization/projects.properties file | annotate | diff | comparison | revisions
src/main/resources/localization/projects_de.properties file | annotate | diff | comparison | revisions
src/main/resources/localization/versions.properties file | annotate | diff | comparison | revisions
src/main/resources/localization/versions_de.properties file | annotate | diff | comparison | revisions
src/main/webapp/WEB-INF/jsp/site.jsp file | annotate | diff | comparison | revisions
src/main/webapp/WEB-INF/jspf/menu-entry.jspf file | annotate | diff | comparison | revisions
src/main/webapp/WEB-INF/web.xml file | annotate | diff | comparison | revisions
src/main/webapp/index.jsp file | annotate | diff | comparison | revisions
src/main/webapp/lightpit.css file | annotate | diff | comparison | revisions
--- 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] {

mercurial