current_user_serial = (int) ($_SESSION[self::USER_SERIAL] ?? 0);
$this->current_user_name = $_SESSION[self::USER_NAME] ?? "";
$this->current_user_email = $_SESSION[self::USER_EMAIL] ?? "";
$this->pagination_page = 1;
}
// ===========
// Setup Users
// ===========
public function setupUsers() {
$users = $this->getUsersPage();
$popovers = (new Popovers())->getPopovers();
$XML = $this->mergeXML($users, "");
$XML = $this->mergeXML($popovers, $XML);
$html = $this->applyXSL($XML, $this->getXSL("setupUsers"));
$this->displayContent($html);
}
// ============
// User Summary
// ============
public function userSummary() {
$user_serial = filter_input(INPUT_POST, "user_serial", FILTER_SANITIZE_NUMBER_INT) ?: 0;
$user = $this->getUser($user_serial);
if ($user->count() == 0) {
$this->logError("Invalid User ({$user_serial}). " . __METHOD__);
}
$notes = (new Notes())->getNotes('User',(int)$user_serial);
$activity=$this->getLoginActivity($user_serial);
$XML = $this->mergeXML($user, "");
$XML = $this->mergeXML($activity, $XML);
$XML = $this->mergeXML($notes, $XML);
$content = $this->applyXSL($XML, $this->getXSL("userSummary"));
$this->displayContent($content);
}
// ========
// Add User
// ========
public function addUser() {
$step = filter_input(INPUT_POST, "step") ?: "prompt";
switch ($step) {
case "prompt":
$popovers = (new Popovers())->getPopovers();
$constants = $this->getConstants();
$XML = $this->mergeXML($popovers, "");
$XML = $this->mergeXML($constants, $XML);
$content = $this->applyXSL($XML, $this->getXSL("addUser"));
$this->displayContent($content);
break;
case "add":
echo json_encode((new AdminAccounts())->create($_POST));
break;
default:
$this->displayHome();
}
}
// ===========
// Delete User
// ===========
public function deleteUser() {
(new MaintenanceService())->deleteUser(MaintenanceService::id($_POST['user_serial']??null));
}
// =========
// Edit User
// =========
public function editUser() {
$step = filter_input(INPUT_POST, "step") ?: "prompt";
switch ($step) {
case "prompt":
$user_serial = (int) filter_input(INPUT_POST, "user_serial", FILTER_SANITIZE_NUMBER_INT) ?: 0;
$user = $this->getUser($user_serial);
if ($user->count() === 0) {
$this->logError("Invalid User ({$user_serial}): " . __METHOD__);
}
$popovers = (new Popovers())->getPopovers();
$constants = $this->getConstants();
$XML = $this->mergeXML($user, "");
$XML = $this->mergeXML($popovers, $XML);
$XML = $this->mergeXML($constants, $XML);
$content = $this->applyXSL($XML, $this->getXSL("editUser"));
$this->displayContent($content);
break;
case "update":
(new MaintenanceService())->updateUser(MaintenanceService::id($_POST['user_serial']??null),$_POST);
break;
default:
$this->displayHome();
}
}
// ====================
// Return a User Object
// ====================
public function getUser($user_serial = 0) {
$user_serial = (int) $user_serial;
$SQL = "select view_users.*
from view_users
where user_serial = {$user_serial}";
return $this->getTable("users", $SQL);
}
// ====================
// Return a User Object
// ====================
public function getUserByUserName($user_name) {
$stmt = $this->connect()->prepare('SELECT * FROM view_users WHERE user_name = :name');
$stmt->execute([':name' => $user_name]);
return $this->getTableXML('users', $stmt);
}
// =====================
// Return a Users Serial
// =====================
public function getUserSerial($user_name = "") {
$users = $this->getUserByUserName($user_name);
return ($users->count() === 0) ? 0 : (int) $users->record->user_serial;
}
// =====================
// Return a Users Object
// =====================
public function getUsers() {
$SQL = "select view_users.* from view_users";
return $this->getTable("users", $SQL);
}
// ==========================
// Return a Users Page Object
// ==========================
public function getUsersPage() {
$this->pagination_size=(int)($_POST['users_per_page']??20);
// Find Criteria
$find_user = filter_input(INPUT_POST, "find_user") ?: "";
$find_user = trim($find_user);
$find_words = implode("|", array_map(static fn($word) => preg_quote($word), explode(" ", $find_user)));
// Record Count
$connection = $this->connect();
$SQL = "select count(*) as count
from view_users
where user_name regexp :user_name
or user_first_name regexp :user_first_name
or user_last_name regexp :user_last_name
or user_email regexp :user_email
or user_role regexp :user_role";
$statement = $connection->prepare($SQL);
$statement->bindValue(":user_name", $find_words);
$statement->bindValue(":user_first_name", $find_words);
$statement->bindValue(":user_last_name", $find_words);
$statement->bindValue(":user_email", $find_words);
$statement->bindValue(":user_role", $find_words);
$statement->execute();
$recordset = $statement->fetch(PDO::FETCH_ASSOC);
$count = (count($recordset) > 0) ? (int) $recordset["count"] : (int) 0;
// Pagination
$pagination = filter_input(INPUT_POST, "pagination") ?: "";
$paging=Pagination::session(__METHOD__,$count,(int)$this->pagination_size,$pagination);
$this->pagination_size=$paging['size'];$this->pagination_page=$paging['page'];
$offset=$paging['offset'];$start=$paging['start'];$end=$paging['end'];
// Data
$SQL = "select view_users.*
from view_users
where user_name regexp :user_name
or user_first_name regexp :user_first_name
or user_last_name regexp :user_last_name
or user_email regexp :user_email
or user_role regexp :user_role
order by user_serial
limit {$this->pagination_size}
offset {$offset}";
$statement = $connection->prepare($SQL);
$statement->bindValue(":user_name", $find_words);
$statement->bindValue(":user_first_name", $find_words);
$statement->bindValue(":user_last_name", $find_words);
$statement->bindValue(":user_email", $find_words);
$statement->bindValue(":user_role", $find_words);
$statement->execute();
$users = $this->getTableXML("users", $statement);
// Insert Pagination Attributes
$users->addAttribute("count", $count);
$users->addAttribute("start", $start);
$users->addAttribute("end", $end);
$users->addAttribute("page", $this->pagination_page);
return $users;
}
// ===========================
// Return an User Object(AJAX)
// ===========================
public function getUser_AJAX($user_name = "") {
$user_name = filter_input(INPUT_POST, "user_name") ?: $user_name;
$users = $this->getUserByUserName($user_name);
// Create the User Object.
$User = array();
$User["exists"] = ($users->count() > 0) ? true : false;
if ($_SESSION[self::AJAX]) {
echo json_encode($User);
} else {
return $User;
}
}
// ==========================
// Determine if a User Exists
// ==========================
public function userExists() {
$user_serial = filter_input(INPUT_POST, "user_serial", FILTER_SANITIZE_NUMBER_INT) ?: 0;
$user_name = filter_input(INPUT_POST, "user_name") ?: "";
$user_name = strtolower(trim(preg_replace("/\s+/", " ", $user_name)));
$SQL = "select user_name
from view_users
where user_serial != :user_serial
and user_name = :user_name";
$statement = $this->connect()->prepare($SQL);
$statement->bindValue(":user_serial", $user_serial);
$statement->bindValue(":user_name", $user_name);
$statement->execute();
$recordset = $statement->fetchAll(PDO::FETCH_ASSOC);
$exists = (count($recordset) > 0) ? true : false;
echo json_encode($exists);
}
// =============================
// Determine if a User is Active
// =============================
public function userActive() {
$id=MaintenanceService::id($_POST['user_serial']??null);
$used=(new MaintenanceService())->userUsed($id);
echo json_encode($used || $id===(int)($_SESSION[self::USER_SERIAL]??0));
}
// ===============
// Change Password
// ===============
public function changePassword() {
$params = ['PASSWORD_ERROR' => ''];
if (($_POST['step'] ?? '') === 'change') {
$this->requireCsrf();
try {
$auth = new AuthService();
if (!$auth->allowAttempt('change-password', (string) $_SESSION[self::USER_SERIAL], 10, 900)) {
throw new InvalidArgumentException('Too many attempts. Please try again later.');
}
if (!$auth->changePassword((int) $_SESSION[self::USER_SERIAL], $_POST['user_current_password'] ?? '', $_POST['user_new_password'] ?? '', $_POST['user_verify_password'] ?? '')) {
throw new InvalidArgumentException('Your current password is incorrect or your session has expired.');
}
$this->logoutSession();
header('Location: main.php', true, 303);
exit;
} catch (InvalidArgumentException $e) { $params['PASSWORD_ERROR'] = $e->getMessage(); }
}
$this->displayContent($this->applyXSL('', $this->getXSL('changePassword'), $params));
}
public function resetPassword() {
$this->requireCsrf();
$stmt=$this->connect()->prepare('SELECT user_email FROM users WHERE user_serial=?');$stmt->execute([MaintenanceService::id($_POST['user_serial']??null)]);$email=$stmt->fetchColumn();
if(!$email)throw new AppException('Account not found.',404);
if(!(new AuthService())->requestReset($email))throw new AppException('The invitation is rate limited or the account is inactive. Try again later.',429);
echo json_encode(['ok'=>true,'message'=>'A password setup link was sent.']);
}
// ===============================================
// Update the Login timestamp for the current User
// ===============================================
public function updateLogin() {
$SQL = "update users
set user_last_login = :user_login
where user_serial = :user_serial";
$statement = $this->connect()->prepare($SQL);
$statement->bindValue(":user_login", $this->getTimestamp());
$statement->bindValue(":user_serial", $this->current_user_serial);
$statement->execute();
}
// ====================
// Update Active Status
// ====================
public function updateActiveStatus() {
(new MaintenanceService())->setUserActive(MaintenanceService::id($_POST['user_serial']??null),MaintenanceService::boolean($_POST['user_active']??null));
}
// ====================================
// Return a Users Login Activity Object
// ====================================
public function getLoginActivity($userserial) {
$stmt=$this->connect()->prepare("SELECT journal_timestamp_verbose,journal_ip FROM view_journal WHERE journal_origin=? AND journal_entry LIKE ? ORDER BY journal_timestamp DESC,journal_serial DESC");
$id=MaintenanceService::id($userserial);$stmt->execute([(string)$id,$id.' logged in%']);
return $this->getTableXML('loginactivity',$stmt);
}
}