A fresh approach to social networking profiles (Part III)

Part III - editing the profile on the one hand, displaying the profiles on the other - with jQuery tabs!

A pending issue left over from Part II

Part III – editing the profile on the one hand, displaying the profiles on the other – with jQuery tabs!

A pending issue left over from Part II

In the Screen shots section of Part II, I said

"Each section has an edit button, after editing and either submitting
changes or cancelling the edit, the user is brought back to the same
user profile page."

But I didn’t show how that is to be done.

In order to bring that off, I am afraid we need a utilities module. In every site with any degree of complexity, one has to have a module whose responsibility it is to encapsulate all the dirty little tasks that need to be accomplished, nodeapi stuff, form alter stuff, custom form generation, what have you.

So we create a module with the same name as our job postings site we are using in this part of the series (called, appropriately enough “empleos” (Spanish for jobs)). In it we place the following code:

* customize forms with hook_form_alter
* Profile forms
function empleos_form_alter($form_id, &$form) {
global $user;
switch ($form_id){
case 'perfil_postulante_node_form':
case 'pp_areasdepref_node_form':
case 'pp_datosestudio_node_form':
case 'pp_experiencialaboral_node_form':
// redirect submit back to user account page
$form['#redirect'] = array('user/' . $form['#node'] -> uid);
// add cancel button and perform any other changes
$form['#theme'] = 'empleos_postulante_profile';

function theme_empleos_postulante_profile($form) {
global $user;
// perform unsets
// unset($form['delete']);
$form['cancel'] = array(
'#type' => 'item',
'#value' => '',
'#weight' => 55,
'#submit' => false
$output = drupal_render($form);
return $output;

Basically we are adding a redirect element to all forms in the nodefamily, so that submit will lead back to the appropriate user account page. And we are specifying a theme function for these nodes, which can unset delete (so user can edit but not delete if that is appropriate for the site) and most importantly adds a cancel button which also redirects back to the appropriate user profile page.

Why does editing have to be different from displaying?

I suppose they shouldn’t be and in a perfect world they wouldn’t be, but without attempting to create any kind of rationalization, in the social networking sites I use (you know, linkedin, facebook, …) the two use cases are separate in form and functionality.

So in this path I am taking here ("There’s more than one way to do it") what we are actually doing is, taking advantage of the user/usernode/nodeprofile/nodeprofile children connection, on the one hand, to get the editing and maintaining of the profiles going (as we saw in detail in part II); while, on the other hand (as we shall see in this part of the series), basing ourselves on views (lists generated from queries) of nodeprofile and its children, for the listing, browsing and detailed profile drilling down on the public display side.

Get a simple view going

So let’s suppose there is a role of manager of member organizations, who has the power of going in and editing member organization profiles… They would want a usernode or user list, and when they clicked on a user, they will be taken to that person’s profile editing view, as we saw in Part II.

But here we are talking about public visitors to the site who want to browse profiles, do searches and view profile details. That’s what we are talking about.

So to get started we need a view of… nodeprofiles! 0ptionally filtered by roles, but that is a more advanced question.

Let’s take as an example in this article the job postings web application (more later!) awebfactory is developing for a Buenos Aires Company (details upon launch Cool). Here the idea is for candidates to have their own online resume and blog, be able to maintain their profile and be able to apply for positions. So, we have a nodeprofile content type with the basic personal info of the candidate (Full name, personal details, mail, other contact info) together with a host of content types configured as "children" of the nodeprofile content type, as follows:

Empleos nodefamily

Empleos nodefamily

English Español
jobs empleos
profile perfil
candidate postulante
info datos
course info datos de estudio
work experience experiencia laboral
last name apellido
gender sexo
zipcode código postal
date of birth fecha de nacimiento
address dirección
etc. etcétera

So, what we are going to do is to make a simple view to search for profiles according to some very simple criteria (large scale cross-node searching will be a subject for the future – not too far into the future, I need to have a solution quickly, this site is launching this month!), then theme the nodeprofile content type to show itself and its children, then make it look nice.

Here’s our first iteration mini-profile browser at work:

Jobs site profile listing

Jobs site profile listing (with info removed to protect the innocent)

Theme the nodeprofile content type to show all children

Now if I click on one of the links in the listing, I get the standard node view for the content type.

So we need to do some custom theming to be able to see the node profile and all the child nodes for the particular profile whose details we would like to see displayed.

So in the theming directory we copy node.tpl.php to make a new template with the name of the nodeprofile content type. In our case here, the name of the nodeprofile content type is "perfil_postulante", so we copy node.tpl.php to node-perfil_postulante.tpl.php. It’s worth looking at the template in its original form:

nid != 1): ?>

We are going to get rid of picture, because we want the profile picture to be an imagefield in the node profile content type. Submitted is probably not appropriate either for this kind of presentation, and possibly idem for taxonomy (depends on site characteristics and navigation model). It is the content div where we will now get to work and put in some code to list all children. First off, you will probably just want to do something like the following to see what we have:

echo '

' . print_r($sections, true) . '


and then we will just make a flat list:

foreach ($sections as $aSection) {
foreach ($aSection as $aSectionNode) {
echo node_view($aSectionNode, false, false, false);

which will print out a nice ugly Drupal enless river of content, including “the whole family” (all nodes in nodefamily for that user).

Now, what if you have several nodes per section. To print out the nodes on a per section basis you would problably have to do something like the following (out of all the exciting alternatives that come to mind):

foreach ($sections as $aSection) {
$sectionName = key($sections);
print '

eye-catching ' . $sectionName . ' section header

foreach ($aSection as $aSectionNode) {
echo node_view($aSectionNode, false, false, false);
echo '

eye-catching ' . $sectionName . 'end-of-section


Make it look nice

Well, of course there’s more than one way to do it, but for this solution, [[http://drupal.org/project/jstools |javascript tools]] to the rescue with the tabs module!

For this installation I have installed the [[http://ftp.drupal.org/files/projects/jstools-5.x-1.x-dev.tar.gz |jstools 5.x-1.x-dev]] version (Nov 23 2007 build).

The following code reflects this:

'Areas de Preferencia',
'pp_conocimientosinformatica' => 'Conocimientos de Informática',
'pp_cursoformacion' => 'Cursos de Formación',
'pp_datosestudio' => 'Educación',
'pp_experiencialaboral' => 'Experiencia Laboral',
'pp_idioma' => 'Idiomas',
'pp_objetivoslaborales' => 'Objectivos Laborales',
'pp_otrosconocimientos' => 'Otros Conocimientos',
'pp_referencialaboral' => 'Referencias Laborales',
// first display the nodeprofile
print $content;
$sections = nodefamily_relation_load_by_type($node -> nid);
if ($sections) {
$tabs = array(
'#type' => 'tabset',
'#name' => 'profiletabs',
// then display each of the sections
foreach ($sections as $aSection) {
$sectionName = $nodeNames[key($sections)];
// initialize $tabContent
$tabTitle = $sectionName;
$tabContent = '';
foreach ($aSection as $aSectionNode) {
$tabContent .= node_view($aSectionNode, false, false, false);
$tabs[] = array(
'#type' => 'tabpage',
'#title' => $tabTitle,
'#content' =>$tabContent,
print tabs_render($tabs);

Screen shots

empleos tabs: top of page (node profile)

empleos tabs: top of page (node profile)

empleos tabs: bottom of page (tabs containing profile "sections")

empleos tabs: bottom of page (tabs containing profile “sections”)