FIX: allows to have custom emoji translation without static file (#9893)

This commit is contained in:
Joffrey JAFFEUX 2020-05-27 20:11:52 +02:00 committed by GitHub
parent 207b72ade1
commit 77801aa9be
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 94 additions and 17 deletions

View File

@ -483,8 +483,15 @@ export default Component.extend({
}
}
if (translations[full]) {
return resolve([translations[full]]);
// note this will only work for emojis starting with :
// eg: :-)
const allTranslations = Object.assign(
{},
translations,
this.getWithDefault("site.custom_emoji_translation", {})
);
if (allTranslations[full]) {
return resolve([allTranslations[full]]);
}
const match = term.match(/^:?(.*?):t([2-6])?$/);

View File

@ -18,6 +18,7 @@ function getOpts(opts) {
getURL: getURLWithCDN,
currentUser: Discourse.__container__.lookup("current-user:main"),
censoredRegexp: site.censored_regexp,
customEmojiTranslation: site.custom_emoji_translation,
siteSettings,
formatUsername
},

View File

@ -98,13 +98,18 @@ export function performEmojiUnescape(string, opts) {
const inlineEmoji = opts.inlineEmoji;
const regexp = unicodeRegexp(inlineEmoji);
const allTranslations = Object.assign(
{},
translations,
opts.customEmojiTranslation || {}
);
return string.replace(regexp, (m, index) => {
const isEmoticon = opts.enableEmojiShortcuts && !!translations[m];
const isEmoticon = opts.enableEmojiShortcuts && !!allTranslations[m];
const isUnicodeEmoticon = !!replacements[m];
let emojiVal;
if (isEmoticon) {
emojiVal = translations[m];
emojiVal = allTranslations[m];
} else if (isUnicodeEmoticon) {
emojiVal = replacements[m];
} else {
@ -131,11 +136,16 @@ export function performEmojiUnescape(string, opts) {
export function performEmojiEscape(string, opts) {
const inlineEmoji = opts.inlineEmoji;
const regexp = unicodeRegexp(inlineEmoji);
const allTranslations = Object.assign(
{},
translations,
opts.customEmojiTranslation || {}
);
return string.replace(regexp, (m, index) => {
if (isReplacableInlineEmoji(string, index, inlineEmoji)) {
if (!!translations[m]) {
return opts.emojiShortcuts ? `:${translations[m]}:` : m;
if (!!allTranslations[m]) {
return opts.emojiShortcuts ? `:${allTranslations[m]}:` : m;
} else if (!!replacements[m]) {
return `:${replacements[m]}:`;
}

View File

@ -5,15 +5,25 @@ const MAX_NAME_LENGTH = 60;
let translationTree = null;
export function resetTranslationTree() {
translationTree = null;
}
// This allows us to efficiently search for aliases
// We build a data structure that allows us to quickly
// search through our N next chars to see if any match
// one of our alias emojis.
function buildTranslationTree() {
function buildTranslationTree(customEmojiTranslation) {
let tree = [];
let lastNode;
Object.keys(translations).forEach(key => {
const allTranslations = Object.assign(
{},
translations,
customEmojiTranslation || {}
);
Object.keys(allTranslations).forEach(key => {
let node = tree;
for (let i = 0; i < key.length; i++) {
@ -37,7 +47,7 @@ function buildTranslationTree() {
}
}
lastNode[2] = translations[key];
lastNode[2] = allTranslations[key];
});
return tree;
@ -114,8 +124,14 @@ function getEmojiTokenByName(name, state) {
}
}
function getEmojiTokenByTranslation(content, pos, state) {
translationTree = translationTree || buildTranslationTree();
function getEmojiTokenByTranslation(
content,
pos,
state,
customEmojiTranslation
) {
translationTree =
translationTree || buildTranslationTree(customEmojiTranslation);
let t = translationTree;
let start = pos;
@ -175,7 +191,8 @@ function applyEmoji(
state,
emojiUnicodeReplacer,
enableShortcuts,
inlineEmoji
inlineEmoji,
customEmojiTranslation
) {
let result = null;
let start = 0;
@ -201,7 +218,12 @@ function applyEmoji(
if (enableShortcuts && !token) {
// handle aliases (note: we can't do this in inline cause ; is not a split point)
const info = getEmojiTokenByTranslation(content, i, state);
const info = getEmojiTokenByTranslation(
content,
i,
state,
customEmojiTranslation
);
if (info) {
offset = info.pos - i;
@ -310,7 +332,8 @@ export function setup(helper) {
s,
md.options.discourse.emojiUnicodeReplacer,
md.options.discourse.features.emojiShortcuts,
md.options.discourse.features.inlineEmoji
md.options.discourse.features.inlineEmoji,
md.options.discourse.customEmojiTranslation
)
)
);

View File

@ -30,7 +30,8 @@ export function buildOptions(state) {
previewing,
linkify,
censoredRegexp,
disableEmojis
disableEmojis,
customEmojiTranslation
} = state;
let features = {
@ -68,6 +69,7 @@ export function buildOptions(state) {
emojiUnicodeReplacer,
lookupUploadUrls,
censoredRegexp,
customEmojiTranslation,
allowedHrefSchemes: siteSettings.allowed_href_schemes
? siteSettings.allowed_href_schemes.split("|")
: null,

View File

@ -126,7 +126,7 @@ class Emoji
end
def self.load_translations
db["translations"].merge(Plugin::CustomEmoji.translations)
db["translations"]
end
def self.base_directory

View File

@ -26,7 +26,8 @@ class SiteSerializer < ApplicationSerializer
:topic_featured_link_allowed_category_ids,
:user_themes,
:censored_regexp,
:shared_drafts_category_id
:shared_drafts_category_id,
:custom_emoji_translation
)
has_many :categories, serializer: SiteCategorySerializer, embed: :objects
@ -154,6 +155,10 @@ class SiteSerializer < ApplicationSerializer
WordWatcher.word_matcher_regexp(:censor)&.source
end
def custom_emoji_translation
Plugin::CustomEmoji.translations
end
def shared_drafts_category_id
SiteSetting.shared_drafts_category.to_i
end

View File

@ -18,6 +18,7 @@ class Plugin::CustomEmoji
def self.clear_cache
@@cache_key = CACHE_KEY
@@emojis = {}
@@translations = {}
end
def self.register(name, url, group = Emoji::DEFAULT_GROUP)

View File

@ -126,6 +126,10 @@ module PrettyText
@ctx
end
def self.reset_translations
v8.eval("__resetTranslationTree()")
end
def self.reset_context
@ctx_init.synchronize do
@ctx&.dispose
@ -159,6 +163,7 @@ module PrettyText
__optInput.getTopicInfo = __getTopicInfo;
__optInput.categoryHashtagLookup = __categoryLookup;
__optInput.customEmoji = #{custom_emoji.to_json};
__optInput.customEmojiTranslation = #{Plugin::CustomEmoji.translations.to_json};
__optInput.emojiUnicodeReplacer = __emojiUnicodeReplacer;
__optInput.lookupUploadUrls = __lookupUploadUrls;
__optInput.censoredRegexp = #{WordWatcher.word_matcher_regexp(:censor)&.source.to_json};

View File

@ -3,6 +3,8 @@ __buildOptions = require("pretty-text/pretty-text").buildOptions;
__performEmojiUnescape = require("pretty-text/emoji").performEmojiUnescape;
__buildReplacementsList = require("pretty-text/emoji").buildReplacementsList;
__performEmojiEscape = require("pretty-text/emoji").performEmojiEscape;
__resetTranslationTree = require("pretty-text/engines/discourse-markdown/emoji")
.resetTranslationTree;
I18n = {
t(a, b) {

View File

@ -1040,6 +1040,27 @@ describe PrettyText do
end
end
describe "custom emoji translation" do
before do
PrettyText.reset_translations
SiteSetting.enable_emoji = true
SiteSetting.enable_emoji_shortcuts = true
plugin = Plugin::Instance.new
plugin.translate_emoji "0:)", "otter"
end
after do
Plugin::CustomEmoji.clear_cache
PrettyText.reset_translations
end
it "sets the custom translation" do
expect(PrettyText.cook("hello 0:)")).to match(/otter/)
end
end
it "replaces skin toned emoji" do
expect(PrettyText.cook("hello 👱🏿‍♀️")).to eq("<p>hello <img src=\"/images/emoji/twitter/blonde_woman/6.png?v=#{Emoji::EMOJI_VERSION}\" title=\":blonde_woman:t6:\" class=\"emoji\" alt=\":blonde_woman:t6:\"></p>")
expect(PrettyText.cook("hello 👩‍🎤")).to eq("<p>hello <img src=\"/images/emoji/twitter/woman_singer.png?v=#{Emoji::EMOJI_VERSION}\" title=\":woman_singer:\" class=\"emoji\" alt=\":woman_singer:\"></p>")