Got a few emails from people asking to see the code, so here is what I got...
This requires a small addition to the uc_orders table:
- Add a field called 'profile_values' of type 'blob'.
(Ideally this should be separated into a new table, but I just did it the quick and dirty way.)
What this (uc_profile) module does:
- Pulls up all data from the user registration form (within uc_profile_checkout_form() )
- *** Removes certain fields
- *** Reorders certain fields
- *** Makes some field sets collapsible
- Upon clicking the 'review order' button the profile data is serialized and stored in uc_orders.profile_values (done in uc_checkout_pane_profile(), process case)
- Upon landing on the review order page (uc_checkout_pane_profile(), review case), the serialized data is pulled and passed to the theme function uc_profile_table()
- *** Data is themed by the theme function
- Upon clicking the 'submit order' button the order gets processed and new user created
- uc_profile_user() is called with the case 'insert,' and the submitted profile data is pushed to the profile_values table for the appropriate UID
*** = specific to my needs and will likely need to be altered
(Sorry for the scrolling - posting in-line for the benefit of search engines and people too lazy to click the download link - you know who you are
.)
<?php
/*******************************************************************************
* Hook Functions (Ubercart)
******************************************************************************/
/**
* Implementation of hook_checkout_pane().
*/
function uc_profile_checkout_pane()
{
$panes[] = array(
'id' => 'profile',
'callback' => 'uc_checkout_pane_profile',
'title' => t('Profile Information'),
'desc' => t("Display additional profile fields."),
'weight' => 10,
'process' => TRUE,
'collapsible' => TRUE,
);
return $panes;
}
function uc_profile_user($op, &$edit, &$account, $category = NULL)
{
switch($op)
{
/**
* User account is being created. At this point we already have
* the user object in $account, and need to populate the profile
* fields with the data stored in the order.
*/
case 'insert':
/**
* Let's load the submitted profile fields from the database.
* We use the user's email to find the row. Emails are unique,
* and since the user is just registering this account this
* will be the user's first (and only) order.
*/
//TODO: Possible freak case: user registers and makes an order,
//account is deleted, user re-registers with the same email
//as before and makes an order. This could result in multiple
//orders with the same email address.
$mail = $account->mail;
$profile = unserialize(db_fetch_object(db_query("SELECT profile_fields FROM {uc_orders} WHERE primary_email='%s'", $mail))->profile_fields);
/**
* Array of fields which will be updated in the {profile_values} table.
* The key is the 'name' field from {profile_fields}
*/
//TODO: Streamline/automate this process.
//Store the profile_field.value in the same array from the start?
$fields = array(
'profile_silverpop' => $profile['Join mailing list'],
'profile_search_privacy' => $profile['Show profile in search results'],
'profile_general_interest' => $profile['No children'],
'profile_babyname' => $profile['First Child']["Child's name"],
'profile_babydate' => $profile['First Child']['Birth year'].$profile['First Child']['Birth month'].$profile['First Child']['Birth day'],
'profile_babygender' => $profile['First Child']['Gender'],
'profile_babyname2' => $profile['Second Child']["Child's name"],
'profile_babydate2' => $profile['Second Child']['Birth year'].$profile['Second Child']['Birth month'].$profile['Second Child']['Birth day'],
'profile_babygender2' => $profile['Second Child']['Gender'],
'profile_babyname3' => $profile['Third Child']["Child's name"],
'profile_babydate3' => $profile['Third Child']['Birth year'].$profile['Third Child']['Birth month'].$profile['Third Child']['Birth day'],
'profile_babygender3' => $profile['Third Child']['Gender'],
'profile_babyname4' => $profile['Fourth Child']["Child's name"],
'profile_babydate4' => $profile['Fourth Child']['Birth year'].$profile['Fourth Child']['Birth month'].$profile['Fourth Child']['Birth day'],
'profile_babygender4' => $profile['Fourth Child']['Gender'],
'profile_priv_policy' => 1,
);
$uid = $account->uid;
/**
* The fields already exist with empty values, so all we need to do is
* update them
*/
foreach($fields as $key => $field)
{
$query = " UPDATE {profile_values} pv, {profile_fields} pf
SET pv.value = '%s'
WHERE pf.name = '%s'
AND pf.fid = pv.fid
AND pv.uid = '%d'
";
db_query($query, $field, $key, $uid);
}
break;
}
}
/*******************************************************************************
* Callback Functions, Forms, and Tables
******************************************************************************/
/**
* Callback for uc_profile_checkout_pane()
*/
function uc_checkout_pane_profile($op, &$arg1, $arg2)
{
switch ($op)
{
case 'view':
$description = t('Please fill out your profile information!');
$contents = uc_profile_checkout_form();
return array('contents' => $contents, 'description' => $description);
case 'process':
/**
* Array holding the submitted data in a custom (cleaner) format.
* The keys are renamed to provide the user with nicer titles on
* the order-review page.
*/
$profile = array();
$profile['Join mailing list'] = $arg2['Stay Informed']['profile_silverpop'];
$profile['Show profile in search results'] = $arg2['Privacy']['profile_search_privacy'];
$profile['No children'] = $arg2['Children']['profile_general_interest'];
$profile['First Child'] = array(
'Child\'s name' => $arg2['First Child']['profile_babyname'],
'Birth month' => $arg2['First Child']['profile_babydate']['month'],
'Birth day' => $arg2['First Child']['profile_babydate']['day'],
'Birth year' => $arg2['First Child']['profile_babydate']['year'],
'Gender' => $arg2['First Child']['profile_babygender']
);
$profile['Second Child'] = array(
'Child\'s name' => $arg2['Second Child']['profile_babyname2'],
'Birth month' => $arg2['Second Child']['profile_babydate2']['month'],
'Birth day' => $arg2['Second Child']['profile_babydate2']['day'],
'Birth year' => $arg2['Second Child']['profile_babydate2']['year'],
'Gender' => $arg2['Second Child']['profile_babygender2']
);
$profile['Third Child'] = array(
'Child\'s name' => $arg2['Third Child']['profile_babyname3'],
'Birth month' => $arg2['Third Child']['profile_babydate3']['month'],
'Birth day' => $arg2['Third Child']['profile_babydate3']['day'],
'Birth year' => $arg2['Third Child']['profile_babydate3']['year'],
'Gender' => $arg2['Third Child']['profile_babygender3']
);
$profile['Fourth Child'] = array(
'Child\'s name' => $arg2['Fourth Child']['profile_babyname4'],
'Birth month' => $arg2['Fourth Child']['profile_babydate4']['month'],
'Birth day' => $arg2['Fourth Child']['profile_babydate4']['day'],
'Birth year' => $arg2['Fourth Child']['profile_babydate4']['year'],
'Gender' => $arg2['Fourth Child']['profile_babygender4']
);
$profile = serialize($profile);
/**
* Store the serialized profile data with the order
*/
//TODO: Separate into antother table with fields 'order_id' and 'profile_filds'
db_query("UPDATE {uc_orders} SET profile_fields='%s' WHERE order_id='%d' LIMIT 1", $profile, $arg1->order_id);
return TRUE;
case 'review':
/**
* Retrieve the profile data stored with the order and pass it to the theme function.
*/
$profile = unserialize(db_fetch_object(db_query("SELECT profile_fields FROM {uc_orders} WHERE order_id='%d'", $arg1->order_id))->profile_fields);
return array(theme('uc_profile_table', $profile));
}
}
/**
* Generates the form displayed on the checkout page
*/
function uc_profile_checkout_form()
{
$form = array();
//if only I knew about this function *before* spending 5 hours
//on tricky ways to pull and re-construct the form manually...
$form = drupal_retrieve_form('user_register');
//remove unnecessary fields
unset($form['user_registration_help']);
unset($form['account']);
unset($form['About You']);
unset($form['submit']);
unset($form['#parameters']);
//reorder fields
$form['Children']['#weight'] = 0;
$form['First Child']['#weight'] = 1;
$form['Second Child']['#weight'] = 2;
$form['Third Child']['#weight'] = 3;
$form['Fourth Child']['#weight'] = 4;
$form['Stay Informed']['#weight'] = 5;
$form['Privacy']['#weight'] = 6;
//make fieldsets collapsible
$form['Second Child']['#collapsible'] = 1;
$form['Second Child']['#collapsed'] = 1;
$form['Third Child']['#collapsible'] = 1;
$form['Third Child']['#collapsed'] = 1;
$form['Fourth Child']['#collapsible'] = 1;
$form['Fourth Child']['#collapsed'] = 1;
return $form;
}
/**
* Overridable theme function to output the submitted profile information
* on the order review screen
*/
function theme_uc_profile_table($data)
{
/**
* Track the depth of table nesting in order to remove
* td-classes from all tables except the exterior one
*/
static $depth;
if(is_null($depth))
$depth = 1;
else
$depth++;
$output = '<table class="order-review-profile-table depth-'.$depth.'">';
foreach($data as $title => $value)
{
$output .= '<tr valign="top">';
//only add the classes to the exterior table's TDs
$class1 = ($depth == 1) ? 'title-col' : '';
$class2 = ($depth == 1) ? 'data-col' : '';
/**
* Don't output anything if the field's value is empty, or if
* the Gender field's value is 0. NOTE: 0 is a string, so it
* must be enclosed in quotes.
*/
if($value != '' && !(strtolower($title) == 'gender' && $value == '0'))
{
if(is_array($value))
{
/**
* Check to see if the child array has any data in the value
* fields. If not, we don't need to output anything.
*/
$dig_deeper = check_empty_array($value);
if($dig_deeper)
{
$output .= '<td class="sub-heading" colspan="2">'.$title.theme_uc_profile_table($value).'</td>';
}
}
else
{
$output .= '<td class="'.$class1.'">'.$title.':</td>';
$output .= '<td class="'.$class2.'">'.$value.'</td>';
}
}
$output .= '</tr>';
}
$output .= '</table>';
$depth--;
return $output;
}
/**
* Checks array values (and sub-array values) to see if there's
* any data, or if all keys contain empty values
*/
function check_empty_array($data)
{
//TODO: Double check functionality with arrays 3+ levels deep and clean up function
$dig_deeper = false;
if(is_array($data) && sizeof($data) > 0)
{
foreach($data as $key => $value)
{
if(is_array($value))
{
$dig_deeper = check_empty_array($value);
if($dig_deeper)
{
//there are non-empty values within the array - that's all we need to know
break;
}
}
else
{
if(!empty($value))
{
$dig_deeper = true;
break;
}
}
}
}
return $dig_deeper;
}
?>Hopefully this proves useful to someone.
Maybe I'll make a proper module out of this one day...
IMPORTANT: this was developed on/for RC4 of Ubercart and Drupal 5.7. NOT tested on any other configuration.



Joined: 05/05/2008