Author image
Senior Developer

Another case of Drupal's sensitivity

If you have a Drupal site with users and you want to allow them to add some profile data about themselves there are a few routes available to you, one of the oldest (and thus simplest) ways is to use profile module. You can add text fields, selects and other bits and bobs to users, with different visibility settings for each field. You also have the chance (in fact it is required that you do) to categorise your fields. These categories then show up as secondary tabs on the user edit page. But what happens when you want to make it really simple and get the profile fields onto the main user edit page itself? At first glance you can't, because if you set the category to 'account', then Drupal will complain. But you can just set the category to 'Account' and you're done, right?

Wrong.

What you've just done is to set yourself up for disaster. The way that a user object is saved and loaded is reasonably clever; You essentially call user_save passing in the object representing the user and an array of fields to be saved, and the category of those fields. hook_user is then invoked to let any modules do their stuff and save the right fields to the right places, profile module is one such module, and it will happily look for values in the array of fields passed in to save into its tables.

Spot the problem? Profile module sees 'Account' and 'account' as the same string, which is why the 'hack' described above worked. Lots of other modules will have the following flow to modify a user (you're fine on the /user/X/edit page btw):

  1. Load a user object.
  2. Compile an array of changes (add a role, or change username/email.)
  3. Save the object using user_save, passing in those changes.

If no category is given, then 'account' is assumed, profile module will try to save those profile fields that you/I've given categories of 'Account'. But it looks in the array of data passed in for the values to store, and if it can't find a matching field it assumes that the string should be ''. Now it saves those empty strings into the database. Fail.

There are two fixes:
First, don't use any string that is 'account', 'Account' or 'aCcount' etc.
Or, fake the fields on save thus:

Create a new module, with a lower weight that profile module. Implement hook_user like this:

/**
* Implementation of hook_user()
*/
function example_user($op, &$edit, &$account, $category = NULL) {
  if (module_exists('profile')) {
    switch ($op) {
      case 'update':
        if (strtolower($category) == 'account') {
          $result = _profile_get_fields($category);
          while ($field = db_fetch_object($result)) {
            if (!isset($edit[$field->name])) {
              if (isset($account->{$field->name})) {
                $edit[$field->name] = $account->{$field->name};
              }
            }
          }
        }

        break;
    }
  }
}

Drupal can be funny about the case of strings you input, so be careful!

Comments

Thanks for trying, but I couldn't understand what you wrote :(

First you say 'set yourself up for disaster', then you say 'the hack above worked'.

Your jokey style made it difficult for a novice (at the Drupal code level) to understand.

It feels like you have assumed I already understand what you are saying before reading it, but I don't ... that's why I'm reading the article.

Sorry. This comment is meant to be positive ... honest!

Why will it ... "basically save empty strings." ?

I guess that's the bit I don't understand ... maybe.

Thanks

:)

Yeah, probably not my finest hour. Give me 12 hours, and I'll explain everything a bit better.

we will all be happy when drupal 7 profiles are based on cck :-)

Comments on this article are now closed, if you want to give us feeback you can use our contact form instead.