"Recently joined" widget for WordPress (Illustrated Tutorial)

In this tutorial, we are going to display recently joined Discourse members in a WordPress widget.

What will you need

  • Admin rights in your Discourse (or ask an admin for an API key)
  • Twig Anything WordPress plugin
  • 10-15 minutes of time

Table of contents

  1. Locate the information you need in Discourse
  2. Find the right Discourse API to use
    • A trick to make it really easy
  3. Learn the data returned by API
  4. Create a Twig Template in WordPress
    • More about Data URL
  5. Enter HTML template
    • Make usernames clickable
    • Display avatars
    • Display time since joined
    • Filter out blocked and suspended users
    • Limit to only show 5 users
    • Save & Publish your template
  6. Embed in a WordPress widget

Step 1. Locate the information you need in Discourse

For some reason, the only place you can get the list of your forum members ordered by creation time is the administration panel.

Open your Admin panel in Discourse, click Users, then click New. You will see the list of your forum members recently joined, the most recent ones coming first:


Step 2. Find the right Discourse API to use

Now that we can literally see the information that we want to retrieve from Discourse, we have to identify which API we want to call to get the information.

Open Developer Tools in Google Chrome:

Select the Chrome menu at the top-right of your browser window, then select Tools > Developer Tools.

Click on the Network tab and then on the XHR section:


A trick to make it really easy

Now, here is a trick. Click on the Active tab and then immediately on the New tab and notice how new requests are sent to the server - active.json and new.json respectively:

It is intuitively understood that new.json is what we need, so click on it and copy the Request URL, but without the ?_=143… part:

http://forum.kozovod.com/admin/users/list/new.json


Step 3. Learn the data returned by API

Click on the response tab and copy all text:

Open a json viewer tool
and paste the response text into the left panel,
then click on the arrow to beautify it:

Now you can explore your API data in a nice way and figure out which information you want for your widget in WordPress.


Step 4. Create a Twig Template in WordPress

Click on the Add New under the Twig Templates menu in your WordPress administration panel:

Give your template a title and configure as on the following screenshot:

  • Source Type = URL - this means we want to fetch data from a URL
  • Data Format = JSON - this means the data fetched is in JSON format
  • Cache lifetime in seconds - keep empty for now, will explain later
  • Data errors handling - leave as is, more explanations below
  • Data URL - this is where we have to put our URL from step 2.

Data URL

Because the information we need from Discourse is found in the discourse admin panel, we can only access it by appending an API key to our URL. So, add this to the end of your URL:

?api_username=XXX&api_key=YYY

Replace XXX with a discourse username and YYY with the corresponding API key.


Step 5. Enter HTML template

We can start making up the output that we want in WordPress.
We will need a bit of HTML and Twig syntax, but both are pretty easy, just keep reading.

Back to our json editor from Step 3, let’s see what data we will need:

First of all, notice that the topmost element is an array. This means we can loop over it in our HTML template.

The data retrieved via API is passed to your template as data variable.
To loop over it, use the Twig’s for syntax:

{% for user in data %}
  {{ user.username }} <br/>
{% endfor %}

Enter the code above into your Twig Template and click on the Preview button:

The preview screen opens and here we go!


Make usernames clickable

Public user profiles in Discourse can be accessed via a link like this:

http://your-discourse-website.com/users/XXX (replace XXX with a lowercase username)

To make the usernames in our widget clickable, let’s use the <a> HTML tag:

{% for user in data %}
  <a href="http://forum.kozovod.com/users/{{user.username_lower}}">
        {{user.username}}
  </a>
  <br/>
{% endfor %}

Notice how we use another field from the API - username_lower.

Clicking on the Preview button shows our new template code in action:


Display avatars

In the JSON-editor, you will see there is a field named avatar_template:

If you append this avatar template to your Discourse URL and replace {size} with a number indicating the size of the avatar square, you will get the actual avatar image. For example:

http://forum.kozovod.com/letter_avatar/olga_kashubina/70/5_fcf819f9b3791cb8c87edf29c8984f83.png

Or a smaller version:

http://forum.kozovod.com/letter_avatar/olga_kashubina/26/5_fcf819f9b3791cb8c87edf29c8984f83.png

(the links above can break with time)

We can use this knowledge to show user avatars in our “recently joined” list:

{% for user in data %}
  <img src="http://forum.kozovod.com{{ user.avatar_template|replace({'{size}':'26'}) }}" />
  <a href="http://forum.kozovod.com/users/{{user.username_lower}}">
        {{user.username}}
  </a>
  <br/>
{% endfor %}

Here we’re using the Twig’s replace function.
Now if you click the Preview button, you will see something like this:


Display time since joined

To display the age since a user registered, use the created_at_age field:

{% for user in data %}
  <img src="http://forum.kozovod.com{{ user.avatar_template|replace({'{size}':'26'}) }}" />
  <a href="http://forum.kozovod.com/users/{{user.username_lower}}">
        {{user.username}}
  </a>
  <small style="color: gray">{{ user.created_at_age }}</small>
  <br/>
{% endfor %}

And voila!


Filter out blocked and suspended users

It would be logical not to show any blocked and/or suspended users in our list.
For this, we would use these two fields from our API result:

Adding an if in the loop would do the job:

{% for user in data if (not user.suspended and not user.blocked) %}
...
{% endif %}

Limit to only show 5 users

This one may seem a bit more complicated, but let me break it down so that it is easy to understand.

First, we will need a counter of how many posts have been displayed already, so add this in the very beginning of your HTML template:

{% set num = 1 %}

Next, we want to increment the counter every time a username is rendered, so just before the end of the loop, add this:

  ...
  {% set num = num + 1 %}
{% endfor %}

Finally, wrap your output inside the loop in {% if %}:

{% if num <= 5 %}
  ...
{% endif %}

Now altogether:

{% set num = 1 %}
{% for user in data if (not user.suspended and not user.blocked) %}
  {% if num <= 5 %}
    <img src="http://forum.kozovod.com{{ user.avatar_template|replace({'{size}':'26'}) }}" />
    <a href="http://forum.kozovod.com/users/{{user.username_lower}}">
          {{user.username}}
    </a>
    <small style="color: gray">{{ user.created_at_age }}</small>
    <br/>
  {% endif %}
  {% set num = num + 1 %}
{% endfor %}

Now only 5 most recent new forum members are shown in the preview:


Save & Publish your template

Once you are satisfied with the template output, don’t forget to publish it by clicking the Publish button. Only published templates can be embedded in WordPress. This follows the same logic as with posts and pages: if a post is not published, it is not seen by the public.


Step 6. Embed in a WordPress widget

It’s show time!

Copy the slug of your template, which can be found just under the template title:

In your Widgets administration panel, drag the Twig Template widget to a widget area (you might have more than one widget areas available):

Give your widget a title and enter the slug we just copied:

Click Save. Done! Now check your home page’s sidebar and smile!

What next?


  • Grab Twig Anything and install it to your WordPress

  • Read the Twig Anything announcement topic

  • Read about what happens if WordPress can’t fetch data from Discourse at some point in time

  • If you are using Visual Composer, see the demo

  • Read the Twig documentation

  • Ask me any questions in this thread: another tutorial? template snippets? more features?

  • If you are using Twig Anything already, show us how!

8 Likes

Would it be possible (I’m guessing yes) to display information in Wordpress relating to the currently logged in user? (# of posts, profile information, etc)

I use wp-discourse and Wordpress as SSO to Discourse, so username and email of logged in Wordpress user would be same as the Discourse user.

Thanks! Looking forward to checking this out - I see a lot of potential uses for it to integrate my Discourse throughout my members area in Wordpress.

For this example it doesn’t matter, but I just want to mention that with a more custom implementation (sans Twig Anything) it’s possible to vary the api_username value based on the current user. This can be helpful when displaying topic listings like /latest.json or /new.json since they will then be tailored to that user.

You’d build URLs like https://meta.discourse.org/users/jesselperry.json to consume and vary the username.

1 Like

Great, let us know how it goes once you try it!

Yes - just insert this macro in your URL:

https://meta.discourse.org/users/{{ wp_get_current_user().user_login }}.json

Because, as you said, username and email of logged in Wordpress user would be same as the Discourse user, you can use wordpress’s username in your url.

wp_get_current_user() is a WordPress function that retrieves a user object. From there, a few fields are available, including user_login, which is a username.

You can use this syntax anywhere - in your Twig Templates, in MySQL queries and in URLs.

Note about caching. Caching results of the API requests will work as expected - first, a URL will be compiled. In other words, it will be converted to a final value, like this:

https://meta.discourse.org/users/jesselperry.json

Only then will the API results be cached for this exact URL. So, if you enter cache lifetime in seconds in your data source configuration, there will be a cached version stored for every username separately - it will never confuse.

That’s an interesting idea!

It is currently possible to use Twig syntax right in the URL field, so it is actually possible to varythe URL as per your proposition.

However, if I’m not mistaken, to access private user data, not only api_username is needed, but also the corresponding api_key. From what I know there is no per-user API key in Discourse, unless it has been introduced just recently?

If an api key had been stored per user basis in user’s metadata named “discourse_api_key” in WordPress , we would access it like this:

{{ get_current_user_meta('discourse_api_key', 1) }}

here, 1 means retrieve single user meta field, just as described in WordPress documentation for the get_user_meta function.

We would then use this syntax right in the URL:

...?api_key={{ get_current_user_meta('discourse_api_key', 1) }}

Hope this answers all of your questions in detail.

1 Like

The “All Users” API key works for this.

Cool to know Twig Anything can support syntax in the URL field. I was unaware of that!

1 Like

I probably misunderstand API keys in Discourse then.
Is there a non-all-users api key in Discourse?

If you go to a user’s admin page you can generate an API key for them specifically:

So, if I use “All Users” API (which is actually “Admin API” with the highest permissions level), won’t I end up seeing a particular user’s profile from the admin’s point of view instead of user’s point of view as you proposed?

Re per-user API key - not sure how it can be useful as admins won’t usually generate an API key for everyone manually and save it into their WordPress profile. Not sure why it is not implemented so that every single user has its own API key by default, though.

Your wording here is confusing so I might be answering incorrectly, but it depends on whatever you set as the api_username. If you use system, whatever system has permission to view will be displayed.

I’m not 100% about this, but I think the user-specific API keys are meant for giving to developers to build apps around your forum and then having the power to revoke the keys if necessary.

So I guess if by “non-all-users API key” you mean an API key that works only in conjunction with a specific api_username, I don’t think that exists. (@riking or anyone else knowledgeable, feel free to correct me.)

1 Like

Nope, @meglio has the right idea here. Actually, for the per-user API keys, the api_username value is no longer needed (as it can be inferred from the key…).

User-accessible API keys are a wishlist item.

3 Likes