From 1c9f5cf3c0cf45c9b423d1727c01562a0db36625 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bruno=20Ryb=C3=A1rsky?= Date: Sun, 28 Apr 2024 22:37:23 +0200 Subject: [PATCH] Add PHPDocs generated by ChatGPT, add additional clarification to some functions, add addNewsComment function and API, currently untested and not implemented in the client, fix a bunch of stuff that PHPStorm pointed out --- assets/script.js | 53 +++++----- assets/style.css | 8 +- endpoints/newsarticle.php | 6 ++ index.php | 42 ++++---- lib/account.php | 201 ++++++++++++++++++++++++++++++++------ lib/config.php | 24 +++++ lib/dynamic_style.php | 8 +- lib/endpoint.php | 30 +++++- lib/inliner.php | 42 ++++++-- lib/meme.php | 77 +++++++++++++-- lib/navigation.php | 37 +++++-- lib/newsarticle.php | 70 ++++++++++++- lib/page.php | 63 ++++++++++-- lib/router.php | 10 +- lib/script_data.php | 12 ++- lib/sitemap.php | 16 ++- lib/upload.php | 102 ++++++++++++++++--- 17 files changed, 659 insertions(+), 142 deletions(-) diff --git a/assets/script.js b/assets/script.js index 1420898..21775ee 100644 --- a/assets/script.js +++ b/assets/script.js @@ -9,7 +9,7 @@ let config = function isLoggedIn() { "use strict"; - return UserInfo.Email && 0 < UserInfo.Email.length; + return UserInfo.Email && UserInfo.Email.length > 0; } async function handleResponse(data, successMessage, failureMessage) { @@ -19,7 +19,7 @@ async function handleResponse(data, successMessage, failureMessage) { const statusMessage = document.createElement("div"); statusMessage.classList.add("status-message"); - if ('Success' === data.Status) { + if (data.Status === 'Success') { statusMessage.innerText = successMessage; statusMessage.classList.add("success"); } else { @@ -104,10 +104,10 @@ async function displayList(data, elementId, deleteFunction) { headerRow.appendChild(th); } - if ("function" === typeof deleteFunction) { + if (typeof deleteFunction === "function") { const th = document.createElement("th"); let deleteBtn = document.createElement('i'); - deleteBtn.classList = ["ri-delete-bin-line"]; + deleteBtn.classList.add("ri-delete-bin-line"); th.appendChild(deleteBtn); headerRow.appendChild(th); } @@ -119,7 +119,7 @@ async function displayList(data, elementId, deleteFunction) { td.appendChild(document.createTextNode(line[key])); dataRow.appendChild(td); } - if ("function" === typeof deleteFunction) { + if (typeof deleteFunction === "function") { const td = document.createElement("td"); const deleteButton = document.createElement('button'); deleteButton.innerHTML = ""; @@ -234,13 +234,13 @@ async function articleInit() { let articleContainerElement = document.getElementById("articlecreatecontainer"); let articleCreateOpenElement = document.getElementById("articlecreateopen"); articleContainerElement.addEventListener("keyup", function (ev) { - if ("Escape" === ev.key) { + if (ev.key === "Escape") { togglearticlecreate(); } }); PageIntervals.push(setInterval(renderarticles, config.articleRefresh)); document.getElementById("articleprivilegeinput").setAttribute("max", UserInfo.Privileges); - if (2 > UserInfo.Privileges) { + if (UserInfo.Privileges < 2) { articleContainerElement.style.display = "none"; articleCreateOpenElement.style.display = "none"; } else { @@ -258,7 +258,7 @@ async function onPageLoad() { clearInterval(interval); } - if ("home" === currentSite && "settings" === currentPage) { + if (currentSite === "home" && currentPage === "settings") { if (document.getElementById("user-settings")) { await populateUserInfoFields(UserInfo); } @@ -267,16 +267,16 @@ async function onPageLoad() { await listUsers(true); } } - if ("account" === currentSite && "index" === currentPage && isLoggedIn()) { + if (currentSite === "account" && currentPage === "index" && isLoggedIn()) { await showDashboardGreeting(); } - if ("news" === currentSite && "index" === currentPage) { + if (currentSite === "news" && currentPage === "index") { await articleInit(); } - if ("account" === currentSite && "files" === currentPage) { + if (currentSite === "account" && currentPage === "files") { await listFiles(); } - if ("memes" === currentSite && "index" === currentPage) { + if (currentSite === "memes" && currentPage === "index") { await getMemeImages(); } } @@ -461,7 +461,7 @@ async function getUserInfo() { }; const result = await doAction('/account', data, "User info retrieved Successfully!", "User info retrieval failed.", true); - if (result && "Success" === result.Status) { + if (result && result.Status === "Success") { Object.keys(result.UserInfo).forEach(index => { let value = result.UserInfo[index]; localStorage.setItem("UserInfo_" + index, value); @@ -498,7 +498,7 @@ async function listUsers(silent) { doAction('/account', data, "User list retrieved Successfully!", "User list retrieval failed.", silent).then((result) => { - if (result && "Success" === result.Status) { + if (result && result.Status === "Success") { displayList(result.Users, "userListTable", deleteUser); } }); @@ -541,7 +541,7 @@ async function deleteActivationCode(activationCode) { //Admin settings end -if ("loading" === document.readyState) { +if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", initAjax); } else { setTimeout(initAjax, 0); @@ -574,7 +574,7 @@ async function getFileList() { action: "getAllFiles" }, "Zoznam súborov bol úspešne stiahnutý", "Nastala chyba pri sťahovaní zoznamu súborov", true); - if ("Success" === resp.Status) { + if (resp.Status === "Success") { return resp.Files; } else { return false; @@ -642,10 +642,10 @@ async function reloadMemeVotes(memeID) { memeVoteCounterElement.innerText = memeVotes; memeVoteCounterElement.classList.remove("positive", "negative", "neutral"); - if (memeVotes > 0) { + if (0 < memeVotes) { memeVoteCounterElement.classList.add("positive"); } - else if (memeVotes < 0) { + else if (0 > memeVotes) { memeVoteCounterElement.classList.add("negative"); } else { @@ -654,20 +654,15 @@ async function reloadMemeVotes(memeID) { memeVoteUpvoteButtonElement.classList.remove('visual_hover'); memeVoteDownvoteButtonElement.classList.remove('visual_hover'); - if (userVote > 0) { + let memeUpvoteVariant = "line"; + let memeDownvoteVariant = "line"; + if (0 < userVote) { memeUpvoteVariant = "fill"; - memeDownvoteVariant = "line"; memeVoteUpvoteButtonElement.classList.add('visual_hover'); - } - else if (userVote < 0) { - memeUpvoteVariant = "line"; + } else if (0 > userVote) { memeDownvoteVariant = "fill"; memeVoteDownvoteButtonElement.classList.add('visual_hover'); } - else { - memeUpvoteVariant = "line"; - memeDownvoteVariant = "line"; - } memeVoteUpvoteElement.classList = [`ri-arrow-up-circle-${memeUpvoteVariant}`]; memeVoteDownvoteElement.classList = [`ri-arrow-down-circle-${memeDownvoteVariant}`]; @@ -680,13 +675,13 @@ async function voteMeme(memeID, isUpvote){ let memeVoteDelete = false; if(isUpvote) { if(memeVoteUpvoteElement.classList.contains("ri-arrow-up-circle-fill")){ - deleteVoteMeme(memeID); + await deleteVoteMeme(memeID); memeVoteDelete = true; } } else { if(memeVoteDownvoteElement.classList.contains("ri-arrow-down-circle-fill")){ - deleteVoteMeme(memeID); + await deleteVoteMeme(memeID); memeVoteDelete = true; } } diff --git a/assets/style.css b/assets/style.css index 49bf616..701d142 100644 --- a/assets/style.css +++ b/assets/style.css @@ -371,11 +371,11 @@ div#articleslist>article{ } .positive { - color: green; + color: #008000; } .negative { - color: red; + color: #ff0000; } .neutral { @@ -390,12 +390,12 @@ div#articleslist>article{ } .visual_hover.meme_upvote { - --pico-background-color: green; + --pico-background-color: #008000; --pico-border-color: unset; } .visual_hover.meme_downvote { - --pico-background-color: red; + --pico-background-color: #ff0000; --pico-border-color: unset; } diff --git a/endpoints/newsarticle.php b/endpoints/newsarticle.php index 099094b..856cb3b 100644 --- a/endpoints/newsarticle.php +++ b/endpoints/newsarticle.php @@ -11,6 +11,12 @@ function endpoint($endpoint_data): array $endpoint_data["title"], $endpoint_data["body"] ), + "addNewsComment" => addNewsComment( + $endpoint_data["user_id"], + $endpoint_data['news_article_id'], + $endpoint_data["comment_text"], + $endpoint_data["parent_id"] + ), default => ["Status" => "Fail", "message" => "Invalid action"], }; } \ No newline at end of file diff --git a/index.php b/index.php index 0196cd2..d683e30 100644 --- a/index.php +++ b/index.php @@ -1,32 +1,40 @@ 0 && !empty($_SESSION["email"]) && $_SESSION["privilege_level"] >= $routerConfig["permissions"]["logged_in_default"]; } +/** + * Checks if the logged-in user is verified. + * + * @global array $routerConfig Global configuration array containing permission thresholds. + * @return bool Returns true if the user is logged in and verified; otherwise, false. + */ function isVerified(): bool { global $routerConfig; return isLoggedIn() && $_SESSION["privilege_level"] >= $routerConfig["permissions"]["verified"]; } +/** + * Checks if the logged-in user is trustworthy. + * + * @global array $routerConfig Global configuration array containing permission thresholds. + * @return bool Returns true if the user is logged in and considered trustworthy; otherwise, false. + */ function isTrustWorthy(): bool { global $routerConfig; return isLoggedIn() && $_SESSION["privilege_level"] >= $routerConfig["permissions"]["trustworthy"]; } - +/** + * Checks if the logged-in user is a moderator. + * + * @global array $routerConfig Global configuration array containing permission thresholds. + * @return bool Returns true if the user is logged in and a moderator; otherwise, false. + */ function isModerator(): bool { global $routerConfig; return isLoggedIn() && $_SESSION["privilege_level"] >= $routerConfig["permissions"]["moderator"]; } - +/** + * Checks if the logged-in user is a user admin. + * + * @global array $routerConfig Global configuration array containing permission thresholds. + * @return bool Returns true if the user is logged in and a user admin; otherwise, false. + */ function isUserAdmin(): bool { global $routerConfig; return isLoggedIn() && $_SESSION["privilege_level"] >= $routerConfig["permissions"]["user_admin"]; } - +/** + * Checks if the logged-in user is an admin. + * + * @global array $routerConfig Global configuration array containing permission thresholds. + * @return bool Returns true if the user is logged in and an admin; otherwise, false. + */ function isAdmin(): bool { global $routerConfig; return isLoggedIn() && $_SESSION["privilege_level"] >= $routerConfig["permissions"]["admin"]; } - -function generateActivationToken(): string +/** + * Generates a secure token for account activation or other purposes using cryptographic methods. + * + * @return string|null Returns a hexadecimal token or null in case of an error. + */ +function generateActivationToken(): ?string { try { return bin2hex(random_bytes(16)); } catch (RandomException) { + return null; } } - -function isEmailAvailable($email): bool +/** + * Checks if an email address is available for registration. + * + * @param string $email The email address to check. + * @return bool Returns true if the email is not already registered; otherwise, false. + *@global mysqli $mysqli Global mysqli object for database access. + */ +function isEmailAvailable(string $email): bool { global $mysqli; $stmt = $mysqli->prepare("SELECT COUNT(*) FROM Users WHERE Email = ?"); @@ -59,7 +102,12 @@ function isEmailAvailable($email): bool return $count === 0; } - +/** + * Sets default session data typically used for a logged-out user(includes users that have just visited the page). + * + * @global array $routerConfig Global configuration array used for setting initial privilege levels. + * @return void + */ function setDefaultSessionData(): void { global $routerConfig; @@ -71,8 +119,15 @@ function setDefaultSessionData(): void $_SESSION["minecraft_nickname"] = ""; $_SESSION["privilege_level"] = $routerConfig["permissions"]["logged_out"]; } - -function verifyPassword($userID, $password): bool +/** + * Verifies if the provided password matches the stored hash for the user. + * + * @param int $userID The user ID whose password is to be verified. + * @param string $password The password to verify. + * @return bool Returns true if the password matches the stored hash; otherwise, false. + *@global mysqli $mysqli Global mysqli object for database access. + */ +function verifyPassword(int $userID, string $password): bool { global $mysqli; $stmt = $mysqli->prepare("SELECT PasswordHash FROM Users WHERE ID = ?"); @@ -85,7 +140,12 @@ function verifyPassword($userID, $password): bool return !empty($password_hash) && !empty($password) && password_verify($password, $password_hash); } - +/** + * Updates session data from the database for the logged-in user. + * + * @global mysqli $mysqli Global mysqli object for database access. + * @return void + */ function UpdateSession(): void { global $mysqli; @@ -118,8 +178,15 @@ function UpdateSession(): void $_SESSION["favorite_color"] = $favorite_color; } - -function doLogin($email, $password): array +/** + * Attempts to log in a user with the given credentials. + * + * @param string $email The user's email address. + * @param string $password The user's password. + * @global mysqli $mysqli Global database connection object. + * @return array An array containing the status of the login attempt ('Success' or 'Fail'). + */ +function doLogin(string $email, string $password): array { global $mysqli; $found = false; @@ -149,7 +216,12 @@ function doLogin($email, $password): array } return $found ? ["Status" => "Success"] : ["Status" => "Fail"]; } - +/** + * Logs out the current user by resetting session data. + * Fails when the user wasn't logged in + * + * @return array An array with the logout status ('Success' if logged out, 'Fail' otherwise). + */ function doLogout(): array { if(isLoggedIn()){ @@ -159,8 +231,19 @@ function doLogout(): array return ["Status" => "Fail"]; } } - -function doRegister($firstname, $lastname, $email, $password, $activation_token): array +/** + * Registers a new user with provided personal details and activation token. + * + * @param string $firstname The user's first name. + * @param string $lastname The user's last name. + * @param string $email The user's email. + * @param string $password The user's password. + * @param string $activation_token The activation token to verify the registration. + * @global mysqli $mysqli Global database connection object. + * @global array $routerConfig Global configuration settings. + * @return array An array with the registration status ('Success' or 'Fail'). + */ +function doRegister(string $firstname, string $lastname, string $email, string $password, string $activation_token): array { global $mysqli, $routerConfig; $status = ["Status" => "Fail"]; @@ -186,8 +269,15 @@ function doRegister($firstname, $lastname, $email, $password, $activation_token) return $status; } - -function changePassword($oldPassword, $newPassword): array +/** + * Changes the user's password after verifying the old password. + * + * @param string $oldPassword The current password for verification. + * @param string $newPassword The new password to be set. + * @return array An array indicating whether the password change was successful ('Success' or 'Fail'). + *@global mysqli $mysqli Global database connection object. + */ +function changePassword(string $oldPassword, string $newPassword): array { global $mysqli; $status = ["Status" => "Fail"]; @@ -206,8 +296,17 @@ function changePassword($oldPassword, $newPassword): array } -// Function to update user profile -function updateUserProfile($firstName, $lastName, $nickname, $minecraft_nickname): array +/** + * Updates user profile information in the database. + * + * @param string $firstName The new first name. + * @param string $lastName The new last name. + * @param string $nickname The new nickname. + * @param string $minecraft_nickname The new Minecraft nickname. + * @return array Status of the profile update ('Success' or 'Fail'). + *@global mysqli $mysqli Global database connection object. + */ +function updateUserProfile(string $firstName, string $lastName, string $nickname, string $minecraft_nickname): array { global $mysqli; $status = ["Status" => "Fail"]; @@ -230,8 +329,14 @@ function updateUserProfile($firstName, $lastName, $nickname, $minecraft_nickname return $status; } -// Function to update user email -function updateUserEmail($email): array +/** + * Updates the email address of the logged-in user after validation. + * + * @param string $email The new email address to update. + * @return array Status of the email update ('Success' or 'Fail'). + *@global mysqli $mysqli Global database connection object. + */ +function updateUserEmail(string $email): array { global $mysqli; $status = ["Status" => "Fail"]; @@ -274,7 +379,11 @@ function updateUserEmail($email): array return $status; } - +/** + * Retrieves and updates the current session with user information from the database. + * + * @return array Contains user information and status if the user is logged in. + */ function getUserInfo(): array { $output = ["Status" => "Fail"]; @@ -310,8 +419,14 @@ function getUserInfo(): array return $output; } - -function addActivationCodes($count): array +/** + * Generates a specified number of activation codes for user registration and adds them to the database. + * + * @param int $count Number of activation codes to generate. + * @return array An array containing the generated codes and status ('Success' or 'Fail'). + *@global mysqli $mysqli Global database connection object. + */ +function addActivationCodes(int $count): array { global $mysqli; $activationCodes = []; @@ -342,7 +457,12 @@ function addActivationCodes($count): array return $output; } - +/** + * Lists all registered users, available only to user admins. + * + * @global mysqli $mysqli Global database connection object. + * @return array An array containing user data and status. + */ function listUsers(): array { global $mysqli; @@ -364,7 +484,12 @@ function listUsers(): array return $output; } - +/** + * Lists activation codes available for assigning to new users, available only for user admins. + * + * @global mysqli $mysqli Global database connection object. + * @return array An array containing activation codes and status. + */ function listActivationCodes(): array { global $mysqli; @@ -409,8 +534,14 @@ function listActivationCodes(): array return $output; } - -function deleteUser($userID): array +/** + * Deletes a user by their ID, available only to user admins. + * + * @param int $userID The ID of the user to delete. + * @return array Status of the delete operation ('Success' or 'Fail'). + *@global mysqli $mysqli Global database connection object. + */ +function deleteUser(int $userID): array { global $mysqli; $status = ["Status" => "Fail"]; @@ -425,8 +556,14 @@ function deleteUser($userID): array } return $status; } - -function deleteActivationCode($activationCode): array +/** + * Deletes an activation code, available only to user admins. + * + * @param string $activationCode The activation code to delete. + * @return array Status of the delete operation ('Success' or 'Fail'). + *@global mysqli $mysqli Global database connection object. + */ +function deleteActivationCode(string $activationCode): array { global $mysqli; $status = ["Status" => "Fail"]; diff --git a/lib/config.php b/lib/config.php index 0db0b39..432c31e 100644 --- a/lib/config.php +++ b/lib/config.php @@ -1,4 +1,28 @@ ` tag with custom CSS if a favorite color is set + * and the user is logged in. Returns an empty string if no conditions are met. + */ function doDynamicStyling() :string { $dynamic_style = ""; diff --git a/lib/endpoint.php b/lib/endpoint.php index e54b0f0..81b9122 100644 --- a/lib/endpoint.php +++ b/lib/endpoint.php @@ -1,6 +1,16 @@ tags + * with corresponding "; }, $inputString); } - -function inlineScriptFromSrc($inputString): string +/** + * Processes an HTML string to inline all external JavaScript files by replacing "; }, $inputString); } - -function minifyCss($css): string +/** + * Minifies CSS content by removing comments, unnecessary whitespaces, semicolons, and optimizing other aspects of the stylesheet. + * Might be broken, currently disabled in the config + * + * @param string $css The original CSS content. + * @return string The minified CSS content. + */ +function minifyCss(string $css): string { // Remove comments $css = preg_replace('!/\*[^*]*\*+([^/][^*]*\*+)*/!', '', $css); @@ -64,8 +84,14 @@ function minifyCss($css): string return trim($css); } - -function minifyJs($js): string +/** + * Minifies JavaScript content by removing comments, unnecessary whitespaces, and optimizing spaces around operators. + * Might be broken, currently disabled in the config + * + * @param string $js The original JavaScript content. + * @return string The minified JavaScript content. + */ +function minifyJs(string $js): string { // Remove newlines and tabs diff --git a/lib/meme.php b/lib/meme.php index 4ce4293..4d73134 100644 --- a/lib/meme.php +++ b/lib/meme.php @@ -2,7 +2,15 @@ require_once "lib/upload.php"; require_once "lib/account.php"; - +/** + * Adds a meme to the database with associated image and text content. + * + * @param string $title The title of the meme. + * @param string $memeText The text content of the meme. + * @param int $imageID The ID of the image associated with the meme. + * @global mysqli $mysqli The database connection object. + * @return array Returns an associative array with the operation status and a message. + */ function addMeme(string $title, string $memeText, int $imageID): array { global $mysqli; @@ -17,7 +25,21 @@ function addMeme(string $title, string $memeText, int $imageID): array } return $output; } - +/** + * Renders a meme into HTML based on provided data and a template. + * + * @param int $id The ID of the meme. + * @param int $authorId The author's user ID. + * @param string $title The title of the meme. + * @param string $textContent The text content of the meme. + * @param string $createdAt The creation timestamp of the meme. + * @param string $filePath The file path of the associated image. + * @param int $imageWidth The width of the image. + * @param int $imageHeight The height of the image. + * @param string $userNickname The nickname of the meme's author. + * @param string $meme_template The HTML template for a meme. (used to not read the template over and over when rendering more memes) + * @return string Returns the rendered HTML of the meme. + */ function renderMeme(int $id, int $authorId, string $title, string $textContent, string $createdAt, string $filePath, int $imageWidth, int $imageHeight, string $userNickname, string $meme_template): string { @@ -64,7 +86,14 @@ function renderMeme(int $id, int $authorId, string $title, string $textContent, return str_replace('__TEMPLATE_MEME_TEXT__', htmlspecialchars($textContent), $meme_out); } - +/** + * Generates an HTML representation of a gallery of memes from the database. + * Renders the memes using renderMeme. + * + * @global mysqli $mysqli The database connection object. + * @global array $routerConfig Configuration settings including paths to templates. + * @return string Returns the complete HTML content of the meme gallery. + */ function renderMemeGallery(): string { global $mysqli; @@ -105,7 +134,13 @@ function renderMemeGallery(): string $stmtlist->close(); return $meme_gallery_out; } - +/** + * Deletes a meme from the database if the current user has the right permissions. + * + * @param int $memeID The ID of the meme to delete. + * @global mysqli $mysqli The database connection object. + * @return array Returns an associative array with the status of the operation. + */ function deleteMeme(int $memeID): array { global $mysqli; @@ -126,7 +161,14 @@ function deleteMeme(int $memeID): array } return $out; } - +/** + * Records or updates a vote on a meme by the current user. + * + * @param int $memeID The ID of the meme to be voted on. + * @param int $isUpvote Indicates whether the vote is an upvote (1) or downvote (0). + * @global mysqli $mysqli The database connection object. + * @return array Returns an associative array with the status of the vote operation. + */ function voteMeme(int $memeID, int $isUpvote): array { global $mysqli; @@ -143,7 +185,13 @@ function voteMeme(int $memeID, int $isUpvote): array $memeVoteConn->close(); return $out; } - +/** + * Deletes a vote previously made by the current user on a meme. + * + * @param int $memeID The ID of the meme whose vote is to be deleted. + * @global mysqli $mysqli The database connection object. + * @return array Returns an associative array with the status of the deletion. + */ function deleteVoteMeme(int $memeID): array { global $mysqli; @@ -157,7 +205,14 @@ function deleteVoteMeme(int $memeID): array $memeVoteConn->close(); return $out; } - +/** + * Calculates the net votes for a meme and determines if the current user has voted on it. + * The array has both the net votes and the user vote(0 when the user hasn't voted) + * + * @param int $memeID The ID of the meme for which votes are being calculated. + * @global mysqli $mysqli The database connection object. + * @return array Returns an array with net votes and the user's vote status. + */ function calculateNetVotes(int $memeID): array { global $mysqli; @@ -193,7 +248,13 @@ function calculateNetVotes(int $memeID): array ]; } - +/** + * Fetches the net votes and user's vote status for a specific meme. + * Essentially just a wrapper of getMemeVotes for an API call + * + * @param int $memeID The ID of the meme to fetch votes for. + * @return array Returns an array with the net votes and the user's vote status, along with operation status. + */ function getMemeVotes(int $memeID): array { $voteData = calculateNetVotes($memeID); diff --git a/lib/navigation.php b/lib/navigation.php index df9fe06..0080b56 100644 --- a/lib/navigation.php +++ b/lib/navigation.php @@ -1,11 +1,24 @@ "Success", diff --git a/lib/newsarticle.php b/lib/newsarticle.php index 0f80108..add6552 100644 --- a/lib/newsarticle.php +++ b/lib/newsarticle.php @@ -1,4 +1,15 @@ close(); } return $output; -} \ No newline at end of file +} + +/** + * Adds a comment to a news article. + * + * @param int $userId User who is commenting. + * @param int $newsArticleId ID of the news article. + * @param string $commentText The content of the comment. + * @param int|null $parentId ID of the parent comment if it's a reply. + * @return array Status array indicating success or failure. + * @global mysqli $mysqli The mysqli database connection object. + */ +function addNewsComment(int $userId, int $newsArticleId, string $commentText, ?int $parentId = null): array { + global $mysqli; + + $output = ["Status" => "Fail"]; // Default Status is "Fail" + + if (!isLoggedIn()) { + $output['Error'] = "User must be logged in."; + return $output; + } + + // Prepare the SQL statement to prevent SQL injection + $stmt = $mysqli->prepare("INSERT INTO NewsComments (ParentID, UserID, NewsArticleID, CommentText) VALUES (?, ?, ?, ?);"); + + // Bind parameters. 'i' denotes an integer and 's' denotes a string. + $stmt->bind_param("iiis", $parentId, $userId, $newsArticleId, $commentText); + + // Execute the query + if ($stmt->execute()) { + // Check if any rows were affected + if ($stmt->affected_rows > 0) { + $output["Status"] = "Success"; + } else { + $output["Error"] = "No rows affected."; + } + } else { + $output["Error"] = $stmt->error; + } + + // Close statement + $stmt->close(); + + return $output; +} diff --git a/lib/page.php b/lib/page.php index a25e615..327f035 100644 --- a/lib/page.php +++ b/lib/page.php @@ -1,16 +1,37 @@ /', '', $content); } - -function parsePageTag($input): array +/** + * Parses custom `` tags from the given input string and extracts parameters. + * Returns the input string with `` tags removed and a list of parameters. + * + * @param string $input The input HTML or text containing `` tags. + * @return array Returns an associative array with 'parameters' (parsed from the tag) + * and 'output' (the modified input string with `` tags removed). + */ +function parsePageTag(string $input): array { // Define the pattern for the tag $pattern = '/]+)><\/page>/i'; @@ -34,8 +55,15 @@ function parsePageTag($input): array // If no match is found, return the original input return ['parameters' => [], 'output' => $input]; } - -function renderPage($page_name = null, $site_name = null): array +/** + * Renders a page based on specified page and site names, handling dynamic and static content, + * permissions, and error pages. + * + * @param string|null $page_name The name of the page to render. If null, uses default from request. + * @param string|null $site_name The name of the site to render. If null, uses default from request. + * @return array Returns an associative array containing the rendered page content, page name, site name, and page title. + */ +function renderPage(string $page_name = null, string $site_name = null): array { global $routerConfig; global $routerRequest; @@ -138,8 +166,16 @@ function renderPage($page_name = null, $site_name = null): array ]; } - -function getPage($site_name_in = null, $page_name_in = null): string +/** + * Compiles a complete web page by injecting dynamic elements into a template skeleton, + * including headers, footers, and SEO tags. + * It is used when not going to a page by AJAX to initialize everything. + * + * @param string|null $site_name_in The site name to be used; defaults from global configuration if null. + * @param string|null $page_name_in The page name to be used; defaults from global configuration if null. + * @return string The complete HTML content of the web page ready for display. + */ +function getPage(string $site_name_in = null, string $page_name_in = null): string { $page_tmp = renderPage($page_name_in, $site_name_in); @@ -223,8 +259,15 @@ function getPage($site_name_in = null, $page_name_in = null): string } return str_replace("__TEMPLATE_PAGE_TITLE__", $page_title, $out); } - -function getPageEndpoint($page_name, $site_name) :array +/** + * Provides an API interface to get page details including content and meta-information for routing purposes. + * This is what enables the page to never refresh. + * + * @param string $page_name The name of the page. + * @param string $site_name The name of the site. + * @return array Returns an array with status, page content, location URL, and title for the requested page. + */ +function getPageEndpoint(string $page_name, string $site_name) :array { $page_location = "/" . $site_name . "/" . $page_name; $page_tmp = renderPage($page_name, $site_name); diff --git a/lib/router.php b/lib/router.php index e42bd7e..6436aaa 100644 --- a/lib/router.php +++ b/lib/router.php @@ -1,6 +1,14 @@ 0 && count(array_filter(array_keys($phpArray), 'is_string')) === count($phpArray)) { // Generate JavaScript code to save each array element to local storage diff --git a/lib/sitemap.php b/lib/sitemap.php index 967be22..a3a5fa1 100644 --- a/lib/sitemap.php +++ b/lib/sitemap.php @@ -1,8 +1,16 @@ ' . PHP_EOL; header('Content-type: application/xml'); - echo $sitemap; + return $sitemap; } \ No newline at end of file diff --git a/lib/upload.php b/lib/upload.php index 276ef30..efb4344 100644 --- a/lib/upload.php +++ b/lib/upload.php @@ -1,6 +1,12 @@ close(); return $stat; } - -function doImageUpload($inFile, $outFile): bool +/** + * Handles the uploading process of an image, including its conversion to webp format, rotation based on orientation, and saving. + * + * @param string $inFile The temporary file path of the uploaded file. + * @param string $outFile The target file path where the processed image should be saved. + * @return bool Returns true if the file was successfully processed and saved, false otherwise. + */ +function doImageUpload(string $inFile, string $outFile): bool { // Create Imagick object $width = 0; @@ -96,15 +126,22 @@ function doImageUpload($inFile, $outFile): bool } catch (ImagickException) { } - // Check if the reencoding was successful + // Check if the reencoding was successful, if yes, save into the database. if (file_exists($outFile)) { return saveUploadedFileInDatabase($outFile, 'image/webp', $width, $height); } else { return false; } } +/** + * Retrieves a list of files from the database, optionally filtered to include only files uploaded by the current user. + * Access to an unfiltered list (files of all users) is only available to moderators. + * + * @param bool $onlyMine Whether to retrieve only files uploaded by the logged-in user. If false, files from all users are returned if the user is a moderator. + * @return array Returns an array containing file data along with a status message. + */ -function listFiles($onlyMine = true): array +function listFiles(bool $onlyMine = true): array { $output = ["Status" => "Success"]; require_once "lib/account.php"; @@ -156,6 +193,12 @@ function listFiles($onlyMine = true): array return $output; } +/** + * Processes incoming files from the $_FILES global (after processed by getIncomingFiles), performs checks, and attempts to upload a file based on its type. + * Currently only supports valid image files. + * + * @return array Returns an array indicating the success or failure ('Status' key) of the file processing operations. + */ function parseIncomingFiles(): array { @@ -183,8 +226,15 @@ function parseIncomingFiles(): array } return $output; } +/** + * Generates a file path for uploading based on the type of the file, the ID of the uploader and the date and time of uploading. + * + * @param string $type The type of the file, typically used to categorize the file. + * @param string $filename The base name of the file, used in generating the final path. + * @return string Returns the full path for storing the file or an empty string if the type is not recognized. + */ -function getUploadPath($type = "unknown", $filename = "hehe"): string +function getUploadPath(string $type = "unknown", string $filename = "hehe"): string { $type = makePathSafe($type); $id = makePathSafe($_SESSION["ID"]); @@ -202,6 +252,15 @@ function getUploadPath($type = "unknown", $filename = "hehe"): string return ""; } } +/** + * Checks if a file with a given ID exists in the database and does permission checks. + * Access is granted to only the user's files, in order to access all files the onlyMine parameter + * must be false and the user must be a moderator. + * + * @param int $fileId The ID of the file to check. + * @param bool $onlyMine Whether to limit the search to files uploaded by the logged-in user. + * @return bool|string Returns the path of the file if it exists and meets the criteria, false otherwise. + */ function fileExists(int $fileId, bool $onlyMine = true): bool|string { @@ -230,7 +289,14 @@ function fileExists(int $fileId, bool $onlyMine = true): bool|string return false; } } - +/** + * Adds a file to a specified group, if the user created the group or creates a new group if + * a group with a specified ID does not exist yet + * + * @param int $groupId The ID of the group to which the file should be added. + * @param int $fileId The ID of the file to add to the group. + * @return array Returns an associative array with a 'Status' key indicating success or failure. + */ function addToGroup(int $groupId, int $fileId): array { $output = ["Status" => "Fail"]; @@ -253,16 +319,22 @@ function addToGroup(int $groupId, int $fileId): array } return $output; } - +/** + * Deletes a file entry from the database and the file system, a user can only delete his own files, + * except when he is a moderator, in that case he can delete all files. + * + * @param int $fileID The ID of the file to be deleted. + * @return array Returns an array with a 'Status' key indicating the success or failure of the deletion operation. + */ function deleteFile(int $fileID): array { global $mysqli; $out = ["Status" => "Fail"]; if (isLoggedIn()) { - $file_location = fileExists($fileID, !isAdmin()); - $query = !isAdmin() ? 'DELETE FROM Files WHERE ID = ? AND UploadedBy = ?' : 'DELETE FROM Files WHERE ID = ?'; + $file_location = fileExists($fileID, !isModerator()); + $query = !isModerator() ? 'DELETE FROM Files WHERE ID = ? AND UploadedBy = ?' : 'DELETE FROM Files WHERE ID = ?'; $stmtDelete = $mysqli->prepare($query); - if (!isAdmin()) { + if (!isModerator()) { $stmtDelete->bind_param('ii', $fileID, $_SESSION['ID']); } else { $stmtDelete->bind_param('i', $fileID);