If you like the data shown on the user profile page but want to change the layout you can skip down the layout section. If you like layout of the data shown on the user profile page but want to change the data, you'll need to able to do some PHP hacking. If you want to change both the data shown and the layout you'll have to do both.

The exceptions are the 'Contact' and 'Member since' fields which are handled directly in the layout template (wups). In order to edit or remove those you'll need to hook the layout template.

Changing the Data

The data on the user profile page is very dynamic based on a myriad of user stats. In order to manipulate them you hook the user profile filter event which is called whenever the user profile template is about to be displayed.

 CCEvents::AddHandler( CC_EVENT_FILTER_USER_PROFILE, 'user_profile_filter' );

 function user_profile_filter(&$rows)
 {
     $user_row  =& $rows[0];
 }

Your handler will be called with an array, the first element of which will be a user record that has already been massaged by the other modules interested in displaying something. In the code above we make a reference to that record for better readability throughout the code.

There are three areas in the user record with potential interest:

  • $user_row['user_fields'] is an array of labeled data fields for display
  • $user_row['user_tag_fields'] is an array list links that are supposed to treated and displayed as folksonomy tags
  • (everything else) in the record is raw data taken out of the database for that user.

The 'user_fields' and 'user_tag_field' arrays are not indexed, which means if you want to remove an element, the safest thing to do is to make a copy of the array:

 $ok_array = array();
  
 foreach( $user_row['user_fields'] as $uf )
 {
    if( $uf['label'] == 'str_stats' )
       continue; 
    $ok_array[] = $uf;
 }

Then set the new array it back into the user record:

 $user_row['user_fields'] = $ok_array;

The actual contents of any of those areas is very sensitive to context and might change from session to session so you should never assume the presence of any, always be ready for any these and don't assume that a comprehensive list is feasible.

Having said all that, possible values to watch for in 'user_fields'

label Typical value
str_user_about_me HTML formatted user description
str_user_home_page HTML formatted link to user's home page
str_user_feeds Link to user_feeds template
str_user_forum_posts Number of posts user has made to the forums
str_playlists Link to number of playlists (or galleries) user is featured in
str_publicize Link to 'publicize' feature
str_review_stats Stats about reviews
str_stats Remix stats

Possible values to search for in 'user_tag_fields'

label Typical value
Favorite people Links to other users
str_prof_what_i_like Links to search/people/whatilike tags
str_prof_what_i_pound_on Links to search/people/whatido tags
str_prof_what_im_looking_for Links to search/people/lookinfor

Other fields that may be available directly off the record:

  • user_avatar_url
  • user_date_format
  • user_emailurl
  • user_homepage
  • user_id
  • user_name
  • user_num_posts
  • user_num_remixed
  • user_num_remixes
  • user_num_reviewed
  • user_num_reviews
  • user_num_uploads
  • user_real_name

You can use these to build up new versions of user fields:

 $user_row['user_fields'][] = array(
              'label' => 'Posting stats for ' . $user_row['user_real_name'],
              'value' => $user_row['user_num_reviews'] . ' reviews, ' . 
                         $user_row['user_num_posts'] . ' forum posts'
              )

Full Example Code

Put the following into a module at <local_files>/lib/my_user_profile_hook.php

<?

CCEvents::AddHandler( CC_EVENT_FILTER_USER_PROFILE, 'user_profile_filter' );

function user_profile_filter(&$rows)
{
   // Setup some useful variables

   $user_row          =& $rows[0];
   $is_user_logged_in = CCUser::IsLoggedIn();
   $is_current_user   = $is_user_logged_in && (CCUser::CurrentUser() == $user_row['user_id']);

   // If we want to remove a field (like the 
   // 'review' stats) then we have make a copy
   // of the user_fields

   $ok_fields = array();
   
   foreach( $user_row['user_fields'] as $uf )
   {
       if( $uf['label'] == 'str_review_stats' )
           continue;
       $ok_fields[] = $uf;
   }

   // We want to add a special message to everybody but 'joe'

   if( !$is_current_user || ($user_row['user_name'] != 'joe') )
   {
       $ok_fields[] = array(
            'label' => "Special Message",
            'value' => "<h3>It's joe's birthday on Friday!</h3>"
             );
   }

   $user_row['user_fields'] = $ok_fields;
 }

 ?>

Changing the Layout

Setting things up

In order to change the look and layout of the user profile copy ccskins/shared/user_profile.tpl to <local_files>/skins/user_profile.tpl (make sure to keep the same file name!)

If you plan on changing the CSS (likely) copy ccskins/shared/css/user_profile.css to <local_files>/skins/css/user_profile.css (make sure to keep the same file name!)

Before you get too far and in order to fully understand this template it helps to temporarily insert an %inspect% macro after the call to %map% while developing:

 %map(#U,records/0)%
 %inspect(#U)%   
 <link rel="stylesheet" title="Default Style" href="%url(css/user_profile.css)%" />

Now browse to a profile (e.g. people/admin/profile) to see if you've properly hooked the system and view a dump of the user record.

Walking through the template

The template has a couple of 'tricks' to get things laid out in a certain way.


A Quick Hack

This template assumes that the user description is at the top of the array and has an ID of user_description_html

Because of convention in social sites, we want the 'Contact' and 'Member Since' fields to follow directly below that. In order to make sure that happens, the template hacks in two user fields into the second and third slot of the array the line that looks like:

  array_splice( $U['user_fields'], 1, 0, array(  ...<snip>

Printing the user_fields

Once those elements are in, we loop over the 'user_fields' displaying them "as is"

   <div id="user_fields">
       %loop(#U/user_fields,uf)%
           <div class="ufc" %if_attr(#uf/id,id)%><span class="ufc_label">%text(#uf/label)%</span> 
               <div class="ufc_value">%text(#uf/value)%</div></div>
       %end_loop%
   </div>

Printing the user_tag_links

Then we output the 'user_tag_fields' which are tag links for this user. The links URLs (HREF value) and text are already formatted, we just have to lay out the links in an orderly fashion. The links are 'grouped' so there's an outer loop for the groups:

   <div class="user_tag_links">
       %loop(#U/user_tag_links,groups)%
           <span class="ufc_label">%text(#groups/label)%</span>

And within each group is array of links:

           <div class="ufc_value taglinks">
                   %loop(#groups/value,link)%
                       <a href="%(#link/tagurl)%">%(#link/tag)%</a>%if_not_last(#link)%, %end_if%
                   %end_loop%
           </div>

Then we close off the outer group loop and DIV:

       %end_loop%
   </div>

Hacking the avatar into the user description

The template then outputs some javascript that will reformat things and 'make things nice.'

If the user has written something about themselves, then we want to put their avatar into the description box and make it round and pretty.

If the user did not fill out a description then we inject the avatar in the top of the profile page.

All of this, of course, is default policy driven lay out code, hopefully your mileage will vary.