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

Steven Wittens's avatar
Steven Wittens committed
4 5 6 7 8 9
function poll_allowvotes(&$node) {
  /*
  ** Only accept votes on specific cases to prevent double voting and abuse.
  ** We only need to determine this once for a poll, but we don't do this in
  ** poll_load() (i.e. for every poll that is loaded) for speed reasons.
  */
Dries's avatar
 
Dries committed
10
  global $user;
Steven Wittens's avatar
Steven Wittens committed
11 12 13 14 15 16 17 18 19 20 21 22

  if ($node->allowvotes != -1) {
    return $node;
  }

  $node->allowvotes = 0;
  if (user_access("vote on polls")) {
    if ($user->uid) {
      // Pad the UID with underscores to allow a simple strstr() search
      $id = "_". $user->uid ."_";
    }
    else {
Dries's avatar
 
Dries committed
23
      $id = $_SERVER["REMOTE_ADDR"];
Steven Wittens's avatar
Steven Wittens committed
24 25 26 27 28 29 30 31 32
    }
    if (!strstr($node->voters, $id)) {
      $node->allowvotes = $node->active;
    }

    // Save this for later
    $node->polluserid = $id;
  }
  return $node;
33 34
}

35 36 37
function poll_access($op, $node) {
  if ($op == "view") {
    return $node->status;
38 39
  }

40
  if ($op == "create") {
Dries's avatar
 
Dries committed
41
    return user_access("create polls");
Dries's avatar
 
Dries committed
42
  }
Steven Wittens's avatar
Steven Wittens committed
43 44
}

Dries's avatar
 
Dries committed
45
function poll_block($op = "list", $delta = 0) {
Dries's avatar
 
Dries committed
46 47 48 49 50 51
  if (user_access("access content")) {
    if ($op == "list") {
      $blocks[0]["info"] = t("Most recent poll");
      return $blocks;
    }
    else {
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 54 55 56
      if ($timestamp) {
        $poll = node_load(array("type" => "poll", "created" => $timestamp, "moderate" => "0", "status" => "1"));
        if ($poll->nid) {
          // Poll_view dumps the output into $poll->body
57
          poll_view($poll, 1, 0, 1);
Dries's avatar
 
Dries committed
58
        }
Dries's avatar
 
Dries committed
59
      }
Dries's avatar
 
Dries committed
60 61
      $block["subject"] = t("Poll");
      $block["content"] = "<div class=\"poll-title\">$poll->title</div>$poll->body";
Dries's avatar
 
Dries committed
62
      return $block;
63 64 65 66
    }
  }
}

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

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


81
function poll_validate(&$node) {
82 83 84 85 86 87
  if (isset($node->title)) {
    // Check for at least two options and validate amount of votes:
    for ($i = 0; $i < $node->choices; $i++) {
      if ($node->choice[$i] != "") {
        $actualchoices++;
      }
Dries's avatar
 
Dries committed
88

89
      if ($node->chvotes[$i] < 0) {
Dries's avatar
 
Dries committed
90
        $error["chvotes][$i"] = theme("error", t("Negative values are not allowed."));
91
      }
92
    }
Dries's avatar
 
Dries committed
93

94
    if ($actualchoices < 2) {
Dries's avatar
 
Dries committed
95
      $error["choice][0"] = theme("error", t("You must fill in at least two choices."));
96 97
    }
  }
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112

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

  return $error;
}

function poll_form(&$node, &$help, &$error) {
  $admin = user_access("administer nodes");

  $_duration = array(0 => t("Unlimited"), 86400 => format_interval(86400), 172800 => format_interval(172800), 345600 => format_interval(345600), 604800 => format_interval(604800), 1209600 => format_interval(1209600), 2419200 => format_interval(2419200), 4838400 => format_interval(4838400), 9676800 => format_interval(9676800), 31536000 => format_interval(31536000));
  $_active = array(0 => t("Closed"), 1 => t("Active"));

  $node->choices = $node->choices ? $node->choices : max(2, count($node->choice) ? count($node->choice) : 5);

  $help = variable_get("poll_help", "");
Dries's avatar
 
Dries committed
113

114 115 116 117
  if (function_exists("taxonomy_node_form")) {
    $output = implode("", taxonomy_node_form("poll", $node));
  }

Dries's avatar
 
Dries committed
118
  for ($c = 2; $c <= 30; $c++) {
119 120
    $opts[$c] = $c;
  }
Dries's avatar
 
Dries committed
121
  $output .= form_select(t("Number of choices"), "choices", $node->choices, $opts, t("This item sets the number of multiple choice options in the poll, but it doesn't have to equal the actual amount of options; you can leave the extra boxes empty."));
Kjartan's avatar
Kjartan committed
122
  $output .= form_submit(t("Preview")) ."<br /><br /><br />";
Dries's avatar
 
Dries committed
123

124
  for ($a = 0; $a < $node->choices; $a++) {
Dries's avatar
 
Dries committed
125
    $output .= form_textfield(t("Choice %n", array("%n" => ($a + 1))), "choice][$a", $node->choice[$a], 50, 127, $error["choice][$a"]);
126
    if ($admin) {
Dries's avatar
 
Dries committed
127
      $output .= form_textfield(t("Votes for choice %n", array("%n" => ($a + 1))), "chvotes][$a", $node->chvotes[$a] ? $node->chvotes[$a] : 0, 7, 7, $error["chvotes][$a"]);
128 129
    }
  }
Dries's avatar
 
Dries committed
130

131
  if ($admin) {
Dries's avatar
 
Dries committed
132
    $output .= form_radios(t("Poll status"), "active", isset($node->active) ? $node->active : 1, $_active);
133
  }
134

Dries's avatar
 
Dries committed
135
  $output .= form_select(t("Poll duration"), "runtime", $node->runtime ? $node->runtime : 0, $_duration, t("After this period, the poll will be closed automatically."));
136

137 138
  return $output;
}
Dries's avatar
 
Dries committed
139

Dries's avatar
 
Dries committed
140
function poll_help($section = "admin/help#poll") {
Dries's avatar
 
Dries committed
141 142 143 144
  $output = "";

  switch ($section) {

Dries's avatar
 
Dries committed
145
    case 'admin/help#poll':
Dries's avatar
 
Dries committed
146 147 148 149 150 151 152 153 154
      $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")));
Dries's avatar
 
Dries committed
155
      break;
Dries's avatar
 
Dries committed
156
    case 'admin/system/modules#description':
Dries's avatar
 
Dries committed
157 158 159 160 161
      $output = t("Enables your site to capture votes on different topics in the form of multiple choice questions.");
      break;
  }

  return $output;
162
}
163

164 165 166
function poll_insert($node) {
  if (!user_access("administer nodes")) {
    // Make sure all votes are 0 initially
Kjartan's avatar
Kjartan committed
167
    for ($i = 0; $i < count($node->chvotes); $i++) $node->chvotes[$i] = 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

173
  for ($i = 0; $i < $node->choices; $i++) {
Dries's avatar
 
Dries committed
174
    if ($node->choice[$i] != "") {
Dries's avatar
 
Dries committed
175
      db_query("INSERT INTO {poll_choices} (nid, chtext, chvotes, chorder) VALUES (%d, '%s', %d, %d)", $node->nid, $node->choice[$i], $node->chvotes[$i], $i);
176 177
    }
  }
178 179
}

Steven Wittens's avatar
Steven Wittens committed
180
function poll_link($type, $node = 0, $main) {
Dries's avatar
 
Dries committed
181 182
  $links = array();

Dries's avatar
 
Dries committed
183 184
  if ($type == "system") {
    if (user_access("create polls")) {
Dries's avatar
 
Dries committed
185 186 187
      menu("node/add/poll", t("poll"), "node_page", 0);
    }
    if (user_access("access content")) {
Dries's avatar
 
Dries committed
188
      menu("poll", t("polls"), "poll_page", 0, MENU_HIDE);
Dries's avatar
 
Dries committed
189
    }
190
  }
Steven Wittens's avatar
Steven Wittens committed
191
  else if ($type == "page" && user_access("access content")) {
Dries's avatar
 
Dries committed
192
    $links[] = l(t("polls"), "poll", array("title" => t("View the list of polls on this site.")));
Steven Wittens's avatar
Steven Wittens committed
193
  }
Steven Wittens's avatar
Steven Wittens committed
194 195 196 197 198 199 200 201
  else if ($type == "node" && $node->type == "poll") {
    /*
    ** Add links to allow the user to switch between the results and the voting
    ** form, if he/she hasn't voted yet.
    */

    // Make sure we have determined the 'allowvotes' flag
    poll_allowvotes($node);
202

Steven Wittens's avatar
Steven Wittens committed
203
    if ($node->allowvotes == 1) {
Dries's avatar
 
Dries committed
204
      $pollresults = $_GET["pollresults"];
Steven Wittens's avatar
Steven Wittens committed
205 206 207 208 209 210

      // Change the current URL: add/edit the value of pollresults[nid]
      if ($pollresults[$node->nid]) {
        // Disable
        $url = eregi_replace("pollresults\[$node->nid\]=1", "pollresults[$node->nid]=0", request_uri());

Dries's avatar
 
Dries committed
211
        $links[] = "<a href=\"". htmlentities($url) ."\">". t("voting form") . "</a>";
Steven Wittens's avatar
Steven Wittens committed
212 213 214 215 216 217 218 219 220 221
      }
      else {
        // Enable
        if (strstr(request_uri(), "pollresults[$node->nid]=")) {
          $url = eregi_replace("pollresults\[$node->nid\]=0", "pollresults[$node->nid]=1", request_uri());
        }
        else {
          $url = request_uri() . (strstr(request_uri(), "?") ? "&amp;" : "?") ."pollresults[$node->nid]=1";
        }

Dries's avatar
 
Dries committed
222
        $links[] = "<a href=\"". htmlentities($url) ."\">". t("view results") . "</a>";
Steven Wittens's avatar
Steven Wittens committed
223 224 225
      }
    }
  }
Dries's avatar
 
Dries committed
226 227

  return $links;
228
}
Dries's avatar
 
Dries committed
229

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

Dries's avatar
 
Dries committed
234
  $result = db_query("SELECT chtext, chvotes, chorder FROM {poll_choices} WHERE nid=%d ORDER BY chorder", $node->nid);
235 236 237 238
  while ($choice = db_fetch_object($result)) {
    $poll->choice[$choice->chorder]  = $choice->chtext;
    $poll->chvotes[$choice->chorder] = $choice->chvotes;
  }
Steven Wittens's avatar
Steven Wittens committed
239 240 241

  // Reset allowvotes flag, will be filled in later on when needed.
  $poll->allowvotes = -1;
242 243
  return $poll;
}
Dries's avatar
 
Dries committed
244

245 246 247 248 249
function poll_node($field) {
  $info["name"] = t("poll");
  $info["description"] = t("A poll is a multiple-choice question which visitors can vote on.");
  return $info[$field];
}
250

Steven Wittens's avatar
Steven Wittens committed
251
function poll_page() {
Dries's avatar
 
Dries committed
252
  $result = db_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");
Kjartan's avatar
Kjartan committed
253
  $output = "<ul>";
Steven Wittens's avatar
Steven Wittens committed
254
  while ($node = db_fetch_object($result)) {
Dries's avatar
 
Dries committed
255
    $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
256
  }
Kjartan's avatar
Kjartan committed
257
  $output .= "</ul>";
Dries's avatar
 
Dries committed
258
  print theme("page", $output);
Steven Wittens's avatar
Steven Wittens committed
259 260
}

261
function poll_perm() {
Dries's avatar
 
Dries committed
262
  return array("create polls", "vote on polls");
263
}
Dries's avatar
 
Dries committed
264

265 266
function poll_teaser($node) {
  // Create a simple teaser that lists all the choices
Dries's avatar
Dries committed
267 268 269 270 271
  if (is_array($node->choice)) {
    foreach ($node->choice as $k => $v) {
      if ($v != "") {
        $teaser .= "* $v\n";
      }
272 273 274 275
    }
  }
  return $teaser;
}
276

Steven Wittens's avatar
Steven Wittens committed
277 278
function poll_view_voting(&$node, $main, $block, $links) {
  // Display the vote form
Dries's avatar
 
Dries committed
279

Dries's avatar
 
Dries committed
280

Steven Wittens's avatar
Steven Wittens committed
281
  $url = request_uri();
Dries's avatar
 
Dries committed
282
  $output .= "<div class=\"poll\"><form action=\"". htmlentities($url) ."\" method=\"post\">";
Dries's avatar
 
Dries committed
283

Dries's avatar
 
Dries committed
284 285
  $output .= "<div class=\"vote-form\">";
  $output .= "<div class=\"choices\">";
Dries's avatar
 
Dries committed
286 287 288
  if ($node->choice) {
    foreach ($node->choice as $key => $value) {
      if ($value != "") {
Dries's avatar
 
Dries committed
289
        $output .= "<div><input type=\"radio\" name=\"pollvote[$node->nid]\" value=\"$key\" />". drupal_specialchars($value) ."</div>";
Dries's avatar
 
Dries committed
290
      }
291
    }
Dries's avatar
 
Dries committed
292
  }
Dries's avatar
 
Dries committed
293
  $output .= "</div>". form_submit(t("Vote"), "vote") ."</div>";
Dries's avatar
 
Dries committed
294 295
  $output .= $block ? "<div class=\"links\">". theme("links", $links) ."</div>" : "";
  $output .= "</form></div>";
Steven Wittens's avatar
Steven Wittens committed
296 297 298 299 300 301

  return $output;
}

function poll_view_results(&$node, $main, $block, $links) {
  // Display the results
Dries's avatar
 
Dries committed
302

303

Steven Wittens's avatar
Steven Wittens committed
304
  // Count the votes and find the maximum
Dries's avatar
 
Dries committed
305 306 307 308 309 310
  if ($node->choice) {
    foreach ($node->choice as $key => $value) {
      $votestotal += $node->chvotes[$key];
      $votesmax = max($votesmax, $node->chvotes[$key]);
    }
    $votesmax = max($votesmax, 1);
Steven Wittens's avatar
Steven Wittens committed
311 312
  }

Dries's avatar
 
Dries committed
313 314
  // Output the divs for the text, bars and percentages
  $output .= "<div class=\"poll\">";
Dries's avatar
 
Dries committed
315 316 317
  if ($node->choice) {
    foreach ($node->choice as $key => $value) {
      if ($value != "") {
Dries's avatar
 
Dries committed
318
        $width = round($node->chvotes[$key] * 100 / max($votestotal, 1));
Dries's avatar
 
Dries committed
319
        $percentage = round($node->chvotes[$key] * 100 / max($votestotal, 1));
Dries's avatar
 
Dries committed
320
        $output .= "<div class=\"text\">". drupal_specialchars($value) ."</div>";
Dries's avatar
 
Dries committed
321 322 323 324 325
        $output .= "<div class=\"bar\">";
        $output .= "<div style=\"width: ". $width ."%;\" class=\"foreground\"></div>";
        $output .= "<div style=\"width: ". (100 - $width) ."%;\" class=\"background\"></div>";
        $output .= "</div>";
        $output .= "<div class=\"percent\"> $percentage%". (!$block ? " (". format_plural($node->chvotes[$key], "1 vote", "%count votes") .")" : "") ."</div>";
326 327
      }
    }
328
  }
Dries's avatar
 
Dries committed
329
  $output .= "<div class=\"total\">". t("Total votes") .": $votestotal</div>";
Dries's avatar
 
Dries committed
330

331
  $output .= ($block ? "<div class=\"links\">". theme("links", $links) ."</div>" : "") ."</div>";
Steven Wittens's avatar
Steven Wittens committed
332 333 334 335 336

  return $output;
}

function poll_view_processvote(&$node) {
Dries's avatar
 
Dries committed
337
  $pollvote = $_POST["pollvote"];
Steven Wittens's avatar
Steven Wittens committed
338 339 340 341

  if (isset($pollvote[$node->nid]) && ($node->allowvotes == 1)) {
    if (!empty($node->choice[$pollvote[$node->nid]])) {
      $node->voters = $node->voters ? ($node->voters ." ". $node->polluserid) : $node->polluserid;
Dries's avatar
 
Dries committed
342 343
      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, $pollvote[$node->nid]);
Steven Wittens's avatar
Steven Wittens committed
344 345
      $node->allowvotes = 0;
      $node->chvotes[$pollvote[$node->nid]]++;
Steven Wittens's avatar
Steven Wittens committed
346
    }
Steven Wittens's avatar
Steven Wittens committed
347 348 349
  }
}

350
function poll_view(&$node, $main = 0, $page = 0, $block = 0) {
Dries's avatar
 
Dries committed
351
  global $user;
Steven Wittens's avatar
Steven Wittens committed
352 353 354 355 356 357 358

  /*
  ** When several polls are displayed on the same page (e.g. on the front page and in the side bar)
  ** we distinguish between them using the nid as index into associative arrays:
  ** $pollvote[nid]    - A user's vote
  ** $pollresults[nid] - When a user hasn't voted, he can choose to see the voting form or the results
  */
Dries's avatar
 
Dries committed
359
  $pollresults = $_GET["pollresults"];
Steven Wittens's avatar
Steven Wittens committed
360 361 362 363 364 365 366 367 368 369 370

  // Make sure we have determined the 'allowvotes' flag
  poll_allowvotes($node);

  // Because the voting form is embedded in the node-display, we process the data here
  poll_view_processvote($node);

  // Add extra link pointing to the list of polls (side-block only)
  if ($block) {
    $node->body = $node->teaser = "";

Steven Wittens's avatar
Steven Wittens committed
371
    $links = link_node($node, $main);
Dries's avatar
 
Dries committed
372
    $links[] = l(t("older polls"), "poll", array("title" => t("View the list of polls on this site.")));
373
  }
Steven Wittens's avatar
Steven Wittens committed
374 375 376 377 378 379 380 381 382

  if (($node->allowvotes == 1) && !$pollresults[$node->nid]) {
    $output = poll_view_voting($node, $main, $block, $links);
  }
  else {
    $output = poll_view_results($node, $main, $block, $links);
  }

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

384
  // We also use poll_view() for the side-block
Steven Wittens's avatar
Steven Wittens committed
385
  if (!$block) {
Dries's avatar
 
Dries committed
386
    return theme("node", $node, $main);
387 388 389
  }
}

390
function poll_update($node) {
Dries's avatar
 
Dries committed
391
  db_query("UPDATE {poll} SET runtime = %d, active = %d WHERE nid = %d", $node->runtime, $node->active, $node->nid);
392

Dries's avatar
 
Dries committed
393
  db_query("DELETE FROM {poll_choices} WHERE nid = %d", $node->nid);
Dries's avatar
 
Dries committed
394
  for ($i = 0; $i < count($node->choice); $i++) {
Dries's avatar
 
Dries committed
395
    $choice->chtext = $node->choice[$i];
396
    $choice->chvotes = (int)$node->chvotes[$i];
Dries's avatar
 
Dries committed
397
    $choice->chorder = $i;
398

399
    if ($choice->chtext != "") {
Dries's avatar
 
Dries committed
400
      db_query("INSERT INTO {poll_choices} (nid, chtext, chvotes, chorder) VALUES (%d, '%s', %d, %d)", $node->nid, $choice->chtext, $choice->chvotes, $choice->chorder);
401
    }
402 403
  }
}
404

405
?>