31 import jakarta.servlet.http.Cookie |
31 import jakarta.servlet.http.Cookie |
32 import jakarta.servlet.http.HttpServlet |
32 import jakarta.servlet.http.HttpServlet |
33 import jakarta.servlet.http.HttpServletRequest |
33 import jakarta.servlet.http.HttpServletRequest |
34 import jakarta.servlet.http.HttpServletResponse |
34 import jakarta.servlet.http.HttpServletResponse |
35 import java.sql.SQLException |
35 import java.sql.SQLException |
|
36 import java.time.ZoneId |
36 import java.util.* |
37 import java.util.* |
37 |
38 |
38 abstract class AbstractServlet : HttpServlet() { |
39 abstract class AbstractServlet : HttpServlet() { |
39 |
40 |
40 companion object { |
41 companion object { |
|
42 const val COOKIE_MAX_AGE = 2592000 // 30 days |
41 const val LANGUAGE_COOKIE_NAME = "lpit_language" |
43 const val LANGUAGE_COOKIE_NAME = "lpit_language" |
|
44 const val TIMEZONE_COOKIE_NAME = "lpit_timezone" |
42 } |
45 } |
43 |
46 |
44 protected val logger = MyLogger() |
47 protected val logger = MyLogger() |
45 |
48 |
46 /** |
49 /** |
134 ) |
137 ) |
135 } else { |
138 } else { |
136 val sessionLocale = session.getAttribute(Constants.SESSION_ATTR_LANGUAGE) as Locale |
139 val sessionLocale = session.getAttribute(Constants.SESSION_ATTR_LANGUAGE) as Locale |
137 resp.locale = sessionLocale |
140 resp.locale = sessionLocale |
138 logger.trace("Continuing session {0} with language {1}", session.id, sessionLocale) |
141 logger.trace("Continuing session {0} with language {1}", session.id, sessionLocale) |
|
142 } |
|
143 |
|
144 // determine the timezone |
|
145 if (session.getAttribute(Constants.SESSION_ATTR_TIMEZONE) == null) { |
|
146 // timezone selection stored in cookie |
|
147 val cookieTimezone = cookieTimezone(http) |
|
148 |
|
149 // if no cookie, fall back to server's timezone (the browser does not transmit one) |
|
150 val timezone = cookieTimezone ?: ZoneId.systemDefault() |
|
151 |
|
152 selectTimezone(http, timezone) |
|
153 logger.debug("Timezone for session {0} set to {1}", session.id, timezone) |
139 } |
154 } |
140 |
155 |
141 // if this is an error path, bypass the normal flow |
156 // if this is an error path, bypass the normal flow |
142 if (fullPath.startsWith("/error/")) { |
157 if (fullPath.startsWith("/error/")) { |
143 http.styleSheets = listOf("error") |
158 http.styleSheets = listOf("error") |
196 |
211 |
197 private fun cookieLanguage(http: HttpRequest): Locale? = |
212 private fun cookieLanguage(http: HttpRequest): Locale? = |
198 http.request.cookies?.firstOrNull { c -> c.name == LANGUAGE_COOKIE_NAME } |
213 http.request.cookies?.firstOrNull { c -> c.name == LANGUAGE_COOKIE_NAME } |
199 ?.runCatching {Locale.forLanguageTag(this.value)}?.getOrNull() |
214 ?.runCatching {Locale.forLanguageTag(this.value)}?.getOrNull() |
200 |
215 |
|
216 protected fun sessionLanguage(http: HttpRequest) = http.session.getAttribute(Constants.SESSION_ATTR_LANGUAGE) as Locale |
|
217 |
|
218 private fun cookieTimezone(http: HttpRequest): ZoneId? = |
|
219 http.request.cookies?.firstOrNull { c -> c.name == TIMEZONE_COOKIE_NAME } |
|
220 ?.runCatching { ZoneId.of(this.value)}?.getOrNull() |
|
221 |
|
222 protected fun sessionTimezone(http: HttpRequest) = http.session.getAttribute(Constants.SESSION_ATTR_TIMEZONE) as String |
|
223 |
|
224 protected fun selectTimezone(http: HttpRequest, zoneId: ZoneId) { |
|
225 http.session.setAttribute(Constants.SESSION_ATTR_TIMEZONE, zoneId.id) |
|
226 val cookie = Cookie(TIMEZONE_COOKIE_NAME, zoneId.id) |
|
227 cookie.isHttpOnly = true |
|
228 cookie.path = http.request.contextPath |
|
229 cookie.maxAge = COOKIE_MAX_AGE |
|
230 http.response.addCookie(cookie) |
|
231 } |
|
232 |
201 protected fun selectLanguage(http: HttpRequest, locale: Locale) { |
233 protected fun selectLanguage(http: HttpRequest, locale: Locale) { |
202 http.response.locale = locale |
234 http.response.locale = locale |
203 http.session.setAttribute(Constants.SESSION_ATTR_LANGUAGE, locale) |
235 http.session.setAttribute(Constants.SESSION_ATTR_LANGUAGE, locale) |
204 // delete cookie if language selection matches request locale, otherwise set cookie |
236 // delete cookie if language selection matches request locale, otherwise set cookie |
205 val cookie = Cookie(LANGUAGE_COOKIE_NAME, "") |
237 val cookie = Cookie(LANGUAGE_COOKIE_NAME, "") |
207 cookie.path = http.request.contextPath |
239 cookie.path = http.request.contextPath |
208 if (http.request.locale.language == locale.language) { |
240 if (http.request.locale.language == locale.language) { |
209 cookie.maxAge = 0 |
241 cookie.maxAge = 0 |
210 } else { |
242 } else { |
211 cookie.value = locale.language |
243 cookie.value = locale.language |
212 cookie.maxAge = 2592000 // 30 days |
244 cookie.maxAge = COOKIE_MAX_AGE |
213 } |
245 } |
214 http.response.addCookie(cookie) |
246 http.response.addCookie(cookie) |
215 } |
247 } |
216 } |
248 } |