This repository has been archived on 2023-03-18. You can view files and clone it, but cannot push or open issues or pull requests.
osr-discourse-src/lib/inline_oneboxer.rb
Osama Sayegh d15867463f
FEATURE: Site setting for blocking onebox of URLs that redirect (#16881)
Meta topic: https://meta.discourse.org/t/prevent-to-linkify-when-there-is-a-redirect/226964/2?u=osama.

This commit adds a new site setting `block_onebox_on_redirect` (default off) for blocking oneboxes (full and inline) of URLs that redirect. Note that an initial http → https redirect is still allowed if the redirect location is identical to the source (minus the scheme of course). For example, if a user includes a link to `http://example.com/page` and the link resolves to `https://example.com/page`, then the link will onebox (assuming it can be oneboxed) even if the setting is enabled. The reason for this is a user may type out a URL (i.e. the URL is short and memorizable) with http and since a lot of sites support TLS with http traffic automatically redirected to https, so we should still allow the URL to onebox.
2022-05-23 13:52:06 +03:00

118 lines
3.1 KiB
Ruby

# frozen_string_literal: true
class InlineOneboxer
MIN_TITLE_LENGTH = 2
def initialize(urls, opts = nil)
@urls = urls
@opts = opts || {}
end
def process
@urls.map { |url| InlineOneboxer.lookup(url, @opts) }.compact
end
def self.invalidate(url)
Discourse.cache.delete(cache_key(url))
end
def self.cache_lookup(url)
Discourse.cache.read(cache_key(url))
end
def self.lookup(url, opts = nil)
opts ||= {}
opts = opts.with_indifferent_access
unless opts[:skip_cache] || opts[:invalidate]
cached = cache_lookup(url)
return cached if cached.present?
end
return unless url
if route = Discourse.route_for(url)
if route[:controller] == "topics"
if topic = Oneboxer.local_topic(url, route, opts)
opts[:skip_cache] = true
post_number = [route[:post_number].to_i, topic.highest_post_number].min
if post_number > 1
opts[:post_number] = post_number
opts[:post_author] = post_author_for_title(topic, post_number)
end
return onebox_for(url, topic.title, opts)
else
# not permitted to see topic
return nil
end
end
end
always_allow = SiteSetting.enable_inline_onebox_on_all_domains
allowed_domains = SiteSetting.allowed_inline_onebox_domains&.split('|') unless always_allow
if always_allow || allowed_domains
uri = begin
URI(url)
rescue URI::Error
end
if uri.present? &&
uri.hostname.present? &&
(always_allow || allowed_domains.include?(uri.hostname)) &&
!Onebox::DomainChecker.is_blocked?(uri.hostname)
if SiteSetting.block_onebox_on_redirect
max_redirects = 0
end
title = RetrieveTitle.crawl(
url,
max_redirects: max_redirects,
initial_https_redirect_ignore_limit: SiteSetting.block_onebox_on_redirect
)
title = nil if title && title.length < MIN_TITLE_LENGTH
return onebox_for(url, title, opts)
end
end
nil
end
private
def self.onebox_for(url, title, opts)
title = title && Emoji.gsub_emoji_to_unicode(title)
if title && opts[:post_number]
title += " - "
if opts[:post_author]
title += I18n.t(
"inline_oneboxer.topic_page_title_post_number_by_user",
post_number: opts[:post_number],
username: opts[:post_author]
)
else
title += I18n.t(
"inline_oneboxer.topic_page_title_post_number",
post_number: opts[:post_number]
)
end
end
onebox = { url: url, title: title && Emoji.gsub_emoji_to_unicode(title) }
Discourse.cache.write(cache_key(url), onebox, expires_in: 1.day) if !opts[:skip_cache]
onebox
end
def self.cache_key(url)
"inline_onebox:#{url}"
end
def self.post_author_for_title(topic, post_number)
guardian = Guardian.new
post = topic.posts.find_by(post_number: post_number)
author = post&.user
if author && guardian.can_see_post?(post) && post.post_type == Post.types[:regular]
author.username
end
end
end