2 // $Id: poll.module 144 2007-03-28 07:52:20Z thierry $
6 * Enables your site to capture votes on different topics in the form of multiple
11 * Implementation of hook_help().
13 function poll_help($section) {
15 case 'admin/help#poll':
16 $output = '<p>'. t('The poll module can be used to create simple polls for site users. A poll is a simple multiple choice questionnaire which displays the cumulative results of the answers to the poll. Having polls on the site is a good way to get instant feedback from community members.') .'</p>';
17 $output .= '<p>'. t('Users can create a poll. 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.The <a href="%poll">poll</a> item in the navigation menu 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.', array('%poll' => url('poll'))) .'</p>';
18 $output .= t('<p>You can</p>
20 <li>view the <a href="%poll">polls page</a>.</li>
21 <li><a href="%admin-node-configure-types-poll">administer >> settings >> content types >> configure poll</a>.</li>
23 ', array('%poll' => url('poll'), '%admin-node-configure-types-poll' => url('admin/settings/content-types/poll')));
24 $output .= '<p>'. t('For more information please read the configuration and customization handbook <a href="%poll">Poll page</a>.', array('%poll' => 'http://drupal.org/handbook/modules/poll/')) .'</p>';
26 case 'admin/modules#description':
27 return t("Allows your site to capture votes on different topics in the form of multiple choice questions.");
29 return t("A poll is a multiple-choice question which visitors can vote on.");
34 * Implementation of hook_access().
36 function poll_access($op, $node) {
37 if ($op == 'create') {
38 return user_access('create polls');
43 * Implementation of hook_block().
45 * Generates a block containing the latest poll.
47 function poll_block($op = 'list', $delta = 0) {
48 if (user_access('access content')) {
50 $blocks[0]['info'] = t('Most recent poll');
53 else if ($op == 'view') {
54 // Retrieve the latest poll.
55 $sql = db_rewrite_sql("SELECT MAX(n.created) FROM {node} n INNER JOIN {poll} p ON p.nid = n.nid WHERE n.status = 1 AND p.active = 1 AND n.moderate = 0");
56 $timestamp = db_result(db_query($sql));
58 $poll = node_load(array('type' => 'poll', 'created' => $timestamp, 'moderate' => 0, 'status' => 1));
61 // poll_view() dumps the output into $poll->body.
62 poll_view($poll, 1, 0, 1);
65 $block['subject'] = t('Poll');
66 $block['content'] = $poll->body;
73 * Implementation of hook_cron().
75 * Closes polls that have exceeded their allowed runtime.
77 function poll_cron() {
78 $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');
79 while ($poll = db_fetch_object($result)) {
80 db_query("UPDATE {poll} SET active = 0 WHERE nid = %d", $poll->nid);
85 * Implementation of hook_delete().
87 function poll_delete($node) {
88 db_query("DELETE FROM {poll} WHERE nid = %d", $node->nid);
89 db_query("DELETE FROM {poll_choices} WHERE nid = %d", $node->nid);
90 db_query("DELETE FROM {poll_votes} WHERE nid = %d", $node->nid);
94 * Implementation of hook_submit().
96 function poll_submit(&$node) {
98 $node->choice = array_values($node->choice);
99 $node->teaser = poll_teaser($node);
103 * Implementation of hook_validate().
105 function poll_validate($node) {
106 if (isset($node->title)) {
107 // Check for at least two options and validate amount of votes:
110 $node->choice = array_values($node->choice);
111 foreach ($node->choice as $i => $choice) {
112 if ($choice['chtext'] != '') {
115 if ($choice['chvotes'] < 0) {
116 form_set_error("choice][$i][chvotes", t('Negative values are not allowed.'));
120 if ($realchoices < 2) {
121 form_set_error("choice][$realchoices][chtext", t('You must fill in at least two choices.'));
127 * Implementation of hook_form().
129 function poll_form(&$node) {
130 $admin = user_access('administer nodes');
132 $form['title'] = array('#type' => 'textfield', '#title' => t('Question'), '#required' => TRUE, '#default_value' => $node->title, '#weight' => -1);
134 $form['choice']['choices'] = array('#type' => 'hidden', '#default_value' => max(2, count($node->choice) ? count($node->choice) : 5));
135 $form['choice']['morechoices'] = array('#type' => 'checkbox', '#title' => t('Need more choices'), '#default_value' => 0, '#description' => t("If the amount of boxes above isn't enough, check this box and click the Preview button below to add some more."), '#weight' => 1);
136 $form['choice'] = form_builder('poll_node_form', $form['choice']);
137 if ($form['choice']['morechoices']['#value']) {
138 $form['choice']['morechoices']['#value'] = 0;
139 $form['choice']['choices']['#value'] *= 2;
142 // if the value was changed in a previous iteration, retain it.
143 $node->choices = $form['choice']['choices']['#value'];
146 $form['choice'] += array('#type' => 'fieldset', '#title' => t('Choices'), '#prefix' => '<div class="poll-form">', '#suffix' => '</div>', '#tree' => TRUE);
147 for ($a = 0; $a < $node->choices; $a++) {
148 $form['choice'][$a]['chtext'] = array('#type' => 'textfield', '#title' => t('Choice %n', array('%n' => ($a + 1))), '#default_value' => $node->choice[$a]['chtext']);
150 $form['choice'][$a]['chvotes'] = array('#type' => 'textfield', '#title' => t('Votes for choice %n', array('%n' => ($a + 1))), '#default_value' => (int)$node->choice[$a]['chvotes'], '#size' => 5, '#maxlength' => 7);
155 $_duration = array(0 => t('Unlimited')) + drupal_map_assoc(array(86400, 172800, 345600, 604800, 1209600, 2419200, 4838400, 9676800, 31536000), "format_interval");
156 $_active = array(0 => t('Closed'), 1 => t('Active'));
159 $form['settings'] = array('#type' => 'fieldset', '#title' => t('Settings'));
160 $form['settings']['active'] = array('#type' => 'radios', '#title' => t('Poll status'), '#default_value' => isset($node->active) ? $node->active : 1, '#options' => $_active, '#description' => t('When a poll is closed, visitors can no longer vote for it.'));
162 $form['settings']['runtime'] = array('#type' => 'select', '#title' => t('Poll duration'), '#default_value' => $node->runtime ? $node->runtime : 0, '#options' => $_duration, '#description' => t('After this period, the poll will be closed automatically.'));
167 function poll_insert($node) {
168 if (!user_access('administer nodes')) {
169 // Make sure all votes are 0 initially
170 foreach ($node->choice as $i => $choice) {
171 $node->choice[$i]['chvotes'] = 0;
176 db_query("INSERT INTO {poll} (nid, runtime, active) VALUES (%d, %d, %d)", $node->nid, $node->runtime, $node->active);
178 foreach ($node->choice as $choice) {
179 if ($choice['chtext'] != '') {
180 db_query("INSERT INTO {poll_choices} (nid, chtext, chvotes, chorder) VALUES (%d, '%s', %d, %d)", $node->nid, $choice['chtext'], $choice['chvotes'], $i++);
186 * Implementation of hook_menu().
188 function poll_menu($may_cache) {
192 $items[] = array('path' => 'node/add/poll', 'title' => t('poll'),
193 'access' => user_access('create polls'));
194 $items[] = array('path' => 'poll', 'title' => t('polls'),
195 'callback' => 'poll_page',
196 'access' => user_access('access content'),
197 'type' => MENU_SUGGESTED_ITEM);
199 $items[] = array('path' => 'poll/vote',
200 'title' => t('vote'),
201 'callback' => 'poll_vote',
202 'access' => user_access('vote on polls'),
203 'type' => MENU_CALLBACK);
206 if (arg(0) == 'node' && is_numeric(arg(1))) {
207 $node = node_load(arg(1));
209 if ($node->type == 'poll' && $node->allowvotes) {
210 $items[] = array('path' => 'node/'. arg(1) .'/results',
211 'title' => t('results'),
212 'callback' => 'poll_results',
213 'access' => user_access('access content'),
215 'type' => MENU_LOCAL_TASK);
224 * Implementation of hook_load().
226 function poll_load($node) {
229 // Load the appropriate choices into the $node object
230 $poll = db_fetch_object(db_query("SELECT runtime, active FROM {poll} WHERE nid = %d", $node->nid));
232 $result = db_query("SELECT chtext, chvotes, chorder FROM {poll_choices} WHERE nid = %d ORDER BY chorder", $node->nid);
233 while ($choice = db_fetch_array($result)) {
234 $poll->choice[$choice['chorder']] = $choice;
237 // Determine whether or not this user is allowed to vote
238 $poll->allowvotes = FALSE;
239 if (user_access('vote on polls') && $poll->active) {
240 if ($user->uid && db_num_rows(db_query('SELECT uid FROM {poll_votes} WHERE nid = %d AND uid = %d', $node->nid, $user->uid)) == 0) {
241 $poll->allowvotes = TRUE;
243 else if ($user->uid == 0 && db_num_rows(db_query("SELECT hostname FROM {poll_votes} WHERE nid = %d AND hostname = '%s'", $node->nid, $_SERVER['REMOTE_ADDR'])) == 0) {
244 $poll->allowvotes = TRUE;
251 * Implementation of hook_node_info().
253 function poll_node_info() {
254 return array('poll' => array('name' => t("poll"), 'base' => 'poll'));
257 function poll_page() {
259 $sql = "SELECT n.nid, n.title, p.active, n.created, 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 n.status = 1 AND n.moderate = 0 GROUP BY n.nid, n.title, p.active, n.created ORDER BY n.created DESC";
260 $sql = db_rewrite_sql($sql);
261 $result = pager_query($sql, 15);
263 while ($node = db_fetch_object($result)) {
264 $output .= '<li>'. l($node->title, "node/$node->nid") .' - '. format_plural($node->votes, '1 vote', '%count votes') .' - '. ($node->active ? t('open') : t('closed')) .'</li>';
267 $output .= theme("pager", NULL, 15);
272 * Implementation of hook_perm().
274 function poll_perm() {
275 return array('create polls', 'vote on polls');
279 * Creates a simple teaser that lists all the choices.
281 function poll_teaser($node) {
283 if (is_array($node->choice)) {
284 foreach ($node->choice as $k => $choice) {
285 $teaser .= '* '. $choice['chtext'] .'\n';
292 * Generates the voting form for a poll.
294 function poll_view_voting(&$node, $teaser, $page, $block) {
295 if ($_POST['op'] == t('Vote')) {
301 foreach ($node->choice as $i => $choice) {
302 $list[$i] = check_plain($choice['chtext']);
304 $form['choice'] = array('#type' => 'radios', '#title' => $page ? '' : check_plain($node->title), '#default_value' => -1, '#options' => $list);
306 $form['nid'] = array('#type' => 'hidden', '#value' => $node->nid);
307 $form['vote'] = array('#type' => 'submit', '#value' => t('Vote'));
308 $form['#action'] = url('node/'. $node->nid);
309 return drupal_get_form('poll_view_voting', $form);
313 * Themes the voting form for a poll.
315 function theme_poll_view_voting($form) {
316 $output .= '<div class="poll">';
317 $output .= ' <div class="vote-form">';
318 $output .= ' <div class="choices">';
319 $output .= form_render($form['choice']);
320 $output .= ' </div>';
321 $output .= form_render($form['nid']);
322 $output .= form_render($form['vote']);
323 $output .= ' </div>';
324 $output .= form_render($form);
330 * Generates a graphical representation of the results of a poll.
332 function poll_view_results(&$node, $teaser, $page, $block) {
333 // Count the votes and find the maximum
334 foreach ($node->choice as $choice) {
335 $total_votes += $choice['chvotes'];
336 $max_votes = max($max_votes, $choice['chvotes']);
339 foreach ($node->choice as $i => $choice) {
340 if ($choice['chtext'] != '') {
341 $poll_results .= theme('poll_bar', check_plain($choice['chtext']), round($choice['chvotes'] * 100 / max($total_votes, 1)), format_plural($choice['chvotes'], '1 vote', '%count votes'), $block);
345 $output .= theme('poll_results', check_plain($node->title), $poll_results, $total_votes, $node->links, $block);
350 function theme_poll_results($title, $results, $votes, $links, $block) {
352 $output .= '<div class="poll">';
353 $output .= '<div class="title">'. $title .'</div>';
355 $output .= '<div class="total">'. t('Total votes: %votes', array('%votes' => $votes)) .'</div>';
357 $output .= '<div class="links">'. theme('links', $links) .'</div>';
360 $output .= '<div class="poll">';
362 $output .= '<div class="total">'. t('Total votes: %votes', array('%votes' => $votes)) .'</div>';
369 function theme_poll_bar($title, $percentage, $votes, $block) {
371 $output = '<div class="text">'. $title .'</div>';
372 $output .= '<div class="bar"><div style="width: '. $percentage .'%;" class="foreground"></div></div>';
373 $output .= '<div class="percent">'. $percentage .'%</div>';
376 $output = '<div class="text">'. $title .'</div>';
377 $output .= '<div class="bar"><div style="width: '. $percentage .'%;" class="foreground"></div></div>';
378 $output .= '<div class="percent">'. $percentage .'% ('. $votes .')</div>';
385 * Callback for the 'results' tab for polls you can vote on
387 function poll_results() {
388 if ($node = node_load(arg(1))) {
389 drupal_set_title(check_plain($node->title));
390 return node_show($node, 0);
398 * Callback for processing a vote
400 function poll_vote(&$node) {
404 if ($node = node_load($nid)) {
405 $edit = $_POST['edit'];
406 $choice = $edit['choice'];
407 $vote = $_POST['vote'];
409 if (isset($choice) && isset($node->choice[$choice])) {
410 if ($node->allowvotes) {
411 // Mark the user or host as having voted.
413 db_query('INSERT INTO {poll_votes} (nid, uid) VALUES (%d, %d)', $node->nid, $user->uid);
416 db_query("INSERT INTO {poll_votes} (nid, hostname) VALUES (%d, '%s')", $node->nid, $_SERVER['REMOTE_ADDR']);
419 // Add one to the votes.
420 db_query("UPDATE {poll_choices} SET chvotes = chvotes + 1 WHERE nid = %d AND chorder = %d", $node->nid, $choice);
422 $node->allowvotes = FALSE;
423 $node->choice[$choice]['chvotes']++;
424 drupal_set_message(t('Your vote was recorded.'));
427 drupal_set_message(t("You're not allowed to vote on this poll."), 'error');
431 drupal_set_message(t("You didn't specify a valid poll choice."), 'error');
434 drupal_goto('node/'. $nid);
442 * Implementation of hook_view().
445 * An extra parameter that adapts the hook to display a block-ready
446 * rendering of the poll.
448 function poll_view(&$node, $teaser = FALSE, $page = FALSE, $block = FALSE) {
452 // Special display for side-block
454 // No 'read more' link
455 $node->body = $node->teaser = '';
457 $links = module_invoke_all('link', 'node', $node, 1);
458 $links[] = l(t('older polls'), 'poll', array('title' => t('View the list of polls on this site.')));
459 if ($node->allowvotes && $block) {
460 $links[] = l(t('results'), 'node/'. $node->nid .'/results', array('title' => t('View the current poll results.')));
463 $node->links = $links;
466 if ($node->allowvotes && ($block || arg(2) != 'results')) {
467 $output .= poll_view_voting($node, $teaser, $page, $block);
470 $output .= poll_view_results($node, $teaser, $page, $block);
473 $node->body = $node->teaser = $output;
477 * Implementation of hook_update().
479 function poll_update($node) {
480 db_query('UPDATE {poll} SET runtime = %d, active = %d WHERE nid = %d', $node->runtime, $node->active, $node->nid);
482 db_query('DELETE FROM {poll_choices} WHERE nid = %d', $node->nid);
483 db_query('DELETE FROM {poll_votes} WHERE nid = %d', $node->nid);
484 foreach ($node->choice as $choice) {
485 $chvotes = (int)$choice['chvotes'];
486 $chtext = $choice['chtext'];
489 db_query("INSERT INTO {poll_choices} (nid, chtext, chvotes, chorder) VALUES (%d, '%s', %d, %d)", $node->nid, $chtext, $chvotes, $i++);
495 * Implementation of hook_user().
497 function poll_user($op, &$edit, &$user) {
498 if ($op == 'delete') {
499 db_query('UPDATE {poll_votes} SET uid = 0 WHERE uid = %d', $user->uid);