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 41
  if ($op == "create") {
    return 1;
Dries's avatar
 
Dries committed
42
  }
Steven Wittens's avatar
Steven Wittens committed
43 44
}

45
function poll_block() {
46
  $timestamp = db_result(db_query("SELECT MAX(created) FROM node WHERE type='poll' AND status='1' AND moderate='0'"));
47
  if ($timestamp) {
48
    $poll = node_load(array("type" => "poll", "created" => $timestamp, "moderate" => "0", "status" => "1"));
49 50
    if ($poll->nid) {
      // Poll_view dumps the output into $poll->body
Steven Wittens's avatar
Steven Wittens committed
51
      poll_view($poll, 1, 1);
52 53
    }
  }
Dries's avatar
 
Dries committed
54
  $blocks[0][subject] = t("Latest poll: %t", array("%t" => $poll->title));
55
  $blocks[0][content] = $poll->body;
56
  $blocks[0][info] = t("Most recent poll");
57
  return $blocks;
58 59
}

60 61
function poll_cron() {
  // Close polls that have exceeded their allowed runtime
Kjartan's avatar
Kjartan committed
62
  $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
63
  while ($poll = db_fetch_object($result)) {
Steven Wittens's avatar
Steven Wittens committed
64
    db_query("UPDATE poll SET active='0' WHERE nid='$poll->nid'");
Dries's avatar
 
Dries committed
65
  }
66 67
}

68 69 70
function poll_delete($node) {
  db_query("DELETE FROM poll WHERE nid='$node->nid'");
  db_query("DELETE FROM poll_choices WHERE nid='$node->nid'");
71 72
}

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

76
  $_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
77
  $_active = array(0 => t("Closed"), 1 => t("Active"));
Dries's avatar
 
Dries committed
78

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

81 82 83 84 85 86
  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
87

88
      if ($node->chvotes[$i] < 0) {
Kjartan's avatar
Kjartan committed
89
        $error["chvotes][$i"] = "<span style=\"color: red;\">". t("Negative values are not allowed.") ."</span>";
90
      }
91
    }
Dries's avatar
 
Dries committed
92

93
    if ($actualchoices < 2) {
Kjartan's avatar
Kjartan committed
94
      $error["choice][0"] = "<span style=\"color: red;\">". t("You must fill in at least two choices.") ."</span>";
95 96 97 98 99
    }
  }
  else {
    $help = variable_get("poll_help", "");
  }
Dries's avatar
 
Dries committed
100

101 102 103 104
  if (function_exists("taxonomy_node_form")) {
    $output = implode("", taxonomy_node_form("poll", $node));
  }

105 106 107
  for ($c = 2; $c <= 20; $c++) {
    $opts[$c] = $c;
  }
Dries's avatar
 
Dries committed
108
  $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
109
  $output .= form_submit(t("Preview")) ."<br /><br /><br />";
Dries's avatar
 
Dries committed
110

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

118 119 120
  if ($admin) {
    $output .= form_select(t("Poll status"), "active", isset($node->active) ? $node->active : 1, $_active);
  }
121

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

124 125
  return $output;
}
Dries's avatar
 
Dries committed
126

127
function poll_help() {
Dries's avatar
 
Dries committed
128
 ?><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>
129 130
 <?php
}
131

132 133 134
function poll_insert($node) {
  if (!user_access("administer nodes")) {
    // Make sure all votes are 0 initially
Kjartan's avatar
Kjartan committed
135
    for ($i = 0; $i < count($node->chvotes); $i++) $node->chvotes[$i] = 0;
136 137
    $node->active = 1;
  }
138

Steven Wittens's avatar
Steven Wittens committed
139
  db_query("INSERT INTO poll (nid, runtime, voters, active) VALUES ('". check_query($node->nid) ."', '". check_query($node->runtime) ."', '', '". check_query($node->active) ."')");
Dries's avatar
 
Dries committed
140

141 142 143 144
  for ($i = 0; $i < $node->choices; $i++) {
    $choice->chtext = filter($node->choice[$i]);
    $choice->chvotes = (int)$node->chvotes[$i];
    $choice->chorder = $i;
Dries's avatar
 
Dries committed
145

146
    if ($choice->chtext != "") {
Steven Wittens's avatar
Steven Wittens committed
147
      db_query("INSERT INTO poll_choices (nid, chtext, chvotes, chorder) VALUES ('". check_query($node->nid) ."', '". check_query($choice->chtext) ."', '". check_query($choice->chvotes) ."', '". check_query($choice->chorder) ."')");
148 149
    }
  }
150 151
}

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

Steven Wittens's avatar
Steven Wittens committed
168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190
    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>";
      }
    }
  }
191 192
  return $links ? $links : array();
}
Dries's avatar
 
Dries committed
193

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

198 199 200 201 202
  $result = db_query("SELECT chtext, chvotes, chorder FROM poll_choices WHERE nid='$node->nid' ORDER BY chorder");
  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
203 204 205

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

209 210 211 212 213
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];
}
214

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

Steven Wittens's avatar
Steven Wittens committed
218
  $theme->header();
Dries's avatar
 
Dries committed
219
  $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
220
  $output = "<ul>";
Steven Wittens's avatar
Steven Wittens committed
221 222 223
  while ($node = db_fetch_object($result)) {
    $output .= "<li>". l($node->title, array("id" => $node->nid)) ." - ". format_plural($node->votes, "vote", "votes") ." - ". ($node->active ? t("open") : t("closed")) ."</li>";
  }
Kjartan's avatar
Kjartan committed
224
  $output .= "</ul>";
Steven Wittens's avatar
Steven Wittens committed
225 226 227 228
  $theme->box(t("Polls"), $output);
  $theme->footer();
}

229 230 231
function poll_perm() {
  return array("vote on polls");
}
Dries's avatar
 
Dries committed
232

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

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

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

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

Steven Wittens's avatar
Steven Wittens committed
256
function poll_system($field){
Dries's avatar
 
Dries committed
257
  $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
258 259 260
  return $system[$field];
}

261 262 263 264 265 266 267 268 269
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;
}
270

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

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

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

Steven Wittens's avatar
Steven Wittens committed
285 286 287 288 289
  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>";
290
  }
Dries's avatar
 
Dries committed
291

Steven Wittens's avatar
Steven Wittens committed
292 293 294 295 296 297 298 299 300
  $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;
301

Steven Wittens's avatar
Steven Wittens committed
302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332
  // Count the votes and find the maximum
  foreach ($node->choice as $key => $value) {
    $votestotal += $node->chvotes[$key];
    $votesmax = max($votesmax, $node->chvotes[$key]);
  }
  $votesmax = max($votesmax, 1);

  /*
  ** 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>";

  foreach ($node->choice as $key => $value) {
    if ($value != "") {
      $width = round($node->chvotes[$key] * 100 / $votesmax);
      $percentage = round($node->chvotes[$key] * 100 / max($votestotal, 1));

      $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], "vote", "votes") .")" : "") ."</div></td></tr></table>";
      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>";
333 334
      }
    }
335
  }
Steven Wittens's avatar
Steven Wittens committed
336
  $output .= "<br /><div align=\"center\">". t("Total votes") .": $votestotal";
Dries's avatar
 
Dries committed
337

Steven Wittens's avatar
Steven Wittens committed
338 339 340 341 342 343 344 345 346 347 348 349 350 351 352
  $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;
      db_query("UPDATE poll SET voters='$node->voters' WHERE nid='$node->nid'");
      db_query("UPDATE poll_choices SET chvotes = chvotes + 1 WHERE nid='$node->nid' AND chorder='". $pollvote[$node->nid] ."'");
      $node->allowvotes = 0;
      $node->chvotes[$pollvote[$node->nid]]++;
Steven Wittens's avatar
Steven Wittens committed
353
    }
Steven Wittens's avatar
Steven Wittens committed
354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377
  }
}

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
378 379
    $links = link_node($node, $main);
    $links[] = lm(t("older polls"), array("mod" => "poll"), "", array("title" => t("View the list of polls on this site.")));
380
  }
Steven Wittens's avatar
Steven Wittens committed
381 382 383 384 385 386 387 388 389

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

391
  // We also use poll_view() for the side-block
Steven Wittens's avatar
Steven Wittens committed
392
  if (!$block) {
393
    $theme->node($node, $main);
394 395 396
  }
}

397 398 399 400 401 402 403
function poll_update($node) {
  db_query("UPDATE poll SET runtime='$node->runtime', active='$node->active' WHERE nid='$node->nid'");

  db_query("DELETE FROM poll_choices WHERE nid='$node->nid'");
  for ($i = 0; $i < $node->choices; $i++) {
    $choice->chtext = filter($node->choice[$i]);
    $choice->chvotes = (int)$node->chvotes[$i];
Dries's avatar
 
Dries committed
404
    $choice->chorder = $i;
405

406
    if ($choice->chtext != "") {
Steven Wittens's avatar
Steven Wittens committed
407
      db_query("INSERT INTO poll_choices (nid, chtext, chvotes, chorder) VALUES ('". check_query($node->nid) ."', '". check_query($choice->chtext) ."', '". check_query($choice->chvotes) ."', '". check_query($choice->chorder) ."')");
408
    }
409 410
  }
}
411

412
?>