FIX: Unassign user titles when a badge is deleted (#9573)

This commit is contained in:
Kane York 2020-05-02 18:02:28 -07:00 committed by GitHub
parent 0e4db91870
commit 784bf2a173
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 99 additions and 34 deletions

View File

@ -129,9 +129,12 @@ class Admin::BadgesController < Admin::AdminController
end
def destroy
badge = find_badge
StaffActionLogger.new(current_user).log_badge_deletion(badge)
badge.destroy
Badge.transaction do
badge = find_badge
StaffActionLogger.new(current_user).log_badge_deletion(badge)
badge.clear_user_titles!
badge.destroy!
end
render body: nil
end

View File

@ -9,43 +9,17 @@ module Jobs
new_title = args[:new_title]
granted_badge_id = args[:granted_badge_id]
action = args[:action]
badge = Badge.find(granted_badge_id) rescue nil
return unless badge # Deleted badge protection
case action
when UPDATE_ACTION
update_titles_for_granted_badge(new_title, granted_badge_id)
badge.update_user_titles!(new_title)
when RESET_ACTION
reset_titles_for_granted_badge(granted_badge_id)
badge.reset_user_titles!
end
end
private
##
# If a badge name or a system badge TranslationOverride changes
# then we need to set all titles granted based on that badge to
# the new name or custom translation
def update_titles_for_granted_badge(new_title, granted_badge_id)
DB.exec(<<~SQL, granted_title_badge_id: granted_badge_id, title: new_title, updated_at: Time.now)
UPDATE users AS u
SET title = :title, updated_at = :updated_at
FROM user_profiles AS up
WHERE up.user_id = u.id AND up.granted_title_badge_id = :granted_title_badge_id
SQL
end
##
# Reset granted titles for a badge back to the original
# badge name. When a system badge has its TranslationOverride
# revoked we want to have all titles based on that translation
# for the badge reset.
def reset_titles_for_granted_badge(granted_badge_id)
DB.exec(<<~SQL, granted_title_badge_id: granted_badge_id, updated_at: Time.now)
UPDATE users AS u
SET title = badges.name, updated_at = :updated_at
FROM user_profiles AS up
INNER JOIN badges ON badges.id = up.granted_title_badge_id
WHERE up.user_id = u.id AND up.granted_title_badge_id = :granted_title_badge_id
SQL
end
end
end

View File

@ -166,6 +166,44 @@ class Badge < ActiveRecord::Base
SQL
end
def clear_user_titles!
DB.exec(<<~SQL, badge_id: self.id, updated_at: Time.zone.now)
UPDATE users AS u
SET title = '', updated_at = :updated_at
FROM user_profiles AS up
WHERE up.user_id = u.id AND up.granted_title_badge_id = :badge_id
SQL
DB.exec(<<~SQL, badge_id: self.id)
UPDATE user_profiles AS up
SET badge_granted_title = false, granted_title_badge_id = NULL
WHERE up.granted_title_badge_id = :badge_id
SQL
end
##
# Update all user titles based on a badge to the new name
def update_user_titles!(new_title)
DB.exec(<<~SQL, granted_title_badge_id: self.id, title: new_title, updated_at: Time.zone.now)
UPDATE users AS u
SET title = :title, updated_at = :updated_at
FROM user_profiles AS up
WHERE up.user_id = u.id AND up.granted_title_badge_id = :granted_title_badge_id
SQL
end
##
# When a badge has its TranslationOverride cleared, reset
# all user titles granted to the standard name.
def reset_user_titles!
DB.exec(<<~SQL, granted_title_badge_id: self.id, updated_at: Time.zone.now)
UPDATE users AS u
SET title = badges.name, updated_at = :updated_at
FROM user_profiles AS up
INNER JOIN badges ON badges.id = up.granted_title_badge_id
WHERE up.user_id = u.id AND up.granted_title_badge_id = :granted_title_badge_id
SQL
end
def self.i18n_name(name)
name.downcase.tr(' ', '_')
end

View File

@ -42,4 +42,54 @@ describe BadgesController do
expect(response.media_type).to eq('application/rss+xml')
end
end
context "user profiles" do
let(:titled_badge) { Fabricate(:badge, name: 'Protector of the Realm', allow_title: true) }
let!(:grant) { UserBadge.create!(user_id: user.id, badge_id: titled_badge.id, granted_at: 1.minute.ago, granted_by_id: -1) }
it "can be assigned as a title by the user" do
sign_in(user)
put "/u/#{user.username}/preferences/badge_title.json", params: {
user_badge_id: grant.id,
}
expect(response.status).to eq(200)
user.reload
expect(user.title).to eq(titled_badge.display_name)
expect(user.user_profile.granted_title_badge_id).to eq(titled_badge.id)
end
end
describe "destroy" do
let(:admin) { Fabricate(:admin) }
context "while assigned as a title" do
let(:titled_badge) { Fabricate(:badge, name: 'Protector of the Realm', allow_title: true) }
let!(:grant) { UserBadge.create!(user_id: user.id, badge_id: titled_badge.id, granted_at: 1.minute.ago, granted_by_id: -1) }
before do
sign_in(user)
put "/u/#{user.username}/preferences/badge_title.json", params: {
user_badge_id: grant.id,
}
user.reload
sign_out
end
it "succeeds and unassigns the title from the user" do
expect(user.title).to eq(titled_badge.display_name)
sign_in(admin)
badge_id = titled_badge.id
delete "/admin/badges/#{titled_badge.id}.json"
expect(response.status).to be(200)
expect(Badge.find_by(id: badge_id)).to be(nil)
user.reload
expect(user.title).to_not eq(titled_badge.display_name)
expect(user.user_profile.granted_title_badge_id).to eq(nil)
end
end
end
end