| [ Index ] |
|
Code source de Mantis 1.1.0rc3 |
1 <?php 2 # Mantis - a php based bugtracking system 3 4 # Copyright (C) 2000 - 2002 Kenzaburo Ito - kenito@300baud.org 5 # Copyright (C) 2002 - 2007 Mantis Team - mantisbt-dev@lists.sourceforge.net 6 7 # Mantis is free software: you can redistribute it and/or modify 8 # it under the terms of the GNU General Public License as published by 9 # the Free Software Foundation, either version 2 of the License, or 10 # (at your option) any later version. 11 # 12 # Mantis is distributed in the hope that it will be useful, 13 # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 # GNU General Public License for more details. 16 # 17 # You should have received a copy of the GNU General Public License 18 # along with Mantis. If not, see <http://www.gnu.org/licenses/>. 19 20 # -------------------------------------------------------- 21 # $Id: email_api.php,v 1.139.2.3 2007-10-22 07:40:59 vboctor Exp $ 22 # -------------------------------------------------------- 23 24 $t_core_dir = dirname( __FILE__ ).DIRECTORY_SEPARATOR; 25 26 define( 'PHPMAILER_PATH', $t_core_dir . 'phpmailer' . DIRECTORY_SEPARATOR ); 27 28 require_once ( $t_core_dir . 'current_user_api.php' ); 29 require_once ( $t_core_dir . 'bug_api.php' ); 30 require_once ( $t_core_dir . 'custom_field_api.php' ); 31 require_once ( $t_core_dir . 'string_api.php' ); 32 require_once ( $t_core_dir . 'history_api.php' ); 33 require_once ( $t_core_dir . 'email_queue_api.php' ); 34 require_once ( $t_core_dir . 'relationship_api.php' ); 35 require_once ( 'disposable' . DIRECTORY_SEPARATOR . 'disposable.php' ); 36 require_once( PHPMAILER_PATH . 'class.phpmailer.php' ); 37 38 # reusable object of class SMTP 39 $g_phpMailer_smtp = null; 40 41 ########################################################################### 42 # Email API 43 ########################################################################### 44 45 # Use a simple perl regex for valid email addresses. This is not a complete regex, 46 # as it does not cover quoted addresses or domain literals, but it is simple and 47 # covers the vast majority of all email addresses without being overly complex. 48 # Callers must surround this with appropriate delimiters with case insentive options. 49 function email_regex_simple() { 50 return "(([a-z0-9!#*+\/=?^_{|}~-]+(?:\.[a-z0-9!#*+\/=?^_{|}~-]+)*)" . # recipient 51 "\@((?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?))"; # @domain 52 } 53 54 # -------------------- 55 # Return a perl compatible regular expression that will 56 # match a valid email address as per RFC 822 (approximately) 57 # 58 # The regex will provide too matched groups: the first will be the 59 # local part (or mailbox name) and the second will be the domain 60 function email_get_rfc822_regex() { 61 # Build up basic RFC 822 BNF definitions. 62 63 # list of the special characters: ( ) < > @ , ; : \ " . [ ] 64 $t_specials = '\(\)\<\>\@\,\;\:\\\"\.\[\]'; 65 # the space character 66 $t_space = '\040'; 67 # valid characters in a quoted string 68 $t_char = '\000-\177'; 69 # control characters 70 $t_ctl = '\000-\037\177'; 71 72 # a chunk of quoted text (anything except " \ \r are valid) 73 $t_qtext_re = '[^"\\\r]+'; 74 # match any valid character preceded by a backslash ( mostly for \" ) 75 $t_qpair_re = "\\\\[$t_char]"; 76 77 # a complete quoted string - " characters with valid characters or 78 # backslash-escaped characters between them 79 $t_quoted_string_re = "(?:\"(?:$t_qtext_re|$t_qpair_re)*\")"; 80 81 # an unquoted atom (anything that isn't a control char, a space, or a 82 # special char) 83 $t_atom_re = "(?:[^$t_ctl$t_space$t_specials]+)"; 84 85 # a domain ref is an atom 86 $t_domain_ref_re = $t_atom_re; 87 88 # the characters in a domain literal can be anything except: [ ] \ \r 89 $t_dtext_re = "[^\\[\\]\\\\\\r]"; 90 # a domain-literal is a sequence of characters or escaped pairs inside 91 # square brackets 92 $t_domain_literal_re = "\\[(?:$t_dtext_re|$t_qpair_re)*\\]"; 93 # a subdomain is a domain ref or a domain literal 94 $t_sub_domain_re = "(?:$t_domain_ref_re|$t_domain_literal_re)"; 95 # a domain is at least one subdomain, with optional further subdomains 96 # separated by periods. eg: '[1.2.3.4]' or 'foo.bar' 97 $t_domain_re = "$t_sub_domain_re(?:\.$t_sub_domain_re)*"; 98 99 # a word is either quoted string or an atom 100 $t_word_re = "(?:$t_atom_re|$t_quoted_string_re)"; 101 102 # the local part of the address spec (the mailbox name) 103 # is one or more words separated by periods 104 $t_local_part_re = "$t_word_re(?:\.$t_word_re)*"; 105 106 # the address spec is made up of a local part, and @ symbol, 107 # and a domain 108 $t_addr_spec_re = "/^($t_local_part_re)\@($t_domain_re)$/"; 109 110 return $t_addr_spec_re; 111 } 112 # -------------------- 113 # check to see that the format is valid and that the mx record exists 114 function email_is_valid( $p_email ) { 115 # if we don't validate then just accept 116 if ( OFF == config_get( 'validate_email' ) ) { 117 return true; 118 } 119 120 if ( is_blank( $p_email ) && ON == config_get( 'allow_blank_email' ) ) { 121 return true; 122 } 123 124 # Use a regular expression to check to see if the email is in valid format 125 # x-xx.xxx@yyy.zzz.abc etc. 126 if ( preg_match( email_get_rfc822_regex(), $p_email, $t_check ) ) { 127 $t_local = $t_check[1]; 128 $t_domain = $t_check[2]; 129 130 # see if we're limited to one domain 131 if ( ON == config_get( 'limit_email_domain' ) ) { 132 if ( 0 != strcasecmp( $t_limit_email_domain, $t_domain ) ) { 133 return false; 134 } 135 } 136 137 if ( preg_match( '/\\[(\d+)\.(\d+)\.(\d+)\.(\d+)\\]/', $t_domain, $t_check ) ) { 138 # Handle domain-literals of the form '[1.2.3.4]' 139 # as long as each segment is less than 255, we're ok 140 if ( $t_check[1] <= 255 && 141 $t_check[2] <= 255 && 142 $t_check[3] <= 255 && 143 $t_check[4] <= 255 ) { 144 return true; 145 } 146 } else if ( ON == config_get( 'check_mx_record' ) ) { 147 # Check for valid mx records 148 if ( getmxrr( $t_domain, $temp ) ) { 149 return true; 150 } else { 151 $host = $t_domain . '.'; 152 153 # for no mx record... try dns check 154 if ( checkdnsrr( $host, 'ANY' ) ) { 155 return true; 156 } 157 } 158 } else { 159 # Email format was valid but did't check for valid mx records 160 return true; 161 } 162 } 163 164 # Everything failed. The email is invalid 165 return false; 166 } 167 # -------------------- 168 # Check if the email address is valid 169 # return true if it is, trigger an ERROR if it isn't 170 function email_ensure_valid( $p_email ) { 171 if ( !email_is_valid( $p_email ) ) { 172 trigger_error( ERROR_EMAIL_INVALID, ERROR ); 173 } 174 } 175 176 # -------------------- 177 # Check if the email address is disposable 178 function email_is_disposable( $p_email ) { 179 return DisposableEmailChecker::is_disposable_email( $p_email ); 180 } 181 182 # -------------------- 183 # Check if the email address is disposable 184 function email_ensure_not_disposable( $p_email ) { 185 if ( email_is_disposable( $p_email ) ) { 186 trigger_error( ERROR_EMAIL_DISPOSABLE, ERROR ); 187 } 188 } 189 190 # -------------------- 191 # email_notify_flag 192 # Get the value associated with the specific action and flag. 193 # For example, you can get the value associated with notifying "admin" 194 # on action "new", i.e. notify administrators on new bugs which can be 195 # ON or OFF. 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 } elseif ( isset ( $t_default_notify_flags[$flag] ) ) { 202 return $t_default_notify_flags[$flag]; 203 } 204 205 return OFF; 206 } 207 208 # @@@ yarick123: email_collect_recipients(...) will be completely rewritten to provide additional 209 # information such as language, user access,.. 210 # @@@ yarick123:sort recipients list by language to reduce switches between different languages 211 function email_collect_recipients( $p_bug_id, $p_notify_type ) { 212 $c_bug_id = db_prepare_int( $p_bug_id ); 213 214 $t_recipients = array(); 215 216 # add Reporter 217 if ( ON == email_notify_flag( $p_notify_type, 'reporter' ) ) { 218 $t_reporter_id = bug_get_field( $p_bug_id, 'reporter_id' ); 219 $t_recipients[$t_reporter_id] = true; 220 log_event( LOG_EMAIL_RECIPIENT, "bug=$p_bug_id, add reporter=$t_reporter_id" ); 221 } 222 223 # add Handler 224 if ( ON == email_notify_flag( $p_notify_type, 'handler' )) { 225 $t_handler_id = bug_get_field( $p_bug_id, 'handler_id' ); 226 227 if ( $t_handler_id > 0 ) { 228 $t_recipients[$t_handler_id] = true; 229 log_event( LOG_EMAIL_RECIPIENT, "bug=$p_bug_id, add handler=$t_handler_id" ); 230 } 231 } 232 233 $t_project_id = bug_get_field( $p_bug_id, 'project_id' ); 234 235 # add users monitoring the bug 236 $t_bug_monitor_table = config_get( 'mantis_bug_monitor_table' ); 237 if ( ON == email_notify_flag( $p_notify_type, 'monitor' ) ) { 238 $query = "SELECT DISTINCT user_id 239 FROM $t_bug_monitor_table 240 WHERE bug_id=$c_bug_id"; 241 $result = db_query( $query ); 242 243 $count = db_num_rows( $result ); 244 for ( $i=0 ; $i < $count ; $i++ ) { 245 $t_user_id = db_result( $result, $i ); 246 $t_recipients[$t_user_id] = true; 247 log_event( LOG_EMAIL_RECIPIENT, "bug=$p_bug_id, add monitor=$t_user_id" ); 248 } 249 } 250 251 # add users who contributed bugnotes 252 $t_bugnote_id = bugnote_get_latest_id( $p_bug_id ); 253 $t_bugnote_view = bugnote_get_field( $t_bugnote_id, 'view_state' ); 254 $t_bugnote_date = db_unixtimestamp( bugnote_get_field( $t_bugnote_id, 'last_modified' ) ); 255 $t_bug_date = bug_get_field( $p_bug_id, 'last_updated' ); 256 257 $t_bugnote_table = config_get( 'mantis_bugnote_table' ); 258 if ( ON == email_notify_flag( $p_notify_type, 'bugnotes' ) ) { 259 $query = "SELECT DISTINCT reporter_id 260 FROM $t_bugnote_table 261 WHERE bug_id = $c_bug_id"; 262 $result = db_query( $query ); 263 264 $count = db_num_rows( $result ); 265 for( $i=0 ; $i < $count ; $i++ ) { 266 $t_user_id = db_result( $result, $i ); 267 $t_recipients[$t_user_id] = true; 268 log_event( LOG_EMAIL_RECIPIENT, "bug=$p_bug_id, add note author=$t_user_id" ); 269 } 270 } 271 272 # add project users who meet the thresholds 273 $t_bug_is_private = bug_get_field( $p_bug_id, 'view_state' ) == VS_PRIVATE; 274 $t_threshold_min = email_notify_flag( $p_notify_type, 'threshold_min' ); 275 $t_threshold_max = email_notify_flag( $p_notify_type, 'threshold_max' ); 276 $t_threshold_users = project_get_all_user_rows( $t_project_id, $t_threshold_min ); 277 foreach( $t_threshold_users as $t_user ) { 278 if ( $t_user['access_level'] <= $t_threshold_max ) { 279 if ( !$t_bug_is_private || access_compare_level( $t_user['access_level'], config_get( 'private_bug_threshold' ) ) ) { 280 $t_recipients[$t_user['id']] = true; 281 log_event( LOG_EMAIL_RECIPIENT, "bug=$p_bug_id, add project user=" . $t_user['id'] ); 282 } 283 } 284 } 285 286 # set up to eliminate unwanted users 287 # get list of status values that are not covered specifically in the prefs 288 # These are handled by email_on_status generically 289 # @@@ thraxisp note that email_on_assigned was co-opted to handle change in handler 290 $t_status_change = get_enum_to_array( config_get( 'status_enum_string' ) ); 291 unset( $t_status_change[NEW_] ); 292 unset( $t_status_change[FEEDBACK] ); 293 unset( $t_status_change[RESOLVED] ); 294 unset( $t_status_change[CLOSED] ); 295 296 if ( 'owner' == $p_notify_type ) { 297 $t_pref_field = 'email_on_assigned'; 298 } else if ( in_array( $p_notify_type, $t_status_change ) ) { 299 $t_pref_field = 'email_on_status'; 300 } else { 301 $t_pref_field = 'email_on_' . $p_notify_type; 302 } 303 $t_user_pref_table = config_get( 'mantis_user_pref_table' ); 304 if ( !db_field_exists( $t_pref_field, $t_user_pref_table ) ) { 305 $t_pref_field = false; 306 } 307 308 # @@@ we could optimize by modifiying user_cache() to take an array 309 # of user ids so we could pull them all in. We'll see if it's necessary 310 $t_final_recipients = array(); 311 # Check whether users should receive the emails 312 # and put email address to $t_recipients[user_id] 313 foreach ( $t_recipients as $t_id => $t_ignore ) { 314 # Possibly eliminate the current user 315 if ( ( auth_get_current_user_id() == $t_id ) && 316 ( OFF == config_get( 'email_receive_own' ) ) ) { 317 log_event( LOG_EMAIL_RECIPIENT, "bug=$p_bug_id, drop $t_id (own)" ); 318 continue; 319 } 320 321 # Eliminate users who don't exist anymore or who are disabled 322 if ( !user_exists( $t_id ) || 323 !user_is_enabled( $t_id ) ) { 324 log_event( LOG_EMAIL_RECIPIENT, "bug=$p_bug_id, drop $t_id (disabled)" ); 325 continue; 326 } 327 328 # Exclude users who have this notification type turned off 329 if ( $t_pref_field ) { 330 $t_notify = user_pref_get_pref( $t_id, $t_pref_field ); 331 if ( OFF == $t_notify ) { 332 log_event( LOG_EMAIL_RECIPIENT, "bug=$p_bug_id, drop $t_id (pref $t_pref_field off)" ); 333 continue; 334 } else { 335 # Users can define the severity of an issue before they are emailed for 336 # each type of notification 337 $t_min_sev_pref_field = $t_pref_field . '_min_severity'; 338 $t_min_sev_notify = user_pref_get_pref( $t_id, $t_min_sev_pref_field ); 339 $t_bug_severity = bug_get_field( $p_bug_id, 'severity' ); 340 341 if ( $t_bug_severity < $t_min_sev_notify ) { 342 log_event( LOG_EMAIL_RECIPIENT, "bug=$p_bug_id, drop $t_id (pref threshold)" ); 343 continue; 344 } 345 } 346 } 347 348 # check that user can see bugnotes if the last update included a bugnote 349 if ( $t_bug_date == $t_bugnote_date ) { 350 if ( !access_has_bugnote_level( VIEWER, $t_bugnote_id, $t_id ) ) { 351 log_event( LOG_EMAIL_RECIPIENT, "bug=$p_bug_id, drop $t_id (access level)" ); 352 continue; 353 } 354 } 355 356 # Finally, let's get their emails, if they've set one 357 $t_email = user_get_email( $t_id ); 358 if ( is_blank( $t_email ) ) { 359 log_event( LOG_EMAIL_RECIPIENT, "bug=$p_bug_id, drop $t_id (no email)" ); 360 } else { 361 # @@@ we could check the emails for validity again but I think 362 # it would be too slow 363 $t_final_recipients[$t_id] = $t_email; 364 } 365 } 366 367 return $t_final_recipients; 368 } 369 370 # -------------------- 371 # Send password to user 372 function email_signup( $p_user_id, $p_password, $p_confirm_hash ) { 373 374 if ( ( OFF == config_get( 'send_reset_password' ) ) || ( OFF == config_get( 'enable_email_notification' ) ) ) { 375 return; 376 } 377 378 # @@@ thraxisp - removed to address #6084 - user won't have any settings yet, 379 # use same language as display for the email 380 # lang_push( user_pref_get_language( $p_user_id ) ); 381 382 # retrieve the username and email 383 $t_username = user_get_field( $p_user_id, 'username' ); 384 $t_email = user_get_email( $p_user_id ); 385 386 # Build Welcome Message 387 $t_subject = '[' . config_get( 'window_title' ) . '] ' . lang_get( 'new_account_subject' ); 388 389 $t_message = lang_get( 'new_account_greeting' ) . $t_username . 390 lang_get( 'new_account_greeting2' ) . " \n\n" . 391 string_get_confirm_hash_url( $p_user_id, $p_confirm_hash ) . " \n\n" . 392 lang_get( 'new_account_message' ) . 393 lang_get( 'new_account_do_not_reply' ); 394 395 # Send signup email regardless of mail notification pref 396 # or else users won't be able to sign up 397 if( !is_blank( $t_email ) ) { 398 email_store( $t_email, $t_subject, $t_message ); 399 log_event( LOG_EMAIL, "signup=$t_email, hash=$p_confirm_hash, id=$p_user_id" ); 400 401 if ( OFF == config_get( 'email_send_using_cronjob' ) ) { 402 email_send_all(); 403 } 404 } 405 406 # lang_pop(); # see above 407 } 408 409 # -------------------- 410 # Send confirm_hash url to user forgets the password 411 function email_send_confirm_hash_url( $p_user_id, $p_confirm_hash ) { 412 if ( ( OFF == config_get( 'send_reset_password' ) ) || ( OFF == config_get( 'enable_email_notification' ) ) ) { 413 return; 414 } 415 416 lang_push( user_pref_get_language( $p_user_id ) ); 417 418 # retrieve the username and email 419 $t_username = user_get_field( $p_user_id, 'username' ); 420 $t_email = user_get_email( $p_user_id ); 421 422 $t_subject = '[' . config_get( 'window_title' ) . '] ' . lang_get( 'lost_password_subject' ); 423 424 $t_message = lang_get( 'reset_request_msg' ) . " \n\n" . 425 string_get_confirm_hash_url( $p_user_id, $p_confirm_hash ) . " \n\n" . 426 lang_get( 'new_account_username' ) . $t_username . " \n" . 427 lang_get( 'new_account_IP' ) . $_SERVER["REMOTE_ADDR"] . " \n\n" . 428 lang_get( 'new_account_do_not_reply' ); 429 430 # Send password reset regardless of mail notification prefs 431 # or else users won't be able to receive their reset pws 432 if( !is_blank( $t_email ) ) { 433 email_store( $t_email, $t_subject, $t_message ); 434 log_event( LOG_EMAIL, "password_reset=$t_email" ); 435 436 if ( OFF == config_get( 'email_send_using_cronjob' ) ) { 437 email_send_all(); 438 } 439 } 440 441 lang_pop(); 442 } 443 444 # -------------------- 445 # notify the selected group a new user has signup 446 function email_notify_new_account( $p_username, $p_email ) { 447 global $g_path; 448 449 $t_threshold_min = config_get( 'notify_new_user_created_threshold_min' ); 450 $t_threshold_users = project_get_all_user_rows( ALL_PROJECTS, $t_threshold_min ); 451 452 foreach( $t_threshold_users as $t_user ) { 453 lang_push( user_pref_get_language( $t_user['id'] ) ); 454 455 $t_recipient_email = user_get_email( $t_user['id'] ); 456 $t_subject = '[' . config_get( 'window_title' ) . '] ' . lang_get( 'new_account_subject' ); 457 458 $t_message = lang_get( 'new_account_signup_msg' ) . " \n\n" . 459 lang_get( 'new_account_username' ) . $p_username . " \n" . 460 lang_get( 'new_account_email' ) . $p_email . " \n" . 461 lang_get( 'new_account_IP' ) . $_SERVER["REMOTE_ADDR"] . " \n" . 462 $g_path . "\n\n" . 463 lang_get( 'new_account_do_not_reply' ); 464 465 if( !is_blank( $t_recipient_email ) ) { 466 email_store( $t_recipient_email, $t_subject, $t_message ); 467 log_event( LOG_EMAIL, "new_account_notify=$t_recipient_email" ); 468 469 if ( OFF == config_get( 'email_send_using_cronjob' ) ) { 470 email_send_all(); 471 } 472 } 473 474 lang_pop(); 475 } 476 } 477 478 # -------------------- 479 # send a generic email 480 # $p_notify_type: use check who she get notified of such event. 481 # $p_message_id: message id to be translated and included at the top of the email message. 482 # Return false if it were problems sending email 483 function email_generic( $p_bug_id, $p_notify_type, $p_message_id = null, $p_header_optional_params = null ) { 484 $t_ok = true; 485 if ( ON === config_get( 'enable_email_notification' ) ) { 486 ignore_user_abort( true ); 487 488 # @@@ yarick123: email_collect_recipients(...) will be completely rewritten to provide additional 489 # information such as language, user access,.. 490 # @@@ yarick123:sort recipients list by language to reduce switches between different languages 491 $t_recipients = email_collect_recipients( $p_bug_id, $p_notify_type ); 492 493 $t_project_id = bug_get_field( $p_bug_id, 'project_id' ); 494 if ( is_array( $t_recipients ) ) { 495 log_event( LOG_EMAIL, sprintf("bug=%d, type=%s, msg=%s, recipients=(%s)", $p_bug_id, $p_notify_type, $p_message_id, implode( '. ', $t_recipients ) ) ); 496 497 # send email to every recipient 498 foreach ( $t_recipients as $t_user_id => $t_user_email ) { 499 # load (push) user language here as build_visible_bug_data assumes current language 500 lang_push( user_pref_get_language( $t_user_id, $t_project_id ) ); 501 502 $t_visible_bug_data = email_build_visible_bug_data( $t_user_id, $p_bug_id, $p_message_id ); 503 $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; 504 505 lang_pop(); 506 } 507 } 508 } 509 510 if ( OFF == config_get( 'email_send_using_cronjob' ) ) { 511 email_send_all(); 512 } 513 514 return $t_ok; 515 } 516 517 # -------------------- 518 # send notices when a relationship is ADDED 519 # MASC RELATIONSHIP 520 function email_relationship_added( $p_bug_id, $p_related_bug_id, $p_rel_type ) { 521 $t_opt = array(); 522 $t_opt[] = bug_format_id( $p_related_bug_id ); 523 global $g_relationships; 524 if ( !isset( $g_relationships[ $p_rel_type ] ) ) { 525 trigger_error( ERROR_RELATIONSHIP_NOT_FOUND, ERROR ); 526 } 527 email_generic( $p_bug_id, 'relation', $g_relationships[ $p_rel_type ][ '#notify_added' ], $t_opt ); 528 } 529 530 # -------------------- 531 # send notices when a relationship is DELETED 532 # MASC RELATIONSHIP 533 function email_relationship_deleted( $p_bug_id, $p_related_bug_id, $p_rel_type ) { 534 $t_opt = array(); 535 $t_opt[] = bug_format_id( $p_related_bug_id ); 536 global $g_relationships; 537 if ( !isset( $g_relationships[ $p_rel_type ] ) ) { 538 trigger_error( ERROR_RELATIONSHIP_NOT_FOUND, ERROR ); 539 } 540 email_generic( $p_bug_id, 'relation', $g_relationships[ $p_rel_type ][ '#notify_deleted' ], $t_opt ); 541 } 542 543 # -------------------- 544 # send notices to all the handlers of the parent bugs when a child bug is RESOLVED 545 # MASC RELATIONSHIP 546 function email_relationship_child_resolved( $p_bug_id ) { 547 email_relationship_child_resolved_closed( $p_bug_id, 'email_notification_title_for_action_relationship_child_resolved' ); 548 } 549 550 # -------------------- 551 # send notices to all the handlers of the parent bugs when a child bug is CLOSED 552 # MASC RELATIONSHIP 553 function email_relationship_child_closed( $p_bug_id ) { 554 email_relationship_child_resolved_closed( $p_bug_id, 'email_notification_title_for_action_relationship_child_closed' ); 555 } 556 557 # -------------------- 558 # send notices to all the handlers of the parent bugs still open when a child bug is resolved/closed 559 # MASC RELATIONSHIP 560 function email_relationship_child_resolved_closed( $p_bug_id, $p_message_id ) { 561 # retrieve all the relationships in which the bug is the destination bug 562 $t_relationship = relationship_get_all_dest( $p_bug_id ); 563 $t_relationship_count = count( $t_relationship ); 564 if ( $t_relationship_count == 0 ) { 565 # no parent bug found 566 return; 567 } 568 569 for ( $i = 0 ; $i < $t_relationship_count ; $i++ ) { 570 if ( $t_relationship[$i]->type == BUG_DEPENDANT ) { 571 $t_src_bug_id = $t_relationship[$i]->src_bug_id; 572 $t_status = bug_get_field( $t_src_bug_id, 'status' ); 573 if ( $t_status < config_get( 'bug_resolved_status_threshold' ) ) { 574 # sent the notification just for parent bugs not resolved/closed 575 $t_opt = array(); 576 $t_opt[] = bug_format_id( $p_bug_id ); 577 email_generic( $t_src_bug_id, 'handler', $p_message_id, $t_opt ); 578 } 579 } 580 } 581 } 582 583 # -------------------- 584 # send notices when a bug is sponsored 585 function email_sponsorship_added( $p_bug_id ) { 586 email_generic( $p_bug_id, 'sponsor', 'email_notification_title_for_action_sponsorship_added' ); 587 } 588 589 # -------------------- 590 # send notices when a sponsorship is modified 591 function email_sponsorship_updated( $p_bug_id ) { 592 email_generic( $p_bug_id, 'sponsor', 'email_notification_title_for_action_sponsorship_updated' ); 593 } 594 595 # -------------------- 596 # send notices when a sponsorship is deleted 597 function email_sponsorship_deleted( $p_bug_id ) { 598 email_generic( $p_bug_id, 'sponsor', 'email_notification_title_for_action_sponsorship_deleted' ); 599 } 600 601 # -------------------- 602 # send notices when a new bug is added 603 function email_new_bug( $p_bug_id ) { 604 email_generic( $p_bug_id, 'new', 'email_notification_title_for_action_bug_submitted' ); 605 } 606 # -------------------- 607 # send notices when a new bugnote 608 function email_bugnote_add( $p_bug_id ) { 609 email_generic( $p_bug_id, 'bugnote', 'email_notification_title_for_action_bugnote_submitted' ); 610 } 611 # -------------------- 612 # send notices when a bug is RESOLVED 613 function email_resolved( $p_bug_id ) { 614 email_generic( $p_bug_id, 'resolved', 'email_notification_title_for_status_bug_resolved' ); 615 } 616 # -------------------- 617 # send notices when a bug is CLOSED 618 function email_close( $p_bug_id ) { 619 email_generic( $p_bug_id, 'closed', 'email_notification_title_for_status_bug_closed' ); 620 } 621 # -------------------- 622 # send notices when a bug is REOPENED 623 function email_reopen( $p_bug_id ) { 624 email_generic( $p_bug_id, 'reopened', 'email_notification_title_for_action_bug_reopened' ); 625 } 626 # -------------------- 627 # send notices when a bug is ASSIGNED 628 function email_assign( $p_bug_id ) { 629 email_generic( $p_bug_id, 'owner', 'email_notification_title_for_action_bug_assigned' ); 630 } 631 # -------------------- 632 # send notices when a bug is DELETED 633 function email_bug_deleted( $p_bug_id ) { 634 email_generic( $p_bug_id, 'deleted', 'email_notification_title_for_action_bug_deleted' ); 635 } 636 # -------------------- 637 function email_store( $p_recipient, $p_subject, $p_message, $p_headers = null ) { 638 $t_recipient = trim( $p_recipient ); 639 $t_subject = string_email( trim( $p_subject ) ); 640 $t_message = string_email_links( trim( $p_message ) ); 641 642 # short-circuit if no recipient is defined, or email disabled 643 # note that this may cause signup messages not to be sent 644 645 if ( is_blank( $p_recipient ) || ( OFF == config_get( 'enable_email_notification' ) ) ) { 646 return; 647 } 648 649 $t_email_data = new EmailData; 650 651 $t_email_data->email = $t_recipient; 652 $t_email_data->subject = $t_subject; 653 $t_email_data->body = $t_message; 654 $t_email_data->metadata = array(); 655 $t_email_data->metadata['headers'] = $p_headers === null ? array() : $p_headers; 656 $t_email_data->metadata['priority'] = config_get( 'mail_priority' ); # Urgent = 1, Not Urgent = 5, Disable = 0 657 $t_email_data->metadata['charset'] = lang_get( 'charset', lang_get_current() ); 658 659 $t_hostname = ''; 660 $t_server = isset( $_SERVER ) ? $_SERVER : $HTTP_SERVER_VARS; 661 if ( isset( $t_server['SERVER_NAME'] ) ) { 662 $t_hostname = $t_server['SERVER_NAME']; 663 } else { 664 $t_address = explode( '@', config_get( 'from_email' ) ); 665 if ( isset( $t_address[1] ) ) { 666 $t_hostname = $t_address[1]; 667 } 668 } 669 $t_email_data->metadata['hostname'] = $t_hostname; 670 671 $t_email_id = email_queue_add( $t_email_data ); 672 673 return $t_email_id; 674 } 675 676 # -------------------- 677 # This function sends all the emails that are stored in the queue. If a failure occurs, then the 678 # function exists. This function will be called after storing emails in case of synchronous 679 # emails, or will be called from a cronjob in case of asynchronous emails. 680 # @@@ In case of synchronous email sending, we may get a race condition where two requests send the same email. 681 function email_send_all() { 682 $t_ids = email_queue_get_ids(); 683 684 $t_emails_recipients_failed = array(); 685 $t_start = microtime_float(); 686 foreach ( $t_ids as $t_id ) { 687 $t_email_data = email_queue_get( $t_id ); 688 689 # check if email was not found. This can happen if another request picks up the email first and sends it. 690 if ( $t_email_data === false ) { 691 continue; 692 } 693 694 # if unable to place the email in the email server queue, then the connection to the server is down, 695 # and hence no point to continue trying with the rest of the emails. 696 if ( !email_send( $t_email_data ) ) { 697 if ( microtime_float() - $t_start > 5) 698 break; 699 else 700 continue; 701 } 702 } 703 } 704 705 # -------------------- 706 # This function sends an email message based on the supplied email data. 707 function email_send( $p_email_data ) { 708 global $g_phpMailer_smtp; 709 710 $t_email_data = $p_email_data; 711 712 $t_recipient = trim( $t_email_data->email ); 713 $t_subject = string_email( trim( $t_email_data->subject ) ); 714 $t_message = string_email_links( trim( $t_email_data->body ) ); 715 716 $t_debug_email = config_get( 'debug_email' ); 717 718 # Visit http://phpmailer.sourceforge.net 719 # if you have problems with phpMailer 720 721 $mail = new PHPMailer; 722 $mail->PluginDir = PHPMAILER_PATH; 723 724 if ( isset( $t_email_data->metadata['hostname'] ) ) { 725 $mail->Hostname = $t_email_data->metadata['hostname']; 726 } 727 728 # @@@ should this be the current language (for the recipient) or the default one (for the user running the command) (thraxisp) 729 $mail->SetLanguage( lang_get( 'phpmailer_language', config_get( 'default_language' ) ), PHPMAILER_PATH . 'language' . DIRECTORY_SEPARATOR ); 730 731 # Select the method to send mail 732 switch ( config_get( 'phpMailer_method' ) ) { 733 case 0: $mail->IsMail(); 734 break; 735 736 case 1: $mail->IsSendmail(); 737 break; 738 739 case 2: $mail->IsSMTP(); 740 { 741 # SMTP collection is always kept alive 742 # 743 $mail->SMTPKeepAlive = true; 744 745 # @@@ yarick123: It is said in phpMailer comments, that phpMailer::smtp has private access. 746 # but there is no common method to reset PHPMailer object, so 747 # I see the smallest evel - to initialize only one 'private' 748 # field phpMailer::smtp in order to reuse smtp connection. 749 750 if( is_null( $g_phpMailer_smtp ) ) { 751 register_shutdown_function( 'email_smtp_close' ); 752 } else { 753 $mail->smtp = $g_phpMailer_smtp; 754 } 755 } 756 break; 757 } 758 759 $mail->IsHTML( false ); # set email format to plain text 760 $mail->WordWrap = 80; # set word wrap to 50 characters 761 $mail->Priority = $t_email_data->metadata['priority']; # Urgent = 1, Not Urgent = 5, Disable = 0 762 $mail->CharSet = $t_email_data->metadata['charset']; 763 $mail->Host = config_get( 'smtp_host' ); 764 $mail->From = config_get( 'from_email' ); 765 $mail->Sender = escapeshellcmd( config_get( 'return_path_email' ) ); 766 $mail->FromName = config_get( 'from_name'); 767 768 769 if ( !is_blank( config_get( 'smtp_username' ) ) ) { # Use SMTP Authentication 770 $mail->SMTPAuth = true; 771 $mail->Username = config_get( 'smtp_username' ); 772 $mail->Password = config_get( 'smtp_password' ); 773 } 774 775 if ( OFF !== $t_debug_email ) { 776 $t_message = 'To: '. $t_recipient . "\n\n" . $t_message; 777 $mail->AddAddress( $t_debug_email, '' ); 778 } else { 779 $mail->AddAddress( $t_recipient, '' ); 780 } 781 782 $mail->Subject = $t_subject; 783 $mail->Body = make_lf_crlf( "\n" . $t_message ); 784 785 if ( isset( $t_email_data->metadata['headers'] ) && is_array( $t_email_data->metadata['headers'] ) ) { 786 foreach ( $t_email_data->metadata['headers'] as $t_key => $t_value ) { 787 $mail->AddCustomHeader( "$t_key: $t_value" ); 788 } 789 } 790 791 if ( !$mail->Send() ) { 792 $t_success = false; 793 } else { 794 $t_success = true; 795 796 if ( $t_email_data->email_id > 0 ) { 797 email_queue_delete( $t_email_data->email_id ); 798 } 799 } 800 801 if ( !is_null( $mail->smtp ) ) { 802 # @@@ yarick123: It is said in phpMailer comments, that phpMailer::smtp has private access. 803 # but there is no common method to reset PHPMailer object, so 804 # I see the smallest evel - to initialize only one 'private' 805 # field phpMailer::smtp in order to reuse smtp connection. 806 $g_phpMailer_smtp = $mail->smtp; 807 } 808 809 return $t_success; 810 } 811 812 # -------------------- 813 # closes opened kept alive SMTP connection (if it was opened) 814 function email_smtp_close() { 815 global $g_phpMailer_smtp; 816 817 if ( !is_null( $g_phpMailer_smtp ) ) { 818 if ( $g_phpMailer_smtp->Connected() ) { 819 $g_phpMailer_smtp->Quit(); 820 $g_phpMailer_smtp->Close(); 821 } 822 $g_phpMailer_smtp = null; 823 } 824 } 825 # -------------------- 826 # formats the subject correctly 827 # we include the project name, bug id, and summary. 828 function email_build_subject( $p_bug_id ) { 829 # grab the project name 830 $p_project_name = project_get_field( bug_get_field( $p_bug_id, 'project_id' ), 'name' ); 831 832 # grab the subject (summary) 833 $p_subject = bug_get_field( $p_bug_id, 'summary' ); 834 835 # padd the bug id with zeros 836 $p_bug_id = bug_format_id( $p_bug_id ); 837 838 return '['.$p_project_name.' '.$p_bug_id.']: '.$p_subject; 839 } 840 # -------------------- 841 # clean up LF to CRLF 842 function make_lf_crlf( $p_string ) { 843 $t_string = str_replace( "\n", "\r\n", $p_string ); 844 return str_replace( "\r\r\n", "\r\n", $t_string ); 845 } 846 # -------------------- 847 # Check limit_email_domain option and append the domain name if it is set 848 function email_append_domain( $p_email ) { 849 $t_limit_email_domain = config_get( 'limit_email_domain' ); 850 if ( $t_limit_email_domain && !is_blank( $p_email ) ) { 851 $p_email = "$p_email@$t_limit_email_domain"; 852 } 853 854 return $p_email; 855 } 856 # -------------------- 857 # Send a bug reminder to each of the given user, or to each user if the first 858 # parameter is an array 859 # return an array of usernames to which the reminder was successfully sent 860 # 861 # @@@ I'm not sure this shouldn't return an array of user ids... more work for 862 # the caller but cleaner from an API point of view. 863 function email_bug_reminder( $p_recipients, $p_bug_id, $p_message ) { 864 865 if ( !is_array( $p_recipients ) ) { 866 $p_recipients = array( $p_recipients ); 867 } 868 869 $t_project_id = bug_get_field( $p_bug_id, 'project_id' ); 870 $t_sender_id = auth_get_current_user_id(); 871 $t_sender = user_get_name( $t_sender_id ); 872 873 $t_subject = email_build_subject( $p_bug_id ); 874 $t_date = date( config_get( 'normal_date_format' ) ); 875 876 $result = array(); 877 foreach ( $p_recipients as $t_recipient ) { 878 lang_push( user_pref_get_language( $t_recipient, $t_project_id ) ); 879 880 $t_email = user_get_email( $t_recipient ); 881 $result[] = user_get_name( $t_recipient ); 882 883 if ( access_has_project_level( config_get( 'show_user_email_threshold' ), $t_project_id, $t_recipient ) ) { 884 $t_sender_email = ' <' . current_user_get_field( 'email' ) . '>' ; 885 } else { 886 $t_sender_email = ''; 887 } 888 $t_header = "\n" . lang_get( 'on' ) . " $t_date, $t_sender $t_sender_email " . 889 lang_get( 'sent_you_this_reminder_about' ) . ": \n\n"; 890 $t_contents = $t_header . 891 string_get_bug_view_url_with_fqdn( $p_bug_id, $t_recipient ) . 892 " \n\n$p_message"; 893 894 if( ON == config_get( 'enable_email_notification' ) ) { 895 email_store( $t_email, $t_subject, $t_contents ); 896 } 897 898 lang_pop(); 899 } 900 901 if ( OFF == config_get( 'email_send_using_cronjob' ) ) { 902 email_send_all(); 903 } 904 905 return $result; 906 } 907 908 # -------------------- 909 # Send bug info to given user 910 # return true on success 911 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 ) { 912 913 $t_user_email = user_get_email( $p_user_id ); 914 915 # check whether email should be sent 916 # @@@ can be email field empty? if yes - then it should be handled here 917 if ( ON !== config_get( 'enable_email_notification' ) || is_blank( $t_user_email ) ) { 918 return true; 919 } 920 921 # build subject 922 $t_subject = '['.$p_visible_bug_data['email_project'].' ' 923 .bug_format_id( $p_visible_bug_data['email_bug'] ) 924 .']: '.$p_visible_bug_data['email_summary']; 925 926 # build message 927 928 $t_message = lang_get_defaulted( $p_message_id, null ); 929 930 if ( is_array( $p_header_optional_params ) ) { 931 $t_message = vsprintf( $t_message, $p_header_optional_params ); 932 } 933 934 if ( ( $t_message !== null ) && ( !is_blank( $t_message ) ) ) { 935 $t_message .= " \n"; 936 } 937 938 $t_message .= email_format_bug_message( $p_visible_bug_data ); 939 940 # build headers 941 $t_bug_id = $p_visible_bug_data['email_bug']; 942 $t_message_md5 = md5( $t_bug_id . $p_visible_bug_data['email_date_submitted'] ); 943 $t_mail_headers = array( 'keywords' => $p_visible_bug_data['set_category'] ); 944 if ( $p_message_id == 'email_notification_title_for_action_bug_submitted' ) { 945 $t_mail_headers['Message-ID'] = "<{$t_message_md5}>"; 946 } else { 947 $t_mail_headers['In-Reply-To'] = "<{$t_message_md5}>"; 948 } 949 950 # send mail 951 # PRINT '<br />email_bug_info::Sending email to :'.$t_user_email; 952 $t_ok = email_store( $t_user_email, $t_subject, $t_message, $t_mail_headers ); 953 954 return $t_ok; 955 } 956 957 # -------------------- 958 # Build the bug info part of the message 959 function email_format_bug_message( $p_visible_bug_data ) { 960 961 $t_normal_date_format = config_get( 'normal_date_format' ); 962 $t_complete_date_format = config_get( 'complete_date_format' ); 963 964 $t_email_separator1 = config_get( 'email_separator1' ); 965 $t_email_separator2 = config_get( 'email_separator2' ); 966 $t_email_padding_length = config_get( 'email_padding_length' ); 967 968 $t_status = $p_visible_bug_data['email_status']; 969 970 $p_visible_bug_data['email_date_submitted'] = date( $t_complete_date_format, $p_visible_bug_data['email_date_submitted'] ); 971 $p_visible_bug_data['email_last_modified'] = date( $t_complete_date_format, $p_visible_bug_data['email_last_modified'] ); 972 973 $p_visible_bug_data['email_status'] = get_enum_element( 'status', $t_status ); 974 $p_visible_bug_data['email_severity'] = get_enum_element( 'severity', $p_visible_bug_data['email_severity'] ); 975 $p_visible_bug_data['email_priority'] = get_enum_element( 'priority', $p_visible_bug_data['email_priority'] ); 976 $p_visible_bug_data['email_reproducibility'] = get_enum_element( 'reproducibility', $p_visible_bug_data['email_reproducibility'] ); 977 978 $t_message = $t_email_separator1 . " \n"; 979 980 if ( isset( $p_visible_bug_data['email_bug_view_url'] ) ) { 981 $t_message .= $p_visible_bug_data['email_bug_view_url'] . " \n"; 982 $t_message .= $t_email_separator1 . " \n"; 983 } 984 985 $t_message .= email_format_attribute( $p_visible_bug_data, 'email_reporter' ); 986 $t_message .= email_format_attribute( $p_visible_bug_data, 'email_handler' ); 987 $t_message .= $t_email_separator1 . " \n"; 988 $t_message .= email_format_attribute( $p_visible_bug_data, 'email_project' ); 989 $t_message .= email_format_attribute( $p_visible_bug_data, 'email_bug' ); 990 $t_message .= email_format_attribute( $p_visible_bug_data, 'email_category' ); 991 $t_message .= email_format_attribute( $p_visible_bug_data, 'email_reproducibility' ); 992 $t_message .= email_format_attribute( $p_visible_bug_data, 'email_severity' ); 993 $t_message .= email_format_attribute( $p_visible_bug_data, 'email_priority' ); 994 $t_message .= email_format_attribute( $p_visible_bug_data, 'email_status' ); 995 $t_message .= email_format_attribute( $p_visible_bug_data, 'email_target_version' ); 996 997 # custom fields formatting 998 foreach( $p_visible_bug_data['custom_fields'] as $t_custom_field_name => $t_custom_field_data ) { 999 $t_message .= str_pad( lang_get_defaulted( $t_custom_field_name, null ) . ': ', $t_email_padding_length, ' ', STR_PAD_RIGHT ); 1000 $t_message .= string_custom_field_value_for_email ( $t_custom_field_data['value'], $t_custom_field_data['type'] ); 1001 $t_message .= " \n"; 1002 } # end foreach custom field 1003 1004 if ( config_get( 'bug_resolved_status_threshold' ) <= $t_status ) { 1005 $p_visible_bug_data['email_resolution'] = get_enum_element( 'resolution', $p_visible_bug_data['email_resolution'] ); 1006 $t_message .= email_format_attribute( $p_visible_bug_data, 'email_resolution' ); 1007 $t_message .= email_format_attribute( $p_visible_bug_data, 'email_duplicate' ); 1008 $t_message .= email_format_attribute( $p_visible_bug_data, 'email_fixed_in_version' ); 1009 } 1010 $t_message .= $t_email_separator1 . " \n"; 1011 1012 $t_message .= email_format_attribute( $p_visible_bug_data, 'email_date_submitted' ); 1013 $t_message .= email_format_attribute( $p_visible_bug_data, 'email_last_modified' ); 1014 $t_message .= $t_email_separator1 . " \n"; 1015 1016 $t_message .= email_format_attribute( $p_visible_bug_data, 'email_summary' ); 1017 1018 $t_message .= lang_get( 'email_description' ) . ": \n".wordwrap( $p_visible_bug_data['email_description'] )."\n"; 1019 1020 # MASC RELATIONSHIP 1021 if ( ON == config_get( 'enable_relationship' ) ) { 1022 if (isset( $p_visible_bug_data['relations'] )) { 1023 $t_message .= $p_visible_bug_data['relations']; 1024 } 1025 } 1026 # MASC RELATIONSHIP 1027 1028 # Sponsorship 1029 if ( isset( $p_visible_bug_data['sponsorship_total'] ) && ( $p_visible_bug_data['sponsorship_total'] > 0 ) ) { 1030 $t_message .= $t_email_separator1 . " \n"; 1031 $t_message .= sprintf( lang_get( 'total_sponsorship_amount' ), sponsorship_format_amount( $p_visible_bug_data['sponsorship_total'] ) ) . "\n" . "\n"; 1032 1033 if ( isset( $p_visible_bug_data['sponsorships'] ) ) { 1034 foreach ( $p_visible_bug_data['sponsorships'] as $t_sponsorship ) { 1035 $t_date_added = date( config_get( 'normal_date_format' ), $t_sponsorship->date_submitted ); 1036 1037 $t_message .= $t_date_added . ': '; 1038 $t_message .= user_get_name( $t_sponsorship->user_id ); 1039 $t_message .= ' (' . sponsorship_format_amount( $t_sponsorship->amount ) . ')' . " \n"; 1040 } 1041 } 1042 } 1043 1044 $t_message .= $t_email_separator1 . " \n\n"; 1045 1046 # format bugnotes 1047 foreach ( $p_visible_bug_data['bugnotes'] as $t_bugnote ) { 1048 $t_last_modified = date( $t_normal_date_format, $t_bugnote->last_modified ); 1049 1050 $t_formatted_bugnote_id = bugnote_format_id( $t_bugnote->id ); 1051 $t_bugnote_link = string_process_bugnote_link ( config_get( 'bugnote_link_tag' ) . $t_bugnote->id, false, false, true ); 1052 1053 $t_time_tracking_minutes = bugnote_get_field ( $t_bugnote->id, 'time_tracking' ); 1054 if ( $t_time_tracking_minutes > 0 ) { 1055 $t_time_tracking = ' ' . lang_get( 'time_tracking' ) . ' ' . db_minutes_to_hhmm( $t_time_tracking_minutes ) . "\n"; 1056 } else { 1057 $t_time_tracking = ''; 1058 } 1059 1060 if ( user_exists( $t_bugnote->reporter_id ) ) { 1061 $t_access_level = access_get_project_level( null, $t_bugnote->reporter_id ); 1062 $t_access_level_string = ' (' . get_enum_element( 'access_levels', $t_access_level ) . ') - '; 1063 } else { 1064 $t_access_level_string = ''; 1065 } 1066 1067 $t_string = ' (' . $t_formatted_bugnote_id . ') ' . 1068 user_get_name( $t_bugnote->reporter_id ) . 1069 $t_access_level_string . 1070 $t_last_modified . "\n" . 1071 $t_time_tracking . ' ' . $t_bugnote_link; 1072 1073 $t_message .= $t_email_separator2 . " \n"; 1074 $t_message .= $t_string . " \n"; 1075 $t_message .= $t_email_separator2 . " \n"; 1076 $t_message .= wordwrap( $t_bugnote->note ) . " \n\n"; 1077 } 1078 1079 # format history 1080 if ( array_key_exists( 'history', $p_visible_bug_data ) ) { 1081 $t_message .= lang_get( 'bug_history' ) . " \n"; 1082 $t_message .= str_pad( lang_get( 'date_modified' ), 17 ) . 1083 str_pad( lang_get( 'username' ), 15 ) . 1084 str_pad( lang_get( 'field' ), 25 ) . 1085 str_pad( lang_get( 'change' ), 20 ). " \n"; 1086 1087 $t_message .= $t_email_separator1 . " \n"; 1088 1089 foreach ( $p_visible_bug_data['history'] as $t_raw_history_item ) { 1090 $t_localized_item = history_localize_item( $t_raw_history_item['field'], 1091 $t_raw_history_item['type'], 1092 $t_raw_history_item['old_value'], 1093 $t_raw_history_item['new_value'] ); 1094 1095 $t_message .= str_pad( date( $t_normal_date_format, $t_raw_history_item['date'] ), 17 ) . 1096 str_pad( $t_raw_history_item['username'], 15 ) . 1097 str_pad( $t_localized_item['note'], 25 ) . 1098 str_pad( $t_localized_item['change'], 20 ) . "\n"; 1099 } 1100 $t_message .= $t_email_separator1 . " \n\n"; 1101 } 1102 1103 return $t_message; 1104 } 1105 1106 # -------------------- 1107 # if $p_visible_bug_data contains specified attribute the function 1108 # returns concatenated translated attribute name and original 1109 # attribute value. Else return empty string. 1110 function email_format_attribute( $p_visible_bug_data, $attribute_id ) { 1111 1112 if ( array_key_exists( $attribute_id, $p_visible_bug_data ) ) { 1113 return str_pad( lang_get( $attribute_id ) . ': ', config_get( 'email_padding_length' ), ' ', STR_PAD_RIGHT ).$p_visible_bug_data[$attribute_id]."\n"; 1114 } 1115 return ''; 1116 } 1117 1118 # -------------------- 1119 # Build the bug raw data visible for specified user to be translated and sent by email to the user 1120 # (Filter the bug data according to user access level) 1121 # return array with bug data. See usage in email_format_bug_message(...) 1122 function email_build_visible_bug_data( $p_user_id, $p_bug_id, $p_message_id ) { 1123 $t_project_id = bug_get_field( $p_bug_id, 'project_id' ); 1124 $t_user_access_level = user_get_access_level( $p_user_id, $t_project_id ); 1125 $t_user_bugnote_order = user_pref_get_pref ( $p_user_id, 'bugnote_order' ); 1126 $t_user_bugnote_limit = user_pref_get_pref ( $p_user_id, 'email_bugnote_limit' ); 1127 1128 $row = bug_get_extended_row( $p_bug_id ); 1129 $t_bug_data = array(); 1130 1131 $t_bug_data['email_bug'] = $p_bug_id; 1132 1133 if ( $p_message_id !== 'email_notification_title_for_action_bug_deleted' ) { 1134 $t_bug_data['email_bug_view_url'] = string_get_bug_view_url_with_fqdn( $p_bug_id ); 1135 } 1136 1137 if ( access_compare_level( $t_user_access_level, config_get( 'view_handler_threshold' ) ) ) { 1138 if ( 0 != $row['handler_id'] ) { 1139 $t_bug_data['email_handler'] = user_get_name( $row['handler_id'] ); 1140 } else { 1141 $t_bug_data['email_handler'] = ''; 1142 } 1143 } 1144 1145 $t_bug_data['email_reporter'] = user_get_name( $row['reporter_id'] ); 1146 $t_bug_data['email_project'] = project_get_field( $row['project_id'], 'name' ); 1147 1148 $t_bug_data['email_category'] = $row['category']; 1149 1150 $t_bug_data['email_date_submitted'] = $row['date_submitted']; 1151 $t_bug_data['email_last_modified'] = $row['last_updated']; 1152 1153 $t_bug_data['email_status'] = $row['status']; 1154 $t_bug_data['email_severity'] = $row['severity']; 1155 $t_bug_data['email_priority'] = $row['priority']; 1156 $t_bug_data['email_reproducibility'] = $row['reproducibility']; 1157 1158 $t_bug_data['email_resolution'] = $row['resolution']; 1159 $t_bug_data['email_fixed_in_version'] = $row['fixed_in_version']; 1160 1161 if ( !is_blank( $row['target_version'] ) && access_compare_level( $t_user_access_level, config_get( 'roadmap_view_threshold' ) ) ) { 1162 $t_bug_data['email_target_version'] = $row['target_version']; 1163 } 1164 1165 if ( DUPLICATE == $row['resolution'] ) { 1166 $t_bug_data['email_duplicate'] = $row['duplicate_id']; 1167 } 1168 1169 $t_bug_data['email_summary'] = $row['summary']; 1170 $t_bug_data['email_description'] = $row['description']; 1171 1172 $t_bug_data['set_category'] = '[' . $t_bug_data['email_project'] . '] ' . $row['category']; 1173 1174 $t_bug_data['custom_fields'] = custom_field_get_linked_fields( $p_bug_id, $t_user_access_level ); 1175 $t_bug_data['bugnotes'] = bugnote_get_all_visible_bugnotes( $p_bug_id, $t_user_access_level, $t_user_bugnote_order, $t_user_bugnote_limit ); 1176 1177 # put history data 1178 if ( ( ON == config_get( 'history_default_visible' ) ) && access_compare_level( $t_user_access_level, config_get( 'view_history_threshold' ) ) ) { 1179 $t_bug_data['history'] = history_get_raw_events_array( $p_bug_id, $p_user_id ); 1180 } 1181 1182 # Sponsorship Information 1183 if ( ( config_get( 'enable_sponsorship' ) == ON ) && ( access_has_bug_level( config_get( 'view_sponsorship_total_threshold' ), $p_bug_id, $p_user_id ) ) ) { 1184 $t_sponsorship_ids = sponsorship_get_all_ids( $p_bug_id ); 1185 $t_bug_data['sponsorship_total'] = sponsorship_get_amount( $t_sponsorship_ids ); 1186 1187 if ( access_has_bug_level( config_get( 'view_sponsorship_details_threshold' ), $p_bug_id, $p_user_id ) ) { 1188 $t_bug_data['sponsorships'] = array(); 1189 foreach ( $t_sponsorship_ids as $id ) { 1190 $t_bug_data['sponsorships'][] = sponsorship_get( $id ); 1191 } 1192 } 1193 } 1194 1195 # MASC RELATIONSHIP 1196 if ( ON == config_get( 'enable_relationship' ) ) { 1197 $t_bug_data['relations'] = relationship_get_summary_text( $p_bug_id ); 1198 } 1199 1200 return $t_bug_data; 1201 } 1202 ?>
titre
Description
Corps
titre
Description
Corps
titre
Description
Corps
titre
Corps
| Généré le : Thu Nov 29 09:42:17 2007 | par Balluche grâce à PHPXref 0.7 |
|