]> littlesong.place Git - poll.git/commitdiff
Functional
authorChris Fulljames <christianfulljames@gmail.com>
Sun, 24 May 2026 16:13:16 +0000 (12:13 -0400)
committerChris Fulljames <christianfulljames@gmail.com>
Sun, 24 May 2026 16:13:16 +0000 (12:13 -0400)
index.php [new file with mode: 0644]
schema.sql [new file with mode: 0644]

diff --git a/index.php b/index.php
new file mode 100644 (file)
index 0000000..d63c944
--- /dev/null
+++ b/index.php
@@ -0,0 +1,294 @@
+<?php
+$GLOBALS['db'] = new PDO("sqlite:database.db");
+
+$qid = $_GET['qid'] ?? NULL;
+$view = $_GET['view'] ?? 'vote';
+
+function query_db($query, $params=null)
+{
+    $statement = $GLOBALS['db']->prepare($query);
+    $statement->setFetchMode(PDO::FETCH_ASSOC);
+    $result = $statement->execute($params);
+    #$statement->debugDumpParams();
+    if (!$result)
+    {
+        error(500, "Uh-oh, something went wrong. Sorry!");
+    }
+    return $statement;
+}
+
+function get_title($qid)
+{
+    $q = query_db(
+        "SELECT title FROM questions WHERE qid = ?",
+        [ $qid ])->fetch();
+
+    return $q['title'];
+}
+
+function get_options($qid)
+{
+    $q = query_db(
+        "SELECT * FROM options WHERE qid = ?",
+        [ $qid ])->fetchAll();
+
+    $opts = array_map(fn($row) => $row['name'], $q);
+    shuffle($opts);
+    return $opts;
+}
+
+function get_results($qid)
+{
+    $q = query_db(
+        "SELECT * FROM options WHERE qid = ?",
+        [ $qid ])->fetchAll();
+
+    $results = [];
+    foreach ($q as $opt)
+    {
+        // Get all points for option
+        $qq = query_db(
+            "SELECT SUM(points) FROM responses WHERE oid = ?",
+            [ $opt['oid'] ])->fetch();
+        $results[$opt['name']] = $qq['SUM(points)'] ?? 0;
+    }
+    asort($results);
+    return array_reverse($results);
+}
+
+function get_num_responses($qid)
+{
+    $q = query_db(
+        "SELECT COUNT(*) FROM responses INNER JOIN options USING (oid) WHERE qid = ?",
+        [ $qid ])->fetch();
+    return $q["COUNT(*)"];
+}
+
+function get_end_date($qid)
+{
+    $q = query_db(
+        "SELECT enddate FROM questions WHERE qid = ?",
+        [ $qid ])->fetch();
+    return $q['enddate'];
+}
+
+function add_vote($qid, $opt, $points)
+{
+    $q = query_db(
+        "SELECT oid FROM options WHERE qid = ? AND name = ?",
+        [$qid, $opt])->fetch();
+
+    if ($q)
+    {
+        $oid = $q['oid'];
+        query_db(
+            "INSERT INTO responses(oid, points) VALUES (?, ?)",
+            [$oid, $points])->fetch();
+    }
+    else
+    {
+        error_log($opt);
+    }
+}
+
+function create_new_poll($title, $options, $enddate)
+{
+    // Create new question
+    $q = query_db(
+        "INSERT INTO questions(title, enddate) VALUES (?, ?) RETURNING qid",
+        [$title, $enddate])->fetch();
+    $qid = $q['qid'];
+
+    // Create options (splitting input into lines)
+    foreach(preg_split("/((\r?\n)|(\r\n?))/", $options) as $o)
+    {
+        query_db(
+            "INSERT INTO options(qid, name) VALUES (?, ?)",
+            [$qid, $o])->fetch();
+    }
+
+    return $qid;
+}
+
+function poll_url($qid, $view="vote")
+{
+    return "?qid=$qid&view=$view";
+}
+
+// Creating new poll
+if (isset($_POST['title']) and isset($_POST['options']))
+{
+    $qid = create_new_poll($_POST['title'], $_POST['options'], $_POST['enddate'] ?? NULL);
+    // Redirect to poll
+    header("Location: ".poll_url($qid));
+    exit;
+}
+
+// Voting
+if (isset($_POST['qid']))
+{
+    foreach ($_POST as $key => $value)
+    {
+        $prefix =  "opt-";
+        if (str_starts_with($key, $prefix))
+        {
+            $opt = substr($key, strlen($prefix));
+            $opt = str_replace("_", " ", $opt);
+            add_vote($_POST['qid'], $opt, (int) $value);
+        }
+    }
+
+    // Redirect to results
+    header("Location: ".poll_url($qid, "results"));
+    exit;
+}
+
+// Background JSON check whether more people have voted
+if (isset($qid) && isset($_GET['count'])) {
+    // Check of more users have joined
+    header('Content-type: application/json');
+    if ($_GET['count'] != get_num_responses($qid)) echo '{"reload": true}';
+    else echo '{"reload": false}';
+    exit;
+}
+
+$ended = false;
+if (isset($qid)) {
+    $enddate = get_end_date($qid);
+    $nowdate = gmdate("Y-m-d\TH:i");
+    $ended = $nowdate > $enddate;
+}
+
+?>
+<!DOCTYPE html>
+<html>
+<head>
+    <title>Poll!</title>
+    <meta name="viewport" content="width=device-width, initial-scale=1">
+    <style>
+    body {
+        font-family: sans-serif;
+        max-width: 30em;
+        margin: 10px auto;
+        border: 1px solid #cca;
+        padding: 10px;
+        background-color: #ffd;
+        color: #434;
+    }
+    input, textarea {
+        margin: 5px;
+        font-family: sans-serif;
+        font-size: 1em;
+    }
+    input[type=submit] {
+        background: #fdf;
+        color: #434;
+        border: 0;
+        cursor: pointer;
+        border-radius: 5px;
+        padding: 5px;
+        font-weight: bold;
+    }
+    input[disabled] {
+        background: none;
+        border: none;
+        color: #434;
+    }
+    ul {
+        list-style-type: none;
+        padding: 0px;
+    }
+    li {
+        margin: 10px 0px;
+        line-height: 1em;
+    }
+    </style>
+
+    <?php if (isset($qid) && $view == "results"): ?>
+    <script>
+    async function reloadIfChanged() {
+        const url = "/?qid=<?= $qid ?>&count=<?= get_num_responses($qid) ?>";
+        const response = await fetch(url);
+        const result = await response.json();
+        if (result.reload) location.reload();
+        else setTimeout(reloadIfChanged, 10000);
+    }
+    </script>
+    <?php endif ?>
+
+</head>
+
+<?php if (isset($qid) && $view == "results"): # Periodically reload ?>
+<body onload="setTimeout('reloadIfChanged()', 10000)">
+<?php else: ?>
+<body>
+<?php endif ?>
+
+<?php #########################################################################
+# VOTE
+
+if (isset($qid) && $view == "vote" && !$ended): ?>
+
+    <h2><?= get_title($qid) ?></h2>
+<?php if (isset($enddate)): ?>
+    <p>Closes: <input type="datetime-local" value="<?= $enddate ?>" disabled> (UTC)</p>
+<?php endif ?>
+    <p>
+        The option with the most points wins!
+        Give your favorite option 5 points, second favorite 4 points, etc.
+        Or give them all 5 points if you really can't decide.
+    </p> 
+    <form method="post">
+        <input name="qid" type="hidden" value="<?= $qid ?>">
+        <ul>
+        <?php foreach (get_options($qid) as $opt): ?>
+            <li><input name="opt-<?= $opt ?>" type="number" min="0" max="5">
+                <b><?= $opt ?></b></li>
+        <?php endforeach ?>
+        </ul>
+        <input type="submit" value="Cast Vote!">
+    </form>
+    <br><a href="<?= poll_url($qid, "results") ?>">Show Results</a>
+    <br><br><a href="/">New Poll</a>
+
+<?php #########################################################################
+# RESULTS
+
+elseif (isset($qid)): ?>
+    <h2><?= get_title($qid) ?></h2>
+
+<?php if ($ended): ?>
+    <p>Poll has ended.</p>
+<?php endif ?>
+
+    <ul>
+    <?php foreach (get_results($qid) as $opt => $points): ?>
+        <li><b><?= $opt ?></b><br>
+            <!-- Point Count Bar -->
+            <small>
+                <?php for ($i = 0; $i < $points; $i ++): ?>/<?php endfor ?>
+            </small> (<?= $points ?>)
+        </li>
+    <?php endforeach ?>
+    </ul>
+    <br><a href="/">New Poll</a>
+
+<?php #########################################################################
+# NEW POLL
+
+else: ?>
+    <h2>New Poll</h2>
+    <form method="post">
+        <label>Question:<br>
+            <input type="text" name="title"></label><br>
+        <label>Options (one per line):<br>
+            <textarea rows="8" cols="30" name="options"></textarea></label><br>
+        <label>Closes on (optional) (UTC):<br>
+            <input type="datetime-local" name="enddate"></label><br>
+        <input type="submit" value="Create Poll">
+    </form>
+<?php endif ?>
+
+</body>
+</html>
+
diff --git a/schema.sql b/schema.sql
new file mode 100644 (file)
index 0000000..152a911
--- /dev/null
@@ -0,0 +1,25 @@
+DROP TABLE IF EXISTS questions;
+CREATE TABLE questions (
+    qid INTEGER NOT NULL PRIMARY KEY,
+    title STRING NOT NULL,
+    enddate STRING
+);
+
+DROP TABLE IF EXISTS options;
+CREATE TABLE options (
+    oid INTEGER NOT NULL PRIMARY KEY,
+    qid INTEGER NOT NULL,
+    name TEXT NOT NULL,
+
+    FOREIGN KEY(qid) REFERENCES questions(qid) ON DELETE CASCADE
+);
+
+DROP TABLE IF EXISTS responses;
+CREATE TABLE responses (
+    rid INTEGER NOT NULL PRIMARY KEY,
+    oid INTEGER NOT NULL,
+    points INTEGER NOT NULL,
+
+    FOREIGN KEY(oid) REFERENCES options(oid) ON DELETE CASCADE
+);
+