diff --git a/src/main/kotlin/ru/mipt/spc/master.kt b/src/main/kotlin/ru/mipt/spc/master.kt index 389ecaa..dc678af 100644 --- a/src/main/kotlin/ru/mipt/spc/master.kt +++ b/src/main/kotlin/ru/mipt/spc/master.kt @@ -183,7 +183,8 @@ context(SiteData) private fun FlowContent.mentors() { mentors.forEach { (name, mentor) -> section { id = mentor.id - a(classes = "image", href = resolveRef("mentor-${mentor.id}")) { + val ref = resolvePage("mentor-${mentor.id}") + a(classes = "image", href = ref) { mentor.imagePath?.let { photoPath -> img( src = resolveRef(photoPath), @@ -197,7 +198,7 @@ context(SiteData) private fun FlowContent.mentors() { div("content") { div("inner") { h2 { - a(href = resolveRef("mentor-${mentor.id}")) { +mentor.name } + a(href = ref) { +mentor.name } } val info = resolveHtml(name.withIndex("info")) if (info != null) { @@ -376,7 +377,7 @@ internal fun SiteBuilder.spcMaster(dataPath: Path, prefix: Name = "magprog".asNa mentors.forEach { li { a { - href = resolveRef(it.mentorPageId) + href = resolvePage(it.mentorPageId) +it.name.substringAfterLast(" ") } } diff --git a/src/main/kotlin/ru/mipt/spc/spcCollection.kt b/src/main/kotlin/ru/mipt/spc/spcCollection.kt index 5b7c3da..3b28e2a 100644 --- a/src/main/kotlin/ru/mipt/spc/spcCollection.kt +++ b/src/main/kotlin/ru/mipt/spc/spcCollection.kt @@ -45,7 +45,7 @@ context(SiteData, FlowContent) private fun spcSpotlightContent( //TODO add smart SNARK ordering section("spotlights") { content.entries.sortedBy { it.value.meta["order"].int ?: Int.MAX_VALUE }.forEach { (name, data) -> - val ref = resolveRef(name) + val ref = resolvePage(name) section { id = data.meta["id"].string ?: name.toString() data.meta["image"]?.let { imageMeta: Meta -> diff --git a/src/main/kotlin/ru/mipt/spc/spcHome.kt b/src/main/kotlin/ru/mipt/spc/spcHome.kt index 373721a..3391f87 100644 --- a/src/main/kotlin/ru/mipt/spc/spcHome.kt +++ b/src/main/kotlin/ru/mipt/spc/spcHome.kt @@ -150,7 +150,7 @@ context(SiteData, HTML) private fun spcHome() { header("major") { h3 { a(classes = "link") { - href = resolveRef("magprog") + href = resolvePage("magprog") +"""Master's program""" } } @@ -167,7 +167,7 @@ context(SiteData, HTML) private fun spcHome() { header("major") { h3 { a(classes = "link") { - href = resolveRef("research") + href = resolvePage("research") +"""Research""" } } @@ -186,7 +186,7 @@ context(SiteData, HTML) private fun spcHome() { header("major") { h3 { a(classes = "link") { - href = resolveRef("consulting") + href = resolvePage("consulting") +"""Consulting""" } } @@ -203,7 +203,7 @@ context(SiteData, HTML) private fun spcHome() { header("major") { h3 { a(classes = "link") { - href = resolveRef("team") + href = resolvePage("team") +"""Team""" } } diff --git a/src/main/kotlin/ru/mipt/spc/spcMisc.kt b/src/main/kotlin/ru/mipt/spc/spcMisc.kt index cd29478..ea98dd8 100644 --- a/src/main/kotlin/ru/mipt/spc/spcMisc.kt +++ b/src/main/kotlin/ru/mipt/spc/spcMisc.kt @@ -3,6 +3,7 @@ package ru.mipt.spc import kotlinx.html.* import space.kscience.snark.SiteData import space.kscience.snark.homeRef +import space.kscience.snark.resolvePage import space.kscience.snark.resolveRef @@ -39,25 +40,25 @@ context(SiteData) internal fun FlowContent.spcHomeMenu() { } li { a { - href = resolveRef("magprog") + href = resolvePage("magprog") +"""Master""" } } li { a { - href = resolveRef("research") + href = resolvePage("research") +"""Research""" } } li { a { - href = resolveRef("consulting") + href = resolvePage("consulting") +"""Consulting""" } } li { a { - href = resolveRef("team") + href = resolvePage("team") +"""Team""" } } diff --git a/src/main/kotlin/space/kscience/snark/SiteBuilder.kt b/src/main/kotlin/space/kscience/snark/SiteBuilder.kt index ccabaa2..7586e8e 100644 --- a/src/main/kotlin/space/kscience/snark/SiteBuilder.kt +++ b/src/main/kotlin/space/kscience/snark/SiteBuilder.kt @@ -4,6 +4,7 @@ import kotlinx.html.HTML import space.kscience.dataforge.context.Context import space.kscience.dataforge.context.ContextAware import space.kscience.dataforge.data.DataTree +import space.kscience.dataforge.meta.Laminate import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.parseAsName import java.nio.file.Path @@ -56,8 +57,8 @@ public inline fun SiteBuilder.route(route: String, block: SiteBuilder.() -> Unit public fun SiteBuilder.mountSite(route: Name, dataRoot: DataTree<*>, block: SiteBuilder.() -> Unit) { val mountedData = data.copy( data = dataRoot, - baseUrlPath = data.baseUrlPath.removeSuffix("/") + "/" + route.toWebPath(), - meta = dataRoot.meta // TODO consider meshing sub-site meta with the parent site + baseUrlPath = data.resolveRef(route.tokens.joinToString(separator = "/")), + meta = Laminate(dataRoot.meta, data.meta) //layering dataRoot meta over existing data ) route(route) { withData(mountedData).block() diff --git a/src/main/kotlin/space/kscience/snark/SiteData.kt b/src/main/kotlin/space/kscience/snark/SiteData.kt index 56bf8cd..e62451e 100644 --- a/src/main/kotlin/space/kscience/snark/SiteData.kt +++ b/src/main/kotlin/space/kscience/snark/SiteData.kt @@ -6,11 +6,8 @@ import space.kscience.dataforge.data.* import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.get import space.kscience.dataforge.meta.string -import space.kscience.dataforge.names.Name -import space.kscience.dataforge.names.NameToken -import space.kscience.dataforge.names.plus -import space.kscience.dataforge.names.startsWith -import space.kscience.snark.SiteData.Companion.INDEX_PAGE_NAME +import space.kscience.dataforge.names.* +import space.kscience.snark.SiteData.Companion.INDEX_PAGE_TOKEN import kotlin.reflect.KType import kotlin.reflect.typeOf @@ -18,7 +15,7 @@ data class SiteData( val snark: SnarkPlugin, val data: DataTree<*>, val baseUrlPath: String, - override val meta: Meta = data.meta, + override val meta: Meta, ) : ContextAware, DataTree by data { override val context: Context get() = snark.context @@ -28,33 +25,49 @@ data class SiteData( companion object { fun empty( snark: SnarkPlugin, - baseUrlPath: String = "/", + baseUrlPath: String = "", meta: Meta = Meta.EMPTY, ): SiteData { + //TODO use empty data from DF val emptyData = object : DataTree { override val items: Map> get() = emptyMap() override val dataType: KType get() = typeOf() override val meta: Meta get() = meta } - return SiteData(snark, emptyData, baseUrlPath) + return SiteData(snark, emptyData, baseUrlPath, meta) } - const val INDEX_PAGE_NAME: String = "index" + const val INDEX_PAGE_TOKEN: String = "index" } } /** * Resolve a resource full path by its name */ -fun SiteData.resolveRef(name: String): String = "${baseUrlPath.removeSuffix("/")}/$name" +fun SiteData.resolveRef(name: String): String = if (baseUrlPath.isEmpty()) { + name +} else { + "${baseUrlPath.removeSuffix("/")}/$name" +} -fun SiteData.resolveRef(name: Name): String = "${baseUrlPath.removeSuffix("/")}/${name.tokens.joinToString("/")}" +/** + * Resolve a page designated by given name. Depending on rendering specifics, some prefixes or suffixes could be added. + */ +fun SiteData.resolvePage(name: Name): String = + resolveRef(name.tokens.joinToString("/")) + (meta["pageSuffix"].string ?: "") + +/** + * + */ +fun SiteData.resolvePage(name: String): String = resolvePage(name.parseAsName()) + +val SiteData.homeRef get() = resolvePage(Name.EMPTY) /** * Resolve a Html builder by its full name */ fun DataTree<*>.resolveHtml(name: Name): HtmlData? { - val resolved = (getByType(name) ?: getByType(name + INDEX_PAGE_NAME)) + val resolved = (getByType(name) ?: getByType(name + INDEX_PAGE_TOKEN)) return resolved?.takeIf { it.published //TODO add language confirmation @@ -71,7 +84,6 @@ fun DataTree<*>.resolveAllHtml(predicate: (name: Name, meta: Meta) -> Boolean): //TODO add language confirmation }.asSequence().associate { it.name to it.data } -val SiteData.homeRef get() = resolveRef("").removeSuffix("/") fun SiteData.findByType(contentType: String, baseName: Name = Name.EMPTY) = resolveAllHtml { name, meta -> name.startsWith(baseName) && meta["content_type"].string == contentType diff --git a/src/main/kotlin/space/kscience/snark/StaticSiteBuilder.kt b/src/main/kotlin/space/kscience/snark/StaticSiteBuilder.kt index 25c163f..038a507 100644 --- a/src/main/kotlin/space/kscience/snark/StaticSiteBuilder.kt +++ b/src/main/kotlin/space/kscience/snark/StaticSiteBuilder.kt @@ -3,21 +3,22 @@ package space.kscience.snark import kotlinx.html.HTML import kotlinx.html.html import kotlinx.html.stream.createHTML +import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.isEmpty import java.nio.file.Files import java.nio.file.Path -import kotlin.io.path.copyTo -import kotlin.io.path.createDirectories -import kotlin.io.path.relativeTo -import kotlin.io.path.writeText +import kotlin.io.path.* class StaticSiteBuilder(override val data: SiteData, private val path: Path) : SiteBuilder { private fun Path.copyRecursively(target: Path) { Files.walk(this).forEach { source: Path -> val destination: Path = target.resolve(source.relativeTo(this)) - source.copyTo(destination,true) + if(!destination.isDirectory()) { + //avoid re-creating directories + source.copyTo(destination, true) + } } } @@ -68,5 +69,12 @@ class StaticSiteBuilder(override val data: SiteData, private val path: Path) : S } fun SnarkPlugin.static(path: Path, block: SiteBuilder.() -> Unit) { - StaticSiteBuilder(SiteData.empty(this), path).block() + val base = SiteData.empty( + this, + baseUrlPath = path.absolutePathString(), + meta = Meta { + "pageSuffix" put "/index.html" + } + ) + StaticSiteBuilder(base, path).block() } \ No newline at end of file