From 1818a538d271bfb99bec6542eb943ca1d4fe0b7b Mon Sep 17 00:00:00 2001 From: Lanius Trolling Date: Sun, 14 Apr 2024 12:14:25 -0400 Subject: [PATCH] Fix XML code --- .../kotlin/info/mechyrdia/Configuration.kt | 2 + .../kotlin/info/mechyrdia/data/DataFiles.kt | 13 ++- .../kotlin/info/mechyrdia/data/Nations.kt | 10 +-- .../info/mechyrdia/data/ViewComments.kt | 3 +- .../kotlin/info/mechyrdia/data/ViewsFiles.kt | 2 +- src/jvmMain/kotlin/info/mechyrdia/data/Xml.kt | 83 ------------------- .../kotlin/info/mechyrdia/lore/Fonts.kt | 2 +- .../info/mechyrdia/lore/ParserBuilder.kt | 3 +- .../kotlin/info/mechyrdia/lore/ViewOg.kt | 3 +- .../kotlin/info/mechyrdia/lore/ViewsQuote.kt | 5 +- .../kotlin/info/mechyrdia/lore/ViewsRobots.kt | 8 +- .../kotlin/info/mechyrdia/lore/ViewsRss.kt | 19 +++-- .../info/mechyrdia/route/ResourceTypes.kt | 7 +- 13 files changed, 41 insertions(+), 119 deletions(-) diff --git a/src/jvmMain/kotlin/info/mechyrdia/Configuration.kt b/src/jvmMain/kotlin/info/mechyrdia/Configuration.kt index 17ff8fd..eba5a89 100644 --- a/src/jvmMain/kotlin/info/mechyrdia/Configuration.kt +++ b/src/jvmMain/kotlin/info/mechyrdia/Configuration.kt @@ -48,3 +48,5 @@ data class Configuration( val OwnerNationId: Id get() = Id(Configuration.Current.ownerNation) + +const val MainDomainName = "https://mechyrdia.info" diff --git a/src/jvmMain/kotlin/info/mechyrdia/data/DataFiles.kt b/src/jvmMain/kotlin/info/mechyrdia/data/DataFiles.kt index d5b5a02..c6a9e80 100644 --- a/src/jvmMain/kotlin/info/mechyrdia/data/DataFiles.kt +++ b/src/jvmMain/kotlin/info/mechyrdia/data/DataFiles.kt @@ -29,17 +29,16 @@ import java.time.Instant import kotlin.String import kotlin.time.Duration.Companion.hours -fun StoragePath.getContentType(): ContentType { - val extension = elements.last().substringAfter('.', "") - return if (extension.isEmpty()) ContentType.Text.Plain else ContentType.defaultForFileExtension(extension) -} +val StoragePath.contentType: ContentType + get() { + val extension = elements.last().substringAfter('.', "") + return if (extension.isEmpty()) ContentType.Text.Plain else ContentType.defaultForFileExtension(extension) + } suspend fun ApplicationCall.respondStoredFile(path: StoragePath) { val content = FileStorage.instance.readFile(path) ?: return respond(HttpStatusCode.NotFound) - val type = path.getContentType() - attributes.put(StoragePathAttributeKey, path) - respondBytes(content, type) + respondBytes(content, path.contentType) } @JvmInline diff --git a/src/jvmMain/kotlin/info/mechyrdia/data/Nations.kt b/src/jvmMain/kotlin/info/mechyrdia/data/Nations.kt index 54d9fad..1cac61c 100644 --- a/src/jvmMain/kotlin/info/mechyrdia/data/Nations.kt +++ b/src/jvmMain/kotlin/info/mechyrdia/data/Nations.kt @@ -49,7 +49,7 @@ data class NationData( } } -val CallNationCacheAttribute = AttributeKey, NationData>>("NationCache") +val CallNationCacheAttribute = AttributeKey, NationData>>("Mechyrdia.NationCache") val ApplicationCall.nationCache: MutableMap, NationData> get() = attributes.getOrNull(CallNationCacheAttribute) @@ -63,7 +63,7 @@ suspend fun MutableMap, NationData>.getNation(id: Id) } } -private val callCurrentNationAttribute = AttributeKey("CurrentNation") +private val CallCurrentNationAttribute = AttributeKey("Mechyrdia.CurrentNation") fun ApplicationCall.ownerNationOnly() { if (sessions.get()?.nationId != OwnerNationId) @@ -71,7 +71,7 @@ fun ApplicationCall.ownerNationOnly() { } suspend fun ApplicationCall.currentNation(): NationData? { - attributes.getOrNull(callCurrentNationAttribute)?.let { sess -> + attributes.getOrNull(CallCurrentNationAttribute)?.let { sess -> return when (sess) { NationSession.Anonymous -> null is NationSession.LoggedIn -> sess.nation @@ -80,10 +80,10 @@ suspend fun ApplicationCall.currentNation(): NationData? { val nationId = sessions.get()?.nationId return if (nationId == null) { - attributes.put(callCurrentNationAttribute, NationSession.Anonymous) + attributes.put(CallCurrentNationAttribute, NationSession.Anonymous) null } else nationCache.getNation(nationId).also { data -> - attributes.put(callCurrentNationAttribute, NationSession.LoggedIn(data)) + attributes.put(CallCurrentNationAttribute, NationSession.LoggedIn(data)) } } diff --git a/src/jvmMain/kotlin/info/mechyrdia/data/ViewComments.kt b/src/jvmMain/kotlin/info/mechyrdia/data/ViewComments.kt index e76dfd1..930f876 100644 --- a/src/jvmMain/kotlin/info/mechyrdia/data/ViewComments.kt +++ b/src/jvmMain/kotlin/info/mechyrdia/data/ViewComments.kt @@ -1,5 +1,6 @@ package info.mechyrdia.data +import info.mechyrdia.MainDomainName import info.mechyrdia.OwnerNationId import info.mechyrdia.lore.* import info.mechyrdia.route.Root @@ -102,7 +103,7 @@ fun FlowContent.commentBox(comment: CommentRenderData, loggedInAs: Id.sortedAsFiles() = toList() +fun Map.sortedAsFiles() = toList() .sortedBy { (name, _) -> name } .sortedBy { (_, type) -> type } diff --git a/src/jvmMain/kotlin/info/mechyrdia/data/Xml.kt b/src/jvmMain/kotlin/info/mechyrdia/data/Xml.kt index 071f1a7..7dc5a91 100644 --- a/src/jvmMain/kotlin/info/mechyrdia/data/Xml.kt +++ b/src/jvmMain/kotlin/info/mechyrdia/data/Xml.kt @@ -17,10 +17,6 @@ import kotlinx.html.stream.createHTML import kotlinx.html.visit import kotlinx.html.visitAndFinalize import org.w3c.dom.Document -import kotlin.collections.component1 -import kotlin.collections.component2 -import kotlin.collections.contains -import kotlin.collections.set @DslMarker annotation class XmlTagMarker @@ -41,8 +37,6 @@ interface XmlTagConsumer : TagConsumer { fun onTagDeclaration(version: String, standalone: Boolean?) fun onTagDeclaration(standalone: Boolean?) = onTagDeclaration("1.0", standalone) - fun onTagNamespace(prefix: String?, namespace: String) - override fun onTagEvent(tag: Tag, event: String, value: (Event) -> Unit) { tagEventsNotSupported() } @@ -90,14 +84,6 @@ private fun TagConsumer<*>.getDeclarationConsumer(): XmlDeclarationConsumer = else -> throw IllegalArgumentException("Unsupported TagConsumer subtype ${this::class.qualifiedName}") } -private data class XmlTagImpl(val tag: Tag, val prefix: String?) : Tag by tag { - override val namespace: String? - get() = null - - override val tagName: String - get() = prefix?.let { "$it:" }.orEmpty() + tag.tagName -} - private class XmlTagConsumerImpl(val downstream: TagConsumer) : XmlTagConsumer, TagConsumer by downstream { private var isDeclared = false @@ -109,53 +95,6 @@ private class XmlTagConsumerImpl(val downstream: TagConsumer) : XmlTag isDeclared = true } - private var defaultNamespace: String? = null - private var namespaces: MutableMap? = mutableMapOf() - private val namespaceLookup = mutableMapOf() - - override fun onTagNamespace(prefix: String?, namespace: String) { - if (prefix == null) { - if (namespaces == null) - error("Unable to add xmlns attribute after document has already started") - defaultNamespace = namespace - } else - (namespaces - ?: error("Unable to add xmlns attribute after document has already started"))[prefix] = namespace - } - - override fun onTagStart(tag: Tag) { - namespaces?.let { namespaceList -> - defaultNamespace?.let { namespaceDefault -> - tag.attributes["xmlns"] = namespaceDefault - } - - for ((prefix, namespace) in namespaceList) { - tag.attributes["xmlns:$prefix"] = namespace - namespaceLookup[namespace] = prefix - } - - namespaces = null - } - - val prefix = when (tag.namespace) { - null, defaultNamespace -> null - in namespaceLookup -> namespaceLookup[tag.namespace] - else -> throw IllegalArgumentException("Unrecognized namespace ${tag.namespace}") - } - - downstream.onTagStart(XmlTagImpl(tag, prefix)) - } - - override fun onTagEnd(tag: Tag) { - val prefix = when (tag.namespace) { - null, defaultNamespace -> null - in namespaceLookup -> namespaceLookup[tag.namespace] - else -> throw IllegalArgumentException("Unrecognized namespace ${tag.namespace}") - } - - downstream.onTagEnd(XmlTagImpl(tag, prefix)) - } - override fun onTagEvent(tag: Tag, event: String, value: (Event) -> Unit) { tagEventsNotSupported() } @@ -166,28 +105,6 @@ fun > C.declaration(version: String = "1.0", standalone onTagDeclaration(version, standalone) } -@XmlTagMarker -fun > C.defaultXmlns(namespace: String) = apply { - onTagNamespace(null, namespace) -} - -@XmlTagMarker -fun > C.prefixedXmlns(prefix: String, namespace: String) = apply { - onTagNamespace(prefix, namespace) -} - -@XmlTagMarker -fun > C.prefixedXmlns(map: Map) = apply { - for ((prefix, namespace) in map) - onTagNamespace(prefix, namespace) -} - -@XmlTagMarker -fun > C.prefixedXmlns(map: Iterable>) = apply { - for ((prefix, namespace) in map) - onTagNamespace(prefix, namespace) -} - @XmlTagMarker class XmlTag( override val tagName: String, diff --git a/src/jvmMain/kotlin/info/mechyrdia/lore/Fonts.kt b/src/jvmMain/kotlin/info/mechyrdia/lore/Fonts.kt index 9c36df5..17c0294 100644 --- a/src/jvmMain/kotlin/info/mechyrdia/lore/Fonts.kt +++ b/src/jvmMain/kotlin/info/mechyrdia/lore/Fonts.kt @@ -61,9 +61,9 @@ data class SvgPath( ) fun > C.svg(svgDoc: SvgDoc) = declaration(standalone = false) - .defaultXmlns("http://www.w3.org/2000/svg") .root( "svg", + namespace = "http://www.w3.org/2000/svg", attributes = mapOf( "width" to svgDoc.width.xmlValue, "height" to svgDoc.height.xmlValue, diff --git a/src/jvmMain/kotlin/info/mechyrdia/lore/ParserBuilder.kt b/src/jvmMain/kotlin/info/mechyrdia/lore/ParserBuilder.kt index f5b90d4..34d4681 100644 --- a/src/jvmMain/kotlin/info/mechyrdia/lore/ParserBuilder.kt +++ b/src/jvmMain/kotlin/info/mechyrdia/lore/ParserBuilder.kt @@ -1,5 +1,6 @@ package info.mechyrdia.lore +import info.mechyrdia.MainDomainName import info.mechyrdia.data.Comment import info.mechyrdia.data.Id @@ -92,7 +93,7 @@ private class ToCPropertyBuilderTag(val converter: (String) -> String, val sette } } -fun String.imagePathToOpenGraphValue() = "https://mechyrdia.info/assets/images/${sanitizeLink()}" +fun String.imagePathToOpenGraphValue() = "$MainDomainName/assets/images/${sanitizeLink()}" enum class ToCBuilderTag(val type: BuilderTag) { H1(ToCHeaderBuilderTag(0)), diff --git a/src/jvmMain/kotlin/info/mechyrdia/lore/ViewOg.kt b/src/jvmMain/kotlin/info/mechyrdia/lore/ViewOg.kt index 94374d7..796ca73 100644 --- a/src/jvmMain/kotlin/info/mechyrdia/lore/ViewOg.kt +++ b/src/jvmMain/kotlin/info/mechyrdia/lore/ViewOg.kt @@ -1,5 +1,6 @@ package info.mechyrdia.lore +import info.mechyrdia.MainDomainName import io.ktor.server.application.* import io.ktor.server.request.* import kotlinx.html.HEAD @@ -27,5 +28,5 @@ fun HEAD.renderOgData(title: String, data: OpenGraphData) { ogProperty("type", "website") ogProperty("description", data.desc) ogProperty("image", data.image) - ogProperty("url", "https://mechyrdia.info/${request.path().removePrefix("/")}") + ogProperty("url", "$MainDomainName/${request.path().removePrefix("/")}") } diff --git a/src/jvmMain/kotlin/info/mechyrdia/lore/ViewsQuote.kt b/src/jvmMain/kotlin/info/mechyrdia/lore/ViewsQuote.kt index 358951b..52aacac 100644 --- a/src/jvmMain/kotlin/info/mechyrdia/lore/ViewsQuote.kt +++ b/src/jvmMain/kotlin/info/mechyrdia/lore/ViewsQuote.kt @@ -1,6 +1,7 @@ package info.mechyrdia.lore import info.mechyrdia.JsonFileCodec +import info.mechyrdia.MainDomainName import info.mechyrdia.data.* import info.mechyrdia.route.KeyedEnumSerializer import io.ktor.http.* @@ -24,13 +25,13 @@ data class Quote( get() = if (portrait.startsWith("http://") || portrait.startsWith("https://")) portrait else - "https://mechyrdia.info/assets/images/$portrait" + "$MainDomainName/assets/images/$portrait" val fullLink: String get() = if (link.startsWith("http://") || link.startsWith("https://")) link else - "https://mechyrdia.info/lore/$link" + "$MainDomainName/lore/$link" } private val quotesListGetter by storedData(StoragePath("quotes.json")) { jsonPath -> diff --git a/src/jvmMain/kotlin/info/mechyrdia/lore/ViewsRobots.kt b/src/jvmMain/kotlin/info/mechyrdia/lore/ViewsRobots.kt index cc86545..76a2cb2 100644 --- a/src/jvmMain/kotlin/info/mechyrdia/lore/ViewsRobots.kt +++ b/src/jvmMain/kotlin/info/mechyrdia/lore/ViewsRobots.kt @@ -1,5 +1,6 @@ package info.mechyrdia.lore +import info.mechyrdia.MainDomainName import info.mechyrdia.data.* import java.time.Instant import java.time.format.DateTimeFormatter @@ -50,7 +51,7 @@ private suspend fun buildIntroSitemap(): SitemapEntry? { val introLastModified = maxOf(introFile.updated, introMetaFile.updated) return SitemapEntry( - loc = "https://mechyrdia.info/", + loc = "$MainDomainName/", lastModified = introLastModified, changeFrequency = AVERAGE_FACTBOOK_INTRO_CHANGEFREQ, priority = FACTBOOK_INTRO_PRIORITY @@ -61,7 +62,7 @@ private suspend fun buildLoreSitemap(): List { return allPages().mapNotNull { page -> if (!page.path.isViewable) null else SitemapEntry( - loc = "https://mechyrdia.info/lore/${page.path}", + loc = "$MainDomainName/lore/${page.path}", lastModified = page.stat.updated, changeFrequency = AVERAGE_FACTBOOK_PAGE_CHANGEFREQ, priority = FACTBOOK_PAGE_PRIORITY @@ -72,8 +73,7 @@ private suspend fun buildLoreSitemap(): List { suspend fun buildSitemap() = listOfNotNull(buildIntroSitemap()) + buildLoreSitemap() fun > C.sitemap(entries: List) = declaration() - .defaultXmlns("http://www.sitemaps.org/schemas/sitemap/0.9") - .root("urlset") { + .root("urlset", namespace = "http://www.sitemaps.org/schemas/sitemap/0.9") { for (entry in entries) +entry } diff --git a/src/jvmMain/kotlin/info/mechyrdia/lore/ViewsRss.kt b/src/jvmMain/kotlin/info/mechyrdia/lore/ViewsRss.kt index ef674d9..deb5566 100644 --- a/src/jvmMain/kotlin/info/mechyrdia/lore/ViewsRss.kt +++ b/src/jvmMain/kotlin/info/mechyrdia/lore/ViewsRss.kt @@ -1,6 +1,7 @@ package info.mechyrdia.lore import com.mongodb.client.model.Sorts +import info.mechyrdia.MainDomainName import info.mechyrdia.OwnerNationId import info.mechyrdia.data.* import io.ktor.http.* @@ -54,7 +55,7 @@ suspend fun generateRecentPageEdits(): RssChannel { return RssChannel( title = "Recently Edited Factbooks | The Hour of Decision", - link = "https://mechyrdia.info", + link = MainDomainName, description = "An RSS feed containing all factbooks in The Hour of Decision, in order of most recently edited.", pubDate = mostRecentChange, lastBuildDate = mostRecentChange, @@ -73,7 +74,7 @@ suspend fun generateRecentPageEdits(): RssChannel { val pageOg = pageToC.toOpenGraph() val imageEnclosure = pageOg?.image?.let { url -> - val assetPath = url.removePrefix("https://mechyrdia.info/assets/") + val assetPath = url.removePrefix("$MainDomainName/assets/") val file = StoragePath.assetDir / assetPath RssItemEnclosure( url = url, @@ -85,9 +86,9 @@ suspend fun generateRecentPageEdits(): RssChannel { RssItem( title = pageToC.toPageTitle(), description = pageOg?.desc, - link = "https://mechyrdia.info/lore${pageLink.joinToString { "/$it" }}", + link = "$MainDomainName/lore${pageLink.joinToString { "/$it" }}", author = null, - comments = "https://mechyrdia.info/lore${pageLink.joinToString { "/$it" }}#comments", + comments = "$MainDomainName/lore${pageLink.joinToString { "/$it" }}#comments", enclosure = imageEnclosure, pubDate = page.stat.updated ) @@ -107,7 +108,7 @@ suspend fun ApplicationCall.recentCommentsRssFeedGenerator(limit: Int): RssChann return RssChannel( title = "Recent Comments - Error | The Hour of Decision", - link = "https://mechyrdia.info/comment/recent", + link = "$MainDomainName/comment/recent", description = "Comment limit must be between ${validLimits.first} and ${validLimits.last}, got $limit", pubDate = null, lastBuildDate = Instant.now(), @@ -134,7 +135,7 @@ suspend fun ApplicationCall.recentCommentsRssFeedGenerator(limit: Int): RssChann return RssChannel( title = "Recent Comments | The Hour of Decision", - link = "https://mechyrdia.info/comment/recent", + link = "$MainDomainName/comment/recent", description = "An RSS feed containing the $limit most recently-submitted comments", pubDate = mostRecentComment, lastBuildDate = mostRecentComment, @@ -144,11 +145,11 @@ suspend fun ApplicationCall.recentCommentsRssFeedGenerator(limit: Int): RssChann ), items = comments.map { comment -> RssItem( - title = "Comment by ${comment.submittedBy.name} on https://mechyrdia.info/lore/${comment.submittedIn}", + title = "Comment by ${comment.submittedBy.name} on $MainDomainName/${comment.submittedIn}", description = comment.contentsRaw.parseAs(ParserTree::toCommentPlainText), - link = "https://mechyrdia.info/comment/view/${comment.id}", + link = "$MainDomainName/comment/view/${comment.id}", author = null, - comments = "https://mechyrdia.info/lore/${comment.submittedIn}#comment-${comment.id}", + comments = "$MainDomainName/lore/${comment.submittedIn}#comment-${comment.id}", pubDate = comment.lastEdit ?: comment.submittedAt, categories = listOf( RssCategory(domain = "https://nationstates.net", category = comment.submittedBy.name) diff --git a/src/jvmMain/kotlin/info/mechyrdia/route/ResourceTypes.kt b/src/jvmMain/kotlin/info/mechyrdia/route/ResourceTypes.kt index 5ac391c..3238b3b 100644 --- a/src/jvmMain/kotlin/info/mechyrdia/route/ResourceTypes.kt +++ b/src/jvmMain/kotlin/info/mechyrdia/route/ResourceTypes.kt @@ -1,8 +1,6 @@ package info.mechyrdia.route -import info.mechyrdia.auth.loginPage -import info.mechyrdia.auth.loginRoute -import info.mechyrdia.auth.logoutRoute +import info.mechyrdia.auth.* import info.mechyrdia.data.* import info.mechyrdia.lore.* import io.ktor.http.* @@ -16,7 +14,7 @@ import io.ktor.util.* import io.ktor.util.pipeline.* import kotlinx.coroutines.delay -val ErrorMessageAttributeKey = AttributeKey("ErrorMessage") +val ErrorMessageAttributeKey = AttributeKey("Mechyrdia.ErrorMessage") @Resource("/") class Root(val error: String? = null) : ResourceHandler, ResourceFilter { @@ -318,6 +316,7 @@ class Root(val error: String? = null) : ResourceHandler, ResourceFilter { class CopyPost(val path: List, val vfs: Vfs = Vfs()) : ResourceReceiver { override suspend fun PipelineContext.handleCall(payload: AdminVfsCopyFilePayload) { with(vfs) { filterCall() } + with(payload) { call.verifyCsrfToken() } call.adminDoCopyFile(StoragePath(payload.from), StoragePath(path)) } -- 2.25.1