2019-05-02 18:17:27 -04:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2015-03-26 16:57:50 -04:00
|
|
|
class TopicCreator
|
2015-08-11 01:46:21 -04:00
|
|
|
|
|
|
|
attr_reader :user, :guardian, :opts
|
|
|
|
|
2015-03-26 16:57:50 -04:00
|
|
|
include HasErrors
|
2013-06-04 14:13:01 -04:00
|
|
|
|
2013-07-21 21:40:39 -04:00
|
|
|
def self.create(user, guardian, opts)
|
|
|
|
self.new(user, guardian, opts).create
|
|
|
|
end
|
|
|
|
|
2013-06-04 14:13:01 -04:00
|
|
|
def initialize(user, guardian, opts)
|
|
|
|
@user = user
|
|
|
|
@guardian = guardian
|
|
|
|
@opts = opts
|
2014-09-08 11:11:56 -04:00
|
|
|
@added_users = []
|
2013-06-04 14:13:01 -04:00
|
|
|
end
|
|
|
|
|
2015-03-26 16:57:50 -04:00
|
|
|
def valid?
|
|
|
|
topic = Topic.new(setup_topic_params)
|
2015-08-11 01:46:21 -04:00
|
|
|
# validate? will clear the error hash
|
|
|
|
# so we fire the validation event after
|
|
|
|
# this allows us to add errors
|
|
|
|
valid = topic.valid?
|
2016-04-25 15:55:15 -04:00
|
|
|
|
2022-12-01 05:26:35 -05:00
|
|
|
validate_visibility(topic)
|
|
|
|
|
2021-04-18 19:43:50 -04:00
|
|
|
category = find_category
|
|
|
|
if category.present? && guardian.can_tag?(topic)
|
2021-10-28 02:29:46 -04:00
|
|
|
tags = @opts[:tags].presence || []
|
|
|
|
existing_tags = tags.present? ? Tag.where(name: tags) : []
|
|
|
|
valid_tags = guardian.can_create_tag? ? tags : existing_tags
|
2021-04-18 19:43:50 -04:00
|
|
|
|
2022-03-28 14:25:26 -04:00
|
|
|
# all add to topic.errors
|
2021-10-28 02:29:46 -04:00
|
|
|
DiscourseTagging.validate_min_required_tags_for_category(guardian, topic, category, valid_tags)
|
|
|
|
DiscourseTagging.validate_required_tags_from_group(guardian, topic, category, existing_tags)
|
2022-03-28 14:25:26 -04:00
|
|
|
DiscourseTagging.validate_category_restricted_tags(guardian, topic, category, valid_tags)
|
2021-04-18 19:43:50 -04:00
|
|
|
end
|
|
|
|
|
2015-08-11 01:46:21 -04:00
|
|
|
DiscourseEvent.trigger(:after_validate_topic, topic, self)
|
|
|
|
valid &&= topic.errors.empty?
|
|
|
|
|
|
|
|
add_errors_from(topic) unless valid
|
|
|
|
|
|
|
|
valid
|
2015-03-26 16:57:50 -04:00
|
|
|
end
|
|
|
|
|
2013-06-04 14:13:01 -04:00
|
|
|
def create
|
2015-03-26 16:57:50 -04:00
|
|
|
topic = Topic.new(setup_topic_params)
|
2022-12-01 05:26:35 -05:00
|
|
|
|
|
|
|
validate_visibility!(topic)
|
2016-05-04 14:02:47 -04:00
|
|
|
setup_tags(topic)
|
2020-02-05 04:32:03 -05:00
|
|
|
|
2019-05-25 09:53:03 -04:00
|
|
|
if fields = @opts[:custom_fields]
|
2020-02-05 04:32:03 -05:00
|
|
|
topic.custom_fields.merge!(fields)
|
2019-05-25 09:53:03 -04:00
|
|
|
end
|
2016-05-04 14:02:47 -04:00
|
|
|
|
2015-08-11 01:46:21 -04:00
|
|
|
DiscourseEvent.trigger(:before_create_topic, topic, self)
|
2013-06-04 14:13:01 -04:00
|
|
|
|
2015-03-26 16:57:50 -04:00
|
|
|
setup_auto_close_time(topic)
|
|
|
|
process_private_message(topic)
|
|
|
|
save_topic(topic)
|
|
|
|
create_warning(topic)
|
|
|
|
watch_topic(topic)
|
2018-03-13 15:59:12 -04:00
|
|
|
create_shared_draft(topic)
|
2019-01-03 12:03:01 -05:00
|
|
|
UserActionManager.topic_created(topic)
|
2013-07-21 21:40:39 -04:00
|
|
|
|
2015-03-26 16:57:50 -04:00
|
|
|
topic
|
2013-06-04 14:13:01 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
private
|
|
|
|
|
2022-12-01 05:26:35 -05:00
|
|
|
def validate_visibility(topic)
|
|
|
|
if !@opts[:skip_validations] && !topic.visible && !guardian.can_create_unlisted_topic?(topic)
|
|
|
|
topic.errors.add(:base, :unable_to_unlist)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def validate_visibility!(topic)
|
|
|
|
validate_visibility(topic)
|
|
|
|
|
|
|
|
rollback_from_errors!(topic) if topic.errors.full_messages.present?
|
|
|
|
end
|
|
|
|
|
2018-03-13 15:59:12 -04:00
|
|
|
def create_shared_draft(topic)
|
2020-12-14 14:08:20 -05:00
|
|
|
return if @opts[:shared_draft].blank? || @opts[:shared_draft] == 'false'
|
|
|
|
|
|
|
|
category_id = @opts[:category].blank? ? SiteSetting.shared_drafts_category.to_i : @opts[:category]
|
|
|
|
SharedDraft.create(topic_id: topic.id, category_id: category_id)
|
2018-03-13 15:59:12 -04:00
|
|
|
end
|
|
|
|
|
2015-03-26 16:57:50 -04:00
|
|
|
def create_warning(topic)
|
2014-09-08 11:11:56 -04:00
|
|
|
return unless @opts[:is_warning]
|
|
|
|
|
|
|
|
# We can only attach warnings to PMs
|
2015-03-26 16:57:50 -04:00
|
|
|
rollback_with!(topic, :warning_requires_pm) unless topic.private_message?
|
2014-09-08 11:11:56 -04:00
|
|
|
|
|
|
|
# Don't create it if there is more than one user
|
2015-03-26 16:57:50 -04:00
|
|
|
rollback_with!(topic, :too_many_users) if @added_users.size != 1
|
2014-09-08 11:11:56 -04:00
|
|
|
|
|
|
|
# Create a warning record
|
2017-04-15 00:11:02 -04:00
|
|
|
UserWarning.create(topic: topic, user: @added_users.first, created_by: @user)
|
2014-09-08 11:11:56 -04:00
|
|
|
end
|
|
|
|
|
2015-03-26 16:57:50 -04:00
|
|
|
def watch_topic(topic)
|
2013-07-21 21:40:39 -04:00
|
|
|
unless @opts[:auto_track] == false
|
2015-03-26 16:57:50 -04:00
|
|
|
topic.notifier.watch_topic!(topic.user_id)
|
2013-07-21 21:40:39 -04:00
|
|
|
end
|
2014-01-22 01:46:52 -05:00
|
|
|
|
2017-08-31 00:06:56 -04:00
|
|
|
topic.reload.topic_allowed_users.each do |tau|
|
2015-12-14 17:17:09 -05:00
|
|
|
next if tau.user_id == -1 || tau.user_id == topic.user_id
|
|
|
|
topic.notifier.watch!(tau.user_id)
|
|
|
|
end
|
2014-06-24 12:31:36 -04:00
|
|
|
|
2019-08-16 04:23:51 -04:00
|
|
|
topic.reload.topic_allowed_groups.each do |topic_allowed_group|
|
|
|
|
topic_allowed_group.group.set_message_default_notification_levels!(topic)
|
2014-01-22 01:46:52 -05:00
|
|
|
end
|
2013-07-21 21:40:39 -04:00
|
|
|
end
|
|
|
|
|
2014-02-06 14:52:50 -05:00
|
|
|
def setup_topic_params
|
2016-07-27 05:50:13 -04:00
|
|
|
@opts[:visible] = true if @opts[:visible].nil?
|
|
|
|
|
2014-01-24 06:57:48 -05:00
|
|
|
topic_params = {
|
|
|
|
title: @opts[:title],
|
|
|
|
user_id: @user.id,
|
2016-07-27 05:50:13 -04:00
|
|
|
last_post_user_id: @user.id,
|
|
|
|
visible: @opts[:visible]
|
2014-01-24 06:57:48 -05:00
|
|
|
}
|
|
|
|
|
2022-02-09 03:37:38 -05:00
|
|
|
[:subtype, :archetype, :meta_data, :import_mode, :advance_draft].each do |key|
|
2014-02-06 14:52:50 -05:00
|
|
|
topic_params[key] = @opts[key] if @opts[key].present?
|
2014-01-24 06:57:48 -05:00
|
|
|
end
|
|
|
|
|
2015-07-16 02:11:20 -04:00
|
|
|
if topic_params[:import_mode] && @opts[:views].to_i > 0
|
|
|
|
topic_params[:views] = @opts[:views].to_i
|
|
|
|
end
|
|
|
|
|
2020-09-09 18:16:57 -04:00
|
|
|
if topic_params[:import_mode] && @opts[:participant_count].to_i > 0
|
|
|
|
topic_params[:participant_count] = @opts[:participant_count].to_i
|
|
|
|
end
|
|
|
|
|
2014-09-08 13:23:40 -04:00
|
|
|
# Automatically give it a moderator warning subtype if specified
|
|
|
|
topic_params[:subtype] = TopicSubtype.moderator_warning if @opts[:is_warning]
|
|
|
|
|
2014-02-06 14:52:50 -05:00
|
|
|
category = find_category
|
2015-10-17 03:48:56 -04:00
|
|
|
@guardian.ensure_can_create!(Topic, category) unless (@opts[:skip_validations] || @opts[:archetype] == Archetype.private_message)
|
2014-01-24 06:57:48 -05:00
|
|
|
|
2019-05-09 19:33:02 -04:00
|
|
|
if @opts[:category].present? && category.nil?
|
|
|
|
raise Discourse::InvalidParameters.new(:category)
|
|
|
|
end
|
2019-05-09 13:22:09 -04:00
|
|
|
|
2013-06-04 14:13:01 -04:00
|
|
|
topic_params[:category_id] = category.id if category.present?
|
2020-03-10 10:35:40 -04:00
|
|
|
topic_params[:created_at] = convert_time(@opts[:created_at]) if @opts[:created_at].present?
|
|
|
|
topic_params[:pinned_at] = convert_time(@opts[:pinned_at]) if @opts[:pinned_at].present?
|
2015-03-13 16:24:11 -04:00
|
|
|
topic_params[:pinned_globally] = @opts[:pinned_globally] if @opts[:pinned_globally].present?
|
2022-02-08 22:55:32 -05:00
|
|
|
topic_params[:external_id] = @opts[:external_id] if @opts[:external_id].present?
|
2021-08-05 05:38:39 -04:00
|
|
|
topic_params[:featured_link] = @opts[:featured_link]
|
2016-12-05 07:31:43 -05:00
|
|
|
|
2013-06-04 14:13:01 -04:00
|
|
|
topic_params
|
|
|
|
end
|
|
|
|
|
2020-03-10 10:35:40 -04:00
|
|
|
def convert_time(timestamp)
|
|
|
|
if timestamp.is_a?(Time)
|
|
|
|
timestamp
|
|
|
|
else
|
|
|
|
Time.zone.parse(timestamp.to_s)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2014-02-06 14:52:50 -05:00
|
|
|
def find_category
|
2019-05-09 02:46:20 -04:00
|
|
|
@category ||= begin
|
|
|
|
# PM can't have a category
|
|
|
|
@opts.delete(:category) if @opts[:archetype].present? && @opts[:archetype] == Archetype.private_message
|
2014-02-06 14:52:50 -05:00
|
|
|
|
2019-05-09 02:46:20 -04:00
|
|
|
if @opts[:shared_draft]
|
|
|
|
return Category.find(SiteSetting.shared_drafts_category)
|
|
|
|
end
|
2018-03-13 15:59:12 -04:00
|
|
|
|
2019-05-09 02:46:20 -04:00
|
|
|
if (@opts[:category].is_a? Integer) || (@opts[:category] =~ /^\d+$/)
|
|
|
|
Category.find_by(id: @opts[:category])
|
|
|
|
end
|
2014-02-06 14:52:50 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2016-05-04 14:02:47 -04:00
|
|
|
def setup_tags(topic)
|
2021-05-14 09:52:10 -04:00
|
|
|
if @opts[:tags].present?
|
|
|
|
valid_tags = DiscourseTagging.tag_topic_by_names(topic, @guardian, @opts[:tags])
|
|
|
|
unless valid_tags
|
|
|
|
topic.errors.add(:base, :unable_to_tag)
|
|
|
|
rollback_from_errors!(topic)
|
|
|
|
end
|
2018-03-28 14:40:26 -04:00
|
|
|
end
|
2021-04-23 09:55:34 -04:00
|
|
|
|
2021-05-14 09:52:10 -04:00
|
|
|
watched_words = WordWatcher.words_for_action(:tag)
|
|
|
|
if watched_words.present?
|
|
|
|
word_watcher = WordWatcher.new("#{@opts[:title]} #{@opts[:raw]}")
|
|
|
|
word_watcher_tags = topic.tags.map(&:name)
|
2022-08-02 04:06:03 -04:00
|
|
|
watched_words.each do |word, opts|
|
|
|
|
if word_watcher.word_matches?(word, case_sensitive: opts[:case_sensitive])
|
|
|
|
word_watcher_tags += opts[:replacement].split(",")
|
|
|
|
end
|
2021-04-23 09:55:34 -04:00
|
|
|
end
|
2021-05-14 09:52:10 -04:00
|
|
|
DiscourseTagging.tag_topic_by_names(topic, Discourse.system_user.guardian, word_watcher_tags)
|
2021-04-23 09:55:34 -04:00
|
|
|
end
|
2016-05-04 14:02:47 -04:00
|
|
|
end
|
|
|
|
|
2015-03-26 16:57:50 -04:00
|
|
|
def setup_auto_close_time(topic)
|
2014-02-06 14:52:50 -05:00
|
|
|
return unless @opts[:auto_close_time].present?
|
2015-03-26 16:57:50 -04:00
|
|
|
return unless @guardian.can_moderate?(topic)
|
2015-05-27 12:22:34 -04:00
|
|
|
topic.set_auto_close(@opts[:auto_close_time], by_user: @user)
|
2013-06-04 14:13:01 -04:00
|
|
|
end
|
|
|
|
|
2015-03-26 16:57:50 -04:00
|
|
|
def process_private_message(topic)
|
2014-02-06 14:52:50 -05:00
|
|
|
return unless @opts[:archetype] == Archetype.private_message
|
2015-03-26 16:57:50 -04:00
|
|
|
topic.subtype = TopicSubtype.user_to_user unless topic.subtype
|
2013-06-04 14:13:01 -04:00
|
|
|
|
2017-08-28 12:07:30 -04:00
|
|
|
unless @opts[:target_usernames].present? || @opts[:target_emails].present? || @opts[:target_group_names].present?
|
2015-03-26 16:57:50 -04:00
|
|
|
rollback_with!(topic, :no_user_selected)
|
2013-06-04 14:13:01 -04:00
|
|
|
end
|
|
|
|
|
2018-08-26 21:38:11 -04:00
|
|
|
if @opts[:target_emails].present? && !@guardian.can_send_private_messages_to_email? then
|
2021-03-30 05:18:57 -04:00
|
|
|
rollback_with!(topic, :send_to_email_disabled)
|
2017-08-28 12:07:30 -04:00
|
|
|
end
|
|
|
|
|
2015-03-26 16:57:50 -04:00
|
|
|
add_users(topic, @opts[:target_usernames])
|
2017-08-28 12:07:30 -04:00
|
|
|
add_emails(topic, @opts[:target_emails])
|
2015-03-26 16:57:50 -04:00
|
|
|
add_groups(topic, @opts[:target_group_names])
|
2018-12-09 22:45:02 -05:00
|
|
|
|
|
|
|
if !@added_users.include?(user)
|
|
|
|
topic.topic_allowed_users.build(user_id: @user.id)
|
|
|
|
end
|
|
|
|
|
2013-06-04 14:13:01 -04:00
|
|
|
end
|
|
|
|
|
2015-03-26 16:57:50 -04:00
|
|
|
def save_topic(topic)
|
2015-04-21 16:48:39 -04:00
|
|
|
topic.disable_rate_limits! if @opts[:skip_validations]
|
|
|
|
|
2015-03-26 16:57:50 -04:00
|
|
|
unless topic.save(validate: !@opts[:skip_validations])
|
|
|
|
rollback_from_errors!(topic)
|
2013-06-04 14:13:01 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def add_users(topic, usernames)
|
|
|
|
return unless usernames
|
2015-12-01 23:49:43 -05:00
|
|
|
|
2020-05-25 12:04:05 -04:00
|
|
|
names = usernames.split(',').flatten.map(&:downcase)
|
2015-12-01 23:49:43 -05:00
|
|
|
len = 0
|
|
|
|
|
2020-10-07 20:28:07 -04:00
|
|
|
User.includes(:user_option).where('username_lower in (?)', names).find_each do |user|
|
2014-09-08 11:11:56 -04:00
|
|
|
check_can_send_permission!(topic, user)
|
|
|
|
@added_users << user
|
2013-06-04 14:13:01 -04:00
|
|
|
topic.topic_allowed_users.build(user_id: user.id)
|
2015-12-01 23:49:43 -05:00
|
|
|
len += 1
|
2013-06-04 14:13:01 -04:00
|
|
|
end
|
2015-12-01 23:49:43 -05:00
|
|
|
|
|
|
|
rollback_with!(topic, :target_user_not_found) unless len == names.length
|
2013-06-04 14:13:01 -04:00
|
|
|
end
|
|
|
|
|
2017-08-28 12:07:30 -04:00
|
|
|
def add_emails(topic, emails)
|
|
|
|
return unless emails
|
|
|
|
|
2017-09-28 00:25:42 -04:00
|
|
|
begin
|
|
|
|
emails = emails.split(',').flatten
|
|
|
|
len = 0
|
|
|
|
|
|
|
|
emails.each do |email|
|
|
|
|
display_name = email.split("@").first
|
|
|
|
|
|
|
|
if user = find_or_create_user(email, display_name)
|
2018-12-09 22:45:02 -05:00
|
|
|
if !@added_users.include?(user)
|
|
|
|
@added_users << user
|
|
|
|
topic.topic_allowed_users.build(user_id: user.id)
|
|
|
|
end
|
2017-09-28 00:25:42 -04:00
|
|
|
len += 1
|
|
|
|
end
|
|
|
|
end
|
|
|
|
ensure
|
|
|
|
rollback_with!(topic, :target_user_not_found) unless len == emails.length
|
2017-08-28 12:07:30 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2013-06-04 14:13:01 -04:00
|
|
|
def add_groups(topic, groups)
|
|
|
|
return unless groups
|
2020-05-27 16:49:00 -04:00
|
|
|
names = groups.split(',').flatten.map(&:downcase)
|
2015-12-01 23:49:43 -05:00
|
|
|
len = 0
|
|
|
|
|
2020-05-27 16:49:00 -04:00
|
|
|
Group.where('lower(name) in (?)', names).each do |group|
|
2015-03-26 16:57:50 -04:00
|
|
|
check_can_send_permission!(topic, group)
|
2013-06-04 14:13:01 -04:00
|
|
|
topic.topic_allowed_groups.build(group_id: group.id)
|
2015-12-01 23:49:43 -05:00
|
|
|
len += 1
|
2015-12-20 01:21:31 -05:00
|
|
|
group.update_columns(has_messages: true) unless group.has_messages
|
2013-06-04 14:13:01 -04:00
|
|
|
end
|
2015-12-01 23:49:43 -05:00
|
|
|
|
|
|
|
rollback_with!(topic, :target_group_not_found) unless len == names.length
|
2013-06-04 14:13:01 -04:00
|
|
|
end
|
|
|
|
|
2015-03-26 16:57:50 -04:00
|
|
|
def check_can_send_permission!(topic, obj)
|
2019-01-24 06:26:59 -05:00
|
|
|
unless @opts[:skip_validations] ||
|
2019-01-24 06:52:17 -05:00
|
|
|
@guardian.can_send_private_message?(obj, notify_moderators: topic&.subtype == TopicSubtype.notify_moderators)
|
2019-01-24 06:26:59 -05:00
|
|
|
rollback_with!(topic, :cant_send_pm)
|
|
|
|
end
|
2013-06-04 14:13:01 -04:00
|
|
|
end
|
2017-08-28 12:07:30 -04:00
|
|
|
|
|
|
|
def find_or_create_user(email, display_name)
|
|
|
|
user = User.find_by_email(email)
|
|
|
|
|
2017-09-28 00:25:42 -04:00
|
|
|
if !user && SiteSetting.enable_staged_users
|
2017-08-28 12:07:30 -04:00
|
|
|
username = UserNameSuggester.sanitize_username(display_name) if display_name.present?
|
2017-09-28 00:25:42 -04:00
|
|
|
|
2017-08-28 12:07:30 -04:00
|
|
|
user = User.create!(
|
|
|
|
email: email,
|
|
|
|
username: UserNameSuggester.suggest(username.presence || email),
|
|
|
|
name: display_name.presence || User.suggest_name(email),
|
|
|
|
staged: true
|
|
|
|
)
|
|
|
|
end
|
|
|
|
|
|
|
|
user
|
|
|
|
end
|
2013-06-04 14:13:01 -04:00
|
|
|
end
|