Subversion Repositories php_users

Rev

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

?>