Parent Directory
|
Revision Log
|
Revision Graph
#99252 Cast votes using Ajax; refactor some results handling and split javascript into two main files - patch by fajerstarter and me
| 1 | <?php |
| 2 | // $Id: advpoll.module,v 1.44 2006/11/28 06:54:15 chriskennedy Exp $ |
| 3 | /** |
| 4 | * @file |
| 5 | * Advanced Poll - a sophisticated polling module for voting, elections, and group decision-making. |
| 6 | */ |
| 7 | |
| 8 | |
| 9 | define('ADVPOLL_DEFAULT_ELECTORAL_LIST', 0); |
| 10 | // always, aftervote, or afterclose |
| 11 | define('ADVPOLL_DEFAULT_VIEW_RESULTS', 'aftervote'); |
| 12 | |
| 13 | /** |
| 14 | * Implementation of hook_access(). |
| 15 | */ |
| 16 | function advpoll_access($op, $node) { |
| 17 | global $user; |
| 18 | |
| 19 | if ($op == 'create') { |
| 20 | return user_access('create polls'); |
| 21 | } |
| 22 | if ($op == 'delete') { |
| 23 | return user_access('delete polls'); |
| 24 | } |
| 25 | if ($op == 'update') { |
| 26 | if (user_access('create polls') && ($user->uid == $node->uid)) { |
| 27 | return TRUE; |
| 28 | } |
| 29 | } |
| 30 | } |
| 31 | |
| 32 | /** |
| 33 | * Implementation of hook_block(). |
| 34 | */ |
| 35 | function advpoll_block($op = 'list', $delta = 'mostrecent', $edit = array()) { |
| 36 | switch($op) { |
| 37 | case 'list': |
| 38 | $blocks['mostrecent']['info'] = t('Advanced Poll - Newest'); |
| 39 | return $blocks; |
| 40 | case 'view': |
| 41 | if (user_access('view polls')) { |
| 42 | switch ($delta) { |
| 43 | case 'mostrecent': default: |
| 44 | $block['content'] = advpoll_block_mostrecent(); |
| 45 | $block['subject'] = t('Advanced Poll - Newest'); |
| 46 | } |
| 47 | |
| 48 | if ($block['content']) { |
| 49 | return $block; |
| 50 | } |
| 51 | } |
| 52 | default: |
| 53 | } |
| 54 | } |
| 55 | |
| 56 | /** |
| 57 | * Content of the block, as returned by advpoll_block('view') |
| 58 | */ |
| 59 | function advpoll_block_mostrecent() { |
| 60 | $output = ''; |
| 61 | $result = db_query('SELECT nid FROM {advpoll} WHERE active=1 ORDER BY nid DESC LIMIT 1'); |
| 62 | // Check that there is an active poll |
| 63 | if (db_num_rows($result) > 0) { |
| 64 | $poll = db_fetch_object($result); |
| 65 | $node = advpoll_view(node_load($poll->nid), FALSE, FALSE, TRUE); |
| 66 | $output = drupal_render($node->content); |
| 67 | } |
| 68 | else { |
| 69 | $output .= t('No active Advanced Poll.'); |
| 70 | } |
| 71 | return $output; |
| 72 | } |
| 73 | |
| 74 | /** |
| 75 | * |
| 76 | */ |
| 77 | function advpoll_page() { |
| 78 | // List all polls |
| 79 | $sql = 'SELECT n.nid, n.title, p.active, n.created, c.value AS votes FROM {node} n INNER JOIN {advpoll} p ON n.nid = p.nid INNER JOIN {votingapi_cache} c ON n.nid = c.content_id WHERE n.status = 1 AND c.tag = "_advpoll" AND c.function="total_votes" AND c.content_type="advpoll" GROUP BY n.nid, n.title, p.active, n.created ORDER BY n.created DESC'; |
| 80 | $sql = db_rewrite_sql($sql); |
| 81 | $result = pager_query($sql, 15); |
| 82 | $output = '<ul>'; |
| 83 | while ($node = db_fetch_object($result)) { |
| 84 | $output .= '<li>'. l($node->title, "node/$node->nid") .' - '. format_plural($node->votes, '1 vote', '@count votes') .' - '. ($node->active ? t('open') : t('closed')) .'</li>'; |
| 85 | } |
| 86 | $output .= '</ul>'; |
| 87 | $output .= theme('pager', NULL, 15); |
| 88 | return $output; |
| 89 | } |
| 90 | |
| 91 | /** |
| 92 | * Implementation of VotingAPI's hook_calculate |
| 93 | * Recalculate results whenever a vote is added or removed |
| 94 | */ |
| 95 | function advpoll_votingapi_calculate(&$results, $votes, $content_type, $content_id) { |
| 96 | // Make sure it's an Advanced Poll content type |
| 97 | if ($content_type == 'advpoll') { |
| 98 | $node = node_load($content_id); |
| 99 | $mode = _advpoll_get_mode($node); |
| 100 | $function = 'advpoll_calculate_results_' . $mode; |
| 101 | if (function_exists($function)) { |
| 102 | $function($results, $votes, $node); |
| 103 | } |
| 104 | cache_clear_all(); |
| 105 | } |
| 106 | } |
| 107 | |
| 108 | |
| 109 | /** |
| 110 | * Implementation of hook_cron(). |
| 111 | * |
| 112 | * Closes polls that have exceeded their allowed runtime. |
| 113 | */ |
| 114 | function advpoll_cron() { |
| 115 | // Open polls with a startdate that is in the past but that need to be opened |
| 116 | $sql = 'SELECT p.nid FROM {advpoll} p WHERE p.startdate IS NOT NULL AND (p.runtime = 0 OR p.startdate + p.runtime >= ' . (time() - time() % (3600 * 24)) . ') AND p.startdate < ' . time() . ' AND p.active = 0'; |
| 117 | $result = db_query($sql); |
| 118 | drupal_set_message($sql); |
| 119 | while ($poll = db_fetch_object($result)) { |
| 120 | db_query("UPDATE {advpoll} SET active = 1 WHERE nid=%d", $poll->nid); |
| 121 | } |
| 122 | |
| 123 | // Close polls |
| 124 | $sql = 'SELECT p.nid FROM {advpoll} p INNER JOIN {node} n ON p.nid = n.nid WHERE ((p.startdate IS NULL AND (n.created + p.runtime) < '. time() .') OR (p.startdate IS NOT NULL AND (p.startdate + p.runtime) < ' . time() . ')) AND p.active = 1 AND p.runtime != 0'; |
| 125 | $result = db_query($sql); |
| 126 | while ($poll = db_fetch_object($result)) { |
| 127 | db_query("UPDATE {advpoll} SET active = 0 WHERE nid=%d", $poll->nid); |
| 128 | } |
| 129 | } |
| 130 | |
| 131 | |
| 132 | /** |
| 133 | * Implementation of hook_delete(). |
| 134 | * |
| 135 | */ |
| 136 | function advpoll_delete($node) { |
| 137 | db_query("DELETE FROM {advpoll} WHERE nid = %d", $node->nid); |
| 138 | db_query("DELETE FROM {advpoll_choices} WHERE nid = %d", $node->nid); |
| 139 | db_query("DELETE FROM {advpoll_electoral_list} WHERE nid = %d", $node->nid); |
| 140 | |
| 141 | // Note: these should be converted to a votingapi method eventually |
| 142 | db_query('DELETE FROM {votingapi_vote} WHERE content_id = %d AND |
| 143 | content_type="advpoll"', $node->nid); |
| 144 | db_query('DELETE FROM {votingapi_cache} WHERE content_id = %d AND |
| 145 | content_type="advpoll"', $node->nid); |
| 146 | } |
| 147 | |
| 148 | /** |
| 149 | * Implementation of hook_form(). |
| 150 | * |
| 151 | * This hook displays the form necessary to create/edit the poll. |
| 152 | */ |
| 153 | function advpoll_form($node, $form_values = NULL) { |
| 154 | $mode = _advpoll_get_mode($node); |
| 155 | |
| 156 | // Only add javascript once, even if _form is called multiple times |
| 157 | static $add_js; |
| 158 | if (!$add_js) { |
| 159 | // Pass translatable strings |
| 160 | drupal_add_js(array('advPoll' => array('remove' => t('Remove'), 'addChoice' => t('Add choice'), 'noLimit' => t('No limit'))), 'setting'); |
| 161 | drupal_add_js(drupal_get_path('module', 'advpoll') .'/advpoll-form.js', 'module'); |
| 162 | drupal_add_css(drupal_get_path('module', 'advpoll') .'/advpoll.css', 'module'); |
| 163 | $add_js = true; |
| 164 | } |
| 165 | |
| 166 | $form['title'] = array( |
| 167 | '#type' => 'textfield', |
| 168 | '#title' => ucfirst($mode) .' '. t('question'), |
| 169 | '#required' => TRUE, |
| 170 | '#default_value' => $node->title, |
| 171 | ); |
| 172 | |
| 173 | $form['body'] = array( |
| 174 | '#type' => 'textarea', |
| 175 | '#title' => t('Description'), |
| 176 | '#required' => FALSE, |
| 177 | '#default_value' => $node->body, |
| 178 | ); |
| 179 | |
| 180 | if (isset($form_values)) { |
| 181 | $choices = $form_values['choices']; |
| 182 | if ($form_values['morechoices']) { |
| 183 | $choices *= 2; |
| 184 | } |
| 185 | } |
| 186 | else { |
| 187 | $choices = max(2, count($node->choice)? count($node->choice) : 5); |
| 188 | } |
| 189 | |
| 190 | $form['choices'] = array( |
| 191 | '#type' => 'hidden', |
| 192 | '#value' => $choices, |
| 193 | ); |
| 194 | |
| 195 | // Advanced Poll choices |
| 196 | $form['choice'] = array( |
| 197 | '#type' => 'fieldset', |
| 198 | '#title' => t('Poll choices'), |
| 199 | '#collapsible' => TRUE, |
| 200 | '#prefix' => '<div class="poll-form">', |
| 201 | '#suffix' => '</div>', |
| 202 | '#tree' => TRUE, |
| 203 | '#weight' => 1, |
| 204 | ); |
| 205 | |
| 206 | $form['choice']['morechoices'] = array( |
| 207 | '#type' => 'checkbox', |
| 208 | '#title' => t('Need more choices'), |
| 209 | '#value' => 0, |
| 210 | '#parents' => array('morechoices'), // don't pollute $form['choice'] |
| 211 | '#prefix' => '<div id="morechoices">', |
| 212 | '#suffix' => '</div>', |
| 213 | '#description' => t("If the amount of boxes above isn't enough, check this box and click the Preview button below to add some more."), |
| 214 | '#weight' => 1 |
| 215 | ); |
| 216 | |
| 217 | for ($a = 1; $a <= $choices; $a++) { |
| 218 | $form['choice'][$a]['label'] = array( |
| 219 | '#type' => 'textfield', |
| 220 | '#title' => t('Choice %n', array('%n' => $a)), |
| 221 | '#default_value' => $node->choice[$a]['label'], |
| 222 | '#attributes' => array('class' => 'choices'), |
| 223 | ); |
| 224 | } |
| 225 | |
| 226 | $form['settings'] = array( |
| 227 | '#type' => 'fieldset', |
| 228 | '#title' => t('Poll settings'), |
| 229 | '#collapsible' => TRUE, |
| 230 | '#weight' => 2, |
| 231 | '#tree' => TRUE, |
| 232 | ); |
| 233 | |
| 234 | $maxChoiceList = array(); |
| 235 | for ($i = 0; $i <= $choices; $i++) { |
| 236 | $maxChoiceList[$i] = ($i == 0? 'No limit' : $i); |
| 237 | } |
| 238 | |
| 239 | $form['settings']['maxchoices'] = array( |
| 240 | '#type' => 'select', |
| 241 | '#title' => t('Maximum choices'), |
| 242 | '#default_value' => ($node->maxchoices? $node->maxchoices : 0), |
| 243 | '#options' => $maxChoiceList, |
| 244 | '#DANGEROUS_SKIP_CHECK' => true, // allow jQuery to add new options |
| 245 | '#description' => t('Limits the total number of choices voters may select.') |
| 246 | ); |
| 247 | |
| 248 | $voting_algorithms = array(); |
| 249 | |
| 250 | foreach (advpoll_algorithms($mode) as $alg) { |
| 251 | $voting_algorithms[$alg] = ucfirst($alg); |
| 252 | } |
| 253 | |
| 254 | $defaultalg = ($form_values['settings']['algorithm']? |
| 255 | $form_values['settings']['algorithm'] : $node->algorithm); |
| 256 | |
| 257 | if (count($voting_algorithms) > 1) { |
| 258 | $form['settings']['algorithm'] = array( |
| 259 | '#type' => 'select', |
| 260 | '#title' => t('Algorithm'), |
| 261 | '#options' => $voting_algorithms, |
| 262 | '#default_value' => $defaultalg, |
| 263 | '#description' => 'Voting algorithm to use to calculate the winner.', |
| 264 | ); |
| 265 | } |
| 266 | else { |
| 267 | $form['settings']['algorithm'] = array( |
| 268 | '#type' => 'hidden', |
| 269 | '#value' => $defaultalg, |
| 270 | ); |
| 271 | } |
| 272 | |
| 273 | $active = array(1 => t('Active'), 0 => t('Closed')); |
| 274 | $form['settings']['active'] = array( |
| 275 | '#type' => 'radios', |
| 276 | '#title' => t('Status'), |
| 277 | '#options' => $active, |
| 278 | '#default_value' => (isset($node->active)? $node->active : 1), |
| 279 | '#description' => t('When a poll is closed users may no longer vote on it.'), |
| 280 | ); |
| 281 | |
| 282 | $_duration = array(0 => t('Unlimited')) + drupal_map_assoc(array(86400, 172800, 345600, 604800, 1209600, 2419200, 4838400, 9676800, 31536000), 'format_interval'); |
| 283 | |
| 284 | $form['settings']['usestart'] = array( |
| 285 | '#type' => 'checkbox', |
| 286 | '#title' => t('Use start date'), |
| 287 | '#description' => t('Specify a date that the poll opens.'), |
| 288 | '#default_value' => (isset($node->startdate)? TRUE : |
| 289 | ($form_values['startdate']? $form_values['startdate'] : FALSE)), |
| 290 | ); |
| 291 | |
| 292 | $date = $form_values['startdate']? $form_values['startdate'] : |
| 293 | $node->startdate? $node->startdate : time(); |
| 294 | |
| 295 | $form['settings']['startdate'] = array( |
| 296 | '#prefix' => '<div class="edit-settings-startdate">', |
| 297 | '#suffix' => '</div>', |
| 298 | '#type' => 'date', |
| 299 | '#title' => t('Starting date'), |
| 300 | '#description' => t('Date that the poll opens'), |
| 301 | '#default_value' => array('year' => date('Y', $date), 'month' => date('m', $date), 'day' => date('d', $date)), |
| 302 | ); |
| 303 | |
| 304 | $form['settings']['startdatejs'] = array( |
| 305 | '#type' => 'hidden', |
| 306 | '#default_value' => $date, |
| 307 | ); |
| 308 | |
| 309 | |
| 310 | |
| 311 | $form['settings']['runtime'] = array( |
| 312 | '#type' => 'select', |
| 313 | '#title' => t('Duration'), |
| 314 | '#default_value' => ($node->runtime? $node->runtime : 0), |
| 315 | '#options' => $_duration, |
| 316 | '#description' => t('After this period, the poll will be closed automatically. This is relative to the start date if it is specified, otherwise the date the poll was created.') |
| 317 | ); |
| 318 | |
| 319 | |
| 320 | $form['settings']['uselist'] = array( |
| 321 | '#type' => 'checkbox', |
| 322 | '#title' => t('Restrict voting to electoral list'), |
| 323 | '#description' => t('If enabled, a list of eligible voters will be created and only that group will be able to vote in the poll.'), |
| 324 | '#default_value' => isset($node->uselist)? $node->uselist : variable_get('advpoll_default_electoral_list', ADVPOLL_DEFAULT_ELECTORAL_LIST), |
| 325 | ); |
| 326 | |
| 327 | $form['settings']['showvotes'] = array( |
| 328 | '#type' => 'checkbox', |
| 329 | '#title' => t('Show individual votes'), |
| 330 | '#description' => t('Users with the appropriate permissions will be able to see how each person voted.'), |
| 331 | '#default_value' => isset($node->showvotes)? $node->showvotes : 1, |
| 332 | ); |
| 333 | |
| 334 | if (user_access('administer polls') && $node->nid) { |
| 335 | $form['settings']['reset'] = array( |
| 336 | '#type' => 'button', |
| 337 | '#value' => t('Reset votes'), |
| 338 | ); |
| 339 | } |
| 340 | |
| 341 | $form['format'] = filter_form($node->form); |
| 342 | $form['#multistep'] = TRUE; |
| 343 | return $form; |
| 344 | } |
| 345 | |
| 346 | |
| 347 | /** |
| 348 | * Implementation of hook_help(). |
| 349 | */ |
| 350 | function advpoll_help($section) { |
| 351 | switch ($section) { |
| 352 | case 'admin/modules#description': |
| 353 | return t('A sophisticated polling module for voting, elections, and group decision-making.'); |
| 354 | default: |
| 355 | } |
| 356 | } |
| 357 | |
| 358 | /** |
| 359 | * Implementation of hook_load(). |
| 360 | * |
| 361 | * Load the votes and poll-specific data into the node object. |
| 362 | */ |
| 363 | function advpoll_load($node) { |
| 364 | global $user; |
| 365 | $poll = db_fetch_object(db_query("SELECT * FROM {advpoll} WHERE nid = %d", $node->nid)); |
| 366 | $result = db_query("SELECT vote_offset, label FROM {advpoll_choices} WHERE nid = %d ORDER BY vote_offset", $node->nid); |
| 367 | while ($choice = db_fetch_array($result)) { |
| 368 | $poll->choice[$choice['vote_offset']] = $choice; |
| 369 | } |
| 370 | $poll->choices = count($poll->choice); |
| 371 | |
| 372 | $poll->voted = FALSE; |
| 373 | $poll->cancel_vote = FALSE; |
| 374 | |
| 375 | // See if user has voted |
| 376 | if ($user->uid) { |
| 377 | $poll->voted = (count(votingapi_get_user_votes('advpoll', $node->nid)) > 0); |
| 378 | if ($poll->voted) { |
| 379 | $poll->cancel_vote = TRUE; |
| 380 | } |
| 381 | } |
| 382 | else { |
| 383 | $result = db_query('SELECT uid, value FROM {votingapi_vote} ' |
| 384 | . 'WHERE content_id=%d AND content_type="advpoll" AND hostname="%s"', |
| 385 | $node->nid, $_SERVER['REMOTE_ADDR']); |
| 386 | if (db_num_rows($result) > 0) { |
| 387 | $poll->voted = TRUE; |
| 388 | $obj = db_fetch_object($result); |
| 389 | if ($obj->uid == 0) { |
| 390 | // Only allow cancelling if initial vote was also anonymous |
| 391 | $poll->cancel_vote = TRUE; |
| 392 | } |
| 393 | } |
| 394 | } |
| 395 | |
| 396 | return $poll; |
| 397 | } |
| 398 | |
| 399 | /** |
| 400 | * Implementation of hook_menu(). |
| 401 | * |
| 402 | */ |
| 403 | function advpoll_menu($may_cache) { |
| 404 | global $user; |
| 405 | |
| 406 | // Load the modes in here instead of _init() toprofit from caching, |
| 407 | // as recommended by http://api.drupal.org/api/head/function/hook_init |
| 408 | $modes = _advpoll_list_modes(); |
| 409 | |
| 410 | $items = array(); |
| 411 | |
| 412 | if ($may_cache) { |
| 413 | foreach ($modes as $mode) { |
| 414 | $items[] = array( |
| 415 | 'path' => 'node/add/advpoll-' . $mode['name'], |
| 416 | 'title' => t('Advanced Poll - ' . $mode['name']), |
| 417 | 'access' => user_access('create polls'), |
| 418 | ); |
| 419 | } |
| 420 | |
| 421 | $items[] = array( |
| 422 | 'path' => 'advpoll/cancel', |
| 423 | 'title' => t('Cancel'), |
| 424 | 'callback' => 'advpoll_cancel', |
| 425 | 'access' => user_access('cancel own vote'), |
| 426 | 'type' => MENU_CALLBACK |
| 427 | ); |
| 428 | |
| 429 | $items[] = array( |
| 430 | 'path' => 'admin/settings/advanced-poll', |
| 431 | 'title' => t('Advanced Poll'), |
| 432 | 'description' => t('Update settings for Advanced Poll'), |
| 433 | 'callback' => 'drupal_get_form', |
| 434 | 'callback arguments' => array('advpoll_admin_settings'), |
| 435 | 'access' => user_access('administer polls'), |
| 436 | ); |
| 437 | |
| 438 | $items[] = array( |
| 439 | 'path' => 'polls', |
| 440 | 'title' => t('Advanced Polls'), |
| 441 | 'callback' => 'advpoll_page', |
| 442 | 'access' => user_access('view polls'), |
| 443 | 'type' => MENU_SUGGESTED_ITEM, |
| 444 | ); |
| 445 | } |
| 446 | else { |
| 447 | // need to be able to extract the nid |
| 448 | if (arg(0) == 'node' && is_numeric(arg(1))) { |
| 449 | $nid = arg(1); |
| 450 | $node = node_load($nid); |
| 451 | // Make sure we're on the actual poll node's page |
| 452 | if (strstr($node->type, 'advpoll_') == 0) { |
| 453 | // Show the results tab |
| 454 | if ($node->active && !$node->voted |
| 455 | && _advpoll_can_view_results($node)) { |
| 456 | $items[] = array( |
| 457 | 'path' => 'node/' . $nid . '/results', |
| 458 | 'title' => t('Results'), |
| 459 | 'callback' => 'advpoll_results', |
| 460 | 'access' => user_access('view polls'), |
| 461 | 'weight' => 3, |
| 462 | 'type' => MENU_LOCAL_TASK, |
| 463 | ); |
| 464 | } |
| 465 | |
| 466 | // Show the votes tab |
| 467 | if ($node->showvotes) { |
| 468 | $items[] = array( |
| 469 | 'path' => 'node/' . $nid . '/votes', |
| 470 | 'title' => t('Votes'), |
| 471 | 'callback' => 'advpoll_tab_votes', |
| 472 | 'access' => user_access('inspect all votes'), |
| 473 | 'weight' => 3, |
| 474 | 'type' => MENU_LOCAL_TASK, |
| 475 | ); |
| 476 | } |
| 477 | |
| 478 | // Show electoral list tab if using the functionality |
| 479 | if ($node->uselist) { |
| 480 | $items[] = array( |
| 481 | 'path' => 'node/' . $nid . '/electoral_list', |
| 482 | 'title' => t('Electoral list'), |
| 483 | 'callback' => 'advpoll_tab_electoral_list', |
| 484 | 'access' => user_access('view polls'), |
| 485 | 'weight' => 3, |
| 486 | 'type' => MENU_LOCAL_TASK, |
| 487 | ); |
| 488 | |
| 489 | // Allow voters to be removed |
| 490 | $items[] = array( |
| 491 | 'path' => 'node/' . $nid . '/remove', |
| 492 | 'callback' => 'advpoll_remove_voter', |
| 493 | 'access' => user_access('administer polls'), |
| 494 | 'weight' => 3, |
| 495 | 'type' => MENU_CALLBACK, |
| 496 | ); |
| 497 | } |
| 498 | |
| 499 | // Allow votes to be reset |
| 500 | $items[] = array( |
| 501 | 'path' => 'node/' . $nid . '/reset', |
| 502 | 'callback' => 'drupal_get_form', |
| 503 | 'callback arguments' => 'advpoll_reset_confirm', |
| 504 | 'access' => user_access('administer polls'), |
| 505 | 'weight' => 3, |
| 506 | 'type' => MENU_CALLBACK, |
| 507 | ); |
| 508 | |
| 509 | |
| 510 | } |
| 511 | } |
| 512 | } |
| 513 | |
| 514 | return $items; |
| 515 | } |
| 516 | |
| 517 | /** |
| 518 | * Display the electoral list tab |
| 519 | */ |
| 520 | function advpoll_tab_electoral_list() { |
| 521 | if ($node = node_load(arg(1))) { |
| 522 | if (!$node->uselist) { |
| 523 | drupal_not_found(); |
| 524 | return; |
| 525 | } |
| 526 | drupal_set_title(check_plain($node->title)); |
| 527 | |
| 528 | if (user_access('administer polls')) { |
| 529 | $output .= drupal_get_form('advpoll_electoral_list_form', $node->nid); |
| 530 | } |
| 531 | |
| 532 | $output .= '<p>' |
| 533 | . t('This table lists all the eligible voters for this poll.') |
| 534 | . '</p>'; |
| 535 | |
| 536 | $header[] = array('data' => t('Voter'), 'field' => 'u.name'); |
| 537 | |
| 538 | $result = pager_query("SELECT u.uid, u.name FROM {advpoll_electoral_list} el LEFT JOIN {users} u ON el.uid = u.uid WHERE el.nid = %d" . tablesort_sql($header), 20, 0, NULL, $node->nid); |
| 539 | $eligible_voters = array(); |
| 540 | while ($voter = db_fetch_object($result)) { |
| 541 | $temp = array( |
| 542 | theme('username', $voter), |
| 543 | ); |
| 544 | |
| 545 | if (user_access('administer polls')) { |
| 546 | $temp[] = l(t('remove'), 'node/'. $node->nid .'/remove/' . $voter->uid); |
| 547 | } |
| 548 | |
| 549 | $eligible_voters[] = $temp; |
| 550 | } |
| 551 | $output .= theme('table', $header, $eligible_voters); |
| 552 | $output .= theme('pager', NULL, 20, 0); |
| 553 | print theme('page', $output); |
| 554 | } |
| 555 | else { |
| 556 | drupal_not_found(); |
| 557 | } |
| 558 | } |
| 559 | |
| 560 | function advpoll_electoral_list_form($nid) { |
| 561 | $form['electoral_list'] = array( |
| 562 | '#type' => 'fieldset', |
| 563 | '#tree' => TRUE, |
| 564 | '#title' => t('Administer electoral list'), |
| 565 | '#collapsible' => TRUE, |
| 566 | '#weight' => 2, |
| 567 | '#collapsed' => TRUE, |
| 568 | ); |
| 569 | |
| 570 | $form['electoral_list']['add_user'] = array( |
| 571 | '#type' => 'textfield', |
| 572 | '#title' => t('Add user'), |
| 573 | '#size' => 40, |
| 574 | '#autocomplete_path' => 'user/autocomplete', |
| 575 | '#description' => t('Add an individual user to the electoral list'), |
| 576 | ); |
| 577 | |
| 578 | $form['electoral_list']['submit'] = array( |
| 579 | '#type' => 'submit', |
| 580 | '#value' => t('Modify electoral list'), |
| 581 | ); |
| 582 | |
| 583 | $form['electoral_list']['reset'] = array( |
| 584 | '#type' => 'button', |
| 585 | '#value' => t('Reset electoral list'), |
| 586 | ); |
| 587 | |
| 588 | $form['nid'] = array('#type' => 'hidden', '#value' => $nid); |
| 589 | return $form; |
| 590 | } |
| 591 | |
| 592 | |
| 593 | /** |
| 594 | * Remove an individual voter from the electoral list |
| 595 | */ |
| 596 | function advpoll_remove_voter() { |
| 597 | $nid = arg(1); |
| 598 | $uid = arg(3); |
| 599 | if ($uid && $node = node_load($nid)) { |
| 600 | $result = db_query('SELECT name FROM {users} WHERE uid=%d', $uid); |
| 601 | if (db_num_rows($result) > 0) { |
| 602 | $user = db_fetch_object($result); |
| 603 | db_query('DELETE FROM {advpoll_electoral_list} WHERE nid=%d AND uid=%d', |
| 604 | $nid, $uid); |
| 605 | drupal_set_message(t('%user removed from the electoral list.', array('%user' => $user->name))); |
| 606 | } |
| 607 | else { |
| 608 | drupal_set_message(t('No user found with a uid of %uid.', array('%uid' => $uid))); |
| 609 | } |
| 610 | |
| 611 | } |
| 612 | drupal_goto('node/' . $node->nid . '/electoral_list'); |
| 613 | } |
| 614 | |
| 615 | /** |
| 616 | * Validate changes to the electoral list |
| 617 | */ |
| 618 | function advpoll_electoral_list_form_validate($form_id, $form_values) { |
| 619 | if ($form_values['op'] == t('Reset electoral list')) { |
| 620 | if (user_access('administer polls')) { |
| 621 | db_query('DELETE FROM {advpoll_electoral_list} WHERE nid=%d', $form_values['nid']); |
| 622 | drupal_set_message(t('Electoral list cleared.')); |
| 623 | return; |
| 624 | } |
| 625 | } |
| 626 | |
| 627 | $add_user = $form_values['electoral_list']['add_user']; |
| 628 | if ($add_user) { |
| 629 | // Check that the user exists |
| 630 | $result = db_query('SELECT uid FROM {users} WHERE name="%s"', $add_user); |
| 631 | if (db_num_rows($result) == 0) { |
| 632 | form_set_error('electoral_list][add_user', t('User %user does not exist.', array('%user' => $add_user))); |
| 633 | return FALSE; |
| 634 | } |
| 635 | } |
| 636 | } |
| 637 | |
| 638 | /** |
| 639 | * Submit changes to the electoral list |
| 640 | */ |
| 641 | function advpoll_electoral_list_form_submit($form_id, $form_values) { |
| 642 | $add_user = $form_values['electoral_list']['add_user']; |
| 643 | if ($add_user) { |
| 644 | db_query('REPLACE INTO {advpoll_electoral_list} (nid, uid) SELECT "%d", u.uid FROM users u WHERE u.name = "%s"', $form_values['nid'], $add_user); |
| 645 | drupal_set_message(t('%user added to electoral list.', array('%user' => $add_user))); |
| 646 | } |
| 647 | } |
| 648 | |
| 649 | /** |
| 650 | * Display the votes tab |
| 651 | */ |
| 652 | function advpoll_tab_votes() { |
| 653 | if ($node = node_load(arg(1))) { |
| 654 | if (!$node->showvotes) { |
| 655 | // Advanced Poll is set to not allow viewing of votes |
| 656 | drupal_not_found(); |
| 657 | return; |
| 658 | } |
| 659 | drupal_set_title(check_plain($node->title)); |
| 660 | $output = t('This table lists all the recorded votes for this poll. If anonymous users are allowed to vote, they will be identified by the IP address of the computer they used when they voted.'); |
| 661 | |
| 662 | $header[] = array('data' => t('Visitor'), 'field' => 'u.name'); |
| 663 | $header[] = array('data' => t('Vote'), ''); |
| 664 | |
| 665 | $result = pager_query('SELECT v.value, v.uid, v.hostname, v.tag, u.name FROM {votingapi_vote} v LEFT JOIN {users} u ON v.uid = u.uid WHERE v.content_type="advpoll" AND v.content_id = %d' . tablesort_sql($header), 20, 0, NULL, $node->nid); |
| 666 | $rows = array(); |
| 667 | while ($vote = db_fetch_object($result)) { |
| 668 | $key = $vote->uid? $vote->uid : $vote->hostname; |
| 669 | $rows[$key]['name'] = $vote->name ? theme('username', $vote) : check_plain($vote->hostname); |
| 670 | if ($node->type == 'advpoll_ranking') { |
| 671 | // Need two dimensional results (if equal rankings are allowed) |
| 672 | $rows[$key]['votes'][$vote->value][] = check_plain($node->choice[$vote->tag]['label']); |
| 673 | } |
| 674 | else { |
| 675 | // Just need one dimensional results |
| 676 | $rows[$key]['votes'][] = check_plain($node->choice[$vote->tag]['label']); |
| 677 | } |
| 678 | } |
| 679 | |
| 680 | $separators = array('advpoll_ranking' => ' > ', 'advpoll_binary' => ', '); |
| 681 | |
| 682 | // create strings out of each vote |
| 683 | $results = array(); |
| 684 | |
| 685 | foreach ($rows as $key => $container) { |
| 686 | $ranking = $container['votes']; |
| 687 | asort($ranking); |
| 688 | $rankings = array(); |
| 689 | if ($node->type == 'advpoll_ranking') { |
| 690 | // Include support for multiple choices having the same ranking |
| 691 | foreach ($ranking as $vote => $choices) { |
| 692 | $rankings[$vote] = implode(' = ', $choices); |
| 693 | } |
| 694 | } |
| 695 | else { |
| 696 | // Just copy the previous array |
| 697 | $rankings = $ranking; |
| 698 | } |
| 699 | ksort($rankings); |
| 700 | $results[$key]['name'] = $rows[$key]['name']; |
| 701 | $results[$key]['vote'] = implode($separators[$node->type], $rankings); |
| 702 | } |
| 703 | |
| 704 | $output .= theme('table', $header, $results); |
| 705 | $output .= theme('pager', NULL, 20, 0); |
| 706 | print theme('page', $output); |
| 707 | } |
| 708 | else { |
| 709 | drupal_not_found(); |
| 710 | } |
| 711 | } |
| 712 | |
| 713 | |
| 714 | /** |
| 715 | * Helper function to abstract view results checking |
| 716 | */ |
| 717 | function _advpoll_can_view_results($node) { |
| 718 | $view_results = variable_get('advpoll_view_results' |
| 719 | , ADVPOLL_DEFAULT_VIEW_RESULTS); |
| 720 | return (!$node->active // node is closed |
| 721 | || ($node->voted && $view_results == 'aftervote') // user voted |
| 722 | || ($view_results == 'always')); // all can view |
| 723 | } |
| 724 | |
| 725 | /** |
| 726 | * Implementation of hook_node_info(). |
| 727 | */ |
| 728 | function advpoll_node_info() { |
| 729 | $modes = _advpoll_list_modes(); |
| 730 | $info = array(); |
| 731 | foreach ($modes as $mode) { |
| 732 | $info['advpoll_' . $mode['name']] = array( |
| 733 | 'name' => t('Advanced Poll - ' . $mode['name']), |
| 734 | 'module' => 'advpoll', |
| 735 | 'description' => $mode['description'], |
| 736 | ); |
| 737 | } |
| 738 | return $info; |
| 739 | } |
| 740 | |
| 741 | /** |
| 742 | * Implementation of hook_perm(). |
| 743 | */ |
| 744 | function advpoll_perm() { |
| 745 | return array('create polls', 'delete polls', 'view polls', 'vote on polls', 'cancel own vote', 'administer polls', 'inspect all votes'); |
| 746 | } |
| 747 | |
| 748 | /** |
| 749 | * Settings page for Drupal 5.0 |
| 750 | */ |
| 751 | function advpoll_admin_settings() { |
| 752 | $enabled = array(0 => t('Disabled'), 1 => t('Enabled')); |
| 753 | |
| 754 | $form['main']['advpoll_default_electoral_list'] = array( |
| 755 | '#type' => 'radios', |
| 756 | '#title' => t('Use electoral list by default'), |
| 757 | '#description' => t('Use an electoral list by default for new advpoll.'), |
| 758 | '#default_value' => variable_get('advpoll_default_electoral_list', ADVPOLL_DEFAULT_ELECTORAL_LIST), |
| 759 | '#options' => $enabled, |
| 760 | ); |
| 761 | |
| 762 | $view_results = array( |
| 763 | 'always' => t('Always'), |
| 764 | 'aftervote' => t('After user has voted'), |
| 765 | 'afterclose' => t('After voting has closed'), |
| 766 | ); |
| 767 | |
| 768 | $form['main']['advpoll_view_results'] = array( |
| 769 | '#type' => 'radios', |
| 770 | '#title' => t('When should results be displayed'), |
| 771 | '#description' => t('Determines when users may view the results of the poll.'), |
| 772 | '#default_value' => variable_get('advpoll_view_results', ADVPOLL_DEFAULT_VIEW_RESULTS), |
| 773 | '#options' => $view_results, |
| 774 | ); |
| 775 | return system_settings_form($form); |
| 776 | } |
| 777 | |
| 778 | |
| 779 | /** |
| 780 | * Helper function to display 'cancel vote' button if user has voted |
| 781 | */ |
| 782 | function advpoll_cancel_form($nid) { |
| 783 | $form['#action'] = url("advpoll/cancel/$nid"); |
| 784 | $form['submit'] = array('#type' => 'submit', '#value' => t('Cancel your vote')); |
| 785 | return $form; |
| 786 | } |
| 787 | |
| 788 | /** |
| 789 | * Implementation of hook_update(). |
| 790 | * |
| 791 | * This is called upon node edition. |
| 792 | */ |
| 793 | function advpoll_update($node) { |
| 794 | if ($node->settings['usestart']) { |
| 795 | $node->settings['active'] = _advpoll_calculate_active($node); |
| 796 | } |
| 797 | |
| 798 | db_query("UPDATE {advpoll} SET active=%d, runtime=%d, maxchoices=%d, algorithm='%s', uselist=%d, showvotes=%d, startdate=%s WHERE nid = %d", |
| 799 | $node->settings['active'], $node->settings['runtime'], |
| 800 | $node->settings['maxchoices'], $node->settings['algorithm'], |
| 801 | $node->settings['uselist'], $node->settings['showvotes'], |
| 802 | $node->settings['usestart']? _advpoll_create_startdate($node): 'NULL', |
| 803 | $node->nid); |
| 804 | |
| 805 | _advpoll_insert_choices($node); |
| 806 | votingapi_recalculate_results('advpoll', $node->nid); |
| 807 | } |
| 808 | |
| 809 | |
| 810 | /** |
| 811 | * Determine if node should be active for _insert and _update operations |
| 812 | * |
| 813 | * This function is needed when changing the startdate and/or duration |
| 814 | */ |
| 815 | function _advpoll_calculate_active($node) { |
| 816 | $startdate = _advpoll_create_startdate($node); |
| 817 | if ($node->settings['runtime']) { |
| 818 | // Check that startdate is in the past and that the duration hasn't elapsed |
| 819 | $active = time() >= $startdate |
| 820 | && time() < $node->settings['runtime'] + $startdate; |
| 821 | } |
| 822 | else { |
| 823 | // Just need to ensure that startdate is in the past |
| 824 | $active = time() >= $startdate; |
| 825 | } |
| 826 | return $active? 1 : 0; |
| 827 | } |
| 828 | |
| 829 | /* |
| 830 | * Create a unix timestamp for startdate based on FormAPI inputs |
| 831 | */ |
| 832 | function _advpoll_create_startdate($node) { |
| 833 | list($hour, $min) = explode(':', date('h:m', time())); |
| 834 | return mktime($hour, $min, 0, |
| 835 | $node->settings['startdate']['month'], |
| 836 | $node->settings['startdate']['day'], |
| 837 | $node->settings['startdate']['year']); |
| 838 | } |
| 839 | |
| 840 | function _advpoll_insert_choices($node) { |
| 841 | db_query('DELETE FROM {advpoll_choices} WHERE nid = %d', $node->nid); |
| 842 | // Start at one rather than 0 due to Drupal FormAPI |
| 843 | $i = 1; |
| 844 | foreach ($_POST['choice'] as $choice) { |
| 845 | if ($choice['label'] != '') { |
| 846 | db_query("INSERT INTO {advpoll_choices} (nid, label, vote_offset) VALUES (%d, '%s', %d)", $node->nid, check_plain($choice['label']), $i++); |
| 847 | } |
| 848 | } |
| 849 | } |
| 850 | |
| 851 | function _advpoll_get_mode($node) { |
| 852 | if ($node->type) { |
| 853 | $types = explode('_', $node->type, 2); |
| 854 | return $types[1]; |
| 855 | } |
| 856 | else { |
| 857 | drupal_set_message(t('No type specified for node %nid', array('%nid' => $node->nid)), 'error'); |
| 858 | return ''; |
| 859 | } |
| 860 | } |
| 861 | |
| 862 | /** |
| 863 | * Implementation of hook_insert() |
| 864 | * |
| 865 | * This is called upon node creation |
| 866 | */ |
| 867 | function advpoll_insert($node) { |
| 868 | $mode = _advpoll_get_mode($node); |
| 869 | if ($node->settings['usestart']) { |
| 870 | $node->settings['active'] = _advpoll_calculate_active($node); |
| 871 | } |
| 872 | |
| 873 | db_query('INSERT INTO {advpoll} (nid, mode, uselist, active, runtime, ' |
| 874 | . 'maxchoices, algorithm, showvotes, startdate)' |
| 875 | . ' VALUES (%d, "%s", %d, %d, %d, %d, "%s", %d, %s)', |
| 876 | $node->nid, $mode, $node->settings['uselist'], $node->settings['active'], |
| 877 | $node->settings['runtime'], $node->settings['maxchoices'], |
| 878 | $node->settings['algorithm'], $node->settings['showvotes'], |
| 879 | $node->settings['usestart']? _advpoll_create_startdate($node): 'NULL'); |
| 880 | |
| 881 | // create the electoral list if desired |
| 882 | |
| 883 | if ($node->settings['uselist']) { |
| 884 | // Check if authenticated users have the right to vote, because authenticated users are not added to the users_roles permission, probably for performance reasons |
| 885 | $result = db_fetch_object(db_query("SELECT COUNT(*) AS hit FROM {permission} JOIN role ON role.rid = permission.rid WHERE FIND_IN_SET(' vote on advpoll', perm) AND role.name = 'authenticated user'")); |
| 886 | if ($result->hit) { |
| 887 | // Special case: any authenticated user can vote |
| 888 | // Add all current users to electoral list |
| 889 | db_query("INSERT INTO {advpoll_electoral_list} (nid, uid) SELECT '%d', u.uid FROM users u WHERE u.uid != 0", $node->nid); |
| 890 | } |
| 891 | else { |
| 892 | // All users must not be allowed to vote, add relevant users only |
| 893 | db_query("INSERT INTO {advpoll_electoral_list} (nid, uid) SELECT '%d', u.uid FROM users_roles u, permission p WHERE FIND_IN_SET(' view advpoll', p.perm) AND u.rid = p.rid AND u.uid != 0", $node->nid); |
| 894 | } |
| 895 | } |
| 896 | |
| 897 | // Insert the choices |
| 898 | _advpoll_insert_choices($node); |
| 899 | } |
| 900 | |
| 901 | /** |
| 902 | * Callback to display a reset votes confirmation form |
| 903 | */ |
| 904 | function advpoll_reset_confirm() { |
| 905 | $edit = $_POST['edit']; |
| 906 | $edit['nid'] = $edit['nid']? $edit['nid'] : arg(1); |
| 907 | $node = node_load($edit['nid']); |
| 908 | |
| 909 | $form['nid'] = array('#type' => 'value', '#value' => $node->nid); |
| 910 | $output = confirm_form($form, |
| 911 | t('Are you sure you want to reset the votes for %title?', |
| 912 | array('%title' => $node->title)), |
| 913 | 'node/' . $node->nid . '/edit', |
| 914 | t('This action cannot be undone.'), |
| 915 | t('Reset votes'), |
| 916 | t('Cancel')); |
| 917 | return $output; |
| 918 | } |
| 919 | |
| 920 | /** |
| 921 | * Reset votes once the confirmation is given |
| 922 | */ |
| 923 | function advpoll_reset_confirm_submit($form_id, $form_values) { |
| 924 | if ($form_values['confirm']) { |
| 925 | if ($node = node_load(array('nid' => arg(1)))) { |
| 926 | // Delete any votes for the poll |
| 927 | db_query('DELETE FROM {votingapi_vote} WHERE content_type="advpoll" AND content_id = %d', $node->nid); |
| 928 | drupal_set_message('Votes have been reset.'); |
| 929 | drupal_goto('node/' . $node->nid . '/edit'); |
| 930 | } |
| 931 | } |
| 932 | |
| 933 | return ''; |
| 934 | } |
| 935 | |
| 936 | /** |
| 937 | * Implementation of hook_validate(). |
| 938 | */ |
| 939 | function advpoll_validate(&$node) { |
| 940 | if ($_POST['op'] == t('Reset votes')) { |
| 941 | drupal_goto('node/' . $node->nid . '/reset'); |
| 942 | } |
| 943 | else { |
| 944 | // Use form_set_error for any errors |
| 945 | $node->choice = array_values($node->choice); |
| 946 | |
| 947 | // Start keys at 1 rather than 0 |
| 948 | array_unshift($node->choice, ''); |
| 949 | unset($node->choice[0]); |
| 950 | |
| 951 | // Check for at least two choices |
| 952 | $realchoices = 0; |
| 953 | foreach ($node->choice as $i => $choice) { |
| 954 | if ($choice['label'] != '') { |
| 955 | $realchoices++; |
| 956 | } |
| 957 | } |
| 958 | |
| 959 | if ($realchoices < 2) { |
| 960 | form_set_error("choice][$realchoices][label", t('You must fill in at least two choices.')); |
| 961 | } |
| 962 | |
| 963 | // Validate maxchoices since it has #DANGEROUS_SKIP_CHECK set to true |
| 964 | |
| 965 | if ($node->settings['maxchoices'] < 0) { |
| 966 | form_set_error('settings][maxchoices]', t('Maximum choices must be a non-negative integer.')); |
| 967 | } |
| 968 | |
| 969 | if ($node->settings['maxchoices'] > count($node->choice)) { |
| 970 | form_set_error('settings][maxchoices]', t('Maximum choices cannot be larger than the number of choices submitted.')); |
| 971 | } |
| 972 | } |
| 973 | } |
| 974 | |
| 975 | function advpoll_submit(&$node) { |
| 976 | $node->choice = array_values($node->choice); |
| 977 | // Start keys at 1 rather than 0 |
| 978 | array_unshift($node->choice, ''); |
| 979 | unset($node->choice[0]); |
| 980 | } |
| 981 | |
| 982 | /** |
| 983 | * Implementation of hook_view(). |
| 984 | */ |
| 985 | function advpoll_view($node, $teaser = FALSE, $page = FALSE, $block = FALSE) { |
| 986 | $mode = _advpoll_get_mode($node); |
| 987 | |
| 988 | if ($block) { |
| 989 | // No 'readmore' link |
| 990 | $node->readmore = FALSE; |
| 991 | //$links = module_invoke_all('links', 'node', $node, 1); |
| 992 | $links[] = array( |
| 993 | 'title' => 'Older polls', |
| 994 | 'href' => 'polls', |
| 995 | 'attributes' => array('title' => |
| 996 | t('View the list of polls on this site.')), |
| 997 | ); |
| 998 | $node->links = $links; |
| 999 | } |
| 1000 | |
| 1001 | // Previewing a node, so don't show results |
| 1002 | if ($node->in_preview) { |
| 1003 | // Show the voting form but don't let them vote |
| 1004 | $node->content['body'] = array( |
| 1005 | '#value' => drupal_get_form('advpoll_voting_' . $mode . '_form', $node, $teaser, $page, $block), |
| 1006 | ); |
| 1007 | } |
| 1008 | else if (!$node->voted && arg(2) != 'results' && $node->active && advpoll_eligible($node)) { |
| 1009 | // User hasn't voted and we're not on the results tab |
| 1010 | |
| 1011 | if ($block) { |
| 1012 | $node->links[] = array( |
| 1013 | 'title' => t('Results'), |
| 1014 | 'href' => 'node/'. $node->nid .'/results', |
| 1015 | 'attributes' => array('title' => t('View the current poll results.')), |
| 1016 | ); |
| 1017 | } |
| 1018 | $node->content['body'] = array( |
| 1019 | '#value' => drupal_get_form('advpoll_voting_' . $node->mode . '_form', |
| 1020 | $node, $teaser, $page, $block) |
| 1021 | //. advpoll_view_electoral_list($node, $teaser), |
| 1022 | ); |
| 1023 | static $addjs = TRUE; |
| 1024 | if ($addjs) { |
| 1025 | // Add javascript for posting voting forms with ajax |
| 1026 | drupal_add_js(drupal_get_path('module', 'advpoll') .'/advpoll-vote.js', 'module'); |
| 1027 | drupal_add_js(drupal_get_path('module', 'advpoll') .'/jquery.form.js', 'module'); |
| 1028 | $addjs = FALSE; |
| 1029 | } |
| 1030 | } |
| 1031 | else { |
| 1032 | // Show results only if the user has voted or poll is closed |
| 1033 | $node->content['body'] = array( |
| 1034 | '#value' => advpoll_view_results($node, $teaser, $page, $block) |
| 1035 | ); |
| 1036 | } |
| 1037 | |
| 1038 | return $node; |
| 1039 | } |
| 1040 | |
| 1041 | /** |
| 1042 | * Currently deprecated |
| 1043 | */ |
| 1044 | function advpoll_view_electoral_list($node, $teaser = FALSE) { |
| 1045 | $output = ''; |
| 1046 | if ($node->uselist) { |
| 1047 | $result = db_query("SELECT COUNT(*) AS voters FROM {advpoll_electoral_list} WHERE nid=%d", $node->nid); |
| 1048 | $electoral_list = db_fetch_object($result); |
| 1049 | } |
| 1050 | $result = db_query("SELECT COUNT(DISTINCT uid) AS voters FROM {votingapi_vote} WHERE content_id=%d GROUP BY uid", $node->nid); |
| 1051 | $votes = db_num_rows($result); |
| 1052 | |
| 1053 | $output = '<div class="advpoll-electoral-list">'; |
| 1054 | |
| 1055 | $output .= t('Total votes: %d', array('%d' => $votes)); |
| 1056 | if ($node->uselist) { |
| 1057 | $output .= t(' (out of %v eligible voter' . |
| 1058 | ($electoral_list->voters == 1? '' : 's') . ')', |
| 1059 | array('%v' => $electoral_list->voters)); |
| 1060 | } |
| 1061 | $output .= '</div>'; |
| 1062 | return $output; |
| 1063 | } |
| 1064 | |
| 1065 | /** |
| 1066 | * Render the voting form. |
| 1067 | */ |
| 1068 | function theme_advpoll_view_voting($form) { |
| 1069 | $output .= '<div class="advpoll">'; |
| 1070 | $output .= ' <div class="choice-form">'; |
| 1071 | $output .= ' <div class="choices">'; |
| 1072 | $output .= drupal_render($form['choice']); |
| 1073 | $output .= ' </div>'; |
| 1074 | $output .= drupal_render($form['nid']); |
| 1075 | $output .= drupal_render($form['vote']); |
| 1076 | $output .= ' </div>'; |
| 1077 | $output .= drupal_render($form); |
| 1078 | $output .= '</div>'; |
| 1079 | return $output; |
| 1080 | } |
| 1081 | |
| 1082 | function theme_advpoll_results($title, $results, $votes, $links, $block, $nid, $voted, $cancel_vote) { |
| 1083 | if ($block) { |
| 1084 | $output .= '<div class="poll">'; |
| 1085 | $output .= '<div class="title">'. $title .'</div>'; |
| 1086 | $output .= $results; |
| 1087 | $output .= '<div class="total">'. t('Total votes: %votes', array('%votes' => $votes)) .'</div>'; |
| 1088 | $output .= '</div>'; |
| 1089 | $output .= '<div class="links">'. theme('links', $links) .'</div>'; |
| 1090 | } |
| 1091 | else { |
| 1092 | $output .= '<div class="poll">'; |
| 1093 | $output .= $results; |
| 1094 | $output .= '<div class="total">'. t('Total votes: %votes', array('%votes' => $votes)) .'</div>'; |
| 1095 | $output .= '</div>'; |
| 1096 | } |
| 1097 | return $output; |
| 1098 | } |
| 1099 | |
| 1100 | function _advpoll_show_cancel_form($node, $block) { |
| 1101 | $output = ''; |
| 1102 | if ($node->voted && $node->cancel_vote && user_access('cancel own vote') |
| 1103 | && !$block) { |
| 1104 | $output .= drupal_get_form('advpoll_cancel_form', $node->nid); |
| 1105 | } |
| 1106 | return $output; |
| 1107 | } |
| 1108 | |
| 1109 | function theme_advpoll_bar($title, $percentage, $votes, $block) { |
| 1110 | if ($block) { |
| 1111 | $output = '<div class="text">'. $title .'</div>'; |
| 1112 | $output .= '<div class="bar"><div style="width: '. $percentage .'%;" class="foreground"></div></div>'; |
| 1113 | $output .= '<div class="percent">'. $percentage .'%</div>'; |
| 1114 | } |
| 1115 | else { |
| 1116 | $output = '<div class="text">'. $title .'</div>'; |
| 1117 | $output .= '<div class="bar"><div style="width: '. $percentage .'%;" class="foreground"></div></div>'; |
| 1118 | $output .= |