Subversion Repositories php_users

Rev

Rev 7 | Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
4 rodolico 1
<?php
2
 
3
class Users {
4
 
5
 
6
   private $dbDefinition = array(
7
      /*
8
       * what to use for html input fields
9
       * These are passed to sprintf, with label, fieldname, title, placeholder and current value, in that order
10
       */
11
      'screens'         => array (
12
         'login form' => "<h1>Login</h1>\n<form class='login_form' action='%s' method='post'>\n%s\n<input type='submit' value='Login'></form>\n",
13
         'edit form'  => "<form class='login_form' action='%s' method='post'>\n%s\n<input type='submit' id='btnUpdate' name='btnUpdate' value='Update'>\n</form>",
14
         '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>",
15
         'adminScreen' => "<input type='hidden' name='doAdmin' value='1'>\n",
16
         'validateScript' => '',
17
         ),
18
      'html input fields' => array(
19
            '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>",
20
            '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>",
21
            '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>",
22
            '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>",
23
         ),
24
      'input prefix' => 'admin_', // prefix the name with this in a form
25
      'tables'    => array(
26
         'users'  => array(
27
            'table'     => '_users',   // table name for user records
28
            'id'        => '_user_id', // ID column name
29
            'display'   => array(      // fields which are displayed to select
30
               'login'
31
               ),         
32
            'form test' => 'login',    // field to test if form submitted
33
            'fields' => array(
34
               'login'  => array(
35
                     'label'        => 'Username',       // login name column name
36
                     'html type'    => 'text',
37
                     'filter'       => '/[a-zA-Z0-9_]/',
38
                     'instructions' => 'Username can only contain alpha numerics and an underscore',
39
                     'hint'         => 'Change User Name'
40
                     ),
41
               'pass'   => array( 
42
                     'label'        => 'Password',    // password column name
43
                     'html type'    => 'password',
44
                     'instructions' => 'Leave blank to keep same password',
45
                     'hint'         => 'Change Password'
46
                     ),
47
               'admin'  => array(
48
                     'label'        => 'isAdmin',
49
                     'html type'    => 'boolean',
50
                     'restrict'     => true,
51
                     'instructions' => 'If checked, user will be able to add/edit users',
52
                     ),
53
               'enabled' => array(
54
                     'label'        => 'Enabled',
55
                     'html type'    => 'boolean',
56
                     'restrict'     => true,
57
                     'instructions' => 'Uncheck to disable log in',
58
                     ),
59
               ) // fields
60
            ) // table users
61
         ) // tables
62
      );
63
 
64
 
65
   private $data = array(
66
      );
67
 
68
//   private $workingOn = array();
69
 
70
 
71
   public function __construct( $customFields = array() ) {
72
      if ( $customFields ) {
73
         $this->dbDefinition = array_merge_recursive( $this->dbDefinition, $customFields );
74
      }
75
   } // constructor
76
 
77
   public function isAdmin() {
78
      return $this->data['admin'];
79
   }
80
 
81
   public function name() {
82
      return isset( $this->data['login'] ) ? $this->data['login'] : null;
83
   }
84
 
85
   public function HTML( $connection, $nextScript = null ) {
86
      if ( isset( $_REQUEST['username'], $_REQUEST['password'] ) ) {
87
         $this->validate( $_REQUEST['username'], $_REQUEST['password'], $connection );
88
      }
89
      if ( isset( $_REQUEST['logout'] ) && $_REQUEST['logout'] == 'Logout' ) {
90
         $this->logOut();
91
      }
92
      if ( ! isset( $this->data['login'], $this->data['id'] ) ) {
93
         return $this->logInScreen();
94
      }
95
   }
96
 
97
   public function validate( $username, $password, $connection ) {
98
      $result = $connection->getPassword( $username );
99
      if ( password_verify( $password, $result['pass'] ) ) {
100
         $result = $connection->getRecord( $username );
101
         $this->data['id'] = $result['id'];
102
         foreach ( $this->dbDefinition['tables']['users']['fields'] as $key => $record ) {
103
            if ( $key != 'pass' )
104
               $this->data[$key] = $result[$key];
105
         }
106
      } else {
107
         $this->errors[] = 'Login Failed';
108
         foreach ( $this->dbDefinition['tables']['users']['fields'] as $key => $record ) {
109
            $this->data[$key] = null;
110
         }
111
      }
112
   } // validate
113
 
114
 
115
   public function allUsersHTML ( $connection, $nextPage = null ) {
116
      $nextPage = self::getNextScript( $nextPage );
117
      $return = '';
118
      $allUsers = $connection->getAllUsers();
119
      foreach ( $allUsers as $row ) {
120
         if ( $row['id'] == $this->data['id'] ) // don't do ourselves
121
            continue;
122
         $return .= sprintf( "<li><a href='$nextPage?doAdmin=1&id=%s'>%s</a></li>\n", $row['id'], $row['login'] );
123
      }
124
      $return .= sprintf( "<li><a href='$nextPage?doAdmin=1&id=%s'>%s</a></li>\n", -1, 'Add New User' );
125
      // wrap in ul, then put a div around it
126
      $return = "<ul class='login_list'>\n$return\n</ul>\n";
127
      $return = "<div class='login_list'>\n$return\n</div>\n";
128
      return $return;
129
   }
130
 
131
   public function logOut( $nextScript = null ) {
132
      $nextScript = $this->getNextScript( $nextScript );
133
      $_SESSION['user'] = null;
134
      session_destroy();
135
      header( "Location: $nextScript" );
136
   }
137
 
138
   private function getNextScript( $nextScript = null ) {
139
      if ( ! isset( $nextScript ) ) {
140
         $nextScript = $this->dbDefinition['screens']['validateScript'] ?:
141
                           htmlentities($_SERVER["PHP_SELF"]);
142
      }
143
      return $nextScript;
144
   }
145
 
146
   public function logInScreen( $nextScript = null ) {
147
      return sprintf( 
148
         $this->dbDefinition['screens']['login form'],
149
         $this->getNextScript( $nextScript ),
150
         $this->dbDefinition['screens']['loginScreen']
151
      );
152
   }
153
 
154
   private function makeHTMLField ( $field, $record, $value ) {
155
      $return = array();
156
      $temp = sprintf( $this->dbDefinition['html input fields'][$record['html type']], 
157
                        $record['label'] ?: $field,
158
                        $this->dbDefinition['input prefix'] . $field, 
159
                        !empty($record['instructions']) ? $record['instructions'] : '',
160
                        !empty($record['hint']) ? $record['hint'] : '',
161
                        $field
162
                     );
163
 
164
      switch ($record['html type'] ) {
165
         case 'text':
166
         case 'textarea':
167
                        $temp = preg_replace( "/~~$field~~/", isset( $value ) ? $value : '', $temp );
168
                        break;
169
         case 'password' :
170
                        break;
171
         case 'boolean' :  // boolean is set by checkboxes
172
                        $temp = preg_replace( "/~~$field~~/", $value ? 'checked' : '', $temp );
173
                        break;
174
      } // case
175
      return $temp;
176
 
177
   } // makeHTMLField
178
 
179
 
180
   public function editScreen() {
181
      $return = array();
182
      $return[] = $this->dbDefinition['screens']['adminScreen'];
183
      $return[] = "<input type='hidden' name='id' value=" . $this->workingOn['id'] . "'>\n";
184
      foreach ( $this->dbDefinition['tables']['users']['fields'] as $field => $record ) {
185
         // if this field is restricted and we are not admin, just skip it
186
         // also skip if it is our record
187
         if ( isset( $record['restrict'] ) && ( $this->data['id'] == $this->workingOn['id'] ) )
188
            continue;
189
         // now process the field
190
         $return[] = $this->makeHTMLField( $field, $record, $this->workingOn[$field] ?? '' );
191
      }
192
      return implode( "\n", $return );
193
   } // editScreen
194
 
195
   public function adminScreen( $connection ) {
196
      return $this->allUsersHTML( $connection );
197
   }
198
 
199
   private function emptyWorkingOn() {
200
      $new = array();
201
      $new['id'] = -1;
202
      foreach ( $this->dbDefinition['tables']['users']['fields'] as $field => $record ) {
203
         if ( isset( $record['default'] ) ) {
204
            $new[$field] = $record['default'];
205
         } else {
206
            switch ($record['html type']) {
207
               case 'text'    :
208
               case 'blob'    :  $new[$field] = '';
209
                                 break;
210
               case 'boolean' :  $new[$field] = 1;
211
                                 break;
212
               case 'password':  $new[$field] = '';
213
                                 break;
214
            }
215
         } // else
216
      } // foreach
217
      return $new;
218
   }
219
 
220
   public function admin ( $connection, $nextScript = null ) {
221
      $nextScript = $this->getNextScript( $nextScript );
222
      if ( ! isset($_REQUEST['id']) ) {
223
         // we're working on ourself
224
         $this->workingOn = $this->data;
225
      } elseif ( isset($_REQUEST['id'] ) && $this->workingOn['id'] != $_REQUEST['id'] ) {
226
         // we're working on a different user
227
         if ( $_REQUEST['id'] == -1 ) { // we are adding a new user
228
            $this->workingOn = $this->emptyWorkingOn();
229
         } else { // this is an existing user
230
            $this->workingOn = $connection->getARecord( array( 'id' => $_REQUEST['id'] ) );
231
         }
232
      }
233
      // default to working on ourself
234
      if ( ! ( isset( $this->workingOn ) && count( $this->workingOn ) ) ) {
235
         $this->workingOn = $this->data;
236
      }
237
      //print "<pre>thisworkingOn=\n" . print_r($this->workingOn, true ) . "</pre>";
238
      // we have no data, so we should create a form for them to enter something
239
      if ( ! isset( $_REQUEST[$this->dbDefinition['input prefix'] . $this->dbDefinition['tables']['users']['form test']] ) ) {
240
         // create the screen
241
         $return = $this->editScreen();
242
         if ( $this->data['admin'] ) {
243
            $return .= self::adminScreen( $connection );
244
         }
245
         return sprintf( $this->dbDefinition['screens']['edit form'],
246
            $nextScript,
247
            $return
248
            );
249
      } else { // we are processing
250
         $data = array();
251
         foreach ( $this->dbDefinition['tables']['users']['fields'] as $field => $record ) {
252
            // if this field is restricted it is our record, skip it
253
            if ( isset( $record['restrict'] ) && ( $this->data['id'] == $this->workingOn['id'] ) )
254
               continue;
255
            $htmlFieldName = $this->dbDefinition['input prefix'] . $field;
256
            $temp = '';
257
            switch ( $record['html type'] ) {
258
               case 'textarea':
259
               case 'text' :
260
                  if ( $this->workingOn['id'] == -1 || ( isset( $_REQUEST[$htmlFieldName] ) && $_REQUEST[$htmlFieldName] !== $this->workingOn[$field] ) ) {
261
                     $data[$field] = $_REQUEST[$htmlFieldName];
262
/*                     if ( isset( $record['filter'] ) && preg_filter( $record['filter'], '', $temp ) !== $temp ) {
263
                        $this->errors[] = "Invalid characters in $field";
264
                        $temp = '';
265
                     }
266
*/                  }
267
                  break;
268
               case 'password':
269
                  if ( ! empty( $_REQUEST[$htmlFieldName] ) )
270
                     $data[$field] = password_hash( $_REQUEST[$htmlFieldName], PASSWORD_DEFAULT );
271
                  break;
272
               case 'boolean' :
273
                  if ( $this->workingOn['id'] == -1 || ( isset( $_REQUEST[$htmlFieldName] ) != $this->workingOn[$field] ) ) {
274
                     $data[$field] = isset( $_REQUEST[$htmlFieldName] ) ? 1 : 0;
275
                  }
276
                  break;
277
            } // switch
278
         } // foreach
279
         if ( $data ) {
280
            $data['id'] = $this->workingOn['id'];
281
            $return = $connection->update( $data ) ? "Updated" : "Failed";
282
            if ( $this->workingOn['id'] == $this->data['id'] ) // we just updated us, reload record
283
               $this->data = $connection->getARecord( array( 'id' => $this->data['id'] ) );
284
         } else {
285
            $return = "No changes";
286
         }
287
         unset( $this->workingOn );
288
         return $return;
289
      } // else
290
   } // admin
291
 
292
   public function errors() {
293
      return $this->errors;
294
   }
295
} // class Users
296
 
297
?>