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
-

Research

+

Research

Analytics, prototyping and research for fundamental science and industry.

@@ -29,7 +30,7 @@ Our research, which primarily includes open source projects, has already been ap
-

Expert activity

+

Expert activity

Analysis and improvement of existing scientific software and projects.

@@ -38,7 +39,7 @@ Our research, which primarily includes open source projects, has already been ap
-

Architecture

+

Architecture

Design the architecture for scientific and technological applications.

@@ -47,7 +48,7 @@ Our research, which primarily includes open source projects, has already been ap
-

Development

+

Development

The development and support of open-source and in-house solutions( libraries, frameworks and end-user application).

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) + } + } + } +} + +