uid == $account->uid && user_access('edit own polls', $account))) { return TRUE; } } } /** * Implementation of hook_node_info(). */ function advpoll_node_info() { $modes = _advpoll_list_modes(); $info = array(); foreach ($modes as $mode) { $info['advpoll_' . $mode['name']] = array( 'name' => t('@name poll', array('@name' => $mode['name_label'])), 'module' => 'advpoll', 'description' => $mode['description'], 'title_label' => t('@name question', array('@name' => $mode['name_label'])), 'body_label' => t('Description'), ); } return $info; } /** * Implementation of hook_menu(). */ function advpoll_menu() { $menu['advpoll/cancel/%node'] = array( 'title' => 'Cancel Vote', 'page callback' => 'advpoll_cancel', 'page arguments' => array(2), 'access arguments' => array('cancel own vote'), 'type' => MENU_CALLBACK, 'file' => 'advpoll.admin.inc', ); $menu['advpoll/js_vote'] = array( 'title' => 'Vote via JavaScript', 'page callback' => 'advpoll_js_vote', 'access arguments' => array('vote on polls'), // TODO: be more specific here. 'type' => MENU_CALLBACK, ); $menu['advpoll/js_more_choices'] = array( 'title' => 'More Choices via JavaScript', 'page callback' => 'advpoll_js_more_choices', 'access arguments' => array('access content'), // TODO: be more specific here. 'type' => MENU_CALLBACK, ); $menu['polls'] = array( 'title' => 'Advanced Polls', 'page callback' => 'advpoll_page', 'access arguments' => array('access content'), 'type' => MENU_SUGGESTED_ITEM, 'file' => 'advpoll.pages.inc', ); $menu['node/%node/results'] = array( 'title' => 'Results', 'page callback' => 'advpoll_results_page', 'page arguments' => array(1), 'access callback' => '_advpoll_results_access', 'access arguments' => array(1), 'weight' => 3, 'type' => MENU_LOCAL_TASK, 'file' => 'advpoll.pages.inc', ); $menu['node/%node/votes'] = array( 'title' => 'Votes', 'page callback' => 'advpoll_votes_page', 'page arguments' => array(1), 'access callback' => '_advpoll_votes_access', 'access arguments' => array(1), 'weight' => 3, 'type' => MENU_LOCAL_TASK, 'file' => 'advpoll.pages.inc', ); // Show electoral list tab if using the functionality. $menu['node/%node/electoral_list'] = array( 'title' => 'Electoral list', 'page callback' => 'advpoll_electoral_list_page', 'page arguments' => array(1), 'access callback' => '_advpoll_electoral_list_access', 'access arguments' => array(1), 'weight' => 3, 'type' => MENU_LOCAL_TASK, 'file' => 'advpoll.pages.inc', ); // Allow voters to be removed. $menu['node/%node/remove'] = array( 'page callback' => 'advpoll_remove_voter', 'page arguments' => array(1), 'access arguments' => array('administer polls'), 'weight' => 3, 'type' => MENU_CALLBACK, 'file' => 'advpoll.admin.inc', ); // Allow votes to be cleared. $menu['node/%node/votes/clear'] = array( 'page callback' => 'advpoll_clear_votes_page', 'page arguments' => array(1), 'access callback' => '_advpoll_clear_votes_access', 'access arguments' => array(1), 'weight' => 3, 'type' => MENU_LOCAL_TASK, 'file' => 'advpoll.pages.inc', ); // Show the write-ins tab if there is at least one. $menu['node/%node/writeins'] = array( 'title' => 'Write-ins', 'page callback' => 'advpoll_writeins_page', 'page arguments' => array(1), 'access callback' => '_advpoll_writeins_access', 'access arguments' => array(1), 'weight' => 3, 'type' => MENU_LOCAL_TASK, 'file' => 'advpoll.pages.inc', ); $menu['admin/settings/advpoll'] = array( 'title' => 'Advanced Poll Settings', 'description' => 'Manage Advanced Poll settings.', 'page callback' => 'drupal_get_form', 'page arguments' => array('_advpoll_settings'), 'access arguments' => array('administer site configuration'), 'type' => MENU_NORMAL_ITEM, 'file' => 'advpoll.admin.inc', ); return $menu; } /** * Implementation of hook_init(). */ function advpoll_init() { // Use poll.module's stylesheet, no need to duplicate at this point. drupal_add_css(drupal_get_path('module', 'poll') . '/poll.css'); // Load the mode include files. _advpoll_list_modes(); } /** * Results access callback. */ function _advpoll_results_access($node) { return strstr($node->type, 'advpoll_') && _advpoll_is_active($node) && $node->votes > 0 && !$node->voted && _advpoll_can_view_results($node); } /** * Electorial list access callback. */ function _advpoll_electoral_list_access($node) { return user_access('access electoral list') && strstr($node->type, 'advpoll_') && $node->use_list; } /** * Votes access callback. */ function _advpoll_votes_access($node) { return strstr($node->type, 'advpoll_') && $node->votes > 0 && ((user_access('inspect all votes') && $node->show_votes) || user_access('administer polls')); } /** * Clear votes access callback. */ function _advpoll_clear_votes_access($node) { return strstr($node->type, 'advpoll_') && $node->votes > 0 && user_access('administer polls'); } /** * Writeins access callback. */ function _advpoll_writeins_access($node) { if (!strstr($node->type, 'advpoll_') || !user_access('administer polls')) { return FALSE; } foreach ($node->choice as $choice) { if ($choice['writein']) { // Has at least one write-in choice. return TRUE; } } return FALSE; } /** * Implementation of hook_block(). */ function advpoll_block($op='list', $delta=0, $edit=array()) { switch ($op) { case 'list': $blocks['latest_poll']['info'] = t('Advanced Poll: Latest poll'); $items = _advpoll_available_blocks(); if ($items) { foreach ($items as $poll) { $blocks['adv_poll_' . $poll->nid]['info'] = t('Advanced Poll: @title', array('@title' => $poll->title)); } } return $blocks; case 'view': if ($delta == 'latest_poll') { $block['subject'] = t('Latest poll'); $block['content'] = theme('advpoll_block_latest_poll'); } else { $nid = intval((str_replace('adv_poll_', '', $delta))); $block['subject'] = t('Poll'); $block['content'] = theme('advpoll_view_block_poll', $nid); } return $block; } } /** * Retrieve available blocks for block list */ function _advpoll_available_blocks() { $timestamp = time(); $result = db_query('SELECT n.nid, n.title FROM {node} n INNER JOIN {advpoll} p ON p.nid = n.nid WHERE n.status = 1 AND p.create_view_block = 1 AND p.start_date < %d AND p.end_date > %d', $timestamp, $timestamp); $results = array(); while ($node = db_fetch_object($result)) { $results[] = $node; } return $results; } /** * Content of the block, as returned by advpoll_block('view'). */ function theme_advpoll_block_latest_poll() { $node = advpoll_latest_poll(); $output = ''; if ($node) { $output .= '

' . l($node->title, 'node/' . $node->nid) . '

'; $output .= drupal_render($node->content); if ($node->voted) { $output .= '

' . l(t('Older polls'), 'polls', array('class' => 'old-polls', 'title' => t('View the list of polls on this site.'))) . '

'; } } return $output; } /** * Content of the block when a poll is flagged to display as a block */ function theme_advpoll_view_block_poll($nid) { $node = advpoll_view(node_load($nid), FALSE, FALSE); $output = ''; if ($node && $node->status && $node->active && $node->start_date < time() && $node->end_date > time()) { $output .= '

' . $node->title . '

'; $output .= drupal_render($node->content); } return $output; } /** * . */ function advpoll_latest_poll() { $timestamp = time(); $result = db_query('SELECT MAX(n.nid) AS nid FROM {node} n INNER JOIN {advpoll} p ON p.nid = n.nid WHERE n.status = 1 AND p.active = 1 AND p.start_date < %d AND p.end_date > %d', $timestamp, $timestamp); $poll = db_fetch_object($result); // The nid will be NULL if there are no active polls. if ($poll->nid) { $node = advpoll_view(node_load($poll->nid), FALSE, FALSE); } return $node; } /** * Implementation of hook_form(). * * This hook displays the form necessary to create/edit the poll. */ function advpoll_form(&$node, $form_state) { $mode = _advpoll_get_mode($node->type); $type = node_get_types('type', $node); $editing = isset($node->nid); $form = array(); // Only add javascript once, even if _form is called multiple times. static $add_js; if (!$add_js) { // Pass translatable strings drupal_add_js(array('advPoll' => array('remove' => t('Remove'), 'addChoice' => t('Add choice'), 'noLimit' => t('No limit'))), 'setting'); drupal_add_js(drupal_get_path('module', 'advpoll') . '/advpoll-form.js', 'module'); drupal_add_css(drupal_get_path('module', 'advpoll') . '/advpoll.css', 'module'); $add_js = TRUE; } $form['title'] = array( '#type' => 'textfield', '#maxlength' => 255, '#title' => check_plain($type->title_label), '#required' => TRUE, '#default_value' => $node->title, ); if ($type->has_body) { $form['body_field'] = node_body_field($node, $type->body_label, $type->min_word_count); } if (isset($form_state['values']['choices'])) { $choices = $form_state['values']['choices']; if ($form_state['values']['more_choices']) { $choices *= 2; } } else { $choices = max(2, isset($node->choice) && count($node->choice) ? count($node->choice) : ADVPOLL_INITIAL_CHOICES); } $form['choices'] = array( '#type' => 'hidden', '#value' => $choices, ); // Advanced Poll choices $form['choice'] = array( '#type' => 'fieldset', '#title' => t('Poll choices'), '#collapsible' => TRUE, '#prefix' => '
', '#suffix' => '
', '#tree' => TRUE, '#weight' => 1, ); if ($editing) { $form['choice']['choice_note'] = array( '#value' => '
' . t('Note: adding or removing choices after voting has begun is not recommended.') . '
', ); } $form['choice']['more_choices'] = array( '#type' => 'checkbox', '#title' => t('Need more choices'), '#value' => 0, '#parents' => array('more_choices'), // Don't pollute $form['choice'] '#prefix' => '
', '#suffix' => '
', '#description' => t("If the number of choices above isn't enough, click here to add more choices."), '#weight' => 1, /* '#submit' => array('advpoll_more_choices_submit'), // If no JavaScript. '#ahah' => array( 'path' => 'advpoll/js_more_choices', 'wrapper' => 'poll-choices', 'method' => 'replace', 'effect' => 'fade', ), */ ); $form['footer_message'] = array( '#type' => 'textfield', '#title' => t('Footer Message'), '#description' => t('Optional message that can appear below the poll.'), '#default_value' => $node->footer_message, '#maxlength' => 255, ); // First, loop through any currently existing choices. $current_choices = 0; $default_choices = ''; if (isset($node->choice)) { foreach ($node->choice as $index => $choice) { $form['choice'][$index]['label'] = array( '#type' => 'textfield', '#title' => t('Choice %n', array('%n' => $current_choices + 1)) . ($choice['writein'] ? ' ' . t('(write-in)') : ''), '#default_value' => $choice['label'], '#attributes' => array('class' => 'choices'), '#maxlength' => ADVPOLL_CHOICE_MAX_LENGTH, ); $current_choices++; $next_index = $index + 1; } } elseif ($default_choices != '') { $default_choices = explode("\n", $default_choices); foreach ($default_choices as $index => $label) { $form['choice'][$index]['label'] = array( '#type' => 'textfield', '#title' => t('Choice %n', array('%n' => $current_choices + 1)), '#default_value' => $label, '#attributes' => array('class' => 'choices'), ); $current_choices++; $next_index = $index + 1; } } else { $next_index = 1; } // Now add on extra choices if we need to. if ($current_choices < $choices) { for ($index = $next_index; $current_choices < $choices; $index++, $current_choices++) { $form['choice'][$index]['label'] = array( '#type' => 'textfield', '#title' => t('Choice %n', array('%n' => $current_choices + 1)), '#attributes' => array('class' => 'choices'), '#maxlength' => ADVPOLL_CHOICE_MAX_LENGTH, ); } } $form['settings'] = array( '#type' => 'fieldset', '#title' => t('Poll settings'), '#collapsible' => TRUE, '#collapsed' => TRUE, '#weight' => 2, '#tree' => TRUE, ); $max_choice_list = array(); for ($i = 0; $i <= $choices; $i++) { $max_choice_list[$i] = ($i == 0 ? t('No limit') : $i); } $form['settings']['max_choices'] = array( '#type' => 'select', '#title' => t('Maximum choices'), '#options' => $max_choice_list, '#default_value' => isset($node->max_choices) ? $node->max_choices : ADVPOLL_MAX_CHOICES, '#description' => t('Limits the total number of choices voters may select.') ); $voting_algorithms = advpoll_algorithms($mode); if (count($voting_algorithms) > 1) { // Create a select field when the poll supports several algorithms. $form['settings']['algorithm'] = array( '#type' => 'select', '#title' => t('Algorithm'), '#options' => $voting_algorithms, '#default_value' => isset($node->algorithm) ? $node->algorithm : key($voting_algorithms), '#description' => t('Voting algorithm to use to calculate the winner.'), ); } else { // Pass the only algorithm as a value. $form['settings']['algorithm'] = array( '#type' => 'value', '#value' => key($voting_algorithms), ); } $form['settings']['close'] = array( '#type' => 'checkbox', '#title' => t('Close poll'), '#description' => t('When a poll is closed users may no longer vote on it.'), '#default_value' => isset($node->active) ? !$node->active : 0, ); // MW modified to allow easier to use date fields and set default dates in a range // of now to +365 days. Eliminated minutes and seconds for ease of use. $format = 'Y-m-d'; $date = date($format, time()); $form['settings']['start_date'] = array( '#type' => 'date_select', '#default_value' => isset($node->start_date) ? date($format, $node->start_date) : date($format, time()), '#date_format' => $format, '#title' => t('Starting date'), '#description' => t('The date that the poll opens.'), ); $form['settings']['end_date'] = array( '#type' => 'date_select', '#default_value' => isset($node->end_date) ? date($format, $node->end_date) : date($format, time() + (60 * 60 * 24 * 365)), '#date_format' => $format, '#title' => t('Ending date'), '#description' => t('Leave blank if you do not want the poll to close automatically.'), ); // Settings available for users with 'administer polls' permission. $default_use_list = isset($node->use_list) ? $node->use_list : ADVPOLL_ELECTORAL_LIST; $default_show_votes = isset($node->show_votes) ? $node->show_votes : ADVPOLL_SHOW_VOTES; $default_writeins = isset($node->writeins) ? $node->writeins : ADVPOLL_WRITEINS; $default_show_writeins = isset($node->show_writeins) ? $node->show_writeins : ADVPOLL_SHOW_WRITEINS; if (user_access('administer polls')) { $form['settings']['admin_note'] = array( '#value' => '
' . t('The settings below are only available for users with the administer polls permission.') . '
', ); $form['settings']['writeins'] = array( '#type' => 'checkbox', '#title' => t('Allow users to cast a write-in vote'), '#default_value' => $default_writeins, '#description' => t('Allow voters with the "add write-ins" permission to write-in up to one choice each.'), '#attributes' => array('class' => 'settings-writeins'), ); $form['settings']['show_writeins'] = array( '#type' => 'checkbox', '#title' => t('Display write-in votes as choices for future voters'), '#default_value' => $default_show_writeins, '#description' => t('Allow voters to see and choose from previously submitted write-in votes.'), '#prefix' => '
', '#suffix' => '
', ); $form['settings']['use_list'] = array( '#type' => 'checkbox', '#title' => t('Restrict voting to electoral list'), '#description' => t('If enabled, a list of eligible voters will be created and only that group will be able to vote in the poll.'), '#default_value' => $default_use_list, ); $form['settings']['show_votes'] = array( '#type' => 'checkbox', '#title' => t('Show individual votes'), '#description' => t('Users with the appropriate permissions will be able to see how each person voted.'), '#default_value' => $default_show_votes, ); $form['settings']['create_view_block'] = array( '#type' => 'checkbox', '#title' => t('Generate a block for this poll'), '#description' => t('When checked, a block for this particular poll will be generated upon submission.'), '#default_value' => isset($node->create_view_block) ? $node->create_view_block : 0, ); } else { // Just pass the values for users without the "administer polls" permission. $defaults = array('use_list' => $default_use_list, 'show_votes' => $default_show_votes, 'writeins' => $default_writeins, 'show_writeins' => $default_show_writeins); foreach ($defaults as $name => $value) { $form['settings'][$name] = array( '#type' => 'value', '#value' => $value, ); } } return $form; } /** * Process advpoll_more_choices form submissions. */ function advpoll_more_choices_submit($form, &$form_state) { // Based on poll.module node_form_submit_build_node($form, $form_state); if ($form_state['values']['more_choices']) { $n = $_GET['q'] == 'advpoll/js_more_choices' ? 1 : 5; $form_state['choice_count'] = count($form_state['values']['choice']) + $n; } } /** * Implementation of hook_form_alter(). */ function advpoll_form_alter(&$form, $form_state, $form_id) { if ($form_id == 'node_type_form' && isset($form['identity']['type'])) { $node_type = $form['old_type']['#value']; // Display poll settings if this is an advpoll content type. if ($form['module']['#value'] == 'advpoll') { $advpollSettings = variable_get('advpoll_settings', array()); // Include JS and CSS for the show_writeins setting toggle. drupal_add_js(drupal_get_path('module', 'advpoll') . '/advpoll-form.js', 'module'); drupal_add_css(drupal_get_path('module', 'advpoll') . '/advpoll.css', 'module'); $form['advpoll'] = array( '#type' => 'fieldset', '#title' => t('Poll settings'), '#collapsible' => TRUE, ); $form['advpoll']['advpoll_choices'] = array( '#type' => 'textarea', '#title' => t('Default choices'), '#default_value' => '', '#description' => t('Add one choice per row. This setting can be overridden on the poll edit page.'), ); $form['advpoll']['advpoll_max_choices'] = array( '#type' => 'select', '#title' => t('Default maximum choices'), '#options' => array(0 => t('No limit')) + drupal_map_assoc(array(1, 2, 3, 4, 5)), '#default_value' => ADVPOLL_MAX_CHOICES, '#description' => t('The default number of maximum choices for new polls. This setting can be overridden on the poll edit page.'), ); $mode = _advpoll_get_mode($node_type); $voting_algorithms = advpoll_algorithms($mode); if (count($voting_algorithms) > 1) { $form['advpoll']['advpoll_algorithm'] = array( '#type' => 'select', '#title' => t('Default algorithm'), '#options' => $voting_algorithms, '#default_value' => key($voting_algorithms), '#description' => t('Default voting algorithm for calculating the winner.'), ); } $form['advpoll']['advpoll_runtime'] = array( '#type' => 'select', '#title' => t('Default duration'), '#default_value' => ADVPOLL_RUNTIME, '#options' => array(0 => t('Unlimited')) + drupal_map_assoc(array(86400, 172800, 345600, 604800, 1209600, 1814400, 2419200, 4838400, 9676800, 31536000), 'format_interval'), '#description' => t('The date the poll was created is used as start date for the default duration. This setting can be overridden on the poll edit page.'), ); $form['advpoll']['advpoll_writeins'] = array( '#type' => 'checkbox', '#title' => t('Allow users to cast a write-in vote by default'), '#default_value' => ADVPOLL_WRITEINS, '#description' => t('Allow voters with the "add write-ins" permission to write-in up to one choice each. Users with the administer polls permission will be able to override this setting.'), '#attributes' => array('class' => 'settings-writeins'), ); $form['advpoll']['advpoll_show_writeins'] = array( '#type' => 'checkbox', '#title' => t('Display write-in votes as choices for future voters by default'), '#default_value' => ADVPOLL_SHOW_WRITEINS, '#description' => t("Allow voters to see and choose from previous voters' write-in votes. Users with the administer polls permission will be able to override this setting."), '#prefix' => '
', '#suffix' => '
', ); $form['advpoll']['advpoll_electoral_list'] = array( '#type' => 'checkbox', '#title' => t('Use electoral list by default'), '#description' => t('Use an electoral list by default for new polls. Users with the administer polls permission will be able to override this setting.'), '#default_value' => ADVPOLL_ELECTORAL_LIST, ); $form['advpoll']['advpoll_show_votes'] = array( '#type' => 'checkbox', '#title' => t('Show individual votes by default'), '#description' => t('Let users with appropriate permissions see how each person voted by default for new polls. Users with the administer polls permission will be able to override this setting.'), '#default_value' => ADVPOLL_SHOW_VOTES, ); $view_results = array( 'always' => t('Always'), 'aftervote' => t('After user has voted'), 'afterclose' => t('After voting has closed'), ); $form['advpoll']['advpoll_view_results'] = array( '#type' => 'radios', '#title' => t('Display results'), '#description' => t('Determines when users may view the results of the poll.'), '#default_value' => (!empty($advpollSettings['show_results'])) ? $advpollSettings['show_results'] : 'aftervote', '#options' => $view_results, ); $form['advpoll']['advpoll_use_question'] = array( '#type' => 'checkbox', '#title' => t('Use question field'), '#description' => t('Use a dedicated question field instead of a combined question and title field. It is recommended to rename Title field label above to "Title" if this is checked.'), '#default_value' => ADVPOLL_USE_QUESTION, ); } } } /** * Implementation of hook_load(). * * Load the votes and poll-specific data into the node object. */ function advpoll_load($node) { $poll = db_fetch_object(db_query('SELECT * FROM {advpoll} WHERE nid = %d', $node->nid)); $result = db_query('SELECT cid, weight, label, writein FROM {advpoll_choices} WHERE nid = %d ORDER BY weight', $node->nid); $poll->choice = array(); $poll->writein_choices = 0; while ($choice = db_fetch_array($result)) { $poll->choice[$choice['cid']] = $choice; if ($choice['writein'] == 1) { $poll->writein_choices++; } } $poll->choices = count($poll->choice); $result = db_query("SELECT value FROM {votingapi_cache} WHERE content_type = 'advpoll' AND content_id = %d AND tag = '_advpoll' AND function = 'total_votes'", $node->nid); if ($cache = db_fetch_object($result)) { // Found total votes in the cache. $poll->votes = $cache->value; } else { $poll->votes = 0; } list($poll->voted, $poll->cancel_vote) = _advpoll_user_voted($node->nid); return $poll; } /** * Implementation of hook_validate(). * * Validate the editing of an advpoll node. */ function advpoll_validate($node, &$form) { // Use form_set_error for any errors. $node->choice = array_values($node->choice); // TODO: verify if this hack is still needed in Drupal 6. // Start keys at 1 rather than 0. array_unshift($node->choice, ''); unset($node->choice[0]); // Check for at least two choices. $real_choices = 0; // TODO: take out _POST foreach ($_POST['choice'] as $i => $choice) { if ($choice['label'] != '') { $real_choices++; } } if ($real_choices < 2) { form_set_error("choice][$real_choices][label", t('You must fill in at least two choices.')); } // Validate max choices since it has #DANGEROUS_SKIP_CHECK set to true. if ($node->settings['max_choices'] < 0) { form_set_error('settings][max_choices]', t('Maximum choices must be a non-negative integer.')); } if ($node->settings['max_choices'] > $real_choices) { form_set_error('settings][max_choices]', t('Maximum choices cannot be larger than the number of choices submitted.')); } // Validate dates. if (!empty($node->settings['start_date']) && strtotime($node->settings['start_date']) <= 0) { form_set_error('settings][start_date', t('You have to specify a valid starting date.')); } if (!empty($node->settings['end_date']) && strtotime($node->settings['end_date']) <= 0) { form_set_error('settings][end_date', t('You have to specify a valid ending date.')); } if (!empty($node->settings['end_date']) && $node->settings['end_date'] < $node->settings['start_date']) { form_set_error('settings][end_date', t('Ending date cannot be before the starting date.')); } } /** * Implementation of hook_insert(). * * This is called upon node creation. */ function advpoll_insert($node) { $mode = _advpoll_get_mode($node->type); if ($mode) { db_query("INSERT INTO {advpoll} (nid, mode, use_list, active, max_choices, algorithm, show_votes, create_view_block, start_date, end_date, writeins, show_writeins, footer_message) VALUES (%d, '%s', %d, %d, %d, '%s', %d, %d, '%s', '%s', %d, %d, '%s')", $node->nid, $mode, $node->settings['use_list'], !$node->settings['close'], $node->settings['max_choices'], $node->settings['algorithm'], $node->settings['show_votes'], $node->settings['create_view_block'], $node->settings['start_date'] ? strtotime($node->settings['start_date']) : 0, $node->settings['end_date'] ? strtotime($node->settings['end_date']) : 0, $node->settings['writeins'], $node->settings['show_writeins'], isset($node->footer_message) ? filter_xss($node->footer_message) : '' ); // Insert the choices. _advpoll_insert_choices($node); } } /** * Implementation of hook_update(). * * This is called upon node edition. */ function advpoll_update($node) { db_query("UPDATE {advpoll} SET active = %d, max_choices = %d, algorithm = '%s', use_list = %d, show_votes = %d, create_view_block = %d, start_date = '%s', end_date = '%s', writeins = %d, show_writeins = %d, footer_message = '%s' WHERE nid = %d", !$node->settings['close'], $node->settings['max_choices'], $node->settings['algorithm'], $node->settings['use_list'], $node->settings['show_votes'], $node->settings['create_view_block'], $node->settings['start_date'] ? strtotime($node->settings['start_date']) : 0, $node->settings['end_date'] ? strtotime($node->settings['end_date']) : 0, $node->settings['writeins'], $node->settings['show_writeins'], isset($node->footer_message) ? filter_xss($node->footer_message) : '', $node->nid); _advpoll_insert_choices($node); votingapi_recalculate_results('advpoll', $node->nid); } /** * Implementation of hook_view(). */ function advpoll_view($node, $teaser = FALSE, $page = FALSE) { drupal_add_css(drupal_get_path('module', 'advpoll') . '/advpoll.css', 'module'); $status = _advpoll_is_active($node, TRUE); $displayResults = _advpoll_check_settings($node); //print 'display results '.$displayResults; if ($node->build_mode == NODE_BUILD_PREVIEW || !$displayResults) { static $add_js = TRUE; if ($add_js) { // Add javascript for posting voting forms with Ajax. drupal_add_js(drupal_get_path('module', 'advpoll') . '/advpoll-vote.js', 'module'); drupal_add_js('misc/jquery.form.js', 'module'); $add_js = FALSE; } // Previewing a node, so display voting form instead of results. $mode = _advpoll_get_mode($node->type); if ($mode) { $poll = drupal_get_form('advpoll_voting_' . $mode . '_form', $node, $teaser, $page, $status); } } else if (!$node->voted && arg(2) != 'results' && ($status == 'open' || $status == 'pending') && $displayResults) { // OLD: else if ((!$node->voted && arg(2) != 'results' && ($status == 'open' || $status == 'pending')) || $_POST['op'] == 'Vote') { // User hasn't voted, we're not on the results tab and poll is open or // opening in the future. Also, we check the $_POST array if the user tried // to submit a vote, so we can validate and give an error if the user has // already voted on the poll. $poll = drupal_get_form('advpoll_voting_' . $node->mode . '_form', $node, $teaser, $page, $status); static $add_js = TRUE; if ($add_js) { // Add javascript for posting voting forms with Ajax. drupal_add_js(drupal_get_path('module', 'advpoll') . '/advpoll-vote.js', 'module'); drupal_add_js('misc/jquery.form.js', 'module'); $add_js = FALSE; } } else { // Show results (the user has voted, poll is closed or poll has passed). if (user_access('show vote results')) { $poll = advpoll_view_results($node, $teaser, $page); } elseif (user_access('cancel own vote')) { $poll = _advpoll_show_cancel_form($node); } } $node->content['poll'] = array( '#weight' => 2, '#value' => $poll, ); return node_prepare($node, $teaser); } /** * Implementation of hook_delete(). */ function advpoll_delete($node) { db_query('DELETE FROM {advpoll} WHERE nid = %d', $node->nid); db_query('DELETE FROM {advpoll_choices} WHERE nid = %d', $node->nid); db_query('DELETE FROM {advpoll_electoral_list} WHERE nid = %d', $node->nid); // TODO: These should be converted to a votingapi method eventually. db_query("DELETE FROM {votingapi_vote} WHERE content_id = %d AND content_type = 'advpoll'", $node->nid); db_query("DELETE FROM {votingapi_cache} WHERE content_id = %d AND content_type = 'advpoll'", $node->nid); } /** * Implementation of VotingAPI's hook_calculate. * * Recalculate results whenever a vote is added or removed. */ function advpoll_votingapi_results_alter(&$cache, $content_type, $content_id) { // Make sure it's an Advanced Poll content type. if ($content_type == 'advpoll') { // Don't load the node from cache in case the mode or algorithm changed. $node = node_load($content_id, NULL, TRUE); $mode = _advpoll_get_mode($node->type); $function = 'advpoll_calculate_results_' . $mode; if (function_exists($function)) { $function($cache, $node); } cache_clear_all(); } } /** * Check if a user has voted on a poll. * * @return Array indicating if user voted and, if so, if the vote is cancellable. */ function _advpoll_user_voted($nid) { global $user; $voted = FALSE; $cancel_vote = FALSE; if ($user->uid) { // Voter is logged in. $voted = count(votingapi_select_votes(array('uid' => $user->uid, 'content_id' => $nid))); if ($voted) { $cancel_vote = TRUE; } } else { // Voter is anonymous. $voted = count(votingapi_select_votes(array('vote_source' => ip_address(), 'content_id' => $nid, 'uid' => 0))); if ($voted) { // Found a vote in the database. $cancel_vote = TRUE; } } return array($voted, $cancel_vote); } /** * Build electorial list form. */ function advpoll_electoral_list_form(&$form_state, $nid) { $form['electoral_list'] = array( '#type' => 'fieldset', '#tree' => TRUE, '#title' => t('Administer electoral list'), '#collapsible' => TRUE, '#weight' => 2, ); $form['electoral_list']['add_user'] = array( '#type' => 'textfield', '#title' => t('Add user'), '#size' => 40, '#description' => t('Add an individual user to the electoral list.'), ); // Enable autocompletion if user has required permission. if (user_access('access user profiles')) { $form['electoral_list']['add_user']['#autocomplete_path'] = 'user/autocomplete'; } // List all roles with "vote on polls" permission, but don't include anonymous users. $result = db_query("SELECT r.name, r.rid FROM {role} r LEFT JOIN {permission} p ON p.rid = r.rid WHERE p.perm LIKE '%vote on polls%' AND r.rid <> 1 ORDER BY r.name"); $role_options = array(0 => t('(Select a role)')); while ($role = db_fetch_object($result)) { $role_options[$role->rid] = $role->name; } $form['electoral_list']['add_role'] = array( '#type' => 'select', '#title' => t('Add users by role'), '#description' => t('Only roles that have the "vote on polls" permission are listed.'), '#options' => $role_options, ); $form['electoral_list']['submit'] = array( '#type' => 'submit', '#value' => t('Add to electoral list'), ); $form['electoral_list']['reset'] = array( '#type' => 'button', '#value' => t('Clear electoral list'), ); $form['nid'] = array('#type' => 'hidden', '#value' => $nid); return $form; } /** * Validate changes to the electoral list. */ function advpoll_electoral_list_form_validate($form, &$form_state) { if ($form_state['values']['op'] == t('Clear electoral list')) { if (user_access('administer polls')) { db_query('DELETE FROM {advpoll_electoral_list} WHERE nid = %d', $form_state['values']['nid']); drupal_set_message(t('Electoral list cleared.')); return; } } $add_user = $form_state['values']['electoral_list']['add_user']; if ($add_user) { // Check that the user exists $result = db_query("SELECT uid FROM {users} WHERE name = '%s'", $add_user); if (!db_fetch_object($result)) { form_set_error('electoral_list][add_user', t('User %user does not exist.', array('%user' => $add_user))); return FALSE; } } } /** * Process advpoll_electorial_list form submissions. * * Submit changes to the electoral list. */ function advpoll_electoral_list_form_submit($form, &$form_state) { $add_user = $form_state['values']['electoral_list']['add_user']; if ($add_user) { db_query("REPLACE INTO {advpoll_electoral_list} (nid, uid) SELECT '%d', u.uid FROM {users} u WHERE u.name = '%s'", $form_state['values']['nid'], $add_user); drupal_set_message(t('%user added to electoral list.', array('%user' => $add_user))); } $add_role = $form_state['values']['electoral_list']['add_role']; if ($add_role) { // Get the current electoral list. $result = db_query('SELECT uid FROM {advpoll_electoral_list} WHERE nid = %d', $form_state['values']['nid']); $current_list = array(0); while ($user = db_fetch_object($result)) { $current_list[] = $user->uid; } $user_in_string = implode(',', $current_list); // Check if all authenticated users should be added. $is_authenticated = db_result(db_query("SELECT COUNT(*) FROM {role} r WHERE r.name = 'authenticated user' AND r.rid = %d", $add_role)); if ($is_authenticated) { // Special case: any authenticated user can vote. // Add all current users to electoral list. $result = db_query("INSERT INTO {advpoll_electoral_list} (nid, uid) SELECT '%d', u.uid FROM {users} u WHERE u.uid NOT IN('%s')", $form_state['values']['nid'], $user_in_string); } else { // Insert new users into the electoral_list based on the role chosen. $result = db_query("INSERT INTO {advpoll_electoral_list} (nid, uid) SELECT '%d', u.uid FROM {users} u LEFT JOIN {users_roles} ur ON u.uid = ur.uid WHERE ur.rid = %d AND u.uid NOT IN('%s')", $form_state['values']['nid'], $add_role, $user_in_string); } drupal_set_message(format_plural(db_affected_rows($result), 'Added 1 user to the electoral list.', 'Added @count users to the electoral list.')); } } /** * Helper function to abstract view results checking. */ function _advpoll_can_view_results($node) { $advpollSettings = variable_get('advpoll_settings', array()); $view_results = (!empty($advpollSettings['show_results'])) ? $advpollSettings['show_results'] : 'aftervote'; return (!_advpoll_is_active($node) // Node is closed || ($node->voted && $view_results == 'aftervote') // User voted || ($view_results == 'always')); // All can view } /** * Helper function to display 'cancel vote' button if user has voted. */ function advpoll_cancel_form(&$form_state, $nid) { $form['#action'] = url('advpoll/cancel/' . $nid); $form['submit'] = array('#type' => 'submit', '#value' => t('Cancel your vote')); return $form; } /** * Helper function to check if a poll is active. */ function _advpoll_is_active($node, $return_status = FALSE) { $active = TRUE; $status = 'open'; $start_date = $node->start_date; $end_date = $node->end_date; // Check if poll is closed. if (!$node->active) { $active = FALSE; $status = 'closed'; } if ($active && $start_date > 0) { // Check that start date is in the past. if (!$active = time() >= $start_date) { $status = 'pending'; } } if ($active && ($end_date > 0)) { // Check that end date is in the future. if (!$active = time() < $end_date) { $status = 'passed'; } } return $return_status ? $status : $active; } /** * Process advpoll_binary_node form submissions. * * Update the choices added or removed when editing a binary poll */ function advpoll_binary_node_form_submit($form, &$form_state) { // Get the submitted (modified) choices from _POST and replace them in the form values // Note: we pass choice data via _POST to allow for dynamic addition of choices. // TODO: Implement AHAH support for advpoll node form if (isset($_POST['choice'])) { $form_state['values']['choice'] = $_POST['choice']; } } /** * Process advpoll_ranking_node form submissions. * * Update the choices added or removed when editing a ranking poll */ function advpoll_ranking_node_form_submit($form, &$form_state) { // Get the submitted (modified) choices from _POST and replace them in the form values // Note: we pass choice data via _POST to allow for dynamic addition of choices. // TODO: Implement AHAH support for advpoll node form if (isset($_POST['choice'])) { $form_state['values']['choice'] = $_POST['choice']; } } /** * Insert/update the choices for a poll. * * Note: we pass choice data via _POST to allow for dynamic addition of choices. * Drupal 6 AHAH support will let us switch to a clean implementation. */ function _advpoll_insert_choices($node) { $nid = $node->nid; $weight = 0; $seen_ids = array(); foreach ($node->choice as $index => $choice) { if ($choice['label'] != '') { // Mark this choice id as being seen. $choice_exist = db_result(db_query("SELECT COUNT(cid) FROM {advpoll_choices} WHERE nid = %d AND cid = %d", $nid, $index)); if (!$choice_exist) { db_query("INSERT INTO {advpoll_choices} (nid, label, weight) VALUES (%d, '%s', %d)", $nid, $choice['label'], $weight); $seen_id = db_last_insert_id('advpoll_choices', 'cid'); } else { db_query("UPDATE {advpoll_choices} SET label = '%s', weight = %d WHERE cid = %d", $choice['label'], $weight, $index); $seen_id = $index; } $seen_ids[$seen_id] = 1; $weight++; } } $all_ids_query = db_query("SELECT cid, label FROM {advpoll_choices} WHERE nid = %d", $nid); $all_ids = array(); while ($row = db_fetch_array($all_ids_query)) { $all_ids[$row['cid']] = array('label' => $row['label']); } // Delete any choices that were removed (either dynamically or by submitting // a blank label). if (isset($node->choice)) { foreach ($all_ids as $id => $choice) { if (!isset($seen_ids[$id])) { db_query('DELETE FROM {advpoll_choices} WHERE cid = %d', $id); drupal_set_message(t('Deleted choice %label', array('%label' => $choice['label'])), 'status'); // We could potentially also delete any votes for this choice, but let's // leave them in the database so that one can go back and check if anyone // voted for a deleted choice. } } } } function _advpoll_get_mode($node_type) { if ($node_type) { $mode = explode('advpoll_', $node_type, 2); if ($mode) { return $mode[1]; } else { return ''; } } } /** * Minimal clear votes form for the votes tab. */ function advpoll_clear_votes_form(&$form_state, $nid) { $form = array(); $form['reset'] = array( '#value' => t('Clear all votes'), '#type' => 'submit', ); $form['#redirect'] = 'node/' . $nid . '/votes/clear'; return $form; } /** * Display a clear votes confirmation form. */ function advpoll_clear_votes_confirm_form(&$form_state, $nid) { $node = node_load($nid); $form = array(); $form['#nid'] = $node->nid; $confirm_question = t('Are you sure you want to clear all votes for %title?', array('%title' => $node->title)); $form['question'] = array('#value' => '

' . $confirm_question . '

'); $form = confirm_form($form, $confirm_question, 'node/' . $node->nid . '/votes', t('This will delete all votes that have been cast for the poll.'), t('Clear all votes'), t('Cancel')); // Override the default theming of confirmation forms. // TODO: need to theme this form better. unset($form['#theme']); return $form; } /** * Process advpoll_clear_votes_confirm form submissions. * * Update the choices added or removed when editing a node * Clear all votes once the confirmation is given. */ function advpoll_clear_votes_confirm_form_submit($form, &$form_state) { if ($form_state['values']['confirm']) { $nid = $form['#nid']; if ($node = node_load($nid)) { // Delete any votes for the poll. db_query("DELETE FROM {votingapi_vote} WHERE content_type = 'advpoll' AND content_id = %d", $node->nid); // Delete any write-in choices. db_query('DELETE FROM {advpoll_choices} WHERE writein = 1 AND nid = %d', $node->nid); votingapi_recalculate_results('advpoll', $node->nid); drupal_set_message(t('Votes have been cleared.')); watchdog('content', 'Cleared all poll votes (%num_votes).', array('%num_votes' => $node->votes), WATCHDOG_NOTICE, l(t('view'), 'node/' . $node->nid)); } $form_state['redirect'] = 'node/' . $node->nid; } } function _advpoll_block_resultslink($node) { return array( 'title' => t('Results'), 'href' => 'node/' . $node->nid . '/results', 'attributes' => array('title' => t('View the current poll results.')), ); } function theme_advpoll_results($title, $results, $votes, $links, $nid, $voted, $cancel_vote, $footer_message) { $output = '
'; if ($results) { $output .= $results; $output .= '
' . t('Total votes: %votes', array('%votes' => $votes)) . '
'; } else { $output .= '

' . t('No votes have been recorded for this poll.') . '

'; } if ($footer_message) { $output .= ''; } $output .= '
'; return $output; } function _advpoll_show_cancel_form($node) { // temporary - prevent cancel vote button from displaying if it is flagged to be displayed in a block // Need to fix the way AJAX is being implemented. $output = ''; if ($node->voted && !$node->create_view_block && $node->cancel_vote && user_access('cancel own vote') && _advpoll_is_active($node)) { $output .= drupal_get_form('advpoll_cancel_form', $node->nid); } return $output; } function theme_advpoll_bar($title, $percentage, $votes, $choice = NULL) { $output = '
' . $title . ($choice && $choice['writein'] ? ' ' . t('(write-in)') : '') . '
'; $output .= '
'; $output .= '
' . $percentage . '% (' . $votes . ')
'; return $output; } function _advpoll_vote_response($node, $form_state) { $advpollSettings = variable_get('advpoll_settings', array()); $msg = (!empty($advpollSettings['vote_registered'])) ? $advpollSettings['vote_registered'] : t('Your vote was registered.'); $votingMode = $advpollSettings['voting_mode']; $displayResults = _advpoll_check_settings($node); // Ajax response if ($form_state['values']['ajax']) { // Do not show results (there are cases where people can vote but shouldn't see a result // but should still see the poll with appropriate messaging // Unset the array of choices so duplicates aren't shown. unset($node->choice); // Get all choices from database. This is necessary to get information about // newly submitted write-in choices. $result = db_query('SELECT cid, weight, label, writein FROM {advpoll_choices} WHERE nid = %d ORDER BY weight', $node->nid); while ($choice = db_fetch_array($result)) { $node->choice[$choice['cid']] = $choice; } // Update the number of choices. $node->choices = count($node->choice); // Get updated total number of votes from database. $result = db_query("SELECT value FROM {votingapi_cache} WHERE content_type = 'advpoll' AND content_id = %d AND tag = '_advpoll' AND function = 'total_votes'", $node->nid); if ($cache = db_fetch_object($result)) { $node->votes = $cache->value; } else { $node->votes = 0; } list($node->voted, $node->cancel_vote) = _advpoll_user_voted($node->nid); $ajax_output = ''; if (user_access('show vote results')) { $ajax_output .= advpoll_view_results($node, NULL, NULL); } elseif (user_access('cancel own vote') && !$votingMode) { $ajax_output .= _advpoll_show_cancel_form($node); } // Remove linebreaks as they will break jQuery's insert-HTML methods. $ajax_output = str_replace("\n", '', $ajax_output); drupal_set_header('Content-Type: text/plain; charset=utf-8'); print drupal_to_js(array('statusMsgs' => '
' . $msg . '
', 'response' => $ajax_output)); exit(); } // Usual response. else { drupal_set_message($msg); } } /** * Show results of the vote. * * This calls the appropriate vote results function, depending on the * mode. It will call advpoll_view_results_$mode, similarly to * advpoll_view_voting(). */ function advpoll_view_results(&$node, $teaser, $page) { $output = ''; $mode = _advpoll_get_mode($node->type); //if (_advpoll_can_view_results($node)) { if (function_exists('advpoll_view_results_' . $mode)) { $results = call_user_func('advpoll_view_results_' . $mode, $node, $teaser, $page); $output .= theme('advpoll_results', check_plain($node->title), $results['results'], $results['votes'], isset($node->links) ? $node->links : array(), $node->nid, $node->voted, $node->cancel_vote, $node->footer_message); } $output .= _advpoll_show_cancel_form($node); return $output; } /** * Check if user is eligible to vote in this poll. */ function advpoll_eligible($node, $uid = NULL) { global $user; if (!isset($uid)) { $uid = $user->uid; } if ($node->use_list) { $eligible = db_result(db_query('SELECT COUNT(*) FROM {advpoll_electoral_list} WHERE nid = %d AND uid = %d', $node->nid, $uid)); } else { $eligible = user_access('vote on polls'); } return $eligible; } function advpoll_algorithms($mode) { return call_user_func('advpoll_algorithms_' . $mode); } /** * Load the available modes. * * This scans the modes subdirectory to find mode.inc files, where * mode is considered to be the mode name. Found files are loaded and * added to the mode list. */ function _advpoll_list_modes() { static $advpoll_modes; if (!$advpoll_modes) { $files = file_scan_directory(dirname(__FILE__) . '/modes/', '^([^\.].*)\.inc$', array('.', '..', 'CVS'), 0, FALSE); foreach ($files as $file) { require_once($file->filename); $mode = $file->name; if (function_exists('advpoll_info_' . $mode)) { $advpoll_modes[$mode] = call_user_func('advpoll_info_' . $mode); } } } return $advpoll_modes; } /** * Helper function for rich text in choices. * * We strip out the paragraphs which are not allowed within the label element * and created by check_markup() when "Line break converter"-filter is used. */ function _advpoll_choice_markup($text, $format = FILTER_FORMAT_DEFAULT, $check = TRUE) { $text = check_markup($text, $format, $check); $text = str_replace(array('

', '

'), '', $text); $text = trim($text); return $text; } /** * Wrapper function around form_set_error() to support validating of Ajax voting. */ function _advpoll_form_set_error($name = NULL, $message = '', $ajax = FALSE) { if ($ajax) { drupal_set_header('Content-Type: text/plain; charset=utf-8'); print drupal_to_js(array('errors' => '
' . $message . '
')); exit; } else { return form_set_error('choice[', $message); } } /** * Voting form validation logic specific to writeins. This has been abstracted * away from includes in the modes directory. */ function _advpoll_writeins_voting_form_validate($node, $writein_option, $writein_text, $ajax) { // Do write-in specific checks if write-ins are enabled and user has permission. if ($node->writeins && user_access('add write-ins')) { // Something is in the write-in textbox. if ($writein_text) { $writein_choice_lower = strtolower($writein_text); foreach ($node->choice as $i => $value) { // Check that user isn't writing in an existing visible choice. (User is // writing in an existing choice and either write-ins are all being // displayed or the existing choice is not a write-in). if ((strtolower($value['label']) == $writein_choice_lower) && ($node->show_writeins || !$value['writein'])) { _advpoll_form_set_error('writein_choice', t("A write-in vote can not be for an existing choice. Select the choice's option instead."), $ajax); } } } // The write-in option is selected and there is nothing in the write-in textbox. if ($writein_option && !$writein_text) { _advpoll_form_set_error('writein_choice', t('If the "write-in" option is selected, a choice must be written in.'), $ajax); } // The write-in option is not selected, but there is something in the write-in textbox. if (!$writein_option && $writein_text) { _advpoll_form_set_error('writein_choice', t('If a choice is written in, the "write-in" option must be selected.'), $ajax); } } } /** * Handle JavaScript voting. */ function advpoll_js_vote() { /* still trying to figure this out */ } /** * Voting form submission logic specific to writeins. This has been abstracted * away from includes in the modes directory. */ function _advpoll_writeins_voting_form_submit($node, $form_state, &$votes, $vote_value = 1) { // A write-in vote is being made. if (isset($form_state['values']['writein_choice']) && $form_state['values']['writein_choice']) { // Check if someone has previously voted for this choice. $result = db_query("SELECT cid FROM {advpoll_choices} WHERE nid = %d AND LOWER(label) = LOWER('%s')", $node->nid, $form_state['values']['writein_choice']); $db_vote = db_fetch_object($result); // If there's more than one match, redo the query, being more exact. if ($db_vote && $second_vote = db_fetch_object($result)) { $result = db_query("SELECT cid FROM {advpoll_choices} WHERE nid = %d AND label = '%s'", $node->nid, $form_state['values']['writein_choice']); $db_vote = db_fetch_object($result); } // If there is at least one match, add a vote for the first one returned. It // should be rare to find more than one choice for any one node with a given // label. if ($db_vote) { $existing_choice = $db_vote->cid; // Set a vote $vote = array(); // TODO: confirm this works for ranking polls. $vote['value'] = $vote_value; $vote['tag'] = $existing_choice; $vote['value_type'] = 'option'; $vote['content_type'] = 'advpoll'; $vote['content_id'] = $node->nid; $vote = _advpoll_votesettings($vote, $node->nid); $votes[] = $vote; } // This write-in choice has not been previously voted for. else { // Get highest weight for this node. $highest_weight = db_result(db_query("SELECT MAX(weight) FROM {advpoll_choices} WHERE nid = %d", $node->nid)); $next_weight = $highest_weight ? $highest_weight + 1 : 1; // Insert new choice. We do a check_plain() on the label to ensure that // there is no possibility of insecure data getting into the database; // if HTML is needed the admin can edit the writein choice manually. db_query("INSERT INTO {advpoll_choices} (nid, label, weight, writein) VALUES (%d, '%s', %d, 1)", $node->nid, check_plain($form_state['values']['writein_choice']), $next_weight); // Get newest choice id. $next_cid = db_result(db_query("SELECT cid FROM {advpoll_choices} WHERE nid = %d AND label = '%s'", $node->nid, $form_state['values']['writein_choice'])); // Add vote $vote = array(); // TODO: confirm this works for ranking polls. $vote['value'] = $vote_value; $vote['tag'] = $next_cid; $vote['value_type'] = 'option'; $vote['content_type'] = 'advpoll'; $vote['content_id'] = $node->nid; $vote = _advpoll_votesettings($vote, $node->nid); $votes[] = $vote; } } } function advpoll_writein_merge_form(&$form_state, $node) { $form = array(); $form['fieldset'] = array( '#type' => 'fieldset', '#collapsible' => FALSE, '#title' => t('Merge write-ins'), ); $form['fieldset']['note'] = array( '#value' => '
' . t('This will delete the write-in and change any votes for it into votes for the selected chocie.') . '
', ); $form['fieldset']['merge'] = array( '#prefix' => '
' . t('Merge') . ' ', '#suffix' => '
', ); $writein_list = array(); $choice_list = array(); foreach ($node->choice as $index => $choice) { $choice_list[$index] = $choice['label']; if ($choice['writein']) { $writein_list[$index] = $choice['label']; } } $form['fieldset']['merge']['source'] = array( '#type' => 'select', '#options' => $writein_list, ); $form['fieldset']['merge']['into'] = array( '#value' => t(' into '), ); $form['fieldset']['merge']['destination'] = array( '#type' => 'select', '#options' => $choice_list, ); $form['fieldset']['merge']['submit'] = array( '#type' => 'submit', '#value' => t('Merge'), ); $form['nid'] = array( '#type' => 'value', '#value' => $node->nid, ); $form['#node'] = $node; return $form; } function advpoll_writein_merge_form_validate($form, &$form_state) { if ($form_state['values']['source'] == $form_state['values']['destination']) { form_set_error('destination', t('The write-in cannot be merged into itself.')); } } /** * Process advpoll_writein_merge form submissions. */ function advpoll_writein_merge_form_submit($form, &$form_state) { // Get a list of votes in this node. $node = $form['#node']; $raw_votes = db_query('SELECT * FROM {votingapi_vote} WHERE content_id = %d', $form_state['values']['nid']); $voters = array(); $affected_voters = array(); while ($vote = db_fetch_object($raw_votes)) { $key = $vote->uid . '-' . $vote->vote_source; if (!isset($voters[$key])) { $voters[$key] = array(); } array_push($voters[$key], $vote); if ($vote->tag == $form_state['values']['source']) { // This voter is affected by the merge; save the index of the source vote. $affected_voters[$key] = count($voters[$key]) - 1; } } // Now fix the affected voters. foreach ($affected_voters as $key => $source_index) { // Find out if they voted for the destination or not. $voted_for_destination = FALSE; foreach ($voters[$key] as $index => $vote) { if ($vote->tag == $form_state['values']['destination']) { $voted_for_destination = TRUE; break; } } if ($voted_for_destination) { // Since they already voted for the destination choice, delete the vote // for the source. db_query('DELETE FROM {votingapi_vote} WHERE vote_id = %d AND tag = %d', $voters[$key][$index]->vote_id, $form_state['values']['source']); } else { // They didn't already vote for the destination, so transfer the vote for // the source to the destination. db_query('UPDATE {votingapi_vote} SET tag = %d WHERE vote_id = %d AND tag = %d', $form_state['values']['destination'], $voters[$key][$index]->vote_id, $form_state['values']['source']); } } // Delete the merged choice. db_query('DELETE FROM {advpoll_choices} WHERE cid = %d', $form_state['values']['source']); votingapi_recalculate_results('advpoll', $form_state['values']['nid']); drupal_set_message(t('Write-in merged.')); // Unset destination form element so that drupal_goto() doesn't use it // mistakenly. unset($_REQUEST['destination']); $writeins_remaining = FALSE; foreach ($node->choice as $choice) { if ($choice['writein'] && $choice['cid'] != $form_state['values']['source']) { $writeins_remaining = TRUE; break; } } drupal_goto('node/' . $form_state['values']['nid'] . ($writeins_remaining ? '/writeins' : '')); } function advpoll_writein_promote_form(&$form_state, $node) { $form = array(); $form['fieldset'] = array( '#type' => 'fieldset', '#collapsible' => FALSE, '#title' => t('Promote write-ins'), ); $form['fieldset']['note'] = array( '#value' => '

' . t('Write-ins can be converted to regular choices. This is useful if users cannot see past write-ins but you want to promote specific write-ins so that they can be seen by users who vote in the future.') . '

', ); $writein_list = array(); foreach ($node->choice as $index => $choice) { if ($choice['writein']) { $writein_list[$index] = $choice['label']; } } $form['fieldset']['promote'] = array( '#type' => 'checkboxes', '#options' => $writein_list, ); $form['fieldset']['submit'] = array( '#type' => 'submit', '#value' => t('Promote'), ); $form['nid'] = array( '#type' => 'value', '#value' => $node->nid, ); return $form; } /** * Process advpoll_writein_promote form submissions. */ function advpoll_writein_promote_form_submit($form, &$form_state) { if (count($form_state['values']['promote'])) { db_query('UPDATE {advpoll_choices} SET writein = 0 WHERE nid = %d AND cid IN(%s)', $form_state['values']['nid'], implode(', ', $form_state['values']['promote'])); drupal_set_message(format_plural(count($form_state['values']['promote']), 'Write-in promoted.', 'Write-ins promoted.')); } drupal_goto('node/' . $form_state['values']['nid']); } /** * Implementation of hook_theme_registry_alter(). */ function advpoll_theme_registry_alter(&$theme_registry) { // MW - added a node template to prevent clickable headline on polls being displayed in node view $theme_registry['node']['theme paths'][] = drupal_get_path('module', 'advpoll'); } /** * Implementation of hook_theme(). */ function advpoll_theme() { return array( 'advpoll_page' => array( 'arguments' => array('' => '') ), 'advpoll_block_latest_poll' => array( 'arguments' => array(), ), 'advpoll_view_block_poll' => array( 'arguments' => array( 'nid' => NULL, ), ), 'advpoll_results' => array( 'arguments' => array( 'title' => '', 'results' => '', 'votes' => '', 'links' => '', 'nid' => '', 'voted' => '', 'cancel_vote' => '', 'footer_message', ) ), 'advpoll_bar' => array( 'arguments' => array( 'title' => '', 'percentage' => '', 'votes' => '', 'choice' => NULL, ) ), 'advpoll_voting_ranking_form' => array( 'template' => 'advpoll-display-ranking-form', 'file' => 'modes/ranking.inc', 'arguments' => array( 'form' => NULL, ) ), 'advpoll_voting_binary_form' => array( 'template' => 'advpoll-display-binary-form', 'file' => 'modes/binary.inc', 'arguments' => array( 'form' => NULL, ) ), ); } /** * AHAH handling of the more choices button. * * Based on http://drupal.org/node/331941 */ function advpoll_js_more_choices() { // We're starting in step #3, preparing for #4. $form_state = array('storage' => NULL, 'submitted' => FALSE); $form_build_id = $_POST['form_build_id']; // Step #4. $form = form_get_cache($form_build_id, $form_state); // Preparing for #5. $args = $form['#parameters']; $form_id = array_shift($args); $form_state['post'] = $form['#post'] = $_POST; $form['#programmed'] = $form['#redirect'] = FALSE; // Step #5. drupal_process_form($form_id, $form, $form_state); // Step #6 and #7 and #8. $form = drupal_rebuild_form($form_id, $form_state, $args, $form_build_id); // Step #9. $choice_form = $form['choice_wrapper']['choice']; unset($choice_form['#prefix'], $choice_form['#suffix']); $output = theme('status_messages') . drupal_render($choice_form); // Final rendering callback. drupal_json(array('status' => TRUE, 'data' => $output)); } /** * Implementation of hook_views_api(). */ function advpoll_views_api() { return array( 'api' => 2, 'path' => drupal_get_path('module', 'advpoll') . '/views', ); } /** * Implementation of hook_votingapi_relationships(). */ function advpoll_votingapi_relationships() { $relationships[] = array( 'description' => t('Advanced Poll'), 'content_type' => 'advpoll', 'base_table' => 'node', 'content_id_column' => 'nid', ); return $relationships; } function _advpoll_votesettings($vote, $nid) { // These settings are established by the configuration form. // option 0 is default behavior, option 1 is poll availability via cookie // and option 2 is constant availability - user can vote again upon refresh $advpollSettings = variable_get('advpoll_settings', array()); $votingMode = $advpollSettings['voting_mode']; if ($votingMode) { $vote['uid'] = 0; $vote['vote_source'] = time(); if ($votingMode == 1) { $duration = (!empty($advpollSettings['cookie_duration'])) ? $advpollSettings['cookie_duration'] : 60; setcookie('advpollvote' . $nid, 'voted', time() + (60 * $duration)); } } return $vote; } function _advpoll_check_settings($node) { // function to check against admin settings only. $advpollSettings = variable_get('advpoll_settings', array()); $votingMode = (!empty($advpollSettings['voting_mode'])) ? $advpollSettings['voting_mode'] : '0'; $showResults = (!empty($advpollSettings['show_results'])) ? $advpollSettings['show_results'] : 'aftervote'; if ($showResults === 'afterclose') { if (!$node->active) { return true; } else { return false; } } // if admin configuration has selected cookies to track vote availability // then force display of results while cookie is alive. if ($votingMode == 1 && isset($_COOKIE['advpollvote' . $node->nid])) { return true; } if (!$votingMode && $node->voted) { return true; } return false; }