2026-10-18 17:01:36 +00:00
<? php
// ===========
// Users Class
// ===========
class Users extends Base {
// Variables
private $current_user_serial = "" ;
private $current_user_name = "" ;
private $current_user_email = "" ;
private $pagination_size = 20 ;
private $pagination_page = 1 ;
// ================================
// __construct : Class Constructor.
// ================================
public function __construct () {
set_error_handler ( array ( $this , "displayError" ));
date_default_timezone_set ( self :: TIMEZONE );
2026-10-18 19:27:17 -04:00
$this -> current_user_serial = ( int ) ( $_SESSION [ self :: USER_SERIAL ] ?? 0 );
2026-10-18 17:01:36 +00:00
$this -> current_user_name = $_SESSION [ self :: USER_NAME ] ?? "" ;
$this -> current_user_email = $_SESSION [ self :: USER_EMAIL ] ?? "" ;
2026-10-20 06:45:30 -04:00
$this -> pagination_page = 1 ;
2026-10-18 17:01:36 +00:00
}
// ===========
// Setup Users
// ===========
public function setupUsers () {
$users = $this -> getUsersPage ();
$popovers = ( new Popovers ()) -> getPopovers ();
$XML = $this -> mergeXML ( $users , "<XML/>" );
$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__ );
}
2026-10-20 06:45:30 -04:00
$notes = ( new Notes ()) -> getNotes ( 'User' ,( int ) $user_serial );
$activity = $this -> getLoginActivity ( $user_serial );
2026-10-18 17:01:36 +00:00
$XML = $this -> mergeXML ( $user , "<XML/>" );
2026-10-20 06:45:30 -04:00
$XML = $this -> mergeXML ( $activity , $XML );
2026-10-18 17:01:36 +00:00
$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/>" );
$XML = $this -> mergeXML ( $constants , $XML );
$content = $this -> applyXSL ( $XML , $this -> getXSL ( "addUser" ));
$this -> displayContent ( $content );
break ;
case "add" :
2026-10-20 06:45:30 -04:00
echo json_encode (( new AdminAccounts ()) -> create ( $_POST ));
2026-10-18 17:01:36 +00:00
break ;
default :
$this -> displayHome ();
}
}
// ===========
// Delete User
// ===========
public function deleteUser () {
2026-10-20 06:45:30 -04:00
( new MaintenanceService ()) -> deleteUser ( MaintenanceService :: id ( $_POST [ 'user_serial' ] ?? null ));
2026-10-18 17:01:36 +00:00
}
// =========
// 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/>" );
$XML = $this -> mergeXML ( $popovers , $XML );
$XML = $this -> mergeXML ( $constants , $XML );
$content = $this -> applyXSL ( $XML , $this -> getXSL ( "editUser" ));
$this -> displayContent ( $content );
break ;
case "update" :
2026-10-20 06:45:30 -04:00
( new MaintenanceService ()) -> updateUser ( MaintenanceService :: id ( $_POST [ 'user_serial' ] ?? null ), $_POST );
2026-10-18 17:01:36 +00:00
break ;
default :
$this -> displayHome ();
}
}
// ====================
// Return a User Object
// ====================
public function getUser ( $user_serial = 0 ) {
2026-10-18 19:27:17 -04:00
$user_serial = ( int ) $user_serial ;
2026-10-18 17:01:36 +00:00
$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 ) {
2026-10-18 19:27:17 -04:00
$stmt = $this -> connect () -> prepare ( 'SELECT * FROM view_users WHERE user_name = :name' );
$stmt -> execute ([ ':name' => $user_name ]);
return $this -> getTableXML ( 'users' , $stmt );
2026-10-18 17:01:36 +00:00
}
// =====================
// Return a Users Serial
// =====================
public function getUserSerial ( $user_name = "" ) {
2026-10-20 06:45:30 -04:00
$users = $this -> getUserByUserName ( $user_name );
2026-10-18 17:01:36 +00:00
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 () {
2026-10-20 06:45:30 -04:00
$this -> pagination_size = ( int )( $_POST [ 'users_per_page' ] ?? 20 );
2026-10-18 17:01:36 +00:00
// Find Criteria
$find_user = filter_input ( INPUT_POST , "find_user" ) ?: "" ;
2026-10-20 06:45:30 -04:00
$find_user = trim ( $find_user );
2026-10-18 17:01:36 +00:00
2026-10-20 06:45:30 -04:00
$find_words = implode ( "|" , array_map ( static fn ( $word ) => preg_quote ( $word ), explode ( " " , $find_user )));
2026-10-18 17:01:36 +00:00
// Record Count
$connection = $this -> connect ();
$SQL = "select count(*) as count
from view_users
where user_name regexp :user_name
2026-10-18 19:27:17 -04:00
or user_first_name regexp :user_first_name
2026-10-20 06:45:30 -04:00
or user_last_name regexp :user_last_name
2026-10-18 17:01:36 +00:00
or user_email regexp :user_email
or user_role regexp :user_role" ;
$statement = $connection -> prepare ( $SQL );
$statement -> bindValue ( ":user_name" , $find_words );
2026-10-18 19:27:17 -04:00
$statement -> bindValue ( ":user_first_name" , $find_words );
2026-10-20 06:45:30 -04:00
$statement -> bindValue ( ":user_last_name" , $find_words );
2026-10-18 17:01:36 +00:00
$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" ) ?: "" ;
2026-10-20 06:45:30 -04:00
$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' ];
2026-10-18 17:01:36 +00:00
// Data
$SQL = "select view_users.*
from view_users
where user_name regexp :user_name
2026-10-18 19:27:17 -04:00
or user_first_name regexp :user_first_name
2026-10-20 06:45:30 -04:00
or user_last_name regexp :user_last_name
2026-10-18 17:01:36 +00:00
or user_email regexp :user_email
or user_role regexp :user_role
2026-10-20 06:45:30 -04:00
order by user_serial
2026-10-18 17:01:36 +00:00
limit { $this -> pagination_size }
offset { $offset } " ;
$statement = $connection -> prepare ( $SQL );
$statement -> bindValue ( ":user_name" , $find_words );
2026-10-18 19:27:17 -04:00
$statement -> bindValue ( ":user_first_name" , $find_words );
2026-10-20 06:45:30 -04:00
$statement -> bindValue ( ":user_last_name" , $find_words );
2026-10-18 17:01:36 +00:00
$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 () {
2026-10-20 06:45:30 -04:00
$id = MaintenanceService :: id ( $_POST [ 'user_serial' ] ?? null );
$used = ( new MaintenanceService ()) -> userUsed ( $id );
echo json_encode ( $used || $id === ( int )( $_SESSION [ self :: USER_SERIAL ] ?? 0 ));
2026-10-18 17:01:36 +00:00
}
// ===============
// Change Password
// ===============
public function changePassword () {
2026-10-18 19:27:17 -04:00
$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.' );
2026-10-18 17:01:36 +00:00
}
2026-10-18 19:27:17 -04:00
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.' );
2026-10-18 17:01:36 +00:00
}
2026-10-18 19:27:17 -04:00
$this -> logoutSession ();
header ( 'Location: main.php' , true , 303 );
exit ;
} catch ( InvalidArgumentException $e ) { $params [ 'PASSWORD_ERROR' ] = $e -> getMessage (); }
2026-10-18 17:01:36 +00:00
}
2026-10-18 19:27:17 -04:00
$this -> displayContent ( $this -> applyXSL ( '<XML/>' , $this -> getXSL ( 'changePassword' ), $params ));
2026-10-18 17:01:36 +00:00
}
public function resetPassword () {
2026-10-18 19:27:17 -04:00
$this -> requireCsrf ();
2026-10-20 06:45:30 -04:00
$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.' ]);
2026-10-18 17:01:36 +00:00
}
// ===============================================
// Update the Login timestamp for the current User
// ===============================================
public function updateLogin () {
$SQL = "update users
2026-10-18 19:27:17 -04:00
set user_last_login = :user_login
2026-10-18 17:01:36 +00:00
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 () {
2026-10-20 06:45:30 -04:00
( new MaintenanceService ()) -> setUserActive ( MaintenanceService :: id ( $_POST [ 'user_serial' ] ?? null ), MaintenanceService :: boolean ( $_POST [ 'user_active' ] ?? null ));
2026-10-18 17:01:36 +00:00
}
// ====================================
// Return a Users Login Activity Object
// ====================================
public function getLoginActivity ( $userserial ) {
2026-10-20 06:45:30 -04:00
$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 );
2026-10-18 17:01:36 +00:00
}
}