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

Steven Wittens's avatar
Steven Wittens committed
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
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.
  */
  global $REMOTE_ADDR, $user;

  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 {
      $id = $REMOTE_ADDR;
    }
    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 46 47 48 49 50 51 52 53 54 55 56 57
function poll_block($op = "list", $delta = 0) {
  if ($op == "list") {
    $blocks[0]["info"] = t("Most recent poll");
    return $blocks;
  }
  else {
    $timestamp = db_result(db_query("SELECT MAX(created) FROM node WHERE type = 'poll' AND status = '1' AND moderate = '0'"));
    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
        poll_view($poll, 1, 1);
      }
58
    }
Dries's avatar
 
Dries committed
59 60 61
    $block["subject"] = t("Latest poll: %t", array("%t" => $poll->title));
    $block["content"] = $poll->body;
    return $block;
62 63 64
  }
}

65 66
function poll_cron() {
  // Close polls that have exceeded their allowed runtime
Kjartan's avatar
Kjartan committed
67
  $result = db_query("SELECT p.nid FROM poll p LEFT 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 79
function poll_form(&$node, &$help, &$error) {
  $admin = user_access("administer nodes");
80

81
  $_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));
Steven Wittens's avatar
Steven Wittens committed
82
  $_active = array(0 => t("Closed"), 1 => t("Active"));
Dries's avatar
 
Dries committed
83

Steven Wittens's avatar
Steven Wittens committed
84
  $node->choices = $node->choices ? $node->choices : max(2, count($node->choice) ? count($node->choice) : 5);
85

86 87 88 89 90 91
  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
92

93
      if ($node->chvotes[$i] < 0) {
Dries's avatar
 
Dries committed
94
        $error["chvotes][$i"] = theme_invoke("theme_error", t("Negative values are not allowed."));
95
      }
96
    }
Dries's avatar
 
Dries committed
97

98
    if ($actualchoices < 2) {
Dries's avatar
 
Dries committed
99
      $error["choice][0"] = theme_invoke("theme_error", t("You must fill in at least two choices."));
100 101 102 103 104
    }
  }
  else {
    $help = variable_get("poll_help", "");
  }
Dries's avatar
 
Dries committed
105

106 107 108 109
  if (function_exists("taxonomy_node_form")) {
    $output = implode("", taxonomy_node_form("poll", $node));
  }

Dries's avatar
 
Dries committed
110
  for ($c = 2; $c <= 30; $c++) {
111 112
    $opts[$c] = $c;
  }
Dries's avatar
 
Dries committed
113
  $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
114
  $output .= form_submit(t("Preview")) ."<br /><br /><br />";
Dries's avatar
 
Dries committed
115

116
  for ($a = 0; $a < $node->choices; $a++) {
Kjartan's avatar
Kjartan committed
117
    $output .= form_textfield(t("Choice") ." ". ($a + 1), "choice][$a", $node->choice[$a], 50, 127, $error["choice][$a"]);
118
    if ($admin) {
Dries's avatar
 
Dries committed
119
      $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"]);
120 121
    }
  }
Dries's avatar
 
Dries committed
122

123 124 125
  if ($admin) {
    $output .= form_select(t("Poll status"), "active", isset($node->active) ? $node->active : 1, $_active);
  }
126

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

129 130
  return $output;
}
Dries's avatar
 
Dries committed
131

132
function poll_help() {
Dries's avatar
 
Dries committed
133 134
 ?>
  <p>Drupal's poll module allows users with at least content posting privileges to submit multiple-choice questions that others can vote on.  Any user with sufficient privileges can vote.  Please note that this is fully controlled by Drupal's access control features.  For example, users might be required to login before voting (or even seeing the page where the voting occurs) or it could be open to the world.</p>
135 136
 <?php
}
137

138 139 140
function poll_insert($node) {
  if (!user_access("administer nodes")) {
    // Make sure all votes are 0 initially
Kjartan's avatar
Kjartan committed
141
    for ($i = 0; $i < count($node->chvotes); $i++) $node->chvotes[$i] = 0;
142 143
    $node->active = 1;
  }
144

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

147
  for ($i = 0; $i < $node->choices; $i++) {
Dries's avatar
 
Dries committed
148
    if ($node->choice[$i] != "") {
Dries's avatar
 
Dries committed
149
      db_query("INSERT INTO poll_choices (nid, chtext, chvotes, chorder) VALUES ('%d', '%s', '%d', '%d')", $node->nid, filter($node->choice[$i]), $node->chvotes[$i], $i);
150 151
    }
  }
152 153
}

Steven Wittens's avatar
Steven Wittens committed
154
function poll_link($type, $node = 0, $main) {
Dries's avatar
 
Dries committed
155
  if ($type == "menu.create" && user_access("create polls")) {
Dries's avatar
 
Dries committed
156
    $links[] = l(t("create poll"), "node/add/poll", array("title" => t("Add a new poll.")));
157
  }
Steven Wittens's avatar
Steven Wittens committed
158
  else if ($type == "page" && user_access("access content")) {
Dries's avatar
 
Dries committed
159
    $links[] = l(t("polls"), "poll", array("title" => t("View the list of polls on this site.")));
Steven Wittens's avatar
Steven Wittens committed
160
  }
Steven Wittens's avatar
Steven Wittens committed
161 162 163 164 165 166 167 168
  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);
169

Steven Wittens's avatar
Steven Wittens committed
170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192
    if ($node->allowvotes == 1) {
      global $pollresults;

      // 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());

        $links[] = "<a href=\"$url\">". t("voting form") . "</a>";
      }
      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";
        }

        $links[] = "<a href=\"$url\">". t("view results") . "</a>";
      }
    }
  }
193 194
  return $links ? $links : array();
}
Dries's avatar
 
Dries committed
195

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

Dries's avatar
 
Dries committed
200
  $result = db_query("SELECT chtext, chvotes, chorder FROM poll_choices WHERE nid='%d' ORDER BY chorder", $node->nid);
201 202 203 204
  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
205 206 207

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

211 212 213 214 215
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];
}
216

Steven Wittens's avatar
Steven Wittens committed
217 218
function poll_page() {
  global $theme;
Steven Wittens's avatar
Steven Wittens committed
219

Steven Wittens's avatar
Steven Wittens committed
220
  $theme->header();
Dries's avatar
 
Dries committed
221
  $result = db_query("SELECT n.nid, n.title, p.active, SUM(c.chvotes) AS votes FROM node n LEFT JOIN poll p ON n.nid=p.nid LEFT 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
222
  $output = "<ul>";
Steven Wittens's avatar
Steven Wittens committed
223
  while ($node = db_fetch_object($result)) {
Dries's avatar
 
Dries committed
224
    $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
225
  }
Kjartan's avatar
Kjartan committed
226
  $output .= "</ul>";
Steven Wittens's avatar
Steven Wittens committed
227 228 229 230
  $theme->box(t("Polls"), $output);
  $theme->footer();
}

231
function poll_perm() {
Dries's avatar
 
Dries committed
232
  return array("create polls", "vote on polls");
233
}
Dries's avatar
 
Dries committed
234

235 236 237
function poll_save($op, $node) {
  if ($op == "approve") {
    return array("status" => 1, "promote" => 1);
238
  }
Dries's avatar
 
Dries committed
239

240 241
  if ($op == "create") {
    if (user_access("administer nodes")) {
Dries's avatar
 
Dries committed
242
      return array("runtime", "active", "choice", "choices", "chvotes", "body" => "", "moderate" => 0, "status" => 1, "teaser" => poll_teaser($node));
243 244
    }
    else {
Dries's avatar
 
Dries committed
245
      return array("runtime", "active", "choice", "choices", "chvotes", "body" => "", "moderate" => 1, "status" => 0, "teaser" => poll_teaser($node));
246
    }
247
  }
Dries's avatar
 
Dries committed
248

249 250
  if ($op == "decline") {
    return array("status" => 0, "promote" => 0);
251 252
  }

253 254 255
  if ($op == "update") {
    return array("runtime", "active", "choice", "choices", "chvotes");
  }
256 257
}

Steven Wittens's avatar
Steven Wittens committed
258
function poll_system($field){
Dries's avatar
 
Dries committed
259
  $system["description"] = t("Enables your site to capture votes on different topics in the form of multiple choice questions.");
Steven Wittens's avatar
Steven Wittens committed
260 261 262
  return $system[$field];
}

263 264 265 266 267 268 269 270 271
function poll_teaser($node) {
  // Create a simple teaser that lists all the choices
  foreach ($node->choice as $k => $v) {
    if ($v != "") {
      $teaser .= "* $v\n";
    }
  }
  return $teaser;
}
272

Steven Wittens's avatar
Steven Wittens committed
273 274 275
function poll_view_voting(&$node, $main, $block, $links) {
  // Display the vote form
  global $theme;
Dries's avatar
 
Dries committed
276

Steven Wittens's avatar
Steven Wittens committed
277 278 279
  $url = request_uri();
  $output .= "<form action=\"$url\" method=\"post\">";
  $output .= "<table border=\"0\" align=\"center\"><tr><td>";
Dries's avatar
 
Dries committed
280

Dries's avatar
 
Dries committed
281 282 283 284 285
  if ($node->choice) {
    foreach ($node->choice as $key => $value) {
      if ($value != "") {
        $output .= "<input type=\"radio\" name=\"pollvote[$node->nid]\" value=\"$key\" /> $value<br />";
      }
286
    }
Dries's avatar
 
Dries committed
287
  }
288

Steven Wittens's avatar
Steven Wittens committed
289 290 291 292 293
  if ($block) {
    $output .= "</td></tr><tr><td><div align=\"center\">". form_submit(t("Vote")) ."</div></td></tr></table>";
  }
  else {
    $output .= "</td><td valign=\"middle\"><div align=\"right\">&nbsp;&nbsp;&nbsp;". form_submit(t("Vote")) ."</div></td></tr></table>";
294
  }
Dries's avatar
 
Dries committed
295

Steven Wittens's avatar
Steven Wittens committed
296 297 298 299 300 301 302 303 304
  $output .= $block ? "<div align=\"center\">". $theme->links($links) ."</div>" : "";
  $output .= "</form>";

  return $output;
}

function poll_view_results(&$node, $main, $block, $links) {
  // Display the results
  global $theme;
305

Steven Wittens's avatar
Steven Wittens committed
306
  // Count the votes and find the maximum
Dries's avatar
 
Dries committed
307 308 309 310 311 312
  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
313 314 315 316 317 318 319 320 321 322 323 324
  }

  /*
  ** Define CSS classes for the bars
  ** (note: style is not allowed outside <head>, but the alternative is very
  ** ugly and it seems to work in all browsers)
  */
  $output .= "<style type=\"text/css\">";
  $output .= "td.pollfg { background-color: ". $theme->foreground ."; font-size: 5pt; }";
  $output .= "td.pollbg { background-color: ". $theme->background ."; font-size: 5pt; }";
  $output .= "</style>";

Dries's avatar
 
Dries committed
325 326 327 328 329
  if ($node->choice) {
    foreach ($node->choice as $key => $value) {
      if ($value != "") {
        $width = round($node->chvotes[$key] * 100 / $votesmax);
        $percentage = round($node->chvotes[$key] * 100 / max($votestotal, 1));
Steven Wittens's avatar
Steven Wittens committed
330

Dries's avatar
 
Dries committed
331
        $output .= "<table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" width=\"95%\" align=\"center\"><tr><td>$value</td><td><div align=\"right\"> $percentage%". (!$block ? " (". format_plural($node->chvotes[$key], "1 vote", "%count votes") .")" : "") ."</div></td></tr></table>";
Dries's avatar
 
Dries committed
332 333 334 335 336 337 338 339 340
        if ($width == 0) {
          $output .= "<table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" width=\"95%\" align=\"center\"><tr><td class=\"pollbg\" width=\"100%\">&nbsp;</td></tr></table>";
        }
        else if ($width == 100) {
          $output .= "<table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" width=\"95%\" align=\"center\"><tr><td class=\"pollfg\" width=\"100%\">&nbsp;</td></tr></table>";
        }
        else {
          $output .= "<table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" width=\"95%\" align=\"center\"><tr><td class=\"pollfg\" width=\"". $width ."%\">&nbsp;</td><td class=\"pollbg\" width=\"". (100 - $width) ."%\">&nbsp;</td></tr></table>";
        }
341 342
      }
    }
343
  }
Steven Wittens's avatar
Steven Wittens committed
344
  $output .= "<br /><div align=\"center\">". t("Total votes") .": $votestotal";
Dries's avatar
 
Dries committed
345

Steven Wittens's avatar
Steven Wittens committed
346 347 348 349 350 351 352 353 354 355 356
  $output .= ($block ? "<br />". $theme->links($links) : "") ."</div>";

  return $output;
}

function poll_view_processvote(&$node) {
  global $pollvote;

  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
357 358
      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
359 360
      $node->allowvotes = 0;
      $node->chvotes[$pollvote[$node->nid]]++;
Steven Wittens's avatar
Steven Wittens committed
361
    }
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 383 384 385
  }
}

function poll_view(&$node, $main = 0, $block = 0) {
  global $theme, $user;

  /*
  ** 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
  */
  global $pollresults;

  // 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
386
    $links = link_node($node, $main);
Dries's avatar
 
Dries committed
387
    $links[] = l(t("older polls"), "poll", array("title" => t("View the list of polls on this site.")));
388
  }
Steven Wittens's avatar
Steven Wittens committed
389 390 391 392 393 394 395 396 397

  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;
398

399
  // We also use poll_view() for the side-block
Steven Wittens's avatar
Steven Wittens committed
400
  if (!$block) {
401
    $theme->node($node, $main);
402 403 404
  }
}

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

Dries's avatar
 
Dries committed
408
  db_query("DELETE FROM poll_choices WHERE nid = '%d'", $node->nid);
409 410 411
  for ($i = 0; $i < $node->choices; $i++) {
    $choice->chtext = filter($node->choice[$i]);
    $choice->chvotes = (int)$node->chvotes[$i];
Dries's avatar
 
Dries committed
412
    $choice->chorder = $i;
413

414
    if ($choice->chtext != "") {
Dries's avatar
 
Dries committed
415
      db_query("INSERT INTO poll_choices (nid, chtext, chvotes, chorder) VALUES ('%d', '%s', '%d', '%d')", $node->nid, $choice->chtext, $choice->chvotes, $choice->chorder);
416
    }
417 418
  }
}
419

Dries's avatar
 
Dries committed
420
?>