283 lines
8.6 KiB
Ruby
283 lines
8.6 KiB
Ruby
# Responsible for creating posts and topics
|
|
#
|
|
require_dependency 'rate_limiter'
|
|
|
|
class PostCreator
|
|
|
|
attr_reader :errors, :opts
|
|
|
|
def self.create(user,opts)
|
|
self.new(user,opts).create
|
|
end
|
|
|
|
# Acceptable options:
|
|
#
|
|
# raw - raw text of post
|
|
# image_sizes - We can pass a list of the sizes of images in the post as a shortcut.
|
|
# invalidate_oneboxes - Whether to force invalidation of oneboxes in this post
|
|
# acting_user - The user performing the action might be different than the user
|
|
# who is the post "author." For example when copying posts to a new
|
|
# topic.
|
|
# created_at - Post creation time (optional)
|
|
#
|
|
# When replying to a topic:
|
|
# topic_id - topic we're replying to
|
|
# reply_to_post_number - post number we're replying to
|
|
#
|
|
# When creating a topic:
|
|
# title - New topic title
|
|
# archetype - Topic archetype
|
|
# category - Category to assign to topic
|
|
# target_usernames - comma delimited list of usernames for membership (private message)
|
|
# target_group_names - comma delimited list of groups for membership (private message)
|
|
# meta_data - Topic meta data hash
|
|
def initialize(user, opts)
|
|
# TODO: we should reload user in case it is tainted, should take in a user_id as opposed to user
|
|
# If we don't do this we introduce a rather risky dependency
|
|
@user = user
|
|
@opts = opts
|
|
@spam = false
|
|
|
|
raise Discourse::InvalidParameters.new(:raw) if @opts[:raw].blank?
|
|
end
|
|
|
|
# True if the post was considered spam
|
|
def spam?
|
|
@spam
|
|
end
|
|
|
|
def guardian
|
|
@guardian ||= Guardian.new(@user)
|
|
end
|
|
|
|
def create
|
|
topic = nil
|
|
post = nil
|
|
new_topic = false
|
|
|
|
Post.transaction do
|
|
if @opts[:topic_id].blank?
|
|
topic = create_topic
|
|
new_topic = true
|
|
else
|
|
topic = Topic.where(id: @opts[:topic_id]).first
|
|
guardian.ensure_can_create!(Post, topic)
|
|
end
|
|
|
|
post = topic.posts.new(raw: @opts[:raw],
|
|
user: @user,
|
|
reply_to_post_number: @opts[:reply_to_post_number])
|
|
|
|
post.post_type = @opts[:post_type] if @opts[:post_type].present?
|
|
post.no_bump = @opts[:no_bump] if @opts[:no_bump].present?
|
|
post.extract_quoted_post_numbers
|
|
post.acting_user = @opts[:acting_user] if @opts[:acting_user].present?
|
|
post.created_at = Time.zone.parse(@opts[:created_at].to_s) if @opts[:created_at].present?
|
|
|
|
post.image_sizes = @opts[:image_sizes] if @opts[:image_sizes].present?
|
|
post.invalidate_oneboxes = @opts[:invalidate_oneboxes] if @opts[:invalidate_oneboxes].present?
|
|
|
|
|
|
# If the post has host spam, roll it back.
|
|
if post.has_host_spam?
|
|
post.errors.add(:base, I18n.t(:spamming_host))
|
|
@errors = post.errors
|
|
@spam = true
|
|
raise ActiveRecord::Rollback.new
|
|
end
|
|
|
|
unless post.save
|
|
@errors = post.errors
|
|
raise ActiveRecord::Rollback.new
|
|
end
|
|
|
|
# Extract links
|
|
TopicLink.extract_from(post)
|
|
|
|
# Store unique post key
|
|
if SiteSetting.unique_posts_mins > 0
|
|
$redis.setex(post.unique_post_key, SiteSetting.unique_posts_mins.minutes.to_i, "1")
|
|
end
|
|
|
|
# send a mail to notify users in case of a private message
|
|
if topic.private_message?
|
|
topic.allowed_users.where(["users.email_private_messages = true and users.id != ?", @user.id]).each do |u|
|
|
Jobs.enqueue_in(SiteSetting.email_time_window_mins.minutes,
|
|
:user_email,
|
|
type: :private_message,
|
|
user_id: u.id,
|
|
post_id: post.id
|
|
)
|
|
end
|
|
|
|
clear_possible_flags(topic) if post.post_number > 1 && topic.user_id != post.user_id
|
|
end
|
|
|
|
# Track the topic
|
|
TopicUser.auto_track(@user.id, topic.id, TopicUser.notification_reasons[:created_post])
|
|
|
|
# We don't count replies to your own topics
|
|
if @user.id != topic.user_id
|
|
@user.update_topic_reply_count
|
|
end
|
|
|
|
@user.last_posted_at = post.created_at
|
|
@user.save!
|
|
|
|
if post.post_number > 1
|
|
MessageBus.publish("/topic/#{post.topic_id}",{
|
|
id: post.id,
|
|
created_at: post.created_at,
|
|
user: BasicUserSerializer.new(post.user).as_json(root: false),
|
|
post_number: post.post_number
|
|
},
|
|
group_ids: secure_group_ids(topic)
|
|
)
|
|
end
|
|
|
|
# Advance the draft sequence
|
|
post.advance_draft_sequence
|
|
|
|
# Save the quote relationships
|
|
post.save_reply_relationships
|
|
end
|
|
|
|
# We need to enqueue jobs after the transaction. Otherwise they might begin before the data has
|
|
# been comitted.
|
|
topic_id = @opts[:topic_id] || topic.try(:id)
|
|
Jobs.enqueue(:feature_topic_users, topic_id: topic.id) if topic_id.present?
|
|
if post
|
|
post.trigger_post_process
|
|
after_topic_create(topic) if new_topic
|
|
end
|
|
|
|
post
|
|
end
|
|
|
|
|
|
# Shortcut
|
|
def self.create(user, opts)
|
|
PostCreator.new(user, opts).create
|
|
end
|
|
|
|
protected
|
|
|
|
def secure_group_ids(topic)
|
|
@secure_group_ids ||= if topic.category && topic.category.secure?
|
|
topic.category.groups.select("groups.id").map{|g| g.id}
|
|
end
|
|
end
|
|
|
|
def after_topic_create(topic)
|
|
|
|
# Don't publish invisible topics
|
|
return unless topic.visible?
|
|
|
|
return if topic.private_message?
|
|
|
|
topic.posters = topic.posters_summary
|
|
topic.posts_count = 1
|
|
topic_json = TopicListItemSerializer.new(topic).as_json
|
|
|
|
group_ids = secure_group_ids(topic)
|
|
|
|
MessageBus.publish("/latest", topic_json, group_ids: group_ids)
|
|
|
|
# If it has a category, add it to the category views too
|
|
if topic.category
|
|
MessageBus.publish("/category/#{topic.category.slug}", topic_json, group_ids: group_ids)
|
|
end
|
|
end
|
|
|
|
def create_topic
|
|
topic_params = {title: @opts[:title], user_id: @user.id, last_post_user_id: @user.id}
|
|
topic_params[:archetype] = @opts[:archetype] if @opts[:archetype].present?
|
|
topic_params[:subtype] = @opts[:subtype] if @opts[:subtype].present?
|
|
|
|
guardian.ensure_can_create!(Topic)
|
|
|
|
category = Category.where(name: @opts[:category]).first
|
|
topic_params[:category_id] = category.id if category.present?
|
|
topic_params[:meta_data] = @opts[:meta_data] if @opts[:meta_data].present?
|
|
topic_params[:created_at] = Time.zone.parse(@opts[:created_at].to_s) if @opts[:created_at].present?
|
|
|
|
topic = Topic.new(topic_params)
|
|
|
|
if @opts[:auto_close_days]
|
|
guardian.ensure_can_moderate!(topic)
|
|
topic.auto_close_days = @opts[:auto_close_days]
|
|
end
|
|
|
|
if @opts[:archetype] == Archetype.private_message
|
|
|
|
topic.subtype = TopicSubtype.user_to_user unless topic.subtype
|
|
|
|
unless @opts[:target_usernames].present? || @opts[:target_group_names].present?
|
|
topic.errors.add(:archetype, :cant_send_pm)
|
|
@errors = topic.errors
|
|
raise ActiveRecord::Rollback.new
|
|
end
|
|
|
|
add_users(topic,@opts[:target_usernames])
|
|
add_groups(topic,@opts[:target_group_names])
|
|
topic.topic_allowed_users.build(user_id: @user.id)
|
|
end
|
|
|
|
unless topic.save
|
|
@errors = topic.errors
|
|
raise ActiveRecord::Rollback.new
|
|
end
|
|
|
|
topic
|
|
end
|
|
|
|
def clear_possible_flags(topic)
|
|
# at this point we know the topic is a PM and has been replied to ... check if we need to clear any flags
|
|
#
|
|
first_post = Post.select(:id).where(topic_id: topic.id).where('post_number = 1').first
|
|
post_action = nil
|
|
|
|
if first_post
|
|
post_action = PostAction.where(
|
|
related_post_id: first_post.id,
|
|
deleted_at: nil,
|
|
post_action_type_id: PostActionType.types[:notify_moderators]
|
|
).first
|
|
end
|
|
|
|
if post_action
|
|
post_action.remove_act!(@user)
|
|
end
|
|
end
|
|
|
|
def add_users(topic, usernames)
|
|
return unless usernames
|
|
usernames = usernames.split(',')
|
|
User.where(username: usernames).each do |u|
|
|
|
|
unless guardian.can_send_private_message?(u)
|
|
topic.errors.add(:archetype, :cant_send_pm)
|
|
@errors = topic.errors
|
|
raise ActiveRecord::Rollback.new
|
|
end
|
|
|
|
topic.topic_allowed_users.build(user_id: u.id)
|
|
end
|
|
end
|
|
|
|
def add_groups(topic, groups)
|
|
return unless groups
|
|
groups = groups.split(',')
|
|
Group.where(name: groups).each do |g|
|
|
|
|
unless guardian.can_send_private_message?(g)
|
|
topic.errors.add(:archetype, :cant_send_pm)
|
|
@errors = topic.errors
|
|
raise ActiveRecord::Rollback.new
|
|
end
|
|
|
|
topic.topic_allowed_groups.build(group_id: g.id)
|
|
end
|
|
end
|
|
end
|