diff --git a/data/home/content/consulting/index.md b/data/home/content/consulting/index.md
index 5baca2b..3ffef2c 100644
--- a/data/home/content/consulting/index.md
+++ b/data/home/content/consulting/index.md
@@ -1,6 +1,7 @@
---
type: page
title: Consulting
+transformation: snark.replaceLinks
language: en
---
@@ -20,7 +21,7 @@ Our research, which primarily includes open source projects, has already been ap
@@ -29,7 +30,7 @@ Our research, which primarily includes open source projects, has already been ap
@@ -38,7 +39,7 @@ Our research, which primarily includes open source projects, has already been ap
@@ -47,7 +48,7 @@ Our research, which primarily includes open source projects, has already been ap
diff --git a/src/main/kotlin/space/kscience/snark/HtmlData.kt b/src/main/kotlin/space/kscience/snark/HtmlData.kt
index ad44fea..b64c3a1 100644
--- a/src/main/kotlin/space/kscience/snark/HtmlData.kt
+++ b/src/main/kotlin/space/kscience/snark/HtmlData.kt
@@ -6,19 +6,23 @@ import kotlinx.html.FlowContent
import kotlinx.html.TagConsumer
import space.kscience.dataforge.data.Data
import space.kscience.dataforge.data.await
-import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.get
import space.kscience.dataforge.meta.int
import space.kscience.dataforge.meta.string
//TODO replace by VisionForge type
-typealias HtmlFragment = context(PageBuilder, TagConsumer<*>) () -> Unit
+//typealias HtmlFragment = context(PageBuilder, TagConsumer<*>) () -> Unit
+
+fun interface HtmlFragment {
+ fun TagConsumer<*>.renderFragment(page: PageBuilder)
+ //TODO move pageBuilder to a context receiver after KT-52967 is fixed
+}
typealias HtmlData = Data
-fun HtmlData(meta: Meta, content: context(PageBuilder, TagConsumer<*>) () -> Unit): HtmlData =
- Data(content, meta)
+//fun HtmlData(meta: Meta, content: context(PageBuilder) TagConsumer<*>.() -> Unit): HtmlData =
+// Data(HtmlFragment(content), meta)
internal val HtmlData.id: String get() = meta["id"]?.string ?: "block[${hashCode()}]"
internal val HtmlData.language: String? get() = meta["language"].string?.lowercase()
@@ -26,5 +30,5 @@ internal val HtmlData.language: String? get() = meta["language"].string?.lowerca
internal val HtmlData.order: Int? get() = meta["order"]?.int
context(PageBuilder) fun FlowContent.htmlData(data: HtmlData) = runBlocking(Dispatchers.IO) {
- data.await().invoke(this@PageBuilder, consumer)
+ with(data.await()) { consumer.renderFragment(this@PageBuilder) }
}
\ No newline at end of file
diff --git a/src/main/kotlin/space/kscience/snark/KtorSiteBuilder.kt b/src/main/kotlin/space/kscience/snark/KtorSiteBuilder.kt
index 7a6c766..ad575f1 100644
--- a/src/main/kotlin/space/kscience/snark/KtorSiteBuilder.kt
+++ b/src/main/kotlin/space/kscience/snark/KtorSiteBuilder.kt
@@ -14,7 +14,6 @@ import io.ktor.server.routing.createRouteFromPath
import io.ktor.server.routing.get
import io.ktor.server.routing.routing
import kotlinx.html.HTML
-import space.kscience.dataforge.context.Context
import space.kscience.dataforge.data.DataTree
import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.withDefault
@@ -57,7 +56,7 @@ internal class KtorSiteBuilder(
val pageBaseUrl: String,
override val meta: Meta = this@KtorSiteBuilder.meta,
) : PageBuilder {
- override val context: Context get() = this@KtorSiteBuilder.context
+ override val snark: SnarkPlugin get() = this@KtorSiteBuilder.snark
override val data: DataTree<*> get() = this@KtorSiteBuilder.data
override fun resolveRef(ref: String): String = resolveRef(pageBaseUrl, ref)
diff --git a/src/main/kotlin/space/kscience/snark/PageBuilder.kt b/src/main/kotlin/space/kscience/snark/PageBuilder.kt
index 7acada4..d675f9e 100644
--- a/src/main/kotlin/space/kscience/snark/PageBuilder.kt
+++ b/src/main/kotlin/space/kscience/snark/PageBuilder.kt
@@ -1,5 +1,6 @@
package space.kscience.snark
+import space.kscience.dataforge.context.Context
import space.kscience.dataforge.context.ContextAware
import space.kscience.dataforge.data.*
import space.kscience.dataforge.meta.Meta
@@ -16,6 +17,11 @@ internal fun Name.toWebPath() = tokens.joinToString(separator = "/"){
}
interface PageBuilder : ContextAware {
+
+ val snark: SnarkPlugin
+
+ override val context: Context get() = snark.context
+
val data: DataTree<*>
val meta: Meta
diff --git a/src/main/kotlin/space/kscience/snark/SiteLayout.kt b/src/main/kotlin/space/kscience/snark/SiteLayout.kt
index f86469d..a964c71 100644
--- a/src/main/kotlin/space/kscience/snark/SiteLayout.kt
+++ b/src/main/kotlin/space/kscience/snark/SiteLayout.kt
@@ -10,6 +10,7 @@ import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.get
import space.kscience.dataforge.meta.getIndexed
import space.kscience.dataforge.meta.string
+import space.kscience.dataforge.misc.Type
import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.NameToken
import space.kscience.dataforge.names.asName
@@ -109,11 +110,14 @@ fun SiteBuilder.pages(
}
+
+@Type(SiteLayout.TYPE)
fun interface SiteLayout {
context(SiteBuilder) fun render(item: DataTreeItem<*>)
companion object {
+ const val TYPE = "snark.layout"
const val LAYOUT_KEY = "layout"
const val ASSETS_KEY = "assets"
val INDEX_PAGE_TOKEN = NameToken("index")
@@ -134,6 +138,7 @@ fun interface SiteLayout {
}
}
+
object DefaultSiteLayout : SiteLayout {
context(SiteBuilder) override fun render(item: DataTreeItem<*>) {
pages(item)
diff --git a/src/main/kotlin/space/kscience/snark/SnarkPlugin.kt b/src/main/kotlin/space/kscience/snark/SnarkPlugin.kt
index 41df20d..42d8f57 100644
--- a/src/main/kotlin/space/kscience/snark/SnarkPlugin.kt
+++ b/src/main/kotlin/space/kscience/snark/SnarkPlugin.kt
@@ -18,6 +18,7 @@ import space.kscience.dataforge.misc.DFExperimental
import space.kscience.dataforge.misc.Type
import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.asName
+import space.kscience.dataforge.names.parseAsName
import space.kscience.dataforge.workspace.FileData
import space.kscience.dataforge.workspace.readDataDirectory
import java.nio.file.Path
@@ -36,12 +37,12 @@ interface SnarkParser {
val priority: Int get() = DEFAULT_PRIORITY
- fun parse(snark: SnarkPlugin, meta: Meta, bytes: ByteArray): R
+ fun parse(context: Context, meta: Meta, bytes: ByteArray): R
- fun reader(snark: SnarkPlugin, meta: Meta) = object : IOReader {
+ fun reader(context: Context, meta: Meta) = object : IOReader {
override val type: KType get() = this@SnarkParser.type
- override fun readObject(input: Input): R = parse(snark, meta, input.readBytes())
+ override fun readObject(input: Input): R = parse(context, meta, input.readBytes())
}
companion object {
@@ -56,7 +57,7 @@ internal class SnarkParserWrapper(
override val type: KType,
override val fileExtensions: Set,
) : SnarkParser {
- override fun parse(snark: SnarkPlugin, meta: Meta, bytes: ByteArray): R = bytes.asBinary().readWith(reader)
+ override fun parse(context: Context, meta: Meta, bytes: ByteArray): R = bytes.asBinary().readWith(reader)
}
/**
@@ -79,6 +80,14 @@ class SnarkPlugin : AbstractPlugin() {
context.gather(SnarkParser.TYPE, true)
}
+ private val layouts: Map by lazy {
+ context.gather(SiteLayout.TYPE, true)
+ }
+
+ private val textTransformations: Map by lazy {
+ context.gather(TextTransformation.TYPE, true)
+ }
+
fun readDirectory(path: Path): DataTree = io.readDataDirectory(path) { dataPath, meta ->
val fileExtension = meta[FileData.META_FILE_EXTENSION_KEY].string ?: dataPath.extension
val parser: SnarkParser = parsers.values.filter { parser ->
@@ -90,11 +99,20 @@ class SnarkPlugin : AbstractPlugin() {
byteArraySnarkParser
}
- parser.reader(this, meta)
+ parser.reader(context, meta)
}
- fun layout(meta: Meta): SiteLayout = when (meta[SiteLayout.LAYOUT_KEY]) {
- else -> DefaultSiteLayout
+ internal fun layout(layoutMeta: Meta): SiteLayout {
+ val layoutName = layoutMeta.string
+ ?: layoutMeta["name"].string ?: error("Layout name not defined in $layoutMeta")
+ return layouts[layoutName.parseAsName()] ?: error("Layout with name $layoutName not found in $this")
+ }
+
+ internal fun textTransformation(transformationMeta: Meta): TextTransformation {
+ val transformationName = transformationMeta.string
+ ?: transformationMeta["name"].string ?: error("Transformation name not defined in $transformationMeta")
+ return textTransformations[transformationName.parseAsName()]
+ ?: error("Text transformation with name $transformationName not found in $this")
}
override fun content(target: String): Map = when (target) {
@@ -107,6 +125,9 @@ class SnarkPlugin : AbstractPlugin() {
"jpg".asName() to SnarkParser(ImageIOReader, "jpg", "jpeg"),
"gif".asName() to SnarkParser(ImageIOReader, "gif"),
)
+ TextTransformation.TYPE -> mapOf(
+ "replaceLinks".asName() to TextTransformation.replaceLinks
+ )
else -> super.content(target)
}
diff --git a/src/main/kotlin/space/kscience/snark/parsers.kt b/src/main/kotlin/space/kscience/snark/SnarkTextParser.kt
similarity index 75%
rename from src/main/kotlin/space/kscience/snark/parsers.kt
rename to src/main/kotlin/space/kscience/snark/SnarkTextParser.kt
index eb0658d..ee8a0f7 100644
--- a/src/main/kotlin/space/kscience/snark/parsers.kt
+++ b/src/main/kotlin/space/kscience/snark/SnarkTextParser.kt
@@ -7,8 +7,10 @@ import kotlinx.html.unsafe
import org.intellij.markdown.flavours.commonmark.CommonMarkFlavourDescriptor
import org.intellij.markdown.html.HtmlGenerator
import org.intellij.markdown.parser.MarkdownParser
+import space.kscience.dataforge.context.Context
import space.kscience.dataforge.io.IOReader
import space.kscience.dataforge.meta.Meta
+import space.kscience.dataforge.meta.get
import java.awt.image.BufferedImage
import javax.imageio.ImageIO
import kotlin.reflect.KType
@@ -17,8 +19,13 @@ import kotlin.reflect.typeOf
abstract class SnarkTextParser : SnarkParser {
abstract fun parseText(text: String, meta: Meta): R
- override fun parse(snark: SnarkPlugin, meta: Meta, bytes: ByteArray): R =
+ override fun parse(context: Context, meta: Meta, bytes: ByteArray): R =
parseText(bytes.decodeToString(), meta)
+
+ fun transformText(text: String, meta: Meta, page: PageBuilder): String =
+ meta[TextTransformation.TEXT_TRANSFORMATION_KEY]?.let {
+ with(page){ page.snark.textTransformation(it).transform(text)}
+ } ?: text
}
@@ -26,9 +33,9 @@ internal object SnarkHtmlParser : SnarkTextParser() {
override val fileExtensions: Set = setOf("html")
override val type: KType = typeOf()
- override fun parseText(text: String, meta: Meta): HtmlFragment = {
+ override fun parseText(text: String, meta: Meta): HtmlFragment = HtmlFragment { page ->
div {
- unsafe { +text }
+ unsafe { +transformText(text, meta, page) }
}
}
}
@@ -44,10 +51,10 @@ internal object SnarkMarkdownParser : SnarkTextParser() {
val parsedTree = markdownParser.buildMarkdownTreeFromString(text)
val htmlString = HtmlGenerator(text, parsedTree, markdownFlavor).generateHtml()
- return {
+ return HtmlFragment { page ->
div {
unsafe {
- +htmlString
+ +SnarkHtmlParser.transformText(htmlString, meta, page)
}
}
}
diff --git a/src/main/kotlin/space/kscience/snark/StaticSiteBuilder.kt b/src/main/kotlin/space/kscience/snark/StaticSiteBuilder.kt
index e4671e6..dbc796b 100644
--- a/src/main/kotlin/space/kscience/snark/StaticSiteBuilder.kt
+++ b/src/main/kotlin/space/kscience/snark/StaticSiteBuilder.kt
@@ -3,7 +3,6 @@ package space.kscience.snark
import kotlinx.html.HTML
import kotlinx.html.html
import kotlinx.html.stream.createHTML
-import space.kscience.dataforge.context.Context
import space.kscience.dataforge.data.DataTree
import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.withDefault
@@ -67,7 +66,8 @@ internal class StaticSiteBuilder(
inner class StaticPageBuilder : PageBuilder {
override val data: DataTree<*> get() = this@StaticSiteBuilder.data
override val meta: Meta get() = this@StaticSiteBuilder.meta
- override val context: Context get() = this@StaticSiteBuilder.context
+ override val snark: SnarkPlugin get() = this@StaticSiteBuilder.snark
+
override fun resolveRef(ref: String): String = resolveRef(baseUrl, ref)
diff --git a/src/main/kotlin/space/kscience/snark/TextTransformation.kt b/src/main/kotlin/space/kscience/snark/TextTransformation.kt
new file mode 100644
index 0000000..2438d18
--- /dev/null
+++ b/src/main/kotlin/space/kscience/snark/TextTransformation.kt
@@ -0,0 +1,22 @@
+package space.kscience.snark
+
+import space.kscience.dataforge.misc.Type
+import space.kscience.dataforge.names.NameToken
+
+@Type(TextTransformation.TYPE)
+fun interface TextTransformation {
+ context(PageBuilder) fun transform(text: String): String
+
+ companion object {
+ const val TYPE = "snark.textTransformation"
+ val TEXT_TRANSFORMATION_KEY = NameToken("transformation")
+
+ val replaceLinks: TextTransformation = object : TextTransformation {
+ context(PageBuilder) override fun transform(text: String): String {
+ return text.replace("\${homeRef}", homeRef)
+ }
+ }
+ }
+}
+
+