From 9a45075010434a4dedd0c5d48a78ff6be3bcfddc Mon Sep 17 00:00:00 2001 From: Joshua Ashton Date: Tue, 25 Mar 2025 19:42:40 -0600 Subject: [PATCH] v0.1.0 completed. only the owner can create new articles, users can modify their password, username, email, or delete their account, there's auth and permissions preventing accessing other user's information, and more. onto the road to v0.2.0 --- components/foot.php | 4 + components/head.php | 1 - components/nav.php | 6 + compose.php | 2 +- css/base.css | 98 +++++++++-- css/dark.css | 21 +++ css/light.css | 21 +++ functions/account_functions.php | 296 +++++++++++++++++++++++++++++++- functions/article_functions.php | 4 +- js/modal.js | 55 ++++++ scripts/init.sql | 2 +- user.php | 30 ++++ 12 files changed, 515 insertions(+), 25 deletions(-) create mode 100644 js/modal.js diff --git a/components/foot.php b/components/foot.php index 15b9011..60f907c 100644 --- a/components/foot.php +++ b/components/foot.php @@ -1,3 +1,7 @@ + + diff --git a/components/head.php b/components/head.php index 751dbdb..71595d7 100644 --- a/components/head.php +++ b/components/head.php @@ -15,7 +15,6 @@ if ($_SESSION['theme'] == 'dark') { '; } ?> - diff --git a/components/nav.php b/components/nav.php index 2765ee9..a0c8660 100644 --- a/components/nav.php +++ b/components/nav.php @@ -9,6 +9,12 @@ $nav = '
  • Articles
  • '; +if (isset($_COOKIE['user_id'])) { + $nav .= ' +
  • Account
  • + '; +} + if (isset($_COOKIE['role_id']) && $_COOKIE['role_id'] <= 3) { $nav .= '
  • Compose
  • diff --git a/compose.php b/compose.php index cfe86b8..69ea01e 100644 --- a/compose.php +++ b/compose.php @@ -17,7 +17,7 @@ function form() { $raw_tags = article_tags(); $tags = ' -
    +
    '; diff --git a/css/base.css b/css/base.css index 66d0f01..0855bcc 100644 --- a/css/base.css +++ b/css/base.css @@ -161,17 +161,19 @@ code { margin-top: 50px; padding: 18px; height: max-content; - width: 60%; + width: 70%; border-radius: 18px; display: flex; flex-direction: column; align-items: center; justify-content: center; + + transition-duration: 1s; } .form_card { - width: 40%; + width: 50%; } .form_card form { @@ -292,7 +294,7 @@ textarea { height: 500px; } -.form_tags {} +.checkboxes {} .form_checkbox_container { display: block; @@ -377,16 +379,82 @@ input[type="file"] { box-sizing: border-box; } -@media (max-width: 980px) { - h1 { - font-size: 4vw; - } - - p { - font-size: 2vw; - } - - .card { - margin: 75% auto; - } +.user_view { + width: 70%; + margin: 0 auto; +} + +.user_view h1, +.user_view .card_pic_box { + margin: 15px; +} + +.user_view .line { + margin: 0px 0px 30px 0px; +} + +.user_actions { + margin: 15px; + padding: 15px; + display: flex; + justify-content: space-evenly; + border-radius: 12px; +} + +.form_card h3, +.form_card h5 { + padding-bottom: 15px; +} + +.profile_picture { + height: 128px; + width: auto; + border-radius: 50%; +} + +/* The Modal (background) */ +.modal { + display: none; + position: fixed; + z-index: 1; + padding-top: 100px; + left: 0; + top: 0; + width: 100%; + height: 100%; + overflow: auto; + backdrop-filter: blur(10px); +} + +/* Modal Content */ +.form_card { + position: relative; +} + +.modal_content { + display: fit-content; + margin: auto; + width: 40%; + height: max-content; +} + +.modal_content .form_card { + width: 100%; + box-sizing: border-box; +} + +/* The Close Button */ +.modal_close { + position: absolute; + font-size: 48px; + top: 15px; + right: 30px; + font-weight: bold; + cursor: pointer; +} + +.modal_close:hover, +.modal_close:focus { + color: orange; + cursor: pointer; } diff --git a/css/dark.css b/css/dark.css index df14e75..e0a786a 100644 --- a/css/dark.css +++ b/css/dark.css @@ -41,6 +41,7 @@ code { .article_card, .form_card { border: 5px solid white; + background-color: black; } .button { @@ -82,3 +83,23 @@ textarea { .upload_button { border: 3px solid white; } + +/* .form_card:hover, */ +/* .form_card:focus, */ +/* .article_card:hover, */ +/* .article_card:focus { */ +/* box-shadow: 0 2px 4px 0 white, 0 4px 8px 0 white; */ +/* } */ + +.modal { + background-color: black; + background-color: rgba(0, 0, 0, 0.9); +} + +.modal_close { + color: white; +} + +.user_actions { + border: 3px solid white; +} diff --git a/css/light.css b/css/light.css index e09a5d4..a2a4f88 100644 --- a/css/light.css +++ b/css/light.css @@ -41,6 +41,7 @@ code { .article_card, .form_card { border: 5px solid black; + background-color: white; } .button { @@ -82,3 +83,23 @@ textarea { .upload_button { border: 3px solid black; } + +/* .form_card:hover, */ +/* .form_card:focus, */ +/* .article_card:hover, */ +/* .article_card:focus { */ +/* box-shadow: 0 2px 4px 0 black, 0 4px 8px 0 black; */ +/* } */ + +.modal { + background-color: white; + background-color: rgba(256, 256, 256, 0.9); +} + +.modal_close { + color: black; +} + +.user_actions { + border: 3px solid black; +} diff --git a/functions/account_functions.php b/functions/account_functions.php index 9e6dd92..a146176 100644 --- a/functions/account_functions.php +++ b/functions/account_functions.php @@ -97,7 +97,7 @@ function cookies($user, $time) function logout() { - foreach ($_COOKIE as $key => $value) { + foreach (array_keys($_COOKIE) as $key) { echo 'unsetting ' . $key . ' now.'; setcookie($key, '', time() - 3600, '/'); unset($_COOKIE[$key]); @@ -110,7 +110,7 @@ function login_form($error_msg = '') { $form = '
    -

    Log In

    +

    Log In

    @@ -123,7 +123,6 @@ function login_form($error_msg = '') - Sign Up ' . $error_msg . '
    @@ -137,7 +136,7 @@ function signup_form($error_msg = '') { $form = '
    -

    Sign Up

    +

    Sign Up

    @@ -159,7 +158,6 @@ function signup_form($error_msg = '') - Log In ' . $error_msg . '
    @@ -168,3 +166,291 @@ function signup_form($error_msg = '') return $form; } + +function reset_pw_form() +{ + $form = ' +
    +
    Reset Password
    + × +
    + + + + + + + + + +
    +
    + '; + + return $form; +} + +function reset_password($user_id, $new_password, $verify_new_password) +{ + if ($new_password != $verify_new_password) { + $error_msg = ' +
    + Passwords do not match! +
    + '; + + return $error_msg; + } + + $password_hash = hash('sha256', $new_password); + $stmt = 'UPDATE user SET password_hash = "' . $password_hash . '" WHERE user_id = ' . $user_id . ';'; + exec_statement($stmt, 0); +} + +function update_email_form() +{ + $form = ' +
    +
    Update Email
    + × +
    + + + + + + + + + +
    +
    + '; + + return $form; +} + +function update_email($user_id, $new_email, $verify_new_email) +{ + if ($new_email != $verify_new_email) { + $error_msg = ' +
    + Emails do not match! +
    + '; + + return $error_msg; + } + + $stmt = 'UPDATE user SET email = "' . $new_email . '" WHERE user_id = ' . $user_id . ';'; + exec_statement($stmt, 0); +} + +function update_username_form() +{ + $form = ' +
    +
    Update Username
    + × +
    + + + + + + + + + +
    +
    + '; + + return $form; +} + +function update_username($user_id, $new_username, $verify_new_username) +{ + if ($new_username != $verify_new_username) { + $error_msg = ' +
    + Usernames do not match! +
    + '; + + return $error_msg; + } + + $query = 'SELECT username FROM user WHERE username = "' . $new_username . '";'; + if (query_one_result($query) != null) { + $error_msg = ' +
    + Usernames is taken! +
    + '; + + return $error_msg; + } + + $stmt = 'UPDATE user SET username = "' . $new_username . '" WHERE user_id = ' . $user_id . ';'; + exec_statement($stmt, 0); +} + +function request_role_change_form() +{ + $query = 'SELECT * FROM roles WHERE role_id <> 1;'; + $results = query_one_or_more_results($query); + + $roles = '
    '; + while ($row = mysqli_fetch_array($results)) { + $roles .= ' + + '; + } + + $roles .= '
    '; + + $form = ' +
    +
    Request Role
    + × +
    + + ' . $roles . ' + + +
    +
    + '; + + return $form; +} + +function request_role_change($user_id, $new_role_id) +{ + // TODO: Need to implement email handler first. +} + +function delete_account_form() +{ + $form = ' +
    +
    Delete Account
    + × +
    + + + + + + + + + +
    +
    + '; + + return $form; +} + +function delete_account($user_id, $password, $verify_password) +{ + if ($user_id == 2025) { + $error_msg = ' +
    + I am the owner, and I cannot delete myself... +
    + '; + + return $error_msg; + } + + if ($password != $verify_password) { + $error_msg = ' +
    + Passwords do not match! +
    + '; + + return $error_msg; + } + + $password_hash = hash('sha256', $password); + $query = 'SELECT user_id FROM user WHERE user_id = ' . $user_id . ' AND password_hash = "' . $password_hash . '";'; + $user = query_one_result($query); + + if ($user == null) { + $error_msg = ' +
    + Username and/or Password are invalid! +
    + '; + + return $error_msg; + } + + $stmt = 'DELETE FROM user WHERE user_id = ' . $user_id . ';'; + exec_statement($stmt, 1); + logout(); +} + +function user_view($user_id) +{ + if (!isset($_COOKIE['role_id']) || ($_COOKIE['role_id'] > 2 && $_COOKIE['user_id'] != $user_id)) + header('Location: articles.php'); + + $query = 'SELECT user_id, username, email, role_id, created_at, last_active, is_active, profile_picture FROM user WHERE user_id = ' . $user_id . ';'; + $user = query_one_result($query); + if ($user == null) + header('Location: articles.php'); + + $view = ' +
    +
    +

    ' . $user['username'] . '

    +
    + +
    +
    +
    +

    User Actions

    + '; + + // TODO: Need to setup an email handler. + // Request Role + // + $view .= ' + + + + +
    + + + + +
    +
    + '; + + return $view; +} + +function admin_user_view($user_id) +{ + $query = 'SELECT user_id, username, email, role_id, created_at, last_active, is_active, profile_picture FROM user WHERE user_id = ' . $user_id . ';'; + $user = query_one_result($query); +} diff --git a/functions/article_functions.php b/functions/article_functions.php index b9693db..e83c795 100644 --- a/functions/article_functions.php +++ b/functions/article_functions.php @@ -78,7 +78,7 @@ function article_card($article_id) $tags_html .= '
    '; } - $author_query = 'SELECT username, profile_picture FROM user WHERE user_id = ' . $article['author_id'] . ';'; + $author_query = 'SELECT user_id, username, profile_picture FROM user WHERE user_id = ' . $article['author_id'] . ';'; $author = query_one_result($author_query); $card = ' @@ -87,7 +87,7 @@ function article_card($article_id)

    ' . $article['title'] . '

    - Written by: ' . $author['username'] . ' + Written by: ' . $author['username'] . ' Published: ' . $article['published_at'] . '
    diff --git a/js/modal.js b/js/modal.js new file mode 100644 index 0000000..f4102f6 --- /dev/null +++ b/js/modal.js @@ -0,0 +1,55 @@ +document.addEventListener('DOMContentLoaded', function() { + var modal = document.getElementById("user_actions_modal"); + var modal_content = document.getElementById("modal_content"); + var buttons = document.getElementsByClassName("modal_button"); + var spans = document.getElementsByClassName("modal_close"); + + for (let i = 0; i < buttons.length; i++) { + buttons[i].onclick = function() { + switch (i) { + case 0: + openModal("reset_pw_form"); + break; + case 1: + openModal("update_email_form"); + break; + case 2: + openModal("update_username_form"); + break; + case 3: + openModal("delete_account_form"); + break; + // case 3: + // openModal("request_role_form"); + // break; + default: + break; + } + } + } + + // Open modal function + function openModal(formId) { + modal_content.innerHTML = document.getElementById(formId).innerHTML; + modal.style.display = "block"; + } + + modal_content.addEventListener('click', function(event) { + if (event.target.classList.contains('modal_close')) { + closeModal(); + } + }); + + window.onclick = function(event) { + if (event.target.classList.contains('modal')) { + closeModal(); + } + }; + + // Close modal function + function closeModal() { + modal_content.innerHTML = ""; // Clear content when closing + modal.style.display = "none"; + } + +}); diff --git a/scripts/init.sql b/scripts/init.sql index 1de4682..c1f3e98 100644 --- a/scripts/init.sql +++ b/scripts/init.sql @@ -16,9 +16,9 @@ CREATE TABLE user ( username VARCHAR(32) NOT NULL UNIQUE, email VARCHAR(64) NOT NULL UNIQUE, password_hash VARCHAR(256) NOT NULL, - last_active TIMESTAMP DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP, role_id INT NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + last_active TIMESTAMP DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP, is_active BOOLEAN DEFAULT FALSE, profile_picture VARCHAR(256), FOREIGN KEY (role_id) REFERENCES roles (role_id) diff --git a/user.php b/user.php index d1d47cc..2409c24 100644 --- a/user.php +++ b/user.php @@ -20,6 +20,33 @@ if (isset($_POST['username'])) { header('Location: .'); } +if (isset($_POST['form_id'])) { + switch ($_POST['form_id']) { + case 'reset_pw_form': + $msg = reset_password($_COOKIE['user_id'], $_POST['new_pw'], $_POST['verify_new_pw']); + // code... + break; + case 'update_email_form': + $msg = update_email($_COOKIE['user_id'], $_POST['new_email'], $_POST['verify_new_email']); + // code... + break; + case 'update_username_form': + $msg = update_username($_COOKIE['user_id'], $_POST['new_username'], $_POST['verify_new_username']); + // code... + break; + case 'delete_account_form': + $msg = delete_account($_COOKIE['user_id'], $_POST['delete_password'], $_POST['delete_verify_password']); + break; + // case 'request_role_form': + // // code... + // break; + default: + // code... + break; + } + echo $msg; +} + include_once 'components/head.php'; if ($_GET['action'] == 'login') @@ -28,5 +55,8 @@ else if ($_GET['action'] == 'signup') echo signup_form(($msg ? $msg : '')); else if ($_GET['action'] == 'logout') logout(); +else if ($_GET['action'] == 'view' && isset($_GET['user_id'])) { + echo user_view($_GET['user_id']); +} include_once 'components/foot.php';