Login/Register/confirm and new comment feature

This commit is contained in:
Kilian Hofmann 2024-06-21 12:47:08 +02:00
parent 70da3e66fd
commit 5f573a468f
15 changed files with 516 additions and 40 deletions

16
base/helpers.php Normal file
View File

@ -0,0 +1,16 @@
<?php
function guidv4($data = null)
{
// Generate 16 bytes (128 bits) of random data or use the data passed into the function.
$data = $data ?? random_bytes(16);
assert(strlen($data) == 16);
// Set version to 0100
$data[6] = chr(ord($data[6]) & 0x0f | 0x40);
// Set bits 6-7 to 10
$data[8] = chr(ord($data[8]) & 0x3f | 0x80);
// Output the 36 character UUID.
return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4));
}

View File

@ -2,4 +2,11 @@
<symbol id="exclamation-triangle-fill" fill="currentColor" viewBox="0 0 16 16">
<path d="M8.982 1.566a1.13 1.13 0 0 0-1.96 0L.165 13.233c-.457.778.091 1.767.98 1.767h13.713c.889 0 1.438-.99.98-1.767L8.982 1.566zM8 5c.535 0 .954.462.9.995l-.35 3.507a.552.552 0 0 1-1.1 0L7.1 5.995A.905.905 0 0 1 8 5zm.002 6a1 1 0 1 1 0 2 1 1 0 0 1 0-2z" />
</symbol>
<symbol id="arrow-left" fill="currentColor" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M15 8a.5.5 0 0 0-.5-.5H2.707l3.147-3.146a.5.5 0 1 0-.708-.708l-4 4a.5.5 0 0 0 0 .708l4 4a.5.5 0 0 0 .708-.708L2.707 8.5H14.5A.5.5 0 0 0 15 8" />
</symbol>
<symbol id="info-fill" fill="currentColor" viewBox="0 0 16 16">
<path d="M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16zm.93-9.412-1 4.705c-.07.34.029.533.304.533.194 0 .487-.07.686-.246l-.088.416c-.287.346-.92.598-1.465.598-.703 0-1.002-.422-.808-1.319l.738-3.468c.064-.293.006-.399-.287-.47l-.451-.081.082-.381 2.29-.287zM8 5.5a1 1 0 1 1 0-2 1 1 0 0 1 0 2z" />
</symbol>
</svg>

Before

Width:  |  Height:  |  Size: 428 B

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -108,7 +108,7 @@
<h2 class="m-3">
Result: <?= $result ?>
</h2>
<? } ?>
<?php } ?>
<?php if (isset($errors)) {
foreach ($errors as $error) { ?>
<h2 class="text-danger"><?= $error ?></h2>

View File

@ -1,4 +1,4 @@
<form action="./actions/comment.php" method="post" class="needs-validation row m-0" novalidate>
<form action="./actions/comment" method="post" class="needs-validation row m-0" novalidate>
<div class="col m-0 g-3">
<h4 class="mb-3">Leave a Comment!</h4>
<div class="mb-3">

View File

@ -1,31 +1,25 @@
<?php
include_once "../../../base/settings.php";
include_once "../../../base/headers.php";
include_once "../../../base/database.php";
include_once "../queries.php";
session_name("PHP_SESSION_guestBook");
session_start();
$user = $_SESSION["user"] ?? null;
$_SESSION["error"] = [];
if (!isset($_POST["title"])) {
array_push($_SESSION["error"], "Title was not among the data sent.");
}
if (!isset($_POST["name"])) {
array_push($_SESSION["error"], "Name was not among the data sent.");
if (!isset($user)) {
Headers::redirect("../login");
return;
}
if (!isset($_POST["comment"])) {
array_push($_SESSION["error"], "Comment was not among the data sent.");
}
$title = trim($_POST["title"]);
$name = trim($_POST["name"]);
$comment = substr(trim($_POST["comment"]), 0, 250);
$time = time();
if ($title === "") {
array_push($_SESSION["error"], "The title was empty.");
}
if ($name === "") {
array_push($_SESSION["error"], "The name was empty.");
}
if ($comment === "") {
array_push($_SESSION["error"], "The comment was empty.");
}
@ -35,11 +29,13 @@ if (count($_SESSION["error"]) > 0) {
return;
}
$string = file_get_contents("../data/data.json") ?? "[]";
$json = json_decode($string);
$db = DB::openConnection();
array_push($json, ["time" => $time, "title" => $title, "name" => $name, "comment" => $comment]);
$stmt = $db->prepare($insertCommentQuery);
$stmt->bindValue(":UID", $user["id"]);
$stmt->bindValue(":COM", $comment);
$stmt->execute();
file_put_contents("../data/data.json", json_encode($json));
DB::closeConnection($db);
Headers::redirect("../");

View File

@ -0,0 +1,74 @@
<?php
include_once "../../../base/settings.php";
include_once "../../../base/headers.php";
include_once "../../../base/database.php";
include_once "../queries.php";
session_name("PHP_SESSION_guestBook");
session_start();
if (isset($_SESSION["user"])) {
Headers::redirect("../");
return;
}
$_SESSION["error"] = [];
unset($_SESSION["user"]);
if (!isset($_POST["username"])) {
array_push($_SESSION["error"], "username was not among the data sent.");
}
if (!isset($_POST["password"])) {
array_push($_SESSION["error"], "password was not among the data sent.");
}
$username = trim($_POST["username"]);
$password = trim($_POST["password"]);
if ($username === "") {
array_push($_SESSION["error"], "The username was empty.");
}
if ($password === "") {
array_push($_SESSION["error"], "The password was empty.");
}
if (count($_SESSION["error"]) > 0) {
Headers::redirect("../login");
return;
}
$db = DB::openConnection();
$stmt = $db->prepare($loginQuery);
$stmt->bindValue(":USR", $username);
$stmt->execute();
$user = $stmt->fetch();
if ($user) {
if (password_verify($password, $user["passwort"])) {
$_SESSION["user"] = $user;
// REHASH for safety should it somehow change
if (password_needs_rehash($user["passwort"], PASSWORD_DEFAULT)) {
$newHash = password_hash($password, PASSWORD_DEFAULT);
$stmt = $db->prepare($updatePasswordQuery);
$stmt->bindValue(":PAS", $newHash);
$stmt->bindValue(":UID", $user["id"]);
$stmt->execute();
}
unset($_SESSION["user"]["passwort"]);
unset($_SESSION["user"]["confirmationcode"]);
} else {
array_push($_SESSION["error"], "Username or Password incorrect.");
}
} else {
array_push($_SESSION["error"], "Username or Password incorrect.");
}
DB::closeConnection($db);
if (count($_SESSION["error"]) > 0) {
Headers::redirect("../login");
return;
}
Headers::redirect("../");

View File

@ -0,0 +1,24 @@
<?php
include_once "../../../base/headers.php";
session_name("PHP_SESSION_guestBook");
session_start();
$_SESSION = array();
if (ini_get("session.use_cookies")) {
$params = session_get_cookie_params();
setcookie(
session_name(),
'',
time() - 42000,
$params["path"],
$params["domain"],
$params["secure"],
$params["httponly"]
);
}
session_destroy();
Headers::redirect("../");

View File

@ -0,0 +1,87 @@
<?php
include_once "../../../base/settings.php";
include_once "../../../base/headers.php";
include_once "../../../base/database.php";
include_once "../../../base/helpers.php";
include_once "../queries.php";
session_name("PHP_SESSION_guestBook");
session_start();
if (isset($_SESSION["user"])) {
Headers::redirect("../");
return;
}
$_SESSION["error"] = [];
$_SESSION["message"] = [];
unset($_SESSION["user"]);
if (!isset($_POST["username"])) {
array_push($_SESSION["error"], "username was not among the data sent.");
}
if (!isset($_POST["email"])) {
array_push($_SESSION["error"], "email was not among the data sent.");
}
if (!isset($_POST["password"])) {
array_push($_SESSION["error"], "password was not among the data sent.");
}
if (!isset($_POST["passwordConfirm"])) {
array_push($_SESSION["error"], "passwordConfirm was not among the data sent.");
}
$username = trim($_POST["username"]);
$email = trim($_POST["email"]);
$password = trim($_POST["password"]);
$passwordConfirm = trim($_POST["passwordConfirm"]);
if ($username === "") {
array_push($_SESSION["error"], "The username was empty.");
}
if ($email === "") {
array_push($_SESSION["error"], "The email was empty.");
}
if ($password === "") {
array_push($_SESSION["error"], "The password was empty.");
}
if ($passwordConfirm === "" || $password !== $passwordConfirm) {
array_push($_SESSION["error"], "The passwords do not match.");
}
if (count($_SESSION["error"]) > 0) {
Headers::redirect("../register");
return;
}
$db = DB::openConnection();
try {
$guid = guidv4();
$stmt = $db->prepare($insertUserQuery);
$stmt->bindValue(":USR", $username);
$stmt->bindValue(":PAS", password_hash($password, PASSWORD_DEFAULT));
$stmt->bindValue(":EMA", $email);
$stmt->bindValue(":COD", $guid);
$stmt->execute();
mail(
$email,
"Account activation GuestBookDB",
"Hello $username. To activate your account, visit https://userpage.fu-berlin.de/khofmann/phpCourse/tasks/guestBookDB/confirm?c=$guid"
);
array_push($_SESSION["message"], "Please confirm your account using the mail we sent you.");
} catch (PDOException $e) {
if ($e->getCode() === "23000") {
array_push($_SESSION["error"], "A user with this username or email already exists");
} else {
array_push($_SESSION["error"], "SQL Error: {$e->getMessage()}");
}
Headers::redirect("../register");
return;
}
DB::closeConnection($db);
Headers::redirect("../login");

View File

@ -0,0 +1,7 @@
<div class="alert alert-primary alert-dismissible fade show mt-3 mb-0">
<svg class="bi flex-shrink-0 me-2" width="24" height="24">
<use xlink:href="#info-fill" />
</svg>
<?= $message ?>
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>

View File

@ -1,20 +1,7 @@
<form action="" method="post" class="needs-validation row g-3 m-0 position-relative" novalidate>
<h1 class="text-danger position-absolute construction">Under Construction</h1>
<fieldset disabled class="mt-0 opacity-50">
<form action="./actions/comment" method="post" class="needs-validation row g-3 m-0 position-relative" novalidate>
<fieldset class="mt-0">
<div class="col m-0 g-3">
<h4 class="mb-3">Leave a Comment!</h4>
<div class="mb-3">
<input class="form-control" type="text" name="title" required placeholder="A catching title" />
<div class="invalid-feedback">
Please enter a title.
</div>
</div>
<div class="mb-3">
<input class="form-control" type="text" name="name" required placeholder="Your name" />
<div class="invalid-feedback">
Please enter a name.
</div>
</div>
<div class="mb-1">
<textarea class="form-control" id="comment" name="comment" maxlength="250" required placeholder="Comment here"></textarea>
<div class="invalid-feedback">

View File

@ -0,0 +1,44 @@
<?php
include_once "../../base/settings.php";
include_once "../../base/headers.php";
include_once "../../base/database.php";
include_once "../../base/helpers.php";
include_once "./queries.php";
session_name("PHP_SESSION_guestBook");
session_start();
if (isset($_SESSION["user"])) {
Headers::redirect("../");
return;
}
$_SESSION["error"] = [];
$_SESSION["message"] = [];
if (!isset($_GET["c"])) {
Headers::redirect(".");
return;
}
$code = $_GET["c"];
$db = DB::openConnection();
$stmt = $db->prepare($confirmFetchUserQuery);
$stmt->bindValue(":COD", $code);
$stmt->execute();
$uid = $stmt->fetch(PDO::FETCH_COLUMN);
if ($uid !== false) {
$stmt = $db->prepare($confirmUserQuery);
$stmt->bindValue(":UID", $uid);
$stmt->execute();
array_push($_SESSION["message"], "Account confirmed, you can now log in!");
} else {
array_push($_SESSION["error"], "Account could not be confirmed");
}
DB::closeConnection($db);
Headers::redirect("./login");

View File

@ -27,6 +27,7 @@
session_name("PHP_SESSION_guestBook");
session_start();
$errors = $_SESSION["error"] ?? [];
$user = $_SESSION["user"] ?? null;
$_SESSION["error"] = [];
if (isset($_GET["p"]) && !is_numeric($_GET["p"])) {
@ -37,8 +38,6 @@
$db = DB::openConnection();
$stmt = $db->prepare($countQuery);
$stmt->execute();
$maxPage = intdiv($stmt->fetch(PDO::FETCH_COLUMN), 9);
@ -55,13 +54,21 @@
?>
<div class="container-fluid p-0">
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<nav class="navbar navbar-expand-lg navbar-light bg-light sticky-top shadow-sm">
<div class="container-fluid">
<a class="navbar-brand" href="#">Guest Book</a>
<?php if (!isset($user)) { ?>
<a href="./login">Login</a>
<?php } else { ?>
<div>
Hallo <?= $user["benutzer"] ?> |
<a class="fs-6" href="./actions/logout">Logout</a>
</div>
<?php } ?>
</div>
</nav>
<div class="row m-0">
<div class="row m-0 mb-3">
<div class="col">
<?php foreach ($errors as $error) {
include "./components/error.php";
@ -69,6 +76,8 @@
</div>
</div>
<div class="row row-cols-1 row-cols-lg-3 g-3 m-0 mb-3">
<?php foreach ($data as $comment) {
include "./components/comment.php";
@ -78,7 +87,16 @@
<?php include "./components/pagination.php" ?>
<hr />
<?php include "./components/newComment.php" ?>
<?php if (isset($user)) { ?>
<?php include "./components/newComment.php" ?>
<?php } else { ?>
<div class="row g-3 m-0">
<h4> You wish to leave a Comment?</h4>
<p>
<a href="./login">Login</a> or <a href="./register">register</a> a new account!
</p>
</div>
<?php } ?>
</div>
</body>

View File

@ -0,0 +1,86 @@
<!DOCTYPE html>
<html lang="en">
<head>
<?php include_once "../../base/meta.php" ?>
<title>Guest Book DB - Login</title>
<script src="./js/formValidation.js"></script>
</head>
<body>
<?php
// HTML
include_once "../../base/icons.php";
// PHP
include_once "../../base/headers.php";
Headers::html();
session_name("PHP_SESSION_guestBook");
session_start();
$errors = $_SESSION["error"] ?? [];
$messages = $_SESSION["message"] ?? [];
$_SESSION["error"] = [];
$_SESSION["message"] = [];
if (isset($_SESSION["user"])) {
Headers::redirect(".");
return;
}
?>
<div class="container-fluid p-0">
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<div class="container-fluid">
<a class="navbar-brand" href=".">
<svg class="bi flex-shrink-0 me-2" width="24" height="24">
<use xlink:href="#arrow-left" />
</svg>
Home
</a>
</div>
</nav>
<div class="row m-0 mb-3">
<div class="col">
<?php foreach ($errors as $error) {
include "./components/error.php";
} ?>
</div>
</div>
<div class="row m-1 mb-3">
<div class="col">
<?php foreach ($messages as $message) {
include "./components/message.php";
} ?>
</div>
</div>
<form action="./actions/login" method="post" class="needs-validation row g-3 m-0 position-relative" novalidate>
<fieldset class="mt-0">
<div class="col m-0 g-3">
<div class="mb-3">
<input class="form-control" type="text" name="username" required placeholder="Username" />
<div class="invalid-feedback">
Please enter a username.
</div>
</div>
<div class="mb-3">
<input class="form-control" type="password" name="password" required placeholder="Password" />
<div class="invalid-feedback">
Please enter a password.
</div>
</div>
<button type="submit" class="btn btn-primary mb-3">Log in</button>
</div>
</fieldset>
<p class="m-0">
Don't have an account yet? <a href="./register">Register</a> now!
</p>
</form>
</body>
</html>

View File

@ -19,3 +19,46 @@ $countQuery = "
COUNT(*)
FROM
egb_gaestebuch";
$loginQuery = "
SELECT
*
FROM
egb_benutzer
WHERE
benutzer LIKE :USR AND
status = 1";
$updatePasswordQuery = "
UPDATE
egb_benutzer
SET
passwort = :PAS
WHERE
id = :UID";
$insertCommentQuery = "
INSERT INTO
egb_gaestebuch(benutzer_id, beitrag)
VALUES
(:UID, :COM)";
$insertUserQuery = "
INSERT INTO
egb_benutzer(benutzer, passwort, email, confirmationcode)
VALUES(:USR, :PAS, :EMA, :COD)";
$confirmFetchUserQuery = "
SELECT
ID
FROM
egb_benutzer
WHERE
confirmationcode LIKE :COD";
$confirmUserQuery = "
UPDATE
egb_benutzer
SET
status = 1
WHERE id = :UID";

View File

@ -0,0 +1,87 @@
<html lang="en">
<head>
<?php include_once "../../base/meta.php" ?>
<title>Guest Book DB - Register</title>
<script src="./js/formValidation.js"></script>
</head>
<body>
<?php
// HTML
include_once "../../base/icons.php";
// PHP
include_once "../../base/headers.php";
Headers::html();
session_name("PHP_SESSION_guestBook");
session_start();
$errors = $_SESSION["error"] ?? [];
$_SESSION["error"] = [];
if (isset($_SESSION["user"])) {
Headers::redirect(".");
return;
}
?>
<div class="container-fluid p-0">
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<div class="container-fluid">
<a class="navbar-brand" href=".">
<svg class="bi flex-shrink-0 me-2" width="24" height="24">
<use xlink:href="#arrow-left" />
</svg>
Home
</a>
</div>
</nav>
<div class="row m-0 mb-3">
<div class="col">
<?php foreach ($errors as $error) {
include "./components/error.php";
} ?>
</div>
</div>
<form action="./actions/register" method="post" class="needs-validation row g-3 m-0 position-relative" novalidate oninput='passwordConfirm.setCustomValidity(passwordConfirm.value != password.value ? "Passwords do not match." : "")'>
<fieldset class="mt-0">
<div class="col m-0 g-3">
<div class="mb-3">
<input class="form-control" type="text" name="username" required placeholder="Username" />
<div class="invalid-feedback">
Please enter a username.
</div>
</div>
<div class="mb-3">
<input class="form-control" type="email" name="email" required placeholder="E-mail" />
<div class="invalid-feedback">
Please enter an email.
</div>
</div>
<div class="mb-3">
<input class="form-control" type="password" name="password" required placeholder="Password" />
<div class="invalid-feedback">
Please enter a password.
</div>
</div>
<div class="mb-3">
<input class="form-control" type="password" name="passwordConfirm" required placeholder="Password again" />
<div class="invalid-feedback">
Please enter the same password.
</div>
</div>
<button type="submit" class="btn btn-primary mb-3">Register</button>
</div>
</fieldset>
<p class="m-0">
Already have an account yet? <a href="./login">Login</a> here!
</p>
</form>
</body>
</html>