userprofile page added

This commit is contained in:
2026-10-09 08:33:53 +02:00
parent d259c62304
commit 450785a4cb
2 changed files with 606 additions and 1 deletions
+1 -1
View File
@@ -65,7 +65,7 @@
<li class="dropdown">
<a class="nav-link dropdown-toggle waves-effect waves-light nav-user" data-toggle="dropdown" href="#" role="button" aria-haspopup="false" aria-expanded="false"> <img src="upload/users/<?php echo $avatarname; ?>" alt="profile-user" class="rounded-circle" /> <span class="ml-1 nav-user-name hidden-sm"><?php echo $nameuser; ?> <i class="mdi mdi-chevron-down"></i></span> </a>
<div class="dropdown-menu dropdown-menu-right">
<a class="dropdown-item" href="profile"><i class="ti-user text-muted mr-2"></i> <?php echo $profile; ?></a>
<a class="dropdown-item" href="userprofile.php"><i class="ti-user text-muted mr-2"></i> <?php echo $profile; ?></a>
<a class="dropdown-item" href="companyprofile.php"><i class="ti-wallet text-muted mr-2"></i> <?php echo $mycompany; ?></a>
<a class="dropdown-item" href="signaturetok.php"><i class="ti-wallet text-muted mr-2"></i> <?php echo $signaturetokentitle; ?></a>
<a class="dropdown-item" href="newusercolleague2.php"><i class="ti-wallet text-muted mr-2"></i> <?php echo $adduserundersamecompany; ?></a>
+605
View File
@@ -0,0 +1,605 @@
<?php
include('include/headscript.php');
if (isset($_SESSION["tempcode"])) {
unset($_SESSION["tempcode"]);
}
/* ------------------------------------------------------------------
* Etichette (spostabili nei file lingua come le altre variabili)
* ------------------------------------------------------------------ */
$t = [
'page_title' => 'My profile',
'section_general' => 'Personal data',
'section_password' => 'Change password',
'section_info' => 'Account information',
'first_name' => 'First name',
'last_name' => 'Last name',
'username' => 'Username',
'email' => 'Email',
'phone' => 'Phone',
'birthday' => 'Birthday',
'signaturecode' => 'Signature code',
'privacyaccepted' => 'Privacy accepted on',
'termuseaccepted' => 'Terms of use accepted on',
'current_password' => 'Current password',
'new_password' => 'New password',
'confirm_password' => 'Confirm new password',
'save' => 'Save changes',
'update_password' => 'Update password',
'not_available' => 'Not available',
'pwd_hint' => 'At least 8 characters, with letters and numbers.',
'ok_profile' => 'Profile updated successfully.',
'ok_password' => 'Password updated successfully.',
'err_csrf' => 'Session expired. Please reload the page and try again.',
'err_required' => 'Email is required.',
'err_email' => 'Invalid email address.',
'err_email_taken' => 'This email is already used by another account.',
'err_birthday' => 'Invalid birthday date.',
'err_current_pwd' => 'Current password is not correct.',
'err_pwd_length' => 'The new password must be at least 8 characters and contain letters and numbers.',
'err_pwd_match' => 'The new password and confirmation do not match.',
'err_generic' => 'An error occurred while saving. Please try again.',
];
function h($v)
{
return htmlspecialchars((string)$v, ENT_QUOTES, 'UTF-8');
}
function fmtDate($d, $fallback)
{
if (empty($d) || $d === '0000-00-00') {
return $fallback;
}
$dt = DateTime::createFromFormat('Y-m-d', substr($d, 0, 10));
return $dt ? $dt->format('d/m/Y') : $fallback;
}
/* ------------------------------------------------------------------
* Connessione e utente loggato
* $cmctrfdb è la connessione mysqli usata nelle altre pagine.
* ------------------------------------------------------------------ */
$db = $cmctrfdb;
$userId = (int)$iduserlogin;
if ($userId <= 0) {
header('Location: index.php');
exit;
}
/* CSRF token */
if (empty($_SESSION['profile_csrf'])) {
$_SESSION['profile_csrf'] = bin2hex(random_bytes(32));
}
$csrf = $_SESSION['profile_csrf'];
$errorsGeneral = [];
$errorsPassword = [];
$successMsg = '';
$activeTab = 'general';
/* Messaggio flash dopo redirect (pattern Post/Redirect/Get) */
if (!empty($_SESSION['profile_flash'])) {
$successMsg = $_SESSION['profile_flash']['msg'];
$activeTab = $_SESSION['profile_flash']['tab'];
unset($_SESSION['profile_flash']);
}
/* ------------------------------------------------------------------
* Gestione POST
* ------------------------------------------------------------------ */
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$action = $_POST['action'] ?? '';
$activeTab = ($action === 'password') ? 'password' : 'general';
$validCsrf = isset($_POST['csrf']) && hash_equals($csrf, $_POST['csrf']);
/* ---------- Dati generali ---------- */
if ($action === 'general') {
$firstName = trim($_POST['first_name'] ?? '');
$lastName = trim($_POST['last_name'] ?? '');
$username = trim($_POST['username'] ?? '');
$email = trim($_POST['email'] ?? '');
$phone = trim($_POST['phone'] ?? '');
$birthday = trim($_POST['birthday'] ?? '');
if (!$validCsrf) {
$errorsGeneral[] = $t['err_csrf'];
} else {
if ($email === '') {
$errorsGeneral[] = $t['err_required'];
} elseif (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
$errorsGeneral[] = $t['err_email'];
} else {
$stmt = $db->prepare('SELECT id FROM auth_users WHERE email = ? AND id <> ? LIMIT 1');
$stmt->bind_param('si', $email, $userId);
$stmt->execute();
$stmt->store_result();
if ($stmt->num_rows > 0) {
$errorsGeneral[] = $t['err_email_taken'];
}
$stmt->close();
}
if ($birthday !== '') {
$bd = DateTime::createFromFormat('Y-m-d', $birthday);
if (!$bd || $bd->format('Y-m-d') !== $birthday || $bd > new DateTime()) {
$errorsGeneral[] = $t['err_birthday'];
}
}
}
if (empty($errorsGeneral)) {
$firstNameDb = ($firstName !== '') ? $firstName : null;
$lastNameDb = ($lastName !== '') ? $lastName : null;
$usernameDb = ($username !== '') ? $username : null;
$phoneDb = ($phone !== '') ? $phone : null;
$birthdayDb = ($birthday !== '') ? $birthday : null;
$stmt = $db->prepare(
'UPDATE auth_users
SET first_name = ?, last_name = ?, username = ?, email = ?,
phone = ?, birthday = ?, updated_at = NOW()
WHERE id = ?'
);
$stmt->bind_param('ssssssi', $firstNameDb, $lastNameDb, $usernameDb, $emailDb, $phoneDb, $birthdayDb, $userId);
$emailDb = $email;
if ($stmt->execute()) {
$stmt->close();
$_SESSION['profile_flash'] = ['msg' => $t['ok_profile'], 'tab' => 'general'];
header('Location: ' . basename(__FILE__));
exit;
}
$stmt->close();
$errorsGeneral[] = $t['err_generic'];
}
}
/* ---------- Password ---------- */
if ($action === 'password') {
$currentPwd = $_POST['current_password'] ?? '';
$newPwd = $_POST['new_password'] ?? '';
$confirmPwd = $_POST['confirm_password'] ?? '';
if (!$validCsrf) {
$errorsPassword[] = $t['err_csrf'];
} else {
$stmt = $db->prepare('SELECT password FROM auth_users WHERE id = ? LIMIT 1');
$stmt->bind_param('i', $userId);
$stmt->execute();
$stmt->bind_result($storedHash);
$stmt->fetch();
$stmt->close();
if (!password_verify($currentPwd, (string)$storedHash)) {
$errorsPassword[] = $t['err_current_pwd'];
}
if (strlen($newPwd) < 8 || !preg_match('/[A-Za-z]/', $newPwd) || !preg_match('/\d/', $newPwd)) {
$errorsPassword[] = $t['err_pwd_length'];
}
if ($newPwd !== $confirmPwd) {
$errorsPassword[] = $t['err_pwd_match'];
}
}
if (empty($errorsPassword)) {
/* bcrypt $2y$ compatibile con Laravel/Vanguard */
$newHash = password_hash($newPwd, PASSWORD_BCRYPT);
$stmt = $db->prepare('UPDATE auth_users SET password = ?, remember_token = NULL, updated_at = NOW() WHERE id = ?');
$stmt->bind_param('si', $newHash, $userId);
if ($stmt->execute()) {
$stmt->close();
session_regenerate_id(true);
$_SESSION['profile_flash'] = ['msg' => $t['ok_password'], 'tab' => 'password'];
header('Location: ' . basename(__FILE__));
exit;
}
$stmt->close();
$errorsPassword[] = $t['err_generic'];
}
}
}
/* ------------------------------------------------------------------
* Lettura dati utente
* ------------------------------------------------------------------ */
$stmt = $db->prepare(
'SELECT first_name, last_name, username, email, phone, birthday,
avatar, signaturecode, privacyaccepted, termuseaccepted
FROM auth_users
WHERE id = ? LIMIT 1'
);
$stmt->bind_param('i', $userId);
$stmt->execute();
$userData = $stmt->get_result()->fetch_assoc();
$stmt->close();
if (!$userData) {
header('Location: index.php');
exit;
}
/* In caso di errore ripropongo i valori inviati */
if (!empty($errorsGeneral)) {
foreach (['first_name', 'last_name', 'username', 'email', 'phone', 'birthday'] as $f) {
$userData[$f] = trim($_POST[$f] ?? '');
}
}
$avatarProfile = !empty($userData['avatar']) ? $userData['avatar'] : $avatarname;
$fullName = trim(($userData['first_name'] ?? '') . ' ' . ($userData['last_name'] ?? ''));
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title><?php echo $titlepage; ?> - <?php echo h($t['page_title']); ?></title>
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta content="CIMAC TRF Portal" name="description" />
<meta content="" name="author" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<!-- App favicon -->
<link rel="shortcut icon" href="../images/favicon.ico">
<!-- App css -->
<link href="assets/css/bootstrap.min.css" rel="stylesheet" type="text/css" />
<link href="assets/css/jquery-ui.min.css" rel="stylesheet">
<link href="assets/css/icons.min.css" rel="stylesheet" type="text/css" />
<link href="assets/css/metisMenu.min.css" rel="stylesheet" type="text/css" />
<link href="assets/css/app.min.css" rel="stylesheet" type="text/css" />
<style>
.btn-custom-dark {
background-color: rgb(101, 136, 241);
border: none;
font-weight: 500;
transition: background-color 0.3s ease;
}
.btn-custom-dark:hover {
background-color: rgb(71, 85, 214);
}
.profile-section-title {
font-size: 1rem;
font-weight: 600;
margin-bottom: 1.25rem;
padding-bottom: .5rem;
border-bottom: 1px solid #eaf0f7;
}
.readonly-box {
background-color: #f6f8fc;
border-radius: 6px;
padding: 12px 16px;
margin-bottom: 12px;
}
.readonly-box .label {
font-size: .8rem;
color: #8997bd;
margin-bottom: 2px;
}
.readonly-box .value {
font-size: 1rem;
font-weight: 500;
}
.toggle-password {
cursor: pointer;
}
.pwd-strength {
height: 4px;
border-radius: 2px;
margin-top: 6px;
background: #eaf0f7;
overflow: hidden;
}
.pwd-strength span {
display: block;
height: 100%;
width: 0;
transition: width .3s ease, background-color .3s ease;
}
</style>
</head>
<body>
<!-- Top Bar Start -->
<?php include('include/topbar.php'); ?>
<!-- Top Bar End -->
<!-- Left Sidenav -->
<?php include('include/leftsidenav.php'); ?>
<!-- end left-sidenav-->
<div class="page-wrapper">
<!-- Page Content-->
<div class="page-content">
<div class="container-fluid">
<!-- Page-Title -->
<div class="row">
<div class="col-sm-12">
<div class="page-title-box">
<div class="float-right">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="index.php"><?php echo $titlepage; ?></a></li>
<li class="breadcrumb-item active"><?php echo h($t['page_title']); ?></li>
</ol>
</div>
<h4 class="page-title"><?php echo h($t['page_title']); ?></h4>
</div><!--end page-title-box-->
</div><!--end col-->
</div>
<!-- Intestazione utente -->
<div class="row">
<div class="col-xl-12">
<div class="card">
<div class="card-body">
<div class="media">
<img src="upload/users/<?php echo h($avatarProfile); ?>" alt="" class="thumb-md rounded-circle mr-2">
<div class="media-body align-self-center">
<h4 class="mt-0 mb-1"><?php echo h($fullName !== '' ? $fullName : $userData['email']); ?></h4>
<p class="text-muted mb-0 font-14"><?php echo h($userData['email']); ?></p>
</div>
</div>
</div>
</div>
</div>
</div>
<?php if ($successMsg !== '') { ?>
<div class="alert alert-success alert-dismissible fade show" role="alert">
<i class="fas fa-check-circle mr-2"></i><?php echo h($successMsg); ?>
<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button>
</div>
<?php } ?>
<div class="row">
<!-- Colonna sinistra: dati generali + password -->
<div class="col-lg-8">
<!-- ===== Sezione dati generali ===== -->
<div class="card">
<div class="card-body">
<h5 class="profile-section-title"><i class="far fa-user mr-2"></i><?php echo h($t['section_general']); ?></h5>
<?php if (!empty($errorsGeneral)) { ?>
<div class="alert alert-danger">
<?php foreach ($errorsGeneral as $e) { ?>
<div><i class="fas fa-exclamation-circle mr-1"></i><?php echo h($e); ?></div>
<?php } ?>
</div>
<?php } ?>
<form method="post" action="" autocomplete="off" novalidate>
<input type="hidden" name="action" value="general">
<input type="hidden" name="csrf" value="<?php echo h($csrf); ?>">
<div class="form-row">
<div class="form-group col-md-6">
<label for="first_name"><?php echo h($t['first_name']); ?></label>
<input type="text" class="form-control" id="first_name" name="first_name" maxlength="191" value="<?php echo h($userData['first_name']); ?>">
</div>
<div class="form-group col-md-6">
<label for="last_name"><?php echo h($t['last_name']); ?></label>
<input type="text" class="form-control" id="last_name" name="last_name" maxlength="191" value="<?php echo h($userData['last_name']); ?>">
</div>
</div>
<div class="form-row">
<div class="form-group col-md-6">
<label for="email"><?php echo h($t['email']); ?> <span class="text-danger">*</span></label>
<input type="email" class="form-control" id="email" name="email" maxlength="191" required value="<?php echo h($userData['email']); ?>">
</div>
<div class="form-group col-md-6">
<label for="username"><?php echo h($t['username']); ?></label>
<input type="text" class="form-control" id="username" name="username" maxlength="191" value="<?php echo h($userData['username']); ?>">
</div>
</div>
<div class="form-row">
<div class="form-group col-md-6">
<label for="phone"><?php echo h($t['phone']); ?></label>
<input type="text" class="form-control" id="phone" name="phone" maxlength="191" value="<?php echo h($userData['phone']); ?>">
</div>
<div class="form-group col-md-6">
<label for="birthday"><?php echo h($t['birthday']); ?></label>
<input type="date" class="form-control" id="birthday" name="birthday" max="<?php echo date('Y-m-d'); ?>" value="<?php echo h($userData['birthday']); ?>">
</div>
</div>
<div class="text-right">
<button type="submit" class="btn btn-custom-dark text-white px-4">
<i class="fas fa-save mr-2"></i><?php echo h($t['save']); ?>
</button>
</div>
</form>
</div>
</div>
<!-- ===== Sezione password ===== -->
<div class="card" id="password-section">
<div class="card-body">
<h5 class="profile-section-title"><i class="fas fa-lock mr-2"></i><?php echo h($t['section_password']); ?></h5>
<?php if (!empty($errorsPassword)) { ?>
<div class="alert alert-danger">
<?php foreach ($errorsPassword as $e) { ?>
<div><i class="fas fa-exclamation-circle mr-1"></i><?php echo h($e); ?></div>
<?php } ?>
</div>
<?php } ?>
<form method="post" action="#password-section" id="passwordForm" autocomplete="off" novalidate>
<input type="hidden" name="action" value="password">
<input type="hidden" name="csrf" value="<?php echo h($csrf); ?>">
<div class="form-group">
<label for="current_password"><?php echo h($t['current_password']); ?></label>
<div class="input-group">
<input type="password" class="form-control" id="current_password" name="current_password" autocomplete="current-password" required>
<div class="input-group-append">
<span class="input-group-text toggle-password" data-target="current_password"><i class="far fa-eye"></i></span>
</div>
</div>
</div>
<div class="form-row">
<div class="form-group col-md-6">
<label for="new_password"><?php echo h($t['new_password']); ?></label>
<div class="input-group">
<input type="password" class="form-control" id="new_password" name="new_password" autocomplete="new-password" minlength="8" required>
<div class="input-group-append">
<span class="input-group-text toggle-password" data-target="new_password"><i class="far fa-eye"></i></span>
</div>
</div>
<div class="pwd-strength"><span id="pwdStrengthBar"></span></div>
<small class="form-text text-muted"><?php echo h($t['pwd_hint']); ?></small>
</div>
<div class="form-group col-md-6">
<label for="confirm_password"><?php echo h($t['confirm_password']); ?></label>
<div class="input-group">
<input type="password" class="form-control" id="confirm_password" name="confirm_password" autocomplete="new-password" required>
<div class="input-group-append">
<span class="input-group-text toggle-password" data-target="confirm_password"><i class="far fa-eye"></i></span>
</div>
</div>
<div class="invalid-feedback" id="confirmFeedback"><?php echo h($t['err_pwd_match']); ?></div>
</div>
</div>
<div class="text-right">
<button type="submit" class="btn btn-custom-dark text-white px-4" id="btnPassword">
<i class="fas fa-key mr-2"></i><?php echo h($t['update_password']); ?>
</button>
</div>
</form>
</div>
</div>
</div><!--end col-lg-8-->
<!-- Colonna destra: informazioni in sola lettura -->
<div class="col-lg-4">
<div class="card">
<div class="card-body">
<h5 class="profile-section-title"><i class="fas fa-info-circle mr-2"></i><?php echo h($t['section_info']); ?></h5>
<div class="readonly-box">
<div class="label"><?php echo h($t['signaturecode']); ?></div>
<div class="value">
<i class="fas fa-signature mr-2 text-muted"></i><?php echo !empty($userData['signaturecode']) ? h($userData['signaturecode']) : '<span class="text-muted">' . h($t['not_available']) . '</span>'; ?>
</div>
</div>
<div class="readonly-box">
<div class="label"><?php echo h($t['privacyaccepted']); ?></div>
<div class="value">
<i class="fas fa-user-shield mr-2 text-muted"></i><?php echo h(fmtDate($userData['privacyaccepted'], $t['not_available'])); ?>
</div>
</div>
<div class="readonly-box">
<div class="label"><?php echo h($t['termuseaccepted']); ?></div>
<div class="value">
<i class="fas fa-file-contract mr-2 text-muted"></i><?php echo h(fmtDate($userData['termuseaccepted'], $t['not_available'])); ?>
</div>
</div>
</div>
</div>
</div><!--end col-lg-4-->
</div><!--end row-->
</div><!-- container -->
<!-- footer start -->
<?php include('include/footer.php'); ?>
</footer><!--end footer-->
</div>
<!-- end page content -->
</div>
<!-- end page-wrapper -->
<!-- jQuery -->
<script src="assets/js/jquery.min.js"></script>
<script src="assets/js/bootstrap.bundle.min.js"></script>
<script src="assets/js/metismenu.min.js"></script>
<script src="assets/js/waves.js"></script>
<script src="assets/js/feather.min.js"></script>
<script src="assets/js/jquery.slimscroll.min.js"></script>
<script src="assets/js/jquery-ui.min.js"></script>
<!-- App js -->
<script src="assets/js/app.js"></script>
<script>
$(function() {
// Occhietto mostra/nascondi password
$('.toggle-password').on('click', function() {
var $input = $('#' + $(this).data('target'));
var $icon = $(this).find('i');
if ($input.attr('type') === 'password') {
$input.attr('type', 'text');
$icon.removeClass('fa-eye').addClass('fa-eye-slash');
} else {
$input.attr('type', 'password');
$icon.removeClass('fa-eye-slash').addClass('fa-eye');
}
});
// Indicatore robustezza password
$('#new_password').on('input', function() {
var v = $(this).val(),
score = 0;
if (v.length >= 8) score++;
if (v.length >= 12) score++;
if (/[a-z]/.test(v) && /[A-Z]/.test(v)) score++;
if (/\d/.test(v)) score++;
if (/[^A-Za-z0-9]/.test(v)) score++;
var colors = ['#f1646c', '#f1646c', '#fdb52b', '#fdb52b', '#1ecab8', '#1ecab8'];
$('#pwdStrengthBar').css({
width: (score * 20) + '%',
backgroundColor: colors[score]
});
checkConfirm();
});
// Controllo conferma password
function checkConfirm() {
var c = $('#confirm_password').val();
if (c.length && c !== $('#new_password').val()) {
$('#confirm_password').addClass('is-invalid');
$('#confirmFeedback').show();
return false;
}
$('#confirm_password').removeClass('is-invalid');
$('#confirmFeedback').hide();
return true;
}
$('#confirm_password').on('input', checkConfirm);
$('#passwordForm').on('submit', function(e) {
if (!checkConfirm()) {
e.preventDefault();
$('#confirm_password').focus();
}
});
<?php if ($activeTab === 'password') { ?>
document.getElementById('password-section').scrollIntoView({
behavior: 'smooth'
});
<?php } ?>
});
</script>
</body>
</html>