Tue, 26 Dec 2017 17:36:47 +0100
adds dynamic fragments to LightPIT request handling framework + basic language recognition code
1.1 --- a/src/java/de/uapcore/lightpit/AbstractLightPITServlet.java Sat Dec 23 17:28:19 2017 +0100 1.2 +++ b/src/java/de/uapcore/lightpit/AbstractLightPITServlet.java Tue Dec 26 17:36:47 2017 +0100 1.3 @@ -31,13 +31,17 @@ 1.4 import java.io.IOException; 1.5 import java.lang.reflect.Method; 1.6 import java.lang.reflect.Modifier; 1.7 +import java.util.Arrays; 1.8 import java.util.HashMap; 1.9 +import java.util.List; 1.10 +import java.util.Locale; 1.11 import java.util.Map; 1.12 import java.util.Optional; 1.13 import javax.servlet.ServletException; 1.14 import javax.servlet.http.HttpServlet; 1.15 import javax.servlet.http.HttpServletRequest; 1.16 import javax.servlet.http.HttpServletResponse; 1.17 +import javax.servlet.http.HttpSession; 1.18 import org.slf4j.Logger; 1.19 import org.slf4j.LoggerFactory; 1.20 1.21 @@ -49,6 +53,8 @@ 1.22 1.23 private static final Logger LOG = LoggerFactory.getLogger(AbstractLightPITServlet.class); 1.24 1.25 + private static final String HTML_FULL_DISPATCHER = Functions.jspPath("html_full"); 1.26 + 1.27 /** 1.28 * Store a reference to the annotation for quicker access. 1.29 */ 1.30 @@ -164,29 +170,43 @@ 1.31 LOG.trace("{} destroyed", getServletName()); 1.32 } 1.33 1.34 + /** 1.35 + * Sets the name of the dynamic fragment. 1.36 + * 1.37 + * It is sufficient to specify the name without any extension. The extension 1.38 + * is added automatically if not specified. 1.39 + * 1.40 + * The fragment must be located in the dynamic fragments folder. 1.41 + * 1.42 + * @param req the servlet request object 1.43 + * @param fragmentName the name of the fragment 1.44 + * @see Constants#DYN_FRAGMENT_PATH_PREFIX 1.45 + */ 1.46 + public void setDynamicFragment(HttpServletRequest req, String fragmentName) { 1.47 + req.setAttribute(Constants.REQ_ATTR_FRAGMENT, Functions.dynFragmentPath(fragmentName)); 1.48 + } 1.49 1.50 /** 1.51 - * Sets several requests attributes, that can be used by the JSP. 1.52 + * Specifies the name of an additional stylesheet used by the module. 1.53 + * 1.54 + * Setting an additional stylesheet is optional, but quite common for HTML 1.55 + * output. 1.56 + * 1.57 + * It is sufficient to specify the name without any extension. The extension 1.58 + * is added automatically if not specified. 1.59 * 1.60 * @param req the servlet request object 1.61 - * @see Constants#REQ_ATTR_PATH 1.62 - * @see Constants#REQ_ATTR_MODULE_CLASSNAME 1.63 - * @see Constants#REQ_ATTR_MODULE_INFO 1.64 + * @param stylesheet the name of the stylesheet 1.65 */ 1.66 - private void setGenericRequestAttributes(HttpServletRequest req) { 1.67 - req.setAttribute(Constants.REQ_ATTR_PATH, Functions.fullPath(req)); 1.68 - 1.69 - req.setAttribute(Constants.REQ_ATTR_MODULE_CLASSNAME, this.getClass().getName()); 1.70 - 1.71 - moduleInfoELProxy.ifPresent((proxy) -> req.setAttribute(Constants.REQ_ATTR_MODULE_INFO, proxy)); 1.72 + public void setStylesheet(HttpServletRequest req, String stylesheet) { 1.73 + req.setAttribute(Constants.REQ_ATTR_STYLESHEET, Functions.enforceExt(stylesheet, ".css")); 1.74 } 1.75 1.76 private void forwardToFullView(HttpServletRequest req, HttpServletResponse resp) 1.77 throws IOException, ServletException { 1.78 1.79 - setGenericRequestAttributes(req); 1.80 req.setAttribute(Constants.REQ_ATTR_MENU, getModuleManager().getMainMenu()); 1.81 - req.getRequestDispatcher(Functions.jspPath("full.jsp")).forward(req, resp); 1.82 + req.getRequestDispatcher(HTML_FULL_DISPATCHER).forward(req, resp); 1.83 } 1.84 1.85 private Optional<HandlerMethod> findMapping(HttpMethod method, HttpServletRequest req) { 1.86 @@ -212,6 +232,22 @@ 1.87 1.88 private void doProcess(HttpMethod method, HttpServletRequest req, HttpServletResponse resp) 1.89 throws ServletException, IOException { 1.90 + 1.91 + HttpSession session = req.getSession(); 1.92 + 1.93 + // choose the requested language as session language (if available) or fall back to english, otherwise 1.94 + if (session.getAttribute(Constants.SESSION_ATTR_LANGUAGE) == null) { 1.95 + Optional<List<String>> availableLanguages = Functions.availableLanguages(getServletContext()).map(Arrays::asList); 1.96 + Optional<Locale> reqLocale = Optional.of(req.getLocale()); 1.97 + Locale sessionLocale = reqLocale.filter((rl) -> availableLanguages.map((al) -> al.contains(rl.getLanguage())).orElse(false)).orElse(Locale.ENGLISH); 1.98 + session.setAttribute(Constants.SESSION_ATTR_LANGUAGE, sessionLocale); 1.99 + LOG.debug("Settng language for new session {}: {}", session.getId(), sessionLocale.getDisplayLanguage()); 1.100 + } 1.101 + 1.102 + req.setAttribute(Constants.REQ_ATTR_PATH, Functions.fullPath(req)); 1.103 + req.setAttribute(Constants.REQ_ATTR_MODULE_CLASSNAME, this.getClass().getName()); 1.104 + moduleInfoELProxy.ifPresent((proxy) -> req.setAttribute(Constants.REQ_ATTR_MODULE_INFO, proxy)); 1.105 + 1.106 Optional<HandlerMethod> mapping = findMapping(method, req); 1.107 if (mapping.isPresent()) { 1.108 forwardAsSepcified(mapping.get().apply(req, resp), req, resp);
2.1 --- a/src/java/de/uapcore/lightpit/Constants.java Sat Dec 23 17:28:19 2017 +0100 2.2 +++ b/src/java/de/uapcore/lightpit/Constants.java Tue Dec 26 17:36:47 2017 +0100 2.3 @@ -31,11 +31,23 @@ 2.4 import static de.uapcore.lightpit.Functions.fqn; 2.5 2.6 /** 2.7 - * Contains all constants used by the this application. 2.8 + * Contains all non-local scope constants used by the this application. 2.9 + * 2.10 + * Constants with (class) local scope are defined in their respective classes. 2.11 */ 2.12 public final class Constants { 2.13 public static final String JSP_PATH_PREFIX = "/WEB-INF/jsp/"; 2.14 2.15 + public static final String JSPF_PATH_PREFIX = "/WEB-INF/jspf/"; 2.16 + 2.17 + public static final String DYN_FRAGMENT_PATH_PREFIX = "/WEB-INF/dynamic_fragments/"; 2.18 + 2.19 + 2.20 + /** 2.21 + * Name for the context parameter specifying the available languages. 2.22 + */ 2.23 + public static final String CTX_ATTR_LANGUAGES = "available-languages"; 2.24 + 2.25 /** 2.26 * Key for the request attribute containing the class name of the currently dispatching module. 2.27 */ 2.28 @@ -55,6 +67,22 @@ 2.29 * Key for the request attribute containing the full path information (servlet path + path info). 2.30 */ 2.31 public static final String REQ_ATTR_PATH = fqn(AbstractLightPITServlet.class, "path"); 2.32 + 2.33 + /** 2.34 + * Key for the name of the fragment which should be rendered. 2.35 + */ 2.36 + public static final String REQ_ATTR_FRAGMENT = fqn(AbstractLightPITServlet.class, "fragment"); 2.37 + 2.38 + /** 2.39 + * Key for the name of the additional stylesheet used by a module. 2.40 + */ 2.41 + public static final String REQ_ATTR_STYLESHEET = fqn(AbstractLightPITServlet.class, "extraCss"); 2.42 + 2.43 + 2.44 + /** 2.45 + * Key for the current language selection within the session. 2.46 + */ 2.47 + public static final String SESSION_ATTR_LANGUAGE = fqn(AbstractLightPITServlet.class, "language"); 2.48 2.49 /** 2.50 * This class is not instantiatable.
3.1 --- a/src/java/de/uapcore/lightpit/Functions.java Sat Dec 23 17:28:19 2017 +0100 3.2 +++ b/src/java/de/uapcore/lightpit/Functions.java Tue Dec 26 17:36:47 2017 +0100 3.3 @@ -29,6 +29,7 @@ 3.4 package de.uapcore.lightpit; 3.5 3.6 import java.util.Optional; 3.7 +import javax.servlet.ServletContext; 3.8 import javax.servlet.http.HttpServletRequest; 3.9 import org.slf4j.Logger; 3.10 import org.slf4j.LoggerFactory; 3.11 @@ -40,8 +41,24 @@ 3.12 3.13 private static final Logger LOG = LoggerFactory.getLogger(Functions.class); 3.14 3.15 + public static Optional<String[]> availableLanguages(ServletContext ctx) { 3.16 + return Optional.ofNullable(ctx.getInitParameter(Constants.CTX_ATTR_LANGUAGES)).map((x) -> x.split("\\s*,\\s*")); 3.17 + } 3.18 + 3.19 + public static String enforceExt(String filename, String ext) { 3.20 + return filename.endsWith(ext) ? filename : filename + ext; 3.21 + } 3.22 + 3.23 public static String jspPath(String filename) { 3.24 - return Constants.JSP_PATH_PREFIX + filename; 3.25 + return enforceExt(Constants.JSP_PATH_PREFIX + filename, ".jsp"); 3.26 + } 3.27 + 3.28 + public static String jspfPath(String filename) { 3.29 + return enforceExt(Constants.JSPF_PATH_PREFIX + filename, ".jspf"); 3.30 + } 3.31 + 3.32 + public static String dynFragmentPath(String filename) { 3.33 + return enforceExt(Constants.DYN_FRAGMENT_PATH_PREFIX + filename, ".jsp"); 3.34 } 3.35 3.36 public static String fqn(String base, String name) {
4.1 --- a/src/java/de/uapcore/lightpit/modules/LanguageModule.java Sat Dec 23 17:28:19 2017 +0100 4.2 +++ b/src/java/de/uapcore/lightpit/modules/LanguageModule.java Tue Dec 26 17:36:47 2017 +0100 4.3 @@ -30,12 +30,22 @@ 4.4 4.5 import de.uapcore.lightpit.LightPITModule; 4.6 import de.uapcore.lightpit.AbstractLightPITServlet; 4.7 +import de.uapcore.lightpit.Constants; 4.8 +import de.uapcore.lightpit.Functions; 4.9 import de.uapcore.lightpit.HttpMethod; 4.10 import javax.servlet.annotation.WebServlet; 4.11 import javax.servlet.http.HttpServletRequest; 4.12 import javax.servlet.http.HttpServletResponse; 4.13 import de.uapcore.lightpit.RequestMapping; 4.14 import de.uapcore.lightpit.ResponseType; 4.15 +import java.util.ArrayList; 4.16 +import java.util.IllformedLocaleException; 4.17 +import java.util.List; 4.18 +import java.util.Locale; 4.19 +import java.util.Optional; 4.20 +import javax.servlet.ServletException; 4.21 +import org.slf4j.Logger; 4.22 +import org.slf4j.LoggerFactory; 4.23 4.24 4.25 @LightPITModule( 4.26 @@ -48,9 +58,42 @@ 4.27 ) 4.28 public final class LanguageModule extends AbstractLightPITServlet { 4.29 4.30 + private static final Logger LOG = LoggerFactory.getLogger(LanguageModule.class); 4.31 + 4.32 + private List<Locale> languages = new ArrayList<>(); 4.33 + 4.34 + @Override 4.35 + public void init() throws ServletException { 4.36 + super.init(); 4.37 + 4.38 + Optional<String[]> langs = Functions.availableLanguages(getServletContext()); 4.39 + if (langs.isPresent()) { 4.40 + for (String lang : langs.get()) { 4.41 + try { 4.42 + Locale locale = Locale.forLanguageTag(lang); 4.43 + if (locale.getLanguage().isEmpty()) { 4.44 + throw new IllformedLocaleException(); 4.45 + } 4.46 + languages.add(locale); 4.47 + } catch (IllformedLocaleException ex) { 4.48 + LOG.warn("Specified lanaguge {} in context parameter cannot be mapped to an existing locale - skipping.", lang); 4.49 + } 4.50 + } 4.51 + 4.52 + } else { 4.53 + languages.add(Locale.ENGLISH); 4.54 + LOG.warn("Context parameter 'available-languges' not found. Only english will be available."); 4.55 + } 4.56 + } 4.57 + 4.58 @RequestMapping(method = HttpMethod.GET) 4.59 public ResponseType handle(HttpServletRequest req, HttpServletResponse resp) { 4.60 + 4.61 + req.setAttribute("languages", languages); 4.62 + req.setAttribute("browserLanguage", req.getLocale()); 4.63 4.64 + setStylesheet(req, "language"); 4.65 + setDynamicFragment(req, "language"); 4.66 return ResponseType.HTML_FULL; 4.67 } 4.68 }
5.1 --- a/src/java/de/uapcore/lightpit/resources/localization/language.properties Sat Dec 23 17:28:19 2017 +0100 5.2 +++ b/src/java/de/uapcore/lightpit/resources/localization/language.properties Tue Dec 26 17:36:47 2017 +0100 5.3 @@ -22,3 +22,5 @@ 5.4 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 5.5 5.6 menuLabel = Languages 5.7 +submit = Switch language 5.8 +browserLanguage = Browser language
6.1 --- a/src/java/de/uapcore/lightpit/resources/localization/language_de.properties Sat Dec 23 17:28:19 2017 +0100 6.2 +++ b/src/java/de/uapcore/lightpit/resources/localization/language_de.properties Tue Dec 26 17:36:47 2017 +0100 6.3 @@ -22,3 +22,5 @@ 6.4 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 6.5 6.6 menuLabel = Sprache 6.7 +submit = Sprache ausw\u00e4hlen 6.8 +browserLanguage = Browsersprache
7.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 7.2 +++ b/src/java/logging.properties Tue Dec 26 17:36:47 2017 +0100 7.3 @@ -0,0 +1,27 @@ 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 + 7.28 +handlers= java.util.logging.ConsoleHandler 7.29 + 7.30 +.level = DEBUG
8.1 --- a/web/META-INF/context.xml Sat Dec 23 17:28:19 2017 +0100 8.2 +++ b/web/META-INF/context.xml Tue Dec 26 17:36:47 2017 +0100 8.3 @@ -1,2 +1,2 @@ 8.4 <?xml version="1.0" encoding="UTF-8"?> 8.5 -<Context path="/lightpit"/> 8.6 +<Context path="/lightpit" />
9.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 9.2 +++ b/web/WEB-INF/dynamic_fragments/language.jsp Tue Dec 26 17:36:47 2017 +0100 9.3 @@ -0,0 +1,46 @@ 9.4 +<%-- 9.5 +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 9.6 + 9.7 +Copyright 2017 Mike Becker. All rights reserved. 9.8 + 9.9 +Redistribution and use in source and binary forms, with or without 9.10 +modification, are permitted provided that the following conditions are met: 9.11 + 9.12 +1. Redistributions of source code must retain the above copyright 9.13 +notice, this list of conditions and the following disclaimer. 9.14 + 9.15 +2. Redistributions in binary form must reproduce the above copyright 9.16 +notice, this list of conditions and the following disclaimer in the 9.17 +documentation and/or other materials provided with the distribution. 9.18 + 9.19 +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 9.20 +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 9.21 +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 9.22 +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 9.23 +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 9.24 +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 9.25 +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 9.26 +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 9.27 +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 9.28 +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 9.29 +--%> 9.30 +<%@page pageEncoding="UTF-8" session="true" %> 9.31 +<%@page import="de.uapcore.lightpit.Constants" %> 9.32 +<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 9.33 +<%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> 9.34 + 9.35 +<c:set scope="page" var="currentLanguage" value="${sessionScope[Constants.SESSION_ATTR_LANGUAGE]}" /> 9.36 + 9.37 +<fmt:bundle basename="${requestScope[Constants.REQ_ATTR_MODULE_INFO].bundleBaseName}"> 9.38 +<form method="POST"id="lang-selector"> 9.39 + <c:forEach items="${languages}" var="l"> 9.40 + <label> 9.41 + <input type="radio" name="language" value="${l.language}" 9.42 + <c:if test="${l.language eq currentLanguage.language}">checked</c:if>/> 9.43 + ${l.displayLanguage} 9.44 + (${l.getDisplayLanguage(currentLanguage)}<c:if test="${not empty browserLanguage and l.language eq browserLanguage.language}"> - <fmt:message key="browserLanguage"/></c:if>) 9.45 + </label> 9.46 + </c:forEach> 9.47 + <input type="submit" value="<fmt:message key="submit" />"/> 9.48 +</form> 9.49 +</fmt:bundle>
10.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 10.2 +++ b/web/WEB-INF/jsp/html_full.jsp Tue Dec 26 17:36:47 2017 +0100 10.3 @@ -0,0 +1,85 @@ 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 ARE 10.22 +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 10.23 +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 10.24 +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 10.25 +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 10.26 +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 10.27 +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 10.28 +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 10.29 +--%> 10.30 +<%@page contentType="text/html" pageEncoding="UTF-8" trimDirectiveWhitespaces="true" session="true" %> 10.31 +<%@page import="de.uapcore.lightpit.Constants" %> 10.32 +<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 10.33 +<%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> 10.34 +<%@taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %> 10.35 + 10.36 +<%-- Define an alias for the main menu --%> 10.37 +<c:set scope="page" var="mainMenu" value="${requestScope[Constants.REQ_ATTR_MENU]}"/> 10.38 + 10.39 +<%-- Define an alias for the fragment name --%> 10.40 +<c:set scope="page" var="fragment" value="${requestScope[Constants.REQ_ATTR_FRAGMENT]}"/> 10.41 + 10.42 +<%-- Define an alias for the additional stylesheet --%> 10.43 +<c:set scope="page" var="extraCss" value="${requestScope[Constants.REQ_ATTR_STYLESHEET]}"/> 10.44 + 10.45 +<%-- Define an alias for the module info --%> 10.46 +<c:set scope="page" var="moduleInfo" value="${requestScope[Constants.REQ_ATTR_MODULE_INFO]}"/> 10.47 + 10.48 +<!DOCTYPE html> 10.49 +<html> 10.50 + <head> 10.51 + <base href="${pageContext.request.scheme}://${pageContext.request.serverName}:${pageContext.request.serverPort}${pageContext.request.contextPath}/"> 10.52 + <title>LightPIT - 10.53 + <fmt:bundle basename="${moduleInfo.bundleBaseName}"> 10.54 + <fmt:message key="${moduleInfo.titleKey}" /> 10.55 + </fmt:bundle> 10.56 + </title> 10.57 + <meta charset="UTF-8"> 10.58 + <link rel="stylesheet" href="lightpit.css" type="text/css"> 10.59 + <c:if test="${not empty extraCss}"> 10.60 + <link rel="stylesheet" href="${extraCss}" type="text/css"> 10.61 + </c:if> 10.62 + </head> 10.63 + <body> 10.64 + <div id="mainMenu"> 10.65 + <c:forEach var="menu" items="${mainMenu}"> 10.66 + <div class="menuEntry" 10.67 + <c:if test="${requestScope[Constants.REQ_ATTR_MODULE_CLASSNAME] eq menu.moduleClassName}"> 10.68 + data-active 10.69 + </c:if> 10.70 + > 10.71 + <a href="${menu.pathName}"> 10.72 + <fmt:bundle basename="${menu.resourceKey.bundle}"> 10.73 + <fmt:message key="${menu.resourceKey.key}" /> 10.74 + </fmt:bundle> 10.75 + </a> 10.76 + </div> 10.77 + </c:forEach> 10.78 + </div> 10.79 + <div id="subMenu"> 10.80 + 10.81 + </div> 10.82 + <div id="content-area"> 10.83 + <c:if test="${not empty fragment}"> 10.84 + <c:import url="${fragment}" /> 10.85 + </c:if> 10.86 + </div> 10.87 + </body> 10.88 +</html>
11.1 --- a/web/WEB-INF/web.xml Sat Dec 23 17:28:19 2017 +0100 11.2 +++ b/web/WEB-INF/web.xml Tue Dec 26 17:36:47 2017 +0100 11.3 @@ -5,4 +5,8 @@ 11.4 30 11.5 </session-timeout> 11.6 </session-config> 11.7 + <context-param> 11.8 + <param-name>available-languages</param-name> 11.9 + <param-value>en,de</param-value> 11.10 + </context-param> 11.11 </web-app>
12.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 12.2 +++ b/web/language.css Tue Dec 26 17:36:47 2017 +0100 12.3 @@ -0,0 +1,39 @@ 12.4 +/* 12.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 12.6 + * 12.7 + * Copyright 2017 Mike Becker. All rights reserved. 12.8 + * 12.9 + * Redistribution and use in source and binary forms, with or without 12.10 + * modification, are permitted provided that the following conditions are met: 12.11 + * 12.12 + * 1. Redistributions of source code must retain the above copyright 12.13 + * notice, this list of conditions and the following disclaimer. 12.14 + * 12.15 + * 2. Redistributions in binary form must reproduce the above copyright 12.16 + * notice, this list of conditions and the following disclaimer in the 12.17 + * documentation and/or other materials provided with the distribution. 12.18 + * 12.19 + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 12.20 + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 12.21 + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 12.22 + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 12.23 + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 12.24 + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 12.25 + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 12.26 + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 12.27 + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 12.28 + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 12.29 + * POSSIBILITY OF SUCH DAMAGE. 12.30 + * 12.31 + */ 12.32 + 12.33 +#lang-selector { 12.34 + max-width: 30%; 12.35 + display: flex; 12.36 + flex-basis: content; 12.37 + flex-direction: column; 12.38 +} 12.39 + 12.40 +input { 12.41 + margin: .5em; 12.42 +}