Extending core Profile module

Events happening in the community are now at Drupal community events on www.drupal.org.
alan d.'s picture

This is a request for comments / interest in extending the core Profile module itself in Drupal 6. It is a bit off topic for the group, but this group is closest of any to the topic.

Background
Leading to a core module hack, so here is the full background.

After developing in Drupal 5 and 6 over the last two years, I just hit complex profile requirements in 3 different projects at the same time. Each have different access requirements and field requirements.

Attempt One: Profile module
From past experiences, aka none, I turned to the profile module. Instantly I realized that this could not cut it. No way to extend it, and no way to store multiple data.

Attempt Two: Try to extend the core Profile module
Using a series of menu_alters, I successfully extended the Profile module, but it felt like a house of cards. New page callbacks extended the fields allowed, but data from these new fields had to be saved and removed prior to the Profile save action, then all loads had to be done manually. All up, about 1/4 of the Profile node functionality was redirected into my own functions to overwrite about 20 lines of code. Strange bugs randomly appeared, so I stopped before wasting to much time.

Attempt Three: Nodes
Initially put off by the size of the issue queue, I gave content profile a go. Overall, the results were pretty good, but the node behavior felt strange when handling user profile data. The profile content types were in the general create content links, general navigation within the node system, ability to have multiple profile nodes yet the interface appeared to only handle one, etc. So back into a series of menu_alters and programmatic controlled content types. Things looked good.

A menu alter intercepted the "node/add/TYPE" into "user/%user/edit/TYPE" with an access check to hide after a node was created belonging to the user. A custom new router entry to "user/%user_load_if_has_node_of_type/edit/TYPE" wrapped the node edit page nicely. Things looked good until realizing that custom menu aliases would be required for every node to automate the interaction between other module and the new paths. This by itself was not a killer blow, but when the access controller on one site failed using the router design, this approach meet the same fate as the first few attempts.

Attempt Four: Hack the Profile module
After spending more than a week on the first three phases, 2 days of programming had allowed me to extend the module to do pretty much everything that I required. This included creating the following extension:

  • Keyed selection list - "safe key | Human readable option" single + multiple selections | select list, radios, checkboxes
  • Node references - filter by content type | single + multiple | select list, radios, checkboxes
  • User references - filter by role | single + multiple | select list, radios, checkboxes
  • Taxonomy - defined vocab per field | single / multiple as defined by vocab
  • Name field - title, first name, surname
  • Address field - physical + postal (11 fields all up)
  • Children field - first name, last name, dob | multiple storage via external table (requires views integration but that's another story)

With the exception of the children fields, all data was saved in the profile_values table, serialized as required.

Goals
To normalize the functionality to be as compatible and flexible as possible with the core profile module as possible. It is not aimed at the user node-like functionality, but to enhance the core user-based profile data.

Finally, the reason for the post!
To find any other interested developers to help direct and extend the hack. The best solution would be to develop a new module that extends Profile, but Profile is very difficult to do this.

Currently, the extensions are done via a very simple field api. This is the core date functionality wrapped into the new api.

<?php

/**
* Fields are presented by three callback functions.
* These need to be in the loaded path.
*
* Required - Provides the input / view of the field
*
* profile_field_FIELD_KEY_form
* profile_field_FIELD_KEY_view
*
* You can specify a different function name by using the
* 'form callback' and 'view callback' keys.
*
* Optional - defines the options field on the administer profile field page.
*
* profile_field_FIELD_KEY_options
*
* No default is generated, so by default this element is not shown.
*
* The way that options field is stored is defaulted to be serialized.
* This can be turned off using the flag.
*
* 'options serialize' => FALSE
*
* Only the core selection list uses this.
*
* Handling the submitted data
*
* All of the fields here store the data in the {profile_values} table.
* As such, all complex or multi-value data needs to be serialized first.
* This is done by defining the 'save callback' and 'load callbacks'.
*
* This could be used to link data to another database table.
*
* 'save callback' => 'profile_serialize',
* 'load callback' => 'profile_unserialize',
*
* The title field is the name of the field to present to the user.
*
* The explanation field is the text to append to the field specific explanation
* to explain how to use the form element.
*/
function profile_profile_fields() {
 
$fields = array(
   
// cut
   
'url' => array(
     
'title' => t('URL'),
    ),
   
'date' => array(
     
'title' => t('date'),
     
'save callback' => 'profile_serialize',
     
'load callback' => 'profile_unserialize',
    ),
   
// cut
 
);

  return
$fields;
}

function
profile_field_date_form($field, $value, $field_info) {
 
$form_field = array('#type' => 'date',
   
'#title' => check_plain($field->title),
   
'#default_value' => $value,
   
'#description' => _profile_form_explanation($field),
   
'#required' => $field->required,
  );
  return
$form_field;
}

function
profile_field_date_view($field, $value, $field_info, $browse) {
 
$format = substr(variable_get('date_format_short', 'm/d/Y - H:i'), 0, 5);
 
// Note: Avoid PHP's date() because it does not handle dates before
  // 1970 on Windows. This would make the date field useless for e.g.
  // birthdays.
 
$replace = array(
   
'd' => sprintf('%02d', $value['day']),
   
'j' => $value['day'],
   
'm' => sprintf('%02d', $value['month']),
   
'M' => map_month($value['month']),
   
'Y' => $value['year'],
   
'H:i' => NULL,
   
'g:ia' => NULL,
  );
  return
strtr($format, $replace);
}

// The save / load handlers - requires the field / account info in more complex types
function profile_serialize($field, $user, $value, $edit) {
  return
serialize($value);
}

function
profile_unserialize($field, $user, $value) {
  return
unserialize($value);
}

?>

Any feedback welcome, I'll supply a zip of the hacked module after things are tidied up a bit + testing is completed, in a week or so.

Comments

Date Field with time

abhishek.kumar's picture

HI ,
CAN YOU PROVIDE ME THAT MODULE

Profile2

michelle's picture

I'm not interested in getting involved as I am happily using nodes for profiles and plan to continue to do so. However, I wanted to mention that you will probably want to get with the Profile 2 folks and make sure this hack that you are doing is forward compatible so you don't end up in a module dead end.

Michelle

Personally holding off...

alan d.'s picture

For reference:

http://drupal.org/project/profile2 [No activity since 10 Jan]

The sad tales of:

Migrate profile module data to field API http://drupal.org/node/394720
New Profile module leveraging fields API http://drupal.org/node/301071

Related

Resolve the conflict between the fieldable users UI and the profile module http://drupal.org/node/608894

There is even talk about removing Fields from the User bundle here! Thx goodness that the powers to be haven't done this yet.

The original (and very minimalist / outdated patch for D7) http://drupal.org/node/83902#comment-2060018

It easily gave all of the functionality of a Webform like module, and extensible too.

I tried to get interest in this fallback last year, but there was ZERO interest from the powers to be. Not even a negative comment.... there was zero feedback. Fields was the be all and end all of all profile issues, even thought in Sept last year I only gave it a 5% chance of fieldable profile module making it into core.

Anyway, the pain of upgrade paths (being realistic here, it will be in 2012 with D8), held me of releasing it. Although it was interesting to note that there was an upgrade path from Profile to Fields written months before the work on a true Fieldable Profile module was even started. I guess the egg really did come before the chicken!

I'd stick to the Content Profile module, although the basic 1 to 1 upgrade script from Profile to Fields would be fairly easy to script up if you know SQL and you have the data model of both firmly embedded in your mind. Making this dynamic is the hard part!

Profiles as nodes

Group organizers

Group notifications

This group offers an RSS feed. Or subscribe to these personalized, sitewide feeds: