FEATURE: Add a site setting to allow emojis to come from an external URL (#12180)

This commit is contained in:
Rafael dos Santos Silva 2021-03-02 16:04:16 -03:00 committed by GitHub
parent 7a53873568
commit 83f332b5a5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 105 additions and 4 deletions

View File

@ -23,6 +23,7 @@ import { notEmpty } from "@ember/object/computed";
import { setting } from "discourse/lib/computed";
import { userPath } from "discourse/lib/url";
import { helperContext } from "discourse-common/lib/helpers";
import { emojiBasePath } from "discourse/lib/settings";
export default Controller.extend(
ModalFunctionality,
@ -84,7 +85,7 @@ export default Controller.extend(
// random number between 2 -6 to render multiple skin tone waving hands
const random = Math.floor(Math.random() * (7 - 2) + 2);
return getURL(`/images/emoji/${emojiSet}/wave/${random}.png`);
return getURL(`${emojiBasePath()}/${emojiSet}/wave/${random}.png`);
},
@discourseComputed(

View File

@ -19,6 +19,7 @@ import { isEmpty } from "@ember/utils";
import { setting } from "discourse/lib/computed";
import showModal from "discourse/lib/show-modal";
import { helperContext } from "discourse-common/lib/helpers";
import { emojiBasePath } from "discourse/lib/settings";
// This is happening outside of the app via popup
const AuthErrors = [
@ -71,7 +72,7 @@ export default Controller.extend(ModalFunctionality, {
// random number between 2 -6 to render multiple skin tone waving hands
const random = Math.floor(Math.random() * (7 - 2) + 2);
return getURL(`/images/emoji/${emojiSet}/wave/${random}.png`);
return getURL(`${emojiBasePath()}/${emojiSet}/wave/${random}.png`);
},
@discourseComputed("showSecondFactor", "showSecurityKey")

View File

@ -7,3 +7,11 @@ export function prioritizeNameInUx(name) {
!siteSettings.prioritize_username_in_ux && name && name.trim().length > 0
);
}
export function emojiBasePath() {
let siteSettings = helperContext().siteSettings;
return siteSettings.external_emoji_url === ""
? "/images/emojis"
: siteSettings.external_emoji_url;
}

View File

@ -90,6 +90,7 @@ function emojiOptions() {
emojiSet: siteSettings.emoji_set,
enableEmojiShortcuts: siteSettings.enable_emoji_shortcuts,
inlineEmoji: siteSettings.enable_inline_emoji_translation,
emojiCDNUrl: siteSettings.external_emoji_url,
};
}

View File

@ -97,6 +97,7 @@ const ORIGINAL_SETTINGS = {
enable_personal_messages: true,
unicode_usernames: false,
secure_media: false,
external_emoji_url: "",
};
let siteSettings = Object.assign({}, ORIGINAL_SETTINGS);

View File

@ -17,6 +17,7 @@ const rawOpts = {
enable_emoji_shortcuts: true,
enable_mentions: true,
emoji_set: "google_classic",
external_emoji_url: "",
highlighted_languages: "json|ruby|javascript",
default_code_lang: "auto",
enable_markdown_linkify: true,
@ -1519,6 +1520,19 @@ var bar = 'bar';
);
});
test("emoji - emojiCDN", function (assert) {
assert.cookedOptions(
":smile:",
{
siteSettings: {
emoji_set: "twitter",
external_emoji_url: "https://emoji.hosting.service",
},
},
`<p><img src="https://emoji.hosting.service/twitter/smile.png?v=${v}" title=":smile:" class="emoji only-emoji" alt=":smile:"></p>`
);
});
test("emoji - registerEmoji", function (assert) {
registerEmoji("foo", "/images/d-logo-sketch.png");

View File

@ -180,6 +180,12 @@ export function buildEmojiUrl(code, opts) {
}
const noToneMatch = code.match(/([^:]+):?/);
let emojiBasePath = "/images/emoji";
if (opts.emojiCDNUrl) {
emojiBasePath = opts.emojiCDNUrl;
}
if (
noToneMatch &&
!url &&
@ -187,7 +193,7 @@ export function buildEmojiUrl(code, opts) {
aliasHash.hasOwnProperty(noToneMatch[1]))
) {
url = opts.getURL(
`/images/emoji/${opts.emojiSet}/${code.replace(/:t/, "/")}.png`
`${emojiBasePath}/${opts.emojiSet}/${code.replace(/:t/, "/")}.png`
);
}

View File

@ -322,6 +322,7 @@ export function setup(helper) {
opts.features.inlineEmoji = !!siteSettings.enable_inline_emoji_translation;
opts.emojiSet = siteSettings.emoji_set || "";
opts.customEmoji = state.customEmoji;
opts.emojiCDNUrl = siteSettings.external_emoji_url;
});
helper.registerPlugin((md) => {

View File

@ -124,6 +124,7 @@ const rule = {
title = performEmojiUnescape(topicInfo.title, {
getURL: options.getURL,
emojiSet: options.emojiSet,
emojiCDNUrl: options.emojiCDNUrl,
enableEmojiShortcuts: options.enableEmojiShortcuts,
inlineEmoji: options.inlineEmoji,
});
@ -160,6 +161,7 @@ export function setup(helper) {
helper.registerOptions((opts, siteSettings) => {
opts.enableEmoji = siteSettings.enable_emoji;
opts.emojiSet = siteSettings.emoji_set;
opts.emojiCDNUrl = siteSettings.external_emoji_url;
opts.enableEmojiShortcuts = siteSettings.enable_emoji_shortcuts;
opts.inlineEmoji = siteSettings.enable_inline_emoji_translation;
});

View File

@ -194,6 +194,7 @@ module Jobs
local_bases = [
Discourse.base_url,
Discourse.asset_host,
SiteSetting.external_emoji_url.presence
].compact.map { |s| normalize_src(s) }
if Discourse.store.has_been_uploaded?(src) || normalize_src(src).start_with?(*local_bases) || src =~ /\A\/[^\/]/i

View File

@ -73,7 +73,11 @@ class Emoji
def self.url_for(name)
name = name.delete_prefix(':').delete_suffix(':').gsub(/(.+):t([1-6])/, '\1/\2')
"#{Discourse.base_path}/images/emoji/#{SiteSetting.emoji_set}/#{name}.png?v=#{EMOJI_VERSION}"
if SiteSetting.external_emoji_url.blank?
"#{Discourse.base_path}/images/emoji/#{SiteSetting.emoji_set}/#{name}.png?v=#{EMOJI_VERSION}"
else
"#{SiteSetting.external_emoji_url}/#{SiteSetting.emoji_set}/#{name}.png?v=#{EMOJI_VERSION}"
end
end
def self.cache_key(name)

View File

@ -1782,6 +1782,7 @@ en:
external_system_avatars_enabled: "Use external system avatars service."
external_system_avatars_url: "URL of the external system avatars service. Allowed substitutions are {username} {first_letter} {color} {size}"
external_emoji_url: "URL of the external service for emoji images. Leave blank to disable."
use_site_small_logo_as_system_avatar: "Use the site's small logo instead of the system user's avatar. Requires the logo to be present."
restrict_letter_avatar_colors: "A list of 6-digit hexadecimal color values to be used for letter avatar background."

View File

@ -1339,6 +1339,9 @@ files:
default: "/letter_avatar_proxy/v4/letter/{first_letter}/{color}/{size}.png"
client: true
regex: '^((https?:)?\/)?\/.+[^\/]'
external_emoji_url:
default: ""
client: true
restrict_letter_avatar_colors:
default: ""
type: list

View File

@ -238,6 +238,7 @@ module PrettyText
__performEmojiUnescape(#{title.inspect}, {
getURL: __getURL,
emojiSet: #{set},
emojiCDNUrl: #{SiteSetting.external_emoji_url.blank? ? "''" : SiteSetting.external_emoji_url},
customEmoji: #{custom},
enableEmojiShortcuts: #{SiteSetting.enable_emoji_shortcuts},
inlineEmoji: #{SiteSetting.enable_inline_emoji_translation}

View File

@ -151,6 +151,45 @@ describe PrettyText do
expect(cook(md)).to eq(html.strip)
end
it "does use emoji CDN when enabled" do
SiteSetting.external_emoji_url = "https://emoji.cdn.com"
html = <<~HTML
<blockquote>
<p>This is a quote with a regular emoji <img src="https://emoji.cdn.com/twitter/upside_down_face.png?v=9" title=":upside_down_face:" class="emoji" alt=":upside_down_face:"></p>
</blockquote>
<blockquote>
<p>This is a quote with an emoji shortcut <img src="https://emoji.cdn.com/twitter/slight_smile.png?v=9" title=":slight_smile:" class="emoji" alt=":slight_smile:"></p>
</blockquote>
<blockquote>
<p>This is a quote with a Unicode emoji <img src="https://emoji.cdn.com/twitter/sunglasses.png?v=9" title=":sunglasses:" class="emoji" alt=":sunglasses:"></p>
</blockquote>
HTML
expect(cook(md)).to eq(html.strip)
end
it "does use emoji CDN when others CDNs are also enabled" do
set_cdn_url('https://cdn.com')
setup_s3
SiteSetting.s3_cdn_url = "https://s3.cdn.com"
SiteSetting.external_emoji_url = "https://emoji.cdn.com"
html = <<~HTML
<blockquote>
<p>This is a quote with a regular emoji <img src="https://emoji.cdn.com/twitter/upside_down_face.png?v=9" title=":upside_down_face:" class="emoji" alt=":upside_down_face:"></p>
</blockquote>
<blockquote>
<p>This is a quote with an emoji shortcut <img src="https://emoji.cdn.com/twitter/slight_smile.png?v=9" title=":slight_smile:" class="emoji" alt=":slight_smile:"></p>
</blockquote>
<blockquote>
<p>This is a quote with a Unicode emoji <img src="https://emoji.cdn.com/twitter/sunglasses.png?v=9" title=":sunglasses:" class="emoji" alt=":sunglasses:"></p>
</blockquote>
HTML
expect(cook(md)).to eq(html.strip)
end
end
it "do off topic quoting of posts from secure categories" do

View File

@ -440,6 +440,23 @@ describe Jobs::PullHotlinkedImages do
expect(subject.should_download_image?(src)).to eq(false)
end
it "returns false for emoji when emoji CDN configured" do
SiteSetting.external_emoji_url = "https://emoji.cdn.com"
src = UrlHelper.cook_url(Emoji.url_for("testemoji.png"))
expect(subject.should_download_image?(src)).to eq(false)
end
it "returns false for emoji when app, S3 *and* emoji CDNs configured" do
setup_s3
SiteSetting.s3_cdn_url = "https://s3.cdn.com"
SiteSetting.external_emoji_url = "https://emoji.cdn.com"
set_cdn_url "https://mydomain.cdn/test"
src = UrlHelper.cook_url(Emoji.url_for("testemoji.png"))
expect(subject.should_download_image?(src)).to eq(false)
end
it "returns false for plugin assets" do
src = UrlHelper.cook_url("/plugins/discourse-amazing-plugin/myasset.png")
expect(subject.should_download_image?(src)).to eq(false)