There was a time, not long ago, when implementing a real-time chat system was beyond the coding skill of most web developers. That's no longer the case, thanks to the awesomeness of Node.js.
By now I suspect most people have at least heard of Node.js, if not looked at it and or played around with it a bit. For anyone not familiar, here's a quick description:
Node.js is a platform built on Chrome's JavaScript runtime for easily building fast, scalable network applications. Node.js uses an event-driven, non-blocking I/O model that makes it lightweight and efficient, perfect for data-intensive real-time applications that run across distributed devices.
So, what does that mean exactly?
In a nutshell, Node.js lets you quickly deploy a specialized event-driven server using a language most web developers are already famliar with; Javascript. For our purposes of building a realtime chat system, this is ideal, because all our chat server needs to do is listen for an incoming chat message, then blast that back out to all connected clients.
To make that io even easier on us, we're also going to leverage the Socket.io module for Node.js.
Socket.io is really cool. It does all of the heavy lifting for us of managing the io between our Node.js server and the client. It will automatically select the best available transport mechanism for each browser. That means modern browsers that support websockets will use that mechanism, whereas crappy old browsers will automatically fall back to a flash based or even long-polling solution. Since socket.io handles all of that for us, we can implement the chat server in one way and know that it will work in essentially every browser and any mobile device.
Gotta love that!
Let's get to it!
This blog post is not going to cover how to get Node.js installed, there is plenty of information on how to do that already on the net. Before you get started, you'll want to have Node.js installed. Also if you don't already, or didn't in the process of installing Node.js also install npm, you should do that, the process of which couldn't be any simpler. In the terminal on your *nix based server, type the following:
curl http://npmjs.org/install.sh | sudo sh
npm is kind of like the module downloading/uploading/etc functionality that drush provides us for Drupal, but for Node.js. So basically it gives us a simple way of installing a Node.js module.
Ok.. on to the Drupally goodness.
Step 1: Create the module directory & .info file
Within your Drupal 7 installation, create a directory for our module. For the purposes of this blog we'll call it example_chat. You can of course call it whatever you'd like, just be sure to change the function names and whatnot accordingly.
So, you should now have a example_chat directory in your /sites/all/modules directory of your Drupal 7 installation.
Within that directory, create a file example_chat.info with the following contents:
name = Example Chat
description = Provides a real-time (Node.js powered) chat client and server.
core = 7.x
version = 7.x-0.1
Step 2: Create the .module file
Next we're going to create the .module file.
Create a file in your example_chat directory called example_chat.module the first line should be an opening php tag.
All told our module file will implement three hooks; hook_permission, hook_menu, hook_theme.
First, lets implement the permission hook so that we have a way of controlling which user roles will have access to our chat system:
/**
* Implements hook_permission().
*/
functoin example_chat_permission() {
return array(
'access example chat' => array(
'title' => t('Access Example Chat'),
'description' => t('Allow access to the chat subsystem'),
),
);
}
Great, now we'll have a new permission in the permissions system called 'Access Example Chat' so we can control which user roles have access to chat on our site!
Next we'll implement the menu hook so that we have a path on the site that our chat system lives:
/**
* Implements hook_menu().
*/
function example_chat_menu() {
$items = array();
$items['chat'] = array(
'title' => 'Example Chat',
'access arguments' => array('access example chat'),
'page callback' => 'example_chat_page',
'type' => MENU_CALLBACK,
);
return $items;
}
Great, now we will be able to access the chat system by going to the /chat path on our site!
Ok, our final drupal hook is a theme hook:
/**
* Implements hook_theme().
*/
function example_chat_theme() {
return array(
'example_chat' => array(
'template' => 'example_chat',
'arguments' => array('username' => NULL),
),
);
}
Great, now we will be able to implement all of the necessary markup and whatnot in a template file.
Theme hooks can be a bit mysterious, so I'll break it down a little bit. Basically we're saying that when something is passed through theme('example_chat'), we want to pass that off to the template file, example_chat.tpl.php. And that we should pass in a variable called username that will be accessible within the template as $username. We'll see how that works in the implementation of our final function for the .module file.
The last function needed is the page callback that is called when we visit the /chat path on our site:
/**
* Page callback for /chat menu item.
*/
function example_chat_page() {
global $user;
return theme('example_chat', array('username' => $user->name));
}
Pretty simple function there too..
First we pull the $user object into scope, then pass $user->name to the example_chat.tpl.php template file as the $username variable, and "return" that.
In other words, render the template as the content area of the /chat path of the site.
Step 3: Create the template file
Ok, in order to be able to enable this module and access the /chat path without errors, we'll need the .tpl.php file to exist.
Create a file example_chat.tpl.php within our example_chat module directory, with the following contents:
<div id="chat">
<div id="messages">
<div id="nicknames">
</div>
<div id="lines">
</div>
</div>
<form id="send-message" autocomplete="off">
<input id="message" type="text" value="Type your chat messages here..." autocomplete="off" />
<button>Send</button>
</form>
</div>
This isn't going to look like much at the moment, nor will it DO anything impressive yet. We'll cover that in the next post... But, you can enable the module without issues at this point.