%PDF- %PDF-
| Direktori : /www/loslex_o/tracker/core/commands/ |
| Current File : /www/loslex_o/tracker/core/commands/IssueAddCommand.php |
<?php
# MantisBT - A PHP based bugtracking system
# MantisBT is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# MantisBT is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with MantisBT. If not, see <http://www.gnu.org/licenses/>.
require_api( 'access_api.php' );
require_api( 'authentication_api.php' );
require_api( 'bug_api.php' );
require_api( 'config_api.php' );
require_api( 'constant_inc.php' );
require_api( 'custom_field_api.php' );
require_api( 'date_api.php' );
require_api( 'email_api.php' );
require_api( 'error_api.php' );
require_api( 'event_api.php' );
require_api( 'file_api.php' );
require_api( 'helper_api.php' );
require_api( 'lang_api.php' );
require_api( 'last_visited_api.php' );
require_api( 'profile_api.php' );
require_api( 'relationship_api.php' );
require_api( 'string_api.php' );
require_api( 'user_api.php' );
require_once( dirname( __FILE__ ) . '/../../api/soap/mc_api.php' );
require_once( dirname( __FILE__ ) . '/../../api/soap/mc_enum_api.php' );
require_once( dirname( __FILE__ ) . '/../../api/soap/mc_issue_api.php' );
require_once( dirname( __FILE__ ) . '/../../api/soap/mc_project_api.php' );
use Mantis\Exceptions\ClientException;
/**
* Sample:
* {
* "query": {
* },
* "payload": {
* ... see rest issue add documentation
* },
* "options: {
* "clone_info": { # Used only in case issue is cloned
* "master_issue_id": 1234,
* "relationship_type": 1, # BUG_RELATED
* "copy_files": true,
* "copy_notes": true,
* }
* }
* }
*/
/**
* A command that adds an issue.
*/
class IssueAddCommand extends Command {
/**
* The issue to add.
*
* @var BugData
*/
private $issue = null;
/**
* @var integer
*/
private $user_id;
/**
* The files to attach with the note.
*/
private $files = array();
/**
* Constructor
*
* @param array $p_data The command data.
*/
function __construct( array $p_data ) {
parent::__construct( $p_data );
}
/**
* Validate the data.
* @throws ClientException
*/
protected function validate() {
$this->user_id = auth_get_current_user_id();
$t_clone_info = $this->option( 'clone_info', array() );
$t_issue = $this->payload( 'issue' );
if( isset( $t_clone_info['master_issue_id'] ) ) {
if( bug_is_readonly( $t_clone_info['master_issue_id'] ) ) {
throw new ClientException(
sprintf( "Master issue '%d' is read-only", $t_clone_info['master_issue_id'] ),
ERROR_BUG_READ_ONLY_ACTION_DENIED,
array( $t_clone_info['master_issue_id'] )
);
}
}
if( !isset( $t_issue['summary'] ) || is_blank( $t_issue['summary'] ) ) {
throw new ClientException(
'Summary not specified',
ERROR_EMPTY_FIELD,
array( 'summary' ) );
}
$t_summary = $t_issue['summary'];
if( !isset( $t_issue['description'] ) || is_blank( $t_issue['description'] ) ) {
throw new ClientException(
'Description not specified',
ERROR_EMPTY_FIELD,
array( 'description' ) );
}
$t_description = $t_issue['description'];
if( !isset( $t_issue['project'] ) ) {
throw new ClientException(
'Project not specified',
ERROR_EMPTY_FIELD,
array( 'project' ) );
}
$t_project_id = mci_get_project_id( $t_issue['project'] );
if( $t_project_id == ALL_PROJECTS ) {
throw new ClientException(
'Project not specified',
ERROR_EMPTY_FIELD,
array( 'project' ) );
}
if( !project_exists( $t_project_id ) ) {
throw new ClientException(
sprintf( "Project '%d' not found", $t_project_id ),
ERROR_PROJECT_NOT_FOUND,
array( $t_project_id ) );
}
# in case the current project is not the same project of the bug we are
# viewing, override the current project. This to avoid problems with
# categories and handlers lists etc.
global $g_project_override;
$g_project_override = $t_project_id;
if( !access_has_project_level( config_get( 'report_bug_threshold' ), $t_project_id, $this->user_id ) ) {
throw new ClientException(
'User does not have access right to report issues',
ERROR_ACCESS_DENIED );
}
$t_handler_id = isset( $t_issue['handler'] ) ? mci_get_user_id( $t_issue['handler'] ) : NO_USER;
$t_priority_id = isset( $t_issue['priority'] ) ? mci_get_priority_id( $t_issue['priority'] ) : config_get( 'default_bug_priority' );
$t_severity_id = isset( $t_issue['severity'] ) ? mci_get_severity_id( $t_issue['severity'] ) : config_get( 'default_bug_severity' );
$t_status_id = isset( $t_issue['status'] ) ? mci_get_status_id( $t_issue['status'] ) : config_get( 'bug_submit_status' );
$t_reproducibility_id = isset( $t_issue['reproducibility'] ) ? mci_get_reproducibility_id( $t_issue['reproducibility'] ) : config_get( 'default_bug_reproducibility' );
$t_resolution_id = isset( $t_issue['resolution'] ) ? mci_get_resolution_id( $t_issue['resolution'] ) : config_get( 'default_bug_resolution' );
$t_projection_id = isset( $t_issue['projection'] ) ? mci_get_projection_id( $t_issue['projection'] ) : config_get( 'default_bug_projection' );
$t_eta_id = isset( $t_issue['eta'] ) ? mci_get_eta_id( $t_issue['eta'] ) : config_get( 'default_bug_eta' );
$t_view_state_id = isset( $t_issue['view_state'] ) ? mci_get_view_state_id( $t_issue['view_state'] ) : config_get( 'default_bug_view_status' );
# TODO: #17777: Add test case for mc_issue_add() and mc_issue_note_add() reporter override
if( isset( $t_issue['reporter'] ) ) {
$t_reporter_id = mci_get_user_id( $t_issue['reporter'] );
if( $t_reporter_id != $this->user_id ) {
# Make sure that active user has access level required to specify a different reporter.
$t_specify_reporter_access_level = config_get( 'webservice_specify_reporter_on_add_access_level_threshold' );
if( !access_has_project_level( $t_specify_reporter_access_level, $t_project_id, $this->user_id ) ) {
throw new ClientException(
'Active user does not have access level required to specify a different issue reporter',
ERROR_ACCESS_DENIED );
}
}
} else {
$t_reporter_id = $this->user_id;
}
# Prevent unauthorized users setting handler when reporting issue
if( $t_handler_id > 0 ) {
if ( !access_has_project_level( config_get( 'update_bug_assign_threshold' ) ) ) {
throw new ClientException(
'User not allowed to assign issues',
ERROR_ACCESS_DENIED );
}
} else {
# Ensure that resolved bugs have a handler
if( $t_handler_id == NO_USER && $t_status_id >= config_get( 'bug_resolved_status_threshold' ) ) {
$t_handler_id = $this->user_id;
}
}
if( $t_handler_id != NO_USER ) {
if( !user_exists( $t_handler_id ) ) {
throw new ClientException(
sprintf( "User '%d' not found.", $t_handler_id ),
ERROR_USER_BY_ID_NOT_FOUND,
array( $t_handler_id ) );
}
if( !access_has_project_level( config_get( 'handle_bug_threshold' ), $t_project_id, $t_handler_id ) ) {
throw new ClientException(
sprintf( "User '%d' can't be assigned issues.", $t_handler_id ),
ERROR_ACCESS_DENIED );
}
}
# Validate tags and make sure user is allowed to create them if needed
if( isset( $t_issue['tags'] ) && is_array( $t_issue['tags'] ) ) {
foreach( $t_issue['tags'] as $t_tag ) {
$t_tag_id = $this->get_tag_id( $t_tag );
if( $t_tag_id === false && !tag_can_create( $this->user_id ) ) {
throw new ClientException(
sprintf( "User '%d' can't create tag '%s'.", $this->user_id, $t_tag['name'] ),
ERROR_TAG_NOT_FOUND,
array( $t_tag['name'] )
);
}
}
}
$t_category = isset( $t_issue['category'] ) ? $t_issue['category'] : null;
$t_category_id = mci_get_category_id( $t_category, $t_project_id );
$this->issue = new BugData;
$this->issue->project_id = $t_project_id;
$this->issue->reporter_id = $t_reporter_id;
$this->issue->summary = $t_summary;
$this->issue->description = $t_description;
$this->issue->steps_to_reproduce = isset( $t_issue['steps_to_reproduce'] ) ? $t_issue['steps_to_reproduce'] : '';
$this->issue->additional_information = isset( $t_issue['additional_information'] ) ? $t_issue['additional_information'] : '';
$this->issue->handler_id = $t_handler_id;
$this->issue->priority = $t_priority_id;
$this->issue->severity = $t_severity_id;
$this->issue->reproducibility = $t_reproducibility_id;
$this->issue->status = $t_status_id;
$this->issue->resolution = $t_resolution_id;
$this->issue->projection = $t_projection_id;
$this->issue->category_id = $t_category_id;
$this->issue->eta = $t_eta_id;
$this->issue->os = isset( $t_issue['os'] ) ? $t_issue['os'] : '';
$this->issue->os_build = isset( $t_issue['os_build'] ) ? $t_issue['os_build'] : '';
$this->issue->platform = isset( $t_issue['platform'] ) ? $t_issue['platform'] : '';
$this->issue->build = isset( $t_issue['build'] ) ? $t_issue['build'] : '';
$this->issue->view_state = $t_view_state_id;
$this->issue->sponsorship_total = isset( $t_issue['sponsorship_total'] ) ? $t_issue['sponsorship_total'] : 0;
if( isset( $t_issue['profile_id'] ) ) {
$t_profile_id = (int)$t_issue['profile_id'];
} else if( isset( $t_issue['profile'] ) && isset( $t_issue['profile']['id'] ) ) {
$t_profile_id = (int)$t_issue['profile']['id'];
} else {
$t_profile_id = 0;
}
$this->issue->profile_id = $t_profile_id;
$t_version_id = isset( $t_issue['version'] ) ? mci_get_version_id( $t_issue['version'], $t_project_id, 'version' ) : 0;
if( $t_version_id != 0 ) {
$this->issue->version = version_get_field( $t_version_id, 'version' );
}
$t_fixed_in_version_id = isset( $t_issue['fixed_in_version'] ) ? mci_get_version_id( $t_issue['fixed_in_version'], $t_project_id, 'fixed_in_version' ) : 0;
if( $t_fixed_in_version_id != 0 ) {
$this->issue->fixed_in_version = version_get_field( $t_fixed_in_version_id, 'version' );
}
$t_target_version_id = isset( $t_issue['target_version'] ) ? mci_get_version_id( $t_issue['target_version'], $t_project_id, 'target_version' ) : 0;
if( $t_target_version_id != 0 && access_has_project_level( config_get( 'roadmap_update_threshold' ), $t_project_id, $this->user_id ) ) {
$this->issue->target_version = version_get_field( $t_target_version_id, 'version' );
}
if( isset( $t_issue['sticky'] ) &&
access_has_project_level( config_get( 'set_bug_sticky_threshold', null, null, $t_project_id ), $t_project_id ) ) {
$this->issue->sticky = $t_issue['sticky'];
}
if( isset( $t_issue['due_date'] ) &&
access_has_project_level( config_get( 'due_date_update_threshold' ), $t_project_id ) ) {
$this->issue->due_date = strtotime( $t_issue['due_date'] );
} else {
$this->issue->due_date = date_get_null();
}
# if a profile was selected then let's use that information
if( $this->issue->profile_id != 0 ) {
if( profile_is_global( $this->issue->profile_id ) ) {
$t_row = user_get_profile_row( ALL_USERS, $this->issue->profile_id );
} else {
$t_row = user_get_profile_row( $this->issue->reporter_id, $this->issue->profile_id );
}
if( is_blank( $this->issue->platform ) ) {
$this->issue->platform = $t_row['platform'];
}
if( is_blank( $this->issue->os ) ) {
$this->issue->os = $t_row['os'];
}
if( is_blank( $this->issue->os_build ) ) {
$this->issue->os_build = $t_row['os_build'];
}
}
mci_project_custom_fields_validate( $t_project_id, $t_issue['custom_fields'] );
if( isset( $t_issue['files'] ) && !empty( $t_issue['files'] ) ) {
if( !file_allow_bug_upload( /* issue id */ null, /* user id */ null, $t_project_id ) ) {
throw new ClientException(
'User not allowed to attach files.',
ERROR_ACCESS_DENIED );
}
foreach( $t_issue['files'] as $t_file ) {
$t_name = $t_file['name'];
if( strlen( $t_name ) > DB_FIELD_SIZE_FILENAME ) {
throw new ClientException(
"File name too long '$t_name'",
ERROR_FILE_NAME_TOO_LONG,
array( $t_name )
);
}
}
$this->files = $t_issue['files'];
}
# Trigger extensibility events to pre-process data before creating issue
helper_call_custom_function( 'issue_create_validate', array( $this->issue ) );
$this->issue = event_signal( 'EVENT_REPORT_BUG_DATA', $this->issue );
}
/**
* Process the command.
*
* @returns array Command response
* @throws ClientException
*/
protected function process() {
$t_issue = $this->payload( 'issue' );
# Create the bug
$t_issue_id = $this->issue->create();
log_event( LOG_WEBSERVICE, "created new issue id '$t_issue_id'" );
# Add Tags
if( isset( $t_issue['tags'] ) && is_array( $t_issue['tags'] ) ) {
$t_tags = array();
foreach( $t_issue['tags'] as $t_tag ) {
if( $this->get_tag_id( $t_tag ) === false ) {
$t_tag['id'] = tag_create( $t_tag['name'], $this->user_id );
log_event( LOG_WEBSERVICE,
"created new tag '" . $t_tag['name'] . "' id '" . $t_tag['id'] . "'"
);
}
$t_tags[] = $t_tag;
}
# @TODO should this be replaced by TagAttachCommand, as suggested in #24441 ?
mci_tag_set_for_issue( $t_issue_id, $t_tags, $this->user_id );
}
# Handle the file upload
file_attach_files( $t_issue_id, $this->files, /* bugnote_id */ null );
# Handle custom field submission
mci_issue_set_custom_fields( $t_issue_id, $t_issue['custom_fields'], /* history log insert */ false );
if( isset( $t_issue['monitors'] ) ) {
mci_issue_set_monitors( $t_issue_id, $this->user_id, $t_issue['monitors'] );
}
$t_clone_info = $this->option( 'clone_info', array() );
if( isset( $t_clone_info['master_issue_id'] ) ) {
$t_master_issue_id = (int)$t_clone_info['master_issue_id'];
# it's a child generation... let's create the relationship and add some lines in the history
# update master bug last updated
bug_update_date( $t_master_issue_id );
# Add log line to record the cloning action
history_log_event_special( $t_issue_id, BUG_CREATED_FROM, '', $t_master_issue_id );
history_log_event_special( $t_master_issue_id, BUG_CLONED_TO, '', $t_issue_id );
# copy notes from parent
if( isset( $t_clone_info['copy_notes'] ) && $t_clone_info['copy_notes'] ) {
$t_parent_bugnotes = bugnote_get_all_bugnotes( $t_master_issue_id );
foreach ( $t_parent_bugnotes as $t_parent_bugnote ) {
$t_private = $t_parent_bugnote->view_state == VS_PRIVATE;
bugnote_add(
$t_issue_id,
$t_parent_bugnote->note,
$t_parent_bugnote->time_tracking,
$t_private,
$t_parent_bugnote->note_type,
$t_parent_bugnote->note_attr,
$t_parent_bugnote->reporter_id,
false,
0,
0,
false );
# Note: we won't trigger mentions in the clone scenario.
}
}
# copy attachments from parent
if( isset( $t_clone_info['copy_files'] ) && $t_clone_info['copy_files'] ) {
file_copy_attachments( $t_master_issue_id, $t_issue_id );
}
if( isset( $t_clone_info['relationship_type'] ) && $t_clone_info['relationship_type'] > BUG_REL_ANY ) {
relationship_add( $t_issue_id, $t_master_issue_id, $t_clone_info['relationship_type'], /* email for source */ false );
}
}
$t_notes = isset( $t_issue['notes'] ) ? $t_issue['notes'] : array();
if( isset( $t_notes ) && is_array( $t_notes ) ) {
foreach( $t_notes as $t_note ) {
if( isset( $t_note['view_state'] ) ) {
$t_view_state = $t_note['view_state'];
} else {
$t_view_state = config_get( 'default_bugnote_view_status' );
}
$t_note_type = isset( $t_note['note_type'] ) ? (int)$t_note['note_type'] : BUGNOTE;
$t_note_attr = isset( $t_note['note_type'] ) ? $t_note['note_attr'] : '';
$t_view_state_id = mci_get_enum_id_from_objectref( 'view_state', $t_view_state );
$t_note_id = bugnote_add(
$t_issue_id,
$t_note['text'],
mci_get_time_tracking_from_note( $t_issue_id, $t_note ),
$t_view_state_id == VS_PRIVATE,
$t_note_type,
$t_note_attr,
$this->user_id,
false ); # don't send mail
bugnote_process_mentions( $t_issue_id, $t_note_id, $t_note['text'] );
log_event( LOG_WEBSERVICE, 'bugnote id \'' . $t_note_id . '\' added to issue \'' . $t_issue_id . '\'' );
}
}
# Mark the added issue as visited so that it appears on the last visited list.
last_visited_issue( $t_issue_id );
# Trigger Email Notifications
$this->issue->process_mentions();
email_bug_added( $t_issue_id );
# Trigger extensibility events
helper_call_custom_function( 'issue_create_notify', array( $t_issue_id ) );
event_signal( 'EVENT_REPORT_BUG', array( $this->issue, $t_issue_id ) );
return array( 'issue_id' => $t_issue_id );
}
/**
* Retrieves the Tag ID for the given Tag element.
*
* A tag element is an array with either an 'id', a 'name' key, or both.
* If id is provided, check that it exists and return it;
* if name is supplied, look it up and return the corresponding ID, or
* false if not found.
*
* @param array $p_tag Tag element
* @return integer|false Tag ID or false if tag does not exist
* @throws ClientException
*/
private function get_tag_id( array $p_tag ) {
if( isset( $p_tag['id'] ) ) {
$t_tag_id = $p_tag['id'];
if( !tag_exists( $t_tag_id ) ) {
throw new ClientException(
"Tag with id $t_tag_id not found.",
ERROR_TAG_NOT_FOUND,
array( $t_tag_id )
);
}
} elseif( isset( $p_tag['name'] ) ) {
$t_matches = array();
if( !tag_name_is_valid( $p_tag['name'], $t_matches )) {
throw new ClientException(
"Tag name '{$p_tag['name']}' is not valid.",
ERROR_TAG_NAME_INVALID,
array( $p_tag['name'] )
);
}
$t_existing_tag = tag_get_by_name( $p_tag['name'] );
$t_tag_id = $t_existing_tag === false ? false : $t_existing_tag['id'];
} else {
throw new ClientException(
'Tag without id or name.',
ERROR_TAG_NAME_INVALID
);
}
return $t_tag_id;
}
}