locales and start on logged in page

This commit is contained in:
trinkey 2025-03-22 12:11:59 -04:00
parent 664020ff37
commit 18e4d29063
9 changed files with 191 additions and 43 deletions

View file

@ -1,6 +1,6 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html lang="en">
<head> <head>
<title><?php <title><?php
if ($title) { if ($title) {
@ -8,7 +8,7 @@
} }
echo $site_name; echo $site_name;
?></title> ?></title>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1"> <meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="theme-color" content="#190b14"> <meta name="theme-color" content="#190b14">
@ -31,4 +31,3 @@
echo "<div class=\"err\">$err<div></div></div>"; echo "<div class=\"err\">$err<div></div></div>";
} }
?> ?>

View file

@ -3,6 +3,7 @@
// site config // site config
$site_name = "Todo List Manager"; $site_name = "Todo List Manager";
$signups = "invite"; // true, false, "invite" - treats it as true if no users currently exist, invite requires an admin to invite the user $signups = "invite"; // true, false, "invite" - treats it as true if no users currently exist, invite requires an admin to invite the user
$language = "en-US"; // an item from the lang folder. exclude the ".json"
$admin_users = array( $admin_users = array(
// "username1", "username2", ... // "username1", "username2", ...
@ -20,6 +21,8 @@ $db = pg_connect($db_info);
// default schema // default schema
// DON'T CHANGE THIS UNLESS YOU KNOW WHAT YOU'RE DOING!!! // DON'T CHANGE THIS UNLESS YOU KNOW WHAT YOU'RE DOING!!!
$lang = json_decode(file_get_contents("lang/$language.json"), true);
$default_format = "For %C<b>%t</b>%c - %d";
$default_schema = array( $default_schema = array(
"general" => array( "general" => array(
"name" => "General", "name" => "General",
@ -27,7 +30,7 @@ $default_schema = array(
"items" => array( "items" => array(
"general" => array( "general" => array(
"name" => "", "name" => "",
"display_format" => "For <b>%d</b> - %c" "display_format" => "For %C<b>%t</b>%c - %d"
) )
) )
) )

View file

@ -33,7 +33,12 @@ body {
font-size: 18px; font-size: 18px;
} }
input { h3 {
margin-bottom: 0;
}
input,
select {
background-color: var(--input-background); background-color: var(--input-background);
color: var(--color); color: var(--color);
border: 1px solid var(--border); border: 1px solid var(--border);
@ -42,7 +47,8 @@ input {
margin: 2px; margin: 2px;
} }
input:focus { input:focus,
select:focus {
outline: 2px solid var(--subtext); outline: 2px solid var(--subtext);
} }
@ -63,6 +69,15 @@ i {
color: var(--subtext); color: var(--subtext);
} }
ul {
margin: 0;
padding-left: 25px;
}
.right {
text-align: right;
}
.err { .err {
color: var(--red); color: var(--red);
border: 2px dashed var(--red); border: 2px dashed var(--red);

View file

@ -28,7 +28,7 @@ function is_logged_in(): false | array {
); );
if ($user_object && get_token($user_object[0]["username"], $user_object[0]["password_hash"]) === $token) { if ($user_object && get_token($user_object[0]["username"], $user_object[0]["password_hash"]) === $token) {
return $user_object; return $user_object[0];
} }
return false; return false;

106
index.php
View file

@ -1,19 +1,109 @@
<?php <?php
include "config.php";
include "helper.php";
include "boilerplate/head.php";
echo(is_logged_in()); include "config.php";
?> include "helper.php";
include "boilerplate/head.php";
<a href="login.php">Log In</a> $user = is_logged_in();
if ($user === false) {
echo "<a href=\"login.php\">{$lang['account']['log_in']}</a>";
<?php
$q = "SELECT count(*) FROM users LIMIT 1;"; $q = "SELECT count(*) FROM users LIMIT 1;";
if ($signups !== false || ($signups === false && pg_fetch_array(pg_query($db, $q))["count"] !== 0)) { if ($signups !== false || ($signups === false && pg_fetch_array(pg_query($db, $q))["count"] !== 0)) {
echo "- <a href=\"signup.php\">Sign Up</a>"; echo " - <a href=\"signup.php\">{$lang['account']['sign_up']}</a>";
} }
include "boilerplate/foot.php"; include "boilerplate/foot.php";
exit();
}
$select_options = "";
$todo_list = "";
$q = "SELECT * FROM section WHERE users='{$user['id']}' ORDER BY LOWER(name) ASC;";
$sections = pg_fetch_all(pg_query($db, $q));
foreach ($sections as $section) {
$q = "SELECT * FROM row WHERE section='{$section['id']}' ORDER BY LOWER(name) ASC;";
$rows = pg_fetch_all(pg_query($db, $q));
$show_subtitle = $section["show_subtitle"] === "t";
$enable_optgroup = $show_subtitle || count($rows) !== 1;
$todo_list .= "<h3>" . htmlspecialchars($section["name"]) . "</h3>";
if ($enable_optgroup) {
$select_options .= "<optgroup label=\"" . htmlspecialchars($section["name"]) . "\">";
}
$first = true;
foreach ($rows as $row) {
$select_options .= "<option value=\"{$section['id']}-{$row['id']}\">";
if ($first && !$show_subtitle) {
$select_options .= htmlspecialchars($section["name"]);
} else {
$select_options .= htmlspecialchars($row["name"]);
$todo_list .= "<strong>" . htmlspecialchars($row["name"]) . "</strong>";
}
$q = "SELECT * FROM item WHERE row='{$row['id']}' ORDER BY date ASC, LOWER(description) ASC;";
$items = pg_fetch_all(pg_query($db, $q));
$todo_list .= "<ul>";
if (count($items) === 0) {
$todo_list .= "<li><i>{$lang['list']['none']}</i></li>";
} else {
foreach ($items as $item) {
$todo_list .= "<li>" . str_replace("%d", htmlspecialchars($item["description"]), $row["display_format"]) . "</li>";
}
}
$todo_list .= "</ul>";
$select_options .= "</option>";
$first = false;
}
if ($enable_optgroup) {
$select_options .= "</optgroup>";
}
}
?>
<div id="container">
<h2><?php echo $lang["add"]["title"]; ?></h2>
<form method="POST">
<table>
<tr>
<td class="right"><label for="section"><?php echo $lang["add"]["section"]; ?></label></td>
<td>
<select required id="section" name="section">
<?php echo $select_options; ?>
</select>
</td>
</tr>
<tr>
<td class="right"><label for="date"><?php echo $lang["add"]["date"]; ?></label></td>
<td><input type="date" name="date" id="date" required></td>
</tr>
<tr>
<td class="right"><label for="description"><?php echo $lang["add"]["description"]; ?></label></td>
<td><input autofocus maxlength="256" placeholder="<?php echo $lang["add"]["description_placeholder"]; ?>" name="description" id="description" required></td>
</tr>
<tr>
<td></td>
<td><input type="submit" value="<?php echo $lang["add"]["button"]; ?>"></td>
</tr>
</table>
</form>
<h2><?php echo $lang["list"]["title"]; ?></h2>
<?php echo $todo_list; ?>
</div>
<?php
include "boilerplate/foot.php";
?> ?>

36
lang/en-US.json Normal file
View file

@ -0,0 +1,36 @@
{
"account": {
"errors": {
"bad_request": "Bad request",
"username_length": "Username must be between 1 and 64 characters",
"username_characters": "Username can only include a-z, 0-9, _, and -",
"user_not_found": "User '%u' not found",
"user_exists": "User '%u' already exists",
"incorrect_password": "Incorrect password",
"password_match": "Passwords don't match"
},
"log_in": "Log In",
"log_in_instead": "Log in instead?",
"sign_up": "Sign Up",
"sign_up_instead": "Sign up instead?",
"username": "Username",
"password": "Password",
"verify": "Verify password"
},
"add": {
"title": "Add Item",
"section": "Section:",
"date": "Date:",
"description": "Description:",
"description_placeholder": "Important thing",
"button": "Add"
},
"list": {
"title": "Current Todo List",
"remove": "Completed",
"none": "None"
}
}

View file

@ -15,18 +15,18 @@
$p = $_POST["password"]; $p = $_POST["password"];
if (!($u && $p)) { if (!($u && $p)) {
$err = "Bad request, missing username or password parameter"; $err = $lang["account"]["errors"]["bad_request"];
} else if (strlen($u) > 64 || strlen($u) === 0) { } else if (strlen($u) > 64 || strlen($u) === 0) {
$err = "Username must be 1-64 chars"; $err = $lang["account"]["errors"]["username_length"];
} else if (!preg_match("/^[a-z0-9_-]{1,64}$/", $u)) { } else if (!preg_match("/^[a-z0-9_-]{1,64}$/", $u)) {
$err = "Username can only include a-z, 0-9, _, and -"; $err = $lang["account"]["errors"]["username_characters"];
} else { } else {
$query = "SELECT password_hash FROM users WHERE username='$u' LIMIT 1;"; $query = "SELECT password_hash FROM users WHERE username='$u' LIMIT 1;";
$response = pg_query($db, $query); $response = pg_query($db, $query);
if (pg_num_rows($response) === 0) { if (pg_num_rows($response) === 0) {
$err = "User '" . htmlspecialchars($u) . "' not found"; $err = str_replace("%u", htmlspecialchars($u), $lang["account"]["errors"]["user_not_found"]);
} else { } else {
$user = pg_fetch_array($response); $user = pg_fetch_array($response);
if (password_verify($p, $user["password_hash"])) { if (password_verify($p, $user["password_hash"])) {
@ -38,27 +38,27 @@
header("Location: index.php"); header("Location: index.php");
exit(); exit();
} else { } else {
$err = "Incorrect password"; $err = $lang["account"]["errors"]["incorrect_password"];
} }
} }
} }
} }
$title = "Log In"; $title = $lang["account"]["log_in"];
include "boilerplate/head.php"; include "boilerplate/head.php";
?> ?>
<form method="POST"> <form method="POST">
<div><input placeholder="Username" name="username" value="<?php echo htmlspecialchars($u); ?>" maxlength="64" required></div> <div><input placeholder="<?php echo $lang["account"]["username"]; ?>" name="username" value="<?php echo htmlspecialchars($u); ?>" maxlength="64" required></div>
<div><input placeholder="Password" name="password" type="password" value="<?php echo htmlspecialchars($p); ?>" required></div> <div><input placeholder="<?php echo $lang["account"]["password"]; ?>" name="password" type="password" value="<?php echo htmlspecialchars($p); ?>" required></div>
<div><input type="submit" value="Log in"></div> <div><input type="submit" value="<?php echo $lang["account"]["log_in"]; ?>"></div>
</form> </form>
<?php <?php
$q = "SELECT count(*) FROM users LIMIT 1;"; $q = "SELECT count(*) FROM users LIMIT 1;";
if ($signups !== false || ($signups === false && pg_fetch_array(pg_query($db, $q))["count"] !== 0)) { if ($signups !== false || ($signups === false && pg_fetch_array(pg_query($db, $q))["count"] !== 0)) {
echo "<p><a href=\"signup.php\">Sign up instead?</a></p>"; echo "<p><a href=\"signup.php\">{$lang['account']['sign_up_instead']}</a></p>";
} }
include "boilerplate/foot.php"; include "boilerplate/foot.php";

View file

@ -6,7 +6,10 @@ $queries = array(
"CREATE TABLE IF NOT EXISTS users ( "CREATE TABLE IF NOT EXISTS users (
id VARCHAR(64) PRIMARY KEY, id VARCHAR(64) PRIMARY KEY,
username VARCHAR(64) UNIQUE NOT NULL, username VARCHAR(64) UNIQUE NOT NULL,
password_hash VARCHAR(256) NOT NULL password_hash VARCHAR(256) NOT NULL,
enable_colors BOOLEAN NOT NULL,
yellow_threshold INT NOT NULL,
gray_threshold INT NOT NULL
);", );",
"CREATE TABLE IF NOT EXISTS section ( "CREATE TABLE IF NOT EXISTS section (
id VARCHAR(64) PRIMARY KEY, id VARCHAR(64) PRIMARY KEY,
@ -17,8 +20,8 @@ $queries = array(
"CREATE TABLE IF NOT EXISTS row ( "CREATE TABLE IF NOT EXISTS row (
id VARCHAR(64) PRIMARY KEY, id VARCHAR(64) PRIMARY KEY,
section VARCHAR(64) NOT NULL REFERENCES section(id), section VARCHAR(64) NOT NULL REFERENCES section(id),
name VARCHAR(128), name VARCHAR(128), -- ignored if first item and show_subtitle is enabled
display_format VARCHAR(128) NOT NULL -- %d - date, %c - description display_format VARCHAR(128) NOT NULL -- %t - date, %d - description, %C - start color, %c - end color
);", );",
"CREATE TABLE IF NOT EXISTS item ( "CREATE TABLE IF NOT EXISTS item (
id VARCHAR(64) PRIMARY KEY, id VARCHAR(64) PRIMARY KEY,

View file

@ -16,13 +16,13 @@
$v = $_POST["verify"]; $v = $_POST["verify"];
if ($v != $p) { if ($v != $p) {
$err = "Passwords don't match"; $err = $lang["account"]["errors"]["password_match"];
} else if (!($u && $p && $v)) { } else if (!($u && $p && $v)) {
$err = "Bad request, missing username, password, or verify parameter"; $err = $lang["account"]["errors"]["bad_request"];
} else if (strlen($u) > 64 || strlen($u) === 0) { } else if (strlen($u) > 64 || strlen($u) === 0) {
$err = "Username must be 1-64 chars"; $err = $lang["account"]["errors"]["username_length"];
} else if (!preg_match("/^[a-z0-9_-]{1,64}$/", $u)) { } else if (!preg_match("/^[a-z0-9_-]{1,64}$/", $u)) {
$err = "Username can only include a-z, 0-9, _, and -"; $err = $lang["account"]["errors"]["username_characters"];
} else { } else {
$query = "SELECT count(*) FROM users WHERE username='$u' LIMIT 1;"; $query = "SELECT count(*) FROM users WHERE username='$u' LIMIT 1;";
@ -35,11 +35,14 @@
$user_parameters = array( $user_parameters = array(
"id" => $user_id, "id" => $user_id,
"username" => $u, "username" => $u,
"password_hash" => $pw_hash "password_hash" => $pw_hash,
"enable_colors" => true,
"yellow_threshold" => 2,
"gray_threshold" => 60
); );
pg_insert($db, "users", $user_parameters); pg_insert($db, "users", $user_parameters);
foreach ($default_schema as $section_id => $section_data) { foreach ($default_schema as $section_id => $section_data) {
$section_id = generate_id(); $section_id = generate_id();
$section_parameters = array( $section_parameters = array(
@ -66,28 +69,27 @@
$token = get_token($u, $pw_hash); $token = get_token($u, $pw_hash);
setcookie( setcookie(
"token", "token", $token,
$token,
time() + 60 * 60 * 24 * 30 * 265 // 1 year from now time() + 60 * 60 * 24 * 30 * 265 // 1 year from now
); );
header("Location: index.php"); header("Location: index.php");
exit(); exit();
} else { } else {
$err = "User '" . htmlspecialchars($u) . "' already exists"; $err = str_replace("%u", htmlspecialchars($u), $lang["account"]["errors"]["user_exists"]);
} }
} }
} }
$title = "Sign Up"; $title = $lang["account"]["sign_up"];
include "boilerplate/head.php"; include "boilerplate/head.php";
?> ?>
<form method="POST"> <form method="POST">
<div><input placeholder="Username" name="username" value="<?php echo htmlspecialchars($u); ?>" maxlength="64" required></div> <div><input placeholder="<?php echo $lang["account"]["username"]; ?>" name="username" value="<?php echo htmlspecialchars($u); ?>" maxlength="64" required></div>
<div><input placeholder="Password" name="password" type="password" value="<?php echo htmlspecialchars($p); ?>" required></div> <div><input placeholder="<?php echo $lang["account"]["password"]; ?>" name="password" type="password" value="<?php echo htmlspecialchars($p); ?>" required></div>
<div><input placeholder="Verify Password" name="verify" type="password" required></div> <div><input placeholder="<?php echo $lang["account"]["verify"]; ?>" name="verify" type="password" required></div>
<div><input type="submit" value="Sign Up"></div> <div><input type="submit" value="<?php echo $lang["account"]["sign_up"]; ?>"></div>
<p><a href="login.php">Log in instead?</a></p> <p><a href="login.php"><?php echo $lang["account"]["log_in_instead"]; ?></a></p>
</form> </form>
<?php <?php