From 9e4e2dd57c2c8876fc74b659671466910d078407 Mon Sep 17 00:00:00 2001 From: Steven Wittens <steven@10.no-reply.drupal.org> Date: Sun, 7 Apr 2002 17:22:59 +0000 Subject: [PATCH] Rewrite of poll.module... improvements: - Anonymous users' voting is controlled by IP - Correct handling of multiple polls on one page - CVS compatible --- modules/poll.module | 460 ++++++++++++++++++++------------------- modules/poll/poll.module | 460 ++++++++++++++++++++------------------- 2 files changed, 466 insertions(+), 454 deletions(-) diff --git a/modules/poll.module b/modules/poll.module index 13a20573ee9f..a4fdafac4e46 100644 --- a/modules/poll.module +++ b/modules/poll.module @@ -1,288 +1,294 @@ <?php // $Id$ -// description: -// A multiple choice Poll allows you to submit a question to other users to which anyone can answer. To be published on Tipic, the Poll has to be approved by the moderators. - -/* - -class Poll { - function Poll($poll) { - $this = new Node($poll); - $this->runtime = $poll[runtime]; - $this->active = $poll[active]; - $this->voters = $poll[voters]; - $this->choice = $poll[choice]; - $this->chvotes = $poll[chvotes]; - $this->chid = $poll[chid]; +function poll_access($op, $node) { + if ($op == "view") { + return $node->status; } -} - -function poll_cron() { - $result = _node_get(array("type" => "poll")); - while ($poll = db_fetch_array($result)) { - if (($poll[active]) && ($poll[runtime])) { - if (($poll[timestamp] + $poll[runtime]) < time()) { - $poll[active] = 0; - node_save($poll, array(active)); - } - } - } -} -function poll_delete($node) { - if ($node->nid) - { - db_query("DELETE FROM poll_choices WHERE nid='" . $node->nid . "'"); + if ($op == "create") { + return 1; } } -function poll_get_choices_array($poll) { - if (!is_array($poll[choice])) { - $result = db_query("SELECT * FROM poll_choices WHERE nid='" . $poll[nid] . "'"); - if ($result) { - while ($choices = db_fetch_array($result)) { - $m = max($m, $choices[chvotes]); - $t += $choices[chvotes]; - $poll[choice][$choices[chorder]] = $choices[chtext]; - $poll[chvotes][$choices[chorder]] = $choices[chvotes]; - $poll[chid][$choices[chorder]] = $choices[chid]; - } - if ($poll[choice]) ksort($poll[choice]); - } - } - $poll[maxvotes] = max(1, $m); - $poll[totalvotes] = $t; - return $poll; -} - -function poll_get_choices_obj($poll) { - if (!is_array($poll->choice)) { - $result = db_query("SELECT * FROM poll_choices WHERE nid='" . $poll->nid . "'"); - if ($result) { - while ($choices = db_fetch_array($result)) { - $m = max($m,$choices[chvotes]); - $t += $choices[chvotes]; - - $poll->choice[$choices[chorder]] = $choices[chtext]; - $poll->chvotes[$choices[chorder]] = $choices[chvotes]; - $poll->chid[$choices[chorder]] = $choices[chid]; - } - if ($poll->choice) ksort($poll->choice); +function poll_block() { + $timestamp = db_result(db_query("SELECT MAX(created) FROM node WHERE type='poll' AND status='1'")); + if ($timestamp) { + $poll = node_load(array("type" => "poll", "created" => $timestamp, "status" => "1")); + if ($poll->nid) { + // Poll_view dumps the output into $poll->body + poll_view($poll, 0, 1); } } - $poll->maxvotes = max(1, $m); - $poll->totalvotes = $t; - return $poll; -} - -function poll_status() { - return array(dumped, queued, posted); + $blocks[0][subject] = "Latest poll"; + $blocks[0][content] = $poll->body; + $blocks[0][info] = "Most recent poll"; + return $blocks; } -function poll_search($keys) { - global $PHP_SELF, $status; - $result = db_query("SELECT n.*, p.* FROM poll p LEFT JOIN node n ON n.nid = p.nid AND n.lid = p.lid WHERE n.status = '$status[posted]' AND (n.title LIKE '%$keys%') LIMIT 20"); - while ($poll = db_fetch_object($result)) { - $find[$i++] = array("title" => check_output($poll->title), "link" => (strstr($PHP_SELF, "admin.php") ? "admin.php?mod=poll&op=edit&id=$poll->nid" : "node.php?id=$poll->nid"), "user" => $poll->name, "date" => $poll->timestamp); - } - return $find; +function poll_cron() { + // Close polls that have exceeded their allowed runtime + $result = db_query("SELECT p.lid 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'"); + while ($poll = db_fetch_object($result)) { + db_query("UPDATE poll SET active='0' WHERE lid='$poll->lid'"); + } } -function poll_help() { - ?> - <p>Poll.module allows you to run simple multiple choice polls on your site. After creating the necessary categories, you can add a poll through this administration interface.</p> - <p>The poll.module also has a block to show the latest poll. If you combine it with a poll category that doesn't promote to the mainpage, you'll get a side-block only poll system.</p> - <p>If you want to set a limit on the duration of a poll, use the <i>Poll Duration</i> setting. After the specified interval, the poll will automatically be closed, preventing anymore voting.</p> - <?php +function poll_delete($node) { + db_query("DELETE FROM poll WHERE nid='$node->nid'"); + db_query("DELETE FROM poll_choices WHERE nid='$node->nid'"); } -function poll_graph($val) { - global $theme; - $clrfill = $theme->foreground ? $theme->foreground : "#000000"; - $clrempty = $theme->background ? $theme->background : "#ffffff"; - $p = round($val * 100); - return "<table width=\"100%\" cellspacing=\"0\" cellpadding=\"0\"><tr>" . ($p ? "<td width=\"" . $p . "%\" style=\"background-color: $clrfill;\"><span style=\"font-size: 4pt;\"> </span></td>" : "") . ($p < 100 ? "<td style=\"background-color: $clrempty;\" width=\"" . (100 - $p) . "%\"><span style=\"font-size: 4pt;\"> </span></td>":"") . "</tr></table>"; -} +function poll_form(&$node, &$help, &$error) { + $admin = user_access("administer nodes"); -function poll_view($node, $main = 0, $block = 0) { - global $theme, $op, $user, $chid; + $_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)); + $_active = array(0 => "Closed", 1 => "Active"); - $pollop = $op; + $node->choices = $node->choices ? $node->choices : max(2, count($node->choices) ? count($node->choices) : 5); - if (($node->active) && (!field_get($node->voters, $user->name))) $voting = 1; - if ((!$voting) && ($pollop != "View")) $pollop = "View"; + 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++; + } - switch ($pollop) { - case "Vote": - if (($node->active) && (!field_get($node->voters, $user->name))) { - $result = db_query("UPDATE poll_choices SET chvotes=chvotes+1 WHERE nid='" . $node->nid . "' && chid='" . check_input($chid) . "'"); - if (($result) && ($user)) { - $new = node_get_array(array("nid" => $node->nid)); - $new[voters] = field_set($node->voters, $user->name, 1); - node_save($new, array(voters)); - $node = node_get_object(array("nid" => $node->nid)); - } + if ($node->chvotes[$i] < 0) { + $error["chvotes][$i"] = "<span style=\"color: red;\">" . t("Negative values are not allowed.") . "</span>"; } + } - case "View": - $node = poll_get_choices_obj($node); + if ($actualchoices < 2) { + $error["choice][0"] = "<span style=\"color: red;\">" . t("You must fill in at least two choices.") . "</span>"; + } + } + else { + $help = variable_get("poll_help", ""); + } - $title = "<b>" . check_output($node->title) . "</b>"; - $footer = "<small>(" . format_plural($node->totalvotes ? $node->totalvotes : 0, "vote", "votes") . ")</small>"; + for ($c = 2; $c <= 20; $c++) { + $opts[$c] = $c; + } + $output .= form_select(t("Number of choices"), "choices", $node->choices, $opts, t("This item only specifies the number of boxes in this form, but it doesn't have to equal the actual amount of options: you can leave the extra boxes empty.")); + $output .= form_submit(t("Preview")) . "<br><br><br>"; - $node->totalvotes = max(1, $node->totalvotes); + for ($a = 0; $a < $node->choices; $a++) { + $output .= form_textfield(t("Choice"). " " . ($a + 1), "choice][$a", $node->choice[$a], 50, 127, $error["choice][$a"]); + if ($admin) { + $output .= form_textfield(strtr(t("Votes for choice %n"), array("%n" => ($a + 1))), "chvotes][$a", $node->chvotes[$a] ? $node->chvotes[$a] : 0, 7, 7, $error["chvotes][$a"]); + } + } - foreach ($node->choice as $key => $value) { - if ($value) { - if ($block) { - $output .= (!$block ? "<br>" : "") . check_output($value) . "<br><table width=\"90%\" align=\"center\" cellspacing=\"1\" cellpadding=\"0\"><tr><td width=\"70%\" valign=\"middle\" align=\"left\">" . poll_graph($node->chvotes[$key] / $node->maxvotes, $theme->pollfill, $theme->pollempty) . "</td><td align=\"right\">" . round(($node->chvotes[$key] / $node->totalvotes) * 100) . "%</td></tr></table>"; - } else { - $output .= "<table cellspacing=\"1\" cellpadding=\"0\" width=\"100%\"><tr><td width=\"20%\" valign=\"middle\" align=\"left\">" . check_output($value) . "</td><td width=\"70%\">" . poll_graph($node->chvotes[$key] / $node->maxvotes, $theme->pollfill, $theme->pollempty) . "</td><td width=\"10%\" align=\"right\">" . round(($node->chvotes[$key] / $node->totalvotes) * 100) . "%</td></tr></table>"; - } - } - } - $output .= "<div align=\"center\">" . $footer . "</div>"; - break; + if ($admin) { + $output .= form_select(t("Poll status"), "active", isset($node->active) ? $node->active : 1, $_active); + } - default: - $node = poll_get_choices_obj($node); + $output .= form_select(t("Poll duration"), "runtime", $node->runtime ? $node->runtime : 0, $_duration, t("After this period, the poll will automatically be closed.")); - $title = "<b>" . check_output($node->title) . "</b>"; - $footer = "<small>(" . format_plural($node->totalvotes ? $node->totalvotes : 0, "vote", "votes") . ")</small>"; + return $output; +} - $node->totalvotes = max(1, $node->totalvotes); +function poll_help() { + ?> + <?php +} - $output .= "<table align=\"center\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\"><tr><td align=\"left\">"; - foreach ($node->choice as $key => $value) { - if ($value) { - $output .= "<input type=\"radio\" name=\"chid\" value=\"" . $node->chid[$key] . "\"> " . check_output($value) . "<br>"; - } - } - if ($block) { - $output .= "</td></tr></table><br><div align=\"center\">" . form_submit("Vote") . "<br>" . $footer . "</div>"; - } else { - $output .= "</td><td> </td><td align=\"center\"><br><div align=\"center\">" . form_submit("Vote") . "<br>" . $footer . "</div></td></tr></table>"; - } - $output = form($output); - } +function poll_insert($node) { + if (!user_access("administer nodes")) { + // Make sure all votes are 0 initially + for ($i = 0; $i < count($node->chvotes); $i++) + $node->chvotes[$i] = 0; + $node->active = 1; + } + db_query("INSERT INTO poll (nid, runtime, voters, active) VALUES ('$node->nid', '$node->runtime', '', '$node->active')"); + + for ($i = 0; $i < $node->choices; $i++) { + $choice->chtext = filter($node->choice[$i]); + $choice->chvotes = (int)$node->chvotes[$i]; + $choice->chorder = $i; - if (!$block) { - $node->body = $output; - $theme->node($node, $main); - } - return array("title" => $title, "content" => $output); + if ($choice->chtext != "") { + db_query("INSERT INTO poll_choices (nid, chtext, chvotes, chorder) VALUES ('$node->nid', '$choice->chtext', '$choice->chvotes', '$choice->chorder')"); + } + } } -function poll_form($edit = array(), $nocheck = 0) { - global $user; +function poll_link($type) { + if ($type == "menu.create" && user_access("post content")) { + $links[] = "<a href=\"module.php?mod=node&op=add&type=poll\" title=\"". t("Add a new poll.") ."\">". t("create poll") ."</a>"; + } - $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)); - $active = array(0 => "Closed", 1 => "Active"); + return $links ? $links : array(); +} - $admin = ($edit[nid] && user_access("administer nodes")) ? 1 : 0; +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'")); + + $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; + } + return $poll; +} - if ($admin && !is_array($edit[choices])) $edit = poll_get_choices_array($edit); +function poll_node($field) { + $info["name"] = t("poll"); + $info["description"] = t("A poll is a multiple-choice question which visitors can vote on."); - // Mini-form for number of choiceboxes - $choices = $edit[choices] ? $edit[choices] : max(2, count($edit[choices]) ? count($edit[choices]) : 5); - for ($c = 2; $c <= 20; $c++) $opts[$c]=$c; - $form .= form_select(t("Number of choices"), "choices", $choices, $opts, t("This box only specifies the number of boxes in this form, it doesn't have to equal the actual amount of choices in the poll.")); - $form .= form_submit(t("Preview")) . "<br><br><br>"; + return $info[$field]; +} - // Main form - $form .= form_item(t("Your name"), ($edit[name] ? $edit[name] : ($user->name ? $user->name : variable_get(anonymous, "Anonymous")))); - $form .= form_hidden("name", $edit[name]); - $form .= form_textfield(t("Question"), "title", $edit[title], 50, 127); +function poll_perm() { + return array("vote on polls"); +} - for ($a = 0; $a < $choices; $a++) { - $form .= form_textfield(t("Choice"). " " . ($a + 1), "choice][$a", $edit[choice][$a], 50, 127); - if ($admin) $form .= form_textfield(strtr(t("Votes for choice %n"), array("%n" => ($a + 1))), "chvotes][$a", $edit[chvotes][$a] ? $edit[chvotes][$a] : 0, 7, 7); +function poll_save($op, $node) { + if ($op == "approve") { + return array("status" => 1, "promote" => 1); } - $form .= form_select(t("Poll duration"), "runtime", $edit[runtime] ? $edit[runtime] : t("1 week"), $duration, t("After this period, the poll will automatically be closed.")); - if ($admin) $form .= form_select(t("Poll status"), "active", $edit[active], $active); - $form .= node_attributes_edit("poll", $edit); - - // hidden fields: - if ($edit[nid] > 0) { - $form .= form_hidden("nid", $edit[nid]); + 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)); + } } - if ($nocheck) { - $form .= form_submit(t("Preview")); - } - else if (!$edit[title]) { - $form .= "<FONT COLOR=\"red\">". t("Warning: you did not supply a question.") ."</FONT><P>\n"; - $form .= form_submit(t("Preview")); - } - else if ((!$edit[choice][0]) && (!$edit[choice][1])) { - $form .= "<FONT COLOR=\"red\">". t("Warning: you must supply at least 2 choices.") ."</FONT><P>\n"; - $form .= form_submit(t("Preview")); - } - else { - $form .= form_submit(t("Preview")); - $form .= form_submit(t("Submit")); + if ($op == "decline") { + return array("status" => 0, "promote" => 0); } - return form($form); + if ($op == "update") { + return array("runtime", "active", "choice", "choices", "chvotes"); + } } -function poll_save($edit) { - global $status, $user; +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; +} - if (!$edit[nid]) { - $nid = node_save($edit, array(active => 1, attributes => node_attributes_save("poll", $edit), author => $user->uid, comment => variable_get("poll_comment", 0), moderate => variable_get("poll_moderate", ""), promote => variable_get("poll_promote", 0), runtime, score => 0, status => variable_get("poll_status", $status[queued]), timestamp => time(), title, type => "poll", votes => 0, voters => "")); +function poll_view(&$node, $main = 0, $block = 0) { + global $theme, $user; + + /* When a poll is displayed twice on the same page (e.g. on the front page and in the side bar) + we only want to vote on one of them. We keep count using $pollid */ + global $pollidcount, $pollvote, $pollid, $REMOTE_ADDR, $REQUEST_URI; + $pollidcount++; + + // Only accept votes on specific cases to prevent double voting + $allowvotes = false; + 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)) { + $allowvotes = $node->active; + } + } + + if (($pollid == $pollidcount) && isset($pollvote) && ($allowvotes)) { + // The user has submitted a valid vote + if (!empty($node->choice[$pollvote])) { + $node->voters = $node->voters ? ($node->voters . " " . $id) : $id; + 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'"); + $allowvotes = false; + $node->chvotes[$pollvote]++; + } } - else if (user_access("administer nodes")) { - $nid = node_save($edit, array(active, attributes => node_attributes_save("poll", $edit), runtime, title, type => "poll")); - db_query("DELETE FROM poll_choices WHERE nid='" . $nid . "'"); + + if ($allowvotes) { + // Display the vote form + $url = $REQUEST_URI . (strstr($REQUEST_URI, "?") ? "&" : "?") . "pollid=" . $pollidcount; + $output .= "<form action=\"$url\" method=\"post\">"; + $output .= "<table border=\"0\" align=\"center\"><tr><td>"; + + foreach ($node->choice as $key => $value) { + if ($value != "") { + $output .= "<input type=\"radio\" name=\"pollvote\" value=\"$key\" /> $value<br />"; + } + } + 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\"> " . form_submit(t("Vote")) . "</div></td></tr></table>"; + } + $output .= "</form>"; } - if ($nid) { - foreach ($edit[choice] as $key => $value) { - if ($value) { - $v[] = "('" . $nid . "', '" . check_input($value) . "', '". check_input($edit[chvotes][$key]) ."', '". check_input($key) ."')"; + else { + // Display the results + + // 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 + $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 ? " (" . $node->chvotes[$key] . " 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%\"> </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%\"> </td></tr></table>"; + } + else { + $output .= "<table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" width=\"95%\" align=\"center\"><tr><td class=\"pollfg\" width=\"" . $width . "%\"> </td><td class=\"pollbg\" width=\"" . (100 - $width) . "%\"> </td></tr></table>"; + } } } - db_query("INSERT INTO poll_choices (nid, chtext, chvotes, chorder) VALUES " . implode(",", $v)); + $output .= "<br><div align=\"center\">Total votes: " . $votestotal . "</div>"; } -} + // Force the output on both the mainpage and elsewhere + $node->body = $output; + $node->teaser = $output; -function poll_block() { - global $status; - $result = _node_get(array("type" => "poll")); - while ($poll = db_fetch_object($result)) { - if (($poll->active) && ($poll->status == $status[posted])) { - $content = poll_view($poll, 0, 1); - $output = "<b>" . $content[title] . "</b><br>" . $content[content] . "<br><div align=\"center\">[ <a href=\"node.php?id=" . $poll->nid . "\">" . t("read more") . "</a> ]</div>"; - break; - } + // We also use poll_view() for the side-block + if ($block == 0) { + $theme->node($node, $main); } - $blocks[0][subject] = "Latest poll"; - $blocks[0][content] = $output ? $output : "No active polls."; - $blocks[0][info] = "Most recent poll"; - $blocks[0][link] = "index.php"; - return $blocks; } +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]; + $choice->chorder = $i; -function poll_user() { - global $edit, $op, $theme, $user; - - switch($op) { - case t("Refresh"): - $refresh = 1; - case t("Preview"): - $theme->box(t("Submit"), poll_form($edit, $refresh)); - break; - case t("Submit"): - poll_save($edit); - $theme->box(t("Submit"), t("Thank you for your submission.")); - break; - default: - $theme->box(t("Submit"), poll_form()); + if ($choice->chtext != "") { + db_query("INSERT INTO poll_choices (nid, chtext, chvotes, chorder) VALUES ('$node->nid', '$choice->chtext', '$choice->chvotes', '$choice->chorder')"); + } } } -*/ + ?> diff --git a/modules/poll/poll.module b/modules/poll/poll.module index 13a20573ee9f..a4fdafac4e46 100644 --- a/modules/poll/poll.module +++ b/modules/poll/poll.module @@ -1,288 +1,294 @@ <?php // $Id$ -// description: -// A multiple choice Poll allows you to submit a question to other users to which anyone can answer. To be published on Tipic, the Poll has to be approved by the moderators. - -/* - -class Poll { - function Poll($poll) { - $this = new Node($poll); - $this->runtime = $poll[runtime]; - $this->active = $poll[active]; - $this->voters = $poll[voters]; - $this->choice = $poll[choice]; - $this->chvotes = $poll[chvotes]; - $this->chid = $poll[chid]; +function poll_access($op, $node) { + if ($op == "view") { + return $node->status; } -} - -function poll_cron() { - $result = _node_get(array("type" => "poll")); - while ($poll = db_fetch_array($result)) { - if (($poll[active]) && ($poll[runtime])) { - if (($poll[timestamp] + $poll[runtime]) < time()) { - $poll[active] = 0; - node_save($poll, array(active)); - } - } - } -} -function poll_delete($node) { - if ($node->nid) - { - db_query("DELETE FROM poll_choices WHERE nid='" . $node->nid . "'"); + if ($op == "create") { + return 1; } } -function poll_get_choices_array($poll) { - if (!is_array($poll[choice])) { - $result = db_query("SELECT * FROM poll_choices WHERE nid='" . $poll[nid] . "'"); - if ($result) { - while ($choices = db_fetch_array($result)) { - $m = max($m, $choices[chvotes]); - $t += $choices[chvotes]; - $poll[choice][$choices[chorder]] = $choices[chtext]; - $poll[chvotes][$choices[chorder]] = $choices[chvotes]; - $poll[chid][$choices[chorder]] = $choices[chid]; - } - if ($poll[choice]) ksort($poll[choice]); - } - } - $poll[maxvotes] = max(1, $m); - $poll[totalvotes] = $t; - return $poll; -} - -function poll_get_choices_obj($poll) { - if (!is_array($poll->choice)) { - $result = db_query("SELECT * FROM poll_choices WHERE nid='" . $poll->nid . "'"); - if ($result) { - while ($choices = db_fetch_array($result)) { - $m = max($m,$choices[chvotes]); - $t += $choices[chvotes]; - - $poll->choice[$choices[chorder]] = $choices[chtext]; - $poll->chvotes[$choices[chorder]] = $choices[chvotes]; - $poll->chid[$choices[chorder]] = $choices[chid]; - } - if ($poll->choice) ksort($poll->choice); +function poll_block() { + $timestamp = db_result(db_query("SELECT MAX(created) FROM node WHERE type='poll' AND status='1'")); + if ($timestamp) { + $poll = node_load(array("type" => "poll", "created" => $timestamp, "status" => "1")); + if ($poll->nid) { + // Poll_view dumps the output into $poll->body + poll_view($poll, 0, 1); } } - $poll->maxvotes = max(1, $m); - $poll->totalvotes = $t; - return $poll; -} - -function poll_status() { - return array(dumped, queued, posted); + $blocks[0][subject] = "Latest poll"; + $blocks[0][content] = $poll->body; + $blocks[0][info] = "Most recent poll"; + return $blocks; } -function poll_search($keys) { - global $PHP_SELF, $status; - $result = db_query("SELECT n.*, p.* FROM poll p LEFT JOIN node n ON n.nid = p.nid AND n.lid = p.lid WHERE n.status = '$status[posted]' AND (n.title LIKE '%$keys%') LIMIT 20"); - while ($poll = db_fetch_object($result)) { - $find[$i++] = array("title" => check_output($poll->title), "link" => (strstr($PHP_SELF, "admin.php") ? "admin.php?mod=poll&op=edit&id=$poll->nid" : "node.php?id=$poll->nid"), "user" => $poll->name, "date" => $poll->timestamp); - } - return $find; +function poll_cron() { + // Close polls that have exceeded their allowed runtime + $result = db_query("SELECT p.lid 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'"); + while ($poll = db_fetch_object($result)) { + db_query("UPDATE poll SET active='0' WHERE lid='$poll->lid'"); + } } -function poll_help() { - ?> - <p>Poll.module allows you to run simple multiple choice polls on your site. After creating the necessary categories, you can add a poll through this administration interface.</p> - <p>The poll.module also has a block to show the latest poll. If you combine it with a poll category that doesn't promote to the mainpage, you'll get a side-block only poll system.</p> - <p>If you want to set a limit on the duration of a poll, use the <i>Poll Duration</i> setting. After the specified interval, the poll will automatically be closed, preventing anymore voting.</p> - <?php +function poll_delete($node) { + db_query("DELETE FROM poll WHERE nid='$node->nid'"); + db_query("DELETE FROM poll_choices WHERE nid='$node->nid'"); } -function poll_graph($val) { - global $theme; - $clrfill = $theme->foreground ? $theme->foreground : "#000000"; - $clrempty = $theme->background ? $theme->background : "#ffffff"; - $p = round($val * 100); - return "<table width=\"100%\" cellspacing=\"0\" cellpadding=\"0\"><tr>" . ($p ? "<td width=\"" . $p . "%\" style=\"background-color: $clrfill;\"><span style=\"font-size: 4pt;\"> </span></td>" : "") . ($p < 100 ? "<td style=\"background-color: $clrempty;\" width=\"" . (100 - $p) . "%\"><span style=\"font-size: 4pt;\"> </span></td>":"") . "</tr></table>"; -} +function poll_form(&$node, &$help, &$error) { + $admin = user_access("administer nodes"); -function poll_view($node, $main = 0, $block = 0) { - global $theme, $op, $user, $chid; + $_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)); + $_active = array(0 => "Closed", 1 => "Active"); - $pollop = $op; + $node->choices = $node->choices ? $node->choices : max(2, count($node->choices) ? count($node->choices) : 5); - if (($node->active) && (!field_get($node->voters, $user->name))) $voting = 1; - if ((!$voting) && ($pollop != "View")) $pollop = "View"; + 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++; + } - switch ($pollop) { - case "Vote": - if (($node->active) && (!field_get($node->voters, $user->name))) { - $result = db_query("UPDATE poll_choices SET chvotes=chvotes+1 WHERE nid='" . $node->nid . "' && chid='" . check_input($chid) . "'"); - if (($result) && ($user)) { - $new = node_get_array(array("nid" => $node->nid)); - $new[voters] = field_set($node->voters, $user->name, 1); - node_save($new, array(voters)); - $node = node_get_object(array("nid" => $node->nid)); - } + if ($node->chvotes[$i] < 0) { + $error["chvotes][$i"] = "<span style=\"color: red;\">" . t("Negative values are not allowed.") . "</span>"; } + } - case "View": - $node = poll_get_choices_obj($node); + if ($actualchoices < 2) { + $error["choice][0"] = "<span style=\"color: red;\">" . t("You must fill in at least two choices.") . "</span>"; + } + } + else { + $help = variable_get("poll_help", ""); + } - $title = "<b>" . check_output($node->title) . "</b>"; - $footer = "<small>(" . format_plural($node->totalvotes ? $node->totalvotes : 0, "vote", "votes") . ")</small>"; + for ($c = 2; $c <= 20; $c++) { + $opts[$c] = $c; + } + $output .= form_select(t("Number of choices"), "choices", $node->choices, $opts, t("This item only specifies the number of boxes in this form, but it doesn't have to equal the actual amount of options: you can leave the extra boxes empty.")); + $output .= form_submit(t("Preview")) . "<br><br><br>"; - $node->totalvotes = max(1, $node->totalvotes); + for ($a = 0; $a < $node->choices; $a++) { + $output .= form_textfield(t("Choice"). " " . ($a + 1), "choice][$a", $node->choice[$a], 50, 127, $error["choice][$a"]); + if ($admin) { + $output .= form_textfield(strtr(t("Votes for choice %n"), array("%n" => ($a + 1))), "chvotes][$a", $node->chvotes[$a] ? $node->chvotes[$a] : 0, 7, 7, $error["chvotes][$a"]); + } + } - foreach ($node->choice as $key => $value) { - if ($value) { - if ($block) { - $output .= (!$block ? "<br>" : "") . check_output($value) . "<br><table width=\"90%\" align=\"center\" cellspacing=\"1\" cellpadding=\"0\"><tr><td width=\"70%\" valign=\"middle\" align=\"left\">" . poll_graph($node->chvotes[$key] / $node->maxvotes, $theme->pollfill, $theme->pollempty) . "</td><td align=\"right\">" . round(($node->chvotes[$key] / $node->totalvotes) * 100) . "%</td></tr></table>"; - } else { - $output .= "<table cellspacing=\"1\" cellpadding=\"0\" width=\"100%\"><tr><td width=\"20%\" valign=\"middle\" align=\"left\">" . check_output($value) . "</td><td width=\"70%\">" . poll_graph($node->chvotes[$key] / $node->maxvotes, $theme->pollfill, $theme->pollempty) . "</td><td width=\"10%\" align=\"right\">" . round(($node->chvotes[$key] / $node->totalvotes) * 100) . "%</td></tr></table>"; - } - } - } - $output .= "<div align=\"center\">" . $footer . "</div>"; - break; + if ($admin) { + $output .= form_select(t("Poll status"), "active", isset($node->active) ? $node->active : 1, $_active); + } - default: - $node = poll_get_choices_obj($node); + $output .= form_select(t("Poll duration"), "runtime", $node->runtime ? $node->runtime : 0, $_duration, t("After this period, the poll will automatically be closed.")); - $title = "<b>" . check_output($node->title) . "</b>"; - $footer = "<small>(" . format_plural($node->totalvotes ? $node->totalvotes : 0, "vote", "votes") . ")</small>"; + return $output; +} - $node->totalvotes = max(1, $node->totalvotes); +function poll_help() { + ?> + <?php +} - $output .= "<table align=\"center\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\"><tr><td align=\"left\">"; - foreach ($node->choice as $key => $value) { - if ($value) { - $output .= "<input type=\"radio\" name=\"chid\" value=\"" . $node->chid[$key] . "\"> " . check_output($value) . "<br>"; - } - } - if ($block) { - $output .= "</td></tr></table><br><div align=\"center\">" . form_submit("Vote") . "<br>" . $footer . "</div>"; - } else { - $output .= "</td><td> </td><td align=\"center\"><br><div align=\"center\">" . form_submit("Vote") . "<br>" . $footer . "</div></td></tr></table>"; - } - $output = form($output); - } +function poll_insert($node) { + if (!user_access("administer nodes")) { + // Make sure all votes are 0 initially + for ($i = 0; $i < count($node->chvotes); $i++) + $node->chvotes[$i] = 0; + $node->active = 1; + } + db_query("INSERT INTO poll (nid, runtime, voters, active) VALUES ('$node->nid', '$node->runtime', '', '$node->active')"); + + for ($i = 0; $i < $node->choices; $i++) { + $choice->chtext = filter($node->choice[$i]); + $choice->chvotes = (int)$node->chvotes[$i]; + $choice->chorder = $i; - if (!$block) { - $node->body = $output; - $theme->node($node, $main); - } - return array("title" => $title, "content" => $output); + if ($choice->chtext != "") { + db_query("INSERT INTO poll_choices (nid, chtext, chvotes, chorder) VALUES ('$node->nid', '$choice->chtext', '$choice->chvotes', '$choice->chorder')"); + } + } } -function poll_form($edit = array(), $nocheck = 0) { - global $user; +function poll_link($type) { + if ($type == "menu.create" && user_access("post content")) { + $links[] = "<a href=\"module.php?mod=node&op=add&type=poll\" title=\"". t("Add a new poll.") ."\">". t("create poll") ."</a>"; + } - $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)); - $active = array(0 => "Closed", 1 => "Active"); + return $links ? $links : array(); +} - $admin = ($edit[nid] && user_access("administer nodes")) ? 1 : 0; +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'")); + + $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; + } + return $poll; +} - if ($admin && !is_array($edit[choices])) $edit = poll_get_choices_array($edit); +function poll_node($field) { + $info["name"] = t("poll"); + $info["description"] = t("A poll is a multiple-choice question which visitors can vote on."); - // Mini-form for number of choiceboxes - $choices = $edit[choices] ? $edit[choices] : max(2, count($edit[choices]) ? count($edit[choices]) : 5); - for ($c = 2; $c <= 20; $c++) $opts[$c]=$c; - $form .= form_select(t("Number of choices"), "choices", $choices, $opts, t("This box only specifies the number of boxes in this form, it doesn't have to equal the actual amount of choices in the poll.")); - $form .= form_submit(t("Preview")) . "<br><br><br>"; + return $info[$field]; +} - // Main form - $form .= form_item(t("Your name"), ($edit[name] ? $edit[name] : ($user->name ? $user->name : variable_get(anonymous, "Anonymous")))); - $form .= form_hidden("name", $edit[name]); - $form .= form_textfield(t("Question"), "title", $edit[title], 50, 127); +function poll_perm() { + return array("vote on polls"); +} - for ($a = 0; $a < $choices; $a++) { - $form .= form_textfield(t("Choice"). " " . ($a + 1), "choice][$a", $edit[choice][$a], 50, 127); - if ($admin) $form .= form_textfield(strtr(t("Votes for choice %n"), array("%n" => ($a + 1))), "chvotes][$a", $edit[chvotes][$a] ? $edit[chvotes][$a] : 0, 7, 7); +function poll_save($op, $node) { + if ($op == "approve") { + return array("status" => 1, "promote" => 1); } - $form .= form_select(t("Poll duration"), "runtime", $edit[runtime] ? $edit[runtime] : t("1 week"), $duration, t("After this period, the poll will automatically be closed.")); - if ($admin) $form .= form_select(t("Poll status"), "active", $edit[active], $active); - $form .= node_attributes_edit("poll", $edit); - - // hidden fields: - if ($edit[nid] > 0) { - $form .= form_hidden("nid", $edit[nid]); + 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)); + } } - if ($nocheck) { - $form .= form_submit(t("Preview")); - } - else if (!$edit[title]) { - $form .= "<FONT COLOR=\"red\">". t("Warning: you did not supply a question.") ."</FONT><P>\n"; - $form .= form_submit(t("Preview")); - } - else if ((!$edit[choice][0]) && (!$edit[choice][1])) { - $form .= "<FONT COLOR=\"red\">". t("Warning: you must supply at least 2 choices.") ."</FONT><P>\n"; - $form .= form_submit(t("Preview")); - } - else { - $form .= form_submit(t("Preview")); - $form .= form_submit(t("Submit")); + if ($op == "decline") { + return array("status" => 0, "promote" => 0); } - return form($form); + if ($op == "update") { + return array("runtime", "active", "choice", "choices", "chvotes"); + } } -function poll_save($edit) { - global $status, $user; +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; +} - if (!$edit[nid]) { - $nid = node_save($edit, array(active => 1, attributes => node_attributes_save("poll", $edit), author => $user->uid, comment => variable_get("poll_comment", 0), moderate => variable_get("poll_moderate", ""), promote => variable_get("poll_promote", 0), runtime, score => 0, status => variable_get("poll_status", $status[queued]), timestamp => time(), title, type => "poll", votes => 0, voters => "")); +function poll_view(&$node, $main = 0, $block = 0) { + global $theme, $user; + + /* When a poll is displayed twice on the same page (e.g. on the front page and in the side bar) + we only want to vote on one of them. We keep count using $pollid */ + global $pollidcount, $pollvote, $pollid, $REMOTE_ADDR, $REQUEST_URI; + $pollidcount++; + + // Only accept votes on specific cases to prevent double voting + $allowvotes = false; + 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)) { + $allowvotes = $node->active; + } + } + + if (($pollid == $pollidcount) && isset($pollvote) && ($allowvotes)) { + // The user has submitted a valid vote + if (!empty($node->choice[$pollvote])) { + $node->voters = $node->voters ? ($node->voters . " " . $id) : $id; + 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'"); + $allowvotes = false; + $node->chvotes[$pollvote]++; + } } - else if (user_access("administer nodes")) { - $nid = node_save($edit, array(active, attributes => node_attributes_save("poll", $edit), runtime, title, type => "poll")); - db_query("DELETE FROM poll_choices WHERE nid='" . $nid . "'"); + + if ($allowvotes) { + // Display the vote form + $url = $REQUEST_URI . (strstr($REQUEST_URI, "?") ? "&" : "?") . "pollid=" . $pollidcount; + $output .= "<form action=\"$url\" method=\"post\">"; + $output .= "<table border=\"0\" align=\"center\"><tr><td>"; + + foreach ($node->choice as $key => $value) { + if ($value != "") { + $output .= "<input type=\"radio\" name=\"pollvote\" value=\"$key\" /> $value<br />"; + } + } + 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\"> " . form_submit(t("Vote")) . "</div></td></tr></table>"; + } + $output .= "</form>"; } - if ($nid) { - foreach ($edit[choice] as $key => $value) { - if ($value) { - $v[] = "('" . $nid . "', '" . check_input($value) . "', '". check_input($edit[chvotes][$key]) ."', '". check_input($key) ."')"; + else { + // Display the results + + // 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 + $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 ? " (" . $node->chvotes[$key] . " 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%\"> </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%\"> </td></tr></table>"; + } + else { + $output .= "<table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" width=\"95%\" align=\"center\"><tr><td class=\"pollfg\" width=\"" . $width . "%\"> </td><td class=\"pollbg\" width=\"" . (100 - $width) . "%\"> </td></tr></table>"; + } } } - db_query("INSERT INTO poll_choices (nid, chtext, chvotes, chorder) VALUES " . implode(",", $v)); + $output .= "<br><div align=\"center\">Total votes: " . $votestotal . "</div>"; } -} + // Force the output on both the mainpage and elsewhere + $node->body = $output; + $node->teaser = $output; -function poll_block() { - global $status; - $result = _node_get(array("type" => "poll")); - while ($poll = db_fetch_object($result)) { - if (($poll->active) && ($poll->status == $status[posted])) { - $content = poll_view($poll, 0, 1); - $output = "<b>" . $content[title] . "</b><br>" . $content[content] . "<br><div align=\"center\">[ <a href=\"node.php?id=" . $poll->nid . "\">" . t("read more") . "</a> ]</div>"; - break; - } + // We also use poll_view() for the side-block + if ($block == 0) { + $theme->node($node, $main); } - $blocks[0][subject] = "Latest poll"; - $blocks[0][content] = $output ? $output : "No active polls."; - $blocks[0][info] = "Most recent poll"; - $blocks[0][link] = "index.php"; - return $blocks; } +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]; + $choice->chorder = $i; -function poll_user() { - global $edit, $op, $theme, $user; - - switch($op) { - case t("Refresh"): - $refresh = 1; - case t("Preview"): - $theme->box(t("Submit"), poll_form($edit, $refresh)); - break; - case t("Submit"): - poll_save($edit); - $theme->box(t("Submit"), t("Thank you for your submission.")); - break; - default: - $theme->box(t("Submit"), poll_form()); + if ($choice->chtext != "") { + db_query("INSERT INTO poll_choices (nid, chtext, chvotes, chorder) VALUES ('$node->nid', '$choice->chtext', '$choice->chvotes', '$choice->chorder')"); + } } } -*/ + ?> -- GitLab