Compare commits
No commits in common. "31bc4d60e40bdf6162c14b5a345b574a476b76b9" and "8280f66b6d89c4a9e81059ae128795392b962cb1" have entirely different histories.
31bc4d60e4
...
8280f66b6d
|
|
@ -4,4 +4,3 @@ jilo.db
|
||||||
jilo-web.db
|
jilo-web.db
|
||||||
packaging/deb-package/
|
packaging/deb-package/
|
||||||
packaging/rpm-package/
|
packaging/rpm-package/
|
||||||
/public_html/uploads/avatars/
|
|
||||||
|
|
|
||||||
|
|
@ -473,20 +473,6 @@ class User {
|
||||||
$newFileName = md5(time() . $fileName) . '.' . $fileExtension;
|
$newFileName = md5(time() . $fileName) . '.' . $fileExtension;
|
||||||
$dest_path = $avatars_path . $newFileName;
|
$dest_path = $avatars_path . $newFileName;
|
||||||
|
|
||||||
// ensure avatars directory exists
|
|
||||||
if (!is_dir($avatars_path)) {
|
|
||||||
if (!mkdir($avatars_path, 0755, true)) {
|
|
||||||
$_SESSION['error'] .= 'Unable to create avatars directory. ';
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if directory is writable
|
|
||||||
if (!is_writable($avatars_path)) {
|
|
||||||
$_SESSION['error'] .= 'Avatars directory is not writable. ';
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// move the file to avatars folder
|
// move the file to avatars folder
|
||||||
if (move_uploaded_file($fileTmpPath, $dest_path)) {
|
if (move_uploaded_file($fileTmpPath, $dest_path)) {
|
||||||
try {
|
try {
|
||||||
|
|
@ -500,50 +486,24 @@ class User {
|
||||||
':user_id' => $userId
|
':user_id' => $userId
|
||||||
]);
|
]);
|
||||||
// all went OK
|
// all went OK
|
||||||
$_SESSION['notice'] = 'Avatar updated successfully. ';
|
$_SESSION['notice'] .= 'Avatar updated successfully. ';
|
||||||
return true;
|
return true;
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
$_SESSION['error'] .= 'Database error updating avatar. ';
|
|
||||||
return $e->getMessage();
|
return $e->getMessage();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$_SESSION['error'] = 'Error moving the uploaded file. Please check directory permissions. ';
|
$_SESSION['error'] .= 'Error moving the uploaded file. ';
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$_SESSION['error'] = 'Invalid avatar file type. Only JPG, PNG, and JPEG are allowed. ';
|
$_SESSION['error'] .= 'Invalid avatar file type. ';
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Handle different upload errors
|
$_SESSION['error'] .= 'Error uploading the avatar file. ';
|
||||||
switch ($avatar_file['error']) {
|
|
||||||
case UPLOAD_ERR_INI_SIZE:
|
|
||||||
case UPLOAD_ERR_FORM_SIZE:
|
|
||||||
$_SESSION['error'] = 'Avatar file is too large. Maximum size is 500KB. ';
|
|
||||||
break;
|
|
||||||
case UPLOAD_ERR_PARTIAL:
|
|
||||||
$_SESSION['error'] = 'Avatar file was only partially uploaded. ';
|
|
||||||
break;
|
|
||||||
case UPLOAD_ERR_NO_FILE:
|
|
||||||
$_SESSION['error'] = 'No avatar file was uploaded. ';
|
|
||||||
break;
|
|
||||||
case UPLOAD_ERR_NO_TMP_DIR:
|
|
||||||
$_SESSION['error'] = 'Missing temporary folder for file upload. ';
|
|
||||||
break;
|
|
||||||
case UPLOAD_ERR_CANT_WRITE:
|
|
||||||
$_SESSION['error'] = 'Failed to write avatar file to disk. ';
|
|
||||||
break;
|
|
||||||
case UPLOAD_ERR_EXTENSION:
|
|
||||||
$_SESSION['error'] = 'File upload stopped by extension. ';
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
$_SESSION['error'] = 'Unknown upload error occurred. ';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
$_SESSION['error'] = 'An error occurred while processing the avatar: ' . $e->getMessage();
|
|
||||||
return $e->getMessage();
|
return $e->getMessage();
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,15 @@
|
||||||
<!-- user profile -->
|
|
||||||
<div class="action-card">
|
<!-- user profile -->
|
||||||
<div class="action-card-header">
|
<div class="tm-profile-card mx-auto">
|
||||||
<p class="action-eyebrow">Account</p>
|
<div class="tm-profile-header">
|
||||||
<h2 class="action-title">Profile of <?= htmlspecialchars($userDetails[0]['username']) ?></h2>
|
<div>
|
||||||
<p class="action-subtitle">Update your personal details, avatar, and access rights in one streamlined view.</p>
|
<p class="tm-profile-eyebrow">Account</p>
|
||||||
|
<h2 class="tm-profile-title">Profile of <?= htmlspecialchars($userDetails[0]['username']) ?></h2>
|
||||||
|
<p class="tm-profile-subtitle">Update your personal details, avatar, and access rights in one streamlined view.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="action-card-body">
|
</div>
|
||||||
<form method="POST" action="<?= htmlspecialchars($app_root) ?>?page=profile" enctype="multipart/form-data" class="action-form" novalidate>
|
|
||||||
|
<form method="POST" action="<?= htmlspecialchars($app_root) ?>?page=profile" enctype="multipart/form-data" class="tm-profile-form" novalidate>
|
||||||
<?php include CSRF_TOKEN_INCLUDE; ?>
|
<?php include CSRF_TOKEN_INCLUDE; ?>
|
||||||
<div class="row g-4 align-items-start">
|
<div class="row g-4 align-items-start">
|
||||||
<div class="col-lg-4">
|
<div class="col-lg-4">
|
||||||
|
|
@ -36,25 +39,20 @@
|
||||||
<h3 class="tm-profile-section-title">Personal info</h3>
|
<h3 class="tm-profile-section-title">Personal info</h3>
|
||||||
<div class="row g-3">
|
<div class="row g-3">
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<div class="action-form-group">
|
<label for="name" class="form-label">Full name</label>
|
||||||
<label for="name" class="action-form-label">Full name</label>
|
<input class="form-control" type="text" name="name" id="name" value="<?= htmlspecialchars($userDetails[0]['name'] ?? '') ?>" autofocus />
|
||||||
<input class="form-control action-form-control" type="text" name="name" id="name" value="<?= htmlspecialchars($userDetails[0]['name'] ?? '') ?>" autofocus />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<div class="action-form-group">
|
<label for="email" class="form-label">Email address</label>
|
||||||
<label for="email" class="action-form-label">Email address</label>
|
<input class="form-control" type="text" name="email" id="email" value="<?= htmlspecialchars($userDetails[0]['email'] ?? '') ?>" />
|
||||||
<input class="form-control action-form-control" type="text" name="email" id="email" value="<?= htmlspecialchars($userDetails[0]['email'] ?? '') ?>" />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="tm-profile-section">
|
<div class="tm-profile-section">
|
||||||
<h3 class="tm-profile-section-title">Timezone</h3>
|
<h3 class="tm-profile-section-title">Timezone</h3>
|
||||||
<div class="action-form-group">
|
<label for="timezone" class="form-label">Preferred timezone</label>
|
||||||
<label for="timezone" class="action-form-label">Preferred timezone</label>
|
<select class="form-control" name="timezone" id="timezone">
|
||||||
<select class="form-control action-form-control" name="timezone" id="timezone">
|
|
||||||
<?php foreach ($allTimezones as $timezone) { ?>
|
<?php foreach ($allTimezones as $timezone) { ?>
|
||||||
<option value="<?= htmlspecialchars($timezone) ?>" <?= $timezone === $userTimezone ? 'selected' : '' ?>>
|
<option value="<?= htmlspecialchars($timezone) ?>" <?= $timezone === $userTimezone ? 'selected' : '' ?>>
|
||||||
<?= htmlspecialchars($timezone) ?> (<?= htmlspecialchars(getUTCOffset($timezone)) ?>)
|
<?= htmlspecialchars($timezone) ?> (<?= htmlspecialchars(getUTCOffset($timezone)) ?>)
|
||||||
|
|
@ -62,13 +60,10 @@
|
||||||
<?php } ?>
|
<?php } ?>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="tm-profile-section">
|
<div class="tm-profile-section">
|
||||||
<h3 class="tm-profile-section-title">Bio</h3>
|
<h3 class="tm-profile-section-title">Bio</h3>
|
||||||
<div class="action-form-group">
|
<textarea class="form-control" name="bio" rows="6" placeholder="Share something about yourself, your role, or preferences."><?= htmlspecialchars($userDetails[0]['bio'] ?? '') ?></textarea>
|
||||||
<textarea class="form-control action-form-control" name="bio" rows="6" placeholder="Share something about yourself, your role, or preferences."><?= htmlspecialchars($userDetails[0]['bio'] ?? '') ?></textarea>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="tm-profile-section">
|
<div class="tm-profile-section">
|
||||||
|
|
@ -91,9 +86,9 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="action-actions">
|
<div class="tm-profile-actions">
|
||||||
<a href="<?= htmlspecialchars($app_root) ?>?page=profile" class="btn btn-light">Cancel</a>
|
<a href="<?= htmlspecialchars($app_root) ?>?page=profile" class="btn btn-light tm-contact-back">Cancel</a>
|
||||||
<button type="submit" class="btn btn-primary">Save changes</button>
|
<button type="submit" class="btn btn-primary tm-contact-submit">Save changes</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -121,23 +116,22 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<!-- /user profile -->
|
||||||
<!-- /user profile -->
|
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
// Preview the uploaded avatar
|
||||||
// Preview the uploaded avatar
|
document.getElementById('avatar-upload').addEventListener('change', function(event) {
|
||||||
document.getElementById('avatar-upload').addEventListener('change', function(event) {
|
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
reader.onload = function() {
|
reader.onload = function() {
|
||||||
document.querySelector('.avatar-img').src = reader.result;
|
document.querySelector('.avatar-img').src = reader.result;
|
||||||
};
|
};
|
||||||
reader.readAsDataURL(event.target.files[0]);
|
reader.readAsDataURL(event.target.files[0]);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Avatar file size and type control
|
// Avatar file size and type control
|
||||||
document.getElementById('avatar-upload').addEventListener('change', function() {
|
document.getElementById('avatar-upload').addEventListener('change', function() {
|
||||||
const maxFileSize = 500 * 1024; // 500 KB in bytes
|
const maxFileSize = 500 * 1024; // 500 KB in bytes
|
||||||
const currentAvatar = '<?= htmlspecialchars($app_root) . htmlspecialchars($avatar) ?>'; // current avatar
|
const currentAvatar = '<?= htmlspecialchars($app_root) . htmlspecialchars($avatar) ?>'; // current avatar
|
||||||
const file = this.files[0];
|
const file = this.files[0];
|
||||||
|
|
@ -150,13 +144,12 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||||
document.querySelector('.avatar-img').src = currentAvatar;
|
document.querySelector('.avatar-img').src = currentAvatar;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Submitting the avatar deletion confirmation modal form
|
// Submitting the avatar deletion confirmation modal form
|
||||||
document.getElementById('confirm-delete').addEventListener('click', function(event) {
|
document.getElementById('confirm-delete').addEventListener('click', function(event) {
|
||||||
event.preventDefault(); // Prevent the outer form from submitting
|
event.preventDefault(); // Prevent the outer form from submitting
|
||||||
document.getElementById('remove-avatar-form').submit();
|
document.getElementById('remove-avatar-form').submit();
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Function to detect user's timezone and select it in the dropdown
|
// Function to detect user's timezone and select it in the dropdown
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue