1 :: Heed MENU_CALLBACK!
One of the frequently overlooked capabilities of Drupal is the so-called MENU_CALLBACK. Strangely, this straightforward menu-item type is among the unpopular ones. MENU_CALLBACK is simple. It only indicates that your defined path will be wired to some callback function in your module. Let’s see how this works:
if (!$may_cache) {
$items[] = array( 'path' => 'json/dispatcher',
'title' => t('Dispatcher of Ajax Requests'),
'callback' => 'mymodule_dispatcher',
'access' => true,
'type' => MENU_CALLBACK
);
}
return $items;
}
The above snippet maps the path website.com/json/dispatcher to the function mymodule_dispatcher(). Everything that comes after the path will be passed into the function as parameters. For example if we used this URL: website.com/json/dispatcher/hello/world, and defined the function like this: function mymodule_dispatcher($var1, $var2) – then in the function $var1 would equal ‘hello’, and $var2 → ‘world’ respectively. This callback functionality is exactly what we need to create an AJAX+JSON event dispatcher.
2 :: Possibilities with callback function.
The trick to writing callback functions ( mymodule_dispatcher() in our case ) is in knowing the Drupal’s framework. Commonly, Drupal is considered to be a CMS (Content Management System). Only when looking deeper, one would uncover the well-designed, powerful framework which makes all the surface functionality possible. If Drupal was a bicycle factory, and you were an engineer working on imroving the bicycles – you would be allowed to redirect the product into your own conveyer belt at any points during production. You could let the factory build the bicycle frame, then redirect it to your own conveyer belt to make your additions/alterations, after which you could let it back into regular production.
Alright, I’m getting a little ahead of myself there. In our module we will talk back to the client using drupal_to_js() function. This function accepts either an associative array or an object, and returns well-cooked JSON.
$object->var1 = 'hello';
$object->var2 = 'world';
$object->var3 = array( 1 => 'fluffy', 2 => 'horse' );
echo drupal_to_js($object);
This snippet would output valid JSON, which is easily parsable by javascript.
3 :: Designing our dispatcher.
Dispatcher is there to dispatch. Let’s follow the KISS principle and use PHP’s switch() control statement.
$out = new stdClass();
switch ($action) {
case 'register':
// handle registration, write output to $out
break;
case 'login':
// handle login, write output to $out
break;
default:
// handle nothing
break;
}
header("Content-type: application/json");
echo drupal_to_js($out);
}
With this simple dispatcher we can accept any URL such as website.com/json/dispatcher/[anything], and most importantly, nobody stops us from POSTing values into that URL. This means we can use Drupal’s framework, and talk directly to client (bypassing theming). Javascript developer would only have to post values to your dispatcher, and work with returned JSON. Lets expand the function a little bit, using the login/register example.
$out = new stdClass();
switch ($action) {
case 'register':
$out->messages = '';
// assuming _nick_available() function is written elsewhere in the module
if ( !_nick_available($_POST['name']) ) {
$out->messages = array('name' => 'This username is already taken.');
break;
}
// this will process our POSTed values as if they were from Drupal's register form
drupal_execute('user_register', $_POST);
if ( $errors = form_get_errors() ) {
$out->messages = $errors;
}
break;
case 'login':
global $user;
$out->messages = '';
if ( !($user->uid > 0) )
$user = user_authenticate( $_POST['user'], $_POST['pass'] );
if ( !($user->uid > 0) )
$out->messages = 'authentication failed';
break;
case 'logout':
user_logout();
$out->messages = 'ok';
break;
default:
// handle nothing
break;
}
drupal_get_messages(null, true); // this will clear drupal's message buffer
header("Content-type: application/json");
echo drupal_to_js($out);
}
As you can see – in the above example I used such Drupal framework’s functions as drupal_execute(), form_get_errors(), user_authenticate(), and drupal_get_messages(). It shows once again that the hardest part about writing callbacks is in knowing which Drupal’s functions to use to achieve the desired effect (without reinventing the bicycle).
4 :: Client Side
The only thing left here is for js developer to create forms with the expected fields. They would then ajax-submit them to our json/dispatcher/register, json/dispatcher/login, or json/dispatcher/logout, and receive the JSON response back from server, which they would then parse and use for any presentational purposes.
Hopefully this article clarified some of the practices we used in Scripteka. More stuff is coming so stay tuned!
[...] here to [...]
[...] here for more Der Beitrag wurde am Tuesday, den 13. November 2007 um 01:14 Uhr veröffentlicht [...]
I love what you have here. How would I use it to implement an AJAX login form, specifically in something like a Thickbox modal popup? I’d like the popup to remain until login is successful. The Thickbox one targets /user/login so it just shows the regular login page if authentication fails. I’d like it all to remain in Javascript.
Thanks, -Patrick