diff --git a/build.sbt b/build.sbt index 878b75f..9f6c3b3 100644 --- a/build.sbt +++ b/build.sbt @@ -45,6 +45,7 @@ "com.typesafe" % "config" % "1.3.0", "com.typesafe.akka" %% "akka-actor" % "2.3.15", "fr.brouillard.oss.security.xhub" % "xhub4j-core" % "1.0.0", + "org.jsoup" % "jsoup" % "1.9.2", "com.enragedginger" %% "akka-quartz-scheduler" % "1.4.0-akka-2.3.x" exclude("c3p0","c3p0"), "org.eclipse.jetty" % "jetty-webapp" % JettyVersion % "provided", "javax.servlet" % "javax.servlet-api" % "3.1.0" % "provided", diff --git a/src/main/scala/gitbucket/core/plugin/Plugin.scala b/src/main/scala/gitbucket/core/plugin/Plugin.scala index caaf726..c82558e 100644 --- a/src/main/scala/gitbucket/core/plugin/Plugin.scala +++ b/src/main/scala/gitbucket/core/plugin/Plugin.scala @@ -150,6 +150,16 @@ def dashboardTabs(registry: PluginRegistry, context: ServletContext, settings: SystemSettings): Seq[(Context) => Option[Link]] = Nil /** + * Override to add text decorators. + */ + val textDecorators: Seq[TextDecorator] = Nil + + /** + * Override to add text decorators. + */ + def textDecorators(registry: PluginRegistry, context: ServletContext, settings: SystemSettings): Seq[TextDecorator] = Nil + + /** * This method is invoked in initialization of plugin system. * Register plugin functionality to PluginRegistry. */ @@ -193,6 +203,9 @@ (dashboardTabs ++ dashboardTabs(registry, context, settings)).foreach { dashboardTab => registry.addDashboardTab(dashboardTab) } + (textDecorators ++ textDecorators(registry, context, settings)).foreach { textDecorator => + registry.addTextDecorator(textDecorator) + } } /** diff --git a/src/main/scala/gitbucket/core/plugin/PluginRegistory.scala b/src/main/scala/gitbucket/core/plugin/PluginRegistory.scala index d784d87..04b9fb7 100644 --- a/src/main/scala/gitbucket/core/plugin/PluginRegistory.scala +++ b/src/main/scala/gitbucket/core/plugin/PluginRegistory.scala @@ -42,6 +42,8 @@ private val systemSettingMenus = new ListBuffer[(Context) => Option[Link]] private val accountSettingMenus = new ListBuffer[(Context) => Option[Link]] private val dashboardTabs = new ListBuffer[(Context) => Option[Link]] + private val textDecorators = new ListBuffer[TextDecorator] + textDecorators += new EmojiDecorator() def addPlugin(pluginInfo: PluginInfo): Unit = { plugins += pluginInfo @@ -159,6 +161,12 @@ def getDashboardTabs: Seq[(Context) => Option[Link]] = dashboardTabs.toSeq + def addTextDecorator(textDecorator: TextDecorator): Unit = { + textDecorators += textDecorator + } + + def getTextDecorators: Seq[TextDecorator] = textDecorators.toSeq + } /** diff --git a/src/main/scala/gitbucket/core/plugin/TextDecorator.scala b/src/main/scala/gitbucket/core/plugin/TextDecorator.scala new file mode 100644 index 0000000..2de1d0c --- /dev/null +++ b/src/main/scala/gitbucket/core/plugin/TextDecorator.scala @@ -0,0 +1,20 @@ +package gitbucket.core.plugin + +import gitbucket.core.controller.Context +import gitbucket.core.service.RepositoryService.RepositoryInfo +import gitbucket.core.util.EmojiUtil + +trait TextDecorator { + + def decorate(text: String)(implicit context: Context): String + + def decorate(text: String, repositoryInfo: RepositoryInfo)(implicit context: Context): String = decorate(text) + +} + +class EmojiDecorator extends TextDecorator { + + override def decorate(text: String)(implicit context: Context): String = EmojiUtil.convertEmojis(text) + +} + diff --git a/src/main/scala/gitbucket/core/view/Markdown.scala b/src/main/scala/gitbucket/core/view/Markdown.scala index a2aa763..d30e623 100644 --- a/src/main/scala/gitbucket/core/view/Markdown.scala +++ b/src/main/scala/gitbucket/core/view/Markdown.scala @@ -6,7 +6,7 @@ import gitbucket.core.controller.Context import gitbucket.core.service.{RepositoryService, RequestCache} -import gitbucket.core.util.{EmojiUtil, StringUtil} +import gitbucket.core.util.StringUtil import io.github.gitbucket.markedj._ import io.github.gitbucket.markedj.Utils._ @@ -44,7 +44,7 @@ val renderer = new GitBucketMarkedRenderer(options, repository, enableWikiLink, enableRefsLink, enableAnchor, enableTaskList, hasWritePermission, pages) - EmojiUtil.convertEmojis(Marked.marked(source, options, renderer)) + helpers.decorateHtml(Marked.marked(source, options, renderer)) } /** diff --git a/src/main/scala/gitbucket/core/view/helpers.scala b/src/main/scala/gitbucket/core/view/helpers.scala index 5e114bc..a7437b6 100644 --- a/src/main/scala/gitbucket/core/view/helpers.scala +++ b/src/main/scala/gitbucket/core/view/helpers.scala @@ -7,7 +7,9 @@ import gitbucket.core.model.CommitState import gitbucket.core.plugin.{PluginRegistry, RenderRequest} import gitbucket.core.service.{RepositoryService, RequestCache} -import gitbucket.core.util.{EmojiUtil, FileUtil, JGitUtil, StringUtil} +import gitbucket.core.util.{FileUtil, JGitUtil, StringUtil} +import org.jsoup.Jsoup +import org.jsoup.nodes.{Element, Node} import play.twirl.api.{Html, HtmlFormat} /** @@ -150,7 +152,7 @@ * Converts commit id, issue id and username to the link. */ def link(value: String, repository: RepositoryService.RepositoryInfo)(implicit context: Context): Html = - Html(EmojiUtil.convertEmojis(convertRefsLinks(value, repository))) + Html(decorateHtml(convertRefsLinks(value, repository))) def cut(value: String, length: Int): String = if(value.length > length){ @@ -334,4 +336,33 @@ HtmlFormat.fill(out) } + + /** + * Decorate text in HTML by TextDecorator. + * + * TODO Move to the other place. + */ + def decorateHtml(text: String)(implicit context: Context): String = { + val textDecorators = PluginRegistry().getTextDecorators + + def processNode(n: Node): Unit = { + n match { + case x: Element => { + if(x.hasText && x.ownText.nonEmpty){ + val text = textDecorators.foldLeft(x.ownText){ case (text, textDecorator) => + textDecorator.decorate(text) + } + x.html(text) + } + x.children.toArray.foreach { c => + processNode(c.asInstanceOf[Node]) + } + } + case _ => () + } + } + val body = Jsoup.parseBodyFragment(text).getElementsByTag("body").get(0) + processNode(body) + body.html + } }