April 28, 2014

Using Drupal Content Channel Tokens with Nodejs

You've decided to add a bit of Node.js functionality to your Drupal site using the Drupal Nodejs Module. After some reading you realize you need to be setting up user message channels using hook_nodejs_user_channels or possibly nodejs_add_user_to_channel. This allows your application to send socket messages to groups of users. Great! But you're writing no simple application. You want to go one step further and manage these channels based not only on some property of the user object, but all users who may be currently viewing a particular Drupal served page. Suddenly the typical user channels no longer cut it. Try as you might, you'll find yourself quickly descending into channel management hell. There has got to be a better way. Thankfully content token channels are here to help.

Send a Message Based on the Content Being Viewed

You can think of content token channels as a per page (or piece of content) channel subscription. This differs from the user channels in that it targets users who are currently viewing a specific page rather than users targeted by some property of the user object (regardless of where they are on the site).

There are two main PHP functions to implement a token channel. nodejs_send_content_channel_token to generate a token channel for a given piece of content, and nodejs_send_content_channel_message to send a message to users subscribed to a given content channel.

To illustrate this lets assume we want to create a channel to send user messages when they are viewing a Drupal node of type page. To begin we will need to setup our content channel for that node type. A good place to do this would be hook_node_view which fires when a user views a node.

function example_node_view($node, $view_mode, $langcode) {
  if($node->type == "page") {

Now every time a node of type page is viewed the Node.js server is notified of the page_node_channel content channel. Node.js then adds the channel to the user for whom the page was rendered.

From here if we wish to send a message to all of the users viewing a node of type page we make a simple call to nodejs_send_content_channel_message. When and where you choose to fire this message is up to you. Possibly you want to notify users if the page they are viewing has been updated (hook_node_update), a user logs in (hook_user_login), or some new content was added (hook_node_insert).

// Build a message object
$message = new stdClass();
$message->channel = 'page_node_channel';
$message->data['body'] = 'Hello World!';

// Send the message to the channel we created

And that's about it! When nodejs_send_content_channel_message fires our message will be broadcast to all users subscribed to the page_node_channel. In this case all users who are currently viewing a node of type page. This message behaves like any other on the Node.js side, so you could send it to a custom callback handler, as mentioned in an earlier post.

Token Messages From an Node.js Extension

If you are writing a Node.js server extension and wish to send a message to a content channel, it is worth noting that there is no immediately exposed functionality to do this. However, Node.js server extensions are aware of what the content channels are via config.tokenChannels. This allows us to mimic the functionality of the Drupal Node.js server with a helper method in our server extension.

function sendMessageToTokenChannel(message, config) {
  if (!message.hasOwnProperty('channel')) {
    console.log('publishMessageToContentChannel: An invalid message object was provided.');
  if (!config.tokenChannels.hasOwnProperty(message.channel)) {
    console.log('publishMessageToContentChannel: The channel "' + message.channel + '" doesn\'t exist.');

  for (var socketId in config.tokenChannels[message.channel].sockets) {
    config.publishMessageToClient(socketId, message);

Adding this method to your server extension, will allow you to send messages to a content channel, by passing the message and config objects from within your exports.setup function.

Using content token channels has greatly simplified several of the Drupal/Node.js projects I have done. However, it wasn't immediately apparent that this functionality existed. So I hope this helps to further clarify the capabilities of this module.

Ryan Oles theoleschool.com