mirror of
https://github.com/discourse/discourse.git
synced 2024-11-25 09:42:07 +08:00
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 {
|
return {
|
||||||
getURL: Discourse.getURLWithCDN,
|
getURL: Discourse.getURLWithCDN,
|
||||||
emojiSet: Discourse.SiteSettings.emoji_set,
|
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("|");
|
.join("|");
|
||||||
}
|
}
|
||||||
|
|
||||||
const unicodeRegexp = new RegExp(
|
let replacementListCache;
|
||||||
buildReplacementsList(replacements) + "|\\B:[^\\s:]+(?::t\\d)?:?\\B",
|
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"
|
"g"
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return unicodeRegexpCache[inlineEmoji];
|
||||||
|
}
|
||||||
|
|
||||||
// add all default emojis
|
// add all default emojis
|
||||||
emojis.forEach(code => (emojiHash[code] = true));
|
emojis.forEach(code => (emojiHash[code] = true));
|
||||||
|
@ -56,12 +77,29 @@ Object.keys(aliases).forEach(name => {
|
||||||
aliases[name].forEach(alias => (aliasHash[alias] = 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) {
|
export function performEmojiUnescape(string, opts) {
|
||||||
if (!string) {
|
if (!string) {
|
||||||
return;
|
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 isEmoticon = opts.enableEmojiShortcuts && !!translations[m];
|
||||||
const isUnicodeEmoticon = !!replacements[m];
|
const isUnicodeEmoticon = !!replacements[m];
|
||||||
let emojiVal;
|
let emojiVal;
|
||||||
|
@ -78,7 +116,11 @@ export function performEmojiUnescape(string, opts) {
|
||||||
? "emoji emoji-custom"
|
? "emoji emoji-custom"
|
||||||
: "emoji";
|
: "emoji";
|
||||||
|
|
||||||
return url && (isEmoticon || hasEndingColon || isUnicodeEmoticon)
|
const isReplacable =
|
||||||
|
(isEmoticon || hasEndingColon || isUnicodeEmoticon) &&
|
||||||
|
isReplacableInlineEmoji(string, index, inlineEmoji);
|
||||||
|
|
||||||
|
return url && isReplacable
|
||||||
? `<img src='${url}' ${
|
? `<img src='${url}' ${
|
||||||
opts.skipTitle ? "" : `title='${emojiVal}'`
|
opts.skipTitle ? "" : `title='${emojiVal}'`
|
||||||
} alt='${emojiVal}' class='${classes}'>`
|
} alt='${emojiVal}' class='${classes}'>`
|
||||||
|
@ -89,14 +131,19 @@ export function performEmojiUnescape(string, opts) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function performEmojiEscape(string, opts) {
|
export function performEmojiEscape(string, opts) {
|
||||||
return string.replace(unicodeRegexp, m => {
|
const inlineEmoji = opts.inlineEmoji;
|
||||||
|
const regexp = unicodeRegexp(inlineEmoji);
|
||||||
|
|
||||||
|
return string.replace(regexp, (m, index) => {
|
||||||
|
if (isReplacableInlineEmoji(string, index, inlineEmoji)) {
|
||||||
if (!!translations[m]) {
|
if (!!translations[m]) {
|
||||||
return opts.emojiShortcuts ? `:${translations[m]}:` : m;
|
return opts.emojiShortcuts ? `:${translations[m]}:` : m;
|
||||||
} else if (!!replacements[m]) {
|
} else if (!!replacements[m]) {
|
||||||
return `:${replacements[m]}:`;
|
return `:${replacements[m]}:`;
|
||||||
} else {
|
|
||||||
return m;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return m;
|
||||||
});
|
});
|
||||||
|
|
||||||
return string;
|
return string;
|
||||||
|
|
|
@ -120,7 +120,8 @@ const rule = {
|
||||||
title = performEmojiUnescape(topicInfo.title, {
|
title = performEmojiUnescape(topicInfo.title, {
|
||||||
getURL: options.getURL,
|
getURL: options.getURL,
|
||||||
emojiSet: options.emojiSet,
|
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.enableEmoji = siteSettings.enable_emoji;
|
||||||
opts.emojiSet = siteSettings.emoji_set;
|
opts.emojiSet = siteSettings.emoji_set;
|
||||||
opts.enableEmojiShortcuts = siteSettings.enable_emoji_shortcuts;
|
opts.enableEmojiShortcuts = siteSettings.enable_emoji_shortcuts;
|
||||||
|
opts.inlineEmoji = siteSettings.enable_inline_emoji_translation;
|
||||||
});
|
});
|
||||||
|
|
||||||
helper.registerPlugin(md => {
|
helper.registerPlugin(md => {
|
||||||
|
|
|
@ -218,6 +218,7 @@ module PrettyText
|
||||||
|
|
||||||
set = SiteSetting.emoji_set.inspect
|
set = SiteSetting.emoji_set.inspect
|
||||||
custom = Emoji.custom.map { |e| [e.name, e.url] }.to_h.to_json
|
custom = Emoji.custom.map { |e| [e.name, e.url] }.to_h.to_json
|
||||||
|
|
||||||
protect do
|
protect do
|
||||||
v8.eval(<<~JS)
|
v8.eval(<<~JS)
|
||||||
__paths = #{paths_json};
|
__paths = #{paths_json};
|
||||||
|
@ -225,7 +226,8 @@ module PrettyText
|
||||||
getURL: __getURL,
|
getURL: __getURL,
|
||||||
emojiSet: #{set},
|
emojiSet: #{set},
|
||||||
customEmoji: #{custom},
|
customEmoji: #{custom},
|
||||||
enableEmojiShortcuts: #{SiteSetting.enable_emoji_shortcuts}
|
enableEmojiShortcuts: #{SiteSetting.enable_emoji_shortcuts},
|
||||||
|
inlineEmoji: #{SiteSetting.enable_inline_emoji_translation}
|
||||||
});
|
});
|
||||||
JS
|
JS
|
||||||
end
|
end
|
||||||
|
@ -238,7 +240,10 @@ module PrettyText
|
||||||
|
|
||||||
protect do
|
protect do
|
||||||
v8.eval(<<~JS)
|
v8.eval(<<~JS)
|
||||||
__performEmojiEscape(#{title.inspect}, { emojiShortcuts: #{replace_emoji_shortcuts} });
|
__performEmojiEscape(#{title.inspect}, {
|
||||||
|
emojiShortcuts: #{replace_emoji_shortcuts},
|
||||||
|
inlineEmoji: #{SiteSetting.enable_inline_emoji_translation}
|
||||||
|
});
|
||||||
JS
|
JS
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -308,6 +308,7 @@ describe Topic do
|
||||||
let(:topic_emoji) { build_topic_with_title("I 💖 candy alot") }
|
let(:topic_emoji) { build_topic_with_title("I 💖 candy alot") }
|
||||||
let(:topic_modifier_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_shortcut_emoji) { build_topic_with_title("I love candy :)") }
|
||||||
|
let(:topic_inline_emoji) { build_topic_with_title("Hello😊World") }
|
||||||
|
|
||||||
it "escapes script contents" do
|
it "escapes script contents" do
|
||||||
expect(topic_script.fancy_title).to eq("Topic with <script>alert(‘title’)</script> script in its title")
|
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 :)")
|
expect(topic_shortcut_emoji.fancy_title).to eq("I love candy :)")
|
||||||
end
|
end
|
||||||
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
|
end
|
||||||
|
|
||||||
context 'fancy title' do
|
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", {
|
acceptance("Topic featured links", {
|
||||||
loggedIn: true,
|
loggedIn: true,
|
||||||
settings: {
|
settings: {
|
||||||
|
|
|
@ -93,6 +93,8 @@ Discourse.SiteSettingsOriginal = {
|
||||||
enable_emoji: true,
|
enable_emoji: true,
|
||||||
enable_emoji_shortcuts: true,
|
enable_emoji_shortcuts: true,
|
||||||
emoji_set: "emoji_one",
|
emoji_set: "emoji_one",
|
||||||
|
enable_emoji_shortcuts: true,
|
||||||
|
enable_inline_emoji_translation: false,
|
||||||
desktop_category_page_style: "categories_and_latest_topics",
|
desktop_category_page_style: "categories_and_latest_topics",
|
||||||
enable_mentions: true,
|
enable_mentions: true,
|
||||||
enable_personal_messages: true,
|
enable_personal_messages: true,
|
||||||
|
|
|
@ -92,6 +92,41 @@ QUnit.test("emojiUnescape", assert => {
|
||||||
"no emoticons when emoji shortcuts are disabled",
|
"no emoticons when emoji shortcuts are disabled",
|
||||||
{ enable_emoji_shortcuts: false }
|
{ 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 => {
|
QUnit.test("Emoji search", assert => {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user