| [ Index ] |
PHP Cross Reference of MantisBT |
[Summary view] [Print] [Text view]
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 * Email API 19 * 20 * @package CoreAPI 21 * @subpackage EmailAPI 22 * @copyright Copyright (C) 2000 - 2002 Kenzaburo Ito - kenito@300baud.org 23 * @copyright Copyright (C) 2002 - 2011 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 bug_api.php 29 * @uses bugnote_api.php 30 * @uses category_api.php 31 * @uses config_api.php 32 * @uses constant_inc.php 33 * @uses current_user_api.php 34 * @uses custom_field_api.php 35 * @uses database_api.php 36 * @uses email_queue_api.php 37 * @uses event_api.php 38 * @uses helper_api.php 39 * @uses history_api.php 40 * @uses lang_api.php 41 * @uses logging_api.php 42 * @uses project_api.php 43 * @uses relationship_api.php 44 * @uses sponsorship_api.php 45 * @uses string_api.php 46 * @uses user_api.php 47 * @uses user_pref_api.php 48 * @uses utility_api.php 49 */ 50 51 require_api( 'access_api.php' ); 52 require_api( 'authentication_api.php' ); 53 require_api( 'bug_api.php' ); 54 require_api( 'bugnote_api.php' ); 55 require_api( 'category_api.php' ); 56 require_api( 'config_api.php' ); 57 require_api( 'constant_inc.php' ); 58 require_api( 'current_user_api.php' ); 59 require_api( 'custom_field_api.php' ); 60 require_api( 'database_api.php' ); 61 require_api( 'email_queue_api.php' ); 62 require_api( 'event_api.php' ); 63 require_api( 'helper_api.php' ); 64 require_api( 'history_api.php' ); 65 require_api( 'lang_api.php' ); 66 require_api( 'logging_api.php' ); 67 require_api( 'project_api.php' ); 68 require_api( 'relationship_api.php' ); 69 require_api( 'sponsorship_api.php' ); 70 require_api( 'string_api.php' ); 71 require_api( 'user_api.php' ); 72 require_api( 'user_pref_api.php' ); 73 require_api( 'utility_api.php' ); 74 75 /** 76 * reusable object of class SMTP 77 */ 78 $g_phpMailer = null; 79 80 /** 81 * 82 * Use a simple perl regex for valid email addresses. This is not a complete regex, 83 * as it does not cover quoted addresses or domain literals, but it is simple and 84 * covers the vast majority of all email addresses without being overly complex. 85 * @return string 86 */ 87 function email_regex_simple() { 88 return "/([a-z0-9!#*+\/=?^_{|}~-]+(?:\.[a-z0-9!#*+\/=?^_{|}~-]+)*)" . # recipient 89 "\@((?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)/i"; # @domain 90 } 91 92 /** 93 * check to see that the format is valid and that the mx record exists 94 * @param string $p_email 95 * @return bool 96 */ 97 function email_is_valid( $p_email ) { 98 # if we don't validate then just accept 99 if( OFF == config_get( 'validate_email' ) ) { 100 return true; 101 } 102 103 if ( ON == config_get( 'use_ldap_email' ) ) { 104 return true; 105 } 106 107 if( is_blank( $p_email ) && ON == config_get( 'allow_blank_email' ) ) { 108 return true; 109 } 110 111 # Use a regular expression to check to see if the email is in valid format 112 # x-xx.xxx@yyy.zzz.abc etc. 113 if( preg_match( email_regex_simple(), $p_email, $t_check ) ) { 114 $t_local = $t_check[1]; 115 $t_domain = $t_check[2]; 116 117 # see if we're limited to one domain 118 $t_limit_email_domain = config_get( 'limit_email_domain' ); 119 if( $t_limit_email_domain !== OFF ) { 120 if( 0 != strcasecmp( $t_limit_email_domain, $t_domain ) ) { 121 return false; 122 } 123 } 124 125 if( ON == config_get( 'check_mx_record' ) ) { 126 $temp = ''; 127 128 # Check for valid mx records 129 if( getmxrr( $t_domain, $temp ) ) { 130 return true; 131 } else { 132 $host = $t_domain . '.'; 133 134 # for no mx record... try dns check 135 if( checkdnsrr( $host, 'ANY' ) ) { 136 return true; 137 } 138 } 139 } else { 140 # Email format was valid but did't check for valid mx records 141 return true; 142 } 143 } 144 145 # Everything failed. The email is invalid 146 return false; 147 } 148 149 /** 150 * Check if the email address is valid 151 * trigger an ERROR if it isn't 152 * @param string $p_email 153 * @return null 154 */ 155 function email_ensure_valid( $p_email ) { 156 if( !email_is_valid( $p_email ) ) { 157 trigger_error( ERROR_EMAIL_INVALID, ERROR ); 158 } 159 } 160 161 /** 162 * Check if the email address is disposable 163 * @param string $p_email 164 * @return bool 165 */ 166 function email_is_disposable( $p_email ) { 167 if( !class_exists( 'DisposableEmailChecker' ) ) { 168 require_lib( 'disposable' . DIRECTORY_SEPARATOR . 'disposable.php' ); 169 } 170 171 return DisposableEmailChecker::is_disposable_email( $p_email ); 172 } 173 174 /** 175 * Check if the email address is disposable 176 * trigger an ERROR if it isn't 177 * @param string $p_email 178 * @return null 179 */ 180 function email_ensure_not_disposable( $p_email ) { 181 if( email_is_disposable( $p_email ) ) { 182 trigger_error( ERROR_EMAIL_DISPOSABLE, ERROR ); 183 } 184 } 185 186 /** 187 * email_notify_flag 188 * Get the value associated with the specific action and flag. 189 * For example, you can get the value associated with notifying "admin" 190 * on action "new", i.e. notify administrators on new bugs which can be 191 * ON or OFF. 192 * @param string $action 193 * @param string $flag 194 * @return int 195 */ 196 function email_notify_flag( $action, $flag ) { 197 $t_notify_flags = config_get( 'notify_flags' ); 198 $t_default_notify_flags = config_get( 'default_notify_flags' ); 199 if( isset( $t_notify_flags[$action][$flag] ) ) { 200 return $t_notify_flags[$action][$flag]; 201 } 202 else if( isset( $t_default_notify_flags[$flag] ) ) { 203 return $t_default_notify_flags[$flag]; 204 } 205 206 return OFF; 207 } 208 209 /** 210 * @todo yarick123: email_collect_recipients(...) will be completely rewritten to provide additional information such as language, user access,.. 211 * @todo yarick123:sort recipients list by language to reduce switches between different languages 212 * @param int $p_bug_id 213 * @param string $p_notify_type 214 * @param array $p_extra_user_ids_to_email 215 * @return array 216 */ 217 function email_collect_recipients( $p_bug_id, $p_notify_type, $p_extra_user_ids_to_email = array() ) { 218 $c_bug_id = db_prepare_int( $p_bug_id ); 219 220 $t_recipients = array(); 221 222 # add explicitly specified users 223 if ( ON == email_notify_flag( $p_notify_type, 'explicit' ) ) { 224 foreach ( $p_extra_user_ids_to_email as $t_user_id ) { 225 $t_recipients[$t_user_id] = true; 226 log_event( LOG_EMAIL_RECIPIENT, sprintf( 'Issue = #%d, add explicitly specified user = @U%d', $p_bug_id, $t_user_id ) ); 227 } 228 } 229 230 # add Reporter 231 if( ON == email_notify_flag( $p_notify_type, 'reporter' ) ) { 232 $t_reporter_id = bug_get_field( $p_bug_id, 'reporter_id' ); 233 $t_recipients[$t_reporter_id] = true; 234 log_event( LOG_EMAIL_RECIPIENT, sprintf( 'Issue = #%d, add Reporter = @U%d', $p_bug_id, $t_reporter_id ) ); 235 } 236 237 # add Handler 238 if( ON == email_notify_flag( $p_notify_type, 'handler' ) ) { 239 $t_handler_id = bug_get_field( $p_bug_id, 'handler_id' ); 240 241 if( $t_handler_id > 0 ) { 242 $t_recipients[$t_handler_id] = true; 243 log_event( LOG_EMAIL_RECIPIENT, sprintf( 'Issue = #%d, add Handler = @U%d', $p_bug_id, $t_handler_id ) ); 244 } 245 } 246 247 $t_project_id = bug_get_field( $p_bug_id, 'project_id' ); 248 249 # add users monitoring the bug 250 $t_bug_monitor_table = db_get_table( 'bug_monitor' ); 251 if( ON == email_notify_flag( $p_notify_type, 'monitor' ) ) { 252 $query = "SELECT DISTINCT user_id 253 FROM $t_bug_monitor_table 254 WHERE bug_id=" . db_param(); 255 $result = db_query_bound( $query, Array( $c_bug_id ) ); 256 257 $count = db_num_rows( $result ); 258 for( $i = 0;$i < $count;$i++ ) { 259 $t_user_id = db_result( $result, $i ); 260 $t_recipients[$t_user_id] = true; 261 log_event( LOG_EMAIL_RECIPIENT, sprintf( 'Issue = #%d, add Monitor = @U%d', $p_bug_id, $t_user_id ) ); 262 } 263 } 264 265 # add users who contributed bugnotes 266 $t_bugnote_id = bugnote_get_latest_id( $p_bug_id ); 267 $t_bugnote_view = bugnote_get_field( $t_bugnote_id, 'view_state' ); 268 $t_bugnote_date = bugnote_get_field( $t_bugnote_id, 'last_modified' ); 269 $t_bug = bug_get( $p_bug_id ); 270 $t_bug_date = $t_bug->last_updated; 271 272 $t_bugnote_table = db_get_table( 'bugnote' ); 273 if( ON == email_notify_flag( $p_notify_type, 'bugnotes' ) ) { 274 $query = "SELECT DISTINCT reporter_id 275 FROM $t_bugnote_table 276 WHERE bug_id = " . db_param(); 277 $result = db_query_bound( $query, Array( $c_bug_id ) ); 278 279 $count = db_num_rows( $result ); 280 for( $i = 0;$i < $count;$i++ ) { 281 $t_user_id = db_result( $result, $i ); 282 $t_recipients[$t_user_id] = true; 283 log_event( LOG_EMAIL_RECIPIENT, sprintf( 'Issue = #%d, add Note Author = @U%d', $p_bug_id, $t_user_id ) ); 284 } 285 } 286 287 # add project users who meet the thresholds 288 $t_bug_is_private = bug_get_field( $p_bug_id, 'view_state' ) == VS_PRIVATE; 289 $t_threshold_min = email_notify_flag( $p_notify_type, 'threshold_min' ); 290 $t_threshold_max = email_notify_flag( $p_notify_type, 'threshold_max' ); 291 $t_threshold_users = project_get_all_user_rows( $t_project_id, $t_threshold_min ); 292 foreach( $t_threshold_users as $t_user ) { 293 if( $t_user['access_level'] <= $t_threshold_max ) { 294 if( !$t_bug_is_private || access_compare_level( $t_user['access_level'], config_get( 'private_bug_threshold' ) ) ) { 295 $t_recipients[$t_user['id']] = true; 296 log_event( LOG_EMAIL_RECIPIENT, sprintf( 'Issue = #%d, add Project User = @U%d', $p_bug_id, $t_user['id'] ) ); 297 } 298 } 299 } 300 301 # add users as specified by plugins 302 $t_recipients_include_data = event_signal( 'EVENT_NOTIFY_USER_INCLUDE', array( $p_bug_id, $p_notify_type ) ); 303 foreach( $t_recipients_include_data as $t_plugin => $t_recipients_include_data2 ) { 304 foreach( $t_recipients_include_data2 as $t_callback => $t_recipients_included ) { 305 # only handle if we get an array from the callback 306 if ( is_array( $t_recipients_included ) ) { 307 foreach( $t_recipients_included as $t_user_id ) { 308 $t_recipients[ $t_user_id ] = true; 309 log_event( LOG_EMAIL_RECIPIENT, sprintf( 'Issue = #%d, %s plugin added user @U%d', $p_bug_id, $t_plugin, $t_user_id ) ); 310 } 311 } 312 } 313 } 314 315 # FIXME: the value of $p_notify_type could at this stage be either a status 316 # or a built-in actions such as 'owner and 'sponsor'. We have absolutely no 317 # idea whether 'new' is indicating a new bug has been filed, or if the 318 # status of an existing bug has been changed to 'new'. Therefore it is best 319 # to just assume built-in actions have precedence over status changes. 320 switch( $p_notify_type) { 321 case 'new': 322 case 'feedback': # This isn't really a built-in action (delete me!) 323 case 'reopened': 324 case 'resolved': 325 case 'closed': 326 case 'bugnote': 327 $t_pref_field = 'email_on_' . $p_notify_type; 328 break; 329 case 'owner': 330 # The email_on_assigned notification type is now effectively 331 # email_on_change_of_handler. 332 $t_pref_field = 'email_on_assigned'; 333 break; 334 case 'deleted': 335 case 'updated': 336 case 'sponsor': 337 case 'relation': 338 case 'monitor': 339 case 'priority': # This is never used, but exists in the database! 340 # FIXME: these notification actions are not actually implemented 341 # in the database and therefore aren't adjustable on a per-user 342 # basis! The exception is 'monitor' that makes no sense being a 343 # customisable per-user preference. 344 $t_pref_field = false; 345 break; 346 default: 347 # Anything not built-in is probably going to be a status 348 $t_pref_field = 'email_on_status'; 349 break; 350 } 351 352 # @@@ we could optimize by modifiying user_cache() to take an array 353 # of user ids so we could pull them all in. We'll see if it's necessary 354 $t_final_recipients = array(); 355 356 $t_user_ids = array_keys( $t_recipients ); 357 user_cache_array_rows( $t_user_ids ); 358 user_pref_cache_array_rows( $t_user_ids ); 359 user_pref_cache_array_rows( $t_user_ids, $t_bug->project_id ); 360 361 # Check whether users should receive the emails 362 # and put email address to $t_recipients[user_id] 363 foreach( $t_recipients as $t_id => $t_ignore ) { 364 # Possibly eliminate the current user 365 if(( auth_get_current_user_id() == $t_id ) && ( OFF == config_get( 'email_receive_own' ) ) ) { 366 log_event( LOG_EMAIL_RECIPIENT, sprintf( 'Issue = #%d, drop @U%d (own)', $p_bug_id, $t_id ) ); 367 continue; 368 } 369 370 # Eliminate users who don't exist anymore or who are disabled 371 if( !user_exists( $t_id ) || !user_is_enabled( $t_id ) ) { 372 log_event( LOG_EMAIL_RECIPIENT, sprintf( 'Issue = #%d, drop @U%d (disabled)', $p_bug_id, $t_id ) ); 373 continue; 374 } 375 376 # Exclude users who have this notification type turned off 377 if( $t_pref_field ) { 378 $t_notify = user_pref_get_pref( $t_id, $t_pref_field ); 379 if( OFF == $t_notify ) { 380 log_event( LOG_EMAIL_RECIPIENT, sprintf( 'Issue = #%d, drop @U%d (pref %s off)', $p_bug_id, $t_id, $t_pref_field ) ); 381 continue; 382 } else { 383 # Users can define the severity of an issue before they are emailed for 384 # each type of notification 385 $t_min_sev_pref_field = $t_pref_field . '_min_severity'; 386 $t_min_sev_notify = user_pref_get_pref( $t_id, $t_min_sev_pref_field ); 387 $t_bug_severity = bug_get_field( $p_bug_id, 'severity' ); 388 389 if( $t_bug_severity < $t_min_sev_notify ) { 390 log_event( LOG_EMAIL_RECIPIENT, sprintf( 'Issue = #%d, drop @U%d (pref threshold)', $p_bug_id, $t_id ) ); 391 continue; 392 } 393 } 394 } 395 396 # check that user can see bugnotes if the last update included a bugnote 397 if( $t_bug_date == $t_bugnote_date ) { 398 if( !access_has_bugnote_level( VIEWER, $t_bugnote_id, $t_id ) ) { 399 log_event( LOG_EMAIL_RECIPIENT, sprintf( 'Issue = #%d, drop @U%d (access level)', $p_bug_id, $t_id ) ); 400 continue; 401 } 402 } 403 404 # check to exclude users as specified by plugins 405 $t_recipient_exclude_data = event_signal( 'EVENT_NOTIFY_USER_EXCLUDE', array( $p_bug_id, $p_notify_type, $t_id ) ); 406 $t_exclude = false; 407 foreach( $t_recipient_exclude_data as $t_plugin => $t_recipient_exclude_data2 ) { 408 foreach( $t_recipient_exclude_data2 as $t_callback => $t_recipient_excluded ) { 409 # exclude if any plugin returns true (excludes the user) 410 if ( $t_recipient_excluded ) { 411 $t_exclude = true; 412 log_event( LOG_EMAIL_RECIPIENT, sprintf( 'Issue = #%d, %s plugin dropped user @U%d', $p_bug_id, $t_plugin, $t_id ) ); 413 } 414 } 415 } 416 417 # user was excluded by a plugin 418 if ( $t_exclude ) { 419 continue; 420 } 421 422 # Finally, let's get their emails, if they've set one 423 $t_email = user_get_email( $t_id ); 424 if( is_blank( $t_email ) ) { 425 log_event( LOG_EMAIL_RECIPIENT, sprintf( 'Issue = #%d, drop @U%d (no email)', $p_bug_id, $t_id ) ); 426 } else { 427 # @@@ we could check the emails for validity again but I think 428 # it would be too slow 429 $t_final_recipients[$t_id] = $t_email; 430 } 431 } 432 433 return $t_final_recipients; 434 } 435 436 /** 437 * Send password to user 438 * @param int $p_user_id 439 * @param string $p_password 440 * @param string $p_confirm_hash 441 * @param string $p_admin_name 442 * @return null 443 */ 444 function email_signup( $p_user_id, $p_password, $p_confirm_hash, $p_admin_name = '' ) { 445 if(( OFF == config_get( 'send_reset_password' ) ) || ( OFF == config_get( 'enable_email_notification' ) ) ) { 446 return; 447 } 448 449 # @@@ thraxisp - removed to address #6084 - user won't have any settings yet, 450 # use same language as display for the email 451 # lang_push( user_pref_get_language( $p_user_id ) ); 452 # retrieve the username and email 453 $t_username = user_get_field( $p_user_id, 'username' ); 454 $t_email = user_get_email( $p_user_id ); 455 456 # Build Welcome Message 457 $t_subject = '[' . config_get( 'window_title' ) . '] ' . lang_get( 'new_account_subject' ); 458 459 //if( $p_admin_created && $p_admin_name) { 460 if( $p_admin_name ) { 461 $intro_text = sprintf( lang_get( 'new_account_greeting_admincreated' ), $p_admin_name, $t_username ); 462 } else { 463 $intro_text = sprintf( lang_get( 'new_account_greeting' ), $t_username ); 464 } 465 466 $t_message = $intro_text . "\n\n" . string_get_confirm_hash_url( $p_user_id, $p_confirm_hash ) . "\n\n" . lang_get( 'new_account_message' ) . "\n\n" . lang_get( 'new_account_do_not_reply' ); 467 468 # Send signup email regardless of mail notification pref 469 # or else users won't be able to sign up 470 if( !is_blank( $t_email ) ) { 471 email_store( $t_email, $t_subject, $t_message ); 472 log_event( LOG_EMAIL, sprintf( 'Signup Email = %s, Hash = %s, User = @U%d', $t_email, $p_confirm_hash, $p_user_id ) ); 473 474 if( OFF == config_get( 'email_send_using_cronjob' ) ) { 475 email_send_all(); 476 } 477 } 478 479 # lang_pop(); # see above 480 } 481 482 /** 483 * Send confirm_hash url to user forgets the password 484 * @param int $p_user_id 485 * @param string $p_confirm_hash 486 * @return null 487 */ 488 function email_send_confirm_hash_url( $p_user_id, $p_confirm_hash ) { 489 if ( OFF == config_get( 'send_reset_password' ) || 490 OFF == config_get( 'enable_email_notification' ) ) { 491 return; 492 } 493 494 lang_push( user_pref_get_language( $p_user_id ) ); 495 496 # retrieve the username and email 497 $t_username = user_get_field( $p_user_id, 'username' ); 498 $t_email = user_get_email( $p_user_id ); 499 500 $t_subject = '[' . config_get( 'window_title' ) . '] ' . lang_get( 'lost_password_subject' ); 501 502 $t_message = lang_get( 'reset_request_msg' ) . " \n\n" . string_get_confirm_hash_url( $p_user_id, $p_confirm_hash ) . " \n\n" . lang_get( 'new_account_username' ) . ' ' . $t_username . " \n" . lang_get( 'new_account_IP' ) . ' ' . $_SERVER["REMOTE_ADDR"] . " \n\n" . lang_get( 'new_account_do_not_reply' ); 503 504 # Send password reset regardless of mail notification prefs 505 # or else users won't be able to receive their reset pws 506 if( !is_blank( $t_email ) ) { 507 email_store( $t_email, $t_subject, $t_message ); 508 log_event( LOG_EMAIL, sprintf( 'Password reset for email = %s', $t_email ) ); 509 510 if( OFF == config_get( 'email_send_using_cronjob' ) ) { 511 email_send_all(); 512 } 513 } 514 515 lang_pop(); 516 } 517 518 /** 519 * notify the selected group a new user has signup 520 * @param string $p_username 521 * @param string $p_email 522 * @return null 523 */ 524 function email_notify_new_account( $p_username, $p_email ) { 525 global $g_path; 526 527 $t_threshold_min = config_get( 'notify_new_user_created_threshold_min' ); 528 $t_threshold_users = project_get_all_user_rows( ALL_PROJECTS, $t_threshold_min ); 529 530 foreach( $t_threshold_users as $t_user ) { 531 lang_push( user_pref_get_language( $t_user['id'] ) ); 532 533 $t_recipient_email = user_get_email( $t_user['id'] ); 534 $t_subject = '[' . config_get( 'window_title' ) . '] ' . lang_get( 'new_account_subject' ); 535 536 $t_message = lang_get( 'new_account_signup_msg' ) . "\n\n" . lang_get( 'new_account_username' ) . ' ' . $p_username . "\n" . lang_get( 'new_account_email' ) . ' ' . $p_email . "\n" . lang_get( 'new_account_IP' ) . ' ' . $_SERVER["REMOTE_ADDR"] . "\n" . $g_path . "\n\n" . lang_get( 'new_account_do_not_reply' ); 537 538 if( !is_blank( $t_recipient_email ) ) { 539 email_store( $t_recipient_email, $t_subject, $t_message ); 540 log_event( LOG_EMAIL, sprintf( 'New Account Notify for email = \'%s\'', $t_recipient_email ) ); 541 542 if( OFF == config_get( 'email_send_using_cronjob' ) ) { 543 email_send_all(); 544 } 545 } 546 547 lang_pop(); 548 } 549 } 550 551 552 /** 553 * send a generic email 554 * $p_notify_type: use check who she get notified of such event. 555 * $p_message_id: message id to be translated and included at the top of the email message. 556 * Return false if it were problems sending email * @param string 557 * @param int $p_bug_id 558 * @param string $p_notify_type 559 * @param int $p_message_id 560 * @param array $p_header_optional_params = null 561 * @param array $p_extra_user_ids_to_email 562 * @return bool 563 */ 564 function email_generic( $p_bug_id, $p_notify_type, $p_message_id = null, $p_header_optional_params = null, $p_extra_user_ids_to_email = array() ) { 565 $t_ok = true; 566 567 if( ON === config_get( 'enable_email_notification' ) ) { 568 ignore_user_abort( true ); 569 570 bugnote_get_all_bugnotes( $p_bug_id ); 571 572 # @todo yarick123: email_collect_recipients(...) will be completely rewritten to provide additional information such as language, user access,.. 573 # @todo yarick123:sort recipients list by language to reduce switches between different languages 574 $t_recipients = email_collect_recipients( $p_bug_id, $p_notify_type, $p_extra_user_ids_to_email ); 575 576 $t_project_id = bug_get_field( $p_bug_id, 'project_id' ); 577 578 if( is_array( $t_recipients ) ) { 579 # send email to every recipient 580 foreach( $t_recipients as $t_user_id => $t_user_email ) { 581 log_event( LOG_EMAIL, sprintf( "Issue = #%d, Type = %s, Msg = '%s', User = @U%d, Email = '%s'.", $p_bug_id, $p_notify_type, $p_message_id, $t_user_id, $t_user_email ) ); 582 583 # load (push) user language here as build_visible_bug_data assumes current language 584 lang_push( user_pref_get_language( $t_user_id, $t_project_id ) ); 585 586 $t_visible_bug_data = email_build_visible_bug_data( $t_user_id, $p_bug_id, $p_message_id ); 587 $t_ok = email_bug_info_to_one_user( $t_visible_bug_data, $p_message_id, $t_project_id, $t_user_id, $p_header_optional_params ) && $t_ok; 588 589 lang_pop(); 590 } 591 } 592 593 # Only trigger the draining of the email queue if cronjob is disabled and email notifications are enabled. 594 if( OFF == config_get( 'email_send_using_cronjob' ) ) { 595 email_send_all(); 596 } 597 } 598 599 return $t_ok; 600 } 601 602 /** 603 * Send notices that a user is now monitoring the bug. Typically this will only be sent when the added 604 * user is not the logged in user. This is assuming that receive own notifications is OFF (default). 605 * @param int $p_bug_id 606 * @param int $p_user_id 607 * @return null 608 */ 609 function email_monitor_added( $p_bug_id, $p_user_id ) { 610 log_event( LOG_EMAIL, sprintf( 'Issue #%d monitored by user @U%d', $p_bug_id, $p_user_id ) ); 611 612 $t_opt = array(); 613 $t_opt[] = bug_format_id( $p_bug_id ); 614 $t_opt[] = user_get_name( $p_user_id ); 615 616 email_generic( $p_bug_id, 'monitor', 'email_notification_title_for_action_monitor', $t_opt, array( $p_user_id ) ); 617 } 618 619 /** 620 * send notices when a relationship is ADDED 621 * @param int $p_bug_id 622 * @param int $p_related_bug_id 623 * @param int $p_rel_type 624 * @return null 625 */ 626 function email_relationship_added( $p_bug_id, $p_related_bug_id, $p_rel_type ) { 627 log_event( LOG_EMAIL, sprintf( 'Relationship added: Issue #%d, related issue %d, relationship type %s.', $p_bug_id, $p_related_bug_id, $p_rel_type ) ); 628 629 $t_opt = array(); 630 $t_opt[] = bug_format_id( $p_related_bug_id ); 631 global $g_relationships; 632 if( !isset( $g_relationships[$p_rel_type] ) ) { 633 trigger_error( ERROR_RELATIONSHIP_NOT_FOUND, ERROR ); 634 } 635 email_generic( $p_bug_id, 'relation', $g_relationships[$p_rel_type]['#notify_added'], $t_opt ); 636 } 637 638 /** 639 * send notices when a relationship is DELETED 640 * @param int $p_bug_id 641 * @param int $p_related_bug_id 642 * @param int $p_rel_type 643 * @return null 644 */ 645 function email_relationship_deleted( $p_bug_id, $p_related_bug_id, $p_rel_type ) { 646 log_event( LOG_EMAIL, sprintf( 'Relationship deleted: Issue #%d, related issue %d, relationship type %s.', $p_bug_id, $p_related_bug_id, $p_rel_type ) ); 647 648 $t_opt = array(); 649 $t_opt[] = bug_format_id( $p_related_bug_id ); 650 global $g_relationships; 651 if( !isset( $g_relationships[$p_rel_type] ) ) { 652 trigger_error( ERROR_RELATIONSHIP_NOT_FOUND, ERROR ); 653 } 654 email_generic( $p_bug_id, 'relation', $g_relationships[$p_rel_type]['#notify_deleted'], $t_opt ); 655 } 656 657 /** 658 * send notices to all the handlers of the parent bugs when a child bug is RESOLVED 659 * @param int $p_bug_id 660 * @return null 661 */ 662 function email_relationship_child_resolved( $p_bug_id ) { 663 email_relationship_child_resolved_closed( $p_bug_id, 'email_notification_title_for_action_relationship_child_resolved' ); 664 } 665 666 /** 667 * send notices to all the handlers of the parent bugs when a child bug is CLOSED 668 * @param int $p_bug_id 669 * @return null 670 */ 671 function email_relationship_child_closed( $p_bug_id ) { 672 email_relationship_child_resolved_closed( $p_bug_id, 'email_notification_title_for_action_relationship_child_closed' ); 673 } 674 675 /** 676 * send notices to all the handlers of the parent bugs still open when a child bug is resolved/closed 677 * 678 * @param int $p_bug_id 679 * @param int $p_message_id 680 * @return null 681 */ 682 function email_relationship_child_resolved_closed( $p_bug_id, $p_message_id ) { 683 # retrieve all the relationships in which the bug is the destination bug 684 $t_relationship = relationship_get_all_dest( $p_bug_id ); 685 $t_relationship_count = count( $t_relationship ); 686 if( $t_relationship_count == 0 ) { 687 # no parent bug found 688 return; 689 } 690 691 for( $i = 0;$i < $t_relationship_count;$i++ ) { 692 if( $t_relationship[$i]->type == BUG_DEPENDANT ) { 693 $t_src_bug_id = $t_relationship[$i]->src_bug_id; 694 $t_status = bug_get_field( $t_src_bug_id, 'status' ); 695 if( $t_status < config_get( 'bug_resolved_status_threshold' ) ) { 696 697 # sent the notification just for parent bugs not resolved/closed 698 $t_opt = array(); 699 $t_opt[] = bug_format_id( $p_bug_id ); 700 email_generic( $t_src_bug_id, 'handler', $p_message_id, $t_opt ); 701 } 702 } 703 } 704 } 705 706 /** 707 * send notices when a bug is sponsored 708 * @param int $p_bug_id 709 * @return null 710 */ 711 function email_sponsorship_added( $p_bug_id ) { 712 email_generic( $p_bug_id, 'sponsor', 'email_notification_title_for_action_sponsorship_added' ); 713 } 714 715 /** 716 * send notices when a sponsorship is modified 717 * @param int $p_bug_id 718 * @return null 719 */ 720 function email_sponsorship_updated( $p_bug_id ) { 721 email_generic( $p_bug_id, 'sponsor', 'email_notification_title_for_action_sponsorship_updated' ); 722 } 723 724 /** 725 * send notices when a sponsorship is deleted 726 * @param int $p_bug_id 727 * @return null 728 */ 729 function email_sponsorship_deleted( $p_bug_id ) { 730 email_generic( $p_bug_id, 'sponsor', 'email_notification_title_for_action_sponsorship_deleted' ); 731 } 732 733 /** 734 * send notices when a new bug is added 735 * @param int $p_bug_id 736 * @return null 737 */ 738 function email_new_bug( $p_bug_id ) { 739 email_generic( $p_bug_id, 'new', 'email_notification_title_for_action_bug_submitted' ); 740 } 741 742 /** 743 * send notices when a new bugnote 744 * @param int $p_bug_id 745 * @return null 746 */ 747 function email_bugnote_add( $p_bug_id ) { 748 email_generic( $p_bug_id, 'bugnote', 'email_notification_title_for_action_bugnote_submitted' ); 749 } 750 751 /** 752 * send notices when a bug is RESOLVED 753 * @param int $p_bug_id 754 * @return null 755 */ 756 function email_resolved( $p_bug_id ) { 757 email_generic( $p_bug_id, 'resolved', 'email_notification_title_for_status_bug_resolved' ); 758 } 759 760 /** 761 * send notices when a bug is CLOSED 762 * @param int $p_bug_id 763 * @return null 764 */ 765 function email_close( $p_bug_id ) { 766 email_generic( $p_bug_id, 'closed', 'email_notification_title_for_status_bug_closed' ); 767 } 768 769 /** 770 * send notices when a bug is REOPENED 771 * @param int $p_bug_id 772 * @return null 773 */ 774 function email_reopen( $p_bug_id ) { 775 email_generic( $p_bug_id, 'reopened', 'email_notification_title_for_action_bug_reopened' ); 776 } 777 778 /** 779 * send notices when a bug is ASSIGNED 780 * @param int $p_bug_id 781 * @return null 782 */ 783 function email_assign( $p_bug_id ) { 784 email_generic( $p_bug_id, 'owner', 'email_notification_title_for_action_bug_assigned' ); 785 } 786 787 /** 788 * send notices when a bug is DELETED 789 * @param int $p_bug_id 790 * @return null 791 */ 792 function email_bug_deleted( $p_bug_id ) { 793 email_generic( $p_bug_id, 'deleted', 'email_notification_title_for_action_bug_deleted' ); 794 } 795 796 /** 797 * Store email in queue for sending 798 * 799 * @param string $p_recipient 800 * @param string $p_subject 801 * @param string $p_message 802 * @param array $p_headers 803 * @return int 804 */ 805 function email_store( $p_recipient, $p_subject, $p_message, $p_headers = null ) { 806 $t_recipient = trim( $p_recipient ); 807 $t_subject = string_email( trim( $p_subject ) ); 808 $t_message = string_email_links( trim( $p_message ) ); 809 810 # short-circuit if no recipient is defined, or email disabled 811 # note that this may cause signup messages not to be sent 812 813 if( is_blank( $p_recipient ) || ( OFF == config_get( 'enable_email_notification' ) ) ) { 814 return; 815 } 816 817 $t_email_data = new EmailData; 818 819 $t_email_data->email = $t_recipient; 820 $t_email_data->subject = $t_subject; 821 $t_email_data->body = $t_message; 822 $t_email_data->metadata = array(); 823 $t_email_data->metadata['headers'] = $p_headers === null ? array() : $p_headers; 824 $t_email_data->metadata['priority'] = config_get( 'mail_priority' ); 825 826 # Urgent = 1, Not Urgent = 5, Disable = 0 827 $t_email_data->metadata['charset'] = 'utf-8'; 828 829 $t_hostname = ''; 830 $t_server = isset( $_SERVER ) ? $_SERVER : $HTTP_SERVER_VARS; 831 if( isset( $t_server['SERVER_NAME'] ) ) { 832 $t_hostname = $t_server['SERVER_NAME']; 833 } else { 834 $t_address = explode( '@', config_get( 'from_email' ) ); 835 if( isset( $t_address[1] ) ) { 836 $t_hostname = $t_address[1]; 837 } 838 } 839 $t_email_data->metadata['hostname'] = $t_hostname; 840 841 $t_email_id = email_queue_add( $t_email_data ); 842 843 return $t_email_id; 844 } 845 846 /** 847 * This function sends all the emails that are stored in the queue. If a failure occurs, then the 848 * function exists. This function will be called after storing emails in case of synchronous 849 * emails, or will be called from a cronjob in case of asynchronous emails. 850 * @todo In case of synchronous email sending, we may get a race condition where two requests send the same email. 851 * @param bool $p_delete_on_failure indicates whether to remove email from queue on failure (default false) 852 * @return null 853 */ 854 function email_send_all($p_delete_on_failure = false) { 855 $t_ids = email_queue_get_ids(); 856 857 $t_emails_recipients_failed = array(); 858 $t_start = microtime(true); 859 foreach( $t_ids as $t_id ) { 860 $t_email_data = email_queue_get( $t_id ); 861 862 # check if email was not found. This can happen if another request picks up the email first and sends it. 863 if( $t_email_data === false ) { 864 continue; 865 } 866 867 # if unable to place the email in the email server queue, then the connection to the server is down, 868 # and hence no point to continue trying with the rest of the emails. 869 if( !email_send( $t_email_data ) ) { 870 if ($p_delete_on_failure) { 871 email_queue_delete( $t_email_data->email_id ); 872 } 873 if( microtime(true) - $t_start > 5 ) { 874 break; 875 } else { 876 continue; 877 } 878 } 879 } 880 } 881 882 /** 883 * This function sends an email message based on the supplied email data. 884 * 885 * @param EmailData $p_email_data 886 * @return bool 887 */ 888 function email_send( $p_email_data ) { 889 global $g_phpMailer; 890 891 $t_email_data = $p_email_data; 892 893 $t_recipient = trim( $t_email_data->email ); 894 $t_subject = string_email( trim( $t_email_data->subject ) ); 895 $t_message = string_email_links( trim( $t_email_data->body ) ); 896 897 $t_debug_email = config_get( 'debug_email' ); 898 $t_mailer_method = config_get( 'phpMailer_method' ); 899 900 if( is_null( $g_phpMailer ) ) { 901 if ( $t_mailer_method == PHPMAILER_METHOD_SMTP ) 902 register_shutdown_function( 'email_smtp_close' ); 903 if( !class_exists( 'PHPMailer' ) ) { 904 require_lib( 'phpmailer' . DIRECTORY_SEPARATOR . 'class.phpmailer.php' ); 905 } 906 $mail = new PHPMailer(true); 907 } else { 908 $mail = $g_phpMailer; 909 } 910 911 if( isset( $t_email_data->metadata['hostname'] ) ) { 912 $mail->Hostname = $t_email_data->metadata['hostname']; 913 } 914 915 # @@@ should this be the current language (for the recipient) or the default one (for the user running the command) (thraxisp) 916 $t_lang = config_get( 'default_language' ); 917 if( 'auto' == $t_lang ) { 918 $t_lang = config_get( 'fallback_language' ); 919 } 920 $mail->SetLanguage( lang_get( 'phpmailer_language', $t_lang ) ); 921 922 # Select the method to send mail 923 switch( config_get( 'phpMailer_method' ) ) { 924 case PHPMAILER_METHOD_MAIL: 925 $mail->IsMail(); 926 break; 927 928 case PHPMAILER_METHOD_SENDMAIL: 929 $mail->IsSendmail(); 930 break; 931 932 case PHPMAILER_METHOD_SMTP: 933 $mail->IsSMTP(); 934 935 // SMTP collection is always kept alive 936 $mail->SMTPKeepAlive = true; 937 938 if ( !is_blank( config_get( 'smtp_username' ) ) ) { 939 # Use SMTP Authentication 940 $mail->SMTPAuth = true; 941 $mail->Username = config_get( 'smtp_username' ); 942 $mail->Password = config_get( 'smtp_password' ); 943 } 944 945 if ( !is_blank( config_get( 'smtp_connection_mode' ) ) ) { 946 $mail->SMTPSecure = config_get( 'smtp_connection_mode' ); 947 } 948 949 $mail->Port = config_get( 'smtp_port' ); 950 951 break; 952 } 953 954 $mail->IsHTML( false ); # set email format to plain text 955 $mail->WordWrap = 80; # set word wrap to 50 characters 956 $mail->Priority = $t_email_data->metadata['priority']; # Urgent = 1, Not Urgent = 5, Disable = 0 957 $mail->CharSet = $t_email_data->metadata['charset']; 958 $mail->Host = config_get( 'smtp_host' ); 959 $mail->From = config_get( 'from_email' ); 960 $mail->Sender = config_get( 'return_path_email' ); 961 $mail->FromName = config_get( 'from_name' ); 962 963 if( OFF !== $t_debug_email ) { 964 $t_message = 'To: ' . $t_recipient . "\n\n" . $t_message; 965 try { 966 $mail->AddAddress( $t_debug_email, '' ); 967 } catch ( phpmailerException $e ) { 968 $t_success = false; 969 $mail->ClearAllRecipients(); 970 $mail->ClearAttachments(); 971 $mail->ClearReplyTos(); 972 $mail->ClearCustomHeaders(); 973 return $t_success; 974 } 975 } else { 976 try { 977 $mail->AddAddress( $t_recipient, '' ); 978 } catch ( phpmailerException $e ) { 979 $t_success = false; 980 $mail->ClearAllRecipients(); 981 $mail->ClearAttachments(); 982 $mail->ClearReplyTos(); 983 $mail->ClearCustomHeaders(); 984 return $t_success; 985 } 986 } 987 988 $mail->Subject = $t_subject; 989 $mail->Body = make_lf_crlf( "\n" . $t_message ); 990 991 if( isset( $t_email_data->metadata['headers'] ) && is_array( $t_email_data->metadata['headers'] ) ) { 992 foreach( $t_email_data->metadata['headers'] as $t_key => $t_value ) { 993 switch( $t_key ) { 994 case 'Message-ID': 995 /* Note: hostname can never be blank here as we set metadata['hostname'] 996 in email_store() where mail gets queued. */ 997 if ( !strchr( $t_value, '@' ) && !is_blank( $mail->Hostname ) ) { 998 $t_value = $t_value . '@' . $mail->Hostname; 999 } 1000 $mail->set( 'MessageID', "<$t_value>" ); 1001 break; 1002 case 'In-Reply-To': 1003 $mail->AddCustomHeader( "$t_key: <{$t_value}@{$mail->Hostname}>" ); 1004 break; 1005 default: 1006 $mail->AddCustomHeader( "$t_key: $t_value" ); 1007 break; 1008 } 1009 } 1010 } 1011 1012 try 1013 { 1014 if ( !$mail->Send() ) { 1015 $t_success = false; 1016 } else { 1017 $t_success = true; 1018 1019 if ( $t_email_data->email_id > 0 ) { 1020 email_queue_delete( $t_email_data->email_id ); 1021 } 1022 } 1023 } 1024 catch ( phpmailerException $e ) 1025 { 1026 $t_success = false; 1027 } 1028 1029 $mail->ClearAllRecipients(); 1030 $mail->ClearAttachments(); 1031 $mail->ClearReplyTos(); 1032 $mail->ClearCustomHeaders(); 1033 1034 return $t_success; 1035 } 1036 1037 /** 1038 * closes opened kept alive SMTP connection (if it was opened) 1039 * 1040 * @param string 1041 * @return null 1042 */ 1043 function email_smtp_close() { 1044 global $g_phpMailer; 1045 1046 if( !is_null( $g_phpMailer ) ) { 1047 if( $g_phpMailer->smtp->Connected() ) { 1048 $g_phpMailer->smtp->Quit(); 1049 $g_phpMailer->smtp->Close(); 1050 } 1051 $g_phpMailer = null; 1052 } 1053 } 1054 1055 /** 1056 * formats the subject correctly 1057 * we include the project name, bug id, and summary. 1058 * 1059 * @param int $p_bug_id 1060 * @return null 1061 */ 1062 function email_build_subject( $p_bug_id ) { 1063 # grab the project name 1064 $p_project_name = project_get_field( bug_get_field( $p_bug_id, 'project_id' ), 'name' ); 1065 1066 # grab the subject (summary) 1067 $p_subject = bug_get_field( $p_bug_id, 'summary' ); 1068 1069 # padd the bug id with zeros 1070 $p_bug_id = bug_format_id( $p_bug_id ); 1071 1072 return '[' . $p_project_name . ' ' . $p_bug_id . ']: ' . $p_subject; 1073 } 1074 1075 /** 1076 * clean up LF to CRLF 1077 * 1078 * @param string $p_string 1079 * @return null 1080 */ 1081 function make_lf_crlf( $p_string ) { 1082 $t_string = str_replace( "\n", "\r\n", $p_string ); 1083 return str_replace( "\r\r\n", "\r\n", $t_string ); 1084 } 1085 1086 /** 1087 * Appends an email domain to the specified email address if email is 1088 * not empty, it doesn't already have a domain part and if a 1089 * limit_email_domain is configured. 1090 * 1091 * Check limit_email_domain option and append the domain name if it is set 1092 * @todo limit_email_domain called after we look for @ in domain name? 1093 * @param string $p_email The email address to append the domain to. 1094 * @returns The email address with the appended domain (if applicable). 1095 */ 1096 function email_append_domain( $p_email ) { 1097 # If email is empty or already contains a domain, then return as is. 1098 if ( is_blank( $p_email ) || strchr( $p_email, '@' ) ) { 1099 return $p_email; 1100 } 1101 1102 # If limit email domain is set, then append it. 1103 $t_limit_email_domain = config_get( 'limit_email_domain' ); 1104 if ( $t_limit_email_domain === OFF ) { 1105 return $p_email; 1106 } 1107 1108 return "$p_email@$t_limit_email_domain"; 1109 } 1110 1111 /** 1112 * Send a bug reminder to each of the given user, or to each user if the first parameter is an array 1113 * return an array of usernames to which the reminder was successfully sent 1114 * 1115 * @todo I'm not sure this shouldn't return an array of user ids... more work for the caller but cleaner from an API point of view. 1116 * @param array $p_recipients 1117 * @param int $p_bug_id 1118 * @param string $p_message 1119 * @return null 1120 */ 1121 function email_bug_reminder( $p_recipients, $p_bug_id, $p_message ) { 1122 if( !is_array( $p_recipients ) ) { 1123 $p_recipients = array( 1124 $p_recipients, 1125 ); 1126 } 1127 1128 $t_project_id = bug_get_field( $p_bug_id, 'project_id' ); 1129 $t_sender_id = auth_get_current_user_id(); 1130 $t_sender = user_get_name( $t_sender_id ); 1131 1132 $t_subject = email_build_subject( $p_bug_id ); 1133 $t_date = date( config_get( 'normal_date_format' ) ); 1134 1135 $result = array(); 1136 foreach( $p_recipients as $t_recipient ) { 1137 lang_push( user_pref_get_language( $t_recipient, $t_project_id ) ); 1138 1139 $t_email = user_get_email( $t_recipient ); 1140 $result[] = user_get_name( $t_recipient ); 1141 1142 if( access_has_project_level( config_get( 'show_user_email_threshold' ), $t_project_id, $t_recipient ) ) { 1143 $t_sender_email = ' <' . current_user_get_field( 'email' ) . '>'; 1144 } else { 1145 $t_sender_email = ''; 1146 } 1147 $t_header = "\n" . lang_get( 'on_date' ) . " $t_date, $t_sender $t_sender_email " . lang_get( 'sent_you_this_reminder_about' ) . ": \n\n"; 1148 $t_contents = $t_header . string_get_bug_view_url_with_fqdn( $p_bug_id, $t_recipient ) . " \n\n$p_message"; 1149 1150 if( ON == config_get( 'enable_email_notification' ) ) { 1151 email_store( $t_email, $t_subject, $t_contents ); 1152 } 1153 1154 lang_pop(); 1155 } 1156 1157 if( OFF == config_get( 'email_send_using_cronjob' ) ) { 1158 email_send_all(); 1159 } 1160 1161 return $result; 1162 } 1163 1164 /** 1165 * Send bug info to given user 1166 * return true on success 1167 * @param array $p_visible_bug_data 1168 * @param string $p_message_id 1169 * @param int $p_project_id 1170 * @param int $p_user_id 1171 * @param array $p_header_optional_params 1172 * @return bool 1173 */ 1174 function email_bug_info_to_one_user( $p_visible_bug_data, $p_message_id, $p_project_id, $p_user_id, $p_header_optional_params = null ) { 1175 $t_user_email = user_get_email( $p_user_id ); 1176 1177 # check whether email should be sent 1178 # @@@ can be email field empty? if yes - then it should be handled here 1179 if( ON !== config_get( 'enable_email_notification' ) || is_blank( $t_user_email ) ) { 1180 return true; 1181 } 1182 1183 # build subject 1184 $t_subject = '[' . $p_visible_bug_data['email_project'] . ' ' . bug_format_id( $p_visible_bug_data['email_bug'] ) . ']: ' . $p_visible_bug_data['email_summary']; 1185 1186 # build message 1187 1188 $t_message = lang_get_defaulted( $p_message_id, null ); 1189 1190 if( is_array( $p_header_optional_params ) ) { 1191 $t_message = vsprintf( $t_message, $p_header_optional_params ); 1192 } 1193 1194 if(( $t_message !== null ) && ( !is_blank( $t_message ) ) ) { 1195 $t_message .= " \n"; 1196 } 1197 1198 $t_message .= email_format_bug_message( $p_visible_bug_data ); 1199 1200 # build headers 1201 $t_bug_id = $p_visible_bug_data['email_bug']; 1202 $t_message_md5 = md5( $t_bug_id . $p_visible_bug_data['email_date_submitted'] ); 1203 $t_mail_headers = array( 1204 'keywords' => $p_visible_bug_data['set_category'], 1205 ); 1206 if( $p_message_id == 'email_notification_title_for_action_bug_submitted' ) { 1207 $t_mail_headers['Message-ID'] = $t_message_md5; 1208 } else { 1209 $t_mail_headers['In-Reply-To'] = $t_message_md5; 1210 } 1211 1212 # send mail 1213 $t_ok = email_store( $t_user_email, $t_subject, $t_message, $t_mail_headers ); 1214 1215 return $t_ok; 1216 } 1217 1218 /** 1219 * Build the bug info part of the message 1220 * @param array $p_visible_bug_data 1221 * @return string 1222 */ 1223 function email_format_bug_message( $p_visible_bug_data ) { 1224 $t_normal_date_format = config_get( 'normal_date_format' ); 1225 $t_complete_date_format = config_get( 'complete_date_format' ); 1226 1227 $t_email_separator1 = config_get( 'email_separator1' ); 1228 $t_email_separator2 = config_get( 'email_separator2' ); 1229 $t_email_padding_length = config_get( 'email_padding_length' ); 1230 1231 $t_status = $p_visible_bug_data['email_status']; 1232 1233 $p_visible_bug_data['email_date_submitted'] = date( $t_complete_date_format, $p_visible_bug_data['email_date_submitted'] ); 1234 $p_visible_bug_data['email_last_modified'] = date( $t_complete_date_format, $p_visible_bug_data['email_last_modified'] ); 1235 1236 $p_visible_bug_data['email_status'] = get_enum_element( 'status', $t_status ); 1237 $p_visible_bug_data['email_severity'] = get_enum_element( 'severity', $p_visible_bug_data['email_severity'] ); 1238 $p_visible_bug_data['email_priority'] = get_enum_element( 'priority', $p_visible_bug_data['email_priority'] ); 1239 $p_visible_bug_data['email_reproducibility'] = get_enum_element( 'reproducibility', $p_visible_bug_data['email_reproducibility'] ); 1240 1241 $t_message = $t_email_separator1 . " \n"; 1242 1243 if( isset( $p_visible_bug_data['email_bug_view_url'] ) ) { 1244 $t_message .= $p_visible_bug_data['email_bug_view_url'] . " \n"; 1245 $t_message .= $t_email_separator1 . " \n"; 1246 } 1247 1248 $t_message .= email_format_attribute( $p_visible_bug_data, 'email_reporter' ); 1249 $t_message .= email_format_attribute( $p_visible_bug_data, 'email_handler' ); 1250 $t_message .= $t_email_separator1 . " \n"; 1251 $t_message .= email_format_attribute( $p_visible_bug_data, 'email_project' ); 1252 $t_message .= email_format_attribute( $p_visible_bug_data, 'email_bug' ); 1253 $t_message .= email_format_attribute( $p_visible_bug_data, 'email_category' ); 1254 $t_message .= email_format_attribute( $p_visible_bug_data, 'email_reproducibility' ); 1255 $t_message .= email_format_attribute( $p_visible_bug_data, 'email_severity' ); 1256 $t_message .= email_format_attribute( $p_visible_bug_data, 'email_priority' ); 1257 $t_message .= email_format_attribute( $p_visible_bug_data, 'email_status' ); 1258 $t_message .= email_format_attribute( $p_visible_bug_data, 'email_target_version' ); 1259 1260 # custom fields formatting 1261 foreach( $p_visible_bug_data['custom_fields'] as $t_custom_field_name => $t_custom_field_data ) { 1262 $t_message .= utf8_str_pad( lang_get_defaulted( $t_custom_field_name, null ) . ': ', $t_email_padding_length, ' ', STR_PAD_RIGHT ); 1263 $t_message .= string_custom_field_value_for_email( $t_custom_field_data['value'], $t_custom_field_data['type'] ); 1264 $t_message .= " \n"; 1265 } 1266 1267 # end foreach custom field 1268 1269 if( config_get( 'bug_resolved_status_threshold' ) <= $t_status ) { 1270 $p_visible_bug_data['email_resolution'] = get_enum_element( 'resolution', $p_visible_bug_data['email_resolution'] ); 1271 $t_message .= email_format_attribute( $p_visible_bug_data, 'email_resolution' ); 1272 $t_message .= email_format_attribute( $p_visible_bug_data, 'email_fixed_in_version' ); 1273 } 1274 $t_message .= $t_email_separator1 . " \n"; 1275 1276 $t_message .= email_format_attribute( $p_visible_bug_data, 'email_date_submitted' ); 1277 $t_message .= email_format_attribute( $p_visible_bug_data, 'email_last_modified' ); 1278 $t_message .= $t_email_separator1 . " \n"; 1279 1280 $t_message .= email_format_attribute( $p_visible_bug_data, 'email_summary' ); 1281 1282 $t_message .= lang_get( 'email_description' ) . ": \n" . $p_visible_bug_data['email_description'] . "\n"; 1283 1284 if ( !is_blank( $p_visible_bug_data['email_steps_to_reproduce'] ) ) { 1285 $t_message .= "\n" . lang_get( 'email_steps_to_reproduce' ) . ": \n" . $p_visible_bug_data['email_steps_to_reproduce'] . "\n"; 1286 } 1287 1288 if ( !is_blank( $p_visible_bug_data['email_additional_information'] ) ) { 1289 $t_message .= "\n" . lang_get( 'email_additional_information' ) . ": \n" . $p_visible_bug_data['email_additional_information'] . "\n"; 1290 } 1291 1292 if( isset( $p_visible_bug_data['relations'] ) ) { 1293 if( $p_visible_bug_data['relations'] != '' ) { 1294 $t_message .= $t_email_separator1 . "\n" . str_pad( lang_get( 'bug_relationships' ), 20 ) . str_pad( lang_get( 'id' ), 8 ) . lang_get( 'summary' ) . "\n" . $t_email_separator2 . "\n" . $p_visible_bug_data['relations']; 1295 } 1296 } 1297 1298 # Sponsorship 1299 if( isset( $p_visible_bug_data['sponsorship_total'] ) && ( $p_visible_bug_data['sponsorship_total'] > 0 ) ) { 1300 $t_message .= $t_email_separator1 . " \n"; 1301 $t_message .= sprintf( lang_get( 'total_sponsorship_amount' ), sponsorship_format_amount( $p_visible_bug_data['sponsorship_total'] ) ) . "\n" . "\n"; 1302 1303 if( isset( $p_visible_bug_data['sponsorships'] ) ) { 1304 foreach( $p_visible_bug_data['sponsorships'] as $t_sponsorship ) { 1305 $t_date_added = date( config_get( 'normal_date_format' ), $t_sponsorship->date_submitted ); 1306 1307 $t_message .= $t_date_added . ': '; 1308 $t_message .= user_get_name( $t_sponsorship->user_id ); 1309 $t_message .= ' (' . sponsorship_format_amount( $t_sponsorship->amount ) . ')' . " \n"; 1310 } 1311 } 1312 } 1313 1314 $t_message .= $t_email_separator1 . " \n\n"; 1315 1316 # format bugnotes 1317 foreach( $p_visible_bug_data['bugnotes'] as $t_bugnote ) { 1318 $t_last_modified = date( $t_normal_date_format, $t_bugnote->last_modified ); 1319 1320 $t_formatted_bugnote_id = bugnote_format_id( $t_bugnote->id ); 1321 $t_bugnote_link = string_process_bugnote_link( config_get( 'bugnote_link_tag' ) . $t_bugnote->id, false, false, true ); 1322 1323 if( $t_bugnote->time_tracking > 0 ) { 1324 $t_time_tracking = ' ' . lang_get( 'time_tracking' ) . ' ' . db_minutes_to_hhmm( $t_bugnote->time_tracking ) . "\n"; 1325 } else { 1326 $t_time_tracking = ''; 1327 } 1328 1329 if( user_exists( $t_bugnote->reporter_id ) ) { 1330 $t_access_level = access_get_project_level( $p_visible_bug_data['email_project_id'] , $t_bugnote->reporter_id ); 1331 $t_access_level_string = ' (' . get_enum_element( 'access_levels', $t_access_level ) . ') - '; 1332 } else { 1333 $t_access_level_string = ''; 1334 } 1335 1336 $t_string = ' (' . $t_formatted_bugnote_id . ') ' . user_get_name( $t_bugnote->reporter_id ) . $t_access_level_string . $t_last_modified . "\n" . $t_time_tracking . ' ' . $t_bugnote_link; 1337 1338 $t_message .= $t_email_separator2 . " \n"; 1339 $t_message .= $t_string . " \n"; 1340 $t_message .= $t_email_separator2 . " \n"; 1341 $t_message .= $t_bugnote->note . " \n\n"; 1342 } 1343 1344 # format history 1345 if( array_key_exists( 'history', $p_visible_bug_data ) ) { 1346 $t_message .= lang_get( 'bug_history' ) . " \n"; 1347 $t_message .= utf8_str_pad( lang_get( 'date_modified' ), 17 ) . utf8_str_pad( lang_get( 'username' ), 15 ) . utf8_str_pad( lang_get( 'field' ), 25 ) . utf8_str_pad( lang_get( 'change' ), 20 ) . " \n"; 1348 1349 $t_message .= $t_email_separator1 . " \n"; 1350 1351 foreach( $p_visible_bug_data['history'] as $t_raw_history_item ) { 1352 $t_localized_item = history_localize_item( $t_raw_history_item['field'], $t_raw_history_item['type'], $t_raw_history_item['old_value'], $t_raw_history_item['new_value'], false ); 1353 1354 $t_message .= utf8_str_pad( date( $t_normal_date_format, $t_raw_history_item['date'] ), 17 ) . utf8_str_pad( $t_raw_history_item['username'], 15 ) . utf8_str_pad( $t_localized_item['note'], 25 ) . utf8_str_pad( $t_localized_item['change'], 20 ) . "\n"; 1355 } 1356 $t_message .= $t_email_separator1 . " \n\n"; 1357 } 1358 1359 return $t_message; 1360 } 1361 1362 /** 1363 * if $p_visible_bug_data contains specified attribute the function 1364 * returns concatenated translated attribute name and original 1365 * attribute value. Else return empty string. 1366 * @param array $p_visible_bug_data 1367 * @param string $p_attribute_id 1368 * @return string 1369 */ 1370 function email_format_attribute( $p_visible_bug_data, $attribute_id ) { 1371 if( array_key_exists( $attribute_id, $p_visible_bug_data ) ) { 1372 return utf8_str_pad( lang_get( $attribute_id ) . ': ', config_get( 'email_padding_length' ), ' ', STR_PAD_RIGHT ) . $p_visible_bug_data[$attribute_id] . "\n"; 1373 } 1374 return ''; 1375 } 1376 1377 /** 1378 * Build the bug raw data visible for specified user to be translated and sent by email to the user 1379 * (Filter the bug data according to user access level) 1380 * return array with bug data. See usage in email_format_bug_message(...) 1381 * @param int $p_user_id 1382 * @param int $p_bug_id 1383 * @param string $p_message_id 1384 * @return array 1385 */ 1386 function email_build_visible_bug_data( $p_user_id, $p_bug_id, $p_message_id ) { 1387 $t_project_id = bug_get_field( $p_bug_id, 'project_id' ); 1388 $t_user_access_level = user_get_access_level( $p_user_id, $t_project_id ); 1389 $t_user_bugnote_order = user_pref_get_pref( $p_user_id, 'bugnote_order' ); 1390 $t_user_bugnote_limit = user_pref_get_pref( $p_user_id, 'email_bugnote_limit' ); 1391 1392 $row = bug_get_extended_row( $p_bug_id ); 1393 $t_bug_data = array(); 1394 1395 $t_bug_data['email_bug'] = $p_bug_id; 1396 1397 if( $p_message_id !== 'email_notification_title_for_action_bug_deleted' ) { 1398 $t_bug_data['email_bug_view_url'] = string_get_bug_view_url_with_fqdn( $p_bug_id ); 1399 } 1400 1401 if( access_compare_level( $t_user_access_level, config_get( 'view_handler_threshold' ) ) ) { 1402 if( 0 != $row['handler_id'] ) { 1403 $t_bug_data['email_handler'] = user_get_name( $row['handler_id'] ); 1404 } else { 1405 $t_bug_data['email_handler'] = ''; 1406 } 1407 } 1408 1409 $t_bug_data['email_reporter'] = user_get_name( $row['reporter_id'] ); 1410 $t_bug_data['email_project_id'] = $row['project_id']; 1411 $t_bug_data['email_project'] = project_get_field( $row['project_id'], 'name' ); 1412 1413 $t_category_name = category_full_name( $row['category_id'], false ); 1414 $t_bug_data['email_category'] = $t_category_name; 1415 1416 $t_bug_data['email_date_submitted'] = $row['date_submitted']; 1417 $t_bug_data['email_last_modified'] = $row['last_updated']; 1418 1419 $t_bug_data['email_status'] = $row['status']; 1420 $t_bug_data['email_severity'] = $row['severity']; 1421 $t_bug_data['email_priority'] = $row['priority']; 1422 $t_bug_data['email_reproducibility'] = $row['reproducibility']; 1423 1424 $t_bug_data['email_resolution'] = $row['resolution']; 1425 $t_bug_data['email_fixed_in_version'] = $row['fixed_in_version']; 1426 1427 if( !is_blank( $row['target_version'] ) && access_compare_level( $t_user_access_level, config_get( 'roadmap_view_threshold' ) ) ) { 1428 $t_bug_data['email_target_version'] = $row['target_version']; 1429 } 1430 1431 $t_bug_data['email_summary'] = $row['summary']; 1432 $t_bug_data['email_description'] = $row['description']; 1433 $t_bug_data['email_additional_information'] = $row['additional_information']; 1434 $t_bug_data['email_steps_to_reproduce'] = $row['steps_to_reproduce']; 1435 1436 $t_bug_data['set_category'] = '[' . $t_bug_data['email_project'] . '] ' . $t_category_name; 1437 1438 $t_bug_data['custom_fields'] = custom_field_get_linked_fields( $p_bug_id, $t_user_access_level ); 1439 $t_bug_data['bugnotes'] = bugnote_get_all_visible_bugnotes( $p_bug_id, $t_user_bugnote_order, $t_user_bugnote_limit, $p_user_id ); 1440 1441 # put history data 1442 if(( ON == config_get( 'history_default_visible' ) ) && access_compare_level( $t_user_access_level, config_get( 'view_history_threshold' ) ) ) { 1443 $t_bug_data['history'] = history_get_raw_events_array( $p_bug_id, $p_user_id ); 1444 } 1445 1446 # Sponsorship Information 1447 if(( config_get( 'enable_sponsorship' ) == ON ) && ( access_has_bug_level( config_get( 'view_sponsorship_total_threshold' ), $p_bug_id, $p_user_id ) ) ) { 1448 $t_sponsorship_ids = sponsorship_get_all_ids( $p_bug_id ); 1449 $t_bug_data['sponsorship_total'] = sponsorship_get_amount( $t_sponsorship_ids ); 1450 1451 if( access_has_bug_level( config_get( 'view_sponsorship_details_threshold' ), $p_bug_id, $p_user_id ) ) { 1452 $t_bug_data['sponsorships'] = array(); 1453 foreach( $t_sponsorship_ids as $id ) { 1454 $t_bug_data['sponsorships'][] = sponsorship_get( $id ); 1455 } 1456 } 1457 } 1458 1459 $t_bug_data['relations'] = relationship_get_summary_text( $p_bug_id ); 1460 1461 return $t_bug_data; 1462 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated: Thu Jul 28 15:48:31 2011 | Cross-referenced by PHPXref 0.7 |