Tuesday, April 27, 2010

Enable Ajax Mode in Drupal

I have been trying to work with Ajax in Drupal and I must say it was a cumbersome experience. First of all, even though I could find some online example (source codes), most of them were sort of advanced intermediate level codes and they were really difficult to understand what was actually going on. Moreover, the example codes I based off of never really worked without having to tweak them a little. I found that there was lack of good example Ajax code for Drupal; hence, I decided to provide my solution (that worked) here.

First of all you will need to define what you want in your forms. Basically with Ajax, you would want something that dynamically changes the contents of your form without having to reload the entire page. In my example, I placed a checkbox which will, after it's been clicked, display additional fields titled 'question' and 'answer'.

Here is basically what you will need to add in your hook_form function:

function activitymanager_form(&$node, $form_state) {
function activitymanager_form(&$node, $form_state) {


$form['textfield'] = array(
'#type' => 'checkbox',
'#title' => t('Textfield'),
'#default_value' => $form_state['values']['textfield'],
'#ahah' => array(
'path' => 'activitymanager/autotextfields/callback',
'wrapper' => 'textfields',
'effect' => 'fade',
)
);

$form['textfields'] = array(
'#title' => t("Generated text fields for the verification types"),
'#prefix' => '
"textfields">',
'#suffix' => '
'
,
'#type' => 'fieldset',
);

if ($form_state['values']['textfield']) {
$form['textfields']['question'] = array(
'#type' => 'textfield',
'#title' => t('Activity Question'),
);
$form['textfields']['answer'] = array(
'#type' => 'textfield',
'#title' => t('Activity Answer'),
);
}
return $form;

}

The Textfield form displays the checkbox itself. The '#ahah' field defines the Ajax behavior. '#path' directory should map to your callback function, which will be called everytime a user clicks on the checkbox. In my case, the callback function is called activitymanager_autoindex_callback. Therefore, the path to the function should be 'activitymanager/autoindex/callback. You must also set the path to your callback function in your hook_menu function as follows:

define(TEST_PATH,'activitymanager/autotextfields');


function activitymanager_menu() {

$items = array();

// Setup the initial menu option.
// yoursite/?q=test
// if you use clear URLs
// yoursite/test
$items['test'] = array(
'title' => 'Test setup',
'description' => 'A simple test form.',
'page callback' => 'drupal_get_form',
'page arguments' => array ('activitymanager_form'),
'access arguments' => array('access content'),
'type' => MENU_NORMAL_ITEM
);

/**
* This is the path in your site to the ajax/ahah callback function.
*/
$items[TEST_PATH] = array(
'title' => 'Test setup',
'description' => 'A simple test AHAH callback.',
'page callback' => 'drupal_get_form',
'page arguments' => array (
// first argument is the callback function.
'activitymanager_autotextfields_callback'),
//'test_ahah_cb'),
'access arguments' => array('access content'),
'type' => MENU_NORMAL_ITEM
);

return $items;
}
The above code is necessary to tell Drupal how it can find the callback function. It is quite cumbersome to add a new entry here everytime you create a new callback function, but it is a necessary step.

Finally here is my callback function:

function activitymanager_autotextfields_callback() {

// Get form from cache.
$form_state = array('storage' => NULL, 'submitted' => NULL);
$form_build_id = $_POST['form_build_id'];
$form = form_get_cache($form_build_id, $form_state);

$args = $form['#parameters'];
$form_id = array_shift($args);
$form_state['post'] = $form['#post'] = $_POST;
$form['#programmed'] = $form['#redirect'] = FALSE;

drupal_process_form($form_id, $form, $form_state);
$form = drupal_rebuild_form($form_id, $form_state, $args, $form_build_id);

$textfields = $form['textfields'];
$output = drupal_render($textfields);

// Final rendering callback.
print drupal_json(array('status' => TRUE, 'data' => $output));
exit();
}
there were some things in this code that I had to tweak to get it to work properly. When I first ran this code, Drupal would for some reason submit the form whenever I clicked on the checkbox without notifying me of it. So if I clicked on the checkbox 5 times, I would end up with 5 duplicate entries in the database. This is obviously not what I wanted, so I began researching what was actually happening. I found out soon that the drupal_process_form function inside my callback function is causing the above behavior. I am yet to understand why Drupal decides to submit the form here, luckily though I figured out a way to work around this issue. I found that the some of the values in $form_state array can be used to change the behavior of drupal_process_form function. I inserted the following code just above my call to drupal_process_form fucntion:
$form_state['rebuild'] = 'notempty';
This eliminated the submit problem I was having. My callback function now looks like this:

function activitymanager_autotextfields_callback() {

// Get form from cache.
$form_state = array('storage' => NULL, 'submitted' => NULL);
$form_build_id = $_POST['form_build_id'];
$form = form_get_cache($form_build_id, $form_state);

$args = $form['#parameters'];
$form_id = array_shift($args);
$form_state['post'] = $form['#post'] = $_POST;
$form['#programmed'] = $form['#redirect'] = FALSE;
$form_state['rebuild'] = 'notempty';

drupal_process_form($form_id, $form, $form_state);
$form = drupal_rebuild_form($form_id, $form_state, $args, $form_build_id);

$textfields = $form['textfields'];
$output = drupal_render($textfields);

// Final rendering callback.
print drupal_json(array('status' => TRUE, 'data' => $output));
exit();
}
I hope that somebody in the future finds this posting helpful, as adding an Ajax function in Drupal was not an easy task (at least for me).

Tuesday, April 20, 2010

Problem with Drupal Coding Standards?

If you are just starting to learn Drupal modules, and you are also new to PHP, it might be a good idea for you to brush up on Drupal Coding Standards. I know this sounds kind of boring, but it is a good practice. It is also very important if you intend to share your module with others in the future. This practice will make your code a lot easier to read for others who may wish to modify your module.

Drupal Coding Standards, as you might be able to guess, is basically similar to PHP coding standards. There are, however, some that are very specific to Drupal modules. The coding standards are explained here:

http://drupal.org/coding-standards

I will explain some of these listed above in detail.

Two spaces for indentation is implemented in Drupal standards. No white spaces should follow the end of the line. All line should end with a '\n' character. As opposed to '\r\n' for carriage return and line feed implemented widely in Windows system.

For all the $form[] = array(...); statements, you should format the lines so that it is easier to read. Following is an example from our Activity Manager module.

$form['activitytype'] = array(
'#type' => 'radios',
'#title' => t('Activity Type'),
'#required' => 'FALSE',
'#options' => array('Activity' => t('Activity'), 'Event' => t('Event')),
'#description' => t("Select type of event"),
'#default_value' => isset($node->activitytype) ? $node->activitytype : '',
'#weight' => -4
);

Notice that each element of the array is separated by a new line, while a comma is inserted right before the '\n' character. Also on the following line:

'#default_value' => isset($node->activitytype) ? $node->activitytype : '',

It is important to note that a space should be inserted before and after the '?' character (which denotes 'then'). This is also true for statements like:

$string = 'Hello' . 'World';

where a space should be inserted before and after the '.' which denotes concatenation.

For long lines that span beyond 80 characters should be formatted with new lines to make them easier to read. For instance in our Activity Manager module:

$additions = db_fetch_object(db_query('SELECT activitytitle, activitytype,'
. ' activitystartdate, activityduration, activitypublicationdate,'
. ' activityexpirationdate, activitykukuinuts, activitydescription,'
. ' activityverificationtype FROM {activity_manager} WHERE vid = %d',
$node->vid));

The code above is a good example of a line of code which could have easily gone beyond 80 characters. We managed to resolve this problem by ending the long string with a single quote, adding a new line and concatenating the next piece of line using the same method. The code provided above is a result of the repetition of the steps described here.

Drupal Coding Standards may seem a little annoying at first, but it is actually a good practice that everybody should follow if you are thinking about creating and sharing your own custom module. I would also suggest that you do this early. Learning about the coding standards after writing 2000 lines of code seems that it could be more than a annoyance.

Tuesday, April 13, 2010

Creating a Drupal Module

Creating a Drupal Module is not an easy task. In my last blog entry, I introduced a method in which to create a very simple Drupal Module. We are now doing something more complicated than a "simple Drupal Module", and I would have to say that initially, it was not a pleasant experience.

As I mentioned before, Drupal needs at least 3 files to run. The .info, install, and the .module files. While most of the important stuff is in the .module file, the three files are almost equally important. The .install file takes care of the database setup. It is basically run only once during the installation of the module. The .module file does almost everything else.

Drupal has an interesting way of handling custom modules. It does so by providing us with _hook functions which we are free to modify. At the same time, however, we are restricted only to modify these _hook functions. It is therefore, important that you first sit and learn what each of these _hook functions are for and how are you supposed to modify them.

Sometimes making more than a module to achieve a task becomes necessary in Drupal. For our case, we have developed an activity module, which handles the creation of activities. We also developed another module called signup that works together with the activity module to enable people to sign up for an activity.















There are also different types of modules in Drupal. The above example "activity manager" that we developed is a node module. As the name suggests, this module creates new nodes and interacts with the database fairly frequently.













The above image demonstrates how a user can fill out all the fields in Activity module to create a node. Each of the field above is a php form in the _form hook function. Once the user clicks on the submit button, the module sends the necessary data to the database to create your new node.














Here is how we made our Signup module. It essentially lists out the activity/events and provides a button for you to be able to signup for them. Unlike the activity module, this module does not produce any nodes. Once the user clicks on one of the sing up buttons, the module first retrieves the necessary information, user name, from the user and saves those information in its own table in the database. This way the module can keep in track of who is signed up for what activity. Also the module allows one person to sign up for more than an activity.

Tuesday, April 6, 2010

Creating a Custom Drupal Module

I had a little practice learning how to create a custom module for Drupal CMS this week, and I would like to take this opportunity to share this experience with the rest of the world.



Fortunately for us, Drupal provides comprehensive tutorial for creating a custom module here:

http://drupal.org/developing/modules

Though it may seem to you that there is a lot to do here (which is true), it is always a good idea to walk through the tutorial so that you get a general idea of the process of creating your own module. If you are like me, and just want to hurry up and see a module in action, I suggest that you simply copy and paste the code to respective files and place them under the correct directory. I will explain this process more in detail below.

A simple Drupal module needs at least three files: .module, .info and .install files. The .module file, to put it in simple terms, defines the function of the module. It is also where the majority of the codes are located. The .info is a small file that contain metadata information about the modules and themes. Typically the contents of .info files look somewhat like this:

; $Id$
name = "Example module"
description = "Gives an example of a module."
core = 6.x

The above is an example taken from Drupal's tutorial. The name and description fields are required fields. The core field should indicate the version number of your Drupal installation. Drupal 6.x version is used for this example.

Last but not least, you must write your .install file. This file basically contains initial set up codes that are run once while installing the module. Typically (depending on the type of module), this file contains about a dozen lines of SQL query commands that creates the necessary tables in your database.

Overall, creating your custom Drupal module is not an easy task. I hope that the above information helps you to get a general idea. I will certainly post a more detail information once I make further progress with my project.