poll.module 14.1 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
57
          poll_view($poll, 1, 0, 1);
Dries's avatar
 
Dries committed
58
        }
Dries's avatar
 
Dries committed
59
      }
Dries's avatar
 
Dries committed
60 61
      $block["subject"] = t("Poll");
      $block["content"] = "<div class=\"poll-title\">$poll->title</div>$poll->body";
Dries's avatar
 
Dries committed
62
      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) {
Dries's avatar
 
Dries committed
90
        $error["chvotes][$i"] = theme("error", t("Negative values are not allowed."));
91
      }
92
    }
Dries's avatar
 
Dries committed
93

94
    if ($actualchoices < 2) {
Dries's avatar
 
Dries committed
95
      $error["choice][0"] = theme("error", t("You must fill in at least two choices."));
96 97
    }
  }
98 99 100 101 102 103 104 105 106

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

  return $error;
}

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

Dries's avatar
 
Dries committed
107
  $_duration = array_merge(array(0 => t("Unlimited")), drupal_map_assoc(array(86400, 172800, 345600, 604800, 1209600, 2419200, 4838400, 9676800, 31536000), "format_interval"));
108 109 110 111 112
  $_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
  $opts = drupal_map_assoc(range(2, 30));
Dries's avatar
 
Dries committed
119
  $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
120
  $output .= form_submit(t("Preview")) ."<br /><br /><br />";
Dries's avatar
 
Dries committed
121

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

129
  if ($admin) {
Dries's avatar
 
Dries committed
130
    $output .= form_radios(t("Poll status"), "active", isset($node->active) ? $node->active : 1, $_active);
131
  }
132

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

135 136
  return $output;
}
Dries's avatar
 
Dries committed
137

Dries's avatar
 
Dries committed
138
function poll_help($section = "admin/help#poll") {
Dries's avatar
 
Dries committed
139 140 141 142
  $output = "";

  switch ($section) {

Dries's avatar
 
Dries committed
143
    case 'admin/help#poll':
Dries's avatar
 
Dries committed
144 145 146 147 148 149 150 151 152
      $output .= t("
      <p>Users with the correct <a href=\"%permissions\">permissions</a> 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 <a href=\"%poll\">Poll</a> 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>", array("%permissions" => url("admin/user/permission"), "%poll" => url("poll")));
Dries's avatar
 
Dries committed
153
      break;
Dries's avatar
 
Dries committed
154
    case 'admin/system/modules#description':
Dries's avatar
 
Dries committed
155 156
      $output = t("Enables your site to capture votes on different topics in the form of multiple choice questions.");
      break;
Dries's avatar
 
Dries committed
157 158 159
    case 'node/add#poll':
      $output = t("A poll is a multiple-choice question which visitors can vote on.");
      break;
Dries's avatar
 
Dries committed
160 161 162
  }

  return $output;
163
}
164

165 166 167
function poll_insert($node) {
  if (!user_access("administer nodes")) {
    // Make sure all votes are 0 initially
Kjartan's avatar
Kjartan committed
168
    for ($i = 0; $i < count($node->chvotes); $i++) $node->chvotes[$i] = 0;
169 170
    $node->active = 1;
  }
171

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

174
  for ($i = 0; $i < $node->choices; $i++) {
Dries's avatar
 
Dries committed
175
    if ($node->choice[$i] != "") {
Dries's avatar
 
Dries committed
176
      db_query("INSERT INTO {poll_choices} (nid, chtext, chvotes, chorder) VALUES (%d, '%s', %d, %d)", $node->nid, $node->choice[$i], $node->chvotes[$i], $i);
177 178
    }
  }
179 180
}

Steven Wittens's avatar
Steven Wittens committed
181
function poll_link($type, $node = 0, $main) {
Dries's avatar
 
Dries committed
182 183
  $links = array();

Dries's avatar
 
Dries committed
184 185
  if ($type == "system") {
    if (user_access("create polls")) {
Dries's avatar
 
Dries committed
186 187 188
      menu("node/add/poll", t("poll"), "node_page", 0);
    }
    if (user_access("access content")) {
Dries's avatar
 
Dries committed
189
      menu("poll", t("polls"), "poll_page", 0, MENU_HIDE);
Dries's avatar
 
Dries committed
190
    }
191
  }
Steven Wittens's avatar
Steven Wittens committed
192
  else if ($type == "page" && user_access("access content")) {
Dries's avatar
 
Dries committed
193
    $links[] = l(t("polls"), "poll", array("title" => t("View the list of polls on this site.")));
Steven Wittens's avatar
Steven Wittens committed
194
  }
Steven Wittens's avatar
Steven Wittens committed
195 196 197 198 199 200 201 202
  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);
203

Steven Wittens's avatar
Steven Wittens committed
204
    if ($node->allowvotes == 1) {
Dries's avatar
 
Dries committed
205
      $pollresults = $_GET["pollresults"];
Steven Wittens's avatar
Steven Wittens committed
206 207 208 209 210 211

      // 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
212
        $links[] = "<a href=\"". htmlentities($url) ."\">". t("voting form") . "</a>";
Steven Wittens's avatar
Steven Wittens committed
213 214 215 216 217 218 219 220 221 222
      }
      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
223
        $links[] = "<a href=\"". htmlentities($url) ."\">". t("view results") . "</a>";
Steven Wittens's avatar
Steven Wittens committed
224 225 226
      }
    }
  }
Dries's avatar
 
Dries committed
227 228

  return $links;
229
}
Dries's avatar
 
Dries committed
230

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

Dries's avatar
 
Dries committed
235
  $result = db_query("SELECT chtext, chvotes, chorder FROM {poll_choices} WHERE nid=%d ORDER BY chorder", $node->nid);
236 237 238 239
  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
240 241 242

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

Dries's avatar
 
Dries committed
246 247
function poll_node_name($node) {
  return t("poll");
248
}
249

Steven Wittens's avatar
Steven Wittens committed
250
function poll_page() {
Dries's avatar
 
Dries committed
251
  $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
252
  $output = "<ul>";
Steven Wittens's avatar
Steven Wittens committed
253
  while ($node = db_fetch_object($result)) {
Dries's avatar
 
Dries committed
254
    $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
255
  }
Kjartan's avatar
Kjartan committed
256
  $output .= "</ul>";
Dries's avatar
 
Dries committed
257
  print theme("page", $output);
Steven Wittens's avatar
Steven Wittens committed
258 259
}

260
function poll_perm() {
Dries's avatar
 
Dries committed
261
  return array("create polls", "vote on polls");
262
}
Dries's avatar
 
Dries committed
263

264 265
function poll_teaser($node) {
  // Create a simple teaser that lists all the choices
Dries's avatar
Dries committed
266 267 268 269 270
  if (is_array($node->choice)) {
    foreach ($node->choice as $k => $v) {
      if ($v != "") {
        $teaser .= "* $v\n";
      }
271 272 273 274
    }
  }
  return $teaser;
}
275

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

Dries's avatar
 
Dries committed
279

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

Dries's avatar
 
Dries committed
283 284
  $output .= "<div class=\"vote-form\">";
  $output .= "<div class=\"choices\">";
Dries's avatar
 
Dries committed
285 286 287
  if ($node->choice) {
    foreach ($node->choice as $key => $value) {
      if ($value != "") {
Dries's avatar
 
Dries committed
288
        $output .= "<div><input type=\"radio\" name=\"pollvote[$node->nid]\" value=\"$key\" />". drupal_specialchars($value) ."</div>";
Dries's avatar
 
Dries committed
289
      }
290
    }
Dries's avatar
 
Dries committed
291
  }
Dries's avatar
 
Dries committed
292
  $output .= "</div>". form_submit(t("Vote"), "vote") ."</div>";
Dries's avatar
 
Dries committed
293 294
  $output .= $block ? "<div class=\"links\">". theme("links", $links) ."</div>" : "";
  $output .= "</form></div>";
Steven Wittens's avatar
Steven Wittens committed
295 296 297 298 299 300

  return $output;
}

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

302

Steven Wittens's avatar
Steven Wittens committed
303
  // Count the votes and find the maximum
Dries's avatar
 
Dries committed
304 305 306 307 308 309
  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
310 311
  }

Dries's avatar
 
Dries committed
312 313
  // Output the divs for the text, bars and percentages
  $output .= "<div class=\"poll\">";
Dries's avatar
 
Dries committed
314 315 316
  if ($node->choice) {
    foreach ($node->choice as $key => $value) {
      if ($value != "") {
Dries's avatar
 
Dries committed
317
        $width = round($node->chvotes[$key] * 100 / max($votestotal, 1));
Dries's avatar
 
Dries committed
318
        $percentage = round($node->chvotes[$key] * 100 / max($votestotal, 1));
Dries's avatar
 
Dries committed
319
        $output .= "<div class=\"text\">". drupal_specialchars($value) ."</div>";
Dries's avatar
 
Dries committed
320 321 322 323 324
        $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>";
325 326
      }
    }
327
  }
Dries's avatar
 
Dries committed
328
  $output .= "<div class=\"total\">". t("Total votes") .": $votestotal</div>";
Dries's avatar
 
Dries committed
329

330
  $output .= ($block ? "<div class=\"links\">". theme("links", $links) ."</div>" : "") ."</div>";
Steven Wittens's avatar
Steven Wittens committed
331 332 333 334 335

  return $output;
}

function poll_view_processvote(&$node) {
Dries's avatar
 
Dries committed
336
  $pollvote = $_POST["pollvote"];
Steven Wittens's avatar
Steven Wittens committed
337 338 339 340

  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
341 342
      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
343 344
      $node->allowvotes = 0;
      $node->chvotes[$pollvote[$node->nid]]++;
Steven Wittens's avatar
Steven Wittens committed
345
    }
Steven Wittens's avatar
Steven Wittens committed
346 347 348
  }
}

349
function poll_view(&$node, $main = 0, $page = 0, $block = 0) {
Dries's avatar
 
Dries committed
350
  global $user;
Steven Wittens's avatar
Steven Wittens committed
351 352 353 354 355 356 357

  /*
  ** 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
358
  $pollresults = $_GET["pollresults"];
Steven Wittens's avatar
Steven Wittens committed
359 360 361 362 363 364 365 366 367 368 369

  // 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
370
    $links = link_node($node, $main);
Dries's avatar
 
Dries committed
371
    $links[] = l(t("older polls"), "poll", array("title" => t("View the list of polls on this site.")));
372
  }
Steven Wittens's avatar
Steven Wittens committed
373 374 375 376 377 378 379 380 381

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

383
  // We also use poll_view() for the side-block
Steven Wittens's avatar
Steven Wittens committed
384
  if (!$block) {
Dries's avatar
 
Dries committed
385
    return theme("node", $node, $main);
386 387 388
  }
}

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

Dries's avatar
 
Dries committed
392
  db_query("DELETE FROM {poll_choices} WHERE nid = %d", $node->nid);
Dries's avatar
 
Dries committed
393
  for ($i = 0; $i < count($node->choice); $i++) {
Dries's avatar
 
Dries committed
394
    $choice->chtext = $node->choice[$i];
395
    $choice->chvotes = (int)$node->chvotes[$i];
Dries's avatar
 
Dries committed
396
    $choice->chorder = $i;
397

398
    if ($choice->chtext != "") {
Dries's avatar
 
Dries committed
399
      db_query("INSERT INTO {poll_choices} (nid, chtext, chvotes, chorder) VALUES (%d, '%s', %d, %d)", $node->nid, $choice->chtext, $choice->chvotes, $choice->chorder);
400
    }
401 402
  }
}
403

404
?>