Merge pull request #2480 from vikhyat/badge-system
Add consistency check to revoke invalid badges
This commit is contained in:
commit
777371bf14
|
@ -23,7 +23,7 @@
|
||||||
<form class="form-horizontal">
|
<form class="form-horizontal">
|
||||||
<div>
|
<div>
|
||||||
<label for="name">{{i18n admin.badges.name}}</label>
|
<label for="name">{{i18n admin.badges.name}}</label>
|
||||||
{{input type="text" name="name" value=name disabled=readonly}}
|
{{input type="text" name="name" value=name disabled=readOnly}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{#if showDisplayName}}
|
{{#if showDisplayName}}
|
||||||
|
@ -35,7 +35,7 @@
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label for="name">{{i18n admin.badges.icon}}</label>
|
<label for="name">{{i18n admin.badges.icon}}</label>
|
||||||
{{input type="text" name="name" value=icon disabled=readonly}}
|
{{input type="text" name="name" value=icon disabled=readOnly}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
|
@ -79,6 +79,12 @@
|
||||||
{{/unless}}
|
{{/unless}}
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="span13 current-badge-actions">
|
||||||
|
<div>
|
||||||
|
{{#link-to 'badges.show' this classNames="btn btn-primary"}}{{i18n badges.granted count=grant_count}}{{/link-to}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
{{/with}}
|
{{/with}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
|
|
|
@ -36,6 +36,13 @@ Discourse.UserBadge.reopenClass({
|
||||||
users[userJson.id] = Discourse.User.create(userJson);
|
users[userJson.id] = Discourse.User.create(userJson);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Create Topic objects.
|
||||||
|
if (json.topics === undefined) { json.topics = []; }
|
||||||
|
var topics = {};
|
||||||
|
json.topics.forEach(function(topicJson) {
|
||||||
|
topics[topicJson.id] = Discourse.Topic.create(topicJson);
|
||||||
|
});
|
||||||
|
|
||||||
// Create the badges.
|
// Create the badges.
|
||||||
if (json.badges === undefined) { json.badges = []; }
|
if (json.badges === undefined) { json.badges = []; }
|
||||||
var badges = {};
|
var badges = {};
|
||||||
|
@ -64,6 +71,9 @@ Discourse.UserBadge.reopenClass({
|
||||||
if (userBadge.get('granted_by_id')) {
|
if (userBadge.get('granted_by_id')) {
|
||||||
userBadge.set('granted_by', users[userBadge.get('granted_by_id')]);
|
userBadge.set('granted_by', users[userBadge.get('granted_by_id')]);
|
||||||
}
|
}
|
||||||
|
if (userBadge.get('topic_id')) {
|
||||||
|
userBadge.set('topic', topics[userBadge.get('topic_id')]);
|
||||||
|
}
|
||||||
return userBadge;
|
return userBadge;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -403,6 +403,12 @@ section.details {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.current-badge-actions {
|
||||||
|
margin: 10px;
|
||||||
|
padding: 10px;
|
||||||
|
border-top: 1px solid scale-color($primary, $lightness: 80%);
|
||||||
|
}
|
||||||
|
|
||||||
.buttons {
|
.buttons {
|
||||||
float: left;
|
float: left;
|
||||||
width: 200px;
|
width: 200px;
|
||||||
|
|
|
@ -14,7 +14,7 @@ class UserBadgesController < ApplicationController
|
||||||
user_badges = user_badges.where('granted_at < ?', Time.at(params[:granted_before].to_f))
|
user_badges = user_badges.where('granted_at < ?', Time.at(params[:granted_before].to_f))
|
||||||
end
|
end
|
||||||
|
|
||||||
user_badges = user_badges.includes(:user, :granted_by, badge: :badge_type)
|
user_badges = user_badges.includes(:user, :granted_by, badge: :badge_type, post: :topic)
|
||||||
|
|
||||||
if params[:grouped]
|
if params[:grouped]
|
||||||
user_badges = user_badges.group(:badge_id).select(UserBadge.attribute_names.map {|x| "MAX(#{x}) as #{x}" }, 'COUNT(*) as count')
|
user_badges = user_badges.group(:badge_id).select(UserBadge.attribute_names.map {|x| "MAX(#{x}) as #{x}" }, 'COUNT(*) as count')
|
||||||
|
|
|
@ -9,6 +9,7 @@ module Jobs
|
||||||
Group.refresh_automatic_groups!
|
Group.refresh_automatic_groups!
|
||||||
Notification.ensure_consistency!
|
Notification.ensure_consistency!
|
||||||
UserAction.ensure_consistency!
|
UserAction.ensure_consistency!
|
||||||
|
UserBadge.ensure_consistency!
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,6 +3,7 @@ class UserBadge < ActiveRecord::Base
|
||||||
belongs_to :user
|
belongs_to :user
|
||||||
belongs_to :granted_by, class_name: 'User'
|
belongs_to :granted_by, class_name: 'User'
|
||||||
belongs_to :notification, dependent: :destroy
|
belongs_to :notification, dependent: :destroy
|
||||||
|
belongs_to :post
|
||||||
|
|
||||||
validates :badge_id, presence: true, uniqueness: {scope: :user_id}, if: 'badge.single_grant?'
|
validates :badge_id, presence: true, uniqueness: {scope: :user_id}, if: 'badge.single_grant?'
|
||||||
validates :user_id, presence: true
|
validates :user_id, presence: true
|
||||||
|
@ -16,6 +17,17 @@ class UserBadge < ActiveRecord::Base
|
||||||
after_destroy do
|
after_destroy do
|
||||||
Badge.decrement_counter 'grant_count', self.badge_id
|
Badge.decrement_counter 'grant_count', self.badge_id
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Make sure we don't have duplicate badges.
|
||||||
|
def self.ensure_consistency!
|
||||||
|
dup_ids = []
|
||||||
|
# Single grant badges shouldn't have duplicates at all.
|
||||||
|
dup_ids += exec_sql("SELECT u1.id FROM user_badges u1, user_badges u2, badges WHERE u1.badge_id = badges.id AND u1.user_id = u2.user_id AND u1.badge_id = u2.badge_id AND (NOT badges.multiple_grant) AND u1.granted_at > u2.granted_at").to_a
|
||||||
|
# Multiple grant badges can have duplicates but not with the same post_ids.
|
||||||
|
dup_ids += exec_sql("SELECT u1.id FROM user_badges u1, user_badges u2, badges WHERE u1.badge_id = badges.id AND u1.user_id = u2.user_id AND u1.badge_id = u2.badge_id AND badges.multiple_grant AND u1.post_id = u2.post_id AND u1.granted_at > u2.granted_at").to_a
|
||||||
|
dup_ids = dup_ids.map {|x| x["id"].to_i }
|
||||||
|
UserBadge.where(id: dup_ids).destroy_all
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# == Schema Information
|
# == Schema Information
|
||||||
|
|
|
@ -1,11 +1,21 @@
|
||||||
class UserBadgeSerializer < ApplicationSerializer
|
class UserBadgeSerializer < ApplicationSerializer
|
||||||
attributes :id, :granted_at, :count
|
attributes :id, :granted_at, :count, :post_id
|
||||||
|
|
||||||
has_one :badge
|
has_one :badge
|
||||||
has_one :user, serializer: BasicUserSerializer, root: :users
|
has_one :user, serializer: BasicUserSerializer, root: :users
|
||||||
has_one :granted_by, serializer: BasicUserSerializer, root: :users
|
has_one :granted_by, serializer: BasicUserSerializer, root: :users
|
||||||
|
has_one :topic, serializer: BasicTopicSerializer
|
||||||
|
|
||||||
def include_count?
|
def include_count?
|
||||||
object.respond_to? :count
|
object.respond_to? :count
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def include_post_id?
|
||||||
|
!object.post_id.nil?
|
||||||
|
end
|
||||||
|
alias :include_topic? :include_post_id?
|
||||||
|
|
||||||
|
def topic
|
||||||
|
object.post.topic
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue