From 50ce93a8508765484ea973ba3b5c3948bb9cbd52 Mon Sep 17 00:00:00 2001 From: Vikhyat Korrapati Date: Wed, 19 Mar 2014 14:46:16 +0530 Subject: [PATCH 1/4] Use render_serialized for rendering instances in UserBadgeController. --- app/controllers/user_badges_controller.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/user_badges_controller.rb b/app/controllers/user_badges_controller.rb index 3044d7c1656..2dc333536cd 100644 --- a/app/controllers/user_badges_controller.rb +++ b/app/controllers/user_badges_controller.rb @@ -2,7 +2,7 @@ class UserBadgesController < ApplicationController def index params.require(:username) user = fetch_user_from_params - render json: user.user_badges + render_serialized(user.user_badges, UserBadgeSerializer, root: "user_badges") end def create @@ -17,7 +17,7 @@ class UserBadgesController < ApplicationController badge = fetch_badge_from_params user_badge = BadgeGranter.grant(badge, user, granted_by: current_user) - render json: user_badge + render_serialized(user_badge, UserBadgeSerializer, root: "user_badge") end def destroy From 8163fcade744b68703856643d196bab519dceedf Mon Sep 17 00:00:00 2001 From: Vikhyat Korrapati Date: Wed, 19 Mar 2014 15:34:43 +0530 Subject: [PATCH 2/4] Combobox view: escape name string. --- app/assets/javascripts/discourse/views/combobox_view.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/javascripts/discourse/views/combobox_view.js b/app/assets/javascripts/discourse/views/combobox_view.js index 8469c0f707d..25472a4b3ff 100644 --- a/app/assets/javascripts/discourse/views/combobox_view.js +++ b/app/assets/javascripts/discourse/views/combobox_view.js @@ -42,7 +42,7 @@ Discourse.ComboboxView = Discourse.View.extend({ if (val) { val = val.toString(); } var selectedText = (val === selected) ? "selected" : ""; - buffer.push(""); + buffer.push(""); }); } }, From 0f9ea25010b0a4b1c4fc60ea3414c62e27ef35e2 Mon Sep 17 00:00:00 2001 From: Vikhyat Korrapati Date: Wed, 19 Mar 2014 19:57:21 +0530 Subject: [PATCH 3/4] Interface for granting/revoking badges from admin user page. --- .../admin_user_badges_controller.js | 52 ++++++++++++++++ .../javascripts/admin/routes/admin_routes.js | 1 + .../admin/routes/admin_user_badges_route.js | 31 ++++++++++ .../admin/templates/user_badges.js.handlebars | 61 +++++++++++++++++++ .../admin/templates/user_index.js.handlebars | 18 ++++-- .../discourse/models/user_badge.js | 46 +++++++++++++- app/controllers/admin/users_controller.rb | 3 + config/locales/client.en.yml | 10 +++ config/routes.rb | 1 + test/javascripts/models/user_badge_test.js | 34 +++++++++-- 10 files changed, 244 insertions(+), 13 deletions(-) create mode 100644 app/assets/javascripts/admin/controllers/admin_user_badges_controller.js create mode 100644 app/assets/javascripts/admin/routes/admin_user_badges_route.js create mode 100644 app/assets/javascripts/admin/templates/user_badges.js.handlebars diff --git a/app/assets/javascripts/admin/controllers/admin_user_badges_controller.js b/app/assets/javascripts/admin/controllers/admin_user_badges_controller.js new file mode 100644 index 00000000000..a534195a129 --- /dev/null +++ b/app/assets/javascripts/admin/controllers/admin_user_badges_controller.js @@ -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); + }); + } + }); + } + + } +}); diff --git a/app/assets/javascripts/admin/routes/admin_routes.js b/app/assets/javascripts/admin/routes/admin_routes.js index 21b2b38b6a6..1143df781ce 100644 --- a/app/assets/javascripts/admin/routes/admin_routes.js +++ b/app/assets/javascripts/admin/routes/admin_routes.js @@ -47,6 +47,7 @@ Discourse.Route.buildRoutes(function() { this.resource('adminUsers', { path: '/users' }, function() { this.resource('adminUser', { path: '/:username' }, function() { + this.route('badges'); this.route('leaderRequirements', { path: '/leader_requirements' }); }); this.resource('adminUsersList', { path: '/list' }, function() { diff --git a/app/assets/javascripts/admin/routes/admin_user_badges_route.js b/app/assets/javascripts/admin/routes/admin_user_badges_route.js new file mode 100644 index 00000000000..b1184b3b12e --- /dev/null +++ b/app/assets/javascripts/admin/routes/admin_user_badges_route.js @@ -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); + } +}); diff --git a/app/assets/javascripts/admin/templates/user_badges.js.handlebars b/app/assets/javascripts/admin/templates/user_badges.js.handlebars new file mode 100644 index 00000000000..aec5a82889f --- /dev/null +++ b/app/assets/javascripts/admin/templates/user_badges.js.handlebars @@ -0,0 +1,61 @@ +
+
+ +
+
+ +{{#if loading}} +
{{i18n loading}}
+{{else}} +
+

{{i18n admin.badges.grant_badge}}

+ {{#if noBadges}} +

{{i18n admin.badges.no_badges}}

+ {{else}} +
+ {{combobox valueAttribute="id" value=controller.selectedBadgeId content=controller.badges}} + + {{/if}} + +
+
+ +

{{i18n admin.badges.granted_badges}}

+
+ + + + + + + + + + + {{#each}} + + + + + + + + {{else}} + + + + {{/each}} +
{{i18n admin.badges.name}}{{i18n admin.badges.badge_type}}{{i18n admin.badges.granted_by}}{{i18n admin.badges.granted_at}}
{{badge.displayName}}{{badge.badge_type.name}} + {{#link-to 'adminUser' badge.granted_by}} + {{avatar granted_by imageSize="tiny"}} + {{granted_by.username}} + {{/link-to}} + {{unboundAgeWithTooltip granted_at}} + +
+

{{i18n admin.badges.no_user_badges name=user.username}}

+
+
+{{/if}} diff --git a/app/assets/javascripts/admin/templates/user_index.js.handlebars b/app/assets/javascripts/admin/templates/user_index.js.handlebars index 8af5cd2c5f3..53c731de8ef 100644 --- a/app/assets/javascripts/admin/templates/user_index.js.handlebars +++ b/app/assets/javascripts/admin/templates/user_index.js.handlebars @@ -77,6 +77,18 @@ + {{#if showBadges}} +
+
{{i18n admin.badges.title}}
+
+ TODO featured badges +
+
+ {{#link-to 'adminUser.badges' this class="btn"}}{{i18n admin.badges.edit_badges}}{{/link-to}} +
+
+ {{/if}} + @@ -336,12 +348,6 @@ -{{#if showBadges}} -
-

{{i18n admin.badges.title}}

-
-{{/if}} -