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

Steven Wittens's avatar
Steven Wittens committed
4
5
function poll_help($section = 'admin/help#poll') {
  $output = '';
Steven Wittens's avatar
Steven Wittens committed
6

Steven Wittens's avatar
Steven Wittens committed
7
  switch ($section) {
Steven Wittens's avatar
Steven Wittens committed
8

Steven Wittens's avatar
Steven Wittens committed
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
    case 'admin/help#poll':
      $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")));
      break;
    case 'admin/system/modules#description':
      $output = t("Enables your site to capture votes on different topics in the form of multiple choice questions.");
      break;
    case 'node/add#poll':
      $output = t("A poll is a multiple-choice question which visitors can vote on.");
      break;
Steven Wittens's avatar
Steven Wittens committed
26
  }
Steven Wittens's avatar
Steven Wittens committed
27
28

  return $output;
29
30
}

31
function poll_access($op, $node) {
Steven Wittens's avatar
Steven Wittens committed
32
  if ($op == 'view') {
33
    return $node->status;
34
35
  }

Steven Wittens's avatar
Steven Wittens committed
36
37
  if ($op == 'create') {
    return user_access('create polls');
Dries Buytaert's avatar
   
Dries Buytaert committed
38
  }
Steven Wittens's avatar
Steven Wittens committed
39
40
}

Steven Wittens's avatar
Steven Wittens committed
41
42
43
44
function poll_block($op = 'list', $delta = 0) {
  if (user_access('access content')) {
    if ($op == 'list') {
      $blocks[0]['info'] = t('Most recent poll');
Dries Buytaert's avatar
   
Dries Buytaert committed
45
46
47
      return $blocks;
    }
    else {
Steven Wittens's avatar
Steven Wittens committed
48
      // Retrieve latest poll
Dries Buytaert's avatar
   
Dries Buytaert committed
49
      $timestamp = db_result(db_query("SELECT MAX(created) FROM {node} WHERE type = 'poll' AND status = '1' AND moderate = '0'"));
Dries Buytaert's avatar
   
Dries Buytaert committed
50
      if ($timestamp) {
Steven Wittens's avatar
Steven Wittens committed
51
52
        $poll = node_load(array('type' => 'poll', 'created' => $timestamp, 'moderate' => 0, 'status' => 1));

Dries Buytaert's avatar
   
Dries Buytaert committed
53
54
        if ($poll->nid) {
          // Poll_view dumps the output into $poll->body
55
          poll_view($poll, 1, 0, 1);
Dries Buytaert's avatar
   
Dries Buytaert committed
56
        }
Dries Buytaert's avatar
   
Dries Buytaert committed
57
      }
Steven Wittens's avatar
Steven Wittens committed
58
59
      $block['subject'] = t('Poll');
      $block['content'] = $poll->body;
Dries Buytaert's avatar
   
Dries Buytaert committed
60
      return $block;
61
62
63
64
    }
  }
}

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

73
function poll_delete($node) {
Dries Buytaert's avatar
   
Dries Buytaert 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
function poll_validate(&$node) {
79
80
  if (isset($node->title)) {
    // Check for at least two options and validate amount of votes:
Steven Wittens's avatar
Steven Wittens committed
81
82
83
84
    $realchoices = 0;
    foreach ($node->choice as $i => $choice) {
      if ($choice['chtext'] != '') {
        $realchoices++;
85
      }
Dries Buytaert's avatar
   
Dries Buytaert committed
86

Steven Wittens's avatar
Steven Wittens committed
87
88
      if ($choice['chvotes'] < 0) {
        $error["choice][$i][chvotes"] = theme('error', t("Negative values are not allowed."));
89
      }
90
    }
Dries Buytaert's avatar
   
Dries Buytaert committed
91

Steven Wittens's avatar
Steven Wittens committed
92
93
    if ($realchoices < 2) {
      $error["choice][0][chtext"] = theme('error', t("You must fill in at least two choices."));
94
95
    }
  }
96
97
98
99
100
101

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

  return $error;
}

Steven Wittens's avatar
Steven Wittens committed
102
103
function poll_form(&$node, &$error) {
  $admin = user_access('administer nodes');
104

Steven Wittens's avatar
Steven Wittens committed
105
106
107
  if (function_exists('taxonomy_node_form')) {
    $output = implode('', taxonomy_node_form('poll', $node));
  }
108

Steven Wittens's avatar
Steven Wittens committed
109
110
111
  if (!isset($node->choices)) {
    $node->choices = max(2, count($node->choice) ? count($node->choice) : 5);
  }
Dries Buytaert's avatar
   
Dries Buytaert committed
112

Steven Wittens's avatar
Steven Wittens committed
113
114
115
  // User ticked 'need more choices'
  if ($node->morechoices) {
    $node->choices *= 2;
116
117
  }

Steven Wittens's avatar
Steven Wittens committed
118
  $output .= '<div class="poll-form">';
Dries Buytaert's avatar
   
Dries Buytaert committed
119

Steven Wittens's avatar
Steven Wittens committed
120
  // Poll choices
Dries Buytaert's avatar
   
Dries Buytaert committed
121
  $opts = drupal_map_assoc(range(2, $node->choices * 2 + 5));
122
  for ($a = 0; $a < $node->choices; $a++) {
Steven Wittens's avatar
Steven Wittens committed
123
    $group1 .= form_textfield(t('Choice %n', array('%n' => ($a + 1))), "choice][$a][chtext", $node->choice[$a]['chtext'], 50, 127, $error["choice][$a][chtext"]);
124
    if ($admin) {
Steven Wittens's avatar
Steven Wittens committed
125
      $group1 .= form_textfield(t('Votes for choice %n', array('%n' => ($a + 1))), "choice][$a][chvotes", (int)$node->choice[$a]['chvotes'], 7, 7, $error["choice][$a][chvotes"]);
126
127
    }
  }
Steven Wittens's avatar
Steven Wittens committed
128
129
130
  $group1 .= form_hidden('choices', $node->choices);
  $group1 .= form_checkbox(t('Need more choices'), 'morechoices', 1, 0, t("If the amount of boxes above isn't enough, check this box and click the Preview button below to add some more."));
  $output .= form_group(t('Choices'), $group1);
Dries Buytaert's avatar
   
Dries Buytaert committed
131

Dries Buytaert's avatar
   
Dries Buytaert committed
132

Steven Wittens's avatar
Steven Wittens committed
133
  // Poll attributes
134
  $_duration = array(0 => t('Unlimited')) + drupal_map_assoc(array(86400, 172800, 345600, 604800, 1209600, 2419200, 4838400, 9676800, 31536000), "format_interval");
Steven Wittens's avatar
Steven Wittens committed
135
  $_active = array(0 => t('Closed'), 1 => t('Active'));
Dries Buytaert's avatar
   
Dries Buytaert committed
136

Steven Wittens's avatar
Steven Wittens committed
137
138
  if ($admin) {
    $group2 .= form_radios(t('Poll status'), 'active', isset($node->active) ? $node->active : 1, $_active, t('When a poll is closed, visitors can no longer vote for it.'));
Dries Buytaert's avatar
   
Dries Buytaert committed
139
  }
Steven Wittens's avatar
Steven Wittens committed
140
  $group2 .= form_select(t('Poll duration'), 'runtime', $node->runtime ? $node->runtime : 0, $_duration, t('After this period, the poll will be closed automatically.'));
Dries Buytaert's avatar
   
Dries Buytaert committed
141
142

  $output .= form_group(t('Settings'), $group2);
Steven Wittens's avatar
Steven Wittens committed
143
  $output .= '</div>';
Dries Buytaert's avatar
   
Dries Buytaert committed
144
145

  return $output;
146
}
147

148
function poll_insert($node) {
Steven Wittens's avatar
Steven Wittens committed
149
  if (!user_access('administer nodes')) {
150
    // Make sure all votes are 0 initially
Steven Wittens's avatar
Steven Wittens committed
151
152
153
    foreach ($node->choice as $i => $choice) {
      $node->choice[$i]['chvotes'] = 0;
    }
154
155
    $node->active = 1;
  }
156

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

Steven Wittens's avatar
Steven Wittens committed
159
160
161
  foreach ($node->choice as $choice) {
    if ($choice['chtext'] != '') {
      db_query("INSERT INTO {poll_choices} (nid, chtext, chvotes, chorder) VALUES (%d, '%s', %d, %d)", $node->nid, $choice['chtext'], $choice['chvotes'], $i++);
162
163
    }
  }
164
165
}

Steven Wittens's avatar
Steven Wittens committed
166
function poll_link($type, $node = 0, $main) {
Dries Buytaert's avatar
   
Dries Buytaert committed
167
168
  $links = array();

Steven Wittens's avatar
Steven Wittens committed
169
  if ($type == 'system') {
Dries Buytaert's avatar
   
Dries Buytaert committed
170
    if (user_access("create polls")) {
Steven Wittens's avatar
Steven Wittens committed
171
      menu('node/add/poll', t('poll'), 'node_page', 0);
Dries Buytaert's avatar
   
Dries Buytaert committed
172
    }
Steven Wittens's avatar
Steven Wittens committed
173
174
    if (user_access('access content')) {
      menu('poll', t('polls'), 'poll_page', 0, MENU_HIDE);
Dries Buytaert's avatar
   
Dries Buytaert committed
175
    }
176
  }
Steven Wittens's avatar
Steven Wittens committed
177
178
  else if ($type == 'page' && user_access('access content')) {
    $links[] = l(t('polls'), 'poll', array('title' => t('View the list of polls on this site.')));
Steven Wittens's avatar
Steven Wittens committed
179
  }
Steven Wittens's avatar
Steven Wittens committed
180
  else if ($type == 'node' && $node->type == 'poll') {
Steven Wittens's avatar
Steven Wittens committed
181
182
183
184
185
    /*
    ** Add links to allow the user to switch between the results and the voting
    ** form, if he/she hasn't voted yet.
    */

Steven Wittens's avatar
Steven Wittens committed
186
187
188
    if ($node->allowvotes) {
      if (arg(3) == 'results') {
        $links[] = l(t('voting form'), 'node/view/'. $node->nid);
Steven Wittens's avatar
Steven Wittens committed
189
190
      }
      else {
Steven Wittens's avatar
Steven Wittens committed
191
        $links[] = l(t('view results'), 'node/view/'. $node->nid .'/results');
Steven Wittens's avatar
Steven Wittens committed
192
193
194
      }
    }
  }
Dries Buytaert's avatar
   
Dries Buytaert committed
195
196

  return $links;
197
}
Dries Buytaert's avatar
   
Dries Buytaert committed
198

Steven Wittens's avatar
Steven Wittens committed
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
/**
 * Determine an adjusted user id, to allow for basic tracking of anonymous
 * users (IP-based).
 */
function poll_uid() {
  global $user;
  if ($user->uid) {
     // Pad the UID with underscores to allow a simple strstr() search
    $id = '_'. $user->uid .'_';
  }
  else {
    $id = $_SERVER['REMOTE_ADDR'];
  }
  return $id;
}

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

Dries Buytaert's avatar
   
Dries Buytaert committed
219
  $result = db_query("SELECT chtext, chvotes, chorder FROM {poll_choices} WHERE nid=%d ORDER BY chorder", $node->nid);
Steven Wittens's avatar
Steven Wittens committed
220
221
  while ($choice = db_fetch_array($result)) {
    $poll->choice[$choice['chorder']] = $choice;
222
  }
Steven Wittens's avatar
Steven Wittens committed
223

Steven Wittens's avatar
Steven Wittens committed
224
225
226
227
228
229
230
  // Determine whether or not this user is allowed to vote
  $poll->allowvotes = false;
  if (user_access('vote on polls')) {
    if (!strstr($poll->voters, poll_uid())) {
      $poll->allowvotes = $poll->active;
    }
  }
231
232
  return $poll;
}
Dries Buytaert's avatar
   
Dries Buytaert committed
233

Dries Buytaert's avatar
   
Dries Buytaert committed
234
235
function poll_node_name($node) {
  return t("poll");
236
}
237

Steven Wittens's avatar
Steven Wittens committed
238
function poll_page() {
Steven Wittens's avatar
Steven Wittens committed
239
240
241
  // List all polls
  $result = pager_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", 15);
  $output = '<ul>';
Steven Wittens's avatar
Steven Wittens committed
242
  while ($node = db_fetch_object($result)) {
Steven Wittens's avatar
Steven Wittens committed
243
    $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
244
  }
Steven Wittens's avatar
Steven Wittens committed
245
246
247
  $output .= '</ul>';
  $output .= theme("pager", NULL, 15);
  print theme('page', $output);
Steven Wittens's avatar
Steven Wittens committed
248
249
}

250
function poll_perm() {
Steven Wittens's avatar
Steven Wittens committed
251
  return array('create polls', 'vote on polls');
252
}
Dries Buytaert's avatar
   
Dries Buytaert committed
253

254
255
function poll_teaser($node) {
  // Create a simple teaser that lists all the choices
Dries Buytaert's avatar
Dries Buytaert committed
256
  if (is_array($node->choice)) {
Steven Wittens's avatar
Steven Wittens committed
257
258
    foreach ($node->choice as $k => $choice) {
      $teaser .= '* '. $choice['chtext'] .'\n';
259
260
261
262
    }
  }
  return $teaser;
}
263

Steven Wittens's avatar
Steven Wittens committed
264
265
266
267
/**
 * Display the vote form
 */
function poll_view_voting(&$node, $main, $page, $block) {
Steven Wittens's avatar
Steven Wittens committed
268
  $url = request_uri();
Steven Wittens's avatar
Steven Wittens committed
269
  $output .= '<div class="poll">';
Dries Buytaert's avatar
   
Dries Buytaert committed
270

Steven Wittens's avatar
Steven Wittens committed
271
272
  $form .= '<div class="vote-form">';
  $form .= '<div class="choices">';
Dries Buytaert's avatar
   
Dries Buytaert committed
273
  if ($node->choice) {
Steven Wittens's avatar
Steven Wittens committed
274
275
276
    $list = array();
    foreach ($node->choice as $i => $choice) {
      $list[$i] = drupal_specialchars($choice['chtext']);
277
    }
Steven Wittens's avatar
Steven Wittens committed
278
    $form .= form_radios($page ? '' : $node->title, 'choice', -1, $list);
Dries Buytaert's avatar
   
Dries Buytaert committed
279
  }
Steven Wittens's avatar
Steven Wittens committed
280
281
282
283
284
285
  $form .= '</div>';
  $form .= form_hidden('nid', $node->nid);
  $form .= form_submit(t('Vote'), 'vote') .'</div>';

  $output .= form($form, 'post', url('node/view/'. $node->nid));
  $output .= '</div>';
Steven Wittens's avatar
Steven Wittens committed
286
287
288
289

  return $output;
}

Steven Wittens's avatar
Steven Wittens committed
290
function poll_view_results(&$node, $main, $page, $block) {
Steven Wittens's avatar
Steven Wittens committed
291
  // Display the results
Dries Buytaert's avatar
   
Dries Buytaert committed
292

Steven Wittens's avatar
Steven Wittens committed
293
  // Count the votes and find the maximum
Steven Wittens's avatar
Steven Wittens committed
294
295
296
  foreach ($node->choice as $choice) {
    $votestotal += $choice['chvotes'];
    $votesmax = max($votesmax, $choice['chvotes']);
Steven Wittens's avatar
Steven Wittens committed
297
298
  }

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

Steven Wittens's avatar
Steven Wittens committed
316
  $output .= '</div>';
Steven Wittens's avatar
Steven Wittens committed
317
318
319
320
321

  return $output;
}

function poll_view_processvote(&$node) {
Steven Wittens's avatar
Steven Wittens committed
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
  $edit = $_POST['edit'];
  $choice = $edit['choice'];
  $vote = $_POST['vote'];

  if ($vote == t('Vote')) {
    if (isset($choice) && isset($node->choice[$choice])) {
      if ($node->allowvotes) {
        $id = poll_uid();
        $node->voters = $node->voters ? ($node->voters .' '. $id) : $id;
        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, $choice);
        $node->allowvotes = false;
        $node->choice[$choice]['chvotes']++;
        drupal_set_message(t('Your vote was recorded.'));
      }
      else {
        drupal_set_message(t("You're not allowed to vote on this poll."), 'error');
      }
    }
    else {
      drupal_set_message(t("You didn't specify a valid poll choice."), 'error');
Steven Wittens's avatar
Steven Wittens committed
343
    }
Steven Wittens's avatar
Steven Wittens committed
344
345
346
  }
}

347
function poll_view(&$node, $main = 0, $page = 0, $block = 0) {
Dries Buytaert's avatar
   
Dries Buytaert committed
348
  global $user;
Steven Wittens's avatar
Steven Wittens committed
349

Steven Wittens's avatar
Steven Wittens committed
350
351
352
353
  if (!$block) {
    // Because the voting form is embedded in the node-display, we process the data here
    poll_view_processvote($node);
  }
Steven Wittens's avatar
Steven Wittens committed
354

Steven Wittens's avatar
Steven Wittens committed
355
356
357
358
359
360
  if ($node->allowvotes && (arg(2) != $node->nid || arg(3) != 'results')) {
    $output .= poll_view_voting($node, $main, $page, $block);
  }
  else {
    $output .= poll_view_results($node, $main, $page, $block);
  }
Steven Wittens's avatar
Steven Wittens committed
361

Steven Wittens's avatar
Steven Wittens committed
362
  // Special display for side-block
Steven Wittens's avatar
Steven Wittens committed
363
  if ($block) {
Steven Wittens's avatar
Steven Wittens committed
364
365
    // No 'read more' link
    $node->body = $node->teaser = '';
Steven Wittens's avatar
Steven Wittens committed
366

Steven Wittens's avatar
Steven Wittens committed
367
    $links = link_node($node, $main);
Steven Wittens's avatar
Steven Wittens committed
368
    $links[] = l(t('older polls'), 'poll', array('title' => t('View the list of polls on this site.')));
Steven Wittens's avatar
Steven Wittens committed
369

Steven Wittens's avatar
Steven Wittens committed
370
    $output .= '<div class="links">'. theme("links", $links) .'</div>';
Steven Wittens's avatar
Steven Wittens committed
371
372
373
  }

  $node->body = $node->teaser = $output;
374

375
  // We also use poll_view() for the side-block
Steven Wittens's avatar
Steven Wittens committed
376
  if (!$block) {
Steven Wittens's avatar
Steven Wittens committed
377
    return theme('node', $node, $main, $page);
378
379
380
  }
}

381
function poll_update($node) {
Steven Wittens's avatar
Steven Wittens committed
382
  db_query('UPDATE {poll} SET runtime = %d, active = %d WHERE nid = %d', $node->runtime, $node->active, $node->nid);
383

Steven Wittens's avatar
Steven Wittens committed
384
385
386
387
  db_query('DELETE FROM {poll_choices} WHERE nid = %d', $node->nid);
  foreach ($node->choice as $choice) {
    $chvotes = (int)$choice['chvotes'];
    $chtext = $choice['chtext'];
388

Steven Wittens's avatar
Steven Wittens committed
389
390
    if ($chtext != '') {
      db_query("INSERT INTO {poll_choices} (nid, chtext, chvotes, chorder) VALUES (%d, '%s', %d, %d)", $node->nid, $chtext, $chvotes, $i++);
391
    }
392
393
  }
}
394

395
?>