Refactor SPC home page to use decoupled representation from KTor
This commit is contained in:
parent
a907c57134
commit
23a736a012
@ -1,10 +1,6 @@
|
|||||||
package ru.mipt.spc
|
package ru.mipt.spc
|
||||||
|
|
||||||
import html5up.forty.fortyScripts
|
import html5up.forty.fortyScripts
|
||||||
import io.ktor.server.application.call
|
|
||||||
import io.ktor.server.html.respondHtml
|
|
||||||
import io.ktor.server.routing.Route
|
|
||||||
import io.ktor.server.routing.get
|
|
||||||
import kotlinx.html.*
|
import kotlinx.html.*
|
||||||
import space.kscience.dataforge.meta.Meta
|
import space.kscience.dataforge.meta.Meta
|
||||||
import space.kscience.dataforge.meta.get
|
import space.kscience.dataforge.meta.get
|
||||||
@ -15,6 +11,13 @@ import space.kscience.dataforge.names.parseAsName
|
|||||||
import space.kscience.dataforge.names.withIndex
|
import space.kscience.dataforge.names.withIndex
|
||||||
import space.kscience.dataforge.values.string
|
import space.kscience.dataforge.values.string
|
||||||
import space.kscience.snark.*
|
import space.kscience.snark.*
|
||||||
|
import kotlin.collections.Map
|
||||||
|
import kotlin.collections.component1
|
||||||
|
import kotlin.collections.component2
|
||||||
|
import kotlin.collections.forEach
|
||||||
|
import kotlin.collections.joinToString
|
||||||
|
import kotlin.collections.set
|
||||||
|
import kotlin.collections.sortedBy
|
||||||
|
|
||||||
context(PageContext) private fun FlowContent.spcSpotlightContent(
|
context(PageContext) private fun FlowContent.spcSpotlightContent(
|
||||||
landing: HtmlData,
|
landing: HtmlData,
|
||||||
@ -89,7 +92,7 @@ context(PageContext) private fun FlowContent.spcSpotlightContent(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
context(PageContext) internal fun Route.spcSpotlight(
|
context(PageContext) internal fun SnarkRoute.spcSpotlight(
|
||||||
name: String,
|
name: String,
|
||||||
contentFilter: (Name, Meta) -> Boolean,
|
contentFilter: (Name, Meta) -> Boolean,
|
||||||
) {
|
) {
|
||||||
@ -97,29 +100,22 @@ context(PageContext) internal fun Route.spcSpotlight(
|
|||||||
val content = resolveAllHtml(contentFilter)
|
val content = resolveAllHtml(contentFilter)
|
||||||
|
|
||||||
val meta = body.meta
|
val meta = body.meta
|
||||||
get(name) {
|
page(name) {
|
||||||
withRequest(call.request) {
|
val title = meta["title"].string ?: SPC_TITLE
|
||||||
call.respondHtml {
|
spcHead(title)
|
||||||
val title = meta["title"].string ?: SPC_TITLE
|
body("is-preload") {
|
||||||
spcHead(title)
|
wrapper {
|
||||||
body("is-preload") {
|
spcSpotlightContent(body, content)
|
||||||
wrapper {
|
|
||||||
spcSpotlightContent(body, content)
|
|
||||||
}
|
|
||||||
|
|
||||||
fortyScripts()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fortyScripts()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
content.forEach { (name, contentBody) ->
|
content.forEach { (name, contentBody) ->
|
||||||
get(name.tokens.joinToString("/")) {
|
page(name.tokens.joinToString("/")){
|
||||||
withRequest(call.request) {
|
spcPageContent(contentBody.meta) {
|
||||||
call.respondHtml {
|
htmlData(contentBody)
|
||||||
spcPageContent(contentBody.meta) {
|
|
||||||
htmlData(contentBody)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,13 +2,13 @@ package ru.mipt.spc
|
|||||||
|
|
||||||
import html5up.forty.fortyScripts
|
import html5up.forty.fortyScripts
|
||||||
import io.ktor.server.application.Application
|
import io.ktor.server.application.Application
|
||||||
import io.ktor.server.application.call
|
import io.ktor.server.routing.route
|
||||||
import io.ktor.server.application.log
|
import io.ktor.server.routing.routing
|
||||||
import io.ktor.server.html.respondHtml
|
|
||||||
import io.ktor.server.routing.*
|
|
||||||
import kotlinx.html.*
|
import kotlinx.html.*
|
||||||
import space.kscience.dataforge.context.Context
|
import space.kscience.dataforge.context.Context
|
||||||
|
import space.kscience.dataforge.context.error
|
||||||
import space.kscience.dataforge.context.fetch
|
import space.kscience.dataforge.context.fetch
|
||||||
|
import space.kscience.dataforge.context.logger
|
||||||
import space.kscience.dataforge.data.filterByType
|
import space.kscience.dataforge.data.filterByType
|
||||||
import space.kscience.dataforge.data.forEach
|
import space.kscience.dataforge.data.forEach
|
||||||
import space.kscience.dataforge.meta.Meta
|
import space.kscience.dataforge.meta.Meta
|
||||||
@ -58,17 +58,13 @@ context(PageContext) internal fun HTML.spcPageContent(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
context(PageContext) internal fun Route.spcPage(subRoute: String, meta: Meta, fragment: FlowContent.() -> Unit) {
|
context(PageContext) internal fun SnarkRoute.spcPage(subRoute: String, meta: Meta, fragment: FlowContent.() -> Unit) {
|
||||||
get(subRoute) {
|
page(subRoute) {
|
||||||
withRequest(call.request) {
|
spcPageContent(meta, fragment = fragment)
|
||||||
call.respondHtml {
|
|
||||||
spcPageContent(meta, fragment = fragment)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
context(PageContext) internal fun Route.spcPage(
|
context(PageContext) internal fun SnarkRoute.spcPage(
|
||||||
subRoute: String,
|
subRoute: String,
|
||||||
dataPath: Name = subRoute.replace("/", ".").parseAsName(),
|
dataPath: Name = subRoute.replace("/", ".").parseAsName(),
|
||||||
more: FlowContent.() -> Unit = {},
|
more: FlowContent.() -> Unit = {},
|
||||||
@ -80,14 +76,14 @@ context(PageContext) internal fun Route.spcPage(
|
|||||||
more()
|
more()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
application.log.error("Content for page with path $dataPath not found")
|
logger.error { "Content for page with path $dataPath not found" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Route a directory
|
* Route a directory
|
||||||
*/
|
*/
|
||||||
context(PageContext) internal fun Route.spcDirectory(
|
context(PageContext) internal fun SnarkRoute.spcDirectory(
|
||||||
subRoute: String,
|
subRoute: String,
|
||||||
dataPath: Name = subRoute.replace("/", ".").parseAsName(),
|
dataPath: Name = subRoute.replace("/", ".").parseAsName(),
|
||||||
) {
|
) {
|
||||||
@ -104,14 +100,14 @@ context(PageContext) internal fun Route.spcDirectory(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
context(PageContext) internal fun Route.spcPage(
|
context(PageContext) internal fun SnarkRoute.spcPage(
|
||||||
name: Name,
|
name: Name,
|
||||||
more: FlowContent.() -> Unit = {},
|
more: FlowContent.() -> Unit = {},
|
||||||
) {
|
) {
|
||||||
spcPage(name.tokens.joinToString("/"), name, more)
|
spcPage(name.tokens.joinToString("/"), name, more)
|
||||||
}
|
}
|
||||||
|
|
||||||
context(PageContext) private fun HTML.spcHome() {
|
context(PageContext, HTML) private fun HTML.spcHome() {
|
||||||
spcHead()
|
spcHead()
|
||||||
body("is-preload") {
|
body("is-preload") {
|
||||||
wrapper {
|
wrapper {
|
||||||
@ -309,13 +305,11 @@ internal fun Application.spcHome(context: Context, rootPath: Path, prefix: Strin
|
|||||||
snark(homePageContext) {
|
snark(homePageContext) {
|
||||||
staticDirectory("assets", rootPath.resolve("assets"))
|
staticDirectory("assets", rootPath.resolve("assets"))
|
||||||
staticDirectory("images", rootPath.resolve("images"))
|
staticDirectory("images", rootPath.resolve("images"))
|
||||||
page { spcHome() }
|
|
||||||
}
|
|
||||||
|
|
||||||
with(homePageContext) {
|
page { spcHome() }
|
||||||
|
|
||||||
spcDirectory("consulting")
|
spcDirectory("consulting")
|
||||||
spcPage("ru/consulting")
|
spcDirectory("ru/consulting")
|
||||||
|
|
||||||
spcSpotlight("team") { _, m -> m["type"].string == "team" }
|
spcSpotlight("team") { _, m -> m["type"].string == "team" }
|
||||||
spcSpotlight("research") { name, m -> name.startsWith("projects".asName()) && m["type"].string == "project" }
|
spcSpotlight("research") { name, m -> name.startsWith("projects".asName()) && m["type"].string == "project" }
|
||||||
|
@ -6,6 +6,8 @@ import io.ktor.server.plugins.origin
|
|||||||
import io.ktor.server.request.ApplicationRequest
|
import io.ktor.server.request.ApplicationRequest
|
||||||
import io.ktor.server.request.host
|
import io.ktor.server.request.host
|
||||||
import io.ktor.server.request.port
|
import io.ktor.server.request.port
|
||||||
|
import space.kscience.dataforge.context.Context
|
||||||
|
import space.kscience.dataforge.context.ContextAware
|
||||||
import space.kscience.dataforge.data.*
|
import space.kscience.dataforge.data.*
|
||||||
import space.kscience.dataforge.meta.Meta
|
import space.kscience.dataforge.meta.Meta
|
||||||
import space.kscience.dataforge.meta.get
|
import space.kscience.dataforge.meta.get
|
||||||
@ -16,7 +18,13 @@ import space.kscience.dataforge.names.startsWith
|
|||||||
import space.kscience.snark.PageContext.Companion.INDEX_PAGE_NAME
|
import space.kscience.snark.PageContext.Companion.INDEX_PAGE_NAME
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
|
|
||||||
data class PageContext(val path: String, val pageMeta: Meta, val data: DataSet<*>) {
|
data class PageContext(
|
||||||
|
override val context: Context,
|
||||||
|
val path: String,
|
||||||
|
val pageMeta: Meta,
|
||||||
|
val data: DataSet<*>,
|
||||||
|
) : ContextAware {
|
||||||
|
|
||||||
val language: String? by pageMeta.string()
|
val language: String? by pageMeta.string()
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@ -61,12 +69,13 @@ fun PageContext.findByType(contentType: String, baseName: Name = Name.EMPTY) = r
|
|||||||
|
|
||||||
internal val Data<*>.published: Boolean get() = meta["published"].string != "false"
|
internal val Data<*>.published: Boolean get() = meta["published"].string != "false"
|
||||||
|
|
||||||
fun PageContext(rootUrl: String, data: DataSet<*>): PageContext = PageContext(rootUrl, data.meta, data)
|
fun PageContext(dfContext: Context, rootUrl: String, data: DataSet<*>): PageContext =
|
||||||
|
PageContext(dfContext, rootUrl, data.meta, data)
|
||||||
|
|
||||||
fun SnarkPlugin.read(path: Path, rootUrl: String = "/"): PageContext {
|
fun SnarkPlugin.read(path: Path, rootUrl: String = "/"): PageContext {
|
||||||
val parsedData: DataSet<Any> = readDirectory(path)
|
val parsedData: DataSet<Any> = readDirectory(path)
|
||||||
|
|
||||||
return PageContext(rootUrl, parsedData)
|
return PageContext(context, rootUrl, parsedData)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,61 +0,0 @@
|
|||||||
package space.kscience.snark
|
|
||||||
|
|
||||||
import io.ktor.server.application.call
|
|
||||||
import io.ktor.server.html.respondHtml
|
|
||||||
import io.ktor.server.http.content.file
|
|
||||||
import io.ktor.server.http.content.files
|
|
||||||
import io.ktor.server.http.content.static
|
|
||||||
import io.ktor.server.routing.Route
|
|
||||||
import io.ktor.server.routing.get
|
|
||||||
import io.ktor.server.routing.route
|
|
||||||
import kotlinx.html.HTML
|
|
||||||
import java.nio.file.Path
|
|
||||||
|
|
||||||
interface RouteBuilder {
|
|
||||||
val pageContext: PageContext
|
|
||||||
|
|
||||||
fun staticFile(remotePath: String, file: Path)
|
|
||||||
|
|
||||||
fun staticDirectory(remotePath: String, directory: Path)
|
|
||||||
|
|
||||||
fun page(route: String = "", content: context(PageContext) HTML.() -> Unit)
|
|
||||||
|
|
||||||
fun route(route: String, block: RouteBuilder.() -> Unit)
|
|
||||||
}
|
|
||||||
|
|
||||||
class KtorRouteBuilder(override val pageContext: PageContext, private val ktorRoute: Route) : RouteBuilder {
|
|
||||||
override fun staticFile(remotePath: String, file: Path) {
|
|
||||||
ktorRoute.file(remotePath, file.toFile())
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun staticDirectory(remotePath: String, directory: Path) {
|
|
||||||
ktorRoute.static(remotePath) {
|
|
||||||
files(directory.toFile())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun page(route: String, content: context(PageContext) HTML.() -> Unit) {
|
|
||||||
ktorRoute.get(route) {
|
|
||||||
with(pageContext) {
|
|
||||||
withRequest(call.request) {
|
|
||||||
val innerContext = this
|
|
||||||
call.respondHtml {
|
|
||||||
content(innerContext, this)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun route(route: String, block: RouteBuilder.() -> Unit) {
|
|
||||||
ktorRoute.route(route) {
|
|
||||||
block(KtorRouteBuilder(pageContext, this))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun Route.snark(pageContext: PageContext, block: context(PageContext) RouteBuilder.() -> Unit) {
|
|
||||||
with(pageContext){
|
|
||||||
block(KtorRouteBuilder(pageContext, this@snark))
|
|
||||||
}
|
|
||||||
}
|
|
64
src/main/kotlin/space/kscience/snark/SnarkRoute.kt
Normal file
64
src/main/kotlin/space/kscience/snark/SnarkRoute.kt
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
package space.kscience.snark
|
||||||
|
|
||||||
|
import io.ktor.server.application.call
|
||||||
|
import io.ktor.server.html.respondHtml
|
||||||
|
import io.ktor.server.http.content.file
|
||||||
|
import io.ktor.server.http.content.files
|
||||||
|
import io.ktor.server.http.content.static
|
||||||
|
import io.ktor.server.routing.Route
|
||||||
|
import io.ktor.server.routing.get
|
||||||
|
import io.ktor.server.routing.route
|
||||||
|
import kotlinx.html.HTML
|
||||||
|
import java.nio.file.Path
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An abstraction, which is used to render sites to the different rendering engines
|
||||||
|
*/
|
||||||
|
interface SnarkRoute {
|
||||||
|
|
||||||
|
fun staticFile(remotePath: String, file: Path)
|
||||||
|
|
||||||
|
fun staticDirectory(remotePath: String, directory: Path)
|
||||||
|
|
||||||
|
context(PageContext) fun page(route: String = "", content: context(PageContext, HTML) () -> Unit)
|
||||||
|
|
||||||
|
context(PageContext) fun route(route: String, block: context(PageContext, SnarkRoute) () -> Unit)
|
||||||
|
}
|
||||||
|
|
||||||
|
class KtorRouteBuilder(private val ktorRoute: Route) : SnarkRoute {
|
||||||
|
override fun staticFile(remotePath: String, file: Path) {
|
||||||
|
ktorRoute.file(remotePath, file.toFile())
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun staticDirectory(remotePath: String, directory: Path) {
|
||||||
|
ktorRoute.static(remotePath) {
|
||||||
|
files(directory.toFile())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
context(PageContext) override fun page(route: String, content: context(PageContext, HTML)() -> Unit) {
|
||||||
|
ktorRoute.get(route) {
|
||||||
|
withRequest(call.request) {
|
||||||
|
call.respondHtml {
|
||||||
|
content(this@PageContext, this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
context(PageContext) override fun route(
|
||||||
|
route: String,
|
||||||
|
block: context(PageContext, SnarkRoute)() -> Unit,
|
||||||
|
) {
|
||||||
|
ktorRoute.route(route) {
|
||||||
|
block(this@PageContext, KtorRouteBuilder(this))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun Route.snark(
|
||||||
|
pageContext: PageContext,
|
||||||
|
block: context(PageContext, SnarkRoute)() -> Unit,
|
||||||
|
) {
|
||||||
|
block(pageContext, KtorRouteBuilder(this@snark))
|
||||||
|
}
|
@ -1,39 +1,26 @@
|
|||||||
package ru.mipt.spc
|
package ru.mipt.spc
|
||||||
|
|
||||||
import kotlinx.html.TagConsumer
|
//class SiteBuilderAction : AbstractAction<Any, HtmlFragment>(typeOf<HtmlFragment>()) {
|
||||||
import space.kscience.dataforge.actions.AbstractAction
|
//
|
||||||
import space.kscience.dataforge.data.Data
|
// private val pageBuilders = HashMap<Name, (DataSet<*>) -> HtmlData>()
|
||||||
import space.kscience.dataforge.data.DataSet
|
//
|
||||||
import space.kscience.dataforge.data.DataSetBuilder
|
// fun page(name: Name, meta: Meta = Meta.EMPTY, builder: context(PageContext) TagConsumer<*>.() -> Unit) {
|
||||||
import space.kscience.dataforge.meta.Meta
|
// val prefix = name.tokens.joinToString(separator = "/", prefix = "/")
|
||||||
import space.kscience.dataforge.meta.copy
|
// pageBuilders[name] = { dataset ->
|
||||||
import space.kscience.dataforge.names.Name
|
// val fragment: HtmlFragment = {
|
||||||
import space.kscience.snark.HtmlData
|
// builder.invoke(PageContext(prefix, dataset), this)
|
||||||
import space.kscience.snark.HtmlFragment
|
// }
|
||||||
import space.kscience.snark.PageContext
|
// Data(fragment, meta.copy {
|
||||||
import kotlin.reflect.typeOf
|
// "name" put name.toString()
|
||||||
|
// })
|
||||||
class SiteBuilderAction : AbstractAction<Any, HtmlFragment>(typeOf<HtmlFragment>()) {
|
// }
|
||||||
|
// }
|
||||||
private val pageBuilders = HashMap<Name, (DataSet<*>) -> HtmlData>()
|
//
|
||||||
|
//
|
||||||
fun page(name: Name, meta: Meta = Meta.EMPTY, builder: context(PageContext) TagConsumer<*>.() -> Unit) {
|
// override fun DataSetBuilder<HtmlFragment>.generate(data: DataSet<Any>, meta: Meta) {
|
||||||
val prefix = name.tokens.joinToString(separator = "/", prefix = "/")
|
// pageBuilders.forEach { (name, builder) ->
|
||||||
pageBuilders[name] = { dataset ->
|
// data(name, builder(data))
|
||||||
val fragment: HtmlFragment = {
|
// }
|
||||||
builder.invoke(PageContext(prefix, dataset), this)
|
// }
|
||||||
}
|
//
|
||||||
Data(fragment, meta.copy {
|
//}
|
||||||
"name" put name.toString()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
override fun DataSetBuilder<HtmlFragment>.generate(data: DataSet<Any>, meta: Meta) {
|
|
||||||
pageBuilders.forEach { (name, builder) ->
|
|
||||||
data(name, builder(data))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user