✨ Add @ToadletPath annotation
[Sone.git] / src / main / kotlin / net / pterodactylus / sone / web / pages / SearchPage.kt
index 76c2341..3b987d8 100644 (file)
@@ -1,45 +1,38 @@
 package net.pterodactylus.sone.web.pages
 
 import com.google.common.base.Ticker
-import com.google.common.cache.Cache
-import com.google.common.cache.CacheBuilder
-import freenet.support.Logger
-import net.pterodactylus.sone.data.Post
-import net.pterodactylus.sone.data.PostReply
-import net.pterodactylus.sone.data.Sone
-import net.pterodactylus.sone.utils.Pagination
-import net.pterodactylus.sone.utils.emptyToNull
-import net.pterodactylus.sone.utils.memoize
-import net.pterodactylus.sone.utils.paginate
-import net.pterodactylus.sone.utils.parameters
-import net.pterodactylus.sone.web.WebInterface
-import net.pterodactylus.sone.web.page.FreenetRequest
-import net.pterodactylus.sone.web.pages.SearchPage.Optionality.FORBIDDEN
-import net.pterodactylus.sone.web.pages.SearchPage.Optionality.OPTIONAL
-import net.pterodactylus.sone.web.pages.SearchPage.Optionality.REQUIRED
-import net.pterodactylus.util.template.Template
-import net.pterodactylus.util.template.TemplateContext
-import net.pterodactylus.util.text.StringEscaper
-import net.pterodactylus.util.text.TextException
-import java.util.concurrent.TimeUnit.MINUTES
-import javax.inject.Inject
+import com.google.common.cache.*
+import freenet.support.*
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.main.*
+import net.pterodactylus.sone.utils.*
+import net.pterodactylus.sone.web.*
+import net.pterodactylus.sone.web.page.*
+import net.pterodactylus.sone.web.pages.SearchPage.Optionality.*
+import net.pterodactylus.util.template.*
+import net.pterodactylus.util.text.*
+import java.util.concurrent.TimeUnit.*
+import javax.inject.*
 
 /**
  * This page lets the user search for posts and replies that contain certain
  * words.
  */
-class SearchPage(template: Template, webInterface: WebInterface, ticker: Ticker = Ticker.systemTicker()) :
-               SoneTemplatePage("search.html", webInterface, template, "Page.Search.Title") {
+@TemplatePath("/templates/search.html")
+@ToadletPath("search.html")
+class SearchPage(webInterface: WebInterface, loaders: Loaders, templateRenderer: TemplateRenderer, ticker: Ticker = Ticker.systemTicker()) :
+               SoneTemplatePage("search.html", webInterface, loaders, templateRenderer, pageTitleKey = "Page.Search.Title") {
 
-       @Inject constructor(template: Template, webInterface: WebInterface) :
-                       this(template, webInterface, Ticker.systemTicker())
+       @Inject
+       constructor(webInterface: WebInterface, loaders: Loaders, templateRenderer: TemplateRenderer) :
+                       this(webInterface, loaders, templateRenderer, Ticker.systemTicker())
 
        private val cache: Cache<Iterable<Phrase>, Pagination<Post>> = CacheBuilder.newBuilder().ticker(ticker).expireAfterAccess(5, MINUTES).build()
 
-       override fun handleRequest(freenetRequest: FreenetRequest, templateContext: TemplateContext) {
+       override fun handleRequest(soneRequest: SoneRequest, templateContext: TemplateContext) {
                val startTime = System.currentTimeMillis()
                val phrases = try {
-                       freenetRequest.parameters["query"].emptyToNull?.parse()
+                       soneRequest.parameters["query"].emptyToNull?.parse()
                } catch (te: TextException) {
                        redirect("index.html")
                }
@@ -49,39 +42,39 @@ class SearchPage(template: Template, webInterface: WebInterface, ticker: Ticker
                        0 -> redirect("index.html")
                        1 -> phrases.first().phrase.also { word ->
                                when {
-                                       word.removePrefix("sone://").let(webInterface.core::getSone) != null -> redirect("viewSone.html?sone=${word.removePrefix("sone://")}")
-                                       word.removePrefix("post://").let(webInterface.core::getPost) != null -> redirect("viewPost.html?post=${word.removePrefix("post://")}")
-                                       word.removePrefix("reply://").let(webInterface.core::getPostReply) != null -> redirect("viewPost.html?post=${word.removePrefix("reply://").let(webInterface.core::getPostReply)?.postId}")
-                                       word.removePrefix("album://").let(webInterface.core::getAlbum) != null -> redirect("imageBrowser.html?album=${word.removePrefix("album://")}")
-                                       word.removePrefix("image://").let { webInterface.core.getImage(it, false) } != null -> redirect("imageBrowser.html?image=${word.removePrefix("image://")}")
+                                       word.removePrefix("sone://").let(soneRequest.core::getSone) != null -> redirect("viewSone.html?sone=${word.removePrefix("sone://")}")
+                                       word.removePrefix("post://").let(soneRequest.core::getPost) != null -> redirect("viewPost.html?post=${word.removePrefix("post://")}")
+                                       word.removePrefix("reply://").let(soneRequest.core::getPostReply) != null -> redirect("viewPost.html?post=${word.removePrefix("reply://").let(soneRequest.core::getPostReply)?.postId}")
+                                       word.removePrefix("album://").let(soneRequest.core::getAlbum) != null -> redirect("imageBrowser.html?album=${word.removePrefix("album://")}")
+                                       word.removePrefix("image://").let { soneRequest.core.getImage(it, false) } != null -> redirect("imageBrowser.html?image=${word.removePrefix("image://")}")
                                }
                        }
                }
 
                val soneNameCache = { sone: Sone -> sone.names() }.memoize()
-               val sonePagination = webInterface.core.sones
-                               .scoreAndPaginate(phrases) { it.allText(soneNameCache) }
-                               .apply { page = freenetRequest.parameters["sonePage"].emptyToNull?.toIntOrNull() ?: 0 }
+               val sonePagination = soneRequest.core.sones
+                               .scoreAndPaginate(phrases, soneRequest.core.preferences.postsPerPage) { it.allText(soneNameCache) }
+                               .apply { page = soneRequest.parameters["sonePage"].emptyToNull?.toIntOrNull() ?: 0 }
                val postPagination = cache.get(phrases) {
-                       webInterface.core.sones
+                       soneRequest.core.sones
                                        .flatMap(Sone::getPosts)
                                        .filter { Post.FUTURE_POSTS_FILTER.apply(it) }
-                                       .scoreAndPaginate(phrases) { it.allText(soneNameCache) }
-               }.apply { page = freenetRequest.parameters["postPage"].emptyToNull?.toIntOrNull() ?: 0 }
+                                       .scoreAndPaginate(phrases, soneRequest.core.preferences.postsPerPage) { it.allText(soneNameCache, soneRequest.core::getReplies) }
+               }.apply { page = soneRequest.parameters["postPage"].emptyToNull?.toIntOrNull() ?: 0 }
 
-               Logger.normal(SearchPage::class.java, "Finished search for “${freenetRequest.parameters["query"]}” in ${System.currentTimeMillis() - startTime}ms.")
+               Logger.normal(SearchPage::class.java, "Finished search for “${soneRequest.parameters["query"]}” in ${System.currentTimeMillis() - startTime}ms.")
                templateContext["sonePagination"] = sonePagination
                templateContext["soneHits"] = sonePagination.items
                templateContext["postPagination"] = postPagination
                templateContext["postHits"] = postPagination.items
        }
 
-       private fun <T> Iterable<T>.scoreAndPaginate(phrases: Iterable<Phrase>, texter: (T) -> String) =
+       private fun <T> Iterable<T>.scoreAndPaginate(phrases: Iterable<Phrase>, postsPerPage: Int, texter: (T) -> String) =
                        map { it to score(texter(it), phrases) }
                                        .filter { it.second > 0 }
                                        .sortedByDescending { it.second }
                                        .map { it.first }
-                                       .paginate(webInterface.core.preferences.postsPerPage)
+                                       .paginate(postsPerPage)
 
        private fun Sone.names() =
                        with(profile) {
@@ -93,8 +86,8 @@ class SearchPage(template: Template, webInterface: WebInterface, ticker: Ticker
        private fun Sone.allText(soneNameCache: (Sone) -> String) =
                        (soneNameCache(this) + profile.fields.map { "${it.name} ${it.value}" }.joinToString(" ", " ")).toLowerCase()
 
-       private fun Post.allText(soneNameCache: (Sone) -> String) =
-                       (text + recipient.orNull()?.let { " ${soneNameCache(it)}" } + webInterface.core.getReplies(id)
+       private fun Post.allText(soneNameCache: (Sone) -> String, getReplies: (String) -> Collection<PostReply>) =
+                       (text + recipient.orNull()?.let { " ${soneNameCache(it)}" } + getReplies(id)
                                        .filter { PostReply.FUTURE_REPLY_FILTER.apply(it) }
                                        .map { "${soneNameCache(it.sone)} ${it.text}" }.joinToString(" ", " ")).toLowerCase()