mirror of
https://github.com/discourse/discourse.git
synced 2024-12-03 02:23:39 +08:00
d554a59102
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.
123 lines
3.9 KiB
Ruby
123 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
|