Fri, 22 May 2020 16:21:31 +0200
adds breadcrumb menu
1.1 --- a/src/main/java/de/uapcore/lightpit/AbstractLightPITServlet.java Tue May 19 19:34:57 2020 +0200 1.2 +++ b/src/main/java/de/uapcore/lightpit/AbstractLightPITServlet.java Fri May 22 16:21:31 2020 +0200 1.3 @@ -230,12 +230,24 @@ 1.4 * @param req the servlet request object 1.5 * @param fragmentName the name of the fragment 1.6 * @see Constants#DYN_FRAGMENT_PATH_PREFIX 1.7 + * @see Constants#REQ_ATTR_FRAGMENT 1.8 */ 1.9 protected void setDynamicFragment(HttpServletRequest req, String fragmentName) { 1.10 req.setAttribute(Constants.REQ_ATTR_FRAGMENT, Functions.dynFragmentPath(fragmentName)); 1.11 } 1.12 1.13 /** 1.14 + * Sets the breadcrumbs menu. 1.15 + * 1.16 + * @param req the servlet request object 1.17 + * @param breadcrumbs the menu entries for the breadcrumbs menu 1.18 + * @see Constants#REQ_ATTR_BREADCRUMBS 1.19 + */ 1.20 + protected void setBreadcrumbs(HttpServletRequest req, List<MenuEntry> breadcrumbs) { 1.21 + req.setAttribute(Constants.REQ_ATTR_BREADCRUMBS, breadcrumbs); 1.22 + } 1.23 + 1.24 + /** 1.25 * @param req the servlet request object 1.26 * @param location the location where to redirect 1.27 * @see Constants#REQ_ATTR_REDIRECT_LOCATION 1.28 @@ -268,16 +280,16 @@ 1.29 * The specified type must have a single-argument constructor accepting a string to perform conversion. 1.30 * The constructor of the specified type may throw an exception on conversion failures. 1.31 * 1.32 - * @param req the servlet request object 1.33 + * @param req the servlet request object 1.34 * @param clazz the class object of the expected type 1.35 - * @param name the name of the parameter 1.36 - * @param <T> the expected type 1.37 + * @param name the name of the parameter 1.38 + * @param <T> the expected type 1.39 * @return the parameter value or an empty optional, if no parameter with the specified name was found 1.40 */ 1.41 - protected<T> Optional<T> getParameter(HttpServletRequest req, Class<T> clazz, String name) { 1.42 + protected <T> Optional<T> getParameter(HttpServletRequest req, Class<T> clazz, String name) { 1.43 final String paramValue = req.getParameter(name); 1.44 if (paramValue == null) return Optional.empty(); 1.45 - if (clazz.equals(String.class)) return Optional.of((T)paramValue); 1.46 + if (clazz.equals(String.class)) return Optional.of((T) paramValue); 1.47 try { 1.48 final Constructor<T> ctor = clazz.getConstructor(String.class); 1.49 return Optional.of(ctor.newInstance(paramValue)); 1.50 @@ -290,16 +302,16 @@ 1.51 /** 1.52 * Tries to look up an entity with a key obtained from a request parameter. 1.53 * 1.54 - * @param req the servlet request object 1.55 + * @param req the servlet request object 1.56 * @param clazz the class representing the type of the request parameter 1.57 - * @param name the name of the request parameter 1.58 - * @param find the find function (typically a DAO function) 1.59 - * @param <T> the type of the request parameter 1.60 - * @param <R> the type of the looked up entity 1.61 + * @param name the name of the request parameter 1.62 + * @param find the find function (typically a DAO function) 1.63 + * @param <T> the type of the request parameter 1.64 + * @param <R> the type of the looked up entity 1.65 * @return the retrieved entity or an empty optional if there is no such entity or the request parameter was missing 1.66 * @throws SQLException if the find function throws an exception 1.67 */ 1.68 - protected<T,R> Optional<R> findByParameter(HttpServletRequest req, Class<T> clazz, String name, SQLFindFunction<? super T, ? extends R> find) throws SQLException { 1.69 + protected <T, R> Optional<R> findByParameter(HttpServletRequest req, Class<T> clazz, String name, SQLFindFunction<? super T, ? extends R> find) throws SQLException { 1.70 final var param = getParameter(req, clazz, name); 1.71 if (param.isPresent()) { 1.72 return Optional.ofNullable(find.apply(param.get())); 1.73 @@ -311,7 +323,13 @@ 1.74 private void forwardToFullView(HttpServletRequest req, HttpServletResponse resp) 1.75 throws IOException, ServletException { 1.76 1.77 - req.setAttribute(Constants.REQ_ATTR_MENU, getModuleManager().getMainMenu()); 1.78 + final var mainMenu = new ArrayList<MenuEntry>(getModuleManager().getMainMenu()); 1.79 + for (var entry : mainMenu) { 1.80 + if (Functions.fullPath(req).startsWith("/" + entry.getPathName())) { 1.81 + entry.setActive(true); 1.82 + } 1.83 + } 1.84 + req.setAttribute(Constants.REQ_ATTR_MENU, mainMenu); 1.85 req.getRequestDispatcher(SITE_JSP).forward(req, resp); 1.86 } 1.87
2.1 --- a/src/main/java/de/uapcore/lightpit/Constants.java Tue May 19 19:34:57 2020 +0200 2.2 +++ b/src/main/java/de/uapcore/lightpit/Constants.java Fri May 22 16:21:31 2020 +0200 2.3 @@ -68,6 +68,11 @@ 2.4 public static final String REQ_ATTR_MENU = fqn(AbstractLightPITServlet.class, "mainMenu"); 2.5 2.6 /** 2.7 + * Key for the request attribute containing the breadcrumb menu. 2.8 + */ 2.9 + public static final String REQ_ATTR_BREADCRUMBS = fqn(AbstractLightPITServlet.class, "breadcrumbs"); 2.10 + 2.11 + /** 2.12 * Key for the request attribute containing the base href. 2.13 */ 2.14 public static final String REQ_ATTR_BASE_HREF = fqn(AbstractLightPITServlet.class, "base_href");
3.1 --- a/src/main/java/de/uapcore/lightpit/MenuEntry.java Tue May 19 19:34:57 2020 +0200 3.2 +++ b/src/main/java/de/uapcore/lightpit/MenuEntry.java Fri May 22 16:21:31 2020 +0200 3.3 @@ -45,6 +45,11 @@ 3.4 private final ResourceKey resourceKey; 3.5 3.6 /** 3.7 + * Custom menu text. 3.8 + */ 3.9 + private final String text; 3.10 + 3.11 + /** 3.12 * Path name of the module, linked by this menu entry. 3.13 */ 3.14 private final String pathName; 3.15 @@ -54,20 +59,33 @@ 3.16 */ 3.17 private final int sequence; 3.18 3.19 + /** 3.20 + * True if this menu entry is active. 3.21 + */ 3.22 + private boolean active = false; 3.23 + 3.24 public MenuEntry(ResourceKey resourceKey, String pathName, int sequence) { 3.25 + this.text = null; 3.26 this.resourceKey = resourceKey; 3.27 this.pathName = pathName; 3.28 this.sequence = sequence; 3.29 } 3.30 3.31 - public MenuEntry(ResourceKey resourceKey, String pathName) { 3.32 - this(resourceKey, pathName, 0); 3.33 + public MenuEntry(String text, String pathName, int sequence) { 3.34 + this.text = text; 3.35 + this.resourceKey = null; 3.36 + this.pathName = pathName; 3.37 + this.sequence = sequence; 3.38 } 3.39 3.40 public ResourceKey getResourceKey() { 3.41 return resourceKey; 3.42 } 3.43 3.44 + public String getText() { 3.45 + return text; 3.46 + } 3.47 + 3.48 public String getPathName() { 3.49 return pathName; 3.50 } 3.51 @@ -76,6 +94,14 @@ 3.52 return sequence; 3.53 } 3.54 3.55 + public boolean isActive() { 3.56 + return this.active; 3.57 + } 3.58 + 3.59 + public void setActive(boolean active) { 3.60 + this.active = true; 3.61 + } 3.62 + 3.63 @Override 3.64 public boolean equals(Object o) { 3.65 if (this == o) return true;
4.1 --- a/src/main/java/de/uapcore/lightpit/modules/ProjectsModule.java Tue May 19 19:34:57 2020 +0200 4.2 +++ b/src/main/java/de/uapcore/lightpit/modules/ProjectsModule.java Fri May 22 16:21:31 2020 +0200 4.3 @@ -40,7 +40,10 @@ 4.4 import javax.servlet.http.HttpServletResponse; 4.5 import java.io.IOException; 4.6 import java.sql.SQLException; 4.7 +import java.util.ArrayList; 4.8 +import java.util.List; 4.9 import java.util.NoSuchElementException; 4.10 +import java.util.Optional; 4.11 4.12 import static de.uapcore.lightpit.Functions.fqn; 4.13 4.14 @@ -63,14 +66,44 @@ 4.15 final var projectDao = dao.getProjectDao(); 4.16 final var session = req.getSession(); 4.17 final var projectSelection = getParameter(req, Integer.class, "pid"); 4.18 + final Project selectedProject; 4.19 if (projectSelection.isPresent()) { 4.20 - final var selectedId = projectSelection.get(); 4.21 - final var selectedProject = projectDao.find(selectedId); 4.22 - session.setAttribute(SESSION_ATTR_SELECTED_PROJECT, selectedProject); 4.23 - return selectedProject; 4.24 + selectedProject = projectDao.find(projectSelection.get()); 4.25 } else { 4.26 - return (Project) session.getAttribute(SESSION_ATTR_SELECTED_PROJECT); 4.27 + final var sessionProject = (Project) session.getAttribute(SESSION_ATTR_SELECTED_PROJECT); 4.28 + selectedProject = sessionProject == null ? null : projectDao.find(sessionProject.getId()); 4.29 } 4.30 + session.setAttribute(SESSION_ATTR_SELECTED_PROJECT, selectedProject); 4.31 + return selectedProject; 4.32 + } 4.33 + 4.34 + 4.35 + /** 4.36 + * Creates the breadcrumb menu. 4.37 + * 4.38 + * @param level the current active level 4.39 + * @param selectedProject the selected project, if any, or null 4.40 + * @return a dynamic breadcrumb menu trying to display as many levels as possible 4.41 + */ 4.42 + private List<MenuEntry> getBreadcrumbs(int level, 4.43 + Project selectedProject) { 4.44 + MenuEntry entry; 4.45 + 4.46 + final var breadcrumbs = new ArrayList<MenuEntry>(); 4.47 + entry = new MenuEntry(new ResourceKey("localization.projects", "menuLabel"), 4.48 + "projects/", 0); 4.49 + breadcrumbs.add(entry); 4.50 + if (level == 0) entry.setActive(true); 4.51 + 4.52 + if (selectedProject == null) 4.53 + return breadcrumbs; 4.54 + 4.55 + entry = new MenuEntry(selectedProject.getName(), 4.56 + "projects/view?pid=" + selectedProject.getId(), 1); 4.57 + if (level == 1) entry.setActive(true); 4.58 + 4.59 + breadcrumbs.add(entry); 4.60 + return breadcrumbs; 4.61 } 4.62 4.63 @RequestMapping(method = HttpMethod.GET) 4.64 @@ -81,20 +114,33 @@ 4.65 setDynamicFragment(req, "projects"); 4.66 setStylesheet(req, "projects"); 4.67 4.68 - if (getSelectedProject(req, dao) == null) { 4.69 - projectList.stream().findFirst().ifPresent(proj -> req.getSession().setAttribute(SESSION_ATTR_SELECTED_PROJECT, proj)); 4.70 - } 4.71 + final var selectedProject = getSelectedProject(req, dao); 4.72 + setBreadcrumbs(req, getBreadcrumbs(0, selectedProject)); 4.73 4.74 return ResponseType.HTML; 4.75 } 4.76 4.77 + private void configureEditForm(HttpServletRequest req, DataAccessObjects dao, Optional<Project> project) throws SQLException { 4.78 + if (project.isPresent()) { 4.79 + req.setAttribute("project", project.get()); 4.80 + setBreadcrumbs(req, getBreadcrumbs(1, project.get())); 4.81 + } else { 4.82 + req.setAttribute("project", new Project(-1)); 4.83 + setBreadcrumbs(req, getBreadcrumbs(0, null)); 4.84 + } 4.85 + 4.86 + req.setAttribute("users", dao.getUserDao().list()); 4.87 + setDynamicFragment(req, "project-form"); 4.88 + } 4.89 + 4.90 @RequestMapping(requestPath = "edit", method = HttpMethod.GET) 4.91 public ResponseType edit(HttpServletRequest req, DataAccessObjects dao) throws SQLException { 4.92 - req.setAttribute("project", findByParameter(req, Integer.class, "id", 4.93 - dao.getProjectDao()::find).orElse(new Project(-1))); 4.94 - req.setAttribute("users", dao.getUserDao().list()); 4.95 4.96 - setDynamicFragment(req, "project-form"); 4.97 + Optional<Project> project = findByParameter(req, Integer.class, "id", dao.getProjectDao()::find); 4.98 + configureEditForm(req, dao, project); 4.99 + if (project.isPresent()) { 4.100 + req.getSession().setAttribute(SESSION_ATTR_SELECTED_PROJECT, project.get()); 4.101 + } 4.102 4.103 return ResponseType.HTML; 4.104 } 4.105 @@ -102,7 +148,7 @@ 4.106 @RequestMapping(requestPath = "commit", method = HttpMethod.POST) 4.107 public ResponseType commit(HttpServletRequest req, DataAccessObjects dao) throws SQLException { 4.108 4.109 - Project project = new Project(-1); 4.110 + Project project = null; 4.111 try { 4.112 project = new Project(getParameter(req, Integer.class, "id").orElseThrow()); 4.113 project.setName(getParameter(req, String.class, "name").orElseThrow()); 4.114 @@ -119,11 +165,9 @@ 4.115 LOG.debug("Successfully updated project {}", project.getName()); 4.116 } catch (NoSuchElementException | NumberFormatException | SQLException ex) { 4.117 // TODO: set request attribute with error text 4.118 - req.setAttribute("project", project); 4.119 - req.setAttribute("users", dao.getUserDao().list()); 4.120 - setDynamicFragment(req, "project-form"); 4.121 LOG.warn("Form validation failure: {}", ex.getMessage()); 4.122 LOG.debug("Details:", ex); 4.123 + configureEditForm(req, dao, Optional.ofNullable(project)); 4.124 } 4.125 4.126 return ResponseType.HTML; 4.127 @@ -140,11 +184,21 @@ 4.128 req.setAttribute("versions", dao.getVersionDao().list(selectedProject)); 4.129 req.setAttribute("issues", dao.getIssueDao().list(selectedProject)); 4.130 4.131 + // TODO: add more levels depending on last visited location 4.132 + setBreadcrumbs(req, getBreadcrumbs(1, selectedProject)); 4.133 + 4.134 setDynamicFragment(req, "project-details"); 4.135 4.136 return ResponseType.HTML; 4.137 } 4.138 4.139 + private void configureEditVersionForm(HttpServletRequest req, Optional<Version> version, Project selectedProject) { 4.140 + req.setAttribute("version", version.orElse(new Version(-1, selectedProject))); 4.141 + req.setAttribute("versionStatusEnum", VersionStatus.values()); 4.142 + 4.143 + setDynamicFragment(req, "version-form"); 4.144 + } 4.145 + 4.146 @RequestMapping(requestPath = "versions/edit", method = HttpMethod.GET) 4.147 public ResponseType editVersion(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws IOException, SQLException { 4.148 final var selectedProject = getSelectedProject(req, dao); 4.149 @@ -153,11 +207,9 @@ 4.150 return ResponseType.NONE; 4.151 } 4.152 4.153 - req.setAttribute("version", findByParameter(req, Integer.class, "id", 4.154 - dao.getVersionDao()::find).orElse(new Version(-1, selectedProject))); 4.155 - req.setAttribute("versionStatusEnum", VersionStatus.values()); 4.156 - 4.157 - setDynamicFragment(req, "version-form"); 4.158 + configureEditVersionForm(req, 4.159 + findByParameter(req, Integer.class, "id", dao.getVersionDao()::find), 4.160 + selectedProject); 4.161 4.162 return ResponseType.HTML; 4.163 } 4.164 @@ -170,7 +222,7 @@ 4.165 return ResponseType.NONE; 4.166 } 4.167 4.168 - Version version = new Version(-1, selectedProject); 4.169 + Version version = null; 4.170 try { 4.171 version = new Version(getParameter(req, Integer.class, "id").orElseThrow(), selectedProject); 4.172 version.setName(getParameter(req, String.class, "name").orElseThrow()); 4.173 @@ -183,16 +235,24 @@ 4.174 LOG.debug("Successfully updated version {} for project {}", version.getName(), selectedProject.getName()); 4.175 } catch (NoSuchElementException | NumberFormatException | SQLException ex) { 4.176 // TODO: set request attribute with error text 4.177 - req.setAttribute("version", version); 4.178 - req.setAttribute("versionStatusEnum", VersionStatus.values()); 4.179 - setDynamicFragment(req, "version-form"); 4.180 LOG.warn("Form validation failure: {}", ex.getMessage()); 4.181 LOG.debug("Details:", ex); 4.182 + configureEditVersionForm(req, Optional.ofNullable(version), selectedProject); 4.183 } 4.184 4.185 return ResponseType.HTML; 4.186 } 4.187 4.188 + private void configureEditIssueForm(HttpServletRequest req, DataAccessObjects dao, Optional<Issue> issue, Project selectedProject) throws SQLException { 4.189 + 4.190 + req.setAttribute("issue", issue.orElse(new Issue(-1, selectedProject))); 4.191 + req.setAttribute("issueStatusEnum", IssueStatus.values()); 4.192 + req.setAttribute("issueCategoryEnum", IssueCategory.values()); 4.193 + req.setAttribute("versions", dao.getVersionDao().list(selectedProject)); 4.194 + 4.195 + setDynamicFragment(req, "issue-form"); 4.196 + } 4.197 + 4.198 @RequestMapping(requestPath = "issues/edit", method = HttpMethod.GET) 4.199 public ResponseType editIssue(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws IOException, SQLException { 4.200 final var selectedProject = getSelectedProject(req, dao); 4.201 @@ -201,12 +261,9 @@ 4.202 return ResponseType.NONE; 4.203 } 4.204 4.205 - req.setAttribute("issue", findByParameter(req, Integer.class, "id", 4.206 - dao.getIssueDao()::find).orElse(new Issue(-1, selectedProject))); 4.207 - req.setAttribute("issueStatusEnum", IssueStatus.values()); 4.208 - req.setAttribute("issueCategoryEnum", IssueCategory.values()); 4.209 - 4.210 - setDynamicFragment(req, "issue-form"); 4.211 + configureEditIssueForm(req, dao, 4.212 + findByParameter(req, Integer.class, "id", dao.getIssueDao()::find), 4.213 + selectedProject); 4.214 4.215 return ResponseType.HTML; 4.216 } 4.217 @@ -219,7 +276,7 @@ 4.218 return ResponseType.NONE; 4.219 } 4.220 4.221 - Issue issue = new Issue(-1, selectedProject); 4.222 + Issue issue = null; 4.223 try { 4.224 issue = new Issue(getParameter(req, Integer.class, "id").orElseThrow(), selectedProject); 4.225 4.226 @@ -232,12 +289,9 @@ 4.227 LOG.debug("Successfully updated issue {} for project {}", issue.getId(), selectedProject.getName()); 4.228 } catch (NoSuchElementException | NumberFormatException | SQLException ex) { 4.229 // TODO: set request attribute with error text 4.230 - req.setAttribute("issue", issue); 4.231 - req.setAttribute("issueStatusEnum", IssueStatus.values()); 4.232 - req.setAttribute("issueCategoryEnum", IssueCategory.values()); 4.233 - setDynamicFragment(req, "issue-form"); 4.234 LOG.warn("Form validation failure: {}", ex.getMessage()); 4.235 LOG.debug("Details:", ex); 4.236 + configureEditIssueForm(req, dao, Optional.ofNullable(issue), selectedProject); 4.237 } 4.238 4.239 return ResponseType.HTML;
5.1 --- a/src/main/webapp/WEB-INF/jsp/site.jsp Tue May 19 19:34:57 2020 +0200 5.2 +++ b/src/main/webapp/WEB-INF/jsp/site.jsp Fri May 22 16:21:31 2020 +0200 5.3 @@ -32,7 +32,7 @@ 5.4 <%@taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %> 5.5 5.6 <%-- Make the base href easily available at request scope --%> 5.7 -<c:set scope="page" var="baseHref" value="${requestScope[Constants.REQ_ATTR_BASE_HREF]}" /> 5.8 +<c:set scope="page" var="baseHref" value="${requestScope[Constants.REQ_ATTR_BASE_HREF]}"/> 5.9 5.10 <%-- Define an alias for the request path --%> 5.11 <c:set scope="page" var="requestPath" value="${requestScope[Constants.REQ_ATTR_PATH]}"/> 5.12 @@ -40,6 +40,9 @@ 5.13 <%-- Define an alias for the main menu --%> 5.14 <c:set scope="page" var="mainMenu" value="${requestScope[Constants.REQ_ATTR_MENU]}"/> 5.15 5.16 +<%-- Define an alias for the main menu --%> 5.17 +<c:set scope="page" var="breadcrumbs" value="${requestScope[Constants.REQ_ATTR_BREADCRUMBS]}"/> 5.18 + 5.19 <%-- Define an alias for the fragment name --%> 5.20 <c:set scope="page" var="fragment" value="${requestScope[Constants.REQ_ATTR_FRAGMENT]}"/> 5.21 5.22 @@ -54,7 +57,7 @@ 5.23 5.24 <%-- Apply the session locale (should always be present, but check nevertheless) --%> 5.25 <c:if test="${not empty sessionScope[Constants.SESSION_ATTR_LANGUAGE]}"> 5.26 -<fmt:setLocale scope="request" value="${sessionScope[Constants.SESSION_ATTR_LANGUAGE]}"/> 5.27 + <fmt:setLocale scope="request" value="${sessionScope[Constants.SESSION_ATTR_LANGUAGE]}"/> 5.28 </c:if> 5.29 5.30 <%-- Selected project, if any --%> 5.31 @@ -62,48 +65,41 @@ 5.32 5.33 <!DOCTYPE html> 5.34 <html> 5.35 - <head> 5.36 - <base href="${baseHref}"> 5.37 - <title>LightPIT - 5.38 - <fmt:bundle basename="${moduleInfo.bundleBaseName}"> 5.39 - <fmt:message key="${moduleInfo.titleKey}" /> 5.40 - </fmt:bundle> 5.41 - </title> 5.42 - <meta charset="UTF-8"> 5.43 - <c:if test="${not empty redirectLocation}"> 5.44 +<head> 5.45 + <base href="${baseHref}"> 5.46 + <title>LightPIT - 5.47 + <fmt:bundle basename="${moduleInfo.bundleBaseName}"> 5.48 + <fmt:message key="${moduleInfo.titleKey}"/> 5.49 + </fmt:bundle> 5.50 + </title> 5.51 + <meta charset="UTF-8"> 5.52 + <c:if test="${not empty redirectLocation}"> 5.53 <meta http-equiv="refresh" content="0; URL=${redirectLocation}"> 5.54 - </c:if> 5.55 - <link rel="stylesheet" href="lightpit.css" type="text/css"> 5.56 - <c:if test="${not empty extraCss}"> 5.57 + </c:if> 5.58 + <link rel="stylesheet" href="lightpit.css" type="text/css"> 5.59 + <c:if test="${not empty extraCss}"> 5.60 <link rel="stylesheet" href="${extraCss}" type="text/css"> 5.61 - </c:if> 5.62 - </head> 5.63 - <body> 5.64 - <div id="mainMenu"> 5.65 - <c:forEach var="menu" items="${mainMenu}"> 5.66 - <div class="menuEntry" 5.67 - <c:set var="menuPath" value="/${menu.pathName}"/> 5.68 - <c:if test="${fn:startsWith(requestPath, menuPath)}"> 5.69 - data-active 5.70 - </c:if> 5.71 - > 5.72 - <a href="${menu.pathName}"> 5.73 - <fmt:bundle basename="${menu.resourceKey.bundle}"> 5.74 - <fmt:message key="${menu.resourceKey.key}"/> 5.75 - </fmt:bundle> 5.76 - </a> 5.77 - </div> 5.78 - </c:forEach> 5.79 - </div> 5.80 - <div id="breadcrumbs"> 5.81 - <%-- TODO: find a strategy to define the breadcrumbs --%> 5.82 - </div> 5.83 - <div id="content-area"> 5.84 - <c:if test="${not empty fragment}"> 5.85 - <fmt:setBundle scope="request" basename="${moduleInfo.bundleBaseName}"/> 5.86 - <fmt:setBundle scope="request" var="lightpit_bundle" basename="localization.lightpit"/> 5.87 - <c:import url="${fragment}" /> 5.88 - </c:if> 5.89 - </div> 5.90 - </body> 5.91 + </c:if> 5.92 +</head> 5.93 +<body> 5.94 +<div id="mainMenu"> 5.95 + <c:forEach var="menu" items="${mainMenu}"> 5.96 + <%@include file="../jspf/menu-entry.jsp" %> 5.97 + </c:forEach> 5.98 +</div> 5.99 +<c:if test="${not empty breadcrumbs}"> 5.100 + <div id="breadcrumbs"> 5.101 + <c:forEach var="menu" items="${breadcrumbs}"> 5.102 + <%@include file="../jspf/menu-entry.jsp" %> 5.103 + </c:forEach> 5.104 + </div> 5.105 +</c:if> 5.106 +<div id="content-area"> 5.107 + <c:if test="${not empty fragment}"> 5.108 + <fmt:setBundle scope="request" basename="${moduleInfo.bundleBaseName}"/> 5.109 + <fmt:setBundle scope="request" var="lightpit_bundle" basename="localization.lightpit"/> 5.110 + <c:import url="${fragment}"/> 5.111 + </c:if> 5.112 +</div> 5.113 +</body> 5.114 </html>
6.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 6.2 +++ b/src/main/webapp/WEB-INF/jspf/menu-entry.jsp Fri May 22 16:21:31 2020 +0200 6.3 @@ -0,0 +1,13 @@ 6.4 +<div class="menuEntry" 6.5 + <c:if test="${menu.active}">data-active</c:if> > 6.6 + <a href="${menu.pathName}"> 6.7 + <c:if test="${empty menu.resourceKey}"> 6.8 + <c:out value="${menu.text}"/> 6.9 + </c:if> 6.10 + <c:if test="${not empty menu.resourceKey}"> 6.11 + <fmt:bundle basename="${menu.resourceKey.bundle}"> 6.12 + <fmt:message key="${menu.resourceKey.key}"/> 6.13 + </fmt:bundle> 6.14 + </c:if> 6.15 + </a> 6.16 +</div> 6.17 \ No newline at end of file
7.1 --- a/src/main/webapp/error.css Tue May 19 19:34:57 2020 +0200 7.2 +++ b/src/main/webapp/error.css Fri May 22 16:21:31 2020 +0200 7.3 @@ -33,15 +33,15 @@ 7.4 7.5 #error-page table { 7.6 width: 100%; 7.7 - 7.8 + 7.9 border-top-style: solid; 7.10 border-top-width: 1pt; 7.11 border-top-color: #606060; 7.12 - 7.13 + 7.14 border-bottom-style: solid; 7.15 border-bottom-width: 1pt; 7.16 border-bottom-color: #505050; 7.17 - 7.18 + 7.19 border-collapse: separate; 7.20 border-spacing: .5em; 7.21 }