/** * Moves an issue from a project to another. * * @todo Validate with sub-project / category inheritance scenarios. * @param int p_bug_id The bug to be moved. * @param int p_target_project_id The target project to move the bug to. * @access public */ function bug_move($p_bug_id, $p_target_project_id) { // Attempt to move disk based attachments to new project file directory. file_move_bug_attachments($p_bug_id, $p_target_project_id); // Move the issue to the new project. bug_set_field($p_bug_id, 'project_id', $p_target_project_id); // Update the category if needed $t_category_id = bug_get_field($p_bug_id, 'category_id'); // Bug has no category if ($t_category_id == 0) { // Category is required in target project, set it to default if (ON != config_get('allow_no_category', null, null, $p_target_project_id)) { bug_set_field($p_bug_id, 'category_id', config_get('default_category_for_moves')); } } else { $t_category_project_id = category_get_field($t_category_id, 'project_id'); if ($t_category_project_id != ALL_PROJECTS && !in_array($t_category_project_id, project_hierarchy_inheritance($p_target_project_id))) { // Map by name $t_category_name = category_get_field($t_category_id, 'name'); $t_target_project_category_id = category_get_id_by_name($t_category_name, $p_target_project_id, false); if ($t_target_project_category_id === false) { // Use default category after moves, since there is no match by name. $t_target_project_category_id = config_get('default_category_for_moves'); } bug_set_field($p_bug_id, 'category_id', $t_target_project_category_id); } } }
/** * Moves an issue from a project to another. * * @todo Validate with sub-project / category inheritance scenarios. * @param int p_bug_id The bug to be moved. * @param int p_target_project_id The target project to move the bug to. * @access public */ function bug_move( $p_bug_id, $p_target_project_id ) { // Attempt to move disk based attachments to new project file directory. file_move_bug_attachments( $p_bug_id, $p_target_project_id ); // Move the issue to the new project. bug_set_field( $p_bug_id, 'project_id', $p_target_project_id ); // Check if the category for the issue is global or not. $t_category_id = bug_get_field( $p_bug_id, 'category_id' ); $t_category_project_id = category_get_field( $t_category_id, 'project_id' ); // If not global, then attempt mapping it to the new project. if ( $t_category_project_id != ALL_PROJECTS && !project_hierarchy_inherit_parent( $p_target_project_id, $t_category_project_id ) ) { // Map by name $t_category_name = category_get_field( $t_category_id, 'name' ); $t_target_project_category_id = category_get_id_by_name( $t_category_name, $p_target_project_id, /* triggerErrors */ false ); if ( $t_target_project_category_id === false ) { // Use default category after moves, since there is no match by name. $t_target_project_category_id = config_get( 'default_category_for_moves' ); } bug_set_field( $p_bug_id, 'category_id', $t_target_project_category_id ); } }
/** * Given a category id, this function returns the category name. * An error will be triggered for a non-existent category id or category id = 0. * @param int $p_category_id category id * @return string category name * @access public */ function category_get_name( $p_category_id ) { return category_get_field( $p_category_id, 'name' ); }
/** * Collect valid email recipients for email notification * @todo yarick123: email_collect_recipients(...) will be completely rewritten to provide additional information such as language, user access,.. * @todo yarick123:sort recipients list by language to reduce switches between different languages * @param integer $p_bug_id A bug identifier. * @param string $p_notify_type Notification type. * @param array $p_extra_user_ids_to_email Array of additional email addresses to notify. * @return array */ function email_collect_recipients($p_bug_id, $p_notify_type, array $p_extra_user_ids_to_email = array()) { $t_recipients = array(); # add explicitly specified users $t_explicit_enabled = ON == email_notify_flag($p_notify_type, 'explicit'); foreach ($p_extra_user_ids_to_email as $t_user_id) { if ($t_explicit_enabled) { $t_recipients[$t_user_id] = true; log_event(LOG_EMAIL_RECIPIENT, 'Issue = #%d, add @U%d (explicitly specified)', $p_bug_id, $t_user_id); } else { log_event(LOG_EMAIL_RECIPIENT, 'Issue = #%d, skip @U%d (explicit disabled)', $p_bug_id, $t_user_id); } } # add Reporter $t_reporter_id = bug_get_field($p_bug_id, 'reporter_id'); if (ON == email_notify_flag($p_notify_type, 'reporter')) { $t_recipients[$t_reporter_id] = true; log_event(LOG_EMAIL_RECIPIENT, 'Issue = #%d, add @U%d (reporter)', $p_bug_id, $t_reporter_id); } else { log_event(LOG_EMAIL_RECIPIENT, 'Issue = #%d, skip @U%d (reporter disabled)', $p_bug_id, $t_reporter_id); } # add Handler $t_handler_id = bug_get_field($p_bug_id, 'handler_id'); if ($t_handler_id > 0) { if (ON == email_notify_flag($p_notify_type, 'handler')) { $t_recipients[$t_handler_id] = true; log_event(LOG_EMAIL_RECIPIENT, 'Issue = #%d, add @U%d (handler)', $p_bug_id, $t_handler_id); } else { log_event(LOG_EMAIL_RECIPIENT, 'Issue = #%d, skip @U%d (handler disabled)', $p_bug_id, $t_handler_id); } } $t_project_id = bug_get_field($p_bug_id, 'project_id'); # add users monitoring the bug $t_monitoring_enabled = ON == email_notify_flag($p_notify_type, 'monitor'); db_param_push(); $t_query = 'SELECT DISTINCT user_id FROM {bug_monitor} WHERE bug_id=' . db_param(); $t_result = db_query($t_query, array($p_bug_id)); while ($t_row = db_fetch_array($t_result)) { $t_user_id = $t_row['user_id']; if ($t_monitoring_enabled) { $t_recipients[$t_user_id] = true; log_event(LOG_EMAIL_RECIPIENT, 'Issue = #%d, add @U%d (monitoring)', $p_bug_id, $t_user_id); } else { log_event(LOG_EMAIL_RECIPIENT, 'Issue = #%d, skip @U%d (monitoring disabled)', $p_bug_id, $t_user_id); } } # add Category Owner if (ON == email_notify_flag($p_notify_type, 'category')) { $t_category_id = bug_get_field($p_bug_id, 'category_id'); if ($t_category_id > 0) { $t_category_assigned_to = category_get_field($t_category_id, 'user_id'); if ($t_category_assigned_to > 0) { $t_recipients[$t_category_assigned_to] = true; log_event(LOG_EMAIL_RECIPIENT, sprintf('Issue = #%d, add Category Owner = @U%d', $p_bug_id, $t_category_assigned_to)); } } } # add users who contributed bugnotes $t_bugnote_id = bugnote_get_latest_id($p_bug_id); $t_bugnote_date = bugnote_get_field($t_bugnote_id, 'last_modified'); $t_bug = bug_get($p_bug_id); $t_bug_date = $t_bug->last_updated; $t_notes_enabled = ON == email_notify_flag($p_notify_type, 'bugnotes'); db_param_push(); $t_query = 'SELECT DISTINCT reporter_id FROM {bugnote} WHERE bug_id = ' . db_param(); $t_result = db_query($t_query, array($p_bug_id)); while ($t_row = db_fetch_array($t_result)) { $t_user_id = $t_row['reporter_id']; if ($t_notes_enabled) { $t_recipients[$t_user_id] = true; log_event(LOG_EMAIL_RECIPIENT, 'Issue = #%d, add @U%d (note author)', $p_bug_id, $t_user_id); } else { log_event(LOG_EMAIL_RECIPIENT, 'Issue = #%d, skip @U%d (note author disabled)', $p_bug_id, $t_user_id); } } # add project users who meet the thresholds $t_bug_is_private = bug_get_field($p_bug_id, 'view_state') == VS_PRIVATE; $t_threshold_min = email_notify_flag($p_notify_type, 'threshold_min'); $t_threshold_max = email_notify_flag($p_notify_type, 'threshold_max'); $t_threshold_users = project_get_all_user_rows($t_project_id, $t_threshold_min); foreach ($t_threshold_users as $t_user) { if ($t_user['access_level'] <= $t_threshold_max) { if (!$t_bug_is_private || access_compare_level($t_user['access_level'], config_get('private_bug_threshold'))) { $t_recipients[$t_user['id']] = true; log_event(LOG_EMAIL_RECIPIENT, 'Issue = #%d, add @U%d (based on access level)', $p_bug_id, $t_user['id']); } } } # add users as specified by plugins $t_recipients_include_data = event_signal('EVENT_NOTIFY_USER_INCLUDE', array($p_bug_id, $p_notify_type)); foreach ($t_recipients_include_data as $t_plugin => $t_recipients_include_data2) { foreach ($t_recipients_include_data2 as $t_callback => $t_recipients_included) { # only handle if we get an array from the callback if (is_array($t_recipients_included)) { foreach ($t_recipients_included as $t_user_id) { $t_recipients[$t_user_id] = true; log_event(LOG_EMAIL_RECIPIENT, 'Issue = #%d, add @U%d (by %s plugin)', $p_bug_id, $t_user_id, $t_plugin); } } } } # FIXME: the value of $p_notify_type could at this stage be either a status # or a built-in actions such as 'owner and 'sponsor'. We have absolutely no # idea whether 'new' is indicating a new bug has been filed, or if the # status of an existing bug has been changed to 'new'. Therefore it is best # to just assume built-in actions have precedence over status changes. switch ($p_notify_type) { case 'new': case 'feedback': # This isn't really a built-in action (delete me!) # This isn't really a built-in action (delete me!) case 'reopened': case 'resolved': case 'closed': case 'bugnote': $t_pref_field = 'email_on_' . $p_notify_type; break; case 'owner': # The email_on_assigned notification type is now effectively # email_on_change_of_handler. $t_pref_field = 'email_on_assigned'; break; case 'deleted': case 'updated': case 'sponsor': case 'relation': case 'monitor': case 'priority': # This is never used, but exists in the database! # Issue #19459 these notification actions are not actually implemented # in the database and therefore aren't adjustable on a per-user # basis! The exception is 'monitor' that makes no sense being a # customisable per-user preference. # This is never used, but exists in the database! # Issue #19459 these notification actions are not actually implemented # in the database and therefore aren't adjustable on a per-user # basis! The exception is 'monitor' that makes no sense being a # customisable per-user preference. default: # Anything not built-in is probably going to be a status $t_pref_field = 'email_on_status'; break; } # @@@ we could optimize by modifiying user_cache() to take an array # of user ids so we could pull them all in. We'll see if it's necessary $t_final_recipients = array(); $t_user_ids = array_keys($t_recipients); user_cache_array_rows($t_user_ids); user_pref_cache_array_rows($t_user_ids); user_pref_cache_array_rows($t_user_ids, $t_bug->project_id); # Check whether users should receive the emails # and put email address to $t_recipients[user_id] foreach ($t_recipients as $t_id => $t_ignore) { # Possibly eliminate the current user if (auth_get_current_user_id() == $t_id && OFF == config_get('email_receive_own')) { log_event(LOG_EMAIL_RECIPIENT, 'Issue = #%d, drop @U%d (own action)', $p_bug_id, $t_id); continue; } # Eliminate users who don't exist anymore or who are disabled if (!user_exists($t_id) || !user_is_enabled($t_id)) { log_event(LOG_EMAIL_RECIPIENT, 'Issue = #%d, drop @U%d (user disabled)', $p_bug_id, $t_id); continue; } # Exclude users who have this notification type turned off if ($t_pref_field) { $t_notify = user_pref_get_pref($t_id, $t_pref_field); if (OFF == $t_notify) { log_event(LOG_EMAIL_RECIPIENT, 'Issue = #%d, drop @U%d (pref %s off)', $p_bug_id, $t_id, $t_pref_field); continue; } else { # Users can define the severity of an issue before they are emailed for # each type of notification $t_min_sev_pref_field = $t_pref_field . '_min_severity'; $t_min_sev_notify = user_pref_get_pref($t_id, $t_min_sev_pref_field); $t_bug_severity = bug_get_field($p_bug_id, 'severity'); if ($t_bug_severity < $t_min_sev_notify) { log_event(LOG_EMAIL_RECIPIENT, 'Issue = #%d, drop @U%d (pref threshold)', $p_bug_id, $t_id); continue; } } } # exclude users who don't have at least viewer access to the bug, # or who can't see bugnotes if the last update included a bugnote if (!access_has_bug_level(config_get('view_bug_threshold', null, $t_id, $t_bug->project_id), $p_bug_id, $t_id) || $t_bug_date == $t_bugnote_date && !access_has_bugnote_level(config_get('view_bug_threshold', null, $t_id, $t_bug->project_id), $t_bugnote_id, $t_id)) { log_event(LOG_EMAIL_RECIPIENT, 'Issue = #%d, drop @U%d (access level)', $p_bug_id, $t_id); continue; } # check to exclude users as specified by plugins $t_recipient_exclude_data = event_signal('EVENT_NOTIFY_USER_EXCLUDE', array($p_bug_id, $p_notify_type, $t_id)); $t_exclude = false; foreach ($t_recipient_exclude_data as $t_plugin => $t_recipient_exclude_data2) { foreach ($t_recipient_exclude_data2 as $t_callback => $t_recipient_excluded) { # exclude if any plugin returns true (excludes the user) if ($t_recipient_excluded) { $t_exclude = true; log_event(LOG_EMAIL_RECIPIENT, 'Issue = #%d, drop @U%d (by %s plugin)', $p_bug_id, $t_id, $t_plugin); } } } # user was excluded by a plugin if ($t_exclude) { continue; } # Finally, let's get their emails, if they've set one $t_email = user_get_email($t_id); if (is_blank($t_email)) { log_event(LOG_EMAIL_RECIPIENT, 'Issue = #%d, drop @U%d (no email address)', $p_bug_id, $t_id); } else { # @@@ we could check the emails for validity again but I think # it would be too slow $t_final_recipients[$t_id] = $t_email; } } return $t_final_recipients; }