Sat, 09 Nov 2024 11:47:20 +0100
change language menu to settings menu and add timezone settings - fixes #402
--- a/src/main/kotlin/de/uapcore/lightpit/AbstractServlet.kt Sat Nov 09 10:54:57 2024 +0100 +++ b/src/main/kotlin/de/uapcore/lightpit/AbstractServlet.kt Sat Nov 09 11:47:20 2024 +0100 @@ -33,12 +33,15 @@ import jakarta.servlet.http.HttpServletRequest import jakarta.servlet.http.HttpServletResponse import java.sql.SQLException +import java.time.ZoneId import java.util.* abstract class AbstractServlet : HttpServlet() { companion object { + const val COOKIE_MAX_AGE = 2592000 // 30 days const val LANGUAGE_COOKIE_NAME = "lpit_language" + const val TIMEZONE_COOKIE_NAME = "lpit_timezone" } protected val logger = MyLogger() @@ -138,6 +141,18 @@ logger.trace("Continuing session {0} with language {1}", session.id, sessionLocale) } + // determine the timezone + if (session.getAttribute(Constants.SESSION_ATTR_TIMEZONE) == null) { + // timezone selection stored in cookie + val cookieTimezone = cookieTimezone(http) + + // if no cookie, fall back to server's timezone (the browser does not transmit one) + val timezone = cookieTimezone ?: ZoneId.systemDefault() + + selectTimezone(http, timezone) + logger.debug("Timezone for session {0} set to {1}", session.id, timezone) + } + // if this is an error path, bypass the normal flow if (fullPath.startsWith("/error/")) { http.styleSheets = listOf("error") @@ -198,6 +213,23 @@ http.request.cookies?.firstOrNull { c -> c.name == LANGUAGE_COOKIE_NAME } ?.runCatching {Locale.forLanguageTag(this.value)}?.getOrNull() + protected fun sessionLanguage(http: HttpRequest) = http.session.getAttribute(Constants.SESSION_ATTR_LANGUAGE) as Locale + + private fun cookieTimezone(http: HttpRequest): ZoneId? = + http.request.cookies?.firstOrNull { c -> c.name == TIMEZONE_COOKIE_NAME } + ?.runCatching { ZoneId.of(this.value)}?.getOrNull() + + protected fun sessionTimezone(http: HttpRequest) = http.session.getAttribute(Constants.SESSION_ATTR_TIMEZONE) as String + + protected fun selectTimezone(http: HttpRequest, zoneId: ZoneId) { + http.session.setAttribute(Constants.SESSION_ATTR_TIMEZONE, zoneId.id) + val cookie = Cookie(TIMEZONE_COOKIE_NAME, zoneId.id) + cookie.isHttpOnly = true + cookie.path = http.request.contextPath + cookie.maxAge = COOKIE_MAX_AGE + http.response.addCookie(cookie) + } + protected fun selectLanguage(http: HttpRequest, locale: Locale) { http.response.locale = locale http.session.setAttribute(Constants.SESSION_ATTR_LANGUAGE, locale) @@ -209,7 +241,7 @@ cookie.maxAge = 0 } else { cookie.value = locale.language - cookie.maxAge = 2592000 // 30 days + cookie.maxAge = COOKIE_MAX_AGE } http.response.addCookie(cookie) }
--- a/src/main/kotlin/de/uapcore/lightpit/Constants.kt Sat Nov 09 10:54:57 2024 +0100 +++ b/src/main/kotlin/de/uapcore/lightpit/Constants.kt Sat Nov 09 11:47:20 2024 +0100 @@ -106,4 +106,9 @@ * Key for the current language selection within the session. */ const val SESSION_ATTR_LANGUAGE = "language" + + /** + * Key for the current timezone selection within the session. + */ + const val SESSION_ATTR_TIMEZONE = "timezone" }
--- a/src/main/kotlin/de/uapcore/lightpit/servlet/LanguageServlet.kt Sat Nov 09 10:54:57 2024 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,68 +0,0 @@ -/* - * Copyright 2021 Mike Becker. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package de.uapcore.lightpit.servlet - -import de.uapcore.lightpit.AbstractServlet -import de.uapcore.lightpit.Constants -import de.uapcore.lightpit.HttpRequest -import de.uapcore.lightpit.dao.DataAccessObject -import de.uapcore.lightpit.viewmodel.LanguageView -import jakarta.servlet.annotation.WebServlet -import java.util.* - -@WebServlet(urlPatterns = ["/language/*"]) -class LanguageServlet : AbstractServlet() { - - init { - get("/", this::viewLanguages) - post("/", this::selectLanguage) - } - - private fun viewLanguages(http: HttpRequest, dao: DataAccessObject) { - with(http) { - view = LanguageView( - availableLanguages(), - session.getAttribute(Constants.SESSION_ATTR_LANGUAGE) as Locale, - request.locale - ) - styleSheets = listOf("language") - render("language") - } - } - - private fun selectLanguage(http: HttpRequest, dao: DataAccessObject) { - val lang = http.param("language") - if (lang != null) { - val locale = Locale.forLanguageTag(lang) - if (!locale.language.isNullOrBlank()) { - super.selectLanguage(http, locale) - } - } - - viewLanguages(http, dao) - } - -} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/kotlin/de/uapcore/lightpit/servlet/SettingsServlet.kt Sat Nov 09 11:47:20 2024 +0100 @@ -0,0 +1,72 @@ +/* + * Copyright 2021 Mike Becker. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package de.uapcore.lightpit.servlet + +import de.uapcore.lightpit.AbstractServlet +import de.uapcore.lightpit.HttpRequest +import de.uapcore.lightpit.dao.DataAccessObject +import de.uapcore.lightpit.viewmodel.SettingsView +import jakarta.servlet.annotation.WebServlet +import java.time.ZoneId +import java.util.* + +@WebServlet(urlPatterns = ["/settings/*"]) +class SettingsServlet : AbstractServlet() { + + init { + get("/", this::viewSettings) + post("/", this::selectSettings) + } + + private fun viewSettings(http: HttpRequest, dao: DataAccessObject) { + with(http) { + view = SettingsView( + availableLanguages(), + sessionLanguage(http), + request.locale, + sessionTimezone(http) + ) + styleSheets = listOf("settings") + render("settings") + } + } + + private fun selectSettings(http: HttpRequest, dao: DataAccessObject) { + val lang = http.param("language") + if (lang != null) { + val locale = Locale.forLanguageTag(lang) + if (!locale.language.isNullOrBlank()) { + super.selectLanguage(http, locale) + } + } + + val timezone = http.param("timezone") + timezone?.runCatching { ZoneId.of(this) }?.onSuccess { selectTimezone(http, it) } + + viewSettings(http, dao) + } + +} \ No newline at end of file
--- a/src/main/kotlin/de/uapcore/lightpit/viewmodel/LanguageView.kt Sat Nov 09 10:54:57 2024 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,34 +0,0 @@ -/* - * Copyright 2021 Mike Becker. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package de.uapcore.lightpit.viewmodel - -import java.util.* - -class LanguageView( - val languages: List<Locale>, - val currentLanguage: Locale, - val browserLanguage: Locale -) : View() \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/kotlin/de/uapcore/lightpit/viewmodel/SettingsView.kt Sat Nov 09 11:47:20 2024 +0100 @@ -0,0 +1,38 @@ +/* + * Copyright 2021 Mike Becker. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package de.uapcore.lightpit.viewmodel + +import java.time.ZoneId +import java.util.* + +class SettingsView( + val languages: List<Locale>, + val currentLanguage: Locale, + val browserLanguage: Locale, + val currentTimezone: String +) : View() { + val timezones: List<String> = ZoneId.getAvailableZoneIds().sorted() +} \ No newline at end of file
--- a/src/main/resources/localization/strings.properties Sat Nov 09 10:54:57 2024 +0100 +++ b/src/main/resources/localization/strings.properties Sat Nov 09 11:47:20 2024 +0100 @@ -36,7 +36,6 @@ button.issue.create.another=Create another Issue button.issue.create=New Issue button.issue.edit=Edit -button.language.submit = Switch language button.okay=OK button.project.create=New Project button.project.details=Project Details @@ -138,8 +137,8 @@ language.browser.unavailable = Browser language not available. menu.about=About menu.issues=Issues -menu.languages=Language menu.projects=Projects +menu.settings=Settings menu.users=Developer navmenu.all=all navmenu.components=Components @@ -165,6 +164,8 @@ project.vcs=Version Control project.vcs.none=Do not analyze repository project=Project +settings.language=Language +settings.timezone=Timezone user.displayname=Developer user.givenname=Given Name user.lastname=Last Name
--- a/src/main/resources/localization/strings_de.properties Sat Nov 09 10:54:57 2024 +0100 +++ b/src/main/resources/localization/strings_de.properties Sat Nov 09 11:47:20 2024 +0100 @@ -36,7 +36,6 @@ button.issue.create.another=Weiteren Vorgang erstellen button.issue.create=Neuer Vorgang button.issue.edit=Bearbeiten -button.language.submit = Sprache ausw\u00e4hlen button.okay=OK button.project.create=Neues Projekt button.project.details=Projektdetails @@ -138,8 +137,8 @@ language.browser.unavailable = Browsersprache nicht verf\u00fcgbar. menu.about=Info menu.issues=Vorg\u00e4nge -menu.languages=Sprache menu.projects=Projekte +menu.settings=Einstellungen menu.users=Entwickler navmenu.all=Alle navmenu.components=Komponenten @@ -165,6 +164,8 @@ project.vcs=Versionskontrolle project.vcs.none=Keine Analyse durchf\u00fchren project=Projekt +settings.language=Sprache +settings.timezone=Zeitzone user.displayname=Entwickler user.givenname=Vorname user.lastname=Nachname
--- a/src/main/webapp/WEB-INF/changelogs/changelog-de.jspf Sat Nov 09 10:54:57 2024 +0100 +++ b/src/main/webapp/WEB-INF/changelogs/changelog-de.jspf Sat Nov 09 11:47:20 2024 +0100 @@ -27,6 +27,7 @@ <h3>Version 1.4.0 (Vorschau)</h3> <ul> + <li>Das Sprachmenü ist nun ein Einstellungsmenü, in dem auch die Zeitzone eingestellt werden kann.</li> <li>Abbrechen-Button zum Kommentar-Editor hinzugefügt.</li> <li>Die Vorgangsliste zeigt nun die Komponente, wenn kein Filter auf Komponenten aktiv ist.</li> <li>Vorgangsstatus zur Vorschlagsliste für das Erstellen einer Vorgangsbeziehung hinzugefügt.</li>
--- a/src/main/webapp/WEB-INF/changelogs/changelog.jspf Sat Nov 09 10:54:57 2024 +0100 +++ b/src/main/webapp/WEB-INF/changelogs/changelog.jspf Sat Nov 09 11:47:20 2024 +0100 @@ -27,6 +27,7 @@ <h3>Version 1.4.0 (snapshot)</h3> <ul> + <li>Change language menu to settings menu and add timezone settings.</li> <li>Add cancel button to comment editor.</li> <li>Add component tag to issue list when no component filter is active.</li> <li>Add issue status to the suggestions for creating an issue relation.</li>
--- a/src/main/webapp/WEB-INF/jsp/error.jsp Sat Nov 09 10:54:57 2024 +0100 +++ b/src/main/webapp/WEB-INF/jsp/error.jsp Sat Nov 09 11:47:20 2024 +0100 @@ -47,7 +47,7 @@ </tr> <tr> <th><fmt:message key="error.timestamp"/>:</th> - <td><fmt:formatDate type="both" value="<%= new java.util.Date()%>"/></td> + <td><fmt:formatDate type="both" value="<%= new java.util.Date()%>" timeZone="${sessionScope[Constants.SESSION_ATTR_TIMEZONE]}"/></td> </tr> <%--@elvariable id="exception" type="java.lang.Exception"--%> <c:if test="${not empty exception}">
--- a/src/main/webapp/WEB-INF/jsp/issue-form.jsp Sat Nov 09 10:54:57 2024 +0100 +++ b/src/main/webapp/WEB-INF/jsp/issue-form.jsp Sat Nov 09 11:47:20 2024 +0100 @@ -151,7 +151,7 @@ </c:if> <tr> <th><label for="issue-eta"><fmt:message key="issue.eta"/></label></th> - <td><input id="issue-eta" name="eta" type="date" value="<fmt:formatDate value="${issue.eta}" pattern="YYYY-MM-dd" />" /> </td> + <td><input id="issue-eta" name="eta" type="date" value="<fmt:formatDate value="${issue.eta}" pattern="YYYY-MM-dd" timeZone="${timezone}" />" /> </td> </tr> <c:if test="${issue.id ge 0}"> <tr>
--- a/src/main/webapp/WEB-INF/jsp/issue-view.jsp Sat Nov 09 10:54:57 2024 +0100 +++ b/src/main/webapp/WEB-INF/jsp/issue-view.jsp Sat Nov 09 11:47:20 2024 +0100 @@ -143,7 +143,7 @@ </tr> <tr> <th><fmt:message key="issue.eta"/></th> - <td><fmt:formatDate value="${issue.eta}" /></td> + <td><fmt:formatDate value="${issue.eta}" timeZone="${timezone}"/></td> </tr> </tbody> </table> @@ -298,10 +298,10 @@ </c:if> </div> <div class="smalltext"> - <fmt:formatDate type="BOTH" value="${comment.created}" /> + <fmt:formatDate type="BOTH" value="${comment.created}" timeZone="${timezone}" /> <c:if test="${comment.updateCount gt 0}"> <span class="comment-edit-info"> - (<fmt:message key="issue.comments.lastupdate"/> <fmt:formatDate type="BOTH" value="${comment.updated}" />, ${comment.updateCount} <fmt:message key="issue.comments.updateCount"/>) + (<fmt:message key="issue.comments.lastupdate"/> <fmt:formatDate type="BOTH" value="${comment.updated}" timeZone="${timezone}" />, ${comment.updateCount} <fmt:message key="issue.comments.updateCount"/>) </span> </c:if> </div>
--- a/src/main/webapp/WEB-INF/jsp/language.jsp Sat Nov 09 10:54:57 2024 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,48 +0,0 @@ -<%-- -DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - -Copyright 2021 Mike Becker. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright -notice, this list of conditions and the following disclaimer in the -documentation and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---%> -<%@page pageEncoding="UTF-8" %> -<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> -<%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> - -<jsp:useBean id="viewmodel" type="de.uapcore.lightpit.viewmodel.LanguageView" scope="request"/> - -<form method="POST" id="lang-selector"> - <c:forEach items="${viewmodel.languages}" var="l"> - <label> - <input type="radio" name="language" value="${l.language}" - <c:if test="${l.language eq viewmodel.currentLanguage.language}">checked</c:if>/> - ${l.displayLanguage} - (${l.getDisplayLanguage(viewmodel.currentLanguage)} - <c:if test="${not empty viewmodel.browserLanguage and l.language eq viewmodel.browserLanguage.language}"><c:set - var="browserLanguagePresent" value="true"/> - <fmt:message key="language.browser"/></c:if>) - </label> - </c:forEach> - <c:if test="${not browserLanguagePresent}"> - <span class="blNA"><fmt:message key="language.browser.unavailable"/></span> - </c:if> - <button type="submit"><fmt:message key="button.language.submit" /></button> -</form>
--- a/src/main/webapp/WEB-INF/jsp/project-details.jsp Sat Nov 09 10:54:57 2024 +0100 +++ b/src/main/webapp/WEB-INF/jsp/project-details.jsp Sat Nov 09 11:47:20 2024 +0100 @@ -69,7 +69,7 @@ <h2> <fmt:message key="version" /> <c:out value="${versionInfo.version.name}" /> - <fmt:message key="version.status.${versionInfo.version.status}"/> <c:if test="${not empty versionInfo.version.releaseOrEolDate}"> - (<fmt:formatDate type="date" value="${versionInfo.version.releaseOrEolDate}"/>) + (<fmt:formatDate type="date" value="${versionInfo.version.releaseOrEolDate}" timeZone="${timezone}"/>) </c:if> </h2>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/webapp/WEB-INF/jsp/settings.jsp Sat Nov 09 11:47:20 2024 +0100 @@ -0,0 +1,59 @@ +<%-- +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + +Copyright 2021 Mike Becker. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--%> +<%@page pageEncoding="UTF-8" %> +<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> +<%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> + +<jsp:useBean id="viewmodel" type="de.uapcore.lightpit.viewmodel.SettingsView" scope="request"/> + +<form method="POST" id="settings-form"> + <h2><fmt:message key="settings.language"/></h2> + <div class="flex-column"> + <c:forEach items="${viewmodel.languages}" var="l"> + <label> + <input type="radio" name="language" value="${l.language}" + <c:if test="${l.language eq viewmodel.currentLanguage.language}">checked</c:if>/> + ${l.displayLanguage} + (${l.getDisplayLanguage(viewmodel.currentLanguage)} + <c:if test="${not empty viewmodel.browserLanguage and l.language eq viewmodel.browserLanguage.language}"><c:set + var="browserLanguagePresent" value="true"/> - <fmt:message key="language.browser"/></c:if>) + </label> + </c:forEach> + <c:if test="${not browserLanguagePresent}"> + <span class="blNA"><fmt:message key="language.browser.unavailable"/></span> + </c:if> + </div> + <h2><fmt:message key="settings.timezone"/></h2> + <div> + <select name="timezone"> + <c:forEach items="${viewmodel.timezones}" var="tz"> + <option value="${tz}" <c:if test="${tz eq viewmodel.currentTimezone}">selected</c:if> >${tz}</option> + </c:forEach> + </select> + </div> + <button type="submit"><fmt:message key="button.apply" /></button> +</form>
--- a/src/main/webapp/WEB-INF/jsp/site.jsp Sat Nov 09 10:54:57 2024 +0100 +++ b/src/main/webapp/WEB-INF/jsp/site.jsp Sat Nov 09 11:47:20 2024 +0100 @@ -57,6 +57,9 @@ <%-- Define an alias for the optional JS file --%> <c:set scope="page" var="javascriptFile" value="${requestScope[Constants.REQ_ATTR_JAVASCRIPT]}"/> +<%-- Define an alias for timezone --%> +<c:set scope="page" var="timezone" value="${sessionScope[Constants.SESSION_ATTR_TIMEZONE]}" /> + <%-- Load resource bundle --%> <fmt:setLocale scope="request" value="${pageContext.response.locale}"/> <fmt:setBundle scope="request" basename="localization.strings"/> @@ -108,9 +111,9 @@ </a> </div> <div class="menuEntry" - <c:if test="${fn:startsWith(requestPath, '/language/')}">data-active</c:if> > - <a href="language/"> - <fmt:message key="menu.languages"/> + <c:if test="${fn:startsWith(requestPath, '/settings/')}">data-active</c:if> > + <a href="settings/"> + <fmt:message key="menu.settings"/> </a> </div> <div class="menuEntry"
--- a/src/main/webapp/WEB-INF/jsp/version-form.jsp Sat Nov 09 10:54:57 2024 +0100 +++ b/src/main/webapp/WEB-INF/jsp/version-form.jsp Sat Nov 09 11:47:20 2024 +0100 @@ -75,13 +75,13 @@ <tr> <th><label for="version-release"><fmt:message key="version.release"/></label></th> <td> - <input id="version-release" name="release" type="date" value="<fmt:formatDate value="${version.release}" pattern="YYYY-MM-dd" />"/> + <input id="version-release" name="release" type="date" value="<fmt:formatDate value="${version.release}" pattern="YYYY-MM-dd" timeZone="${timezone}" />"/> </td> </tr> <tr> <th><label for="version-eol"><fmt:message key="version.eol"/></label></th> <td> - <input id="version-eol" name="eol" type="date" value="<fmt:formatDate value="${version.eol}" pattern="YYYY-MM-dd" />"/> + <input id="version-eol" name="eol" type="date" value="<fmt:formatDate value="${version.eol}" pattern="YYYY-MM-dd" timeZone="${timezone}" />"/> </td> </tr> </tbody>
--- a/src/main/webapp/WEB-INF/jsp/versions.jsp Sat Nov 09 10:54:57 2024 +0100 +++ b/src/main/webapp/WEB-INF/jsp/versions.jsp Sat Nov 09 11:47:20 2024 +0100 @@ -87,7 +87,7 @@ </a> <div class="version-tag version-${versionInfo.version.status}" <c:if test="${not empty versionInfo.version.releaseOrEolDate}"> - title="<fmt:formatDate type="date" value="${versionInfo.version.releaseOrEolDate}"/>" + title="<fmt:formatDate type="date" value="${versionInfo.version.releaseOrEolDate}" timeZone="${timezone}"/>" </c:if> > <fmt:message key="version.status.${versionInfo.version.status}"/>
--- a/src/main/webapp/WEB-INF/jspf/date-with-tooltip.jspf Sat Nov 09 10:54:57 2024 +0100 +++ b/src/main/webapp/WEB-INF/jspf/date-with-tooltip.jspf Sat Nov 09 11:47:20 2024 +0100 @@ -28,6 +28,6 @@ dateValue: DateTime --%> -<span title="<fmt:formatDate value="${dateValue}" type="time"/>"> - <fmt:formatDate value="${dateValue}" type="date"/> +<span title="<fmt:formatDate value="${dateValue}" type="time" timeZone="${timezone}"/>"> + <fmt:formatDate value="${dateValue}" type="date" timeZone="${timezone}"/> </span>
--- a/src/main/webapp/WEB-INF/jspf/issue-list.jspf Sat Nov 09 10:54:57 2024 +0100 +++ b/src/main/webapp/WEB-INF/jspf/issue-list.jspf Sat Nov 09 11:47:20 2024 +0100 @@ -61,12 +61,12 @@ </td> <td> <span class="nowrap <c:if test="${issue.overdue}">eta-overdue</c:if> "> - <fmt:formatDate value="${issue.eta}" /> + <fmt:formatDate value="${issue.eta}" timeZone="${timezone}" /> </span> </td> <td> <span class="nowrap"> - <fmt:formatDate value="${issue.updated}" /> + <fmt:formatDate value="${issue.updated}" timeZone="${timezone}" /> </span> </td> <td>
--- a/src/main/webapp/language.css Sat Nov 09 10:54:57 2024 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,51 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2021 Mike Becker. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ - -#lang-selector { - display: flex; - flex-basis: content; - flex-direction: column; - align-items: flex-start; -} - -input { - margin: .5em; -} - -button { - margin-top: 1.5em; -} - -/* browser language not available */ -span.blNA { - margin: .5em; - color: red; - font-style: italic; - font-size: smaller; -}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/webapp/settings.css Sat Nov 09 11:47:20 2024 +0100 @@ -0,0 +1,51 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2021 Mike Becker. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +.flex-column { + display: flex; + flex-basis: content; + flex-direction: column; + align-items: flex-start; +} + +input { + margin: .5em; +} + +button { + margin-top: 1.5em; +} + +/* browser language not available */ +span.blNA { + margin: .5em; + color: red; + font-style: italic; + font-size: smaller; +}