Merge branch 'release-0.9.7'
[Sone.git] / src / main / kotlin / net / pterodactylus / sone / template / RenderFilter.kt
1 package net.pterodactylus.sone.template
2
3 import net.pterodactylus.sone.core.Core
4 import net.pterodactylus.sone.text.FreemailPart
5 import net.pterodactylus.sone.text.FreenetLinkPart
6 import net.pterodactylus.sone.text.LinkPart
7 import net.pterodactylus.sone.text.Part
8 import net.pterodactylus.sone.text.PlainTextPart
9 import net.pterodactylus.sone.text.PostPart
10 import net.pterodactylus.sone.text.SonePart
11 import net.pterodactylus.sone.text.SoneTextParser
12 import net.pterodactylus.sone.text.SoneTextParserContext
13 import net.pterodactylus.sone.utils.asTemplate
14 import net.pterodactylus.util.template.Filter
15 import net.pterodactylus.util.template.TemplateContext
16 import net.pterodactylus.util.template.TemplateContextFactory
17 import java.io.StringWriter
18 import java.io.Writer
19 import java.net.URLEncoder
20
21 /**
22  * Renders a number of pre-parsed [Part] into a [String].
23  *
24  * @author [David ‘Bombe’ Roden](mailto:bombe@pterodactylus.net)
25  */
26 class RenderFilter(private val core: Core, private val templateContextFactory: TemplateContextFactory) : Filter {
27
28         companion object {
29                 private val plainTextTemplate = "<%text|html>".asTemplate()
30                 private val linkTemplate = "<a class=\"<%cssClass|html>\" href=\"<%link|html>\" title=\"<%title|html>\"><%text|html></a>".asTemplate()
31         }
32
33         override fun format(templateContext: TemplateContext?, data: Any?, parameters: MutableMap<String, Any?>?): Any? {
34                 @Suppress("UNCHECKED_CAST")
35                 val parts = data as? Iterable<Part> ?: return null
36                 val parsedTextWriter = StringWriter()
37                 render(parsedTextWriter, parts)
38                 return parsedTextWriter.toString()
39         }
40
41         private fun render(writer: Writer, parts: Iterable<Part>) {
42                 parts.forEach { render(writer, it) }
43         }
44
45         private fun render(writer: Writer, part: Part) {
46                 @Suppress("UNCHECKED_CAST")
47                 when (part) {
48                         is PlainTextPart -> render(writer, part)
49                         is FreenetLinkPart -> render(writer, part)
50                         is LinkPart -> render(writer, part)
51                         is SonePart -> render(writer, part)
52                         is PostPart -> render(writer, part)
53                         is FreemailPart -> render(writer, part)
54                 }
55         }
56
57         private fun render(writer: Writer, plainTextPart: PlainTextPart) {
58                 val templateContext = templateContextFactory.createTemplateContext()
59                 templateContext.set("text", plainTextPart.text)
60                 plainTextTemplate.render(templateContext, writer)
61         }
62
63         private fun render(writer: Writer, freenetLinkPart: FreenetLinkPart) {
64                 renderLink(writer, "/${freenetLinkPart.link}", freenetLinkPart.text, freenetLinkPart.title, if (freenetLinkPart.trusted) "freenet-trusted" else "freenet")
65         }
66
67         private fun render(writer: Writer, linkPart: LinkPart) {
68                 renderLink(writer, "/external-link/?_CHECKED_HTTP_=${linkPart.link.urlEncode()}", linkPart.text, linkPart.title, "internet")
69         }
70
71         private fun String.urlEncode(): String = URLEncoder.encode(this, "UTF-8")
72
73         private fun render(writer: Writer, sonePart: SonePart) {
74                 if (sonePart.sone.name != null) {
75                         renderLink(writer, "viewSone.html?sone=${sonePart.sone.id}", SoneAccessor.getNiceName(sonePart.sone), SoneAccessor.getNiceName(sonePart.sone), "in-sone")
76                 } else {
77                         renderLink(writer, "/WebOfTrust/ShowIdentity?id=${sonePart.sone.id}", sonePart.sone.id, sonePart.sone.id, "in-sone")
78                 }
79         }
80
81         private fun render(writer: Writer, postPart: PostPart) {
82                 val parser = SoneTextParser(core, core)
83                 val parserContext = SoneTextParserContext(postPart.post.sone)
84                 val parts = parser.parse(postPart.post.text, parserContext)
85                 val excerpt = StringBuilder()
86                 for (part in parts) {
87                         excerpt.append(part.text)
88                         if (excerpt.length > 20) {
89                                 val lastSpace = excerpt.lastIndexOf(" ", 20)
90                                 if (lastSpace > -1) {
91                                         excerpt.setLength(lastSpace)
92                                 } else {
93                                         excerpt.setLength(20)
94                                 }
95                                 excerpt.append("…")
96                                 break
97                         }
98                 }
99                 renderLink(writer, "viewPost.html?post=${postPart.post.id}", excerpt.toString(), SoneAccessor.getNiceName(postPart.post.sone), "in-sone")
100         }
101
102         private fun render(writer: Writer, freemailPart: FreemailPart) {
103                 val sone = core.getSone(freemailPart.identityId)
104                 val soneName = sone.transform(SoneAccessor::getNiceName).or(freemailPart.identityId)
105                 renderLink(writer,
106                                 "/Freemail/NewMessage?to=${freemailPart.identityId}",
107                                 "${freemailPart.emailLocalPart}@$soneName.freemail",
108                                 "$soneName\n${freemailPart.emailLocalPart}@${freemailPart.freemailId}.freemail",
109                                 "in-sone")
110         }
111
112         private fun renderLink(writer: Writer, link: String, text: String, title: String, cssClass: String) {
113                 val templateContext = templateContextFactory.createTemplateContext()
114                 templateContext["cssClass"] = cssClass
115                 templateContext["link"] = link
116                 templateContext["text"] = text
117                 templateContext["title"] = title
118                 linkTemplate.render(templateContext, writer)
119         }
120
121 }