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) {
Dries's avatar
 
Dries committed
89
        $error["chvotes][$i"] = theme_invoke("theme_error", t("Negative values are not allowed."));
90
      }
91
    }
Dries's avatar
 
Dries committed
92

93
    if ($actualchoices < 2) {
Dries's avatar
 
Dries committed
94
      $error["choice][0"] = theme_invoke("theme_error", t("You must fill in at least two choices."));
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 129
 ?>
  <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>
130 131
 <?php
}
132

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

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

142
  for ($i = 0; $i < $node->choices; $i++) {
Dries's avatar
 
Dries committed
143 144
    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);
145 146
    }
  }
147 148
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Dries's avatar
 
Dries committed
415
?>