diff --git a/project/build.scala b/project/build.scala
index d2f2d1a..9d2e16d 100644
--- a/project/build.scala
+++ b/project/build.scala
@@ -15,6 +15,7 @@
"gitbucket",
file("."),
settings = Defaults.defaultSettings ++ ScalatraPlugin.scalatraWithJRebel ++ Seq(
+ sourcesInBase := false,
organization := Organization,
name := Name,
version := Version,
@@ -43,7 +44,9 @@
"ch.qos.logback" % "logback-classic" % "1.0.13" % "runtime",
"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"),
- "junit" % "junit" % "4.11" % "test"
+ "junit" % "junit" % "4.11" % "test",
+ "org.asciidoctor" % "asciidoctor-java-integration" % "0.1.4",
+ "net.sourceforge.htmlcleaner" % "htmlcleaner" % "2.7"
),
EclipseKeys.withSource := true,
javacOptions in compile ++= Seq("-target", "6", "-source", "6"),
diff --git a/src/main/scala/app/RepositoryViewerController.scala b/src/main/scala/app/RepositoryViewerController.scala
index 1f5d831..dda0e61 100644
--- a/src/main/scala/app/RepositoryViewerController.scala
+++ b/src/main/scala/app/RepositoryViewerController.scala
@@ -252,7 +252,7 @@
}
- private val readmeFiles = Seq("readme.md", "readme.markdown")
+ private val readmeFiles = view.helpers.renderableSuffixes.map(suffix => s"readme${suffix}") ++ Seq("readme.txt", "readme")
/**
* Provides HTML of the file list.
@@ -270,14 +270,16 @@
//val revisions = Seq(if(revstr.isEmpty) repository.repository.defaultBranch else revstr, repository.branchList.head)
// get specified commit
JGitUtil.getDefaultBranch(git, repository, revstr).map { case (objectId, revision) =>
- defining(JGitUtil.getRevCommitFromId(git, objectId)){ revCommit =>
- // get files
+ defining(JGitUtil.getRevCommitFromId(git, objectId)) { revCommit =>
+ // get files
val files = JGitUtil.getFileList(git, revision, path)
+ val parentPath = if (path == ".") Nil else path.split("/").toList
// process README.md or README.markdown
val readme = files.find { file =>
readmeFiles.contains(file.name.toLowerCase)
}.map { file =>
- file -> StringUtil.convertFromByteArray(JGitUtil.getContentFromId(
+ val path = (file.name :: parentPath.reverse).reverse
+ path -> StringUtil.convertFromByteArray(JGitUtil.getContentFromId(
Git.open(getRepositoryDir(repository.owner, repository.name)), file.id, true).get)
}
diff --git a/src/main/scala/view/Asciidoc.scala b/src/main/scala/view/Asciidoc.scala
new file mode 100644
index 0000000..5c131c4
--- /dev/null
+++ b/src/main/scala/view/Asciidoc.scala
@@ -0,0 +1,62 @@
+package view
+
+import org.asciidoctor.Asciidoctor
+import org.asciidoctor.AttributesBuilder
+import org.asciidoctor.OptionsBuilder
+import org.asciidoctor.SafeMode
+import org.htmlcleaner.HtmlCleaner
+import org.htmlcleaner.HtmlNode
+import org.htmlcleaner.SimpleHtmlSerializer
+import org.htmlcleaner.TagNode
+import org.htmlcleaner.TagNodeVisitor
+
+object Asciidoc {
+
+ private[this] lazy val asciidoctor = Asciidoctor.Factory.create()
+
+ /**
+ * Converts Markdown of Wiki pages to HTML.
+ */
+ def toHtml(filePath: List[String], asciidoc: String, branch: String, repository: service.RepositoryService.RepositoryInfo,
+ enableWikiLink: Boolean, enableRefsLink: Boolean)(implicit context: app.Context): String = {
+
+ val options = OptionsBuilder.options()
+ options.safe(SafeMode.SECURE)
+ val attributes = AttributesBuilder.attributes()
+ attributes.showTitle(true)
+ options.attributes(attributes.get())
+ val rendered = asciidoctor.render(asciidoc, options)
+
+ val path = filePath.reverse.tail.reverse match {
+ case Nil => ""
+ case p => p.mkString("", "/", "/")
+ }
+ val relativeUrlPrefix = s"${helpers.url(repository)}/blob/${branch}/${path}"
+ prefixRelativeUrls(rendered, relativeUrlPrefix)
+ }
+
+ private[this] val exceptionPrefixes = Seq("#", "/", "http://", "https://")
+
+ 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 (exceptionPrefixes.forall(p => !href.startsWith(p))) {
+ tag.addAttribute("href", s"${urlPrefix}${href}")
+ }
+ }
+ case _ =>
+ }
+ // continue traversal
+ true
+ }
+ })
+ new SimpleHtmlSerializer(cleaner.getProperties()).getAsString(node)
+ }
+
+}
+
diff --git a/src/main/scala/view/helpers.scala b/src/main/scala/view/helpers.scala
index 8dc2adf..feb5f21 100644
--- a/src/main/scala/view/helpers.scala
+++ b/src/main/scala/view/helpers.scala
@@ -27,6 +27,16 @@
def plural(count: Int, singular: String, plural: String = ""): String =
if(count == 1) singular else if(plural.isEmpty) singular + "s" else plural
+ private[this] val renderersBySuffix: Seq[(String, (List[String], String, String, service.RepositoryService.RepositoryInfo, Boolean, Boolean, app.Context) => Html)] =
+ Seq(
+ ".md" -> ((filePath, fileContent, branch, repository, enableWikiLink, enableRefsLink, context) => markdown(fileContent, repository, enableWikiLink, enableRefsLink)(context)),
+ ".markdown" -> ((filePath, fileContent, branch, repository, enableWikiLink, enableRefsLink, context) => markdown(fileContent, repository, enableWikiLink, enableRefsLink)(context)),
+ ".adoc" -> ((filePath, fileContent, branch, repository, enableWikiLink, enableRefsLink, context) => asciidoc(filePath, fileContent, branch, repository, enableWikiLink, enableRefsLink)(context)),
+ ".asciidoc" -> ((filePath, fileContent, branch, repository, enableWikiLink, enableRefsLink, context) => asciidoc(filePath, fileContent, branch, repository, enableWikiLink, enableRefsLink)(context))
+ )
+
+ def renderableSuffixes: Seq[String] = renderersBySuffix.map(_._1)
+
/**
* Converts Markdown of Wiki pages to HTML.
*/
@@ -34,6 +44,25 @@
enableWikiLink: Boolean, enableRefsLink: Boolean)(implicit context: app.Context): Html =
Html(Markdown.toHtml(value, repository, enableWikiLink, enableRefsLink))
+ def renderMarkup(filePath: List[String], fileContent: String, branch: String,
+ repository: service.RepositoryService.RepositoryInfo,
+ enableWikiLink: Boolean, enableRefsLink: Boolean)(implicit context: app.Context): Html = {
+
+ val fileNameLower = filePath.reverse.head.toLowerCase
+ renderersBySuffix.find { case (suffix, _) => fileNameLower.endsWith(suffix) } match {
+ case Some((_, handler)) => handler(filePath, fileContent, branch, repository, enableWikiLink, enableRefsLink, context)
+ case None => Html(
+ s"${
+ fileContent.split("(\\r\\n)|\\n").map(xml.Utility.escape(_)).mkString("
")
+ }"
+ )
+ }
+ }
+
+ def asciidoc(filePath: List[String], value: String, branch: String, repository: service.RepositoryService.RepositoryInfo,
+ enableWikiLink: Boolean, enableRefsLink: Boolean)(implicit context: app.Context): Html =
+ Html(Asciidoc.toHtml(filePath, value, branch, repository, enableWikiLink, enableRefsLink))
+
/**
* Returns <img> which displays the avatar icon for the given user name.
* This method looks up Gravatar if avatar icon has not been configured in user settings.
diff --git a/src/main/twirl/repo/blob.scala.html b/src/main/twirl/repo/blob.scala.html
index 25a9910..acb193a 100644
--- a/src/main/twirl/repo/blob.scala.html
+++ b/src/main/twirl/repo/blob.scala.html
@@ -37,7 +37,13 @@
@content.content.get+ @defining(pathList.reverse.head) { file => + @if(renderableSuffixes.find(suffix => file.toLowerCase.endsWith(suffix))) { + @renderMarkup(pathList, content.content.get, branch, repository, false, false) + } else { +
@content.content.get+ } + } } @if(content.viewType == "image"){