| FROM docker.io/bravecheng/php-nginx-sqlite:latest | |||||
| USER nobody | |||||
| RUN mkdir -p /var/www/html/inc/config | |||||
| RUN chown nobody:nobody /var/www/html/inc/config | |||||
| VOLUME /var/www/html/inc/config | |||||
| USER root | |||||
| WORKDIR /var/www | |||||
| COPY cli cli | |||||
| COPY web html | |||||
| USER nobody |
| MIN(username) as `username`, | MIN(username) as `username`, | ||||
| group_concat(proxy.voting_id) as `proxies`, | group_concat(proxy.voting_id) as `proxies`, | ||||
| MIN(upstream_proxy.delegate_id) as `delegate`, | MIN(upstream_proxy.delegate_id) as `delegate`, | ||||
| md5(coalesce(MIN(email), "")) as `gravatar_hash` | |||||
| coalesce(MIN(email), "") as `gravatar_email` | |||||
| from members | from members | ||||
| left join proxy on (members.voting_id=proxy.delegate_id) | left join proxy on (members.voting_id=proxy.delegate_id) | ||||
| left join proxy as upstream_proxy on (upstream_proxy.voting_id=members.voting_id) | left join proxy as upstream_proxy on (upstream_proxy.voting_id=members.voting_id) | ||||
| where members.voting_id is not null | where members.voting_id is not null | ||||
| group by members.voting_id | group by members.voting_id | ||||
| UNION | UNION | ||||
| select skymanager_id, voting_id, name, username, NULL as `proxies`, NULL as `delegate`, md5(coalesce(email, "")) as `gravatar_hash` | |||||
| select skymanager_id, voting_id, name, username, NULL as `proxies`, NULL as `delegate`, coalesce(email, "") as `gravatar_email` | |||||
| from members where members.voting_id is null'); | from members where members.voting_id is null'); | ||||
| get_gravatar_assoc($voters); | |||||
| ?> | ?> | ||||
| <script type="text/javascript"> | <script type="text/javascript"> | ||||
| var voters = <?= json_encode($voters); ?>; | var voters = <?= json_encode($voters); ?>; |
| if (empty($error)) { | if (empty($error)) { | ||||
| $result = $db->query("INSERT INTO votes (candidate_id, position, member_id, vote_type, submitter_id) SELECT $candidate_selected, \"$ballot\", $voter_selected, 'IN PERSON', $voter_selected UNION SELECT $candidate_selected, \"$ballot\", voting_id, 'PROXY IN PERSON', delegate_id from proxy where delegate_id=$voter_selected"); | $result = $db->query("INSERT INTO votes (candidate_id, position, member_id, vote_type, submitter_id) SELECT $candidate_selected, \"$ballot\", $voter_selected, 'IN PERSON', $voter_selected UNION SELECT $candidate_selected, \"$ballot\", voting_id, 'PROXY IN PERSON', delegate_id from proxy where delegate_id=$voter_selected"); | ||||
| $candidate = $db->fetchRow('select skymanager_id, name, username, md5(coalesce(email, "")) as `gravatar_hash` from members where skymanager_id=' . $candidate_selected); | |||||
| $candidate = $db->fetchRow('select skymanager_id, name, username, coalesce(email, "") as `gravatar_email` from members where skymanager_id=' . $candidate_selected); | |||||
| if ($result) { | if ($result) { | ||||
| $proxy_votes = $db->fetchAssoc("SELECT member_id, submitter_id from votes where submitter_id=$voter_selected and position=\"$ballot\""); | $proxy_votes = $db->fetchAssoc("SELECT member_id, submitter_id from votes where submitter_id=$voter_selected and position=\"$ballot\""); | ||||
| $header->setAttribute('tagline', 'Election Poll Worker Tools'); | $header->setAttribute('tagline', 'Election Poll Worker Tools'); | ||||
| $header->output(); | $header->output(); | ||||
| $candidates = $db->fetchAssoc('select skymanager_id, name, username, md5(coalesce(email, "")) as `gravatar_hash` from members where voting_id is not null'); | |||||
| $voters = $db->fetchAssoc('select MIN(skymanager_id) as `skymanager_id`, MIN(members.voting_id) as `voting_id`, MIN(name) as `name`, MIN(username) as `username`, group_concat(proxy.voting_id) as `proxies`, MIN(upstream_proxy.delegate_id) as `delegate`, md5(coalesce(MIN(email), "")) as `gravatar_hash` from members left join proxy on (members.voting_id=proxy.delegate_id) left join proxy as upstream_proxy on (upstream_proxy.voting_id=members.voting_id) where members.voting_id is not null group by members.voting_id UNION select skymanager_id, voting_id, name, username, NULL as `proxies`, NULL as `delegate`, md5(coalesce(email, "")) as `gravatar_hash` from members where members.voting_id is null'); | |||||
| $candidates = $db->fetchAssoc('select skymanager_id, name, username, coalesce(email, "") as `gravatar_email` from members where voting_id is not null'); | |||||
| $voters = $db->fetchAssoc('select MIN(skymanager_id) as `skymanager_id`, MIN(members.voting_id) as `voting_id`, MIN(name) as `name`, MIN(username) as `username`, group_concat(proxy.voting_id) as `proxies`, MIN(upstream_proxy.delegate_id) as `delegate`, coalesce(MIN(email), "") as `gravatar_email` from members left join proxy on (members.voting_id=proxy.delegate_id) left join proxy as upstream_proxy on (upstream_proxy.voting_id=members.voting_id) where members.voting_id is not null group by members.voting_id UNION select skymanager_id, voting_id, name, username, NULL as `proxies`, NULL as `delegate`, coalesce(email, "") as `gravatar_email` from members where members.voting_id is null'); | |||||
| get_gravatar_assoc($candidates); | |||||
| get_gravatar_assoc($voters); | |||||
| ?> | ?> | ||||
| <script type="text/javascript"> | <script type="text/javascript"> | ||||
| var voters = <?= json_encode($voters); ?>; | var voters = <?= json_encode($voters); ?>; | ||||
| <h4 class="section-heading">Candidate</h4> | <h4 class="section-heading">Candidate</h4> | ||||
| <div id="vote-profile" class="candidate"> | <div id="vote-profile" class="candidate"> | ||||
| <div class="profile-icon"> | <div class="profile-icon"> | ||||
| <img src="https://www.gravatar.com/avatar/<?= $candidate['gravatar_hash']; ?>.png?d=mp&s=64" /> | |||||
| <img src="https://www.gravatar.com/avatar/<?= md5($candidate['gravatar_email']); ?>.png?d=mp&s=64" /> | |||||
| </div> | </div> | ||||
| <div class="profile"> | <div class="profile"> | ||||
| <h2 class="profile-name"><?= $candidate['name']; ?></h2> | <h2 class="profile-name"><?= $candidate['name']; ?></h2> |
| function loadVoters() { | function loadVoters() { | ||||
| global $db; | global $db; | ||||
| return $db->fetchAssoc(' | |||||
| $voters = $db->fetchAssoc(' | |||||
| select | select | ||||
| MIN(skymanager_id) as `skymanager_id`, | MIN(skymanager_id) as `skymanager_id`, | ||||
| MIN(members.voting_id) as `voting_id`, | MIN(members.voting_id) as `voting_id`, | ||||
| MIN(username) as `username`, | MIN(username) as `username`, | ||||
| group_concat(proxy.voting_id) as `proxies`, | group_concat(proxy.voting_id) as `proxies`, | ||||
| MIN(upstream_proxy.delegate_id) as `delegate`, | MIN(upstream_proxy.delegate_id) as `delegate`, | ||||
| md5(coalesce(MIN(email), "")) as `gravatar_hash` | |||||
| coalesce(MIN(email), "") as `gravatar_email` | |||||
| from members | from members | ||||
| left join proxy on (members.voting_id=proxy.delegate_id) | left join proxy on (members.voting_id=proxy.delegate_id) | ||||
| left join proxy as upstream_proxy on (upstream_proxy.voting_id=members.voting_id) | left join proxy as upstream_proxy on (upstream_proxy.voting_id=members.voting_id) | ||||
| where members.voting_id is not null | where members.voting_id is not null | ||||
| group by members.voting_id | group by members.voting_id | ||||
| UNION | UNION | ||||
| select skymanager_id, voting_id, name, username, NULL as `proxies`, NULL as `delegate`, md5(coalesce(email, "")) as `gravatar_hash` | |||||
| select skymanager_id, voting_id, name, username, NULL as `proxies`, NULL as `delegate`, coalesce(email, "") as `gravatar_email` | |||||
| from members where members.voting_id is null'); | from members where members.voting_id is null'); | ||||
| get_gravatar_assoc($voters); | |||||
| return $voters; | |||||
| } | } | ||||
| $result = null; | $result = null; | ||||
| $error = "Created position " . htmlspecialchars($desc); | $error = "Created position " . htmlspecialchars($desc); | ||||
| } | } | ||||
| } else if (!empty($_POST['setActive']) || !empty($_POST['deactivate'])) { | } else if (!empty($_POST['setActive']) || !empty($_POST['deactivate'])) { | ||||
| $positions = loadPositions(); | |||||
| $position = $_POST['ballot']; | |||||
| if (!array_key_exists($position, $positions)) $error = "That position does not exist"; | |||||
| if (!empty($_POST['deactivate'])) | |||||
| $position=''; | |||||
| if (empty($error)) { | |||||
| $result = $db->query('UPDATE positions set active=(position="' . $db->sanitize($position) . '")'); | |||||
| if ($result === false) | |||||
| $error = "Failed to set active position"; | |||||
| else if (empty($position)) | |||||
| $error = "Deactivated voting form"; | |||||
| else | |||||
| $error = "Set " . htmlspecialchars($positions[$_POST['ballot']]['label']) . " as active."; | |||||
| if (!array_key_exists('ballot', $_POST)) { | |||||
| $error = "No position selected"; | |||||
| } else { | |||||
| $positions = loadPositions(); | |||||
| $position = $_POST['ballot']; | |||||
| if (!array_key_exists($position, $positions)) $error = "That position does not exist"; | |||||
| if (!empty($_POST['deactivate'])) | |||||
| $position=''; | |||||
| if (empty($error)) { | |||||
| $result = $db->query('UPDATE positions set active=(position="' . $db->sanitize($position) . '")'); | |||||
| if ($result === false) | |||||
| $error = "Failed to set active position"; | |||||
| else if (empty($position)) | |||||
| $error = "Deactivated voting form"; | |||||
| else | |||||
| $error = "Set " . htmlspecialchars($positions[$_POST['ballot']]['label']) . " as active."; | |||||
| } | |||||
| } | } | ||||
| } else if (!empty($_POST['remove'])) { | } else if (!empty($_POST['remove'])) { | ||||
| $positions = loadPositions(); | |||||
| if (!array_key_exists($_POST['ballot'], $positions)) $error = "That position does not exist"; | |||||
| if (empty($error)) { | |||||
| $result = $db->query('DELETE FROM positions WHERE position="' . $db->sanitize($_POST['ballot']) . '"'); | |||||
| if ($result === false) | |||||
| $error = "Failed to remove position"; | |||||
| else | |||||
| $error = "Removed " . htmlspecialchars($positions[$_POST['ballot']]['label']) . " and discarded cast ballots."; | |||||
| if (!array_key_exists('ballot', $_POST)) { | |||||
| $error = "No position selected"; | |||||
| } else { | |||||
| $positions = loadPositions(); | |||||
| if (!array_key_exists($_POST['ballot'], $positions)) $error = "That position does not exist"; | |||||
| if (empty($error)) { | |||||
| $result = $db->query('DELETE FROM positions WHERE position="' . $db->sanitize($_POST['ballot']) . '"'); | |||||
| if ($result === false) | |||||
| $error = "Failed to remove position"; | |||||
| else | |||||
| $error = "Removed " . htmlspecialchars($positions[$_POST['ballot']]['label']) . " and discarded cast ballots."; | |||||
| } | |||||
| } | } | ||||
| } else if (!empty($_POST['force'])) { | } else if (!empty($_POST['force'])) { | ||||
| $voters = loadVoters(); | $voters = loadVoters(); |
| require_once(BASE . '/inc/db.php'); | require_once(BASE . '/inc/db.php'); | ||||
| require_once(BASE . '/inc/user.php'); | require_once(BASE . '/inc/user.php'); | ||||
| $required = ['db-host', 'db-username', 'db-password', 'db-database', 'flyers-user', 'flyers-password']; | |||||
| $fieldNames = ['db-type', 'db-host', 'db-username', 'db-password', 'db-database', 'flyers-user', 'flyers-password']; | |||||
| function test_config($params) { | function test_config($params) { | ||||
| global $required, $db, $user; | |||||
| global $fieldNames, $db, $user; | |||||
| if (!empty($params) && count($params) != count($required)) | |||||
| return "All fields are required"; | |||||
| mysqli_report(MYSQLI_REPORT_OFF); | |||||
| $mysql = mysqli_connect($params['db-host'], $params['db-username'], $params['db-password']); | |||||
| if (!$mysql) | |||||
| return "Unable to connect to the database."; | |||||
| $config = [ | |||||
| "type" => $params['db-type'] | |||||
| ]; | |||||
| mysqli_select_db($mysql, $params['db-database']); | |||||
| if (mysqli_error($mysql)) | |||||
| return "Unable to access database '" . htmlspecialchars($params['db-database']) . "': " . mysqli_error($mysql); | |||||
| if (empty($params['flyers-user']) || empty($params['flyers-password'])) | |||||
| return "All fields are required"; | |||||
| mysqli_multi_query($mysql, " | |||||
| switch ($params['db-type']) { | |||||
| case "mysql": | |||||
| if (!empty($params) && count($params) != count($fieldNames)) | |||||
| return "All fields are required"; | |||||
| $config += [ | |||||
| 'host' => $params['db-host'], | |||||
| 'user' => $params['db-username'], | |||||
| 'pass' => $params['db-password'], | |||||
| 'db' => $params['db-database'] | |||||
| ]; | |||||
| $db = MysqlDb::Connect($config->host, $config->user, $config->pass, $config->db); | |||||
| break; | |||||
| case "sqlite": | |||||
| $db = SqliteDb::Connect(); | |||||
| break; | |||||
| default: | |||||
| return "Invalid Database Type"; | |||||
| } | |||||
| $success = $db->exec_multi(" | |||||
| CREATE TABLE IF NOT EXISTS `members` ( | CREATE TABLE IF NOT EXISTS `members` ( | ||||
| `skymanager_id` integer NOT NULL PRIMARY KEY, | `skymanager_id` integer NOT NULL PRIMARY KEY, | ||||
| `name` varchar(128) NOT NULL, | `name` varchar(128) NOT NULL, | ||||
| `candidate_id` integer NOT NULL, | `candidate_id` integer NOT NULL, | ||||
| `position` varchar(64) NOT NULL, | `position` varchar(64) NOT NULL, | ||||
| `member_id` integer NOT NULL, | `member_id` integer NOT NULL, | ||||
| `vote_type` enum('IN PERSON','ONLINE','PROXY IN PERSON','PROXY ONLINE','UNANIMOUS') NOT NULL DEFAULT 'ONLINE', | |||||
| -- `vote_type` enum('IN PERSON','ONLINE','PROXY IN PERSON','PROXY ONLINE','UNANIMOUS') NOT NULL DEFAULT 'ONLINE', | |||||
| `vote_type` varchar(24) NOT NULL DEFAULT 'ONLINE', | |||||
| `submitted_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, | `submitted_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, | ||||
| `submitter_id` integer NOT NULL, | `submitter_id` integer NOT NULL, | ||||
| PRIMARY KEY (`position`,`member_id`), | PRIMARY KEY (`position`,`member_id`), | ||||
| FOREIGN KEY (`position`) REFERENCES `positions` (`position`) ON DELETE CASCADE) | FOREIGN KEY (`position`) REFERENCES `positions` (`position`) ON DELETE CASCADE) | ||||
| "); | "); | ||||
| if (!$success) | |||||
| return "Failed to set up database schema: " . $db->getError(); | |||||
| do { | |||||
| if (mysqli_error($mysql)) | |||||
| return "Unable to set up tables: " . mysqli_error($mysql); | |||||
| } while (mysqli_next_result($mysql) || mysqli_error($mysql)); | |||||
| $db = DBHandler::wrap($mysql); | |||||
| $success = $user->login($params['flyers-user'], $params['flyers-password']); | $success = $user->login($params['flyers-user'], $params['flyers-password']); | ||||
| if (!$success) | if (!$success) | ||||
| return "Login Failed"; | return "Login Failed"; | ||||
| $db->query("UPDATE members SET `pollworker`=TRUE where skymanager_id=" . ((int) $user->getUserId())); | $db->query("UPDATE members SET `pollworker`=TRUE where skymanager_id=" . ((int) $user->getUserId())); | ||||
| if ($db->getError()) | |||||
| return "Failed to update user permissions"; | |||||
| if ($err = $db->getError()) | |||||
| return "Failed to update user permissions: $err"; | |||||
| $conf = json_encode([ | |||||
| 'host' => $params['db-host'], | |||||
| 'user' => $params['db-username'], | |||||
| 'pass' => $params['db-password'], | |||||
| 'db' => $params['db-database'] | |||||
| ], JSON_PRETTY_PRINT); | |||||
| $conf = ""; | |||||
| $conf = json_encode($config, JSON_PRETTY_PRINT); | |||||
| if (file_put_contents(BASE . "/inc/config.json", $conf) === false) | |||||
| if (file_put_contents(BASE . "/inc/config/config.json", $conf) === false) | |||||
| return "Failed to write configuration."; | return "Failed to write configuration."; | ||||
| return false; | return false; | ||||
| } | } | ||||
| $params = []; | $params = []; | ||||
| foreach ($required as $field) { | |||||
| foreach ($fieldNames as $field) { | |||||
| if (array_key_exists($field, $_POST) && !empty($_POST[$field])) | if (array_key_exists($field, $_POST) && !empty($_POST[$field])) | ||||
| $params[$field] = $_POST[$field]; | $params[$field] = $_POST[$field]; | ||||
| } | } | ||||
| <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" /> | <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" /> | ||||
| <link rel="stylesheet" type="text/css" href="https://fonts.googleapis.com/css2?family=Fira+Sans:wght@400;600;800&display=swap" /> | <link rel="stylesheet" type="text/css" href="https://fonts.googleapis.com/css2?family=Fira+Sans:wght@400;600;800&display=swap" /> | ||||
| <link rel="stylesheet" type="text/css" href="/styles/style.css" /> | <link rel="stylesheet" type="text/css" href="/styles/style.css" /> | ||||
| <style type="text/css"> | |||||
| form input#db-sqlite:checked~.form-row label[for="db-sqlite"] .radio-button-label, | |||||
| form input#db-mysql:checked~.form-row label[for="db-mysql"] .radio-button-label { | |||||
| background-color: #000; | |||||
| color: #fff; | |||||
| border: 2px solid #fff; | |||||
| box-shadow: 0px 0px 0px 2px #000; | |||||
| } | |||||
| form .form-row.conditional { display: none; } | |||||
| form input#db-mysql:checked~.form-row.mysql { display: block; } | |||||
| </style> | |||||
| </head> | </head> | ||||
| <body> | <body> | ||||
| <div id="container"> | <div id="container"> | ||||
| <?php if(!empty($error)) echo "<span class=\"errormessage\">$error</span>"; ?> | <?php if(!empty($error)) echo "<span class=\"errormessage\">$error</span>"; ?> | ||||
| <form action="configure.php" method="POST"> | <form action="configure.php" method="POST"> | ||||
| <div class="form-section"> | <div class="form-section"> | ||||
| <input type="radio" id="db-sqlite" name="db-type" value="sqlite" checked /> | |||||
| <input type="radio" id="db-mysql" name="db-type" value="mysql" /> | |||||
| <h3>Database Setup</h3> | <h3>Database Setup</h3> | ||||
| <div class="form-row"> | <div class="form-row"> | ||||
| <div class="selector"> | |||||
| <label class="radio" for="db-sqlite"> | |||||
| <span class="radio-button-label">SQLite</span> | |||||
| </label> | |||||
| <label class="radio" for="db-mysql"> | |||||
| <span class="radio-button-label">MySQL</span> | |||||
| </label> | |||||
| </div> | |||||
| </div> | |||||
| <div class="form-row conditional mysql"> | |||||
| <label for="db-host">Host</label> | <label for="db-host">Host</label> | ||||
| <input type="text" id="db-host" name="db-host" value="localhost" /> | <input type="text" id="db-host" name="db-host" value="localhost" /> | ||||
| </div> | </div> | ||||
| <div class="form-row"> | |||||
| <div class="form-row conditional mysql"> | |||||
| <label for="db-host">Host</label> | |||||
| <input type="text" id="db-host" name="db-host" value="localhost" /> | |||||
| </div> | |||||
| <div class="form-row conditional mysql"> | |||||
| <label for="db-database">Database Name</label> | <label for="db-database">Database Name</label> | ||||
| <input type="text" id="db-database" name="db-database" /> | <input type="text" id="db-database" name="db-database" /> | ||||
| </div> | </div> | ||||
| <div class="form-row"> | |||||
| <div class="form-row conditional mysql"> | |||||
| <label for="db-username">Username</label> | <label for="db-username">Username</label> | ||||
| <input type="text" id="db-username" name="db-username" /> | <input type="text" id="db-username" name="db-username" /> | ||||
| </div> | </div> | ||||
| <div class="form-row"> | |||||
| <div class="form-row conditional mysql"> | |||||
| <label for="db-password">Password</label> | <label for="db-password">Password</label> | ||||
| <input type="password" id="db-password" name="db-password" /> | <input type="password" id="db-password" name="db-password" /> | ||||
| </div> | </div> |
| <?php | <?php | ||||
| class DBHandler{ | |||||
| class MysqlDb { | |||||
| private $mysql; | private $mysql; | ||||
| private function __construct() {} | private function __construct() {} | ||||
| public static function Connect($hostname, $username, $password, $database){ | public static function Connect($hostname, $username, $password, $database){ | ||||
| $handler = new DBHandler(); | |||||
| $handler = new MysqlDb(); | |||||
| mysqli_report(MYSQLI_REPORT_OFF); | |||||
| $handler->mysql = mysqli_connect($hostname, $username, $password); | $handler->mysql = mysqli_connect($hostname, $username, $password); | ||||
| if(!$handler->mysql) die("MySql error: " . mysql_error()); | |||||
| if(!$handler->mysql) return false; | |||||
| mysqli_select_db($handler->mysql, $database); | mysqli_select_db($handler->mysql, $database); | ||||
| return $handler; | |||||
| } | |||||
| if (mysqli_error($handler->mysql)) | |||||
| return false; | |||||
| public static function Wrap($mysql) { | |||||
| $handler = new DBHandler(); | |||||
| $handler->mysql = $mysql; | |||||
| return $handler; | return $handler; | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| public function insert($table, $fields, $values_assoc, $ignore = false) { | |||||
| $field_list = implode(",", $fields); | |||||
| $values_arr = []; | |||||
| foreach ($values_assoc as $value) { | |||||
| $value_arr = []; | |||||
| foreach ($value as $v) { | |||||
| if ($this->verifyInteger($v)) | |||||
| $value_arr[] = $v; | |||||
| else if ($v === "NULL") | |||||
| $value_arr[] = "NULL"; | |||||
| else | |||||
| $value_arr[] = "\"{$this->sanitize($v)}\""; | |||||
| } | |||||
| $values_arr[] = "(" . implode(',', $value_arr) . ")"; | |||||
| } | |||||
| $values_list = implode(',', $values_arr); | |||||
| $ignore_str = ''; | |||||
| if ($ignore) | |||||
| $ignore_str = 'IGNORE'; | |||||
| return $this->query("INSERT $ignore_str INTO $table ($field_list) VALUES $values_list"); | |||||
| } | |||||
| public function exec_multi($query){ | |||||
| try { | |||||
| $res = mysqli_multi_query($this->mysql, $query); | |||||
| do { | |||||
| if ($err = $this->getError()) | |||||
| return false; | |||||
| } while (mysqli_next_result($this->mysqli) || $this->getError()); | |||||
| return true; | |||||
| } catch (Throwable $err) { | |||||
| return false; | |||||
| } | |||||
| } | |||||
| public function fetchRow($query){ | public function fetchRow($query){ | ||||
| $result = $this->query($query); | $result = $this->query($query); | ||||
| if($result === false || $result === true) die(mysqli_error($this->mysql));//return $result; | if($result === false || $result === true) die(mysqli_error($this->mysql));//return $result; | ||||
| return mysqli_error($this->mysql); | return mysqli_error($this->mysql); | ||||
| } | } | ||||
| public function lastInsertId(){ | |||||
| return mysqli_insert_id($this->mysql); | |||||
| //public function lastInsertId(){ | |||||
| // return mysqli_insert_id($this->mysql); | |||||
| //} | |||||
| //public function getAffectedRows() { | |||||
| // return mysqli_affected_rows($this->mysql); | |||||
| //} | |||||
| public function verifyInteger($input, $minimum = 1){ | |||||
| /* | |||||
| * Pretty hacky solution. | |||||
| * | |||||
| * First checks if the integer cast of the input is equal to itself. | |||||
| * This filters out decimals, alternate bases, and exponents. | |||||
| * Then checks if the input is numeric, which filters out other strings that slip by the first check. | |||||
| * This guarantees that it's in a numeric format, which combined with the first filter, should guarantee that it is an integer | |||||
| */ | |||||
| return (((int) $input == $input) && is_numeric($input) && (($minimum === false) || (int) $input >= $minimum)); | |||||
| } | |||||
| // Always returns a key of length 40. TODO: Add arbitrary length | |||||
| public function randomKey(){ | |||||
| //Cryptographically Secure Key | |||||
| if(function_exists('openssl_random_pseudo_bytes')) return base64_encode(openssl_random_pseudo_bytes(30)); | |||||
| //Fallback (Not cryptographically secure) | |||||
| $str = ""; | |||||
| for($i=0; $i<30; $i++){ | |||||
| $str .= chr(mt_rand(0,255)); | |||||
| } | |||||
| return base64_encode($str); | |||||
| } | |||||
| } | |||||
| class SqliteDb { | |||||
| private $sqlite; | |||||
| private function __construct() {} | |||||
| public static function Connect(){ | |||||
| $handler = new SqliteDb(); | |||||
| $handler->sqlite = new Sqlite3(BASE . "/inc/config/sqlite3.db"); | |||||
| $handler->sqlite->exec('PRAGMA foreign_keys = ON'); | |||||
| return $handler; | |||||
| } | |||||
| public function sanitize($text){ | |||||
| return SQLite3::escapeString($text); | |||||
| } | |||||
| public function query($query){ | |||||
| try { | |||||
| return $this->sqlite->query($query); | |||||
| } catch (Throwable $err) { | |||||
| return false; | |||||
| } | |||||
| } | |||||
| public function insert($table, $fields, $values_assoc, $ignore = false) { | |||||
| $field_list = implode(",", $fields); | |||||
| $values_arr = []; | |||||
| foreach ($values_assoc as $value) { | |||||
| $value_arr = []; | |||||
| foreach ($value as $v) { | |||||
| if ($this->verifyInteger($v)) | |||||
| $value_arr[] = $v; | |||||
| else if ($v === "NULL") | |||||
| $value_arr[] = "NULL"; | |||||
| else | |||||
| $value_arr[] = "\"{$this->sanitize($v)}\""; | |||||
| } | |||||
| $values_arr[] = "(" . implode(',', $value_arr) . ")"; | |||||
| } | |||||
| $values_list = implode(',', $values_arr); | |||||
| $ignore_str = ''; | |||||
| if ($ignore) | |||||
| $ignore_str = 'OR IGNORE'; | |||||
| return $this->query("INSERT $ignore_str INTO $table ($field_list) VALUES $values_list"); | |||||
| } | |||||
| public function exec_multi($query){ | |||||
| try { | |||||
| return $this->sqlite->exec($query); | |||||
| } catch (Throwable $err) { | |||||
| return false; | |||||
| } | |||||
| } | |||||
| public function fetchRow($query){ | |||||
| $result = $this->query($query); | |||||
| if ($result === false || $result === true) | |||||
| return $result; | |||||
| return $result->fetchArray(); | |||||
| } | |||||
| public function fetchAssoc($query){ | |||||
| $result = $this->query($query); | |||||
| if($result === false || $result === true) die($this->getError());//return $result; | |||||
| $data = array(); | |||||
| while($row = $result->fetchArray()){ | |||||
| $data[] = $row; | |||||
| } | |||||
| return $data; | |||||
| } | } | ||||
| public function getAffectedRows() { | |||||
| return mysqli_affected_rows($this->mysql); | |||||
| public function getError() { | |||||
| return $this->sqlite->lastErrorCode() === 0 ? "" : $this->sqlite->lastErrorMsg(); | |||||
| } | } | ||||
| //public function lastInsertId(){ | |||||
| // return mysqli_insert_id($this->mysql); | |||||
| //} | |||||
| //public function getAffectedRows() { | |||||
| // return mysqli_affected_rows($this->mysql); | |||||
| //} | |||||
| public function verifyInteger($input, $minimum = 1){ | public function verifyInteger($input, $minimum = 1){ | ||||
| /* | /* | ||||
| * Pretty hacky solution. | * Pretty hacky solution. |
| define('BASE', dirname(__DIR__)); | define('BASE', dirname(__DIR__)); | ||||
| define('BASEURL', $_SERVER['SERVER_NAME']); | define('BASEURL', $_SERVER['SERVER_NAME']); | ||||
| $config = json_decode(file_get_contents(BASE . "/inc/config.json")); | |||||
| $config = json_decode(file_get_contents(BASE . "/inc/config/config.json")); | |||||
| if (empty($config)) { | if (empty($config)) { | ||||
| header('Location: /configure.php'); | header('Location: /configure.php'); | ||||
| die(); | die(); | ||||
| // Database and Authentication | // Database and Authentication | ||||
| require_once(BASE . '/inc/db.php'); | require_once(BASE . '/inc/db.php'); | ||||
| $db = DBHandler::Connect($config->host, $config->user, $config->pass, $config->db); | |||||
| if ($config->type == "mysql") | |||||
| $db = MysqlDb::Connect($config->host, $config->user, $config->pass, $config->db); | |||||
| else | |||||
| $db = SqliteDb::Connect(); | |||||
| require_once(BASE . '/inc/user.php'); | require_once(BASE . '/inc/user.php'); | ||||
| require_once(BASE . '/inc/misc.php'); | |||||
| // Templates | // Templates | ||||
| require_once(BASE . '/templates/header.php'); | require_once(BASE . '/templates/header.php'); |
| <?php | |||||
| function get_gravatar_assoc(&$results) { | |||||
| foreach ($results as &$result) { | |||||
| $result['gravatar_hash'] = md5($result['gravatar_email']); | |||||
| unset($result['gravatar_email']); | |||||
| } | |||||
| } | |||||
| $this->loggedin = true; | $this->loggedin = true; | ||||
| // Create user automatically on login | // Create user automatically on login | ||||
| $_ = $db->query('insert ignore into members (skymanager_id, name, username, email) VALUES (' . ((int) $this->uid) . ', "' . $db->sanitize($this->name) . '", "' . $db->sanitize($this->username) . '", ' . (empty($this->email) ? 'NULL' : '"' . $db->sanitize($this->email) . '"') . ')'); | |||||
| //$_ = $db->query('insert into members (skymanager_id, name, username, email) VALUES (' . ((int) $this->uid) . ', "' . $db->sanitize($this->name) . '", "' . $db->sanitize($this->username) . '", ' . (empty($this->email) ? 'NULL' : '"' . $db->sanitize($this->email) . '"') . ') ON DUPLICATE KEY UPDATE skymanager_id=skymanager_id'); | |||||
| $_ = $db->insert('members', ['skymanager_id', 'name', 'username', 'email'], [[((int) $this->uid), $this->name, $this->username, (empty($this->email) ? 'NULL' : $this->email)]], true); | |||||
| // Get voter ID | // Get voter ID | ||||
| $result = $db->fetchRow('select members.voting_id from members left join proxy on (members.voting_id=proxy.voting_id) where proxy.delegate_id is null and skymanager_id=' . ((int) $this->uid)); | $result = $db->fetchRow('select members.voting_id from members left join proxy on (members.voting_id=proxy.voting_id) where proxy.delegate_id is null and skymanager_id=' . ((int) $this->uid)); |
| $header->setAttribute('tagline', 'Online Ballot'); | $header->setAttribute('tagline', 'Online Ballot'); | ||||
| $header->output(); | $header->output(); | ||||
| $candidates = $db->fetchAssoc('select skymanager_id, name, username, md5(coalesce(email, "")) as `gravatar_hash` from members where voting_id is not null'); | |||||
| $candidates = $db->fetchAssoc('select skymanager_id, name, username, coalesce(email, "") as `gravatar_email` from members where voting_id is not null'); | |||||
| $votes = $db->fetchAssoc("select position from votes where member_id={$user->voterId()}"); | $votes = $db->fetchAssoc("select position from votes where member_id={$user->voterId()}"); | ||||
| $position = $db->fetchRow("select position as code, description as label from positions where active<>0 limit 1"); | $position = $db->fetchRow("select position as code, description as label from positions where active<>0 limit 1"); | ||||
| get_gravatar_assoc($candidates); | |||||
| foreach ($votes as &$vote) { | foreach ($votes as &$vote) { | ||||
| $vote = $vote['position']; | $vote = $vote['position']; | ||||
| } | } |
| flex-grow: 1; | flex-grow: 1; | ||||
| } | } | ||||
| form label.radio input { | |||||
| form label.radio input, form input[type=radio] { | |||||
| display: none; | display: none; | ||||
| } | } | ||||
| border-radius: 10px; | border-radius: 10px; | ||||
| } | } | ||||
| form label.radio input:checked+.radio-button-label { | |||||
| form label.radio input:checked+.radio-button-label, form input:checked+label.radio .radio-button-label { | |||||
| background-color: #000; | background-color: #000; | ||||
| color: #fff; | color: #fff; | ||||
| border: 2px solid #fff; | border: 2px solid #fff; |
| try { | try { | ||||
| $result = $db->query("INSERT INTO votes (candidate_id, position, member_id, vote_type, submitter_id) SELECT $candidate_selected, \"$ballot\", {$user->voterId()}, 'ONLINE', {$user->voterId()} UNION SELECT $candidate_selected, \"$ballot\", voting_id, 'PROXY ONLINE', delegate_id from proxy where delegate_id={$user->voterId()}"); | $result = $db->query("INSERT INTO votes (candidate_id, position, member_id, vote_type, submitter_id) SELECT $candidate_selected, \"$ballot\", {$user->voterId()}, 'ONLINE', {$user->voterId()} UNION SELECT $candidate_selected, \"$ballot\", voting_id, 'PROXY ONLINE', delegate_id from proxy where delegate_id={$user->voterId()}"); | ||||
| } catch (Throwable $ignore) {} | } catch (Throwable $ignore) {} | ||||
| $candidate = $db->fetchRow('select skymanager_id, name, username, md5(coalesce(email, "")) as `gravatar_hash` from members where skymanager_id=' . $candidate_selected); | |||||
| $candidate = $db->fetchRow('select skymanager_id, name, username, coalesce(email, "") as `gravatar_email` from members where skymanager_id=' . $candidate_selected); | |||||
| if ($result) { | if ($result) { | ||||
| $to = 'mf2022elec@gmail.com'; | $to = 'mf2022elec@gmail.com'; | ||||
| $from = 'noreply@tyzoid.com'; | $from = 'noreply@tyzoid.com'; | ||||
| <h4 class="section-heading">Candidate</h4> | <h4 class="section-heading">Candidate</h4> | ||||
| <div id="vote-profile" class="candidate"> | <div id="vote-profile" class="candidate"> | ||||
| <div class="profile-icon"> | <div class="profile-icon"> | ||||
| <img src="https://www.gravatar.com/avatar/<?= $candidate['gravatar_hash']; ?>.png?d=mp&s=64" /> | |||||
| <img src="https://www.gravatar.com/avatar/<?= md5($candidate['gravatar_email']); ?>.png?d=mp&s=64" /> | |||||
| </div> | </div> | ||||
| <div class="profile"> | <div class="profile"> | ||||
| <h2 class="profile-name"><?= $candidate['name']; ?></h2> | <h2 class="profile-name"><?= $candidate['name']; ?></h2> |