FIX: allows to have custom emoji translation without static file (#9893)
This commit is contained in:
parent
207b72ade1
commit
77801aa9be
|
@ -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])?$/);
|
||||
|
|
|
@ -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
|
||||
},
|
||||
|
|
|
@ -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]}:`;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
)
|
||||
)
|
||||
);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -126,7 +126,7 @@ class Emoji
|
|||
end
|
||||
|
||||
def self.load_translations
|
||||
db["translations"].merge(Plugin::CustomEmoji.translations)
|
||||
db["translations"]
|
||||
end
|
||||
|
||||
def self.base_directory
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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>")
|
||||
|
|
Loading…
Reference in New Issue