Commit 67a6a0b29b1ca9563244f914bd0eaf0165389efd

Authored by Robert Speicher
1 parent 90f587f4

Change Gitlab::Markdown to a module; add emoji parsing

Showing 1 changed file with 69 additions and 13 deletions   Show diff stats
lib/gitlab/markdown.rb
1 module Gitlab 1 module Gitlab
2 # Custom parser for Gitlab-flavored Markdown 2 # Custom parser for Gitlab-flavored Markdown
3 # 3 #
4 - # It replaces references in the text with links to the appropriate items in Gitlab. 4 + # It replaces references in the text with links to the appropriate items in
  5 + # Gitlab.
5 # 6 #
6 # Supported reference formats are: 7 # Supported reference formats are:
7 # * @foo for team members 8 # * @foo for team members
@@ -10,19 +11,20 @@ module Gitlab @@ -10,19 +11,20 @@ module Gitlab
10 # * $123 for snippets 11 # * $123 for snippets
11 # * 123456 for commits 12 # * 123456 for commits
12 # 13 #
13 - # Examples 14 + # It also parses Emoji codes to insert images. See
  15 + # http://www.emoji-cheat-sheet.com/ for a list of the supported icons.
14 # 16 #
15 - # >> m = Markdown.new(...) 17 + # Examples
16 # 18 #
17 - # >> m.parse("Hey @david, can you fix this?") 19 + # >> gfm("Hey @david, can you fix this?")
18 # => "Hey <a href="/gitlab/team_members/1">@david</a>, can you fix this?" 20 # => "Hey <a href="/gitlab/team_members/1">@david</a>, can you fix this?"
19 # 21 #
20 - # >> m.parse("Commit 35d5f7c closes #1234") 22 + # >> gfm("Commit 35d5f7c closes #1234")
21 # => "Commit <a href="/gitlab/commits/35d5f7c">35d5f7c</a> closes <a href="/gitlab/issues/1234">#1234</a>" 23 # => "Commit <a href="/gitlab/commits/35d5f7c">35d5f7c</a> closes <a href="/gitlab/issues/1234">#1234</a>"
22 - class Markdown  
23 - include Rails.application.routes.url_helpers  
24 - include ActionView::Helpers  
25 - 24 + #
  25 + # >> gfm(":trollface:")
  26 + # => "<img alt=\":trollface:\" class=\"emoji\" src=\"/images/trollface.png" title=\":trollface:\" />
  27 + module Markdown
26 REFERENCE_PATTERN = %r{ 28 REFERENCE_PATTERN = %r{
27 ([^\w&;])? # Prefix (1) 29 ([^\w&;])? # Prefix (1)
28 ( # Reference (2) 30 ( # Reference (2)
@@ -33,15 +35,52 @@ module Gitlab @@ -33,15 +35,52 @@ module Gitlab
33 ([^\w&;])? # Suffix (6) 35 ([^\w&;])? # Suffix (6)
34 }x.freeze 36 }x.freeze
35 37
  38 + EMOJI_PATTERN = %r{(:([\w\-\+]+):)}.freeze
  39 +
36 attr_reader :html_options 40 attr_reader :html_options
37 41
38 - def initialize(project, html_options = {})  
39 - @project = project 42 + # Public: Parse the provided text with GitLab-Flavored Markdown
  43 + #
  44 + # text - the source text
  45 + # html_options - extra options for the reference links as given to link_to
  46 + #
  47 + # Note: reference links will only be generated if @project is set
  48 + def gfm(text, html_options = {})
  49 + return text if text.nil?
  50 + return text if @project.nil?
  51 +
40 @html_options = html_options 52 @html_options = html_options
  53 +
  54 + # Extract pre blocks so they are not altered
  55 + # from http://github.github.com/github-flavored-markdown/
  56 + extractions = {}
  57 + text.gsub!(%r{<pre>.*?</pre>|<code>.*?</code>}m) do |match|
  58 + md5 = Digest::MD5.hexdigest(match)
  59 + extractions[md5] = match
  60 + "{gfm-extraction-#{md5}}"
  61 + end
  62 +
  63 + # TODO: add popups with additional information
  64 +
  65 + text = parse(text)
  66 +
  67 + # Insert pre block extractions
  68 + text.gsub!(/\{gfm-extraction-(\h{32})\}/) do
  69 + extractions[$1]
  70 + end
  71 +
  72 + sanitize text.html_safe, attributes: ActionView::Base.sanitized_allowed_attributes + %w(id class)
41 end 73 end
42 74
  75 + private
  76 +
  77 + # Private: Parses text for references and emoji
  78 + #
  79 + # text - Text to parse
  80 + #
  81 + # Returns parsed text
43 def parse(text) 82 def parse(text)
44 - text.gsub(REFERENCE_PATTERN) do |match| 83 + text = text.gsub(REFERENCE_PATTERN) do |match|
45 prefix = $1 || '' 84 prefix = $1 || ''
46 reference = $2 85 reference = $2
47 identifier = $3 || $4 || $5 86 identifier = $3 || $4 || $5
@@ -53,9 +92,26 @@ module Gitlab @@ -53,9 +92,26 @@ module Gitlab
53 match 92 match
54 end 93 end
55 end 94 end
  95 +
  96 + text = text.gsub(EMOJI_PATTERN) do |match|
  97 + if valid_emoji?($2)
  98 + helper.image_tag("#{$2}.png", class: 'emoji', title: $1, alt: $1)
  99 + else
  100 + match
  101 + end
  102 + end
  103 +
  104 + text
56 end 105 end
57 106
58 - private 107 + # Private: Checks if an emoji icon exists in the image asset directory
  108 + #
  109 + # emoji - Identifier of the emoji as a string (e.g., "+1", "heart")
  110 + #
  111 + # Returns boolean
  112 + def valid_emoji?(emoji)
  113 + File.exists?(Rails.root.join('app', 'assets', 'images', 'emoji', "#{emoji}.png"))
  114 + end
59 115
60 # Private: Dispatches to a dedicated processing method based on reference 116 # Private: Dispatches to a dedicated processing method based on reference
61 # 117 #