mirror of
https://github.com/discourse/discourse.git
synced 2024-11-24 05:50:14 +08:00
f91631b625
As of ba3f62f576
, handlebars templates are colocated with js files so the path to hbs templates referenced by this rake task is no longer valid. This commit fixes the path to hbs templates and updates a couple of files that are generated by the rake task.
301 lines
7.5 KiB
Ruby
301 lines
7.5 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
class Emoji
|
|
# update this to clear the cache
|
|
EMOJI_VERSION = "12"
|
|
|
|
FITZPATRICK_SCALE ||= %w[1f3fb 1f3fc 1f3fd 1f3fe 1f3ff]
|
|
|
|
DEFAULT_GROUP ||= "default"
|
|
|
|
include ActiveModel::SerializerSupport
|
|
|
|
attr_accessor :name, :url, :tonable, :group, :search_aliases
|
|
|
|
def self.global_emoji_cache
|
|
@global_emoji_cache ||= DistributedCache.new("global_emoji_cache", namespace: false)
|
|
end
|
|
|
|
def self.site_emoji_cache
|
|
@site_emoji_cache ||= DistributedCache.new("site_emoji_cache")
|
|
end
|
|
|
|
def self.all
|
|
Discourse.cache.fetch(cache_key("all_emojis")) { standard | custom }
|
|
end
|
|
|
|
def self.standard
|
|
Discourse.cache.fetch(cache_key("standard_emojis")) { load_standard }
|
|
end
|
|
|
|
def self.aliases
|
|
db["aliases"]
|
|
end
|
|
|
|
def self.search_aliases
|
|
db["searchAliases"]
|
|
end
|
|
|
|
def self.translations
|
|
Discourse.cache.fetch(cache_key("translations_emojis")) { load_translations }
|
|
end
|
|
|
|
def self.custom
|
|
Discourse.cache.fetch(cache_key("custom_emojis")) { load_custom }
|
|
end
|
|
|
|
def self.tonable_emojis
|
|
db["tonableEmojis"]
|
|
end
|
|
|
|
def self.custom?(name)
|
|
name = name.delete_prefix(":").delete_suffix(":")
|
|
Emoji.custom.detect { |e| e.name == name }.present?
|
|
end
|
|
|
|
def self.exists?(name)
|
|
Emoji[name].present?
|
|
end
|
|
|
|
def self.[](name)
|
|
name = name.delete_prefix(":").delete_suffix(":")
|
|
is_toned = name.match?(/.+:t[1-6]/)
|
|
normalized_name = name.gsub(/(.+):t[1-6]/, '\1')
|
|
|
|
found_emoji = nil
|
|
|
|
[[global_emoji_cache, :standard], [site_emoji_cache, :custom]].each do |cache, list_key|
|
|
cache_postfix, found_emoji =
|
|
cache.defer_get_set(normalized_name) do
|
|
emoji =
|
|
Emoji
|
|
.public_send(list_key)
|
|
.detect { |e| e.name == normalized_name && (!is_toned || (is_toned && e.tonable)) }
|
|
[self.cache_postfix, emoji]
|
|
end
|
|
|
|
if found_emoji && (cache_postfix != self.cache_postfix)
|
|
cache.delete(normalized_name)
|
|
redo
|
|
end
|
|
|
|
break if found_emoji
|
|
end
|
|
|
|
found_emoji
|
|
end
|
|
|
|
def self.create_from_db_item(emoji)
|
|
name = emoji["name"]
|
|
return unless group = groups[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)
|
|
e.group = group
|
|
e.search_aliases = search_aliases[name] || []
|
|
end
|
|
end
|
|
|
|
def self.url_for(name)
|
|
name = name.delete_prefix(":").delete_suffix(":").gsub(/(.+):t([1-6])/, '\1/\2')
|
|
if SiteSetting.external_emoji_url.blank?
|
|
"#{Discourse.base_path}/images/emoji/#{SiteSetting.emoji_set}/#{name}.png?v=#{EMOJI_VERSION}"
|
|
else
|
|
"#{SiteSetting.external_emoji_url}/#{SiteSetting.emoji_set}/#{name}.png?v=#{EMOJI_VERSION}"
|
|
end
|
|
end
|
|
|
|
def self.cache_key(name)
|
|
"#{name}#{cache_postfix}"
|
|
end
|
|
|
|
def self.cache_postfix
|
|
":v#{EMOJI_VERSION}:#{Plugin::CustomEmoji.cache_key}"
|
|
end
|
|
|
|
def self.clear_cache
|
|
%w[custom standard translations all].each do |key|
|
|
Discourse.cache.delete(cache_key("#{key}_emojis"))
|
|
end
|
|
global_emoji_cache.clear
|
|
site_emoji_cache.clear
|
|
end
|
|
|
|
def self.groups_file
|
|
@groups_file ||= "#{Rails.root}/lib/emoji/groups.json"
|
|
end
|
|
|
|
def self.groups
|
|
@groups ||=
|
|
begin
|
|
groups = {}
|
|
|
|
File
|
|
.open(groups_file, "r:UTF-8") { |f| JSON.parse(f.read) }
|
|
.each { |group| group["icons"].each { |icon| groups[icon["name"]] = group["name"] } }
|
|
|
|
groups
|
|
end
|
|
end
|
|
|
|
def self.db_file
|
|
@db_file ||= "#{Rails.root}/lib/emoji/db.json"
|
|
end
|
|
|
|
def self.db
|
|
@db ||= File.open(db_file, "r:UTF-8") { |f| JSON.parse(f.read) }
|
|
end
|
|
|
|
def self.load_standard
|
|
db["emojis"].map { |e| Emoji.create_from_db_item(e) }.compact
|
|
end
|
|
|
|
def self.load_custom
|
|
result = []
|
|
|
|
if !GlobalSetting.skip_db?
|
|
CustomEmoji
|
|
.includes(:upload)
|
|
.order(:name)
|
|
.each do |emoji|
|
|
result << Emoji.new.tap do |e|
|
|
e.name = emoji.name
|
|
e.url = emoji.upload&.url
|
|
e.group = emoji.group || DEFAULT_GROUP
|
|
end
|
|
end
|
|
end
|
|
|
|
Plugin::CustomEmoji.emojis.each do |group, emojis|
|
|
emojis.each do |name, url|
|
|
result << Emoji.new.tap do |e|
|
|
e.name = name
|
|
url = (Discourse.base_path + url) if url[%r{\A/[^/]}]
|
|
e.url = url
|
|
e.group = group || DEFAULT_GROUP
|
|
end
|
|
end
|
|
end
|
|
|
|
result
|
|
end
|
|
|
|
def self.load_translations
|
|
db["translations"]
|
|
end
|
|
|
|
def self.base_directory
|
|
"public#{base_url}"
|
|
end
|
|
|
|
def self.base_url
|
|
db = RailsMultisite::ConnectionManagement.current_db
|
|
"#{Discourse.base_path}/uploads/#{db}/_emoji"
|
|
end
|
|
|
|
def self.replacement_code(code)
|
|
code.split("-").map!(&:hex).pack("U*")
|
|
end
|
|
|
|
def self.unicode_replacements
|
|
@unicode_replacements ||=
|
|
begin
|
|
replacements = {}
|
|
is_tonable_emojis = Emoji.tonable_emojis
|
|
fitzpatrick_scales = FITZPATRICK_SCALE.map { |scale| scale.to_i(16) }
|
|
|
|
db["emojis"].each do |e|
|
|
name = e["name"]
|
|
|
|
# special cased as we prefer to keep these as symbols
|
|
next if name == "registered"
|
|
next if name == "copyright"
|
|
next if name == "tm"
|
|
next if name == "left_right_arrow"
|
|
|
|
code = replacement_code(e["code"])
|
|
next unless code
|
|
|
|
replacements[code] = name
|
|
if is_tonable_emojis.include?(name)
|
|
fitzpatrick_scales.each_with_index do |scale, index|
|
|
toned_code = code.codepoints.insert(1, scale).pack("U*")
|
|
replacements[toned_code] = "#{name}:t#{index + 2}"
|
|
end
|
|
end
|
|
end
|
|
|
|
replacements["\u{2639}"] = "frowning"
|
|
replacements["\u{263B}"] = "slight_smile"
|
|
replacements["\u{2661}"] = "heart"
|
|
replacements["\u{2665}"] = "heart"
|
|
|
|
replacements
|
|
end
|
|
end
|
|
|
|
def self.unicode_unescape(string)
|
|
PrettyText.escape_emoji(string)
|
|
end
|
|
|
|
def self.gsub_emoji_to_unicode(str)
|
|
str.gsub(/:([\w\-+]*(?::t\d)?):/) { |name| Emoji.lookup_unicode($1) || name } if str
|
|
end
|
|
|
|
def self.lookup_unicode(name)
|
|
@reverse_map ||=
|
|
begin
|
|
map = {}
|
|
is_tonable_emojis = Emoji.tonable_emojis
|
|
|
|
db["emojis"].each do |e|
|
|
next if e["name"] == "tm"
|
|
|
|
code = replacement_code(e["code"])
|
|
next unless code
|
|
|
|
map[e["name"]] = code
|
|
if is_tonable_emojis.include?(e["name"])
|
|
FITZPATRICK_SCALE.each_with_index do |scale, index|
|
|
toned_code = (code.codepoints.insert(1, scale.to_i(16))).pack("U*")
|
|
map["#{e["name"]}:t#{index + 2}"] = toned_code
|
|
end
|
|
end
|
|
end
|
|
|
|
Emoji.aliases.each do |key, alias_names|
|
|
next unless alias_code = map[key]
|
|
alias_names.each { |alias_name| map[alias_name] = alias_code }
|
|
end
|
|
|
|
map
|
|
end
|
|
@reverse_map[name]
|
|
end
|
|
|
|
def self.unicode_replacements_json
|
|
@unicode_replacements_json ||= unicode_replacements.to_json
|
|
end
|
|
|
|
def self.codes_to_img(str)
|
|
return if str.blank?
|
|
|
|
str =
|
|
str.gsub(/:([\w\-+]*(?::t\d)?):/) do |name|
|
|
code = $1
|
|
|
|
if code && Emoji.custom?(code)
|
|
emoji = Emoji[code]
|
|
"<img src=\"#{emoji.url}\" title=\"#{code}\" class=\"emoji\" alt=\"#{code}\" loading=\"lazy\" width=\"20\" height=\"20\">"
|
|
elsif code && Emoji.exists?(code)
|
|
"<img src=\"#{Emoji.url_for(code)}\" title=\"#{code}\" class=\"emoji\" alt=\"#{code}\" loading=\"lazy\" width=\"20\" height=\"20\">"
|
|
else
|
|
name
|
|
end
|
|
end
|
|
end
|
|
end
|