Commit 4f8a4aa4 authored by Dmitriy Zaporozhets's avatar Dmitriy Zaporozhets

Merge pull request #1384 from tsigo/emoji

Emoji!
parents 7c0a1068 2faa4bba
module GitlabMarkdownHelper
# Replaces references (i.e. @abc, #123, !456, ...) in the text with links to
# the appropriate items in GitLab.
#
# 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
#
# see Gitlab::Markdown for details on the supported syntax
def gfm(text, html_options = {})
return text if text.nil?
return text if @project.nil?
# Extract pre blocks so they are not altered
# from http://github.github.com/github-flavored-markdown/
extractions = {}
text.gsub!(%r{<pre>.*?</pre>|<code>.*?</code>}m) do |match|
md5 = Digest::MD5.hexdigest(match)
extractions[md5] = match
"{gfm-extraction-#{md5}}"
end
# TODO: add popups with additional information
parser = Gitlab::Markdown.new(@project, html_options)
text = parser.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
include Gitlab::Markdown
# Use this in places where you would normally use link_to(gfm(...), ...).
#
......
......@@ -103,7 +103,7 @@ class Note < ActiveRecord::Base
# Returns true if this is an upvote note,
# otherwise false is returned
def upvote?
note =~ /^\+1/ ? true : false
note.start_with?('+1') || note.start_with?(':+1:')
end
end
# == Schema Information
......
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
#
# Examples
# It also parses Emoji codes to insert images. See
# http://www.emoji-cheat-sheet.com/ for a list of the supported icons.
#
# >> m = Markdown.new(...)
# Examples
#
# >> m.parse("Hey @david, can you fix this?")
# >> gfm("Hey @david, can you fix this?")
# => "Hey <a href="/gitlab/team_members/1">@david</a>, can you fix this?"
#
# >> m.parse("Commit 35d5f7c closes #1234")
# >> gfm("Commit 35d5f7c closes #1234")
# => "Commit <a href="/gitlab/commits/35d5f7c">35d5f7c</a> closes <a href="/gitlab/issues/1234">#1234</a>"
class Markdown
include Rails.application.routes.url_helpers
include ActionView::Helpers
#
# >> gfm(":trollface:")
# => "<img alt=\":trollface:\" class=\"emoji\" src=\"/images/trollface.png" title=\":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{(:(\S+):)}.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{<pre>.*?</pre>|<code>.*?</code>}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", size: "20x20", 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
#
......
......@@ -208,6 +208,46 @@ describe GitlabMarkdownHelper do
gfm(actual).should match(expected)
end
end
describe "emoji" do
it "matches at the start of a string" do
gfm(":+1:").should match(/<img/)
end
it "matches at the end of a string" do
gfm("This gets a :-1:").should match(/<img/)
end
it "matches with adjacent text" do
gfm("+1 (:+1:)").should match(/<img/)
end
it "has a title attribute" do
gfm(":-1:").should match(/title=":-1:"/)
end
it "has an alt attribute" do
gfm(":-1:").should match(/alt=":-1:"/)
end
it "has an emoji class" do
gfm(":+1:").should match('class="emoji"')
end
it "sets height and width" do
actual = gfm(":+1:")
actual.should match(/width="20"/)
actual.should match(/height="20"/)
end
it "keeps whitespace intact" do
gfm("This deserves a :+1: big time.").should match(/deserves a <img.+\/> big time/)
end
it "ignores invalid emoji" do
gfm(":invalid-emoji:").should_not match(/<img/)
end
end
end
describe "#link_to_gfm" do
......
......@@ -35,6 +35,16 @@ describe Note do
note = Factory(:note, note: "-1 for this")
note.should_not be_upvote
end
it "recognizes a +1 emoji as a vote" do
note = build(:note, note: ":+1: for this")
note.should be_upvote
end
it "recognizes a neutral emoji note" do
note = build(:note, note: "I would :+1: this, but I don't want to")
note.should_not be_upvote
end
end
let(:project) { create(:project) }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment