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_form(&$node, &$help, &$error) {
  $admin = user_access("administer nodes");
82

83
  $_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
84
  $_active = array(0 => t("Closed"), 1 => t("Active"));
Dries's avatar
 
Dries committed
85

Steven Wittens's avatar
Steven Wittens committed
86
  $node->choices = $node->choices ? $node->choices : max(2, count($node->choice) ? count($node->choice) : 5);
87

88 89 90 91 92 93
  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
94

95
      if ($node->chvotes[$i] < 0) {
96
        $error["chvotes][$i"] = theme("theme_error", t("Negative values are not allowed."));
97
      }
98
    }
Dries's avatar
 
Dries committed
99

100
    if ($actualchoices < 2) {
101
      $error["choice][0"] = theme("theme_error", t("You must fill in at least two choices."));
102 103 104 105 106
    }
  }
  else {
    $help = variable_get("poll_help", "");
  }
Dries's avatar
 
Dries committed
107

108 109 110 111
  if (function_exists("taxonomy_node_form")) {
    $output = implode("", taxonomy_node_form("poll", $node));
  }

Dries's avatar
 
Dries committed
112
  for ($c = 2; $c <= 30; $c++) {
113 114
    $opts[$c] = $c;
  }
Dries's avatar
 
Dries committed
115
  $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
116
  $output .= form_submit(t("Preview")) ."<br /><br /><br />";
Dries's avatar
 
Dries committed
117

118
  for ($a = 0; $a < $node->choices; $a++) {
Kjartan's avatar
Kjartan committed
119
    $output .= form_textfield(t("Choice") ." ". ($a + 1), "choice][$a", $node->choice[$a], 50, 127, $error["choice][$a"]);
120
    if ($admin) {
Dries's avatar
 
Dries committed
121
      $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"]);
122 123
    }
  }
Dries's avatar
 
Dries committed
124

125 126 127
  if ($admin) {
    $output .= form_select(t("Poll status"), "active", isset($node->active) ? $node->active : 1, $_active);
  }
128

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

131 132
  return $output;
}
Dries's avatar
 
Dries committed
133

134
function poll_help() {
Dries's avatar
 
Dries committed
135
  $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
136
  return t($output);
137
}
138

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

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

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

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

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

Steven Wittens's avatar
Steven Wittens committed
173
    if ($node->allowvotes == 1) {
Dries's avatar
 
Dries committed
174
      $pollresults = $_GET["pollresults"];
Steven Wittens's avatar
Steven Wittens committed
175 176 177 178 179 180

      // 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
181
        $links[] = "<a href=\"". htmlentities($url) ."\">". t("voting form") . "</a>";
Steven Wittens's avatar
Steven Wittens committed
182 183 184 185 186 187 188 189 190 191
      }
      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
192
        $links[] = "<a href=\"". htmlentities($url) ."\">". t("view results") . "</a>";
Steven Wittens's avatar
Steven Wittens committed
193 194 195
      }
    }
  }
Dries's avatar
 
Dries committed
196 197

  return $links;
198
}
Dries's avatar
 
Dries committed
199

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

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

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

215 216 217 218 219
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];
}
220

Steven Wittens's avatar
Steven Wittens committed
221
function poll_page() {
Steven Wittens's avatar
Steven Wittens committed
222

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

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

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

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

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

Dries's avatar
 
Dries committed
258

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

Dries's avatar
 
Dries committed
262 263
  $output .= "<div class=\"vote-form\">";
  $output .= "<div class=\"choices\">";
Dries's avatar
 
Dries committed
264 265 266
  if ($node->choice) {
    foreach ($node->choice as $key => $value) {
      if ($value != "") {
Dries's avatar
 
Dries committed
267
        $output .= "<div><input type=\"radio\" name=\"pollvote[$node->nid]\" value=\"$key\" />". filter($value) ."</div>";
Dries's avatar
 
Dries committed
268
      }
269
    }
Dries's avatar
 
Dries committed
270
  }
Dries's avatar
 
Dries committed
271
  $output .= "</div>". form_submit(t("Vote"), "vote") ."</div>";
Dries's avatar
 
Dries committed
272 273
  $output .= $block ? "<div class=\"links\">". theme("links", $links) ."</div>" : "";
  $output .= "</form></div>";
Steven Wittens's avatar
Steven Wittens committed
274 275 276 277 278 279

  return $output;
}

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

281

Steven Wittens's avatar
Steven Wittens committed
282
  // Count the votes and find the maximum
Dries's avatar
 
Dries committed
283 284 285 286 287 288
  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
289 290
  }

Dries's avatar
 
Dries committed
291 292
  // Output the divs for the text, bars and percentages
  $output .= "<div class=\"poll\">";
Dries's avatar
 
Dries committed
293 294 295
  if ($node->choice) {
    foreach ($node->choice as $key => $value) {
      if ($value != "") {
Dries's avatar
 
Dries committed
296
        $width = round($node->chvotes[$key] * 100 / max($votestotal, 1));
Dries's avatar
 
Dries committed
297
        $percentage = round($node->chvotes[$key] * 100 / max($votestotal, 1));
Dries's avatar
 
Dries committed
298 299 300 301 302 303
        $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>";
304 305
      }
    }
306
  }
Dries's avatar
 
Dries committed
307
  $output .= "<div class=\"total\">". t("Total votes") .": $votestotal</div>";
Dries's avatar
 
Dries committed
308

Dries's avatar
 
Dries committed
309
  $output .= ($block ? "<div class=\"links\" />". theme("links", $links) ."</div>" : "") ."</div>";
Steven Wittens's avatar
Steven Wittens committed
310 311 312 313 314

  return $output;
}

function poll_view_processvote(&$node) {
Dries's avatar
 
Dries committed
315
  $pollvote = $_POST["pollvote"];
Steven Wittens's avatar
Steven Wittens committed
316 317 318 319

  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
320 321
      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
322 323
      $node->allowvotes = 0;
      $node->chvotes[$pollvote[$node->nid]]++;
Steven Wittens's avatar
Steven Wittens committed
324
    }
Steven Wittens's avatar
Steven Wittens committed
325 326 327
  }
}

Dries's avatar
 
Dries committed
328
function poll_view(&$node, $main = 0, $block = 0) {
Dries's avatar
 
Dries committed
329
  global $user;
Steven Wittens's avatar
Steven Wittens committed
330 331 332 333 334 335 336

  /*
  ** 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
337
  $pollresults = $_GET["pollresults"];
Steven Wittens's avatar
Steven Wittens committed
338 339 340 341 342 343 344 345 346 347 348

  // 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
349
    $links = link_node($node, $main);
Dries's avatar
 
Dries committed
350
    $links[] = l(t("older polls"), "poll", array("title" => t("View the list of polls on this site.")));
351
  }
Steven Wittens's avatar
Steven Wittens committed
352 353 354 355 356 357 358 359 360

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

362
  // We also use poll_view() for the side-block
Steven Wittens's avatar
Steven Wittens committed
363
  if (!$block) {
Dries's avatar
 
Dries committed
364
    theme("node", $node, $main);
365 366 367
  }
}

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

Dries's avatar
 
Dries committed
371
  db_query("DELETE FROM {poll_choices} WHERE nid = %d", $node->nid);
Dries's avatar
 
Dries committed
372
  for ($i = 0; $i < count($node->choice); $i++) {
Dries's avatar
 
Dries committed
373
    $choice->chtext = $node->choice[$i];
374
    $choice->chvotes = (int)$node->chvotes[$i];
Dries's avatar
 
Dries committed
375
    $choice->chorder = $i;
376

377
    if ($choice->chtext != "") {
Dries's avatar
 
Dries committed
378
      db_query("INSERT INTO {poll_choices} (nid, chtext, chvotes, chorder) VALUES (%d, '%s', %d, %d)", $node->nid, $choice->chtext, $choice->chvotes, $choice->chorder);
379
    }
380 381
  }
}
382

383
function poll_validate(&$node) {
Dries's avatar
 
Dries committed
384
  $node->teaser = poll_teaser($node);
385 386
}

387
?>