FIX: Respect `enable_inline_emoji_translation` setting in titles
This commit is contained in:
parent
86b81b5f63
commit
9ebb69e8eb
|
@ -68,7 +68,8 @@ function emojiOptions() {
|
|||
return {
|
||||
getURL: Discourse.getURLWithCDN,
|
||||
emojiSet: Discourse.SiteSettings.emoji_set,
|
||||
enableEmojiShortcuts: Discourse.SiteSettings.enable_emoji_shortcuts
|
||||
enableEmojiShortcuts: Discourse.SiteSettings.enable_emoji_shortcuts,
|
||||
inlineEmoji: Discourse.SiteSettings.enable_inline_emoji_translation
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -42,10 +42,31 @@ export function buildReplacementsList(emojiReplacements) {
|
|||
.join("|");
|
||||
}
|
||||
|
||||
const unicodeRegexp = new RegExp(
|
||||
buildReplacementsList(replacements) + "|\\B:[^\\s:]+(?::t\\d)?:?\\B",
|
||||
"g"
|
||||
);
|
||||
let replacementListCache;
|
||||
const unicodeRegexpCache = {};
|
||||
|
||||
function replacementList() {
|
||||
if (replacementListCache === undefined) {
|
||||
replacementListCache = buildReplacementsList(replacements);
|
||||
}
|
||||
|
||||
return replacementListCache;
|
||||
}
|
||||
|
||||
function unicodeRegexp(inlineEmoji) {
|
||||
if (unicodeRegexpCache[inlineEmoji] === undefined) {
|
||||
const emojiExpression = inlineEmoji
|
||||
? "|:[^\\s:]+(?::t\\d)?:?"
|
||||
: "|\\B:[^\\s:]+(?::t\\d)?:?\\B";
|
||||
|
||||
unicodeRegexpCache[inlineEmoji] = new RegExp(
|
||||
replacementList() + emojiExpression,
|
||||
"g"
|
||||
);
|
||||
}
|
||||
|
||||
return unicodeRegexpCache[inlineEmoji];
|
||||
}
|
||||
|
||||
// add all default emojis
|
||||
emojis.forEach(code => (emojiHash[code] = true));
|
||||
|
@ -56,12 +77,29 @@ Object.keys(aliases).forEach(name => {
|
|||
aliases[name].forEach(alias => (aliasHash[alias] = name));
|
||||
});
|
||||
|
||||
function isReplacableInlineEmoji(string, index, inlineEmoji) {
|
||||
if (inlineEmoji) return true;
|
||||
|
||||
// index depends on regex; when `inlineEmoji` is false, the regex starts
|
||||
// with a `\B` character, so there's no need to subtract from the index
|
||||
const beforeEmoji = string.slice(0, index - (inlineEmoji ? 1 : 0));
|
||||
|
||||
return (
|
||||
beforeEmoji.length === 0 ||
|
||||
/(?:\s|[>.,\/#!$%^&*;:{}=\-_`~()])$/.test(beforeEmoji) ||
|
||||
new RegExp(`(?:${replacementList()})$`).test(beforeEmoji)
|
||||
);
|
||||
}
|
||||
|
||||
export function performEmojiUnescape(string, opts) {
|
||||
if (!string) {
|
||||
return;
|
||||
}
|
||||
|
||||
return string.replace(unicodeRegexp, m => {
|
||||
const inlineEmoji = opts.inlineEmoji;
|
||||
const regexp = unicodeRegexp(inlineEmoji);
|
||||
|
||||
return string.replace(regexp, (m, index) => {
|
||||
const isEmoticon = opts.enableEmojiShortcuts && !!translations[m];
|
||||
const isUnicodeEmoticon = !!replacements[m];
|
||||
let emojiVal;
|
||||
|
@ -78,7 +116,11 @@ export function performEmojiUnescape(string, opts) {
|
|||
? "emoji emoji-custom"
|
||||
: "emoji";
|
||||
|
||||
return url && (isEmoticon || hasEndingColon || isUnicodeEmoticon)
|
||||
const isReplacable =
|
||||
(isEmoticon || hasEndingColon || isUnicodeEmoticon) &&
|
||||
isReplacableInlineEmoji(string, index, inlineEmoji);
|
||||
|
||||
return url && isReplacable
|
||||
? `<img src='${url}' ${
|
||||
opts.skipTitle ? "" : `title='${emojiVal}'`
|
||||
} alt='${emojiVal}' class='${classes}'>`
|
||||
|
@ -89,14 +131,19 @@ export function performEmojiUnescape(string, opts) {
|
|||
}
|
||||
|
||||
export function performEmojiEscape(string, opts) {
|
||||
return string.replace(unicodeRegexp, m => {
|
||||
if (!!translations[m]) {
|
||||
return opts.emojiShortcuts ? `:${translations[m]}:` : m;
|
||||
} else if (!!replacements[m]) {
|
||||
return `:${replacements[m]}:`;
|
||||
} else {
|
||||
return m;
|
||||
const inlineEmoji = opts.inlineEmoji;
|
||||
const regexp = unicodeRegexp(inlineEmoji);
|
||||
|
||||
return string.replace(regexp, (m, index) => {
|
||||
if (isReplacableInlineEmoji(string, index, inlineEmoji)) {
|
||||
if (!!translations[m]) {
|
||||
return opts.emojiShortcuts ? `:${translations[m]}:` : m;
|
||||
} else if (!!replacements[m]) {
|
||||
return `:${replacements[m]}:`;
|
||||
}
|
||||
}
|
||||
|
||||
return m;
|
||||
});
|
||||
|
||||
return string;
|
||||
|
|
|
@ -120,7 +120,8 @@ const rule = {
|
|||
title = performEmojiUnescape(topicInfo.title, {
|
||||
getURL: options.getURL,
|
||||
emojiSet: options.emojiSet,
|
||||
enableEmojiShortcuts: options.enableEmojiShortcuts
|
||||
enableEmojiShortcuts: options.enableEmojiShortcuts,
|
||||
inlineEmoji: options.inlineEmoji
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -156,6 +157,7 @@ export function setup(helper) {
|
|||
opts.enableEmoji = siteSettings.enable_emoji;
|
||||
opts.emojiSet = siteSettings.emoji_set;
|
||||
opts.enableEmojiShortcuts = siteSettings.enable_emoji_shortcuts;
|
||||
opts.inlineEmoji = siteSettings.enable_inline_emoji_translation;
|
||||
});
|
||||
|
||||
helper.registerPlugin(md => {
|
||||
|
|
|
@ -218,6 +218,7 @@ module PrettyText
|
|||
|
||||
set = SiteSetting.emoji_set.inspect
|
||||
custom = Emoji.custom.map { |e| [e.name, e.url] }.to_h.to_json
|
||||
|
||||
protect do
|
||||
v8.eval(<<~JS)
|
||||
__paths = #{paths_json};
|
||||
|
@ -225,7 +226,8 @@ module PrettyText
|
|||
getURL: __getURL,
|
||||
emojiSet: #{set},
|
||||
customEmoji: #{custom},
|
||||
enableEmojiShortcuts: #{SiteSetting.enable_emoji_shortcuts}
|
||||
enableEmojiShortcuts: #{SiteSetting.enable_emoji_shortcuts},
|
||||
inlineEmoji: #{SiteSetting.enable_inline_emoji_translation}
|
||||
});
|
||||
JS
|
||||
end
|
||||
|
@ -238,7 +240,10 @@ module PrettyText
|
|||
|
||||
protect do
|
||||
v8.eval(<<~JS)
|
||||
__performEmojiEscape(#{title.inspect}, { emojiShortcuts: #{replace_emoji_shortcuts} });
|
||||
__performEmojiEscape(#{title.inspect}, {
|
||||
emojiShortcuts: #{replace_emoji_shortcuts},
|
||||
inlineEmoji: #{SiteSetting.enable_inline_emoji_translation}
|
||||
});
|
||||
JS
|
||||
end
|
||||
end
|
||||
|
|
|
@ -308,6 +308,7 @@ describe Topic do
|
|||
let(:topic_emoji) { build_topic_with_title("I 💖 candy alot") }
|
||||
let(:topic_modifier_emoji) { build_topic_with_title("I 👨🌾 candy alot") }
|
||||
let(:topic_shortcut_emoji) { build_topic_with_title("I love candy :)") }
|
||||
let(:topic_inline_emoji) { build_topic_with_title("Hello😊World") }
|
||||
|
||||
it "escapes script contents" do
|
||||
expect(topic_script.fancy_title).to eq("Topic with <script>alert(‘title’)</script> script in its title")
|
||||
|
@ -359,6 +360,16 @@ describe Topic do
|
|||
expect(topic_shortcut_emoji.fancy_title).to eq("I love candy :)")
|
||||
end
|
||||
end
|
||||
|
||||
it "keeps inline emojis if inline emoji setting disabled" do
|
||||
SiteSetting.enable_inline_emoji_translation = false
|
||||
expect(topic_inline_emoji.fancy_title).to eq("Hello😊World")
|
||||
end
|
||||
|
||||
it "expands inline emojis if inline emoji setting enabled" do
|
||||
SiteSetting.enable_inline_emoji_translation = true
|
||||
expect(topic_inline_emoji.fancy_title).to eq("Hello:blush:World")
|
||||
end
|
||||
end
|
||||
|
||||
context 'fancy title' do
|
||||
|
|
|
@ -186,6 +186,27 @@ QUnit.test("Updating the topic title with unicode emojis", async assert => {
|
|||
);
|
||||
});
|
||||
|
||||
QUnit.test(
|
||||
"Updating the topic title with unicode emojis without whitespaces",
|
||||
async assert => {
|
||||
Discourse.SiteSettings.enable_inline_emoji_translation = true;
|
||||
await visit("/t/internationalization-localization/280");
|
||||
await click("#topic-title .d-icon-pencil-alt");
|
||||
|
||||
await fillIn("#edit-title", "Test🙂Title");
|
||||
|
||||
await click("#topic-title .submit-edit");
|
||||
|
||||
assert.equal(
|
||||
find(".fancy-title")
|
||||
.html()
|
||||
.trim(),
|
||||
`Test<img src="/images/emoji/emoji_one/slightly_smiling_face.png?v=${v}" title="slightly_smiling_face" alt="slightly_smiling_face" class="emoji">Title`,
|
||||
"it displays the new title with escaped unicode emojis"
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
acceptance("Topic featured links", {
|
||||
loggedIn: true,
|
||||
settings: {
|
||||
|
|
|
@ -93,6 +93,8 @@ Discourse.SiteSettingsOriginal = {
|
|||
enable_emoji: true,
|
||||
enable_emoji_shortcuts: true,
|
||||
emoji_set: "emoji_one",
|
||||
enable_emoji_shortcuts: true,
|
||||
enable_inline_emoji_translation: false,
|
||||
desktop_category_page_style: "categories_and_latest_topics",
|
||||
enable_mentions: true,
|
||||
enable_personal_messages: true,
|
||||
|
|
|
@ -92,6 +92,41 @@ QUnit.test("emojiUnescape", assert => {
|
|||
"no emoticons when emoji shortcuts are disabled",
|
||||
{ enable_emoji_shortcuts: false }
|
||||
);
|
||||
testUnescape(
|
||||
"Hello 😊 World",
|
||||
`Hello <img src='/images/emoji/emoji_one/blush.png?v=${v}' title='blush' alt='blush' class='emoji'> World`,
|
||||
"emoji from Unicode emoji"
|
||||
);
|
||||
testUnescape(
|
||||
"Hello😊World",
|
||||
"Hello😊World",
|
||||
"keeps Unicode emoji when inline translation disabled",
|
||||
{
|
||||
enable_inline_emoji_translation: false
|
||||
}
|
||||
);
|
||||
testUnescape(
|
||||
"Hello😊World",
|
||||
`Hello<img src='/images/emoji/emoji_one/blush.png?v=${v}' title='blush' alt='blush' class='emoji'>World`,
|
||||
"emoji from Unicode emoji when inline translation enabled",
|
||||
{
|
||||
enable_inline_emoji_translation: true
|
||||
}
|
||||
);
|
||||
testUnescape(
|
||||
"hi:smile:",
|
||||
"hi:smile:",
|
||||
"no emojis when inline translation disabled",
|
||||
{
|
||||
enable_inline_emoji_translation: false
|
||||
}
|
||||
);
|
||||
testUnescape(
|
||||
"hi:smile:",
|
||||
`hi<img src='/images/emoji/emoji_one/smile.png?v=${v}' title='smile' alt='smile' class='emoji'>`,
|
||||
"emoji when inline translation enabled",
|
||||
{ enable_inline_emoji_translation: true }
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test("Emoji search", assert => {
|
||||
|
|
Loading…
Reference in New Issue