Articles and Tutorials

While I have been working on websites for a while, there seems to be no end to my learning. As I push myself to do more and more complex things, I find myself digging into the docs and sometimes even the code as well as getting direct help from others. I would never have gotten as far as I have without the Drupal community.

This "book", a collection of tips, tricks, articles, and full blown tutorials, is my way of giving back to the community that has helped me so much. It is now and always will be a work in progress. As I learn newer and better ways of doing things and as Drupal itself evolves, I will keep adding more and sometimes go back and revise what's already there.

User Profiles

A big part of any social networking site is the user profiles. Profiles let your users get to know each other and put a face on the anonymous internet. They can be basic, with little more than name, join date, and a few facts, or elaborate and complex. It all depends on the needs of your community. While the core profile module works for simple profiles, no one module will create a complex profile like you find on Myspace and Facebook. Instead, we start with a base module and link other contribs to it.

This section starts with the notes I gathered as I decided which base module build my profiles. Then comes the first and second tutorials. These served the community for a long time and are left for historical purposes, but I don't recommend using them anymore. Version 3 has turned into the Advanced Profile module. There will be tutorials to go with it but the bulk of the work will be done for you so the tutorials will focus on explaining what it's doing and giving ideas for enhancing it.

If you have any questions, please use the issue queue or my support forum. Also, I'd prefer it if people link to this node rather than subpages so people following the link always have the most up to date info.

Deciding which modules to use for profiles

[Update July 23, 2008: For those on D6, you might want to read this thread and learn about Content Profile, which is shaping up to be the successor to both bio and nodeprofile.]

So you want to pretty up your user profiles? But what module do you use? As with most things in Drupal, there's a lot of options. Of course, I recommend using Advanced Profile for the overall glue, but you still need some place to store the user profile data. This article will run through the pros and cons of each.

The contenders:

Bio

Usage:
Download, install, and enable the Bio module.
Setup at admin/user/bio
Add CCK fields as needed

Pros

  • Integrates with the user page for both viewing and editing just by checking boxes in settings
  • Uses any node type for the biography or you can extend the built in type with CCK
  • Lets you link to the bio from content without any special coding
  • Automatically enforces one bio per user
  • Let's you choose fields from your bio node to put on the registration screen
  • Designed to be lightweight and simple

Cons:

  • You can have one and only one node per user which is not as flexible as nodeprofile for complex profiles
  • Currently no way to have it auto create the profile node unless you put at least one field on the reg screen, but this may change soon.
  • Doesn't work with some views such as in buddylist so you end up needing to use usernode as well. This may be changing soon as well.

My opinion:
If you only need one node to hold the profile data per user, I reccomend using Bio. It's simple and works well with advanced profile. You can add a lot of other information to the profile panel using advprofile so bio should work for most sites' needs.

Nodeprofile

Usage:
Install nodeprofile, nodefamily, and subform element modules
Edit the content type you want to use for your profile and tell it that it's a node profile
Edit the options on the new nodeprofile tab that appears
More complex usage can be had by having multiple content types for your profile and using pageroute to hook it all together.

Pros:

  • Can have multiple content types that are node profiles which is great for complex profiles or for having different profiles for different roles.
  • Lets you put your node profiles on the user view page
  • Lets you add multiple node profiles to the registration page. This is nice because you can have one of your profile node types show and not others or put them all depending on your needs.
  • Is part of a family of modules including usernode and pageroute that give you a ton of options

Cons:

  • Unlike bio, which lets you totally take over the user page, nodeprofile just adds the profile nodes to what is there. The author considers this a pro but I, personally, don't like the looks of this, so I'm marking it as a con.
  • It's complex. In order to provide the flexibility of having 1 or more profile nodes from multiple content types the setup and code is complicated.
  • You need usernode as well to make it work with some views, which adds another node per user, and views fusion, which lets you merge the usernode view and the nodeprofile view is complex.

My opinion:
Nodeprofile is part of an awesome family of modules that let you do complex user profiles. If you need to get fancy with different profiles for different roles or having a wizard like process for the user to fill out multiple nodes in their profile, then this is what you want.

Core profile module

Usage:
Enable the module (it comes with Drupal)
Configure the profile by adding items in categories. Each category will show up on a separate tab of the edit account page.

Pros:

  • For each field, you can control the visibility with these options:
    • Hidden profile field, only accessible by administrators, modules and themes.
    • Private field, content only available to privileged users.
    • Public field, content shown on profile page but not used on member list pages.
    • Public field, content shown on profile page and on member list pages.
  • It has the ability to set a page title for a field so you can do things such as view everyone who's favourite colour is blue.
  • For each field, you can set whether it shows up on the registration page.
  • Editing and displaying of the profile is right on the account page with no extra work
  • No additional modules to install; just enable it

Cons:

  • Profiles are not nodes, which can limit flexibility, especially with views.
  • According to the Lullabot podcast, the backend is a mess

My opinion:
I only briefly played with this module but it was a lot better than I was expecting. It's simple to use and has some nice options. If you don't need profiles to be nodes, this looks like a solid option. There are a lot of handbook pages dedicated to customizing it and it can do quite a bit more advanced things than I thought. Not working with views is a big drawback but that will be eliminated in D6 with views 2.

More information:
Profile: extending user account information
Customising the user profile layout

Usernode with CCK fields

Usage:
The usernode module can be used to create user profiles by adding fields to it with CCK.

Pros:

  • No need to add extra modules. Just install usernode (and CCK if you don't have that) and add fields
  • Usernodes are automatically created / deleted with users
  • You can use views with your profiles

Cons:

  • If you decide later you'd rather have multiple pages in your profile, you're out of luck
  • No facility for adding the profile fields to the registration form
  • Adding the profile into the user page requires jumping through some hoops

My opinion:
This option is deceptive in that it looks like the best option on the surface if you only need one node profile per user. The trouble is that you are then locked into using the usernode as your profile. If you later decide you want the extra functionality that bio or nodeprofile provide, you're out of luck. The maintainer doesn't reccomend this option and neither do I, but I'm presenting it here for completeness.

Looking to the future

What about D6? There's a possibility that bio and nodeprofile may come together for a new and improved module. Interested? See Profiles as nodes in d6 for the discussion.

If you have comments or suggestions for improving this article, feel free to add a comment. Please do not use the comments for support requests.

Converting from nodeprofile to bio

I decided to convert CRO from nodeprofile to bio. Nodeprofile is nice for really complex profiles with multiple nodes but I didn't need anything that fancy. More important to me was the ability to put individual fields on the registration page.

While nodeprofile just looks at the type to find your nodeprofile node, bio actually stores the uid/nid pair in a table. So that table must be populated. Fortunately, it's pretty easy. Just fire up phpmyadmin or whatever mysql proggie you use and put:

INSERT INTO bio select nid, uid FROM node WHERE type = 'uprofile';

You'll need to change "uprofile" to whatever your user profile node type is. And, of course, this only works if you had just one type for the profile under nodeprofile. If you have more than one, then you need to stick with nodeprofile.

User Profiles Version 1

This is the original tutorial from last May. It has since been replaced by a newer version. See the User Profiles page for details.

Goal: The aim of this tutorial is to walk you through every step you need to create a user profile like this one.

Notes:

  • This tutorial assumes Drupal version 5.x
  • This does not use the core profile module or the bio module
  • The steps assume you want a profile exactly like mine. Most likely you'll want to do different things for your profile. I will try to provide alternates where possible but I'm not going to explore every possible permutation. I'll mark these alternates with [DIY...]
  • This tutorial only discusses the one user = one profile page method. You can get much more advanced using multiple nodes and pageroute, but that's beyond the scope of this tutorial.
  • You will be told to add things to your template.php file. If your theme doesn't have one, create a blank file in your theme with this name. The snippits are shown surrounded by < ? php and ? > (without the spaces) so the codefilter picks them up. You only need one < ? php at the top of your file and do not need to put them around every snippit.

Modules needed:

  • Automatic Nodetitles - Not strictly needed, but it's handy so users don't have to put a title on their profiles and to keep the title consistant. If you don't use this, you'll need to adjust node-uprofile-edit.tpl.php so users can enter a title.
  • Buddylist - Allows users to designate other users as their "buddy". We will show a list of buddies on the profile page.
  • CCK - We need this to create the content type for nodeprofile to use. Also enable the field type modules that come with it as well as content copy.
  • Contemplate - Makes it easier to theme the profile. If you are adept at theming, you can skip this and use .tpl files instead.
  • Date - CCK date field used for birthdate. Also needs the adodb date library if you have people born before 1970. Just unzip it into the date directory.
  • Guestbook - Allows others to comment on a profile in a bit more organized way than simply enabling comments.
  • Link - CCK link field.
  • Node Profile (5.x-1.1 or later) - This is what allows us to make profiles out of nodes.
  • Node Profile Bonus - This has the code for taking over the user view page as well as fixes for the registration page. The plan is to include this with NodeProfile but is available attached to this tutorial for now. Until it's part of the package, you'll need to manually copy nodeprofile_bonus.module and nodeprofile_bonus.info into your nodeprofile module directory.
  • Node Family - Required by Node Profile
  • Panels - We will actually only be using the CSS from this because I suck at CSS layout. If you can handle the CSS on your own, you don't actually need this module for this tutorial.
  • Private Message - We will check if the user allows private messages and add a link to do it if so. If you don't want private messaging on your site, you can skip this. Be sure to take out the reference in the contemplate code.
  • Subform Element - Required by Node Profile
  • Token - Used by auotmatic nodetitles
  • Usernode - This is used for the buddylist view.
  • Views - We will use this for the user's recent posts, the buddy list, and also searching users. Don't forget to enable the views UI module as well.

Step 1 - Getting prepared

  1. Download and install all the above modules.
  2. Set the user access control (?q=admin/user/access) for: buddylist, guestbook, privatemsg, user module (access user profiles) like like this.
  3. Configure buddylist (?q=admin/settings/buddylist) and privatmsg (?q=admin/settings/privatemsg) to taste. See guestbook section below before bothering with config.
  4. Set usernodes to not be promoted to front page (?q=admin/content/types/usernode)
  5. Download the file User-Profile-Tutorial.zip attached to this tutorial. This contains as much of my profile as I was able to export to save you time copying and pasting.

Step 2 - Creating and configuring the profile content type
Create the content type (see note at end of "Add the fields" section):

  1. Go to Administer -> Content Management -> Content Types -> [Add Content Type] (?q=admin/content/types/add)
  2. Expand "Automatic title generation". Check the box and put in [author-name]. [DIY: You can choose another token or add text if you prefer a different title for profiles.]
  3. Name: User Profile
  4. Type: uprofile
  5. Description: Whatever you want
  6. Title field label: Leave as title as the user won't see it (unless you've elected to not use auto nodetitles)
  7. Body field label: Empty this out as we don't need the body
  8. Default options: Uncheck promoted to front page
  9. Maximum population: 1
  10. Check "Use this content type as a nodeprofile for users"
  11. Comments / attachments / pms: Disable all of these
  12. Save content type

Add the fields:

  1. Go to Administer -> Content Management -> Content Types -> [Import] (?q=admin/content/types/import)
  2. Content type: User Profile
  3. Paste in the contents of "User-Profile_uprofile-cck-fields.txt" found in the attached zip. Click submit. [DIY - Edit the User Profile content type and manually add all the fields that you want users to enter in their profile.]
  4. Your content type should now look like screenshots/User-Profile-Content-Type.jpg

Note: The reason I had you create the content type first and then import into it is that there seems to be a bug with groups not getting created when you create the content type at the same time. You can get around this by doing the import twice if you don't want to manually create the type.

Fix the fields display:

  1. Edit the User Profile content type you just created and click the tab for "Display Fields" (?q=admin/content/types/uprofile/display)
  2. Change all the aboves for the fields to inlines. This doesn't come across with the importing, unfortunately.
  3. Submit

Set the nodeprofile options:

  1. Edit the User Profile content type you just created and click the tab for "Node profile" (?q=admin/content/types/uprofile/nodeprofile)
  2. Keep unchecked "Integrate this node profile with user categories."
  3. Check "Show this node profile during user registration." [DIY: Leave unchecked if you don't want it on the reg screen]
  4. User page display style: Display it as full node view
  5. Uncheck "Include an edit link to the display"
  6. Weight: 10 (this makes it come under username/email on the registration page but is unneeded otherwise)
  7. Submit

Access control:

  1. Go to Administer > User management > Access control (?q=admin/user/access)
  2. Set the permissions on "create uprofile content" and "edit own uprofile content".

Step 3 - Avoiding naming collisions

DON'T DO THIS STEP!!! If you've already done it, undo it. It breaks usernode!

To avoid having your usernode and nodeprofiles having the same titles, edit the usernode content type (?q=admin/content/types/usernode) and change the autotitle token to something else. I used "Usernode of [author-name]". The user will never see this, but it's nice to make it clear to admins which is which.

Step 4 - Take over the user account view tab
By default, the nodeprofile module will put your user profile as a section on the user account view tab (located at /user/#). This is fine for simple profiles but doesn't look right for a full featured profile. [See screenshots/User-Account-Default-View.jpg]

This step makes the user profile take over the entire view tab. The downside is that you will lose anything that other modules add to this page. If you add a module that normally has a link on the user view page, make sure you provide alternate access to it. If you are fine with the way it displays by default, you can skip this step.

  1. Make sure the node_profile_bonus module is enabled. This has code for removing everything that's not a profile from the page and also adds an "Edit Profile" tab to the user account page for easy access.
  2. If you want to change your Edit tab to Edit Account to make things clearer, you can add the uncommented code into your template.php. If you don't already have this function, add in the commented code as well (uncomment it). Don't forget that you only need one < ? php (without spaces) at the top of the file and not around each snippit.

    <?php
    //function _phptemplate_variables($hook, $vars) {
    //  if ($hook == 'page') {
       
    if (arg(0) == 'user') {
         
    $vars['tabs'] = str_replace('Edit</a>', 'Edit Account</a>', $vars['tabs']);
        }
    //    return $vars;
    //  }
    //}
    ?>

  3. Add this code to your template.php:
    <?php
    // Override the display of the nodeprofile to just show the node itself and not the surrounding markup or the tabs.
    // Theming of the nodeprofile will be handled separately. Note that this affects it wherever it is displayed and
    // not only on the user page.
    function phptemplate_nodeprofile_display_box($element, $content) {
     
    $head = isset($element['#title']) ? '<h2 class="nodeprofile-title">'. check_plain($element['#title']) .'</h2>' : '';

      return
    $content;
    }

    // Override the entire user account view page to not show the categories or to seperate items into divs.
    // Note that this is set up to still show everything that is thrown on this page. Getting rid of non-nodeprofile
    // sections is done in the node_profile_bonus module by unsetting them but could also be done here by conditionally
    // printing them to begin with.
    function phptemplate_user_profile($account, $fields) {
      foreach (
    $fields as $category => $items) {
        foreach (
    $items as $item) {
         
    $output .= $item['value'] ;
        }
      }

     
    // Returning nothing from this function causes the entire page to whitescreen, so let's put something in by default
      // A better idea might be to load up some sort of standard thing that has the views and just not the profile fields
     
    if (empty($output)) {
       
    $output = "This user has not filled out a user profile.";
      }

      return
    $output;
    }
    ?>

Your user account page (?q=user/1) should now look like this: screenshots/User-Account-After-Page-Take-Over.jpg

Step 5 - Taxonomy
My profile uses taxonomy for some of the fields. I did this so you can click on a term and see all the other users with that term. With some coding, the taxonomy is used as though it was a normal CCK field.

Go to Administer > Content management > Categories (?q=admin/content/taxonomy). Create the following vocabularies: Communities, Hobbies, Reading, Music, TV and Movies. Communities is a multi select with the terms entered in via add term. The others are freetagging. Note: If you add these in a different order or choose different vocabularies, you will need to alter the contemplate code and the input form to match.

[DIY - Most people reading this are not going to want preset communities. Feel free to skip that vocab and add it as a normal CCK text field if you want and I'll make notes where you need to delete references to it later.]

Attach each of these vocabularies to your User Profile content type.

Step 6 - Add the views

  1. Go to Administer > Site building > Views > [Import] (?q=admin/build/views/import)
  2. Import the contents of User-Profile_User-tracker-view.txt. Submit and save the view. This view will display all the posts by a particular user.
  3. Import the contents of User-Profile_Buddylist-view.txt. Change the filter for node type to usernode. I don't know why this doesn't import correctly. Submit and save the view. This view will display the photo and name of all the user's buddies from the buddylist module.
  4. To theme the buddylist view so that we get rid of the list markup, add this to your template.php.
    <?php
    function phptemplate_views_view_list_buddylist_of_uid($view, $nodes, $type) {
     
    $fields = _views_get_fields();

     
    $taken = array();

     
    // Set up the fields in nicely named chunks.
     
    foreach ($view->field as $id => $field) {
       
    $field_name = $field['field'];
        if (isset(
    $taken[$field_name])) {
         
    $field_name = $field['queryname'];
        }
       
    $taken[$field_name] = true;
       
    $field_names[$id] = $field_name;
      }

     
    // Set up some variables that won't change.
     
    $base_vars = array(
       
    'view' => $view,
       
    'view_type' => $type,
      );

      foreach (
    $nodes as $i => $node) {
       
    $vars = $base_vars;
       
    $vars['node'] = $node;
       
    $vars['count'] = $i;
       
    $vars['stripe'] = $i % 2 ? 'even' : 'odd';
        foreach (
    $view->field as $id => $field) {
         
    $name = $field_names[$id];
         
    $vars[$name] = views_theme_field('views_handle_field', $field['queryname'], $fields, $field, $node, $view);
          if (isset(
    $field['label'])) {
           
    $vars[$name . '_label'] = $field['label'];
          }
        }
       
    $items[] = _phptemplate_callback('views-list-buddylist_of_uid', $vars);
      }
      if (
    $items) {
     
    //MAC Instead of returning an item list, we just seperate with spaces
      //return theme('item_list', $items);
     
    return implode(" ",$items);
      }
    }
    ?>
  5. Copy views-list-buddylist_of_uid.tpl.php to your theme directory. This formats the view to just show photo and name with no extra divs. The end result of this, the step above, and the CSS that will be added later is a mini thumbnail gallery of buddies.

A screenshot isn't possible at this point because these additions won't show up on the profile page until you add the contemplate templates.

Step 7 - Add the guestbook
The guestbook has been tweaked quite a bit to work as a commenting system on the profile page. This has made some of the options on the guestbook module non functional:

  • Anonymous users are not allowed to add entries.
  • Comments are turned off. (Instead, it's been modded so the user can post to his/her own guestbook.)
  • The pager is hard coded in the .tpl and ignores the options.
  • The add comment form is always on the page and not just linked to.

Directions:

  1. Copy guestbook.tpl.php to your theme dir. This controls the overall display of the guestbook section
  2. Copy guestbook-entry.tpl.php to your theme dir. This controls the display of a single guestbook entry
  3. Copy guestbook-form-entry-form.tpl.php to your theme dir. This controls whether the entry form or a note is displayed
  4. Add this code to your template.php:

    <?php
    // These overrides load custom .tpl files to handle the guestbook display as a whole, a single entry display, and the input form
    function phptemplate_guestbook($uid, $entries, $comment_entry, $limit = 20) {
      return
    _phptemplate_callback('guestbook', array('uid'=>$uid, 'entries'=>$entries, 'comment_entry' => $comment_entry, 'limit' => 20));
    }

    function
    phptemplate_guestbook_entry($uid, $entry, $comment_entry = NULL, $confirm_delete = false) {
      return
    _phptemplate_callback('guestbook-entry', array('uid'=>$uid, 'entry'=>$entry, 'comment_entry' => NULL, 'confirm_delete' => false));
    }

    function
    phptemplate_guestbook_form_entry_form($form) {
      return
    _phptemplate_callback('guestbook-form-entry-form', array('form'=>$form));
    }
    ?>

  5. While modifying module code is not recommended, there were a few changes I couldn't find a better way to do. I've put my modified version of guestbook.module into the zip file. You will need to overwrite the stock guestbook.module with this. All my changes are marked with //MAC and commented. If a new version of guestbook comes out, these changes will need to be merged in. That's why modifying modules is a bad idea. If anyone has a better way of doing it, I'd love to hear it. These are the changes:
    • Added a separate query so the guestbook on the profile page shows the last 5 entries with newest on top and the regular guestbook page shows them with oldest on top.
    • Removed the title from the text area on the add entry form as it was redundant.
    • Allowed users to post on their own guestbook
    • Changed the link on the poster's name/photo to go to their profile rather than their guestbook.

A screenshot isn't possible at this point because these additions won't show up on the profile page until you add the contemplate templates.

Step 8 - Theming the profile
I use the contemplate module to make theming the CCK nodes easier. If you are proficient in theming, you can use .tpl files instead.

  1. Go to Administer > Content management > Content templates and click on "Create Template" for User Profile. (?q=admin/content/templates/uprofile)
  2. Check "Affect teaser output" and paste the contents of User-Profile_Contemplate-teaser.txt into the "Teaser Template" box.
  3. Check "Affect body output" and paste the contents of User-Profile_Contemplate-full-node.txt into the "Body Template" box.
  4. If you are not using clean URLs, you will have to adjust the links for the taxonomy terms.
  5. Correct the path, if needed, in this line right at the top: drupal_add_css("sites/all/modules/panels/layouts/twocol_stacked.css");
  6. Submit to save the changes.

Your account view page should now look like screenshots/User-Account-Before-CSS.jpg

Get rid of submitted by / time on profile:
Note:You can skip this if you use the provided node-uprofile.tpl.php since it doesn't print the post info.

  1. Go to Administer > Site building > Themes > Configure > General Settings (?q=admin/build/themes/settings)
  2. Uncheck "Display post information on" for User Profile
  3. Save Configuration

Other theming:

  1. The CSS used for the user profile can be found in User-Profile-CSS.txt. Paste the contents of that file into your theme's .css file and edit to taste.
  2. Copy the "profile" directory into the "images" directory of your theme. This contains icons used by the contemplate code.
  3. Copy the node-uprofile.tpl.php to your theme directory for a stripped down node display that just shows the content.

Step 9 - Theme the input form
The default input form you get after adding all the fields and taxonomy to your usernode can be overwhelming, so I moved things around and simplified them. If you want to use my simplified edit form:

  1. Add node-uprofile-edit.tpl.php to your theme directory.
  2. Add this code to your template.php to call it:

    <?php
    function phptemplate_node_form($form) {
       if (
    $form['#node']->type == 'uprofile') {
         return
    _phptemplate_callback('node-uprofile-edit', array('form' => $form));
      }
      else {
         return
    theme_node_form($form);
      }
    }
    ?>

  3. Because the taxonomy terms are given by number instead of name, you may need to adjust this file if the terms don't show up as they should.
  4. If you opted to not make a term for community, leave out this part:
    print "Which community do you live in? If you don't live in the area, you can leave this blank.";
    print drupal_render($form['taxonomy']['1']);

Step 10 - Searchable view of profiles

  1. Go to Administer > Site building > Views > [Import] (?q=admin/build/views/import)
  2. Import the contents of User-Profile_User-listing-view.txt. Submit and save the view. This view will display a filterable list of all users with profiles.
  3. If your taxonomy is not set up as Communities, Hobbies, Reading, Music, TV and Movies numbered 1-5, the view will complain. You'll have to edit the view to change the numbers before importing.

Note: - If you want to display all users regardless of whether they have profiles, you will need to get the views_fusion module. There are instructions on drupal.org on how to use it but, honestly, I couldn't figure it out. Since I have the nodeprofile on the registration page, all users will have profiles, so I elected to take the easy way out and just do a view on nodeprofiles.

Step 11 - Known issues

  • Admin created accounts don't work right
  • After adding / editing nodeprofile, it sends you back to the nodeprofile node, not the user screen
  • User listing view sends you to the nodeprofile node

Step 12 - Further plans

  • There's a div set aside for the user's gallery. At this point, I haven't added image handling to my site, so I haven't done this section of the profile. It should just be a matter of inserting a view there, though, or perhaps a call to one of the image handling module's display.
  • Selectable color scheme. I was thinking it would be nice to have a few different CSS options for users. Not quite on the level of MySpace but maybe allow them to choose from a pre-defined list. This could be done by adding another field to the nodeprofile and a little code to load CSS based on the value.
  • Limit viewing to people on buddy list. I'd like to give users the option to hide their profile information from anyone who's not on their buddylist. This would work in conjunction with setting the buddylist option to make people get approval before adding someone as a buddy. I haven't looked into the best way to do this, but it shouldn't be too hard to determine if the logged in user is a buddy and use if tests around the printing of the fields to control the display.

User Profiles Version 2

As of February 6, 2008 I am marking this tutorial as deprecated. You can do everything in here with Advanced Profile faster and easier. It's still in alpha but is less buggy than this nearly year old tutorial. It's had a good run and made a lot of people happy but it's time to move on. I just don't have the time to support it anymore.

Please don't link to this page; use http://shellmultimedia.com/misc/user-profiles instead.

Goal: The aim of this tutorial is to walk you through every step you need to create a user profile like this one.

Skill level: This is a complex tutorial involving several modules. While I tried to make it as easy to follow as possible, it is not meant for beginners to Drupal and is not a quick drop-in solution. Please read it over and make sure you understand the concepts before attempting it. If you have problems, please use the support forum and I will answer if I am able.

Notes:

  • This tutorial assumes Drupal version 5.x
  • This does not use the core profile module or the bio module
  • The steps assume you want a profile exactly like mine. Most likely you'll want to do different things for your profile. I will try to provide alternates where possible but I'm not going to explore every possible permutation. I'll mark these alternates with [DIY...]
  • This tutorial only discusses the one user = one profile page method. You can get much more advanced using multiple nodes and pageroute, but that's beyond the scope of this tutorial.
  • You will be told to add things to your template.php file. If your theme doesn't have one, create a blank file in your theme with this name. The snippits are shown surrounded by < ? php and ? > (without the spaces) so the codefilter picks them up. You only need one < ? php at the top of your file and should not put them around every snippit.
  • If you are using subthemes, put the .tpl.php files in your subdirectory.

Modules needed:

  • Automatic Nodetitles - Not strictly needed, but it's handy so users don't have to put a title on their profiles and to keep the title consistant. If you don't use this, you'll need to adjust node-uprofile-edit.tpl.php so users can enter a title.
  • Buddylist - Allows users to designate other users as their "buddy". We will show a list of buddies on the profile page. If you don't use buddylist, take out the references to it. Unfortunately, I didn't do a check for the module so you will have problems if you don't remove the references yourself.
  • CCK - We need this to create the content type for nodeprofile to use. Also enable the field type modules that come with it as well as content copy.
  • Date - CCK date field used for birthdate. Also needs the adodb date library if you have people born before 1970. Just unzip it into the date directory.
  • Guestbook - Allows others to comment on a profile in a bit more organized way than simply enabling comments.
  • Link - CCK link field.
  • Node Profile (5.x-1.1 or later) - This is what allows us to make profiles out of nodes.
  • Node Profile Bonus - This has the code for taking over the user view page as well as fixes for the registration page. This is included in the zip and does not come with the nodeprofile module. You'll need to manually copy nodeprofile_bonus.module and nodeprofile_bonus.info into your nodeprofile module directory.
  • Node Family - Required by Node Profile
  • Private Message - We will check if the user allows private messages and add a link to do it if so. If you don't want private messaging on your site, you can skip this. Be sure to take out the reference in the node-uprofile.tpl.php file. Note: Prvatemsg has some new dependencies since this was written so get those as well.
  • Subform Element - Required by Node Profile
  • Token - Used by auotmatic nodetitles. WARNING: some versions of Token cause a WSOD when used with Link. If you are getting strange WSOD problems, try the dev version of Token.
  • Usernode - This is just used for the buddylist view.
  • Views - We will use this for the user's recent posts, the buddy list, and also searching users. Don't forget to enable the views UI module as well.

Step 1 - Getting prepared

  1. Download and install all the above modules.
  2. Set the user access control (?q=admin/user/access) for: buddylist, guestbook, privatemsg, user module (access user profiles) like like this. Note that you won't have permissions for the uprofile content until you create it in step 2.
  3. Configure buddylist (?q=admin/settings/buddylist) and privatmsg (?q=admin/settings/privatemsg) to taste. See guestbook section below before bothering with config.
  4. Set usernodes to not be promoted to front page (?q=admin/content/types/usernode)
  5. Download the file User-Profile-Tutorial.zip attached to this tutorial. This contains as much of my profile as I was able to export to save you time copying and pasting.

Step 2 - Creating and configuring the profile content type
Create the content type (see note at end of "Add the fields" section):

  1. Go to Administer -> Content Management -> Content Types -> [Add Content Type] (?q=admin/content/types/add)
  2. Name: User Profile
  3. Type: uprofile [DIY: If you name this something else, you will need to change it a few of the tutorial files or things will break.]
  4. Description: Whatever you want
  5. Title field label: If you're using auto_nodetitles this will be hidden.
  6. Body field label: Empty this out as we don't need the body
  7. Default options: Uncheck promoted to front page
  8. Maximum population: 1
  9. Check "Use this content type as a nodeprofile for users"
  10. Comments / attachments / pms: Disable all of these
  11. Save content type

Add the fields:

  1. Go to Administer -> Content Management -> Content Types -> [Import] (?q=admin/content/types/import)
  2. Content type: User Profile
  3. Paste in the contents of "User-Profile_uprofile-cck-fields.txt" found in the attached zip. Click submit. [DIY - Edit the User Profile content type and manually add all the fields that you want users to enter in their profile.]
  4. Your content type should now look like screenshots/User-Profile-Content-Type.jpg

Note: The reason I had you create the content type first and then import into it is that there seems to be a bug with groups not getting created when you create the content type at the same time. You can get around this by doing the import twice if you don't want to manually create the type.

Fix the fields display:

  1. Edit the User Profile content type you just created and click the tab for "Display Fields" (?q=admin/content/types/uprofile/display)
  2. Change all the labels for the fields to "inline". The groups can stay set to "above". This doesn't come across with the importing, unfortunately.
  3. Submit

Set the nodeprofile options:

  1. Edit the User Profile content type you just created and click the tab for "Node profile" (?q=admin/content/types/uprofile/nodeprofile) This tab is added by the nodeprofile module. If you don't see it, make sure you checked the box to make this type a nodeprofile node. Also make sure you have the latest version of nodeprofile as it's a new feature.
  2. Keep unchecked "Integrate this node profile with user categories."
  3. Check "Show this node profile during user registration." [DIY: Leave unchecked if you don't want it on the reg screen]
  4. User page display style: Display it as full node view
  5. Uncheck "Include an edit link to the display"
  6. Weight: 10 (this makes it come under username/email on the registration page but is unneeded otherwise)
  7. Submit

Access control:

  1. Go to Administer > User management > Access control (?q=admin/user/access)
  2. Set the permissions on "create uprofile content" and "edit own uprofile content".

Step 3 - Take over the user account view tab
By default, the nodeprofile module will put your user profile as a section on the user account view tab (located at /user/#). This is fine for simple profiles but doesn't look right for a full featured profile. [See screenshots/User-Account-Default-View.jpg]

This step makes the user profile take over the entire view tab. The downside is that you will lose anything that other modules add to this page. If you add a module that normally has a link on the user view page, make sure you provide alternate access to it. If you are fine with the way it displays by default, you can skip this step.

  1. Make sure the node_profile_bonus module is enabled. This has code for removing everything that's not a profile from the page and also adds an "Edit Profile" tab to the user account page for easy access.
  2. If you want to change your Edit tab to Edit Account to make things clearer, you need to add this code to your template.php:

    <?php
       
    if (arg(0) == 'user') {
         
    $vars['tabs'] = str_replace('Edit</a>', 'Edit Account</a>', $vars['tabs']);
        }
    ?>

    Adding this code is tricky because it needs to go in the _phptemplate_variables function. If you don't already have this function in your template.php, you can just add the entire function:

    <?php
    function _phptemplate_variables($hook, $vars) {
      if (
    $hook == 'page') {
        if (
    arg(0) == 'user') {
         
    $vars['tabs'] = str_replace('Edit</a>', 'Edit Account</a>', $vars['tabs']);
        }
        return
    $vars;
      }
    }
    ?>

    If you already have the function, you will need to merge it. The line "if ($hook == 'page') {" will likely already be there. So just put the code after that as it is in the first snippit.

  3. Add this code to your template.php:
    <?php
    // Override the display of the nodeprofile to just show the node itself and not the surrounding markup or the tabs.
    // Theming of the nodeprofile will be handled separately. Note that this affects it wherever it is displayed and
    // not only on the user page.
    function phptemplate_nodeprofile_display_box($element, $content) {
     
    $head = isset($element['#title']) ? '<h2 class="nodeprofile-title">'. check_plain($element['#title']) .'</h2>' : '';

      return
    $content;
    }

    // Override the entire user account view page to not show the categories or to seperate items into divs.
    // Note that this is set up to still show everything that is thrown on this page. Getting rid of non-nodeprofile
    // sections is done in the node_profile_bonus module by unsetting them but could also be done here by conditionally
    // printing them to begin with.
    function phptemplate_user_profile($account, $fields) {
      foreach (
    $fields as $category => $items) {
        foreach (
    $items as $item) {
         
    $output .= $item['value'] ;
        }
      }

     
    // Returning nothing from this function causes the entire page to whitescreen, so let's put something in by default
      // A better idea might be to load up some sort of standard thing that has the views and just not the profile fields
     
    if (empty($output)) {
       
    $output = "This user has not filled out a user profile.";
      }

      return
    $output;
    }
    ?>

Your user account page (?q=user/1) should now look like this: screenshots/User-Account-After-Page-Take-Over.jpg

Step 4 - Taxonomy
My profile uses taxonomy for some of the fields. I did this so you can click on a term and see all the other users with that term. With some coding, the taxonomy is used as though it was a normal CCK field.

Go to Administer > Content management > Categories (?q=admin/content/taxonomy). Create the following vocabularies: Communities, Hobbies, Reading, Music, TV and Movies. Communities is a multi select with the terms entered in via add term. The others are freetagging. Note: If you add these in a different order or choose different vocabularies, you will need to alter the node-uprofile.tpl.php code and the input form to match.

[DIY - Most people reading this are not going to want preset communities. Feel free to skip that vocab and add it as a normal CCK text field if you want and I'll make notes where you need to delete references to it later.]

Attach each of these vocabularies to your User Profile content type.

Step 5 - Add the views

  1. Go to Administer > Site building > Views > [Import] (?q=admin/build/views/import)
  2. Import the contents of User-Profile_User-tracker-view.txt. Submit and save the view. This view will display all the posts by a particular user.
  3. Import the contents of User-Profile_Buddylist-view.txt. Change the filter for node type to usernode. I don't know why this doesn't import correctly. Submit and save the view. This view will display the photo and name of all the user's buddies from the buddylist module. Make sure that you have a default user photo set or you will get errors if the user doesn't have a photo.
  4. To theme the buddylist view so that we get rid of the list markup, add this to your template.php. This code is all stock except for what is being returned at the end.
    <?php
    function phptemplate_views_view_list_buddylist_of_uid($view, $nodes, $type) {
     
    $fields = _views_get_fields();

     
    $taken = array();

     
    // Set up the fields in nicely named chunks.
     
    foreach ($view->field as $id => $field) {
       
    $field_name = $field['field'];
        if (isset(
    $taken[$field_name])) {
         
    $field_name = $field['queryname'];
        }
       
    $taken[$field_name] = true;
       
    $field_names[$id] = $field_name;
      }

     
    // Set up some variables that won't change.
     
    $base_vars = array(
       
    'view' => $view,
       
    'view_type' => $type,
      );

      foreach (
    $nodes as $i => $node) {
       
    $vars = $base_vars;
       
    $vars['node'] = $node;
       
    $vars['count'] = $i;
       
    $vars['stripe'] = $i % 2 ? 'even' : 'odd';
        foreach (
    $view->field as $id => $field) {
         
    $name = $field_names[$id];
         
    $vars[$name] = views_theme_field('views_handle_field', $field['queryname'], $fields, $field, $node, $view);
          if (isset(
    $field['label'])) {
           
    $vars[$name . '_label'] = $field['label'];
          }
        }
       
    $items[] = _phptemplate_callback('views-list-buddylist_of_uid', $vars);
      }
      if (
    $items) {
        return
    implode(" ",$items);
      }
    }
    ?>
  5. Copy views-list-buddylist_of_uid.tpl.php to your theme directory. This formats the view to just show photo and name with no extra divs. The end result of this, the step above, and the CSS that will be added later is a mini thumbnail gallery of buddies.

A screenshot isn't possible at this point because these additions won't show up on the profile page until you get to the theming section.

Step 6 - Add the guestbook
Note: Advanced Profile doesnot use the guestbook module and I don't plan on trying to convert guestbook entries. If you plan on converting to advprofile, I reccomend leaving this step out.

The guestbook has been tweaked quite a bit to work as a commenting system on the profile page. This has made some of the options on the guestbook module non functional:

  • Anonymous users are not allowed to add entries.
  • Comments are turned off. (Instead, it's been modded so the user can post to his/her own guestbook.)
  • The pager is hard coded in the .tpl and ignores the options.
  • The add comment form is always on the page and not just linked to.

Directions:

  1. Copy guestbook.tpl.php to your theme dir. This controls the overall display of the guestbook section
  2. Copy guestbook-entry.tpl.php to your theme dir. This controls the display of a single guestbook entry
  3. Copy guestbook-form-entry-form.tpl.php to your theme dir. This controls whether the entry form or a note is displayed
  4. Add this code to your template.php:

    <?php
    // These overrides load custom .tpl files to handle the guestbook display as a whole, a single entry display, and the input form
    function phptemplate_guestbook($uid, $entries, $comment_entry, $limit = 20) {
      return
    _phptemplate_callback('guestbook', array('uid'=>$uid, 'entries'=>$entries, 'comment_entry' => $comment_entry, 'limit' => 20));
    }

    function
    phptemplate_guestbook_entry($uid, $entry, $comment_entry = NULL, $confirm_delete = false) {
      return
    _phptemplate_callback('guestbook-entry', array('uid'=>$uid, 'entry'=>$entry, 'comment_entry' => NULL, 'confirm_delete' => false));
    }

    function
    phptemplate_guestbook_form_entry_form($form) {
      return
    _phptemplate_callback('guestbook-form-entry-form', array('form'=>$form));
    }
    ?>

  5. While modifying module code is not recommended, there were a few changes I couldn't find a better way to do. I've put my modified version of guestbook.module into the zip file. You will need to overwrite the stock guestbook.module with this. All my changes are marked with //MAC and commented. If a new version of guestbook comes out, these changes will need to be merged in. That's why modifying modules is a bad idea. If anyone has a better way of doing it, I'd love to hear it. These are the changes:
    • Added a separate query so the guestbook on the profile page shows the last 5 entries with newest on top and the regular guestbook page shows them with oldest on top.
    • Removed the title from the text area on the add entry form as it was redundant.
    • Allowed users to post on their own guestbook
    • Changed the link on the poster's name/photo to go to their profile rather than their guestbook.

    I've also included a .patch file for those who prefer to make the changes to the module that way.

A screenshot isn't possible at this point because these additions won't show up on the profile page until you get to the theming section.

Step 7 - User gallery
The user gallery section depends on how you handle image galleries on your site and is beyond the scope of this tutorial. On my site, I used the following tutorial to make galleries: http://drupal.org/node/144725 . If you do it that way, you can use the view I included for a mini user gallery. To use this view, uncomment these lines in node-uprofile.tpl.php:

// $view = views_get_view('gallery_user');
// print views_build_view('embed', $view, array($profileuser->uid), false, false);

Then import the view found in User-Profile_Views_User-gallery.txt.

Other suggestions can be found in the comments here and here.

Step 8 - Theming the profile

  1. Copy node-uprofile.tpl.php to your theme directory. This contains all the code and fields for the user profile page and the teaser. Your account view page should now look like screenshots/User-Account-Before-CSS.jpg
  2. The CSS used for the user profile can be found in User-Profile-CSS.txt. Paste the contents of that file into your theme's .css file and edit to taste.
  3. Copy the "profile" directory into the "images" directory of your theme. This contains icons used by node-uprofile.tpl.php.

Step 9 - Theme the input form
The default input form you get after adding all the fields and taxonomy to your usernode can be overwhelming, so I moved things around and simplified them. If you want to use my simplified edit form:

  1. Add node-uprofile-edit.tpl.php to your theme directory.
  2. Add this code to your template.php to call it:

    <?php
    function phptemplate_node_form($form) {
       if (
    $form['#node']->type == 'uprofile') {
         return
    _phptemplate_callback('node-uprofile-edit', array('form' => $form));
      }
      else {
         return
    theme_node_form($form);
      }
    }
    ?>

  3. Because the taxonomy terms are given by number instead of name, you may need to adjust this file if the terms don't show up as they should. For example, print drupal_render($form['taxonomy']['tags']['2']) ; will print the textbox to put in terms for vocabulary ID #2. If that's the wrong vocabulary, change the "2" to the right number.
  4. If you opted to not make a term for community, leave out this part:
    print "Which community do you live in? If you don't live in the area, you can leave this blank.";
    print drupal_render($form['taxonomy']['1']);

Step 10 - Searchable view of profiles

  1. Go to Administer > Site building > Views > [Import] (?q=admin/build/views/import)
  2. Import the contents of User-Profile_User-listing-view.txt. Submit and save the view. This view will display a filterable list of all users with profiles.
  3. If your taxonomy is not set up as Communities, Hobbies, Reading, Music, TV and Movies numbered 1-5, the view will complain. You'll have to edit the view to change the numbers before importing.

Note: - If you want to display all users regardless of whether they have profiles, you will need to get the views_fusion module. There are instructions on drupal.org on how to use it but, honestly, I couldn't figure it out. Since I have the nodeprofile on the registration page, all users will have profiles, so I elected to take the easy way out and just do a view on nodeprofiles.

Step 11 - Redirect usernode / nodeprofile
One annoyance with having usernode and nodeprofile and the user page is you end up with paths going 3 different places. If you use the included node-usernode.tpl.php, it has a 301 redirect to the user page. node-uprofile.tpl.php also has this at the top. So anything that sends you to either the usernode or the nodeprofile node will zap you over to the user account page instead. Since it uses a 301, it's SEO friendly. There's probably better ways of doing it, but this is a nice global fix so I didn't have to worry about hunting down every link and fixing it.

Step 12 - Further plans

  • Selectable color scheme. I was thinking it would be nice to have a few different CSS options for users. Not quite on the level of MySpace but maybe allow them to choose from a pre-defined list. This could be done by adding another field to the nodeprofile and a little code to load CSS based on the value.
  • Limit viewing to people on buddy list. I'd like to give users the option to hide their profile information from anyone who's not on their buddylist. This would work in conjunction with setting the buddylist option to make people get approval before adding someone as a buddy. I haven't looked into the best way to do this, but it shouldn't be too hard to determine if the logged in user is a buddy and use if tests around the printing of the fields to control the display.

User Profiles Version 3 - Advanced Profile

User Profiles Version 3 is no longer just a tutorial. I have taken as much as I could of version 2 and put it into the Advanced Profile module. By using Panels 2 as a base, I can provide a base to build on and a bunch of building blocks which non programmers can use to put together a customized user profile. It's still a little bit of work to set up but is much simpler than the tutorial and also much more stable. And it's tons more flexible. It works with either bio or nodeprofile and you can even skip the whole profiles as nodes and use the core profile, though you do lose a lot of functionality that way. You can add as much or as little as you like to make it work with your site.

The module is still being developed and more features are being added but it is quite usable as it is. I'm reasonably confident reccomending it for use on live sites, though you should test it out on a dev copy first just to be sure. The really awesome thing about basing this on panels is that it is fully exportable. You can set it all up on your dev site and just export it to be used on your live site. That's actually how much of the module was built; by exporting the panels I put together.

You can find the module on the project page and the documentation for getting all set up in the Drupal handbook. More advanced tutorials for customizing your profiles will be here in my handbook when I get time to write them.

If you have any issues with the module, please use the issue queue. It's much easier for me to track them and that's also where other people having issues will look for answers.

Enjoy!

Converting from user profiles tutorial to advanced profile

I've started converting Coulee Region ONLINE, which was the basis of the user profile tutorials, to using advanced profile. So I have some preliminary docs for converting from the tutorial:

  1. Take your site offline as this will make a temporary mess
  2. Disable nodeprofile bonus (this won't be needed anymore so can be deleted as well if you want)
  3. In template.php, remove:
  4. - function phptemplate_views_view_list_buddylist_of_uid
  5. - function phptemplate_nodeprofile_display_box
  6. - function phptemplate_user_profile
  7. Add "basic info" group to your uprofile node type
  8. Move the fields that aren't already in a group into "basic info"
  9. Edit the user profile panel page. Remove the existing guestbook content at the bottom. Add custom content and put in this code:
    <?php
    print guestbook_page(arg(1));
    ?>
  10. Follow the install directions of Advanced Profile including the instructions for using with nodeprofile. You will not need to generate the node type as you already have one.

All that's left is interests as taxonomy. The way I did it in the tutorial is really hacky so I'm looking at a better method possibly using the content taxonomy module.

Advanced Forums

I tweaked the forums on CRO quite a bit but it's been too much of a mess of hacks to share. When I decided to copy my changes to the forums here, I realized it was time to clean it up and make it portable. Which has spawned a new module.

You can read the discussion of it here: http://groups.drupal.org/node/7064

This is not even remotely ready for production use; there is still a huge amount of work to be done. I'll be making a project on drupal.org soonish but you can find a very early sneak preview attached.

AttachmentSize
advanced_forum_0711121730.zip8.44 KB

Tips and tricks

This will be a section for short bits of stuff that others may find useful.

How to move the comments on node display

Now that Fivestar has the ability to add rating to comments, I decided to switch from the way I was doing rate and review to using comments for reviews. But I have my nodes heavily themed including parcelling out bits into tabs and reviews are one of those bits. I didn't want them stuck down where the comments normally go.

Searching brought me to this thread on drupal.org which claimed it was impossible without hacking core. Well, I've gone 2.5 years without hacking core and wasn't about to start. After some discussion on IRC, litwol came up with the idea of clearing out $node->comment to keep the core function from doing its thing. That was the bit I needed to make this work.

So, to move the comments, just put this snippit where you want the comments to show up in your node-TYPE.tpl.php:

<?php
if (function_exists('comment_render') && $node->comment) {
   print
comment_render($node, $node->cid);
  
$node->comment = NULL;
}
?>

How to tell if there are nodes on a page

On my CRO site, I wanted to have a border around pages that have no nodes on them. This makes them match with the pages that are just one node. But I went nuts trying to find a way, in page.tpl.php, to determine if the page has nodes on it. Telling if it's a single node is easy because $node will be defined but there isn't any way I found to tell, for example, a teaser list view page from a table view page. I want the border around the table view page but not around the teaser view. I spent a lot of time looking through the defined vars and trying this and that and finally hit upon the solution.

merlinofchaos had suggested, when I initially was asking how to tell a teaser view from a table view, to put a global variable in the argument code. Well, that would work, but I didn't want to have to mod every view for this so I kept looking. Then it dawned on me that I can use a similar idea.

So what did I end up doing? I put this in function _phptemplate_variables in template.php:

<?php
   
case 'node':
   
// Set a global variable is the page has one or more node on it
    // Used by the theme to wrap pages with no nodes
   
global $nodeonpage;
   
$nodeonpage = TRUE;
?>

If there's at least one node on the page, it hits that and, bam, global variable set. Then I just need to check if that variable is true in page.tpl.php. Cool beans. :)

Michelle

How to turn off all contrib modules

If your site is totally crashed and you suspect it's a contrib module but don't know which, here's a quick way to turn them all off in the database:

UPDATE `system` SET status=0 WHERE filename LIKE '%sites%'

This simply disables them; it doesn't uninstall them. It also assumes you are putting your contrib under sites\all or sites\sitename.

Michelle

Managing your views in code

Note: This is for Drupal 5.x and views 1.6. You can find a tutorial for Views 2 here.

Panels 2 comes with a nifty exporter that will dump all your panels related stuff (pages, views panes, mini panels, etc) into one export file set up to work as a module. It's a really useful feature for moving them from site to site or simply keeping them in code for outside the database backups.

Views 1, being a much older module, doesn't have quite as nice of an exporter. It does allow you to export your views one at a time but using that export in a module takes a bit more work. This article will show you one way of doing it. There are other ways, such as putting all the views in one file, but I find this method easier to maintain.

Step 1: Create a module.
If you already have a site specific module, you can just use that. If not, you'll need to create a module. It's not hard. All you need is a .info and a .module. You can find more information in this tutorial.

Add this function to the module:

<?php
function MODULENAME_views_default_views() {
 
$views = array();
 
$path = drupal_get_path('module', 'MODULENAME') . '/views';
 
$files = drupal_system_listing('.inc$', $path, 'name', 0);
  foreach(
$files as $file) {
    include_once
$file->filename;
  }
  return
$views;
}
?>

Be sure to replace MODULENAME with the name of your module, in lower case.

Step 2: Create a views directory.
In your module's directory, create a subdirectory called "views". This is where we will be storing the views include files.

Step 3: Export your views.
On the views administration page, every non-default view has an option to export. For each one, export the view and copy the results into its own text file. At the top, add the line <?php so Drupal recognizes it as code. Save each text file as SOMETHING.inc into the views directory from step 2. What you call them doesn't matter, but I recommend using the name of the view to keep them straight.

Step 4: Let views know what you did.
Enable your module if you haven't already done so. On the views admin page is a tab called "tools". Go there and you'll see an option to clear the views cache. Do so. Now go back to the listing page. All the views you exported will be in the "default" views section and labeled overridden. If you don't need to make any more changes to the views, you can safely delete the database version (labeled "existing views") and just run off the ones in the .inc files. The usual disclaimers about backing up your database first apply here. :)

That's it. You now have all your views running off of code. You can take this module to another site, turn it on, and there are your views. (Assuming, of course, that any modules the views rely on are enabled at the new site.)

If you need to change a view, just override it, change it, re-export it, and overwrite the corresponding .inc file.

If you have comments on this technique or you've spotted an error, feel free to leave a message. But please no "how do I make a module" type questions.

Thanks to merlinofchaos for the code to pull in the views as well as for the awesome views module to begin with. :)

Populating a forum with devel

In working on the advanced forum module, I needed a full forum to work on. I used the devel module and ended up with forums full of "this post has been moved". It turns out that devel doesn't fill in forum.tid. So I needed to run this SQL:

update forum
inner join term_node on forum.nid = term_node.nid
set forum.tid = term_node.tid

Unfortunately, the devel module doesn't create the forum structure, so I had to do that part by hand. It also creates posts for forum containers, so I had to go in and clean that out. A bit of a pain, but easier than creating a ton of dummy posts by hand.

Michelle

hook_theme_registry_alter for advanced template control

I'm in the process of porting Advanced Forum to Drupal 6.x and am trying to take advantage of the new theme system. One of the things I wanted to do was get rid of the requirement to copy the forum theme to the site theme directory. My goal was to make it so the whole thing could be run right out of the module directory but allow any template copied to the user's theme to override what's in the module. merlinofchaos helped me quite a bit and told me about hook_theme_registry_alter(), which isn't documented much yet. I decided to write up what I learned today in this article. Keep in mind that this is written by someone who has only really dug into the new theme system in the last few days, but merlinofchaos gave it a look over and he thought it looked ok. If you have comments on the article, I'd like to hear from you, but please no support questions. I barely understand this myself. ;)

---

The theme system in D6 does an excellent job of handling template files which will work well for most people. If you need very specific control over the theme registry, however, there is hook_theme_registry_alter(). This hook lets you get in and modify the registry directly. It has a paramater, $theme_registry, which holds the whole registry. To see what's in it, you can use something like this:

<?php
MODULENAME_theme_registry_alter
($theme_registry) {
 
// Assuming you have the devel module installed
 
dsm($theme_registry);
}
?>

This will give you a nicely formatted listing of the entire array to check out. By modifying this array, you can affect how template files are handled.

Example 1
Let's say you want to provide a node template in your module but you want the user of your module to be able to override that template in their theme. How do you do that? Well, you start out with:

<?php
function MODULENAME_preprocess_node(&$vars) {
  if (
$some_condition_is_met) {
   
$vars['template_files'][] = 'special-template-name';
  }
}
?>

That works great for the special-template-name.tpl in the user's theme directory, but what if they don't want to put it in their theme? How do you get it to default to the one in your module? You might think you can just add in the path to the module before that, but that doesn't work. the template_files variable won't accept paths outside of the theme. So, the trick is to convince the registry that your module is a theme. That's where the hook comes in.

<?php
function MODULENAME_theme_registry_alter($theme_registry) {
 
// Remove the first path under 'node' which is the one for the
  // module that created the template
 
$originalpath = array_shift($theme_registry[$template]['theme paths']);
 
 
// Get the path to your module
 
$modulepath = drupal_get_path('module', 'MODULENAME');
 
 
// Stick the original path and then your module path back on top
 
array_unshift($theme_registry[$template]['theme paths'], $originalpath, $modulepath);
}
?>

Now the theme registry will look first to the user's theme for the template file and then to your module directory.

Example 2
You can use MODULENAME_preprocess_ITEM() to add in variables before they go to the template file. This can be used on your own theme items or existing ones. In the case of existing ones, your variables are merged into the ones made by the original preprocess code. This is a great feature if you just want to add to it. But what if you don't want the other preprocess code to run at all? hook_theme_registry_alter() to the rescue again.

<?php
function MODULENAME_theme_registry_alter($theme_registry) {
  foreach (
$theme_registry['TEMPLATE_TO_OVERRIDE']['preprocess functions'] as $key => $value) {
    if (
$value == 'PREPROCESS_FUNCTION_TO_OVERRIDE') {
      unset(
$theme_registry['TEMPLATE_TO_OVERRIDE']['preprocess functions'][$key]);
    }
  }
?>

By doing a foreach, you wipe out only the preprocess function you don't want to run and leave any others in the chain, such as your own.

Porting themes

This isn't much of an article since I'm not doing any how to. I just wanted to say that porting OSWD themes is amazingly easy. I know sepeck's written on this and didn't expect it to be terribly hard, but I was impressed that it took less than 10 minutes. Of course tweaking is needed to make it look right with all modules and such, but it's not bad for 10 minutes of work.

If you're curious, the original theme is here: http://www.oswd.org/design/preview/id/3628 and my super quickie port is in the attached zip.

Edited to add: A bit more work and it now looks like: http://momsclubofholmen.org/

Michelle

AttachmentSize
beautifulday-port.zip22.38 KB

Area directory

I've had a lot of people asking about the directory I'm working on here: http://couleeregiononline.com/directory . I plan on doing a tutorial but first I need to finish version 3 of user profiles along with all the other stuff I have to do. So it's going to be a while. In the mean time, here's a few pointers to help those who can't wait:

Hopefully that will help a bit until I can get the tutorial written.

Michelle

Rate and review in one step

Oct 31, 2007 - This tutorial works but I've since found a more elegant way of doing it, so I recommend Rate and Review 2.

Purpose: This tutorial will show you one way of allowing users to rate and review a node all in the same form. This isn't the only way of doing it. If you are looking for a more drop in solution, see http://drupal.org/project/userreview . I went this route because I wanted to use fivestar and I already have CCK and views installed so all I needed was prepopulate.
Skill level: This tutorial is not aimed at beginners. You need to be comfortable with creating content types and theming them.
Support: Please use the support forum if you have questions and I will try to answer if I am able.
Modules needed: CCK, Views, Fivestar (as well as dependencies for those modules)

Note: In order for this to work, you need to make the following change to fivestar_field.inc:

Change
$items[$delta]['target'] = drupal eval($item['target']);
to
$items[$delta]['target'] = eval($item['target']);

The reason for this is that the $node object is not in scope in the evaluated PHP when using drupal_eval but it is with just eval. Hacking a module is uncool, so suggestions to get around that fix are welcome.

Step 1 - The review type:

  1. Create a node type named "Review"
  2. Get rid of the body field (makes theming easier)
  3. Do not enable fivestar (we will be adding it as a CCK field instead)
  4. Add the follwing fields:
    • Review - Text area field
    • ParentNID - Integer text field. This is a reference to the node ID of the node that is being reviewed.
    • Rating - Fivestar field
  5. On Manage fields tab, configure the rating field. In the node id text box, put "return $node->field_parentnid[0][value];". Ignore the help text that says to surround it with PHP tags. You must have permission to use PHP for this in access control.
  6. Set access control to give permissions for create/edit of the review type
  7. Set up pathauto (if you use it) for review nodes.

Step 2 - Clean up the form:

If you have a site specific module, add this form_alter code:

<?php
function YOURMODULENAME_form_alter($form_id, &$form) {
  switch (
$form_id) {
   
// Only do this for the review type's form
   
case ('review_node_form'):
     
//If we are adding a review, we need to grab the parent node ID from the URL
     
if (arg(1) == "add") {
       
// Get the parent node's id from the url
       
$pnid = $_GET['pnid'];
     
       
// Set the parentnid field's value
       
$form['field_parentnid'][0]['value']['#value'] = $pnid;
      } else {
       
// Otherwise, grab the existing parent node ID to redirect to
       
$pnid = $form['field_parentnid'][0]['value']['#default_value'];
      }
           
     
// Hide the field
     
$form['field_parentnid'][0]['value']['#access'] = FALSE;
     
     
// On submission, redirect to the parent node
     
$form['#redirect'] = "node/$pnid";
     
     
// This is in case we add more forms later
     
break;
  }
}
?>

If you don't already have one and don't know how to make a module, just use the one in the zip.

Step 3 - Create a view:

  1. Add a new view called "reviews_of_node"
  2. Make it a page view
  3. Have it list teasers or full nodes (your choice)
  4. Add "parentnid" as an argument
  5. Sort by node updated time descending

Step 4 - Create node types that can be reviewed:

  1. Create or edit the node type for which you want reviews enabled
  2. Enable fivestar and set teaser and body both to hidden.
  3. To show the stars, put this in your node-type.tpl.php:
    <?php
           $current_rating
    = votingapi_get_voting_result('node', $nid, 'percent', 'vote', 'average');
          
    $numstars = 5; // Change this to how many stars you want
          
    print theme('fivestar_static', $current_rating->value, $numstars);
         
    ?>
  4. To add link a to add a review, put this in your node-type.tpl.php:
    <?php
    print l("Add a review","node/add/review",NULL,"pnid=" . $node->nid,NULL,NULL,TRUE);
    ?>
  5. To show the reviews, put this in your node-type.tpl.php at the end:
    <?php
     
         
    // Retrieve the view
         
    $view = views_get_view('reviews_of_node');
         
         
    // Build the view into the $reviews variable with paging on and nodes per page set to 20
         
    $reviews = views_build_view('embed', $view, array(strval($node->nid)), true, 20);
         
         
    // Get the number of reviews
         
    global $pager_total_items;
         
    $numreviews = $pager_total_items[0];
         
         
    // Print the reviews
         
    print "Number of reviews: $numreviews";
          print
    $reviews
         
    ?>

Enjoy!

AttachmentSize
rateandreview.zip817 bytes

Rate and review 2

Aug 26, 2008: A recent user of this tutorial wrote some updates here.

Note: This tutorial was written before fivestar gained the ability to use comments for rating. It's still useful if you want your reviews to be nodes.

This is a revised version of Rate and review in one step using the node relativity module. (Thanks to greggles for pointing this module out.)

Purpose: This tutorial will show you one way of allowing users to rate and review a node all in the same form. This isn't the only way of doing it. If you are looking for a more drop in solution, see http://drupal.org/project/userreview .
Skill level: This tutorial is not aimed at beginners. You need to be comfortable with creating content types and theming them.
Support: Please use the support forum if you have questions and I will try to answer if I am able.
Modules needed: CCK, Views, Fivestar, and Node Relativity (as well as dependencies for those modules)

Step 1 - Modify fivestar:

Fivestar uses drupal_eval() to determine the target node for the voting widget. Unfortunately, the $node object is not in scope in drupal_eval() and we need to pull the target from the $node object. The fix is to make the following change to fivestar_field.inc:

Change
$items[$delta]['target'] = drupal eval($item['target']);
to
$items[$delta]['target'] = eval($item['target']);

Hacking a module is uncool, so suggestions to get around that fix are welcome.

Step 2 - Create node type(s) that can be reviewed:

  1. Create or edit the node type for which you want reviews enabled
  2. Enable fivestar and set teaser and body both to hidden.
  3. To show the stars, put this in your node-type.tpl.php:
    <?php
           $current_rating
    = votingapi_get_voting_result('node', $nid, 'percent', 'vote', 'average');
          
    $numstars = 5; // Change this to how many stars you want
          
    print theme('fivestar_static', $current_rating->value, $numstars);
         
    ?>
  4. Node Relativity will automatically add a link to add a child node. If you want to place it where you want, turn that off in settings and put this in your node-type.tpl.php:
    <?php
    print l("node/add/review/parent/$node->nid",NULL,NULL,NULL,FALSE,TRUE);
    ?>
  5. Node Relativity will automatically list the child nodes but you can add this code to your node-type.tpl.php to place them manually:
    <?php
     
         
    // Retrieve the view
         
    $view = views_get_view('reviews_of_node');
         
         
    // Build the view into the $reviews variable with paging on and nodes per page set to 20
         
    $reviews = views_build_view('embed', $view, array(strval($node->nid)), true, 20);
         
         
    // Get the number of reviews
         
    global $pager_total_items;
         
    $numreviews = $pager_total_items[0];
         
         
    // Print the reviews
         
    print "Number of reviews: $numreviews";
          print
    $reviews
         
    ?>

Step 3 - The review type:

  1. Create a node type named "Review"
  2. Get rid of the body field (makes theming easier)
  3. Do not enable fivestar (we will be adding it as a CCK field instead)
  4. Add the follwing fields:
    • Review - Text area field
    • Rating - Fivestar field
  5. On Manage fields tab, configure the rating field. In the node id text box, put "return $node->parent_node;". Ignore the help text that says to surround it with PHP tags. You must have permission to use PHP for this in access control.
  6. Set access control to give permissions for create/edit of the review type
  7. Set up pathauto (if you use it) for review nodes. Note that the relativity module adds tokens to access the parent node in pathauto such as [parent-title]

Step 4 - Create a view:

  1. Add a new view called "reviews_of_node"
  2. Make it a page view
  3. Have it list teasers or full nodes (your choice)
  4. Set empty text to "No reviews found."
  5. Add "Relativity: Parent Node ID" as an argument and use empty text
  6. Sort by node updated time descending

Step 5 - Putting it all together with Node Relativity:

  1. Go to example.com?=admin/settings/relativity [screenshot]
  2. For the question "Which node types should relationships be managed for?", select all node types for which you are enabling reviewing. In the screenshot, you can see we have a node type of "general" that will be reviewed. You will also need to select "Review" here.
  3. For each type you selected in 1 (except review), set "Parental Ordinality" to none and "Allowable Child Node types" to "Review"
  4. Under "Options for Review (review) nodes", set "Parental Ordinality" to one and "Allowable Child Node types" to "none"
  5. Go to example.com?=admin/settings/relativity/advanced [screenshot]
  6. Check "Enforce Parental Rules"
  7. Go to example.com?=admin/settings/relativity/display [screenshot]
  8. For each content type for which you enabled reviews, set "Rendering option for children nodes of type Review" to the view you created in step 3. In our example, we have set the option for the general content type to "reviews_of_node"

Step 6 - Set up redirection:

When submitting the review, it goes to the review view page. It makes more sense to go back to the node being reviewed. If you have already have a site specific module, add this form_alter code:

<?php
function YOURMODULENAME_form_alter($form_id, &$form) {
  switch (
$form_id) {
   
// Only do this for the review type's form
   
case ('review_node_form'):
     
// On submission, redirect to the parent node
     
$pnid = arg(4);
     
$form['#redirect'] = "node/$pnid";
     
     
// This is in case we add more forms later
     
break;
  }
}
?>

If you don't already have one and don't know how to make a module, just use the one in the zip.

Enjoy!

Directory browse page

It's likely to be some months before I get to writing this tutorial so I decided to throw up some tidbits. These are offered as is. Please don't ask for support. I'm already behind in supporting forums & profiles and just can't add to that. Don't expect to be able to just drop this in and have it work but, if you have some idea of what you're doing, this may be helpful.

For the top level directory page, I have a vocabulary, Directory Categories, with two levels of terms. The top level is a broad category and the second level narrows it down. I used the panels_by_term view style found when you have both the views bonus pack and the panels module. This is with panels 1 as this was done before panels 2 was around. This is the export of the view, called directory_browse:

<?php
  $view
= new stdClass();
 
$view->name = 'directory_browse';
 
$view->description = 'Browsable directory';
 
$view->access = array (
);
 
$view->view_args_php = '// Always use 8 as the augment since that\'s the directory\'s taxonomy
if (!$args[0]) {
  $args[0] = 8;
}'
;
 
$view->page = TRUE;
 
$view->page_title = 'Browse Directory';
 
$view->page_header = '';
 
$view->page_header_format = '1';
 
$view->page_footer = '';
 
$view->page_footer_format = '1';
 
$view->page_empty = '';
 
$view->page_empty_format = '1';
 
$view->page_type = 'panels_threecol_term';
 
$view->url = 'directory/browse';
 
$view->use_pager = FALSE;
 
$view->nodes_per_page = '0';
 
$view->menu = TRUE;
 
$view->menu_title = 'Browse directory';
 
$view->menu_tab = TRUE;
 
$view->menu_tab_weight = '0';
 
$view->menu_tab_default = TRUE;
 
$view->menu_tab_default_parent = NULL;
 
$view->menu_tab_default_parent_type = 'normal';
 
$view->menu_parent_tab_weight = '1';
 
$view->menu_parent_title = 'Directory';
 
$view->sort = array (
    array (
     
'tablename' => 'node',
     
'field' => 'sticky',
     
'sortorder' => 'DESC',
     
'options' => '',
    ),
    array (
     
'tablename' => 'node',
     
'field' => 'title',
     
'sortorder' => 'ASC',
     
'options' => '',
    ),
  );
 
$view->argument = array (
    array (
     
'type' => 'vocid',
     
'argdefault' => '1',
     
'title' => '%1',
     
'options' => '',
     
'wildcard' => '',
     
'wildcard_substitution' => '',
    ),
    array (
     
'type' => 'taxid',
     
'argdefault' => '6',
     
'title' => '%1 - %2',
     
'options' => '9',
     
'wildcard' => '',
     
'wildcard_substitution' => '',
    ),
  );
 
$view->field = array (
    array (
     
'tablename' => 'node',
     
'field' => 'title',
     
'label' => '',
     
'handler' => 'views_handler_field_nodelink',
     
'options' => 'link',
    ),
  );
 
$view->filter = array (
  );
 
$view->exposed_filter = array (
  );
 
$view->requires = array(node);
 
$views[$view->name] = $view;
?>

Note the argument code is hardcoded with my vocab ID.

To make the page look how it does takes some heavy theming. You can put this in your template.php:

<?php
// BEGIN Override the browse directory view (panels by term) theming
function phptemplate_views_bonus_panels_byterm_summary_directory_browse($view) {
 
 
drupal_add_css(drupal_get_path('module', 'views_bonus') .'/views_bonus.css');
 
 
// argument 0 must be a vocabulary ID
 
$vid = $view->args[0];
 
 
// Get the taxonomy tree from the argument to the view
 
$tree = taxonomy_get_tree($vid);
 
 
// group terms with their parent
 
foreach ($tree as $term) {
   
$cnt = taxonomy_term_count_nodes($term->tid);

    if (
$term->depth == 0) {
     
// Top level category. Add the "all" to the URL to show all subterms when the link is clicked
     
$txt = array($prefix. l($term->name . " ($cnt)", "taxonomy/term/$term->tid/all"));
     
$parent_tid = $term->tid;
     
$groups[$parent_tid]['header'] = $txt;
    } else {
     
// This bit is a pseudo tagadelic for the terms. It takes the number of nodes for a term and divides
      // it by 5 and then uses just the whole number part. If a term has at least one node, it gets a 1. If
      // a term has more than 25 nodes, it stays at 5. These numbers are then used to add an extra class of
      // "sizedterm-N" where N is 0-5 that can be styled in CSS to make the terms look different based on
      // the number of nodes in the term.
     
$sizeclass = floor($cnt/5);
      if (
$sizeclass == 0 && $cnt > 0) {
       
$sizeclass = 1;
      }
      if (
$sizeclass > 5) {
       
$sizeclass = 5;
      }
     
     
$txt = array('<span class="sizedterm-' . $sizeclass . '">' . l($term->name . " ($cnt)", "taxonomy/term/$term->tid") . '</span>');
     
$groups[$parent_tid]['rows'][] = $txt;
    }   
  }
 
 
// Spread the across 2 columns
 
$content = array();
 
$i=0;
  foreach (
$groups as $group) {
    if (
$i % 2) {
     
$section = 'left';
    } else {
     
$section = 'right';
    }

   
// Create a comma separated list of subterms
   
$subterms = array();
    foreach (
$group['rows'] as $subterm) {
     
array_push($subterms, $subterm[0]);
    }
   
$subterms = implode(", ", $subterms);
   
   
$content[$section] .=  '<div style="display:block;"><h3>' . $group['header'][0] . "</h3>" . $subterms . "</div>";
   
   
$i++;
  } 
  return
panels_print_layout('twocol', $content);
}

// END Override the browse directory view (panels by term) theming
?>

I think this is all the CSS for it but there may be more spread out in my .css file:

.view-summary-directory-browse h3 {
background: #356AA0 url(images/bluegradient-medium.jpg) repeat-x;
color: White;
padding : .25em;
margin-bottom: .25em;
}

.view-summary-directory-browse h3 a {
color: White;
}

.sizedterm-0 {
font-size: .9em;
}
.sizedterm-1 {
font-weight:bold;
font-size: 1.1em;
}
.sizedterm-2 {
font-weight:bold;
font-size: 1.2em;
}
.sizedterm-3 {
font-weight:bold;
font-size: 1.3em;
}
.sizedterm-4 {
font-weight:bold;
font-size: 1.4em;
}
.sizedterm-5 {
font-weight:bold;
font-size: 1.5em;
}

I'll try to throw more tidbits out as time permits. Hopefully that will help someone.

Michelle

Site recipe: Building a community portal

November 3, 2008: This tutorial sort of petered out as I refocused my efforts into Advanced Forum and Advanced Profile Kit. Sometime early next year I plan on moving Coulee Region ONLINE into D6 and resume work on it. At that point, I'll be revamping this site recipe. In the mean time, I'm leaving the old pages in case someone still finds them useful.

When I started work on Coulee Region ONLINE, I planned on writing up a detailed summary of what all went into it to post on the Drupal Showcase forum. As I got more into the project and spent untold hours trying to make my ideas work, often with poor or no documentation, and with a lot of help from the nice folks on IRC, I decided to go beyond that. I am in the process of creating a series of tutorials hits the major points of my site to show you how I did it. While I will try to make the tutorials as useful as possible, they are not drop in solutions. It's expected that you are familiar with Drupal and willing to take the time to research things you don't understand.

This is an ongoing and long term effort so don't expect it to be done anytime soon.

Planned tutorials (in no particular order):

Michelle

Coulee Region ONLINE - The Saga Begins

I have been wanting to create a local community site for around 10 years. Back in 1998, I made a Geocities club (later Yahoo Groups) as my first attempt. It failed pretty miserably, unfortunately, but I also didn't put the time into it that I should have. I was never happy with the limitations of using a free host and just couldn't get excited about it.

Since discovering Drupal, my interest has been rekindled. I had a couple other sites I needed to do first and finally have them to the point that they don't need my active attention. That frees up some of my very toddler-limited time to start making this dream a reality.

So why am I telling you this? Well, I thought it might be of interest to others to follow along. This is the first in what will be a series of articles about Coulee Region ONLINE (CRO). I'll tag them with both Drupal and CRO so you can find them easily. When the site is done, I'll go back and turn my articles into handbook pages. I expect this will be a long process, possibly a year or more. So don't expect me to post every day or even every week. But I will post eventually, so drop by now and then to see the progress.

The live site is at http://couleeregiononline.com and the dev site is at http://dev.couleeregiononline.com

Michelle

Daydreams and Brainstorms

Being a full time mom to an active toddler, I don't have a lot of time at the computer. But I find myself with a lot of thinking time during the day when driving place to place or watching my son at the park or any of the zillion other things that keeps me from the computer but doesn't take much thought.

Because of this, I tend to do a lot of site design in my head. There's pros and cons with this. One big pro is that I think through what I want and often realize something isn't going to work before I actually implement it. The big con is that it's hard to keep a whole site in my head and I'm sure I've forgotten lots of ideas I had. But, until my son is old enough that I can spend more time on the computer, it's what I have to work with.

He's doing a good job of playing by himself at the moment, so I'm going to write down as many of my daydreams and brainstorms as I can until he comes to fetch me. This will be messy, but I can always refine it later. :)

  • Full featured forums with all the usual community goodies.
  • Public and private forums
  • Community participation with commenting and voting on nodes
  • A listing of every park with photos and other info.
  • A listing for every business in the area so people can easily do local searches.
  • All items tagged by location and age appropriateness.
  • Google maps integrated for all locations
  • A section with information about each community.
  • Gallery for local photos
  • RSS feeds for local news
  • Polls
  • Blogs
  • Photo gallery per user
  • Customized profile page
  • Classifieds where members can list items
  • Communtiy event calendar with reminder ability
  • A few ads to help support the site but not tons of them that get in the way of content.
  • General theme thoughts: brown gradient gutter. White background for main site. Vibrant colour for boxes. Local photos in header... maybe a slide show.
  • Front page - Recent content, upcoming events, latest photos
  • Menu - Main navigation to forums, etc plus a menu block that has the same options but links are filtered by the currently selected community

That's a good start to work with. I'm sure I'll think of more goodies to add as I go along but at least I got a lot of the ideas in my head written down in shorthand so I'll remember them.

The next step is organizing and focusing my brainstorm into concrete specs.

General Plan

In my last post, I wrote out a quick list of the ideas that have been floating around in my head. Now I need to get organized. My ideas fall into two main categories:

1) Community interaction. I don't want this site to be just a list of links. I want real people talking to each other. I want a living, breathing community. This is my main goal for the site. To that end, I wrote what will likely become my mission statement:

"Coulee Region ONLINE is a community portal for La Crosse, Wisconsin and surrounding communities. While other local portal sites are focused on links and general information, our focus is the people of the community. With forums, chats, blogs, and other interactive features, our goal is to move beyond just giving information and provide a gathering place where people can interact with their neighbors whether they be next door or across the Coulee Region."

2) The other part to my site is informational. I plan to have a ton of information about the communities that make up the Coulee Region. I'll have business links like the other portals do but I also want to add things like local parks and activities. Plus community news, and more. This part of the site will be interactive as well, with people able to add reviews and ratings to each item.

In my next posts, I'll go into detail about each of these options, detailing the functionality and finding out what I need to implement them.

The Kitchen Sink Approach

I took a break from writing up specs to indulge myself in going through the contrib modules to see what I might like to use. I set the filter to "all" and copied the links to anything that caught my eye. When I was done, I had roughly 300 modules! Of course, many of these do the same thing as others and some of them aren't maintained anymore. I spent some time organizing them by category but haven't further weeded them. I'm posting the list here for the curious and will post again once I've weeded it down.

DISCUSSION
phpfreechat
Forummail
Announcement
Chatbox
Chatroom
Guestbook
Instant messenger
Smileys
Shoutbook
Shoutbox
SpamSpan filter
Survey
YShout AJAX Chat
Live Discussion
poll_inline
Quote
Privatemsg

BLOGGING
Blog Client
Blog Archive
Blog Information
Blogger
Blogroll
Latest and Greatest

USER ENHANCEMENTS
Node Profile
Profile Plus
User Search
Buddylist
Advanced User
Bio
Custom Ranks by Post Count
Karma
imood

USER TOOLS
Bookmarks
Checkmail

TELL A FRIEND
User Referral
Forward
Email This Page
Invite
tellafriend
Send

MULTIMEDIA
Acidfree
audio
Flash gallery
Flash Filter
Gallery
Image
Image Exact Sizes
Image filter
Image Publishing
Imagedrop
Imagemagick
IMCE for TinyMCE
Img_assist
Slideshow
Lightbox V2
Node Image Block
Photobar
Slideshow 2
Video
WebMedia
shazamgallery
Webcam
Flickrstickr
Flickr Module
Flickr Block
Flickr
flickrhood

SITE BUILDING & ADMINISTRATION
Actions
Advanced menu item settings
Contact List & Forms
Content Construction Kit (CCK)
Category
Flatcomments
Forum Access
Extended user status change notifications
Content Templates (Contemplate)
Dynamic Menus
Devel
CustomError
Inactive User
Separate "management" theme for administrative pages
Sanity
microcontent

CONTENT ENTRY & FILTERS
AJAX Spellcheck
Attachment
Authorship Module
Autosave
Bbcode
BBCode Formatting Bar
Comment Upload
FCKeditor
Coolfilter
Excerpt
Embed filter
Edit As New
HTML corrector
Htmlarea
Htmltidy
htmlwrap
Inline
Insert Block
Insert View
Import / Export API
Import-export
Invisimail
quicktags
Remember Filter
webform
Workflow
TinyMCE WYSIWYG Editor
Node Go To

MODERATION
Comment mover
captcha
Flag content
Content Moderator
modr8
Node Moderation

ADVERTISING
AdSense
Banner
Click Thru Tracking
TextLinkAds.com Integration

MAPPING
Carto - opengis web mapping
KML module
Googlemap
GMap Module
Map module
location (API, module)

EVENTS
EventFinder
Event Repeat
Event
Remindme
RSVP
Signup

LINKING
Easylinks
Janode
Links Package
LinksDB
Web Links
Weblink
Delicious
Service links

USER HELP
Help Tip
Favorites
faq

SEARCHING AND SEO
Google Analytics
Google Search
Google Sitemap
Global Redirect

MISC & FUN STUFF
Daylight Savings Reminder
Countdown
Vote up/down Package
FeedButtons
Feedback
Feature
Endorsements Module
Cpanel Integration
helpers
Job Search
Quotes
Recipe
Weather
Postcard

I/O
Syndication
Syndication2
Node import

USER
LoginToboggan
Nodeauthor information
Node Profile
Node Adoption
mypage
Members
Masquerade
Onlinestatus Indicator
Profile Plus
Profile Privacy
Profile visit
Role Assign
Site Profile Directory
Staff Bio
troll
User Badges
User Import
User List
User Maintenance
User Points
User Referral
User Related Content
User Search
User Tags
userlink
UserLists
Usernode
userplus
userposts
userreview
Login Destination
Masquerade
Legal
Remember me

EMAIL & NOTIFICATIONS
Listhandler
Mailhandler
mailman mailing list admin
Mailman Mailing List Management Wrapper
Notify
Subscribe
Subscription
Subscriptions

VOTING & RECCOMENDING
Node Vote
NodeReview
mediumvote
Recommended Posts
Vote up/down Package
Voting
Voting Actions
Voting API
Loves and Hates
Movie Review
Review

PRESENTATION
Nicelinks
Nice Menus
Outgoing links filter
Page title
Paging
Panels
Menu bar
S/P Ajax
S/P Magic Menus
Portal
Print Friendly Pages
Views
s Views Bonus Pack
Views Bookmarks
Views Calc
Views Fusion
Side Content
sIFR
Signwriter
SiteMenu
Superteaser
Switchtheme
Node Queue
Weight
Sections
Planet

ACCESS CONTROL
Path Access
Restricted Text
Taxonomy Access Control
Taxonomy Access Control Lite

STATISTICS
Log Search
Xstatistics
Statistics filtering
Statistics trends

SEARCH & SEO
Pathauto
Node (key)words
Search 404
Search config
Path Redirect
Path Filter
Zeitgeist

TAXONOMY
Taxonomy Breadcrumb
Taxonomy Browser
Taxonomy context
Taxonomy Defaults
Taxonomy Easy Filter
Taxonomy Filter
Taxonomy hide
Taxonomy Multi Editor
Taxonomy Switch
Taxonomy Theme
Primary Term
Tagadelic

Making the Site Interactive

As I said before, the main goal of this site is real chat with real people. All other ideas are secondary to this goal. So let's go into detail of what I need to get people talking.

Forums. The forums will be the central place for chat. I plan on using the forum module that comes with Drupal and add some contrib modules and tweaks to get the functionality people are used to from other sites.

Private messaging I'm not a big fan of private messages and prefer to use email, but I know it's something a lot of users like. So I'll add privatemsg for that.

Chatting I'd like to have an IRC like chat going. I thought about interfacing with an actual IRC channel but the various JS offerings look like a much better alternative. I had a look at the demos of YShout AJAX Chat and chatbox but the demo of phpfreechat really impressed me. It's not native Drupal but rather an integration of another OS app. I want to try and avoid that where possible, but I like this one so much better than the others that I'm going to go with it for now. chatroom has potential as a native Drupal solution, but it's not ready yet. I'll keep an eye on it and may change at some point.

WYSIWYG I'm haven't decided, yet, on what, if any, WYSIWYG editor to use. I may leave this decision until I get some users and see what they want. Or maybe start with something simple like BBCode or Quicktags instead of more complex ones like TinyMCE or fckeditor

Voting and reviews
After looking at Webchick's excellent article I've decided to go with userreview for nodes where I want both comments and rates, such as business links. I might also end up using Vote up/down Package if I decide to go for a Digg like structure on some content.

Classifieds
I want to have a place where people can place classified ads. I know I talked with someone who had a custom module for this that they were thinking of releasing, but I can't remember who, now... If I can't track that down, I may just use a combination of CCK and views.

Content Types and Categories

The second part of my general plan is informational. This isn't totally disconnected from the interactive part, of course, but the focus is a little different.

The informational side will consist of these areas:

Communities Each community will have its own page. This page will list general info about that community as well as have a tracker of posts tagged with the community.

Directory. This is sort of like a phone book in that it will list all the area businesses. In addition, it will list parks and other attractions. Since I'm interested in making this site useful more than making money, adding a link will be free, though I'm thinking of offering better positioning for paid links using the sticky option.

Events Local events, including an event calendar. I haven't decided, yet, if I want to use the event module or go with a CCK/views combination.

I'll be using CCK to make a few custom types. Communities, Events and Parks will each get their own type due to the specific details I want recorded. Businesses, restaurants, attractions, and "other" can probably share a content type and just use categories to differentiate. I can't decide what to call this content type... It's one of those niggly things that just keeps eluding me. I'd welcome any suggestions. :)

My original plan was to use the category module primarily because it makes terms into nodes which is perfect for the community pages. After trying it, though, I decided the code just isn't ready for production use. So I've decided to use regular taxonomy. To simulate the terms as nodes functionality, I've added URL aliases to link each community term's page to the proper community landing page. This is a lot more work, but only needs to be done once for each community. I may revisit the category module sometime in the future, but this will work for now.