From e5af3aca66f4579a640321c9b2a42f435b7261a5 Mon Sep 17 00:00:00 2001 From: Josh Ashton Date: Thu, 3 Apr 2025 10:50:58 -0600 Subject: [PATCH] sync, implemented sort, viewing user pages & articles written, updating db --- articles.php | 42 +++++++++++++++++-- components/head.php | 9 ++-- components/nav.php | 2 +- compose.php | 3 +- css/base.css | 74 ++++++++++++++++++++++++++++----- functions/account_functions.php | 56 +++++++++++++++---------- functions/article_functions.php | 52 +++++++++++++++++++++-- functions/functions.php | 11 +++++ functions/init.php | 4 +- js/md_preview.js | 17 ++++++++ scripts/init.sql | 5 +++ vintagecoding.net.md | 10 ++--- 12 files changed, 231 insertions(+), 54 deletions(-) create mode 100644 js/md_preview.js diff --git a/articles.php b/articles.php index 639afbf..666d896 100644 --- a/articles.php +++ b/articles.php @@ -6,14 +6,50 @@ include_once 'components/head.php'; include_once 'functions/article_functions.php'; -// TODO: Move to a grid, where the left hand column is either a search and filtering system, or...Table of Contents? - // Default view, it'll show articles by recency if (!isset($_GET['article_id']) && !isset($_GET['tag']) && !isset($_GET['author_id'])) { - $ids = article_ids_by_recency(); + $filters = ' +
+
+ '; + if (!isset($_GET['sort']) || $_GET['sort'] == 'newest') { + $ids = article_ids_by_newest(); + $filters .= ' +

Newest - Oldest

+ + '; + } else if ($_GET['sort'] == 'oldest') { + $ids = article_ids_by_oldest(); + $filters .= ' +

Oldest - Newest

+ + '; + } + + $filters .= ' +
+
+ '; + + + echo ' +
+
+ ' . $filters . ' +
+
+ '; foreach ($ids as $id) echo article_card($id); + echo ' +
+
+ '; } else if (isset($_GET['article_id'])) { increment_read_counter($_GET['article_id']); echo article_page_from_markdown($_GET['article_id']); diff --git a/components/head.php b/components/head.php index 71595d7..11f05e4 100644 --- a/components/head.php +++ b/components/head.php @@ -4,17 +4,18 @@ + '; -} elseif ($_SESSION['theme'] == 'light') { + } elseif ($_SESSION['theme'] == 'light') { echo ' '; -} -?> + } + ?> diff --git a/components/nav.php b/components/nav.php index bbf7315..47be020 100644 --- a/components/nav.php +++ b/components/nav.php @@ -6,7 +6,7 @@ $nav = '
-

User Actions

'; // TODO: Need to setup an email handler. // Request Role // - $view .= ' -
- - - - -
+ if ((isset($_COOKIE['user_id']) && $_COOKIE['user_id'] == $user_id) || (isset($_COOKIE['role_id']) && $_COOKIE['role_id'] <= 2)) { + $view .= ' +

User Actions

+
+ + + + +
- '; + } return $view; } diff --git a/functions/article_functions.php b/functions/article_functions.php index 8292638..f220027 100644 --- a/functions/article_functions.php +++ b/functions/article_functions.php @@ -1,7 +1,7 @@ query('SELECT article_id FROM article ORDER BY published_at DESC'); @@ -14,6 +14,19 @@ function article_ids_by_recency() return $ids; } +function article_ids_by_oldest() +{ + $conn = get_connection(); + $results = $conn->query('SELECT article_id FROM article ORDER BY published_at ASC'); + + $ids = []; + + while ($row = $results->fetch_assoc()) + $ids[] = $row['article_id']; + + return $ids; +} + function articles_ids_by_author($author_id) { $conn = get_connection(); @@ -77,6 +90,13 @@ function article_page_from_markdown($article_id) $markdown = read_file_one_string($_ENV['ARTICLES_FQ_PATH'] . $article_id . '/article.md'); $parsedown = new Parsedown(); + $article_content = $parsedown->text($markdown); + $article_toc = ''; + + $lines = preg_split("/\r\n|\n|\r/", $article_content); + foreach ($lines as $line) + if (str_contains($line, '

')) + $article_toc .= '

' . extract_content_from_tag($line, '

', '

') . '

'; $html = '
@@ -91,14 +111,37 @@ function article_page_from_markdown($article_id)
+
+
+ ' . $article_toc . ' +
+
+ ' . $article_content . ' +
+
+ '; - $html .= $parsedown->text($markdown); - $html .= ''; - return $html; } +function article_filters() +{ + $filters = ' +
+ + + +
+ '; + + return $filters; +} + /* * Generates cards for a given article. */ @@ -188,6 +231,7 @@ function create_article($author_id, $title, $excerpt, $tags, $markdown_file_cont return $id; } + function delete_article($article_id) { $conn = get_connection(); diff --git a/functions/functions.php b/functions/functions.php index 51deb8f..fbcb4ab 100644 --- a/functions/functions.php +++ b/functions/functions.php @@ -128,6 +128,17 @@ function read_file($file_path) return $fs; } +function extract_content_from_tag($string, $start, $end) +{ + $ini = strpos($string, $start); + if ($ini != 0) + return "nope"; + + $ini += strlen($start); + $len = strpos($string, $end, $ini) - $ini; + return substr($string, $ini, $len); +} + function check_image_upload($target, $filetype, $image) { return true; diff --git a/functions/init.php b/functions/init.php index 0721bd0..4dbf3bc 100644 --- a/functions/init.php +++ b/functions/init.php @@ -37,7 +37,7 @@ if (isset($_COOKIE['user_id'])) { $stmt->execute(); $user = $stmt->get_result()->fetch_assoc(); - $time = time() + 60 * 60 * 1; + $time = time() + 60 * 15; cookies($user, $time); } else if (isset($_COOKIE['remember_user'])) { $conn = get_connection(); @@ -64,7 +64,7 @@ if (isset($_COOKIE['user_id'])) { $stmt->bind_param('i', $_COOKIE['user_id']); $stmt->execute(); - $time = time() + 60 * 60 * 1; + $time = time() + 60 * 15; cookies($user, $time); } } diff --git a/js/md_preview.js b/js/md_preview.js new file mode 100644 index 0000000..233fce5 --- /dev/null +++ b/js/md_preview.js @@ -0,0 +1,17 @@ +// Optional: Real-time preview using JavaScript +const textarea = document.querySelector('textarea'); +const preview = document.getElementById('preview'); + +textarea.addEventListener('input', function() { + fetch('preview.php', { // Create a separate preview.php file for real-time updates + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + body: 'markdown=' + encodeURIComponent(this.value), + }) + .then(response => response.text()) + .then(data => { + preview.innerHTML = data; + }); +}); diff --git a/scripts/init.sql b/scripts/init.sql index e9c84b1..23c9d4f 100644 --- a/scripts/init.sql +++ b/scripts/init.sql @@ -21,6 +21,11 @@ CREATE TABLE user ( last_active TIMESTAMP DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP, is_active BOOLEAN DEFAULT FALSE, profile_picture VARCHAR(256), + bio VARCHAR(256), + x_username VARCHAR(32), + fb_username VARCHAR(32), + ig_username VARCHAR(32), + website VARCHAR(64), FOREIGN KEY (role_id) REFERENCES roles (role_id) ); diff --git a/vintagecoding.net.md b/vintagecoding.net.md index eb97b94..29cdf39 100644 --- a/vintagecoding.net.md +++ b/vintagecoding.net.md @@ -8,7 +8,7 @@ - [x] Ensure a consistent design & UX aesthetic --- v0.1.0 - [ ] Sensible CSS class names for a modular styling approach --- v0.2.0 -- [ ] Convert db_functions.php queries/statements into prepared SQL for protection against SQL injection --- v0.2.0 +- [x] Convert db_functions.php queries/statements into prepared SQL for protection against SQL injection --- v0.2.0 - [ ] Email Handler: - [ ] Email users subscribed to author or tag whenever a new article is posted. --- v0.3.0 @@ -17,7 +17,7 @@ #### Systems Interfaces - [x] Admin panel (manage articles & accounts) --- v0.2.0 -- [ ] Implement a notification modal system for handling error and success messages. --- v0.2.0 +- [ ] Implement a notification modal system for handling error and success messages. --- v0.3.0 - [ ] Accounts: #### Account CRUD --- account_functions.php @@ -61,11 +61,11 @@ - [x] Delete Articles by Author (for account deletion) --- v0.1.0 - [ ] Draft/unpublished status --- v0.2.0 - [ ] Update Article by ID (required to be the author updating) --- v0.2.0 - - [ ] Get Articles by Author --- v0.2.0 - - [ ] Sort / filter system --- v0.2.0 + - [x] Get Articles by Author --- v0.2.0 + - [x] Sort / filter system --- v0.2.0 #### Article System Interface - - [ ] Convert articles.php layout to a grid, to display a left hand column with a search/filtering system or table of contents. --- v0.2.0 + - [x] Convert articles.php layout to a grid, to display a left hand column with a search/filtering system or table of contents. --- v0.2.0 - [ ] Advanced markdown editor or upload markdown, with media upload support (images & code files, requires contributor or higher auth) --- v0.3.0 ### Unplanned but implemented