mirror of
https://github.com/discourse/discourse.git
synced 2025-01-06 10:43:44 +08:00
74d55f14fe
This adds a hidden site setting of `skip_email_bulk_invites` If set to `true`, the `BulkInvite` job will pass the value to `Invite`, meaning the generated invite wont trigger an email notification being sent to the newly invited user. (This is useful if you want to manage the sending of the invite emails outside of Discourse.)
247 lines
6.0 KiB
Ruby
247 lines
6.0 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
module Jobs
|
|
class BulkInvite < ::Jobs::Base
|
|
sidekiq_options retry: false
|
|
|
|
def initialize
|
|
super
|
|
|
|
@logs = []
|
|
@sent = 0
|
|
@skipped = 0
|
|
@warnings = 0
|
|
@failed = 0
|
|
@groups = {}
|
|
@user_fields = {}
|
|
@valid_groups = {}
|
|
end
|
|
|
|
def execute(args)
|
|
@invites = args[:invites]
|
|
raise Discourse::InvalidParameters.new(:invites) if @invites.blank?
|
|
|
|
@current_user = User.find_by(id: args[:current_user_id])
|
|
raise Discourse::InvalidParameters.new(:current_user_id) unless @current_user
|
|
|
|
@skip_email = SiteSetting.skip_email_bulk_invites
|
|
|
|
@guardian = Guardian.new(@current_user)
|
|
|
|
process_invites(@invites)
|
|
|
|
if @invites.length > Invite::BULK_INVITE_EMAIL_LIMIT
|
|
::Jobs.enqueue(:process_bulk_invite_emails)
|
|
end
|
|
ensure
|
|
notify_user
|
|
end
|
|
|
|
private
|
|
|
|
def process_invites(invites)
|
|
invites.each do |invite|
|
|
if EmailAddressValidator.valid_value?(invite[:email])
|
|
# email is valid
|
|
result = send_invite(invite)
|
|
if Invite === result
|
|
@sent += 1
|
|
elsif User === result
|
|
@skipped += 1
|
|
else
|
|
@failed += 1
|
|
end
|
|
else
|
|
# invalid email
|
|
save_log "Invalid Email '#{invite[:email]}"
|
|
@failed += 1
|
|
end
|
|
end
|
|
rescue Exception => e
|
|
save_log "Bulk Invite Process Failed -- '#{e.message}'"
|
|
@failed += 1
|
|
end
|
|
|
|
def get_groups(group_names)
|
|
groups = []
|
|
|
|
if group_names
|
|
group_names = group_names.split(";")
|
|
|
|
group_names.each do |group_name|
|
|
group = fetch_group(group_name)
|
|
|
|
if group && can_edit_group?(group)
|
|
# valid group
|
|
groups.push(group)
|
|
else
|
|
# invalid group
|
|
save_log "Invalid Group '#{group_name}'"
|
|
@warnings += 1
|
|
end
|
|
end
|
|
end
|
|
|
|
groups
|
|
end
|
|
|
|
def get_topic(topic_id)
|
|
topic = nil
|
|
|
|
if topic_id
|
|
topic = Topic.find_by_id(topic_id)
|
|
if topic.nil?
|
|
save_log "Invalid Topic ID '#{topic_id}'"
|
|
@warnings += 1
|
|
end
|
|
end
|
|
|
|
topic
|
|
end
|
|
|
|
def get_user_fields(fields)
|
|
user_fields = {}
|
|
|
|
fields.each do |key, value|
|
|
@user_fields[key] ||= UserField
|
|
.includes(:user_field_options)
|
|
.where("name ILIKE ?", key)
|
|
.first || :nil
|
|
if @user_fields[key] == :nil
|
|
save_log "Invalid User Field '#{key}'"
|
|
@warnings += 1
|
|
next
|
|
end
|
|
|
|
# Automatically correct user field value
|
|
if @user_fields[key].field_type == "dropdown"
|
|
value =
|
|
@user_fields[key].user_field_options.find { |ufo| ufo.value.casecmp?(value) }&.value
|
|
end
|
|
|
|
user_fields[@user_fields[key].id] = value
|
|
end
|
|
|
|
user_fields
|
|
end
|
|
|
|
def send_invite(invite)
|
|
email = invite[:email]
|
|
groups = get_groups(invite[:groups])
|
|
topic = get_topic(invite[:topic_id])
|
|
locale = invite[:locale]
|
|
user_fields = get_user_fields(invite.except(:email, :groups, :topic_id, :locale))
|
|
|
|
begin
|
|
if user = Invite.find_user_by_email(email)
|
|
if groups.present?
|
|
Group.transaction do
|
|
groups.each do |group|
|
|
group.add(user)
|
|
|
|
GroupActionLogger.new(@current_user, group).log_add_user_to_group(user)
|
|
end
|
|
end
|
|
end
|
|
|
|
if user_fields.present?
|
|
user_fields.each { |user_field, value| user.set_user_field(user_field, value) }
|
|
user.save_custom_fields
|
|
end
|
|
|
|
if locale.present?
|
|
user.locale = locale
|
|
user.save!
|
|
end
|
|
|
|
user
|
|
else
|
|
if user_fields.present? || locale.present?
|
|
user = User.where(staged: true).find_by_email(email)
|
|
user ||=
|
|
User.new(username: UserNameSuggester.suggest(email), email: email, staged: true)
|
|
|
|
if user_fields.present?
|
|
user_fields.each { |user_field, value| user.set_user_field(user_field, value) }
|
|
end
|
|
|
|
user.locale = locale if locale.present?
|
|
|
|
user.save!
|
|
end
|
|
|
|
invite_opts = {
|
|
email: email,
|
|
topic: topic,
|
|
group_ids: groups.map(&:id),
|
|
skip_email: @skip_email,
|
|
}
|
|
|
|
if @invites.length > Invite::BULK_INVITE_EMAIL_LIMIT
|
|
invite_opts[:emailed_status] = Invite.emailed_status_types[:bulk_pending]
|
|
end
|
|
|
|
Invite.generate(@current_user, invite_opts)
|
|
end
|
|
rescue => e
|
|
save_log "Error inviting '#{email}' -- #{Rails::Html::FullSanitizer.new.sanitize(e.message)}"
|
|
|
|
nil
|
|
end
|
|
end
|
|
|
|
def save_log(message)
|
|
@logs << "[#{Time.now}] #{message}"
|
|
end
|
|
|
|
def notify_user
|
|
if @current_user
|
|
if @sent > 0 && @failed == 0
|
|
SystemMessage.create_from_system_user(
|
|
@current_user,
|
|
:bulk_invite_succeeded,
|
|
sent: @sent,
|
|
skipped: @skipped,
|
|
warnings: @warnings,
|
|
logs: @logs.join("\n"),
|
|
)
|
|
else
|
|
SystemMessage.create_from_system_user(
|
|
@current_user,
|
|
:bulk_invite_failed,
|
|
sent: @sent,
|
|
skipped: @skipped,
|
|
warnings: @warnings,
|
|
failed: @failed,
|
|
logs: @logs.join("\n"),
|
|
)
|
|
end
|
|
end
|
|
end
|
|
|
|
def fetch_group(group_name)
|
|
group_name = group_name.downcase
|
|
group = @groups[group_name]
|
|
|
|
unless group
|
|
group = Group.find_by("lower(name) = ?", group_name)
|
|
@groups[group_name] = group
|
|
end
|
|
|
|
group
|
|
end
|
|
|
|
def can_edit_group?(group)
|
|
group_name = group.name.downcase
|
|
result = @valid_groups[group_name]
|
|
|
|
unless result
|
|
result = @guardian.can_edit_group?(group)
|
|
@valid_groups[group_name] = result
|
|
end
|
|
|
|
result
|
|
end
|
|
end
|
|
end
|