poll.module 14.3 KB
Newer Older
1
<?php
2
// $Id$
3

4 5 6 7
/**
 * Implementation of hook_help().
 */
function poll_help($section) {
Steven Wittens's avatar
Steven Wittens committed
8 9
  switch ($section) {
    case 'admin/help#poll':
10
      return t("
Steven Wittens's avatar
Steven Wittens committed
11 12 13 14 15 16 17
      <p>Users with the correct <a href=\"%permissions\">permissions</a> can create and/or vote on polls.</p>
      <ul>
      <li>To create a poll a user needs the \"create polls\" permission.</li>
      <li>To vote on a poll question a user must have the \"vote on polls\" permission.</li>
      <li>To view the results one needs the \"access content\" permission.</li>
      <li>To administer polls you need the \"administer nodes\" permission.</li>
      </ul>
18
      <p>Creating a poll is much like creating any other node. Click \"create poll\" in your user box. The title of the poll should be the question, then enter the answers and the \"base\" vote counts. You can also choose the time period over which the vote will run.</p><p>The <a href=\"%poll\">Poll</a> item in the navigation links will take you to a page where you can see all the current polls, vote on them (if you haven't already) and view the results.</p>", array("%permissions" => url("admin/user/configure/permission"), "%poll" => url("poll")));
Dries's avatar
 
Dries committed
19
    case 'admin/modules#description':
20
      return t("Enables your site to capture votes on different topics in the form of multiple choice questions.");
Steven Wittens's avatar
Steven Wittens committed
21
    case 'node/add#poll':
22
      return t("A poll is a multiple-choice question which visitors can vote on.");
Steven Wittens's avatar
Steven Wittens committed
23
  }
24 25
}

26 27 28
/**
 * Implementation of hook_access().
 */
29
function poll_access($op, $node) {
Steven Wittens's avatar
Steven Wittens committed
30 31
  if ($op == 'create') {
    return user_access('create polls');
Dries's avatar
 
Dries committed
32
  }
Steven Wittens's avatar
Steven Wittens committed
33 34
}

35 36 37 38 39
/**
 * Implementation of hook_block().
 *
 * Generates a block containing the latest poll.
 */
Steven Wittens's avatar
Steven Wittens committed
40 41 42 43
function poll_block($op = 'list', $delta = 0) {
  if (user_access('access content')) {
    if ($op == 'list') {
      $blocks[0]['info'] = t('Most recent poll');
Dries's avatar
 
Dries committed
44 45 46
      return $blocks;
    }
    else {
47
      // Retrieve the latest poll.
Dries's avatar
 
Dries committed
48
      $timestamp = db_result(db_query('SELECT MAX(n.created) FROM {node} n '. node_access_join_sql() ." WHERE n.type = 'poll' AND n.status = 1 AND ". node_access_where_sql() .' AND n.moderate = 0'));
Dries's avatar
 
Dries committed
49
      if ($timestamp) {
Steven Wittens's avatar
Steven Wittens committed
50 51
        $poll = node_load(array('type' => 'poll', 'created' => $timestamp, 'moderate' => 0, 'status' => 1));

Dries's avatar
 
Dries committed
52
        if ($poll->nid) {
53
          // poll_view() dumps the output into $poll->body.
54
          poll_view($poll, 1, 0, 1);
Dries's avatar
 
Dries committed
55
        }
Dries's avatar
 
Dries committed
56
      }
Steven Wittens's avatar
Steven Wittens committed
57 58
      $block['subject'] = t('Poll');
      $block['content'] = $poll->body;
Dries's avatar
 
Dries committed
59
      return $block;
60 61 62 63
    }
  }
}

64 65 66 67 68
/**
 * Implementation of hook_cron().
 *
 * Closes polls that have exceeded their allowed runtime.
 */
69
function poll_cron() {
Dries's avatar
 
Dries committed
70
  $result = db_query("SELECT p.nid FROM {poll} p INNER JOIN {node} n ON p.nid=n.nid WHERE (n.created + p.runtime) < '". time() ."' AND p.active = '1' AND p.runtime != '0'");
Dries's avatar
 
Dries committed
71
  while ($poll = db_fetch_object($result)) {
Dries's avatar
 
Dries committed
72
    db_query("UPDATE {poll} SET active='0' WHERE nid = %d", $poll->nid);
Dries's avatar
 
Dries committed
73
  }
74 75
}

76 77 78
/**
 * Implementation of hook_delete().
 */
79
function poll_delete($node) {
Dries's avatar
 
Dries committed
80 81
  db_query("DELETE FROM {poll} WHERE nid=%d", $node->nid);
  db_query("DELETE FROM {poll_choices} WHERE nid = %d", $node->nid);
82 83
}

84 85 86
/**
 * Implementation of hook_validate().
 */
87
function poll_validate(&$node) {
88 89
  if (isset($node->title)) {
    // Check for at least two options and validate amount of votes:
Steven Wittens's avatar
Steven Wittens committed
90 91 92 93
    $realchoices = 0;
    foreach ($node->choice as $i => $choice) {
      if ($choice['chtext'] != '') {
        $realchoices++;
94
      }
Dries's avatar
 
Dries committed
95

Steven Wittens's avatar
Steven Wittens committed
96
      if ($choice['chvotes'] < 0) {
Dries's avatar
 
Dries committed
97
        form_set_error("choice][$i][chvotes", t("Negative values are not allowed."));
98
      }
99
    }
Dries's avatar
 
Dries committed
100

Steven Wittens's avatar
Steven Wittens committed
101
    if ($realchoices < 2) {
Dries's avatar
 
Dries committed
102
      form_set_error("choice][$realchoices][chtext", t("You must fill in at least two choices."));
103 104
    }
  }
105 106 107 108

  $node->teaser = poll_teaser($node);
}

109 110 111
/**
 * Implementation of hook_form().
 */
Dries's avatar
 
Dries committed
112
function poll_form(&$node) {
Steven Wittens's avatar
Steven Wittens committed
113
  $admin = user_access('administer nodes');
114

Steven Wittens's avatar
Steven Wittens committed
115 116 117
  if (function_exists('taxonomy_node_form')) {
    $output = implode('', taxonomy_node_form('poll', $node));
  }
118

Steven Wittens's avatar
Steven Wittens committed
119 120 121
  if (!isset($node->choices)) {
    $node->choices = max(2, count($node->choice) ? count($node->choice) : 5);
  }
Dries's avatar
 
Dries committed
122

123
  // User ticked 'need more choices'.
Steven Wittens's avatar
Steven Wittens committed
124 125
  if ($node->morechoices) {
    $node->choices *= 2;
126 127
  }

Steven Wittens's avatar
Steven Wittens committed
128
  $output .= '<div class="poll-form">';
Dries's avatar
 
Dries committed
129

Steven Wittens's avatar
Steven Wittens committed
130
  // Poll choices
Dries's avatar
 
Dries committed
131
  $opts = drupal_map_assoc(range(2, $node->choices * 2 + 5));
132
  for ($a = 0; $a < $node->choices; $a++) {
Dries's avatar
 
Dries committed
133
    $group1 .= form_textfield(t('Choice %n', array('%n' => ($a + 1))), "choice][$a][chtext", $node->choice[$a]['chtext'], 50, 127);
134
    if ($admin) {
Dries's avatar
 
Dries committed
135
      $group1 .= form_textfield(t('Votes for choice %n', array('%n' => ($a + 1))), "choice][$a][chvotes", (int)$node->choice[$a]['chvotes'], 7, 7);
136 137
    }
  }
Steven Wittens's avatar
Steven Wittens committed
138 139 140
  $group1 .= form_hidden('choices', $node->choices);
  $group1 .= form_checkbox(t('Need more choices'), 'morechoices', 1, 0, t("If the amount of boxes above isn't enough, check this box and click the Preview button below to add some more."));
  $output .= form_group(t('Choices'), $group1);
Dries's avatar
 
Dries committed
141

Dries's avatar
 
Dries committed
142

Steven Wittens's avatar
Steven Wittens committed
143
  // Poll attributes
144
  $_duration = array(0 => t('Unlimited')) + drupal_map_assoc(array(86400, 172800, 345600, 604800, 1209600, 2419200, 4838400, 9676800, 31536000), "format_interval");
Steven Wittens's avatar
Steven Wittens committed
145
  $_active = array(0 => t('Closed'), 1 => t('Active'));
Dries's avatar
 
Dries committed
146

Steven Wittens's avatar
Steven Wittens committed
147 148
  if ($admin) {
    $group2 .= form_radios(t('Poll status'), 'active', isset($node->active) ? $node->active : 1, $_active, t('When a poll is closed, visitors can no longer vote for it.'));
Dries's avatar
 
Dries committed
149
  }
Steven Wittens's avatar
Steven Wittens committed
150
  $group2 .= form_select(t('Poll duration'), 'runtime', $node->runtime ? $node->runtime : 0, $_duration, t('After this period, the poll will be closed automatically.'));
Dries's avatar
 
Dries committed
151 152

  $output .= form_group(t('Settings'), $group2);
Steven Wittens's avatar
Steven Wittens committed
153
  $output .= '</div>';
Dries's avatar
 
Dries committed
154 155

  return $output;
156
}
157

158
function poll_insert($node) {
Steven Wittens's avatar
Steven Wittens committed
159
  if (!user_access('administer nodes')) {
160
    // Make sure all votes are 0 initially
Steven Wittens's avatar
Steven Wittens committed
161 162 163
    foreach ($node->choice as $i => $choice) {
      $node->choice[$i]['chvotes'] = 0;
    }
164 165
    $node->active = 1;
  }
166

Dries's avatar
 
Dries committed
167
  db_query("INSERT INTO {poll} (nid, runtime, voters, active) VALUES (%d, %d, '', %d)", $node->nid, $node->runtime, $node->active);
Dries's avatar
 
Dries committed
168

Steven Wittens's avatar
Steven Wittens committed
169 170 171
  foreach ($node->choice as $choice) {
    if ($choice['chtext'] != '') {
      db_query("INSERT INTO {poll_choices} (nid, chtext, chvotes, chorder) VALUES (%d, '%s', %d, %d)", $node->nid, $choice['chtext'], $choice['chvotes'], $i++);
172 173
    }
  }
174 175
}

Dries's avatar
 
Dries committed
176 177 178
/**
 * Implementation of hook_link().
 */
Steven Wittens's avatar
Steven Wittens committed
179
function poll_link($type, $node = 0, $main) {
Dries's avatar
 
Dries committed
180 181
  $links = array();

Dries's avatar
 
Dries committed
182
  if ($type == 'page' && user_access('access content')) {
Steven Wittens's avatar
Steven Wittens committed
183
    $links[] = l(t('polls'), 'poll', array('title' => t('View the list of polls on this site.')));
Steven Wittens's avatar
Steven Wittens committed
184
  }
Dries's avatar
 
Dries committed
185 186

  return $links;
187
}
Dries's avatar
 
Dries committed
188

Dries's avatar
 
Dries committed
189 190 191 192 193 194 195 196 197 198 199
/**
 * Implementation of hook_menu().
 */
function poll_menu() {
  $items = array();
  $items[] = array('path' => 'node/add/poll', 'title' => t('poll'),
    'access' => user_access('create polls'));
  $items[] = array('path' => 'poll', 'title' => t('polls'),
    'callback' => 'poll_page',
    'access' => user_access('access content'),
    'type' => MENU_SUGGESTED_ITEM);
Steven Wittens's avatar
Steven Wittens committed
200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218

  $items[] = array('path' => 'poll/vote',
    'title' => t('vote'),
    'callback' => 'poll_vote',
    'access' => user_access('vote on polls'),
    'type' => MENU_CALLBACK);

  if (arg(0) == 'node' && is_numeric(arg(1))) {
    $node = node_load(array('nid' => arg(1)));

    if ($node->type == 'poll' && $node->allowvotes) {
      $items[] = array('path' => 'node/'. arg(1) .'/results',
        'title' => t('results'),
        'callback' => 'poll_results',
        'access' => user_access('access content'),
        'weight' => 3,
        'type' => MENU_LOCAL_TASK);
    }
  }
Dries's avatar
 
Dries committed
219 220 221
  return $items;
}

Steven Wittens's avatar
Steven Wittens committed
222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237
/**
 * Determine an adjusted user id, to allow for basic tracking of anonymous
 * users (IP-based).
 */
function poll_uid() {
  global $user;
  if ($user->uid) {
     // Pad the UID with underscores to allow a simple strstr() search
    $id = '_'. $user->uid .'_';
  }
  else {
    $id = $_SERVER['REMOTE_ADDR'];
  }
  return $id;
}

238 239 240
/**
 * Implementation of hook_load().
 */
241 242
function poll_load($node) {
  // Load the appropriate choices into the $node object
Dries's avatar
 
Dries committed
243
  $poll = db_fetch_object(db_query("SELECT runtime, voters, active FROM {poll} WHERE nid = %d", $node->nid));
Dries's avatar
 
Dries committed
244

Dries's avatar
 
Dries committed
245
  $result = db_query("SELECT chtext, chvotes, chorder FROM {poll_choices} WHERE nid=%d ORDER BY chorder", $node->nid);
Steven Wittens's avatar
Steven Wittens committed
246 247
  while ($choice = db_fetch_array($result)) {
    $poll->choice[$choice['chorder']] = $choice;
248
  }
Steven Wittens's avatar
Steven Wittens committed
249

Steven Wittens's avatar
Steven Wittens committed
250 251 252 253 254 255 256
  // Determine whether or not this user is allowed to vote
  $poll->allowvotes = false;
  if (user_access('vote on polls')) {
    if (!strstr($poll->voters, poll_uid())) {
      $poll->allowvotes = $poll->active;
    }
  }
257 258
  return $poll;
}
Dries's avatar
 
Dries committed
259

260 261 262
/**
 * Implementation of hook_node_name().
 */
Dries's avatar
 
Dries committed
263 264
function poll_node_name($node) {
  return t("poll");
265
}
266

Steven Wittens's avatar
Steven Wittens committed
267
function poll_page() {
Steven Wittens's avatar
Steven Wittens committed
268
  // List all polls
Dries's avatar
 
Dries committed
269
  $result = pager_query("SELECT DISTINCT(n.nid), n.title, p.active, SUM(c.chvotes) AS votes FROM {node} n ". node_access_join_sql() ." INNER JOIN {poll} p ON n.nid=p.nid INNER JOIN {poll_choices} c ON n.nid=c.nid WHERE type = 'poll' AND status = 1 AND ". node_access_where_sql() ." AND moderate = 0 GROUP BY n.nid, n.title, p.active, n.created ORDER BY n.created DESC", 15);
Steven Wittens's avatar
Steven Wittens committed
270
  $output = '<ul>';
Steven Wittens's avatar
Steven Wittens committed
271
  while ($node = db_fetch_object($result)) {
Dries's avatar
 
Dries committed
272
    $output .= '<li>'. l($node->title, "node/$node->nid") .' - '. format_plural($node->votes, '1 vote', '%count votes') .' - '. ($node->active ? t('open') : t('closed')) .'</li>';
Steven Wittens's avatar
Steven Wittens committed
273
  }
Steven Wittens's avatar
Steven Wittens committed
274 275 276
  $output .= '</ul>';
  $output .= theme("pager", NULL, 15);
  print theme('page', $output);
Steven Wittens's avatar
Steven Wittens committed
277 278
}

279 280 281
/**
 * Implementation of hook_perm().
 */
282
function poll_perm() {
Steven Wittens's avatar
Steven Wittens committed
283
  return array('create polls', 'vote on polls');
284
}
Dries's avatar
 
Dries committed
285

286 287 288
/**
 * Creates a simple teaser that lists all the choices.
 */
289
function poll_teaser($node) {
Dries's avatar
Dries committed
290
  if (is_array($node->choice)) {
Steven Wittens's avatar
Steven Wittens committed
291 292
    foreach ($node->choice as $k => $choice) {
      $teaser .= '* '. $choice['chtext'] .'\n';
293 294 295 296
    }
  }
  return $teaser;
}
297

Steven Wittens's avatar
Steven Wittens committed
298
/**
299
 * Generates the voting form for a poll.
Steven Wittens's avatar
Steven Wittens committed
300 301
 */
function poll_view_voting(&$node, $main, $page, $block) {
Steven Wittens's avatar
Steven Wittens committed
302
  $url = request_uri();
Dries's avatar
 
Dries committed
303
  $output = '<div class="poll">';
Dries's avatar
 
Dries committed
304

Dries's avatar
 
Dries committed
305
  $form = '<div class="vote-form">';
Steven Wittens's avatar
Steven Wittens committed
306
  $form .= '<div class="choices">';
Dries's avatar
 
Dries committed
307
  if ($node->choice) {
Steven Wittens's avatar
Steven Wittens committed
308 309 310
    $list = array();
    foreach ($node->choice as $i => $choice) {
      $list[$i] = drupal_specialchars($choice['chtext']);
311
    }
Steven Wittens's avatar
Steven Wittens committed
312
    $form .= form_radios($page ? '' : $node->title, 'choice', -1, $list);
Dries's avatar
 
Dries committed
313
  }
Steven Wittens's avatar
Steven Wittens committed
314 315 316 317
  $form .= '</div>';
  $form .= form_hidden('nid', $node->nid);
  $form .= form_submit(t('Vote'), 'vote') .'</div>';

Steven Wittens's avatar
Steven Wittens committed
318
  $output .= form($form, 'post', url('poll/vote/'. $node->nid));
Steven Wittens's avatar
Steven Wittens committed
319
  $output .= '</div>';
Steven Wittens's avatar
Steven Wittens committed
320 321 322 323

  return $output;
}

324 325 326
/**
 * Generates a graphical representation of the results of a poll.
 */
Steven Wittens's avatar
Steven Wittens committed
327
function poll_view_results(&$node, $main, $page, $block) {
Steven Wittens's avatar
Steven Wittens committed
328
  // Display the results
Dries's avatar
 
Dries committed
329

Steven Wittens's avatar
Steven Wittens committed
330
  // Count the votes and find the maximum
Steven Wittens's avatar
Steven Wittens committed
331 332 333
  foreach ($node->choice as $choice) {
    $votestotal += $choice['chvotes'];
    $votesmax = max($votesmax, $choice['chvotes']);
Steven Wittens's avatar
Steven Wittens committed
334 335
  }

Dries's avatar
 
Dries committed
336
  // Output the divs for the text, bars and percentages
Steven Wittens's avatar
Steven Wittens committed
337 338 339 340 341 342 343 344 345 346 347 348
  $output .= '<div class="poll">';
  if ($block) {
    $output .= '<div class="title">'. $node->title .'</div>';
  }
  foreach ($node->choice as $i => $choice) {
    if ($choice['chtext'] != '') {
      $percentage = round($choice['chvotes'] * 100 / max($votestotal, 1));
      $output .= '<div class="text">'. drupal_specialchars($choice['chtext']) .'</div>';
      $output .= '<div class="bar">';
      $output .= '<div style="width: '. $percentage .'%;" class="foreground"></div>';
      $output .= '</div>';
      $output .= '<div class="percent">'. $percentage .'%'. (!$block ? ' ('. format_plural($choice['chvotes'], '1 vote', '%count votes') .')' : '') .'</div>';
349
    }
350
  }
Steven Wittens's avatar
Steven Wittens committed
351
  $output .= '<div class="total">'. t('Total votes') .": $votestotal</div>";
Dries's avatar
 
Dries committed
352

Steven Wittens's avatar
Steven Wittens committed
353
  $output .= '</div>';
Steven Wittens's avatar
Steven Wittens committed
354 355 356 357

  return $output;
}

Steven Wittens's avatar
Steven Wittens committed
358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378
/**
 * Callback for the 'results' tab for polls you can vote on
 */
function poll_results() {
  if ($node = node_load(array('nid' => arg(1)))) {
    print theme('page', node_show($node, 0), $node->title);
  }
  else {
    drupal_not_found();
  }
}

/**
 * Callback for processing a vote
 */
function poll_vote(&$node) {
  $nid = arg(2);
  if ($node = node_load(array('nid' => $nid))) {
    $edit = $_POST['edit'];
    $choice = $edit['choice'];
    $vote = $_POST['vote'];
Steven Wittens's avatar
Steven Wittens committed
379 380 381 382 383 384 385 386 387

    if (isset($choice) && isset($node->choice[$choice])) {
      if ($node->allowvotes) {
        $id = poll_uid();
        $node->voters = $node->voters ? ($node->voters .' '. $id) : $id;
        db_query("UPDATE {poll} SET voters='%s' WHERE nid = %d", $node->voters, $node->nid);
        db_query("UPDATE {poll_choices} SET chvotes = chvotes + 1 WHERE nid = %d AND chorder = %d", $node->nid, $choice);
        $node->allowvotes = false;
        $node->choice[$choice]['chvotes']++;
Dries's avatar
 
Dries committed
388
        drupal_set_message(t('Your vote was recorded.'));
Steven Wittens's avatar
Steven Wittens committed
389 390
      }
      else {
Dries's avatar
 
Dries committed
391
        drupal_set_message(t("You're not allowed to vote on this poll."), 'error');
Steven Wittens's avatar
Steven Wittens committed
392 393 394
      }
    }
    else {
Dries's avatar
 
Dries committed
395
      drupal_set_message(t("You didn't specify a valid poll choice."), 'error');
Steven Wittens's avatar
Steven Wittens committed
396
    }
Steven Wittens's avatar
Steven Wittens committed
397 398 399 400 401

    drupal_goto('node/'. $nid);
  }
  else {
    drupal_not_found();
Steven Wittens's avatar
Steven Wittens committed
402 403 404
  }
}

405 406 407 408 409 410 411
/**
 * Implementation of hook_view().
 *
 * @param $block
 *   An extra parameter that adapts the hook to display a block-ready
 *   rendering of the poll.
 */
Dries's avatar
 
Dries committed
412
function poll_view(&$node, $teaser = FALSE, $page = FALSE, $block = FALSE) {
Dries's avatar
 
Dries committed
413
  global $user;
Dries's avatar
 
Dries committed
414
  $output = '';
Steven Wittens's avatar
Steven Wittens committed
415

Steven Wittens's avatar
Steven Wittens committed
416
  if ($node->allowvotes && ($block || arg(2) != 'results')) {
Steven Wittens's avatar
Steven Wittens committed
417 418 419 420 421
    $output .= poll_view_voting($node, $main, $page, $block);
  }
  else {
    $output .= poll_view_results($node, $main, $page, $block);
  }
Steven Wittens's avatar
Steven Wittens committed
422

Steven Wittens's avatar
Steven Wittens committed
423
  // Special display for side-block
Steven Wittens's avatar
Steven Wittens committed
424
  if ($block) {
Steven Wittens's avatar
Steven Wittens committed
425 426
    // No 'read more' link
    $node->body = $node->teaser = '';
Steven Wittens's avatar
Steven Wittens committed
427

Steven Wittens's avatar
Steven Wittens committed
428
    $links = link_node($node, $main);
Steven Wittens's avatar
Steven Wittens committed
429
    $links[] = l(t('older polls'), 'poll', array('title' => t('View the list of polls on this site.')));
Steven Wittens's avatar
Steven Wittens committed
430 431 432
    if ($node->allowvotes && $block) {
      $links[] = l(t('results'), 'node/'. $node->nid .'/results', array('title' => t('View the current poll results.')));
    }
Steven Wittens's avatar
Steven Wittens committed
433

Steven Wittens's avatar
Steven Wittens committed
434
    $output .= '<div class="links">'. theme("links", $links) .'</div>';
Steven Wittens's avatar
Steven Wittens committed
435 436 437
  }

  $node->body = $node->teaser = $output;
438 439
}

440 441 442
/**
 * Implementation of hook_update().
 */
443
function poll_update($node) {
Steven Wittens's avatar
Steven Wittens committed
444
  db_query('UPDATE {poll} SET runtime = %d, active = %d WHERE nid = %d', $node->runtime, $node->active, $node->nid);
445

Steven Wittens's avatar
Steven Wittens committed
446 447 448 449
  db_query('DELETE FROM {poll_choices} WHERE nid = %d', $node->nid);
  foreach ($node->choice as $choice) {
    $chvotes = (int)$choice['chvotes'];
    $chtext = $choice['chtext'];
450

Steven Wittens's avatar
Steven Wittens committed
451 452
    if ($chtext != '') {
      db_query("INSERT INTO {poll_choices} (nid, chtext, chvotes, chorder) VALUES (%d, '%s', %d, %d)", $node->nid, $chtext, $chvotes, $i++);
453
    }
454 455
  }
}
456

457
?>