Initial commit
This commit is contained in:
+20
@@ -0,0 +1,20 @@
|
||||
# Windows thumbnails
|
||||
Thumbs.db
|
||||
Thumbs.db:encryptable
|
||||
ehthumbs.db
|
||||
ehthumbs_vista.db
|
||||
|
||||
# Volatile files
|
||||
backups/*
|
||||
profiles/*
|
||||
|
||||
# Files that are not appropriate for git
|
||||
created.txt
|
||||
description.txt
|
||||
title.txt
|
||||
anti_doxx.json
|
||||
|
||||
# Sensitive information
|
||||
api_key.php
|
||||
local_secret.php
|
||||
account.php
|
||||
@@ -0,0 +1,635 @@
|
||||
<?php
|
||||
session_start();
|
||||
|
||||
// error reporting
|
||||
error_reporting(E_ALL);
|
||||
ini_set('display_errors', 1);
|
||||
|
||||
// libs
|
||||
require "template_engine.php";
|
||||
|
||||
// globals
|
||||
$session_username_key = "todo-username";
|
||||
$session_password_key = "todo-password";
|
||||
$session_register_notice_key = "todo-register-notice";
|
||||
$get_register_key = "reg";
|
||||
$profile_template_path = "new_profile.json";
|
||||
$profile_extension = ".json";
|
||||
$profiles_path = "profiles/";
|
||||
$backup_path = "backups/";
|
||||
$model = array();
|
||||
|
||||
// setup
|
||||
if (!file_exists("backups"))
|
||||
{
|
||||
mkdir("backups");
|
||||
}
|
||||
if (!file_exists("profiles"))
|
||||
{
|
||||
mkdir("profiles");
|
||||
}
|
||||
|
||||
// post
|
||||
if (!empty($_POST["intent"]))
|
||||
{
|
||||
if ($_POST["intent"] == "login" && !empty($_POST["uname"]) && !empty($_POST["pwd"]))
|
||||
{
|
||||
$username = $_POST["uname"];
|
||||
$password = $_POST["pwd"];
|
||||
|
||||
$_SESSION[$session_username_key] = $username;
|
||||
$_SESSION[$session_password_key] = $password;
|
||||
redirect("./");
|
||||
exit(0);
|
||||
}
|
||||
else if ($_POST["intent"] == "register" && !empty($_POST["uname"]) && !empty($_POST["pwd"]) && !empty($_POST["pwd2"]))
|
||||
{
|
||||
unset($_SESSION[$session_register_notice_key]);
|
||||
|
||||
$username = $_POST["uname"];
|
||||
$password = $_POST["pwd"];
|
||||
$password2 = $_POST["pwd2"];
|
||||
|
||||
$valid = false;
|
||||
if (preg_match("/^[A-Za-z0-9]{3,16}$/", $username) === 1)
|
||||
{
|
||||
$valid = true;
|
||||
}
|
||||
|
||||
$profile_file = $profiles_path . strtolower($username) . $profile_extension;
|
||||
if (!$valid)
|
||||
{
|
||||
$_SESSION[$session_register_notice_key] = "Error: Invalid username.";
|
||||
redirect("./?{$get_register_key}");
|
||||
}
|
||||
else if (file_exists($profile_file))
|
||||
{
|
||||
$_SESSION[$session_register_notice_key] = "Error: Username is already taken.";
|
||||
redirect("./?{$get_register_key}");
|
||||
}
|
||||
else if (preg_match("/.{8,265}/", $password) !== 1)
|
||||
{
|
||||
$_SESSION[$session_register_notice_key] = "Error: Invalid password.";
|
||||
redirect("./?{$get_register_key}");
|
||||
}
|
||||
else if ($password != $password2)
|
||||
{
|
||||
$_SESSION[$session_register_notice_key] = "Error: Passwords don't match.";
|
||||
redirect("./?{$get_register_key}");
|
||||
}
|
||||
else
|
||||
{
|
||||
$new_profile = json_decode(file_get_contents($profile_template_path), true);
|
||||
$new_profile["file"] = $profile_file;
|
||||
$new_profile["username"] = $username;
|
||||
$new_profile["password"] = $password;
|
||||
|
||||
file_put_contents($profile_file, json_encode($new_profile));
|
||||
|
||||
$_SESSION[$session_username_key] = $username;
|
||||
$_SESSION[$session_password_key] = $password;
|
||||
redirect("./");
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($_POST["logout"]) || isset($_GET["logout"]))
|
||||
{
|
||||
unset($_SESSION[$session_username_key]);
|
||||
unset($_SESSION[$session_password_key]);
|
||||
redirect("./");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
// session
|
||||
if (isset($_GET[$get_register_key]))
|
||||
{
|
||||
$notice = "";
|
||||
if (!empty($_SESSION[$session_register_notice_key]))
|
||||
{
|
||||
$notice = $_SESSION[$session_register_notice_key];
|
||||
}
|
||||
|
||||
echo register_form($notice);
|
||||
exit(0);
|
||||
}
|
||||
else if (!empty($_SESSION[$session_username_key]))
|
||||
{
|
||||
$username = $_SESSION[$session_username_key];
|
||||
$password = "";
|
||||
if (!empty($_SESSION[$session_password_key])) $password = $_SESSION[$session_password_key];
|
||||
|
||||
$profiles_whitelist = array();
|
||||
$glob = glob($profiles_path . "*{$profile_extension}");
|
||||
foreach ($glob as $key => $profile)
|
||||
{
|
||||
$profiles_whitelist[$key] = strtolower(pathinfo($profile)["filename"]);
|
||||
}
|
||||
|
||||
if (!in_array(strtolower($username), $profiles_whitelist))
|
||||
{
|
||||
echo login_form("Error: username not found.");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
$index = array_search(strtolower($username), $profiles_whitelist);
|
||||
$raw_json = file_get_contents($glob[$index]);
|
||||
$json = json_decode($raw_json, true);
|
||||
|
||||
if ($json["username"] !== $username)
|
||||
{
|
||||
echo login_form("Error: wrong username.");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if ($json["password"] !== $password)
|
||||
{
|
||||
echo login_form("Error: wrong password.");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if ($json["username"] === $username && $json["password"] === $password) // just to be safe, the if is here
|
||||
{
|
||||
$model = $json;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
echo login_form();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
|
||||
// actual
|
||||
if (isset($_POST["backup"]) || isset($_GET["backup"]))
|
||||
{
|
||||
$time_part = "-" . time();
|
||||
$backup_filename = $backup_path . strtolower($model["username"]) . $time_part . $profile_extension;
|
||||
file_put_contents($backup_filename, json_encode($model));
|
||||
redirect("./");
|
||||
exit(0);
|
||||
}
|
||||
else if (!empty($_POST["restore"]) || !empty($_GET["restore"]))
|
||||
{
|
||||
$timestamp = 0;
|
||||
if (!empty($_POST["restore"]))
|
||||
{
|
||||
$timestamp = $_POST["restore"];
|
||||
}
|
||||
else if (!empty($_GET["restore"]))
|
||||
{
|
||||
$timestamp = $_GET["restore"];
|
||||
}
|
||||
|
||||
$backup_prefix = $backup_path . strtolower($model["username"]) . "-";
|
||||
$backup_filename = $backup_prefix . strval($timestamp) . $profile_extension;
|
||||
$backups = glob($backup_prefix . "*" . $profile_extension);
|
||||
if (in_array($backup_filename, $backups) && file_exists($backup_filename))
|
||||
{
|
||||
$backup_json = json_decode(file_get_contents($backup_filename), true);
|
||||
$model["list"] = $backup_json["list"];
|
||||
}
|
||||
|
||||
$file = $model["file"];
|
||||
$raw_json = json_encode($model);
|
||||
file_put_contents($file, $raw_json);
|
||||
redirect("./");
|
||||
exit(0);
|
||||
}
|
||||
else if (isset($_POST["delete-restore"]) || isset($_GET["delete-restore"]))
|
||||
{
|
||||
$timestamp = 0;
|
||||
if (!empty($_POST["delete-restore"]))
|
||||
{
|
||||
$timestamp = $_POST["delete-restore"];
|
||||
}
|
||||
else if (!empty($_GET["delete-restore"]))
|
||||
{
|
||||
$timestamp = $_GET["delete-restore"];
|
||||
}
|
||||
|
||||
$backup_prefix = $backup_path . strtolower($model["username"]) . "-";
|
||||
$backup_filename = $backup_prefix . strval($timestamp) . $profile_extension;
|
||||
$backup_new_filename = $backup_path . "deleted-" . strtolower($model["username"]) . "-" . strval($timestamp) . $profile_extension;
|
||||
$backups = glob($backup_prefix . "*" . $profile_extension);
|
||||
if (in_array($backup_filename, $backups) && file_exists($backup_filename))
|
||||
{
|
||||
rename($backup_filename, $backup_new_filename);
|
||||
}
|
||||
redirect("./?restore-list");
|
||||
exit(0);
|
||||
}
|
||||
else if (isset($_POST["restore-list"]) || isset($_GET["restore-list"]))
|
||||
{
|
||||
$backups = array();
|
||||
$backup_prefix = $backup_path . strtolower($model["username"]) . "-";
|
||||
$glob = glob($backup_prefix . "*" . $profile_extension);
|
||||
natcasesort($glob);
|
||||
$glob = array_reverse($glob);
|
||||
foreach ($glob as $backup)
|
||||
{
|
||||
$timestamp_and_ext = str_replace($backup_prefix, "", $backup);
|
||||
$timestamp = intval(str_replace($profile_extension, "", $timestamp_and_ext));
|
||||
$datestamp = date("r", $timestamp);
|
||||
$backups[] = array(
|
||||
"timestamp" => $timestamp,
|
||||
"date" => $datestamp,
|
||||
"file" => $backup,
|
||||
);
|
||||
}
|
||||
|
||||
echo restore_form($backups);
|
||||
exit(0);
|
||||
}
|
||||
else if (!empty($_POST["action"]))
|
||||
{
|
||||
$action = $_POST["action"];
|
||||
$modified = false;
|
||||
if ($action === "add")
|
||||
{
|
||||
if (!empty($_POST["todo_title"]))
|
||||
{
|
||||
$title = $_POST["todo_title"];
|
||||
|
||||
$description = "";
|
||||
if (!empty($_POST["todo_description"]))
|
||||
{
|
||||
$description = $_POST["todo_description"];
|
||||
}
|
||||
|
||||
$deadline = "";
|
||||
if (!empty($_POST["todo_deadline"]))
|
||||
{
|
||||
$deadline = strtotime($_POST["todo_deadline"]);
|
||||
}
|
||||
|
||||
$category = "";
|
||||
if (!empty($_POST["todo_category"]))
|
||||
{
|
||||
$category = $_POST["todo_category"];
|
||||
}
|
||||
|
||||
$now = time();
|
||||
|
||||
if (empty($model["list"]))
|
||||
{
|
||||
$model["list"] = array();
|
||||
}
|
||||
|
||||
$model["list"][] = array(
|
||||
"title" => $title,
|
||||
"description" => $description,
|
||||
"deadline" => $deadline,
|
||||
"category" => $category,
|
||||
"created" => $now,
|
||||
"modified" => $now,
|
||||
);
|
||||
|
||||
$modified = true;
|
||||
}
|
||||
}
|
||||
else if ($action === "remove")
|
||||
{
|
||||
if (isset($_POST["todo_item_id"]))
|
||||
{
|
||||
$id = $_POST["todo_item_id"];
|
||||
|
||||
if (isset($model["list"][$id]))
|
||||
{
|
||||
unset($model["list"][$id]);
|
||||
$model["list"] = array_values($model["list"]); // renumber
|
||||
|
||||
$modified = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ($action === "delay" && !empty($_POST["delay_by"]))
|
||||
{
|
||||
$delay_by = $_POST["delay_by"];
|
||||
if (isset($_POST["todo_item_id"]))
|
||||
{
|
||||
$id = $_POST["todo_item_id"];
|
||||
|
||||
if (isset($model["list"][$id]) && !empty($model["list"][$id]["deadline"]))
|
||||
{
|
||||
$now = time();
|
||||
|
||||
$deadline = strtotime("@".$model["list"][$id]["deadline"]);
|
||||
$deadline_new = strtotime($delay_by, $deadline);
|
||||
$model["list"][$id]["deadline"] = $deadline_new;
|
||||
$model["list"][$id]["modified"] = $now;
|
||||
$modified = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($modified)
|
||||
{
|
||||
if (!empty($model["file"]) && file_exists($model["file"]))
|
||||
{
|
||||
$file = $model["file"];
|
||||
$raw_json = json_encode($model);
|
||||
file_put_contents($file, $raw_json);
|
||||
redirect("./");
|
||||
exit(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
login_form("Internal error!");
|
||||
exit(0);
|
||||
exit(0);
|
||||
exit(0);
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
echo todo_list($model);
|
||||
}
|
||||
|
||||
// renderers
|
||||
function todo_list(array $model = []) : string
|
||||
{
|
||||
$list = array();
|
||||
if (!empty($model["list"]))
|
||||
{
|
||||
$list = $model["list"];
|
||||
}
|
||||
|
||||
$te = new template_engine();
|
||||
$te->set_block("TITLE", $model["username"] . "'s to-do List");
|
||||
$te->append_block_template("CONTENT", "TODO_LIST");
|
||||
$te->append_block_template("CONTENT", "ADD_FORM");
|
||||
$te->append_block_template("CONTENT", "NAVBAR");
|
||||
|
||||
$categories = array();
|
||||
|
||||
$now = time();
|
||||
$now_dayth = intval(date("z", $now));
|
||||
|
||||
if (!empty($list))
|
||||
{
|
||||
foreach ($list as $key => $item)
|
||||
{
|
||||
if (!empty($item["category"]))
|
||||
{
|
||||
$category = strtolower($item["category"]);
|
||||
$category_name = $item["category"];
|
||||
$category_basis = "custom";
|
||||
}
|
||||
else if (!empty($item["deadline"]))
|
||||
{
|
||||
$deadline = $item["deadline"];
|
||||
|
||||
$ymd = date("y-m-d", $deadline);
|
||||
$day = date("l", $deadline);
|
||||
$dayth = intval(date("z", $deadline));
|
||||
|
||||
$category = $ymd;
|
||||
if ($dayth - $now_dayth == 0)
|
||||
{
|
||||
$category_name = "Today ({$day})";
|
||||
}
|
||||
else if ($dayth - $now_dayth == 1)
|
||||
{
|
||||
$category_name = "Tomorrow ({$day})";
|
||||
}
|
||||
else if ($dayth - $now_dayth == -1)
|
||||
{
|
||||
$category_name = "Yesterday ({$day})";
|
||||
}
|
||||
else if ($dayth - $now_dayth < -7)
|
||||
{
|
||||
$category_name = "{$day} ({$ymd})";
|
||||
}
|
||||
else if ($dayth - $now_dayth < 0)
|
||||
{
|
||||
$category_name = "Last " . $day;
|
||||
}
|
||||
else if ($dayth - $now_dayth < 7)
|
||||
{
|
||||
$category_name = $day;
|
||||
}
|
||||
else if ($dayth - $now_dayth < 14)
|
||||
{
|
||||
$category_name = "Next " . $day;
|
||||
}
|
||||
else
|
||||
{
|
||||
$category_name = "{$day} ({$ymd})";
|
||||
}
|
||||
|
||||
$category_basis = "date";
|
||||
}
|
||||
else
|
||||
{
|
||||
$category = "uncategorized";
|
||||
$category_name = "Uncategorized";
|
||||
$category_basis = "default";
|
||||
}
|
||||
|
||||
if (empty($categories[$category]))
|
||||
{
|
||||
$categories[$category] = array(
|
||||
"title" => $category_name,
|
||||
"basis" => $category_basis,
|
||||
"list" => array(),
|
||||
);
|
||||
}
|
||||
|
||||
$categories[$category]["list"][$key] = $item;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!empty($categories))
|
||||
{
|
||||
$te->set_block("TODO_ITEMS", "");
|
||||
}
|
||||
$te->set_block("DATALIST_AUTOFILLS", "");
|
||||
|
||||
ksort($categories);
|
||||
|
||||
$titles = array();
|
||||
foreach ($categories as $category_key => $category)
|
||||
{
|
||||
$category_name = $category["title"];
|
||||
$category_basis = $category["basis"];
|
||||
$te->set_block("TODO_CATEGORY_ITEMS", "");
|
||||
|
||||
if ($category_basis == "custom")
|
||||
{
|
||||
$te->append_argumented_block("DATALIST_AUTOFILLS", "DATALIST_AUTOFILL", [
|
||||
"DATALIST_AUTOFILL_DATA" => $category_name,
|
||||
]);
|
||||
}
|
||||
|
||||
foreach ($category["list"] as $key => $item)
|
||||
{
|
||||
$titles[] = $item["title"];
|
||||
|
||||
if (empty($item["description"]))
|
||||
{
|
||||
$te->set_block_template("TODO_ITEM_SUMMARY", "TODO_ITEM_SUMMARY_NODESC");
|
||||
}
|
||||
else
|
||||
{
|
||||
$te->set_block_template("TODO_ITEM_SUMMARY", "TODO_ITEM_SUMMARY_DESC");
|
||||
}
|
||||
|
||||
$te->append_argumented_block("TODO_CATEGORY_ITEMS", "TODO_ITEM", [
|
||||
"TODO_ITEM_ID" => $key,
|
||||
"TODO_ITEM_TITLE" => $item["title"],
|
||||
"TODO_ITEM_DESCRIPTION" => $item["description"],
|
||||
]);
|
||||
}
|
||||
|
||||
$te->append_argumented_block("TODO_ITEMS", "TODO_CATEGORY", [
|
||||
"TODO_CATEGORY_ID" => $category_key,
|
||||
"TODO_CATEGORY_TITLE" => $category_name,
|
||||
]);
|
||||
}
|
||||
$te->append_argumented_block("DATALISTS", "DATALIST", [
|
||||
"DATALIST_ID" => "todo_categories",
|
||||
]);
|
||||
|
||||
$te->set_block("DATALIST_AUTOFILLS", "");
|
||||
foreach (array_unique($titles) as $title)
|
||||
{
|
||||
$te->append_argumented_block("DATALIST_AUTOFILLS", "DATALIST_AUTOFILL", [
|
||||
"DATALIST_AUTOFILL_DATA" => $title,
|
||||
]);
|
||||
}
|
||||
$te->append_argumented_block("DATALISTS", "DATALIST", [
|
||||
"DATALIST_ID" => "todo_titles",
|
||||
]);
|
||||
|
||||
// $te->append_block("TODO_ITEMS", "<pre><details open><summary>dump</summary>".print_r($model, true)."</details></pre>");
|
||||
// $te->append_block("TODO_ITEMS", "<pre><details open><summary>dump</summary>".print_r($categories, true)."</details></pre>");
|
||||
return $te->get_html();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
function restore_form(array $backups = array()) : string
|
||||
{
|
||||
$te = new template_engine();
|
||||
|
||||
$number = count($backups);
|
||||
foreach($backups as $key => $backup)
|
||||
{
|
||||
$title = "#" . $backup["timestamp"] . ": " . $backup["date"];
|
||||
$te->append_argumented_block("RESTORE_ITEMS", "RESTORE_ITEM", [
|
||||
"RESTORE_ITEM_TIMESTAMP" => $backup["timestamp"],
|
||||
"RESTORE_ITEM_TITLE" => $title,
|
||||
]);
|
||||
}
|
||||
$te->set_block("TITLE", "Zovguran: To-Do");
|
||||
$te->append_block_template("CONTENT", "RESTORE_CONTAINER");
|
||||
return $te->get_html();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
function login_form(string $notice = "") : string
|
||||
{
|
||||
$te = new template_engine();
|
||||
$te->set_block("TITLE", "Zovguran: To-Do");
|
||||
$te->set_block("LOGIN_NOTICE", $notice);
|
||||
$te->append_block_template("CONTENT", "LOGIN_FORM");
|
||||
return $te->get_html();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
function register_form(string $notice = "") : string
|
||||
{
|
||||
$te = new template_engine();
|
||||
$te->set_block("TITLE", "Sign Up");
|
||||
$te->set_block("REGISTER_NOTICE", $notice);
|
||||
$te->append_block_template("CONTENT", "REGISTER_FORM");
|
||||
return $te->get_html();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
function redirect($path)
|
||||
{
|
||||
header('Location: ' . $path);
|
||||
exit(0); // TERMINATE!
|
||||
}
|
||||
|
||||
// function reference($model)
|
||||
// {
|
||||
// $te = new template_engine();
|
||||
// $te->get_block_names();
|
||||
// $te->set_block("TITLE", $model->get_title());
|
||||
// $te->set_block("PREVIEW_WIDTH", $model->get_preview_width());
|
||||
// $te->set_block("PREVIEW_HEIGHT", $model->get_preview_height());
|
||||
// $te->set_block("PREVIEW_WIDTH_BORDER", $model->get_preview_width() + site_data::$border_size*2);
|
||||
// $te->set_block("PREVIEW_HEIGHT_BORDER", $model->get_preview_height() + site_data::$border_size*2);
|
||||
|
||||
// foreach ($model->get_border_colors() as $info)
|
||||
// {
|
||||
// $te->append_argumented_block("EXTRA_STYLES", "BORDER_COLOR_STYLE", [
|
||||
// "BORDER_CLASS" => $info["id"],
|
||||
// "BORDER_COLOR" => $info["color"]
|
||||
// ]);
|
||||
// }
|
||||
|
||||
// if ($model->get_mode() == "image" || $model->get_mode() == "collection_image")
|
||||
// {
|
||||
// $te->set_block_template("PREVIEW_MAIN_CLASS", "PREVIEW_SINGLE_CLASS");
|
||||
// }
|
||||
|
||||
// if ($model->has_previews())
|
||||
// {
|
||||
// $te->append_block_template("CONTENT", "PREVIEW_CONTAINER");
|
||||
// foreach ($model->get_contents() as $preview)
|
||||
// {
|
||||
// if (!isset($preview["class"])) $preview["class"] = ""; // should not be here
|
||||
// $te->append_argumented_block("PREVIEWS", "PREVIEW", [
|
||||
// "PREVIEW_LINK" => $preview["link"],
|
||||
// "PREVIEW_CLASS" => $preview["class"],
|
||||
// "PREVIEW_IMAGE" => $preview["image"]
|
||||
// ]);
|
||||
// }
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// $te->append_block_template("CONTENT", "ITEM_CONTAINER");
|
||||
// foreach ($model->get_contents() as $item_id => $item)
|
||||
// {
|
||||
// if (isset($item["text"])) $text = $item["text"];
|
||||
// else if (isset($item["id"])) $text = $item["id"];
|
||||
// else $text = $item_id;
|
||||
|
||||
// $te->append_argumented_block("ITEMS", "ITEM", [
|
||||
// "ITEM_LINK" => $item["link"],
|
||||
// "ITEM_TEXT" => $text
|
||||
// ]);
|
||||
// }
|
||||
// }
|
||||
|
||||
// $link_categories = $model->get_navbar_links();
|
||||
// ksort($link_categories); // sort
|
||||
// foreach ($link_categories as $key => $link_category)
|
||||
// {
|
||||
// foreach ($link_category as $key2 => $link)
|
||||
// {
|
||||
// if ($link["id"] == null) $link["id"] = $key . "-" . $key2;
|
||||
|
||||
// if ($key == "hidden") $class = "hidden";
|
||||
// else $class = "";
|
||||
|
||||
// $te->append_argumented_block("NAVBAR_ITEMS", "NAVBAR_LINK", [
|
||||
// "NAVBAR_LINK_TEXT" => $link["text"],
|
||||
// "NAVBAR_LINK_LINK" => $link["link"],
|
||||
// "NAVBAR_LINK_CLASS" => $class,
|
||||
// "NAVBAR_LINK_ID" => $link["id"]
|
||||
// ]);
|
||||
// }
|
||||
// if ($key != array_key_last($link_categories))
|
||||
// $te->append_block_template("NAVBAR_ITEMS", "NAVBAR_SPACER");
|
||||
// }
|
||||
|
||||
// echo $te->get_html();
|
||||
// }
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"file":"FILE_PATH",
|
||||
"username":"USERNAME",
|
||||
"password":"PASSWORD",
|
||||
"list":[]
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
<?php
|
||||
class template_engine
|
||||
{
|
||||
public static $base_block = "BASE";
|
||||
|
||||
public $blocks = array();
|
||||
public $templates = array();
|
||||
|
||||
private $block_pattern = '/\w*/';
|
||||
private $block_pattern_chars = '[\w,_,-]*';
|
||||
private $block_pattern_prefix = '{{ ';
|
||||
private $block_pattern_suffix = ' }}';
|
||||
private $source_files = array();
|
||||
private $template_folder = "./templates/";
|
||||
|
||||
private function init_templates(array $source_files) : void
|
||||
{
|
||||
foreach ($source_files as $name => $source)
|
||||
{
|
||||
$this->templates[$name] = file_get_contents($source);
|
||||
}
|
||||
}
|
||||
|
||||
private function discover_templates(string $template_folder) : void
|
||||
{
|
||||
foreach (glob($template_folder."*") as $file)
|
||||
{
|
||||
$info = pathinfo($file);
|
||||
$template_id = strtoupper(str_replace("-", "_", $info["filename"]));
|
||||
$this->source_files[$template_id] = $file;
|
||||
}
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->block_pattern = '/' . $this->block_pattern_prefix . $this->block_pattern_chars . $this->block_pattern_suffix . '/';
|
||||
$this->discover_templates($this->template_folder);
|
||||
$this->init_templates($this->source_files);
|
||||
$this->new();
|
||||
}
|
||||
|
||||
public function new() : void
|
||||
{
|
||||
$this->blocks = array();
|
||||
foreach ($this->templates as $key => $template)
|
||||
{
|
||||
$this->blocks[$key] = $template;
|
||||
}
|
||||
// $this->blocks[self::$base_block] = $this->templates[self::$base_block];
|
||||
}
|
||||
|
||||
public function get_block_names() : array
|
||||
{
|
||||
$all_matches = array();
|
||||
foreach ($this->blocks as $block)
|
||||
{
|
||||
$matches = array();
|
||||
preg_match_all($this->block_pattern, $block, $matches);
|
||||
$all_matches = array_merge($all_matches, $matches[0]);
|
||||
}
|
||||
|
||||
$block_names = array();
|
||||
foreach (array_unique($all_matches) as $match)
|
||||
{
|
||||
$block_name = str_replace([$this->block_pattern_prefix, $this->block_pattern_suffix], "", $match);
|
||||
$block_names[$block_name] = array_key_exists($block_name, $this->blocks) ? true : false;
|
||||
}
|
||||
return $block_names;
|
||||
}
|
||||
|
||||
public function set_block_template(string $block, string $template) : void
|
||||
{
|
||||
$this->blocks[$block] = $this->templates[$template];
|
||||
}
|
||||
|
||||
public function set_block(string $block, string $content) : void
|
||||
{
|
||||
$this->blocks[$block] = $content;
|
||||
}
|
||||
|
||||
public function append_block(string $block, string $content) : void
|
||||
{
|
||||
if (empty($this->blocks[$block])) $this->blocks[$block] = "";
|
||||
$this->blocks[$block] .= $content;
|
||||
}
|
||||
|
||||
public function append_block_template(string $block, string $template) : void
|
||||
{
|
||||
if (empty($this->blocks[$block])) $this->blocks[$block] = "";
|
||||
$this->blocks[$block] .= $this->templates[$template];
|
||||
}
|
||||
|
||||
public function append_built_block(string $block, string $block_name) : void
|
||||
{
|
||||
$this->append_block($block, $this->build_block($block_name));
|
||||
}
|
||||
|
||||
public function append_argumented_block(string $block, string $block_name, array $args) : void
|
||||
{
|
||||
$this->append_block($block, $this->build_argumented_block($block_name, $args));
|
||||
}
|
||||
|
||||
public function build_argumented_block(string $block, array $args) : string
|
||||
{
|
||||
foreach ($args as $key => $value) $this->set_block($key, $value);
|
||||
return $this->build_block($block);
|
||||
}
|
||||
|
||||
public function build_block(string $block) : string
|
||||
{
|
||||
$matches = array();
|
||||
$html = $this->blocks[$block];
|
||||
|
||||
$from = array();
|
||||
$to = array();
|
||||
foreach ($this->get_block_names() as $block_name => $has_block)
|
||||
{
|
||||
$from[] = $this->block_pattern_prefix . $block_name . $this->block_pattern_suffix;
|
||||
if ($has_block) $to[] = $this->blocks[$block_name];
|
||||
else $to[] = "";
|
||||
}
|
||||
while (preg_match_all($this->block_pattern, $html) > 0)
|
||||
{
|
||||
$html = str_replace($from, $to, $html);
|
||||
}
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
public function get_raw_html() : string
|
||||
{
|
||||
return $this->build_block(self::$base_block);
|
||||
}
|
||||
|
||||
public function get_html() : string
|
||||
{
|
||||
$strip = [ "\t" ];
|
||||
return str_replace($strip, "", $this->get_raw_html());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
<div class="todo-add-container">
|
||||
<details>
|
||||
<summary>New</summary>
|
||||
<form action="./" method="POST" autocomplete="off">
|
||||
<input type="hidden" name="action" value="add" />
|
||||
<div class="todo-add-div">
|
||||
<p>{{ ADD_NOTICE }}</p>
|
||||
</div>
|
||||
|
||||
<div class="todo-add-div">
|
||||
<label class="label" for="todo_title">Title</label>
|
||||
<input id="todo_title" type="text" list="todo_titles" name="todo_title" required />
|
||||
</div>
|
||||
|
||||
<div class="todo-add-div">
|
||||
<label class="label" for="todo_description">Description</label>
|
||||
<input id="todo_description" type="text" name="todo_description" />
|
||||
</div>
|
||||
|
||||
<div class="todo-add-div">
|
||||
<label class="label" for="todo_deadline">Deadline</label>
|
||||
<input id="todo_deadline" type="date" name="todo_deadline" /><!--time-local-->
|
||||
<i>(optional)</i>
|
||||
</div>
|
||||
|
||||
<div class="todo-add-div">
|
||||
<label class="label" for="todo_category">Category</label>
|
||||
<input id="todo_category" type="text" list="todo_categories" name="todo_category" />
|
||||
<i>(optional)</i>
|
||||
</div>
|
||||
|
||||
<div class="todo-add-div">
|
||||
<input class="todo-add-button" type="submit" value="Add" />
|
||||
</div>
|
||||
</form>
|
||||
</details>
|
||||
</div>
|
||||
@@ -0,0 +1 @@
|
||||
<html><head>{{ HEAD }}</head><body id="body">{{ BODY }}</body></html>
|
||||
@@ -0,0 +1,3 @@
|
||||
{{ HEADER }}
|
||||
{{ CONTENT }}
|
||||
{{ FOOTER }}
|
||||
@@ -0,0 +1 @@
|
||||
<option value="{{ DATALIST_AUTOFILL_DATA }}" />
|
||||
@@ -0,0 +1,3 @@
|
||||
<datalist id="{{ DATALIST_ID }}">
|
||||
{{ DATALIST_AUTOFILLS }}
|
||||
</datalist>
|
||||
@@ -0,0 +1,3 @@
|
||||
<div class="footer" id="footer">
|
||||
{{ DATALISTS }}
|
||||
</div>
|
||||
@@ -0,0 +1,6 @@
|
||||
<title>Zvg: {{ TITLE }}</title>
|
||||
<style>{{ STYLE }}</style>
|
||||
<script>{{ SCRIPT }}</script>
|
||||
<link rel="icon" type="image/png" href="/favicon.png">
|
||||
<link rel="icon" type="image/png" sizes="64x64" href="/_src/favicon/64.png">
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="/_src/favicon/32.png">
|
||||
@@ -0,0 +1,3 @@
|
||||
<div class="header" id="header">
|
||||
<h1>{{ TITLE }}</h1>
|
||||
</div>
|
||||
@@ -0,0 +1,20 @@
|
||||
<div class="login-container">
|
||||
<form action="./" method="POST">
|
||||
<input type="hidden" name="intent" value="login" />
|
||||
<div class="login-div">
|
||||
<p>{{ LOGIN_NOTICE }}</p>
|
||||
</div>
|
||||
<div class="login-div">
|
||||
<label class="label" for="uname">Username</label>
|
||||
<input class="login-input" id="uname" type="text" name="uname" required />
|
||||
</div>
|
||||
<div class="login-div">
|
||||
<label class="label" for="pwd">Password</label>
|
||||
<input class="login-input" id="pwd" type="password" autocomplete="off" name="pwd" required />
|
||||
</div>
|
||||
<div class="login-div">
|
||||
<input class="login-button" type="submit" value="Log in" />
|
||||
</div>
|
||||
</form>
|
||||
<p>Don't have an account? <a href="./?reg">Sign up!</a></p>
|
||||
</div>
|
||||
@@ -0,0 +1,16 @@
|
||||
<div class="navbar">
|
||||
<form action="./" method="GET" class="navbar-form">
|
||||
<input type="hidden" name="backup" value="1">
|
||||
<!-- Backing up actually works but there is no restore function yet... -->
|
||||
<!-- <input type="submit" value="Back up"> -->
|
||||
<input type="submit" value="Back up">
|
||||
</form>
|
||||
<form action="./" method="GET" class="navbar-form">
|
||||
<input type="hidden" name="restore-list" value="1">
|
||||
<input type="submit" value="Restore">
|
||||
</form>
|
||||
<form action="./" method="GET" class="navbar-form">
|
||||
<input type="hidden" name="logout" value="1">
|
||||
<input type="submit" value="Log out">
|
||||
</form>
|
||||
</div>
|
||||
@@ -0,0 +1,24 @@
|
||||
<div class="login-container">
|
||||
<form action="./" method="POST">
|
||||
<input type="hidden" name="intent" value="register" />
|
||||
<div class="login-div">
|
||||
<p>{{ REGISTER_NOTICE }}</p>
|
||||
</div>
|
||||
<div class="login-div">
|
||||
<label class="label" for="uname">Username</label>
|
||||
<input class="login-input" id="uname" type="text" name="uname" required />
|
||||
</div>
|
||||
<div class="login-div">
|
||||
<label class="label" for="pwd">Password</label>
|
||||
<input class="login-input" id="pwd" type="password" autocomplete="off" name="pwd" required />
|
||||
</div>
|
||||
<div class="login-div">
|
||||
<label class="label" for="pwd2">Password (Again)</label>
|
||||
<input class="login-input" id="pwd2" type="password" autocomplete="off" name="pwd2" required />
|
||||
</div>
|
||||
<div class="login-div">
|
||||
<input class="login-button" type="submit" value="Sign Up" />
|
||||
</div>
|
||||
</form>
|
||||
<p>Have an account? <a href="./?">Log in!</a></p>
|
||||
</div>
|
||||
@@ -0,0 +1,6 @@
|
||||
<div class="navbar">
|
||||
<form action="./" method="GET" class="navbar-form">
|
||||
<input type="submit" value="Cancel">
|
||||
</form>
|
||||
</div>
|
||||
<div class="todo-list">{{ RESTORE_ITEMS }}</div>
|
||||
@@ -0,0 +1,17 @@
|
||||
<div class="todo-item" id="todo-item-{{ RESTORE_ITEM_TIMESTAMP }}">
|
||||
<details>
|
||||
<summary>{{ RESTORE_ITEM_TITLE }}</summary>
|
||||
|
||||
<form action="./" method="POST">
|
||||
<input type="hidden" name="restore" value="{{ RESTORE_ITEM_TIMESTAMP }}" />
|
||||
<input type="submit" value="Restore" />
|
||||
</form>
|
||||
|
||||
<div style="display:inline-block;width:1em;"></div>
|
||||
|
||||
<form action="./" method="POST">
|
||||
<input type="hidden" name="delete-restore" value="{{ RESTORE_ITEM_TIMESTAMP }}" />
|
||||
<input type="submit" value="Delete" />
|
||||
</form>
|
||||
</details>
|
||||
</div>
|
||||
@@ -0,0 +1,3 @@
|
||||
<div class="todo-item" id="todo-item-{{ RESTORE_ITEM_TIMESTAMP }}">
|
||||
<summary>WARNING: Restoring a backup will overwrite your current list.</summary>
|
||||
</div>
|
||||
@@ -0,0 +1,16 @@
|
||||
document.addEventListener("DOMContentLoaded", function(){
|
||||
// Fetch all the details element.
|
||||
const details = document.querySelectorAll("details");
|
||||
|
||||
// Add the onclick listeners.
|
||||
details.forEach((targetDetail) => {
|
||||
targetDetail.addEventListener("click", () => {
|
||||
// Close all the details that are not targetDetail.
|
||||
details.forEach((detail) => {
|
||||
if (detail !== targetDetail) {
|
||||
detail.removeAttribute("open");
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,153 @@
|
||||
html {
|
||||
font-family: 'Roboto', sans-serif;
|
||||
background-color: #111111;
|
||||
color: #ffffff;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
a:link, a:hover, a:visited, a:active {
|
||||
color: DeepPink;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
body {
|
||||
max-width: 800px;
|
||||
margin: 30px auto;
|
||||
text-align: left;
|
||||
}
|
||||
.header {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.login-container form {
|
||||
width: inherit;
|
||||
margin: inherit;
|
||||
}
|
||||
|
||||
.todo-category {
|
||||
margin: 5px;
|
||||
padding: 10px;
|
||||
border: 1px dashed #222222;
|
||||
}
|
||||
|
||||
.todo-category .todo-item {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.todo-item form, .navbar-form {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: inline-block;
|
||||
}
|
||||
.todo-item form input, .navbar-form input {
|
||||
border: 2px solid #333333;
|
||||
padding: 5px;
|
||||
margin: 0;
|
||||
background-color: transparent;
|
||||
color: white;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.todo-item form input:focus, .navbar-form input:focus {
|
||||
outline: none;
|
||||
}
|
||||
.todo-item form input:active, .navbar-form input:active {
|
||||
background-color: #222222;
|
||||
}
|
||||
|
||||
summary, details[open] {
|
||||
padding: 0.5em;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
summary {
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
|
||||
details[open] summary {
|
||||
margin: -0.5em;
|
||||
margin-bottom: 0.7em;
|
||||
border: none;
|
||||
}
|
||||
|
||||
details[open] {
|
||||
border: 1px solid #222222;
|
||||
border-radius: 0 0 10px 10px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
summary:focus {
|
||||
outline: none;
|
||||
background-color: #333333;
|
||||
}
|
||||
|
||||
.todo-add-div, .todo-add-container {
|
||||
margin: 20px;
|
||||
}
|
||||
|
||||
.todo-add-div input[type="text"],
|
||||
.todo-add-div input[type="datetime-local"],
|
||||
.todo-add-div input[type="date"]
|
||||
{
|
||||
width: 30em;
|
||||
}
|
||||
|
||||
.todo-add-div label {
|
||||
display: inline-block;
|
||||
width: 6em;
|
||||
text-align: right;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.todo-add-button {
|
||||
border: none;
|
||||
background-color: Green;
|
||||
color: White;
|
||||
padding: 5px;
|
||||
border-radius: 3px;
|
||||
width: 100%;
|
||||
}
|
||||
.todo-add-button:active {
|
||||
background-color: Lime;
|
||||
color: DimGrey;
|
||||
}
|
||||
.navbar {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/* LOGIN */
|
||||
.login-input {
|
||||
width: 100%;
|
||||
border: 1px solid LightPink;
|
||||
background-color: transparent;
|
||||
padding: 5px;
|
||||
border-radius: 3px;
|
||||
color: white;
|
||||
}
|
||||
.login-button {
|
||||
border: none;
|
||||
background-color: DeepPink;
|
||||
padding: 5px;
|
||||
border-radius: 3px;
|
||||
width: 100%;
|
||||
}
|
||||
.login-button:active {
|
||||
background-color: MediumVioletRed;
|
||||
}
|
||||
.login-div {
|
||||
margin: 20px;
|
||||
}
|
||||
.login-container {
|
||||
width: fit-content;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.login-container p {
|
||||
text-align: center;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
<div class="todo-category" id="todo-category-{{ TODO_CATEGORY_ID }}">
|
||||
<h3>{{ TODO_CATEGORY_TITLE }}</h3>
|
||||
{{ TODO_CATEGORY_ITEMS }}
|
||||
</div>
|
||||
@@ -0,0 +1 @@
|
||||
<b>{{ TODO_ITEM_TITLE }}:</b> {{ TODO_ITEM_DESCRIPTION }}
|
||||
@@ -0,0 +1 @@
|
||||
<b>{{ TODO_ITEM_TITLE }}</b>
|
||||
@@ -0,0 +1,45 @@
|
||||
<div class="todo-item" id="todo-item-{{ TODO_ITEM_ID }}">
|
||||
<details>
|
||||
<summary>{{ TODO_ITEM_SUMMARY }}</summary>
|
||||
|
||||
<form action="./" method="POST">
|
||||
<input type="hidden" name="action" value="delay" />
|
||||
<input type="hidden" name="delay_by" value="-1 week" />
|
||||
<input type="hidden" name="todo_item_id" value="{{ TODO_ITEM_ID }}" />
|
||||
<input type="submit" value="-1 week" />
|
||||
</form>
|
||||
|
||||
<form action="./" method="POST">
|
||||
<input type="hidden" name="action" value="delay" />
|
||||
<input type="hidden" name="delay_by" value="-1 day" />
|
||||
<input type="hidden" name="todo_item_id" value="{{ TODO_ITEM_ID }}" />
|
||||
<input type="submit" value="-1 day" />
|
||||
</form>
|
||||
|
||||
<div style="display:inline-block;width:1em;"></div>
|
||||
|
||||
<form action="./" method="POST">
|
||||
<input type="hidden" name="action" value="delay" />
|
||||
<input type="hidden" name="delay_by" value="+1 day" />
|
||||
<input type="hidden" name="todo_item_id" value="{{ TODO_ITEM_ID }}" />
|
||||
<input type="submit" value="+1 day" />
|
||||
</form>
|
||||
|
||||
<form action="./" method="POST">
|
||||
<input type="hidden" name="action" value="delay" />
|
||||
<input type="hidden" name="delay_by" value="+1 week" />
|
||||
<input type="hidden" name="todo_item_id" value="{{ TODO_ITEM_ID }}" />
|
||||
<input type="submit" value="+1 week" />
|
||||
</form>
|
||||
|
||||
<div style="display:inline-block;width:1em;"></div>
|
||||
<div style="display:inline-block;width:1em;"></div>
|
||||
|
||||
<form action="./" method="POST">
|
||||
<input type="hidden" name="action" value="remove" />
|
||||
<input type="hidden" name="todo_item_id" value="{{ TODO_ITEM_ID }}" />
|
||||
<input type="submit" value="Remove" />
|
||||
</form>
|
||||
|
||||
</details>
|
||||
</div>
|
||||
@@ -0,0 +1 @@
|
||||
Nothing here yet...
|
||||
@@ -0,0 +1 @@
|
||||
<div class="todo-list">{{ TODO_ITEMS }}</div>
|
||||
Reference in New Issue
Block a user