How to get badges for a user?

javascript
badge
plugins

(CJ) #1

For a plugin, I want to get the collection of badges for a given user in javscript. Using UserBadges doesn’t seem to give me the wrong ID numbers for the user_badges. The code below:

var currentUser = Discourse.User.current();
var badges = Discourse.UserBadge.findByUsername(currentUser.username);
console.log(JSON.stringify(badges, null, 4));

Returns this:

"{
    "_id": 83,
    "_subscribers": []
}" 

When I’m expecting something like this, which is stored in the DB.

discourse_development=# select id, user_id, badge_id from user_badges where user_id=28;
 41 |      28 |      100

which should give me JSON like

"{
    "_id": 41,
    "_user_id": 28,
    "_badge_id": 100
}" 

How did I get a list of badges for a user? I’m not sure where I’m going wrong here using UserBadge. The end goal is to be able to loop through the badges. e.g. list all the badge names

for (var badge in badges){
    console.log(badge.name);
}

How to pass currentUser info into iframe with javascript
(Jens Maier) #2

The result of Discourse.UserBadge.findByUsername(currentUser.username); is an async promise, you need to wait for the server’s reply:

Discourse.UserBadge.findByUsername(Discourse.User.current().username).then(function(result) {
    console.log(JSON.stringify(result, null, 2));
});

(Kane York) #3

Note that .then() returns another promise, so you can keep chaining. Or you can return a new promise from the then function, which again works as expected.


(CJ) #4

Right. Thank you. Still getting the hang of Javascript + Ember + Discourse.

    Discourse.UserBadge.findByUsername(Discourse.User.current().username).then(function(result) {
      result.forEach(function(entry) {
          console.log(entry.badge.name);
      }
   );
});

(CJ) #5

How can I preload the badges before view loads?

With the promise being asynchronous it loads too late and after everything else is initialized. I understand that you cannot wait for a promise to load, but is a way to get the same data for the badges using PreloadStore.get('badges') or a different method that will complete in init method of the plugin?

Edit:

I’m thinking I need to extend a controller or some other functionality to get the badges prior to the plugin loading so it could be retrieved like trust_level although I’m not sure where I would extend/modify discourse.

var currentUser = Discourse.User.current();
currentUser.get('trust_level')

Feature proposal: improved badge granting workflow
(Kane York) #6

In that case, it needs to be part of the model, which means you need to modify the route…

Something like:

reopen({
  model: function() {
    return this._super().then(function(model) {
      return Ember.RSVP.Promise(function(resolve, reject) {
        // happy fun times
        // you want to return a promise so that you don't block the runloop
        // but you also want to modify the returned model

(CJ) #7

Thanks. Maybe I’m not understanding what/when is returning the promise to make it a property. Please forgive my Ember + Discourse ignorance.

import UserRoute from 'discourse/routes/user';
import UserModel from 'discourse/model/user';
export default {
  name: "my_plugin",
  initialize: function(container, application) {
    UserRoute.reopen({
      model: function() {
        return this._super().then(function(model) {
          return Ember.RSVP.Promise(function(resolve, reject) {
                var user = this.modelFor('user');
                return user.userBadges();
          });
       });
     }
    });

    UserModel.reopen({
      userBadges: function() {
        return Discourse.UserBadge.findByUsername(Discourse.User.current().username);
      }.property('user_badges'),,
    });
  
    var currentUser = Discourse.User.current();
    console.log( currentUser.get('user_badges'));

This isn’t yielding the desired result. I think I’ve put the wrong items in the model and route. Should they be other way around?

e.g.

UserRoute.reopen({
  model: function() {
    return this._super().then(function(model) {
      return Ember.RSVP.Promise(function(resolve, reject) {
           resolve Discourse.UserBadge.findByUsername(Discourse.User.current().username);
      });
   });
 }
});

(Jens Maier) #8

Hmm, isn’t the user model simply called, well… User? At least I can find no reference to a UserModel anywhere in the codebase or the debugger.

This seems to work:

Discourse.User.reopen({
  getUserBadges: function() {
    return Discourse.UserBadge.findByUsername(this.username);
  }
});

Discourse.User.findByUsername("elberet").then(function(result) {
  return result.getUserBadges();
}).then(function(result) {
  console.log(result);
});

If you reopen the User class in an initializer, Discourse.User.current().getUserBadges() should work fine, too, but since I tested this in the console, I had to force construction of a new User instance.


(CJ) #9

@elberet thanks, this works well although I need to put it in an Promise.RSVP as @riking suggested because the problem is that the promise returns after the handlebars template loads.

I’m trying to expand the original discourse-adsense plugin to use badges and trust-level attributes of the user.

Without the RSVP the handlebars template loads and create the adsense block.

My next thought was to change how Handlebars registers the helper Handlebars.regsiterHelper and see if that could be changed to load at a different time. Here is my code thus far:

export default {
  name: "adsense_plugin",
  initialize: function(container, application) {

    Discourse.SiteSettings.ShowAds = true;
    var currentUser = Discourse.User.current();

    Discourse.User.reopen({
      getUserBadges: function() {
        return Discourse.UserBadge.findByUsername(this.username);
      }
    });

    if (currentUser ) {
     Discourse.User.findByUsername(Discourse.User.current().username).then(function(result) {
          return result.getUserBadges();
        }).then(function(result) {
            result.forEach(function(entry) {
              if (entry.badge.name == Discourse.SiteSettings.adsense_through_badge){
                    Discourse.SiteSettings.ShowAds = false;
              }
            });
        });
    }
  }
};

(function() {

  Handlebars.registerHelper('adsenseBlock', function(width, height, slotid) {
    var currentUser = Discourse.User.current();
    if ((currentUser) && ( currentUser.get('trust_level') > Discourse.SiteSettings.adsense_through_trust_level )) {
        return "";
    }

    if ((currentUser) && !Discourse.SiteSettings.ShowAds){
       return "";
    }

    var position = slotid.replace('_mobile', '');
    if (Discourse.SiteSettings['adsense_show_' + position]) {

      return new Handlebars.SafeString('<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>' +
        '<div class="adsense adsense_' + slotid.trim() + '">' +
        '<ins class="adsbygoogle" style="display:inline-block;width:' +
        width + 'px;height:'+ height + 'px" data-ad-client="' + Discourse.SiteSettings.adsense_publisher_code.trim() +
        '" data-ad-slot="' + Discourse.SiteSettings['adsense_ad_slot_' + slotid.trim()] + '"></ins>' +
        '</div>' +
        '<script> (adsbygoogle = window.adsbygoogle || []).push({}); </script>'
      );
    }
    return "";
  });

})();

(function() {

  function __push() {
    var i = $('.adsense').size();
    var j = $('.adsense .adsbygoogle ins ins').size();

    $('ins.adsbygoogle').each(function(){
      if ($(this).html() == '') {
        adsbygoogle.push({});
      }
    });
    if(i>j) {
      window.setTimeout(__push, 300);
    }
  }

  function __reload_gads () {
    var ads = document.getElementById("adsense_loader");
    if (ads) {
      // clear the old element and its state
      //ads.remove();
      ads.parentNode.removeChild(ads);
      for (key in window) {
        if (key.indexOf("google") !== -1){
          window[key] = undefined;
        }
      }
    }
    window.adsbygoogle = [];
    var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true; ga.id="adsense_loader";
    ga.src = '//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js';
    var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
    window.setTimeout(__push, 200);
  }

  Discourse.PageTracker.current().on('change', function(url) {

    if('' != Discourse.SiteSettings.adsense_publisher_code ) {

      __reload_gads();
    }
  });
})();