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