Merge pull request #2311 from vikhyat/badge-system

Add automatically assigned trust level badges
This commit is contained in:
Sam 2014-05-16 09:08:00 +10:00
commit 5914d0e132
18 changed files with 134 additions and 17 deletions

View File

@ -35,7 +35,7 @@ Discourse.Badge = Discourse.Model.extend({
@type {String}
**/
displayName: function() {
var i18nKey = "badges." + this.get('i18nNameKey') + ".name";
var i18nKey = "badges.badge." + this.get('i18nNameKey') + ".name";
return I18n.t(i18nKey, {defaultValue: this.get('name')});
}.property('name', 'i18nNameKey'),
@ -46,7 +46,7 @@ Discourse.Badge = Discourse.Model.extend({
@type {String}
**/
translatedDescription: function() {
var i18nKey = "badges." + this.get('i18nNameKey') + ".description",
var i18nKey = "badges.badge." + this.get('i18nNameKey') + ".description",
translation = I18n.t(i18nKey);
if (translation.indexOf(i18nKey) !== -1) {
translation = null;

View File

@ -5,7 +5,7 @@
{{#each}}
<tr>
<td class='badge'>{{user-badge badge=this}}</td>
<td class='description'>{{description}}</td>
<td class='description'>{{translatedDescription}}</td>
<td class='grant-count'>{{i18n badges.granted count=grant_count}}</td>
</tr>
{{/each}}

View File

@ -8,7 +8,7 @@
<table class='badges-listing'>
<tr>
<td class='badge'>{{user-badge badge=this}}</td>
<td class='description'>{{description}}</td>
<td class='description'>{{translatedDescription}}</td>
<td class='grant-count'>{{i18n badges.granted count=grant_count}}</td>
</tr>
</table>

View File

@ -1,6 +1,6 @@
{{#link-to 'badges.show' badge}}
<span {{bind-attr class=":user-badge badgeTypeClassName" data-badge-name="badge.name" title="badge.description"}}>
<span {{bind-attr class=":user-badge badgeTypeClassName" data-badge-name="badge.name" title="badge.translatedDescription"}}>
<i class='fa fa-certificate'></i>
{{badge.name}}
{{badge.displayName}}
</span>
{{/link-to}}

View File

@ -5,6 +5,10 @@ class Badge < ActiveRecord::Base
validates :name, presence: true, uniqueness: true
validates :badge_type, presence: true
validates :allow_title, inclusion: [true, false]
def self.trust_level_badge_ids
(1..4).to_a
end
end
# == Schema Information

View File

@ -447,6 +447,7 @@ class User < ActiveRecord::Base
transaction do
self.save!
Group.user_trust_level_change!(self.id, self.trust_level)
BadgeGranter.update_badges(self, trust_level: trust_level)
end
end

View File

@ -24,11 +24,13 @@ class BadgeGranter
StaffActionLogger.new(@granted_by).log_badge_grant(user_badge)
end
if SiteSetting.enable_badges?
@user.notifications.create(notification_type: Notification.types[:granted_badge],
data: { badge_id: @badge.id,
badge_name: @badge.name }.to_json)
end
end
end
user_badge
end
@ -54,4 +56,22 @@ class BadgeGranter
end
end
def self.update_badges(user, opts={})
if opts.has_key?(:trust_level)
# Update trust level badges.
trust_level = opts[:trust_level]
Badge.trust_level_badge_ids.each do |badge_id|
user_badge = UserBadge.find_by(user_id: user.id, badge_id: badge_id)
if user_badge
# Revoke the badge if trust level was lowered.
BadgeGranter.revoke(user_badge) if trust_level < badge_id
else
# Grant the badge if trust level was increased.
badge = Badge.find(badge_id)
BadgeGranter.grant(badge, user) if trust_level >= badge_id
end
end
end
end
end

View File

@ -1850,6 +1850,16 @@ en:
one: "1 granted"
other: "%{count} granted"
select_badge_for_title: Select a badge to use as your title
example_badge:
name: Example Badge
description: This is a generic example badge.
badge:
basic_user:
name: Basic User
description: Trusted as a basic user.
regular_user:
name: Regular User
description: Trusted as a regular user.
leader:
name: Leader
description: One of the most active and prolific users.
elder:
name: Elder
description: Long term community leader who has been around and seen everything.

47
db/fixtures/601_badges.rb Normal file
View File

@ -0,0 +1,47 @@
def reset_badge_grant_count(badge)
badge.grant_count = UserBadge.where(badge_id: badge.id).count
badge.save!
end
def grant_trust_level_badges_to_user(user)
return if user.id == Discourse.system_user.id
Badge.trust_level_badge_ids.each do |badge_id|
user_badge = UserBadge.where(user_id: user.id, badge_id: badge_id).first
if user_badge
# Revoke the badge if the user is not supposed to have it.
if user.trust_level < badge_id
user_badge.destroy!
end
else
# Grant the badge if the user is supposed to have it.
badge = Badge.find(badge_id)
if user.trust_level >= badge_id
UserBadge.create!(badge: badge, user: user, granted_by: Discourse.system_user, granted_at: Time.now)
end
end
end
end
trust_level_badges = [
{id: 1, name: "Basic User", type: 3},
{id: 2, name: "Regular User", type: 3},
{id: 3, name: "Leader", type: 2},
{id: 4, name: "Elder", type: 1}
]
backfill_trust_level_badges = false
trust_level_badges.each do |spec|
backfill_trust_level_badges ||= Badge.where(id: spec[:id]).first.nil?
Badge.seed do |b|
b.id = spec[:id]
b.name = spec[:name]
b.badge_type_id = spec[:type]
end
end
if backfill_trust_level_badges
User.find_each {|user| grant_trust_level_badges_to_user(user) }
Badge.where(id: Badge.trust_level_badge_ids).each {|badge| reset_badge_grant_count(badge) }
end

View File

@ -5,6 +5,6 @@ class MigrateBookmarksToPostActions < ActiveRecord::Migration
def down
# I can reverse this, but not really worth the work
raise ActiveRecord::IrriversableMigration
raise ActiveRecord::IrreversibleMigration
end
end

View File

@ -15,6 +15,6 @@ select
end
def down
raise ActiveRecord::IrriversableMigration
raise ActiveRecord::IrreversibleMigration
end
end

View File

@ -13,6 +13,6 @@ class FixSearch < ActiveRecord::Migration
end
def down
raise ActiveRecord::IrriversableMigration
raise ActiveRecord::IrreversibleMigration
end
end

View File

@ -0,0 +1,16 @@
class IncrementReservedTrustLevelBadgeIds < ActiveRecord::Migration
def up
execute "ALTER SEQUENCE badges_id_seq START WITH 100"
max_badge_id = Badge.order('id DESC').limit(1).first.try(:id)
Badge.where('id > 0 AND id <= 100').find_each do |badge|
new_id = badge.id + max_badge_id + 100
UserBadge.where(badge_id: badge.id).update_all badge_id: new_id
badge.update_column :id, new_id
end
end
def down
raise ActiveRecord::IrreversibleMigration
end
end

View File

@ -18,6 +18,7 @@ class BoostTrustLevel
@user.update_attributes!(trust_level: @level)
end
@logger.log_trust_level_change(@user, previous_level, @level)
BadgeGranter.update_badges(@user, trust_level: @level)
success
end

View File

@ -9,7 +9,7 @@ describe BadgesController do
response.status.should == 200
parsed = JSON.parse(response.body)
parsed["badges"].length.should == 1
parsed["badges"].length.should == Badge.count
end
end

View File

@ -1,10 +1,15 @@
require 'spec_helper'
require_dependency 'boost_trust_level'
describe BadgeGranter do
let(:badge) { Fabricate(:badge) }
let(:user) { Fabricate(:user) }
before do
SiteSetting.enable_badges = true
end
describe 'grant' do
it 'grants a badge' do
@ -66,4 +71,17 @@ describe BadgeGranter do
end
context "update_badges" do
let(:user) { Fabricate(:user) }
let(:logger) { StaffActionLogger.new(Fabricate(:admin)) }
it "grants and revokes trust level badges" do
user.change_trust_level!(:elder)
UserBadge.where(user_id: user.id, badge_id: Badge.trust_level_badge_ids).count.should eq(4)
BoostTrustLevel.new(user: user, level: 1, logger: logger).save!
UserBadge.where(user_id: user.id, badge_id: 1).first.should_not be_nil
UserBadge.where(user_id: user.id, badge_id: 2).first.should be_nil
end
end
end

View File

@ -13,7 +13,7 @@ test('displayName', function() {
this.stub(I18n, "t").returnsArg(0);
var badge2 = Discourse.Badge.create({id: 2, name: "Test Badge 2"});
equal(badge2.get('displayName'), "badges.test_badge_2.name", "uses translation when available");
equal(badge2.get('displayName'), "badges.badge.test_badge_2.name", "uses translation when available");
});
test('translatedDescription', function() {