Class: UserDestroyer

Inherits:
Object
  • Object
show all
Defined in:
app/services/user_destroyer.rb

Overview

Responsible for destroying a User record

Defined Under Namespace

Classes: PostsExistError

Instance Method Summary collapse

Constructor Details

#initialize(actor) ⇒ UserDestroyer

Returns a new instance of UserDestroyer.



8
9
10
11
12
# File 'app/services/user_destroyer.rb', line 8

def initialize(actor)
  @actor = actor
  raise Discourse::InvalidParameters.new("acting user is nil") unless @actor && @actor.is_a?(User)
  @guardian = Guardian.new(actor)
end

Instance Method Details

#destroy(user, opts = {}) ⇒ Object

Returns false if the user failed to be deleted. Returns a frozen instance of the User if the delete succeeded.



16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# File 'app/services/user_destroyer.rb', line 16

def destroy(user, opts = {})
  raise Discourse::InvalidParameters.new("user is nil") unless user && user.is_a?(User)
  raise PostsExistError if !opts[:delete_posts] && user.posts.joins(:topic).count != 0
  @guardian.ensure_can_delete_user!(user)

  # default to using a transaction
  opts[:transaction] = true if opts[:transaction] != false

  prepare_for_destroy(user) if opts[:prepare_for_destroy] == true

  result = nil

  optional_transaction(open_transaction: opts[:transaction]) do
    UserSecurityKey.where(user_id: user.id).delete_all
    Bookmark.where(user_id: user.id).delete_all
    Draft.where(user_id: user.id).delete_all
    Reviewable.where(created_by_id: user.id).delete_all

    category_topic_ids = Category.where("topic_id IS NOT NULL").pluck(:topic_id)

    if opts[:delete_posts]
      DiscoursePluginRegistry.user_destroyer_on_content_deletion_callbacks.each do |cb|
        cb.call(user, @guardian, opts)
      end

      agree_with_flags(user) if opts[:delete_as_spammer]
      block_external_urls(user) if opts[:block_urls]
      delete_posts(user, category_topic_ids, opts)
    end

    user.post_actions.find_each { |post_action| post_action.remove_act!(Discourse.system_user) }

    # Add info about the user to staff action logs
    UserHistory.staff_action_records(
      Discourse.system_user,
      acting_user: user.username,
    ).update_all(
      ["details = CONCAT(details, ?)", "\nuser_id: #{user.id}\nusername: #{user.username}"],
    )

    # keep track of emails used
    user_emails = user.user_emails.pluck(:email)

    if result = user.destroy
      if opts[:block_email]
        user_emails.each do |email|
          ScreenedEmail.block(email, ip_address: result.ip_address)&.record_match!
        end
      end

      if opts[:block_ip] && result.ip_address
        ScreenedIpAddress.watch(result.ip_address)&.record_match!
        if result.registration_ip_address && result.ip_address != result.registration_ip_address
          ScreenedIpAddress.watch(result.registration_ip_address)&.record_match!
        end
      end

      Post.unscoped.where(user_id: result.id).update_all(user_id: nil)

      # If this user created categories, fix those up:
      Category
        .where(user_id: result.id)
        .each do |c|
          c.user_id = Discourse::SYSTEM_USER_ID
          c.save!
          if topic = Topic.unscoped.find_by(id: c.topic_id)
            topic.recover!
            topic.user_id = Discourse::SYSTEM_USER_ID
            topic.save!
          end
        end

      Invite
        .where(email: user_emails)
        .each do |invite|
          # invited_users will be removed by dependent destroy association when user is destroyed
          invite.invited_groups.destroy_all
          invite.topic_invites.destroy_all
          invite.destroy
        end

      unless opts[:quiet]
        if @actor == user
          deleted_by = Discourse.system_user
          opts[:context] = I18n.t("staff_action_logs.user_delete_self", url: opts[:context])
        else
          deleted_by = @actor
        end
        StaffActionLogger.new(deleted_by).log_user_deletion(user, opts.slice(:context))
        if opts.slice(:context).blank?
          Rails.logger.warn("User destroyed without context from: #{caller_locations(14, 1)[0]}")
        end
      end
      MessageBus.publish "/logout/#{result.id}", result.id, user_ids: [result.id]
    end
  end

  # After the user is deleted, remove the reviewable
  if reviewable = ReviewableUser.pending.find_by(target: user)
    reviewable.perform(@actor, :delete_user)
  end

  result
end