adds breadcrumb menu

Fri, 22 May 2020 16:21:31 +0200

author
Mike Becker <universe@uap-core.de>
date
Fri, 22 May 2020 16:21:31 +0200
changeset 71
dca186d3911f
parent 70
821c4950b619
child 72
0646c14e36fb

adds breadcrumb menu

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/MenuEntry.java file | annotate | diff | comparison | revisions
src/main/java/de/uapcore/lightpit/modules/ProjectsModule.java 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.jsp file | annotate | diff | comparison | revisions
src/main/webapp/error.css file | annotate | diff | comparison | revisions
     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  }

mercurial