mirror of
https://github.com/discourse/discourse.git
synced 2024-11-28 02:43:44 +08:00
30990006a9
This reduces chances of errors where consumers of strings mutate inputs and reduces memory usage of the app. Test suite passes now, but there may be some stuff left, so we will run a few sites on a branch prior to merging
150 lines
3.5 KiB
Ruby
150 lines
3.5 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require_dependency 'oneboxer'
|
|
require_dependency 'email_cook'
|
|
|
|
class PostAnalyzer
|
|
|
|
def initialize(raw, topic_id)
|
|
@raw = raw
|
|
@topic_id = topic_id
|
|
@onebox_urls = []
|
|
end
|
|
|
|
def found_oneboxes?
|
|
@onebox_urls.present?
|
|
end
|
|
|
|
def has_oneboxes?
|
|
return false unless @raw.present?
|
|
|
|
cooked_stripped
|
|
found_oneboxes?
|
|
end
|
|
|
|
# What we use to cook posts
|
|
def cook(raw, opts = {})
|
|
cook_method = opts[:cook_method]
|
|
return raw if cook_method == Post.cook_methods[:raw_html]
|
|
|
|
if cook_method == Post.cook_methods[:email]
|
|
cooked = EmailCook.new(raw).cook(opts)
|
|
else
|
|
cooked = PrettyText.cook(raw, opts)
|
|
end
|
|
|
|
result = Oneboxer.apply(cooked) do |url|
|
|
@onebox_urls << url
|
|
Oneboxer.invalidate(url) if opts[:invalidate_oneboxes]
|
|
Oneboxer.cached_onebox(url)
|
|
end
|
|
|
|
cooked = result.to_html if result.changed?
|
|
cooked
|
|
end
|
|
|
|
# How many images are present in the post
|
|
def image_count
|
|
return 0 unless @raw.present?
|
|
|
|
cooked_stripped.css("img").reject do |t|
|
|
if dom_class = t["class"]
|
|
(Post.white_listed_image_classes & dom_class.split).count > 0
|
|
end
|
|
end.count
|
|
end
|
|
|
|
# How many attachments are present in the post
|
|
def attachment_count
|
|
return 0 unless @raw.present?
|
|
|
|
attachments = cooked_stripped.css("a.attachment[href^=\"#{Discourse.store.absolute_base_url}\"]")
|
|
attachments += cooked_stripped.css("a.attachment[href^=\"#{Discourse.store.relative_base_url}\"]") if Discourse.store.internal?
|
|
attachments.count
|
|
end
|
|
|
|
def raw_mentions
|
|
return [] if @raw.blank?
|
|
return @raw_mentions if @raw_mentions.present?
|
|
|
|
raw_mentions = cooked_stripped.css('.mention, .mention-group').map do |e|
|
|
if name = e.inner_text
|
|
name = name[1..-1]
|
|
name = User.normalize_username(name)
|
|
name
|
|
end
|
|
end
|
|
|
|
raw_mentions.compact!
|
|
raw_mentions.uniq!
|
|
@raw_mentions = raw_mentions
|
|
end
|
|
|
|
# from rack ... compat with ruby 2.2
|
|
def self.parse_uri_rfc2396(uri)
|
|
@parser ||= defined?(URI::RFC2396_Parser) ? URI::RFC2396_Parser.new : URI
|
|
@parser.parse(uri)
|
|
end
|
|
|
|
# Count how many hosts are linked in the post
|
|
def linked_hosts
|
|
all_links = raw_links + @onebox_urls
|
|
|
|
return {} if all_links.blank?
|
|
return @linked_hosts if @linked_hosts.present?
|
|
|
|
@linked_hosts = {}
|
|
|
|
all_links.each do |u|
|
|
begin
|
|
uri = self.class.parse_uri_rfc2396(u)
|
|
host = uri.host
|
|
@linked_hosts[host] ||= 1 unless host.nil?
|
|
rescue URI::Error
|
|
# An invalid URI does not count as a host
|
|
next
|
|
end
|
|
end
|
|
|
|
@linked_hosts
|
|
end
|
|
|
|
# Returns an array of all links in a post excluding mentions
|
|
def raw_links
|
|
return [] unless @raw.present?
|
|
return @raw_links if @raw_links.present?
|
|
|
|
@raw_links = []
|
|
cooked_stripped.css("a").each do |l|
|
|
# Don't include @mentions in the link count
|
|
next if link_is_a_mention?(l)
|
|
@raw_links << l['href'].to_s
|
|
end
|
|
|
|
@raw_links
|
|
end
|
|
|
|
# How many links are present in the post
|
|
def link_count
|
|
raw_links.size
|
|
end
|
|
|
|
def cooked_stripped
|
|
@cooked_stripped ||= begin
|
|
doc = Nokogiri::HTML.fragment(cook(@raw, topic_id: @topic_id))
|
|
doc.css("pre .mention, aside.quote > .title, aside.quote .mention, .onebox, .elided").remove
|
|
doc
|
|
end
|
|
end
|
|
|
|
private
|
|
|
|
def link_is_a_mention?(l)
|
|
html_class = l['class']
|
|
return false if html_class.blank?
|
|
href = l['href'].to_s
|
|
html_class.to_s['mention'] && href[/^\/u\//] || href[/^\/users\//]
|
|
end
|
|
|
|
end
|