mirror of
https://github.com/gitbucket/gitbucket.git
synced 2025-11-09 15:05:50 +01:00
Merge branch 'master' into feature-tasklist
This commit is contained in:
@@ -1,62 +0,0 @@
|
||||
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)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ trait AvatarImageProvider { self: RequestCache =>
|
||||
// by user name
|
||||
getAccountByUserName(userName).map { account =>
|
||||
if(account.image.isEmpty && context.settings.gravatar){
|
||||
s"""https://www.gravatar.com/avatar/${StringUtil.md5(account.mailAddress.toLowerCase)}?s=${size}"""
|
||||
s"""https://www.gravatar.com/avatar/${StringUtil.md5(account.mailAddress.toLowerCase)}?s=${size}&d=retro&r=g"""
|
||||
} else {
|
||||
s"""${context.path}/${account.userName}/_avatar"""
|
||||
}
|
||||
@@ -28,13 +28,13 @@ trait AvatarImageProvider { self: RequestCache =>
|
||||
// by mail address
|
||||
getAccountByMailAddress(mailAddress).map { account =>
|
||||
if(account.image.isEmpty && context.settings.gravatar){
|
||||
s"""https://www.gravatar.com/avatar/${StringUtil.md5(account.mailAddress.toLowerCase)}?s=${size}"""
|
||||
s"""https://www.gravatar.com/avatar/${StringUtil.md5(account.mailAddress.toLowerCase)}?s=${size}&d=retro&r=g"""
|
||||
} else {
|
||||
s"""${context.path}/${account.userName}/_avatar"""
|
||||
}
|
||||
} getOrElse {
|
||||
if(context.settings.gravatar){
|
||||
s"""https://www.gravatar.com/avatar/${StringUtil.md5(mailAddress.toLowerCase)}?s=${size}"""
|
||||
s"""https://www.gravatar.com/avatar/${StringUtil.md5(mailAddress.toLowerCase)}?s=${size}&d=retro&r=g"""
|
||||
} else {
|
||||
s"""${context.path}/_unknown/_avatar"""
|
||||
}
|
||||
@@ -42,9 +42,9 @@ trait AvatarImageProvider { self: RequestCache =>
|
||||
}
|
||||
|
||||
if(tooltip){
|
||||
Html(s"""<img src="${src}" class="avatar" style="width: ${size}px; height: ${size}px;" data-toggle="tooltip" title="${userName}"/>""")
|
||||
Html(s"""<img src="${src}" class="${if(size > 20){"avatar"} else {"avatar-mini"}}" style="width: ${size}px; height: ${size}px;" data-toggle="tooltip" title="${userName}"/>""")
|
||||
} else {
|
||||
Html(s"""<img src="${src}" class="avatar" style="width: ${size}px; height: ${size}px;" />""")
|
||||
Html(s"""<img src="${src}" class="${if(size > 20){"avatar"} else {"avatar-mini"}}" style="width: ${size}px; height: ${size}px;" />""")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -96,7 +96,8 @@ class GitBucketHtmlSerializer(
|
||||
) with RepositoryService with LinkConverter with RequestCache {
|
||||
|
||||
override protected def printImageTag(imageNode: SuperNode, url: String): Unit =
|
||||
printer.print("<img src=\"").print(fixUrl(url)).print("\" alt=\"").printEncoded(printChildrenToString(imageNode)).print("\"/>")
|
||||
printer.print("<a target=\"_blank\" href=\"").print(fixUrl(url)).print("\">")
|
||||
.print("<img src=\"").print(fixUrl(url)).print("\" alt=\"").printEncoded(printChildrenToString(imageNode)).print("\"/></a>")
|
||||
|
||||
override protected def printLink(rendering: LinkRenderer.Rendering): Unit = {
|
||||
printer.print('<').print('a')
|
||||
|
||||
@@ -15,6 +15,11 @@ object helpers extends AvatarImageProvider with LinkConverter with RequestCache
|
||||
*/
|
||||
def datetime(date: Date): String = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(date)
|
||||
|
||||
/**
|
||||
* Format java.util.Date to "yyyy-MM-dd'T'hh:mm:ss'Z'".
|
||||
*/
|
||||
def datetimeRFC3339(date: Date): String = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss'Z'").format(date).replaceAll("(\\d\\d)(\\d\\d)$","$1:$2")
|
||||
|
||||
/**
|
||||
* Format java.util.Date to "yyyy-MM-dd".
|
||||
*/
|
||||
@@ -30,9 +35,7 @@ object helpers extends AvatarImageProvider with LinkConverter with RequestCache
|
||||
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))
|
||||
".markdown" -> ((filePath, fileContent, branch, repository, enableWikiLink, enableRefsLink, context) => markdown(fileContent, repository, enableWikiLink, enableRefsLink)(context))
|
||||
)
|
||||
|
||||
def renderableSuffixes: Seq[String] = renderersBySuffix.map(_._1)
|
||||
@@ -59,10 +62,6 @@ object helpers extends AvatarImageProvider with LinkConverter with RequestCache
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
@@ -164,6 +163,45 @@ object helpers extends AvatarImageProvider with LinkConverter with RequestCache
|
||||
*/
|
||||
def isPast(date: Date): Boolean = System.currentTimeMillis > date.getTime
|
||||
|
||||
/**
|
||||
* Returns file type for AceEditor.
|
||||
*/
|
||||
def editorType(fileName: String): String = {
|
||||
fileName.toLowerCase match {
|
||||
case x if(x.endsWith(".bat")) => "batchfile"
|
||||
case x if(x.endsWith(".java")) => "java"
|
||||
case x if(x.endsWith(".scala")) => "scala"
|
||||
case x if(x.endsWith(".js")) => "javascript"
|
||||
case x if(x.endsWith(".css")) => "css"
|
||||
case x if(x.endsWith(".md")) => "markdown"
|
||||
case x if(x.endsWith(".html")) => "html"
|
||||
case x if(x.endsWith(".xml")) => "xml"
|
||||
case x if(x.endsWith(".c")) => "c_cpp"
|
||||
case x if(x.endsWith(".cpp")) => "c_cpp"
|
||||
case x if(x.endsWith(".coffee")) => "coffee"
|
||||
case x if(x.endsWith(".ejs")) => "ejs"
|
||||
case x if(x.endsWith(".hs")) => "haskell"
|
||||
case x if(x.endsWith(".json")) => "json"
|
||||
case x if(x.endsWith(".jsp")) => "jsp"
|
||||
case x if(x.endsWith(".jsx")) => "jsx"
|
||||
case x if(x.endsWith(".cl")) => "lisp"
|
||||
case x if(x.endsWith(".clojure")) => "lisp"
|
||||
case x if(x.endsWith(".lua")) => "lua"
|
||||
case x if(x.endsWith(".php")) => "php"
|
||||
case x if(x.endsWith(".py")) => "python"
|
||||
case x if(x.endsWith(".rdoc")) => "rdoc"
|
||||
case x if(x.endsWith(".rhtml")) => "rhtml"
|
||||
case x if(x.endsWith(".ruby")) => "ruby"
|
||||
case x if(x.endsWith(".sh")) => "sh"
|
||||
case x if(x.endsWith(".sql")) => "sql"
|
||||
case x if(x.endsWith(".tcl")) => "tcl"
|
||||
case x if(x.endsWith(".vbs")) => "vbscript"
|
||||
case x if(x.endsWith(".tcl")) => "tcl"
|
||||
case x if(x.endsWith(".yml")) => "yaml"
|
||||
case _ => "plain_text"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implicit conversion to add mkHtml() to Seq[Html].
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user