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
This commit is contained in:
Bruno Rybársky 2024-04-28 22:37:23 +02:00
parent 6e7df7f034
commit 1c9f5cf3c0
17 changed files with 659 additions and 142 deletions

@ -9,7 +9,7 @@ let config =
function isLoggedIn() { function isLoggedIn() {
"use strict"; "use strict";
return UserInfo.Email && 0 < UserInfo.Email.length; return UserInfo.Email && UserInfo.Email.length > 0;
} }
async function handleResponse(data, successMessage, failureMessage) { async function handleResponse(data, successMessage, failureMessage) {
@ -19,7 +19,7 @@ async function handleResponse(data, successMessage, failureMessage) {
const statusMessage = document.createElement("div"); const statusMessage = document.createElement("div");
statusMessage.classList.add("status-message"); statusMessage.classList.add("status-message");
if ('Success' === data.Status) { if (data.Status === 'Success') {
statusMessage.innerText = successMessage; statusMessage.innerText = successMessage;
statusMessage.classList.add("success"); statusMessage.classList.add("success");
} else { } else {
@ -104,10 +104,10 @@ async function displayList(data, elementId, deleteFunction) {
headerRow.appendChild(th); headerRow.appendChild(th);
} }
if ("function" === typeof deleteFunction) { if (typeof deleteFunction === "function") {
const th = document.createElement("th"); const th = document.createElement("th");
let deleteBtn = document.createElement('i'); let deleteBtn = document.createElement('i');
deleteBtn.classList = ["ri-delete-bin-line"]; deleteBtn.classList.add("ri-delete-bin-line");
th.appendChild(deleteBtn); th.appendChild(deleteBtn);
headerRow.appendChild(th); headerRow.appendChild(th);
} }
@ -119,7 +119,7 @@ async function displayList(data, elementId, deleteFunction) {
td.appendChild(document.createTextNode(line[key])); td.appendChild(document.createTextNode(line[key]));
dataRow.appendChild(td); dataRow.appendChild(td);
} }
if ("function" === typeof deleteFunction) { if (typeof deleteFunction === "function") {
const td = document.createElement("td"); const td = document.createElement("td");
const deleteButton = document.createElement('button'); const deleteButton = document.createElement('button');
deleteButton.innerHTML = "<i class='ri-delete-bin-line'></i>"; deleteButton.innerHTML = "<i class='ri-delete-bin-line'></i>";
@ -234,13 +234,13 @@ async function articleInit() {
let articleContainerElement = document.getElementById("articlecreatecontainer"); let articleContainerElement = document.getElementById("articlecreatecontainer");
let articleCreateOpenElement = document.getElementById("articlecreateopen"); let articleCreateOpenElement = document.getElementById("articlecreateopen");
articleContainerElement.addEventListener("keyup", function (ev) { articleContainerElement.addEventListener("keyup", function (ev) {
if ("Escape" === ev.key) { if (ev.key === "Escape") {
togglearticlecreate(); togglearticlecreate();
} }
}); });
PageIntervals.push(setInterval(renderarticles, config.articleRefresh)); PageIntervals.push(setInterval(renderarticles, config.articleRefresh));
document.getElementById("articleprivilegeinput").setAttribute("max", UserInfo.Privileges); document.getElementById("articleprivilegeinput").setAttribute("max", UserInfo.Privileges);
if (2 > UserInfo.Privileges) { if (UserInfo.Privileges < 2) {
articleContainerElement.style.display = "none"; articleContainerElement.style.display = "none";
articleCreateOpenElement.style.display = "none"; articleCreateOpenElement.style.display = "none";
} else { } else {
@ -258,7 +258,7 @@ async function onPageLoad() {
clearInterval(interval); clearInterval(interval);
} }
if ("home" === currentSite && "settings" === currentPage) { if (currentSite === "home" && currentPage === "settings") {
if (document.getElementById("user-settings")) { if (document.getElementById("user-settings")) {
await populateUserInfoFields(UserInfo); await populateUserInfoFields(UserInfo);
} }
@ -267,16 +267,16 @@ async function onPageLoad() {
await listUsers(true); await listUsers(true);
} }
} }
if ("account" === currentSite && "index" === currentPage && isLoggedIn()) { if (currentSite === "account" && currentPage === "index" && isLoggedIn()) {
await showDashboardGreeting(); await showDashboardGreeting();
} }
if ("news" === currentSite && "index" === currentPage) { if (currentSite === "news" && currentPage === "index") {
await articleInit(); await articleInit();
} }
if ("account" === currentSite && "files" === currentPage) { if (currentSite === "account" && currentPage === "files") {
await listFiles(); await listFiles();
} }
if ("memes" === currentSite && "index" === currentPage) { if (currentSite === "memes" && currentPage === "index") {
await getMemeImages(); await getMemeImages();
} }
} }
@ -461,7 +461,7 @@ async function getUserInfo() {
}; };
const result = await doAction('/account', data, "User info retrieved Successfully!", "User info retrieval failed.", true); 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 => { Object.keys(result.UserInfo).forEach(index => {
let value = result.UserInfo[index]; let value = result.UserInfo[index];
localStorage.setItem("UserInfo_" + index, value); 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) => { 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); displayList(result.Users, "userListTable", deleteUser);
} }
}); });
@ -541,7 +541,7 @@ async function deleteActivationCode(activationCode) {
//Admin settings end //Admin settings end
if ("loading" === document.readyState) { if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", initAjax); document.addEventListener("DOMContentLoaded", initAjax);
} else { } else {
setTimeout(initAjax, 0); setTimeout(initAjax, 0);
@ -574,7 +574,7 @@ async function getFileList() {
action: "getAllFiles" action: "getAllFiles"
}, "Zoznam súborov bol úspešne stiahnutý", "Nastala chyba pri sťahovaní zoznamu súborov", true); }, "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; return resp.Files;
} else { } else {
return false; return false;
@ -642,10 +642,10 @@ async function reloadMemeVotes(memeID) {
memeVoteCounterElement.innerText = memeVotes; memeVoteCounterElement.innerText = memeVotes;
memeVoteCounterElement.classList.remove("positive", "negative", "neutral"); memeVoteCounterElement.classList.remove("positive", "negative", "neutral");
if (memeVotes > 0) { if (0 < memeVotes) {
memeVoteCounterElement.classList.add("positive"); memeVoteCounterElement.classList.add("positive");
} }
else if (memeVotes < 0) { else if (0 > memeVotes) {
memeVoteCounterElement.classList.add("negative"); memeVoteCounterElement.classList.add("negative");
} }
else { else {
@ -654,20 +654,15 @@ async function reloadMemeVotes(memeID) {
memeVoteUpvoteButtonElement.classList.remove('visual_hover'); memeVoteUpvoteButtonElement.classList.remove('visual_hover');
memeVoteDownvoteButtonElement.classList.remove('visual_hover'); memeVoteDownvoteButtonElement.classList.remove('visual_hover');
if (userVote > 0) { let memeUpvoteVariant = "line";
let memeDownvoteVariant = "line";
if (0 < userVote) {
memeUpvoteVariant = "fill"; memeUpvoteVariant = "fill";
memeDownvoteVariant = "line";
memeVoteUpvoteButtonElement.classList.add('visual_hover'); memeVoteUpvoteButtonElement.classList.add('visual_hover');
} } else if (0 > userVote) {
else if (userVote < 0) {
memeUpvoteVariant = "line";
memeDownvoteVariant = "fill"; memeDownvoteVariant = "fill";
memeVoteDownvoteButtonElement.classList.add('visual_hover'); memeVoteDownvoteButtonElement.classList.add('visual_hover');
} }
else {
memeUpvoteVariant = "line";
memeDownvoteVariant = "line";
}
memeVoteUpvoteElement.classList = [`ri-arrow-up-circle-${memeUpvoteVariant}`]; memeVoteUpvoteElement.classList = [`ri-arrow-up-circle-${memeUpvoteVariant}`];
memeVoteDownvoteElement.classList = [`ri-arrow-down-circle-${memeDownvoteVariant}`]; memeVoteDownvoteElement.classList = [`ri-arrow-down-circle-${memeDownvoteVariant}`];
@ -680,13 +675,13 @@ async function voteMeme(memeID, isUpvote){
let memeVoteDelete = false; let memeVoteDelete = false;
if(isUpvote) { if(isUpvote) {
if(memeVoteUpvoteElement.classList.contains("ri-arrow-up-circle-fill")){ if(memeVoteUpvoteElement.classList.contains("ri-arrow-up-circle-fill")){
deleteVoteMeme(memeID); await deleteVoteMeme(memeID);
memeVoteDelete = true; memeVoteDelete = true;
} }
} }
else { else {
if(memeVoteDownvoteElement.classList.contains("ri-arrow-down-circle-fill")){ if(memeVoteDownvoteElement.classList.contains("ri-arrow-down-circle-fill")){
deleteVoteMeme(memeID); await deleteVoteMeme(memeID);
memeVoteDelete = true; memeVoteDelete = true;
} }
} }

@ -371,11 +371,11 @@ div#articleslist>article{
} }
.positive { .positive {
color: green; color: #008000;
} }
.negative { .negative {
color: red; color: #ff0000;
} }
.neutral { .neutral {
@ -390,12 +390,12 @@ div#articleslist>article{
} }
.visual_hover.meme_upvote { .visual_hover.meme_upvote {
--pico-background-color: green; --pico-background-color: #008000;
--pico-border-color: unset; --pico-border-color: unset;
} }
.visual_hover.meme_downvote { .visual_hover.meme_downvote {
--pico-background-color: red; --pico-background-color: #ff0000;
--pico-border-color: unset; --pico-border-color: unset;
} }

@ -11,6 +11,12 @@ function endpoint($endpoint_data): array
$endpoint_data["title"], $endpoint_data["title"],
$endpoint_data["body"] $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"], default => ["Status" => "Fail", "message" => "Invalid action"],
}; };
} }

@ -1,32 +1,40 @@
<?php <?php
/** @noinspection PhpIncludeInspection */ /** @noinspection PhpIncludeInspection */
require_once 'secrets/config.php'; // Include essential configuration and function libraries.
require_once 'lib/config.php'; require_once 'secrets/config.php'; // Load sensitive configuration such as database credentials.
require_once 'lib/navigation.php'; require_once 'lib/config.php'; // Load general site configuration settings.
require_once 'lib/router.php'; require_once 'lib/navigation.php'; // Include functions related to navigation generation.
require_once 'lib/page.php'; require_once 'lib/router.php'; // Include routing functionality to manage URL routing.
require_once 'lib/endpoint.php'; require_once 'lib/page.php'; // Functions related to page content generation and management.
require_once 'lib/account.php'; require_once 'lib/endpoint.php'; // Functions for handling API endpoints.
require_once 'lib/account.php'; // Include user account management functionality.
// Load configuration for the router from the configuration files.
$routerConfig = loadRouterConfig(); $routerConfig = loadRouterConfig();
// Initialize the router to parse the request URI and determine the requested site/page.
$routerRequest = initRouter(); $routerRequest = initRouter();
// Start or resume a session to manage user sessions across requests.
session_start(); session_start();
// Set default session data if the user is not logged in.
if (!isLoggedIn()) { if (!isLoggedIn()) {
setDefaultSessionData(); setDefaultSessionData();
} }
if($routerRequest["site_name"] == "sitemap.xml") { // Handle requests for the sitemap.
require "lib/sitemap.php"; if ($routerRequest["site_name"] == "sitemap.xml") {
generateSitemap(); require "lib/sitemap.php"; // Include sitemap generation functions.
exit(); echo generateSitemap(); // Generate and output the sitemap XML.
exit(); // Stop script execution after sitemap generation.
} }
// Handle API type requests by fetching and outputting the endpoint response.
if ($routerRequest["type"] == "api") { if ($routerRequest["type"] == "api") {
echo getEndpoint($routerRequest["site_name"]); echo getEndpoint($routerRequest["site_name"]);
}
} elseif ($routerRequest["type"] == "page") { // Handle page type requests by fetching and rendering the page content.
echo getPage($routerRequest["site_name"], $routerRequest["page_name"]); elseif ($routerRequest["type"] == "page") {
} echo getPage($routerRequest["site_name"], $routerRequest["page_name"]);
}

@ -1,52 +1,95 @@
<?php <?php
use Random\RandomException; use Random\RandomException;
/**
* Checks if the current session represents a logged-in user.
*
* @global array $routerConfig Global configuration array containing permission thresholds.
* @return bool Returns true if the user is logged in and meets the minimum privilege level; otherwise, false.
*/
function isLoggedIn(): bool function isLoggedIn(): bool
{ {
global $routerConfig; global $routerConfig;
return $_SESSION["ID"] > 0 && !empty($_SESSION["email"]) && $_SESSION["privilege_level"] >= $routerConfig["permissions"]["logged_in_default"]; return $_SESSION["ID"] > 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 function isVerified(): bool
{ {
global $routerConfig; global $routerConfig;
return isLoggedIn() && $_SESSION["privilege_level"] >= $routerConfig["permissions"]["verified"]; 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 function isTrustWorthy(): bool
{ {
global $routerConfig; global $routerConfig;
return isLoggedIn() && $_SESSION["privilege_level"] >= $routerConfig["permissions"]["trustworthy"]; 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 function isModerator(): bool
{ {
global $routerConfig; global $routerConfig;
return isLoggedIn() && $_SESSION["privilege_level"] >= $routerConfig["permissions"]["moderator"]; 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 function isUserAdmin(): bool
{ {
global $routerConfig; global $routerConfig;
return isLoggedIn() && $_SESSION["privilege_level"] >= $routerConfig["permissions"]["user_admin"]; 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 function isAdmin(): bool
{ {
global $routerConfig; global $routerConfig;
return isLoggedIn() && $_SESSION["privilege_level"] >= $routerConfig["permissions"]["admin"]; 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 { try {
return bin2hex(random_bytes(16)); return bin2hex(random_bytes(16));
} catch (RandomException) { } 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; global $mysqli;
$stmt = $mysqli->prepare("SELECT COUNT(*) FROM Users WHERE Email = ?"); $stmt = $mysqli->prepare("SELECT COUNT(*) FROM Users WHERE Email = ?");
@ -59,7 +102,12 @@ function isEmailAvailable($email): bool
return $count === 0; 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 function setDefaultSessionData(): void
{ {
global $routerConfig; global $routerConfig;
@ -71,8 +119,15 @@ function setDefaultSessionData(): void
$_SESSION["minecraft_nickname"] = ""; $_SESSION["minecraft_nickname"] = "";
$_SESSION["privilege_level"] = $routerConfig["permissions"]["logged_out"]; $_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; global $mysqli;
$stmt = $mysqli->prepare("SELECT PasswordHash FROM Users WHERE ID = ?"); $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); 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 function UpdateSession(): void
{ {
global $mysqli; global $mysqli;
@ -118,8 +178,15 @@ function UpdateSession(): void
$_SESSION["favorite_color"] = $favorite_color; $_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; global $mysqli;
$found = false; $found = false;
@ -149,7 +216,12 @@ function doLogin($email, $password): array
} }
return $found ? ["Status" => "Success"] : ["Status" => "Fail"]; 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 function doLogout(): array
{ {
if(isLoggedIn()){ if(isLoggedIn()){
@ -159,8 +231,19 @@ function doLogout(): array
return ["Status" => "Fail"]; 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; global $mysqli, $routerConfig;
$status = ["Status" => "Fail"]; $status = ["Status" => "Fail"];
@ -186,8 +269,15 @@ function doRegister($firstname, $lastname, $email, $password, $activation_token)
return $status; 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; global $mysqli;
$status = ["Status" => "Fail"]; $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; global $mysqli;
$status = ["Status" => "Fail"]; $status = ["Status" => "Fail"];
@ -230,8 +329,14 @@ function updateUserProfile($firstName, $lastName, $nickname, $minecraft_nickname
return $status; 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; global $mysqli;
$status = ["Status" => "Fail"]; $status = ["Status" => "Fail"];
@ -274,7 +379,11 @@ function updateUserEmail($email): array
return $status; 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 function getUserInfo(): array
{ {
$output = ["Status" => "Fail"]; $output = ["Status" => "Fail"];
@ -310,8 +419,14 @@ function getUserInfo(): array
return $output; 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; global $mysqli;
$activationCodes = []; $activationCodes = [];
@ -342,7 +457,12 @@ function addActivationCodes($count): array
return $output; 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 function listUsers(): array
{ {
global $mysqli; global $mysqli;
@ -364,7 +484,12 @@ function listUsers(): array
return $output; 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 function listActivationCodes(): array
{ {
global $mysqli; global $mysqli;
@ -409,8 +534,14 @@ function listActivationCodes(): array
return $output; 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; global $mysqli;
$status = ["Status" => "Fail"]; $status = ["Status" => "Fail"];
@ -425,8 +556,14 @@ function deleteUser($userID): array
} }
return $status; 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; global $mysqli;
$status = ["Status" => "Fail"]; $status = ["Status" => "Fail"];

@ -1,4 +1,28 @@
<?php <?php
/**
* Loads and returns the configuration settings for the router.
*
* This configuration includes various paths, default settings, security levels, SEO settings,
* and other parameters essential for the operation of the router and the website's page management.
* The configuration array is structured to provide easy access to paths, protocols, permissions,
* and other critical settings that define how the router handles requests and serves content.
*
* @return array Returns an associative array containing all router configuration settings, such as:
* - 'inlining': Boolean value determining if CSS/JS should be inlined.
* - 'domain': The primary domain name of the website.
* - 'tld': Top-level domain for the website.
* - 'default_page': Default page to load if no specific page is requested.
* - 'default_site': Default site to load if no specific site is requested.
* - 'template_dir': Directory path where templates are stored.
* - 'endpoint_dir': Directory path for endpoint scripts.
* - 'page_dir': Directory path where site pages are stored.
* - 'protocol': Protocol to be used (e.g., 'https://').
* - 'site_prefix': Prefix for the site title.
* - 'permissions': Associative array of user permissions by role.
* - 'page': Default settings for pages including secret status and permissions.
* - 'newsarticle': Default permissions for news articles.
* - 'seo': Search engine optimization settings like author, description, and keywords.
*/
function loadRouterConfig(): array function loadRouterConfig(): array
{ {

@ -1,5 +1,11 @@
<?php <?php
/**
* Generates dynamic CSS styling based on user preferences stored in the session.
* Specifically, it creates a CSS rule for the user's favorite color if it's specified in their session data.
*
* @return string Returns a string containing a `<style>` 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 function doDynamicStyling() :string
{ {
$dynamic_style = ""; $dynamic_style = "";

@ -1,6 +1,16 @@
<?php <?php
/**
function runEndpoint($endpoint_file): ?array * Executes an endpoint script and returns the results.
*
* This function includes an endpoint PHP file that defines a function named `endpoint` which
* is expected to accept an array parameter and return an array result.
* It simply scopes an external file into a function to prevent variable conflicts.
*
* @param string $endpoint_file The path to the endpoint PHP file.
* @return array|null Returns the result of the endpoint function if successful, or null if the
* endpoint function or file does not behave as expected.
*/
function runEndpoint(string $endpoint_file): ?array
{ {
$endpoint_data = $_POST; $endpoint_data = $_POST;
@ -9,8 +19,20 @@ function runEndpoint($endpoint_file): ?array
return endpoint($endpoint_data); return endpoint($endpoint_data);
} }
/**
function getEndpoint($endpoint_name): string * Retrieves and processes the output of a specified endpoint.
*
* This function determines the appropriate endpoint PHP file based on the provided endpoint name,
* executes the endpoint, and returns its results as a JSON-encoded string. It handles and
* translates different return types into a JSON format and manages HTTP response codes based on
* success or failure of the endpoint execution.
*
* @param string $endpoint_name The name of the endpoint, which is used to construct the file path to the endpoint script.
* @return string A JSON-encoded string representing the result of the endpoint, including a status indicator and any relevant data or error messages.
*@global array $routerRequest Current request data that might influence the endpoint processing.
* @global array $routerConfig Global configuration that contains paths and settings.
*/
function getEndpoint(string $endpoint_name): string
{ {
$output = array(); $output = array();
$output["Status"] = "Fail"; $output["Status"] = "Fail";

@ -1,6 +1,13 @@
<?php <?php
/**
function inlineLocalStylesFromHref($inputString): string * Processes an HTML string to inline all linked stylesheets by replacing <link> tags
* with corresponding <style> tags containing the CSS content.
* Might be broken, currently disabled in the config
*
* @param string $inputString The HTML content containing <link> tags to stylesheets.
* @return string The modified HTML content with stylesheets inlined within <style> tags.
*/
function inlineLocalStylesFromHref(string $inputString): string
{ {
$pattern = '/<link[^>]*?\srel=["\']?stylesheet["\'].*?\shref=["\']?\/(.*?)["\'][^>]*?>/i'; $pattern = '/<link[^>]*?\srel=["\']?stylesheet["\'].*?\shref=["\']?\/(.*?)["\'][^>]*?>/i';
@ -30,8 +37,15 @@ function inlineLocalStylesFromHref($inputString): string
return "<style>$cssContent</style>"; return "<style>$cssContent</style>";
}, $inputString); }, $inputString);
} }
/**
function inlineScriptFromSrc($inputString): string * Processes an HTML string to inline all external JavaScript files by replacing <script src="..."> tags
* with <script> tags containing the JavaScript content.
* Might be broken, currently disabled in the config
*
* @param string $inputString The HTML content containing <script src=""> tags.
* @return string The modified HTML content with external scripts inlined within <script> tags.
*/
function inlineScriptFromSrc(string $inputString): string
{ {
$pattern = '/<script.*?src=["\']\/(.*?)["\'].*?>\s*<\/script>/i'; $pattern = '/<script.*?src=["\']\/(.*?)["\'].*?>\s*<\/script>/i';
@ -44,8 +58,14 @@ function inlineScriptFromSrc($inputString): string
return "<script>$jsContent</script>"; return "<script>$jsContent</script>";
}, $inputString); }, $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 // Remove comments
$css = preg_replace('!/\*[^*]*\*+([^/][^*]*\*+)*/!', '', $css); $css = preg_replace('!/\*[^*]*\*+([^/][^*]*\*+)*/!', '', $css);
@ -64,8 +84,14 @@ function minifyCss($css): string
return trim($css); 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 // Remove newlines and tabs

@ -2,7 +2,15 @@
require_once "lib/upload.php"; require_once "lib/upload.php";
require_once "lib/account.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 function addMeme(string $title, string $memeText, int $imageID): array
{ {
global $mysqli; global $mysqli;
@ -17,7 +25,21 @@ function addMeme(string $title, string $memeText, int $imageID): array
} }
return $output; 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 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); 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 function renderMemeGallery(): string
{ {
global $mysqli; global $mysqli;
@ -105,7 +134,13 @@ function renderMemeGallery(): string
$stmtlist->close(); $stmtlist->close();
return $meme_gallery_out; 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 function deleteMeme(int $memeID): array
{ {
global $mysqli; global $mysqli;
@ -126,7 +161,14 @@ function deleteMeme(int $memeID): array
} }
return $out; 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 function voteMeme(int $memeID, int $isUpvote): array
{ {
global $mysqli; global $mysqli;
@ -143,7 +185,13 @@ function voteMeme(int $memeID, int $isUpvote): array
$memeVoteConn->close(); $memeVoteConn->close();
return $out; 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 function deleteVoteMeme(int $memeID): array
{ {
global $mysqli; global $mysqli;
@ -157,7 +205,14 @@ function deleteVoteMeme(int $memeID): array
$memeVoteConn->close(); $memeVoteConn->close();
return $out; 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 function calculateNetVotes(int $memeID): array
{ {
global $mysqli; 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 function getMemeVotes(int $memeID): array
{ {
$voteData = calculateNetVotes($memeID); $voteData = calculateNetVotes($memeID);

@ -1,11 +1,24 @@
<?php <?php
/**
function getDynamicMetadata($file): array{ * Includes a PHP file that returns metadata associated with a dynamic page.
* It simply scopes an external file into a function to prevent variable conflicts.
*
* @param string $file The file path to the PHP file that contains metadata.
* @return array Returns an associative array of metadata from the included PHP file.
*/
function getDynamicMetadata(string $file): array{
return include($file); return include($file);
} }
/**
function getDynamicPermission($metadata): int { * Extracts and validates the minimal permission level required to access certain content,
* defaulting to system configuration if not properly set or in case of an error.
*
* @param array $metadata Metadata array that should include a 'parameters' key with 'minimal_permission_level'.
* @return int Returns the minimal permission level required to access a page.
*@global array $routerConfig Global router configuration settings.
*/
function getDynamicPermission(array $metadata): int {
global $routerConfig; global $routerConfig;
$params = $metadata["parameters"]; $params = $metadata["parameters"];
try { try {
@ -21,7 +34,14 @@ function getDynamicPermission($metadata): int {
return $permission_level; return $permission_level;
} }
} }
/**
* Generates HTML navigation links for all sites and pages configured in the router,
* adjusting active states based on current request and filtering links by user permissions.
*
* @global array $routerConfig Global configuration that includes directory paths and default settings.
* @global array $routerRequest Current request details including site and page name.
* @return string Returns the HTML string of the navigation menu.
*/
function generateNavigation(): string function generateNavigation(): string
{ {
global $routerConfig; global $routerConfig;
@ -107,7 +127,12 @@ function generateNavigation(): string
return str_replace("__NAV_PAGES__", $nav_out, $nav); return str_replace("__NAV_PAGES__", $nav_out, $nav);
} }
/**
* Provides a simple API endpoint-like response for fetching generated navigation HTML.
* Wraps generateNavigation for an API.
*
* @return array Returns an associative array with the navigation HTML and a status indicating success.
*/
function getNavigationEndpoint() :array{ function getNavigationEndpoint() :array{
return [ return [
"Status" => "Success", "Status" => "Success",

@ -1,4 +1,15 @@
<?php <?php
require_once "lib/account.php";
/**
* Retrieves news articles based on the current user's privilege level.
* The function queries the NewsArticles and Users tables to fetch articles
* that the user has the privilege to view. Articles are joined with user
* information to include the author's nickname.
*
* @global mysqli $mysqli The mysqli database connection object.
* @return array Returns an associative array with a status key indicating the success or failure,
* and an 'Articles' key containing an array of articles if successful.
*/
function getNewsArticles() :array function getNewsArticles() :array
{ {
global $mysqli; global $mysqli;
@ -39,7 +50,18 @@ function getNewsArticles() :array
return $output; return $output;
} }
function addNewsArticle($title="Nazov", $body="Obsah", $privilegeLevel=0) :array /**
* Adds a new news article to the database if the user is logged in and has the appropriate
* privilege level. The function sanitizes the title and body of the article to prevent XSS attacks.
*
* @global mysqli $mysqli The mysqli database connection object.
* @global array $routerConfig Configuration array that includes default permission settings.
* @param string $title The title of the news article. Default value is "Nazov".
* @param string $body The body of the news article. Default value is "Obsah".
* @param int $privilegeLevel The privilege level required to view the article. If set to 0, uses default from configuration.
* @return array Returns an associative array with a status key that indicates the success or failure of the operation.
*/
function addNewsArticle(string $title="Nazov", string $body="Obsah", int $privilegeLevel=0) :array
{ {
global $mysqli; global $mysqli;
global $routerConfig; global $routerConfig;
@ -61,4 +83,48 @@ function addNewsArticle($title="Nazov", $body="Obsah", $privilegeLevel=0) :array
$query->close(); $query->close();
} }
return $output; return $output;
} }
/**
* 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;
}

@ -1,16 +1,37 @@
<?php <?php
require_once "lib/dynamic_style.php"; require_once "lib/dynamic_style.php";
require_once "lib/script_data.php"; require_once "lib/script_data.php";
function renderDynamicPage($page_file): array /**
* Loads and returns the result of a PHP file.
* This function is typically used to process dynamic content of a page.
* It simply scopes an external file into a function to prevent variable conflicts.
*
* @param string $page_file The file path to the dynamic page.
* @return array Returns the array of data generated by including the PHP file.
*/
function renderDynamicPage(string $page_file): array
{ {
return require $page_file; return require $page_file;
} }
function removeHtmlComments($content = '') :string { /**
* Removes all HTML comments from the provided content string.
*
* @param string $content The HTML content from which to remove comments.
* @return string The content without any HTML comments.
*/
function removeHtmlComments(string $content = '') :string {
return preg_replace('/<!--(.|\s)*?-->/', '', $content); return preg_replace('/<!--(.|\s)*?-->/', '', $content);
} }
/**
function parsePageTag($input): array * Parses custom `<page>` tags from the given input string and extracts parameters.
* Returns the input string with `<page>` tags removed and a list of parameters.
*
* @param string $input The input HTML or text containing `<page>` tags.
* @return array Returns an associative array with 'parameters' (parsed from the tag)
* and 'output' (the modified input string with `<page>` tags removed).
*/
function parsePageTag(string $input): array
{ {
// Define the pattern for the tag // Define the pattern for the tag
$pattern = '/<page\s+([^>]+)><\/page>/i'; $pattern = '/<page\s+([^>]+)><\/page>/i';
@ -34,8 +55,15 @@ function parsePageTag($input): array
// If no match is found, return the original input // If no match is found, return the original input
return ['parameters' => [], 'output' => $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 $routerConfig;
global $routerRequest; 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); $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); 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_location = "/" . $site_name . "/" . $page_name;
$page_tmp = renderPage($page_name, $site_name); $page_tmp = renderPage($page_name, $site_name);

@ -1,6 +1,14 @@
<?php <?php
/**
* Initializes the routing system for a web application. This function processes the incoming
* URL and determines the site name and page name based on the configuration and the URL structure.
* It handles default configurations and supports different types of requests like API or page requests.
*
* @global array $routerConfig The global configuration array that includes default site and page settings.
* @return array Returns an associative array containing the routing information, including the site name,
* page name, request type, and parsed request address from the HTTP host.
*/
function initRouter(): array function initRouter(): array
{ {
global $routerConfig; global $routerConfig;

@ -1,5 +1,15 @@
<?php <?php
function generateScriptData($phpArray):string { /**
* Generates a JavaScript script tag containing commands to store PHP array key-value pairs in local storage.
* This function is designed to translate PHP associative array data into JavaScript local storage items.
* It ensures that the array is associative and single-level before proceeding with the JavaScript code generation.
* This is used when dumping session data into local storage for use by the client script.
*
* @param array $phpArray The associative array whose data will be converted into JavaScript local storage setItem calls.
* @return string Returns a script tag with JavaScript code. If the input is not a valid single-level associative array,
* it returns a script with an error logged to the console.
*/
function generateScriptData(array $phpArray):string {
// Check if the array is associative and single-level // Check if the array is associative and single-level
if (is_array($phpArray) && count($phpArray) > 0 && count(array_filter(array_keys($phpArray), 'is_string')) === count($phpArray)) { if (is_array($phpArray) && count($phpArray) > 0 && count(array_filter(array_keys($phpArray), 'is_string')) === count($phpArray)) {
// Generate JavaScript code to save each array element to local storage // Generate JavaScript code to save each array element to local storage

@ -1,8 +1,16 @@
<?php <?php
require_once "lib/account.php"; require_once "lib/account.php";
/**
* Generates an XML sitemap as a string for a website, considering only pages that the current session
function generateSitemap(): void{ * has sufficient privileges to access. It scans directories specified in the router configuration
* for .html and .php files, and constructs a sitemap entry for each accessible page based on their
* required permission levels. This function returns the sitemap as a string and
* sets the appropriate header for XML content.
*
* @global array $routerConfig The global configuration array containing directory paths and default settings.
* @return string The XML sitemap content, properly formatted in accordance with the sitemap protocol.
*/
function generateSitemap(): string{
global $routerConfig; global $routerConfig;
$site_dirs = array_diff(scandir($routerConfig["page_dir"]), array('.', '..')); $site_dirs = array_diff(scandir($routerConfig["page_dir"]), array('.', '..'));
@ -54,5 +62,5 @@ function generateSitemap(): void{
$sitemap .= '</urlset>' . PHP_EOL; $sitemap .= '</urlset>' . PHP_EOL;
header('Content-type: application/xml'); header('Content-type: application/xml');
echo $sitemap; return $sitemap;
} }

@ -1,6 +1,12 @@
<?php <?php
/**
function makePathSafe($userInput): string * Sanitizes user input to be used as a part of a file path to prevent security vulnerabilities such as directory traversal.
*
* @param string $userInput The input string to be sanitized.
* @return string Returns a safe string where only alphanumeric characters, underscores, and hyphens are retained.
* Special characters and potential path traversal payloads are removed or replaced.
*/
function makePathSafe(string $userInput): string
{ {
// Keep only alphanumeric characters, underscores, and hyphens // Keep only alphanumeric characters, underscores, and hyphens
$safeString = preg_replace('/[^\w\-]/', '', $userInput); $safeString = preg_replace('/[^\w\-]/', '', $userInput);
@ -17,7 +23,12 @@ function makePathSafe($userInput): string
// Limit length for safety // Limit length for safety
return substr($safeString, 0, 255); return substr($safeString, 0, 255);
} }
/**
* Automatically rotates an image based on its EXIF data to adjust its orientation.
*
* @param Imagick $imagick An Imagick object representing the image to be rotated.
* @return void
*/
function autoRotateImage(Imagick $imagick): void { function autoRotateImage(Imagick $imagick): void {
// Get the current orientation of the image // Get the current orientation of the image
try { try {
@ -42,7 +53,11 @@ function autoRotateImage(Imagick $imagick): void {
} }
} }
/**
* Processes the global $_FILES array to normalize the structure and filter out any files with errors.
*
* @return array Returns an array of files that are ready for further processing, structured uniformly.
*/
function getIncomingFiles(): array function getIncomingFiles(): array
{ {
$files = $_FILES; $files = $_FILES;
@ -67,7 +82,16 @@ function getIncomingFiles(): array
} }
return $files3; return $files3;
} }
/**
* Saves file metadata in the database.
* This creates the only record of the file existing.
*
* @param string $filePath The path where the file is stored.
* @param string $fileType The MIME type of the file.
* @param int $width The width of the image file.
* @param int $height The height of the image file.
* @return bool Returns true if the file metadata was successfully saved to the database, false otherwise.
*/
function saveUploadedFileInDatabase(string $filePath, string $fileType, int $width, int $height): bool function saveUploadedFileInDatabase(string $filePath, string $fileType, int $width, int $height): bool
{ {
global $mysqli; global $mysqli;
@ -78,8 +102,14 @@ function saveUploadedFileInDatabase(string $filePath, string $fileType, int $wid
$stmt->close(); $stmt->close();
return $stat; 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 // Create Imagick object
$width = 0; $width = 0;
@ -96,15 +126,22 @@ function doImageUpload($inFile, $outFile): bool
} catch (ImagickException) { } catch (ImagickException) {
} }
// Check if the reencoding was successful // Check if the reencoding was successful, if yes, save into the database.
if (file_exists($outFile)) { if (file_exists($outFile)) {
return saveUploadedFileInDatabase($outFile, 'image/webp', $width, $height); return saveUploadedFileInDatabase($outFile, 'image/webp', $width, $height);
} else { } else {
return false; 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"]; $output = ["Status" => "Success"];
require_once "lib/account.php"; require_once "lib/account.php";
@ -156,6 +193,12 @@ function listFiles($onlyMine = true): array
return $output; 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 function parseIncomingFiles(): array
{ {
@ -183,8 +226,15 @@ function parseIncomingFiles(): array
} }
return $output; 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); $type = makePathSafe($type);
$id = makePathSafe($_SESSION["ID"]); $id = makePathSafe($_SESSION["ID"]);
@ -202,6 +252,15 @@ function getUploadPath($type = "unknown", $filename = "hehe"): string
return ""; 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 function fileExists(int $fileId, bool $onlyMine = true): bool|string
{ {
@ -230,7 +289,14 @@ function fileExists(int $fileId, bool $onlyMine = true): bool|string
return false; 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 function addToGroup(int $groupId, int $fileId): array
{ {
$output = ["Status" => "Fail"]; $output = ["Status" => "Fail"];
@ -253,16 +319,22 @@ function addToGroup(int $groupId, int $fileId): array
} }
return $output; 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 function deleteFile(int $fileID): array
{ {
global $mysqli; global $mysqli;
$out = ["Status" => "Fail"]; $out = ["Status" => "Fail"];
if (isLoggedIn()) { if (isLoggedIn()) {
$file_location = fileExists($fileID, !isAdmin()); $file_location = fileExists($fileID, !isModerator());
$query = !isAdmin() ? 'DELETE FROM Files WHERE ID = ? AND UploadedBy = ?' : 'DELETE FROM Files WHERE ID = ?'; $query = !isModerator() ? 'DELETE FROM Files WHERE ID = ? AND UploadedBy = ?' : 'DELETE FROM Files WHERE ID = ?';
$stmtDelete = $mysqli->prepare($query); $stmtDelete = $mysqli->prepare($query);
if (!isAdmin()) { if (!isModerator()) {
$stmtDelete->bind_param('ii', $fileID, $_SESSION['ID']); $stmtDelete->bind_param('ii', $fileID, $_SESSION['ID']);
} else { } else {
$stmtDelete->bind_param('i', $fileID); $stmtDelete->bind_param('i', $fileID);