mirror of
https://github.com/discourse/discourse.git
synced 2025-05-09 03:33:04 +08:00
Merge pull request #2150 from vikhyat/badge-system
Interface for granting/revoking badges from admin user page
This commit is contained in:
commit
28ffb3025c
@ -0,0 +1,52 @@
|
|||||||
|
/**
|
||||||
|
This controller supports the interface for granting and revoking badges from
|
||||||
|
individual users.
|
||||||
|
|
||||||
|
@class AdminUserBadgesController
|
||||||
|
@extends Ember.ArrayController
|
||||||
|
@namespace Discourse
|
||||||
|
@module Discourse
|
||||||
|
**/
|
||||||
|
Discourse.AdminUserBadgesController = Ember.ArrayController.extend({
|
||||||
|
needs: ["adminUser"],
|
||||||
|
user: Em.computed.alias('controllers.adminUser'),
|
||||||
|
sortProperties: ['granted_at'],
|
||||||
|
sortAscending: false,
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
|
||||||
|
/**
|
||||||
|
Grant the selected badge to the user.
|
||||||
|
|
||||||
|
@method grantBadge
|
||||||
|
@param {Integer} badgeId id of the badge we want to grant.
|
||||||
|
**/
|
||||||
|
grantBadge: function(badgeId) {
|
||||||
|
var self = this;
|
||||||
|
Discourse.UserBadge.grant(badgeId, this.get('user.username')).then(function(userBadge) {
|
||||||
|
self.pushObject(userBadge);
|
||||||
|
}, function() {
|
||||||
|
// Failure
|
||||||
|
bootbox.alert(I18n.t('generic_error'));
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
Revoke the selected userBadge.
|
||||||
|
|
||||||
|
@method revokeBadge
|
||||||
|
@param {Discourse.UserBadge} userBadge the `Discourse.UserBadge` instance that needs to be revoked.
|
||||||
|
**/
|
||||||
|
revokeBadge: function(userBadge) {
|
||||||
|
var self = this;
|
||||||
|
return bootbox.confirm(I18n.t("admin.badges.revoke_confirm"), I18n.t("no_value"), I18n.t("yes_value"), function(result) {
|
||||||
|
if (result) {
|
||||||
|
userBadge.revoke().then(function() {
|
||||||
|
self.get('model').removeObject(userBadge);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
@ -47,6 +47,7 @@ Discourse.Route.buildRoutes(function() {
|
|||||||
|
|
||||||
this.resource('adminUsers', { path: '/users' }, function() {
|
this.resource('adminUsers', { path: '/users' }, function() {
|
||||||
this.resource('adminUser', { path: '/:username' }, function() {
|
this.resource('adminUser', { path: '/:username' }, function() {
|
||||||
|
this.route('badges');
|
||||||
this.route('leaderRequirements', { path: '/leader_requirements' });
|
this.route('leaderRequirements', { path: '/leader_requirements' });
|
||||||
});
|
});
|
||||||
this.resource('adminUsersList', { path: '/list' }, function() {
|
this.resource('adminUsersList', { path: '/list' }, function() {
|
||||||
|
@ -0,0 +1,31 @@
|
|||||||
|
/**
|
||||||
|
Shows all of the badges that have been granted to a user, and allow granting and
|
||||||
|
revoking badges.
|
||||||
|
|
||||||
|
@class AdminUserBadgesRoute
|
||||||
|
@extends Discourse.Route
|
||||||
|
@namespace Discourse
|
||||||
|
@module Discourse
|
||||||
|
**/
|
||||||
|
Discourse.AdminUserBadgesRoute = Discourse.Route.extend({
|
||||||
|
model: function() {
|
||||||
|
var username = this.controllerFor('adminUser').get('username');
|
||||||
|
return Discourse.UserBadge.findByUsername(username);
|
||||||
|
},
|
||||||
|
|
||||||
|
setupController: function(controller, model) {
|
||||||
|
// Find all badges.
|
||||||
|
controller.set('loading', true);
|
||||||
|
Discourse.Badge.findAll().then(function(badges) {
|
||||||
|
controller.set('badges', badges);
|
||||||
|
if (badges.length > 0) {
|
||||||
|
controller.set('selectedBadgeId', badges[0].get('id'));
|
||||||
|
} else {
|
||||||
|
controller.set('noBadges', true);
|
||||||
|
}
|
||||||
|
controller.set('loading', false);
|
||||||
|
});
|
||||||
|
// Set the model.
|
||||||
|
controller.set('model', model);
|
||||||
|
}
|
||||||
|
});
|
@ -0,0 +1,61 @@
|
|||||||
|
<div class='admin-controls'>
|
||||||
|
<div class='span15'>
|
||||||
|
<ul class='nav nav-pills'>
|
||||||
|
<li>{{#link-to 'adminUser' user}}<i class="fa fa-caret-left"></i> {{user.username}}{{/link-to}}</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{#if loading}}
|
||||||
|
<div class='spinner'>{{i18n loading}}</div>
|
||||||
|
{{else}}
|
||||||
|
<div class='admin-container user-badges'>
|
||||||
|
<h2>{{i18n admin.badges.grant_badge}}</h2>
|
||||||
|
{{#if noBadges}}
|
||||||
|
<p>{{i18n admin.badges.no_badges}}</p>
|
||||||
|
{{else}}
|
||||||
|
<br>
|
||||||
|
{{combobox valueAttribute="id" value=controller.selectedBadgeId content=controller.badges}}
|
||||||
|
<button class='btn btn-primary' {{action grantBadge controller.selectedBadgeId}}>{{i18n admin.badges.grant}}</button>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
|
||||||
|
<h2>{{i18n admin.badges.granted_badges}}</h2>
|
||||||
|
<br>
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th>{{i18n admin.badges.name}}</th>
|
||||||
|
<th>{{i18n admin.badges.badge_type}}</th>
|
||||||
|
<th>{{i18n admin.badges.granted_by}}</th>
|
||||||
|
<th>{{i18n admin.badges.granted_at}}</th>
|
||||||
|
<th></th>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
{{#each}}
|
||||||
|
<tr>
|
||||||
|
<td>{{badge.displayName}}</td>
|
||||||
|
<td>{{badge.badge_type.name}}</td>
|
||||||
|
<td>
|
||||||
|
{{#link-to 'adminUser' badge.granted_by}}
|
||||||
|
{{avatar granted_by imageSize="tiny"}}
|
||||||
|
{{granted_by.username}}
|
||||||
|
{{/link-to}}
|
||||||
|
</td>
|
||||||
|
<td>{{unboundAgeWithTooltip granted_at}}</td>
|
||||||
|
<td>
|
||||||
|
<button class='btn' {{action revokeBadge this}}>{{i18n admin.badges.revoke}}</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{{else}}
|
||||||
|
<tr>
|
||||||
|
<td colspan="5">
|
||||||
|
<p>{{i18n admin.badges.no_user_badges name=user.username}}</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{{/each}}
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
@ -77,6 +77,18 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{{#if showBadges}}
|
||||||
|
<div class='display-row'>
|
||||||
|
<div class='field'>{{i18n admin.badges.title}}</div>
|
||||||
|
<div class='value'>
|
||||||
|
TODO featured badges
|
||||||
|
</div>
|
||||||
|
<div class='controls'>
|
||||||
|
{{#link-to 'adminUser.badges' this class="btn"}}{{i18n admin.badges.edit_badges}}{{/link-to}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|
||||||
@ -336,12 +348,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{{#if showBadges}}
|
|
||||||
<section class='details'>
|
|
||||||
<h1>{{i18n admin.badges.title}}</h1>
|
|
||||||
</section>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
<hr/>
|
<hr/>
|
||||||
<button {{bind-attr class=":btn :btn-danger :pull-right deleteForbidden:hidden"}} {{action destroy target="content"}} {{bind-attr disabled="deleteForbidden"}}>
|
<button {{bind-attr class=":btn :btn-danger :pull-right deleteForbidden:hidden"}} {{action destroy target="content"}} {{bind-attr disabled="deleteForbidden"}}>
|
||||||
|
@ -7,6 +7,17 @@
|
|||||||
@module Discourse
|
@module Discourse
|
||||||
**/
|
**/
|
||||||
Discourse.UserBadge = Discourse.Model.extend({
|
Discourse.UserBadge = Discourse.Model.extend({
|
||||||
|
/**
|
||||||
|
Revoke this badge.
|
||||||
|
|
||||||
|
@method revoke
|
||||||
|
@returns {Promise} a promise that resolves when the badge has been revoked.
|
||||||
|
**/
|
||||||
|
revoke: function() {
|
||||||
|
return Discourse.ajax("/user_badges/" + this.get('id'), {
|
||||||
|
type: "DELETE"
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Discourse.UserBadge.reopenClass({
|
Discourse.UserBadge.reopenClass({
|
||||||
@ -19,14 +30,15 @@ Discourse.UserBadge.reopenClass({
|
|||||||
**/
|
**/
|
||||||
createFromJson: function(json) {
|
createFromJson: function(json) {
|
||||||
// Create User objects.
|
// Create User objects.
|
||||||
|
if (json.users === undefined) { json.users = []; }
|
||||||
var users = {};
|
var users = {};
|
||||||
json.users.forEach(function(userJson) {
|
json.users.forEach(function(userJson) {
|
||||||
users[userJson.id] = Discourse.User.create(userJson);
|
users[userJson.id] = Discourse.User.create(userJson);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Create the badges.
|
// Create the badges.
|
||||||
|
if (json.badges === undefined) { json.badges = []; }
|
||||||
var badges = {};
|
var badges = {};
|
||||||
|
|
||||||
Discourse.Badge.createFromJson(json).forEach(function(badge) {
|
Discourse.Badge.createFromJson(json).forEach(function(badge) {
|
||||||
badges[badge.get('id')] = badge;
|
badges[badge.get('id')] = badge;
|
||||||
});
|
});
|
||||||
@ -53,5 +65,37 @@ Discourse.UserBadge.reopenClass({
|
|||||||
} else {
|
} else {
|
||||||
return userBadges;
|
return userBadges;
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
Find all badges for a given username.
|
||||||
|
|
||||||
|
@method findByUsername
|
||||||
|
@returns {Promise} a promise that resolves to an array of `Discourse.UserBadge`.
|
||||||
|
**/
|
||||||
|
findByUsername: function(username) {
|
||||||
|
return Discourse.ajax("/user_badges.json?username=" + username).then(function(json) {
|
||||||
|
return Discourse.UserBadge.createFromJson(json);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
Grant the badge having id `badgeId` to the user identified by `username`.
|
||||||
|
|
||||||
|
@method grant
|
||||||
|
@param {Integer} badgeId id of the badge to be granted.
|
||||||
|
@param {String} username username of the user to be granted the badge.
|
||||||
|
@returns {Promise} a promise that resolves to an instance of `Discourse.UserBadge`.
|
||||||
|
**/
|
||||||
|
grant: function(badgeId, username) {
|
||||||
|
return Discourse.ajax("/user_badges", {
|
||||||
|
type: "POST",
|
||||||
|
data: {
|
||||||
|
username: username,
|
||||||
|
badge_id: badgeId
|
||||||
|
}
|
||||||
|
}).then(function(json) {
|
||||||
|
return Discourse.UserBadge.createFromJson(json);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -42,7 +42,7 @@ Discourse.ComboboxView = Discourse.View.extend({
|
|||||||
if (val) { val = val.toString(); }
|
if (val) { val = val.toString(); }
|
||||||
|
|
||||||
var selectedText = (val === selected) ? "selected" : "";
|
var selectedText = (val === selected) ? "selected" : "";
|
||||||
buffer.push("<option " + selectedText + " value=\"" + val + "\" " + self.buildData(o) + ">" + Em.get(o, nameProperty) + "</option>");
|
buffer.push("<option " + selectedText + " value=\"" + val + "\" " + self.buildData(o) + ">" + Handlebars.Utils.escapeExpression(Em.get(o, nameProperty)) + "</option>");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -169,6 +169,9 @@ class Admin::UsersController < Admin::AdminController
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def badges
|
||||||
|
end
|
||||||
|
|
||||||
def leader_requirements
|
def leader_requirements
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@ class UserBadgesController < ApplicationController
|
|||||||
def index
|
def index
|
||||||
params.require(:username)
|
params.require(:username)
|
||||||
user = fetch_user_from_params
|
user = fetch_user_from_params
|
||||||
render json: user.user_badges
|
render_serialized(user.user_badges, UserBadgeSerializer, root: "user_badges")
|
||||||
end
|
end
|
||||||
|
|
||||||
def create
|
def create
|
||||||
@ -17,7 +17,7 @@ class UserBadgesController < ApplicationController
|
|||||||
badge = fetch_badge_from_params
|
badge = fetch_badge_from_params
|
||||||
user_badge = BadgeGranter.grant(badge, user, granted_by: current_user)
|
user_badge = BadgeGranter.grant(badge, user, granted_by: current_user)
|
||||||
|
|
||||||
render json: user_badge
|
render_serialized(user_badge, UserBadgeSerializer, root: "user_badge")
|
||||||
end
|
end
|
||||||
|
|
||||||
def destroy
|
def destroy
|
||||||
@ -29,7 +29,7 @@ class UserBadgesController < ApplicationController
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
BadgeGranter.revoke(user_badge)
|
BadgeGranter.revoke(user_badge, revoked_by: current_user)
|
||||||
render json: success_json
|
render json: success_json
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -23,7 +23,9 @@ class UserHistory < ActiveRecord::Base
|
|||||||
:notified_about_dominating_topic,
|
:notified_about_dominating_topic,
|
||||||
:suspend_user,
|
:suspend_user,
|
||||||
:unsuspend_user,
|
:unsuspend_user,
|
||||||
:facebook_no_email)
|
:facebook_no_email,
|
||||||
|
:grant_badge,
|
||||||
|
:revoke_badge)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Staff actions is a subset of all actions, used to audit actions taken by staff users.
|
# Staff actions is a subset of all actions, used to audit actions taken by staff users.
|
||||||
@ -34,7 +36,9 @@ class UserHistory < ActiveRecord::Base
|
|||||||
:change_site_customization,
|
:change_site_customization,
|
||||||
:delete_site_customization,
|
:delete_site_customization,
|
||||||
:suspend_user,
|
:suspend_user,
|
||||||
:unsuspend_user]
|
:unsuspend_user,
|
||||||
|
:grant_badge,
|
||||||
|
:revoke_badge]
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.staff_action_ids
|
def self.staff_action_ids
|
||||||
|
@ -19,15 +19,21 @@ class BadgeGranter
|
|||||||
granted_by: @granted_by, granted_at: Time.now)
|
granted_by: @granted_by, granted_at: Time.now)
|
||||||
|
|
||||||
Badge.increment_counter 'grant_count', @badge.id
|
Badge.increment_counter 'grant_count', @badge.id
|
||||||
|
if @granted_by != Discourse.system_user
|
||||||
|
StaffActionLogger.new(@granted_by).log_badge_grant(user_badge)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
user_badge
|
user_badge
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.revoke(user_badge)
|
def self.revoke(user_badge, options={})
|
||||||
UserBadge.transaction do
|
UserBadge.transaction do
|
||||||
user_badge.destroy!
|
user_badge.destroy!
|
||||||
Badge.decrement_counter 'grant_count', user_badge.badge.id
|
Badge.decrement_counter 'grant_count', user_badge.badge.id
|
||||||
|
if options[:revoked_by]
|
||||||
|
StaffActionLogger.new(options[:revoked_by]).log_badge_revoke(user_badge)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -74,6 +74,24 @@ class StaffActionLogger
|
|||||||
}))
|
}))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def log_badge_grant(user_badge, opts={})
|
||||||
|
raise Discourse::InvalidParameters.new('user_badge is nil') unless user_badge
|
||||||
|
UserHistory.create( params(opts).merge({
|
||||||
|
action: UserHistory.actions[:grant_badge],
|
||||||
|
target_user_id: user_badge.user_id,
|
||||||
|
details: user_badge.badge.name
|
||||||
|
}))
|
||||||
|
end
|
||||||
|
|
||||||
|
def log_badge_revoke(user_badge, opts={})
|
||||||
|
raise Discourse::InvalidParameters.new('user_badge is nil') unless user_badge
|
||||||
|
UserHistory.create( params(opts).merge({
|
||||||
|
action: UserHistory.actions[:revoke_badge],
|
||||||
|
target_user_id: user_badge.user_id,
|
||||||
|
details: user_badge.badge.name
|
||||||
|
}))
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def params(opts)
|
def params(opts)
|
||||||
|
@ -1484,6 +1484,8 @@ en:
|
|||||||
delete_site_customization: "delete site customization"
|
delete_site_customization: "delete site customization"
|
||||||
suspend_user: "suspend user"
|
suspend_user: "suspend user"
|
||||||
unsuspend_user: "unsuspend user"
|
unsuspend_user: "unsuspend user"
|
||||||
|
grant_badge: "grant badge"
|
||||||
|
revoke_badge: "revoke badge"
|
||||||
screened_emails:
|
screened_emails:
|
||||||
title: "Screened Emails"
|
title: "Screened Emails"
|
||||||
description: "When someone tries to create a new account, the following email addresses will be checked and the registration will be blocked, or some other action performed."
|
description: "When someone tries to create a new account, the following email addresses will be checked and the registration will be blocked, or some other action performed."
|
||||||
@ -1678,9 +1680,19 @@ en:
|
|||||||
display_name: Display Name
|
display_name: Display Name
|
||||||
description: Description
|
description: Description
|
||||||
badge_type: Badge Type
|
badge_type: Badge Type
|
||||||
|
granted_by: Granted By
|
||||||
|
granted_at: Granted At
|
||||||
save: Save
|
save: Save
|
||||||
delete: Delete
|
delete: Delete
|
||||||
delete_confirm: Are you sure you want to delete this badge?
|
delete_confirm: Are you sure you want to delete this badge?
|
||||||
|
revoke: Revoke
|
||||||
|
revoke_confirm: Are you sure you want to revoke this badge?
|
||||||
|
edit_badges: Edit Badges
|
||||||
|
grant_badge: Grant Badge
|
||||||
|
granted_badges: Granted Badges
|
||||||
|
grant: Grant
|
||||||
|
no_user_badges: "%{name} has not been granted any badges."
|
||||||
|
no_badges: There are no badges that can be granted.
|
||||||
|
|
||||||
lightbox:
|
lightbox:
|
||||||
download: "download"
|
download: "download"
|
||||||
|
@ -61,6 +61,7 @@ Discourse::Application.routes.draw do
|
|||||||
put "unblock"
|
put "unblock"
|
||||||
put "trust_level"
|
put "trust_level"
|
||||||
put "primary_group"
|
put "primary_group"
|
||||||
|
get "badges"
|
||||||
get "leader_requirements"
|
get "leader_requirements"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -36,6 +36,7 @@ describe UserBadgesController do
|
|||||||
it 'grants badges from staff' do
|
it 'grants badges from staff' do
|
||||||
admin = Fabricate(:admin)
|
admin = Fabricate(:admin)
|
||||||
log_in_user admin
|
log_in_user admin
|
||||||
|
StaffActionLogger.any_instance.expects(:log_badge_grant).once
|
||||||
xhr :post, :create, badge_id: badge.id, username: user.username
|
xhr :post, :create, badge_id: badge.id, username: user.username
|
||||||
response.status.should == 200
|
response.status.should == 200
|
||||||
user_badge = UserBadge.where(user: user, badge: badge).first
|
user_badge = UserBadge.where(user: user, badge: badge).first
|
||||||
@ -51,6 +52,7 @@ describe UserBadgesController do
|
|||||||
|
|
||||||
it 'grants badges from master api calls' do
|
it 'grants badges from master api calls' do
|
||||||
api_key = Fabricate(:api_key)
|
api_key = Fabricate(:api_key)
|
||||||
|
StaffActionLogger.any_instance.expects(:log_badge_grant).never
|
||||||
xhr :post, :create, badge_id: badge.id, username: user.username, api_key: api_key.key
|
xhr :post, :create, badge_id: badge.id, username: user.username, api_key: api_key.key
|
||||||
response.status.should == 200
|
response.status.should == 200
|
||||||
user_badge = UserBadge.where(user: user, badge: badge).first
|
user_badge = UserBadge.where(user: user, badge: badge).first
|
||||||
@ -71,6 +73,7 @@ describe UserBadgesController do
|
|||||||
|
|
||||||
it 'revokes the badge' do
|
it 'revokes the badge' do
|
||||||
log_in :admin
|
log_in :admin
|
||||||
|
StaffActionLogger.any_instance.expects(:log_badge_revoke).once
|
||||||
xhr :delete, :destroy, id: @user_badge.id
|
xhr :delete, :destroy, id: @user_badge.id
|
||||||
response.status.should == 200
|
response.status.should == 200
|
||||||
UserBadge.where(id: @user_badge.id).first.should be_nil
|
UserBadge.where(id: @user_badge.id).first.should be_nil
|
||||||
|
@ -24,11 +24,13 @@ describe BadgeGranter do
|
|||||||
|
|
||||||
it 'sets granted_by if the option is present' do
|
it 'sets granted_by if the option is present' do
|
||||||
admin = Fabricate(:admin)
|
admin = Fabricate(:admin)
|
||||||
|
StaffActionLogger.any_instance.expects(:log_badge_grant).once
|
||||||
user_badge = BadgeGranter.grant(badge, user, granted_by: admin)
|
user_badge = BadgeGranter.grant(badge, user, granted_by: admin)
|
||||||
user_badge.granted_by.should eq(admin)
|
user_badge.granted_by.should eq(admin)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'defaults granted_by to the system user' do
|
it 'defaults granted_by to the system user' do
|
||||||
|
StaffActionLogger.any_instance.expects(:log_badge_grant).never
|
||||||
user_badge = BadgeGranter.grant(badge, user)
|
user_badge = BadgeGranter.grant(badge, user)
|
||||||
user_badge.granted_by_id.should eq(Discourse.system_user.id)
|
user_badge.granted_by_id.should eq(Discourse.system_user.id)
|
||||||
end
|
end
|
||||||
@ -47,11 +49,13 @@ describe BadgeGranter do
|
|||||||
|
|
||||||
describe 'revoke' do
|
describe 'revoke' do
|
||||||
|
|
||||||
|
let(:admin) { Fabricate(:admin) }
|
||||||
let!(:user_badge) { BadgeGranter.grant(badge, user) }
|
let!(:user_badge) { BadgeGranter.grant(badge, user) }
|
||||||
|
|
||||||
it 'revokes the badge and decrements grant_count' do
|
it 'revokes the badge and decrements grant_count' do
|
||||||
badge.reload.grant_count.should eq(1)
|
badge.reload.grant_count.should eq(1)
|
||||||
BadgeGranter.revoke(user_badge)
|
StaffActionLogger.any_instance.expects(:log_badge_revoke).with(user_badge)
|
||||||
|
BadgeGranter.revoke(user_badge, revoked_by: admin)
|
||||||
UserBadge.where(user: user, badge: badge).first.should_not be_present
|
UserBadge.where(user: user, badge: badge).first.should_not be_present
|
||||||
badge.reload.grant_count.should eq(0)
|
badge.reload.grant_count.should eq(0)
|
||||||
end
|
end
|
||||||
|
@ -150,4 +150,38 @@ describe StaffActionLogger do
|
|||||||
log_record.target_user.should == user
|
log_record.target_user.should == user
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "log_badge_grant" do
|
||||||
|
let(:user) { Fabricate(:user) }
|
||||||
|
let(:badge) { Fabricate(:badge) }
|
||||||
|
let(:user_badge) { BadgeGranter.grant(badge, user) }
|
||||||
|
|
||||||
|
it "raises an error when argument is missing" do
|
||||||
|
expect { logger.log_badge_grant(nil) }.to raise_error(Discourse::InvalidParameters)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "creates a new UserHistory record" do
|
||||||
|
log_record = logger.log_badge_grant(user_badge)
|
||||||
|
log_record.should be_valid
|
||||||
|
log_record.target_user.should == user
|
||||||
|
log_record.details.should == badge.name
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "log_badge_revoke" do
|
||||||
|
let(:user) { Fabricate(:user) }
|
||||||
|
let(:badge) { Fabricate(:badge) }
|
||||||
|
let(:user_badge) { BadgeGranter.grant(badge, user) }
|
||||||
|
|
||||||
|
it "raises an error when argument is missing" do
|
||||||
|
expect { logger.log_badge_revoke(nil) }.to raise_error(Discourse::InvalidParameters)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "creates a new UserHistory record" do
|
||||||
|
log_record = logger.log_badge_revoke(user_badge)
|
||||||
|
log_record.should be_valid
|
||||||
|
log_record.target_user.should == user
|
||||||
|
log_record.details.should == badge.name
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
module("Discourse.UserBadge");
|
module("Discourse.UserBadge");
|
||||||
|
|
||||||
test('createFromJson single', function() {
|
var singleBadgeJson = {"badges":[{"id":874,"name":"Badge 2","description":null,"badge_type_id":7}],"badge_types":[{"id":7,"name":"Silver 2","color_hexcode":"#c0c0c0"}],"users":[{"id":13470,"username":"anne3","avatar_template":"//www.gravatar.com/avatar/a4151b1fd72089c54e2374565a87da7f.png?s={size}\u0026r=pg\u0026d=identicon"}],"user_badge":{"id":665,"granted_at":"2014-03-09T20:30:01.190-04:00","badge_id":874,"granted_by_id":13470}},
|
||||||
var json = {"badges":[{"id":874,"name":"Badge 2","description":null,"badge_type_id":7}],"badge_types":[{"id":7,"name":"Silver 2","color_hexcode":"#c0c0c0"}],"users":[{"id":13470,"username":"anne3","avatar_template":"//www.gravatar.com/avatar/a4151b1fd72089c54e2374565a87da7f.png?s={size}\u0026r=pg\u0026d=identicon"}],"user_badge":{"id":665,"granted_at":"2014-03-09T20:30:01.190-04:00","badge_id":874,"granted_by_id":13470}};
|
multipleBadgesJson = {"badges":[{"id":880,"name":"Badge 8","description":null,"badge_type_id":13}],"badge_types":[{"id":13,"name":"Silver 8","color_hexcode":"#c0c0c0"}],"users":[],"user_badges":[{"id":668,"granted_at":"2014-03-09T20:30:01.420-04:00","badge_id":880,"granted_by_id":null}]};
|
||||||
|
|
||||||
var userBadge = Discourse.UserBadge.createFromJson(json);
|
test('createFromJson single', function() {
|
||||||
|
var userBadge = Discourse.UserBadge.createFromJson(singleBadgeJson);
|
||||||
ok(!Array.isArray(userBadge), "does not return an array");
|
ok(!Array.isArray(userBadge), "does not return an array");
|
||||||
equal(userBadge.get('badge.name'), "Badge 2", "badge reference is set");
|
equal(userBadge.get('badge.name'), "Badge 2", "badge reference is set");
|
||||||
equal(userBadge.get('badge.badge_type.name'), "Silver 2", "badge.badge_type reference is set");
|
equal(userBadge.get('badge.badge_type.name'), "Silver 2", "badge.badge_type reference is set");
|
||||||
@ -11,9 +12,30 @@ test('createFromJson single', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('createFromJson array', function() {
|
test('createFromJson array', function() {
|
||||||
var json = {"badges":[{"id":880,"name":"Badge 8","description":null,"badge_type_id":13}],"badge_types":[{"id":13,"name":"Silver 8","color_hexcode":"#c0c0c0"}],"users":[],"user_badges":[{"id":668,"granted_at":"2014-03-09T20:30:01.420-04:00","badge_id":880,"granted_by_id":null}]};
|
var userBadges = Discourse.UserBadge.createFromJson(multipleBadgesJson);
|
||||||
|
|
||||||
var userBadges = Discourse.UserBadge.createFromJson(json);
|
|
||||||
ok(Array.isArray(userBadges), "returns an array");
|
ok(Array.isArray(userBadges), "returns an array");
|
||||||
equal(userBadges[0].get('granted_by'), null, "granted_by reference is not set when null");
|
equal(userBadges[0].get('granted_by'), null, "granted_by reference is not set when null");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('findByUsername', function() {
|
||||||
|
this.stub(Discourse, 'ajax').returns(Ember.RSVP.resolve(multipleBadgesJson));
|
||||||
|
Discourse.UserBadge.findByUsername("anne3").then(function(badges) {
|
||||||
|
ok(Array.isArray(badges), "returns an array");
|
||||||
|
});
|
||||||
|
ok(Discourse.ajax.calledOnce, "makes an AJAX call");
|
||||||
|
});
|
||||||
|
|
||||||
|
test('grant', function() {
|
||||||
|
this.stub(Discourse, 'ajax').returns(Ember.RSVP.resolve(singleBadgeJson));
|
||||||
|
Discourse.UserBadge.grant(1, "username").then(function(userBadge) {
|
||||||
|
ok(!Array.isArray(userBadge), "does not return an array");
|
||||||
|
});
|
||||||
|
ok(Discourse.ajax.calledOnce, "makes an AJAX call");
|
||||||
|
});
|
||||||
|
|
||||||
|
test('revoke', function() {
|
||||||
|
this.stub(Discourse, 'ajax');
|
||||||
|
var userBadge = Discourse.UserBadge.create({id: 1});
|
||||||
|
userBadge.revoke();
|
||||||
|
ok(Discourse.ajax.calledOnce, "makes an AJAX call");
|
||||||
|
});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user