Showing posts with label drupal 6. Show all posts
Showing posts with label drupal 6. Show all posts

Tuesday, 9 December 2008

Drupal 6: Core hacking for the simplest of things

I was recently faced with a problem concerning changing something in Drupal's core user module. This wasn't even a big change; it was as simple as changing the line of text that appears underneath the username field on the registration form. You know, the one that helps the user to choose an appropriate username.

The core has no option for changing this text, so the most obvious and worst thing possible is to open up user.module in the core and edit the string. This is bad because the first rule of Drupal club is do not hack the core. Actually, the second rule of Drupal club is... you get the idea. It would mean that when Drupal is upgraded, the change will disappear.

After ruling out the above method, I was faced with a number of opportunities to accomplish what I needed, but they all came with downsides.

Override the core


This involves making a copy of the core user module inside my site's modules folder. Drupal will then use the copy instead of the one in the core and changes can be made safely to this copy, because it is not going to be upgraded when a new version of Drupal is released, unlike the core itself.

My main concern here is that technically it's a core hack in disguise. In my case, I would be making a copy of Drupal 6.6's user module and then changing it. But, what happens when Drupal gets upgraded to 6.7? The core user module will be 6.7, but the site won't be using it. Any new or changed functionality in the user module will be missing from the site, and worse, this could introduce incompatibilities.

Suppose something has changed in 6.7's user module, and the other core modules expect this change to be in place? This would break Drupal, because it's not expecting to encounter the 6.6 user module here. How likely is this to happen? I'd say very unlikely in an upgrade from 6.6 to 6.7, but still possible. More than that, it's far more likely to happen once 6.7 becomes 6.8, 6.9 and so forth.


Locale


Drupal's translation system makes it possible to provide a translation string for the text underneath the username field, as though it were being translated into French or Italian, but this feels wrong. It's not a ‘real’ translation because we just want to change the string itself, not change it into another language.

String Overrides


There exists a module called string overrides. It provides a way of changing any text that is passed through the t() function, by saving these translations in the variables table. This seemed like an ideal solution at first, but on closer examination, there are a few drawbacks.

Firstly, since the overrides are stored in the database, we lose version control, which is never a good thing. Secondly, the more modules you add to a site, the more overhead there is, both in terms of memory and in terms of processing times.

Custom module


It would be possible to write a custom module specifically for the site in question. The module would mimic the functionality of the string overrides module, but store the overrides in a file instead of the database, thereby providing version control. The only slight downside to this is that it requires a lot more time than the other approaches. It seems like extreme overkill to have to write an entire module simply to change one string.

To conclude


In the end we decided to go with the core override method. I am not entirely happy with this, but I am sure I would not have been entirely happy with any of the solutions I've presented here. I can acknowledge and understand that there are many compromises to be made on the way to a completely custom Drupal site, but there is always room for improving Drupal to accommodate many of the more common requests. The questions are: is this one of the more common requests? and: what is the best way forward here?

Wednesday, 12 November 2008

Drupal: Pitfalls when converting CCK field modules from Drupal 5 to Drupal 6

I recently needed to convert a CCK field module from Drupal 5 to Drupal 6. There is a lot to take in, but I was faced with a particular problem:

warning: array_shift() [function.array-shift]: The argument should be an array in D:\wamp\www\drupal6\includes\form.inc on line 1320.

Oh dear. It's always difficult to debug this kind of thing because the problem lies in the code that called the function on line 1320 of form.inc, rather than there being a problem with form.inc. I used debug_backtrace() to see what parameters the previous functions in the call stack were using, and noticed that only the first two parameters in _form_set_value() were populated; the other two were NULL. This was the immediate source of the error. The following line was failing because $parents was NULL:

$parent = array_shift($parents);

Obviously you can't array_shift() NULL. I then went back further in the backtrace, to the form_set_value() function (note the function is not preceded by an underscore, like the last one). In this function, the second parameter ($value) was NULL. This was causing the NULL in the _form_set_value() function.

The solution
It turns out that I was using this code to handle the form widget's processing in my CCK field module:

/**
* Process the mcimage element.
*/
function mcimage_mcimage_process($element, $edit, $form_state, $form) {
$field_name = $element['#field_name'];
$field = $form['#field_info'][$field_name];
$field_key = $element['#columns'][0];
$value = isset($element['#value'][$field_key]) ? $element['#value'][$field_key] : '';

$element[$field_key] = array(
'#type' => 'hidden',
'#default_value' => $value,
// The following values were set by the content module and need
// to be passed down to the nested element.
'#title' => $element['#title'],
'#description' => $element['#description'],
'#required' => $element['#required'],
'#field_name' => $element['#field_name'],
'#type_name' => $element['#type_name'],
'#delta' => $element['#delta'],
'#columns' => $element['#columns'],
);
}
My mistake? The function does not return anything! It was as simple as adding a return statement at the end of the function, so that the form element could be processed correctly:

/**
* Process the mcimage element.
*/
function mcimage_mcimage_process($element, $edit, $form_state, $form) {
$field_name = $element['#field_name'];
$field = $form['#field_info'][$field_name];
$field_key = $element['#columns'][0];
$value = isset($element['#value'][$field_key]) ? $element['#value'][$field_key] : '';

$element[$field_key] = array(
'#type' => 'hidden',
'#default_value' => $value,
// The following values were set by the content module and need
// to be passed down to the nested element.
'#title' => $element['#title'],
'#description' => $element['#description'],
'#required' => $element['#required'],
'#field_name' => $element['#field_name'],
'#type_name' => $element['#type_name'],
'#delta' => $element['#delta'],
'#columns' => $element['#columns'],
);

return $element;
}