FEATURE: auto grant an available title when removing old title

* FEATURE: auto grant an available title when removing old title
This commit is contained in:
Kyle Zhao 2018-09-21 10:06:08 +08:00 committed by Sam
parent e622adfb89
commit e402394375
7 changed files with 139 additions and 29 deletions

View File

@ -159,6 +159,15 @@ class Badge < ActiveRecord::Base
SQL
end
def self.i18n_name(name)
name.downcase.tr(' ', '_')
end
def self.display_name(name)
key = "badges.#{i18n_name(name)}.name"
I18n.t(key, default: name)
end
def awarded_for_trust_level?
id <= 4
end
@ -191,8 +200,7 @@ class Badge < ActiveRecord::Base
end
def display_name
key = "badges.#{i18n_name}.name"
I18n.t(key, default: self.name)
self.class.display_name(name)
end
def long_description
@ -230,9 +238,8 @@ class Badge < ActiveRecord::Base
end
def i18n_name
self.name.downcase.tr(' ', '_')
@i18n_name ||= self.class.i18n_name(name)
end
end
# == Schema Information

View File

@ -5,7 +5,7 @@ class GroupUser < ActiveRecord::Base
belongs_to :user
after_save :update_title
after_destroy :remove_title
after_destroy :grant_other_available_title
after_save :set_primary_group
after_destroy :remove_primary_group, :recalculate_trust_level
@ -43,13 +43,9 @@ class GroupUser < ActiveRecord::Base
)
end
def remove_title
if group.title.present?
DB.exec("
UPDATE users SET title = NULL
WHERE title = :title AND id = :id",
id: user_id, title: group.title
)
def grant_other_available_title
if group.title.present? && group.title == user.title
user.update!(title: user.next_best_title)
end
end
@ -85,7 +81,6 @@ class GroupUser < ActiveRecord::Base
user.update!(group_locked_trust_level: highest_level)
Promotion.recalculate(user)
end
end
# == Schema Information

View File

@ -110,6 +110,7 @@ class User < ActiveRecord::Base
before_save :update_username_lower
before_save :ensure_password_is_hashed
before_save :match_title_to_primary_group_changes
before_save :check_if_title_is_badged_granted
after_save :expire_tokens_if_password_changed
after_save :clear_global_notice_if_needed
@ -1000,13 +1001,6 @@ class User < ActiveRecord::Base
@user_fields
end
def title=(val)
write_attribute(:title, val)
if !new_record? && user_profile
user_profile.update_column(:badge_granted_title, false)
end
end
def number_of_deleted_posts
Post.with_deleted
.where(user_id: self.id)
@ -1123,6 +1117,19 @@ class User < ActiveRecord::Base
from_staged? && self.created_at && self.created_at < 1.day.ago
end
def next_best_title
group_titles_query = groups.where("groups.title <> ''")
group_titles_query = group_titles_query.order("groups.id = #{primary_group_id} DESC") if primary_group_id
group_titles_query = group_titles_query.order("groups.primary_group DESC").limit(1)
if next_best_group_title = group_titles_query.pluck(:title).first
return next_best_group_title
end
next_best_badge_title = badges.where(allow_title: true).limit(1).pluck(:name).first
next_best_badge_title ? Badge.display_name(next_best_badge_title) : nil
end
protected
def badge_grant
@ -1285,6 +1292,13 @@ class User < ActiveRecord::Base
private
def check_if_title_is_badged_granted
if title_changed? && !new_record? && user_profile
badge_granted_title = title.present? && badges.where(allow_title: true, name: title).exists?
user_profile.update_column(:badge_granted_title, badge_granted_title)
end
end
def previous_visit_at_update_required?(timestamp)
seen_before? && (last_seen_at < (timestamp - SiteSetting.previous_visit_timeout_hours.hours))
end

View File

@ -66,9 +66,6 @@ class UserUpdater
attributes[:title] != user.title &&
guardian.can_grant_title?(user, attributes[:title])
user.title = attributes[:title]
if user.badges.where(name: user.title).exists?
user_profile.badge_granted_title = true
end
end
CATEGORY_IDS.each do |attribute, level|

View File

@ -80,4 +80,21 @@ describe Badge do
it { is_expected.to be true }
end
end
describe '.i18n_name' do
it 'transforms to lower case letters, and replaces spaces with underscores' do
expect(Badge.i18n_name('Basic User')).to eq('basic_user')
end
end
describe '.display_name' do
it 'fetches from translations when i18n_name key exists' do
expect(Badge.display_name('basic_user')).to eq('Basic')
expect(Badge.display_name('Basic User')).to eq('Basic')
end
it 'fallbacks to argument value when translation does not exist' do
expect(Badge.display_name('Not In Translations')).to eq('Not In Translations')
end
end
end

View File

@ -675,13 +675,22 @@ describe Group do
describe '#remove' do
before { group.add(user) }
it "only strips user's title if exact match" do
group.update(title: 'Awesome')
expect { group.remove(user) }.to change { user.reload.title }.from('Awesome').to(nil)
context 'when stripping title' do
it "only strips user's title if exact match" do
group.update!(title: 'Awesome')
expect { group.remove(user) }.to change { user.reload.title }.from('Awesome').to(nil)
group.add(user)
user.update_columns(title: 'Different')
expect { group.remove(user) }.to_not change { user.reload.title }
group.add(user)
user.update_columns(title: 'Different')
expect { group.remove(user) }.to_not change { user.reload.title }
end
it "grants another title when the user has other available titles" do
group.update!(title: 'Awesome')
Fabricate(:group, title: 'Super').add(user)
expect { group.remove(user) }.to change { user.reload.title }.from('Awesome').to('Super')
end
end
it "unsets the user's primary group" do

View File

@ -1826,4 +1826,75 @@ describe User do
expect { user.update(primary_group: primary_group_a) }.to_not change { user.reload.title }
end
end
describe '#title=' do
let(:badge) { Fabricate(:badge, name: 'Badge', allow_title: false) }
it 'sets badge_granted_title correctly' do
BadgeGranter.grant(badge, user)
user.update!(title: badge.name)
expect(user.user_profile.reload.badge_granted_title).to eq(false)
user.update!(title: 'Custom')
expect(user.user_profile.reload.badge_granted_title).to eq(false)
badge.update!(allow_title: true)
user.update!(title: badge.name)
expect(user.user_profile.reload.badge_granted_title).to eq(true)
user.update!(title: nil)
expect(user.user_profile.reload.badge_granted_title).to eq(false)
end
end
describe '#next_best_title' do
let(:group_a) { Fabricate(:group, title: 'Group A') }
let(:group_b) { Fabricate(:group, title: 'Group B') }
let(:group_c) { Fabricate(:group, title: 'Group C') }
let(:badge) { Fabricate(:badge, name: 'Badge', allow_title: true) }
it 'only includes groups with title' do
group_a.add(user)
expect(user.next_best_title).to eq('Group A')
group_a.update!(title: nil)
expect(user.next_best_title).to eq(nil)
end
it 'only includes badges that allow to be set as title' do
BadgeGranter.grant(badge, user)
expect(user.next_best_title).to eq('Badge')
badge.update!(allow_title: false)
expect(user.next_best_title).to eq(nil)
end
it "picks the next best title in the order: user's primary group, primary group, groups, and badges" do
group_a.add(user)
group_b.add(user)
group_c.add(user)
BadgeGranter.grant(badge, user)
group_a.update!(primary_group: true)
group_b.update!(primary_group: true)
user.update!(primary_group_id: group_a.id)
expect(user.next_best_title).to eq('Group A')
user.update!(primary_group_id: group_b.id)
expect(user.next_best_title).to eq('Group B')
group_b.remove(user)
expect(user.next_best_title).to eq('Group A')
group_a.remove(user)
expect(user.next_best_title).to eq('Group C')
group_c.remove(user)
expect(user.next_best_title).to eq('Badge')
BadgeGranter.revoke(UserBadge.find_by(user_id: user.id, badge_id: badge.id))
expect(user.next_best_title).to eq(nil)
end
end
end