commit 49d2581a9896fbcb60e435beb236846858a6d66c
parent da4133455749ad995882cc4ca122c10c6c51d32d
Author: JayVii <jayvii[AT]posteo[DOT]de>
Date: Sat, 26 Oct 2024 11:08:38 +0200
feat: finish basic functionality
Diffstat:
10 files changed, 599 insertions(+), 274 deletions(-)
diff --git a/assets/css/custom.css b/assets/css/custom.css
@@ -0,0 +1,71 @@
+/* Forms and its elements should stretch full width */
+form:not(.inline) > *
+{
+ width: 100%;
+}
+
+/* The content text area should have a substantial height */
+textarea#content {
+ height: 400px;
+}
+
+/* provide inline utility class for buttons, forms, etc. */
+.inline {
+ display: inline-block;
+}
+
+/* Ensure the notes list items do not overflow and break properly */
+.notes_list_item {
+ overflow: hidden;
+ word-break: break-all;
+ margin-bottom: 1em;
+ padding-bottom: 0.5em;
+ border-bottom: 1px solid grey;
+}
+
+/* provide a utility class so buttons behave like anchor-objects */
+.likeanchor {
+ background-color: unset !important;
+ border: unset !important;
+ color: var(--accent) !important;
+ margin: initial !important;
+ padding: initial !important;
+ text-decoration: underline;
+ display: inline-block;
+}
+.likeanchor:hover {
+ color: var(--accent-hover) !important;
+ cursor: pointer !important;
+}
+
+.likenavitem {
+ background-color: unset !important;
+ border: 1px solid var(--border) !important;
+ border-radius: var(--standard-border-radius) !important;
+ color: var(--text) !important;
+ margin: 0 .5rem 1rem !important;
+ padding: .1rem 1rem !important;
+ text-decoration: none !important;
+ display: inline-block;
+}
+
+.likenavitem:hover {
+ background-color: unset !important;
+ border-color: var(--accent) !important;
+ color: var(--accent) !important;
+ cursor: pointer !important;
+}
+
+header {
+ margin-bottom: 2em;
+}
+
+.danger {
+ background-color: #ff0000 !important;
+ border-color: #ff0000 !important;
+}
+
+.danger:hover {
+ background-color: #ff6161 !important;
+ border-color: #ff6161 !important;
+}
diff --git a/assets/js/deletion.js b/assets/js/deletion.js
@@ -0,0 +1,9 @@
+function deletion_confirm(id) {
+ // Ask user about intentions
+ var status = confirm("Are you sure you want to delete this note?");
+
+ // If user confirms, submit the given form (delete item)
+ if (status) {
+ document.getElementById(id).submit();
+ }
+}
diff --git a/config/i18n.php b/config/i18n.php
@@ -13,11 +13,13 @@ if ($lang == "en") {
$GLOBALS["i18n_ctime"] = "Last edited";
$GLOBALS["i18n_edit"] = "Edit note";
$GLOBALS["i18n_new"] = "New note";
- $GLOBALS["i18n_list"] = "List notes";
+ $GLOBALS["i18n_list"] = "All";
$GLOBALS["i18n_login"] = "Login";
$GLOBALS["i18n_user"] = "Username";
$GLOBALS["i18n_pass"] = "Password";
$GLOBALS["i18n_logout"] = "Logout";
+ $GLOBALS["i18n_categories"] = "Categories";
+ $GLOBALS["i18n_delete"] = "Delete";
}
diff --git a/index.php b/index.php
@@ -4,11 +4,6 @@
<?php
- /* DEVELOPMENT SETTINGS */
- if (!array_key_exists("action", $_POST)) {
- $_POST["action"] = "list";
- }
-
/* Load library functions */
foreach (glob("./lib/*.php") as $lib) {
include($lib);
@@ -40,8 +35,12 @@
$action = "login";
}
- /* ensure the given filename has a valid extension */
- if (count(preg_grep("/\.txt$/", array($filename), PREG_GREP_INVERT)) > 0) {
+ /* ensure the given filename (if it is set!) has a valid extension */
+ if (
+ (count(preg_grep("/\.txt$/", array($filename), PREG_GREP_INVERT)) > 0)
+ &&
+ ($filename != "")
+ ){
$filename = $filename . ".txt";
}
@@ -55,6 +54,7 @@
<title>Rememori</title>
<link rel="stylesheet" type="text/css" href="assets/css/simple.min.css"/>
<link rel="stylesheet" href="assets/css/custom.css" />
+ <script async src="assets/js/deletion.js"></script>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
@@ -69,177 +69,50 @@
<header>
<!-- Buttons -->
- <nav>
-
- <!-- New Note: Edit-Action -->
- <form action="/" target="_self" method="post">
- <input
- id="user"
- name="user"
- type="hidden"
- value="<?php echo $GLOBALS["user"]; ?>"
- >
- <input
- id="token"
- name="token"
- type="hidden"
- value="<?php echo $GLOBALS["token"]; ?>"
- >
- <input
- id="category"
- name="category"
- type="hidden"
- value=""
- >
- <input
- id="filename"
- name="filename"
- type="hidden"
- value=""
- >
- <input
- id="action"
- name="action"
- type="hidden"
- value="show"
- >
- <input
- type="submit"
- value="<?php echo $GLOBALS["i18n_new"]; ?>"
- >
- </form>
- <!-- Logout-Action -->
- <form action="/" target="_self" method="post">
- <input
- id="user"
- name="user"
- type="hidden"
- value=""
- >
- <input
- id="pass"
- name="pass"
- type="hidden"
- value=""
- >
- <input
- id="token"
- name="token"
- type="hidden"
- value=""
- >
- <input
- id="action"
- name="action"
- type="hidden"
- value="list"
- >
- <input
- type="submit"
- value="<?php echo $GLOBALS["i18n_logout"]; ?>"
- >
- </form>
- </nav>
+ <?php top_navigation(); ?>
<!-- Headline -->
<h1>Rememori</h1>
- <!-- List all categories -->
- <form action="/" target="_self" method="post">
- <input
- id="user"
- name="user"
- type="hidden"
- value="<?php echo $GLOBALS["user"]; ?>"
- >
- <input
- id="token"
- name="token"
- type="hidden"
- value="<?php echo $GLOBALS["token"]; ?>"
- >
- <input
- id="action"
- name="action"
- type="hidden"
- value="list"
- >
- <input
- type="submit"
- value="<?php echo $GLOBALS["i18n_list"]; ?>"
- >
- </form>
-
-<?php
- /* Fetch all categories of the user */
- $categories_path = glob("./data/" . $GLOBALS["user"] . "/*");
- foreach ($categories_path as $category_path) {
-
- if (count(glob($category_path . "/" . "*.txt")) > 0) {
-?>
- <form action="/" target="_self" method="post">
- <input
- id="user"
- name="user"
- type="hidden"
- value="<?php echo $GLOBALS["user"]; ?>"
- >
- <input
- id="token"
- name="token"
- type="hidden"
- value="<?php echo $GLOBALS["token"]; ?>"
- >
- <input
- id="category"
- name="category"
- type="hidden"
- value="<?php echo basename($category_path); ?>"
- >
- <input
- id="action"
- name="action"
- type="hidden"
- value="list"
- >
- <input
- type="submit"
- value="<?php echo basename($category_path); ?>"
- >
- </form>
-<?php
- } // if-statement
- } // foreach-loop
-?>
-
</header>
<?php
} // if-statement
+?>
+
+<?php
/* Login action */
if ($action == "login") {
show_login_form("list");
- die();
+ die(); // ensure the process stops after this
+ }
+
+ /* Deletion action */
+ if ($action == "delete") {
+ delete_note(
+ $category,
+ $filename
+ );
+ /* set action to "list", so we return to the main view again */
+ $action = "list";
+ $category = "";
}
/* Listing action */
if ($action == "list") {
-
- /* if no category is given, list all. otherwise only show given */
- if ($category != "") {
- $categories_path = array(
- "./data/" . $GLOBALS["user"] . "/" . $category
- );
- }
- foreach ($categories_path as $category_path) {
- list_notes(basename($category_path));
- }
+ category_menu();
+ list_notes($category);
}
/* Edit action */
if ($action == "edit") {
+ /* if no filename is given, try to come up with one ourselves */
+ if ($filename == "") {
+ $filename = generate_filename($content);
+ }
+ /* edit note */
edit_note(
$category,
$filename,
diff --git a/lib/delete.php b/lib/delete.php
@@ -0,0 +1,20 @@
+<?php
+
+function delete_note(
+ string $category,
+ string $filename,
+) {
+
+ /* Create full file path name */
+ $dirpath = "./data/" . $GLOBALS["user"] . "/" . $category;
+ $filepath = $dirpath . "/" . $filename;
+
+ /* delete note */
+ $status = unlink($filepath);
+
+ /* return status of deletion action */
+ return $status;
+}
+
+?>
+
diff --git a/lib/edit.php b/lib/edit.php
@@ -24,12 +24,13 @@ function edit_note(
$filepath_t0 = $category . "/" . $filename;
/* if old and new filepaths differ, the file was likely renamed.
- * delete the old one and exit immediately */
+ * delete the old one and return immediately */
+ $deletion = false;
if ($filepath_t1 != $filepath_t0) {
- return unlink("./data/" . $GLOBALS["user"] . "/" . $filepath_t1);
+ return delete_note($category, basename($filepath_t1));
}
- /* if all actions went through, return "true" */
+ /* if all went fine, return "true" */
return true;
} // function
diff --git a/lib/helpers.php b/lib/helpers.php
@@ -10,9 +10,9 @@ function gather_post(string $key) {
}
function validate_input_string(string $in) {
- /* Only allows alphanumeric characters and any of "-_." */
+ /* Only allows alphanumeric characters and any of "-_. " */
$out = preg_replace(
- "/[^A-Za-z0-9\-\_\.]/",
+ "/[^A-Za-z0-9\-\_\.\s]/",
"_",
$in
);
@@ -50,4 +50,109 @@ function link_in_first_line(string $filepath) {
}
+function gather_notes(string $category = "") {
+
+ /* initilise files array */
+ $filenames = array();
+
+ /* Create full path name */
+ $dirpath = "./data/" . $GLOBALS["user"];
+
+ /* if no category is given, search in all categories
+ * if a category is given, only search in this one */
+ if ($category == "") {
+
+ /* list all available categories */
+ $categories_path = glob($dirpath . "/*");
+
+ } else {
+
+ $categories_path = array($dirpath . "/" . $category);
+ }
+
+ /* loop through each category path and find all files within them */
+ foreach ($categories_path as $category_path) {
+
+ $files_path = glob($category_path . "/" . "*.txt");
+
+ foreach ($files_path as $file_path) {
+
+ array_push(
+ $filenames,
+ array(
+ "category" => basename($category_path),
+ "name" => preg_replace(
+ "/\.txt$/",
+ "",
+ basename($file_path)
+ ),
+ "time" => filectime($file_path),
+ "link" => link_in_first_line($file_path)
+ )
+ );
+
+ } // foreach-loop
+ } // foreach-loop
+
+ /* return filenames array */
+ return $filenames;
+
+} // function
+
+function page_title(
+ string $url
+) {
+
+ /* initilise title for page */
+ $title = "";
+
+ /* open url handle */
+ $handle = fopen($url, "r");
+
+ /* if connection suceeded, fetch the content up until </title> */
+ if ($handle) {
+ $string = stream_get_line($handle, 0, "</title>");
+ fclose($handle);
+ $string = explode("<title", $string)[1];
+
+ /* if a title could be gathered, clean it up */
+ if (!empty($string)) {
+ $title = trim((explode(">", $string))[1]);
+ }
+ }
+
+ /* return title */
+ return $title;
+}
+
+function generate_filename(string $content) {
+
+ /* initilise new filename */
+ $filename = "";
+
+ /* if the first line of the content contains a URL,
+ * we may use the page title */
+ $file_link = preg_replace(
+ '/^(http(s){0,1}:\/\/[^\s]+)*.*$/',
+ '\1',
+ explode(PHP_EOL, $content)[0]
+ );
+ if ($file_link != "") {
+ $filename = page_title($file_link);
+ }
+
+ /* if filename is still unset after this
+ * (file did not contain link OR fetching page title failed)
+ * we use the sha256sum of the content as fallback name */
+ if ($filename == "") {
+ $filename = hash("sha256", $content, false);
+ }
+
+ /* ensure filename as a proper file extension */
+ $filename = $filename . ".txt";
+
+ /* return new filename */
+ return $filename;
+}
+
?>
diff --git a/lib/list.php b/lib/list.php
@@ -1,129 +1,126 @@
<?php
function list_notes(
- string $category
+ string $category = ""
) {
- /* Create full path name */
- $dirpath = "./data/" . $GLOBALS["user"] . "/" . $category;
+ /* gather note paths, based on given category */
+ $filenames = gather_notes($category);
- /* list all available notes */
- $filematches = glob($dirpath . "/" . "*.txt");
+ /* Sort filenames by edit timestamp */
+ usort($filenames, "sort_by_time");
- if (count($filematches) > 0) {
-
- /* Gather each file next to its edit time stamp */
- $filenames = array();
- foreach ($filematches as $filename) {
-
- array_push(
- $filenames,
- array(
- "name" => preg_replace(
- "/\.txt$/",
- "",
- basename($filename)
- ),
- "time" => filectime($filename),
- "link" => link_in_first_line($filename)
- )
- );
- }
-
- /* Sort filenames by edit timestamp */
- usort($filenames, "sort_by_time");
-
-?>
-
- <h4><?php echo $category; ?></h4>
- <table>
- <thead>
- <tr>
- <!-- 1st column: filename -->
- <th><?php echo $GLOBALS["i18n_filename"]; ?></th>
- <!-- 2nd column: edit time -->
- <th><?php echo $GLOBALS["i18n_ctime"]; ?></th>
- <!-- 3rd column: edit button -->
- <th></th>
- </tr>
- </thead>
- <tbody>
-
-<?php
- /* loop through each filename and draw a table row */
- foreach ($filenames as $filename) {
+ /* loop through each filename and draw a table row */
+ foreach ($filenames as $filename) {
?>
- <tr>
- <!-- 1st column: filename including URL if applicable -->
- <td>
-<?php
- /* if first line of */
- if ($filename["link"] !== false) {
-?>
- <a href="<?php echo $filename["link"]; ?>" target="_blank">
- <?php echo $filename["name"]; ?>
- </a>
-<?php
- } else {
- echo $filename["name"];
- }
-?>
- </td>
- <!-- 2nd column: edit time -->
- <td><?php echo date("Y-m-d H:i:s", $filename["time"]); ?></td>
- <!-- 3rd columne:form with edit-button -->
- <td>
- <form action="/" target="_self" method="post">
- <input
- id="user"
- name="user"
- type="hidden"
- value="<?php echo $GLOBALS["user"]; ?>"
- >
- <input
- id="token"
- name="token"
- type="hidden"
- value="<?php echo $GLOBALS["token"]; ?>"
- >
- <input
- id="category"
- name="category"
- type="hidden"
- value="<?php echo $category; ?>"
- >
- <input
- id="filename"
- name="filename"
- type="hidden"
- value="<?php echo $filename["name"] . ".txt"; ?>"
- >
- <input
- id="action"
- name="action"
- type="hidden"
- value="show"
- >
- <input
- type="submit"
- value="<?php echo $GLOBALS["i18n_edit"]; ?>"
- >
- </form>
- </td>
- </tr>
+ <!-- note list item -->
+ <div
+ id="<?php echo $filename["category"] . "_" . $filename["name"]; ?>"
+ class="notes_list_item"
+ >
<?php
-
- } // for-loop
-
+ /* if note starts with an URL, link it in the title
+ * otherwise just print the title */
+ if ($filename["link"] !== false) {
?>
- </tbody>
- </table>
+ <a href="<?php echo $filename["link"]; ?>" target="_blank">
+ <strong><?php echo $filename["name"]; ?></strong>
+ </a><br>
+
+<?php } else { ?>
+ <strong><?php echo $filename["name"]; ?></strong><br>
+<?php } ?>
+
+ <!-- date marker -->
+ <span class="inline">
+ <?php echo date("Y-m-d H:i:s", $filename["time"]); ?>
+ </span>
+
+ <!-- separator -->
+ <span class="inline">|</span>
+
+ <!-- category button -->
+ <form action="/" target="_self" method="post" class="inline">
+ <input
+ id="user"
+ name="user"
+ type="hidden"
+ value="<?php echo $GLOBALS["user"]; ?>"
+ >
+ <input
+ id="token"
+ name="token"
+ type="hidden"
+ value="<?php echo $GLOBALS["token"]; ?>"
+ >
+ <input
+ id="category"
+ name="category"
+ type="hidden"
+ value="<?php echo $filename["category"]; ?>"
+ >
+ <input
+ id="action"
+ name="action"
+ type="hidden"
+ value="list"
+ >
+ <input
+ type="submit"
+ class="likeanchor"
+ value="<?php echo $filename["category"]; ?>"
+ >
+ </form>
+
+ <!-- separator -->
+ <span class="inline">|</span>
+
+ <!-- edit button -->
+ <form action="/" method="post" class="inline">
+ <input
+ id="user"
+ name="user"
+ type="hidden"
+ value="<?php echo $GLOBALS["user"]; ?>"
+ >
+ <input
+ id="token"
+ name="token"
+ type="hidden"
+ value="<?php echo $GLOBALS["token"]; ?>"
+ >
+ <input
+ id="category"
+ name="category"
+ type="hidden"
+ value="<?php echo $filename["category"]; ?>"
+ >
+ <input
+ id="filename"
+ name="filename"
+ type="hidden"
+ value="<?php echo $filename["name"] . ".txt"; ?>"
+ >
+ <input
+ id="action"
+ name="action"
+ type="hidden"
+ value="show"
+ >
+ <input
+ type="submit"
+ class="likeanchor"
+ value="<?php echo $GLOBALS["i18n_edit"]; ?>"
+ >
+ </form>
+
+ </div>
<?php
- } // if-statement
+ } // foreach-loop
} // function
diff --git a/lib/menus.php b/lib/menus.php
@@ -0,0 +1,207 @@
+<?php
+
+function top_navigation() {
+
+?>
+
+<!-- Navigation Buttons -->
+<nav>
+
+ <!-- All Notes: List-Action -->
+ <form action="/" method="post" class="inline">
+ <input
+ id="user"
+ name="user"
+ type="hidden"
+ value="<?php echo $GLOBALS["user"]; ?>"
+ >
+ <input
+ id="token"
+ name="token"
+ type="hidden"
+ value="<?php echo $GLOBALS["token"]; ?>"
+ >
+ <input
+ id="category"
+ name="category"
+ type="hidden"
+ value=""
+ >
+ <input
+ id="action"
+ name="action"
+ type="hidden"
+ value="list"
+ >
+ <input
+ class="likenavitem"
+ type="submit"
+ value="<?php echo $GLOBALS["i18n_list"]; ?>"
+ >
+ </form>
+
+ <!-- New Note: Edit-Action -->
+ <form action="/" method="post" class="inline">
+ <input
+ id="user"
+ name="user"
+ type="hidden"
+ value="<?php echo $GLOBALS["user"]; ?>"
+ >
+ <input
+ id="token"
+ name="token"
+ type="hidden"
+ value="<?php echo $GLOBALS["token"]; ?>"
+ >
+ <input
+ id="category"
+ name="category"
+ type="hidden"
+ value=""
+ >
+ <input
+ id="filename"
+ name="filename"
+ type="hidden"
+ value=""
+ >
+ <input
+ id="action"
+ name="action"
+ type="hidden"
+ value="show"
+ >
+ <input
+ class="likenavitem"
+ type="submit"
+ value="<?php echo $GLOBALS["i18n_new"]; ?>"
+ >
+ </form>
+
+ <!-- Logout-Action -->
+ <form action="/" method="post" class="inline">
+ <input
+ id="user"
+ name="user"
+ type="hidden"
+ value=""
+ >
+ <input
+ id="pass"
+ name="pass"
+ type="hidden"
+ value=""
+ >
+ <input
+ id="token"
+ name="token"
+ type="hidden"
+ value=""
+ >
+ <input
+ id="action"
+ name="action"
+ type="hidden"
+ value="list"
+ >
+ <input
+ class="likenavitem"
+ type="submit"
+ value="<?php echo $GLOBALS["i18n_logout"]; ?>"
+ >
+ </form>
+
+</nav>
+
+<?php
+
+} // function
+
+?>
+
+<?php
+
+function category_menu() {
+
+?>
+
+<!-- List all categories -->
+<details>
+ <summary><?php echo $GLOBALS["i18n_categories"]; ?></summary>
+ <div class="inline">
+ <form action="/" target="_self" method="post" class="inline">
+ <input
+ id="user"
+ name="user"
+ type="hidden"
+ value="<?php echo $GLOBALS["user"]; ?>"
+ >
+ <input
+ id="token"
+ name="token"
+ type="hidden"
+ value="<?php echo $GLOBALS["token"]; ?>"
+ >
+ <input
+ id="action"
+ name="action"
+ type="hidden"
+ value="list"
+ >
+ <input
+ type="submit"
+ value="<?php echo $GLOBALS["i18n_list"]; ?>"
+ >
+ </form>
+
+<?php
+ /* Fetch all categories of the user */
+ $categories_path = glob("./data/" . $GLOBALS["user"] . "/*");
+ foreach ($categories_path as $category_path) {
+
+ if (count(glob($category_path . "/" . "*.txt")) > 0) {
+?>
+
+ <form action="/" target="_self" method="post" class="inline">
+ <input
+ id="user"
+ name="user"
+ type="hidden"
+ value="<?php echo $GLOBALS["user"]; ?>"
+ >
+ <input
+ id="token"
+ name="token"
+ type="hidden"
+ value="<?php echo $GLOBALS["token"]; ?>"
+ >
+ <input
+ id="category"
+ name="category"
+ type="hidden"
+ value="<?php echo basename($category_path); ?>"
+ >
+ <input
+ id="action"
+ name="action"
+ type="hidden"
+ value="list"
+ >
+ <input
+ type="submit"
+ value="<?php echo basename($category_path); ?>"
+ >
+ </form>
+
+<?php
+ } // if-statement
+ } // foreach-loop
+?>
+
+ </div>
+</details>
+
+<?php
+} // function
+?>
diff --git a/lib/show.php b/lib/show.php
@@ -55,6 +55,46 @@ function show_note(
<input type="submit" value="<?php echo $GLOBALS["i18n_save"]; ?>">
</form>
+<form action="/" method="post" enctype="multipart/form-data" id="delete">
+ <input
+ id="category"
+ name="category"
+ type="hidden"
+ value="<?php echo $category; ?>"
+ >
+ <input
+ id="filename"
+ name="filename"
+ type="hidden"
+ value="<?php echo $filename; ?>"
+ >
+ <input
+ id="action"
+ name="action"
+ type="hidden"
+ value="delete"
+ >
+ <input
+ id="user"
+ name="user"
+ type="hidden"
+ value="<?php echo $GLOBALS["user"]; ?>"
+ >
+ <input
+ id="token"
+ name="token"
+ type="hidden"
+ value="<?php echo $GLOBALS["token"]; ?>"
+ >
+ <input
+ class="danger"
+ type="button"
+ value="<?php echo $GLOBALS["i18n_delete"]; ?>"
+ onclick="deletion_confirm('delete');"
+ >
+</form>
+
+
<?php
} // function