✅ Fix tests failing with non-English locales
[Sone.git] / src / main / kotlin / net / pterodactylus / sone / web / ajax / GetStatusAjaxPage.kt
1 package net.pterodactylus.sone.web.ajax
2
3 import com.fasterxml.jackson.databind.JsonNode
4 import com.fasterxml.jackson.databind.ObjectMapper
5 import net.pterodactylus.sone.core.ElementLoader
6 import net.pterodactylus.sone.core.LinkedElement
7 import net.pterodactylus.sone.data.Post
8 import net.pterodactylus.sone.data.PostReply
9 import net.pterodactylus.sone.data.Sone
10 import net.pterodactylus.sone.data.SoneOptions
11 import net.pterodactylus.sone.freenet.L10nFilter
12 import net.pterodactylus.sone.template.SoneAccessor
13 import net.pterodactylus.sone.text.TimeTextConverter
14 import net.pterodactylus.sone.utils.jsonObject
15 import net.pterodactylus.sone.utils.toArray
16 import net.pterodactylus.sone.web.NewElements
17 import net.pterodactylus.sone.web.WebInterface
18 import net.pterodactylus.sone.web.page.*
19 import java.text.SimpleDateFormat
20 import java.util.TimeZone
21 import javax.inject.Inject
22
23 /**
24  * The “get status” AJAX handler returns all information that is necessary to
25  * update the web interface in real-time.
26  */
27 @ToadletPath("getStatus.ajax")
28 class GetStatusAjaxPage(webInterface: WebInterface, private val elementLoader: ElementLoader, private val newElements: NewElements, private val timeTextConverter: TimeTextConverter, private val l10nFilter: L10nFilter, timeZone: TimeZone):
29                 JsonPage(webInterface) {
30
31         @Inject constructor(webInterface: WebInterface, elementLoader: ElementLoader, newElements: NewElements, timeTextConverter: TimeTextConverter, l10nFilter: L10nFilter):
32                         this(webInterface, elementLoader, newElements, timeTextConverter, l10nFilter, TimeZone.getDefault())
33
34         private val dateFormatter by lazy {
35                 SimpleDateFormat("MMM d, yyyy, HH:mm:ss").apply {
36                         this.timeZone = timeZone
37                 }
38         }
39
40         override fun createJsonObject(request: FreenetRequest) =
41                         getCurrentSone(request.toadletContext).let { currentSone ->
42                                 createSuccessJsonObject().apply {
43                                         this["loggedIn"] = currentSone != null
44                                         this["options"] = currentSone?.options?.toJsonOptions() ?: jsonObject {}
45                                         this["notificationHash"] = webInterface.getNotifications(currentSone).sortedBy { it.createdTime }.hashCode()
46                                         this["sones"] = request.httpRequest.getParam("soneIds").split(',').mapNotNull(core::getSone).plus(currentSone).filterNotNull().toJsonSones()
47                                         this["newPosts"] = newElements.newPosts.toJsonPosts()
48                                         this["newReplies"] = newElements.newReplies.toJsonReplies()
49                                         this["linkedElements"] = request.httpRequest.getParam("elements", "[]").asJson().map(JsonNode::asText).map(elementLoader::loadElement).toJsonElements()
50                                 }
51                         }
52
53         private operator fun JsonReturnObject.set(key: String, value: JsonNode) = put(key, value)
54         private operator fun JsonReturnObject.set(key: String, value: Int) = put(key, value)
55         private operator fun JsonReturnObject.set(key: String, value: Boolean) = put(key, value)
56
57         private fun String.asJson() = ObjectMapper().readTree(this).asIterable()
58
59         override val needsFormPassword = false
60         override val requiresLogin = false
61
62         private fun SoneOptions.toJsonOptions() = jsonObject {
63                 put("ShowNotification/NewSones", isShowNewSoneNotifications)
64                 put("ShowNotification/NewPosts", isShowNewPostNotifications)
65                 put("ShowNotification/NewReplies", isShowNewReplyNotifications)
66         }
67
68         private fun Iterable<Sone>.toJsonSones() = map { sone ->
69                 jsonObject {
70                         put("id", sone.id)
71                         put("name", SoneAccessor.getNiceName(sone))
72                         put("local", sone.isLocal)
73                         put("status", sone.status.name)
74                         put("modified", core.isModifiedSone(sone))
75                         put("locked", core.isLocked(sone))
76                         put("lastUpdatedUnknown", sone.time == 0L)
77                         synchronized(dateFormatter) {
78                                 put("lastUpdated", dateFormatter.format(sone.time))
79                         }
80                         put("lastUpdatedText", timeTextConverter.getTimeText(sone.time).l10nText.let { l10nFilter.format(null, it, emptyMap()) })
81                 }
82         }.toArray()
83
84         private fun Iterable<Post>.toJsonPosts() = map { post ->
85                 jsonObject {
86                         put("id", post.id)
87                         put("sone", post.sone.id)
88                         put("time", post.time)
89                         put("recipient", post.recipientId.orNull())
90                 }
91         }.toArray()
92
93         private fun Iterable<PostReply>.toJsonReplies() = map { reply ->
94                 jsonObject {
95                         put("id", reply.id)
96                         put("sone", reply.sone.id)
97                         put("post", reply.postId)
98                         put("postSone", reply.post.get().sone.id)
99                 }
100         }.toArray()
101
102         private fun Iterable<LinkedElement>.toJsonElements() = map { (link, failed, loading) ->
103                 jsonObject {
104                         put("link", link)
105                         put("loading", loading)
106                         put("failed", failed)
107                 }
108         }.toArray()
109
110 }