2

In a product node type with a review node type that has a reference field to it, how do I only permit one review per user per product node? Do I use the Rules module and if so how do I configure the rule? Do I make a custom query and then unset the Add Review button/link on the product node in a custom module or in template.php?

(edit) Take the example of product reviews on Amazon or muffler repair reviews on Yelp. The user who makes the review gets one review per listing or item. After they create the review, they can edit it but can't make another one for that listing or item. Seems logical. Likewise the Fivestar rating widget only allows one vote per item so having several reviews for an item will not permit more than just one Fivestar rating.........

Unless somebody has a better solution, I'm going to test -- since the Fivestar widget makes the database query anyway -- if it has been set by the user on the node view. If true then I will disable the Node Reference URL widget button in the links area. If I need more security, I will create code or a Rue that tests on the Node Add page if the Fivestar widget has already been set to disable the Node Add page and redirect the user back to the product.

  • What do you mean with "only permit one review per user"? – kiamlaluno May 20 '11 at 5:39
3

You might need to try a custom module for this. Rules are too generic as you can only control a user's right to create a type of content. If I was to write a module for this, it would go something along these lines:

• Use hook_form_alter to add an extra validation function to your Review node form • Check if the user has submitted a review before in the db • Throw an error if a record exists in the database

Something like the following code should work, but this is untested.

Edited to note that this is Drupal 6 code but if you use it as a template it shouldn't be too far away.

<?php

function MYMODULE_form_alter($form_id, &$form) {
    // If the form is a "review" node form
    if ($form_id == 'review_node_form') {
        // Add an extra validation function
        $form['#validate'][] = 'MYMODULE_user_node_limit';
    }
}

// Validation function
function MYMODULE_user_node_limit($node) {
    // Get our logged in user's details
    global $user;
    // Run a database query (something like this)
    $sql = "SELECT {node}.uid \n"
    . "FROM {content_type_review}, {node} "
    . "WHERE {content_type_page}.field_node_ref_nid = {node}.nid "
    . "AND {node}.nid = %d"
    . "AND {node}.uid = %d";
    $result = db_query($sql, $node->nid, $node->uid);
    if ($result === FALSE) {
        // Throw error
        drupal_set_message('You have already submitted a review of this item.', 'error');
    }
}
?>

Edited to answer additional comment by asker

Regarding the comment made by Adam S - you could use the code I have already posted in conjunction with the Drupal 7 equivalent of Drupal 6's hook_nodeapi. Here is a list of Drupal 7's node hooks for you to have a look at. Also, have a look at Eric London's Drupal 6 example of a problem similar to yours of limiting a user's node writing privileges. Apologies for not providing an exact Drupal 7 example but if you follow the basic premise of Eric London's link but modify it in conjunction with my code, you should be able to solve your problem.

  • Although it doesn't solve the problem of the link from the node reference URL widget being shown on the page it does validate the node to force only one review in case some crafty person wanted to place several reviews of their product from the same user. So I'll just unset the link and validate the form with this. However, it might be better to validate before they receive the form and redirect back to the page. – Adam S May 21 '11 at 4:24
  • I have amended my answer to answer your comment, Adam. – Jamie Hollern May 23 '11 at 8:32
1

For what it's worth, I took a slightly different approach that might be useful. It isn't perfect, but works very well for me. I borrowed heavily from the solution here:

How to limit a user to one vote when using Fivestar?

But I didn't like changing my theme. I wanted to to it in the form alter, as I wanted to control things before they got to the browser (so other #validate solutions weren't right for me). Anyway, here is my approach:

<?php
function MYMODULE_form_alter($form_id, &$form, $form_id) {
    switch($form_id) {
        case 'comment_node_product_form':  //Modifications to the voting for five star on comment forms
            MYMODULE_fivestar_limit_votes($form);
            break;
    }
}

/**
 * Function to limit votes on a particular node to one per user.  Also requires users to be logged in to vote.
* This assumes that you are passing in a node as the entity for which the vote is recorded.  That little bit would
* of course be more flexible if done differently.
* 
* @param unknown $form
* @param string $voteTag
* @param string $entityType
*/
function MYMODULE_fivestar_limit_votes(&$form, $voteTag = 'rating', $entityType = 'node') {
    $path = drupal_get_path('module', 'fivestar');
    // These are likely added already, but no harm done just in case
    drupal_add_js($path . '/js/fivestar.js');
    drupal_add_css($path . '/css/fivestar.css');
    $replacement = false;
    $is_login = user_is_logged_in();
    $nid = $form['#node']->nid;

    $rating = votingapi_select_single_result_value(array(
        'entity_id' => $nid,
        'entity_type' => $entityType,
        'tag' => $voteTag,
        'function' => 'average',
    ));
    $lang = $form['field_product_rating']['#language'];
    $widget = $form['field_product_rating'][$lang][0]['rating']['#widget'];
    if ($is_login) {
        if (isset($rating)) {
            $voting_message = "<div>You have already rated this.</div>";
            $replacement = theme('fivestar_static', array('rating' => $rating, 'stars' => 5, 'tag' => $voteTag, 'widget' => $widget)) . $voting_message;
        }
        // The 'else' here is to leave the form alone.
    }
    else {
        $fivestar_links = l('Login', 'user/login') . ' or ' . l('Register', 'user/register');
        $voting_message = "<div>Only registered user can rate this content type.<br/>$fivestar_links to rate this content type.</div>";
        $replacement = theme('fivestar_static', array('rating' => $rating, 'stars' => 5, 'tag' => $voteTag, 'widget' => $widget)) . $voting_message;
    }
    if ($replacement) {
        $form['field_product_rating']['#type'] = 'markup';
        $form['field_product_rating']['#markup'] = $replacement;
        $form['field_product_rating']['#tree'] = false;
        unset($form['field_product_rating']['und']);
    }
?>

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.