~jan0sch/smederee

Showing details for patch b8316626532ba5ffea2f1c914e1041910dc58d6c.
2022-11-06 (Sun), 12:46 PM - Jens Grassel - b8316626532ba5ffea2f1c914e1041910dc58d6c

VCS: Fix broken relative links in README on repository overview page.

- add `MarkdownRenderer` helper
- adjust code
Summary of changes
1 files added
  • modules/hub/src/main/scala/de/smederee/hub/MarkdownRenderer.scala
2 files modified with 2 lines added and 8 lines removed
  • CHANGELOG.md with 1 added and 0 removed lines
  • modules/hub/src/main/scala/de/smederee/hub/VcsRepositoryRoutes.scala with 1 added and 8 removed lines
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)
           }