FIX: provides an emoji helper to replace codes by images (#7802)

FIX: provides an emoji helper to replace codes by images (#7802)

diff --git a/app/helpers/emoji_helper.rb b/app/helpers/emoji_helper.rb
new file mode 100644
index 0000000..f1f13b1
--- /dev/null
+++ b/app/helpers/emoji_helper.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+module EmojiHelper
+  def emoji_codes_to_img(str)
+    return if str.blank?
+
+    str = str.gsub(/:([\w\-+]*(?::t\d)?):/) do |name|
+      code = $1
+
+      if Emoji.exists?(code)
+        "<img src=\"#{Emoji.url_for(code)}\" title=\"#{code}\" class=\"emoji\" alt=\"#{code}\">"
+      else
+        name
+      end
+    end
+
+    raw(str)
+  end
+end
diff --git a/app/models/emoji.rb b/app/models/emoji.rb
index e6aa723..22076c0 100644
--- a/app/models/emoji.rb
+++ b/app/models/emoji.rb
@@ -8,12 +8,7 @@ class Emoji
 
   include ActiveModel::SerializerSupport
 
-  attr_reader :path
-  attr_accessor :name, :url
-
-  def initialize(path = nil)
-    @path = path
-  end
+  attr_accessor :name, :url, :tonable
 
   def self.all
     Discourse.cache.fetch(cache_key("all_emojis")) { standard | custom }
@@ -48,19 +43,29 @@ class Emoji
   end
 
   def self.[](name)
-    Emoji.custom.detect { |e| e.name == name }
+    name = name.delete_prefix(':').delete_suffix(':')
+    is_toned = name.match?(/.+:t[1-6]/)
+    normalized_name = name.gsub(/(.+):t[1-6]/, '\1')
+
+    Emoji.all.detect do |e|
+      e.name == normalized_name &&
+      (!is_toned || (is_toned && e.tonable))
+    end
   end
 
   def self.create_from_db_item(emoji)
     name = emoji["name"]
     filename = emoji['filename'] || name
+
     Emoji.new.tap do |e|
       e.name = name
+      e.tonable = Emoji.tonable_emojis.include?(name)
       e.url = Emoji.url_for(filename)
     end
   end
 
   def self.url_for(name)
+    name = name.delete_prefix(':').delete_suffix(':').gsub(/(.+):t([1-6])/, '\1/\2')
     "#{Discourse.base_uri}/images/emoji/#{SiteSetting.emoji_set}/#{name}.png?v=#{EMOJI_VERSION}"
   end
 
diff --git a/app/views/exceptions/_not_found_topics.html.erb b/app/views/exceptions/_not_found_topics.html.erb
index 59cf088..2e6c107 100644
--- a/app/views/exceptions/_not_found_topics.html.erb
+++ b/app/views/exceptions/_not_found_topics.html.erb
@@ -3,7 +3,7 @@
     <h2 class="popular-topics-title"><%= t 'page_not_found.popular_topics' %></h2>
     <% @top_viewed.each do |t| %>
       <div class='not-found-topic'>
-        <%= link_to t.title, t.relative_url %><%= category_badge(t.category) %>
+        <%= link_to emoji_codes_to_img(t.fancy_title), t.relative_url %><%= category_badge(t.category) %>
       </div>
     <% end %>
     <a href="<%= path "/top" %>" class="btn btn-default"><%= t 'page_not_found.see_more' %>&hellip;</a>
diff --git a/spec/helpers/emoji_helper_spec.rb b/spec/helpers/emoji_helper_spec.rb
new file mode 100644
index 0000000..7d7fa32
--- /dev/null
+++ b/spec/helpers/emoji_helper_spec.rb
@@ -0,0 +1,24 @@
+# coding: utf-8
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe EmojiHelper do
+
+  describe "emoji_codes_to_img" do
+    it "replaces emoji codes by images" do
+      str = "This is a good day :woman: :man:t4:"
+      replaced_str = helper.emoji_codes_to_img(str)
+
+      expect(replaced_str).to eq("This is a good day <img src=\"/images/emoji/twitter/woman.png?v=#{Emoji::EMOJI_VERSION}\" title=\"woman\" class=\"emoji\" alt=\"woman\"> <img src=\"/images/emoji/twitter/man/4.png?v=#{Emoji::EMOJI_VERSION}\" title=\"man:t4\" class=\"emoji\" alt=\"man:t4\">")
+    end
+
+    it "doesn't replace if code doesn't exist" do
+      str = "This is a good day :woman: :foo: :bar:t4: :man:t8:"
+      replaced_str = helper.emoji_codes_to_img(str)
+
+      expect(replaced_str).to eq("This is a good day <img src=\"/images/emoji/twitter/woman.png?v=#{Emoji::EMOJI_VERSION}\" title=\"woman\" class=\"emoji\" alt=\"woman\"> :foo: :bar:t4: :man:t8:")
+    end
+  end
+
+end
diff --git a/spec/models/emoji_spec.rb b/spec/models/emoji_spec.rb
index 1628324..c36f316 100644
--- a/spec/models/emoji_spec.rb
+++ b/spec/models/emoji_spec.rb
@@ -44,4 +44,52 @@ describe Emoji do
     end
   end
 
+  describe '.url_for' do
+    expected_url = "/images/emoji/twitter/blonde_woman.png?v=#{Emoji::EMOJI_VERSION}"
+    expected_toned_url = "/images/emoji/twitter/blonde_woman/6.png?v=#{Emoji::EMOJI_VERSION}"
+
+    it 'should return url with filename' do
+      expect(Emoji.url_for("blonde_woman")).to eq(expected_url)
+    end
+
+    it 'should return url with skin toned filename' do
+      expect(Emoji.url_for("blonde_woman/6")).to eq(expected_toned_url)
+    end
+
+    it 'should return url with code' do
+      expect(Emoji.url_for(":blonde_woman:")).to eq(expected_url)
+    end
+
+    it 'should return url with skin toned code' do
+      expect(Emoji.url_for(":blonde_woman:t6:")).to eq(expected_toned_url)
+      expect(Emoji.url_for("blonde_woman:t6")).to eq(expected_toned_url)
+    end
+  end
+
+  describe '.exists?' do
+    it 'finds existing emoji' do
+      expect(Emoji.exists?(":blonde_woman:")).to be(true)
+      expect(Emoji.exists?("blonde_woman")).to be(true)
+    end
+
+    it 'finds existing skin toned emoji' do
+      expect(Emoji.exists?(":blonde_woman:t1:")).to be(true)
+      expect(Emoji.exists?("blonde_woman:t6")).to be(true)
+    end
+
+    it 'finds existing custom emoji' do
+      CustomEmoji.create!(name: 'test', upload_id: 9999)
+      Emoji.clear_cache
+      expect(Emoji.exists?(":test:")).to be(true)
+      expect(Emoji.exists?("test")).to be(true)
+    end
+
+    it 'doesn’t find non-existing emoji' do
+      expect(Emoji.exists?(":foo-bar:")).to be(false)
+      expect(Emoji.exists?(":blonde_woman:t7:")).to be(false)
+      expect(Emoji.exists?("blonde_woman:t0")).to be(false)
+      expect(Emoji.exists?("blonde_woman:t")).to be(false)
+    end
+  end
+
 end

GitHub sha: dfde59f6

1 Like

This commit has been mentioned on Discourse Meta. There might be relevant details there: