poll.module 14 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) {
94
        $error["chvotes][$i"] = theme("theme_error", t("Negative values are not allowed."));
95
      }
96
    }
Dries's avatar
 
Dries committed
97

98
    if ($actualchoices < 2) {
99
      $error["choice][0"] = theme("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
function poll_page() {
Steven Wittens's avatar
Steven Wittens committed
218

Dries's avatar
 
Dries committed
219 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>";
Dries's avatar
 
Dries committed
227 228
  theme("box", t("Polls"), $output);
  theme("footer");
Steven Wittens's avatar
Steven Wittens committed
229 230
}

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

Steven Wittens's avatar
Steven Wittens committed
235
function poll_system($field){
Dries's avatar
 
Dries committed
236
  $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
237 238 239
  return $system[$field];
}

240 241 242 243 244 245 246 247 248
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;
}
249

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

Dries's avatar
 
Dries committed
253

Steven Wittens's avatar
Steven Wittens committed
254 255 256
  $url = request_uri();
  $output .= "<form action=\"$url\" method=\"post\">";
  $output .= "<table border=\"0\" align=\"center\"><tr><td>";
Dries's avatar
 
Dries committed
257

Dries's avatar
 
Dries committed
258 259 260 261 262
  if ($node->choice) {
    foreach ($node->choice as $key => $value) {
      if ($value != "") {
        $output .= "<input type=\"radio\" name=\"pollvote[$node->nid]\" value=\"$key\" /> $value<br />";
      }
263
    }
Dries's avatar
 
Dries committed
264
  }
265

Steven Wittens's avatar
Steven Wittens committed
266 267 268 269 270
  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>";
271
  }
Dries's avatar
 
Dries committed
272

Dries's avatar
 
Dries committed
273
  $output .= $block ? "<div align=\"center\">". theme("links", $links) ."</div>" : "";
Steven Wittens's avatar
Steven Wittens committed
274 275 276 277 278 279 280
  $output .= "</form>";

  return $output;
}

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

282

Steven Wittens's avatar
Steven Wittens committed
283
  // Count the votes and find the maximum
Dries's avatar
 
Dries committed
284 285 286 287 288 289
  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
290 291 292 293 294 295 296 297
  }

  /*
  ** 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)
  */

Dries's avatar
 
Dries committed
298 299 300 301 302
  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
303

Dries's avatar
 
Dries committed
304
        $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
305 306 307 308 309 310 311 312 313
        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>";
        }
314 315
      }
    }
316
  }
Steven Wittens's avatar
Steven Wittens committed
317
  $output .= "<br /><div align=\"center\">". t("Total votes") .": $votestotal";
Dries's avatar
 
Dries committed
318

Dries's avatar
 
Dries committed
319
  $output .= ($block ? "<br />". theme("links", $links) : "") ."</div>";
Steven Wittens's avatar
Steven Wittens committed
320 321 322 323 324 325 326 327 328 329

  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
330 331
      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
332 333
      $node->allowvotes = 0;
      $node->chvotes[$pollvote[$node->nid]]++;
Steven Wittens's avatar
Steven Wittens committed
334
    }
Steven Wittens's avatar
Steven Wittens committed
335 336 337 338
  }
}

function poll_view(&$node, $main = 0, $block = 0) {
Dries's avatar
 
Dries committed
339
  global $user;
Steven Wittens's avatar
Steven Wittens committed
340 341 342 343 344 345 346 347 348 349 350 351 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
  */
  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
359
    $links = link_node($node, $main);
Dries's avatar
 
Dries committed
360
    $links[] = l(t("older polls"), "poll", array("title" => t("View the list of polls on this site.")));
361
  }
Steven Wittens's avatar
Steven Wittens committed
362 363 364 365 366 367 368 369 370

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

372
  // We also use poll_view() for the side-block
Steven Wittens's avatar
Steven Wittens committed
373
  if (!$block) {
Dries's avatar
 
Dries committed
374
    theme("node", $node, $main);
375 376 377
  }
}

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

Dries's avatar
 
Dries committed
381
  db_query("DELETE FROM poll_choices WHERE nid = '%d'", $node->nid);
382 383 384
  for ($i = 0; $i < $node->choices; $i++) {
    $choice->chtext = filter($node->choice[$i]);
    $choice->chvotes = (int)$node->chvotes[$i];
Dries's avatar
 
Dries committed
385
    $choice->chorder = $i;
386

387
    if ($choice->chtext != "") {
Dries's avatar
 
Dries committed
388
      db_query("INSERT INTO poll_choices (nid, chtext, chvotes, chorder) VALUES ('%d', '%s', '%d', '%d')", $node->nid, $choice->chtext, $choice->chvotes, $choice->chorder);
389
    }
390 391
  }
}
392

393 394 395 396
function poll_validate(&$node) {
  $node ->teaser = poll_teaser($node);
}

397
?>