jilo-web/app/classes/user.php

426 lines
13 KiB
PHP
Raw Normal View History

<?php
2024-11-21 13:14:07 +00:00
/**
2024-11-29 16:38:49 +00:00
* class User
2024-11-25 14:09:47 +00:00
*
2024-11-21 13:14:07 +00:00
* Handles user-related functionalities such as registration, login, rights management, and profile updates.
*/
class User {
2024-11-21 13:14:07 +00:00
/**
2024-11-25 14:09:47 +00:00
* @var PDO|null $db The database connection instance.
2024-11-21 13:14:07 +00:00
*/
private $db;
2024-11-21 13:14:07 +00:00
/**
* User constructor.
2024-11-29 16:47:18 +00:00
* Initializes the database connection.
2024-11-26 14:17:41 +00:00
*
2024-11-29 16:47:18 +00:00
* @param object $database The database object to initialize the connection.
2024-11-21 13:14:07 +00:00
*/
public function __construct($database) {
$this->db = $database->getConnection();
}
2024-11-21 13:14:07 +00:00
/**
* Registers a new user.
*
* @param string $username The username of the new user.
* @param string $password The password for the new user.
2024-11-29 16:38:49 +00:00
*
2024-11-21 13:14:07 +00:00
* @return bool|string True if registration is successful, error message otherwise.
*/
public function register($username, $password) {
2024-09-13 10:49:17 +00:00
try {
// we have two inserts, start a transaction
$this->db->beginTransaction();
2024-09-13 10:49:17 +00:00
// hash the password, don't store it plain
$hashedPassword = password_hash($password, PASSWORD_DEFAULT);
// insert into users table
$sql = 'INSERT
INTO users (username, password)
VALUES (:username, :password)';
$query = $this->db->prepare($sql);
$query->bindValue(':username', $username);
$query->bindValue(':password', $hashedPassword);
// execute the first query
if (!$query->execute()) {
// rollback on error
$this->db->rollBack();
return false;
}
// insert the last user id into users_meta table
$sql2 = 'INSERT
INTO users_meta (user_id)
VALUES (:user_id)';
$query2 = $this->db->prepare($sql2);
$query2->bindValue(':user_id', $this->db->lastInsertId());
// execute the second query
if (!$query2->execute()) {
// rollback on error
$this->db->rollBack();
return false;
}
// if all is OK, commit the transaction
$this->db->commit();
return true;
} catch (Exception $e) {
// rollback on any error
$this->db->rollBack();
return $e->getMessage();
}
}
2024-11-21 13:14:07 +00:00
/**
* Logs in a user by verifying credentials.
*
* @param string $username The username of the user.
* @param string $password The password of the user.
2024-11-29 16:38:49 +00:00
*
2024-11-21 13:14:07 +00:00
* @return bool True if login is successful, false otherwise.
*/
public function login($username, $password) {
$query = $this->db->prepare("SELECT * FROM users WHERE username = :username");
$query->bindParam(':username', $username);
$query->execute();
$user = $query->fetch(PDO::FETCH_ASSOC);
if ( $user && password_verify($password, $user['password'])) {
$_SESSION['user_id'] = $user['id'];
$_SESSION['username'] = $user['username'];
return true;
} else {
return false;
}
}
2024-11-21 13:14:07 +00:00
/**
* Retrieves a user ID based on the username.
*
* @param string $username The username to look up.
2024-11-29 16:38:49 +00:00
*
2024-11-21 13:14:07 +00:00
* @return array|null User ID details or null if not found.
*/
2024-09-07 20:05:22 +00:00
// FIXME not used now?
public function getUserId($username) {
$sql = 'SELECT id FROM users WHERE username = :username';
$query = $this->db->prepare($sql);
$query->bindParam(':username', $username);
$query->execute();
return $query->fetchAll(PDO::FETCH_ASSOC);
}
2024-09-08 10:48:21 +00:00
2024-11-21 13:14:07 +00:00
/**
* Fetches user details by user ID.
*
* @param int $user_id The user ID.
2024-11-29 16:38:49 +00:00
*
2024-11-21 13:14:07 +00:00
* @return array|null User details or null if not found.
*/
2024-09-11 13:21:05 +00:00
public function getUserDetails($user_id) {
$sql = 'SELECT
um.*,
u.username
FROM
users_meta um
LEFT JOIN users u
ON um.user_id = u.id
2024-09-11 13:21:05 +00:00
WHERE
u.id = :user_id';
$query = $this->db->prepare($sql);
$query->execute([
':user_id' => $user_id,
]);
return $query->fetchAll(PDO::FETCH_ASSOC);
}
2024-11-21 13:14:07 +00:00
/**
* Grants a user a specific right.
*
* @param int $user_id The user ID.
* @param int $right_id The right ID to grant.
2024-11-29 16:38:49 +00:00
*
2024-11-21 13:14:07 +00:00
* @return void
*/
2024-09-11 19:51:46 +00:00
public function addUserRight($user_id, $right_id) {
$sql = 'INSERT INTO users_rights
(user_id, right_id)
2024-09-11 19:51:46 +00:00
VALUES
(:user_id, :right_id)';
2024-09-11 19:51:46 +00:00
$query = $this->db->prepare($sql);
$query->execute([
':user_id' => $user_id,
':right_id' => $right_id,
]);
}
2024-11-21 13:14:07 +00:00
/**
* Revokes a specific right from a user.
*
* @param int $user_id The user ID.
* @param int $right_id The right ID to revoke.
2024-11-29 16:38:49 +00:00
*
2024-11-21 13:14:07 +00:00
* @return void
*/
2024-09-11 19:51:46 +00:00
public function removeUserRight($user_id, $right_id) {
$sql = 'DELETE FROM users_rights
WHERE
user_id = :user_id
AND
right_id = :right_id';
$query = $this->db->prepare($sql);
$query->execute([
':user_id' => $user_id,
':right_id' => $right_id,
]);
}
2024-11-21 13:14:07 +00:00
/**
* Retrieves all rights in the system.
*
* @return array List of rights.
*/
2024-09-11 19:51:46 +00:00
public function getAllRights() {
$sql = 'SELECT
id AS right_id,
name AS right_name
FROM rights
ORDER BY id ASC';
2024-09-11 19:51:46 +00:00
$query = $this->db->prepare($sql);
$query->execute();
return $query->fetchAll(PDO::FETCH_ASSOC);
}
2024-11-21 13:14:07 +00:00
/**
* Retrieves the rights assigned to a specific user.
*
* @param int $user_id The user ID.
2024-11-29 16:38:49 +00:00
*
2024-11-21 13:14:07 +00:00
* @return array List of user rights.
*/
2024-09-11 13:21:05 +00:00
public function getUserRights($user_id) {
$sql = 'SELECT
u.id AS user_id,
2024-09-11 19:51:46 +00:00
r.id AS right_id,
r.name AS right_name
2024-09-11 13:21:05 +00:00
FROM
users u
LEFT JOIN users_rights ur
ON u.id = ur.user_id
LEFT JOIN rights r
ON ur.right_id = r.id
WHERE
u.id = :user_id';
2024-09-11 13:21:05 +00:00
2024-09-07 20:05:22 +00:00
$query = $this->db->prepare($sql);
$query->execute([
2024-09-11 13:21:05 +00:00
':user_id' => $user_id,
2024-09-07 20:05:22 +00:00
]);
$result = $query->fetchAll(PDO::FETCH_ASSOC);
// ensure specific entries are included in the result
$specialEntries = [];
// user 1 is always superuser
if ($user_id == 1) {
$specialEntries = [
[
'user_id' => 1,
'right_id' => 1,
'right_name' => 'superuser'
]
];
// user 2 is always demo
} elseif ($user_id == 2) {
$specialEntries = [
[
'user_id' => 2,
'right_id' => 100,
'right_name' => 'demo user'
]
];
}
// merge the special entries with the existing results
$result = array_merge($specialEntries, $result);
// remove duplicates if necessary
$result = array_unique($result, SORT_REGULAR);
// return the modified result
return $result;
2024-09-07 20:05:22 +00:00
}
2024-11-21 13:14:07 +00:00
/**
* Check if the user has a specific right.
*
* @param int $user_id The user ID.
* @param string $right_name The human-readable name of the user right.
2024-11-29 16:38:49 +00:00
*
2024-11-21 13:14:07 +00:00
* @return bool True if the user has the right, false otherwise.
*/
function hasRight($user_id, $right_name) {
$userRights = $this->getUserRights($user_id);
$userHasRight = false;
// superuser always has all the rights
if ($user_id === 1) {
$userHasRight = true;
}
foreach ($userRights as $right) {
if ($right['right_name'] === $right_name) {
$userHasRight = true;
break;
}
}
return $userHasRight;
}
2024-11-21 13:14:07 +00:00
/**
* Updates a user's metadata in the database.
*
* @param int $user_id The ID of the user to update.
* @param array $updatedUser An associative array containing updated user data:
* - 'name' (string): The updated name of the user.
* - 'email' (string): The updated email of the user.
* - 'timezone' (string): The updated timezone of the user.
* - 'bio' (string): The updated biography of the user.
*
* @return bool|string Returns true if the update is successful, or an error message if an exception occurs.
*/
2024-09-08 10:48:21 +00:00
public function editUser($user_id, $updatedUser) {
try {
$sql = 'UPDATE users_meta SET
name = :name,
email = :email,
2024-09-13 10:04:15 +00:00
timezone = :timezone,
2024-09-08 10:48:21 +00:00
bio = :bio
WHERE user_id = :user_id';
$query = $this->db->prepare($sql);
$query->execute([
':user_id' => $user_id,
':name' => $updatedUser['name'],
':email' => $updatedUser['email'],
2024-09-13 10:04:15 +00:00
':timezone' => $updatedUser['timezone'],
2024-09-08 10:48:21 +00:00
':bio' => $updatedUser['bio']
]);
return true;
} catch (Exception $e) {
return $e->getMessage();
}
}
2024-11-21 13:14:07 +00:00
/**
* Removes a user's avatar from the database and deletes the associated file.
*
* @param int $user_id The ID of the user whose avatar is being removed.
* @param string $old_avatar Optional. The file path of the current avatar to delete. Default is an empty string.
*
* @return bool|string Returns true if the avatar is successfully removed, or an error message if an exception occurs.
*/
2024-09-09 12:20:21 +00:00
public function removeAvatar($user_id, $old_avatar = '') {
try {
// remove from database
$sql = 'UPDATE users_meta SET
avatar = NULL
WHERE user_id = :user_id';
$query = $this->db->prepare($sql);
$query->execute([
':user_id' => $user_id,
]);
// delete the old avatar file
if ($old_avatar && file_exists($old_avatar)) {
unlink($old_avatar);
}
return true;
} catch (Exception $e) {
return $e->getMessage();
}
}
2024-11-21 13:14:07 +00:00
/**
* Updates a user's avatar by uploading a new file and saving its path in the database.
*
* @param int $user_id The ID of the user whose avatar is being updated.
* @param array $avatar_file The uploaded avatar file from the $_FILES array.
* Should include 'tmp_name', 'name', 'error', etc.
* @param string $avatars_path The directory path where avatar files should be saved.
*
* @return bool|string Returns true if the avatar is successfully updated, or an error message if an exception occurs.
*/
2024-09-09 12:20:21 +00:00
public function changeAvatar($user_id, $avatar_file, $avatars_path) {
try {
// check if the file was uploaded
if (isset($avatar_file) && $avatar_file['error'] === UPLOAD_ERR_OK) {
$fileTmpPath = $avatar_file['tmp_name'];
$fileName = $avatar_file['name'];
$fileExtension = strtolower(pathinfo($fileName, PATHINFO_EXTENSION));
// validate file extension
if (in_array($fileExtension, ['jpg', 'png', 'jpeg'])) {
$newFileName = md5(time() . $fileName) . '.' . $fileExtension;
$dest_path = $avatars_path . $newFileName;
// move the file to avatars folder
if (move_uploaded_file($fileTmpPath, $dest_path)) {
try {
// update user's avatar path in DB
$sql = 'UPDATE users_meta SET
avatar = :avatar
WHERE user_id = :user_id';
$query = $this->db->prepare($sql);
$query->execute([
':avatar' => $newFileName,
':user_id' => $user_id
]);
// all went OK
2024-09-10 11:05:38 +00:00
$_SESSION['notice'] .= 'Avatar updated successfully. ';
2024-09-09 12:20:21 +00:00
return true;
} catch (Exception $e) {
return $e->getMessage();
}
} else {
2024-09-10 11:05:38 +00:00
$_SESSION['error'] .= 'Error moving the uploaded file. ';
2024-09-09 12:20:21 +00:00
}
} else {
2024-09-10 11:05:38 +00:00
$_SESSION['error'] .= 'Invalid avatar file type. ';
2024-09-09 12:20:21 +00:00
}
} else {
2024-09-10 11:05:38 +00:00
$_SESSION['error'] .= 'Error uploading the avatar file. ';
2024-09-09 12:20:21 +00:00
}
} catch (Exception $e) {
return $e->getMessage();
}
}
}
?>