'Vote', 'page callback' => 'selection_vote', 'page arguments' => array(2), 'access arguments' => array('vote on decisions'), 'type' => MENU_CALLBACK, ); return $items; } /* * Implementation of hook_theme(). */ function selection_theme() { return array( 'selection_decisions_view_results' => array( 'arguments' => array('node' => NULL, 'teaser' => FALSE, 'page' => FALSE, 'this_vote' => array()), ), ); } function selection_help($section) { $help = ""; switch ($section) { case 'admin/modules#description': break; default: if ($section == 'node/add#decisions-selection') { $help = t('Creates a decision where the user selects one or many options.'); } break; } return $help; } function selection_node_info() { return array('decisions_selection' => array('name' => t('Decisions - selection'), 'module' => 'decisions', 'description' => t('Creates a decision where the user selects one or many options.'))); } /** * Implementation of the decisions_algorithms() hook */ function selection_decisions_algorithms() { return array('plurality' => t('The plurality algorithm determines the winner based solely on the number of votes received: the one with the larger number of votes wins. See the Plurality voting system on Wikipedia for more information')); } /* * Callback for single-click voting. * @param $nid * The nid being voted upon. * @param $cid * The choice ID used in the vote. * @return * Returns HTML for AHAH voting, or redirects to the destination for synchronous voting. */ function selection_vote($node, $cid) { global $user; $new_vote = array(); if (selection_check_token($_REQUEST['token'], $node->nid, $cid)) { $valid_cid = !empty($node->choice[$cid]['label']); if (empty($valid_cid)) { if (empty($_REQUEST['ajax'])) { drupal_goto(drupal_get_destination()); } else { exit(); } } if (empty($voted)) { $new_vote = array( 'value' => 1, 'tag' => $cid, 'value_type' => 'option', 'content_type' => 'decisions', 'content_id' => $node->nid, ); $votes[] = $new_vote; votingapi_add_votes($votes); votingapi_recalculate_results('decisions', $node->nid); } else { if (empty($_REQUEST['ajax'])) { drupal_set_message(t("You have already voted.")); drupal_goto(drupal_get_destination()); } else { exit(); } } // Render the results if (empty($_REQUEST['ajax'])) { // Vote confirmation is displayed in the results so that the degradable behavior matches the AHAH behavior. drupal_goto(drupal_get_destination()); } else { // We pass along the vote cast in this request so that the user's choice // can be displayed along with the results. print theme('selection_decisions_view_results', $node, FALSE, FALSE, $new_vote); exit(); } } // Invalid token. if (empty($_REQUEST['ajax'])) { drupal_set_message(t("Invalid token. Please try voting again.")); drupal_goto(drupal_get_destination()); } else { exit(); } } /* * Generates a token for use with AJAX voting. */ function selection_get_token($nid = 0, $cid = 0) { global $user; $session_id = isset($user->uid) && $user->uid > 0 ? session_id() : ''; $private_key = drupal_get_private_key(); return md5($session_id . "selection-vote-$nid-$cid" . $private_key); } /* * Checks a token for use with AJAX voting. */ function selection_check_token($token, $nid = 0, $cid = 0) { return selection_get_token($nid, $cid) == $token; } /** * Implementation of decisions_hook_voting_form. * Presents a list of choices for the user to vote on. */ function decisions_selection_voting_form($form_state, $node, $teaser, $page) { $one_click = variable_get('decisions_1click', 0); global $user; $form['node'] = array( '#type' => 'value', '#value' => $node, ); $form['#prefix'] = '
'; //The decisions_selection_results div exists as a workaround for 1click voting when jQuery 1.3+'s live() is unavailable. $form['#suffix'] = '
'; if ($node->choice) { $list = array(); // Put options in random order if randomize option // selected on node create/edit form. if ($node->randomize) { $node->choice = _decisions_randomize_options($node->choice, $node->choices); } //Single choice, "Plurality" voting. if ($node->maxchoices == 1) { //Are we using the 1-click form? if ($one_click == 1) { drupal_add_css(drupal_get_path('module', 'selection') . '/selection.css', 'module'); $links = ''; foreach ($node->choice as $i => $choice) { $links[$i]['attributes']['class'] = 'decisions_selection_vote'; $links[$i]['query'] = drupal_get_destination() . '&token='. selection_get_token($node->nid, $i); $links[$i]['title'] = check_plain($choice['label']); $links[$i]['href'] = "decisions_selection/vote/$node->nid/$i/"; } if (!empty($links)) { $output = ''; $output .= '
'; $output .= theme('links', $links, array('class' => 'decisions_selection_1click')); $output .= '
'; } $form['choices']['1click'] = array( '#value' => $output, ); //If asynchronous voting is enabled, add the necessary JS. if (variable_get('decisions_ahah', 0) == 1) { drupal_add_js(drupal_get_path('module', 'selection') . '/selection.js'); } //At this point, we can return the form since 1-click uses its own callback for vote submission. return $form; } //Not using 1-click. Use radios. else { foreach ($node->choice as $i => $choice) { if ($choice['label']) { $list[$i] = check_plain($choice['label']); } } } $form['choice'] = array( '#type' => 'radios', '#title' => $page ? '' : check_plain($node->title), '#default_value' => -1, '#options' => $list, ); } //Approval/multiple-choice voting. else { foreach ($node->choice as $i => $choice) { if ($choice['label']) { $list[$i] = check_plain($choice['label']); } } $form['choice'] = array( '#type' => 'checkboxes', '#title' => $page ? '' : check_plain($node->title), '#options' => $list, ); } } //Add the submit button. 1click has already returned the form by this point. $form['vote'] = array( '#type' => 'submit', '#value' => t('Vote'), ); return $form; } /** * Display the selection results. * TODO: implement (http://drupal.org/node/48249) */ function selection_decisions_view_results($node, $teaser, $page, $new_vote = array()) { global $user; $vote = !empty($new_vote) ? $new_vote : $node->vote; $output = ""; $results = votingapi_select_results(array('content_id' => $node->nid, 'content_type' => 'decisions')); // Count the votes for each choice $votes = array(); foreach ($results as $result) { // approval $voteval = $result['tag']; if (!array_key_exists($voteval, $votes)) { $votes[$voteval] = 0; } // the value is the position chosen in this case, we just want to increment $votes[$voteval]+=$result['value']; } foreach ($node->choice as $i => $ch) { if (!array_key_exists($i, $votes)) { $votes[$i] = 0; } } // sort the votes arsort($votes); $total_votes = array_sum($votes); if ($node->choice && $total_votes > 0) { // TODO: Those
s and
s should be in a theme function. First collect all the data in a structure, then theme it. // display results for each possible choice //If AHAH and 1click are enabled, ensure that the fallback behavior shows the same vote confirmation. if (variable_get('decisions_1click', 0) && ($node->maxchoices == 1)) { if (!empty($new_vote)) { //Fallback behavior. $vote = $new_vote; } $vote_label = $node->choice[$vote['tag']]['label']; $output .= '' . t('You voted: @vote_tag', array('@vote_tag' => $vote_label)) . ''; } $output .= '
'; $max_votes = 0; foreach ($votes as $i => $vote) { $max_votes = ($vote > $max_votes ? $vote : $max_votes); } foreach ($votes as $i => $vote) { $percentage = round(100 * $vote / $total_votes, 0); $output .= theme('decisions_bar', check_plain($node->choice[$i]['label']), $vote, $total_votes, $max_votes); } $output .= '
'; } $output .= '
'; return $output; } /** * implementation of the format_votes() hook. * * formats how a user's votes should be displayed. * * @returns a formatted string */ function selection_decisions_format_votes($node, $votes) { $unordered_votes = array(); foreach ($votes as $vote) { // Just need one dimensional results if ($vote->value > 0) { $unordered_votes[] = check_plain($node->choice[$vote->tag]['label']); } } return implode(', ', $unordered_votes); } /** * Registers the vote as a key for this node using votingapi_set_vote(). */ function decisions_selection_voting_form_submit($form, &$form_state) { $node = $form_state['values']['node']; $one_click = variable_get('decisions_1click', 0); global $user; $node = $form_state['values']['node']; $votes = array(); if ($node->maxchoices == 1) { // plurality voting $vote = array( 'value' => 1, 'tag' => $form_state['values']['choice'], 'value_type' => 'option', 'content_type' => 'decisions', 'content_id' => $node->nid, ); $votes[] = $vote; } else { // approval voting foreach ($node->choice as $key => $choice) { if (isset($form_state['values']['choice'][$key]) && $form_state['values']['choice'][$key]) { $vote = array( 'value' => 1, 'value_type' => 'option', 'tag' => $key, 'content_type' => 'decisions', 'content_id' => $node->nid, ); $votes[] = $vote; } } } votingapi_add_votes($votes); //If this becomes a performance problem, we use votingapi_set_votes and the site-wide recalculation time. votingapi_recalculate_results('decisions', $node->nid); drupal_set_message(t('Your vote was registered.')); } /** * implementation of the vote_validate() hook * * check if the submitted key exists, just to make sure the form is not bypassed. * * @returns boolean true if the form is valid */ function decisions_selection_voting_form_validate($form, &$form_state) { $node = $form_state['values']['node']; if ($node->maxchoices == 1) { if (!array_key_exists($form_state['values']['choice'], $node->choice)) { form_set_error('choice', 'At least one choice must be selected.'); } } else { //Approval/Multiple-choice voting // array used to check which values are set $setvalues = array(); foreach ($node->choice as $key => $choice) { // see if the box is checked if (!empty($form_state['values']['choice'][$key])) { $numchoices++; } } // too many choices ranked if ($node->maxchoices != 0 && $numchoices > $node->maxchoices) { form_set_error('choice', t('%num choices were selected but only %max are allowed.', array('%num' => $numchoices, '%max' => $node->maxchoices) ) ); } // not enough choices ranked $minchoices = 1; if ($numchoices < $minchoices) { form_set_error('choice', t('At least one choice must be selected.')); } } } function theme_selection_decisions_view_results($node = NULL, $teaser = FALSE, $page = FALSE, $new_vote = array()) { return selection_decisions_view_results($node, $teaser, $page, $new_vote); }