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 search string supplied 00379 if (strlen(trim($SearchString)) > 0) 00380 { 00381 # build the sorting clause 00382 $QueryOrderBy = " ORDER BY $SortFieldName"; 00383 00384 if ($ReturnNumber) 00385 { 00386 $QueryLimit = " LIMIT "; 00387 if ($ResultsStartAt) 00388 { 00389 $QueryLimit .= "$ResultsStartAt, $ReturnNumber"; 00390 } 00391 else 00392 $QueryLimit .= $ReturnNumber; 00393 } 00394 else 00395 { 00396 $QueryLimit = ""; 00397 } 00398 00399 $Query = "SELECT * FROM APUsers"; 00400 00401 # the Criteria Query will be used to get the total number 00402 # of results without the limit clause 00403 $CriteriaQuery = $Query; 00404 00405 00406 # if specific field comparison requested 00407 if ($FieldName != NULL) 00408 { 00409 # append queries with criteria 00410 $Query .= " WHERE ".$FieldName." REGEXP '".addslashes($SearchString)."'"; 00411 00412 $CriteriaQuery = $Query; 00413 00414 # tack on ordering and limiting 00415 $Query .= $QueryOrderBy.$QueryLimit; 00416 00417 } 00418 # optimize for returning all users 00419 elseif (strcasecmp($SearchString, ".*.") == 0) 00420 { 00421 $Query .= $QueryOrderBy.$QueryLimit; 00422 00423 # set field name to username - this would be the first field 00424 # returned by a field to field search using the above RegExp 00425 $FieldName = "UserName"; 00426 } 00427 else 00428 { 00429 # search all fields - can't limit here, but we can order by 00430 $Query .= $QueryOrderBy; 00431 } 00432 00433 # execute query 00434 $this->DB->Query($Query); 00435 00436 # process query return 00437 while ($Record = $this->DB->FetchRow()) 00438 { 00439 00440 # if specific field or all users requested 00441 if ($FieldName != NULL) 00442 { 00443 # add user to return array 00444 $ReturnValue[$Record["UserId"]] = $Record; 00445 00446 # add matching search field to return array 00447 $ReturnValue[$Record["UserId"]]["APMatchingField"] = $FieldName; 00448 } 00449 else 00450 { 00451 # for each user data field 00452 foreach ($Record as $FName => $FValue) 00453 { 00454 # if search string appears in data field 00455 if (ereg($SearchString, $Record[$FName])) 00456 { 00457 # add user to return array 00458 $ReturnValue[$Record["UserId"]] = $Record; 00459 00460 # add matching search field to return array 00461 $ReturnValue[$Record["UserId"]]["APMatchingField"] = $FName; 00462 00463 # move on to next user 00464 continue; 00465 } 00466 } 00467 00468 # cut return array down to requested size 00469 if (isset($ReturnNumber)) 00470 { 00471 $ReturnValue = array_slice($ReturnValue, $ResultsStartAt, $ReturnNumber, true); 00472 } 00473 } 00474 } 00475 00476 if (!isset($this->MatchingUserCount)) 00477 { 00478 $this->DB->Query($CriteriaQuery); 00479 $this->MatchingUserCount = $this->DB->NumRowsSelected(); 00480 } 00481 00482 } 00483 00484 # return array of matching users to caller 00485 return $ReturnValue; 00486 } 00487 00488 # check whether user name currently exists 00489 function UserNameExists($UserName) 00490 { 00491 # normalize user name 00492 $UserName = User::NormalizeUserName($UserName); 00493 00494 # check whether user name is already in use 00495 $NameCount = $this->DB->Query( 00496 "SELECT COUNT(*) AS NameCount FROM APUsers" 00497 ." WHERE UserName = '".addslashes($UserName)."'", 00498 "NameCount"); 00499 00500 # report to caller whether name exists 00501 return ($NameCount > 0); 00502 } 00503 00504 # check whether e-mail address currently has account associated with it 00505 function EMailAddressIsInUse($Address) 00506 { 00507 # normalize address 00508 $UserName = User::NormalizeEMailAddress($Address); 00509 00510 # check whether address is already in use 00511 $AddressCount = $this->DB->Query( 00512 "SELECT COUNT(*) AS AddressCount FROM APUsers" 00513 ." WHERE EMail = '".addslashes($Address)."'", 00514 "AddressCount"); 00515 00516 # report to caller whether address is in use 00517 return ($AddressCount > 0); 00518 } 00519 00525 public function GetNewestUsers($Limit = 5) 00526 { 00527 # assume no users will be found 00528 $Users = array(); 00529 00530 # fetch the newest users 00531 $this->DB->Query("SELECT *" 00532 ." FROM APUsers" 00533 ." ORDER BY CreationDate DESC" 00534 ." LIMIT ".intval($Limit)); 00535 $UserIds = $this->DB->FetchColumn("UserId"); 00536 00537 # for each user id found 00538 foreach ($UserIds as $UserId) 00539 { 00540 $Users[$UserId] = new SPTUser($UserId); 00541 } 00542 00543 # return the newest users 00544 return $Users; 00545 } 00546 00547 # ---- PRIVATE INTERFACE ------------------------------------------------- 00548 00549 var $DB; 00550 var $Session; 00551 var $SortFieldName; 00552 var $MatchingUserCount; 00553 00554 # callback function for sorting users 00555 function CompareUsersForSort($UserA, $UserB) 00556 { 00557 return strcasecmp($UserA[$this->SortFieldName], $UserB[$this->SortFieldName]); 00558 } 00559 }; 00560 00561 00562 ?>