2013-02-05 14:16:51 -05:00
# Responsible for creating posts and topics
#
require_dependency 'rate_limiter'
class PostCreator
2013-03-18 13:55:34 -04:00
attr_reader :errors , :opts
2013-02-05 14:16:51 -05:00
# Acceptable options:
#
# raw - raw text of post
2013-02-25 19:42:20 +03:00
# image_sizes - We can pass a list of the sizes of images in the post as a shortcut.
2013-03-18 13:55:34 -04:00
# invalidate_oneboxes - Whether to force invalidation of oneboxes in this post
2013-05-13 14:06:16 -04:00
# 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.
2013-02-05 14:16:51 -05:00
#
# 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)
2013-05-09 17:37:34 +10:00
# target_group_names - comma delimited list of groups for membership (private message)
2013-02-05 14:16:51 -05:00
# meta_data - Topic meta data hash
def initialize ( user , opts )
2013-04-05 15:29:46 +11:00
# 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
2013-02-25 19:42:20 +03:00
@user = user
2013-02-05 14:16:51 -05:00
@opts = opts
2013-05-10 16:58:23 -04:00
@spam = false
2013-02-05 14:16:51 -05:00
raise Discourse :: InvalidParameters . new ( :raw ) if @opts [ :raw ] . blank?
end
2013-05-10 16:58:23 -04:00
# True if the post was considered spam
def spam?
@spam
end
2013-02-05 14:16:51 -05:00
def guardian
@guardian || = Guardian . new ( @user )
end
def create
topic = nil
post = nil
2013-05-16 15:03:03 +10:00
new_topic = false
2013-02-05 14:16:51 -05:00
Post . transaction do
if @opts [ :topic_id ] . blank?
2013-05-16 15:03:03 +10:00
topic = create_topic
new_topic = true
2013-02-05 14:16:51 -05:00
else
topic = Topic . where ( id : @opts [ :topic_id ] ) . first
guardian . ensure_can_create! ( Post , topic )
end
2013-02-25 19:42:20 +03:00
post = topic . posts . new ( raw : @opts [ :raw ] ,
2013-02-05 14:16:51 -05:00
user : @user ,
reply_to_post_number : @opts [ :reply_to_post_number ] )
2013-03-28 16:40:54 -04:00
post . post_type = @opts [ :post_type ] if @opts [ :post_type ] . present?
post . no_bump = @opts [ :no_bump ] if @opts [ :no_bump ] . present?
2013-03-18 15:54:08 -04:00
post . extract_quoted_post_numbers
2013-05-13 14:06:16 -04:00
post . acting_user = @opts [ :acting_user ] if @opts [ :acting_user ] . present?
2013-05-18 21:24:29 +02:00
post . created_at = Time . zone . parse ( @opts [ :created_at ] . to_s ) if @opts [ :created_at ] . present?
2013-03-18 15:54:08 -04:00
2013-02-05 14:16:51 -05:00
post . image_sizes = @opts [ :image_sizes ] if @opts [ :image_sizes ] . present?
2013-03-18 13:55:34 -04:00
post . invalidate_oneboxes = @opts [ :invalidate_oneboxes ] if @opts [ :invalidate_oneboxes ] . present?
2013-05-10 16:58:23 -04:00
# 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
2013-02-05 14:16:51 -05:00
unless post . save
@errors = post . errors
2013-02-25 19:42:20 +03:00
raise ActiveRecord :: Rollback . new
2013-02-05 14:16:51 -05:00
end
2013-02-25 19:42:20 +03:00
# Extract links
2013-02-05 14:16:51 -05:00
TopicLink . extract_from ( post )
2013-03-18 13:55:34 -04:00
# 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 |
2013-05-16 15:03:03 +10:00
Jobs . enqueue_in ( SiteSetting . email_time_window_mins . minutes ,
:user_email ,
type : :private_message ,
user_id : u . id ,
post_id : post . id
)
2013-03-18 13:55:34 -04:00
end
2013-05-13 11:48:01 +10:00
clear_possible_flags ( topic ) if post . post_number > 1 && topic . user_id != post . user_id
2013-03-18 13:55:34 -04:00
end
# Track the topic
TopicUser . auto_track ( @user . id , topic . id , TopicUser . notification_reasons [ :created_post ] )
2013-05-15 15:16:42 -04:00
# We don't count replies to your own topics
2013-04-05 15:29:46 +11:00
if @user . id != topic . user_id
2013-04-05 16:48:38 +11:00
@user . update_topic_reply_count
2013-04-05 15:29:46 +11:00
end
@user . last_posted_at = post . created_at
@user . save!
2013-03-18 14:45:05 -04:00
2013-05-16 15:03:03 +10:00
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
2013-03-18 15:12:31 -04:00
# Advance the draft sequence
post . advance_draft_sequence
2013-03-18 15:54:08 -04:00
# Save the quote relationships
post . save_reply_relationships
2013-02-05 14:16:51 -05:00
end
2013-04-08 10:51:01 -04:00
# We need to enqueue jobs after the transaction. Otherwise they might begin before the data has
# been comitted.
2013-04-08 11:23:03 -04:00
topic_id = @opts [ :topic_id ] || topic . try ( :id )
Jobs . enqueue ( :feature_topic_users , topic_id : topic . id ) if topic_id . present?
2013-05-16 15:03:03 +10:00
if post
post . trigger_post_process
after_topic_create ( topic ) if new_topic
end
2013-04-08 10:51:01 -04:00
2013-02-05 14:16:51 -05:00
post
end
2013-04-08 10:51:01 -04:00
2013-02-05 14:16:51 -05:00
# Shortcut
def self . create ( user , opts )
2013-02-25 19:42:20 +03:00
PostCreator . new ( user , opts ) . create
2013-02-05 14:16:51 -05:00
end
2013-05-02 15:15:17 +10:00
protected
2013-05-16 15:03:03 +10:00
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?
2013-05-18 21:24:29 +02:00
topic_params [ :created_at ] = Time . zone . parse ( @opts [ :created_at ] . to_s ) if @opts [ :created_at ] . present?
2013-05-16 15:03:03 +10:00
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
2013-05-13 11:48:01 +10:00
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
2013-05-02 15:15:17 +10:00
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
2013-02-05 14:16:51 -05:00
end