mirror of
https://github.com/gitbucket/gitbucket.git
synced 2025-11-09 15:05:50 +01:00
Merge pull request #288 from lefou/asciidoctorj
Support AsciiDoc markup for all files + Plain text Readme's
This commit is contained in:
@@ -15,6 +15,7 @@ object MyBuild extends Build {
|
|||||||
"gitbucket",
|
"gitbucket",
|
||||||
file("."),
|
file("."),
|
||||||
settings = Defaults.defaultSettings ++ ScalatraPlugin.scalatraWithJRebel ++ Seq(
|
settings = Defaults.defaultSettings ++ ScalatraPlugin.scalatraWithJRebel ++ Seq(
|
||||||
|
sourcesInBase := false,
|
||||||
organization := Organization,
|
organization := Organization,
|
||||||
name := Name,
|
name := Name,
|
||||||
version := Version,
|
version := Version,
|
||||||
@@ -43,7 +44,9 @@ object MyBuild extends Build {
|
|||||||
"ch.qos.logback" % "logback-classic" % "1.0.13" % "runtime",
|
"ch.qos.logback" % "logback-classic" % "1.0.13" % "runtime",
|
||||||
"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",
|
||||||
|
"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"),
|
||||||
|
|||||||
@@ -252,7 +252,7 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
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.
|
* Provides HTML of the file list.
|
||||||
@@ -270,14 +270,16 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
|||||||
//val revisions = Seq(if(revstr.isEmpty) repository.repository.defaultBranch else revstr, repository.branchList.head)
|
//val revisions = Seq(if(revstr.isEmpty) repository.repository.defaultBranch else revstr, repository.branchList.head)
|
||||||
// get specified commit
|
// get specified commit
|
||||||
JGitUtil.getDefaultBranch(git, repository, revstr).map { case (objectId, revision) =>
|
JGitUtil.getDefaultBranch(git, repository, revstr).map { case (objectId, revision) =>
|
||||||
defining(JGitUtil.getRevCommitFromId(git, objectId)){ revCommit =>
|
defining(JGitUtil.getRevCommitFromId(git, objectId)) { revCommit =>
|
||||||
// get files
|
// get files
|
||||||
val files = JGitUtil.getFileList(git, revision, path)
|
val files = JGitUtil.getFileList(git, revision, path)
|
||||||
|
val parentPath = if (path == ".") Nil else path.split("/").toList
|
||||||
// process README.md or README.markdown
|
// process README.md or README.markdown
|
||||||
val readme = files.find { file =>
|
val readme = files.find { file =>
|
||||||
readmeFiles.contains(file.name.toLowerCase)
|
readmeFiles.contains(file.name.toLowerCase)
|
||||||
}.map { file =>
|
}.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)
|
Git.open(getRepositoryDir(repository.owner, repository.name)), file.id, true).get)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
62
src/main/scala/view/Asciidoc.scala
Normal file
62
src/main/scala/view/Asciidoc.scala
Normal file
@@ -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)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@@ -27,6 +27,16 @@ 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, (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.
|
* Converts Markdown of Wiki pages to HTML.
|
||||||
*/
|
*/
|
||||||
@@ -34,6 +44,25 @@ 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(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"<tt>${
|
||||||
|
fileContent.split("(\\r\\n)|\\n").map(xml.Utility.escape(_)).mkString("<br/>")
|
||||||
|
}</tt>"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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.
|
* 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.
|
* This method looks up Gravatar if avatar icon has not been configured in user settings.
|
||||||
|
|||||||
@@ -37,8 +37,14 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
@if(content.viewType == "text"){
|
@if(content.viewType == "text"){
|
||||||
|
@defining(pathList.reverse.head) { file =>
|
||||||
|
@if(renderableSuffixes.find(suffix => file.toLowerCase.endsWith(suffix))) {
|
||||||
|
@renderMarkup(pathList, content.content.get, branch, repository, false, false)
|
||||||
|
} else {
|
||||||
<pre class="prettyprint linenums blob">@content.content.get</pre>
|
<pre class="prettyprint linenums blob">@content.content.get</pre>
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@if(content.viewType == "image"){
|
@if(content.viewType == "image"){
|
||||||
<img src="?raw=true"/>
|
<img src="?raw=true"/>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
pathList: List[String],
|
pathList: List[String],
|
||||||
latestCommit: util.JGitUtil.CommitInfo,
|
latestCommit: util.JGitUtil.CommitInfo,
|
||||||
files: List[util.JGitUtil.FileInfo],
|
files: List[util.JGitUtil.FileInfo],
|
||||||
readme: Option[(util.JGitUtil.FileInfo, String)])(implicit context: app.Context)
|
readme: Option[(List[String], String)])(implicit context: app.Context)
|
||||||
@import context._
|
@import context._
|
||||||
@import view.helpers._
|
@import view.helpers._
|
||||||
@html.main(s"${repository.owner}/${repository.name}", Some(repository)) {
|
@html.main(s"${repository.owner}/${repository.name}", Some(repository)) {
|
||||||
@@ -85,10 +85,10 @@
|
|||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@readme.map { case(file, content) =>
|
@readme.map { case(filePath, content) =>
|
||||||
<div id="readme" class="box">
|
<div id="readme" class="box">
|
||||||
<div class="box-header">@file.name</div>
|
<div class="box-header">@filePath.reverse.head</div>
|
||||||
<div class="box-content markdown-body">@markdown(content, repository, false, false)</div>
|
<div class="box-content markdown-body">@renderMarkup(filePath, content, branch, repository, false, false)</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user