From 1923a1d29693a7fc6209e9c64b77b1218d715ab3 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Thu, 4 Jan 2024 11:26:23 +0300 Subject: [PATCH] [WIP] refactor in progress --- build.gradle.kts | 2 +- gradle.properties | 2 +- gradle/wrapper/gradle-wrapper.properties | 2 +- src/main/kotlin/center/sciprog/Application.kt | 18 +- src/main/kotlin/center/sciprog/bmkLanding.kt | 310 +++++++++--------- .../kotlin/center/sciprog/spcCollection.kt | 31 +- src/main/kotlin/center/sciprog/spcHome.kt | 172 +++++----- src/main/kotlin/center/sciprog/spcMasters.kt | 277 ++++++++-------- src/main/kotlin/center/sciprog/spcMisc.kt | 16 +- src/main/kotlin/center/sciprog/spcSite.kt | 13 +- .../kotlin/center/sciprog/staticRender.kt | 19 +- src/main/kotlin/html5up/forty/common.kt | 5 +- src/main/kotlin/html5up/forty/landing.kt | 9 +- src/main/kotlin/html5up/forty/page.kt | 5 +- 14 files changed, 455 insertions(+), 426 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index a7b240a..c5b956c 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -35,7 +35,7 @@ dependencies { implementation("io.ktor:ktor-server-netty:$ktorVersion") implementation("io.ktor:ktor-server-http-redirect:$ktorVersion") implementation("io.ktor:ktor-server-forwarded-header:$ktorVersion") - implementation("ch.qos.logback:logback-classic:1.2.11") + implementation("ch.qos.logback:logback-classic:1.4.12") testImplementation("io.ktor:ktor-server-tests:$ktorVersion") } diff --git a/gradle.properties b/gradle.properties index ae81459..936d461 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ kotlin.code.style=official -toolsVersion=0.14.9-kotlin-1.8.20 +toolsVersion=0.15.2-kotlin-1.9.21 snarkVersion=0.1.0-dev-1 \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index e1bef7e..e411586 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/src/main/kotlin/center/sciprog/Application.kt b/src/main/kotlin/center/sciprog/Application.kt index afa6952..6ed5280 100644 --- a/src/main/kotlin/center/sciprog/Application.kt +++ b/src/main/kotlin/center/sciprog/Application.kt @@ -10,12 +10,11 @@ import io.ktor.server.plugins.forwardedheaders.XForwardedHeaders import io.ktor.server.response.respondRedirect import io.ktor.server.routing.get import io.ktor.server.routing.routing -import space.kscience.dataforge.context.Global +import space.kscience.dataforge.context.Context import space.kscience.dataforge.context.request import space.kscience.dataforge.data.DataTree -import space.kscience.snark.html.SiteBuilder -import space.kscience.snark.html.SnarkHtmlPlugin -import space.kscience.snark.html.readDirectory +import space.kscience.dataforge.workspace.readDataDirectory +import space.kscience.snark.html.* import space.kscience.snark.ktor.prepareSnarkDataCacheDirectory import space.kscience.snark.ktor.site import java.nio.file.FileSystems @@ -69,7 +68,11 @@ fun Application.spcModule() { install(ForwardedHeaders) install(XForwardedHeaders) - val snark = Global.request(SnarkHtmlPlugin) + val context = Context { + plugin(SnarkHtml) + } + + val snark = context.request(SnarkHtml) val dataDirectory = Path.of( environment.config.tryGetString("ktor.environment.dataDirectory") ?: "data" @@ -81,14 +84,13 @@ fun Application.spcModule() { copyResource("magprog", dataDirectory) } - val siteData: DataTree = snark.readDirectory(dataDirectory) - - site(snark, siteData, block = SiteBuilder::spcSite) + val siteData: DataTree = snark.io.readDataDirectory(dataDirectory) routing { get("magprog") { call.respondRedirect("education/masters") } + site(context, siteData, content = spcSite) } } diff --git a/src/main/kotlin/center/sciprog/bmkLanding.kt b/src/main/kotlin/center/sciprog/bmkLanding.kt index e57ca33..7e14e6c 100644 --- a/src/main/kotlin/center/sciprog/bmkLanding.kt +++ b/src/main/kotlin/center/sciprog/bmkLanding.kt @@ -3,7 +3,6 @@ package center.sciprog import kotlinx.coroutines.runBlocking import kotlinx.html.* import space.kscience.dataforge.data.Data -import space.kscience.dataforge.data.DataTree import space.kscience.dataforge.data.await import space.kscience.dataforge.data.getByType import space.kscience.dataforge.meta.Meta @@ -11,7 +10,6 @@ import space.kscience.dataforge.meta.get import space.kscience.dataforge.meta.getIndexed import space.kscience.dataforge.meta.string import space.kscience.dataforge.names.Name -import space.kscience.dataforge.names.parseAsName import space.kscience.snark.html.* @@ -22,58 +20,55 @@ private val Data<*>.fragment: String get() = meta["fragment"].string ?: "" -internal fun SiteBuilder.bmk(data: DataTree, prefix: Name = "bmk".parseAsName()) { +internal val bmk = HtmlSite { -// val data: DataTree = snark.readDirectory(dataPath.resolve("content")) + static("assets") + static("images") + static("common.assets.webfonts", "assets/webfonts") + static("common", "") - site(prefix, data) { - static("assets") - static("images") - static("common.assets.webfonts", "assets/webfonts") - static("common", "") + val about: Data = siteData.resolveHtml("about") + val team: Data = siteData.resolveHtml("team.index") + val teamData: Map> = siteData.resolveAllHtml { _, meta -> meta["type"].string == "team" } + val solutions: Data = siteData.resolveHtml("lotSeis") + val partners: Data = siteData.resolveHtml("partners") + val partnersData = runBlocking { siteData.getByType("partnersData")!!.await() } - val about: Data = data.resolveHtml("about") - val team: Data = data.resolveHtml("team.index") - val teamData: Map> = data.resolveAllHtml { _, meta -> meta["type"].string == "team" } - val solutions: Data = data.resolveHtml("lotSeis") - val partners: Data = data.resolveHtml("partners") - val partnersData = runBlocking { data.getByType("partnersData")!!.await() } - - page { - head { - title = "БМК-Сервис" - meta { - charset = "utf-8" - } - meta { - name = "viewport" - content = "width=device-width, initial-scale=1, user-scalable=no" - } + page { + head { + title = "БМК-Сервис" + meta { + charset = "utf-8" + } + meta { + name = "viewport" + content = "width=device-width, initial-scale=1, user-scalable=no" + } + link { + rel = "stylesheet" + href = resolveRef("assets/css/main.css") + } + noScript { link { rel = "stylesheet" - href = resolveRef("assets/css/main.css") - } - noScript { - link { - rel = "stylesheet" - href = resolveRef("assets/css/noscript.css") - } + href = resolveRef("assets/css/noscript.css") } } - body("is-preload") { + } + body("is-preload") { // Wrapper - div { - id = "wrapper" + div { + id = "wrapper" // Header - header("alt") { - id = "header" - span("logo") { - img { - src = "images/logo.svg" - alt = "" - } + header("alt") { + id = "header" + span("logo") { + img { + src = "images/logo.svg" + alt = "" } - h1 { +"""БМК-Сервис""" } + } + h1 { +"""БМК-Сервис""" } // p { // +"""Just another free, fully responsive site template""" // br { @@ -90,99 +85,98 @@ internal fun SiteBuilder.bmk(data: DataTree, prefix: Name = "bmk".parseAsNa // } // +""".""" // } - } + } // Nav - nav { - id = "nav" - ul { - li { - a(classes = "active") { - href = "#${about.fragment}" - +about.title + nav { + id = "nav" + ul { + li { + a(classes = "active") { + href = "#${about.fragment}" + +about.title + } + } + li { + a { + href = "#${team.fragment}" + +team.title + } + } + li { + a { + href = "#${solutions.fragment}" + +solutions.title + } + } + li { + a { + href = "#${partners.fragment}" + +partners.title + } + } + } + } + div { + id = "main" + section("main") { + id = about.fragment + div("spotlight") { + div("content") { + header("major") { + h2 { +about.title } + } + htmlData(about) + } + } + } + section("main") { + id = team.fragment + header("major") { + h2 { +team.title } + } + fragment(team) + teamData.values.sortedBy { it.order }.forEach { data -> + span("image left") { + img { + src = resolveRef("images/${data.meta["image"].string!!}") + height = "120dp" } } - li { - a { - href = "#${team.fragment}" - +team.title - } - } - li { - a { - href = "#${solutions.fragment}" - +solutions.title - } - } - li { - a { - href = "#${partners.fragment}" - +partners.title + h3 { +data.title } + fragment(data) + } + } + section("main") { + id = solutions.fragment + header("major") { + h2 { +solutions.title } + fragment(solutions) + span("image fit") { + img { + src = resolveRef("images/fresnel_lands_critdepth2.png") } } } } - div { - id = "main" - section("main") { - id = about.fragment - div("spotlight") { - div("content") { - header("major") { - h2 { +about.title } - } - htmlData(about) - } - } - } - section("main") { - id = team.fragment - header("major") { - h2 { +team.title } - } - htmlData(team) - teamData.values.sortedBy { it.order }.forEach { data -> - span("image left") { - img { - src = resolveRef("images/${data.meta["image"].string!!}") - height = "120dp" - } - } - h3 { +data.title } - htmlData(data) - } - } - section("main") { - id = solutions.fragment - header("major") { - h2 { +solutions.title } - htmlData(solutions) - span("image fit") { - img { - src = resolveRef("images/fresnel_lands_critdepth2.png") - } - } - } - } - section("main") { - id = partners.fragment - header("major") { - h2 { +partners.title } - htmlData(partners) - table { - partnersData.getIndexed("content").values.forEach { - tr { - td { - span("image right") { - img { - src = resolveRef(it["image"].string!!) - height = "120dp" - width = "auto" - } + section("main") { + id = partners.fragment + header("major") { + h2 { +partners.title } + fragment(partners) + table { + partnersData.getIndexed("content").values.forEach { + tr { + td { + span("image right") { + img { + src = resolveRef(it["image"].string!!) + height = "120dp" + width = "auto" } - h3 { - a(href = it["target"].string!!) { - +it["title"].string!! - } + } + h3 { + a(href = it["target"].string!!) { + +it["title"].string!! } } } @@ -191,8 +185,9 @@ internal fun SiteBuilder.bmk(data: DataTree, prefix: Name = "bmk".parseAsNa } } } + } // Footer - footer { + footer { // id = "footer" // section { // h2 { +"""Aliquam sed mauris""" } @@ -254,38 +249,37 @@ internal fun SiteBuilder.bmk(data: DataTree, prefix: Name = "bmk".parseAsNa // } // } // } - p("copyright") { - +"""SPC. Design:""" - a { - href = "https://html5up.net" - +"""HTML5 UP""" - } - +""".""" + p("copyright") { + +"""SPC. Design:""" + a { + href = "https://html5up.net" + +"""HTML5 UP""" } + +""".""" } } + } // Scripts - script { - src = resolveRef("assets/js/jquery.min.js") - } - script { - src = resolveRef("assets/js/jquery.scrollex.min.js") - } - script { - src = resolveRef("assets/js/jquery.scrolly.min.js") - } - script { - src = resolveRef("assets/js/browser.min.js") - } - script { - src = resolveRef("assets/js/breakpoints.min.js") - } - script { - src = resolveRef("assets/js/util.js") - } - script { - src = resolveRef("assets/js/main.js") - } + script { + src = resolveRef("assets/js/jquery.min.js") + } + script { + src = resolveRef("assets/js/jquery.scrollex.min.js") + } + script { + src = resolveRef("assets/js/jquery.scrolly.min.js") + } + script { + src = resolveRef("assets/js/browser.min.js") + } + script { + src = resolveRef("assets/js/breakpoints.min.js") + } + script { + src = resolveRef("assets/js/util.js") + } + script { + src = resolveRef("assets/js/main.js") } } } diff --git a/src/main/kotlin/center/sciprog/spcCollection.kt b/src/main/kotlin/center/sciprog/spcCollection.kt index 85f2e26..a5826a6 100644 --- a/src/main/kotlin/center/sciprog/spcCollection.kt +++ b/src/main/kotlin/center/sciprog/spcCollection.kt @@ -13,9 +13,11 @@ import kotlin.collections.component1 import kotlin.collections.component2 import kotlin.collections.set -context(WebPage) private fun FlowContent.spcSpotlightContent( - landing: HtmlData, - content: Map, + +context(PageContextWithData) +private fun FlowContent.spcSpotlightContent( + landing: Data, + content: Map>, ) { // Banner // Note: The "styleN" class below should match that of the header element. @@ -32,7 +34,7 @@ context(WebPage) private fun FlowContent.spcSpotlightContent( h1 { +(landing.meta["title"].string ?: "???") } } div("content") { - htmlData(landing) + fragment(landing) } } } @@ -63,8 +65,9 @@ context(WebPage) private fun FlowContent.spcSpotlightContent( header("major") { h3 { +(entry.meta["title"].string ?: "???") } } - val infoData = data.resolveHtmlOrNull(name.replaceLast { NameToken(it.body + "[info]") }) ?: entry - htmlData(infoData) + val infoData = + data.resolveHtmlOrNull(name.replaceLast { NameToken(it.body + "[info]") }) ?: entry + fragment(infoData) ul("actions") { li { a(classes = "button") { @@ -82,16 +85,16 @@ context(WebPage) private fun FlowContent.spcSpotlightContent( } -internal fun SiteBuilder.spcSpotlight( +internal fun SiteContextWithData.spcSpotlight( address: String, contentFilter: (Name, Meta) -> Boolean, ) { val pageName = address.parseAsName() val languagePrefix = languagePrefix - val body = data.resolveHtmlOrNull(languagePrefix + pageName) - ?: data.resolveHtmlOrNull(pageName) ?: error("Could not find body for $pageName") - val content: Map> = - data.resolveAllHtml { name, meta -> name.startsWith(languagePrefix) && contentFilter(name, meta) } + val body = siteData.resolveHtmlOrNull(languagePrefix + pageName) + ?: siteData.resolveHtmlOrNull(pageName) ?: error("Could not find body for $pageName") + val content: Map> = + siteData.resolveAllHtml { name, meta -> name.startsWith(languagePrefix) && contentFilter(name, meta) } val meta = body.meta page(pageName) { @@ -107,10 +110,6 @@ internal fun SiteBuilder.spcSpotlight( } content.forEach { (name, contentBody) -> - page(name, contentBody.meta) { - spcPageContent { - htmlData(contentBody) - } - } + page(name, contentBody.meta, spcPage(contentBody)) } } \ No newline at end of file diff --git a/src/main/kotlin/center/sciprog/spcHome.kt b/src/main/kotlin/center/sciprog/spcHome.kt index f97f3a1..819658c 100644 --- a/src/main/kotlin/center/sciprog/spcHome.kt +++ b/src/main/kotlin/center/sciprog/spcHome.kt @@ -2,19 +2,15 @@ package center.sciprog import html5up.forty.fortyScripts import kotlinx.html.* -import space.kscience.dataforge.data.Data -import space.kscience.dataforge.data.DataTree -import space.kscience.dataforge.meta.* -import space.kscience.dataforge.names.Name +import space.kscience.dataforge.data.* +import space.kscience.dataforge.meta.get +import space.kscience.dataforge.meta.string import space.kscience.dataforge.names.asName import space.kscience.dataforge.names.startsWith import space.kscience.snark.html.* -import kotlin.reflect.typeOf -context(WebPage) internal fun HTML.spcPageContent( - fragment: FlowContent.() -> Unit, -) { +internal fun spcPage(content: Data) = HtmlPage { val title by pageMeta.string { SPC_TITLE } val pageName by pageMeta.string { title } spcHead(pageName) @@ -30,7 +26,8 @@ context(WebPage) internal fun HTML.spcPageContent( } pageMeta["image"]?.let { imageMeta -> val imagePath = - imageMeta.value?.string ?: imageMeta["path"].string ?: error("Image path not provided") + imageMeta.value?.string ?: imageMeta["path"].string + ?: error("Image path not provided") val imageClass = imageMeta["position"].string ?: "main" span("image $imageClass") { img { @@ -39,7 +36,7 @@ context(WebPage) internal fun HTML.spcPageContent( } } } - fragment() + fragment(content) } } } @@ -49,34 +46,7 @@ context(WebPage) internal fun HTML.spcPageContent( } } -@Suppress("UNCHECKED_CAST") -internal val FortyDataRenderer: DataRenderer = object : DataRenderer { - - context(SiteBuilder) - override fun invoke(name: Name, data: Data) { - if (data.type == typeOf()) { - data as Data - val languageMeta: Meta = Language.forName(name) - - val dataMeta: Meta = if (languageMeta.isEmpty()) { - data.meta - } else { - data.meta.toMutableMeta().apply { - Language.LANGUAGES_KEY put languageMeta - } - } - - page(name, dataMeta) { - spcPageContent { - htmlData(data) - } - } - } - } -} - - -context(WebPage) private fun HTML.spcHomePage() { +internal val spcHomePage = HtmlPage { spcHead() body("is-preload") { wrapper { @@ -156,14 +126,14 @@ context(WebPage) private fun HTML.spcHomePage() { article { span("image") { img { - src = resolveRef("images/pic01.jpg") + src = page.resolveRef("images/pic01.jpg") alt = "" } } header("major") { h3 { a(classes = "link") { - href = resolvePageRef("education") + href = page.resolvePageRef("education") +"""Education""" } } @@ -173,14 +143,14 @@ context(WebPage) private fun HTML.spcHomePage() { article { span("image") { img { - src = resolveRef("images/pic02.jpg") + src = page.resolveRef("images/pic02.jpg") alt = "" } } header("major") { h3 { a(classes = "link") { - href = resolvePageRef("research") + href = page.resolvePageRef("research") +"""Research""" } } @@ -192,14 +162,14 @@ context(WebPage) private fun HTML.spcHomePage() { article { span("image") { img { - src = resolveRef("images/pic03.jpg") + src = page.resolveRef("images/pic03.jpg") alt = "" } } header("major") { h3 { a(classes = "link") { - href = resolvePageRef("consulting.index") + href = page.resolvePageRef("consulting.index") +"""Consulting""" } } @@ -209,14 +179,14 @@ context(WebPage) private fun HTML.spcHomePage() { article { span("image") { img { - src = resolveRef("images/pic04.jpg") + src = page.resolveRef("images/pic04.jpg") alt = "" } } header("major") { h3 { a(classes = "link") { - href = resolvePageRef("team") + href = page.resolvePageRef("team") +"""Team""" } } @@ -263,32 +233,88 @@ context(WebPage) private fun HTML.spcHomePage() { } } -internal fun SiteBuilder.spcHome(homePageData: DataTree, prefix: Name = Name.EMPTY) { - - //val homePageData: DataTree = snark.readDirectory(dataPath.resolve("content")) - - site(prefix, homePageData) { - static("assets") - static("images") - static("common", "") - - withLanguages( - "en" to "", - "ru" to "ru" - ) { - page { spcHomePage() } - - localizedPages("consulting", dataRenderer = FortyDataRenderer) - - localizedPages("education", dataRenderer = FortyDataRenderer) - - spcSpotlight("team") { _, meta -> - meta["type"].string == "team" - } - - spcSpotlight("research") { name, meta -> - name.startsWith("projects".asName()) && meta["type"].string == "project" - } - } +private fun SiteContextWithData.allPagesIn(location: String){ + siteData.filterByType { name, meta -> + name.startsWith(location) && meta["type"].string == "page" + }.forEach { (name, content) -> + page(name, content = spcPage(content)) } } + + +internal val spcHome: HtmlSite = HtmlSite { + static("assets") + static("images") + static("common", "") + + multiLanguageSite( + siteData, + mapOf( + "en" to Language(""), + "ru" to Language("ru"), + ) + ) { + page(content = spcHomePage) + + allPagesIn("consulting") + + allPagesIn("education") + + spcSpotlight("team") { _, meta -> + meta["type"].string == "team" + } + + spcSpotlight("research") { name, meta -> + name.startsWith("projects".asName()) && meta["type"].string == "project" + } + } +// withLanguages( +// "en" to "", +// "ru" to "ru" +// ) { +// page { spcHomePage() } +// +// localizedPages("consulting", dataRenderer = FortyDataRenderer) +// +// localizedPages("education", dataRenderer = FortyDataRenderer) +// +// spcSpotlight("team") { _, meta -> +// meta["type"].string == "team" +// } +// +// spcSpotlight("research") { name, meta -> +// name.startsWith("projects".asName()) && meta["type"].string == "project" +// } +// } + +} +// +//internal fun SiteBuilder.spcHome(homePageData: DataTree, prefix: Name = Name.EMPTY) { +// +// //val homePageData: DataTree = snark.readDirectory(dataPath.resolve("content")) +// +// site(prefix, homePageData) { +// static("assets") +// static("images") +// static("common", "") +// +// withLanguages( +// "en" to "", +// "ru" to "ru" +// ) { +// page { spcHomePage() } +// +// localizedPages("consulting", dataRenderer = FortyDataRenderer) +// +// localizedPages("education", dataRenderer = FortyDataRenderer) +// +// spcSpotlight("team") { _, meta -> +// meta["type"].string == "team" +// } +// +// spcSpotlight("research") { name, meta -> +// name.startsWith("projects".asName()) && meta["type"].string == "project" +// } +// } +// } +//} diff --git a/src/main/kotlin/center/sciprog/spcMasters.kt b/src/main/kotlin/center/sciprog/spcMasters.kt index 3efb4ad..6595864 100644 --- a/src/main/kotlin/center/sciprog/spcMasters.kt +++ b/src/main/kotlin/center/sciprog/spcMasters.kt @@ -2,7 +2,7 @@ package center.sciprog import kotlinx.coroutines.runBlocking import kotlinx.html.* -import space.kscience.dataforge.data.DataTree +import space.kscience.dataforge.data.Data import space.kscience.dataforge.data.await import space.kscience.dataforge.data.getByType import space.kscience.dataforge.meta.Meta @@ -29,17 +29,19 @@ import kotlin.collections.set // } //} -private val HtmlData.imagePath: String? get() = meta["image"]?.string ?: meta["image.path"].string -private val HtmlData.name: String get() = meta["name"].string ?: error("Name not found") +private val Data.imagePath: String? get() = meta["image"]?.string ?: meta["image.path"].string +private val Data.name: String get() = meta["name"].string ?: error("Name not found") -context(WebPage) class MagProgSection( +context(PageContext) +class MagProgSection( val id: String, val title: String, val style: String, val content: FlowContent.() -> Unit, ) -context(WebPage) private fun wrapSection( +context(PageContext) +private fun wrapSection( id: String, title: String, sectionContent: FlowContent.() -> Unit, @@ -50,14 +52,15 @@ context(WebPage) private fun wrapSection( } } -context(WebPage) private fun wrapSection( - block: HtmlData, +context(PageContextWithData) +private fun wrapSection( + section: Data, idOverride: String? = null, ): MagProgSection = wrapSection( - idOverride ?: block.id, - block.meta["section_title"]?.string ?: error("Section without title"), + idOverride ?: section.id, + section.meta["section_title"]?.string ?: error("Section without title"), ) { - htmlData(block) + fragment(section) } private val CONTENT_NODE_NAME = Name.EMPTY//"content".asName() @@ -68,24 +71,25 @@ private val PROGRAM_PATH: Name = CONTENT_NODE_NAME + "program" private val RECOMMENDED_COURSES_PATH: Name = CONTENT_NODE_NAME + "recommendedCourses" private val PARTNERS_PATH: Name = CONTENT_NODE_NAME + "partners" -context(WebPage) private fun FlowContent.programSection() { +private val programSection = PageFragment { val programBlock = data.resolveHtmlOrNull(PROGRAM_PATH)!! val recommendedBlock = data.resolveHtmlOrNull(RECOMMENDED_COURSES_PATH)!! div("inner") { h2 { +"Учебная программа" } - htmlData(programBlock) + fragment(programBlock) button(classes = "fit collapsible") { attributes["data-target"] = "recommended-courses-content" +"Рекомендованные курсы" } div(classes = "collapsible-content") { id = "recommended-courses-content" - htmlData(recommendedBlock) + fragment(recommendedBlock) } } } -context(WebPage) private fun FlowContent.partners() { + +private val partners = PageFragment { //val partnersData: Meta = resolve(PARTNERS_PATH)?.meta ?: Meta.EMPTY val partnersData: Meta = runBlocking { data.getByType(PARTNERS_PATH)?.await() } ?: Meta.EMPTY div("inner") { @@ -115,8 +119,8 @@ context(WebPage) private fun FlowContent.partners() { // val photo: String? by meta.string() //} -context(WebPage) private fun FlowContent.team() { - val team = data.findByContentType("magprog_team").values.sortedBy { it.order } +private val team = PageFragment { + val team = data.findHtmlByContentType("magprog_team").values.sortedBy { it.order } div("inner") { h2 { +"Команда" } @@ -131,7 +135,7 @@ context(WebPage) private fun FlowContent.team() { alt = imagePath ) { h3 { +member.name } - htmlData(member) + fragment(member) } } } @@ -170,8 +174,8 @@ context(WebPage) private fun FlowContent.team() { // } } -context(WebPage) private fun FlowContent.mentors() { - val mentors = data.findByContentType("magprog_mentor").entries.sortedBy { it.value.id } +val mentors = PageFragment { + val mentors = data.findHtmlByContentType("magprog_mentor").entries.sortedBy { it.value.id } div("inner") { h2 { @@ -200,7 +204,7 @@ context(WebPage) private fun FlowContent.mentors() { } val info = data.resolveHtmlOrNull(name.replaceLast { NameToken(it.body + "[info]") }) if (info != null) { - htmlData(info) + fragment(info) } } } @@ -208,7 +212,8 @@ context(WebPage) private fun FlowContent.mentors() { } } -context(WebPage) internal fun HTML.magProgHead(title: String) { +context(PageContext) +internal fun HTML.magProgHead(title: String) { head { title { +title @@ -258,7 +263,8 @@ context(WebPage) internal fun HTML.magProgHead(title: String) { } } -context(WebPage) internal fun BODY.magProgFooter() { +context(PageContext) +internal fun BODY.magProgFooter() { footer("wrapper style1-alt") { id = "footer" div("inner") { @@ -297,72 +303,110 @@ context(WebPage) internal fun BODY.magProgFooter() { } } -context(SnarkContext) private val HtmlData.mentorPageId get() = "mentor-${id}" +context(SnarkContext) private val Data.mentorPageId get() = "mentor-${id}" -internal fun SiteBuilder.spcMasters(magProgData: DataTree, prefix: Name = "education.masters".parseAsName()) { - //val magProgData: DataTree = snark.readDirectory(dataPath.resolve("content")) +internal val spcMasters = HtmlSite { + static("assets") + static("images") + static("common", "") - site(prefix, magProgData) { - static("assets") - static("images") - static("common", "") + page{ + val sections = listOf( + wrapSection(data.resolveHtmlOrNull(INTRO_PATH)!!, "intro"), + MagProgSection( + id = "partners", + title = "Партнеры", + style = "wrapper style3 fullscreen fade-up" + ) { + fragment(partners) + }, + // section(props.data.partners), + MagProgSection( + id = "mentors", + title = "Научные руководители", + style = "wrapper style2 spotlights", + ) { + fragment(mentors) + }, + MagProgSection( + id = "program", + title = "Учебная программа", + style = "wrapper style3 fullscreen fade-up" + ) { + fragment(programSection) + }, + wrapSection(data.resolveHtmlOrNull(ENROLL_PATH)!!, "enroll"), + wrapSection(id = "contacts", title = "Контакты") { + fragment(data.resolveHtmlOrNull(CONTACTS_PATH)!!) + fragment(team) + } + ) - page { - val sections = listOf( - wrapSection(page.data.resolveHtmlOrNull(INTRO_PATH)!!, "intro"), - MagProgSection( - id = "partners", - title = "Партнеры", - style = "wrapper style3 fullscreen fade-up" - ) { - partners() - }, - // section(props.data.partners), - MagProgSection( - id = "mentors", - title = "Научные руководители", - style = "wrapper style2 spotlights", - ) { - mentors() - }, - MagProgSection( - id = "program", - title = "Учебная программа", - style = "wrapper style3 fullscreen fade-up" - ) { - programSection() - }, - wrapSection(page.data.resolveHtmlOrNull(ENROLL_PATH)!!, "enroll"), - wrapSection(id = "contacts", title = "Контакты") { - htmlData(page.data.resolveHtmlOrNull(CONTACTS_PATH)!!) - team() - } - ) - - magProgHead("Магистратура \"Научное программирование\"") - body("is-preload magprog-body") { - section { - id = "sidebar" - div("inner") { - nav { - ul { + magProgHead("Магистратура \"Научное программирование\"") + body("is-preload magprog-body") { + section { + id = "sidebar" + div("inner") { + nav { + ul { + li { + a( + classes = "spc-home", + href = "/" //TODO provide a way to navigate out-of-site + ) { + i("fa fa-home") { + attributes["aria-hidden"] = "true" + } + +"SPC" + } + } + sections.forEach { section -> li { - a( - classes = "spc-home", - href = "/" //TODO provide a way to navigate out-of-site - ) { - i("fa fa-home") { - attributes["aria-hidden"] = "true" - } - +"SPC" + a(href = "#${section.id}") { + +section.title } } - sections.forEach { section -> - li { - a(href = "#${section.id}") { - +section.title - } + } + } + } + } + } + div { + id = "wrapper" + sections.forEach { sec -> + section(sec.style) { + id = sec.id + with(sec) { content() } + } + } + } + magProgFooter() + } + } + + + val mentors = siteData.findHtmlByContentType("magprog_mentor").values.sortedBy { + it.order + } + + mentors.forEach { mentor -> + page(mentor.mentorPageId.asName(), siteData) { + magProgHead("Научное программирование: ${mentor.name}") + body("is-preload") { + header { + id = "header" + a(classes = "title") { + href = "${page.homeRef}#mentors" + +"Научные руководители" + } + nav { + ul { + mentors.forEach { + li { + a { + href = page.resolvePageRef(it.mentorPageId) + +it.name.substringAfterLast(" ") } } } @@ -371,68 +415,25 @@ internal fun SiteBuilder.spcMasters(magProgData: DataTree, prefix: Name = " } div { id = "wrapper" - sections.forEach { sec -> - section(sec.style) { - id = sec.id - with(sec) { content() } + section("wrapper") { + id = "main" + div("inner") { + h1("major") { +mentor.name } + val imageClass = mentor.meta["image.position"].string ?: "left" + span("image $imageClass") { + mentor.imagePath?.let { photoPath -> + img( + src = page.resolveRef(photoPath), + alt = mentor.name + ) + } + } + fragment(mentor) } } } magProgFooter() } } - - - val mentors = data.findByContentType("magprog_mentor").values.sortedBy { - it.order - } - - mentors.forEach { mentor -> - page(mentor.mentorPageId.asName()) { - - magProgHead("Научное программирование: ${mentor.name}") - body("is-preload") { - header { - id = "header" - a(classes = "title") { - href = "$homeRef#mentors" - +"Научные руководители" - } - nav { - ul { - mentors.forEach { - li { - a { - href = resolvePageRef(it.mentorPageId) - +it.name.substringAfterLast(" ") - } - } - } - } - } - } - div { - id = "wrapper" - section("wrapper") { - id = "main" - div("inner") { - h1("major") { +mentor.name } - val imageClass = mentor.meta["image.position"].string ?: "left" - span("image $imageClass") { - mentor.imagePath?.let { photoPath -> - img( - src = resolveRef(photoPath), - alt = mentor.name - ) - } - } - htmlData(mentor) - } - } - } - magProgFooter() - } - } - } } } diff --git a/src/main/kotlin/center/sciprog/spcMisc.kt b/src/main/kotlin/center/sciprog/spcMisc.kt index c700853..1dc3aea 100644 --- a/src/main/kotlin/center/sciprog/spcMisc.kt +++ b/src/main/kotlin/center/sciprog/spcMisc.kt @@ -3,16 +3,16 @@ package center.sciprog import kotlinx.html.* import space.kscience.dataforge.meta.get import space.kscience.dataforge.meta.string -import space.kscience.snark.html.WebPage +import space.kscience.snark.html.PageContext +import space.kscience.snark.html.getLanguageMap import space.kscience.snark.html.homeRef -import space.kscience.snark.html.languages import space.kscience.snark.html.resolvePageRef import java.time.LocalDate internal const val SPC_TITLE = "Scientific Programming Centre" -context(WebPage) +context(PageContext) internal fun HTML.spcHead(title: String = SPC_TITLE) { head { title { @@ -53,7 +53,7 @@ internal fun HTML.spcHead(title: String = SPC_TITLE) { } } -context(WebPage) +context(PageContext) internal fun FlowContent.spcHomeMenu() { nav { id = "menu" @@ -106,7 +106,7 @@ internal fun FlowContent.spcHomeMenu() { } } -context(WebPage) +context(PageContext) internal fun FlowContent.spcFooter() { footer { id = "footer" @@ -158,7 +158,7 @@ internal fun FlowContent.spcFooter() { } } -context(WebPage) +context(PageContext) internal fun FlowContent.wrapper(contentBody: FlowContent.() -> Unit) { div { id = "wrapper" @@ -172,9 +172,9 @@ internal fun FlowContent.wrapper(contentBody: FlowContent.() -> Unit) { } - if (languages.isNotEmpty()) { + getLanguageMap().takeIf { it.isNotEmpty() }?.let { languageMap-> div { - languages.forEach { (key, meta) -> + languageMap.forEach { (key, meta) -> a(classes = "button primary small") { href = resolvePageRef(meta["target"].string ?: "#") +key diff --git a/src/main/kotlin/center/sciprog/spcSite.kt b/src/main/kotlin/center/sciprog/spcSite.kt index a3ffba5..acce458 100644 --- a/src/main/kotlin/center/sciprog/spcSite.kt +++ b/src/main/kotlin/center/sciprog/spcSite.kt @@ -3,18 +3,19 @@ package center.sciprog import space.kscience.dataforge.data.* import space.kscience.dataforge.misc.DFExperimental import space.kscience.dataforge.names.Name -import space.kscience.snark.html.SiteBuilder +import space.kscience.snark.html.HtmlSite +import space.kscience.snark.html.site @OptIn(DFExperimental::class) -private fun DataSet.siteData(branchName: String): DataTree = DataTree(dataType) { +private fun DataSet.contentFor(branchName: String): DataTree = DataTree(dataType) { populateFrom(branch(Name.of(branchName, "content"))) node("common", branch("common")) node("assets", branch(Name.of(branchName, "assets"))) node("images", branch(Name.of(branchName, "images"))) } -fun SiteBuilder.spcSite() { - spcHome(data.siteData("home")) - spcMasters(data.siteData("magprog")) +val spcSite = HtmlSite { + route(Name.EMPTY, siteData.contentFor("home"), content = spcHome) + site("education.masters", siteData.contentFor("magprog"), content = spcMasters) // bmk(data.branch("bmk").withBranch("common", commonData)) -} \ No newline at end of file +} diff --git a/src/main/kotlin/center/sciprog/staticRender.kt b/src/main/kotlin/center/sciprog/staticRender.kt index 47dbbd4..88ad595 100644 --- a/src/main/kotlin/center/sciprog/staticRender.kt +++ b/src/main/kotlin/center/sciprog/staticRender.kt @@ -1,18 +1,21 @@ package center.sciprog -import space.kscience.dataforge.context.Global +import kotlinx.coroutines.coroutineScope +import space.kscience.dataforge.context.Context import space.kscience.dataforge.context.request -import space.kscience.snark.html.SiteBuilder -import space.kscience.snark.html.SnarkHtmlPlugin -import space.kscience.snark.html.readResources -import space.kscience.snark.html.static +import space.kscience.snark.html.* +import space.kscience.snark.html.static.staticSite import java.nio.file.Path -fun main(args: Array) { +suspend fun main(args: Array) = coroutineScope{ + val context = Context{ + plugin(SnarkHtml) + } + val destinationPath = args.firstOrNull() ?: "build/public" - val snark = Global.request(SnarkHtmlPlugin) + val snark = context.request(SnarkHtml) val siteData = snark.readResources("common", "home", "magprog") - snark.static(siteData, Path.of(destinationPath), block = SiteBuilder::spcSite) + snark.staticSite(siteData, Path.of(destinationPath), content = spcSite) } \ No newline at end of file diff --git a/src/main/kotlin/html5up/forty/common.kt b/src/main/kotlin/html5up/forty/common.kt index 93e8ad0..7258f42 100644 --- a/src/main/kotlin/html5up/forty/common.kt +++ b/src/main/kotlin/html5up/forty/common.kt @@ -1,7 +1,7 @@ package html5up.forty import kotlinx.html.* -import space.kscience.snark.html.WebPage +import space.kscience.snark.html.PageContext internal fun FlowContent.fortyMenu() { @@ -200,7 +200,8 @@ internal fun FlowContent.fortyFooter() { } } -context(WebPage) internal fun BODY.fortyScripts() { +context(PageContext) +internal fun BODY.fortyScripts() { script { src = resolveRef("assets/js/jquery.min.js") } diff --git a/src/main/kotlin/html5up/forty/landing.kt b/src/main/kotlin/html5up/forty/landing.kt index 530242b..7199556 100644 --- a/src/main/kotlin/html5up/forty/landing.kt +++ b/src/main/kotlin/html5up/forty/landing.kt @@ -1,9 +1,10 @@ package html5up.forty import kotlinx.html.* -import space.kscience.snark.html.WebPage +import space.kscience.snark.html.PageContext -context(WebPage) internal fun HTML.landing(){ +context(PageContext) +internal fun HTML.landing() { head { title { } @@ -30,7 +31,7 @@ context(WebPage) internal fun HTML.landing(){ div { id = "wrapper" // Header - // Note: The "styleN" class below should match that of the banner element. --> + // Note: The "styleN" class below should match that of the banner element. --> header("alt style2") { id = "header" @@ -48,7 +49,7 @@ context(WebPage) internal fun HTML.landing(){ } fortyMenu() // Banner - // Note: The "styleN" class below should match that of the header element. + // Note: The "styleN" class below should match that of the header element. section("style2") { id = "banner" div("inner") { diff --git a/src/main/kotlin/html5up/forty/page.kt b/src/main/kotlin/html5up/forty/page.kt index 81d7128..1c50460 100644 --- a/src/main/kotlin/html5up/forty/page.kt +++ b/src/main/kotlin/html5up/forty/page.kt @@ -1,9 +1,10 @@ package html5up.forty import kotlinx.html.* -import space.kscience.snark.html.WebPage +import space.kscience.snark.html.PageContext -context(WebPage) internal fun HTML.fortyPage(){ +context(PageContext) +internal fun HTML.fortyPage() { head { title { }