discourse/app/jobs/regular/bulk_invite.rb
jbrw 74d55f14fe
DEV: Add skip_email_bulk_invites hidden site setting (#26430)
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.)
2024-03-29 13:22:00 -04:00

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