3 * Container for user management class.
5 * Copyright 2003 Mark O'Sullivan
6 * This file is part of Lussumo's Software Library.
7 * Lussumo's Software Library is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.
8 * Lussumo's Software Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
9 * You should have received a copy of the GNU General Public License along with Vanilla; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
10 * The latest source code is available at www.lussumo.com
11 * Contact Mark O'Sullivan at mark [at] lussumo [dot] com
13 * @author Mark O'Sullivan
14 * @copyright 2003 Mark O'Sullivan
15 * @license http://lussumo.com/community/gpl.txt GPL 2
22 * Container for user management class.
25 class UserManager
extends Delegation
{
27 function AddBookmark($UserID, $DiscussionID) {
28 $s = $this->Context
->ObjectFactory
->NewContextObject($this->Context
, 'SqlBuilder');
29 $s->SetMainTable('UserBookmark', 'b');
30 $s->AddFieldNameValue('UserID', $UserID);
31 $s->AddFieldNameValue('DiscussionID', $DiscussionID);
32 $this->Context
->Database
->Insert($s, $this->Name
, 'AddBookmark', 'An error occurred while adding the bookmark.');
35 function AddCategoryBlock($CategoryID) {
36 $s = $this->Context
->ObjectFactory
->NewContextObject($this->Context
, 'SqlBuilder');
37 $s->SetMainTable('CategoryBlock', 'b');
38 $s->AddFieldNameValue('UserID', $this->Context
->Session
->UserID
);
39 $s->AddFieldNameValue('CategoryID', $CategoryID);
40 $s->AddFieldNameValue('Blocked', 1);
41 // Don't stress over errors (ie. duplicate entries) since this is indexed and duplicates cannot be inserted
42 if ($this->Context
->Database
->Insert($s, $this->Name
, 'AddCategoryBlock', 'Failed to add category block.', 0, 0)) {
44 $s->SetMainTable('User', 'u');
45 $s->AddFieldNameValue('UserBlocksCategories', '1');
46 $s->AddWhere('u', 'UserID', '', $this->Context
->Session
->UserID
, '=');
47 $this->Context
->Database
->Update($s, $this->Name
, 'AddCategoryBlock', 'Failed to update category block.', 0);
51 function AddCommentBlock($CommentID) {
52 $s = $this->Context
->ObjectFactory
->NewContextObject($this->Context
, 'SqlBuilder');
53 $s->SetMainTable('CommentBlock', 'b');
54 $s->AddFieldNameValue('BlockingUserID', $this->Context
->Session
->UserID
);
55 $s->AddFieldNameValue('BlockedCommentID', $CommentID);
56 $s->AddFieldNameValue('Blocked', 1);
57 // Don't stress over errors (ie. duplicate entries) since this is indexed and duplicates cannot be inserted
58 $this->Context
->Database
->Insert($s, $this->Name
, 'AddCommentBlock', 'Failed to add comment block.', 0, 0);
61 function AddUserBlock($UserID) {
62 $s = $this->Context
->ObjectFactory
->NewContextObject($this->Context
, 'SqlBuilder');
63 $s->SetMainTable('UserBlock', 'b');
64 $s->AddFieldNameValue('BlockingUserID', $this->Context
->Session
->UserID
);
65 $s->AddFieldNameValue('BlockedUserID', $UserID);
66 $s->AddFieldNameValue('Blocked', 1);
67 // Don't stress over errors (ie. duplicate entries) since this is indexed and duplicates cannot be inserted
68 $this->Context
->Database
->Insert($s, $this->Name
, 'AddCommentBlock', 'Failed to add user block.', 0, 0);
71 function AddUserIP($UserID, $IP='') {
72 if (!$this->Context
->Configuration
['LOG_ALL_IPS']) {
80 $UserID = ForceInt($UserID, 0);
81 $IP = FormatStringForDatabaseInput($IP);
84 $s = $this->Context
->ObjectFactory
->NewContextObject($this->Context
, 'SqlBuilder');
85 $s->SetMainTable('IpHistory', 'i');
86 $s->AddFieldNameValue('UserID', $UserID);
87 $s->AddFieldNameValue('RemoteIp', $IP);
88 $s->AddFieldNameValue('DateLogged', MysqlDateTime());
90 $this->Context
->Database
->Insert($s,
93 'An error occurred while logging your IP address.',
94 false); // fail silently
98 function ApproveApplicant($ApplicantID) {
99 $urh = $this->Context
->ObjectFactory
->NewObject($this->Context
, 'UserRoleHistory');
100 if (!is_array($ApplicantID)) {
101 $ApplicantID = array($ApplicantID);
103 for ($i = 0; $i < count($ApplicantID); $i++
) {
104 $aid = ForceInt($ApplicantID[$i], 0);
106 $urh->UserID
= $ApplicantID[$i];
107 $urh->Notes
= $this->Context
->GetDefinition('NewMemberWelcomeAboard');
108 $urh->RoleID
= $this->Context
->Configuration
['APPROVAL_ROLE'];
109 $this->AssignRole($urh);
112 return $this->Context
->WarningCollector
->Iif();
115 function AssignRole($UserRoleHistory, $NewUser = '0') {
116 $NewUser = ForceBool($NewUser, 0);
117 if (!$this->Context
->Session
->User
->Permission('PERMISSION_CHANGE_USER_ROLE') && !$this->Context
->Session
->User
->Permission('PERMISSION_APPROVE_APPLICANTS') && !$NewUser) {
118 $this->Context
->WarningCollector
->Add($this->Context
->GetDefinition('ErrPermissionInsufficient'));
119 } elseif ($UserRoleHistory->Notes
== '') {
120 $this->Context
->WarningCollector
->Add($this->Context
->GetDefinition('ErrRoleNotes'));
122 // Assign the user to the role first
123 $s = $this->Context
->ObjectFactory
->NewContextObject($this->Context
, 'SqlBuilder');
124 $s->SetMainTable('User', 'u');
125 $s->AddFieldNameValue('RoleID', $UserRoleHistory->RoleID
);
126 $s->AddWhere('u', 'UserID', '', $UserRoleHistory->UserID
, '=');
127 $this->Context
->Database
->Update($s, $this->Name
, 'AssignRole', 'An error occurred while assigning the user to a role.');
129 // Now record the change
130 $UserRoleHistory->Notes
= FormatStringForDatabaseInput($UserRoleHistory->Notes
);
132 $s->SetMainTable('UserRoleHistory', 'h');
133 $s->AddFieldNameValue('UserID', $UserRoleHistory->UserID
);
134 $s->AddFieldNameValue('RoleID', $UserRoleHistory->RoleID
);
135 $s->AddFieldNameValue('Date', MysqlDateTime());
136 $s->AddFieldNameValue('AdminUserID', ($NewUser?
0:$this->Context
->Session
->UserID
));
137 $s->AddFieldNameValue('Notes', $UserRoleHistory->Notes
);
138 $s->AddFieldNameValue('RemoteIp', GetRemoteIp(1));
139 $this->Context
->Database
->Insert($s, $this->Name
, 'AssignRole', 'An error occurred while recording the role change.');
141 // Now email the user about the role change
143 // Retrieve user information
144 $AffectedUser = $this->GetUserById($UserRoleHistory->UserID
);
146 $e = $this->Context
->ObjectFactory
->NewContextObject($this->Context
, 'Email');
148 $e->WarningCollector
= &$this->Context
->WarningCollector
;
149 $e->ErrorManager
= &$this->Context
->ErrorManager
;
150 $e->AddFrom($this->Context
->Configuration
['SUPPORT_EMAIL'], $this->Context
->Configuration
['SUPPORT_NAME']);
151 $e->AddRecipient($AffectedUser->Email
, $AffectedUser->Name
);
152 $e->Subject
= $this->Context
->Configuration
['APPLICATION_TITLE'].' '.$this->Context
->GetDefinition('AccountChangeNotification');
155 if ($AffectedUser->PERMISSION_SIGN_IN
) {
156 $File = $this->Context
->Configuration
['LANGUAGES_PATH']
157 .$this->Context
->Configuration
['LANGUAGE']
158 .'/email_role_change.txt';
160 $File = $this->Context
->Configuration
['LANGUAGES_PATH']
161 .$this->Context
->Configuration
['LANGUAGE']
162 .'/email_banned.txt';
165 $EmailBody = @file_get_contents
($File);
167 if (!$EmailBody) $this->Context
->ErrorManager
->AddError($this->Context
, $this->Name
, 'AssignRole', 'Failed to read email template ('.$File.').');
169 $EmailBody = str_replace(
178 $this->Context
->Configuration
['APPLICATION_TITLE'],
179 strtolower($AffectedUser->Role
),
180 GetUrl($this->Context
->Configuration
, 'account.php', '', 'u', $AffectedUser->UserID
)
185 $this->DelegateParameters
['AffectedUser'] = &$AffectedUser;
186 $this->DelegateParameters
['EmailBody'] = &$EmailBody;
187 $this->CallDelegate('PreRoleChangeNotification');
189 $e->Body
= $EmailBody;
193 return $this->Context
->WarningCollector
->Iif();
197 * Change user's password
199 * @todo Could it be part of the authenticator?
203 function ChangePassword($User) {
204 if ($this->Context
->Configuration
['ALLOW_PASSWORD_CHANGE']) {
205 // Ensure that the person performing this action has access to do so
206 // Everyone can edit themselves
207 if ($this->Context
->Session
->UserID
!= $User->UserID
208 && !$this->Context
->Session
->User
->Permission('PERMISSION_EDIT_USERS')
210 $this->Context
->WarningCollector
->Add(
211 $this->Context
->GetDefinition('ErrPermissionInsufficient'));
216 Validate($this->Context
->GetDefinition('NewPasswordLower'), 1, $User->NewPassword
, 100, '', $this->Context
);
217 if ($User->NewPassword
!= $User->ConfirmPassword
) $this->Context
->WarningCollector
->Add($this->Context
->GetDefinition('ErrNewPasswordMatchBad'));
219 // Validate old password
220 $UserCredentials = $this->GetUserCredentials($User->UserID
);
221 // Reset verification key
222 $UserCredentials->VerificationKey
= DefineVerificationKey();
223 if (!$this->CheckPassword($UserCredentials, $User->OldPassword
, 0)) {
224 $this->Context
->WarningCollector
->Add(
225 $this->Context
->GetDefinition('ErrOldPasswordBad'));
226 } // Set new Password
227 else if ($this->Context
->WarningCollector
->Count() == 0) {
228 if ($this->SetNewPassword($User, $User->NewPassword
)
229 && $this->Context
->Session
->UserID
== $User->UserID
231 $this->Context
->Session
->RegenerateId($this->Context
);
235 return $this->Context
->WarningCollector
->Iif();
239 * Check Password against its hash.
243 * @param string $Password
244 * @param boolean $RegenerateHash Should it regenerate the hash if it use an old hash type
247 function CheckPassword($User, $Password, $RegenerateHash=1) {
248 if (isset($this->Context
->Authenticator
->PasswordHash
)) {
249 return $this->Context
->Authenticator
->PasswordHash
->CheckPassword(
250 $User, $Password, $RegenerateHash);
251 } else if ($Password && $User->Password
!== '*'
252 && (md5($Password) === $User->Password ||
$Password === $User->Password
)
260 function CreateUser(&$User) {
261 $SafeUser = clone($User);
263 // Instantiate a new validator for each field
264 Validate($this->Context
->GetDefinition('EmailLower'), 1, $SafeUser->Email
, 200,
265 '^([A-Z0-9+_-][A-Z0-9+_.-]{0,63})@(([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})|([A-Z0-9][A-Z0-9.-]{0,244}\.[A-Z]{2,10}))$',
268 Validate($this->Context
->GetDefinition('UsernameLower'), 1, $SafeUser->Name
, 20, '', $this->Context
);
269 Validate($this->Context
->GetDefinition('PasswordLower'), 1, $SafeUser->NewPassword
, 50, '', $this->Context
);
270 if ($SafeUser->NewPassword
!= $SafeUser->ConfirmPassword
) $this->Context
->WarningCollector
->Add($this->Context
->GetDefinition('ErrPasswordsMatchBad'));
271 if (!$SafeUser->AgreeToTerms
) $this->Context
->WarningCollector
->Add($this->Context
->GetDefinition('ErrAgreeTOS'));
273 // Ensure the username isn't taken already
274 $s = $this->Context
->ObjectFactory
->NewContextObject($this->Context
, 'SqlBuilder');
275 $s->SetMainTable('User', 'u');
276 $s->AddSelect('UserID', 'u');
277 $s->AddWhere('u', 'Name', '', FormatStringForDatabaseInput($SafeUser->Name
), '=');
278 $result = $this->Context
->Database
->Select($s, $this->Name
, 'CreateUser', 'A fatal error occurred while validating your input.');
279 $MatchCount = $this->Context
->Database
->RowCount($result);
280 if ($MatchCount > 0) $this->Context
->WarningCollector
->Add($this->Context
->GetDefinition('ErrUsernameTaken'));
282 $this->DelegateParameters
['User'] = &$User;
283 $this->CallDelegate('PostValidation');
285 // If validation was successful
286 if ($this->Context
->WarningCollector
->Count() == 0) {
287 $SafeUser->Password
= $this->HashPassword($SafeUser->NewPassword
);
288 $SafeUser->FormatPropertiesForDatabaseInput();
291 $s->SetMainTable('User', 'u');
292 $s->AddFieldNameValue('FirstName', $SafeUser->FirstName
);
293 $s->AddFieldNameValue('LastName', $SafeUser->LastName
);
294 $s->AddFieldNameValue('Name', $SafeUser->Name
);
295 $s->AddFieldNameValue('Email', $SafeUser->Email
);
296 $s->AddFieldNameValue('Password', $SafeUser->Password
);
297 $s->AddFieldNameValue('DateFirstVisit', MysqlDateTime());
298 $s->AddFieldNameValue('DateLastActive', MysqlDateTime());
299 $s->AddFieldNameValue('CountVisit', 0);
300 $s->AddFieldNameValue('CountDiscussions', 0);
301 $s->AddFieldNameValue('CountComments', 0);
302 $s->AddFieldNameValue('RoleID', $this->Context
->Configuration
['DEFAULT_ROLE']);
303 $s->AddFieldNameValue('StyleID', 0);
304 $s->AddFieldNameValue('UtilizeEmail', $this->Context
->Configuration
['DEFAULT_EMAIL_VISIBLE']);
305 $s->AddFieldNameValue('Attributes', '');
306 $s->AddFieldNameValue('RemoteIp', GetRemoteIp(1));
308 $this->DelegateParameters
['User'] = &$User;
309 $this->DelegateParameters
['SqlBuilder'] = &$s;
310 $this->CallDelegate('PreDataInsert');
312 $User->UserID
= $this->Context
->Database
->Insert($s, $this->Name
, 'CreateUser', 'An error occurred while creating a new user.');
314 $Urh = $this->Context
->ObjectFactory
->NewObject($this->Context
, 'UserRoleHistory');
315 $Urh->UserID
= $User->UserID
;
316 $Urh->AdminUserID
= 0;
317 $Urh->RoleID
= $this->Context
->Configuration
['DEFAULT_ROLE'];
318 if ($this->Context
->Configuration
['ALLOW_IMMEDIATE_ACCESS']) {
319 $Urh->Notes
= $this->Context
->GetDefinition('RegistrationAccepted');
321 $Urh->Notes
= $this->Context
->GetDefinition('RegistrationPendingApproval');
323 $this->AssignRole($Urh, 1);
325 $this->CallDelegate('PostRoleAssignment');
327 // Notify user administrators
328 if (!$this->Context
->Configuration
['ALLOW_IMMEDIATE_ACCESS']) {
330 $s->SetMainTable('User', 'u');
331 $s->AddJoin('Role', 'r', 'RoleID', 'u', 'RoleID', 'inner join');
332 $s->AddWhere('r', 'PERMISSION_RECEIVE_APPLICATION_NOTIFICATION', '', 1, '=');
333 $s->AddWhere('u', 'SendNewApplicantNotifications', '', 1, '=');
334 $s->AddSelect(array('Name', 'Email'), 'u');
335 $Administrators = $this->Context
->Database
->Select($s, $this->Name
, 'CreateUser', 'An error occurred while retrieving administrator email addresses.', 0);
336 // Fail silently if an error occurs while notifying administrators
338 // Get the email body
339 $File = $this->Context
->Configuration
['LANGUAGES_PATH']
340 .$this->Context
->Configuration
['LANGUAGE']
341 .'/email_applicant.txt';
342 $EmailBody = @file_get_contents
($File);
344 if ($EmailBody && $Administrators) {
345 $EmailBody = str_replace(
354 GetUrl($this->Context
->Configuration
, 'settings.php', '', '', '', '', 'PostBackAction=Applicants')
359 $this->DelegateParameters
['User'] = &$User;
360 $this->DelegateParameters
['EmailBody'] = &$EmailBody;
361 $this->CallDelegate('PreNewUserNotification');
363 if ($this->Context
->Database
->RowCount($Administrators) > 0) {
364 $e = $this->Context
->ObjectFactory
->NewContextObject($this->Context
, 'Email');
366 $e->ErrorManager
= &$this->Context
->ErrorManager
;
367 $e->WarningCollector
= &$this->Context
->WarningCollector
;
368 $e->AddFrom($User->Email
, $User->Name
);
371 while ($Row = $this->Context
->Database
->GetRow($Administrators)) {
372 $AdminEmail = ForceString($Row['Email'], '');
373 $AdminName = ForceString($Row['Name'], '');
374 if ($AdminEmail != '') $e->AddRecipient($AdminEmail, $AdminName);
376 $e->Subject
= $this->Context
->GetDefinition('NewApplicant');
377 $e->Body
= $EmailBody;
383 return $this->Context
->WarningCollector
->Iif();
386 function GetApplicantCount() {
387 $ApplicantData = $this->GetUsersByRoleId(0);
388 if ($ApplicantData) {
389 return $this->Context
->Database
->RowCount($ApplicantData);
395 function GetIpHistory($UserID) {
396 $s = $this->Context
->ObjectFactory
->NewContextObject($this->Context
, 'SqlBuilder');
397 $s->SetMainTable('IpHistory', 'i');
398 $s->AddSelect('IpHistoryID', 'i', 'UsageCount', 'count');
399 $s->AddSelect('RemoteIp', 'i');
400 $s->AddGroupBy('RemoteIp', 'i');
401 $s->AddWhere('i', 'UserID', '', $UserID, '=');
402 $ResultSet = $this->Context
->Database
->Select($s, $this->Name
, 'GetIpHistory', 'An error occurred while retrieving historical IP usage data.');
404 $SharedWith = array();
407 $SharedUserName = '';
409 while ($Row = $this->Context
->Database
->GetRow($ResultSet)) {
410 $CurrentIp = ForceString($Row['RemoteIp'], '');
411 $UsageCount = ForceInt($Row['UsageCount'], 0);
412 $UserData = $this->GetUsersByIp($CurrentIp);
413 while ($UserRow = $this->Context
->Database
->GetRow($UserData)) {
414 $SharedUserName = ForceString($UserRow['Name'], '');
415 $SharedUserID = ForceInt($UserRow['UserID'], 0);
416 if ($SharedUserID > 0 && $SharedUserID != $UserID) {
417 $SharedWith[] = array('UserID' => $SharedUserID, 'Name' => $SharedUserName);
420 $IpData[] = array('IP' => $CurrentIp, 'UsageCount' => $UsageCount, 'SharedWith' => $SharedWith);
421 $SharedWith = array();
426 // Returns a SqlBuilder object with all of the user properties already defined in the select
427 function GetUserBuilder() {
428 $s = $this->Context
->ObjectFactory
->NewContextObject($this->Context
, 'SqlBuilder');
429 $s->SetMainTable('User', 'u');
430 $s->AddJoin('Role', 'r', 'RoleID', 'u', 'RoleID', 'left join');
431 $s->AddJoin('Style', 's', 'StyleID', 'u', 'StyleID', 'left join');
432 $s->AddSelect(array('UserID', 'Name', 'FirstName', 'LastName', 'Email', 'UtilizeEmail', 'Icon', 'Picture', 'Attributes', 'Preferences', 'CountVisit', 'CountDiscussions', 'CountComments', 'RemoteIp', 'DateFirstVisit', 'DateLastActive', 'RoleID', 'StyleID', 'CustomStyle', 'ShowName', 'UserBlocksCategories', 'DefaultFormatType', 'Discovery', 'SendNewApplicantNotifications'), 'u');
433 $s->AddSelect(array('PERMISSION_SIGN_IN', 'PERMISSION_RECEIVE_APPLICATION_NOTIFICATION', 'PERMISSION_HTML_ALLOWED', 'Permissions'), 'r');
434 $s->AddSelect('Name', 'r', 'Role');
435 $s->AddSelect('Description', 'r', 'RoleDescription');
436 $s->AddSelect('Icon', 'r', 'RoleIcon');
437 $s->AddSelect('Url', 's', 'StyleUrl');
438 $s->AddSelect('Name', 's', 'Style');
442 function GetUserById($UserID) {
444 $s = $this->GetUserBuilder();
445 $s->AddWhere('u', 'UserID', '', $UserID, '=');
448 $User = $this->Context
->ObjectFactory
->NewContextObject($this->Context
, 'User');
449 $UserData = $this->Context
->Database
->Select($s, $this->Name
, 'GetUserById', 'An error occurred while attempting to retrieve the requested user.');
450 while ($rows = $this->Context
->Database
->GetRow($UserData)) {
451 $User->GetPropertiesFromDataSet($rows);
454 return $Found ?
$User : false;
461 * Get user credentials
464 * @param string $UserName
467 function GetUserCredentials($UserID=0, $UserName='') {
468 $UserName = FormatStringForDatabaseInput($UserName);
469 $UserID = ForceInt($UserID, 0);
470 if ($UserName ||
$UserID) {
471 $s = $this->Context
->ObjectFactory
->NewContextObject($this->Context
, 'SqlBuilder');
472 $s->SetMainTable('User', 'u');
473 $s->AddJoin('Role', 'r', 'RoleID', 'u', 'RoleID', 'left join');
474 $s->AddSelect(array('UserID', 'Password', 'VerificationKey'), 'u');
475 $s->AddSelect('PERMISSION_SIGN_IN', 'r');
476 if ($UserName) $s->AddWhere('u', 'Name', '', $UserName, '=');
477 if ($UserID) $s->AddWhere('u', 'UserID', '', $UserID, '=');
478 $UserResult = $this->Context
->Database
->Select($s, $this->Name
, 'GetUserCredentials',
479 'An error occurred while attempting to find your user acount');
486 $User = $this->Context
->ObjectFactory
->NewContextObject(
487 $this->Context
, 'User');
488 while ($rows = $this->Context
->Database
->GetRow($UserResult)) {
489 $User->GetPropertiesFromDataSet($rows);
492 return $Found ?
$User : false;
498 function GetUserIdByName($Username) {
499 $Username = FormatStringForDatabaseInput(ForceString($Username, ''), 1);
501 if ($Username != '') {
502 $s = $this->Context
->ObjectFactory
->NewContextObject($this->Context
, 'SqlBuilder');
503 $s->SetMainTable('User', 'u');
504 $s->AddSelect('UserID', 'u');
505 $s->AddWhere('u', 'Name', '', $Username, '=');
506 $result = $this->Context
->Database
->Select($s, $this->Name
, 'GetUserIdByName', 'An error occurred while attempting to retrieve the requested user information.');
507 while ($rows = $this->Context
->Database
->GetRow($result)) {
508 $UserID = ForceInt($rows['UserID'], 0);
514 function GetUserIdsByVerificationKey($VerificationKey) {
515 $VerificationKey = FormatStringForDatabaseInput($VerificationKey);
517 if ($VerificationKey) {
518 $s = $this->Context
->ObjectFactory
->NewContextObject($this->Context
, 'SqlBuilder');
519 $s->SetMainTable('User', 'u');
520 $s->AddSelect('UserID', 'u');
521 $s->AddWhere('u', 'VerificationKey', '', $VerificationKey, '=');
523 $Result = $this->Context
->Database
->Select($s,
525 'GetUserIdsByVerificationKey',
526 'An error occurred while attempting to validate your remember me credentials');
529 while ($rows = $this->Context
->Database
->GetRow($Result)) {
530 $UserIDs[] = ForceInt($rows['UserID'], 0);
537 function GetUserRoleHistoryByUserId($UserID) {
538 $s = $this->Context
->ObjectFactory
->NewContextObject($this->Context
, 'SqlBuilder');
539 $s->SetMainTable('UserRoleHistory', 'h');
540 $s->AddJoin('Role', 'r', 'RoleID', 'h', 'RoleID', 'inner join');
541 $s->AddJoin('User', 'u', 'UserID', 'h', 'UserID', 'inner join');
542 $s->AddJoin('User', 'a', 'UserID', 'h', 'AdminUserID', 'left join');
543 $s->AddSelect(array('UserID', 'RoleID', 'AdminUserID', 'Notes', 'Date'), 'h');
544 $s->AddSelect('Name', 'u', 'Username');
545 $s->AddSelect('Name', 'a', 'AdminUsername');
546 $s->AddSelect('Name', 'r', 'Role');
547 $s->AddSelect('Description', 'r', 'RoleDescription');
548 $s->AddSelect('Icon', 'r', 'RoleIcon');
549 $s->AddWhere('h', 'UserID', '', $UserID, '=');
550 $s->AddOrderBy('Date', 'h', 'desc');
551 return $this->Context
->Database
->Select($s, $this->Name
, 'GetUserRoleHistoryByUserId', "An error occurred while attempting to retrieve the user's role history.");
554 function GetUsersByIp($Ip) {
555 $s = $this->Context
->ObjectFactory
->NewContextObject($this->Context
, 'SqlBuilder');
556 $s->SetMainTable('User', 'u');
557 $s->AddJoin('IpHistory', 'i', 'UserID', 'u', 'UserID', 'inner join', ' and i.'.$this->Context
->DatabaseColumns
['IpHistory']['RemoteIp']." = '$Ip'");
558 $s->AddSelect(array('UserID', 'Name'), 'u');
559 $s->AddGroupBy('UserID', 'u');
560 return $this->Context
->Database
->Select($s, $this->Name
, 'GetUsersByIp', 'An error occurred while retrieving users by IP.');
563 function GetUsersByRoleId($RoleID, $RecordsToReturn = '0') {
564 $RecordsToReturn = ForceInt($RecordsToReturn, 0);
565 $s = $this->GetUserBuilder();
566 $s->AddSelect('Discovery', 'u');
567 $s->AddWhere('u', 'RoleID', '', $RoleID, '=');
568 if ($RecordsToReturn > 0) $s->AddLimit(0,$RecordsToReturn);
569 return $this->Context
->Database
->Select($s, $this->Name
, 'GetUsersByRoleId', 'An error occurred while attempting to retrieve users from the specified role.');
572 function GetUserSearch($Search, $RowsPerPage, $CurrentPage) {
573 $s = $this->GetSearchBuilder($Search);
574 $SortField = $Search->UserOrder
;
575 if (!in_array($SortField, array('Name', 'Date'))) $SortField = 'Name';
576 if ($SortField != 'Name') $SortField = 'DateLastActive';
577 $SortDirection = ($SortField == 'Name'?
'asc':'desc');
578 $s->AddOrderBy($SortField, 'u', $SortDirection);
579 if ($RowsPerPage > 0) {
580 $CurrentPage = ForceInt($CurrentPage, 1);
581 if ($CurrentPage < 1) $CurrentPage = 1;
582 $RowsPerPage = ForceInt($RowsPerPage, 50);
583 $FirstRecord = ($CurrentPage * $RowsPerPage) - $RowsPerPage;
585 if ($RowsPerPage > 0) $s->AddLimit($FirstRecord, $RowsPerPage+
1);
586 return $this->Context
->Database
->Select($s, $this->Name
, 'GetUserSearch', 'An error occurred while retrieving search results.');
590 * Create a password hash.
593 * @param string $Password
596 function HashPassword($Password) {
597 if (isset($this->Context
->Authenticator
->PasswordHash
)) {
598 return $this->Context
->Authenticator
->PasswordHash
->HashPassword($Password);
600 return md5($Password);
604 function GetSearchBuilder($Search) {
605 $Search->FormatPropertiesForDatabaseInput();
606 $s = $this->Context
->ObjectFactory
->NewContextObject($this->Context
, 'SqlSearch');
607 $s->UserQuery
= $Search->Query
;
608 $s->SearchFields
= array('u.Name');
609 if ($this->Context
->Session
->User
->Permission('PERMISSION_EDIT_USERS')
610 ||
$this->Context
->Session
->User
->Permission('PERMISSION_APPROVE_APPLICANTS')
611 ||
$this->Context
->Session
->User
->Permission('PERMISSION_CHANGE_USER_ROLE')) {
612 $s->SearchFields
[] = 'u.FirstName';
613 $s->SearchFields
[] = 'u.LastName';
614 $s->SearchFields
[] = 'u.Email';
616 $s->SetMainTable('User', 'u');
617 $s->AddJoin('Style', 's', 'StyleID', 'u', 'StyleID', 'left join');
618 $s->AddJoin('Role', 'r', 'RoleID', 'u', 'RoleID', 'left join');
619 $s->AddSelect(array('UserID', 'RoleID', 'StyleID', 'CustomStyle', 'FirstName', 'LastName', 'Name', 'Email', 'UtilizeEmail', 'Icon', 'CountVisit', 'CountDiscussions', 'CountComments', 'DateFirstVisit', 'DateLastActive'), 'u');
620 $s->AddSelect('Name', 's', 'Style');
621 $s->AddSelect('Name', 'r', 'Role');
622 $s->AddSelect('Icon', 'r', 'RoleIcon');
624 $this->DelegateParameters
['SqlBuilder'] = &$s;
625 $this->CallDelegate('PreDefineSearch');
628 if ($Search->Roles
!= '') {
629 $Roles = explode(',',$Search->Roles
);
630 $RoleCount = count($Roles);
631 $s->AddWhere('', '1', '', '0', '=', 'and', '', 0, 1);
632 for ($i = 0; $i < $RoleCount; $i++
) {
633 if ($Roles[$i] == $this->Context
->GetDefinition('Applicant')) {
634 $s->AddWhere('u', 'RoleID', '', 0, '=', 'or', '', 1);
635 $s->AddWhere('u', 'RoleID', '', 0, '=', 'or', '' ,0);
637 $s->AddWhere('r', 'Name', '', trim($Roles[$i]), '=', 'or');
642 if ($this->Context
->Session
->User
&& $this->Context
->Session
->User
->Permission('PERMISSION_APPROVE_APPLICANTS')) {
643 // Allow the applicant search
645 // DON'T allow the applicant search
646 $s->AddWhere('u', 'RoleID', '', 0, '<>', 'and');
651 // Just retrieve user properties relevant to the session
652 function GetSessionDataById($UserID) {
653 $s = $this->Context
->ObjectFactory
->NewContextObject($this->Context
, 'SqlBuilder');
654 $s->SetMainTable('User', 'u');
655 $s->AddJoin('Role', 'r', 'RoleID', 'u', 'RoleID', 'left join');
656 $s->AddJoin('Style', 's', 'StyleID', 'u', 'StyleID', 'left join');
658 $s->AddSelect(array('Name', 'UserID', 'RoleID', 'StyleID', 'CustomStyle', 'UserBlocksCategories', 'DefaultFormatType', 'Preferences', 'SendNewApplicantNotifications'), 'u');
659 $s->AddSelect(array('PERMISSION_SIGN_IN', 'PERMISSION_RECEIVE_APPLICATION_NOTIFICATION', 'PERMISSION_HTML_ALLOWED', 'Permissions'), 'r');
660 $s->AddSelect('Url', 's', 'StyleUrl');
661 $s->AddWhere('u', 'UserID', '', $UserID, '=');
663 $User = $this->Context
->ObjectFactory
->NewContextObject($this->Context
, 'User');
664 $UserData = $this->Context
->Database
->Select($s, $this->Name
, 'GetSessionDataById', 'An error occurred while attempting to retrieve the requested user.');
665 if ($this->Context
->Database
->RowCount($UserData) == 0) {
666 // This warning is in plain english, because at the point that
667 // this method is called, the dictionary object is not yet loaded
668 // (this is called in the context object's constructor when the session is started)
669 $this->Context
->WarningCollector
->Add('The requested user could not be found.');
671 while ($rows = $this->Context
->Database
->GetRow($UserData)) {
672 $User->GetPropertiesFromDataSet($rows);
675 return $this->Context
->WarningCollector
->Iif($User, false);
678 function LogIp($UserID) {
679 if ($this->Context
->Configuration
['LOG_ALL_IPS']) {
680 $s = $this->Context
->ObjectFactory
->NewContextObject($this->Context
, 'SqlBuilder');
681 $s->SetMainTable('IpHistory', 'i');
682 $s->AddFieldNameValue('UserID', $UserID);
683 $s->AddFieldNameValue('RemoteIp', GetRemoteIp(1));
684 $s->AddFieldNameValue('DateLogged', MysqlDateTime());
685 $this->Context
->Database
->Insert($s, $this->Name
, 'LogIp', 'An error occurred while logging user data.');
689 function RemoveApplicant($UserID) {
690 // Ensure that the user has not made any contributions to the application in any way
691 if (!is_array($UserID)) $UserID = array($UserID);
692 $s = $this->Context
->ObjectFactory
->NewContextObject($this->Context
, 'SqlBuilder');
694 for ($i = 0; $i < count($UserID); $i++
) {
695 $uid = ForceInt($UserID[$i], 0);
700 $s->SetMainTable('Style', 's');
701 $s->AddSelect('StyleID', 's');
702 $s->AddWhere('s', 'AuthUserID', '', $uid, '=');
703 $Result = $this->Context
->Database
->Select($s, $this->Name
, 'RemoveApplicant', 'An error occurred while removing the user.');
704 if ($this->Context
->Database
->RowCount($Result) > 0) $this->Context
->WarningCollector
->Add($this->Context
->GetDefinition('ErrRemoveUserStyle'));
705 if ($this->Context
->WarningCollector
->Count() > 0) return false;
709 $s->SetMainTable('Comment', 'm');
710 $s->AddSelect('CommentID', 'm');
711 $s->AddWhere('m', 'AuthUserID', '', $uid, '=');
712 $Result = $this->Context
->Database
->Select($s, $this->Name
, 'RemoveApplicant', 'An error occurred while removing the user.');
713 if ($this->Context
->Database
->RowCount($Result) > 0) $this->Context
->WarningCollector
->Add($this->Context
->GetDefinition('ErrRemoveUserComments'));
714 if ($this->Context
->WarningCollector
->Count() > 0) return false;
718 $s->SetMainTable('Discussion', 't');
719 $s->AddSelect('DiscussionID', 't');
720 $s->AddWhere('t', 'AuthUserID', '', $uid, '=');
721 $Result = $this->Context
->Database
->Select($s, $this->Name
, 'RemoveApplicant', 'An error occurred while removing the user.');
722 if ($this->Context
->Database
->RowCount($Result) > 0) $this->Context
->WarningCollector
->Add($this->Context
->GetDefinition('ErrRemoveUserDiscussions'));
723 if ($this->Context
->WarningCollector
->Count() > 0) return false;
725 // Remove other data the user has created
728 $s->SetMainTable('UserBookmark', 'b');
729 $s->AddWhere('b', 'UserID', '', $uid, '=');
730 $this->Context
->Database
->Delete($s, $this->Name
, 'RemoveApplicant', "An error occurred while removing the user's bookmarks.");
734 $s->SetMainTable('UserRoleHistory', 'r');
735 $s->AddWhere('r', 'UserID', '', $uid, '=');
736 $this->Context
->Database
->Delete($s, $this->Name
, 'RemoveApplicant', "An error occurred while removing the user's role history.");
740 $s->SetMainTable('UserDiscussionWatch', 'w');
741 $s->AddWhere('w', 'UserID', '', $uid, '=');
742 $this->Context
->Database
->Delete($s, $this->Name
, 'RemoveApplicant', "An error occurred while removing the user's discussion history.");
746 $s->SetMainTable('User', 'u');
747 $s->AddWhere('u', 'UserID', '', $uid, '=');
748 // Adding in this little check to make sure that only applicants can be removed.
749 $s->AddWhere('u', 'RoleID', '', 0, '=');
750 $this->Context
->Database
->Delete($s, $this->Name
, 'RemoveApplicant', 'An error occurred while removing the user.');
757 function RemoveBookmark($UserID, $DiscussionID) {
758 $s = $this->Context
->ObjectFactory
->NewContextObject($this->Context
, 'SqlBuilder');
759 $s->SetMainTable('UserBookmark', 'b');
760 $s->AddWhere('b', 'UserID', '', $UserID, '=');
761 $s->AddWhere('b', 'DiscussionID', '', $DiscussionID, '=');
762 $this->Context
->Database
->Delete($s, $this->Name
, 'RemoveBookmark', 'An error occurred while removing the bookmark.');
765 function RemoveCategoryBlock($CategoryID) {
766 $s = $this->Context
->ObjectFactory
->NewContextObject($this->Context
, 'SqlBuilder');
767 $s->SetMainTable('CategoryBlock', 'b');
768 $s->AddWhere('b', 'CategoryID', '', $CategoryID, '=');
769 $s->AddWhere('b', 'UserID', '', $this->Context
->Session
->UserID
, '=');
770 // Don't stress over errors (ie. duplicate entries) since this is indexed and duplicates cannot be inserted
771 if ($this->Context
->Database
->Delete($s, $this->Name
, 'RemoveCategoryBlock', 'An error occurred while removing the category block.', 0)) {
773 $s->SetMainTable('CategoryBlock', 'b');
774 $s->AddWhere('b', 'UserID', '', $this->Context
->Session
->UserID
, '=');
775 $s->AddSelect('CategoryID', 'b');
776 $Result = $this->Context
->Database
->Select($s, $this->Name
, 'RemoveCategoryBlock', 'Related category block information could not be found.', 0);
778 if ($this->Context
->Database
->RowCount($Result) == 0) {
780 $s->SetMainTable('User', 'u');
781 $s->AddFieldNameValue('UserBlocksCategories', '0');
782 $s->AddWhere('u', 'UserID', '', $this->Context
->Session
->UserID
, '=');
783 $this->Context
->Database
->Update($s, $this->Name
, 'RemoveCategoryBlock', 'An error occurred while updating category block information.', 0);
789 function RemoveCommentBlock($CommentID) {
790 $s = $this->Context
->ObjectFactory
->NewContextObject($this->Context
, 'SqlBuilder');
791 $s->SetMainTable('CommentBlock', 'b');
792 $s->AddWhere('b', 'BlockedCommentID', '', $CommentID, '=');
793 $s->AddWhere('b', 'BlockingUserID', '', $this->Context
->Session
->UserID
, '=');
794 echo($s->GetDelete());
795 // Don't stress over errors (ie. duplicate entries) since this is indexed and duplicates cannot be inserted
796 $this->Context
->Database
->Delete($s, $this->Name
, 'RemoveCommentBlock', 'An error occurred while removing the comment block.', 0);
799 function RemoveUserBlock($UserID) {
800 $s = $this->Context
->ObjectFactory
->NewContextObject($this->Context
, 'SqlBuilder');
801 $s->SetMainTable('UserBlock', 'b');
802 $s->AddWhere('b', 'BlockingUserID', '', $this->Context
->Session
->UserID
, '=');
803 $s->AddWhere('b', 'BlockedUserID', '',$UserID, '=');
804 // Don't stress over errors (ie. duplicate entries) since this is indexed and duplicates cannot be inserted
805 $this->Context
->Database
->Delete($s, $this->Name
, 'RemoveUserBlock', 'An error occurred while removing the user block.', 0);
808 function RequestPasswordReset($Username) {
809 $Username = FormatStringForDatabaseInput($Username, '');
811 if ($Username == '') {
812 $this->Context
->WarningCollector
->Add($this->Context
->GetDefinition('ErrInvalidUsername'));
814 // Attempt to retrieve email address
815 $s = $this->Context
->ObjectFactory
->NewContextObject($this->Context
, 'SqlBuilder');
816 $s->SetMainTable('User', 'u');
817 $s->AddSelect(array('Email', 'Name', 'UserID'), 'u');
818 $s->AddWhere('u', 'Name', '', $Username, '=');
821 $UserResult = $this->Context
->Database
->Select($s, $this->Name
, 'RequestPasswordReset', 'An error occurred while retrieving account information.');
822 if ($this->Context
->Database
->RowCount($UserResult) == 0) {
823 $this->Context
->WarningCollector
->Add($this->Context
->GetDefinition('ErrAccountNotFound'));
828 while ($rows = $this->Context
->Database
->GetRow($UserResult)) {
829 $UserID = ForceInt($rows['UserID'], 0);
830 $Email = ForceString($rows['Email'], '');
831 $Name = FormatStringForDisplay($rows['Name'], 1);
833 // Now that we have the email, generate an email verification key
834 $EmailVerificationKey = DefineVerificationKey();
836 // Insert the email verification key into the user table
838 $s->SetMainTable('User', 'u');
839 $s->AddFieldNameValue('EmailVerificationKey', $EmailVerificationKey,1);
840 $s->AddWhere('u', 'UserID', '', $UserID, '=');
841 $this->Context
->Database
->Update($s, $this->Name
, 'RequestPasswordReset', 'An error occurred while managing your account information.');
843 // If there are no errors, send the user an email
844 if ($this->Context
->WarningCollector
->Count() == 0) {
845 // Retrieve the email body
846 $File = $this->Context
->Configuration
['LANGUAGES_PATH']
847 .$this->Context
->Configuration
['LANGUAGE']
848 .'/email_password_request.txt';
850 $EmailBody = @file_get_contents
($File);
851 if (!$EmailBody) $this->Context
->ErrorManager
->AddError($this->Context
, $this->Name
, 'AssignRole', 'Failed to read email template ('.$File.').');
853 $e = $this->Context
->ObjectFactory
->NewContextObject($this->Context
, 'Email');
855 $e->WarningCollector
= &$this->Context
->WarningCollector
;
856 $e->ErrorManager
= &$this->Context
->ErrorManager
;
857 $e->AddFrom($this->Context
->Configuration
['SUPPORT_EMAIL'], $this->Context
->Configuration
['SUPPORT_NAME']);
858 $e->AddRecipient($Email, $Name);
859 $e->Subject
= $this->Context
->Configuration
['APPLICATION_TITLE'].' '.$this->Context
->GetDefinition('PasswordResetRequest');
860 $e->Body
= str_replace(
868 $this->Context
->Configuration
['APPLICATION_TITLE'],
869 GetUrl($this->Context
->Configuration
, 'people.php', '', '', '', '', 'PostBackAction=PasswordResetForm&u='.$UserID.'&k='.$EmailVerificationKey)
878 return $this->Context
->WarningCollector
->Iif($Email,false);
881 function ResetPassword($PassUserID, $EmailVerificationKey, $NewPassword, $ConfirmPassword) {
882 // Validate the passwords
883 if ($NewPassword == '') $this->Context
->WarningCollector
->Add($this->Context
->GetDefinition('ErrPasswordRequired'));
884 if ($NewPassword != $ConfirmPassword) $this->Context
->WarningCollector
->Add($this->Context
->GetDefinition('ErrPasswordsMatchBad'));
886 if ($this->Context
->WarningCollector
->Count() == 0) {
887 $PassUserID = ForceInt($PassUserID, 0);
888 $PasswordCrypt = FormatStringForDatabaseInput($this->HashPassword($NewPassword));
889 $EmailVerificationKey = FormatStringForDatabaseInput($EmailVerificationKey);
891 // Attempt to retrieve email address
892 $s = $this->Context
->ObjectFactory
->NewContextObject($this->Context
, 'SqlBuilder');
893 $s->SetMainTable('User', 'u');
894 $s->AddFieldNameValue('EmailVerificationKey', '', 1);
895 $s->AddFieldNameValue('Password', $PasswordCrypt, 1);
896 $s->AddWhere('u', 'UserID', '', $PassUserID, '=');
897 $s->AddWhere('u', 'EmailVerificationKey', '', $EmailVerificationKey, '=');
898 $this->Context
->Database
->Update($s, $this->Name
, 'ResetPassword', 'An error occurred while updating your password.');
900 return $this->Context
->WarningCollector
->Iif();
903 function SaveIdentity($User) {
904 // Ensure that the person performing this action has access to do so
905 // Everyone can edit themselves
906 if ($this->Context
->Session
->UserID
!= $User->UserID
&& !$this->Context
->Session
->User
->Permission('PERMISSION_EDIT_USERS')) $this->Context
->WarningCollector
->Add($this->Context
->GetDefinition('ErrPermissionInsufficient'));
908 if ($this->Context
->WarningCollector
->Count() == 0) {
909 // Validate the properties
910 if($this->ValidateUser($User)) {
911 $s = $this->Context
->ObjectFactory
->NewContextObject($this->Context
, 'SqlBuilder');
912 $s->SetMainTable('User', 'u');
913 if ($this->Context
->Configuration
['ALLOW_NAME_CHANGE'] == '1') $s->AddFieldNameValue('Name', $User->Name
);
914 if ($this->Context
->Configuration
['USE_REAL_NAMES'] == '1') {
915 $s->AddFieldNameValue('FirstName', $User->FirstName
);
916 $s->AddFieldNameValue('LastName', $User->LastName
);
917 $s->AddFieldNameValue('ShowName', $User->ShowName
);
919 if ($this->Context
->Configuration
['ALLOW_EMAIL_CHANGE'] == '1') $s->AddFieldNameValue('Email', $User->Email
);
920 $s->AddFieldNameValue('UtilizeEmail', $User->UtilizeEmail
);
921 $s->AddFieldNameValue('Icon', $User->Icon
);
922 $s->AddFieldNameValue('Picture', $User->Picture
);
923 $s->AddFieldNameValue('Attributes', $User->Attributes
);
924 $s->AddWhere('u', 'UserID', '', $User->UserID
, '=');
925 $this->DelegateParameters
['User'] = &$User;
926 $this->DelegateParameters
['SqlBuilder'] = &$s;
927 $this->CallDelegate('PreIdentityUpdate');
928 $this->Context
->Database
->Update($s, $this->Name
, 'SaveIdentity', 'An error occurred while attempting to update the identity data.');
931 return $this->Context
->WarningCollector
->Iif();
934 function SaveStyle($User) {
935 // Ensure that the person performing this action has access to do so
936 // Everyone can edit themselves
937 if ($this->Context
->Session
->UserID
!= $User->UserID
) $this->Context
->WarningCollector
->Add($this->Context
->GetDefinition('ErrPermissionInsufficient'));
939 if ($this->Context
->WarningCollector
->Count() == 0) {
940 // Make sure they've got a style of some kind
941 if ($User->CustomStyle
== '' && $User->StyleID
== 0) $User->StyleID
= 1;
942 $User->FormatPropertiesForDatabaseInput();
943 $s = $this->Context
->ObjectFactory
->NewContextObject($this->Context
, 'SqlBuilder');
944 $s->SetMainTable('User', 'u');
945 $s->AddFieldNameValue('StyleID', $User->StyleID
);
946 if ($User->StyleID
== 0) $s->AddFieldNameValue('CustomStyle', $User->CustomStyle
);
947 $s->AddWhere('u', 'UserID', '', $User->UserID
, '=');
948 $this->Context
->Database
->Update($s, $this->Name
, 'SaveStyle', 'An error occurred while attempting to update the style data.');
950 $this->Context
->Session
->User
->StyleID
= $User->StyleID
;
951 $this->Context
->Session
->User
->CustomStyle
= $User->CustomStyle
;
953 return $this->Context
->WarningCollector
->Iif();
956 function SetDefaultFormatType($UserID, $FormatType) {
957 $this->Context
->Session
->User
->DefaultFormatType
= $FormatType;
958 return $this->SwitchUserProperty($UserID, 'DefaultFormatType', $FormatType);
965 * @param string $NewPassword
968 function SetNewPassword($User, $NewPassword) {
969 if (isset($this->Context
->Authenticator
->PasswordHash
)) {
970 return $this->Context
->Authenticator
->PasswordHash
->SetNewPassword($User, $NewPassword);
972 $SafeUser = clone($User);
973 $SafeUser->Password
= md5($NewPassword);
974 return $this->SaveUserCredentials($SafeUser);
978 function SwitchUserProperty($UserID, $PropertyName, $Switch) {
979 $UserID = ForceInt($UserID, 0);
980 if ($UserID == 0) $this->Context
->WarningCollector
->Add($this->Context
->GetDefinition('ErrUserID'));
982 if ($UserID != $this->Context
->Session
->UserID
) $this->Context
->WarningCollector
->Add($this->Context
->GetDefinition('ErrPermissionUserSettings'));
984 if ($this->Context
->WarningCollector
->Count() == 0) {
985 $s = $this->Context
->ObjectFactory
->NewContextObject($this->Context
, 'SqlBuilder');
986 $s->SetMainTable('User', 'u');
987 $s->AddFieldNameValue($PropertyName, $Switch);
988 $s->AddWhere('u', 'UserID', '', $UserID, '=');
989 $this->Context
->Database
->Update($s, $this->Name
, 'SwitchUserProperty', 'An error occurred while manipulating user properties.');
991 return $this->Context
->WarningCollector
->Iif();
994 // Boolean preferences
995 function SwitchUserPreference($PreferenceName, $Switch) {
996 $Switch = ForceBool($Switch, 0);
997 if ($this->Context
->Session
->UserID
== 0) $this->Context
->WarningCollector
->Add($this->Context
->GetDefinition('ErrUserID'));
999 if ($this->Context
->WarningCollector
->Count() == 0) {
1000 // Set the value for the user
1001 $this->Context
->Session
->User
->Preferences
[$PreferenceName] = $Switch;
1002 $this->SaveUserPreferences($this->Context
->Session
->User
);
1004 return $this->Context
->WarningCollector
->Iif();
1007 // Customizable strings
1008 function SaveUserCustomization($CustomizationName, $Value) {
1009 $Value = ForceString($Value, '');
1010 if ($this->Context
->Session
->UserID
== 0) $this->Context
->WarningCollector
->Add($this->Context
->GetDefinition('ErrUserID'));
1011 if ($this->Context
->WarningCollector
->Count() == 0) {
1012 // Set the value for the user
1013 $this->Context
->Session
->User
->Preferences
[$CustomizationName] = $Value;
1014 $this->SaveUserPreferences($this->Context
->Session
->User
);
1016 return $this->Context
->WarningCollector
->Iif();
1019 function SaveUserCustomizationsFromForm(&$User) {
1021 while (list($CustomizationName) = each($this->Context
->Configuration
)) {
1022 if (strpos($CustomizationName, 'CUSTOMIZATION_') !== false) {
1024 $Value = ForceIncomingString($CustomizationName, '');
1025 $CustomizationName = substr($CustomizationName, 14);
1026 $User->Preferences
[$CustomizationName] = $Value;
1029 if ($ValueSet) $this->SaveUserPreferences($User);
1034 * Save user's password and verification key
1036 * @param $User $UserCredential
1039 function SaveUserCredentials($User) {
1040 $UserCredentials = clone($User);
1041 $UserCredentials->FormatPropertiesForDatabaseInput();
1043 $s = $this->Context
->ObjectFactory
->NewContextObject($this->Context
, 'SqlBuilder');
1044 $s->SetMainTable('User', 'u');
1045 $s->AddFieldNameValue('Password', $UserCredentials->Password
, 1);
1046 $s->AddFieldNameValue('VerificationKey', $UserCredentials->VerificationKey
, 1);
1047 $s->AddWhere('u', 'UserID', '', $UserCredentials->UserID
, '=');
1049 $this->Context
->Database
->Update($s, $this->Name
, 'SaveUserCredentials',
1050 'An error occurred while attempting to save your crendentials.');
1052 return $this->Context
->WarningCollector
->Iif();
1055 function SaveUserPreferences($User) {
1056 if ($User->UserID
> 0) {
1057 // Serialize and save the settings
1058 $SerializedPreferences = SerializeArray($User->Preferences
);
1059 $s = $this->Context
->ObjectFactory
->NewContextObject($this->Context
, 'SqlBuilder');
1060 $s->SetMainTable('User', 'u');
1061 $s->AddFieldNameValue('Preferences', $SerializedPreferences);
1062 $s->AddWhere('u', 'UserID', '', $User->UserID
, '=');
1063 $this->Context
->Database
->Update($s, $this->Name
, 'SaveUserPreferences', 'An error occurred while manipulating user preferences.');
1068 function UpdateUserBlogCount($UserID) {
1069 $s = $this->Context
->ObjectFactory
->NewContextObject($this->Context
, 'SqlBuilder');
1070 $s->SetMainTable('User', 'u');
1071 $s->AddFieldNameValue('CountBlogs', 'CountBlogs+1', 0);
1072 $s->AddWhere('u', 'UserID', '', $UserID, '=');
1073 $this->Context
->Database
->Update($s, $this->Name
, 'UpdateUserBlogCount', 'An error occurred while updating the blog count.');
1076 function UpdateUserCommentCount($UserID) {
1077 if ($this->Context
->WarningCollector
->Count() == 0) {
1078 $UserID = ForceInt($UserID, 0);
1080 if ($UserID == 0) $this->Context
->ErrorManager
->AddError($this->Context
, $this->Name
, 'UpdateUserCommentCount', 'User identifier not supplied');
1082 // Select the LastCommentPost, and CommentSpamCheck values from the user's profile
1083 $s = $this->Context
->ObjectFactory
->NewContextObject($this->Context
, 'SqlBuilder');
1084 $s->SetMainTable('User', 'u');
1085 $s->AddSelect(array('LastCommentPost', 'CommentSpamCheck'), 'u');
1086 $s->AddWhere('u', 'UserID', '', $UserID, '=');
1089 $CommentSpamCheck = 0;
1090 $result = $this->Context
->Database
->Select($s, $this->Name
, 'UpdateUserCommentCount', 'An error occurred while retrieving user activity data.');
1091 while ($rows = $this->Context
->Database
->GetRow($result)) {
1092 $LastCommentPost = UnixTimestamp($rows['LastCommentPost']);
1093 $DateDiff = mktime() - $LastCommentPost;
1094 $CommentSpamCheck = ForceInt($rows['CommentSpamCheck'], 0);
1097 // If a non-numeric value was returned, then this is the user's first post
1098 $SecondsSinceLastPost = ForceInt($DateDiff, 0);
1099 // If the LastCommentPost is less than 30 seconds ago
1100 // and the CommentSpamCheck is greater than five, throw a warning
1101 if ($SecondsSinceLastPost < $this->Context
->Configuration
['COMMENT_THRESHOLD_PUNISHMENT'] && $CommentSpamCheck >= $this->Context
->Configuration
['COMMENT_POST_THRESHOLD'] && $DateDiff != '') {
1102 $this->Context
->WarningCollector
->Add(str_replace(array('//1', '//2', '//3'),
1103 array($this->Context
->Configuration
['COMMENT_POST_THRESHOLD'], $this->Context
->Configuration
['COMMENT_TIME_THRESHOLD'], $this->Context
->Configuration
['COMMENT_THRESHOLD_PUNISHMENT']),
1104 $this->Context
->GetDefinition('ErrSpamComments')));
1108 $s->SetMainTable('User', 'u');
1109 if ($this->Context
->WarningCollector
->Count() == 0) {
1110 // make sure to update the 'datelastactive' field
1111 $s->AddFieldNameValue('DateLastActive', MysqlDateTime());
1112 $s->AddFieldNameValue('CountComments', $this->Context
->DatabaseColumns
['User']['CountComments'].'+1', 0);
1113 // If the LastCommentPost is less than 30 seconds ago
1114 // and the DiscussionSpamCheck is less than 6,
1115 // update the user profile and add 1 to the CommentSpamCheck
1116 if ($SecondsSinceLastPost == 0) {
1117 $s->AddFieldNameValue('LastCommentPost', MysqlDateTime());
1118 } elseif ($SecondsSinceLastPost < $this->Context
->Configuration
['COMMENT_TIME_THRESHOLD'] && $CommentSpamCheck <= $this->Context
->Configuration
['COMMENT_POST_THRESHOLD'] && $DateDiff != '') {
1119 $s->AddFieldNameValue('CommentSpamCheck', $this->Context
->DatabaseColumns
['User']['CommentSpamCheck'].'+1', 0);
1121 // If the LastCommentPost is more than 60 seconds ago,
1122 // set the CommentSpamCheck to 1, LastCommentPost to now(),
1123 // and update the user profile
1124 $s->AddFieldNameValue('CommentSpamCheck', 1);
1125 $s->AddFieldNameValue('LastCommentPost', MysqlDateTime());
1127 $s->AddWhere('u', 'UserID', '', $UserID, '=');
1128 $this->Context
->Database
->Update($s, $this->Name
, 'UpdateUserCommentCount', 'An error occurred while updating the user profile.');
1130 // Update the 'Waiting period' every time they try to post again
1131 $s->AddFieldNameValue('DateLastActive', MysqlDateTime());
1132 $s->AddFieldNameValue('LastCommentPost', MysqlDateTime());
1133 $s->AddWhere('u', 'UserID', '', $UserID, '=');
1134 $this->Context
->Database
->Update($s, $this->Name
, 'UpdateUserCommentCount', 'An error occurred while updating the user profile.');
1138 return $this->Context
->WarningCollector
->Iif();
1141 function UpdateUserDiscussionCount($UserID) {
1142 if ($this->Context
->WarningCollector
->Iif()) {
1143 $UserID = ForceInt($UserID, 0);
1145 if ($UserID == 0) $this->Context
->ErrorManager
->AddError($this->Context
, $this->Name
, 'UpdateUserDiscussionCount', 'User identifier not supplied');
1147 // Select the LastDiscussionPost, and DiscussionSpamCheck values from the user's profile
1148 $s = $this->Context
->ObjectFactory
->NewContextObject($this->Context
, 'SqlBuilder');
1149 $s->SetMainTable('User', 'u');
1150 $s->AddSelect(array('LastDiscussionPost', 'DiscussionSpamCheck'), 'u');
1151 $s->AddWhere('u', 'UserID', '', $UserID, '=');
1153 $DiscussionSpamCheck = 0;
1154 $result = $this->Context
->Database
->Select($s, $this->Name
, 'UpdateUserDiscussionCount', 'An error occurred while retrieving user activity data.');
1155 while ($rows = $this->Context
->Database
->GetRow($result)) {
1156 $LastDiscussionPost = UnixTimestamp($rows['LastDiscussionPost']);
1157 $DateDiff = mktime() - $LastDiscussionPost;
1158 $DiscussionSpamCheck = ForceInt($rows['DiscussionSpamCheck'], 0);
1160 $SecondsSinceLastPost = ForceInt($DateDiff, 0);
1162 // If the LastDiscussionPost is less than 1 minute ago
1163 // and the DiscussionSpamCheck is greater than three, throw a warning
1164 if ($SecondsSinceLastPost < $this->Context
->Configuration
['DISCUSSION_THRESHOLD_PUNISHMENT'] && $DiscussionSpamCheck >= $this->Context
->Configuration
['DISCUSSION_POST_THRESHOLD'] && $DateDiff != '') {
1165 $this->Context
->WarningCollector
->Add(str_replace(array('//1', '//2', '//3'),
1166 array($this->Context
->Configuration
['DISCUSSION_POST_THRESHOLD'], $this->Context
->Configuration
['DISCUSSION_TIME_THRESHOLD'], $this->Context
->Configuration
['DISCUSSION_THRESHOLD_PUNISHMENT']),
1167 $this->Context
->GetDefinition('ErrSpamDiscussions')));
1171 $s->SetMainTable('User', 'u');
1172 if ($this->Context
->WarningCollector
->Count() == 0) {
1173 $s->AddFieldNameValue('DateLastActive', MysqlDateTime());
1174 $s->AddFieldNameValue('CountDiscussions', $this->Context
->DatabaseColumns
['User']['CountDiscussions'].'+1', 0);
1175 // If the LastDiscussionPost is less than 1 minute ago
1176 // and the DiscussionSpamCheck is less than four,
1177 // update the user profile and add 1 to the DiscussionSpamCheck
1178 if ($SecondsSinceLastPost < $this->Context
->Configuration
['DISCUSSION_TIME_THRESHOLD'] && $DiscussionSpamCheck <= $this->Context
->Configuration
['DISCUSSION_POST_THRESHOLD'] && $DateDiff != '') {
1179 $s->AddFieldNameValue('DiscussionSpamCheck', $this->Context
->DatabaseColumns
['User']['DiscussionSpamCheck'].'+1', 0);
1181 // If the LastDiscussionPost is more than 1 minute ago,
1182 // set the DiscussionSpamCheck to 1, LastDiscussionPost to now(),
1183 // and update the user profile
1184 $s->AddFieldNameValue('DiscussionSpamCheck', 1);
1185 $s->AddFieldNameValue('LastDiscussionPost', MysqlDateTime());
1187 $s->AddWhere('u', 'UserID', '', $UserID, '=');
1188 $this->Context
->Database
->Update($s, $this->Name
, 'UpdateUserDiscussionCount', 'An error occurred while updating the user profile.');
1190 // Update the 'Waiting period' every time they try to post again
1191 $s->AddFieldNameValue('DateLastActive', MysqlDateTime());
1192 $s->AddFieldNameValue('LastDiscussionPost', MysqlDateTime());
1193 $s->AddWhere('u', 'UserID', '', $UserID, '=');
1194 $this->Context
->Database
->Update($s, $this->Name
, 'UpdateUserCommentCount', 'An error occurred while updating the user profile.');
1198 return $this->Context
->WarningCollector
->Iif();
1201 function UpdateUserLastVisit($UserID, $VerificationKey = '') {
1202 $s = $this->Context
->ObjectFactory
->NewContextObject($this->Context
, 'SqlBuilder');
1203 $s->SetMainTable('User', 'u');
1204 $s->AddFieldNameValue('DateLastActive', MysqlDateTime());
1205 if ($VerificationKey) $s->AddFieldNameValue('VerificationKey', $VerificationKey);
1206 $s->AddFieldNameValue('CountVisit', 'CountVisit + 1', 0);
1207 $s->AddWhere('u', 'UserID', '', $UserID, '=');
1209 $this->Context
->Database
->Update($s,
1212 'An error occurred while updating your profile.',
1213 false); // fail silently
1217 function UserManager(&$Context) {
1218 $this->Name
= 'UserManager';
1219 $this->Delegation($Context);
1222 // Validates and formats User properties ensuring they're safe for database input
1223 // Returns: boolean value indicating success
1224 // Usage: $Boolean = $UserManager->ValidateUser($MyUser);
1225 function ValidateUser(&$User) {
1226 // First update the values so they are safe for db input
1227 $SafeUser = clone($User);
1228 $SafeUser->FormatPropertiesForDatabaseInput();
1230 // Instantiate a new validator for each field
1231 if ($this->Context
->Configuration
['ALLOW_NAME_CHANGE'] == '1') {
1232 Validate($this->Context
->GetDefinition('UsernameLower'), 1, $SafeUser->Name
, 20, '', $this->Context
);
1234 // Ensure the username isn't taken already
1235 $s = $this->Context
->ObjectFactory
->NewContextObject($this->Context
, 'SqlBuilder');
1236 $s->SetMainTable('User', 'u');
1237 $s->AddSelect('UserID', 'u');
1238 $s->AddWhere('u', 'Name', '', $SafeUser->Name
, '=');
1239 if ($User->UserID
> 0) $s->AddWhere('u', 'UserID', '', $User->UserID
, '<>');
1241 $result = $this->Context
->Database
->Select($s, $this->Name
, 'ValidateUser', 'A fatal error occurred while validating your input.');
1242 $MatchCount = $this->Context
->Database
->RowCount($result);
1244 if ($MatchCount > 0) $this->Context
->WarningCollector
->Add($this->Context
->GetDefinition('ErrUsernameTaken'));
1247 if ($this->Context
->Configuration
['ALLOW_EMAIL_CHANGE'] == '1') {
1249 $this->Context
->GetDefinition('EmailLower'), 1, $SafeUser->Email
, 200,
1250 '^([A-Z0-9+_-][A-Z0-9+_.-]{0,63})@(([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})|([A-Z0-9][A-Z0-9.-]{0,244}\.[A-Z]{2,10}))$',
1255 // If validation was successful, then reset the properties to db safe values for saving
1256 if ($this->Context
->WarningCollector
->Count() == 0) $User = $SafeUser;
1258 return $this->Context
->WarningCollector
->Iif();
1261 function ValidateUserCredentials($Username, $Password, $PersistentSession) {
1262 if ($Username == '') $this->Context
->WarningCollector
->Add($this->Context
->GetDefinition('ErrInvalidUsername'));
1263 if ($Password == '') $this->Context
->WarningCollector
->Add($this->Context
->GetDefinition('ErrInvalidPassword'));
1265 // Only continue if there have been no errors/warnings
1266 if ($this->Context
->WarningCollector
->Count() == 0) {
1267 $UserID = $this->Context
->Authenticator
->Authenticate($Username, $Password, $PersistentSession);
1268 if ($UserID == -2) $this->Context
->ErrorManager
->AddError($this->Context
, $this->Name
, 'ValidateUserCredentials', 'An error occurred while validating your credentials.');
1269 if ($UserID == -1) $this->Context
->WarningCollector
->Add($this->Context
->GetDefinition('ErrNoLogin'));
1270 if ($UserID == 0) $this->Context
->WarningCollector
->Add($this->Context
->GetDefinition('ErrUserCombination'));
1271 if ($UserID > 0) $this->Context
->Session
->Start($this->Context
, $this->Context
->Authenticator
, $UserID);
1273 return $this->Context
->WarningCollector
->Iif();
1276 function VerifyPasswordResetRequest($VerificationUserID, $EmailVerificationKey) {
1277 $VerificationUserID = ForceInt($VerificationUserID, 0);
1278 $EmailVerificationKey = ForceString($EmailVerificationKey, '');
1279 $EmailVerificationKey = FormatStringForDatabaseInput($EmailVerificationKey);
1281 // Attempt to retrieve email address
1282 $s = $this->Context
->ObjectFactory
->NewContextObject($this->Context
, 'SqlBuilder');
1283 $s->SetMainTable('User', 'u');
1284 $s->AddSelect('UserID', 'u');
1285 $s->AddWhere('u', 'UserID', '', $VerificationUserID, '=');
1286 $s->AddWhere('u', 'EmailVerificationKey', '', $EmailVerificationKey, '=');
1287 $UserResult = $this->Context
->Database
->Select($s, $this->Name
, 'VerifyPasswordResetRequest', 'An error occurred while retrieving account information.');
1288 if ($this->Context
->Database
->RowCount($UserResult) == 0) $this->Context
->WarningCollector
->Add($this->Context
->GetDefinition('ErrPasswordResetRequest'));
1289 return $this->Context
->WarningCollector
->Iif();
1293 * Validate user's Verification Key
1298 * @param string $EncryptedUserID
1299 * @param string $VerificationKey
1302 function ValidateVerificationKey($EncryptedUserID, $VerificationKey) {
1303 $EncryptedUserID = ForceString($EncryptedUserID, '');
1304 if ($EncryptedUserID && $VerificationKey) {
1305 $UserIDs = $this->GetUserIdsByVerificationKey($VerificationKey);
1306 foreach ($UserIDs as $UserID) {
1307 // For backward compatibility, the UserID might not be encrypted
1308 if ($EncryptedUserID === ForceString($UserID, '0')
1309 ||
$EncryptedUserID === md5($UserID)