poll.module 13.8 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
  $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
134
  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
    if ($node->allowvotes == 1) {
Dries's avatar
 
Dries committed
172
      $pollresults = $_GET["pollresults"];
Steven Wittens's avatar
Steven Wittens committed
173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193

      // 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
  $url = request_uri();
Dries's avatar
 
Dries committed
259
  $output .= "<div class=\"poll\"><form action=\"$url\" method=\"post\">";
Dries's avatar
 
Dries committed
260

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

  return $output;
}

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

280

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

Dries's avatar
 
Dries committed
290 291
  // Output the divs for the text, bars and percentages
  $output .= "<div class=\"poll\">";
Dries's avatar
 
Dries committed
292 293 294 295 296
  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));
Dries's avatar
 
Dries committed
297 298 299 300 301 302
        $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>";
303 304
      }
    }
305
  }
Dries's avatar
 
Dries committed
306
  $output .= "<div class=\"total\">". t("Total votes") .": $votestotal</div>";
Dries's avatar
 
Dries committed
307

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

  return $output;
}

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

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

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

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

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

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

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

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

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

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

382 383 384 385
function poll_validate(&$node) {
  $node ->teaser = poll_teaser($node);
}

386
?>