Axis--UserFactory.php
Go to the documentation of this file.
00001 <?PHP 00002 00003 # 00004 # Axis--UserFactory.php 00005 # An Meta-Object for Handling User Information 00006 # 00007 # Copyright 2003 Axis Data 00008 # This code is free software that can be used or redistributed under the 00009 # terms of Version 2 of the GNU General Public License, as published by the 00010 # Free Software Foundation (http://www.fsf.org). 00011 # 00012 # Author: Edward Almasy (almasy@axisdata.com) 00013 # 00014 # Part of the AxisPHP library v1.2.4 00015 # For more information see http://www.axisdata.com/AxisPHP/ 00016 # 00017 00018 class UserFactory { 00019 00020 # ---- PUBLIC INTERFACE -------------------------------------------------- 00021 00022 # object constructor 00023 function UserFactory($SessionOrDb) 00024 { 00025 # if a session was passed in 00026 if (is_object($SessionOrDb) && method_exists($SessionOrDb, "Session")) 00027 { 00028 # swipe database handle from session 00029 $this->DB = $SessionOrDb->DB; 00030 00031 # save session 00032 $this->Session = $SessionOrDb; 00033 } 00034 # else if database handle was passed in 00035 elseif (is_object($SessionOrDb) && method_exists($SessionOrDb, "Database")) 00036 { 00037 # save database handle 00038 $this->DB = $SessionOrDb; 00039 00040 # create session 00041 $this->Session = new Session($this->DB); 00042 } 00043 else 00044 { 00045 # error out 00046 $this->Result = U_ERROR; 00047 exit(1); 00048 } 00049 } 00050 00063 function CreateNewUser( 00064 $UserName, $Password, $PasswordAgain, $EMail, $EMailAgain, 00065 $IgnoreErrorCodes = NULL) 00066 { 00067 # check incoming values 00068 $ErrorCodes = $this->TestNewUserValues( 00069 $UserName, $Password, $PasswordAgain, $EMail, $EMailAgain); 00070 00071 # discard any errors we are supposed to ignore 00072 if ($IgnoreErrorCodes) 00073 { 00074 $ErrorCodes = array_diff($ErrorCodes, $IgnoreErrorCodes); 00075 } 00076 00077 # if error found in incoming values return error codes to caller 00078 if (count($ErrorCodes)) { return $ErrorCodes; } 00079 00080 # add user to database 00081 $UserName = User::NormalizeUserName($UserName); 00082 $this->DB->Query("INSERT INTO APUsers" 00083 ." (UserName, CreationDate)" 00084 ." VALUES ('".addslashes($UserName)."', NOW())"); 00085 00086 # create new user object 00087 $UserId = $this->DB->LastInsertId("APUsers"); 00088 $User = new User($this->DB, (int)$UserId); 00089 00090 # if new user object creation failed return error code to caller 00091 if ($User->Status() != U_OKAY) { return array($User->Status()); } 00092 00093 # set password and e-mail address 00094 $User->SetPassword($Password); 00095 $User->Set("EMail", $EMail); 00096 00097 # return new user object to caller 00098 return $User; 00099 } 00100 00101 # test new user creation values (returns array of error codes) 00102 function TestNewUserValues( 00103 $UserName, $Password, $PasswordAgain, $EMail, $EMailAgain) 00104 { 00105 $ErrorCodes = array(); 00106 if (strlen(User::NormalizeUserName($UserName)) == 0) 00107 { $ErrorCodes[] = U_EMPTYUSERNAME; } 00108 elseif (!User::IsValidUserName($UserName)) 00109 { $ErrorCodes[] = U_ILLEGALUSERNAME; } 00110 elseif ($this->UserNameExists($UserName)) 00111 { $ErrorCodes[] = U_DUPLICATEUSERNAME; } 00112 00113 if ($this->EMailAddressIsInUse($EMail)) 00114 { $ErrorCodes[] = U_DUPLICATEEMAIL; } 00115 00116 $FoundOtherPasswordError = FALSE; 00117 if (strlen(User::NormalizePassword($Password)) == 0) 00118 { 00119 $ErrorCodes[] = U_EMPTYPASSWORD; 00120 $FoundOtherPasswordError = TRUE; 00121 } 00122 elseif (!User::IsValidPassword($Password)) 00123 { 00124 $ErrorCodes[] = U_ILLEGALPASSWORD; 00125 $FoundOtherPasswordError = TRUE; 00126 } 00127 00128 if (strlen(User::NormalizePassword($PasswordAgain)) == 0) 00129 { 00130 $ErrorCodes[] = U_EMPTYPASSWORDAGAIN; 00131 $FoundOtherPasswordError = TRUE; 00132 } 00133 elseif (!User::IsValidPassword($PasswordAgain)) 00134 { 00135 $ErrorCodes[] = U_ILLEGALPASSWORDAGAIN; 00136 $FoundOtherPasswordError = TRUE; 00137 } 00138 00139 if ($FoundOtherPasswordError == FALSE) 00140 { 00141 if (User::NormalizePassword($Password) 00142 != User::NormalizePassword($PasswordAgain)) 00143 { 00144 $ErrorCodes[] = U_PASSWORDSDONTMATCH; 00145 } 00146 } 00147 00148 $FoundOtherEMailError = FALSE; 00149 if (strlen(User::NormalizeEMailAddress($EMail)) == 0) 00150 { 00151 $ErrorCodes[] = U_EMPTYEMAIL; 00152 $FoundOtherEMailError = TRUE; 00153 } 00154 elseif (!User::IsValidLookingEMailAddress($EMail)) 00155 { 00156 $ErrorCodes[] = U_ILLEGALEMAIL; 00157 $FoundOtherEMailError = TRUE; 00158 } 00159 00160 if (strlen(User::NormalizeEMailAddress($EMailAgain)) == 0) 00161 { 00162 $ErrorCodes[] = U_EMPTYEMAILAGAIN; 00163 $FoundOtherEMailError = TRUE; 00164 } 00165 elseif (!User::IsValidLookingEMailAddress($EMailAgain)) 00166 { 00167 $ErrorCodes[] = U_ILLEGALEMAILAGAIN; 00168 $FoundOtherEMailError = TRUE; 00169 } 00170 00171 if ($FoundOtherEMailError == FALSE) 00172 { 00173 if (User::NormalizeEMailAddress($EMail) 00174 != User::NormalizeEMailAddress($EMailAgain)) 00175 { 00176 $ErrorCodes[] = U_EMAILSDONTMATCH; 00177 } 00178 } 00179 00180 return $ErrorCodes; 00181 } 00182 00188 function GetUserCount($Condition = NULL) 00189 { 00190 return $this->DB->Query("SELECT COUNT(*) AS UserCount FROM APUsers" 00191 .($Condition ? " WHERE ".$Condition : ""), "UserCount"); 00192 } 00193 00194 # return total number of user that matched last GetMatchingUsers call 00195 # before the return size was limited 00196 function GetMatchingUserCount() 00197 { 00198 return $this->MatchingUserCount; 00199 } 00200 00201 # return array of users currently logged in 00202 function GetLoggedInUsers() 00203 { 00204 # start with empty array (to prevent array errors) 00205 $ReturnValue = array(); 00206 00207 # load array of logged in user 00208 $UserIds = $this->Session->GetFromAllSessions("APUserId"); 00209 00210 # for each logged in user 00211 foreach ($UserIds as $UserId) 00212 { 00213 # load all data values for user 00214 $this->DB->Query("SELECT * FROM APUsers WHERE UserId = '".$UserId."'"); 00215 $ReturnValue[$UserId] = $this->DB->FetchRow(); 00216 } 00217 00218 # return array of user data to caller 00219 return $ReturnValue; 00220 } 00221 00222 # return array of users recently logged in. returns 10 users by default 00223 function GetRecentlyLoggedInUsers($Since = NULL, $Limit = 10) 00224 { 00225 # start with empty array (to prevent array errors) 00226 $ReturnValue = array(); 00227 00228 # get users recently logged in during the last 24 hours if no date given 00229 if (is_null($Since)) 00230 { 00231 $Date = date("Y-m-d H:i:s", time()-86400); 00232 } 00233 00234 else 00235 { 00236 $Date = date("Y-m-d H:i:s", strtotime($Since)); 00237 } 00238 00239 # query for the users who were logged in since the given date 00240 $this->DB->Query(" 00241 SELECT U.* 00242 FROM APUsers U 00243 LEFT JOIN 00244 -- the dummy table is an optimization. see the comment by Vimal 00245 -- Gupta in the MySQL docs: 00246 -- http://dev.mysql.com/doc/refman/5.0/en/in-subquery-optimization.html 00247 (SELECT DataValue 00248 FROM APSessionData 00249 WHERE DataName = 'APUserId' 00250 -- allows fetching distinct DataValue values but with GROUP BY 00251 -- optimizations 00252 GROUP BY DataValue) as Dummy 00253 -- using this convoluted method because DataValue is an integer 00254 -- (UserId) serialized by PHP to a string 00255 ON CONCAT('s:', CHAR_LENGTH(CAST(U.UserId AS CHAR)), 00256 ':\"', U.UserId,'\";') = Dummy.DataValue 00257 WHERE U.LastActiveDate >= '".$Date."' 00258 AND Dummy.DataValue IS NULL 00259 ORDER BY U.LastActiveDate DESC 00260 LIMIT ".intval($Limit)); 00261 00262 while (FALSE !== ($Row = $this->DB->FetchRow())) 00263 { 00264 $ReturnValue[$Row["UserId"]] = $Row; 00265 } 00266 00267 # return array of user data to caller 00268 return $ReturnValue; 00269 } 00270 00271 # return array of user names who have the specified privileges 00272 # (array index is user IDs) 00273 function GetUsersWithPrivileges() 00274 { 00275 # start with query string that will return all users 00276 $QueryString = "SELECT DISTINCT APUsers.UserId, UserName FROM APUsers, APUserPrivileges"; 00277 00278 # for each specified privilege 00279 $Args = func_get_args(); 00280 foreach ($Args as $Index => $Arg) 00281 { 00282 # add condition to query string 00283 $QueryString .= ($Index == 0) ? " WHERE (" : " OR"; 00284 $QueryString .= " APUserPrivileges.Privilege = ".$Arg; 00285 } 00286 00287 # close privilege condition in query string and add user ID condition 00288 $QueryString.= count($Args) ? ") AND APUsers.UserId = APUserPrivileges.UserId" : ""; 00289 00290 # perform query 00291 $this->DB->Query($QueryString); 00292 00293 # copy query result into user info array 00294 $Users = $this->DB->FetchColumn("UserName", "UserId"); 00295 00296 # return array of users to caller 00297 return $Users; 00298 } 00299 00300 # return array of user objects who have values matching search string 00301 # (array indexes are user IDs) 00302 function FindUsers($SearchString, $FieldName = "UserName", 00303 $SortFieldName = "UserName", $Offset = 0, $Count = 9999999) 00304 { 00305 # retrieve matching user IDs 00306 $UserNames = $this->FindUserNames( 00307 $SearchString, $FieldName, $SortFieldName, $Offset, $Count); 00308 00309 # create user objects 00310 $Users = array(); 00311 foreach ($UserNames as $UserId => $UserName) 00312 { 00313 $Users[$UserId] = new User($this->DB, intval($UserId)); 00314 } 00315 00316 # return array of user objects to caller 00317 return $Users; 00318 } 00319 00320 # return array of user names/IDs who have values matching search string 00321 # (array indexes are user IDs, array values are user names) 00322 function FindUserNames($SearchString, $FieldName = "UserName", 00323 $SortFieldName = "UserName", $Offset = 0, $Count = 9999999) 00324 { 00325 # Construct a database query: 00326 $QueryString = "SELECT UserId, UserName FROM APUsers WHERE"; 00327 00328 # If the search string is a valid username which is shorter than the 00329 # minimum word length indexed by the FTS, just do a normal 00330 # equality test instead of using the index. 00331 # Otherwise, FTS away. 00332 $MinWordLen = $this->DB->Query( 00333 "SHOW VARIABLES WHERE variable_name='ft_min_word_len'", "Value"); 00334 if (User::IsValidUserName($SearchString) && 00335 strlen($SearchString) < $MinWordLen ) 00336 { 00337 $QueryString .= " UserName='".addslashes($SearchString)."'"; 00338 } 00339 else 00340 { 00341 # massage search string to use AND logic 00342 $Words = preg_split("/[\s]+/", trim($SearchString)); 00343 $NewSearchString = ""; 00344 $InQuotedString = FALSE; 00345 foreach ($Words as $Word) 00346 { 00347 if ($InQuotedString == FALSE) { $NewSearchString .= "+"; } 00348 if (preg_match("/^\"/", $Word)) { $InQuotedString = TRUE; } 00349 if (preg_match("/\"$/", $Word)) { $InQuotedString = FALSE; } 00350 $NewSearchString .= $Word." "; 00351 } 00352 $QueryString .= " MATCH (".$FieldName.")" 00353 ." AGAINST ('".addslashes(trim($NewSearchString))."'" 00354 ." IN BOOLEAN MODE)"; 00355 } 00356 $QueryString .= " ORDER BY ".$SortFieldName 00357 ." LIMIT ".$Offset.", ".$Count; 00358 00359 # retrieve matching user IDs 00360 $this->DB->Query($QueryString); 00361 $UserNames = $this->DB->FetchColumn("UserName", "UserId"); 00362 00363 # return names/IDs to caller 00364 return $UserNames; 00365 } 00366 00367 # return array of users who have values matching search string (in specific field if requested) 00368 # (search string respects POSIX-compatible regular expressions) 00369 # optimization: $SearchString = ".*." and $FieldName = NULL will return all 00370 # users ordered by $SortFieldName 00371 function GetMatchingUsers($SearchString, $FieldName = NULL, 00372 $SortFieldName = "UserName", 00373 $ResultsStartAt = 0, $ReturnNumber = NULL) 00374 { 00375 # start with empty array (to prevent array errors) 00376 $ReturnValue = array(); 00377 00378 # if empty search string supplied, return nothing 00379 $TrimmedSearchString = trim($SearchString); 00380 if (empty($TrimmedSearchString)) 00381 { 00382 return $ReturnValue; 00383 } 00384 00385 # make sure ordering is done by user name if not specified 00386 $SortFieldName = empty($SortFieldName) ? "UserName" : $SortFieldName; 00387 00388 # begin constructing the query 00389 $Query = "SELECT * FROM APUsers"; 00390 $QueryOrderBy = " ORDER BY $SortFieldName"; 00391 $QueryLimit = empty($ReturnNumber) ? "" : " LIMIT $ResultsStartAt, $ReturnNumber"; 00392 00393 # the Criteria Query will be used to get the total number of results without the 00394 # limit clause 00395 $CriteriaQuery = $Query; 00396 00397 # if specific field comparison requested 00398 if (!empty($FieldName)) 00399 { 00400 # append queries with criteria 00401 $Query .= " WHERE ".$FieldName." REGEXP '".addslashes($SearchString)."'"; 00402 $CriteriaQuery = $Query; 00403 } 00404 00405 # optimize for returning all users 00406 else if ($SearchString == ".*.") 00407 { 00408 # set field name to username - this would be the first field 00409 # returned by a field to field search using the above RegExp 00410 $FieldName = "UserName"; 00411 } 00412 00413 # add order by and limit to query for optimizing 00414 $Query .= $QueryOrderBy.$QueryLimit; 00415 00416 # execute query... 00417 $this->DB->Query($Query); 00418 00419 # ...and process query return 00420 while ($Record = $this->DB->FetchRow()) 00421 { 00422 # if specific field or all users requested 00423 if (!empty($FieldName)) 00424 { 00425 # add user to return array 00426 $ReturnValue[$Record["UserId"]] = $Record; 00427 00428 # add matching search field to return array 00429 $ReturnValue[$Record["UserId"]]["APMatchingField"] = $FieldName; 00430 } 00431 00432 else 00433 { 00434 # for each user data field 00435 foreach ($Record as $FName => $FValue) 00436 { 00437 # if search string appears in data field 00438 if (strpos($Record[$FName], $SearchString) !== FALSE) 00439 { 00440 # add user to return array 00441 $ReturnValue[$Record["UserId"]] = $Record; 00442 00443 # add matching search field to return array 00444 $ReturnValue[$Record["UserId"]]["APMatchingField"] = $FName; 00445 } 00446 } 00447 } 00448 } 00449 00450 # add matching user count 00451 $this->DB->Query($CriteriaQuery); 00452 $this->MatchingUserCount = $this->DB->NumRowsSelected(); 00453 00454 # return array of matching users to caller 00455 return $ReturnValue; 00456 } 00457 00458 # check whether user name currently exists 00459 function UserNameExists($UserName) 00460 { 00461 # normalize user name 00462 $UserName = User::NormalizeUserName($UserName); 00463 00464 # check whether user name is already in use 00465 $NameCount = $this->DB->Query( 00466 "SELECT COUNT(*) AS NameCount FROM APUsers" 00467 ." WHERE UserName = '".addslashes($UserName)."'", 00468 "NameCount"); 00469 00470 # report to caller whether name exists 00471 return ($NameCount > 0); 00472 } 00473 00474 # check whether e-mail address currently has account associated with it 00475 function EMailAddressIsInUse($Address) 00476 { 00477 # normalize address 00478 $UserName = User::NormalizeEMailAddress($Address); 00479 00480 # check whether address is already in use 00481 $AddressCount = $this->DB->Query( 00482 "SELECT COUNT(*) AS AddressCount FROM APUsers" 00483 ." WHERE EMail = '".addslashes($Address)."'", 00484 "AddressCount"); 00485 00486 # report to caller whether address is in use 00487 return ($AddressCount > 0); 00488 } 00489 00495 public function GetNewestUsers($Limit = 5) 00496 { 00497 # assume no users will be found 00498 $Users = array(); 00499 00500 # fetch the newest users 00501 $this->DB->Query("SELECT *" 00502 ." FROM APUsers" 00503 ." ORDER BY CreationDate DESC" 00504 ." LIMIT ".intval($Limit)); 00505 $UserIds = $this->DB->FetchColumn("UserId"); 00506 00507 # for each user id found 00508 foreach ($UserIds as $UserId) 00509 { 00510 $Users[$UserId] = new SPTUser($UserId); 00511 } 00512 00513 # return the newest users 00514 return $Users; 00515 } 00516 00517 # ---- PRIVATE INTERFACE ------------------------------------------------- 00518 00519 var $DB; 00520 var $Session; 00521 var $SortFieldName; 00522 var $MatchingUserCount; 00523 00524 # callback function for sorting users 00525 function CompareUsersForSort($UserA, $UserB) 00526 { 00527 return strcasecmp($UserA[$this->SortFieldName], $UserB[$this->SortFieldName]); 00528 } 00529 00530 }