import javax.annotation.Nullable;
import net.pterodactylus.sone.core.Core;
+import net.pterodactylus.sone.core.DefaultElementLoader;
import net.pterodactylus.sone.core.ElementLoader;
import net.pterodactylus.sone.core.event.*;
import net.pterodactylus.sone.data.Sone;
pageToadletRegistry.addPage(new DismissNotificationPage(this, loaders, templateRenderer));
pageToadletRegistry.addPage(new DebugPage(this, loaders, templateRenderer));
pageToadletRegistry.addDebugPage(new MetricsPage(this, loaders, templateRenderer, metricRegistry));
+ pageToadletRegistry.addDebugPage(new ElementsPage(this, loaders, templateRenderer, (DefaultElementLoader) elementLoader));
pageToadletRegistry.addPage(loaders.<FreenetRequest>loadStaticPage("css/", "/static/css/", "text/css"));
pageToadletRegistry.addPage(loaders.<FreenetRequest>loadStaticPage("javascript/", "/static/javascript/", "text/javascript"));
pageToadletRegistry.addPage(loaders.<FreenetRequest>loadStaticPage("images/", "/static/images/", "image/png"));
--- /dev/null
+package net.pterodactylus.sone.web.pages
+
+import jakarta.inject.Inject
+import net.pterodactylus.sone.core.DefaultElementLoader
+import net.pterodactylus.sone.main.Loaders
+import net.pterodactylus.sone.web.WebInterface
+import net.pterodactylus.sone.web.page.MenuName
+import net.pterodactylus.sone.web.page.SoneRequest
+import net.pterodactylus.sone.web.page.TemplatePath
+import net.pterodactylus.sone.web.page.TemplateRenderer
+import net.pterodactylus.sone.web.page.ToadletPath
+import net.pterodactylus.util.template.TemplateContext
+
+@MenuName("Elements")
+@TemplatePath("/templates/elements.html")
+@ToadletPath("elements.html")
+class ElementsPage @Inject constructor(webInterface: WebInterface, loaders: Loaders, templateRenderer: TemplateRenderer, private val elementsLoader: DefaultElementLoader) :
+ SoneTemplatePage(webInterface, loaders, templateRenderer, pageTitleKey = "Page.ElementLoader.Title") {
+
+ override fun handleRequest(soneRequest: SoneRequest, templateContext: TemplateContext) {
+ templateContext.set("loadingElements", elementsLoader.loadingElements)
+ templateContext.set("loadedElements", elementsLoader.loadedElements)
+ templateContext.set("rejectedElements", elementsLoader.rejectedElements)
+ templateContext.set("failedElements", elementsLoader.failedElements)
+ }
+
+}
Navigation.Menu.Sone.Item.About.Tooltip=Informationen über Sone
Navigation.Menu.Sone.Item.Metrics.Name=Metriken
Navigation.Menu.Sone.Item.Metrics.Tooltip=Von Sone gesammelte Metriken
+Navigation.Menu.Sone.Item.Elements.Name=Elemente
+Navigation.Menu.Sone.Item.Elements.Tooltip=Informationen über geladene Elemente
Page.About.Title=Über - Sone
Page.About.Page.Title=Über
Page.Metrics.SoneParseDuration.Title=Parsdauer einer Sone
Page.Metrics.ConfigurationSaveDuration.Title=Konfigurationsspeicherdauer
+Page.ElementLoader.Title=Elementlader
+Page.ElementLoader.Page.Title=Elementlader
+Page.ElementLoader.Section.LoadingElements.Title=Ladende Elemente
+Page.ElementLoader.Section.LoadingElements.Description=Dieser Abschnitt zeigt Elemente, die gerade geladen werden.
+Page.ElementLoader.Section.LoadedElements.Title=Geladene Elemente
+Page.ElementLoader.Section.LoadedElements.Description=Dieser Abschnitt zeigt Elemente, die bereits erfolgreich geladen wurden, und die in Sone angezeigt werden können.
+Page.ElementLoader.Section.RejectedElements.Title=Verweigerte Elemente
+Page.ElementLoader.Section.RejectedElements.Description=Dieser Abschnitt zeigt Element, die nicht geladen werden, weil sie die Anforderungen nicht erfüllen.
+Page.ElementLoader.Section.FailedElements.Title=Fehlgeschlagene Elemente
+Page.ElementLoader.Section.FailedElements.Description=Dieser Abschnitt zeigt Elemente, die aufgrund eines Fehlers nicht geladen werden konnten. Elemente werden nach 30 Minuten aus dieser Liste entfernt.
+
View.Search.Button.Search=Suchen
View.CreateSone.Text.WotIdentityRequired=Um eine Sone anzulegen, brauchen Sie eine Identität aus dem {link}„Web of Trust“ Plugin{/link}.
Navigation.Menu.Sone.Item.About.Tooltip=Information about Sone
Navigation.Menu.Sone.Item.Metrics.Name=Metrics
Navigation.Menu.Sone.Item.Metrics.Tooltip=Metrics collected by Sone
+Navigation.Menu.Sone.Item.Elements.Name=Elements
+Navigation.Menu.Sone.Item.Elements.Tooltip=Information about Loaded Elements
Page.About.Title=About - Sone
Page.About.Page.Title=About
Page.Metrics.SoneParseDuration.Title=Sone Parse Duration
Page.Metrics.ConfigurationSaveDuration.Title=Configuration Save Duration
+Page.ElementLoader.Title=Element Loader
+Page.ElementLoader.Page.Title=Element Loader
+Page.ElementLoader.Section.LoadingElements.Title=Loading Elements
+Page.ElementLoader.Section.LoadingElements.Description=This section lists elements that are currently being loaded.
+Page.ElementLoader.Section.LoadedElements.Title=Loaded Elements
+Page.ElementLoader.Section.LoadedElements.Description=This section lists elements that have successfully been loaded and can be displayed in Sone.
+Page.ElementLoader.Section.RejectedElements.Title=Rejected Elements
+Page.ElementLoader.Section.RejectedElements.Description=This section lists elements that were loaded, but rejected.
+Page.ElementLoader.Section.FailedElements.Title=Failed Elements
+Page.ElementLoader.Section.FailedElements.Description=This section lists elements that could not be loaded. Elements in this list are removed after 30 minutes.
+
View.Search.Button.Search=Search
View.CreateSone.Text.WotIdentityRequired=To create a Sone you need an identity from the {link}Web of Trust plugin{/link}.
--- /dev/null
+<%include include/head.html>
+
+<h1><%= Page.ElementLoader.Page.Title|l10n|html></h1>
+
+<h2><%= Page.ElementLoader.Section.LoadingElements.Title|l10n|html></h2>
+<p><%= Page.ElementLoader.Section.LoadingElements.Description|l10n|html></p>
+
+<%foreach loadingElements loadingElement>
+ <%first>
+ <ul class="loading-elements">
+ <%/first>
+ <li class="key"><a href="/<% loadingElement>"><% loadingElement></a></li>
+ <%last>
+ </ul>
+ <%/last>
+<%/foreach>
+
+<h2><%= Page.ElementLoader.Section.LoadedElements.Title|l10n|html></h2>
+<p><%= Page.ElementLoader.Section.LoadedElements.Description|l10n|html></p>
+
+<%foreach loadedElements loadedElement>
+ <%first>
+ <ul class="loaded-elements">
+ <%/first>
+ <li class="key">
+ <a href="/<% loadedElement.link>"><% loadedElement.link></a>
+ <%foreach loadedElement.properties.entries property>
+ <%first>
+ <ul>
+ <%/first>
+ <li class="property-entry">
+ <span class="key"><% property.key></span>:
+ <span class="value"><% property.value></span>
+ </li>
+ <%last>
+ </ul>
+ <%/last>
+ <%/foreach>
+ </li>
+ <%last>
+ </ul>
+ <%/last>
+<%/foreach>
+
+<h2><%= Page.ElementLoader.Section.RejectedElements.Title|l10n|html></h2>
+<p><%= Page.ElementLoader.Section.RejectedElements.Description|l10n|html></p>
+
+<%foreach rejectedElements rejectedElement>
+<%first>
+<ul class="rejected-elements">
+ <%/first>
+ <li class="key">
+ <a href="/<% rejectedElement.key>"><% rejectedElement.key></a>: <% rejectedElement.value.reason> (<% rejectedElement.value.data>)
+ </li>
+ <%last>
+</ul>
+<%/last>
+<%/foreach>
+
+<h2><%= Page.ElementLoader.Section.FailedElements.Title|l10n|html></h2>
+<p><%= Page.ElementLoader.Section.FailedElements.Description|l10n|html></p>
+
+<%foreach failedElements failedElement>
+<%first>
+<ul class="failed-elements">
+ <%/first>
+ <li class="key">
+ <a href="/<% failedElement>"><% failedElement></a>
+ </li>
+ <%last>
+</ul>
+<%/last>
+<%/foreach>
+
+<%include include/tail.html>
--- /dev/null
+package net.pterodactylus.sone.web.pages
+
+import com.google.inject.Guice.createInjector
+import kotlin.test.Test
+import net.pterodactylus.sone.core.DefaultElementLoader
+import net.pterodactylus.sone.core.DefaultElementLoader.Rejected
+import net.pterodactylus.sone.core.DefaultElementLoader.RejectionReason.InvalidMediaType
+import net.pterodactylus.sone.core.DefaultElementLoader.RejectionReason.SizeTooLarge
+import net.pterodactylus.sone.core.LinkedElement
+import net.pterodactylus.sone.test.getInstance
+import net.pterodactylus.sone.test.isProvidedByDeepMock
+import net.pterodactylus.sone.test.isProvidedByMock
+import net.pterodactylus.sone.test.mock
+import net.pterodactylus.sone.test.whenever
+import net.pterodactylus.sone.web.WebInterface
+import net.pterodactylus.sone.web.page.TemplateRenderer
+import org.hamcrest.MatcherAssert.assertThat
+import org.hamcrest.Matchers.allOf
+import org.hamcrest.Matchers.containsInAnyOrder
+import org.hamcrest.Matchers.equalTo
+import org.hamcrest.Matchers.hasEntry
+
+class ElementsPageTest : WebPageTest() {
+
+ @Test
+ fun `page returns correct path`() {
+ assertThat(page.path, equalTo("elements.html"))
+ }
+
+ @Test
+ fun `page does not require login`() {
+ assertThat(page.requiresLogin(), equalTo(false))
+ }
+
+ @Test
+ fun `page returns correct title`() {
+ addTranslation("Page.ElementLoader.Title", "elements page title")
+ assertThat(page.getPageTitle(soneRequest), equalTo("elements page title"))
+ }
+
+ @Test
+ fun `page lists loading elements`() {
+ whenever(elementsLoader.loadingElements).thenReturn(setOf("key 1", "key 2"))
+ page.handleRequest(freenetRequest, templateContext)
+ assertThat(templateContext["loadingElements"] as Collection<*>, containsInAnyOrder("key 1", "key 2"))
+ }
+
+ @Test
+ fun `page lists loaded elements`() {
+ whenever(elementsLoader.loadedElements).thenReturn(setOf(LinkedElement("key 3"), LinkedElement("key 4")))
+ page.handleRequest(freenetRequest, templateContext)
+ assertThat(templateContext["loadedElements"] as Collection<*>, containsInAnyOrder(LinkedElement("key 3"), LinkedElement("key 4")))
+ }
+
+ @Test
+ fun `page lists rejected elements`() {
+ whenever(elementsLoader.rejectedElements).thenReturn(mapOf(
+ "key 5" to Rejected(InvalidMediaType, "application/test"),
+ "key 6" to Rejected(SizeTooLarge, 1234567L),
+ ))
+ page.handleRequest(freenetRequest, templateContext)
+ assertThat(templateContext["rejectedElements"] as Map<*, *>, allOf(
+ hasEntry("key 5", Rejected(InvalidMediaType, "application/test")),
+ hasEntry("key 6", Rejected(SizeTooLarge, 1234567L)),
+ ))
+ }
+
+ @Test
+ fun `page lists failed elements`() {
+ whenever(elementsLoader.failedElements).thenReturn(setOf("key 7", "key 8"))
+ page.handleRequest(freenetRequest, templateContext)
+ assertThat(templateContext["failedElements"] as Collection<*>, containsInAnyOrder("key 7", "key 8"))
+ }
+
+ @Test
+ fun `page can be created by Guice`() {
+ val injector = createInjector(
+ WebInterface::class.isProvidedByDeepMock(),
+ TemplateRenderer::class.isProvidedByMock(),
+ DefaultElementLoader::class.isProvidedByMock(),
+ )
+ injector.getInstance<ElementsPage>()
+ }
+
+ private val elementsLoader: DefaultElementLoader = mock<DefaultElementLoader>()
+ override val page: SoneTemplatePage
+ get() = ElementsPage(webInterface, loaders, templateRenderer, elementsLoader)
+
+}