~jan0sch/smederee
Showing details for patch b8316626532ba5ffea2f1c914e1041910dc58d6c.
diff -rN -u old-smederee/CHANGELOG.md new-smederee/CHANGELOG.md --- old-smederee/CHANGELOG.md 2025-02-01 15:50:48.566338787 +0000 +++ new-smederee/CHANGELOG.md 2025-02-01 15:50:48.566338787 +0000 @@ -27,6 +27,7 @@ ### Fixed - history summary shows wrong number of lines that were added/removed +- relative links in rendered markdown README are broken on overview page ## 0.2.0 (2022-11-03) diff -rN -u old-smederee/modules/hub/src/main/scala/de/smederee/hub/MarkdownRenderer.scala new-smederee/modules/hub/src/main/scala/de/smederee/hub/MarkdownRenderer.scala --- old-smederee/modules/hub/src/main/scala/de/smederee/hub/MarkdownRenderer.scala 1970-01-01 00:00:00.000000000 +0000 +++ new-smederee/modules/hub/src/main/scala/de/smederee/hub/MarkdownRenderer.scala 2025-02-01 15:50:48.566338787 +0000 @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2022 Contributors as noted in the AUTHORS.md file + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package de.smederee.hub + +import cats.syntax.all._ +import org.commonmark.node._ +import org.commonmark.parser.Parser +import org.commonmark.renderer.html._ +import org.http4s.Uri +import org.slf4j.LoggerFactory + +import scala.jdk.CollectionConverters._ + +object MarkdownRenderer { + private val log = LoggerFactory.getLogger(getClass()) + + /** Render the given markdown sources and adjust all relative links by prefixing them with the path for the repostiory + * file browsing (`repo-name/files`). This function is implemented to work with the README file shown on the + * repository overview page. + * + * @param repo + * The repository which contains the markdown sources. + * @param markdownSource + * A string containing the markdown sources to be rendered (usually the content of the README.md file in the + * repository root). + * @return + * A string containing the rendered markdown (HTML). + */ + def renderRepositoryOverviewReadme(repo: Option[VcsRepository])(markdownSource: String): String = { + val parser = Parser.builder().build() + val markdown = parser.parse(markdownSource) + val renderer = HtmlRenderer + .builder() + .attributeProviderFactory(new AttributeProviderFactory { + override def create(context: AttributeProviderContext): AttributeProvider = new LinkHrefCorrector(repo) + }) + .escapeHtml(true) + .sanitizeUrls(true) + .build() + renderer.render(markdown) + } + + /** A helper class used by the `renderRepositoryOverviewReadme` function to adjust the `href` attribute of links that + * are not absolute. + * + * @param repo + * The repository which contains the markdown sources. + */ + class LinkHrefCorrector(private val repo: Option[VcsRepository]) extends AttributeProvider { + override def setAttributes(node: Node, tagName: String, attributes: java.util.Map[String, String]): Unit = + if (node.isInstanceOf[Link]) { + (repo, attributes.asScala.get("href").flatMap(href => Uri.fromString(href).toOption)).mapN { + case (repository, uri) => + if (uri.scheme.isEmpty) { + val pathPrefix = Uri.Path(Vector(Uri.Path.Segment(repository.name.toString), Uri.Path.Segment("files"))) + val correctedUri = uri.copy(path = pathPrefix |+| uri.path) + log.debug(s"Corrected URI for repository overview README rendering: $uri -> $correctedUri") + attributes.put("href", correctedUri.toString) + } + } + } + } +} diff -rN -u old-smederee/modules/hub/src/main/scala/de/smederee/hub/VcsRepositoryRoutes.scala new-smederee/modules/hub/src/main/scala/de/smederee/hub/VcsRepositoryRoutes.scala --- old-smederee/modules/hub/src/main/scala/de/smederee/hub/VcsRepositoryRoutes.scala 2025-02-01 15:50:48.566338787 +0000 +++ new-smederee/modules/hub/src/main/scala/de/smederee/hub/VcsRepositoryRoutes.scala 2025-02-01 15:50:48.566338787 +0000 @@ -319,14 +319,7 @@ readme <- readmeData match { case Some((lines, Some(filename))) => if (filename.matches("(?iu).*\\.(md|markdown)$")) { - for { - parser <- Sync[F].delay(Parser.builder().build()) - markdown <- Sync[F].delay( - parser.parse(lines.mkString("\n")) - ) // Line breaks are important for markdown - renderer <- Sync[F].delay(HtmlRenderer.builder().escapeHtml(true).sanitizeUrls(true).build()) - html <- Sync[F].delay(renderer.render(markdown)) // TODO Correct links to repo files. - } yield html.some + Sync[F].delay(MarkdownRenderer.renderRepositoryOverviewReadme(repo)(lines.mkString("\n"))).map(_.some) } else { Sync[F].delay(lines.mkString("\n").some) }