From 2f146ee57fb779693314802405d6024a6560a8dd Mon Sep 17 00:00:00 2001 From: deepend-tildeclub <58404188+deepend-tildeclub@users.noreply.github.com> Date: Fri, 24 Jan 2025 18:24:46 -0700 Subject: [PATCH 1/5] Create index.php --- polls/index.php | 201 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 201 insertions(+) create mode 100644 polls/index.php diff --git a/polls/index.php b/polls/index.php new file mode 100644 index 0000000..c961bac --- /dev/null +++ b/polls/index.php @@ -0,0 +1,201 @@ +prepare("SELECT * FROM poll_questions WHERE id = :id LIMIT 1"); + $stmt->bindValue(':id', $pollId, PDO::PARAM_INT); + $stmt->execute(); + return $stmt->fetch(PDO::FETCH_ASSOC); +} + +// Helper function: Fetch poll options (and their results) by poll ID +function getPollOptions($db, $pollId) { + $stmt = $db->prepare(" + SELECT + po.id AS option_id, + po.option_text, + IFNULL(pr.vote_count, 0) AS vote_count + FROM poll_options po + LEFT JOIN poll_results pr ON po.id = pr.option_id + WHERE po.question_id = :question_id + ORDER BY po.id ASC + "); + $stmt->bindValue(':question_id', $pollId, PDO::PARAM_INT); + $stmt->execute(); + return $stmt->fetchAll(PDO::FETCH_ASSOC); +} + +// If a vote is being cast +if (isset($_POST['vote']) && isset($_POST['option_id']) && isset($_POST['poll_id'])) { + $pollId = (int)$_POST['poll_id']; + $optionId = (int)$_POST['option_id']; + + // Ensure this user hasn't already voted on this poll in this session + if (!in_array($pollId, $_SESSION['voted_polls'], true)) { + // Update the vote count + $updateStmt = $db->prepare(" + UPDATE poll_results + SET vote_count = vote_count + 1 + WHERE question_id = :question_id + AND option_id = :option_id + "); + $updateStmt->bindValue(':question_id', $pollId, PDO::PARAM_INT); + $updateStmt->bindValue(':option_id', $optionId, PDO::PARAM_INT); + $updateStmt->execute(); + + // Mark the user as having voted + $_SESSION['voted_polls'][] = $pollId; + } + + // Redirect back to the same poll to show results + header("Location: index.php?poll_id=" . $pollId); + exit; +} + +// Check if a specific poll is requested +$pollId = isset($_GET['poll_id']) ? (int)$_GET['poll_id'] : null; +?> + + + + + Poll Application + + + + + +
+

Available Polls

+ query("SELECT id, question_text, created_at FROM poll_questions ORDER BY id DESC"); + $allPolls = $allPollsStmt->fetchAll(PDO::FETCH_ASSOC); + + if ($allPolls) { + foreach ($allPolls as $poll) { + ?> +
+ Question:
+ Created at:
+ View Poll +
+ +

No polls available.

+ +
+ +
+ Poll not found.

"; + } else { + $options = getPollOptions($db, $pollId); + $hasVoted = in_array($pollId, $_SESSION['voted_polls'], true); + ?> +

+ + +
+
+
    + +
  • + +
  • + +
+
+ + +
+ No options available for this poll.

"; + } + } else { + // Show the results if user has already voted + ?> +

Results

+
+ +

Total votes:

+
+ + Back to Poll List +
+ + + + From 002ea7dfe7358400637cc182e95669311a27a3c7 Mon Sep 17 00:00:00 2001 From: deepend-tildeclub <58404188+deepend-tildeclub@users.noreply.github.com> Date: Fri, 24 Jan 2025 18:25:42 -0700 Subject: [PATCH 2/5] Create api.php --- polls/api.php | 161 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 161 insertions(+) create mode 100644 polls/api.php diff --git a/polls/api.php b/polls/api.php new file mode 100644 index 0000000..f4a6b3e --- /dev/null +++ b/polls/api.php @@ -0,0 +1,161 @@ +query("SELECT id, question_text FROM poll_questions ORDER BY id DESC"); + $polls = $stmt->fetchAll(PDO::FETCH_ASSOC); + sendJson(['success' => true, 'polls' => $polls]); + } catch (Exception $e) { + sendJson(['success' => false, 'error' => $e->getMessage()], 500); + } + break; + + // -------------------------------------------------- + // 2) Get a single poll (question + options) + // -------------------------------------------------- + case 'get_poll': + $pollId = (int)($_GET['poll_id'] ?? 0); + if ($pollId <= 0) { + sendJson(['success' => false, 'error' => 'Invalid poll_id'], 400); + } + try { + // Fetch poll question + $stmt = $db->prepare("SELECT id, question_text FROM poll_questions WHERE id = :id"); + $stmt->execute([':id' => $pollId]); + $poll = $stmt->fetch(PDO::FETCH_ASSOC); + + if (!$poll) { + sendJson(['success' => false, 'error' => 'Poll not found'], 404); + } + + // Fetch options + $optionsStmt = $db->prepare(" + SELECT po.id AS option_id, po.option_text, + IFNULL(pr.vote_count, 0) AS vote_count + FROM poll_options po + LEFT JOIN poll_results pr ON po.id = pr.option_id + WHERE po.question_id = :question_id + ORDER BY po.id ASC + "); + $optionsStmt->execute([':question_id' => $pollId]); + $options = $optionsStmt->fetchAll(PDO::FETCH_ASSOC); + + sendJson([ + 'success' => true, + 'poll' => [ + 'id' => $poll['id'], + 'question_text' => $poll['question_text'], + 'options' => $options + ] + ]); + } catch (Exception $e) { + sendJson(['success' => false, 'error' => $e->getMessage()], 500); + } + break; + + // -------------------------------------------------- + // 3) Cast a vote + // Expects: poll_id, option_id, username + // -------------------------------------------------- + case 'vote': + // This can come from POST or GET. We'll assume POST for clarity. + $pollId = (int)($_POST['poll_id'] ?? 0); + $optionId = (int)($_POST['option_id'] ?? 0); + $username = trim($_POST['username'] ?? ''); + + if ($pollId <= 0 || $optionId <= 0 || empty($username)) { + sendJson(['success' => false, 'error' => 'Missing or invalid parameters'], 400); + } + + // Check if user already voted on this poll + try { + // 1) Ensure poll & option exist + $checkOption = $db->prepare(" + SELECT COUNT(*) + FROM poll_options + WHERE id = :option_id + AND question_id = :poll_id + "); + $checkOption->execute([ + ':option_id' => $optionId, + ':poll_id' => $pollId + ]); + if (!$checkOption->fetchColumn()) { + sendJson(['success' => false, 'error' => 'Option does not belong to poll or does not exist'], 400); + } + + // 2) Check if user already voted + $checkVote = $db->prepare(" + SELECT COUNT(*) + FROM user_votes + WHERE question_id = :poll_id + AND user_name = :username + "); + $checkVote->execute([ + ':poll_id' => $pollId, + ':username' => $username + ]); + if ($checkVote->fetchColumn() > 0) { + // Already voted + sendJson(['success' => false, 'error' => 'Already voted'], 403); + } + + // 3) Cast the vote (increment poll_results) + $updateStmt = $db->prepare(" + UPDATE poll_results + SET vote_count = vote_count + 1 + WHERE question_id = :poll_id + AND option_id = :option_id + "); + $updateStmt->execute([ + ':poll_id' => $pollId, + ':option_id' => $optionId + ]); + + // 4) Record the user vote + // Ensure user_votes table is created: + // CREATE TABLE IF NOT EXISTS user_votes ( + // id INTEGER PRIMARY KEY AUTOINCREMENT, + // question_id INTEGER NOT NULL, + // option_id INTEGER NOT NULL, + // user_name TEXT NOT NULL, + // voted_at DATETIME DEFAULT CURRENT_TIMESTAMP + // ); + $insertVote = $db->prepare(" + INSERT INTO user_votes (question_id, option_id, user_name) + VALUES (:poll_id, :option_id, :username) + "); + $insertVote->execute([ + ':poll_id' => $pollId, + ':option_id' => $optionId, + ':username' => $username + ]); + + sendJson(['success' => true, 'message' => 'Vote cast successfully']); + } catch (Exception $e) { + sendJson(['success' => false, 'error' => $e->getMessage()], 500); + } + break; + + // -------------------------------------------------- + // 4) Unknown / default + // -------------------------------------------------- + default: + sendJson(['success' => false, 'error' => 'Unknown action'], 400); + break; +} From 040c0bf64e426aa39d16d6485df4a1fa4869a5a8 Mon Sep 17 00:00:00 2001 From: deepend-tildeclub <58404188+deepend-tildeclub@users.noreply.github.com> Date: Fri, 24 Jan 2025 18:26:55 -0700 Subject: [PATCH 3/5] Create admin.php --- polls/admin.php | 161 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 161 insertions(+) create mode 100644 polls/admin.php diff --git a/polls/admin.php b/polls/admin.php new file mode 100644 index 0000000..f4a6b3e --- /dev/null +++ b/polls/admin.php @@ -0,0 +1,161 @@ +query("SELECT id, question_text FROM poll_questions ORDER BY id DESC"); + $polls = $stmt->fetchAll(PDO::FETCH_ASSOC); + sendJson(['success' => true, 'polls' => $polls]); + } catch (Exception $e) { + sendJson(['success' => false, 'error' => $e->getMessage()], 500); + } + break; + + // -------------------------------------------------- + // 2) Get a single poll (question + options) + // -------------------------------------------------- + case 'get_poll': + $pollId = (int)($_GET['poll_id'] ?? 0); + if ($pollId <= 0) { + sendJson(['success' => false, 'error' => 'Invalid poll_id'], 400); + } + try { + // Fetch poll question + $stmt = $db->prepare("SELECT id, question_text FROM poll_questions WHERE id = :id"); + $stmt->execute([':id' => $pollId]); + $poll = $stmt->fetch(PDO::FETCH_ASSOC); + + if (!$poll) { + sendJson(['success' => false, 'error' => 'Poll not found'], 404); + } + + // Fetch options + $optionsStmt = $db->prepare(" + SELECT po.id AS option_id, po.option_text, + IFNULL(pr.vote_count, 0) AS vote_count + FROM poll_options po + LEFT JOIN poll_results pr ON po.id = pr.option_id + WHERE po.question_id = :question_id + ORDER BY po.id ASC + "); + $optionsStmt->execute([':question_id' => $pollId]); + $options = $optionsStmt->fetchAll(PDO::FETCH_ASSOC); + + sendJson([ + 'success' => true, + 'poll' => [ + 'id' => $poll['id'], + 'question_text' => $poll['question_text'], + 'options' => $options + ] + ]); + } catch (Exception $e) { + sendJson(['success' => false, 'error' => $e->getMessage()], 500); + } + break; + + // -------------------------------------------------- + // 3) Cast a vote + // Expects: poll_id, option_id, username + // -------------------------------------------------- + case 'vote': + // This can come from POST or GET. We'll assume POST for clarity. + $pollId = (int)($_POST['poll_id'] ?? 0); + $optionId = (int)($_POST['option_id'] ?? 0); + $username = trim($_POST['username'] ?? ''); + + if ($pollId <= 0 || $optionId <= 0 || empty($username)) { + sendJson(['success' => false, 'error' => 'Missing or invalid parameters'], 400); + } + + // Check if user already voted on this poll + try { + // 1) Ensure poll & option exist + $checkOption = $db->prepare(" + SELECT COUNT(*) + FROM poll_options + WHERE id = :option_id + AND question_id = :poll_id + "); + $checkOption->execute([ + ':option_id' => $optionId, + ':poll_id' => $pollId + ]); + if (!$checkOption->fetchColumn()) { + sendJson(['success' => false, 'error' => 'Option does not belong to poll or does not exist'], 400); + } + + // 2) Check if user already voted + $checkVote = $db->prepare(" + SELECT COUNT(*) + FROM user_votes + WHERE question_id = :poll_id + AND user_name = :username + "); + $checkVote->execute([ + ':poll_id' => $pollId, + ':username' => $username + ]); + if ($checkVote->fetchColumn() > 0) { + // Already voted + sendJson(['success' => false, 'error' => 'Already voted'], 403); + } + + // 3) Cast the vote (increment poll_results) + $updateStmt = $db->prepare(" + UPDATE poll_results + SET vote_count = vote_count + 1 + WHERE question_id = :poll_id + AND option_id = :option_id + "); + $updateStmt->execute([ + ':poll_id' => $pollId, + ':option_id' => $optionId + ]); + + // 4) Record the user vote + // Ensure user_votes table is created: + // CREATE TABLE IF NOT EXISTS user_votes ( + // id INTEGER PRIMARY KEY AUTOINCREMENT, + // question_id INTEGER NOT NULL, + // option_id INTEGER NOT NULL, + // user_name TEXT NOT NULL, + // voted_at DATETIME DEFAULT CURRENT_TIMESTAMP + // ); + $insertVote = $db->prepare(" + INSERT INTO user_votes (question_id, option_id, user_name) + VALUES (:poll_id, :option_id, :username) + "); + $insertVote->execute([ + ':poll_id' => $pollId, + ':option_id' => $optionId, + ':username' => $username + ]); + + sendJson(['success' => true, 'message' => 'Vote cast successfully']); + } catch (Exception $e) { + sendJson(['success' => false, 'error' => $e->getMessage()], 500); + } + break; + + // -------------------------------------------------- + // 4) Unknown / default + // -------------------------------------------------- + default: + sendJson(['success' => false, 'error' => 'Unknown action'], 400); + break; +} From ff4e747196abf64b58fd6693f63a646f9bd8944b Mon Sep 17 00:00:00 2001 From: deepend-tildeclub <58404188+deepend-tildeclub@users.noreply.github.com> Date: Fri, 24 Jan 2025 18:31:20 -0700 Subject: [PATCH 4/5] Create setup.php --- polls/setup.php | 166 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 166 insertions(+) create mode 100644 polls/setup.php diff --git a/polls/setup.php b/polls/setup.php new file mode 100644 index 0000000..c02894e --- /dev/null +++ b/polls/setup.php @@ -0,0 +1,166 @@ +query("SELECT COUNT(*) FROM users")->fetchColumn(); + +// If at least one user exists, show a message and no form +if ($checkTotal > 0) { + ?> + + + + + Setup Admin User + + + +
+

Admin User Already Exists

+

+ An admin user has already been created. No additional admins can be set up here. +

+

+ Go back to the Polls site. +

+
+ + + prepare(" + INSERT INTO users (username, password) + VALUES (:username, :password) + "); + $insertStmt->bindValue(':username', $username, PDO::PARAM_STR); + $insertStmt->bindValue(':password', $hashedPassword, PDO::PARAM_STR); + $insertStmt->execute(); + + $success = "Admin user '$username' created successfully."; + } +} +?> + + + + + Setup Admin User + + + +
+

Setup Admin User

+ + +
+ + +
+

You can now go to the Admin page to log in.

+ +
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ +
+
+ +
+ + From 6a342bcc7a23ce829f4d4dff6ae8b13a6458b389 Mon Sep 17 00:00:00 2001 From: deepend-tildeclub <58404188+deepend-tildeclub@users.noreply.github.com> Date: Fri, 24 Jan 2025 18:32:13 -0700 Subject: [PATCH 5/5] Create db.php --- polls/db.php | 166 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 166 insertions(+) create mode 100644 polls/db.php diff --git a/polls/db.php b/polls/db.php new file mode 100644 index 0000000..c02894e --- /dev/null +++ b/polls/db.php @@ -0,0 +1,166 @@ +query("SELECT COUNT(*) FROM users")->fetchColumn(); + +// If at least one user exists, show a message and no form +if ($checkTotal > 0) { + ?> + + + + + Setup Admin User + + + +
+

Admin User Already Exists

+

+ An admin user has already been created. No additional admins can be set up here. +

+

+ Go back to the Polls site. +

+
+ + + prepare(" + INSERT INTO users (username, password) + VALUES (:username, :password) + "); + $insertStmt->bindValue(':username', $username, PDO::PARAM_STR); + $insertStmt->bindValue(':password', $hashedPassword, PDO::PARAM_STR); + $insertStmt->execute(); + + $success = "Admin user '$username' created successfully."; + } +} +?> + + + + + Setup Admin User + + + +
+

Setup Admin User

+ + +
+ + +
+

You can now go to the Admin page to log in.

+ +
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ +
+
+ +
+ +