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

Steven Wittens's avatar
Steven Wittens committed
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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.
  */
  global $REMOTE_ADDR, $user;

  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 {
      $id = $REMOTE_ADDR;
    }
    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
41
  if ($op == "create") {
    return 1;
Dries's avatar
   
Dries committed
42
  }
Steven Wittens's avatar
Steven Wittens committed
43
44
}

45
function poll_block() {
46
  $timestamp = db_result(db_query("SELECT MAX(created) FROM node WHERE type='poll' AND status='1' AND moderate='0'"));
47
  if ($timestamp) {
48
    $poll = node_load(array("type" => "poll", "created" => $timestamp, "moderate" => "0", "status" => "1"));
49
50
    if ($poll->nid) {
      // Poll_view dumps the output into $poll->body
Steven Wittens's avatar
Steven Wittens committed
51
      poll_view($poll, 1, 1);
52
53
    }
  }
Dries's avatar
   
Dries committed
54
  $blocks[0][subject] = t("Latest poll: %t", array("%t" => $poll->title));
55
  $blocks[0][content] = $poll->body;
56
  $blocks[0][info] = t("Most recent poll");
57
  return $blocks;
58
59
}

60
61
function poll_cron() {
  // Close polls that have exceeded their allowed runtime
Kjartan's avatar
Kjartan committed
62
  $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
63
  while ($poll = db_fetch_object($result)) {
Steven Wittens's avatar
Steven Wittens committed
64
    db_query("UPDATE poll SET active='0' WHERE nid='$poll->nid'");
Dries's avatar
   
Dries committed
65
  }
66
67
}

68
69
70
function poll_delete($node) {
  db_query("DELETE FROM poll WHERE nid='$node->nid'");
  db_query("DELETE FROM poll_choices WHERE nid='$node->nid'");
71
72
}

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

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

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

81
82
83
84
85
86
  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
87

88
      if ($node->chvotes[$i] < 0) {
Kjartan's avatar
Kjartan committed
89
        $error["chvotes][$i"] = "<span style=\"color: red;\">". t("Negative values are not allowed.") ."</span>";
90
      }
91
    }
Dries's avatar
   
Dries committed
92

93
    if ($actualchoices < 2) {
Kjartan's avatar
Kjartan committed
94
      $error["choice][0"] = "<span style=\"color: red;\">". t("You must fill in at least two choices.") ."</span>";
95
96
97
98
99
    }
  }
  else {
    $help = variable_get("poll_help", "");
  }
Dries's avatar
   
Dries committed
100

101
102
103
104
  if (function_exists("taxonomy_node_form")) {
    $output = implode("", taxonomy_node_form("poll", $node));
  }

105
106
107
  for ($c = 2; $c <= 20; $c++) {
    $opts[$c] = $c;
  }
Dries's avatar
   
Dries committed
108
  $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
109
  $output .= form_submit(t("Preview")) ."<br /><br /><br />";
Dries's avatar
   
Dries committed
110

111
  for ($a = 0; $a < $node->choices; $a++) {
Kjartan's avatar
Kjartan committed
112
    $output .= form_textfield(t("Choice") ." ". ($a + 1), "choice][$a", $node->choice[$a], 50, 127, $error["choice][$a"]);
113
    if ($admin) {
Dries's avatar
   
Dries committed
114
      $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"]);
115
116
    }
  }
Dries's avatar
   
Dries committed
117

118
119
120
  if ($admin) {
    $output .= form_select(t("Poll status"), "active", isset($node->active) ? $node->active : 1, $_active);
  }
121

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

124
125
  return $output;
}
Dries's avatar
   
Dries committed
126

127
function poll_help() {
Dries's avatar
   
Dries committed
128
 ?><p>Drupal's poll module allows users with at least content posting privileges to submit multiple-choice questions that others can vote on.  Any user with sufficient privileges can vote.  Please note that this is fully controlled by Drupal's access control features.  For example, users might be required to login before voting (or even seeing the page where the voting occurs) or it could be open to the world.</p>
129
130
 <?php
}
131

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

139
  db_query("INSERT INTO poll (nid, runtime, voters, active) VALUES ('$node->nid', '$node->runtime', '', '$node->active')");
Dries's avatar
   
Dries committed
140

141
142
143
144
  for ($i = 0; $i < $node->choices; $i++) {
    $choice->chtext = filter($node->choice[$i]);
    $choice->chvotes = (int)$node->chvotes[$i];
    $choice->chorder = $i;
Dries's avatar
   
Dries committed
145

146
147
148
149
    if ($choice->chtext != "") {
      db_query("INSERT INTO poll_choices (nid, chtext, chvotes, chorder) VALUES ('$node->nid', '$choice->chtext', '$choice->chvotes', '$choice->chorder')");
    }
  }
150
151
}

Steven Wittens's avatar
Steven Wittens committed
152
function poll_link($type, $node = 0, $main) {
153
  if ($type == "menu.create" && user_access("post content")) {
Dries's avatar
   
Dries committed
154
    $links[] = lm(t("create poll"), array("mod" => "node", "op" => "add", "type" => "poll"), "", array("title" => t("Add a new poll.")));
155
  }
Steven Wittens's avatar
Steven Wittens committed
156
157
158
  else if ($type == "page" && user_access("access content")) {
    $links[] = lm(t("polls"), array("mod" => "poll"), "", array("title" => t("View the list of polls on this site.")));
  }
Steven Wittens's avatar
Steven Wittens committed
159
160
161
162
163
164
165
166
  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);
167

Steven Wittens's avatar
Steven Wittens committed
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
    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>";
      }
    }
  }
191
192
  return $links ? $links : array();
}
Dries's avatar
   
Dries committed
193

194
195
196
function poll_load($node) {
  // Load the appropriate choices into the $node object
  $poll = db_fetch_object(db_query("SELECT runtime, voters, active FROM poll WHERE nid = '$node->nid'"));
Dries's avatar
   
Dries committed
197

198
199
200
201
202
  $result = db_query("SELECT chtext, chvotes, chorder FROM poll_choices WHERE nid='$node->nid' ORDER BY chorder");
  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
203
204
205

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

209
210
211
212
213
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];
}
214

Steven Wittens's avatar
Steven Wittens committed
215
216
function poll_page() {
  global $theme;
Steven Wittens's avatar
Steven Wittens committed
217

Steven Wittens's avatar
Steven Wittens committed
218
  $theme->header();
219
  $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 ORDER BY n.created DESC");
Steven Wittens's avatar
Steven Wittens committed
220
221
222
223
224
225
226
  while ($node = db_fetch_object($result)) {
    $output .= "<li>". l($node->title, array("id" => $node->nid)) ." - ". format_plural($node->votes, "vote", "votes") ." - ". ($node->active ? t("open") : t("closed")) ."</li>";
  }
  $theme->box(t("Polls"), $output);
  $theme->footer();
}

227
228
229
function poll_perm() {
  return array("vote on polls");
}
Dries's avatar
   
Dries committed
230

231
232
233
function poll_save($op, $node) {
  if ($op == "approve") {
    return array("status" => 1, "promote" => 1);
234
  }
Dries's avatar
   
Dries committed
235

236
237
238
239
240
241
242
  if ($op == "create") {
    if (user_access("administer nodes")) {
      return array("runtime", "active", "choice", "choices", "chvotes", "body" => "", "teaser" => poll_teaser($node));
    }
    else {
      return array("runtime", "active", "choice", "choices", "chvotes", "body" => "", "moderate" => 1, "teaser" => poll_teaser($node));
    }
243
  }
Dries's avatar
   
Dries committed
244

245
246
  if ($op == "decline") {
    return array("status" => 0, "promote" => 0);
247
248
  }

249
250
251
  if ($op == "update") {
    return array("runtime", "active", "choice", "choices", "chvotes");
  }
252
253
}

Steven Wittens's avatar
Steven Wittens committed
254
function poll_system($field){
Dries's avatar
   
Dries committed
255
  $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
256
257
258
  return $system[$field];
}

259
260
261
262
263
264
265
266
267
function poll_teaser($node) {
  // Create a simple teaser that lists all the choices
  foreach ($node->choice as $k => $v) {
    if ($v != "") {
      $teaser .= "* $v\n";
    }
  }
  return $teaser;
}
268

Steven Wittens's avatar
Steven Wittens committed
269
270
271
function poll_view_voting(&$node, $main, $block, $links) {
  // Display the vote form
  global $theme;
Dries's avatar
   
Dries committed
272

Steven Wittens's avatar
Steven Wittens committed
273
274
275
  $url = request_uri();
  $output .= "<form action=\"$url\" method=\"post\">";
  $output .= "<table border=\"0\" align=\"center\"><tr><td>";
Dries's avatar
   
Dries committed
276

Steven Wittens's avatar
Steven Wittens committed
277
278
279
  foreach ($node->choice as $key => $value) {
    if ($value != "") {
      $output .= "<input type=\"radio\" name=\"pollvote[$node->nid]\" value=\"$key\" /> $value<br />";
280
    }
Dries's avatar
   
Dries committed
281
  }
282

Steven Wittens's avatar
Steven Wittens committed
283
284
285
286
287
  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>";
288
  }
Dries's avatar
   
Dries committed
289

Steven Wittens's avatar
Steven Wittens committed
290
291
292
293
294
295
296
297
298
  $output .= $block ? "<div align=\"center\">". $theme->links($links) ."</div>" : "";
  $output .= "</form>";

  return $output;
}

function poll_view_results(&$node, $main, $block, $links) {
  // Display the results
  global $theme;
299

Steven Wittens's avatar
Steven Wittens committed
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
  // Count the votes and find the maximum
  foreach ($node->choice as $key => $value) {
    $votestotal += $node->chvotes[$key];
    $votesmax = max($votesmax, $node->chvotes[$key]);
  }
  $votesmax = max($votesmax, 1);

  /*
  ** 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)
  */
  $output .= "<style type=\"text/css\">";
  $output .= "td.pollfg { background-color: ". $theme->foreground ."; font-size: 5pt; }";
  $output .= "td.pollbg { background-color: ". $theme->background ."; font-size: 5pt; }";
  $output .= "</style>";

  foreach ($node->choice as $key => $value) {
    if ($value != "") {
      $width = round($node->chvotes[$key] * 100 / $votesmax);
      $percentage = round($node->chvotes[$key] * 100 / max($votestotal, 1));

      $output .= "<table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" width=\"95%\" align=\"center\"><tr><td>$value</td><td><div align=\"right\"> $percentage%". (!$block ? " (". format_plural($node->chvotes[$key], "vote", "votes") .")" : "") ."</div></td></tr></table>";
      if ($width == 0) {
        $output .= "<table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" width=\"95%\" align=\"center\"><tr><td class=\"pollbg\" width=\"100%\">&nbsp;</td></tr></table>";
      }
      else if ($width == 100) {
        $output .= "<table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" width=\"95%\" align=\"center\"><tr><td class=\"pollfg\" width=\"100%\">&nbsp;</td></tr></table>";
      }
      else {
        $output .= "<table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" width=\"95%\" align=\"center\"><tr><td class=\"pollfg\" width=\"". $width ."%\">&nbsp;</td><td class=\"pollbg\" width=\"". (100 - $width) ."%\">&nbsp;</td></tr></table>";
331
332
      }
    }
333
  }
Steven Wittens's avatar
Steven Wittens committed
334
  $output .= "<br /><div align=\"center\">". t("Total votes") .": $votestotal";
Dries's avatar
   
Dries committed
335

Steven Wittens's avatar
Steven Wittens committed
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
  $output .= ($block ? "<br />". $theme->links($links) : "") ."</div>";

  return $output;
}

function poll_view_processvote(&$node) {
  global $pollvote;

  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;
      db_query("UPDATE poll SET voters='$node->voters' WHERE nid='$node->nid'");
      db_query("UPDATE poll_choices SET chvotes = chvotes + 1 WHERE nid='$node->nid' AND chorder='". $pollvote[$node->nid] ."'");
      $node->allowvotes = 0;
      $node->chvotes[$pollvote[$node->nid]]++;
Steven Wittens's avatar
Steven Wittens committed
351
    }
Steven Wittens's avatar
Steven Wittens committed
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
  }
}

function poll_view(&$node, $main = 0, $block = 0) {
  global $theme, $user;

  /*
  ** 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
376
377
    $links = link_node($node, $main);
    $links[] = lm(t("older polls"), array("mod" => "poll"), "", array("title" => t("View the list of polls on this site.")));
378
  }
Steven Wittens's avatar
Steven Wittens committed
379
380
381
382
383
384
385
386
387

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

389
  // We also use poll_view() for the side-block
Steven Wittens's avatar
Steven Wittens committed
390
  if (!$block) {
391
    $theme->node($node, $main);
392
393
394
  }
}

395
396
397
398
399
400
401
function poll_update($node) {
  db_query("UPDATE poll SET runtime='$node->runtime', active='$node->active' WHERE nid='$node->nid'");

  db_query("DELETE FROM poll_choices WHERE nid='$node->nid'");
  for ($i = 0; $i < $node->choices; $i++) {
    $choice->chtext = filter($node->choice[$i]);
    $choice->chvotes = (int)$node->chvotes[$i];
Dries's avatar
   
Dries committed
402
    $choice->chorder = $i;
403

404
405
406
    if ($choice->chtext != "") {
      db_query("INSERT INTO poll_choices (nid, chtext, chvotes, chorder) VALUES ('$node->nid', '$choice->chtext', '$choice->chvotes', '$choice->chorder')");
    }
407
408
  }
}
409

410
?>