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

This commit is contained in:
Joshua Ashton
2025-03-25 19:42:40 -06:00
parent 0a27cd1f5c
commit 9a45075010
12 changed files with 515 additions and 25 deletions
+4
View File
@@ -1,3 +1,7 @@
</body> </body>
<footer>
<script src="js/modal.js"></script>
</footer>
</html> </html>
-1
View File
@@ -15,7 +15,6 @@ if ($_SESSION['theme'] == 'dark') {
'; ';
} }
?> ?>
<script src="js/theme-toggle.js"></script>
</head> </head>
+6
View File
@@ -9,6 +9,12 @@ $nav = '
<li><a href="/articles.php"' . ($current_page == '/articles.php' ? ' class="underline"' : '') . '>Articles</a></li> <li><a href="/articles.php"' . ($current_page == '/articles.php' ? ' class="underline"' : '') . '>Articles</a></li>
'; ';
if (isset($_COOKIE['user_id'])) {
$nav .= '
<li><a href="/user.php?action=view&user_id=' . $_COOKIE['user_id'] . '"' . ($current_page == '/user.php' ? ' class="underline"' : '') . '>Account</a></li>
';
}
if (isset($_COOKIE['role_id']) && $_COOKIE['role_id'] <= 3) { if (isset($_COOKIE['role_id']) && $_COOKIE['role_id'] <= 3) {
$nav .= ' $nav .= '
<li><a href="/compose.php"' . ($current_page == '/compose.php' ? ' class="underline"' : '') . '>Compose</a></li> <li><a href="/compose.php"' . ($current_page == '/compose.php' ? ' class="underline"' : '') . '>Compose</a></li>
+1 -1
View File
@@ -17,7 +17,7 @@ function form()
{ {
$raw_tags = article_tags(); $raw_tags = article_tags();
$tags = ' $tags = '
<div class="form_tags"> <div class="checkboxes">
<label>Tags</label> <label>Tags</label>
'; ';
+83 -15
View File
@@ -161,17 +161,19 @@ code {
margin-top: 50px; margin-top: 50px;
padding: 18px; padding: 18px;
height: max-content; height: max-content;
width: 60%; width: 70%;
border-radius: 18px; border-radius: 18px;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
transition-duration: 1s;
} }
.form_card { .form_card {
width: 40%; width: 50%;
} }
.form_card form { .form_card form {
@@ -292,7 +294,7 @@ textarea {
height: 500px; height: 500px;
} }
.form_tags {} .checkboxes {}
.form_checkbox_container { .form_checkbox_container {
display: block; display: block;
@@ -377,16 +379,82 @@ input[type="file"] {
box-sizing: border-box; box-sizing: border-box;
} }
@media (max-width: 980px) { .user_view {
h1 { width: 70%;
font-size: 4vw; margin: 0 auto;
} }
p { .user_view h1,
font-size: 2vw; .user_view .card_pic_box {
} margin: 15px;
}
.card {
margin: 75% auto; .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;
} }
+21
View File
@@ -41,6 +41,7 @@ code {
.article_card, .article_card,
.form_card { .form_card {
border: 5px solid white; border: 5px solid white;
background-color: black;
} }
.button { .button {
@@ -82,3 +83,23 @@ textarea {
.upload_button { .upload_button {
border: 3px solid white; 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;
}
+21
View File
@@ -41,6 +41,7 @@ code {
.article_card, .article_card,
.form_card { .form_card {
border: 5px solid black; border: 5px solid black;
background-color: white;
} }
.button { .button {
@@ -82,3 +83,23 @@ textarea {
.upload_button { .upload_button {
border: 3px solid black; 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;
}
+291 -5
View File
@@ -97,7 +97,7 @@ function cookies($user, $time)
function logout() function logout()
{ {
foreach ($_COOKIE as $key => $value) { foreach (array_keys($_COOKIE) as $key) {
echo 'unsetting ' . $key . ' now.'; echo 'unsetting ' . $key . ' now.';
setcookie($key, '', time() - 3600, '/'); setcookie($key, '', time() - 3600, '/');
unset($_COOKIE[$key]); unset($_COOKIE[$key]);
@@ -110,7 +110,7 @@ function login_form($error_msg = '')
{ {
$form = ' $form = '
<div class="form_card"> <div class="form_card">
<h3>Log In</h3> <h3 class="underline">Log In</h3>
<form method="post"> <form method="post">
<label for="username">Username / Email</label> <label for="username">Username / Email</label>
<input id="username" name="username" required> <input id="username" name="username" required>
@@ -123,7 +123,6 @@ function login_form($error_msg = '')
<span class="checkmark"></span> <span class="checkmark"></span>
</label> </label>
<a href="user.php?action=signup">Sign Up</a>
<input class="button" type="submit" value="Log In"> <input class="button" type="submit" value="Log In">
' . $error_msg . ' ' . $error_msg . '
</form> </form>
@@ -137,7 +136,7 @@ function signup_form($error_msg = '')
{ {
$form = ' $form = '
<div class="form_card"> <div class="form_card">
<h3>Sign Up</h3> <h3 class="underline">Sign Up</h3>
<form method="post"> <form method="post">
<label for="username">Username</label> <label for="username">Username</label>
<input id="username" name="username" required> <input id="username" name="username" required>
@@ -159,7 +158,6 @@ function signup_form($error_msg = '')
<span class="checkmark"></span> <span class="checkmark"></span>
</label> </label>
<a href="user.php?action=login">Log In</a>
<input class="button" type="submit" value="Sign Up"> <input class="button" type="submit" value="Sign Up">
' . $error_msg . ' ' . $error_msg . '
</form> </form>
@@ -168,3 +166,291 @@ function signup_form($error_msg = '')
return $form; return $form;
} }
function reset_pw_form()
{
$form = '
<div class="form_card">
<h5 class="underline">Reset Password</h5>
<span class="modal_close">&times;</span>
<form method="post">
<input type="hidden" name="form_id" value="reset_pw_form">
<label for="new_pw">New Password</label>
<input id="new_pw" name="new_pw" type="password" required>
<label for="verify_new_pw">Verify New Password</label>
<input id="verify_new_pw" name="verify_new_pw" type="password" required>
<input type="submit" class="button" value="Reset Password">
</form>
</div>
';
return $form;
}
function reset_password($user_id, $new_password, $verify_new_password)
{
if ($new_password != $verify_new_password) {
$error_msg = '
<div class="error">
Passwords do not match!
</div>
';
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 = '
<div class="form_card">
<h5 class="underline">Update Email</h5>
<span class="modal_close">&times;</span>
<form method="post">
<input type="hidden" name="form_id" value="update_email_form">
<label for="new_email">New Email</label>
<input id="new_email" name="new_email" required>
<label for="verify_new_email">Verify New Email</label>
<input id="verify_new_email" name="verify_new_email" required>
<input type="submit" class="button" value="Update Email">
</form>
</div>
';
return $form;
}
function update_email($user_id, $new_email, $verify_new_email)
{
if ($new_email != $verify_new_email) {
$error_msg = '
<div class="error">
Emails do not match!
</div>
';
return $error_msg;
}
$stmt = 'UPDATE user SET email = "' . $new_email . '" WHERE user_id = ' . $user_id . ';';
exec_statement($stmt, 0);
}
function update_username_form()
{
$form = '
<div class="form_card">
<h5 class="underline">Update Username</h5>
<span class="modal_close">&times;</span>
<form method="post">
<input type="hidden" name="form_id" value="update_username_form">
<label for="new_username">New Username</label>
<input id="new_username" name="new_username" required>
<label for="verify_new_username">Verify New Username</label>
<input id="verify_new_username" name="verify_new_username" required>
<input type="submit" class="button" value="Update Username">
</form>
</div>
';
return $form;
}
function update_username($user_id, $new_username, $verify_new_username)
{
if ($new_username != $verify_new_username) {
$error_msg = '
<div class="error">
Usernames do not match!
</div>
';
return $error_msg;
}
$query = 'SELECT username FROM user WHERE username = "' . $new_username . '";';
if (query_one_result($query) != null) {
$error_msg = '
<div class="error">
Usernames is taken!
</div>
';
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 = '<div class="checkboxes">';
while ($row = mysqli_fetch_array($results)) {
$roles .= '
<label class="form_checkbox_container">' . $row['role'] . '
<input name="requested_role" value="' . $row['role_id'] . '" type="checkbox"' . ($row['role_id'] == $_COOKIE['role_id'] ? ' checked' : '') . '>
<span class="checkmark"></span>
</label>
';
}
$roles .= '</div>';
$form = '
<div class="form_card">
<h5 class="underline">Request Role</h5>
<span class="modal_close">&times;</span>
<form method="post">
<input type="hidden" name="form_id" value="request_role_form">
' . $roles . '
<input type="submit" class="button" value="Request Role">
</form>
</div>
';
return $form;
}
function request_role_change($user_id, $new_role_id)
{
// TODO: Need to implement email handler first.
}
function delete_account_form()
{
$form = '
<div class="form_card">
<h5 class="underline">Delete Account</h5>
<span class="modal_close">&times;</span>
<form method="post">
<input type="hidden" name="form_id" value="delete_account_form">
<label for="delete_password">Password</label>
<input id="delete_password" name="delete_password" type="password" required>
<label for="delete_verify_password">Verify Password</label>
<input id="delete_verify_password" name="delete_verify_password" type="password" required>
<input type="submit" class="button" value="Delete Account">
</form>
</div>
';
return $form;
}
function delete_account($user_id, $password, $verify_password)
{
if ($user_id == 2025) {
$error_msg = '
<div class="error">
I am the owner, and I cannot delete myself...
</div>
';
return $error_msg;
}
if ($password != $verify_password) {
$error_msg = '
<div class="error">
Passwords do not match!
</div>
';
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 = '
<div class="error">
Username and/or Password are invalid!
</div>
';
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 = '
<div class="user_view">
<div class="row">
<h1>' . $user['username'] . '</h1>
<div class="card_pic_box">
<img src="/profile_pictures/' . $user['profile_picture'] . '" class="profile_picture" />
</div>
</div>
<div class="line"></div>
<h3>User Actions</h3>
';
// TODO: Need to setup an email handler.
// <a class="modal_button underline">Request Role</a>
// <div id="request_role_form" class="modal_form">' . request_role_change_form() . '</div>
$view .= '
<div class="user_actions">
<a class="modal_button underline">Reset Password</a>
<a class="modal_button underline">Update Email</a>
<a class="modal_button underline">Update Username</a>
<a class="modal_button underline">Delete Account</a>
</div>
<div id="user_actions_modal" class="modal">
<div class="modal_content">
<div id="modal_content"></div>
</div>
</div>
<div style="display: none;">
<div id="reset_pw_form" class="modal_form">' . reset_pw_form() . '</div>
<div id="update_email_form" class="modal_form">' . update_email_form() . '</div>
<div id="update_username_form" class="modal_form">' . update_username_form() . '</div>
<div id="delete_account_form" class="modal_form">' . delete_account_form() . '</div>
</div>
</div>
';
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);
}
+2 -2
View File
@@ -78,7 +78,7 @@ function article_card($article_id)
$tags_html .= '</div>'; $tags_html .= '</div>';
} }
$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); $author = query_one_result($author_query);
$card = ' $card = '
@@ -87,7 +87,7 @@ function article_card($article_id)
<h4>' . $article['title'] . '</h4> <h4>' . $article['title'] . '</h4>
<div class="card_info_box"> <div class="card_info_box">
<div class="col-right"> <div class="col-right">
<small>Written by: ' . $author['username'] . '</small> <a href="user.php?action=view&user_id=' . $author['user_id'] . '">Written by: ' . $author['username'] . '</a>
<small>Published: ' . $article['published_at'] . '</small> <small>Published: ' . $article['published_at'] . '</small>
</div> </div>
<div class="card_pic_box"> <div class="card_pic_box">
+55
View File
@@ -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";
}
});
+1 -1
View File
@@ -16,9 +16,9 @@ CREATE TABLE user (
username VARCHAR(32) NOT NULL UNIQUE, username VARCHAR(32) NOT NULL UNIQUE,
email VARCHAR(64) NOT NULL UNIQUE, email VARCHAR(64) NOT NULL UNIQUE,
password_hash VARCHAR(256) NOT NULL, password_hash VARCHAR(256) NOT NULL,
last_active TIMESTAMP DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
role_id INT NOT NULL, role_id INT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
last_active TIMESTAMP DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
is_active BOOLEAN DEFAULT FALSE, is_active BOOLEAN DEFAULT FALSE,
profile_picture VARCHAR(256), profile_picture VARCHAR(256),
FOREIGN KEY (role_id) REFERENCES roles (role_id) FOREIGN KEY (role_id) REFERENCES roles (role_id)
+30
View File
@@ -20,6 +20,33 @@ if (isset($_POST['username'])) {
header('Location: .'); 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'; include_once 'components/head.php';
if ($_GET['action'] == 'login') if ($_GET['action'] == 'login')
@@ -28,5 +55,8 @@ else if ($_GET['action'] == 'signup')
echo signup_form(($msg ? $msg : '')); echo signup_form(($msg ? $msg : ''));
else if ($_GET['action'] == 'logout') else if ($_GET['action'] == 'logout')
logout(); logout();
else if ($_GET['action'] == 'view' && isset($_GET['user_id'])) {
echo user_view($_GET['user_id']);
}
include_once 'components/foot.php'; include_once 'components/foot.php';