user friendly error pages for codes 404, 403 and 500

Sun, 31 Dec 2017 17:43:39 +0100

author
Mike Becker <universe@uap-core.de>
date
Sun, 31 Dec 2017 17:43:39 +0100
changeset 18
a94b172c3a93
parent 17
d1036b776eee
child 19
1a0ac419f714

user friendly error pages for codes 404, 403 and 500

src/java/de/uapcore/lightpit/AbstractLightPITServlet.java file | annotate | diff | comparison | revisions
src/java/de/uapcore/lightpit/LightPITModule.java file | annotate | diff | comparison | revisions
src/java/de/uapcore/lightpit/ModuleManager.java file | annotate | diff | comparison | revisions
src/java/de/uapcore/lightpit/RequestMapping.java file | annotate | diff | comparison | revisions
src/java/de/uapcore/lightpit/modules/ErrorModule.java file | annotate | diff | comparison | revisions
src/java/de/uapcore/lightpit/resources/localization/error.properties file | annotate | diff | comparison | revisions
src/java/de/uapcore/lightpit/resources/localization/error_de.properties file | annotate | diff | comparison | revisions
web/WEB-INF/dynamic_fragments/error.jsp file | annotate | diff | comparison | revisions
web/WEB-INF/web.xml file | annotate | diff | comparison | revisions
web/error.css file | annotate | diff | comparison | revisions
web/lightpit.css file | annotate | diff | comparison | revisions
     1.1 --- a/src/java/de/uapcore/lightpit/AbstractLightPITServlet.java	Sat Dec 30 20:41:55 2017 +0100
     1.2 +++ b/src/java/de/uapcore/lightpit/AbstractLightPITServlet.java	Sun Dec 31 17:43:39 2017 +0100
     1.3 @@ -73,6 +73,12 @@
     1.4      
     1.5      /**
     1.6       * Invocation mapping gathered from the {@link RequestMapping} annotations.
     1.7 +     * 
     1.8 +     * Paths in this map must always start with a leading slash, although
     1.9 +     * the specification in the annotation must not start with a leading slash.
    1.10 +     * 
    1.11 +     * The reason for this is the different handling of empty paths in 
    1.12 +     * {@link HttpServletRequest#getPathInfo()}.
    1.13       */
    1.14      private final Map<HttpMethod, Map<String, HandlerMethod>> mappings = new HashMap<>();
    1.15  
    1.16 @@ -145,9 +151,11 @@
    1.17                      if (params.length == 2
    1.18                              && HttpServletRequest.class.isAssignableFrom(params[0])
    1.19                              && HttpServletResponse.class.isAssignableFrom(params[1])) {
    1.20 +                        
    1.21 +                        final String requestPath = "/"+mapping.get().requestPath();
    1.22  
    1.23                          if (mappings.computeIfAbsent(mapping.get().method(), k -> new HashMap<>()).
    1.24 -                                putIfAbsent(mapping.get().requestPath(),
    1.25 +                                putIfAbsent(requestPath,
    1.26                                          (req, resp) -> invokeMapping(method, req, resp)) != null) {
    1.27                              LOG.warn("{} {} has multiple mappings",
    1.28                                      mapping.get().method(),
    1.29 @@ -157,7 +165,7 @@
    1.30  
    1.31                          LOG.info("{} {} maps to {}",
    1.32                                  mapping.get().method(),
    1.33 -                                mapping.get().requestPath(),
    1.34 +                                requestPath,
    1.35                                  method.getName()
    1.36                          );
    1.37                      } else {
    1.38 @@ -219,7 +227,7 @@
    1.39      
    1.40      private Optional<HandlerMethod> findMapping(HttpMethod method, HttpServletRequest req) {
    1.41          return Optional.ofNullable(mappings.get(method)).map(
    1.42 -                (rm) -> rm.get(Optional.ofNullable(req.getPathInfo()).orElse(""))
    1.43 +                (rm) -> rm.get(Optional.ofNullable(req.getPathInfo()).orElse("/"))
    1.44          );
    1.45      }
    1.46      
     2.1 --- a/src/java/de/uapcore/lightpit/LightPITModule.java	Sat Dec 30 20:41:55 2017 +0100
     2.2 +++ b/src/java/de/uapcore/lightpit/LightPITModule.java	Sun Dec 31 17:43:39 2017 +0100
     2.3 @@ -71,6 +71,10 @@
     2.4      
     2.5      /**
     2.6       * Returns the properties key for the menu label.
     2.7 +     * 
     2.8 +     * Set this string to empty string, if the module should be hidden from
     2.9 +     * the menu.
    2.10 +     * 
    2.11       * @return the properties key
    2.12       */
    2.13      String menuKey() default "menuLabel";
     3.1 --- a/src/java/de/uapcore/lightpit/ModuleManager.java	Sat Dec 30 20:41:55 2017 +0100
     3.2 +++ b/src/java/de/uapcore/lightpit/ModuleManager.java	Sun Dec 31 17:43:39 2017 +0100
     3.3 @@ -31,6 +31,7 @@
     3.4  import java.util.ArrayList;
     3.5  import java.util.Collections;
     3.6  import java.util.List;
     3.7 +import java.util.Objects;
     3.8  import java.util.Optional;
     3.9  import javax.servlet.Registration;
    3.10  import javax.servlet.ServletContext;
    3.11 @@ -118,10 +119,12 @@
    3.12      
    3.13      private void handleServletRegistration(String name, Registration reg) {
    3.14          final Optional<LightPITModule> moduleInfo = getModuleInfo(reg);
    3.15 -        if (moduleInfo.isPresent()) {
    3.16 +        if (moduleInfo.isPresent()) {            
    3.17 +            // TODO: implement dependency resolver
    3.18              
    3.19 -            // TODO: remove this call and add the module to some dependency resolver, first
    3.20 -            addModuleToMenu(reg.getClassName(), moduleInfo.get());
    3.21 +            if (!moduleInfo.get().menuKey().isEmpty()) {
    3.22 +                addModuleToMenu(reg.getClassName(), moduleInfo.get());
    3.23 +            }
    3.24              
    3.25              LOG.info("Module detected: {}", name);
    3.26          } else {
     4.1 --- a/src/java/de/uapcore/lightpit/RequestMapping.java	Sat Dec 30 20:41:55 2017 +0100
     4.2 +++ b/src/java/de/uapcore/lightpit/RequestMapping.java	Sun Dec 31 17:43:39 2017 +0100
     4.3 @@ -59,7 +59,7 @@
     4.4       * If a menu key is specified, this is also the path, which is linked
     4.5       * by the menu entry.
     4.6       * 
     4.7 -     * The path must be specified <em>without</em> a trailing slash.
     4.8 +     * The path must be specified <em>without</em> leading and trailing slash.
     4.9       * 
    4.10       * @return the request path the annotated method should handle
    4.11       */
     5.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     5.2 +++ b/src/java/de/uapcore/lightpit/modules/ErrorModule.java	Sun Dec 31 17:43:39 2017 +0100
     5.3 @@ -0,0 +1,80 @@
     5.4 +/*
     5.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     5.6 + * 
     5.7 + * Copyright 2017 Mike Becker. All rights reserved.
     5.8 + * 
     5.9 + * Redistribution and use in source and binary forms, with or without
    5.10 + * modification, are permitted provided that the following conditions are met:
    5.11 + *
    5.12 + *   1. Redistributions of source code must retain the above copyright
    5.13 + *      notice, this list of conditions and the following disclaimer.
    5.14 + *
    5.15 + *   2. Redistributions in binary form must reproduce the above copyright
    5.16 + *      notice, this list of conditions and the following disclaimer in the
    5.17 + *      documentation and/or other materials provided with the distribution.
    5.18 + *
    5.19 + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    5.20 + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    5.21 + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    5.22 + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
    5.23 + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    5.24 + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    5.25 + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    5.26 + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    5.27 + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    5.28 + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    5.29 + * POSSIBILITY OF SUCH DAMAGE.
    5.30 + * 
    5.31 + */
    5.32 +package de.uapcore.lightpit.modules;
    5.33 +
    5.34 +import de.uapcore.lightpit.LightPITModule;
    5.35 +import de.uapcore.lightpit.AbstractLightPITServlet;
    5.36 +import de.uapcore.lightpit.HttpMethod;
    5.37 +import de.uapcore.lightpit.RequestMapping;
    5.38 +import de.uapcore.lightpit.ResponseType;
    5.39 +import javax.servlet.annotation.WebServlet;
    5.40 +import javax.servlet.http.HttpServletRequest;
    5.41 +import javax.servlet.http.HttpServletResponse;
    5.42 +
    5.43 +/**
    5.44 + * Entry point for the application.
    5.45 + */
    5.46 +@LightPITModule(
    5.47 +        bundleBaseName = "de.uapcore.lightpit.resources.localization.error",
    5.48 +        modulePath = "error",
    5.49 +        menuKey = "",
    5.50 +        titleKey = "title"
    5.51 +)
    5.52 +@WebServlet(
    5.53 +        name = "ErrorModule",
    5.54 +        urlPatterns = "/error/*"
    5.55 +)
    5.56 +public final class ErrorModule extends AbstractLightPITServlet {
    5.57 +    
    5.58 +    public static final String REQ_ATTR_ERROR_CODE = "errorCode";
    5.59 +    
    5.60 +    private ResponseType handle(HttpServletRequest req, HttpServletResponse resp, int sc) {
    5.61 +        
    5.62 +        req.setAttribute(REQ_ATTR_ERROR_CODE, sc);
    5.63 +        setStylesheet(req, "error");
    5.64 +        setDynamicFragment(req, "error");
    5.65 +        
    5.66 +        return ResponseType.HTML_FULL;
    5.67 +    }
    5.68 +    
    5.69 +    @RequestMapping(requestPath = "404", method = HttpMethod.GET)
    5.70 +    public ResponseType handle404(HttpServletRequest req, HttpServletResponse resp) {
    5.71 +        return handle(req, resp, 404);
    5.72 +    }
    5.73 +    
    5.74 +    @RequestMapping(requestPath = "403", method = HttpMethod.GET)
    5.75 +    public ResponseType handle403(HttpServletRequest req, HttpServletResponse resp) {
    5.76 +        return handle(req, resp, 403);
    5.77 +    }
    5.78 +    
    5.79 +    @RequestMapping(requestPath = "500", method = HttpMethod.GET)
    5.80 +    public ResponseType handle500(HttpServletRequest req, HttpServletResponse resp) {
    5.81 +        return handle(req, resp, 500);
    5.82 +    }
    5.83 +}
     6.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     6.2 +++ b/src/java/de/uapcore/lightpit/resources/localization/error.properties	Sun Dec 31 17:43:39 2017 +0100
     6.3 @@ -0,0 +1,35 @@
     6.4 +# Copyright 2017 Mike Becker. All rights reserved.
     6.5 +#
     6.6 +# Redistribution and use in source and binary forms, with or without
     6.7 +# modification, are permitted provided that the following conditions are met:
     6.8 +#
     6.9 +# 1. Redistributions of source code must retain the above copyright
    6.10 +# notice, this list of conditions and the following disclaimer.
    6.11 +#
    6.12 +# 2. Redistributions in binary form must reproduce the above copyright
    6.13 +# notice, this list of conditions and the following disclaimer in the
    6.14 +# documentation and/or other materials provided with the distribution.
    6.15 +#
    6.16 +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    6.17 +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    6.18 +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
    6.19 +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
    6.20 +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
    6.21 +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
    6.22 +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
    6.23 +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
    6.24 +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
    6.25 +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
    6.26 +
    6.27 +title = Error
    6.28 +
    6.29 +h1 = The requested page cannot be displayed.
    6.30 +errorCode = Code
    6.31 +
    6.32 +errorMessage = Message
    6.33 +errorMessage.404 = Page not found
    6.34 +errorMessage.403 = Access denied
    6.35 +errorMessage.500 = Internal error
    6.36 +
    6.37 +errorTimestamp = Timestamp
    6.38 +errorExceptionText = Internal Exception
     7.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     7.2 +++ b/src/java/de/uapcore/lightpit/resources/localization/error_de.properties	Sun Dec 31 17:43:39 2017 +0100
     7.3 @@ -0,0 +1,35 @@
     7.4 +# Copyright 2017 Mike Becker. All rights reserved.
     7.5 +#
     7.6 +# Redistribution and use in source and binary forms, with or without
     7.7 +# modification, are permitted provided that the following conditions are met:
     7.8 +#
     7.9 +# 1. Redistributions of source code must retain the above copyright
    7.10 +# notice, this list of conditions and the following disclaimer.
    7.11 +#
    7.12 +# 2. Redistributions in binary form must reproduce the above copyright
    7.13 +# notice, this list of conditions and the following disclaimer in the
    7.14 +# documentation and/or other materials provided with the distribution.
    7.15 +#
    7.16 +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    7.17 +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    7.18 +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
    7.19 +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
    7.20 +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
    7.21 +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
    7.22 +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
    7.23 +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
    7.24 +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
    7.25 +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
    7.26 +
    7.27 +title = Fehler
    7.28 +
    7.29 +h1 = Die angeforderte Seite kann nicht angezeigt werden.
    7.30 +errorCode = Fehlercode
    7.31 +
    7.32 +errorMessage = Nachricht
    7.33 +errorMessage.404 = Seite nicht gefunden
    7.34 +errorMessage.403 = Zugriff verboten
    7.35 +errorMessage.500 = Interner Fehler
    7.36 +
    7.37 +errorTimestamp = Zeitstempel
    7.38 +errorExceptionText = Interne Ausnahme
     8.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     8.2 +++ b/web/WEB-INF/dynamic_fragments/error.jsp	Sun Dec 31 17:43:39 2017 +0100
     8.3 @@ -0,0 +1,56 @@
     8.4 +<%-- 
     8.5 +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     8.6 +
     8.7 +Copyright 2017 Mike Becker. All rights reserved.
     8.8 +
     8.9 +Redistribution and use in source and binary forms, with or without
    8.10 +modification, are permitted provided that the following conditions are met:
    8.11 +
    8.12 +1. Redistributions of source code must retain the above copyright
    8.13 +notice, this list of conditions and the following disclaimer.
    8.14 +
    8.15 +2. Redistributions in binary form must reproduce the above copyright
    8.16 +notice, this list of conditions and the following disclaimer in the
    8.17 +documentation and/or other materials provided with the distribution.
    8.18 +
    8.19 +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    8.20 +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    8.21 +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
    8.22 +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
    8.23 +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
    8.24 +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
    8.25 +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
    8.26 +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
    8.27 +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
    8.28 +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
    8.29 +--%>
    8.30 +<%@page pageEncoding="UTF-8" session="true" %>
    8.31 +<%@page import="de.uapcore.lightpit.modules.ErrorModule" %>
    8.32 +<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
    8.33 +<%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
    8.34 +
    8.35 +<c:set scope="page" var="errorCode" value="${requestScope[ErrorModule.REQ_ATTR_ERROR_CODE]}"/>
    8.36 +
    8.37 +<div id="error-page">
    8.38 +    <h1><fmt:message key="h1"/></h1>
    8.39 +    <table>
    8.40 +        <tr>
    8.41 +            <th><fmt:message key="errorCode" />:</th>
    8.42 +            <td>${errorCode}</td>
    8.43 +        </tr>
    8.44 +        <tr>
    8.45 +            <th><fmt:message key="errorMessage" />:</th>
    8.46 +            <td><fmt:message key="errorMessage.${errorCode}" /></td>
    8.47 +        </tr>
    8.48 +        <tr>
    8.49 +            <th><fmt:message key="errorTimestamp" />:</th>
    8.50 +            <td><fmt:formatDate type="both" value="<%= new java.util.Date()%>"/></td>
    8.51 +        </tr>
    8.52 +        <c:if test="${not empty exception}">
    8.53 +        <tr>
    8.54 +            <th><fmt:message key="errorExceptionText" />:</th>
    8.55 +            <td>${exception.class.name} - ${exception.message}</td>
    8.56 +        </tr>    
    8.57 +        </c:if>
    8.58 +    </table>
    8.59 +</div>
     9.1 --- a/web/WEB-INF/web.xml	Sat Dec 30 20:41:55 2017 +0100
     9.2 +++ b/web/WEB-INF/web.xml	Sun Dec 31 17:43:39 2017 +0100
     9.3 @@ -9,4 +9,16 @@
     9.4          <param-name>available-languages</param-name>
     9.5          <param-value>en,de</param-value>
     9.6      </context-param>
     9.7 +    <error-page>
     9.8 +        <error-code>404</error-code>
     9.9 +        <location>/error/404</location>
    9.10 +    </error-page>
    9.11 +    <error-page>
    9.12 +        <error-code>403</error-code>
    9.13 +        <location>/error/403</location>
    9.14 +    </error-page>
    9.15 +    <error-page>
    9.16 +        <error-code>500</error-code>
    9.17 +        <location>/error/500</location>
    9.18 +    </error-page>
    9.19  </web-app>
    10.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    10.2 +++ b/web/error.css	Sun Dec 31 17:43:39 2017 +0100
    10.3 @@ -0,0 +1,55 @@
    10.4 +/*
    10.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
    10.6 + * 
    10.7 + * Copyright 2017 Mike Becker. All rights reserved.
    10.8 + * 
    10.9 + * Redistribution and use in source and binary forms, with or without
   10.10 + * modification, are permitted provided that the following conditions are met:
   10.11 + *
   10.12 + *   1. Redistributions of source code must retain the above copyright
   10.13 + *      notice, this list of conditions and the following disclaimer.
   10.14 + *
   10.15 + *   2. Redistributions in binary form must reproduce the above copyright
   10.16 + *      notice, this list of conditions and the following disclaimer in the
   10.17 + *      documentation and/or other materials provided with the distribution.
   10.18 + *
   10.19 + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
   10.20 + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   10.21 + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   10.22 + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
   10.23 + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
   10.24 + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   10.25 + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   10.26 + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
   10.27 + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   10.28 + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   10.29 + * POSSIBILITY OF SUCH DAMAGE.
   10.30 + * 
   10.31 + */
   10.32 +
   10.33 +#error-page h1 {
   10.34 +    font-size: 1.5em;
   10.35 +}
   10.36 +
   10.37 +#error-page table {
   10.38 +    width: 100%;
   10.39 +    
   10.40 +    border-top-style: solid;
   10.41 +    border-top-width: 1pt;
   10.42 +    border-top-color: #606060;
   10.43 +    
   10.44 +    border-bottom-style: solid;
   10.45 +    border-bottom-width: 1pt;
   10.46 +    border-bottom-color: #505050;
   10.47 +    
   10.48 +    border-collapse: separate;
   10.49 +    border-spacing: .5em;
   10.50 +}
   10.51 +
   10.52 +#error-page table th {
   10.53 +    text-align: right;
   10.54 +}
   10.55 +
   10.56 +#error-page table td {
   10.57 +    width: 100%;
   10.58 +}
    11.1 --- a/web/lightpit.css	Sat Dec 30 20:41:55 2017 +0100
    11.2 +++ b/web/lightpit.css	Sun Dec 31 17:43:39 2017 +0100
    11.3 @@ -39,7 +39,7 @@
    11.4      border-style: solid;
    11.5      border-width: 1pt;
    11.6      
    11.7 -    color: black;
    11.8 +    color: #1c202e;
    11.9  }
   11.10  
   11.11  a {

mercurial