Fix emoji conversion

This commit is contained in:
Naoki Takezoe
2016-07-10 02:03:25 +09:00
parent 9c4cc12a02
commit fd181b9a0c
4 changed files with 50 additions and 44 deletions

View File

@@ -6,7 +6,7 @@ import java.util.Locale
import gitbucket.core.controller.Context import gitbucket.core.controller.Context
import gitbucket.core.service.{RepositoryService, RequestCache} import gitbucket.core.service.{RepositoryService, RequestCache}
import gitbucket.core.util.StringUtil import gitbucket.core.util.{EmojiUtil, StringUtil}
import io.github.gitbucket.markedj._ import io.github.gitbucket.markedj._
import io.github.gitbucket.markedj.Utils._ import io.github.gitbucket.markedj.Utils._
@@ -44,7 +44,7 @@ object Markdown {
val renderer = new GitBucketMarkedRenderer(options, repository, val renderer = new GitBucketMarkedRenderer(options, repository,
enableWikiLink, enableRefsLink, enableAnchor, enableTaskList, hasWritePermission, pages) enableWikiLink, enableRefsLink, enableAnchor, enableTaskList, hasWritePermission, pages)
helpers.decorateHtml(Marked.marked(source, options, renderer), repository) EmojiUtil.convertEmojis(Marked.marked(source, options, renderer))
} }
/** /**

View File

@@ -153,7 +153,7 @@ object helpers extends AvatarImageProvider with LinkConverter with RequestCache
* Converts commit id, issue id and username to the link. * Converts commit id, issue id and username to the link.
*/ */
def link(value: String, repository: RepositoryService.RepositoryInfo)(implicit context: Context): Html = def link(value: String, repository: RepositoryService.RepositoryInfo)(implicit context: Context): Html =
Html(decorateHtml(convertRefsLinks(value, repository), repository)) Html(EmojiUtil.convertEmojis(convertRefsLinks(value, repository)))
def cut(value: String, length: Int): String = def cut(value: String, length: Int): String =
if(value.length > length){ if(value.length > length){
@@ -321,7 +321,7 @@ object helpers extends AvatarImageProvider with LinkConverter with RequestCache
// This pattern comes from: http://stackoverflow.com/a/4390768/1771641 (extract-url-from-string) // This pattern comes from: http://stackoverflow.com/a/4390768/1771641 (extract-url-from-string)
private[this] val detectAndRenderLinksRegex = """(?i)\b((?:https?://|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,13}/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'".,<>?«»“”‘’]))""".r private[this] val detectAndRenderLinksRegex = """(?i)\b((?:https?://|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,13}/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'".,<>?«»“”‘’]))""".r
def detectAndRenderLinks(text: String): Html = { def detectAndRenderLinks(text: String)(implicit context: Context): String = {
val matches = detectAndRenderLinksRegex.findAllMatchIn(text).toSeq val matches = detectAndRenderLinksRegex.findAllMatchIn(text).toSeq
val (x, pos) = matches.foldLeft((collection.immutable.Seq.empty[Html], 0)){ case ((x, pos), m) => val (x, pos) = matches.foldLeft((collection.immutable.Seq.empty[Html], 0)){ case ((x, pos), m) =>
@@ -335,36 +335,38 @@ object helpers extends AvatarImageProvider with LinkConverter with RequestCache
// append rest fragment // append rest fragment
val out = if (pos < text.length) x :+ HtmlFormat.escape(text.substring(pos)) else x val out = if (pos < text.length) x :+ HtmlFormat.escape(text.substring(pos)) else x
HtmlFormat.fill(out) EmojiUtil.convertEmojis(HtmlFormat.fill(out).toString)
//HtmlFormat.fill(out).toString
} }
/** // /**
* Decorate text in HTML by TextDecorator. // * Decorate text in HTML by TextDecorator.
* // *
* TODO Move to the other place. // * TODO Move to the other place.
*/ // */
def decorateHtml(text: String, repository: RepositoryInfo)(implicit context: Context): String = { // def decorateHtml(text: String, repository: RepositoryInfo)(implicit context: Context): String = {
// val textDecorators = PluginRegistry().getTextDecorators //
//// val textDecorators = PluginRegistry().getTextDecorators
def processNode(n: Node): Unit = { //
n match { // def processNode(n: Node): Unit = {
case x: Element => { // n match {
if(x.hasText && x.ownText.nonEmpty){ // case x: Element => {
val text = EmojiUtil.convertEmojis(x.ownText) // if(x.hasText && x.ownText.nonEmpty){
// val text = textDecorators.foldLeft(x.ownText){ case (text, textDecorator) => // val text = EmojiUtil.convertEmojis(x.ownText)
// textDecorator.decorate(text, repository) //// val text = textDecorators.foldLeft(x.ownText){ case (text, textDecorator) =>
// } //// textDecorator.decorate(text, repository)
x.html(text) //// }
} // x.html(text)
x.children.toArray.foreach { c => // }
processNode(c.asInstanceOf[Node]) // x.children.toArray.foreach { c =>
} // processNode(c.asInstanceOf[Node])
} // }
case _ => () // }
} // case _ => ()
} // }
val body = Jsoup.parseBodyFragment(text).getElementsByTag("body").get(0) // }
processNode(body) // val body = Jsoup.parseBodyFragment(text).getElementsByTag("body").get(0)
body.html // processNode(body)
} // body.html
// }
} }

View File

@@ -73,7 +73,7 @@
</div> </div>
} }
@x.description.map { description => @x.description.map { description =>
<div class="normal muted" style="margin-left: 36px; font-size: 80%;">@helpers.detectAndRenderLinks(description)</div> <div class="normal muted" style="margin-left: 36px; font-size: 80%;">@Html(helpers.detectAndRenderLinks(description))</div>
} }
} }
</div> </div>

View File

@@ -1,58 +1,62 @@
package gitbucket.core.view package gitbucket.core.view
import gitbucket.core.controller.Context
import gitbucket.core.service.RepositoryService.RepositoryInfo
import org.scalatest.FunSpec import org.scalatest.FunSpec
import org.scalatest.mock.MockitoSugar
class HelpersSpec extends FunSpec { class HelpersSpec extends FunSpec with MockitoSugar {
implicit val context = mock[Context]
import helpers._ import helpers._
describe("detect and render links") { describe("detect and render links") {
it("should pass identical string when no link is present") { it("should pass identical string when no link is present") {
val before = "Description" val before = "Description"
val after = detectAndRenderLinks(before).toString() val after = detectAndRenderLinks(before)
assert(after == before) assert(after == before)
} }
it("should convert a single link") { it("should convert a single link") {
val before = "http://example.com" val before = "http://example.com"
val after = detectAndRenderLinks(before).toString() val after = detectAndRenderLinks(before)
assert(after == """<a href="http://example.com">http://example.com</a>""") assert(after == """<a href="http://example.com">http://example.com</a>""")
} }
it("should convert a single link within trailing text") { it("should convert a single link within trailing text") {
val before = "Example Project. http://example.com" val before = "Example Project. http://example.com"
val after = detectAndRenderLinks(before).toString() val after = detectAndRenderLinks(before)
assert(after == """Example Project. <a href="http://example.com">http://example.com</a>""") assert(after == """Example Project. <a href="http://example.com">http://example.com</a>""")
} }
it("should convert a mulitple links within text") { it("should convert a mulitple links within text") {
val before = "Example Project. http://example.com. (See also https://github.com/)" val before = "Example Project. http://example.com. (See also https://github.com/)"
val after = detectAndRenderLinks(before).toString() val after = detectAndRenderLinks(before)
assert(after == """Example Project. <a href="http://example.com">http://example.com</a>. (See also <a href="https://github.com/">https://github.com/</a>)""") assert(after == """Example Project. <a href="http://example.com">http://example.com</a>. (See also <a href="https://github.com/">https://github.com/</a>)""")
} }
it("should properly escape html metacharacters") { it("should properly escape html metacharacters") {
val before = "<>&" val before = "<>&"
val after = detectAndRenderLinks(before).toString() val after = detectAndRenderLinks(before)
assert(after == """&lt;&gt;&amp;""") assert(after == """&lt;&gt;&amp;""")
} }
it("should escape html metacharacters adjacent to a link") { it("should escape html metacharacters adjacent to a link") {
val before = "<http://example.com>" val before = "<http://example.com>"
val after = detectAndRenderLinks(before).toString() val after = detectAndRenderLinks(before)
assert(after == """&lt;<a href="http://example.com">http://example.com</a>&gt;""") assert(after == """&lt;<a href="http://example.com">http://example.com</a>&gt;""")
} }
it("should stop link recognition at a metacharacter") { it("should stop link recognition at a metacharacter") {
val before = "http://exa<mple.com" val before = "http://exa<mple.com"
val after = detectAndRenderLinks(before).toString() val after = detectAndRenderLinks(before)
assert(after == """<a href="http://exa">http://exa</a>&lt;mple.com""") assert(after == """<a href="http://exa">http://exa</a>&lt;mple.com""")
} }
it("should make sure there are no double quotes in the href attribute") { it("should make sure there are no double quotes in the href attribute") {
val before = "http://exa\"mple.com" val before = "http://exa\"mple.com"
val after = detectAndRenderLinks(before).toString() val after = detectAndRenderLinks(before)
assert(after == """<a href="http://exa&quot;mple.com">http://exa"mple.com</a>""") assert(after == """<a href="http://exa&quot;mple.com">http://exa"mple.com</a>""")
} }
} }