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

Steven Wittens's avatar
Steven Wittens committed
4 5
function poll_help($section = 'admin/help#poll') {
  $output = '';
Steven Wittens's avatar
Steven Wittens committed
6

Steven Wittens's avatar
Steven Wittens committed
7
  switch ($section) {
Steven Wittens's avatar
Steven Wittens committed
8

Steven Wittens's avatar
Steven Wittens committed
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
    case 'admin/help#poll':
      $output .= t("
      <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>
      <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/permission"), "%poll" => url("poll")));
      break;
    case 'admin/system/modules#description':
      $output = t("Enables your site to capture votes on different topics in the form of multiple choice questions.");
      break;
    case 'node/add#poll':
      $output = t("A poll is a multiple-choice question which visitors can vote on.");
      break;
Steven Wittens's avatar
Steven Wittens committed
26
  }
Steven Wittens's avatar
Steven Wittens committed
27 28

  return $output;
29 30
}

31
function poll_access($op, $node) {
Steven Wittens's avatar
Steven Wittens committed
32
  if ($op == 'view') {
33
    return $node->status;
34 35
  }

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

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

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

65 66
function poll_cron() {
  // Close polls that have exceeded their allowed runtime
Dries's avatar
 
Dries committed
67
  $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
68
  while ($poll = db_fetch_object($result)) {
Dries's avatar
 
Dries committed
69
    db_query("UPDATE {poll} SET active='0' WHERE nid = %d", $poll->nid);
Dries's avatar
 
Dries committed
70
  }
71 72
}

73
function poll_delete($node) {
Dries's avatar
 
Dries committed
74 75
  db_query("DELETE FROM {poll} WHERE nid=%d", $node->nid);
  db_query("DELETE FROM {poll_choices} WHERE nid = %d", $node->nid);
76 77
}

78
function poll_validate(&$node) {
79 80
  if (isset($node->title)) {
    // Check for at least two options and validate amount of votes:
Steven Wittens's avatar
Steven Wittens committed
81 82 83 84
    $realchoices = 0;
    foreach ($node->choice as $i => $choice) {
      if ($choice['chtext'] != '') {
        $realchoices++;
85
      }
Dries's avatar
 
Dries committed
86

Steven Wittens's avatar
Steven Wittens committed
87 88
      if ($choice['chvotes'] < 0) {
        $error["choice][$i][chvotes"] = theme('error', t("Negative values are not allowed."));
89
      }
90
    }
Dries's avatar
 
Dries committed
91

Steven Wittens's avatar
Steven Wittens committed
92 93
    if ($realchoices < 2) {
      $error["choice][0][chtext"] = theme('error', t("You must fill in at least two choices."));
94 95
    }
  }
96 97 98 99 100 101

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

  return $error;
}

Steven Wittens's avatar
Steven Wittens committed
102 103
function poll_form(&$node, &$error) {
  $admin = user_access('administer nodes');
104

Steven Wittens's avatar
Steven Wittens committed
105 106 107
  if (function_exists('taxonomy_node_form')) {
    $output = implode('', taxonomy_node_form('poll', $node));
  }
108

Steven Wittens's avatar
Steven Wittens committed
109 110 111
  if (!isset($node->choices)) {
    $node->choices = max(2, count($node->choice) ? count($node->choice) : 5);
  }
Dries's avatar
 
Dries committed
112

Steven Wittens's avatar
Steven Wittens committed
113 114 115
  // User ticked 'need more choices'
  if ($node->morechoices) {
    $node->choices *= 2;
116 117
  }

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

Steven Wittens's avatar
Steven Wittens committed
120
  // Poll choices
Dries's avatar
 
Dries committed
121
  $opts = drupal_map_assoc(range(2, $node->choices * 2 + 5));
122
  for ($a = 0; $a < $node->choices; $a++) {
Steven Wittens's avatar
Steven Wittens committed
123
    $group1 .= form_textfield(t('Choice %n', array('%n' => ($a + 1))), "choice][$a][chtext", $node->choice[$a]['chtext'], 50, 127, $error["choice][$a][chtext"]);
124
    if ($admin) {
Steven Wittens's avatar
Steven Wittens committed
125
      $group1 .= form_textfield(t('Votes for choice %n', array('%n' => ($a + 1))), "choice][$a][chvotes", (int)$node->choice[$a]['chvotes'], 7, 7, $error["choice][$a][chvotes"]);
126 127
    }
  }
Steven Wittens's avatar
Steven Wittens committed
128 129 130
  $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
131

Dries's avatar
 
Dries committed
132

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

Steven Wittens's avatar
Steven Wittens committed
137 138
  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
139
  }
Steven Wittens's avatar
Steven Wittens committed
140
  $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
141 142

  $output .= form_group(t('Settings'), $group2);
Steven Wittens's avatar
Steven Wittens committed
143
  $output .= '</div>';
Dries's avatar
 
Dries committed
144 145

  return $output;
146
}
147

148
function poll_insert($node) {
Steven Wittens's avatar
Steven Wittens committed
149
  if (!user_access('administer nodes')) {
150
    // Make sure all votes are 0 initially
Steven Wittens's avatar
Steven Wittens committed
151 152 153
    foreach ($node->choice as $i => $choice) {
      $node->choice[$i]['chvotes'] = 0;
    }
154 155
    $node->active = 1;
  }
156

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

Steven Wittens's avatar
Steven Wittens committed
159 160 161
  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++);
162 163
    }
  }
164 165
}

Dries's avatar
 
Dries committed
166 167 168
/**
 * Implementation of hook_link().
 */
Steven Wittens's avatar
Steven Wittens committed
169
function poll_link($type, $node = 0, $main) {
Dries's avatar
 
Dries committed
170 171
  $links = array();

Steven Wittens's avatar
Steven Wittens committed
172
  if ($type == 'system') {
Dries's avatar
 
Dries committed
173 174
    menu('node/add/poll', t('poll'), user_access('create polls') ? 'node_page' : MENU_DENIED, 0);
    menu('poll', t('polls'), user_access('access content') ? 'poll_page' : MENU_DENIED, 0, MENU_HIDE);
175
  }
Steven Wittens's avatar
Steven Wittens committed
176 177
  else if ($type == 'page' && user_access('access content')) {
    $links[] = l(t('polls'), 'poll', array('title' => t('View the list of polls on this site.')));
Steven Wittens's avatar
Steven Wittens committed
178
  }
Steven Wittens's avatar
Steven Wittens committed
179
  else if ($type == 'node' && $node->type == 'poll') {
Steven Wittens's avatar
Steven Wittens committed
180 181 182 183 184
    /*
    ** Add links to allow the user to switch between the results and the voting
    ** form, if he/she hasn't voted yet.
    */

Steven Wittens's avatar
Steven Wittens committed
185 186 187
    if ($node->allowvotes) {
      if (arg(3) == 'results') {
        $links[] = l(t('voting form'), 'node/view/'. $node->nid);
Steven Wittens's avatar
Steven Wittens committed
188 189
      }
      else {
Steven Wittens's avatar
Steven Wittens committed
190
        $links[] = l(t('view results'), 'node/view/'. $node->nid .'/results');
Steven Wittens's avatar
Steven Wittens committed
191 192 193
      }
    }
  }
Dries's avatar
 
Dries committed
194 195

  return $links;
196
}
Dries's avatar
 
Dries committed
197

Steven Wittens's avatar
Steven Wittens committed
198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213
/**
 * 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;
}

214 215
function poll_load($node) {
  // Load the appropriate choices into the $node object
Dries's avatar
 
Dries committed
216
  $poll = db_fetch_object(db_query("SELECT runtime, voters, active FROM {poll} WHERE nid = %d", $node->nid));
Dries's avatar
 
Dries committed
217

Dries's avatar
 
Dries committed
218
  $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
219 220
  while ($choice = db_fetch_array($result)) {
    $poll->choice[$choice['chorder']] = $choice;
221
  }
Steven Wittens's avatar
Steven Wittens committed
222

Steven Wittens's avatar
Steven Wittens committed
223 224 225 226 227 228 229
  // 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;
    }
  }
230 231
  return $poll;
}
Dries's avatar
 
Dries committed
232

Dries's avatar
 
Dries committed
233 234
function poll_node_name($node) {
  return t("poll");
235
}
236

Steven Wittens's avatar
Steven Wittens committed
237
function poll_page() {
Steven Wittens's avatar
Steven Wittens committed
238 239 240
  // 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
241
  while ($node = db_fetch_object($result)) {
Steven Wittens's avatar
Steven Wittens committed
242
    $output .= '<li>'. l($node->title, "node/view/$node->nid") .' - '. format_plural($node->votes, '1 vote', '%count votes') .' - '. ($node->active ? t('open') : t('closed')) .'</li>';
Steven Wittens's avatar
Steven Wittens committed
243
  }
Steven Wittens's avatar
Steven Wittens committed
244 245 246
  $output .= '</ul>';
  $output .= theme("pager", NULL, 15);
  print theme('page', $output);
Steven Wittens's avatar
Steven Wittens committed
247 248
}

249
function poll_perm() {
Steven Wittens's avatar
Steven Wittens committed
250
  return array('create polls', 'vote on polls');
251
}
Dries's avatar
 
Dries committed
252

253 254
function poll_teaser($node) {
  // Create a simple teaser that lists all the choices
Dries's avatar
Dries committed
255
  if (is_array($node->choice)) {
Steven Wittens's avatar
Steven Wittens committed
256 257
    foreach ($node->choice as $k => $choice) {
      $teaser .= '* '. $choice['chtext'] .'\n';
258 259 260 261
    }
  }
  return $teaser;
}
262

Steven Wittens's avatar
Steven Wittens committed
263 264 265 266
/**
 * Display the vote form
 */
function poll_view_voting(&$node, $main, $page, $block) {
Steven Wittens's avatar
Steven Wittens committed
267
  $url = request_uri();
Steven Wittens's avatar
Steven Wittens committed
268
  $output .= '<div class="poll">';
Dries's avatar
 
Dries committed
269

Steven Wittens's avatar
Steven Wittens committed
270 271
  $form .= '<div class="vote-form">';
  $form .= '<div class="choices">';
Dries's avatar
 
Dries committed
272
  if ($node->choice) {
Steven Wittens's avatar
Steven Wittens committed
273 274 275
    $list = array();
    foreach ($node->choice as $i => $choice) {
      $list[$i] = drupal_specialchars($choice['chtext']);
276
    }
Steven Wittens's avatar
Steven Wittens committed
277
    $form .= form_radios($page ? '' : $node->title, 'choice', -1, $list);
Dries's avatar
 
Dries committed
278
  }
Steven Wittens's avatar
Steven Wittens committed
279 280 281 282 283 284
  $form .= '</div>';
  $form .= form_hidden('nid', $node->nid);
  $form .= form_submit(t('Vote'), 'vote') .'</div>';

  $output .= form($form, 'post', url('node/view/'. $node->nid));
  $output .= '</div>';
Steven Wittens's avatar
Steven Wittens committed
285 286 287 288

  return $output;
}

Steven Wittens's avatar
Steven Wittens committed
289
function poll_view_results(&$node, $main, $page, $block) {
Steven Wittens's avatar
Steven Wittens committed
290
  // Display the results
Dries's avatar
 
Dries committed
291

Steven Wittens's avatar
Steven Wittens committed
292
  // Count the votes and find the maximum
Steven Wittens's avatar
Steven Wittens committed
293 294 295
  foreach ($node->choice as $choice) {
    $votestotal += $choice['chvotes'];
    $votesmax = max($votesmax, $choice['chvotes']);
Steven Wittens's avatar
Steven Wittens committed
296 297
  }

Dries's avatar
 
Dries committed
298
  // Output the divs for the text, bars and percentages
Steven Wittens's avatar
Steven Wittens committed
299 300 301 302 303 304 305 306 307 308 309 310
  $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>';
311
    }
312
  }
Steven Wittens's avatar
Steven Wittens committed
313
  $output .= '<div class="total">'. t('Total votes') .": $votestotal</div>";
Dries's avatar
 
Dries committed
314

Steven Wittens's avatar
Steven Wittens committed
315
  $output .= '</div>';
Steven Wittens's avatar
Steven Wittens committed
316 317 318 319 320

  return $output;
}

function poll_view_processvote(&$node) {
Steven Wittens's avatar
Steven Wittens committed
321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341
  $edit = $_POST['edit'];
  $choice = $edit['choice'];
  $vote = $_POST['vote'];

  if ($vote == t('Vote')) {
    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']++;
        drupal_set_message(t('Your vote was recorded.'));
      }
      else {
        drupal_set_message(t("You're not allowed to vote on this poll."), 'error');
      }
    }
    else {
      drupal_set_message(t("You didn't specify a valid poll choice."), 'error');
Steven Wittens's avatar
Steven Wittens committed
342
    }
Steven Wittens's avatar
Steven Wittens committed
343 344 345
  }
}

346
function poll_view(&$node, $main = 0, $page = 0, $block = 0) {
Dries's avatar
 
Dries committed
347
  global $user;
Steven Wittens's avatar
Steven Wittens committed
348

Steven Wittens's avatar
Steven Wittens committed
349 350 351 352
  if (!$block) {
    // Because the voting form is embedded in the node-display, we process the data here
    poll_view_processvote($node);
  }
Steven Wittens's avatar
Steven Wittens committed
353

Steven Wittens's avatar
Steven Wittens committed
354 355 356 357 358 359
  if ($node->allowvotes && (arg(2) != $node->nid || arg(3) != 'results')) {
    $output .= poll_view_voting($node, $main, $page, $block);
  }
  else {
    $output .= poll_view_results($node, $main, $page, $block);
  }
Steven Wittens's avatar
Steven Wittens committed
360

Steven Wittens's avatar
Steven Wittens committed
361
  // Special display for side-block
Steven Wittens's avatar
Steven Wittens committed
362
  if ($block) {
Steven Wittens's avatar
Steven Wittens committed
363 364
    // No 'read more' link
    $node->body = $node->teaser = '';
Steven Wittens's avatar
Steven Wittens committed
365

Steven Wittens's avatar
Steven Wittens committed
366
    $links = link_node($node, $main);
Steven Wittens's avatar
Steven Wittens committed
367
    $links[] = l(t('older polls'), 'poll', array('title' => t('View the list of polls on this site.')));
Steven Wittens's avatar
Steven Wittens committed
368

Steven Wittens's avatar
Steven Wittens committed
369
    $output .= '<div class="links">'. theme("links", $links) .'</div>';
Steven Wittens's avatar
Steven Wittens committed
370 371 372
  }

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

374
  // We also use poll_view() for the side-block
Steven Wittens's avatar
Steven Wittens committed
375
  if (!$block) {
Steven Wittens's avatar
Steven Wittens committed
376
    return theme('node', $node, $main, $page);
377 378 379
  }
}

380
function poll_update($node) {
Steven Wittens's avatar
Steven Wittens committed
381
  db_query('UPDATE {poll} SET runtime = %d, active = %d WHERE nid = %d', $node->runtime, $node->active, $node->nid);
382

Steven Wittens's avatar
Steven Wittens committed
383 384 385 386
  db_query('DELETE FROM {poll_choices} WHERE nid = %d', $node->nid);
  foreach ($node->choice as $choice) {
    $chvotes = (int)$choice['chvotes'];
    $chtext = $choice['chtext'];
387

Steven Wittens's avatar
Steven Wittens committed
388 389
    if ($chtext != '') {
      db_query("INSERT INTO {poll_choices} (nid, chtext, chvotes, chorder) VALUES (%d, '%s', %d, %d)", $node->nid, $chtext, $chvotes, $i++);
390
    }
391 392
  }
}
393

394
?>