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;
}