1<?php 2# MantisBT - A PHP based bugtracking system 3 4# MantisBT is free software: you can redistribute it and/or modify 5# it under the terms of the GNU General Public License as published by 6# the Free Software Foundation, either version 2 of the License, or 7# (at your option) any later version. 8# 9# MantisBT is distributed in the hope that it will be useful, 10# but WITHOUT ANY WARRANTY; without even the implied warranty of 11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12# GNU General Public License for more details. 13# 14# You should have received a copy of the GNU General Public License 15# along with MantisBT. If not, see <http://www.gnu.org/licenses/>. 16 17/** 18 * User API 19 * 20 * @package CoreAPI 21 * @subpackage UserAPI 22 * @copyright Copyright 2000 - 2002 Kenzaburo Ito - kenito@300baud.org 23 * @copyright Copyright 2002 MantisBT Team - mantisbt-dev@lists.sourceforge.net 24 * @link http://www.mantisbt.org 25 * 26 * @uses access_api.php 27 * @uses authentication_api.php 28 * @uses config_api.php 29 * @uses constant_inc.php 30 * @uses database_api.php 31 * @uses email_api.php 32 * @uses error_api.php 33 * @uses filter_api.php 34 * @uses helper_api.php 35 * @uses lang_api.php 36 * @uses ldap_api.php 37 * @uses project_api.php 38 * @uses project_hierarchy_api.php 39 * @uses string_api.php 40 * @uses user_pref_api.php 41 * @uses utility_api.php 42 */ 43 44require_api( 'access_api.php' ); 45require_api( 'authentication_api.php' ); 46require_api( 'config_api.php' ); 47require_api( 'constant_inc.php' ); 48require_api( 'database_api.php' ); 49require_api( 'email_api.php' ); 50require_api( 'error_api.php' ); 51require_api( 'filter_api.php' ); 52require_api( 'helper_api.php' ); 53require_api( 'lang_api.php' ); 54require_api( 'ldap_api.php' ); 55require_api( 'project_api.php' ); 56require_api( 'project_hierarchy_api.php' ); 57require_api( 'string_api.php' ); 58require_api( 'user_pref_api.php' ); 59require_api( 'utility_api.php' ); 60 61use Mantis\Exceptions\ClientException; 62 63# Cache of user rows from {user} table, indexed by user_id 64# If id does not exists, a value of 'false' is stored 65$g_cache_user = array(); 66 67$g_user_accessible_subprojects_cache = null; 68 69/** 70 * Cache a user row if necessary and return the cached copy 71 * If the second parameter is true (default), trigger an error 72 * if the user can't be found. If the second parameter is 73 * false, return false if the user can't be found. 74 * 75 * @param integer $p_user_id A valid user identifier. 76 * @param boolean $p_trigger_errors Trigger an error is the user does not exist. 77 * @return array|boolean array of database data or false if not found 78 */ 79function user_cache_row( $p_user_id, $p_trigger_errors = true ) { 80 global $g_cache_user; 81 82 $c_user_id = (int)$p_user_id; 83 84 if( !isset( $g_cache_user[$c_user_id] ) ) { 85 user_cache_array_rows( array( $c_user_id ) ); 86 87 if( !isset( $g_cache_user[$c_user_id] ) ) { 88 if( $p_trigger_errors ) { 89 throw new ClientException( 90 sprintf( "User id '%d' not found.", (integer)$p_user_id ), 91 ERROR_USER_BY_ID_NOT_FOUND, 92 array( (integer)$p_user_id ) 93 ); 94 } 95 96 return false; 97 } 98 } 99 100 return $g_cache_user[$c_user_id]; 101} 102 103/** 104 * Loads user rows in cache for a set of User ID's 105 * Store false if the user does not exists 106 * @param array $p_user_id_array An array of user identifiers. 107 * @return void 108 */ 109function user_cache_array_rows( array $p_user_id_array ) { 110 global $g_cache_user; 111 $c_user_id_array = array(); 112 113 foreach( $p_user_id_array as $t_user_id ) { 114 if( !isset( $g_cache_user[(int)$t_user_id] ) ) { 115 $c_user_id_array[(int)$t_user_id] = (int)$t_user_id; 116 } 117 } 118 if( empty( $c_user_id_array ) ) { 119 return; 120 } 121 122 db_param_push(); 123 $t_params = array(); 124 $t_sql_in_params = array(); 125 foreach( $c_user_id_array as $t_id ) { 126 $t_params[] = $t_id; 127 $t_sql_in_params[] = db_param(); 128 } 129 $t_query = 'SELECT * FROM {user} WHERE id IN (' . implode( ',', $t_sql_in_params ) . ')'; 130 $t_result = db_query( $t_query, $t_params ); 131 132 if( $t_result !== false ) { 133 while( $t_row = db_fetch_array( $t_result ) ) { 134 $c_user_id = (int)$t_row['id']; 135 $g_cache_user[$c_user_id] = $t_row; 136 unset( $c_user_id_array[$c_user_id] ); 137 } 138 # set the remaining ids to false as not-found 139 foreach( $c_user_id_array as $t_id ) { 140 $g_cache_user[$t_id] = false; 141 } 142 } 143} 144 145/** 146 * Cache a user row 147 * @param array $p_user_database_result A user row to cache. 148 * @return void 149 */ 150function user_cache_database_result( array $p_user_database_result ) { 151 global $g_cache_user; 152 153 $g_cache_user[$p_user_database_result['id']] = $p_user_database_result; 154} 155 156/** 157 * Clear the user cache (or just the given id if specified) 158 * @param integer $p_user_id A valid user identifier or the default of null to clear cache for all users. 159 * @return boolean 160 */ 161function user_clear_cache( $p_user_id = null ) { 162 global $g_cache_user; 163 164 if( null === $p_user_id ) { 165 $g_cache_user = array(); 166 } else { 167 unset( $g_cache_user[$p_user_id] ); 168 } 169 170 return true; 171} 172 173/** 174 * Update Cache entry for a given user and field 175 * @param integer $p_user_id A valid user id to update. 176 * @param string $p_field The name of the field on the user object to update. 177 * @param mixed $p_value The updated value for the user object field. 178 * @return void 179 */ 180function user_update_cache( $p_user_id, $p_field, $p_value ) { 181 global $g_cache_user; 182 183 if( isset( $g_cache_user[$p_user_id] ) && isset( $g_cache_user[$p_user_id][$p_field] ) ) { 184 $g_cache_user[$p_user_id][$p_field] = $p_value; 185 } else { 186 user_clear_cache( $p_user_id ); 187 } 188} 189 190/** 191 * Searches the cache for a given field and value pair against any user, 192 * and returns the first user id that matches 193 * 194 * @param string $p_field The user object field name to search the cache for. 195 * @param mixed $p_value The field value to look for in the cache. 196 * @return integer|boolean 197 */ 198function user_search_cache( $p_field, $p_value ) { 199 global $g_cache_user; 200 if( isset( $g_cache_user ) ) { 201 foreach( $g_cache_user as $t_user ) { 202 if( $t_user && $t_user[$p_field] == $p_value ) { 203 return $t_user; 204 } 205 } 206 } 207 return false; 208} 209 210/** 211 * check to see if user exists by id 212 * return true if it does, false otherwise 213 * 214 * @param integer $p_user_id A valid user identifier. 215 * @return boolean 216 */ 217function user_exists( $p_user_id ) { 218 $t_row = user_cache_row( $p_user_id, false ); 219 220 if( false === $t_row ) { 221 return false; 222 } else { 223 return true; 224 } 225} 226 227/** 228 * check to see if user exists by id 229 * if the user does not exist, trigger an error 230 * 231 * @param integer $p_user_id A valid user identifier. 232 * @return void 233 */ 234function user_ensure_exists( $p_user_id ) { 235 $c_user_id = (integer)$p_user_id; 236 237 if( !user_exists( $c_user_id ) ) { 238 throw new ClientException( "User $c_user_id not found", ERROR_USER_BY_ID_NOT_FOUND, array( $c_user_id ) ); 239 } 240} 241 242/** 243 * return true if the username is unique, false if there is already a user with that username 244 * @param string $p_username The username to check. 245 * @param integer|null The user id allowed to conflict, otherwise null. 246 * @return boolean 247 */ 248function user_is_name_unique( $p_username, $p_user_id = null ) { 249 $t_existing_user_id = user_get_id_by_name( $p_username ); 250 if( $t_existing_user_id !== false && ( $p_user_id === null || (int)$t_existing_user_id !== $p_user_id ) ) { 251 return false; 252 } 253 254 $t_existing_user_id = user_get_id_by_realname( $p_username ); 255 if( $t_existing_user_id !== false && ( $p_user_id === null || (int)$t_existing_user_id !== $p_user_id ) ) { 256 return false; 257 } 258 259 return true; 260} 261 262/** 263 * Check if the username is unique and trigger an ERROR if it isn't 264 * @param string $p_username The username to check. 265 * @param integer|null $p_user_id The user id allowed to conflict, otherwise null. 266 * @return void 267 */ 268function user_ensure_name_unique( $p_username, $p_user_id = null ) { 269 if( !user_is_name_unique( $p_username, $p_user_id ) ) { 270 throw new ClientException( 271 sprintf( "Username '%s' already used.", $p_username ), 272 ERROR_USER_NAME_NOT_UNIQUE ); 273 } 274} 275 276/** 277 * Checks if the email address is unique. 278 * 279 * @param string $p_email The email to check. 280 * @param integer $p_user_id The user id that we are validating for or null for 281 * the case of a new user. 282 * 283 * @return boolean true: unique or blank, false: otherwise 284 */ 285function user_is_email_unique( $p_email, $p_user_id = null ) { 286 if( is_blank( $p_email ) ) { 287 return true; 288 } 289 290 $p_email = trim( $p_email ); 291 292 db_param_push(); 293 if ( $p_user_id === null ) { 294 $t_query = 'SELECT email FROM {user} WHERE email=' . db_param(); 295 $t_result = db_query( $t_query, array( $p_email ), 1 ); 296 } else { 297 $t_query = 'SELECT email FROM {user} WHERE id<>' . db_param() . 298 ' AND email=' . db_param(); 299 $t_result = db_query( $t_query, array( $p_user_id, $p_email ), 1 ); 300 } 301 302 return !db_result( $t_result ); 303} 304 305/** 306 * Check if the email is unique and trigger an ERROR if it isn't 307 * 308 * @param string $p_email The email address to check. 309 * @param integer $p_user_id The user id that we are validating for or null for 310 * the case of a new user. 311 * 312 * @return void 313 */ 314function user_ensure_email_unique( $p_email, $p_user_id = null ) { 315 if( !config_get_global( 'email_ensure_unique' ) ) { 316 return; 317 } 318 319 if( !user_is_email_unique( $p_email, $p_user_id ) ) { 320 throw new ClientException( 321 sprintf( "Email '%s' already used.", $p_email ), 322 ERROR_USER_EMAIL_NOT_UNIQUE ); 323 } 324} 325 326/** 327 * Check if the username is a valid username (does not account for uniqueness) realname can match 328 * @param string $p_username The username to check. 329 * @return boolean return true if user name is valid, false otherwise 330 */ 331function user_is_name_valid( $p_username ) { 332 # The DB field is hard-coded. DB_FIELD_SIZE_USERNAME should not be modified. 333 if( mb_strlen( $p_username ) > DB_FIELD_SIZE_USERNAME ) { 334 return false; 335 } 336 337 # username must consist of at least one character 338 if( is_blank( $p_username ) ) { 339 return false; 340 } 341 342 # Only allow a basic set of characters 343 if( 0 == preg_match( config_get( 'user_login_valid_regex' ), $p_username ) ) { 344 return false; 345 } 346 347 # We have a valid username 348 return true; 349} 350 351/** 352 * Check if the username is a valid username (does not account for uniqueness) 353 * Trigger an error if the username is not valid 354 * @param string $p_username The username to check. 355 * @return void 356 */ 357function user_ensure_name_valid( $p_username ) { 358 if( !user_is_name_valid( $p_username ) ) { 359 throw new ClientException( 360 sprintf( "Invalid username '%s'", $p_username ), 361 ERROR_USER_NAME_INVALID ); 362 } 363} 364 365/** 366 * return whether user is monitoring bug for the user id and bug id 367 * @param integer $p_user_id A valid user identifier. 368 * @param integer $p_bug_id A valid bug identifier. 369 * @return boolean 370 */ 371function user_is_monitoring_bug( $p_user_id, $p_bug_id ) { 372 db_param_push(); 373 $t_query = 'SELECT COUNT(*) FROM {bug_monitor} 374 WHERE user_id=' . db_param() . ' AND bug_id=' . db_param(); 375 376 $t_result = db_query( $t_query, array( (int)$p_user_id, (int)$p_bug_id ) ); 377 378 if( 0 == db_result( $t_result ) ) { 379 return false; 380 } else { 381 return true; 382 } 383} 384 385/** 386 * Check if the specified user is an enabled user with admin access level or above. 387 * @param integer $p_user_id A valid user identifier. 388 * @return boolean true: admin, false: otherwise. 389 */ 390function user_is_administrator( $p_user_id ) { 391 if( !user_is_enabled( $p_user_id ) ) { 392 return false; 393 } 394 395 $t_access_level = user_get_field( $p_user_id, 'access_level' ); 396 return $t_access_level >= config_get_global( 'admin_site_threshold' ); 397} 398 399/** 400 * Check if a user has a protected user account. 401 * Protected user accounts cannot be updated without manage_user_threshold 402 * permission. If the user ID supplied is that of the anonymous user, this 403 * function will always return true. The anonymous user account is always 404 * considered to be protected. 405 * 406 * @param integer $p_user_id A valid user identifier. 407 * @return boolean true: user is protected; false: user is not protected. 408 * @access public 409 */ 410function user_is_protected( $p_user_id ) { 411 return user_is_anonymous( $p_user_id ) || ON == user_get_field( $p_user_id, 'protected' ); 412} 413 414/** 415 * Check if a user is the anonymous user account. 416 * When anonymous logins are disabled this function will always return false. 417 * 418 * @param integer $p_user_id A valid user identifier. 419 * @return boolean true: user is the anonymous user; false: user is not the anonymous user. 420 * @access public 421 */ 422function user_is_anonymous( $p_user_id ) { 423 return auth_anonymous_enabled() && strcasecmp( user_get_username( $p_user_id ), auth_anonymous_account() ) == 0; 424} 425 426/** 427 * Trigger an ERROR if the user account is protected 428 * 429 * @param integer $p_user_id A valid user identifier. 430 * @return void 431 */ 432function user_ensure_unprotected( $p_user_id ) { 433 if( user_is_protected( $p_user_id ) ) { 434 throw new ClientException( 435 'User protected.', 436 ERROR_PROTECTED_ACCOUNT ); 437 } 438} 439 440/** 441 * return true is the user account is enabled, false otherwise 442 * 443 * @param integer $p_user_id A valid user identifier. 444 * @return boolean 445 */ 446function user_is_enabled( $p_user_id ) { 447 if( ON == user_get_field( $p_user_id, 'enabled' ) ) { 448 return true; 449 } else { 450 return false; 451 } 452} 453 454/** 455 * Count the number of users at or greater than a specific level 456 * 457 * @param integer $p_level Access Level to count users. The default is to include ANYBODY. 458 * @param bool $p_enabled true: must be enabled, false: must be disabled, null: don't care. 459 * @return integer The number of users. 460 */ 461function user_count_level( $p_level = ANYBODY, $p_enabled = null ) { 462 db_param_push(); 463 $t_query = 'SELECT COUNT(id) FROM {user} WHERE access_level >= ' . db_param(); 464 $t_param = array( $p_level ); 465 466 if( $p_enabled !== null ) { 467 $t_query .= ' AND enabled = ' . db_param(); 468 $t_param[] = (bool)$p_enabled; 469 } 470 471 # Get the number of users 472 $t_result = db_query( $t_query, $t_param ); 473 $t_count = db_result( $t_result ); 474 475 return $t_count; 476} 477 478/** 479 * Return an array of user ids that are logged in. 480 * A user is considered logged in if the last visit timestamp is within the 481 * specified session duration. 482 * If the session duration is 0, then no users will be returned. 483 * @param integer $p_session_duration_in_minutes The duration to return logged in users for. 484 * @return array 485 */ 486function user_get_logged_in_user_ids( $p_session_duration_in_minutes ) { 487 $t_session_duration_in_minutes = (integer)$p_session_duration_in_minutes; 488 489 # if session duration is 0, then there is no logged in users. 490 if( $t_session_duration_in_minutes == 0 ) { 491 return array(); 492 } 493 494 # Generate timestamp 495 $t_last_timestamp_threshold = mktime( date( 'H' ), date( 'i' ) - 1 * $t_session_duration_in_minutes, date( 's' ), date( 'm' ), date( 'd' ), date( 'Y' ) ); 496 497 # Execute query 498 db_param_push(); 499 $t_query = 'SELECT id FROM {user} WHERE last_visit > ' . db_param(); 500 $t_result = db_query( $t_query, array( $t_last_timestamp_threshold ), 1 ); 501 502 # Get the list of connected users 503 $t_users_connected = array(); 504 while( $t_row = db_fetch_array( $t_result ) ) { 505 $t_users_connected[] = (int)$t_row['id']; 506 } 507 508 return $t_users_connected; 509} 510 511/** 512 * Create a user. 513 * returns false if error, the generated cookie string if valid 514 * 515 * @param string $p_username A valid username. 516 * @param string $p_password The password to set for the user. 517 * @param string $p_email The Email Address of the user. 518 * @param integer $p_access_level The global access level for the user. 519 * @param boolean $p_protected Whether the account is protected from modifications (default false). 520 * @param boolean $p_enabled Whether the account is enabled. 521 * @param string $p_realname The realname of the user. 522 * @param string $p_admin_name The name of the administrator creating the account. 523 * @return string Cookie String 524 */ 525function user_create( $p_username, $p_password, $p_email = '', 526 $p_access_level = null, $p_protected = false, $p_enabled = true, 527 $p_realname = '', $p_admin_name = '' ) { 528 if( null === $p_access_level ) { 529 $p_access_level = config_get( 'default_new_account_access_level' ); 530 } 531 532 $t_password = auth_process_plain_password( $p_password ); 533 534 $c_enabled = (bool)$p_enabled; 535 536 user_ensure_name_valid( $p_username ); 537 user_ensure_name_unique( $p_username ); 538 user_ensure_email_unique( $p_email ); 539 email_ensure_valid( $p_email ); 540 email_ensure_not_disposable( $p_email ); 541 542 $t_cookie_string = auth_generate_unique_cookie_string(); 543 544 db_param_push(); 545 $t_query = 'INSERT INTO {user} 546 ( username, email, password, date_created, last_visit, 547 enabled, access_level, login_count, cookie_string, realname ) 548 VALUES 549 ( ' . db_param() . ', ' . db_param() . ', ' . db_param() . ', ' . db_param() . ', ' . db_param() . ', 550 ' . db_param() . ',' . db_param() . ',' . db_param() . ',' . db_param() . ', ' . db_param() . ')'; 551 db_query( $t_query, array( $p_username, $p_email, $t_password, db_now(), db_now(), $c_enabled, (int)$p_access_level, 0, $t_cookie_string, $p_realname ) ); 552 553 # Create preferences for the user 554 $t_user_id = db_insert_id( db_get_table( 'user' ) ); 555 556 # Users are added with protected set to FALSE in order to be able to update 557 # preferences. Now set the real value of protected. 558 if( $p_protected ) { 559 user_set_field( $t_user_id, 'protected', (bool)$p_protected ); 560 } 561 562 # Send notification email 563 if( !is_blank( $p_email ) ) { 564 $t_confirm_hash = auth_generate_confirm_hash( $t_user_id ); 565 token_set( TOKEN_ACCOUNT_ACTIVATION, $t_confirm_hash, TOKEN_EXPIRY_ACCOUNT_ACTIVATION, $t_user_id ); 566 email_signup( $t_user_id, $t_confirm_hash, $p_admin_name ); 567 } 568 569 event_signal( 'EVENT_MANAGE_USER_CREATE', array( $t_user_id ) ); 570 571 return $t_cookie_string; 572} 573 574/** 575 * Signup a user. 576 * If the use_ldap_email config option is on then tries to find email using 577 * ldap. $p_email may be empty, but the user won't get any emails. 578 * returns false if error, the generated cookie string if ok 579 * @param string $p_username The username to sign up. 580 * @param string $p_email The email address of the user signing up. 581 * @return string|boolean cookie string or false on error 582 */ 583function user_signup( $p_username, $p_email = null ) { 584 if( null === $p_email ) { 585 $p_email = ''; 586 587 # @@@ I think the ldap_email stuff is a bit borked 588 # Where is it being set? When is it being used? 589 # Shouldn't we override an email that is passed in here? 590 # If the user doesn't exist in ldap, is the account created? 591 # If so, there password won't get set anywhere... (etc) 592 # RJF: I was going to check for the existence of an LDAP email. 593 # however, since we can't create an LDAP account at the moment, 594 # and we don't know the user password in advance, we may not be able 595 # to retrieve it anyway. 596 # I'll re-enable this once a plan has been properly formulated for LDAP 597 # account management and creation. 598 # $t_email = ''; 599 # if( ON == config_get_global( 'use_ldap_email' ) ) { 600 # $t_email = ldap_email_from_username( $p_username ); 601 # } 602 # if( !is_blank( $t_email ) ) { 603 # $p_email = $t_email; 604 # } 605 } 606 607 $p_email = trim( $p_email ); 608 609 email_ensure_not_disposable( $p_email ); 610 email_ensure_valid( $p_email ); 611 user_ensure_email_unique( $p_email ); 612 613 # Create random password 614 $t_password = auth_generate_random_password(); 615 616 return user_create( $p_username, $t_password, $p_email, auth_signup_access_level() ); 617} 618 619/** 620 * delete project-specific user access levels. 621 * returns true when successfully deleted 622 * 623 * @param integer $p_user_id A valid user identifier. 624 * @return boolean Always true 625 */ 626function user_delete_project_specific_access_levels( $p_user_id ) { 627 user_ensure_unprotected( $p_user_id ); 628 629 db_param_push(); 630 $t_query = 'DELETE FROM {project_user_list} WHERE user_id=' . db_param(); 631 db_query( $t_query, array( (int)$p_user_id ) ); 632 633 user_clear_cache( $p_user_id ); 634 635 return true; 636} 637 638/** 639 * delete profiles for the specified user 640 * returns true when successfully deleted 641 * @param integer $p_user_id A valid user identifier. 642 * @return boolean 643 */ 644function user_delete_profiles( $p_user_id ) { 645 user_ensure_unprotected( $p_user_id ); 646 647 # Remove associated profiles 648 db_param_push(); 649 $t_query = 'DELETE FROM {user_profile} WHERE user_id=' . db_param(); 650 db_query( $t_query, array( (int)$p_user_id ) ); 651 652 user_clear_cache( $p_user_id ); 653 654 return true; 655} 656 657/** 658 * delete a user account (account, profiles, preferences, project-specific access levels) 659 * returns true when the account was successfully deleted 660 * 661 * @param integer $p_user_id A valid user identifier. 662 * @return boolean Always true 663 */ 664function user_delete( $p_user_id ) { 665 $c_user_id = (int)$p_user_id; 666 667 user_ensure_unprotected( $p_user_id ); 668 669 event_signal( 'EVENT_MANAGE_USER_DELETE', array( $p_user_id ) ); 670 671 # Remove associated profiles 672 user_delete_profiles( $p_user_id ); 673 674 # Remove associated preferences 675 user_pref_db_delete_user( $p_user_id ); 676 677 # Remove project specific access levels 678 user_delete_project_specific_access_levels( $p_user_id ); 679 user_clear_cache( $p_user_id ); 680 681 # Remove account 682 db_param_push(); 683 $t_query = 'DELETE FROM {user} WHERE id=' . db_param(); 684 db_query( $t_query, array( $c_user_id ) ); 685 686 return true; 687} 688 689/** 690 * get a user id from a username 691 * return false if the username does not exist 692 * 693 * @param string $p_username The username to retrieve data for. 694 * @param boolean $p_throw true to throw if not found, false otherwise. 695 * @return integer|boolean 696 */ 697function user_get_id_by_name( $p_username, $p_throw = false ) { 698 if( $t_user = user_search_cache( 'username', $p_username ) ) { 699 return (int)$t_user['id']; 700 } 701 702 db_param_push(); 703 $t_query = 'SELECT * FROM {user} WHERE username=' . db_param(); 704 $t_result = db_query( $t_query, array( $p_username ) ); 705 706 $t_row = db_fetch_array( $t_result ); 707 if( $t_row ) { 708 user_cache_database_result( $t_row ); 709 return (int)$t_row['id']; 710 } 711 712 if( $p_throw ) { 713 throw new ClientException( 714 "Username '$p_username' not found", 715 ERROR_USER_BY_NAME_NOT_FOUND, 716 array( $p_username ) ); 717 } 718 719 return false; 720} 721 722/** 723 * Get a user id from their email address 724 * 725 * @param string $p_email The email address to retrieve data for. 726 * @param boolean $p_throw true to throw exception when not found, false otherwise. 727 * @return integer|boolean 728 */ 729function user_get_id_by_email( $p_email, $p_throw = false ) { 730 if( $t_user = user_search_cache( 'email', $p_email ) ) { 731 return (int)$t_user['id']; 732 } 733 734 db_param_push(); 735 $t_query = 'SELECT * FROM {user} WHERE email=' . db_param(); 736 $t_result = db_query( $t_query, array( $p_email ) ); 737 738 $t_row = db_fetch_array( $t_result ); 739 if( $t_row ) { 740 user_cache_database_result( $t_row ); 741 return (int)$t_row['id']; 742 } 743 744 if( $p_throw ) { 745 throw new ClientException( 746 "User with email '$p_email' not found", 747 ERROR_USER_BY_EMAIL_NOT_FOUND, 748 array( $p_email ) ); 749 } 750 751 return false; 752} 753 754/** 755 * Given an email address, this method returns the ids of the enabled users with 756 * that address. 757 * 758 * The returned list will be sorted by higher access level first. 759 * 760 * @param string $p_email The email address, can be an empty string to get users 761 * without an email address. 762 * 763 * @return array The user ids or an empty array. 764 */ 765function user_get_enabled_ids_by_email( $p_email ) { 766 db_param_push(); 767 $t_query = 'SELECT * FROM {user} WHERE email=' . db_param() . 768 ' AND enabled=' . db_param() . ' ORDER BY access_level DESC'; 769 $t_result = db_query( $t_query, array( $p_email, 1 ) ); 770 771 $t_user_ids = array(); 772 while ( $t_row = db_fetch_array( $t_result ) ) { 773 user_cache_database_result( $t_row ); 774 $t_user_ids[] = (int)$t_row['id']; 775 } 776 777 return $t_user_ids; 778} 779 780/** 781 * Get a user id from their real name 782 * 783 * @param string $p_realname The realname to retrieve data for. 784 * @param boolean $p_throw true to throw if not found, false otherwise. 785 * @return integer|boolean 786 */ 787function user_get_id_by_realname( $p_realname, $p_throw = false ) { 788 if( $t_user = user_search_cache( 'realname', $p_realname ) ) { 789 return (int)$t_user['id']; 790 } 791 792 db_param_push(); 793 $t_query = 'SELECT * FROM {user} WHERE realname=' . db_param(); 794 $t_result = db_query( $t_query, array( $p_realname ) ); 795 796 $t_row = db_fetch_array( $t_result ); 797 798 if( !$t_row ) { 799 if( $p_throw ) { 800 throw new ClientException( "User realname '$p_realname' not found", ERROR_USER_BY_NAME_NOT_FOUND, array( $p_realname ) ); 801 } 802 803 return false; 804 } 805 806 user_cache_database_result( $t_row ); 807 return (int)$t_row['id']; 808} 809 810/** 811 * Get a user id from their cookie string 812 * 813 * @param string $p_cookie_string The cookie string to retrieve data for. 814 * @param boolean $p_throw true to throw if not found, false otherwise. 815 * 816 * @return int|false User Id, false if cookie string not found 817 * 818 * @throws ClientException 819 */ 820function user_get_id_by_cookie( $p_cookie_string, $p_throw = false ) { 821 if( $t_user = user_search_cache( 'cookie_string', $p_cookie_string ) ) { 822 return (int)$t_user['id']; 823 } 824 825 db_param_push(); 826 $t_query = 'SELECT * FROM {user} WHERE cookie_string=' . db_param(); 827 $t_result = db_query( $t_query, array( $p_cookie_string ) ); 828 829 $t_row = db_fetch_array( $t_result ); 830 831 if( !$t_row ) { 832 if( $p_throw ) { 833 throw new ClientException( 834 "User Cookie String '$p_cookie_string' not found", 835 ERROR_USER_BY_NAME_NOT_FOUND, 836 array( $p_cookie_string ) 837 ); 838 } 839 return false; 840 } 841 842 user_cache_database_result( $t_row ); 843 return (int)$t_row['id']; 844} 845 846/** 847 * Get a user id given an array that may have id, name, real_name, email, or name_or_realname. 848 * 849 * @param array $p_user The user info. 850 * @param boolean $p_throw_if_id_not_found If id specified and doesn't exist, then throw. 851 * @return integer user id 852 * @throws ClientException 853 */ 854function user_get_id_by_user_info( array $p_user, $p_throw_if_id_not_found = false ) { 855 if( isset( $p_user['id'] ) && (int)$p_user['id'] != 0 ) { 856 $t_user_id = (int)$p_user['id']; 857 if( $p_throw_if_id_not_found && !user_exists( $t_user_id ) ) { 858 throw new ClientException( 859 sprintf( "User with id '%d' doesn't exist", $t_user_id ), 860 ERROR_USER_BY_ID_NOT_FOUND, 861 array( $t_user_id ) ); 862 } 863 } else if( isset( $p_user['name'] ) && !is_blank( $p_user['name'] ) ) { 864 $t_user_id = user_get_id_by_name( $p_user['name'], /* throw */ true ); 865 } else if( isset( $p_user['email'] ) && !is_blank( $p_user['email'] ) ) { 866 $t_user_id = user_get_id_by_email( $p_user['email'], /* throw */ true ); 867 } else if( isset( $p_user['real_name'] ) && !is_blank( $p_user['real_name'] ) ) { 868 $t_user_id = user_get_id_by_realname( $p_user['real_name'], /* throw */ true ); 869 } else if( isset( $p_user['name_or_realname' ] ) && !is_blank( $p_user['name_or_realname' ] ) ) { 870 $t_identifier = $p_user['name_or_realname']; 871 $t_user_id = user_get_id_by_name( $t_identifier ); 872 873 if( !$t_user_id ) { 874 $t_user_id = user_get_id_by_realname( $t_identifier ); 875 } 876 877 if( !$t_user_id ) { 878 throw new ClientException( 879 "User '$t_identifier' not found", 880 ERROR_USER_BY_NAME_NOT_FOUND, 881 array( $t_identifier ) ); 882 } 883 } else { 884 throw new ClientException( 885 "User id missing", 886 ERROR_GPC_VAR_NOT_FOUND, 887 array( 'user id' ) ); 888 } 889 890 return $t_user_id; 891} 892 893/** 894 * return all data associated with a particular user name 895 * return false if the username does not exist 896 * 897 * @param integer $p_username The username to retrieve data for. 898 * @return array 899 */ 900function user_get_row_by_name( $p_username ) { 901 $t_user_id = user_get_id_by_name( $p_username ); 902 903 if( false === $t_user_id ) { 904 return false; 905 } 906 907 $t_row = user_get_row( $t_user_id ); 908 909 return $t_row; 910} 911 912/** 913 * return a user row 914 * 915 * @param integer $p_user_id A valid user identifier. 916 * @return array 917 */ 918function user_get_row( $p_user_id ) { 919 return user_cache_row( $p_user_id ); 920} 921 922/** 923 * return the specified user field for the user id 924 * 925 * @param integer $p_user_id A valid user identifier. 926 * @param string $p_field_name The field name to retrieve. 927 * @return string 928 */ 929function user_get_field( $p_user_id, $p_field_name ) { 930 if( NO_USER == $p_user_id ) { 931 error_parameters( NO_USER ); 932 trigger_error( ERROR_USER_BY_ID_NOT_FOUND, WARNING ); 933 return '@null@'; 934 } 935 936 $t_row = user_get_row( $p_user_id ); 937 938 if( isset( $t_row[$p_field_name] ) ) { 939 switch( $p_field_name ) { 940 case 'access_level': 941 return (int)$t_row[$p_field_name]; 942 default: 943 return $t_row[$p_field_name]; 944 } 945 } else { 946 error_parameters( $p_field_name ); 947 trigger_error( ERROR_DB_FIELD_NOT_FOUND, WARNING ); 948 return ''; 949 } 950} 951 952/** 953 * lookup the user's email in LDAP or the db as appropriate 954 * 955 * @param integer $p_user_id A valid user identifier. 956 * @return string 957 */ 958function user_get_email( $p_user_id ) { 959 $t_email = ''; 960 if( LDAP == config_get_global( 'login_method' ) && ON == config_get_global( 'use_ldap_email' ) ) { 961 $t_email = ldap_email( $p_user_id ); 962 } 963 if( is_blank( $t_email ) ) { 964 $t_email = user_get_field( $p_user_id, 'email' ); 965 } 966 return $t_email; 967} 968 969/** 970 * Lookup the user's login name (username) 971 * 972 * @param integer $p_user_id A valid user identifier. 973 * @return string 974 */ 975function user_get_username( $p_user_id ) { 976 $t_row = user_cache_row( $p_user_id, false ); 977 if( false == $t_row ) { 978 return lang_get( 'prefix_for_deleted_users' ) . (int)$p_user_id; 979 } 980 981 return $t_row['username']; 982} 983 984/** 985 * lookup the user's realname 986 * 987 * @param integer $p_user_id A valid user identifier. 988 * @return string 989 */ 990function user_get_realname( $p_user_id ) { 991 $t_realname = ''; 992 993 if( LDAP == config_get_global( 'login_method' ) && ON == config_get_global( 'use_ldap_realname' ) ) { 994 $t_realname = ldap_realname( $p_user_id ); 995 } 996 997 if( is_blank( $t_realname ) ) { 998 $t_realname = user_get_field( $p_user_id, 'realname' ); 999 } 1000 1001 return $t_realname; 1002} 1003 1004/** 1005 * Return the user's name for display. 1006 * 1007 * The name is determined based on the following sequence: 1008 * - if the user does not exist, returns the user ID prefixed by a localized 1009 * string (prefix_for_deleted_users, "user" by default); 1010 * - if user_show_realname() is true and realname is not empty, return the user's Real Name; 1011 * - Otherwise, return the username 1012 * 1013 * NOTE: do not use this function to retrieve the user's username 1014 * @see user_get_username() 1015 * 1016 * @param integer $p_user_id A valid user identifier. 1017 * 1018 * @return string 1019 */ 1020function user_get_name( $p_user_id ) { 1021 $t_row = user_cache_row( $p_user_id, false ); 1022 1023 if( false == $t_row ) { 1024 return lang_get( 'prefix_for_deleted_users' ) . (int)$p_user_id; 1025 } 1026 1027 return user_get_name_from_row( $t_row ); 1028} 1029 1030/** 1031 * Should realnames be shown to logged in user? 1032 * 1033 * @return bool true to show, false otherwise. 1034 */ 1035function user_show_realname() { 1036 return config_get( 'show_realname' ) == ON; 1037} 1038 1039/** 1040 * Return the user's name for display. If user_show_realname() is true and realname is not empty 1041 * return realname otherwise return username. 1042 * 1043 * @param array $p_user_row The user row with 'realname' and 'username' fields 1044 * @return string display name 1045 */ 1046function user_get_name_from_row( array $p_user_row ) { 1047 if( user_show_realname() ) { 1048 if( !is_blank( $p_user_row['realname'] ) ) { 1049 return $p_user_row['realname']; 1050 } 1051 } 1052 1053 return $p_user_row['username']; 1054} 1055 1056/** 1057 * Return display name in format "realname (username)" if user_show_realname() is true and 1058 * realname is not empty otherwise return username 1059 * 1060 * @param array $p_user_row The user row with 'realname' and 'username' fields 1061 * @return string display name 1062 */ 1063function user_get_expanded_name_from_row( array $p_user_row ) { 1064 $t_name = user_get_name_from_row( $p_user_row ); 1065 if( $t_name != $p_user_row['username'] ) { 1066 return $t_name . ' (' . $p_user_row['username'] . ')'; 1067 } 1068 1069 return $p_user_row['username']; 1070} 1071 1072/** 1073 * Get name used for sorting. 1074 * 1075 * @param array $p_user_row The user row with 'realname' and 'username' fields 1076 * @return string name for sorting 1077 */ 1078function user_get_name_for_sorting_from_row( array $p_user_row ) { 1079 if( !is_blank( $p_user_row['realname'] ) ) { 1080 if( user_show_realname() ) { 1081 if( config_get( 'sort_by_last_name' ) == ON ) { 1082 $t_sort_name_bits = explode( ' ', mb_strtolower( trim( $p_user_row['realname'] ) ), 2 ); 1083 return ( isset( $t_sort_name_bits[1] ) ? $t_sort_name_bits[1] . ', ' : '' ) . $t_sort_name_bits[0]; 1084 } 1085 1086 return mb_strtolower( trim( $p_user_row['realname'] ) ); 1087 } 1088 } 1089 1090 return mb_strtolower( $p_user_row['username'] ); 1091} 1092 1093/** 1094 * return the user's access level 1095 * account for private project and the project user lists 1096 * 1097 * @param integer $p_user_id A valid user identifier. 1098 * @param integer $p_project_id A valid project identifier. 1099 * @return integer 1100 */ 1101function user_get_access_level( $p_user_id, $p_project_id = ALL_PROJECTS ) { 1102 $t_access_level = user_get_field( $p_user_id, 'access_level' ); 1103 1104 if( user_is_administrator( $p_user_id ) ) { 1105 return $t_access_level; 1106 } 1107 1108 $t_project_access_level = access_get_local_level( $p_user_id, $p_project_id ); 1109 1110 if( false === $t_project_access_level ) { 1111 return $t_access_level; 1112 } else { 1113 return $t_project_access_level; 1114 } 1115} 1116 1117$g_user_accessible_projects_cache = null; 1118 1119/** 1120 * return an array of project IDs to which the user has access 1121 * 1122 * @param integer $p_user_id A valid user identifier. 1123 * @param boolean $p_show_disabled Whether to include disabled projects in the result array. 1124 * @return array 1125 */ 1126function user_get_accessible_projects( $p_user_id, $p_show_disabled = false ) { 1127 global $g_user_accessible_projects_cache; 1128 1129 if( null !== $g_user_accessible_projects_cache && auth_get_current_user_id() == $p_user_id && false == $p_show_disabled ) { 1130 return $g_user_accessible_projects_cache; 1131 } 1132 1133 if( access_has_global_level( config_get( 'private_project_threshold' ), $p_user_id ) ) { 1134 $t_projects = project_hierarchy_get_subprojects( ALL_PROJECTS, $p_show_disabled ); 1135 } else { 1136 $t_public = VS_PUBLIC; 1137 $t_private = VS_PRIVATE; 1138 1139 db_param_push(); 1140 $t_query = 'SELECT p.id, p.name, ph.parent_id 1141 FROM {project} p 1142 LEFT JOIN {project_user_list} u 1143 ON p.id=u.project_id AND u.user_id=' . db_param() . ' 1144 LEFT JOIN {project_hierarchy} ph 1145 ON ph.child_id = p.id 1146 WHERE ' . ( $p_show_disabled ? '' : ( 'p.enabled = ' . db_param() . ' AND ' ) ) . ' 1147 ( p.view_state=' . db_param() . ' 1148 OR (p.view_state=' . db_param() . ' 1149 AND 1150 u.user_id=' . db_param() . ' ) 1151 ) ORDER BY p.name'; 1152 $t_result = db_query( $t_query, ( $p_show_disabled ? array( $p_user_id, $t_public, $t_private, $p_user_id ) : array( $p_user_id, true, $t_public, $t_private, $p_user_id ) ) ); 1153 1154 $t_projects = array(); 1155 1156 while( $t_row = db_fetch_array( $t_result ) ) { 1157 $t_projects[(int)$t_row['id']] = ( $t_row['parent_id'] === null ) ? 0 : (int)$t_row['parent_id']; 1158 } 1159 1160 # prune out children where the parents are already listed. Make the list 1161 # first, then prune to avoid pruning a parent before the child is found. 1162 $t_prune = array(); 1163 foreach( $t_projects as $t_id => $t_parent ) { 1164 if( ( $t_parent !== 0 ) && isset( $t_projects[$t_parent] ) ) { 1165 $t_prune[] = $t_id; 1166 } 1167 } 1168 foreach( $t_prune as $t_id ) { 1169 unset( $t_projects[$t_id] ); 1170 } 1171 $t_projects = array_keys( $t_projects ); 1172 } 1173 1174 if( auth_get_current_user_id() == $p_user_id ) { 1175 $g_user_accessible_projects_cache = $t_projects; 1176 } 1177 1178 return $t_projects; 1179} 1180 1181/** 1182 * return an array of sub-project IDs of a certain project to which the user has access 1183 * @param integer $p_user_id A valid user identifier. 1184 * @param integer $p_project_id A valid project identifier. 1185 * @param boolean $p_show_disabled Include disabled projects in the resulting array. 1186 * @return array 1187 */ 1188function user_get_accessible_subprojects( $p_user_id, $p_project_id, $p_show_disabled = false ) { 1189 global $g_user_accessible_subprojects_cache; 1190 1191 if( null !== $g_user_accessible_subprojects_cache && auth_get_current_user_id() == $p_user_id && false == $p_show_disabled ) { 1192 if( isset( $g_user_accessible_subprojects_cache[$p_project_id] ) ) { 1193 return $g_user_accessible_subprojects_cache[$p_project_id]; 1194 } else { 1195 return array(); 1196 } 1197 } 1198 1199 db_param_push(); 1200 1201 if( access_has_global_level( config_get( 'private_project_threshold' ), $p_user_id ) ) { 1202 $t_enabled_clause = $p_show_disabled ? '' : 'p.enabled = ' . db_param() . ' AND'; 1203 $t_query = 'SELECT DISTINCT p.id, p.name, ph.parent_id 1204 FROM {project} p 1205 LEFT JOIN {project_hierarchy} ph 1206 ON ph.child_id = p.id 1207 WHERE ' . $t_enabled_clause . ' 1208 ph.parent_id IS NOT NULL 1209 ORDER BY p.name'; 1210 $t_result = db_query( $t_query, ( $p_show_disabled ? array() : array( true ) ) ); 1211 } else { 1212 $t_query = 'SELECT DISTINCT p.id, p.name, ph.parent_id 1213 FROM {project} p 1214 LEFT JOIN {project_user_list} u 1215 ON p.id = u.project_id AND u.user_id=' . db_param() . ' 1216 LEFT JOIN {project_hierarchy} ph 1217 ON ph.child_id = p.id 1218 WHERE ' . ( $p_show_disabled ? '' : ( 'p.enabled = ' . db_param() . ' AND ' ) ) . ' 1219 ph.parent_id IS NOT NULL AND 1220 ( p.view_state=' . db_param() . ' 1221 OR (p.view_state=' . db_param() . ' 1222 AND 1223 u.user_id=' . db_param() . ' ) 1224 ) 1225 ORDER BY p.name'; 1226 $t_param = array( $p_user_id, VS_PUBLIC, VS_PRIVATE, $p_user_id ); 1227 if( !$p_show_disabled ) { 1228 # Insert enabled flag value in 2nd position of parameter array 1229 array_splice( $t_param, 1, 0, true ); 1230 } 1231 $t_result = db_query( $t_query, $t_param ); 1232 } 1233 1234 $t_projects = array(); 1235 1236 while( $t_row = db_fetch_array( $t_result ) ) { 1237 if( !isset( $t_projects[(int)$t_row['parent_id']] ) ) { 1238 $t_projects[(int)$t_row['parent_id']] = array(); 1239 } 1240 1241 array_push( $t_projects[(int)$t_row['parent_id']], (int)$t_row['id'] ); 1242 } 1243 1244 if( auth_get_current_user_id() == $p_user_id ) { 1245 $g_user_accessible_subprojects_cache = $t_projects; 1246 } 1247 1248 if( !isset( $t_projects[(int)$p_project_id] ) ) { 1249 $t_projects[(int)$p_project_id] = array(); 1250 } 1251 1252 return $t_projects[(int)$p_project_id]; 1253} 1254 1255/** 1256 * return an array of sub-project IDs of all sub-projects project to which the user has access 1257 * @param integer $p_user_id A valid user identifier. 1258 * @param integer $p_project_id A valid project identifier. 1259 * @return array 1260 */ 1261function user_get_all_accessible_subprojects( $p_user_id, $p_project_id ) { 1262 # @todo (thraxisp) Should all top level projects be a sub-project of ALL_PROJECTS implicitly? 1263 # affects how news and some summaries are generated 1264 $t_todo = user_get_accessible_subprojects( $p_user_id, $p_project_id ); 1265 $t_subprojects = array(); 1266 1267 while( $t_todo ) { 1268 $t_elem = (int)array_shift( $t_todo ); 1269 if( !in_array( $t_elem, $t_subprojects ) ) { 1270 array_push( $t_subprojects, $t_elem ); 1271 $t_todo = array_merge( $t_todo, user_get_accessible_subprojects( $p_user_id, $t_elem ) ); 1272 } 1273 } 1274 1275 return $t_subprojects; 1276} 1277 1278/** 1279 * Returns an array of project and sub-project IDs of all projects to which the 1280 * user has access and that are children of the specified project. 1281 * 1282 * @param integer $p_user_id A valid user identifier or null for logged in user. 1283 * @param integer $p_project_id A valid project identifier. ALL_PROJECTS returns 1284 * all top level projects and sub-projects. 1285 * @return array 1286 */ 1287function user_get_all_accessible_projects( $p_user_id = null, $p_project_id = ALL_PROJECTS ) { 1288 if( $p_user_id === null ) { 1289 $p_user_id = auth_get_current_user_id(); 1290 } 1291 1292 if( ALL_PROJECTS == $p_project_id ) { 1293 $t_top_projects = user_get_accessible_projects( $p_user_id ); 1294 1295 # Cover the case for PHP < 5.4 where array_combine() returns 1296 # false and triggers warning if arrays are empty (see #16187) 1297 if( empty( $t_top_projects ) ) { 1298 return array(); 1299 } 1300 1301 # Create a combined array where key = value 1302 $t_project_ids = array_combine( $t_top_projects, $t_top_projects ); 1303 1304 # Add all subprojects user has access to 1305 foreach( $t_top_projects as $t_project ) { 1306 $t_subprojects_ids = user_get_all_accessible_subprojects( $p_user_id, $t_project ); 1307 foreach( $t_subprojects_ids as $t_id ) { 1308 $t_project_ids[$t_id] = $t_id; 1309 } 1310 } 1311 } else { 1312 access_ensure_project_level( config_get( 'view_bug_threshold' ), $p_project_id ); 1313 $t_project_ids = user_get_all_accessible_subprojects( $p_user_id, $p_project_id ); 1314 array_unshift( $t_project_ids, $p_project_id ); 1315 } 1316 1317 return $t_project_ids; 1318} 1319 1320/** 1321 * Get a list of projects the specified user is assigned to. 1322 * @param integer $p_user_id A valid user identifier. 1323 * @return array An array of projects by project id the specified user is assigned to. 1324 * The array contains the id, name, view state, and project access level for the user. 1325 */ 1326function user_get_assigned_projects( $p_user_id ) { 1327 db_param_push(); 1328 $t_query = 'SELECT DISTINCT p.id, p.name, p.view_state, u.access_level 1329 FROM {project} p 1330 LEFT JOIN {project_user_list} u 1331 ON p.id=u.project_id 1332 WHERE p.enabled = \'1\' AND 1333 u.user_id=' . db_param() . ' 1334 ORDER BY p.name'; 1335 $t_result = db_query( $t_query, array( $p_user_id ) ); 1336 $t_projects = array(); 1337 while( $t_row = db_fetch_array( $t_result ) ) { 1338 $t_project_id = $t_row['id']; 1339 $t_projects[$t_project_id] = $t_row; 1340 } 1341 return $t_projects; 1342} 1343 1344/** 1345 * List of users that are NOT in the specified project and that are enabled 1346 * if no project is specified use the current project 1347 * also exclude any administrators 1348 * @param integer $p_project_id A valid project identifier. 1349 * @return array List of users not assigned to the specified project 1350 */ 1351function user_get_unassigned_by_project_id( $p_project_id = null ) { 1352 if( null === $p_project_id ) { 1353 $p_project_id = helper_get_current_project(); 1354 } 1355 1356 $t_adm = config_get_global( 'admin_site_threshold' ); 1357 db_param_push(); 1358 $t_query = 'SELECT DISTINCT u.id, u.username, u.realname 1359 FROM {user} u 1360 LEFT JOIN {project_user_list} p 1361 ON p.user_id=u.id AND p.project_id=' . db_param() . ' 1362 WHERE u.access_level<' . db_param() . ' AND 1363 u.enabled = ' . db_param() . ' AND 1364 p.user_id IS NULL 1365 ORDER BY u.realname, u.username'; 1366 $t_result = db_query( $t_query, array( $p_project_id, $t_adm, true ) ); 1367 $t_display = array(); 1368 $t_sort = array(); 1369 $t_users = array(); 1370 1371 while( $t_row = db_fetch_array( $t_result ) ) { 1372 $t_users[] = (int)$t_row['id']; 1373 $t_display[] = user_get_expanded_name_from_row( $t_row ); 1374 $t_sort[] = user_get_name_for_sorting_from_row( $t_row ); 1375 } 1376 1377 array_multisort( $t_sort, SORT_ASC, SORT_STRING, $t_users, $t_display ); 1378 1379 $t_count = count( $t_sort ); 1380 $t_user_list = array(); 1381 for( $i = 0;$i < $t_count; $i++ ) { 1382 $t_user_list[$t_users[$i]] = $t_display[$i]; 1383 } 1384 return $t_user_list; 1385} 1386 1387/** 1388 * return the number of open assigned bugs to a user in a project 1389 * 1390 * @param integer $p_user_id A valid user identifier. 1391 * @param integer $p_project_id A valid project identifier. 1392 * @return integer 1393 */ 1394function user_get_assigned_open_bug_count( $p_user_id, $p_project_id = ALL_PROJECTS ) { 1395 $t_where_prj = helper_project_specific_where( $p_project_id, $p_user_id ) . ' AND'; 1396 1397 $t_resolved = config_get( 'bug_resolved_status_threshold' ); 1398 1399 db_param_push(); 1400 $t_query = 'SELECT COUNT(*) 1401 FROM {bug} 1402 WHERE ' . $t_where_prj . ' 1403 status<' . db_param() . ' AND 1404 handler_id=' . db_param(); 1405 $t_result = db_query( $t_query, array( $t_resolved, $p_user_id ) ); 1406 1407 return db_result( $t_result ); 1408} 1409 1410/** 1411 * return the number of open reported bugs by a user in a project 1412 * 1413 * @param integer $p_user_id A valid user identifier. 1414 * @param integer $p_project_id A valid project identifier. 1415 * @return integer 1416 */ 1417function user_get_reported_open_bug_count( $p_user_id, $p_project_id = ALL_PROJECTS ) { 1418 $t_where_prj = helper_project_specific_where( $p_project_id, $p_user_id ) . ' AND'; 1419 1420 $t_resolved = config_get( 'bug_resolved_status_threshold' ); 1421 1422 db_param_push(); 1423 $t_query = 'SELECT COUNT(*) FROM {bug} 1424 WHERE ' . $t_where_prj . ' 1425 status<' . db_param() . ' AND 1426 reporter_id=' . db_param(); 1427 $t_result = db_query( $t_query, array( $t_resolved, $p_user_id ) ); 1428 1429 return db_result( $t_result ); 1430} 1431 1432/** 1433 * return a profile row 1434 * 1435 * @param integer $p_user_id A valid user identifier. 1436 * @param integer $p_profile_id The profile identifier to retrieve. 1437 * @return array 1438 */ 1439function user_get_profile_row( $p_user_id, $p_profile_id ) { 1440 db_param_push(); 1441 $t_query = 'SELECT * FROM {user_profile} 1442 WHERE id=' . db_param() . ' AND 1443 user_id=' . db_param(); 1444 $t_result = db_query( $t_query, array( $p_profile_id, $p_user_id ) ); 1445 1446 $t_row = db_fetch_array( $t_result ); 1447 1448 if( !$t_row ) { 1449 trigger_error( ERROR_USER_PROFILE_NOT_FOUND, ERROR ); 1450 } 1451 1452 return $t_row; 1453} 1454 1455/** 1456 * Get failed login attempts 1457 * 1458 * @param integer $p_user_id A valid user identifier. 1459 * @return boolean 1460 */ 1461function user_is_login_request_allowed( $p_user_id ) { 1462 $t_max_failed_login_count = config_get( 'max_failed_login_count' ); 1463 $t_failed_login_count = user_get_field( $p_user_id, 'failed_login_count' ); 1464 return( $t_failed_login_count < $t_max_failed_login_count || OFF == $t_max_failed_login_count ); 1465} 1466 1467/** 1468 * Get 'lost password' in progress attempts 1469 * 1470 * @param integer $p_user_id A valid user identifier. 1471 * @return boolean 1472 */ 1473function user_is_lost_password_request_allowed( $p_user_id ) { 1474 if( OFF == config_get( 'lost_password_feature' ) ) { 1475 return false; 1476 } 1477 $t_max_lost_password_in_progress_count = config_get( 'max_lost_password_in_progress_count' ); 1478 $t_lost_password_in_progress_count = user_get_field( $p_user_id, 'lost_password_request_count' ); 1479 return( $t_lost_password_in_progress_count < $t_max_lost_password_in_progress_count || OFF == $t_max_lost_password_in_progress_count ); 1480} 1481 1482/** 1483 * return the bug filter parameters for the specified user 1484 * 1485 * @param integer $p_user_id A valid user identifier. 1486 * @param integer $p_project_id A valid project identifier. 1487 * @return array The user filter, or default filter if not valid. 1488 */ 1489function user_get_bug_filter( $p_user_id, $p_project_id = null ) { 1490 if( null === $p_project_id ) { 1491 $t_project_id = helper_get_current_project(); 1492 } else { 1493 $t_project_id = $p_project_id; 1494 } 1495 1496 # Currently we use the filters saved in db as "current" special filters, 1497 # to track the active settings for filters in use. 1498 1499 # for anonymous user, we don't allow using persistent filter 1500 # if this function is reached, we return a default filter for it. 1501 if( user_is_anonymous( $p_user_id ) ) { 1502 return filter_get_default(); 1503 } 1504 1505 $t_filter_id = filter_db_get_project_current( $t_project_id, $p_user_id ); 1506 if( $t_filter_id ) { 1507 return filter_get( $t_filter_id ); 1508 } else { 1509 return filter_get_default(); 1510 } 1511} 1512 1513/** 1514 * Update the last_visited field to be now 1515 * 1516 * @param integer $p_user_id A valid user identifier. 1517 * @return boolean always true 1518 */ 1519function user_update_last_visit( $p_user_id ) { 1520 $c_user_id = (int)$p_user_id; 1521 $c_value = db_now(); 1522 1523 db_param_push(); 1524 $t_query = 'UPDATE {user} SET last_visit=' . db_param() . ' WHERE id=' . db_param(); 1525 db_query( $t_query, array( $c_value, $c_user_id ) ); 1526 1527 user_update_cache( $c_user_id, 'last_visit', $c_value ); 1528 1529 return true; 1530} 1531 1532/** 1533 * Increment the number of times the user has logged in 1534 * This function is only called from the login.php script 1535 * 1536 * @param integer $p_user_id A valid user identifier. 1537 * @return boolean always true 1538 */ 1539function user_increment_login_count( $p_user_id ) { 1540 db_param_push(); 1541 $t_query = 'UPDATE {user} SET login_count=login_count+1 WHERE id=' . db_param(); 1542 db_query( $t_query, array( (int)$p_user_id ) ); 1543 1544 user_clear_cache( $p_user_id ); 1545 1546 return true; 1547} 1548 1549/** 1550 * Reset to zero the failed login attempts 1551 * 1552 * @param integer $p_user_id A valid user identifier. 1553 * @return boolean always true 1554 */ 1555function user_reset_failed_login_count_to_zero( $p_user_id ) { 1556 db_param_push(); 1557 $t_query = 'UPDATE {user} SET failed_login_count=0 WHERE id=' . db_param(); 1558 db_query( $t_query, array( (int)$p_user_id ) ); 1559 1560 user_clear_cache( $p_user_id ); 1561 1562 return true; 1563} 1564 1565/** 1566 * Increment the failed login count by 1 1567 * 1568 * @param integer $p_user_id A valid user identifier. 1569 * @return boolean always true 1570 */ 1571function user_increment_failed_login_count( $p_user_id ) { 1572 db_param_push(); 1573 $t_query = 'UPDATE {user} SET failed_login_count=failed_login_count+1 WHERE id=' . db_param(); 1574 db_query( $t_query, array( $p_user_id ) ); 1575 1576 user_clear_cache( $p_user_id ); 1577 1578 return true; 1579} 1580 1581/** 1582 * Reset to zero the 'lost password' in progress attempts 1583 * 1584 * @param integer $p_user_id A valid user identifier. 1585 * @return boolean always true 1586 */ 1587function user_reset_lost_password_in_progress_count_to_zero( $p_user_id ) { 1588 db_param_push(); 1589 $t_query = 'UPDATE {user} SET lost_password_request_count=0 WHERE id=' . db_param(); 1590 db_query( $t_query, array( $p_user_id ) ); 1591 1592 user_clear_cache( $p_user_id ); 1593 1594 return true; 1595} 1596 1597/** 1598 * Increment the failed login count by 1 1599 * 1600 * @param integer $p_user_id A valid user identifier. 1601 * @return boolean always true 1602 */ 1603function user_increment_lost_password_in_progress_count( $p_user_id ) { 1604 db_param_push(); 1605 $t_query = 'UPDATE {user} 1606 SET lost_password_request_count=lost_password_request_count+1 1607 WHERE id=' . db_param(); 1608 db_query( $t_query, array( $p_user_id ) ); 1609 1610 user_clear_cache( $p_user_id ); 1611 1612 return true; 1613} 1614 1615/** 1616 * Sets multiple fields on a user 1617 * 1618 * @param integer $p_user_id A valid user identifier. 1619 * @param array $p_fields Keys are the field names and the values are the field values. 1620 * @return void 1621 */ 1622function user_set_fields( $p_user_id, array $p_fields ) { 1623 if( empty( $p_fields ) ) { 1624 return; 1625 } 1626 1627 if( !array_key_exists( 'protected', $p_fields ) ) { 1628 user_ensure_unprotected( $p_user_id ); 1629 } 1630 1631 db_param_push(); 1632 $t_query = 'UPDATE {user}'; 1633 $t_parameters = array(); 1634 1635 foreach ( $p_fields as $t_field_name => $t_field_value ) { 1636 $c_field_name = db_prepare_string( $t_field_name ); 1637 1638 if( count( $t_parameters ) == 0 ) { 1639 $t_query .= ' SET '. $c_field_name. '=' . db_param(); 1640 } else { 1641 $t_query .= ' , ' . $c_field_name. '=' . db_param(); 1642 } 1643 1644 array_push( $t_parameters, $t_field_value ); 1645 } 1646 1647 $t_query .= ' WHERE id=' . db_param(); 1648 array_push( $t_parameters, (int)$p_user_id ); 1649 1650 db_query( $t_query, $t_parameters ); 1651 1652 user_clear_cache( $p_user_id ); 1653} 1654 1655/** 1656 * Set a user field 1657 * 1658 * @param integer $p_user_id A valid user identifier. 1659 * @param string $p_field_name A valid field name to set. 1660 * @param string $p_field_value The field value to set. 1661 * @return boolean always true 1662 */ 1663function user_set_field( $p_user_id, $p_field_name, $p_field_value ) { 1664 user_set_fields( $p_user_id, array ( $p_field_name => $p_field_value ) ); 1665 1666 return true; 1667} 1668 1669/** 1670 * Set Users Default project in preferences 1671 * @param integer $p_user_id A valid user identifier. 1672 * @param integer $p_project_id A valid project identifier. 1673 * @return void 1674 */ 1675function user_set_default_project( $p_user_id, $p_project_id ) { 1676 user_pref_set_pref( $p_user_id, 'default_project', (int)$p_project_id ); 1677} 1678 1679/** 1680 * Set the user's password to the given string, encoded as appropriate 1681 * 1682 * @param integer $p_user_id A valid user identifier. 1683 * @param string $p_password A password to set. 1684 * @param boolean $p_allow_protected Whether Allow password change to a protected account. This defaults to false. 1685 * @return boolean always true 1686 */ 1687function user_set_password( $p_user_id, $p_password, $p_allow_protected = false ) { 1688 if( !$p_allow_protected ) { 1689 user_ensure_unprotected( $p_user_id ); 1690 } 1691 1692 # When the password is changed, invalidate the cookie to expire sessions that 1693 # may be active on all browsers. 1694 $c_cookie_string = auth_generate_unique_cookie_string(); 1695 # Delete token for password activation if there is any 1696 token_delete( TOKEN_ACCOUNT_ACTIVATION, $p_user_id ); 1697 1698 $c_password = auth_process_plain_password( $p_password ); 1699 1700 db_param_push(); 1701 $t_query = 'UPDATE {user} 1702 SET password=' . db_param() . ', cookie_string=' . db_param() . ' 1703 WHERE id=' . db_param(); 1704 db_query( $t_query, array( $c_password, $c_cookie_string, (int)$p_user_id ) ); 1705 1706 return true; 1707} 1708 1709/** 1710 * Set the user's email to the given string after checking that it is a valid email 1711 * @param integer $p_user_id A valid user identifier. 1712 * @param string $p_email An email address to set. 1713 * @return boolean 1714 */ 1715function user_set_email( $p_user_id, $p_email ) { 1716 $p_email = trim( $p_email ); 1717 1718 email_ensure_valid( $p_email ); 1719 email_ensure_not_disposable( $p_email ); 1720 1721 $t_old_email = user_get_email( $p_user_id ); 1722 if( strcasecmp( $t_old_email, $p_email ) != 0 ) { 1723 user_ensure_email_unique( $p_email ); 1724 } 1725 1726 return user_set_field( $p_user_id, 'email', $p_email ); 1727} 1728 1729/** 1730 * Set the user's realname to the given string after checking validity 1731 * @param integer $p_user_id A valid user identifier. 1732 * @param string $p_realname A realname to set. 1733 * @return boolean 1734 */ 1735function user_set_realname( $p_user_id, $p_realname ) { 1736 return user_set_field( $p_user_id, 'realname', $p_realname ); 1737} 1738 1739/** 1740 * Set the user's username to the given string after checking that it is valid 1741 * @param integer $p_user_id A valid user identifier. 1742 * @param string $p_username A valid username to set. 1743 * @return boolean 1744 */ 1745function user_set_name( $p_user_id, $p_username ) { 1746 user_ensure_name_valid( $p_username ); 1747 user_ensure_name_unique( $p_username, $p_user_id ); 1748 1749 return user_set_field( $p_user_id, 'username', $p_username ); 1750} 1751 1752/** 1753 * Reset the user's password 1754 * Take into account the 'send_reset_password' setting 1755 * - if it is ON, generate a random password and send an email 1756 * (unless the second parameter is false) 1757 * - if it is OFF, set the password to blank 1758 * 1759 * @param integer $p_user_id A valid user identifier. 1760 * @param boolean $p_send_email Whether to send confirmation email. 1761 * @return boolean True if the password was successfully reset 1762 * False if the user is protected. 1763 * @throws ClientException 1764 */ 1765function user_reset_password( $p_user_id, $p_send_email = true ) { 1766 if( user_is_protected( $p_user_id ) ) { 1767 return false; 1768 } 1769 1770 # Go with random password and email it to the user 1771 # @@@ do we want to force blank password instead of random if 1772 # email notifications are turned off? 1773 # How would we indicate that we had done this with a return value? 1774 # Should we just have two functions? (user_reset_password_random() 1775 # and user_reset_password() )? 1776 if( ( ON == config_get( 'send_reset_password' ) ) && ( ON == config_get( 'enable_email_notification' ) ) ) { 1777 $t_email = user_get_field( $p_user_id, 'email' ); 1778 if( is_blank( $t_email ) ) { 1779 trigger_error( ERROR_LOST_PASSWORD_NO_EMAIL_SPECIFIED, ERROR ); 1780 throw new ClientException( 1781 sprintf( "User id '%d' does not have an e-mail address.", (int)$p_user_id ), 1782 ERROR_LOST_PASSWORD_NO_EMAIL_SPECIFIED, 1783 array( (int)$p_user_id ) 1784 ); 1785 } 1786 1787 # Create random password 1788 $t_password = auth_generate_random_password(); 1789 $t_password2 = auth_process_plain_password( $t_password ); 1790 1791 user_set_field( $p_user_id, 'password', $t_password2 ); 1792 1793 # Send notification email 1794 if( $p_send_email ) { 1795 $t_confirm_hash = auth_generate_confirm_hash( $p_user_id ); 1796 token_set( TOKEN_ACCOUNT_ACTIVATION, $t_confirm_hash, TOKEN_EXPIRY_ACCOUNT_ACTIVATION, $p_user_id ); 1797 email_send_confirm_hash_url( $p_user_id, $t_confirm_hash, true ); 1798 } 1799 } else { 1800 # use blank password, no emailing 1801 $t_password = auth_process_plain_password( '' ); 1802 user_set_field( $p_user_id, 'password', $t_password ); 1803 1804 # reset the failed login count because in this mode there is no emailing 1805 user_reset_failed_login_count_to_zero( $p_user_id ); 1806 } 1807 1808 return true; 1809} 1810 1811/** 1812 * Helper function to check if the user has access to more than one project 1813 * (any kind of project or subproject). This can be used to simplify logic when 1814 * the user only has one project to choose from. 1815 * 1816 * @param integer $p_user_id A valid user identifier. 1817 * @return boolean True if the user has access to more than one project. 1818 */ 1819function user_has_more_than_one_project( $p_user_id ) { 1820 $t_project_ids = user_get_accessible_projects( $p_user_id ); 1821 $t_count = count( $t_project_ids ); 1822 if( 0 == $t_count ) { 1823 return false; 1824 } 1825 if( 1 == $t_count ) { 1826 $t_project_id = (int) $t_project_ids[0]; 1827 if( count( user_get_accessible_subprojects( $p_user_id, $t_project_id ) ) == 0 ) { 1828 return false; 1829 } 1830 } 1831 return true; 1832} 1833