discourse/app/models/topic_link.rb
Robin Ward d554a59102 Support for a new site setting: newuser_spam_host_threshold. If a new user posts a link
to the same host enough tiles, they will not be able to post the same link again.

Additionally, the site will flag all their previous posts with links as spam and they will
be instantly hidden via the auto hide workflow.
2013-05-16 12:19:50 -04:00

122 lines
3.9 KiB
Ruby

require 'uri'
require_dependency 'slug'
class TopicLink < ActiveRecord::Base
belongs_to :topic
belongs_to :user
belongs_to :post
belongs_to :link_topic, class_name: 'Topic'
validates_presence_of :url
validates_length_of :url, maximum: 500
validates_uniqueness_of :url, scope: [:topic_id, :post_id]
has_many :topic_link_clicks
validate :link_to_self
# Make sure a topic can't link to itself
def link_to_self
errors.add(:base, "can't link to the same topic") if (topic_id == link_topic_id)
end
# Extract any urls in body
def self.extract_from(post)
return unless post.present?
TopicLink.transaction do
added_urls = []
reflected_urls = []
PrettyText
.extract_links(post.cooked)
.map{|u| [u, URI.parse(u)] rescue nil}
.reject{|u,p| p.nil?}
.uniq{|u,p| u}
.each do |url, parsed|
begin
internal = false
topic_id = nil
post_number = nil
if parsed.host == Discourse.current_hostname || !parsed.host
internal = true
route = Rails.application.routes.recognize_path(parsed.path)
# We aren't interested in tracking internal links to users
next if route[:controller] == 'users'
topic_id = route[:topic_id]
post_number = route[:post_number] || 1
# Store the canonical URL
topic = Topic.where(id: topic_id).first
if topic.present?
url = "#{Discourse.base_url}#{topic.relative_url}"
url << "/#{post_number}" if post_number.to_i > 1
end
end
# Skip linking to ourselves
next if topic_id == post.topic_id
added_urls << url
TopicLink.create(post_id: post.id,
user_id: post.user_id,
topic_id: post.topic_id,
url: url,
domain: parsed.host || Discourse.current_hostname,
internal: internal,
link_topic_id: topic_id)
# Create the reflection if we can
if topic_id.present?
topic = Topic.where(id: topic_id).first
if topic && post.topic.archetype != 'private_message' && topic.archetype != 'private_message'
prefix = Discourse.base_url
reflected_post = nil
if post_number.present?
reflected_post = Post.where(topic_id: topic_id, post_number: post_number.to_i).first
end
reflected_url = "#{prefix}#{post.topic.relative_url(post.post_number)}"
reflected_urls << reflected_url
TopicLink.create(user_id: post.user_id,
topic_id: topic_id,
post_id: reflected_post.try(:id),
url: reflected_url,
domain: Discourse.current_hostname,
reflection: true,
internal: true,
link_topic_id: post.topic_id,
link_post_id: post.id)
end
end
rescue URI::InvalidURIError
# if the URI is invalid, don't store it.
rescue ActionController::RoutingError
# If we can't find the route, no big deal
end
end
# Remove links that aren't there anymore
if added_urls.present?
TopicLink.delete_all ["(url not in (:urls)) AND (post_id = :post_id)", urls: added_urls, post_id: post.id]
TopicLink.delete_all ["(url not in (:urls)) AND (link_post_id = :post_id)", urls: reflected_urls, post_id: post.id]
else
TopicLink.delete_all ["post_id = :post_id OR link_post_id = :post_id", post_id: post.id]
end
end
end
end