1 package net.pterodactylus.sone.template
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.util.template.Filter
14 import net.pterodactylus.util.template.TemplateContext
15 import net.pterodactylus.util.template.TemplateContextFactory
16 import net.pterodactylus.util.template.TemplateParser
17 import java.io.StringReader
18 import java.io.StringWriter
20 import java.net.URLEncoder
21 import java.util.ArrayList
24 * Renders a number of pre-parsed [Part] into a [String].
26 * @author [David ‘Bombe’ Roden](mailto:bombe@pterodactylus.net)
28 class RenderFilter(private val core: Core, private val templateContextFactory: TemplateContextFactory) : Filter {
31 private val plainTextTemplate = TemplateParser.parse(StringReader("<%text|html>"))
32 private val linkTemplate = TemplateParser.parse(StringReader("<a class=\"<%cssClass|html>\" href=\"<%link|html>\" title=\"<%title|html>\"><%text|html></a>"))
35 override fun format(templateContext: TemplateContext?, data: Any?, parameters: MutableMap<String, Any?>?): Any? {
36 @Suppress("UNCHECKED_CAST")
37 val parts = getPartsToRender(parameters, data as? Iterable<Part> ?: return null)
38 val parsedTextWriter = StringWriter()
39 render(parsedTextWriter, parts)
40 return parsedTextWriter.toString()
43 private fun Map<String, Any?>.parseInt(key: String) = this[key]?.toString()?.toInt()
45 private fun getPartsToRender(parameters: MutableMap<String, Any?>?, parts: Iterable<Part>): Iterable<Part> {
46 val length = parameters?.parseInt("length") ?: -1
47 val cutOffLength = parameters?.parseInt("cut-off-length") ?: length
49 var allPartsLength = 0
50 val shortenedParts = ArrayList<Part>()
52 if (part is PlainTextPart) {
53 val longText = part.text
54 if (allPartsLength < cutOffLength) {
55 if (allPartsLength + longText.length > cutOffLength) {
56 shortenedParts.add(PlainTextPart(longText.substring(0, cutOffLength - allPartsLength) + "…"))
58 shortenedParts.add(part)
61 allPartsLength += longText.length
62 } else if (part is LinkPart) {
63 if (allPartsLength < cutOffLength) {
64 shortenedParts.add(part)
66 allPartsLength += part.text.length
68 if (allPartsLength < cutOffLength) {
69 shortenedParts.add(part)
73 if (allPartsLength >= length) {
80 private fun render(writer: Writer, parts: Iterable<Part>) {
81 parts.forEach { render(writer, it) }
84 private fun render(writer: Writer, part: Part) {
85 @Suppress("UNCHECKED_CAST")
87 is PlainTextPart -> render(writer, part)
88 is FreenetLinkPart -> render(writer, part)
89 is LinkPart -> render(writer, part)
90 is SonePart -> render(writer, part)
91 is PostPart -> render(writer, part)
92 is FreemailPart -> render(writer, part)
93 is Iterable<*> -> render(writer, part as Iterable<Part>)
97 private fun render(writer: Writer, plainTextPart: PlainTextPart) {
98 val templateContext = templateContextFactory.createTemplateContext()
99 templateContext.set("text", plainTextPart.text)
100 plainTextTemplate.render(templateContext, writer)
103 private fun render(writer: Writer, freenetLinkPart: FreenetLinkPart) {
104 renderLink(writer, "/${freenetLinkPart.link}", freenetLinkPart.text, freenetLinkPart.title, if (freenetLinkPart.trusted) "freenet-trusted" else "freenet")
107 private fun render(writer: Writer, linkPart: LinkPart) {
108 renderLink(writer, "/external-link/?_CHECKED_HTTP_=${linkPart.link.urlEncode()}", linkPart.text, linkPart.title, "internet")
111 private fun String.urlEncode() = URLEncoder.encode(this, "UTF-8")!!
113 private fun render(writer: Writer, sonePart: SonePart) {
114 if (sonePart.sone.name != null) {
115 renderLink(writer, "viewSone.html?sone=${sonePart.sone.id}", SoneAccessor.getNiceName(sonePart.sone), SoneAccessor.getNiceName(sonePart.sone), "in-sone")
117 renderLink(writer, "/WebOfTrust/ShowIdentity?id=${sonePart.sone.id}", sonePart.sone.id, sonePart.sone.id, "in-sone")
121 private fun render(writer: Writer, postPart: PostPart) {
122 val parser = SoneTextParser(core, core)
123 val parserContext = SoneTextParserContext(postPart.post.sone)
124 val parts = parser.parse(postPart.post.text, parserContext)
125 val excerpt = StringBuilder()
126 for (part in parts) {
127 excerpt.append(part.text)
128 if (excerpt.length > 20) {
129 val lastSpace = excerpt.lastIndexOf(" ", 20)
130 if (lastSpace > -1) {
131 excerpt.setLength(lastSpace)
133 excerpt.setLength(20)
139 renderLink(writer, "viewPost.html?post=${postPart.post.id}", excerpt.toString(), SoneAccessor.getNiceName(postPart.post.sone), "in-sone")
142 private fun render(writer: Writer, freemailPart: FreemailPart) {
143 val sone = core.getSone(freemailPart.identityId)
144 val soneName = sone.transform(SoneAccessor::getNiceName).or(freemailPart.identityId)
146 "/Freemail/NewMessage?to=${freemailPart.identityId}",
147 "${freemailPart.emailLocalPart}@$soneName.freemail",
148 "$soneName\n${freemailPart.emailLocalPart}@${freemailPart.freemailId}.freemail",
152 private fun renderLink(writer: Writer, link: String, text: String, title: String, cssClass: String) {
153 val templateContext = templateContextFactory.createTemplateContext()
154 templateContext["cssClass"] = cssClass
155 templateContext["link"] = link
156 templateContext["text"] = text
157 templateContext["title"] = title
158 linkTemplate.render(templateContext, writer)