DiscussionCustomPrefix: Added new icons and translations
[vanilla-miry.git] / library / People / People.Class.UserManager.php
blob5141f2d0332351861a0c58d98715e9f782ee89ca
1 <?php
2 /**
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
16 * @package People
17 * @version 1.1.8
21 /**
22 * Container for user management class.
23 * @package People
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)) {
43 $s->Clear();
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']) {
73 return;
76 if (!$IP) {
77 $IP = GetRemoteIp(1);
80 $UserID = ForceInt($UserID, 0);
81 $IP = FormatStringForDatabaseInput($IP);
83 if ($UserID && $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,
91 $this->Name,
92 'AddUserIP',
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);
105 if ($aid > 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'));
121 } else {
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);
131 $s->Clear();
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
142 if (!$NewUser) {
143 // Retrieve user information
144 $AffectedUser = $this->GetUserById($UserRoleHistory->UserID);
146 $e = $this->Context->ObjectFactory->NewContextObject($this->Context, 'Email');
147 $e->HtmlOn = 0;
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');
154 $File = '';
155 if ($AffectedUser->PERMISSION_SIGN_IN) {
156 $File = $this->Context->Configuration['LANGUAGES_PATH']
157 .$this->Context->Configuration['LANGUAGE']
158 .'/email_role_change.txt';
159 } else {
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(
170 array(
171 '{user_name}',
172 '{forum_name}',
173 '{role_name}',
174 '{forum_url}'
176 array(
177 $AffectedUser->Name,
178 $this->Context->Configuration['APPLICATION_TITLE'],
179 strtolower($AffectedUser->Role),
180 GetUrl($this->Context->Configuration, 'account.php', '', 'u', $AffectedUser->UserID)
182 $EmailBody
185 $this->DelegateParameters['AffectedUser'] = &$AffectedUser;
186 $this->DelegateParameters['EmailBody'] = &$EmailBody;
187 $this->CallDelegate('PreRoleChangeNotification');
189 $e->Body = $EmailBody;
190 $e->Send();
193 return $this->Context->WarningCollector->Iif();
197 * Change user's password
199 * @todo Could it be part of the authenticator?
200 * @param User $User
201 * @return boolean
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'));
212 return 0;
215 // Validate inputs
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.
241 * @deprecated
242 * @param User $User
243 * @param string $Password
244 * @param boolean $RegenerateHash Should it regenerate the hash if it use an old hash type
245 * @return boolean
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)
254 return true;
255 } else {
256 return false;
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}))$',
266 $this->Context
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();
290 $s->Clear();
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');
320 } else {
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']) {
329 $s->Clear();
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(
346 array(
347 '{applicant_name}',
348 '{applicant_email}',
349 '{application_url}'
351 array(
352 $User->Name,
353 $User->Email,
354 GetUrl($this->Context->Configuration, 'settings.php', '', '', '', '', 'PostBackAction=Applicants')
356 $EmailBody
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');
365 $e->HtmlOn = 0;
366 $e->ErrorManager = &$this->Context->ErrorManager;
367 $e->WarningCollector = &$this->Context->WarningCollector;
368 $e->AddFrom($User->Email, $User->Name);
369 $AdminEmail = '';
370 $AdminName = '';
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;
378 $e->Send(0);
383 return $this->Context->WarningCollector->Iif();
386 function GetApplicantCount() {
387 $ApplicantData = $this->GetUsersByRoleId(0);
388 if ($ApplicantData) {
389 return $this->Context->Database->RowCount($ApplicantData);
390 } else {
391 return 0;
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.');
403 $IpData = array();
404 $SharedWith = array();
405 $CurrentIp = '';
406 $UsageCount = 0;
407 $SharedUserName = '';
408 $SharedUserID = '';
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();
423 return $IpData;
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');
439 return $s;
442 function GetUserById($UserID) {
443 if ($UserID > 0) {
444 $s = $this->GetUserBuilder();
445 $s->AddWhere('u', 'UserID', '', $UserID, '=');
446 $Found = false;
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);
452 $Found = true;
454 return $Found ? $User : false;
455 } else {
456 return false;
461 * Get user credentials
463 * @param int $UserID
464 * @param string $UserName
465 * @return User
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');
481 if (!$UserResult) {
482 return null;
485 $Found = false;
486 $User = $this->Context->ObjectFactory->NewContextObject(
487 $this->Context, 'User');
488 while ($rows = $this->Context->Database->GetRow($UserResult)) {
489 $User->GetPropertiesFromDataSet($rows);
490 $Found = true;
492 return $Found ? $User : false;
493 } else {
494 return false;
498 function GetUserIdByName($Username) {
499 $Username = FormatStringForDatabaseInput(ForceString($Username, ''), 1);
500 $UserID = 0;
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);
511 return $UserID;
514 function GetUserIdsByVerificationKey($VerificationKey) {
515 $VerificationKey = FormatStringForDatabaseInput($VerificationKey);
516 $UserIDs = array();
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,
524 $this->Name,
525 'GetUserIdsByVerificationKey',
526 'An error occurred while attempting to validate your remember me credentials');
528 if ($Result) {
529 while ($rows = $this->Context->Database->GetRow($Result)) {
530 $UserIDs[] = ForceInt($rows['UserID'], 0);
534 return $UserIDs;
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.
592 * @deprecated
593 * @param string $Password
594 * @return string
596 function HashPassword($Password) {
597 if (isset($this->Context->Authenticator->PasswordHash)) {
598 return $this->Context->Authenticator->PasswordHash->HashPassword($Password);
599 } else {
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');
627 $s->DefineSearch();
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);
636 } else {
637 $s->AddWhere('r', 'Name', '', trim($Roles[$i]), '=', 'or');
640 $s->EndWhereGroup();
642 if ($this->Context->Session->User && $this->Context->Session->User->Permission('PERMISSION_APPROVE_APPLICANTS')) {
643 // Allow the applicant search
644 } else {
645 // DON'T allow the applicant search
646 $s->AddWhere('u', 'RoleID', '', 0, '<>', 'and');
648 return $s;
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.');
670 } else {
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);
697 if ($uid > 0) {
698 // Styles
699 $s->Clear();
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;
707 // Comments
708 $s->Clear();
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;
716 // Discussions
717 $s->Clear();
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
726 // Bookmarks
727 $s->Clear();
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.");
732 // Role History
733 $s->Clear();
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.");
738 // Discussion Watch
739 $s->Clear();
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.");
744 // Remove the user
745 $s->Clear();
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.');
754 return true;
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)) {
772 $s->Clear();
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);
777 if ($Result) {
778 if ($this->Context->Database->RowCount($Result) == 0) {
779 $s->Clear();
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, '');
810 $Email = false;
811 if ($Username == '') {
812 $this->Context->WarningCollector->Add($this->Context->GetDefinition('ErrInvalidUsername'));
813 } else {
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'));
824 } else {
825 $Name = '';
826 $Email = '';
827 $UserID = 0;
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
837 $s->Clear();
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');
854 $e->HtmlOn = 0;
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(
861 array(
862 '{user_name}',
863 '{forum_name}',
864 '{password_url}'
866 array(
867 $Name,
868 $this->Context->Configuration['APPLICATION_TITLE'],
869 GetUrl($this->Context->Configuration, 'people.php', '', '', '', '', 'PostBackAction=PasswordResetForm&u='.$UserID.'&k='.$EmailVerificationKey)
871 $EmailBody
874 $e->Send();
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);
962 * Save user Password
964 * @param User $User
965 * @param string $NewPassword
966 * @return boolean
968 function SetNewPassword($User, $NewPassword) {
969 if (isset($this->Context->Authenticator->PasswordHash)) {
970 return $this->Context->Authenticator->PasswordHash->SetNewPassword($User, $NewPassword);
971 } else {
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) {
1020 $ValueSet = 0;
1021 while (list($CustomizationName) = each($this->Context->Configuration)) {
1022 if (strpos($CustomizationName, 'CUSTOMIZATION_') !== false) {
1023 $ValueSet = 1;
1024 $Value = ForceIncomingString($CustomizationName, '');
1025 $CustomizationName = substr($CustomizationName, 14);
1026 $User->Preferences[$CustomizationName] = $Value;
1029 if ($ValueSet) $this->SaveUserPreferences($User);
1030 return true;
1034 * Save user's password and verification key
1036 * @param $User $UserCredential
1037 * @return boolean
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.');
1065 return true;
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, '=');
1088 $DateDiff = '';
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')));
1107 $s->Clear();
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);
1120 } else {
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.');
1129 } else {
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, '=');
1152 $DateDiff = '';
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')));
1170 $s->Clear();
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);
1180 } else {
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.');
1189 } else {
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,
1210 $this->Name,
1211 'UpdateLastVisit',
1212 'An error occurred while updating your profile.',
1213 false); // fail silently
1216 // Constructor
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, '<>');
1240 $MatchCount = 0;
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') {
1248 Validate(
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}))$',
1251 $this->Context
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
1295 * Return user's id
1297 * @deprecated
1298 * @param string $EncryptedUserID
1299 * @param string $VerificationKey
1300 * @return int
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)
1311 return $UserID;
1315 return 0;