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
function poll_block($op = "list", $delta = 0) {
Dries's avatar
 
Dries committed
46 47 48 49 50 51
  if (user_access("access content")) {
    if ($op == "list") {
      $blocks[0]["info"] = t("Most recent poll");
      return $blocks;
    }
    else {
Dries's avatar
 
Dries committed
52
      $timestamp = db_result(db_query("SELECT MAX(created) FROM {node} WHERE type = 'poll' AND status = '1' AND moderate = '0'"));
Dries's avatar
 
Dries committed
53 54 55 56
      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
Dries's avatar
 
Dries committed
57
          poll_view($poll, 1, 1);
Dries's avatar
 
Dries committed
58
        }
Dries's avatar
 
Dries committed
59
      }
Dries's avatar
 
Dries committed
60 61 62
      $block["subject"] = t("Poll: %t", array("%t" => $poll->title));
      $block["content"] = $poll->body;
      return $block;
63 64 65 66
    }
  }
}

67 68
function poll_cron() {
  // Close polls that have exceeded their allowed runtime
Dries's avatar
 
Dries committed
69
  $result = db_query("SELECT p.nid FROM {poll} p INNER 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
70
  while ($poll = db_fetch_object($result)) {
Dries's avatar
 
Dries committed
71
    db_query("UPDATE {poll} SET active='0' WHERE nid = %d", $poll->nid);
Dries's avatar
 
Dries committed
72
  }
73 74
}

75
function poll_delete($node) {
Dries's avatar
 
Dries committed
76 77
  db_query("DELETE FROM {poll} WHERE nid=%d", $node->nid);
  db_query("DELETE FROM {poll_choices} WHERE nid = %d", $node->nid);
78 79 80
}


81
function poll_validate(&$node) {
82 83 84 85 86 87
  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
88

89
      if ($node->chvotes[$i] < 0) {
90
        $error["chvotes][$i"] = theme("theme_error", t("Negative values are not allowed."));
91
      }
92
    }
Dries's avatar
 
Dries committed
93

94
    if ($actualchoices < 2) {
95
      $error["choice][0"] = theme("theme_error", t("You must fill in at least two choices."));
96 97
    }
  }
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112

  $node->teaser = poll_teaser($node);

  return $error;
}

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

  $_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));
  $_active = array(0 => t("Closed"), 1 => t("Active"));

  $node->choices = $node->choices ? $node->choices : max(2, count($node->choice) ? count($node->choice) : 5);

  $help = variable_get("poll_help", "");
Dries's avatar
 
Dries committed
113

114 115 116 117
  if (function_exists("taxonomy_node_form")) {
    $output = implode("", taxonomy_node_form("poll", $node));
  }

Dries's avatar
 
Dries committed
118
  for ($c = 2; $c <= 30; $c++) {
119 120
    $opts[$c] = $c;
  }
Dries's avatar
 
Dries committed
121
  $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
122
  $output .= form_submit(t("Preview")) ."<br /><br /><br />";
Dries's avatar
 
Dries committed
123

124
  for ($a = 0; $a < $node->choices; $a++) {
Dries's avatar
 
Dries committed
125
    $output .= form_textfield(t("Choice %n", array("%n" => ($a + 1))), "choice][$a", $node->choice[$a], 50, 127, $error["choice][$a"]);
126
    if ($admin) {
Dries's avatar
 
Dries committed
127
      $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"]);
128 129
    }
  }
Dries's avatar
 
Dries committed
130

131 132 133
  if ($admin) {
    $output .= form_select(t("Poll status"), "active", isset($node->active) ? $node->active : 1, $_active);
  }
134

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

137 138
  return $output;
}
Dries's avatar
 
Dries committed
139

140
function poll_help() {
Dries's avatar
 
Dries committed
141
  $output .= "<p>Users with the correct ". l("permissions","admin/user/permission") ." can create and/or vote on polls.</p><ul><li> To create a poll a user 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 polls you need the \"administer nodes\" permission.</li></ul><p>Creating a poll is much like creating any other node. Click \"create poll\" in your user box. The title of the poll should be the question, then enter the answers and the \"base\" vote counts. You can also choose the time period over which the vote will run.</p><p>The ". l("Poll", "poll") ." item in the navigation links 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.</p>";
Dries's avatar
 
Dries committed
142
  return t($output);
143
}
144

145 146 147
function poll_insert($node) {
  if (!user_access("administer nodes")) {
    // Make sure all votes are 0 initially
Kjartan's avatar
Kjartan committed
148
    for ($i = 0; $i < count($node->chvotes); $i++) $node->chvotes[$i] = 0;
149 150
    $node->active = 1;
  }
151

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

154
  for ($i = 0; $i < $node->choices; $i++) {
Dries's avatar
 
Dries committed
155
    if ($node->choice[$i] != "") {
Dries's avatar
 
Dries committed
156
      db_query("INSERT INTO {poll_choices} (nid, chtext, chvotes, chorder) VALUES (%d, '%s', %d, %d)", $node->nid, $node->choice[$i], $node->chvotes[$i], $i);
157 158
    }
  }
159 160
}

Steven Wittens's avatar
Steven Wittens committed
161
function poll_link($type, $node = 0, $main) {
Dries's avatar
 
Dries committed
162 163
  $links = array();

Dries's avatar
 
Dries committed
164 165
  if ($type == "system") {
    if (user_access("create polls")) {
Dries's avatar
 
Dries committed
166
      menu("node/add/poll",t("poll"), "poll_page", NULL, 0);
Dries's avatar
 
Dries committed
167
    }
168
  }
Steven Wittens's avatar
Steven Wittens committed
169
  else if ($type == "page" && user_access("access content")) {
Dries's avatar
 
Dries committed
170
    $links[] = l(t("polls"), "poll", array("title" => t("View the list of polls on this site.")));
Steven Wittens's avatar
Steven Wittens committed
171
  }
Steven Wittens's avatar
Steven Wittens committed
172 173 174 175 176 177 178 179
  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);
180

Steven Wittens's avatar
Steven Wittens committed
181
    if ($node->allowvotes == 1) {
Dries's avatar
 
Dries committed
182
      $pollresults = $_GET["pollresults"];
Steven Wittens's avatar
Steven Wittens committed
183 184 185 186 187 188

      // 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());

Dries's avatar
 
Dries committed
189
        $links[] = "<a href=\"". htmlentities($url) ."\">". t("voting form") . "</a>";
Steven Wittens's avatar
Steven Wittens committed
190 191 192 193 194 195 196 197 198 199
      }
      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";
        }

Dries's avatar
 
Dries committed
200
        $links[] = "<a href=\"". htmlentities($url) ."\">". t("view results") . "</a>";
Steven Wittens's avatar
Steven Wittens committed
201 202 203
      }
    }
  }
Dries's avatar
 
Dries committed
204 205

  return $links;
206
}
Dries's avatar
 
Dries committed
207

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

Dries's avatar
 
Dries committed
212
  $result = db_query("SELECT chtext, chvotes, chorder FROM {poll_choices} WHERE nid=%d ORDER BY chorder", $node->nid);
213 214 215 216
  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
217 218 219

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

223 224 225 226 227
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];
}
228

Steven Wittens's avatar
Steven Wittens committed
229
function poll_page() {
Steven Wittens's avatar
Steven Wittens committed
230

Dries's avatar
 
Dries committed
231
  theme("header");
Dries's avatar
 
Dries committed
232
  $result = db_query("SELECT n.nid, n.title, p.active, SUM(c.chvotes) AS votes FROM {node} n INNER JOIN {poll} p ON n.nid=p.nid INNER 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
233
  $output = "<ul>";
Steven Wittens's avatar
Steven Wittens committed
234
  while ($node = db_fetch_object($result)) {
Dries's avatar
 
Dries committed
235
    $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
236
  }
Kjartan's avatar
Kjartan committed
237
  $output .= "</ul>";
Dries's avatar
 
Dries committed
238 239
  theme("box", t("Polls"), $output);
  theme("footer");
Steven Wittens's avatar
Steven Wittens committed
240 241
}

242
function poll_perm() {
Dries's avatar
 
Dries committed
243
  return array("create polls", "vote on polls");
244
}
Dries's avatar
 
Dries committed
245

Steven Wittens's avatar
Steven Wittens committed
246
function poll_system($field){
Dries's avatar
 
Dries committed
247
  $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
248 249 250
  return $system[$field];
}

251 252
function poll_teaser($node) {
  // Create a simple teaser that lists all the choices
Dries's avatar
Dries committed
253 254 255 256 257
  if (is_array($node->choice)) {
    foreach ($node->choice as $k => $v) {
      if ($v != "") {
        $teaser .= "* $v\n";
      }
258 259 260 261
    }
  }
  return $teaser;
}
262

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

Dries's avatar
 
Dries committed
266

Steven Wittens's avatar
Steven Wittens committed
267
  $url = request_uri();
Dries's avatar
 
Dries committed
268
  $output .= "<div class=\"poll\"><form action=\"". htmlentities($url) ."\" method=\"post\">";
Dries's avatar
 
Dries committed
269

Dries's avatar
 
Dries committed
270 271
  $output .= "<div class=\"vote-form\">";
  $output .= "<div class=\"choices\">";
Dries's avatar
 
Dries committed
272 273 274
  if ($node->choice) {
    foreach ($node->choice as $key => $value) {
      if ($value != "") {
Dries's avatar
 
Dries committed
275
        $output .= "<div><input type=\"radio\" name=\"pollvote[$node->nid]\" value=\"$key\" />". filter($value) ."</div>";
Dries's avatar
 
Dries committed
276
      }
277
    }
Dries's avatar
 
Dries committed
278
  }
Dries's avatar
 
Dries committed
279
  $output .= "</div>". form_submit(t("Vote"), "vote") ."</div>";
Dries's avatar
 
Dries committed
280 281
  $output .= $block ? "<div class=\"links\">". theme("links", $links) ."</div>" : "";
  $output .= "</form></div>";
Steven Wittens's avatar
Steven Wittens committed
282 283 284 285 286 287

  return $output;
}

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

289

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

Dries's avatar
 
Dries committed
299 300
  // Output the divs for the text, bars and percentages
  $output .= "<div class=\"poll\">";
Dries's avatar
 
Dries committed
301 302 303
  if ($node->choice) {
    foreach ($node->choice as $key => $value) {
      if ($value != "") {
Dries's avatar
 
Dries committed
304
        $width = round($node->chvotes[$key] * 100 / max($votestotal, 1));
Dries's avatar
 
Dries committed
305
        $percentage = round($node->chvotes[$key] * 100 / max($votestotal, 1));
Dries's avatar
 
Dries committed
306 307 308 309 310 311
        $output .= "<div class=\"text\">". filter($value) ."</div>";
        $output .= "<div class=\"bar\">";
        $output .= "<div style=\"width: ". $width ."%;\" class=\"foreground\"></div>";
        $output .= "<div style=\"width: ". (100 - $width) ."%;\" class=\"background\"></div>";
        $output .= "</div>";
        $output .= "<div class=\"percent\"> $percentage%". (!$block ? " (". format_plural($node->chvotes[$key], "1 vote", "%count votes") .")" : "") ."</div>";
312 313
      }
    }
314
  }
Dries's avatar
 
Dries committed
315
  $output .= "<div class=\"total\">". t("Total votes") .": $votestotal</div>";
Dries's avatar
 
Dries committed
316

Dries's avatar
 
Dries committed
317
  $output .= ($block ? "<div class=\"links\" />". theme("links", $links) ."</div>" : "") ."</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
  }
}

Dries's avatar
 
Dries committed
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

  /*
  ** 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
  */
Dries's avatar
 
Dries committed
345
  $pollresults = $_GET["pollresults"];
Steven Wittens's avatar
Steven Wittens committed
346 347 348 349 350 351 352 353 354 355 356

  // 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);
Dries's avatar
 
Dries committed
380
  for ($i = 0; $i < count($node->choice); $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
?>