Skip to content

Instantly share code, notes, and snippets.

@davidmintz
Last active January 8, 2024 01:18
Show Gist options
  • Save davidmintz/2f1ec5b472f7e3e52b038f0ca9b75be9 to your computer and use it in GitHub Desktop.
Save davidmintz/2f1ec5b472f7e3e52b038f0ca9b75be9 to your computer and use it in GitHub Desktop.
quick and dirty survey in PHP and Javascript
<?php
/**
* collect and record a "vote" for a simple survey with one question, three choices.
*
* I thought it would be so simple that it would be easy to do in one physical file without
* getting too monolithic. Not so. But... oh well.
*/
/**
* @return PDO
*/
function get_db_connection() : PDO
{
$path = __DIR__.'/../../data/database.sqlite';
$db = new PDO("sqlite:$path");
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
return $db;
}
function fetch_member(PDO $db, string $token) :? stdClass
{
$stmt = $db->prepare('SELECT * FROM members WHERE hash = :token',);
$stmt->execute(['token'=>$token]);
try { return $stmt->fetchObject(); } catch (\Exception $e) {
}
return null;
}
function validate_token(PDO $db) : string
{
$token = $_GET['t'] ?: null;
$error = false;
if (! $token) {
return '<p id="token-element" class="alert alert-danger p-3"><strong>Error:</strong> missing URL token. Please check your URL and try again.</p>';
}
$db = get_db_connection();
try {
// validate the token
$member = fetch_member($db, $token);
//exit("shit? " . __LINE__);
if (! $member) {
$error = '<p id="token-element" class="alert alert-danger p-3"><strong>Sorry!</strong> Your URL token is not valid. Please check your URL and try again.</p>';
}
//exit("shit?");
// make sure they have not yet voted
elseif ($member->vote) {
$error = sprintf('<p id="token-element" data-token="" class="alert alert-danger p-3"><strong>Sorry!</strong> A vote has already been cast by (or on behalf of) member <strong>%s</strong></p>',
// $token,
$member->name);
}
if ($error) {
return $error;
}
// all good
return sprintf('<p id="token-element" data-token="%s" class="alert alert-info p-3">Welcome %s. Please make your choice and hit submit.</p>',$token, $member->name);
} catch (\Exception $e) {
return '<p id="token-element" class="alert alert-danger p-3"><strong>Damn!</strong> database access error. Sorry!</p>';
}
}
if (date('Ymd.Hi') > '20240108.1200') {
exit('Sorry. The voting period ended at 12:00 pm Monday, 08 Jan 2024');
}
if (!empty($_SERVER)) {
if ($_SERVER['REQUEST_METHOD'] == 'GET') {
$output = validate_token($db = get_db_connection());
} elseif($_SERVER['REQUEST_METHOD'] == 'POST') {
// record vote
// input is (should be) via xhr
// can't just read $_POST. I didn't know this either, till now
$request_body = file_get_contents('php://input');
$post = json_decode($request_body);
// validate
header('Content-type: application/json',true);
if (empty($post->vote) or ! in_array($post->vote, [1,2,3])) {
exit(json_encode(['message'=>"You must submit a valid vote",'status' => 'FAILED']));
}
if (empty($post->token)) {
exit(json_encode(['message'=>"Your request is missing an identification token",'status' => 'FAILED']));
}
// find the member again, make sure they exist and have not voted
$db = get_db_connection();
$member = fetch_member($db, $post->token);
$error = null;
if (! $member) {
$error = 'Invalid URL identification token. Please double-check your URL and try again.';
} elseif ($member->vote) {
// because they could legitimately load the page in two different tabs or windows, etc
$error = "A vote has already been cast on by (or on behalf of) $member->name";
}
if ($error) {
exit(json_encode(['message' => $error, 'status'=>'FAILED']));
}
// all good
try {
$stmt = $db->prepare('UPDATE members SET vote = :vote, vote_datetime = datetime() WHERE hash = :token');
$result = $stmt->execute(['vote' => $post->vote,'token'=>$post->token]);
} catch (\Exception $e) {
exit(json_encode(['message' => 'Shit. Database error. Sorry!', 'status'=>'FAILED']));
}
exit(json_encode(['message' => 'Success! Your vote has been recorded. Thank you.', 'status'=>'OK']));
} else {
$output = 'invalid request method';
exit($output);
}
} else exit("wtf?!");
?>
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="residents of Martha's Vineyard, Massachussets, USA, who demand a ceasefire in Gaza">
<meta property="og:type" content="website" />
<meta property="og:title" content="Ceasefire MV | MV For Palestine" />
<meta property="og:image" content="https://ceasefiremv.org/images/logo.png" />
<meta property="og:description" content="Ceasefire MV, also know as MV For Palestine, is a group of people residing on Martha's Vineyard, Massachussets, USA, who demand an immediate and permanent ceasefire and a halt to the Israeli military assault on Gaza.">
<title>Ceasefire MV | MV For Palestine: Stop The War On Gaza</title>
<style>
body { max-width: 1200px; margin: auto; }
</style>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<link href="/css/bootstrap.min.css" rel="stylesheet">
</head>
<main class="container">
<div class="row">
<div class="col-md-7 offset-md-3">
<div class="d-flex justify-content-between align-items-center">
<div class="w-100 text-center ">
<h1 class="text-center py-3 text">CEASEFIRE NOW!</h1>
</div>
<img style="max-height: 220px" src="/images/logo.png" alt="CeasefireMV: Islanders for Palestine" class="img-fluid">
</div>
<?= $output ?>
<p>My preference is</p>
<form id="form-vote" class="" action="/survey/index.php" method="post">
<input type="radio" name="vote" value="2"> groups.io for email management<br>
<input type="radio" name="vote" value="3"> ordinary email messages to 30+ recipients<br>
<input type="radio" name="vote" value="1"> I don't care<br>
<input id="btn-submit" class="btn btn-primary mt-2" type="submit" value="submit">
</form>
</div>
</div>
</main>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js" integrity="sha384-C6RzsynM9kWDrMNeT87bh95OGNyZPhcTNXj1NW7RuBCsyN/o0jlpcV8Qyq46cDfL" crossorigin="anonymous"></script>
</body>
<script>
const button = document.getElementById("btn-submit");
const status_element = document.getElementById('token-element');
const token = status_element.getAttribute("data-token");
if (! token) {
document.getElementById('btn-submit').setAttribute("disabled", "disabled");
//console.log("SHIT SHOULD BE disabled");
}
button.addEventListener("click", function(e){
e.preventDefault();
let form_data = new FormData(document.getElementById("form-vote"));
axios.post("/survey/index.php",{vote: form_data.get("vote"), token })
.then(function(response){
let data = response.data;
if (data.status === 'FAILED') {
console.log('shit failed: '+ data.message);
status_element.classList.remove("alert-info");
status_element.classList.add("alert-danger");
} else {
status_element.classList.remove("alert-info");
status_element.classList.remove("alert-danger");
status_element.classList.add("alert-success");
document.getElementById('btn-submit').setAttribute("disabled", "disabled");
}
status_element.innerText = data.message;
});
});
</script>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment