2013-02-05 14:16:51 -05:00
# Responsible for creating posts and topics
#
require_dependency 'rate_limiter'
2013-06-04 20:13:01 +02:00
require_dependency 'topic_creator'
2014-02-17 01:57:37 -05:00
require_dependency 'post_jobs_enqueuer'
2013-02-05 14:16:51 -05:00
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-05-20 16:44:06 +10:00
# created_at - Post creation time (optional)
2013-07-22 11:40:39 +10:00
# auto_track - Automatically track this topic if needed (default true)
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
2013-06-21 11:36:33 -04:00
# cooking_options - Options for rendering the text
#
2013-02-05 14:16:51 -05:00
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-06-21 11:36:33 -04:00
@opts = opts || { }
2013-05-10 16:58:23 -04:00
@spam = false
2013-02-05 14:16:51 -05:00
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
2013-06-04 20:13:01 +02:00
@topic = nil
@post = nil
2013-02-05 14:16:51 -05:00
Post . transaction do
2013-06-04 20:13:01 +02:00
setup_topic
setup_post
rollback_if_host_spam_detected
save_post
extract_links
store_unique_post_key
2013-08-07 13:02:49 -04:00
consider_clearing_flags
2013-06-04 20:13:01 +02:00
track_topic
2013-07-22 15:06:53 +10:00
update_topic_stats
2013-06-04 20:13:01 +02:00
update_user_counts
2014-04-03 14:42:26 -04:00
create_embedded_topic
2014-03-18 13:12:07 +11:00
2013-06-04 20:13:01 +02:00
publish
2013-09-06 14:07:23 +10:00
ensure_in_allowed_users if guardian . is_staff?
2013-06-04 20:13:01 +02:00
@post . advance_draft_sequence
@post . save_reply_relationships
2013-02-05 14:16:51 -05:00
end
2014-03-18 15:22:39 +11:00
if @post
PostAlerter . post_created ( @post )
handle_spam
track_latest_on_category
enqueue_jobs
end
2014-02-10 20:29:31 +01:00
2013-06-04 20:13:01 +02:00
@post
2013-02-05 14:16:51 -05:00
end
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-06-09 18:48:44 +02:00
def self . before_create_tasks ( post )
2014-02-10 20:29:31 +01:00
set_reply_user_id ( post )
2013-06-09 18:48:44 +02:00
2013-12-10 13:47:07 -05:00
post . word_count = post . raw . scan ( / \ w+ / ) . size
2013-06-09 18:48:44 +02:00
post . post_number || = Topic . next_post_number ( post . topic_id , post . reply_to_post_number . present? )
2013-06-21 11:36:33 -04:00
cooking_options = post . cooking_options || { }
cooking_options [ :topic_id ] = post . topic_id
post . cooked || = post . cook ( post . raw , cooking_options )
2013-06-09 18:48:44 +02:00
post . sort_order = post . post_number
DiscourseEvent . trigger ( :before_create_post , post )
post . last_version_at || = Time . now
end
2014-02-10 20:29:31 +01:00
def self . set_reply_user_id ( post )
return unless post . reply_to_post_number . present?
2014-05-06 14:41:59 +01:00
post . reply_to_user_id || = Post . select ( :user_id ) . find_by ( topic_id : post . topic_id , post_number : post . reply_to_post_number ) . try ( :user_id )
2014-02-10 20:29:31 +01:00
end
2013-06-09 18:48:44 +02:00
2013-05-02 15:15:17 +10:00
protected
2014-04-03 14:42:26 -04:00
# You can supply an `embed_url` for a post to set up the embedded relationship.
# This is used by the wp-discourse plugin to associate a remote post with a
# discourse post.
def create_embedded_topic
return unless @opts [ :embed_url ] . present?
TopicEmbed . create! ( topic_id : @post . topic_id , post_id : @post . id , embed_url : @opts [ :embed_url ] )
end
2014-02-10 20:29:31 +01:00
def handle_spam
if @spam
GroupMessage . create ( Group [ :moderators ] . name ,
:spam_post_blocked ,
{ user : @user ,
limit_once_per : 24 . hours ,
message_params : { domains : @post . linked_hosts . keys . join ( ', ' ) } } )
elsif @post && ! @post . errors . present? && ! @opts [ :skip_validations ]
SpamRulesEnforcer . enforce! ( @post )
2013-10-17 17:44:56 +11:00
end
end
2014-02-10 20:29:31 +01:00
def track_latest_on_category
return unless @post && @post . errors . count == 0 && @topic && @topic . category_id
Category . where ( id : @topic . category_id ) . update_all ( latest_post_id : @post . id )
Category . where ( id : @topic . category_id ) . update_all ( latest_topic_id : @topic . id ) if @post . post_number == 1
end
2013-09-06 14:07:23 +10:00
def ensure_in_allowed_users
return unless @topic . private_message?
unless @topic . topic_allowed_users . where ( user_id : @user . id ) . exists?
@topic . topic_allowed_users . create! ( user_id : @user . id )
end
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
#
2014-05-06 14:41:59 +01:00
first_post = Post . select ( :id ) . where ( topic_id : topic . id ) . find_by ( " post_number = 1 " )
2013-05-13 11:48:01 +10:00
post_action = nil
if first_post
2014-05-06 14:41:59 +01:00
post_action = PostAction . find_by ( related_post_id : first_post . id , deleted_at : nil , post_action_type_id : PostActionType . types [ :notify_moderators ] )
2013-05-13 11:48:01 +10:00
end
if post_action
post_action . remove_act! ( @user )
end
end
2013-06-04 20:13:01 +02:00
private
def setup_topic
2014-02-17 01:57:37 -05:00
if new_topic?
2013-06-04 20:13:01 +02:00
topic_creator = TopicCreator . new ( @user , guardian , @opts )
2013-06-07 12:36:37 -04:00
begin
topic = topic_creator . create
@errors = topic_creator . errors
rescue ActiveRecord :: Rollback = > ex
# In the event of a rollback, grab the errors from the topic
@errors = topic_creator . errors
raise ex
end
2013-06-04 20:13:01 +02:00
else
2014-05-06 14:41:59 +01:00
topic = Topic . find_by ( id : @opts [ :topic_id ] )
2013-06-04 20:13:01 +02:00
guardian . ensure_can_create! ( Post , topic )
2013-05-02 15:15:17 +10:00
end
2013-06-04 20:13:01 +02:00
@topic = topic
2013-05-02 15:15:17 +10:00
end
2013-07-22 15:06:53 +10:00
def update_topic_stats
# Update attributes on the topic - featured users and last posted.
attrs = { last_posted_at : @post . created_at , last_post_user_id : @post . user_id }
attrs [ :bumped_at ] = @post . created_at unless @post . no_bump
2013-12-10 13:47:07 -05:00
attrs [ :word_count ] = ( @topic . word_count || 0 ) + @post . word_count
2014-03-18 13:40:40 -04:00
attrs [ :excerpt ] = @post . excerpt ( 220 , strip_links : true ) if new_topic?
2013-07-22 15:06:53 +10:00
@topic . update_attributes ( attrs )
end
2013-06-04 20:13:01 +02:00
def setup_post
post = @topic . posts . new ( raw : @opts [ :raw ] ,
2013-06-29 17:57:10 -04:00
user : @user ,
reply_to_post_number : @opts [ :reply_to_post_number ] )
2013-06-04 20:13:01 +02:00
2013-06-21 11:36:33 -04:00
# Attributes we pass through to the post instance if present
2014-02-10 20:29:31 +01:00
[ :post_type , :no_bump , :cooking_options , :image_sizes , :acting_user , :invalidate_oneboxes , :cook_method ] . each do | a |
2013-06-21 11:36:33 -04:00
post . send ( " #{ a } = " , @opts [ a ] ) if @opts [ a ] . present?
end
2013-06-04 20:13:01 +02:00
post . extract_quoted_post_numbers
post . created_at = Time . zone . parse ( @opts [ :created_at ] . to_s ) if @opts [ :created_at ] . present?
2014-02-10 20:29:31 +01:00
2013-06-04 20:13:01 +02:00
@post = post
end
def rollback_if_host_spam_detected
2014-03-18 18:02:33 -04:00
return if @opts [ :skip_validations ]
2013-06-04 20:13:01 +02:00
if @post . has_host_spam?
@post . errors . add ( :base , I18n . t ( :spamming_host ) )
@errors = @post . errors
@spam = true
raise ActiveRecord :: Rollback . new
2013-05-28 09:13:53 +10:00
end
end
2013-05-02 15:15:17 +10:00
2013-06-04 20:13:01 +02:00
def save_post
2013-07-02 12:22:56 +10:00
unless @post . save ( validate : ! @opts [ :skip_validations ] )
2013-06-04 20:13:01 +02:00
@errors = @post . errors
2013-05-28 09:13:53 +10:00
raise ActiveRecord :: Rollback . new
2013-05-02 15:15:17 +10:00
end
end
2013-06-04 20:13:01 +02:00
def store_unique_post_key
2013-09-09 16:17:31 -04:00
@post . store_unique_post_key
2013-06-04 20:13:01 +02:00
end
2013-08-07 13:02:49 -04:00
def consider_clearing_flags
2014-02-10 20:29:31 +01:00
return unless @topic . private_message? && @post . post_number > 1 && @topic . user_id != @post . user_id
clear_possible_flags ( @topic )
2013-06-04 20:13:01 +02:00
end
def update_user_counts
# We don't count replies to your own topics
if @user . id != @topic . user_id
2013-10-04 13:28:49 +10:00
@user . user_stat . update_topic_reply_count
@user . user_stat . save!
2013-06-04 20:13:01 +02:00
end
@user . last_posted_at = @post . created_at
@user . save!
end
def publish
2014-02-10 20:29:31 +01:00
return unless @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
} ,
2014-03-24 12:19:08 +11:00
group_ids : @topic . secure_group_ids
2014-02-10 20:29:31 +01:00
)
2013-06-04 20:13:01 +02:00
end
def extract_links
TopicLink . extract_from ( @post )
end
def track_topic
2014-02-10 20:29:31 +01:00
return if @opts [ :auto_track ] == false
TopicUser . auto_track ( @user . id , @topic . id , TopicUser . notification_reasons [ :created_post ] )
# Update topic user data
TopicUser . change ( @post . user . id ,
@post . topic . id ,
posted : true ,
last_read_post_number : @post . post_number ,
seen_post_count : @post . post_number )
2013-06-04 20:13:01 +02:00
end
def enqueue_jobs
2014-02-10 20:29:31 +01:00
return unless @post && ! @post . errors . present?
2014-02-17 01:57:37 -05:00
PostJobsEnqueuer . new ( @post , @topic , new_topic? ) . enqueue_jobs
end
2014-02-10 20:29:31 +01:00
2014-02-17 01:57:37 -05:00
def new_topic?
@opts [ :topic_id ] . blank?
2013-06-04 20:13:01 +02:00
end
2014-02-17 01:57:37 -05:00
2013-02-05 14:16:51 -05:00
end