diff --git a/lib/gitlab/markdown.rb b/lib/gitlab/markdown.rb index 9a07133d0b3..f000ffb4e24 100644 --- a/lib/gitlab/markdown.rb +++ b/lib/gitlab/markdown.rb @@ -1,7 +1,8 @@ module Gitlab # Custom parser for Gitlab-flavored Markdown # - # It replaces references in the text with links to the appropriate items in Gitlab. + # It replaces references in the text with links to the appropriate items in + # Gitlab. # # Supported reference formats are: # * @foo for team members @@ -10,19 +11,20 @@ module Gitlab # * $123 for snippets # * 123456 for commits # + # It also parses Emoji codes to insert images. See + # http://www.emoji-cheat-sheet.com/ for a list of the supported icons. + # # Examples # - # >> m = Markdown.new(...) - # - # >> m.parse("Hey @david, can you fix this?") + # >> gfm("Hey @david, can you fix this?") # => "Hey @david, can you fix this?" # - # >> m.parse("Commit 35d5f7c closes #1234") + # >> gfm("Commit 35d5f7c closes #1234") # => "Commit 35d5f7c closes #1234" - class Markdown - include Rails.application.routes.url_helpers - include ActionView::Helpers - + # + # >> gfm(":trollface:") + # => " + module Markdown REFERENCE_PATTERN = %r{ ([^\w&;])? # Prefix (1) ( # Reference (2) @@ -33,15 +35,52 @@ module Gitlab ([^\w&;])? # Suffix (6) }x.freeze + EMOJI_PATTERN = %r{(:([\w\-\+]+):)}.freeze + attr_reader :html_options - def initialize(project, html_options = {}) - @project = project + # Public: Parse the provided text with GitLab-Flavored Markdown + # + # text - the source text + # html_options - extra options for the reference links as given to link_to + # + # Note: reference links will only be generated if @project is set + def gfm(text, html_options = {}) + return text if text.nil? + return text if @project.nil? + @html_options = html_options + + # Extract pre blocks so they are not altered + # from http://github.github.com/github-flavored-markdown/ + extractions = {} + text.gsub!(%r{
.*?|
.*?
}m) do |match|
+ md5 = Digest::MD5.hexdigest(match)
+ extractions[md5] = match
+ "{gfm-extraction-#{md5}}"
+ end
+
+ # TODO: add popups with additional information
+
+ text = parse(text)
+
+ # Insert pre block extractions
+ text.gsub!(/\{gfm-extraction-(\h{32})\}/) do
+ extractions[$1]
+ end
+
+ sanitize text.html_safe, attributes: ActionView::Base.sanitized_allowed_attributes + %w(id class)
end
+ private
+
+ # Private: Parses text for references and emoji
+ #
+ # text - Text to parse
+ #
+ # Returns parsed text
def parse(text)
- text.gsub(REFERENCE_PATTERN) do |match|
+ text = text.gsub(REFERENCE_PATTERN) do |match|
prefix = $1 || ''
reference = $2
identifier = $3 || $4 || $5
@@ -53,9 +92,26 @@ module Gitlab
match
end
end
+
+ text = text.gsub(EMOJI_PATTERN) do |match|
+ if valid_emoji?($2)
+ helper.image_tag("#{$2}.png", class: 'emoji', title: $1, alt: $1)
+ else
+ match
+ end
+ end
+
+ text
end
- private
+ # Private: Checks if an emoji icon exists in the image asset directory
+ #
+ # emoji - Identifier of the emoji as a string (e.g., "+1", "heart")
+ #
+ # Returns boolean
+ def valid_emoji?(emoji)
+ File.exists?(Rails.root.join('app', 'assets', 'images', 'emoji', "#{emoji}.png"))
+ end
# Private: Dispatches to a dedicated processing method based on reference
#