src/main/kotlin/de/uapcore/lightpit/AbstractServlet.kt

changeset 367
0a9065936aac
parent 358
e46bef1bdddd
--- a/src/main/kotlin/de/uapcore/lightpit/AbstractServlet.kt	Fri Mar 14 08:09:05 2025 +0100
+++ b/src/main/kotlin/de/uapcore/lightpit/AbstractServlet.kt	Sat May 17 17:39:48 2025 +0200
@@ -28,6 +28,7 @@
 import de.uapcore.lightpit.DataSourceProvider.Companion.SC_ATTR_NAME
 import de.uapcore.lightpit.dao.DataAccessObject
 import de.uapcore.lightpit.dao.createDataAccessObject
+import de.uapcore.lightpit.entities.User
 import jakarta.servlet.http.Cookie
 import jakarta.servlet.http.HttpServlet
 import jakarta.servlet.http.HttpServletRequest
@@ -35,6 +36,7 @@
 import java.sql.SQLException
 import java.time.ZoneId
 import java.util.*
+import java.sql.Date as SqlDate
 
 abstract class AbstractServlet : HttpServlet() {
 
@@ -89,8 +91,10 @@
     ) {
         val params = mapping.first.obtainPathParameters(sanitizedRequestPath(req))
         val method = mapping.second
+        val authenticatedUser = req.remoteUser?.let(dao::findUserByName)
+        showWhatsNewPopup(authenticatedUser, req, dao)
         logger.trace("invoke {0}", method)
-        method(HttpRequest(req, resp, params), dao)
+        method(HttpRequest(authenticatedUser, req, resp, params), dao)
     }
 
     private fun sanitizedRequestPath(req: HttpServletRequest) = req.pathInfo ?: "/"
@@ -124,9 +128,7 @@
         req.characterEncoding = "UTF-8"
 
         // set some internal request attributes
-        val http = HttpRequest(req, resp)
         val fullPath = req.servletPath + Optional.ofNullable(req.pathInfo).orElse("")
-        req.setAttribute(Constants.REQ_ATTR_BASE_HREF, http.baseHref)
         req.setAttribute(Constants.REQ_ATTR_PATH, fullPath)
         req.getHeader("Referer")?.let {
             // TODO: add a sanity check to avoid link injection
@@ -136,7 +138,7 @@
         // choose the requested language as session language (if available)
         if (session.getAttribute(Constants.SESSION_ATTR_LANGUAGE) == null) {
             // language selection stored in cookie
-            val cookieLocale = cookieLanguage(http)
+            val cookieLocale = cookieLanguage(req)
 
             // if no cookie, fall back to request locale a.k.a "Browser Language"
             val reqLocale = cookieLocale ?: req.locale
@@ -145,7 +147,7 @@
             val sessionLocale = if (availableLanguages.contains(reqLocale)) reqLocale else availableLanguages.first()
 
             // select the language (this will also refresh the cookie max-age)
-            selectLanguage(http, sessionLocale)
+            selectLanguage(req, resp, sessionLocale)
 
             logger.debug(
                 "Setting language for new session {0}: {1}", session.id, sessionLocale.displayLanguage
@@ -159,17 +161,18 @@
         // determine the timezone
         if (session.getAttribute(Constants.SESSION_ATTR_TIMEZONE) == null) {
             // timezone selection stored in cookie
-            val cookieTimezone = cookieTimezone(http)
+            val cookieTimezone = cookieTimezone(req)
 
             // if no cookie, fall back to server's timezone (the browser does not transmit one)
             val timezone = cookieTimezone ?: ZoneId.systemDefault()
 
-            selectTimezone(http, timezone)
+            selectTimezone(req, resp, 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/")) {
+            val http = HttpRequest(null, req, resp)
             http.styleSheets = listOf("error")
             http.render("error")
             return
@@ -210,6 +213,16 @@
         }
     }
 
+    private fun showWhatsNewPopup(user: User?, req: HttpServletRequest, dao: DataAccessObject) {
+        if (user == null) return
+        logger.trace("show user with ID {0} what's new", user.id)
+        val userKnowsUpdatesUntil = dao.untilWhenUserKnowsUpdates(user)
+        if (userKnowsUpdatesUntil == null || userKnowsUpdatesUntil.before(SqlDate.valueOf(Constants.VERSION_DATE))) {
+            dao.updateUserKnowsUpdates(user)
+            req.setAttribute("showWhatsNew", true)
+        }
+    }
+
     override fun doGet(req: HttpServletRequest, resp: HttpServletResponse) {
         doProcess(req, resp, getMappings)
     }
@@ -224,40 +237,48 @@
         return locales.ifEmpty { listOf(Locale.ENGLISH) }
     }
 
-    private fun cookieLanguage(http: HttpRequest): Locale? =
-        http.request.cookies?.firstOrNull { c -> c.name == LANGUAGE_COOKIE_NAME }
+    private fun cookieLanguage(request: HttpServletRequest): Locale? =
+        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 }
+    private fun cookieTimezone(request: HttpServletRequest): ZoneId? =
+        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)
+        selectTimezone(http.request, http.response, zoneId)
+    }
+
+    private fun selectTimezone(request: HttpServletRequest, response: HttpServletResponse, zoneId: ZoneId) {
+        request.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.path = request.contextPath
         cookie.maxAge = COOKIE_MAX_AGE
-        http.response.addCookie(cookie)
+        response.addCookie(cookie)
     }
 
     protected fun selectLanguage(http: HttpRequest, locale: Locale) {
-        http.response.locale = locale
-        http.session.setAttribute(Constants.SESSION_ATTR_LANGUAGE, locale)
+        selectLanguage(http.request, http.response, locale)
+    }
+
+    private fun selectLanguage(request: HttpServletRequest, response: HttpServletResponse, locale: Locale) {
+        response.locale = locale
+        request.session.setAttribute(Constants.SESSION_ATTR_LANGUAGE, locale)
         // delete cookie if language selection matches request locale, otherwise set cookie
         val cookie = Cookie(LANGUAGE_COOKIE_NAME, "")
         cookie.isHttpOnly = true
-        cookie.path = http.request.contextPath
-        if (http.request.locale.language == locale.language) {
+        cookie.path = request.contextPath
+        if (request.locale.language == locale.language) {
             cookie.maxAge = 0
         } else {
             cookie.value = locale.language
             cookie.maxAge = COOKIE_MAX_AGE
         }
-        http.response.addCookie(cookie)
+        response.addCookie(cookie)
     }
 }

mercurial