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

Steven Wittens's avatar
Steven Wittens committed
4 5 6 7 8 9
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.
  */
Dries's avatar
 
Dries committed
10
  global $user;
Steven Wittens's avatar
Steven Wittens committed
11 12 13 14 15 16 17 18 19 20 21 22

  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 {
Dries's avatar
 
Dries committed
23
      $id = $_SERVER["REMOTE_ADDR"];
Steven Wittens's avatar
Steven Wittens committed
24 25 26 27 28 29 30 31 32
    }
    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
    $block["subject"] = t("Poll: %t", array("%t" => $poll->title));
Dries's avatar
 
Dries committed
60 61
    $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
  $output .= "<p>Drupal's poll module allows users to vote on polls create by administrators, or even other users. Access to, voting on, and creation of polls is completely controlled by the Drupal ". l("access system","admin/user/permission") ."</p><ul><li> To create a poll a users needs the \"create polls\" permission.</li><li>To vote on a poll question a user must have the \"vote on polls\" permission.</li><li>To view the results one needs the \"access content\" permission.</li><li>To administer a vote you need the \"administer nodes\" permission.</li></ul><p>Creating a vote can be done with \"create poll\". The title of the poll should be the question, then enter the answers and the \"base\" vote counts. Finally the time span the vote will run.</p><p>". l("Poll", "poll") ." on the menu bar will take you to a page where you can see all the current polls, vote on them -- if you haven't already --, and view the results. To edit the node, and its vote counts, go to node administration.</p>";
  return t($output);
135
}
136

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

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

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

Steven Wittens's avatar
Steven Wittens committed
153
function poll_link($type, $node = 0, $main) {
Dries's avatar
 
Dries committed
154 155
  $links = array();

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

Steven Wittens's avatar
Steven Wittens committed
171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193
    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>";
      }
    }
  }
Dries's avatar
 
Dries committed
194 195

  return $links;
196
}
Dries's avatar
 
Dries committed
197

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

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

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

213 214 215 216 217
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];
}
218

Steven Wittens's avatar
Steven Wittens committed
219
function poll_page() {
Steven Wittens's avatar
Steven Wittens committed
220

Dries's avatar
 
Dries committed
221 222

  theme("header");
Dries's avatar
 
Dries committed
223
  $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
224
  $output = "<ul>";
Steven Wittens's avatar
Steven Wittens committed
225
  while ($node = db_fetch_object($result)) {
Dries's avatar
 
Dries committed
226
    $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
227
  }
Kjartan's avatar
Kjartan committed
228
  $output .= "</ul>";
Dries's avatar
 
Dries committed
229 230
  theme("box", t("Polls"), $output);
  theme("footer");
Steven Wittens's avatar
Steven Wittens committed
231 232
}

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

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

242 243
function poll_teaser($node) {
  // Create a simple teaser that lists all the choices
Dries's avatar
Dries committed
244 245 246 247 248
  if (is_array($node->choice)) {
    foreach ($node->choice as $k => $v) {
      if ($v != "") {
        $teaser .= "* $v\n";
      }
249 250 251 252
    }
  }
  return $teaser;
}
253

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

Dries's avatar
 
Dries committed
257

Steven Wittens's avatar
Steven Wittens committed
258 259 260
  $url = request_uri();
  $output .= "<form action=\"$url\" method=\"post\">";
  $output .= "<table border=\"0\" align=\"center\"><tr><td>";
Dries's avatar
 
Dries committed
261

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

Steven Wittens's avatar
Steven Wittens committed
270 271 272 273 274
  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>";
275
  }
Dries's avatar
 
Dries committed
276

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

  return $output;
}

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

286

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

  /*
  ** 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
302 303 304 305 306
  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
307

Dries's avatar
 
Dries committed
308
        $output .= "<div class=\"poll-text\">". filter($value) ."</div>";
Dries's avatar
 
Dries committed
309 310
        $output .= "<div style=\"width:". $width ."%;\" class=\"poll-foreground\"></div>";
        $output .= "<div style=\"width:". (100 - $width) ."%;\" class=\"poll-background\"></div>";
Dries's avatar
Dries committed
311
        $output .= "<div align=\"right\"> $percentage%". (!$block ? " (". format_plural($node->chvotes[$key], "1 vote", "%count votes") .")" : "") ."</div>";
312 313
      }
    }
314
  }
Steven Wittens's avatar
Steven Wittens committed
315
  $output .= "<br /><div align=\"center\">". t("Total votes") .": $votestotal";
Dries's avatar
 
Dries committed
316

Dries's avatar
 
Dries committed
317
  $output .= ($block ? "<br />". theme("links", $links) : "") ."</div>";
Steven Wittens's avatar
Steven Wittens committed
318 319 320 321 322

  return $output;
}

function poll_view_processvote(&$node) {
Dries's avatar
 
Dries committed
323
  $pollvote = $_POST["pollvote"];
Steven Wittens's avatar
Steven Wittens committed
324 325 326 327

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

function poll_view(&$node, $main = 0, $block = 0) {
Dries's avatar
 
Dries committed
337
  global $user;
Steven Wittens's avatar
Steven Wittens committed
338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356

  /*
  ** 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
357
    $links = link_node($node, $main);
Dries's avatar
 
Dries committed
358
    $links[] = l(t("older polls"), "poll", array("title" => t("View the list of polls on this site.")));
359
  }
Steven Wittens's avatar
Steven Wittens committed
360 361 362 363 364 365 366 367 368

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

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

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

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

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

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

395
?>