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); } }