mirror of
https://github.com/discourse/discourse.git
synced 2025-06-01 03:54:33 +08:00
DEV: Apply syntax_tree formatting to lib/*
This commit is contained in:
@ -1,8 +1,8 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'mini_racer'
|
||||
require 'nokogiri'
|
||||
require 'erb'
|
||||
require "mini_racer"
|
||||
require "nokogiri"
|
||||
require "erb"
|
||||
|
||||
module PrettyText
|
||||
DANGEROUS_BIDI_CHARACTERS = [
|
||||
@ -57,21 +57,19 @@ module PrettyText
|
||||
# Look for vendored stuff
|
||||
vendor_root = "#{Rails.root}/vendor/assets/javascripts/"
|
||||
filename = find_file(vendor_root, part_name)
|
||||
if filename
|
||||
ctx.eval(File.read("#{vendor_root}#{filename}"))
|
||||
end
|
||||
ctx.eval(File.read("#{vendor_root}#{filename}")) if filename
|
||||
end
|
||||
end
|
||||
|
||||
def self.ctx_load_directory(ctx, path)
|
||||
root_path = "#{Rails.root}/app/assets/javascripts/"
|
||||
Dir["#{root_path}#{path}/**/*"].sort.each do |f|
|
||||
apply_es6_file(ctx, root_path, f.sub(root_path, '').sub(/\.js(.es6)?$/, ''))
|
||||
apply_es6_file(ctx, root_path, f.sub(root_path, "").sub(/\.js(.es6)?$/, ""))
|
||||
end
|
||||
end
|
||||
|
||||
def self.create_es6_context
|
||||
ctx = MiniRacer::Context.new(timeout: 25000, ensure_gc_after_idle: 2000)
|
||||
ctx = MiniRacer::Context.new(timeout: 25_000, ensure_gc_after_idle: 2000)
|
||||
|
||||
ctx.eval("window = {}; window.devicePixelRatio = 2;") # hack to make code think stuff is retina
|
||||
|
||||
@ -118,19 +116,15 @@ module PrettyText
|
||||
to_load << a if File.file?(a) && a =~ /discourse-markdown/
|
||||
end
|
||||
to_load.uniq.each do |f|
|
||||
if f =~ /^.+assets\/javascripts\//
|
||||
if f =~ %r{^.+assets/javascripts/}
|
||||
root = Regexp.last_match[0]
|
||||
apply_es6_file(ctx, root, f.sub(root, '').sub(/\.js(\.es6)?$/, ''))
|
||||
apply_es6_file(ctx, root, f.sub(root, "").sub(/\.js(\.es6)?$/, ""))
|
||||
end
|
||||
end
|
||||
|
||||
DiscoursePluginRegistry.vendored_core_pretty_text.each do |vpt|
|
||||
ctx.eval(File.read(vpt))
|
||||
end
|
||||
DiscoursePluginRegistry.vendored_core_pretty_text.each { |vpt| ctx.eval(File.read(vpt)) }
|
||||
|
||||
DiscoursePluginRegistry.vendored_pretty_text.each do |vpt|
|
||||
ctx.eval(File.read(vpt))
|
||||
end
|
||||
DiscoursePluginRegistry.vendored_pretty_text.each { |vpt| ctx.eval(File.read(vpt)) }
|
||||
|
||||
ctx
|
||||
end
|
||||
@ -209,15 +203,13 @@ module PrettyText
|
||||
__optInput.customEmojiTranslation = #{Plugin::CustomEmoji.translations.to_json};
|
||||
__optInput.emojiUnicodeReplacer = __emojiUnicodeReplacer;
|
||||
__optInput.lookupUploadUrls = __lookupUploadUrls;
|
||||
__optInput.censoredRegexp = #{WordWatcher.serializable_word_matcher_regexp(:censor).to_json };
|
||||
__optInput.censoredRegexp = #{WordWatcher.serializable_word_matcher_regexp(:censor).to_json};
|
||||
__optInput.watchedWordsReplace = #{WordWatcher.word_matcher_regexps(:replace).to_json};
|
||||
__optInput.watchedWordsLink = #{WordWatcher.word_matcher_regexps(:link).to_json};
|
||||
__optInput.additionalOptions = #{Site.markdown_additional_options.to_json};
|
||||
JS
|
||||
|
||||
if opts[:topic_id]
|
||||
buffer << "__optInput.topicId = #{opts[:topic_id].to_i};\n"
|
||||
end
|
||||
buffer << "__optInput.topicId = #{opts[:topic_id].to_i};\n" if opts[:topic_id]
|
||||
|
||||
if opts[:force_quote_link]
|
||||
buffer << "__optInput.forceQuoteLink = #{opts[:force_quote_link]};\n"
|
||||
@ -235,10 +227,13 @@ module PrettyText
|
||||
end
|
||||
|
||||
opts[:hashtag_context] = opts[:hashtag_context] || "topic-composer"
|
||||
hashtag_types_as_js = HashtagAutocompleteService.ordered_types_for_context(
|
||||
opts[:hashtag_context]
|
||||
).map { |t| "'#{t}'" }.join(",")
|
||||
hashtag_icons_as_js = HashtagAutocompleteService.data_source_icons.map { |i| "'#{i}'" }.join(",")
|
||||
hashtag_types_as_js =
|
||||
HashtagAutocompleteService
|
||||
.ordered_types_for_context(opts[:hashtag_context])
|
||||
.map { |t| "'#{t}'" }
|
||||
.join(",")
|
||||
hashtag_icons_as_js =
|
||||
HashtagAutocompleteService.data_source_icons.map { |i| "'#{i}'" }.join(",")
|
||||
buffer << "__optInput.hashtagTypesInPriorityOrder = [#{hashtag_types_as_js}];\n"
|
||||
buffer << "__optInput.hashtagIcons = [#{hashtag_icons_as_js}];\n"
|
||||
|
||||
@ -246,9 +241,7 @@ module PrettyText
|
||||
buffer << ("__pt = new __PrettyText(__textOptions);")
|
||||
|
||||
# Be careful disabling sanitization. We allow for custom emails
|
||||
if opts[:sanitize] == false
|
||||
buffer << ('__pt.disableSanitizer();')
|
||||
end
|
||||
buffer << ("__pt.disableSanitizer();") if opts[:sanitize] == false
|
||||
|
||||
opts = context.eval(buffer)
|
||||
|
||||
@ -260,15 +253,10 @@ module PrettyText
|
||||
end
|
||||
|
||||
def self.paths_json
|
||||
paths = {
|
||||
baseUri: Discourse.base_path,
|
||||
CDN: Rails.configuration.action_controller.asset_host,
|
||||
}
|
||||
paths = { baseUri: Discourse.base_path, CDN: Rails.configuration.action_controller.asset_host }
|
||||
|
||||
if SiteSetting.Upload.enable_s3_uploads
|
||||
if SiteSetting.Upload.s3_cdn_url.present?
|
||||
paths[:S3CDN] = SiteSetting.Upload.s3_cdn_url
|
||||
end
|
||||
paths[:S3CDN] = SiteSetting.Upload.s3_cdn_url if SiteSetting.Upload.s3_cdn_url.present?
|
||||
paths[:S3BaseUrl] = Discourse.store.absolute_base_url
|
||||
end
|
||||
|
||||
@ -277,12 +265,10 @@ module PrettyText
|
||||
|
||||
# leaving this here, cause it invokes v8, don't want to implement twice
|
||||
def self.avatar_img(avatar_template, size)
|
||||
protect do
|
||||
v8.eval(<<~JS)
|
||||
protect { v8.eval(<<~JS) }
|
||||
__paths = #{paths_json};
|
||||
__utils.avatarImg({size: #{size.inspect}, avatarTemplate: #{avatar_template.inspect}}, __getURL);
|
||||
JS
|
||||
end
|
||||
end
|
||||
|
||||
def self.unescape_emoji(title)
|
||||
@ -291,8 +277,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)
|
||||
protect { v8.eval(<<~JS) }
|
||||
__paths = #{paths_json};
|
||||
__performEmojiUnescape(#{title.inspect}, {
|
||||
getURL: __getURL,
|
||||
@ -303,7 +288,6 @@ module PrettyText
|
||||
inlineEmoji: #{SiteSetting.enable_inline_emoji_translation}
|
||||
});
|
||||
JS
|
||||
end
|
||||
end
|
||||
|
||||
def self.escape_emoji(title)
|
||||
@ -311,14 +295,12 @@ module PrettyText
|
||||
|
||||
replace_emoji_shortcuts = SiteSetting.enable_emoji && SiteSetting.enable_emoji_shortcuts
|
||||
|
||||
protect do
|
||||
v8.eval(<<~JS)
|
||||
protect { v8.eval(<<~JS) }
|
||||
__performEmojiEscape(#{title.inspect}, {
|
||||
emojiShortcuts: #{replace_emoji_shortcuts},
|
||||
inlineEmoji: #{SiteSetting.enable_inline_emoji_translation}
|
||||
});
|
||||
JS
|
||||
end
|
||||
end
|
||||
|
||||
def self.cook(text, opts = {})
|
||||
@ -334,13 +316,9 @@ module PrettyText
|
||||
strip_hidden_unicode_bidirectional_characters(doc)
|
||||
sanitize_hotlinked_media(doc)
|
||||
|
||||
if SiteSetting.enable_mentions
|
||||
add_mentions(doc, user_id: opts[:user_id])
|
||||
end
|
||||
add_mentions(doc, user_id: opts[:user_id]) if SiteSetting.enable_mentions
|
||||
|
||||
scrubber = Loofah::Scrubber.new do |node|
|
||||
node.remove if node.name == 'script'
|
||||
end
|
||||
scrubber = Loofah::Scrubber.new { |node| node.remove if node.name == "script" }
|
||||
loofah_fragment = Loofah.fragment(doc.to_html)
|
||||
loofah_fragment.scrub!(scrubber).to_html
|
||||
end
|
||||
@ -348,19 +326,22 @@ module PrettyText
|
||||
def self.strip_hidden_unicode_bidirectional_characters(doc)
|
||||
return if !DANGEROUS_BIDI_REGEXP.match?(doc.content)
|
||||
|
||||
doc.css("code,pre").each do |code_tag|
|
||||
next if !DANGEROUS_BIDI_REGEXP.match?(code_tag.content)
|
||||
doc
|
||||
.css("code,pre")
|
||||
.each do |code_tag|
|
||||
next if !DANGEROUS_BIDI_REGEXP.match?(code_tag.content)
|
||||
|
||||
DANGEROUS_BIDI_CHARACTERS.each do |bidi|
|
||||
next if !code_tag.content.include?(bidi)
|
||||
DANGEROUS_BIDI_CHARACTERS.each do |bidi|
|
||||
next if !code_tag.content.include?(bidi)
|
||||
|
||||
formatted = "<U+#{bidi.ord.to_s(16).upcase}>"
|
||||
code_tag.inner_html = code_tag.inner_html.gsub(
|
||||
bidi,
|
||||
"<span class=\"bidi-warning\" title=\"#{I18n.t("post.hidden_bidi_character")}\">#{formatted}</span>"
|
||||
)
|
||||
formatted = "<U+#{bidi.ord.to_s(16).upcase}>"
|
||||
code_tag.inner_html =
|
||||
code_tag.inner_html.gsub(
|
||||
bidi,
|
||||
"<span class=\"bidi-warning\" title=\"#{I18n.t("post.hidden_bidi_character")}\">#{formatted}</span>",
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def self.sanitize_hotlinked_media(doc)
|
||||
@ -368,56 +349,63 @@ module PrettyText
|
||||
|
||||
allowed_pattern = allowed_src_pattern
|
||||
|
||||
doc.css("img[src], source[src], source[srcset], track[src]").each do |el|
|
||||
if el["src"] && !el["src"].match?(allowed_pattern)
|
||||
el[PrettyText::BLOCKED_HOTLINKED_SRC_ATTR] = el.delete("src")
|
||||
end
|
||||
doc
|
||||
.css("img[src], source[src], source[srcset], track[src]")
|
||||
.each do |el|
|
||||
if el["src"] && !el["src"].match?(allowed_pattern)
|
||||
el[PrettyText::BLOCKED_HOTLINKED_SRC_ATTR] = el.delete("src")
|
||||
end
|
||||
|
||||
if el["srcset"]
|
||||
srcs = el["srcset"].split(',').map { |e| e.split(' ', 2)[0].presence }
|
||||
if srcs.any? { |src| !src.match?(allowed_pattern) }
|
||||
el[PrettyText::BLOCKED_HOTLINKED_SRCSET_ATTR] = el.delete("srcset")
|
||||
if el["srcset"]
|
||||
srcs = el["srcset"].split(",").map { |e| e.split(" ", 2)[0].presence }
|
||||
if srcs.any? { |src| !src.match?(allowed_pattern) }
|
||||
el[PrettyText::BLOCKED_HOTLINKED_SRCSET_ATTR] = el.delete("srcset")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def self.add_rel_attributes_to_user_content(doc, add_nofollow)
|
||||
allowlist = []
|
||||
|
||||
domains = SiteSetting.exclude_rel_nofollow_domains
|
||||
allowlist = domains.split('|') if domains.present?
|
||||
allowlist = domains.split("|") if domains.present?
|
||||
|
||||
site_uri = nil
|
||||
doc.css("a").each do |l|
|
||||
href = l["href"].to_s
|
||||
l["rel"] = "noopener" if l["target"] == "_blank"
|
||||
doc
|
||||
.css("a")
|
||||
.each do |l|
|
||||
href = l["href"].to_s
|
||||
l["rel"] = "noopener" if l["target"] == "_blank"
|
||||
|
||||
begin
|
||||
uri = URI(UrlHelper.encode_component(href))
|
||||
site_uri ||= URI(Discourse.base_url)
|
||||
begin
|
||||
uri = URI(UrlHelper.encode_component(href))
|
||||
site_uri ||= URI(Discourse.base_url)
|
||||
|
||||
same_domain = !uri.host.present? ||
|
||||
uri.host == site_uri.host ||
|
||||
uri.host.ends_with?(".#{site_uri.host}") ||
|
||||
allowlist.any? { |u| uri.host == u || uri.host.ends_with?(".#{u}") }
|
||||
same_domain =
|
||||
!uri.host.present? || uri.host == site_uri.host ||
|
||||
uri.host.ends_with?(".#{site_uri.host}") ||
|
||||
allowlist.any? { |u| uri.host == u || uri.host.ends_with?(".#{u}") }
|
||||
|
||||
l["rel"] = "noopener nofollow ugc" if add_nofollow && !same_domain
|
||||
rescue URI::Error
|
||||
# add a nofollow anyway
|
||||
l["rel"] = "noopener nofollow ugc"
|
||||
l["rel"] = "noopener nofollow ugc" if add_nofollow && !same_domain
|
||||
rescue URI::Error
|
||||
# add a nofollow anyway
|
||||
l["rel"] = "noopener nofollow ugc"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class DetectedLink < Struct.new(:url, :is_quote); end
|
||||
class DetectedLink < Struct.new(:url, :is_quote)
|
||||
end
|
||||
|
||||
def self.extract_links(html)
|
||||
links = []
|
||||
doc = Nokogiri::HTML5.fragment(html)
|
||||
|
||||
# extract onebox links
|
||||
doc.css("aside.onebox[data-onebox-src]").each { |onebox| links << DetectedLink.new(onebox["data-onebox-src"], false) }
|
||||
doc
|
||||
.css("aside.onebox[data-onebox-src]")
|
||||
.each { |onebox| links << DetectedLink.new(onebox["data-onebox-src"], false) }
|
||||
|
||||
# remove href inside quotes & oneboxes & elided part
|
||||
doc.css("aside.quote a, aside.onebox a, .elided a").remove
|
||||
@ -426,39 +414,49 @@ module PrettyText
|
||||
doc.css("a.onebox > img").each { |img| img.parent.remove }
|
||||
|
||||
# extract all links
|
||||
doc.css("a").each do |a|
|
||||
if a["href"].present? && a["href"][0] != "#"
|
||||
links << DetectedLink.new(a["href"], false)
|
||||
doc
|
||||
.css("a")
|
||||
.each do |a|
|
||||
links << DetectedLink.new(a["href"], false) if a["href"].present? && a["href"][0] != "#"
|
||||
end
|
||||
end
|
||||
|
||||
# extract quotes
|
||||
doc.css("aside.quote[data-topic]").each do |aside|
|
||||
if aside["data-topic"].present?
|
||||
url = +"/t/#{aside["data-topic"]}"
|
||||
url << "/#{aside["data-post"]}" if aside["data-post"].present?
|
||||
links << DetectedLink.new(url, true)
|
||||
doc
|
||||
.css("aside.quote[data-topic]")
|
||||
.each do |aside|
|
||||
if aside["data-topic"].present?
|
||||
url = +"/t/#{aside["data-topic"]}"
|
||||
url << "/#{aside["data-post"]}" if aside["data-post"].present?
|
||||
links << DetectedLink.new(url, true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# extract Youtube links
|
||||
doc.css("div[data-youtube-id]").each do |div|
|
||||
if div["data-youtube-id"].present?
|
||||
links << DetectedLink.new("https://www.youtube.com/watch?v=#{div['data-youtube-id']}", false)
|
||||
doc
|
||||
.css("div[data-youtube-id]")
|
||||
.each do |div|
|
||||
if div["data-youtube-id"].present?
|
||||
links << DetectedLink.new(
|
||||
"https://www.youtube.com/watch?v=#{div["data-youtube-id"]}",
|
||||
false,
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
links
|
||||
end
|
||||
|
||||
def self.extract_mentions(cooked)
|
||||
mentions = cooked.css('.mention, .mention-group').map do |e|
|
||||
if (name = e.inner_text)
|
||||
name = name[1..-1]
|
||||
name = User.normalize_username(name)
|
||||
name
|
||||
end
|
||||
end
|
||||
mentions =
|
||||
cooked
|
||||
.css(".mention, .mention-group")
|
||||
.map do |e|
|
||||
if (name = e.inner_text)
|
||||
name = name[1..-1]
|
||||
name = User.normalize_username(name)
|
||||
name
|
||||
end
|
||||
end
|
||||
|
||||
mentions.compact!
|
||||
mentions.uniq!
|
||||
@ -480,24 +478,26 @@ module PrettyText
|
||||
|
||||
# If the user is not basic, strip links from their bio
|
||||
fragment = Nokogiri::HTML5.fragment(string)
|
||||
fragment.css('a').each { |a| a.replace(a.inner_html) }
|
||||
fragment.css("a").each { |a| a.replace(a.inner_html) }
|
||||
fragment.to_html
|
||||
end
|
||||
|
||||
def self.make_all_links_absolute(doc)
|
||||
site_uri = nil
|
||||
doc.css("a").each do |link|
|
||||
href = link["href"].to_s
|
||||
begin
|
||||
uri = URI(href)
|
||||
site_uri ||= URI(Discourse.base_url)
|
||||
unless uri.host.present? || href.start_with?('mailto')
|
||||
link["href"] = "#{site_uri}#{link['href']}"
|
||||
doc
|
||||
.css("a")
|
||||
.each do |link|
|
||||
href = link["href"].to_s
|
||||
begin
|
||||
uri = URI(href)
|
||||
site_uri ||= URI(Discourse.base_url)
|
||||
unless uri.host.present? || href.start_with?("mailto")
|
||||
link["href"] = "#{site_uri}#{link["href"]}"
|
||||
end
|
||||
rescue URI::Error
|
||||
# leave it
|
||||
end
|
||||
rescue URI::Error
|
||||
# leave it
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def self.strip_image_wrapping(doc)
|
||||
@ -510,94 +510,119 @@ module PrettyText
|
||||
end
|
||||
|
||||
def self.convert_vimeo_iframes(doc)
|
||||
doc.css("iframe[src*='player.vimeo.com']").each do |iframe|
|
||||
if iframe["data-original-href"].present?
|
||||
vimeo_url = UrlHelper.normalized_encode(iframe["data-original-href"])
|
||||
else
|
||||
vimeo_id = iframe['src'].split('/').last
|
||||
vimeo_url = "https://vimeo.com/#{vimeo_id}"
|
||||
doc
|
||||
.css("iframe[src*='player.vimeo.com']")
|
||||
.each do |iframe|
|
||||
if iframe["data-original-href"].present?
|
||||
vimeo_url = UrlHelper.normalized_encode(iframe["data-original-href"])
|
||||
else
|
||||
vimeo_id = iframe["src"].split("/").last
|
||||
vimeo_url = "https://vimeo.com/#{vimeo_id}"
|
||||
end
|
||||
iframe.replace Nokogiri::HTML5.fragment("<p><a href='#{vimeo_url}'>#{vimeo_url}</a></p>")
|
||||
end
|
||||
iframe.replace Nokogiri::HTML5.fragment("<p><a href='#{vimeo_url}'>#{vimeo_url}</a></p>")
|
||||
end
|
||||
end
|
||||
|
||||
def self.strip_secure_uploads(doc)
|
||||
# images inside a lightbox or other link
|
||||
doc.css('a[href]').each do |a|
|
||||
next if !Upload.secure_uploads_url?(a['href'])
|
||||
doc
|
||||
.css("a[href]")
|
||||
.each do |a|
|
||||
next if !Upload.secure_uploads_url?(a["href"])
|
||||
|
||||
non_image_media = %w(video audio).include?(a&.parent&.name)
|
||||
target = non_image_media ? a.parent : a
|
||||
next if target.to_s.include?('stripped-secure-view-media') || target.to_s.include?('stripped-secure-view-upload')
|
||||
|
||||
next if a.css('img[src]').empty? && !non_image_media
|
||||
|
||||
if a.classes.include?('lightbox')
|
||||
img = a.css('img[src]').first
|
||||
srcset = img&.attributes['srcset']&.value
|
||||
if srcset
|
||||
# if available, use the first image from the srcset here
|
||||
# so we get the optimized image instead of the possibly huge original
|
||||
url = srcset.split(',').first
|
||||
else
|
||||
url = img['src']
|
||||
non_image_media = %w[video audio].include?(a&.parent&.name)
|
||||
target = non_image_media ? a.parent : a
|
||||
if target.to_s.include?("stripped-secure-view-media") ||
|
||||
target.to_s.include?("stripped-secure-view-upload")
|
||||
next
|
||||
end
|
||||
|
||||
next if a.css("img[src]").empty? && !non_image_media
|
||||
|
||||
if a.classes.include?("lightbox")
|
||||
img = a.css("img[src]").first
|
||||
srcset = img&.attributes["srcset"]&.value
|
||||
if srcset
|
||||
# if available, use the first image from the srcset here
|
||||
# so we get the optimized image instead of the possibly huge original
|
||||
url = srcset.split(",").first
|
||||
else
|
||||
url = img["src"]
|
||||
end
|
||||
a.add_next_sibling secure_uploads_placeholder(
|
||||
doc,
|
||||
url,
|
||||
width: img["width"],
|
||||
height: img["height"],
|
||||
)
|
||||
a.remove
|
||||
else
|
||||
width = non_image_media ? nil : a.at_css("img").attr("width")
|
||||
height = non_image_media ? nil : a.at_css("img").attr("height")
|
||||
target.add_next_sibling secure_uploads_placeholder(
|
||||
doc,
|
||||
a["href"],
|
||||
width: width,
|
||||
height: height,
|
||||
)
|
||||
target.remove
|
||||
end
|
||||
a.add_next_sibling secure_uploads_placeholder(doc, url, width: img['width'], height: img['height'])
|
||||
a.remove
|
||||
else
|
||||
width = non_image_media ? nil : a.at_css('img').attr('width')
|
||||
height = non_image_media ? nil : a.at_css('img').attr('height')
|
||||
target.add_next_sibling secure_uploads_placeholder(doc, a['href'], width: width, height: height)
|
||||
target.remove
|
||||
end
|
||||
end
|
||||
|
||||
# images by themselves or inside a onebox
|
||||
doc.css('img[src]').each do |img|
|
||||
url = if img.parent.classes.include?("aspect-image") && img.attributes["srcset"].present?
|
||||
doc
|
||||
.css("img[src]")
|
||||
.each do |img|
|
||||
url =
|
||||
if img.parent.classes.include?("aspect-image") && img.attributes["srcset"].present?
|
||||
# we are using the first image from the srcset here so we get the
|
||||
# optimized image instead of the original, because an optimized
|
||||
# image may be used for the onebox thumbnail
|
||||
srcset = img.attributes["srcset"].value
|
||||
srcset.split(",").first
|
||||
else
|
||||
img["src"]
|
||||
end
|
||||
|
||||
# we are using the first image from the srcset here so we get the
|
||||
# optimized image instead of the original, because an optimized
|
||||
# image may be used for the onebox thumbnail
|
||||
srcset = img.attributes["srcset"].value
|
||||
srcset.split(",").first
|
||||
else
|
||||
img['src']
|
||||
end
|
||||
width = img["width"]
|
||||
height = img["height"]
|
||||
onebox_type = nil
|
||||
|
||||
width = img['width']
|
||||
height = img['height']
|
||||
onebox_type = nil
|
||||
if img.ancestors.css(".onebox-body").any?
|
||||
if img.classes.include?("onebox-avatar-inline")
|
||||
onebox_type = "avatar-inline"
|
||||
else
|
||||
onebox_type = "thumbnail"
|
||||
end
|
||||
end
|
||||
|
||||
if img.ancestors.css(".onebox-body").any?
|
||||
if img.classes.include?("onebox-avatar-inline")
|
||||
onebox_type = "avatar-inline"
|
||||
else
|
||||
onebox_type = "thumbnail"
|
||||
# we always want this to be tiny and without any special styles
|
||||
if img.classes.include?("site-icon")
|
||||
onebox_type = nil
|
||||
width = 16
|
||||
height = 16
|
||||
end
|
||||
|
||||
if Upload.secure_uploads_url?(url)
|
||||
img.add_next_sibling secure_uploads_placeholder(
|
||||
doc,
|
||||
url,
|
||||
onebox_type: onebox_type,
|
||||
width: width,
|
||||
height: height,
|
||||
)
|
||||
img.remove
|
||||
end
|
||||
end
|
||||
|
||||
# we always want this to be tiny and without any special styles
|
||||
if img.classes.include?('site-icon')
|
||||
onebox_type = nil
|
||||
width = 16
|
||||
height = 16
|
||||
end
|
||||
|
||||
if Upload.secure_uploads_url?(url)
|
||||
img.add_next_sibling secure_uploads_placeholder(doc, url, onebox_type: onebox_type, width: width, height: height)
|
||||
img.remove
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def self.secure_uploads_placeholder(doc, url, onebox_type: false, width: nil, height: nil)
|
||||
data_width = width ? "data-width=#{width}" : ''
|
||||
data_height = height ? "data-height=#{height}" : ''
|
||||
data_onebox_type = onebox_type ? "data-onebox-type='#{onebox_type}'" : ''
|
||||
data_width = width ? "data-width=#{width}" : ""
|
||||
data_height = height ? "data-height=#{height}" : ""
|
||||
data_onebox_type = onebox_type ? "data-onebox-type='#{onebox_type}'" : ""
|
||||
<<~HTML
|
||||
<div class="secure-upload-notice" data-stripped-secure-upload="#{url}" #{data_onebox_type} #{data_width} #{data_height}>
|
||||
#{I18n.t('emails.secure_uploads_placeholder')} <a class='stripped-secure-view-upload' href="#{url}">#{I18n.t("emails.view_redacted_media")}</a>.
|
||||
#{I18n.t("emails.secure_uploads_placeholder")} <a class='stripped-secure-view-upload' href="#{url}">#{I18n.t("emails.view_redacted_media")}</a>.
|
||||
</div>
|
||||
HTML
|
||||
end
|
||||
@ -621,28 +646,23 @@ module PrettyText
|
||||
@message = message
|
||||
@backtrace = backtrace
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def self.protect
|
||||
rval = nil
|
||||
@mutex.synchronize do
|
||||
rval = yield
|
||||
end
|
||||
@mutex.synchronize { rval = yield }
|
||||
rval
|
||||
end
|
||||
|
||||
def self.ctx_load(ctx, *files)
|
||||
files.each do |file|
|
||||
ctx.load(app_root + file)
|
||||
end
|
||||
files.each { |file| ctx.load(app_root + file) }
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
USER_TYPE ||= 'user'
|
||||
GROUP_TYPE ||= 'group'
|
||||
GROUP_MENTIONABLE_TYPE ||= 'group-mentionable'
|
||||
USER_TYPE ||= "user"
|
||||
GROUP_TYPE ||= "group"
|
||||
GROUP_MENTIONABLE_TYPE ||= "group-mentionable"
|
||||
|
||||
def self.add_mentions(doc, user_id: nil)
|
||||
elements = doc.css("span.mention")
|
||||
@ -655,21 +675,19 @@ module PrettyText
|
||||
name.downcase!
|
||||
|
||||
if type = mentions[name]
|
||||
element.name = 'a'
|
||||
element.name = "a"
|
||||
|
||||
element.children = PrettyText::Helpers.format_username(
|
||||
element.children.text
|
||||
)
|
||||
element.children = PrettyText::Helpers.format_username(element.children.text)
|
||||
|
||||
case type
|
||||
when USER_TYPE
|
||||
element['href'] = "#{Discourse.base_path}/u/#{UrlHelper.encode_component(name)}"
|
||||
element["href"] = "#{Discourse.base_path}/u/#{UrlHelper.encode_component(name)}"
|
||||
when GROUP_MENTIONABLE_TYPE
|
||||
element['class'] = 'mention-group notify'
|
||||
element['href'] = "#{Discourse.base_path}/groups/#{UrlHelper.encode_component(name)}"
|
||||
element["class"] = "mention-group notify"
|
||||
element["href"] = "#{Discourse.base_path}/groups/#{UrlHelper.encode_component(name)}"
|
||||
when GROUP_TYPE
|
||||
element['class'] = 'mention-group'
|
||||
element['href'] = "#{Discourse.base_path}/groups/#{UrlHelper.encode_component(name)}"
|
||||
element["class"] = "mention-group"
|
||||
element["href"] = "#{Discourse.base_path}/groups/#{UrlHelper.encode_component(name)}"
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -707,14 +725,16 @@ module PrettyText
|
||||
user = User.find_by(id: user_id)
|
||||
names.each(&:downcase!)
|
||||
|
||||
results = DB.query(sql,
|
||||
names: names,
|
||||
user_type: USER_TYPE,
|
||||
group_type: GROUP_TYPE,
|
||||
group_mentionable_type: GROUP_MENTIONABLE_TYPE,
|
||||
levels: Group.alias_levels(user),
|
||||
user_id: user_id
|
||||
)
|
||||
results =
|
||||
DB.query(
|
||||
sql,
|
||||
names: names,
|
||||
user_type: USER_TYPE,
|
||||
group_type: GROUP_TYPE,
|
||||
group_mentionable_type: GROUP_MENTIONABLE_TYPE,
|
||||
levels: Group.alias_levels(user),
|
||||
user_id: user_id,
|
||||
)
|
||||
|
||||
mentions = {}
|
||||
results.each { |result| mentions[result.name] = result.type }
|
||||
@ -728,17 +748,18 @@ module PrettyText
|
||||
GlobalSetting.s3_cdn_url,
|
||||
GlobalSetting.cdn_url,
|
||||
SiteSetting.external_emoji_url.presence,
|
||||
*SiteSetting.block_hotlinked_media_exceptions.split("|")
|
||||
*SiteSetting.block_hotlinked_media_exceptions.split("|"),
|
||||
]
|
||||
|
||||
patterns = allowed_src_prefixes.compact.map do |url|
|
||||
pattern = Regexp.escape(url)
|
||||
patterns =
|
||||
allowed_src_prefixes.compact.map do |url|
|
||||
pattern = Regexp.escape(url)
|
||||
|
||||
# If 'https://example.com' is allowed, ensure 'https://example.com.blah.com' is not
|
||||
pattern += '(?:/|\z)' if !pattern.ends_with?("\/")
|
||||
# If 'https://example.com' is allowed, ensure 'https://example.com.blah.com' is not
|
||||
pattern += '(?:/|\z)' if !pattern.ends_with?("\/")
|
||||
|
||||
pattern
|
||||
end
|
||||
pattern
|
||||
end
|
||||
|
||||
/\A(data:|#{patterns.join("|")})/
|
||||
end
|
||||
|
Reference in New Issue
Block a user