mirror of
https://github.com/gitbucket/gitbucket.git
synced 2025-11-10 07:25:50 +01:00
(refs #775)Add new extension point to add markup render
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package gitbucket.core.controller
|
||||
|
||||
import gitbucket.core.api._
|
||||
import gitbucket.core.plugin.PluginRegistry
|
||||
import gitbucket.core.repo.html
|
||||
import gitbucket.core.helper
|
||||
import gitbucket.core.service._
|
||||
@@ -546,7 +547,9 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
||||
}
|
||||
|
||||
|
||||
private val readmeFiles = view.helpers.renderableSuffixes.map(suffix => s"readme${suffix}") ++ Seq("readme.txt", "readme")
|
||||
private val readmeFiles = PluginRegistry().renderableExtensions.map { extension =>
|
||||
s"readme.${extension}"
|
||||
} ++ Seq("readme.txt", "readme")
|
||||
|
||||
/**
|
||||
* Provides HTML of the file list.
|
||||
|
||||
@@ -32,6 +32,11 @@ trait Plugin {
|
||||
*/
|
||||
val javaScripts: Seq[(String, String)] = Nil
|
||||
|
||||
/**
|
||||
* Override to declare this plug-in provides renderers.
|
||||
*/
|
||||
val renderers: Seq[(String, Renderer)] = Nil
|
||||
|
||||
/**
|
||||
* This method is invoked in initialization of plugin system.
|
||||
* Register plugin functionality to PluginRegistry.
|
||||
@@ -46,6 +51,9 @@ trait Plugin {
|
||||
javaScripts.foreach { case (path, script) =>
|
||||
registry.addJavaScript(path, script)
|
||||
}
|
||||
renderers.foreach { case (extension, renderer) =>
|
||||
registry.addRenderer(extension, renderer)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -24,6 +24,10 @@ class PluginRegistry {
|
||||
private val javaScripts = new ListBuffer[(String, String)]
|
||||
private val controllers = new ListBuffer[(ControllerBase, String)]
|
||||
private val images = mutable.Map[String, String]()
|
||||
private val renderers = mutable.Map[String, Renderer]()
|
||||
renderers ++= Seq(
|
||||
"md" -> MarkdownRenderer, "markdown" -> MarkdownRenderer
|
||||
)
|
||||
|
||||
def addPlugin(pluginInfo: PluginInfo): Unit = {
|
||||
plugins += pluginInfo
|
||||
@@ -60,15 +64,23 @@ class PluginRegistry {
|
||||
def getControllers(): List[(ControllerBase, String)] = controllers.toList
|
||||
|
||||
def addJavaScript(path: String, script: String): Unit = {
|
||||
javaScripts += Tuple2(path, script)
|
||||
javaScripts += ((path, script))
|
||||
}
|
||||
|
||||
//def getJavaScripts(): List[(String, String)] = javaScripts.toList
|
||||
|
||||
def getJavaScript(currentPath: String): List[String] = {
|
||||
javaScripts.filter(x => currentPath.matches(x._1)).toList.map(_._2)
|
||||
}
|
||||
|
||||
def addRenderer(extension: String, renderer: Renderer): Unit = {
|
||||
renderers += ((extension, renderer))
|
||||
}
|
||||
|
||||
def getRenderer(extension: String): Renderer = {
|
||||
renderers.get(extension).getOrElse(DefaultRenderer)
|
||||
}
|
||||
|
||||
def renderableExtensions: Seq[String] = renderers.keys.toSeq
|
||||
|
||||
private case class GlobalAction(
|
||||
method: String,
|
||||
path: String,
|
||||
@@ -97,6 +109,10 @@ object PluginRegistry {
|
||||
*/
|
||||
def apply(): PluginRegistry = instance
|
||||
|
||||
def isRenderable(fileName: String): Boolean = {
|
||||
instance.renderableExtensions.exists(extension => fileName.toLowerCase.endsWith("." + extension))
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes all installed plugins.
|
||||
*/
|
||||
|
||||
44
src/main/scala/gitbucket/core/plugin/Renderer.scala
Normal file
44
src/main/scala/gitbucket/core/plugin/Renderer.scala
Normal file
@@ -0,0 +1,44 @@
|
||||
package gitbucket.core.plugin
|
||||
|
||||
import gitbucket.core.controller.Context
|
||||
import gitbucket.core.service.RepositoryService
|
||||
import gitbucket.core.view.Markdown
|
||||
import play.twirl.api.Html
|
||||
|
||||
/**
|
||||
* A render engine to render content to HTML.
|
||||
*/
|
||||
trait Renderer {
|
||||
|
||||
/**
|
||||
* Render the given request to HTML.
|
||||
*/
|
||||
def render(request: RenderRequest): Html
|
||||
|
||||
}
|
||||
|
||||
object MarkdownRenderer extends Renderer {
|
||||
override def render(request: RenderRequest): Html = {
|
||||
import request._
|
||||
Html(Markdown.toHtml(fileContent, repository, enableWikiLink, enableRefsLink)(context))
|
||||
}
|
||||
}
|
||||
|
||||
object DefaultRenderer extends Renderer {
|
||||
override def render(request: RenderRequest): Html = {
|
||||
import request._
|
||||
Html(
|
||||
s"<tt>${
|
||||
fileContent.split("(\\r\\n)|\\n").map(xml.Utility.escape(_)).mkString("<br/>")
|
||||
}</tt>"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
case class RenderRequest(filePath: List[String],
|
||||
fileContent: String,
|
||||
branch: String,
|
||||
repository: RepositoryService.RepositoryInfo,
|
||||
enableWikiLink: Boolean,
|
||||
enableRefsLink: Boolean,
|
||||
context: Context)
|
||||
@@ -5,8 +5,9 @@ import java.util.{Date, Locale, TimeZone}
|
||||
|
||||
import gitbucket.core.controller.Context
|
||||
import gitbucket.core.model.CommitState
|
||||
import gitbucket.core.plugin.{RenderRequest, PluginRegistry, Renderer}
|
||||
import gitbucket.core.service.{RepositoryService, RequestCache}
|
||||
import gitbucket.core.util.{JGitUtil, StringUtil}
|
||||
import gitbucket.core.util.{FileUtil, JGitUtil, StringUtil}
|
||||
|
||||
import play.twirl.api.Html
|
||||
|
||||
@@ -83,14 +84,6 @@ object helpers extends AvatarImageProvider with LinkConverter with RequestCache
|
||||
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, RepositoryService.RepositoryInfo, Boolean, Boolean, 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))
|
||||
)
|
||||
|
||||
def renderableSuffixes: Seq[String] = renderersBySuffix.map(_._1)
|
||||
|
||||
/**
|
||||
* Converts Markdown of Wiki pages to HTML.
|
||||
*/
|
||||
@@ -107,15 +100,10 @@ object helpers extends AvatarImageProvider with LinkConverter with RequestCache
|
||||
repository: RepositoryService.RepositoryInfo,
|
||||
enableWikiLink: Boolean, enableRefsLink: Boolean)(implicit context: 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>"
|
||||
)
|
||||
}
|
||||
val fileName = filePath.reverse.head.toLowerCase
|
||||
val extension = FileUtil.getExtension(fileName)
|
||||
val renderer = PluginRegistry().getRenderer(extension)
|
||||
renderer.render(RenderRequest(filePath, fileContent, branch, repository, enableWikiLink, enableRefsLink, context))
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
isBlame: Boolean)(implicit context: gitbucket.core.controller.Context)
|
||||
@import context._
|
||||
@import gitbucket.core.view.helpers._
|
||||
@import gitbucket.core.plugin.PluginRegistry
|
||||
@html.main(s"${repository.owner}/${repository.name}", Some(repository)) {
|
||||
@html.menu("code", repository){
|
||||
<div class="head">
|
||||
@@ -75,7 +76,7 @@
|
||||
<tr>
|
||||
<td>
|
||||
@if(content.viewType == "text"){
|
||||
@defining(renderableSuffixes.find(suffix => pathList.reverse.head.toLowerCase.endsWith(suffix))) { isRrenderable =>
|
||||
@defining(PluginRegistry.isRenderable(pathList.reverse.head)){ isRrenderable =>
|
||||
@if(!isBlame && isRrenderable) {
|
||||
<div class="box-content markdown-body" style="border: none; padding-left: 16px; padding-right: 16px;">
|
||||
@renderMarkup(pathList, content.content.get, branch, repository, false, false)
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
content: gitbucket.core.util.JGitUtil.ContentInfo)(implicit context: gitbucket.core.controller.Context)
|
||||
@import context._
|
||||
@import gitbucket.core.view.helpers._
|
||||
@import gitbucket.core.plugin.PluginRegistry
|
||||
@html.main(if(fileName.isEmpty) "New File" else s"Editing ${fileName.get} at ${branch} - ${repository.owner}/${repository.name}", Some(repository)) {
|
||||
@html.menu("code", repository){
|
||||
<form method="POST" action="@url(repository)/@if(fileName.isEmpty){create}else{update}" validate="true">
|
||||
@@ -120,7 +121,7 @@ $(function(){
|
||||
$('#editor').hide();
|
||||
$('#preview').show()
|
||||
|
||||
@if(renderableSuffixes.find(suffix => fileName.map(_.toLowerCase.endsWith(suffix)).getOrElse(false))) {
|
||||
@if(fileName.map(PluginRegistry.isRenderable _).getOrElse(false)) {
|
||||
// update preview
|
||||
$('#preview').html('<img src="@assets/common/images/indicator.gif"> Previewing...');
|
||||
$.post('@url(repository)/_preview', {
|
||||
|
||||
Reference in New Issue
Block a user