Live demo: 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”:
</head>
section:
<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:
.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
<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/” - 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:
- I’m using WordPress to fetch info from Discourse with using my admin api key
- In WordPress, I’m using Twig Anything plugin and its API Endpoints add-on to build my own API in WordPress.
- From user browser who’s exploring Discourse, I query my WordPress API to get the info I need.
Why I did so:
- 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.
- 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:
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:
[
{% 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:
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:
Done!