- dashboard.php: fix PDO parameter count crash for users with no groups
(used max(count,1) for placeholder count but provided 0 group params)
- index.php: add missing auth check on get_tasks_status API endpoint
(was accessible without a session, exposing all task data)
- index.php: enforce ownership check on delete_comment API
(any logged-in user could delete any comment; now requires authorship or admin)
- tasks.php: fix task creation notification using undefined $_SESSION['username']
(replaced with $current_user['username'] so {assignor} template works correctly)
- tasks.php: remove duplicate id attribute on att-lightbox-body element
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
462 lines
21 KiB
PHP
462 lines
21 KiB
PHP
<?php
|
|
session_start();
|
|
date_default_timezone_set('Etc/GMT-7');
|
|
require_once 'db.php';
|
|
|
|
$action = (string)($_GET['action'] ?? 'dashboard');
|
|
|
|
// API updates for task kanban drag n drop
|
|
if ($action === 'api') {
|
|
$api_action = $_GET['api_action'] ?? '';
|
|
header('Content-Type: application/json; charset=utf-8');
|
|
|
|
// Ensure newer task columns exist (API might be called without visiting the tasks page first).
|
|
try { $pdo->exec("ALTER TABLE tasks ADD COLUMN assignor_type TEXT DEFAULT 'user'"); } catch(Exception $e) {}
|
|
try { $pdo->exec("ALTER TABLE tasks ADD COLUMN assignor_group_id INTEGER"); } catch(Exception $e) {}
|
|
try { $pdo->exec("ALTER TABLE tasks ADD COLUMN status_updated_at DATETIME"); } catch(Exception $e) {}
|
|
try { $pdo->exec("ALTER TABLE tasks ADD COLUMN due_date DATETIME"); } catch(Exception $e) {}
|
|
try { $pdo->exec("ALTER TABLE tasks ADD COLUMN custom_assignee_name TEXT"); } catch(Exception $e) {}
|
|
try { $pdo->exec("ALTER TABLE tasks ADD COLUMN completed_by INTEGER DEFAULT NULL"); } catch(Exception $e) {}
|
|
try { $pdo->exec("ALTER TABLE tasks ADD COLUMN archived_by INTEGER DEFAULT NULL"); } catch(Exception $e) {}
|
|
try { $pdo->exec("CREATE TABLE IF NOT EXISTS task_attachments (id INTEGER PRIMARY KEY AUTOINCREMENT, task_id INTEGER, file_name TEXT, file_path TEXT, owner_id INTEGER, created_at DATETIME DEFAULT CURRENT_TIMESTAMP)"); } catch(Exception $e) {}
|
|
try { $pdo->exec("CREATE TABLE IF NOT EXISTS task_comments (id INTEGER PRIMARY KEY AUTOINCREMENT, task_id INTEGER, comment TEXT NOT NULL, created_by INTEGER, created_at DATETIME DEFAULT CURRENT_TIMESTAMP)"); } catch(Exception $e) {}
|
|
|
|
$format_dt = function($value) {
|
|
$value = is_string($value) ? trim($value) : $value;
|
|
if(!$value) return null;
|
|
$ts = strtotime((string)$value);
|
|
if($ts === false) return null;
|
|
return date('Y-m-d H:i', $ts);
|
|
};
|
|
|
|
$format_status = function($value) {
|
|
if(!$value) return null;
|
|
$s = strtolower(trim((string)$value));
|
|
if($s === 'in_progress') return 'In Progress';
|
|
$s = str_replace('_', ' ', $s);
|
|
return ucfirst($s);
|
|
};
|
|
|
|
if($api_action === 'update_task_status') {
|
|
if(!isset($_SESSION['user_id'])) die(json_encode(['status'=>'error','msg'=>'unauthorized']));
|
|
$task_id = $_POST['task_id'] ?? null;
|
|
$status = $_POST['status'] ?? null;
|
|
$user_id = $_SESSION['user_id'];
|
|
|
|
$set_clause = "status = ?, status_updated_at = CURRENT_TIMESTAMP";
|
|
$params = [$status];
|
|
|
|
if ($status === 'completed') {
|
|
$set_clause .= ", completed_by = ?";
|
|
$params[] = $user_id;
|
|
} elseif ($status === 'archived') {
|
|
$set_clause .= ", archived_by = ?";
|
|
$params[] = $user_id;
|
|
}
|
|
$params[] = $task_id;
|
|
|
|
$stmt = $pdo->prepare("UPDATE tasks SET $set_clause WHERE id = ?");
|
|
$stmt->execute($params);
|
|
$t = query("SELECT title, assignee_type, assignee_id FROM tasks WHERE id=?", [$task_id])->fetch();
|
|
if($t) {
|
|
$ctx = ['action' => 'Moved to '.$status, 'module' => 'Task', 'item' => $t['title']];
|
|
if($t['assignee_type'] === 'user' && $t['assignee_id']) {
|
|
notify_user($t['assignee_id'], "Task '{$t['title']}' moved to $status", $ctx);
|
|
} elseif($t['assignee_type'] === 'group' && $t['assignee_id']) {
|
|
notify_group($t['assignee_id'], "Group task '{$t['title']}' moved to $status", $ctx);
|
|
}
|
|
}
|
|
echo json_encode(['status'=>'ok']);
|
|
exit;
|
|
}
|
|
|
|
if($api_action === 'add_comment') {
|
|
if(!isset($_SESSION['user_id'])) die(json_encode(['status'=>'error','msg'=>'unauthorized']));
|
|
$task_id = $_POST['task_id'] ?? null;
|
|
$comment = $_POST['comment'] ?? null;
|
|
$user_id = $_SESSION['user_id'];
|
|
if($task_id && $comment) {
|
|
$stmt = $pdo->prepare("INSERT INTO task_comments (task_id, comment, created_by) VALUES (?, ?, ?)");
|
|
$stmt->execute([$task_id, $comment, $user_id]);
|
|
$t = query("SELECT title, assignee_type, assignee_id FROM tasks WHERE id=?", [$task_id])->fetch();
|
|
if($t) {
|
|
$ctx = ['action' => 'New Comment', 'module' => 'Task', 'item' => $t['title']];
|
|
if($t['assignee_type'] === 'user' && $t['assignee_id']) {
|
|
notify_user($t['assignee_id'], "New comment on '{$t['title']}'", $ctx);
|
|
} elseif($t['assignee_type'] === 'group' && $t['assignee_id']) {
|
|
notify_group($t['assignee_id'], "New comment on group task '{$t['title']}'", $ctx);
|
|
}
|
|
}
|
|
}
|
|
echo json_encode(['status'=>'ok']);
|
|
exit;
|
|
}
|
|
|
|
if($api_action === 'delete_comment') {
|
|
if(!isset($_SESSION['user_id'])) die(json_encode(['status'=>'error','msg'=>'unauthorized']));
|
|
$comment_id = $_POST['comment_id'] ?? null;
|
|
if($comment_id) {
|
|
$user_id = $_SESSION['user_id'];
|
|
$is_admin = !empty($_SESSION['is_admin']);
|
|
$comment_check = query("SELECT created_by FROM task_comments WHERE id = ?", [$comment_id])->fetch();
|
|
if($comment_check && ($comment_check['created_by'] == $user_id || $is_admin)) {
|
|
$stmt = $pdo->prepare("DELETE FROM task_comments WHERE id = ?");
|
|
$stmt->execute([$comment_id]);
|
|
} else {
|
|
echo json_encode(['status'=>'error','msg'=>'forbidden']);
|
|
exit;
|
|
}
|
|
}
|
|
echo json_encode(['status'=>'ok']);
|
|
exit;
|
|
}
|
|
|
|
if($api_action === 'get_tasks_status') {
|
|
if(!isset($_SESSION['user_id'])) die(json_encode(['status'=>'error','msg'=>'unauthorized']));
|
|
$task_id = $_GET['task_id'] ?? null;
|
|
|
|
// Correlated subquery gives attachment count per task.
|
|
$sql = "
|
|
SELECT
|
|
t.*,
|
|
u.username as assignor_user_name,
|
|
g.name as assignor_group_name,
|
|
au.username as assignee_user_name,
|
|
ag.name as assignee_group_name,
|
|
(SELECT COUNT(*) FROM task_attachments ta WHERE ta.task_id = t.id) AS attachments_count,
|
|
(SELECT COUNT(*) FROM task_comments tc WHERE tc.task_id = t.id) AS comments_count,
|
|
(SELECT comment FROM task_comments tc WHERE tc.task_id = t.id ORDER BY tc.created_at DESC LIMIT 1) AS last_comment,
|
|
(SELECT created_at FROM task_comments tc WHERE tc.task_id = t.id ORDER BY tc.created_at DESC LIMIT 1) AS last_comment_date,
|
|
(SELECT cu.username FROM task_comments tc JOIN users cu ON tc.created_by = cu.id WHERE tc.task_id = t.id ORDER BY tc.created_at DESC LIMIT 1) AS last_commenter,
|
|
t.status_updated_at,
|
|
t.completed_by,
|
|
t.archived_by,
|
|
t.tags,
|
|
t.title
|
|
FROM tasks t
|
|
LEFT JOIN users u ON t.assignor_id = u.id
|
|
LEFT JOIN groups g ON t.assignor_group_id = g.id
|
|
LEFT JOIN users au ON (t.assignee_type='user' AND t.assignee_id = au.id)
|
|
LEFT JOIN groups ag ON (t.assignee_type='group' AND t.assignee_id = ag.id)
|
|
";
|
|
$params = [];
|
|
|
|
if($task_id !== null && $task_id !== '') {
|
|
if(!ctype_digit((string)$task_id)) {
|
|
echo json_encode(['status'=>'error','msg'=>'Invalid task_id']);
|
|
exit;
|
|
}
|
|
$sql .= " WHERE t.id = ? LIMIT 1";
|
|
$params[] = (int)$task_id;
|
|
} else {
|
|
$sql .= " ORDER BY t.created_at DESC";
|
|
}
|
|
|
|
$stmt = $pdo->prepare($sql);
|
|
$stmt->execute($params);
|
|
$rows = $stmt->fetchAll();
|
|
|
|
if($task_id !== null && $task_id !== '') {
|
|
$row = $rows ? $rows[0] : null;
|
|
if(!$row) {
|
|
echo json_encode(['status'=>'error','msg'=>'Task not found']);
|
|
exit;
|
|
}
|
|
|
|
$from = null;
|
|
if(($row['assignor_type'] ?? null) === 'group') $from = $row['assignor_group_name'] ?? null;
|
|
elseif(($row['assignor_type'] ?? null) === 'user') $from = $row['assignor_user_name'] ?? null;
|
|
|
|
$to = null;
|
|
if(($row['assignee_type'] ?? null) === 'user') $to = $row['assignee_user_name'] ?? null;
|
|
elseif(($row['assignee_type'] ?? null) === 'group') $to = $row['assignee_group_name'] ?? null;
|
|
elseif(($row['assignee_type'] ?? null) === 'custom') $to = $row['custom_assignee_name'] ?? null;
|
|
|
|
$desc = (string)($row['description'] ?? '');
|
|
$desc = trim($desc) !== '' ? $desc : (string)($row['title'] ?? '');
|
|
|
|
$tags = !empty($row['tags']) ? array_filter(array_map('trim', explode(',', $row['tags']))) : [];
|
|
|
|
// Logic for last_modified_by
|
|
// If it's archived, 'Archived by' user; if completed, 'Completed by' user; otherwise latest commenter or creator.
|
|
$last_mod_by = 'System';
|
|
if ($row['status'] === 'archived' && $row['archived_by']) {
|
|
$last_mod_by = query("SELECT username FROM users WHERE id=?", [$row['archived_by']])->fetchColumn() ?: 'System';
|
|
} elseif ($row['status'] === 'completed' && $row['completed_by']) {
|
|
$last_mod_by = query("SELECT username FROM users WHERE id=?", [$row['completed_by']])->fetchColumn() ?: 'System';
|
|
} else {
|
|
$last_mod_by = $row['last_commenter'] ?: $row['assignor_user_name'];
|
|
}
|
|
|
|
$payload = [
|
|
'task_id' => (string)$row['id'],
|
|
'task_name' => (string)$row['title'],
|
|
'task_created_date' => $format_dt($row['created_at'] ? $row['created_at'] . ' UTC' : null),
|
|
'from' => $from,
|
|
'to' => $to,
|
|
'task_description' => $desc,
|
|
'due_date' => $format_dt($row['due_date'] ?? null),
|
|
'current_status' => $format_status($row['status'] ?? null),
|
|
'attachments_count' => (int)($row['attachments_count'] ?? 0),
|
|
'tags_count' => count($tags),
|
|
'comments_count' => (int)($row['comments_count'] ?? 0),
|
|
'last_modified_by' => $last_mod_by,
|
|
'last_modified_date' => $format_dt(($row['status_updated_at'] ?: $row['created_at']) . ' UTC'),
|
|
'last_comment' => $row['last_comment'] ?: null,
|
|
'last_comment_date' => $format_dt($row['last_comment_date'] ? $row['last_comment_date'] . ' UTC' : null),
|
|
];
|
|
|
|
echo json_encode($payload);
|
|
exit;
|
|
}
|
|
|
|
$result = [];
|
|
foreach($rows as $row) {
|
|
$from = null;
|
|
if(($row['assignor_type'] ?? null) === 'group') $from = $row['assignor_group_name'] ?? null;
|
|
elseif(($row['assignor_type'] ?? null) === 'user') $from = $row['assignor_user_name'] ?? null;
|
|
|
|
$to = null;
|
|
if(($row['assignee_type'] ?? null) === 'user') $to = $row['assignee_user_name'] ?? null;
|
|
elseif(($row['assignee_type'] ?? null) === 'group') $to = $row['assignee_group_name'] ?? null;
|
|
elseif(($row['assignee_type'] ?? null) === 'custom') $to = $row['custom_assignee_name'] ?? null;
|
|
|
|
$desc = (string)($row['description'] ?? '');
|
|
$desc = trim($desc) !== '' ? $desc : (string)($row['title'] ?? '');
|
|
|
|
$tags = !empty($row['tags']) ? array_filter(array_map('trim', explode(',', $row['tags']))) : [];
|
|
|
|
$last_mod_by = 'System';
|
|
if ($row['status'] === 'archived' && $row['archived_by']) {
|
|
$last_mod_by = query("SELECT username FROM users WHERE id=?", [$row['archived_by']])->fetchColumn() ?: 'System';
|
|
} elseif ($row['status'] === 'completed' && $row['completed_by']) {
|
|
$last_mod_by = query("SELECT username FROM users WHERE id=?", [$row['completed_by']])->fetchColumn() ?: 'System';
|
|
} else {
|
|
$last_mod_by = $row['last_commenter'] ?: $row['assignor_user_name'];
|
|
}
|
|
|
|
$result[] = [
|
|
'task_id' => (string)$row['id'],
|
|
'task_name' => (string)$row['title'],
|
|
'task_created_date' => $format_dt($row['created_at'] ? $row['created_at'] . ' UTC' : null),
|
|
'from' => $from,
|
|
'to' => $to,
|
|
'task_description' => $desc,
|
|
'due_date' => $format_dt($row['due_date'] ?? null),
|
|
'current_status' => $format_status($row['status'] ?? null),
|
|
'attachments_count' => (int)($row['attachments_count'] ?? 0),
|
|
'tags_count' => count($tags),
|
|
'comments_count' => (int)($row['comments_count'] ?? 0),
|
|
'last_modified_by' => $last_mod_by,
|
|
'last_modified_date' => $format_dt(($row['status_updated_at'] ?: $row['created_at']) . ' UTC'),
|
|
'last_comment' => $row['last_comment'] ?: null,
|
|
'last_comment_date' => $format_dt($row['last_comment_date'] ? $row['last_comment_date'] . ' UTC' : null),
|
|
];
|
|
}
|
|
|
|
echo json_encode($result);
|
|
exit;
|
|
}
|
|
|
|
if($api_action === 'add_task_tag') {
|
|
if(!isset($_SESSION['user_id'])) die(json_encode(['success'=>false,'error'=>'unauthorized']));
|
|
$task_id = $_POST['task_id'] ?? null;
|
|
$tag = trim($_POST['tag'] ?? '');
|
|
$user_id = $_SESSION['user_id'];
|
|
|
|
if($task_id && $tag) {
|
|
$stmt = $pdo->prepare("SELECT tags FROM tasks WHERE id = ?");
|
|
$stmt->execute([$task_id]);
|
|
$row = $stmt->fetch();
|
|
$current_tags = $row ? ($row['tags'] ?? '') : '';
|
|
|
|
$tags_array = $current_tags ? array_filter(array_map('trim', explode(',', $current_tags))) : [];
|
|
if(!in_array($tag, $tags_array)) {
|
|
$tags_array[] = $tag;
|
|
$new_tags = implode(',', $tags_array);
|
|
$pdo->prepare("UPDATE tasks SET tags = ? WHERE id = ?")->execute([$new_tags, $task_id]);
|
|
}
|
|
echo json_encode(['success'=>true]);
|
|
} else {
|
|
echo json_encode(['success'=>false,'error'=>'Missing task_id or tag']);
|
|
}
|
|
exit;
|
|
}
|
|
|
|
if($api_action === 'remove_task_tag') {
|
|
if(!isset($_SESSION['user_id'])) die(json_encode(['success'=>false,'error'=>'unauthorized']));
|
|
$task_id = $_POST['task_id'] ?? null;
|
|
$tag = trim($_POST['tag'] ?? '');
|
|
|
|
if($task_id && $tag) {
|
|
$stmt = $pdo->prepare("SELECT tags FROM tasks WHERE id = ?");
|
|
$stmt->execute([$task_id]);
|
|
$row = $stmt->fetch();
|
|
$current_tags = $row ? ($row['tags'] ?? '') : '';
|
|
|
|
$tags_array = $current_tags ? array_filter(array_map('trim', explode(',', $current_tags))) : [];
|
|
$tags_array = array_diff($tags_array, [$tag]);
|
|
$new_tags = implode(',', $tags_array);
|
|
$pdo->prepare("UPDATE tasks SET tags = ? WHERE id = ?")->execute([$new_tags, $task_id]);
|
|
echo json_encode(['success'=>true]);
|
|
} else {
|
|
echo json_encode(['success'=>false,'error'=>'Missing task_id or tag']);
|
|
}
|
|
exit;
|
|
}
|
|
|
|
echo json_encode(['status'=>'error','msg'=>'Unknown api_action']);
|
|
exit;
|
|
}
|
|
|
|
if (!isset($_SESSION['user_id']) && $action !== 'login' && $action !== 'setup') {
|
|
header('Location: ?action=login');
|
|
exit;
|
|
}
|
|
|
|
$user_id = $_SESSION['user_id'] ?? null;
|
|
$current_user = null;
|
|
$unread_notifications = 0;
|
|
if ($user_id) {
|
|
$current_user = get_user($user_id);
|
|
$stmt = $pdo->prepare("SELECT COUNT(*) FROM notifications WHERE user_id = ? AND is_read = 0");
|
|
$stmt->execute([$user_id]);
|
|
$unread_notifications = $stmt->fetchColumn();
|
|
}
|
|
|
|
if ($action === 'logout') {
|
|
session_destroy();
|
|
header('Location: ?action=login');
|
|
exit;
|
|
}
|
|
|
|
if ($action === 'setup') {
|
|
include 'setup.php';
|
|
exit;
|
|
}
|
|
|
|
if ($action === 'login') {
|
|
$error = '';
|
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|
$username = trim((string)($_POST['username'] ?? ''));
|
|
$password = trim((string)($_POST['password'] ?? ''));
|
|
$stmt = $pdo->prepare("SELECT * FROM users WHERE username = ?");
|
|
$stmt->execute([$username]);
|
|
$user = $stmt->fetch();
|
|
if ($user && password_verify($password, $user['password_hash'])) {
|
|
$_SESSION['user_id'] = $user['id'];
|
|
$_SESSION['is_admin'] = $user['is_admin'];
|
|
header('Location: ?action=dashboard');
|
|
exit;
|
|
} else {
|
|
$error = "Invalid username or password";
|
|
}
|
|
}
|
|
include 'views/login.php';
|
|
exit;
|
|
}
|
|
|
|
// Handle POST actions that need a redirect BEFORE layout outputs any HTML.
|
|
if ($_SERVER['REQUEST_METHOD'] === 'POST' && $user_id) {
|
|
// Mood setter
|
|
if (isset($_POST['set_mood'])) {
|
|
try { $pdo->exec("ALTER TABLE users ADD COLUMN mood TEXT DEFAULT NULL"); } catch(Exception $e) {}
|
|
try { $pdo->exec("ALTER TABLE users ADD COLUMN mood_updated_at DATETIME DEFAULT NULL"); } catch(Exception $e) {}
|
|
try { $pdo->exec("CREATE TABLE IF NOT EXISTS activity_log (id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER, action TEXT NOT NULL, detail TEXT, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE)"); } catch(Exception $e) {}
|
|
$mood = trim((string)($_POST['mood'] ?? ''));
|
|
$allowed = ['😊','😢','😠','😴','🤩','😟','😎','🤔','😐','🥳'];
|
|
if (in_array($mood, $allowed) || $mood === '') {
|
|
$pdo->prepare("UPDATE users SET mood=?, mood_updated_at=CURRENT_TIMESTAMP WHERE id=?")
|
|
->execute([$mood ?: null, $user_id]);
|
|
try {
|
|
$pdo->prepare("INSERT INTO activity_log (user_id, action, detail) VALUES (?, 'mood_set', ?)")
|
|
->execute([$user_id, $mood ?: 'cleared']);
|
|
} catch(Exception $e) {}
|
|
}
|
|
header('Location: ?action=dashboard');
|
|
exit;
|
|
}
|
|
|
|
// Profile picture upload
|
|
if (isset($_POST['upload_profile_pic'])) {
|
|
// Ensure column exists
|
|
try { $pdo->exec("ALTER TABLE users ADD COLUMN profile_pic TEXT DEFAULT NULL"); } catch(Exception $e) {}
|
|
if (isset($_FILES['profile_pic']) && $_FILES['profile_pic']['error'] === 0) {
|
|
$allowed_types = ['image/jpeg','image/png','image/gif','image/webp'];
|
|
if (in_array($_FILES['profile_pic']['type'], $allowed_types)) {
|
|
$ext = pathinfo($_FILES['profile_pic']['name'], PATHINFO_EXTENSION);
|
|
$filename = 'avatar_' . $user_id . '_' . time() . '.' . $ext;
|
|
$dest = 'uploads/' . $filename;
|
|
if (move_uploaded_file($_FILES['profile_pic']['tmp_name'], $dest)) {
|
|
// Delete old pic
|
|
$old = $pdo->prepare("SELECT profile_pic FROM users WHERE id=?");
|
|
$old->execute([$user_id]);
|
|
$old_pic = $old->fetchColumn();
|
|
if ($old_pic && file_exists($old_pic)) @unlink($old_pic);
|
|
$pdo->prepare("UPDATE users SET profile_pic=? WHERE id=?")->execute([$dest, $user_id]);
|
|
}
|
|
}
|
|
}
|
|
header('Location: ?action=' . ($_POST['redirect_to'] ?? 'dashboard'));
|
|
exit;
|
|
}
|
|
|
|
// Delete profile picture
|
|
if (isset($_POST['delete_profile_pic'])) {
|
|
try { $pdo->exec("ALTER TABLE users ADD COLUMN profile_pic TEXT DEFAULT NULL"); } catch(Exception $e) {}
|
|
$old = $pdo->prepare("SELECT profile_pic FROM users WHERE id=?");
|
|
$old->execute([$user_id]);
|
|
$old_pic = $old->fetchColumn();
|
|
if ($old_pic && file_exists($old_pic)) @unlink($old_pic);
|
|
$pdo->prepare("UPDATE users SET profile_pic=NULL WHERE id=?")->execute([$user_id]);
|
|
header('Location: ?action=' . ($_POST['redirect_to'] ?? 'dashboard'));
|
|
exit;
|
|
}
|
|
|
|
// Mark all notifications as read
|
|
if (isset($_POST['mark_all_read'])) {
|
|
$pdo->prepare("UPDATE notifications SET is_read=1 WHERE user_id=?")->execute([$user_id]);
|
|
header('Location: ?action=' . ($_POST['redirect_to'] ?? 'dashboard'));
|
|
exit;
|
|
}
|
|
|
|
// Change password
|
|
if (isset($_POST['change_password'])) {
|
|
$cur = trim($_POST['current_password'] ?? '');
|
|
$new = trim($_POST['new_password'] ?? '');
|
|
$cnf = trim($_POST['confirm_password'] ?? '');
|
|
$user = get_user($user_id);
|
|
$pw_error = '';
|
|
if (!password_verify($cur, $user['password_hash'])) {
|
|
$pw_error = 'Current password is incorrect.';
|
|
} elseif (strlen($new) < 6) {
|
|
$pw_error = 'New password must be at least 6 characters.';
|
|
} elseif ($new !== $cnf) {
|
|
$pw_error = 'Passwords do not match.';
|
|
} else {
|
|
$pdo->prepare("UPDATE users SET password_hash=? WHERE id=?")
|
|
->execute([password_hash($new, PASSWORD_DEFAULT), $user_id]);
|
|
}
|
|
$_SESSION['pw_change_msg'] = $pw_error ?: 'success';
|
|
header('Location: ?action=' . ($_POST['redirect_to'] ?? 'dashboard'));
|
|
exit;
|
|
}
|
|
}
|
|
|
|
$view_file = 'views/' . basename($action) . '.php';
|
|
if (file_exists($view_file)) {
|
|
$is_ajax_post = isset($_POST['ajax_submit'])
|
|
|| isset($_POST['delete_attachment_ajax'])
|
|
|| isset($_POST['upload_attachment_ajax']);
|
|
|
|
if ($is_ajax_post) {
|
|
include $view_file;
|
|
} else {
|
|
include 'views/layout_top.php';
|
|
include $view_file;
|
|
include 'views/layout_bottom.php';
|
|
}
|
|
} else {
|
|
echo "404 Not Found";
|
|
}
|