Part II – The new-[[http://drupal.org/user/16747 |fago]]’d way: Enter node profile package!
- Intro and an aside on hunting down Drupal documentation
- A summary of node profile options, and a path chosen
- Use cases
- Adding more profile sections with nodefamily
- Editing the profile and its sections
- Screen shots
- Code and step-by-step explanation
- Coming soon
So now for something completely different, which can take advantage of the cck + views (ability to create and persist business objects, plus, ability to list them in a fully flexible and fully themeable manner, all without programming) and all the other good stuff Drupal 5.x offers.
An aside on hunting down Drupal documentation: it’s all there, believe me!
So when you are using some of the drupal modules, where do you find documentation on it? First in the README and associated files in the actual module directory. But also in a catchy, user intuitive place, called "Read documentation" in the project page Resources section. Taking views as an example, let’s suppose I want to find a place which explains to me how to include an embedded view in any place I can use PHP (in a theming template, for example). Well, do this:
- Go to http://drupal.org/project/views
- Click on Resources / Read documentation and be taken to http://drupal.org/handbook/modules/views
- Look carefully!
- Delucidate Please see the [[http://drupal.org/node/109604 |Views Documentation Page]] for information on how to use Views in your site and with your modules.
- Click to be taken to http://drupal.org/node/109604
- Cool! Well, here we would want "Views 1.x module developer API" and find in the middle of the page "Advanced: Using views_build_view to control your own views". In some of the comments, all is revealed!!!
Now for nifty [[http://drupal.org/user/16747 |fago]]’d node profiles, it’s actually a lot easier. Let’s suppose that for some dog forsaken reason you wind up on the http://drupal.org/project/nodeprofile page. There are a plethora of resources to point you to a whole study of the subject. Among the best, is the good old Resources / Read documentation, upon which we will base ourselves here for an initial "best cut" solution from amongst all the options.
So go to [[http://drupal.org/node/129957 |Node Profile Package]], and read through the whole thing at least once, until you have a special buzz going on (in my case I am greatly helped by frequent recourse to Mate, I am sure you all have your own excuse.
A summary of node profile options, and a path chosen
Use Case 1 Member Organization wants to maintain its profile
Use Case 2 Member Organization wants to divide info up into several well-defined sections
Use Case 3 Visitor to the site wants to browse member organizations
So given our use cases (without which we cannot work) our aim here is to "build a node profile consisting of several content types". So we will be using the http://drupal.org/project/nodeprofile and hence the http://drupal.org/project/nodefamily modules.
For Use Case 3, we will be needing to use the usernode module, "for presenting the users nodeprofiles information to the public" using "tools for nodes … e.g. for listing users with views."
Now the first child page looks exceptionally promising, [[http://drupal.org/node/130489 |Getting Started: A Step by Step Tutorial]], so let’s do that first.
We do Step 1, and install nodefamily and nodeprofile.
We then design the content type which is going to be the profile node, calling it member organization, and we give it just a few essential fields, like name (title), logo and geographic location. All the other important stuff, like contact info, news, membership list, or whatever, will go into other, children nodes later. The important thing first of all is that we have checked the option (which appears after correctly installing nodefamily and nodeprofile) to "Use this content type as a nodeprofile for users". Since this nodeprofile node will be the root node of a tree of children nodes (non-nodeprofile nodes, but related to the nodeprofile node via nodefamily: more on this below) and will be "the" profile for each user, it is important that the maximum population be set to 1.
I also installed usernode at this point, since in this solution we know we are going to use it.
Now, as to how the user will edit his profile, the tutorial suggests two methods, described in steps 2a (nodeprofile module supported url) and 2b (using the pageroute "wizard" editing system). If either are right for you (go ahead and check them out) that’s great. In this articles I am going for a third option, something a bit more homegrown and manageable than pageroute, but the latter may be just what you need, so, try it out on an example setup.
Implementing Use Case 2: Adding more profile sections with nodefamily
We skip to Step 3: extend the nodeprofile with further content types (optional), and after reaching for my mate a couple of times, basically do the following:
- create a series of further content types (profile sections).
- go to administer > node family (admin/content/nodefamily)
- Usernode > Member Organization Profile
- Member Organization Profile > Member Organization Contact
- Member Organization Profile > Member Organization Institutional Information
- Member Organization Profile > Member Organization Financial
- Member Organization Profile > Member Organization News
- ending with something like this. We are assuming that each member organization is actually a Drupal user , with a usernode automatically created (and deleted should the user be deleted), and an association automagically generated among all profile content types:
So Use Case 2 is implemented, with each child of Member Organization Profile constituting itself into a self-contained "profile section".
Use Case 1: implementing the editing of the Member Organization Profile.
So here I have opted for an advanced but "quite simple really" solution, easily (automatically!) extended if you add more content types to the profile constellation of nodes.
First off, as in Part I, we override theme_user_profile in order to setup a user profile template to be invoked by the callback function. But we don’t want designers to have to be fooling around with php in that template, we want them to be able to simply print variables and re-arrange them in semantic divs, etc. So we do a large part of the dirty work here, in template.php (see phptemplate_user_profile() code in code section, below):
Right, now when a (Member Organization) user goes to his account, he will see… nothing at all, yet, since we have not set up the templates we are invoking with the callbacks.
According to the user_profile theming override function we have written, we need the following templates:
- profile-only-section.tpl.php
- profile-section.tpl.php
- user_profile.tpl.php
Now, the beautiful part is that we are now free to theme each of the profile section nodes separately!!! And that individual theming will be invoked here as part of the editing and later on when we display the profiles in site-wide browsing. Cool. And, any profile section added later will be automatically added to the mix (automatically included in phptemplate_user_profile(), and automatically dealt with in profile-section.tpl.php).
So optionally, we will want to add the following templates:
- node-mo_contact.tpl.php
- node-mo_institutional.tpl.php
- node-mo_financial.tpl.php
- node-mo_news.tpl.php
(Note: node-mo_profile.tpl.php will be invoked only for displaying purposes, this will be explained in Part III of this series).
Screen shots
The splendid end result can be seen by the following two or three screen shots. For the code and step-by-step exegesis, see below.
Basically, the profile and child profile sections are all listed on the user’s profile page in collapsible sections. 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.
User account page – initial
User account page – a peek at a single section with edit button
Editing the news photos section
Code and step by step explanation
Overriding theme_user_profile() in phptemplate.php
/**
* Catch the theme_profile_profile function, and redirect through the template api
*/
function phptemplate_user_profile($user, $fields = array()) {
// Pass to phptemplate, including translating the parameters to an associative array.
// The element names are the names that the variables
// will be assigned within your template.
$profile_node = nodefamily_relation_load($user->node_id);
$profile_nid = $profile_node[0] -> nid;
if ($profile_nid) {
// This user has a profile_nid and so requires special treatment
// first get the nodeprofile node themed for the editing part without using
// its node specific theming
$profile = _phptemplate_callback('profile-only-section', array('node' => $profile_node[0]));
// now let's get an array of variables, each one complete
// with the html for each of the profile sections
$profile_sections = '';
$profile_node_sections = nodefamily_relation_load_by_type($profile_nid);
foreach ($profile_node_sections as $aSection) {
foreach ($aSection as $aSectionNode) {
$profile_sections .= _phptemplate_callback('profile-section', array('node' => $aSectionNode));
}
}
return _phptemplate_callback('user_profile', array(
'profile' => $profile,
'profile_sections' => $profile_sections,
'user' => $user,
'fields' => $fields)
);
} else {
// this user doesn't have a profile node,
// meaning treat the user account page in the default manner
$vars = array(
'user' => $user,
'fields' => $fields,
'picture' => theme('user_picture', $user),
'categories' => '',
);
// Extract each field into its own template.
foreach ($fields as $category => $items) {
$vars['category'] = $category;
$vars['items'] = ''; // reset this variable!
foreach ($items as $item) {
// we could use array_merge here but I'm putting them all in for
// clarity of what variables we have available.
$vars['title'] = $item['title'];
$vars['class'] = $item['class'];
$vars['value'] = $item['value'];
// run the item template
$vars['items'] .= _phptemplate_callback('user_profile_item', $vars);
}
// Now that we have all the items, run it through our category template.
$vars['categories'] .= _phptemplate_callback('user_profile_category', $vars);
}
// And put it all in the final wrapper.
return _phptemplate_callback('user_profile', array('vars' => $vars));
}
profile-only-section.tpl.php
profile-section.tpl.php
0) { ?>
user_profile.tpl.php
0) { ?>
Coming soon:
- Part III – Displaying profile sections with jstools tabs
- Part IV – Query by example site wide searching for profiles