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
  if ($op == 'view') {
31
    return $node->status;
32 33
  }

Steven Wittens's avatar
Steven Wittens committed
34 35
  if ($op == 'create') {
    return user_access('create polls');
Dries's avatar
 
Dries committed
36
  }
Steven Wittens's avatar
Steven Wittens committed
37 38
}

39 40 41 42 43
/**
 * Implementation of hook_block().
 *
 * Generates a block containing the latest poll.
 */
Steven Wittens's avatar
Steven Wittens committed
44 45 46 47
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
48 49 50
      return $blocks;
    }
    else {
51
      // Retrieve the latest poll.
Dries's avatar
 
Dries committed
52
      $timestamp = db_result(db_query("SELECT MAX(created) FROM {node} WHERE type = 'poll' AND status = '1' AND moderate = '0'"));
Dries's avatar
 
Dries committed
53
      if ($timestamp) {
Steven Wittens's avatar
Steven Wittens committed
54 55
        $poll = node_load(array('type' => 'poll', 'created' => $timestamp, 'moderate' => 0, 'status' => 1));

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

68 69 70 71 72
/**
 * Implementation of hook_cron().
 *
 * Closes polls that have exceeded their allowed runtime.
 */
73
function poll_cron() {
Dries's avatar
 
Dries committed
74
  $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
75
  while ($poll = db_fetch_object($result)) {
Dries's avatar
 
Dries committed
76
    db_query("UPDATE {poll} SET active='0' WHERE nid = %d", $poll->nid);
Dries's avatar
 
Dries committed
77
  }
78 79
}

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

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

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

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

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

113 114 115
/**
 * Implementation of hook_form().
 */
Dries's avatar
 
Dries committed
116
function poll_form(&$node) {
Steven Wittens's avatar
Steven Wittens committed
117
  $admin = user_access('administer nodes');
118

Steven Wittens's avatar
Steven Wittens committed
119 120 121
  if (function_exists('taxonomy_node_form')) {
    $output = implode('', taxonomy_node_form('poll', $node));
  }
122

Steven Wittens's avatar
Steven Wittens committed
123 124 125
  if (!isset($node->choices)) {
    $node->choices = max(2, count($node->choice) ? count($node->choice) : 5);
  }
Dries's avatar
 
Dries committed
126

127
  // User ticked 'need more choices'.
Steven Wittens's avatar
Steven Wittens committed
128 129
  if ($node->morechoices) {
    $node->choices *= 2;
130 131
  }

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

Steven Wittens's avatar
Steven Wittens committed
134
  // Poll choices
Dries's avatar
 
Dries committed
135
  $opts = drupal_map_assoc(range(2, $node->choices * 2 + 5));
136
  for ($a = 0; $a < $node->choices; $a++) {
Dries's avatar
 
Dries committed
137
    $group1 .= form_textfield(t('Choice %n', array('%n' => ($a + 1))), "choice][$a][chtext", $node->choice[$a]['chtext'], 50, 127);
138
    if ($admin) {
Dries's avatar
 
Dries committed
139
      $group1 .= form_textfield(t('Votes for choice %n', array('%n' => ($a + 1))), "choice][$a][chvotes", (int)$node->choice[$a]['chvotes'], 7, 7);
140 141
    }
  }
Steven Wittens's avatar
Steven Wittens committed
142 143 144
  $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
145

Dries's avatar
 
Dries committed
146

Steven Wittens's avatar
Steven Wittens committed
147
  // Poll attributes
148
  $_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
149
  $_active = array(0 => t('Closed'), 1 => t('Active'));
Dries's avatar
 
Dries committed
150

Steven Wittens's avatar
Steven Wittens committed
151 152
  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
153
  }
Steven Wittens's avatar
Steven Wittens committed
154
  $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
155 156

  $output .= form_group(t('Settings'), $group2);
Steven Wittens's avatar
Steven Wittens committed
157
  $output .= '</div>';
Dries's avatar
 
Dries committed
158 159

  return $output;
160
}
161

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

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

Steven Wittens's avatar
Steven Wittens committed
173 174 175
  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++);
176 177
    }
  }
178 179
}

Dries's avatar
 
Dries committed
180 181 182
/**
 * Implementation of hook_link().
 */
Steven Wittens's avatar
Steven Wittens committed
183
function poll_link($type, $node = 0, $main) {
Dries's avatar
 
Dries committed
184 185
  $links = array();

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

  return $links;
191
}
Dries's avatar
 
Dries committed
192

Dries's avatar
 
Dries committed
193 194 195 196 197 198 199 200 201 202 203
/**
 * 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
204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222

  $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
223 224 225
  return $items;
}

Steven Wittens's avatar
Steven Wittens committed
226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241
/**
 * 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;
}

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

Dries's avatar
 
Dries committed
249
  $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
250 251
  while ($choice = db_fetch_array($result)) {
    $poll->choice[$choice['chorder']] = $choice;
252
  }
Steven Wittens's avatar
Steven Wittens committed
253

Steven Wittens's avatar
Steven Wittens committed
254 255 256 257 258 259 260
  // 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;
    }
  }
261 262
  return $poll;
}
Dries's avatar
 
Dries committed
263

264 265 266
/**
 * Implementation of hook_node_name().
 */
Dries's avatar
 
Dries committed
267 268
function poll_node_name($node) {
  return t("poll");
269
}
270

Steven Wittens's avatar
Steven Wittens committed
271
function poll_page() {
Steven Wittens's avatar
Steven Wittens committed
272 273 274
  // List all polls
  $result = pager_query("SELECT n.nid, n.title, p.active, SUM(c.chvotes) AS votes FROM {node} n 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 moderate = '0' GROUP BY n.nid, n.title, p.active, n.created ORDER BY n.created DESC", 15);
  $output = '<ul>';
Steven Wittens's avatar
Steven Wittens committed
275
  while ($node = db_fetch_object($result)) {
Dries's avatar
 
Dries committed
276
    $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
277
  }
Steven Wittens's avatar
Steven Wittens committed
278 279 280
  $output .= '</ul>';
  $output .= theme("pager", NULL, 15);
  print theme('page', $output);
Steven Wittens's avatar
Steven Wittens committed
281 282
}

283 284 285
/**
 * Implementation of hook_perm().
 */
286
function poll_perm() {
Steven Wittens's avatar
Steven Wittens committed
287
  return array('create polls', 'vote on polls');
288
}
Dries's avatar
 
Dries committed
289

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

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

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

Steven Wittens's avatar
Steven Wittens committed
322
  $output .= form($form, 'post', url('poll/vote/'. $node->nid));
Steven Wittens's avatar
Steven Wittens committed
323
  $output .= '</div>';
Steven Wittens's avatar
Steven Wittens committed
324 325 326 327

  return $output;
}

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

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

Dries's avatar
 
Dries committed
340
  // Output the divs for the text, bars and percentages
Steven Wittens's avatar
Steven Wittens committed
341 342 343 344 345 346 347 348 349 350 351 352
  $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>';
353
    }
354
  }
Steven Wittens's avatar
Steven Wittens committed
355
  $output .= '<div class="total">'. t('Total votes') .": $votestotal</div>";
Dries's avatar
 
Dries committed
356

Steven Wittens's avatar
Steven Wittens committed
357
  $output .= '</div>';
Steven Wittens's avatar
Steven Wittens committed
358 359 360 361

  return $output;
}

Steven Wittens's avatar
Steven Wittens committed
362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382
/**
 * 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
383 384 385 386 387 388 389 390 391

    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']++;
392
        drupal_set_message(t('your vote was recorded.'));
Steven Wittens's avatar
Steven Wittens committed
393 394
      }
      else {
395
        drupal_set_message(t("you're not allowed to vote on this poll."), 'error');
Steven Wittens's avatar
Steven Wittens committed
396 397 398
      }
    }
    else {
399
      drupal_set_message(t("you didn't specify a valid poll choice."), 'error');
Steven Wittens's avatar
Steven Wittens committed
400
    }
Steven Wittens's avatar
Steven Wittens committed
401 402 403 404 405

    drupal_goto('node/'. $nid);
  }
  else {
    drupal_not_found();
Steven Wittens's avatar
Steven Wittens committed
406 407 408
  }
}

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

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

Steven Wittens's avatar
Steven Wittens committed
427
  // Special display for side-block
Steven Wittens's avatar
Steven Wittens committed
428
  if ($block) {
Steven Wittens's avatar
Steven Wittens committed
429 430
    // No 'read more' link
    $node->body = $node->teaser = '';
Steven Wittens's avatar
Steven Wittens committed
431

Steven Wittens's avatar
Steven Wittens committed
432
    $links = link_node($node, $main);
Steven Wittens's avatar
Steven Wittens committed
433
    $links[] = l(t('older polls'), 'poll', array('title' => t('View the list of polls on this site.')));
Steven Wittens's avatar
Steven Wittens committed
434 435 436
    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
437

Steven Wittens's avatar
Steven Wittens committed
438
    $output .= '<div class="links">'. theme("links", $links) .'</div>';
Steven Wittens's avatar
Steven Wittens committed
439 440 441
  }

  $node->body = $node->teaser = $output;
442

443
  // We also use poll_view() for the side-block
Steven Wittens's avatar
Steven Wittens committed
444
  if (!$block) {
Steven Wittens's avatar
Steven Wittens committed
445
    return theme('node', $node, $main, $page);
446 447 448
  }
}

449 450 451
/**
 * Implementation of hook_update().
 */
452
function poll_update($node) {
Steven Wittens's avatar
Steven Wittens committed
453
  db_query('UPDATE {poll} SET runtime = %d, active = %d WHERE nid = %d', $node->runtime, $node->active, $node->nid);
454

Steven Wittens's avatar
Steven Wittens committed
455 456 457 458
  db_query('DELETE FROM {poll_choices} WHERE nid = %d', $node->nid);
  foreach ($node->choice as $choice) {
    $chvotes = (int)$choice['chvotes'];
    $chtext = $choice['chtext'];
459

Steven Wittens's avatar
Steven Wittens committed
460 461
    if ($chtext != '') {
      db_query("INSERT INTO {poll_choices} (nid, chtext, chvotes, chorder) VALUES (%d, '%s', %d, %d)", $node->nid, $chtext, $chvotes, $i++);
462
    }
463 464
  }
}
465

466
?>