UX: Better emoji escaping for topic title (#7218)
* FIX: Fixed failing discourse-prometheus-alert-receiver plugin specs
This commit is contained in:
parent
6450969c7b
commit
f7b156ffbd
|
@ -3,7 +3,8 @@ import {
|
||||||
aliases,
|
aliases,
|
||||||
searchAliases,
|
searchAliases,
|
||||||
translations,
|
translations,
|
||||||
tonableEmojis
|
tonableEmojis,
|
||||||
|
replacements
|
||||||
} from "pretty-text/emoji/data";
|
} from "pretty-text/emoji/data";
|
||||||
import { IMAGE_VERSION } from "pretty-text/emoji/version";
|
import { IMAGE_VERSION } from "pretty-text/emoji/version";
|
||||||
|
|
||||||
|
@ -34,24 +35,43 @@ export function performEmojiUnescape(string, opts) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// this can be further improved by supporting matches of emoticons that don't begin with a colon
|
return string.replace(/[\u1000-\uFFFF]+|\B:[^\s:]+(?::t\d)?:?\B/g, m => {
|
||||||
if (string.indexOf(":") >= 0) {
|
const isEmoticon = !!translations[m];
|
||||||
return string.replace(/\B:[^\s:]+(?::t\d)?:?\B/g, m => {
|
const isUnicodeEmoticon = !!replacements[m];
|
||||||
const isEmoticon = !!translations[m];
|
let emojiVal;
|
||||||
const emojiVal = isEmoticon ? translations[m] : m.slice(1, m.length - 1);
|
if (isEmoticon) {
|
||||||
const hasEndingColon = m.lastIndexOf(":") === m.length - 1;
|
emojiVal = translations[m];
|
||||||
const url = buildEmojiUrl(emojiVal, opts);
|
} else if (isUnicodeEmoticon) {
|
||||||
const classes = isCustomEmoji(emojiVal, opts)
|
emojiVal = replacements[m];
|
||||||
? "emoji emoji-custom"
|
} else {
|
||||||
: "emoji";
|
emojiVal = m.slice(1, m.length - 1);
|
||||||
|
}
|
||||||
|
const hasEndingColon = m.lastIndexOf(":") === m.length - 1;
|
||||||
|
const url = buildEmojiUrl(emojiVal, opts);
|
||||||
|
const classes = isCustomEmoji(emojiVal, opts)
|
||||||
|
? "emoji emoji-custom"
|
||||||
|
: "emoji";
|
||||||
|
|
||||||
return url && (isEmoticon || hasEndingColon)
|
return url && (isEmoticon || hasEndingColon || isUnicodeEmoticon)
|
||||||
? `<img src='${url}' ${
|
? `<img src='${url}' ${
|
||||||
opts.skipTitle ? "" : `title='${emojiVal}'`
|
opts.skipTitle ? "" : `title='${emojiVal}'`
|
||||||
} alt='${emojiVal}' class='${classes}'>`
|
} alt='${emojiVal}' class='${classes}'>`
|
||||||
: m;
|
: m;
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
return string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function performEmojiEscape(string) {
|
||||||
|
return string.replace(/[\u1000-\uFFFF]+|\B:[^\s:]+(?::t\d)?:?\B/g, m => {
|
||||||
|
if (!!translations[m]) {
|
||||||
|
return ":" + translations[m] + ":";
|
||||||
|
} else if (!!replacements[m]) {
|
||||||
|
return ":" + replacements[m] + ":";
|
||||||
|
} else {
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return string;
|
return string;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,3 +3,4 @@ export const tonableEmojis = <%= Emoji.tonable_emojis.flatten.inspect %>;
|
||||||
export const aliases = <%= Emoji.aliases.inspect.gsub("=>", ":") %>;
|
export const aliases = <%= Emoji.aliases.inspect.gsub("=>", ":") %>;
|
||||||
export const searchAliases = <%= Emoji.search_aliases.inspect.gsub("=>", ":") %>;
|
export const searchAliases = <%= Emoji.search_aliases.inspect.gsub("=>", ":") %>;
|
||||||
export const translations = <%= Emoji.translations.inspect.gsub("=>", ":") %>;
|
export const translations = <%= Emoji.translations.inspect.gsub("=>", ":") %>;
|
||||||
|
export const replacements = <%= Emoji.unicode_replacements_json %>;
|
||||||
|
|
|
@ -157,13 +157,7 @@ class Emoji
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.unicode_unescape(string)
|
def self.unicode_unescape(string)
|
||||||
string.each_char.map do |c|
|
PrettyText.escape_emoji(string)
|
||||||
if str = unicode_replacements[c]
|
|
||||||
":#{str}:"
|
|
||||||
else
|
|
||||||
c
|
|
||||||
end
|
|
||||||
end.join
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.gsub_emoji_to_unicode(str)
|
def self.gsub_emoji_to_unicode(str)
|
||||||
|
|
|
@ -227,7 +227,7 @@ module PrettyText
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.unescape_emoji(title)
|
def self.unescape_emoji(title)
|
||||||
return title unless SiteSetting.enable_emoji?
|
return title unless SiteSetting.enable_emoji? && title
|
||||||
|
|
||||||
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
|
||||||
|
@ -239,6 +239,16 @@ module PrettyText
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.escape_emoji(title)
|
||||||
|
return unless title
|
||||||
|
|
||||||
|
protect do
|
||||||
|
v8.eval(<<~JS)
|
||||||
|
__performEmojiEscape(#{title.inspect});
|
||||||
|
JS
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def self.cook(text, opts = {})
|
def self.cook(text, opts = {})
|
||||||
options = opts.dup
|
options = opts.dup
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
__PrettyText = require("pretty-text/pretty-text").default;
|
__PrettyText = require("pretty-text/pretty-text").default;
|
||||||
__buildOptions = require("pretty-text/pretty-text").buildOptions;
|
__buildOptions = require("pretty-text/pretty-text").buildOptions;
|
||||||
__performEmojiUnescape = require("pretty-text/emoji").performEmojiUnescape;
|
__performEmojiUnescape = require("pretty-text/emoji").performEmojiUnescape;
|
||||||
|
__performEmojiEscape = require("pretty-text/emoji").performEmojiEscape;
|
||||||
|
|
||||||
__utils = require("discourse/lib/utilities");
|
__utils = require("discourse/lib/utilities");
|
||||||
|
|
||||||
|
|
|
@ -279,6 +279,7 @@ describe Topic do
|
||||||
let(:topic_image) { build_topic_with_title("Topic with <img src='something'> image in its title") }
|
let(:topic_image) { build_topic_with_title("Topic with <img src='something'> image in its title") }
|
||||||
let(:topic_script) { build_topic_with_title("Topic with <script>alert('title')</script> script in its title") }
|
let(:topic_script) { build_topic_with_title("Topic with <script>alert('title')</script> script in its title") }
|
||||||
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") }
|
||||||
|
|
||||||
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")
|
||||||
|
@ -288,6 +289,10 @@ describe Topic do
|
||||||
expect(topic_emoji.fancy_title).to eq("I :sparkling_heart: candy alot")
|
expect(topic_emoji.fancy_title).to eq("I :sparkling_heart: candy alot")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "keeps combined emojis" do
|
||||||
|
expect(topic_modifier_emoji.fancy_title).to eq("I :man_farmer: candy alot")
|
||||||
|
end
|
||||||
|
|
||||||
it "escapes bold contents" do
|
it "escapes bold contents" do
|
||||||
expect(topic_bold.fancy_title).to eq("Topic with <b>bold</b> text in its title")
|
expect(topic_bold.fancy_title).to eq("Topic with <b>bold</b> text in its title")
|
||||||
end
|
end
|
||||||
|
|
|
@ -184,6 +184,23 @@ QUnit.test("Updating the topic title with emojis", async assert => {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
QUnit.test("Updating the topic title with unicode emojis", async assert => {
|
||||||
|
await visit("/t/internationalization-localization/280");
|
||||||
|
await click("#topic-title .d-icon-pencil-alt");
|
||||||
|
|
||||||
|
await fillIn("#edit-title", "emojis title 👨🌾");
|
||||||
|
|
||||||
|
await click("#topic-title .submit-edit");
|
||||||
|
|
||||||
|
assert.equal(
|
||||||
|
find(".fancy-title")
|
||||||
|
.html()
|
||||||
|
.trim(),
|
||||||
|
`emojis title <img src="/images/emoji/emoji_one/man_farmer.png?v=${v}" title="man_farmer" alt="man_farmer" class="emoji">`,
|
||||||
|
"it displays the new title with escaped unicode emojis"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
acceptance("Topic featured links", {
|
acceptance("Topic featured links", {
|
||||||
loggedIn: true,
|
loggedIn: true,
|
||||||
settings: {
|
settings: {
|
||||||
|
|
Loading…
Reference in New Issue