Merge pull request #2372 from vikhyat/badge-system

Multiple grant badges
This commit is contained in:
Sam
2014-05-30 10:18:00 +10:00
25 changed files with 171 additions and 42 deletions

View File

@@ -9,14 +9,54 @@
Discourse.AdminBadgesController = Ember.ArrayController.extend({
itemController: 'adminBadge',
queryParams: ['badgeId'],
badgeId: Em.computed.alias('selectedId'),
/**
ID of the currently selected badge.
@property badgeId
@property selectedId
@type {Integer}
**/
badgeId: Em.computed.alias('selectedItem.id'),
selectedId: null,
/**
Badge that is currently selected.
@property selectedItem
@type {Discourse.Badge}
**/
selectedItem: function() {
if (this.get('selectedId') === undefined || this.get('selectedId') === "undefined") {
// New Badge
return this.get('newBadge');
} else {
// Existing Badge
var selectedId = parseInt(this.get('selectedId'));
return this.get('model').filter(function(badge) {
return parseInt(badge.get('id')) === selectedId;
})[0];
}
}.property('selectedId', 'newBadge'),
/**
Unsaved badge, if one exists.
@property newBadge
@type {Discourse.Badge}
**/
newBadge: function() {
return this.get('model').filter(function(badge) {
return badge.get('id') === undefined;
})[0];
}.property('model.@each.id'),
/**
Whether a new unsaved badge exists.
@property newBadgeExists
@type {Discourse.Badge}
**/
newBadgeExists: Em.computed.notEmpty('newBadge'),
/**
We don't allow setting a description if a translation for the given badge
@@ -42,7 +82,7 @@ Discourse.AdminBadgesController = Ember.ArrayController.extend({
@method newBadge
**/
newBadge: function() {
createNewBadge: function() {
var badge = Discourse.Badge.create({
name: I18n.t('admin.badges.new_badge')
});
@@ -57,7 +97,7 @@ Discourse.AdminBadgesController = Ember.ArrayController.extend({
@param {Discourse.Badge} badge The badge to be selected
**/
selectBadge: function(badge) {
this.set('selectedItem', badge);
this.set('selectedId', badge.get('id'));
},
/**
@@ -80,7 +120,7 @@ Discourse.AdminBadgesController = Ember.ArrayController.extend({
// Delete immediately if the selected badge is new.
if (!this.get('selectedItem.id')) {
this.get('model').removeObject(this.get('selectedItem'));
this.set('selectedItem', null);
this.set('selectedId', null);
return;
}
@@ -90,7 +130,7 @@ Discourse.AdminBadgesController = Ember.ArrayController.extend({
var selected = self.get('selectedItem');
selected.destroy().then(function() {
// Success.
self.set('selectedItem', null);
self.set('selectedId', null);
self.get('model').removeObject(selected);
}, function() {
// Failure.

View File

@@ -27,7 +27,7 @@ Discourse.AdminUserBadgesController = Ember.ArrayController.extend({
var badges = [];
this.get('badges').forEach(function(badge) {
if (!granted[badge.get('id')]) {
if (badge.get('multiple_grant') || !granted[badge.get('id')]) {
badges.push(badge);
}
});

View File

@@ -14,7 +14,7 @@
</li>
{{/each}}
</ul>
<button {{action newBadge}} class='btn'><i class="fa fa-plus"></i>{{i18n admin.badges.new}}</button>
<button {{action createNewBadge}} {{bind-attr disabled=newBadgeExists}} class='btn'><i class="fa fa-plus"></i>{{i18n admin.badges.new}}</button>
</div>
{{#if selectedItem}}
@@ -47,7 +47,7 @@
{{#if controller.canEditDescription}}
{{textarea name="description" value=description}}
{{else}}
{{textarea name="description" value=translatedDescription disabled=true}}
{{textarea name="description" value=displayDescription disabled=true}}
{{/if}}
</div>
@@ -58,6 +58,13 @@
</span>
</div>
<div>
<span>
{{input type="checkbox" checked=multiple_grant disabled=readOnly}}
{{i18n admin.badges.multiple_grant}}
</span>
</div>
{{#unless readOnly}}
<div class='buttons'>
<button {{action save}} {{bind-attr disabled=controller.disableSave}} class='btn btn-primary'>{{i18n admin.badges.save}}</button>

View File

@@ -3,5 +3,9 @@ Discourse.UserBadgeComponent = Ember.Component.extend({
badgeTypeClassName: function() {
return "badge-type-" + this.get('badge.badge_type.name').toLowerCase();
}.property('badge.badge_type.name')
}.property('badge.badge_type.name'),
showGrantCount: function() {
return this.get('count') && this.get('count') > 1;
}.property('count')
});

View File

@@ -40,8 +40,8 @@ Discourse.Badge = Discourse.Model.extend({
}.property('name', 'i18nNameKey'),
/**
The i18n translated description for this badge. Returns the original
description if no translation exists.
The i18n translated description for this badge. Returns the null if no
translation exists.
@property translatedDescription
@type {String}
@@ -50,11 +50,23 @@ Discourse.Badge = Discourse.Model.extend({
var i18nKey = "badges.badge." + this.get('i18nNameKey') + ".description",
translation = I18n.t(i18nKey);
if (translation.indexOf(i18nKey) !== -1) {
translation = this.get('description');
translation = null;
}
return translation;
}.property('i18nNameKey'),
/**
Display-friendly description string. Returns either a translation or the
original description string.
@property displayDescription
@type {String}
**/
displayDescription: function() {
var translated = this.get('translatedDescription');
return translated === null ? this.get('description') : translated;
}.property('description', 'translatedDescription'),
/**
Update this badge with the response returned by the server on save.
@@ -103,7 +115,8 @@ Discourse.Badge = Discourse.Model.extend({
name: this.get('name'),
description: this.get('description'),
badge_type_id: this.get('badge_type_id'),
allow_title: this.get('allow_title')
allow_title: !!this.get('allow_title'),
multiple_grant: !!this.get('multiple_grant')
}
}).then(function(json) {
self.updateFromJson(json);

View File

@@ -79,10 +79,15 @@ Discourse.UserBadge.reopenClass({
@method findByUsername
@param {String} username
@param {Object} options
@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) {
findByUsername: function(username, options) {
var url = "/user_badges.json?username=" + username;
if (options && options.aggregated) {
url += "&aggregated=true";
}
return Discourse.ajax(url).then(function(json) {
return Discourse.UserBadge.createFromJson(json);
});
},

View File

@@ -8,7 +8,7 @@
**/
Discourse.UserBadgesRoute = Discourse.Route.extend({
model: function() {
return Discourse.UserBadge.findByUsername(this.modelFor('user').get('username_lower'));
return Discourse.UserBadge.findByUsername(this.modelFor('user').get('username_lower'), {aggregated: true});
},
setupController: function(controller, model) {

View File

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

View File

@@ -1,6 +1,9 @@
{{#link-to 'badges.show' badge}}
<span {{bind-attr class=":user-badge badgeTypeClassName" data-badge-name="badge.name" title="badge.translatedDescription"}}>
<span {{bind-attr class=":user-badge badgeTypeClassName" data-badge-name="badge.name" title="badge.displayDescription"}}>
<i class='fa fa-certificate'></i>
{{badge.displayName}}
{{#if showGrantCount}}
<span class="count">(&times;&nbsp;{{count}})</span>
{{/if}}
</span>
{{/link-to}}

View File

@@ -1,5 +1,5 @@
<section class='user-content user-badges-list'>
{{#each}}
{{user-badge badge=badge}}
{{user-badge badge=badge count=count}}
{{/each}}
</section>