poll.module 14.8 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)) {
Dries's avatar
 
Dries committed
64
    db_query("UPDATE poll SET active='0' WHERE nid = '%d'", $poll->nid);
Dries's avatar
 
Dries committed
65
  }
66 67
}

68
function poll_delete($node) {
Dries's avatar
 
Dries committed
69 70
  db_query("DELETE FROM poll WHERE nid='%d'", $node->nid);
  db_query("DELETE FROM poll_choices WHERE nid = '%d'", $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));
  }

Dries's avatar
 
Dries committed
105
  for ($c = 2; $c <= 30; $c++) {
106 107
    $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

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

141
  for ($i = 0; $i < $node->choices; $i++) {
Dries's avatar
 
Dries committed
142 143
    if ($node->choice[$i] != "") {
      db_query("INSERT INTO poll_choices (nid, chtext, chvotes, chorder) VALUES ('%d', '%s', '%d', '%d')", $node->nid, $node->choice[$i], $node->chvotes[$i], $i);
144 145
    }
  }
146 147
}

Steven Wittens's avatar
Steven Wittens committed
148
function poll_link($type, $node = 0, $main) {
149
  if ($type == "menu.create" && user_access("post content")) {
Dries's avatar
 
Dries committed
150
    $links[] = lm(t("create poll"), array("mod" => "node", "op" => "add", "type" => "poll"), "", array("title" => t("Add a new poll.")));
151
  }
Steven Wittens's avatar
Steven Wittens committed
152 153 154
  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
155 156 157 158 159 160 161 162
  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);
163

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

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

Dries's avatar
 
Dries committed
194
  $result = db_query("SELECT chtext, chvotes, chorder FROM poll_choices WHERE nid='%d' ORDER BY chorder", $node->nid);
195 196 197 198
  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
199 200 201

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

205 206 207 208 209
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];
}
210

Steven Wittens's avatar
Steven Wittens committed
211 212
function poll_page() {
  global $theme;
Steven Wittens's avatar
Steven Wittens committed
213

Steven Wittens's avatar
Steven Wittens committed
214
  $theme->header();
Dries's avatar
 
Dries committed
215
  $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
216
  $output = "<ul>";
Steven Wittens's avatar
Steven Wittens committed
217 218 219
  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
220
  $output .= "</ul>";
Steven Wittens's avatar
Steven Wittens committed
221 222 223 224
  $theme->box(t("Polls"), $output);
  $theme->footer();
}

225 226 227
function poll_perm() {
  return array("vote on polls");
}
Dries's avatar
 
Dries committed
228

229 230 231
function poll_save($op, $node) {
  if ($op == "approve") {
    return array("status" => 1, "promote" => 1);
232
  }
Dries's avatar
 
Dries committed
233

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

243 244
  if ($op == "decline") {
    return array("status" => 0, "promote" => 0);
245 246
  }

247 248 249
  if ($op == "update") {
    return array("runtime", "active", "choice", "choices", "chvotes");
  }
250 251
}

Steven Wittens's avatar
Steven Wittens committed
252
function poll_system($field){
Dries's avatar
 
Dries committed
253
  $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
254 255 256
  return $system[$field];
}

257 258 259 260 261 262 263 264 265
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;
}
266

Steven Wittens's avatar
Steven Wittens committed
267 268 269
function poll_view_voting(&$node, $main, $block, $links) {
  // Display the vote form
  global $theme;
Dries's avatar
 
Dries committed
270

Steven Wittens's avatar
Steven Wittens committed
271 272 273
  $url = request_uri();
  $output .= "<form action=\"$url\" method=\"post\">";
  $output .= "<table border=\"0\" align=\"center\"><tr><td>";
Dries's avatar
 
Dries committed
274

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

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

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

Steven Wittens's avatar
Steven Wittens committed
300
  // Count the votes and find the maximum
Dries's avatar
 
Dries committed
301 302 303 304 305 306
  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
307 308 309 310 311 312 313 314 315 316 317 318
  }

  /*
  ** 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
319 320 321 322 323
  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
324

Dries's avatar
 
Dries committed
325 326 327 328 329 330 331 332 333 334
        $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>";
        }
335 336
      }
    }
337
  }
Steven Wittens's avatar
Steven Wittens committed
338
  $output .= "<br /><div align=\"center\">". t("Total votes") .": $votestotal";
Dries's avatar
 
Dries committed
339

Steven Wittens's avatar
Steven Wittens committed
340 341 342 343 344 345 346 347 348 349 350
  $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
351 352
      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
353 354
      $node->allowvotes = 0;
      $node->chvotes[$pollvote[$node->nid]]++;
Steven Wittens's avatar
Steven Wittens committed
355
    }
Steven Wittens's avatar
Steven Wittens committed
356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379
  }
}

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

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

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

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

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

408
    if ($choice->chtext != "") {
Dries's avatar
 
Dries committed
409
      db_query("INSERT INTO poll_choices (nid, chtext, chvotes, chorder) VALUES ('%d', '%s', '%d', '%d')", $node->nid, $choice->chtext, $choice->chvotes, $choice->chorder);
410
    }
411 412
  }
}
413

Dries's avatar
 
Dries committed
414
?>