# "Who's online" widget in Discourse - my version

**URL:** <https://meta.discourse.org/t/whos-online-widget-in-discourse-my-version/31722>\
**Category:** Marketplace\
**Tags:** wordpress\
**Created:** [2015 年8 月 4 日 02:25 UTC](https://meta.discourse.org/t/whos-online-widget-in-discourse-my-version/31722 "2015-08-04T02:25:06Z")\
**Posts on this page:** 1\
**Showing post:** 1

<div class="post-metadata">

作者： ![meglio](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/meglio/32/71444_2.png) [@meglio](https://meta.discourse.org/u/meglio)\
发布日期： [2015 年8 月 4 日 02:25 UTC](https://meta.discourse.org/t/whos-online-widget-in-discourse-my-version/31722/1 "2015-08-04T02:25:07Z")

</div>

![](https://global.discourse-cdn.com/meta/original/3X/b/7/b7b45ff66b85069d962df58936b38a0fc714b536.png) 

* * *

Live demo: [http://forum.kozovod.com/](http://forum.kozovod.com/)

# Disclosure

This is my take on showing a simple “who’s online” list in my Discourse setup.

**NB.** This solution does not follow best practices to add functionality to Discourse!  
However, I wanted it a lot and had very little time, so I used the tools I knew and had at my hands.

### Drawbacks of this solution

- It uses React.js instead of Ember. Simply because I don’t know Ember, don’t like it and failed learning it in a short period of time (it was the opposite with React).
- It uses WordPress with Twig Anything plugin - the only reason being that presence information is not available
- It does not use Discourse message bus. It makes an HTTP request every X seconds or minutes as by your configuration. If you have plenty plenty of users online, this might be bad for your service traffic channel.

### Good side

- It works!
- It is easily customizable!
- It is what my Discourse community needs - here and now!
- It works fine on mobiles as well - tested.

* * *

# Step 1. Customization section in Discourse

Create a new customization and name it e.g. “React.js”:

 ![](https://global.discourse-cdn.com/meta/original/3X/4/4/44b04f1ca2f31ea10662a798439c7660c862b7be.png) 

### `</head>` section:

```html
<script src="https://fb.me/react-with-addons-0.13.3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.6.15/browser.min.js"></script>

```

First line loads react, second line loads in-browser compiler for React components. Both are CDN-hosted.

### CSS section

For now, use this. Feel free to customize when you got it working:

```css
.who-is-online-user {
    display: inline-block;
    padding: 0 3px;
}
.who-is-online-status {
    display: inline-block;
    margin-right: 2px;
    font-size: 28px;
    vertical-align: middle;
}
.who-is-online-status-online {
    color: green;
}
.who-is-online-status-idle {
    color: orange;
}
.who-is-online-prefix-text {
    color: white;
    background-color: gray;
    display: inline-block;
    margin-right: 4px;
    padding: 1px 4px;
}

```

Copy the same CSS code to the mobile CSS section.

### `</body>` section

```js
<section id="who-is-online-widget"></section>

<script type="text/babel">

var WhoIsOnlineWidget = React.createClass({
    getInitialState: function() {
        return {
            updating: false,
            data: []
        };
    },
    updateUsersList: function() {
        if (this.state.updating) {
            return;
        }
        var me = this;
        this.setState({updating: true}, function() {
            jQuery.ajax(this.props.apiUrl, {
                method: "POST"
            })
            .done(function(data) {
                // On success, update the list
                if (!data || !data.code || data.code !== 'OK' || !data.result) {
                    return;
                }
                me.setState({data: data.result});
            })
            .fail(function() {
                // On failure do nothing
            })
            .always(function(){
                // Always plan the next fetch
                me.setState({updating: false}, function() {
                    setTimeout(me.updateUsersList, me.props.updateSeconds);
                });
            });
        });
    },
    render: function() {
        var items = [];
        for (var i = 0; i < this.state.data.length; i++) {
            var user = this.state.data[i];
            items.push(
                <div className="who-is-online-user">
                    <span className={"who-is-online-status who-is-online-status-"+user.status}>{"\u2022"}</span>
                    <a className="userNickname"
                        href={user.user_absolute_url}>
                        {user.user_from_api.username}
                    </a>
                </div>
            );
        }
        var prefixText = null;
        if (this.props.prefixText.length > 0) {
            prefixText = (
                <span className="who-is-online-prefix-text">
                    {this.props.prefixText}
                </span>
            );
        }
        return (
            <div className="container">
                {prefixText}
                {items}
            </div>
        );
    }
});

React.render(
    <WhoIsOnlineWidget
        apiUrl="http://kozovod.com/api-endpoint/whos-online/"
        updateSeconds={300000}
        prefixText="Who's online"/>,
    document.getElementById('who-is-online-widget'),
    function(){
        this.updateUsersList();
    }
);

</script>

```

**apiUrl=“[http://kozovod.com/api-endpoint/whos-online/](http://kozovod.com/api-endpoint/whos-online/)”** - URL to the API that we will build in WordPress, details follow.

**updateSeconds={300000}** - how often to update the online status in user browser, in milliseconds. I set it to 5 minutes to avoid any trouble with my very little WordPress server instance.

**prefixText=“Who’s online”** - the text you would like to be shown in a gray box.

* * *

# Step 2. New API Endpoint in WordPress

Yes, it sounds really weird, but it works:

1. I’m using WordPress to fetch info from Discourse with using my admin api key
2. In WordPress, I’m using Twig Anything plugin and its API Endpoints add-on to build my own API in WordPress.
3. From user browser who’s exploring Discourse, I query my WordPress API to get the info I need.

Why I did so:

1. To keep Discourse admin API hidden from public. I could request the API directly from user browser, but it would reveal API key, which is no-no.
2. It saved me time to use the tools I already have in place, i.e. the Twig Anything plugin and its add-ons.

### Data Source configuration

Create a Twig Template in your WordPress and configure its data source section as on the following screenshot:

 ![](https://global.discourse-cdn.com/meta/original/3X/7/5/751007f58619081dfeb802337ee4cbb07fd9fb19.png) 

The URL is:

`http://your-discourse.com/admin/users/list/active.json?api_username=XXX&api_key=YYY`

XXX your Discourse admin username  
YYY your Discourse api key

* * *

### Twig Template

Here is the template code. Note that it outputs a JSON-array, which we are going to make a part of API responst with using the API Endpoints add-on:

```twig
[
{% set num = 1 %}
{% for user in data if (not user.suspended and not user.blocked and user.username != 'api_ru') %}
  {% if num <= 11 %}

    {% if not loop.first %},{% endif %}
    {

    {% if date(user.last_seen_at) > date('-30minutes') %}
      "status": "online",
    {% else %}
      "status": "idle",
    {% endif %}
    "avatar_absolute_url": "http://forum.kozovod.com{{ user.avatar_template|replace({'{size}':'22'}) }}",
    "user_absolute_url": "http://forum.kozovod.com/users/{{user.username_lower}}",
    "user_from_api": {{user|json}}

  }

  {% endif %}
  {% set num = num + 1 %}
{% endfor %}
]

```

A few notes:

**user.username != ‘api\_ru’** - I’m using a separate admin user api\_ru in Discourse for API calls. This check makes sure it won’t appear in the resulting list of online users.

**{% if num \<= 11 %}** - up to 11 users in the list

**`http://forum.kozovod.com`** - don’t forget to change to your own Discourse URL in all links

**{% if date(user.last\_seen\_at) \> date(‘-30minutes’) %}** - users seen in last 30 minutes will be shown with a green circle; the rest will have an orange circle. Adjust this timeframe to your needs.

* * *

# API Endpoint in WordPress

Create a new API Endpoint in **Settings** → **API Endpoints** in your WordPress admin panel:

 ![](https://global.discourse-cdn.com/meta/original/3X/c/8/c84b0d3cd12176829e30e30401a93f814a226ec3.png) 

A few notes:

**Success and Error templates** are there for you by default

In the **HTTP headers section** , don’t forget the following 2 headers:

`Content-Type: application/json; charset=utf-8`

- this one will indicate that the API output is JSON

`Access-Control-Allow-Origin: *`

- this one will allow for API to be requested from outside of your WordPress domain

* * *

### API Output

Your new API endpoint will return JSON of the following structure:

 ![](https://global.discourse-cdn.com/meta/original/3X/e/2/e21620b02bcadfac74165ed400e8b7b4474d5df6.png) 

* * *

Done!

---

_[View the full topic](https://meta.discourse.org/t/whos-online-widget-in-discourse-my-version/31722)._
