Rev 7 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed
<?php
class Users {
private $dbDefinition = array(
/*
* what to use for html input fields
* These are passed to sprintf, with label, fieldname, title, placeholder and current value, in that order
*/
'screens' => array (
'login form' => "<h1>Login</h1>\n<form class='login_form' action='%s' method='post'>\n%s\n<input type='submit' value='Login'></form>\n",
'edit form' => "<form class='login_form' action='%s' method='post'>\n%s\n<input type='submit' id='btnUpdate' name='btnUpdate' value='Update'>\n</form>",
'loginScreen' => "<div class='login_field'>\n<input class='login_field' type='text' name='username' placeholder='Username' required>\n</div>\n<div class='login_field'>\n<input class='login_field' type='password' name='password' placeholder='Password' required>\n</div>",
'adminScreen' => "<input type='hidden' name='doAdmin' value='1'>\n",
'validateScript' => '',
),
'html input fields' => array(
'text' => "<div class='login_field'>\n<label>%s\n<input class='login_field' type='text' name='%s' title='%s' placeholder='%s' value='~~%s~~'>\n</label>\n</div>",
'password' => "<div class='login_field'>\n<label>%s\n<input class='login_field' type='password' name='%s' title='%s' placeholder='%s'>\n</label>\n</div>",
'boolean' => "<div class='login_field'>\n<label>%s\n<input class='login_field' type='checkbox' name='%s' title='%s' value='1' %s ~~%s~~>\n</label>\n</div>",
'textarea' => "<div class='login_field'>\n<label>%s\n<textarea class='login_field' name='%s' title='%s' placeholder='%s'>~~%s~~</textarea>\n</label>\n</div>",
),
'input prefix' => 'admin_', // prefix the name with this in a form
'tables' => array(
'users' => array(
'table' => '_users', // table name for user records
'id' => '_user_id', // ID column name
'display' => array( // fields which are displayed to select
'login'
),
'form test' => 'login', // field to test if form submitted
'fields' => array(
'login' => array(
'label' => 'Username', // login name column name
'html type' => 'text',
'filter' => '/[a-zA-Z0-9_]/',
'instructions' => 'Username can only contain alpha numerics and an underscore',
'hint' => 'Change User Name'
),
'pass' => array(
'label' => 'Password', // password column name
'html type' => 'password',
'instructions' => 'Leave blank to keep same password',
'hint' => 'Change Password'
),
'admin' => array(
'label' => 'isAdmin',
'html type' => 'boolean',
'restrict' => true,
'instructions' => 'If checked, user will be able to add/edit users',
),
'enabled' => array(
'label' => 'Enabled',
'html type' => 'boolean',
'restrict' => true,
'instructions' => 'Uncheck to disable log in',
),
) // fields
) // table users
) // tables
);
private $data = array(
);
// private $workingOn = array();
public function __construct( $customFields = array() ) {
if ( $customFields ) {
$this->dbDefinition = array_merge_recursive( $this->dbDefinition, $customFields );
}
} // constructor
public function isAdmin() {
return $this->data['admin'];
}
public function name() {
return isset( $this->data['login'] ) ? $this->data['login'] : null;
}
public function HTML( $connection, $nextScript = null ) {
if ( isset( $_REQUEST['username'], $_REQUEST['password'] ) ) {
$this->validate( $_REQUEST['username'], $_REQUEST['password'], $connection );
}
if ( isset( $_REQUEST['logout'] ) && $_REQUEST['logout'] == 'Logout' ) {
$this->logOut();
}
if ( ! isset( $this->data['login'], $this->data['id'] ) ) {
return $this->logInScreen();
}
}
public function validate( $username, $password, $connection ) {
$result = $connection->getPassword( $username );
if ( password_verify( $password, $result['pass'] ) ) {
$result = $connection->getRecord( $username );
$this->data['id'] = $result['id'];
foreach ( $this->dbDefinition['tables']['users']['fields'] as $key => $record ) {
if ( $key != 'pass' )
$this->data[$key] = $result[$key];
}
} else {
$this->errors[] = 'Login Failed';
foreach ( $this->dbDefinition['tables']['users']['fields'] as $key => $record ) {
$this->data[$key] = null;
}
}
} // validate
public function allUsersHTML ( $connection, $nextPage = null ) {
$nextPage = self::getNextScript( $nextPage );
$return = '';
$allUsers = $connection->getAllUsers();
foreach ( $allUsers as $row ) {
if ( $row['id'] == $this->data['id'] ) // don't do ourselves
continue;
$return .= sprintf( "<li><a href='$nextPage?doAdmin=1&id=%s'>%s</a></li>\n", $row['id'], $row['login'] );
}
$return .= sprintf( "<li><a href='$nextPage?doAdmin=1&id=%s'>%s</a></li>\n", -1, 'Add New User' );
// wrap in ul, then put a div around it
$return = "<ul class='login_list'>\n$return\n</ul>\n";
$return = "<div class='login_list'>\n$return\n</div>\n";
return $return;
}
public function logOut( $nextScript = null ) {
$nextScript = $this->getNextScript( $nextScript );
$_SESSION['user'] = null;
session_destroy();
header( "Location: $nextScript" );
}
private function getNextScript( $nextScript = null ) {
if ( ! isset( $nextScript ) ) {
$nextScript = $this->dbDefinition['screens']['validateScript'] ?:
htmlentities($_SERVER["PHP_SELF"]);
}
return $nextScript;
}
public function logInScreen( $nextScript = null ) {
return sprintf(
$this->dbDefinition['screens']['login form'],
$this->getNextScript( $nextScript ),
$this->dbDefinition['screens']['loginScreen']
);
}
private function makeHTMLField ( $field, $record, $value ) {
$return = array();
$temp = sprintf( $this->dbDefinition['html input fields'][$record['html type']],
$record['label'] ?: $field,
$this->dbDefinition['input prefix'] . $field,
!empty($record['instructions']) ? $record['instructions'] : '',
!empty($record['hint']) ? $record['hint'] : '',
$field
);
switch ($record['html type'] ) {
case 'text':
case 'textarea':
$temp = preg_replace( "/~~$field~~/", isset( $value ) ? $value : '', $temp );
break;
case 'password' :
break;
case 'boolean' : // boolean is set by checkboxes
$temp = preg_replace( "/~~$field~~/", $value ? 'checked' : '', $temp );
break;
} // case
return $temp;
} // makeHTMLField
public function editScreen() {
$return = array();
$return[] = $this->dbDefinition['screens']['adminScreen'];
$return[] = "<input type='hidden' name='id' value=" . $this->workingOn['id'] . "'>\n";
foreach ( $this->dbDefinition['tables']['users']['fields'] as $field => $record ) {
// if this field is restricted and we are not admin, just skip it
// also skip if it is our record
if ( isset( $record['restrict'] ) && ( $this->data['id'] == $this->workingOn['id'] ) )
continue;
// now process the field
$return[] = $this->makeHTMLField( $field, $record, $this->workingOn[$field] ?? '' );
}
return implode( "\n", $return );
} // editScreen
public function adminScreen( $connection ) {
return $this->allUsersHTML( $connection );
}
private function emptyWorkingOn() {
$new = array();
$new['id'] = -1;
foreach ( $this->dbDefinition['tables']['users']['fields'] as $field => $record ) {
if ( isset( $record['default'] ) ) {
$new[$field] = $record['default'];
} else {
switch ($record['html type']) {
case 'text' :
case 'blob' : $new[$field] = '';
break;
case 'boolean' : $new[$field] = 1;
break;
case 'password': $new[$field] = '';
break;
}
} // else
} // foreach
return $new;
}
public function admin ( $connection, $nextScript = null ) {
$nextScript = $this->getNextScript( $nextScript );
if ( ! isset($_REQUEST['id']) ) {
// we're working on ourself
$this->workingOn = $this->data;
} elseif ( isset($_REQUEST['id'] ) && $this->workingOn['id'] != $_REQUEST['id'] ) {
// we're working on a different user
if ( $_REQUEST['id'] == -1 ) { // we are adding a new user
$this->workingOn = $this->emptyWorkingOn();
} else { // this is an existing user
$this->workingOn = $connection->getARecord( array( 'id' => $_REQUEST['id'] ) );
}
}
// default to working on ourself
if ( ! ( isset( $this->workingOn ) && count( $this->workingOn ) ) ) {
$this->workingOn = $this->data;
}
//print "<pre>thisworkingOn=\n" . print_r($this->workingOn, true ) . "</pre>";
// we have no data, so we should create a form for them to enter something
if ( ! isset( $_REQUEST[$this->dbDefinition['input prefix'] . $this->dbDefinition['tables']['users']['form test']] ) ) {
// create the screen
$return = $this->editScreen();
if ( $this->data['admin'] ) {
$return .= self::adminScreen( $connection );
}
return sprintf( $this->dbDefinition['screens']['edit form'],
$nextScript,
$return
);
} else { // we are processing
$data = array();
foreach ( $this->dbDefinition['tables']['users']['fields'] as $field => $record ) {
// if this field is restricted it is our record, skip it
if ( isset( $record['restrict'] ) && ( $this->data['id'] == $this->workingOn['id'] ) )
continue;
$htmlFieldName = $this->dbDefinition['input prefix'] . $field;
$temp = '';
switch ( $record['html type'] ) {
case 'textarea':
case 'text' :
if ( $this->workingOn['id'] == -1 || ( isset( $_REQUEST[$htmlFieldName] ) && $_REQUEST[$htmlFieldName] !== $this->workingOn[$field] ) ) {
$data[$field] = $_REQUEST[$htmlFieldName];
/* if ( isset( $record['filter'] ) && preg_filter( $record['filter'], '', $temp ) !== $temp ) {
$this->errors[] = "Invalid characters in $field";
$temp = '';
}
*/ }
break;
case 'password':
if ( ! empty( $_REQUEST[$htmlFieldName] ) )
$data[$field] = password_hash( $_REQUEST[$htmlFieldName], PASSWORD_DEFAULT );
break;
case 'boolean' :
if ( $this->workingOn['id'] == -1 || ( isset( $_REQUEST[$htmlFieldName] ) != $this->workingOn[$field] ) ) {
$data[$field] = isset( $_REQUEST[$htmlFieldName] ) ? 1 : 0;
}
break;
} // switch
} // foreach
if ( $data ) {
$data['id'] = $this->workingOn['id'];
$return = $connection->update( $data ) ? "Updated" : "Failed";
if ( $this->workingOn['id'] == $this->data['id'] ) // we just updated us, reload record
$this->data = $connection->getARecord( array( 'id' => $this->data['id'] ) );
} else {
$return = "No changes";
}
unset( $this->workingOn );
return $return;
} // else
} // admin
public function errors() {
return $this->errors;
}
} // class Users
?>