Rewrite relative links to reflect the base url of the repo.

This commit is contained in:
Tobias Roeser
2014-03-04 11:38:56 +01:00
parent 0311359922
commit 3db3bf1b74
4 changed files with 50 additions and 25 deletions

View File

@@ -44,7 +44,8 @@ object MyBuild extends Build {
"org.eclipse.jetty" % "jetty-webapp" % "8.1.8.v20121106" % "container;provided", "org.eclipse.jetty" % "jetty-webapp" % "8.1.8.v20121106" % "container;provided",
"org.eclipse.jetty.orbit" % "javax.servlet" % "3.0.0.v201112011016" % "container;provided;test" artifacts Artifact("javax.servlet", "jar", "jar"), "org.eclipse.jetty.orbit" % "javax.servlet" % "3.0.0.v201112011016" % "container;provided;test" artifacts Artifact("javax.servlet", "jar", "jar"),
"junit" % "junit" % "4.11" % "test", "junit" % "junit" % "4.11" % "test",
"org.asciidoctor" % "asciidoctor-java-integration" % "0.1.4" "org.asciidoctor" % "asciidoctor-java-integration" % "0.1.4",
"net.sourceforge.htmlcleaner" % "htmlcleaner" % "2.7"
), ),
EclipseKeys.withSource := true, EclipseKeys.withSource := true,
javacOptions in compile ++= Seq("-target", "6", "-source", "6"), javacOptions in compile ++= Seq("-target", "6", "-source", "6"),

View File

@@ -1,17 +1,14 @@
package view package view
import util.StringUtil import org.asciidoctor.Asciidoctor
import util.ControlUtil._ import org.asciidoctor.AttributesBuilder
import util.Directory._ import org.asciidoctor.OptionsBuilder
import org.parboiled.common.StringUtils import org.asciidoctor.SafeMode
import org.pegdown._ import org.htmlcleaner.HtmlCleaner
import org.pegdown.ast._ import org.htmlcleaner.HtmlNode
import org.pegdown.LinkRenderer.Rendering import org.htmlcleaner.SimpleHtmlSerializer
import java.text.Normalizer import org.htmlcleaner.TagNode
import java.util.Locale import org.htmlcleaner.TagNodeVisitor
import scala.collection.JavaConverters._
import service.{ RequestCache, WikiService }
import org.asciidoctor.{ Asciidoctor, Attributes, AttributesBuilder, OptionsBuilder, SafeMode }
object Asciidoc { object Asciidoc {
@@ -20,14 +17,41 @@ object Asciidoc {
/** /**
* Converts Markdown of Wiki pages to HTML. * Converts Markdown of Wiki pages to HTML.
*/ */
def toHtml(asciidoc: String, repository: service.RepositoryService.RepositoryInfo, def toHtml(asciidoc: String, branch: String, repository: service.RepositoryService.RepositoryInfo,
enableWikiLink: Boolean, enableRefsLink: Boolean)(implicit context: app.Context): String = { enableWikiLink: Boolean, enableRefsLink: Boolean)(implicit context: app.Context): String = {
val options = OptionsBuilder.options() val options = OptionsBuilder.options()
options.safe(SafeMode.SECURE) options.safe(SafeMode.SECURE)
val attributes = AttributesBuilder.attributes() val attributes = AttributesBuilder.attributes()
attributes.showTitle(true) attributes.showTitle(true)
options.attributes(attributes.get()) options.attributes(attributes.get())
asciidoctor.render(asciidoc, options) val rendered = asciidoctor.render(asciidoc, options)
// this is always relative to the base dir of the repo, as we currently only render README files.
val relativeUrlPrefix = s"${helpers.url(repository)}/blob/${branch}/"
prefixRelativeUrls(rendered, relativeUrlPrefix)
} }
def prefixRelativeUrls(html: String, urlPrefix: String): String = {
val cleaner = new HtmlCleaner()
val node = cleaner.clean(html)
node.traverse(new TagNodeVisitor() {
override def visit(tagNode: TagNode, htmlNode: HtmlNode): Boolean = {
htmlNode match {
case tag: TagNode if tag.getName == "a" =>
Option(tag.getAttributeByName("href")) foreach { href =>
if (!href.startsWith("/") && !href.startsWith("http://") && !href.startsWith("https://")) {
tag.addAttribute("href", s"${urlPrefix}${href}")
}
}
case _ =>
}
// continue traversal
true
}
})
new SimpleHtmlSerializer(cleaner.getProperties()).getAsString(node)
}
} }

View File

@@ -27,12 +27,12 @@ object helpers extends AvatarImageProvider with LinkConverter with RequestCache
def plural(count: Int, singular: String, plural: String = ""): String = def plural(count: Int, singular: String, plural: String = ""): String =
if(count == 1) singular else if(plural.isEmpty) singular + "s" else plural if(count == 1) singular else if(plural.isEmpty) singular + "s" else plural
private[this] val renderersBySuffix: Seq[(String, (String, service.RepositoryService.RepositoryInfo, Boolean, Boolean, app.Context) => Html)] = private[this] val renderersBySuffix: Seq[(String, (String, String, service.RepositoryService.RepositoryInfo, Boolean, Boolean, app.Context) => Html)] =
Seq( Seq(
".md" -> ((fileContent, repository, enableWikiLink, enableRefsLink, context) => markdown(fileContent, repository, enableWikiLink, enableRefsLink)(context)), ".md" -> ((fileContent, branch, repository, enableWikiLink, enableRefsLink, context) => markdown(fileContent, repository, enableWikiLink, enableRefsLink)(context)),
".markdown" -> ((fileContent, repository, enableWikiLink, enableRefsLink, context) => markdown(fileContent, repository, enableWikiLink, enableRefsLink)(context)), ".markdown" -> ((fileContent, branch, repository, enableWikiLink, enableRefsLink, context) => markdown(fileContent, repository, enableWikiLink, enableRefsLink)(context)),
".adoc" -> ((fileContent, repository, enableWikiLink, enableRefsLink, context) => asciidoc(fileContent, repository, enableWikiLink, enableRefsLink)(context)), ".adoc" -> ((fileContent, branch, repository, enableWikiLink, enableRefsLink, context) => asciidoc(fileContent, branch, repository, enableWikiLink, enableRefsLink)(context)),
".asciidoc" -> ((fileContent, repository, enableWikiLink, enableRefsLink, context) => asciidoc(fileContent, repository, enableWikiLink, enableRefsLink)(context)) ".asciidoc" -> ((fileContent, branch, repository, enableWikiLink, enableRefsLink, context) => asciidoc(fileContent, branch, repository, enableWikiLink, enableRefsLink)(context))
) )
def renderableSuffixes: Seq[String] = renderersBySuffix.map(_._1) def renderableSuffixes: Seq[String] = renderersBySuffix.map(_._1)
@@ -44,13 +44,13 @@ object helpers extends AvatarImageProvider with LinkConverter with RequestCache
enableWikiLink: Boolean, enableRefsLink: Boolean)(implicit context: app.Context): Html = enableWikiLink: Boolean, enableRefsLink: Boolean)(implicit context: app.Context): Html =
Html(Markdown.toHtml(value, repository, enableWikiLink, enableRefsLink)) Html(Markdown.toHtml(value, repository, enableWikiLink, enableRefsLink))
def renderMarkup(fileName: String, fileContent: String, def renderMarkup(fileName: String, fileContent: String, branch: String,
repository: service.RepositoryService.RepositoryInfo, repository: service.RepositoryService.RepositoryInfo,
enableWikiLink: Boolean, enableRefsLink: Boolean)(implicit context: app.Context): Html = { enableWikiLink: Boolean, enableRefsLink: Boolean)(implicit context: app.Context): Html = {
val fileNameLower = fileName.toLowerCase val fileNameLower = fileName.toLowerCase
renderersBySuffix.find { case (suffix, _) => fileNameLower.endsWith(suffix) } match { renderersBySuffix.find { case (suffix, _) => fileNameLower.endsWith(suffix) } match {
case Some((_, handler)) => handler(fileContent, repository, enableWikiLink, enableRefsLink, context) case Some((_, handler)) => handler(fileContent, branch, repository, enableWikiLink, enableRefsLink, context)
case None => Html( case None => Html(
s"<tt>${ s"<tt>${
fileContent.split("(\\r\\n)|\\n").map(xml.Utility.escape(_)).mkString("<br/>") fileContent.split("(\\r\\n)|\\n").map(xml.Utility.escape(_)).mkString("<br/>")
@@ -59,9 +59,9 @@ object helpers extends AvatarImageProvider with LinkConverter with RequestCache
} }
} }
def asciidoc(value: String, repository: service.RepositoryService.RepositoryInfo, def asciidoc(value: String, branch: String, repository: service.RepositoryService.RepositoryInfo,
enableWikiLink: Boolean, enableRefsLink: Boolean)(implicit context: app.Context): Html = enableWikiLink: Boolean, enableRefsLink: Boolean)(implicit context: app.Context): Html =
Html(Asciidoc.toHtml(value, repository, enableWikiLink, enableRefsLink)) Html(Asciidoc.toHtml(value, branch, repository, enableWikiLink, enableRefsLink))
/** /**
* Returns &lt;img&gt; which displays the avatar icon for the given user name. * Returns &lt;img&gt; which displays the avatar icon for the given user name.

View File

@@ -80,7 +80,7 @@
@readme.map { case(file, content) => @readme.map { case(file, content) =>
<div id="readme" class="box"> <div id="readme" class="box">
<div class="box-header">@file.name</div> <div class="box-header">@file.name</div>
<div class="box-content markdown-body">@renderMarkup(file.name, content, repository, false, false)</div> <div class="box-content markdown-body">@renderMarkup(file.name, content, branch, repository, false, false)</div>
</div> </div>
} }
} }