View Issue Details
ID | Project | Category | View Status | Date Submitted | Last Update |
---|---|---|---|---|---|
0012013 | mantisbt | plug-ins | public | 2010-06-09 02:49 | 2014-12-08 00:33 |
Reporter | dominik | Assigned To | dregad | ||
Priority | normal | Severity | minor | Reproducibility | always |
Status | closed | Resolution | fixed | ||
Product Version | 1.2.1 | ||||
Target Version | 1.3.0-beta.1 | Fixed in Version | 1.3.0-beta.1 | ||
Summary | 0012013: Improvements for plugin ImportExportXml (and required core changes) | ||||
Description | Attached a patch file with some ImportExportXml plugin improvements... Improvements done: Added support for custom fields, bugnotes and attachments Added support for dates (date submitted, last updated) - keep dates as given in import file Added function to easily retrieve the contents of a file (file_api.php) | ||||
Tags | patch | ||||
Attached Files | 2010-06-09_improvements_for_plugin_ImportExportXml.patch (25,825 bytes)
From 63d96cebfed6db935e0deae47025d931c1c54f61 Mon Sep 17 00:00:00 2001 From: Dominik Blunk <dominik@blunk.ch> Date: Mon, 7 Jun 2010 16:37:30 +0200 Subject: [PATCH 18/19] Set default for profile_id in constructor --- core/bug_api.php | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/core/bug_api.php b/core/bug_api.php index 7d6e030..02dcc96 100644 --- a/core/bug_api.php +++ b/core/bug_api.php @@ -100,7 +100,7 @@ class BugData { # omitted: # var $bug_text_id - protected $profile_id; + protected $profile_id = 0; # extended info protected $description = ''; -- 1.7.0.2.msysgit.0 From 1a13b8fcb4f54fec2a0d1e74b675e69010204418 Mon Sep 17 00:00:00 2001 From: Dominik Blunk <dominik@blunk.ch> Date: Tue, 8 Jun 2010 16:48:42 +0200 Subject: [PATCH 19/19] Plugin ImportXml enhanced with bugnotes, custom fields and attachments --- core/bug_api.php | 13 ++- core/bugnote_api.php | 15 ++- core/constant_inc.php | 1 + core/file_api.php | 117 +++++++++++++++++++++- lang/strings_english.txt | 1 + plugins/XmlImportExport/ImportXml.php | 32 +++++- plugins/XmlImportExport/ImportXml/Issue.php | 143 +++++++++++++++++++++++++-- plugins/XmlImportExport/pages/export.php | 87 ++++++++++++++++ 8 files changed, 383 insertions(+), 26 deletions(-) diff --git a/core/bug_api.php b/core/bug_api.php index 02dcc96..7a2b5d4 100644 --- a/core/bug_api.php +++ b/core/bug_api.php @@ -302,7 +302,14 @@ class BugData { # check due_date format if( is_blank( $this->due_date ) ) { - $this_due_date = date_get_null(); + $this->due_date = date_get_null(); + } + # check date submitted and last modified + if( is_blank( $this->date_submitted ) ) { + $this->date_submitted = db_now(); + } + if( is_blank( $this->last_updated ) ) { + $this->last_updated = db_now(); } $t_bug_table = db_get_table( 'mantis_bug_table' ); @@ -366,7 +373,7 @@ class BugData { " . db_param() . ',' . db_param() . ',' . db_param() . ',' . db_param() . ", " . db_param() . ',' . db_param() . ',' . db_param() . ',' . db_param() . ')'; - db_query_bound( $query, Array( $this->project_id, $this->reporter_id, $this->handler_id, $this->duplicate_id, $this->priority, $this->severity, $this->reproducibility, $t_status, $this->resolution, $this->projection, $this->category_id, db_now(), db_now(), $this->eta, $t_text_id, $this->os, $this->os_build, $this->platform, $this->version, $this->build, $this->profile_id, $this->summary, $this->view_state, $this->sponsorship_total, $this->sticky, $this->fixed_in_version, $this->target_version, $this->due_date ) ); + db_query_bound( $query, Array( $this->project_id, $this->reporter_id, $this->handler_id, $this->duplicate_id, $this->priority, $this->severity, $this->reproducibility, $t_status, $this->resolution, $this->projection, $this->category_id, $this->date_submitted, $this->last_updated, $this->eta, $t_text_id, $this->os, $this->os_build, $this->platform, $this->version, $this->build, $this->profile_id, $this->summary, $this->view_state, $this->sponsorship_total, $this->sticky, $this->fixed_in_version, $this->target_version, $this->due_date ) ); $this->id = db_insert_id( $t_bug_table ); @@ -1683,7 +1690,7 @@ function bug_reopen( $p_bug_id, $p_bugnote_text = '', $p_time_tracking = '0:00', */ function bug_update_date( $p_bug_id ) { $c_bug_id = (int) $p_bug_id; - + $t_bug_table = db_get_table( 'mantis_bug_table' ); $query = "UPDATE $t_bug_table diff --git a/core/bugnote_api.php b/core/bugnote_api.php index 230eff7..fde2f76 100644 --- a/core/bugnote_api.php +++ b/core/bugnote_api.php @@ -120,14 +120,19 @@ function bugnote_is_user_reporter( $p_bugnote_id, $p_user_id ) { * @param string $p_attr * @param int $p_user_id user id * @param bool $p_send_email generate email? + * @param int $p_date_submitted date submitted (defaults to now()) + * @param int $p_last_modified last modification date (defaults to now()) + * @param bool $p_skip_bug_update skip bug last modification update (useful when importing bugs/bugnotes) * @return false|int false or indicating bugnote id added * @access public */ -function bugnote_add( $p_bug_id, $p_bugnote_text, $p_time_tracking = '0:00', $p_private = false, $p_type = 0, $p_attr = '', $p_user_id = null, $p_send_email = TRUE ) { +function bugnote_add( $p_bug_id, $p_bugnote_text, $p_time_tracking = '0:00', $p_private = false, $p_type = 0, $p_attr = '', $p_user_id = null, $p_send_email = TRUE, $p_date_submitted = 0, $p_last_modified = 0, $p_skip_bug_update = FALSE ) { $c_bug_id = db_prepare_int( $p_bug_id ); $c_time_tracking = helper_duration_to_minutes( $p_time_tracking ); $c_private = db_prepare_bool( $p_private ); $c_type = db_prepare_int( $p_type ); + $c_date_submitted = $p_date_submitted <= 0 ? db_now() : db_prepare_int( $p_date_submitted ); + $c_last_modified = $p_last_modified <= 0 ? db_now() : db_prepare_int( $p_last_modified ); $t_bugnote_text_table = db_get_table( 'mantis_bugnote_text_table' ); $t_bugnote_table = db_get_table( 'mantis_bugnote_table' ); @@ -166,7 +171,7 @@ function bugnote_add( $p_bug_id, $p_bugnote_text, $p_time_tracking = '0:00', $p_ # Check for private bugnotes. # @@@ VB: Should we allow users to report private bugnotes, and possibly see only their own private ones - if( $p_private && access_has_bug_level( config_get( 'private_bugnote_threshold' ), $p_bug_id, $c_user_id ) ) { + if( $c_private && access_has_bug_level( config_get( 'private_bugnote_threshold' ), $p_bug_id, $c_user_id ) ) { $t_view_state = VS_PRIVATE; } else { $t_view_state = VS_PUBLIC; @@ -177,13 +182,15 @@ function bugnote_add( $p_bug_id, $p_bugnote_text, $p_time_tracking = '0:00', $p_ (bug_id, reporter_id, bugnote_text_id, view_state, date_submitted, last_modified, note_type, note_attr, time_tracking ) VALUES (" . db_param() . ', ' . db_param() . ',' . db_param() . ', ' . db_param() . ', ' . db_param() . ',' . db_param() . ', ' . db_param() . ', ' . db_param() . ', ' . db_param() . ' )'; - db_query_bound( $query, Array( $c_bug_id, $c_user_id, $t_bugnote_text_id, $t_view_state, db_now(), db_now(), $c_type, $p_attr, $c_time_tracking ) ); + db_query_bound( $query, Array( $c_bug_id, $c_user_id, $t_bugnote_text_id, $t_view_state, $c_date_submitted, $c_last_modified, $c_type, $p_attr, $c_time_tracking ) ); # get bugnote id $t_bugnote_id = db_insert_id( $t_bugnote_table ); # update bug last updated - bug_update_date( $p_bug_id ); + if ( !$p_skip_bug_update ) { + bug_update_date( $p_bug_id ); + } # log new bug history_log_event_special( $p_bug_id, BUGNOTE_ADDED, bugnote_format_id( $t_bugnote_id ) ); diff --git a/core/constant_inc.php b/core/constant_inc.php index 3dc5a91..6f17b41 100644 --- a/core/constant_inc.php +++ b/core/constant_inc.php @@ -303,6 +303,7 @@ define( 'ERROR_CUSTOM_FIELD_NAME_NOT_UNIQUE', 1301 ); define( 'ERROR_CUSTOM_FIELD_IN_USE', 1302 ); define( 'ERROR_CUSTOM_FIELD_INVALID_VALUE', 1303 ); define( 'ERROR_CUSTOM_FIELD_INVALID_DEFINITION', 1304 ); +define( 'ERROR_CUSTOM_FIELD_NOT_LINKED_TO_PROJECT', 1305 ); # ERROR_LDAP_* define( 'ERROR_LDAP_AUTH_FAILED', 1400 ); diff --git a/core/file_api.php b/core/file_api.php index 6fafe2b..6ff2d85 100644 --- a/core/file_api.php +++ b/core/file_api.php @@ -615,12 +615,21 @@ function file_is_name_unique( $p_name, $p_bug_id ) { * * @param integer $p_bug_id the bug id * @param array $p_file the uploaded file info, as retrieved from gpc_get_file() + * @param array $p_file the uploaded file info, as retrieved from gpc_get_file() + * @param string $p_table table ('bug' or 'doc') + * @param string $p_title file title + * @param string $p_desc file description + * @param int $p_user_id user id + * @param int $p_date_added date added + * @param bool $p_skip_bug_update skip bug last modification update (useful when importing bug attachments) */ -function file_add( $p_bug_id, $p_file, $p_table = 'bug', $p_title = '', $p_desc = '', $p_user_id = null ) { +function file_add( $p_bug_id, $p_file, $p_table = 'bug', $p_title = '', $p_desc = '', $p_user_id = null, $p_date_added = 0, $p_skip_bug_update = false ) { file_ensure_uploaded( $p_file ); $t_file_name = $p_file['name']; $t_tmp_file = $p_file['tmp_name']; + + $c_date_added = $p_date_added <= 0 ? db_now() : db_prepare_int( $p_date_added ); if( !file_type_check( $t_file_name ) ) { trigger_error( ERROR_FILE_NOT_ALLOWED, ERROR ); @@ -715,13 +724,15 @@ function file_add( $p_bug_id, $p_file, $p_table = 'bug', $p_title = '', $p_desc $query = "INSERT INTO $t_file_table (" . $p_table . "_id, title, description, diskfile, filename, folder, filesize, file_type, date_added, content, user_id) VALUES - ($c_id, '$c_title', '$c_desc', '$c_unique_name', '$c_new_file_name', '$c_file_path', $c_file_size, '$c_file_type', '" . db_now() . "', $c_content, $c_user_id)"; + ($c_id, '$c_title', '$c_desc', '$c_unique_name', '$c_new_file_name', '$c_file_path', $c_file_size, '$c_file_type', '" . $c_date_added . "', $c_content, $c_user_id)"; db_query( $query ); if( 'bug' == $p_table ) { # updated the last_updated date - $result = bug_update_date( $p_bug_id ); + if ( !$p_skip_bug_update ) { + $result = bug_update_date( $p_bug_id ); + } # log new bug history_log_event_special( $p_bug_id, FILE_ADDED, $t_file_name ); @@ -856,3 +867,103 @@ function file_get_extension( $p_filename ) { } return $t_extension; } + +/** + * Get file content + * + * @param int $p_file_id file id + * @param string $p_type file type + * @return array file type and content + */ +function file_get_content( $p_file_id, $p_type = 'bug' ) { + # we handle the case where the file is attached to a bug + # or attached to a project as a project doc. + $query = ''; + switch ( $p_type ) { + case 'bug': + $t_bug_file_table = db_get_table( 'mantis_bug_file_table' ); + $query = "SELECT * + FROM $t_bug_file_table + WHERE id=" . db_param(); + break; + case 'doc': + $t_project_file_table = db_get_table( 'mantis_project_file_table' ); + $query = "SELECT * + FROM $t_project_file_table + WHERE id=" . db_param(); + break; + default: + return false; + } + $result = db_query_bound( $query, Array( $p_file_id ) ); + $row = db_fetch_array( $result ); + + if ( $f_type == 'bug' ) { + $t_project_id = bug_get_field( $row['bug_id'], 'project_id' ); + } else { + $t_project_id = $row['bug_id']; + } + + # If finfo is available (always true for PHP >= 5.3.0) we can use it to determine the MIME type of files + $finfo_available = false; + if ( class_exists( 'finfo' ) ) { + $t_info_file = config_get( 'fileinfo_magic_db_file' ); + + if ( is_blank( $t_info_file ) ) { + $finfo = new finfo( FILEINFO_MIME ); + } else { + $finfo = new finfo( FILEINFO_MIME, $t_info_file ); + } + + if ( $finfo ) { + $finfo_available = true; + } + } + + $t_content_type = $row['file_type']; + + # dump file content to the connection. + switch ( config_get( 'file_upload_method' ) ) { + case DISK: + $t_local_disk_file = file_normalize_attachment_path( $row['diskfile'], $t_project_id ); + + if ( file_exists( $t_local_disk_file ) ) { + if ( $finfo_available ) { + $t_file_info_type = $finfo->file( $t_local_disk_file ); + + if ( $t_file_info_type !== false ) { + $t_content_type = $t_file_info_type; + } + } + return array( 'type' => $t_content_type, 'content' => file_get_contents( $t_local_disk_file ) ); + } + break; + case FTP: + $t_local_disk_file = file_normalize_attachment_path( $row['diskfile'], $t_project_id ); + + if ( !file_exists( $t_local_disk_file ) ) { + $ftp = file_ftp_connect(); + file_ftp_get ( $ftp, $t_local_disk_file, $row['diskfile'] ); + file_ftp_disconnect( $ftp ); + } + + if ( $finfo_available ) { + $t_file_info_type = $finfo->file( $t_local_disk_file ); + + if ( $t_file_info_type !== false ) { + $t_content_type = $t_file_info_type; + } + } + return array( 'type' => $t_content_type, 'content' => file_get_contents( $t_local_disk_file ) ); + break; + default: + if ( $finfo_available ) { + $t_file_info_type = $finfo->buffer( $row['content'] ); + + if ( $t_file_info_type !== false ) { + $t_content_type = $t_file_info_type; + } + } + return array( 'type' => $t_content_type, 'content' => $row['content'] ); + } +} diff --git a/lang/strings_english.txt b/lang/strings_english.txt index ed7534d..9ac4ebf 100644 --- a/lang/strings_english.txt +++ b/lang/strings_english.txt @@ -239,6 +239,7 @@ $MANTIS_ERROR[ERROR_CUSTOM_FIELD_NAME_NOT_UNIQUE] = 'This is a duplicate name.'; $MANTIS_ERROR[ERROR_CUSTOM_FIELD_IN_USE] = 'At least one project still uses this field.'; $MANTIS_ERROR[ERROR_CUSTOM_FIELD_INVALID_VALUE] = 'Invalid value for field "%1$s".'; $MANTIS_ERROR[ERROR_CUSTOM_FIELD_INVALID_DEFINITION] = 'Invalid custom field definition.'; +$MANTIS_ERROR[ERROR_CUSTOM_FIELD_NOT_LINKED_TO_PROJECT] = 'Custom field "%1$s" (id %2$s) not linked to currently active project.'; $MANTIS_ERROR[ERROR_LDAP_AUTH_FAILED] = 'LDAP Authentication Failed.'; $MANTIS_ERROR[ERROR_LDAP_SERVER_CONNECT_FAILED] = 'LDAP Server Connection Failed.'; $MANTIS_ERROR[ERROR_LDAP_UPDATE_FAILED] = 'LDAP Record Update has failed.'; diff --git a/plugins/XmlImportExport/ImportXml.php b/plugins/XmlImportExport/ImportXml.php index 9b0ebc9..fa25252 100644 --- a/plugins/XmlImportExport/ImportXml.php +++ b/plugins/XmlImportExport/ImportXml.php @@ -101,18 +101,38 @@ class ImportXML { } echo " Done\n"; - + + // replace references in bug description and additional information $importedIssues = $this->itemsMap_->getall( 'issue' ); printf( "Processing cross-references for %s issues...", count( $importedIssues ) ); foreach( $importedIssues as $oldId => $newId ) { $bugData = bug_get( $newId, true ); - + + $content_replaced = false; $bugLinkRegexp = '/(^|[^\w])(' . preg_quote( $this->source_->issuelink, '/' ) . ')(\d+)\b/e'; - $replacement = '"\\1" . $this->getReplacementString( "\\2", "\\3" )'; - - $bugData->description = preg_replace( $bugLinkRegexp, $replacement, $bugData->description ); - bug_update( $newId, $bugData, true, true ); + // replace links in description + preg_match_all( $bugLinkRegexp, $bugData->description, $matches ); + if ( is_array( $matches[3] && count( $matches[3] ) > 0 ) ) { + $content_replaced = true; + foreach ( $matches[3] as $old_id ) { + $bugData->description = str_replace( $this->source_->issuelink . $old_id, $this->getReplacementString( $this->source_->issuelink, $old_id ), $bugData->description); + } + } + // replace links in additional information + preg_match_all( $bugLinkRegexp, $bugData->additional_information, $matches ); + if ( is_array( $matches[3] && count( $matches[3] ) > 0 ) ) { + $content_replaced = true; + foreach ( $matches[3] as $old_id ) { + $bugData->additional_information = str_replace( $this->source_->issuelink . $old_id, $this->getReplacementString( $this->source_->issuelink, $old_id ), $bugData->additional_information); + } + } + if ( $content_replaced ) { + // only update bug if necessary (otherwise last update date would be unnecessarily overwritten) + $bugData->update( true ); + } } + + // @todo: replace references within bugnotes echo " Done\n"; } diff --git a/plugins/XmlImportExport/ImportXml/Issue.php b/plugins/XmlImportExport/ImportXml/Issue.php index ba1220a..23bd7fe 100644 --- a/plugins/XmlImportExport/ImportXml/Issue.php +++ b/plugins/XmlImportExport/ImportXml/Issue.php @@ -37,12 +37,12 @@ class ImportXml_Issue implements ImportXml_Interface { // Read stream until current item finishes, processing // the data found - public function process( XMLreader$reader ) { + public function process( XMLreader $reader ) { //print "\nImportIssue process()\n"; $t_project_id = helper_get_current_project(); // TODO: category_get_id_by_name could work by default on current project $userId = auth_get_current_user_id( ); - + $depth = $reader->depth; while( $reader->read() && ($reader->depth > $depth || @@ -108,10 +108,91 @@ class ImportXml_Issue implements ImportXml_Interface { break; case 'project'; - - // ignore original value, use current project - $this->newbug_->project_id = $t_project_id; + // ignore original value, use current project + $this->newbug_->project_id = $t_project_id; break; + + case 'custom_fields': + // store custom fields + $i = -1; + $depth_cf = $reader->depth; + while ( $reader->read() && + ( $reader->depth > $depth_cf || + $reader->nodeType != XMLReader::END_ELEMENT ) ) + { + + if ( $reader->nodeType == XMLReader::ELEMENT ) { + if ($reader->localName == 'custom_field') { + $i++; + } + switch ( $reader->localName ) { + default: + $field = $reader->localName; + $reader->read( ); + $t_custom_fields[$i]->$field = $reader->value; + } + } + } + break; + + case 'bugnotes': + // store bug notes + $i = -1; + $depth_bn = $reader->depth; + while ( $reader->read() && + ( $reader->depth > $depth_bn || + $reader->nodeType != XMLReader::END_ELEMENT ) ) + { + + if ( $reader->nodeType == XMLReader::ELEMENT ) { + if ($reader->localName == 'bugnote') { + $i++; + } + switch ( $reader->localName ) { + case 'reporter': + $t_old_id = $reader->getAttribute( 'id' ); + $reader->read( ); + $t_bugnotes[$i]->reporter_id = $this->get_user_id( $reader->value, $userId ); + break; + + case 'view_state': + $t_old_id = $reader->getAttribute( 'id' ); + $reader->read( ); + $t_bugnotes[$i]->private = $reader->value == VS_PRIVATE ? true : false; + break; + + default: + $field = $reader->localName; + $reader->read( ); + $t_bugnotes[$i]->$field = $reader->value; + } + } + } + break; + + case 'attachments': + // store attachments + $i = -1; + $depth_att = $reader->depth; + while ( $reader->read() && + ( $reader->depth > $depth_att || + $reader->nodeType != XMLReader::END_ELEMENT ) ) + { + + if ( $reader->nodeType == XMLReader::ELEMENT ) { + if ($reader->localName == 'attachment') { + $i++; + } + switch ( $reader->localName ) { + default: + $field = $reader->localName; + $reader->read( ); + $t_attachments[$i]->$field = $reader->value; + } + } + } + break; + default: $field = $reader->localName; @@ -124,11 +205,49 @@ class ImportXml_Issue implements ImportXml_Interface { // now save the new bug $this->new_id_ = $this->newbug_->create(); + + // add custom fields + if ( $this->new_id_ > 0 && is_array( $t_custom_fields ) && count( $t_custom_fields ) > 0 ) { + foreach ( $t_custom_fields as $t_custom_field) { + $t_custom_field_id = custom_field_get_id_from_name( $t_custom_field->name ); + if ( custom_field_ensure_exists( $t_custom_field_id ) && custom_field_is_linked( $t_custom_field_id, $t_project_id ) ) { + custom_field_set_value( $t_custom_field->id, $this->new_id_, $t_custom_field->value ); + } + else { + error_parameters( $t_custom_field->name, $t_custom_field_id ); + trigger_error( ERROR_CUSTOM_FIELD_NOT_LINKED_TO_PROJECT, ERROR ); + } + } + } + + // add bugnotes + if ( $this->new_id_ > 0 && is_array( $t_bugnotes ) && count( $t_bugnotes ) > 0 ) { + foreach ( $t_bugnotes as $t_bugnote) { + bugnote_add( $this->new_id_, $t_bugnote->note, $t_bugnote->time_tracking, $t_bugnote->private, $t_bugnote->note_type, $t_bugnote_>note_attr, $t_bugnote->reporter_id, false, $t_bugnote->date_submitted, $t_bugnote->last_modified, true ); + } + } + + // add attachments + if ( $this->new_id_ > 0 && is_array( $t_attachments ) && count( $t_attachments ) > 0 ) { + foreach ( $t_attachments as $t_attachment) { + // Create a temporary file in the temporary files directory using sys_get_temp_dir() + $temp_file_name = tempnam( sys_get_temp_dir(), 'MantisImport' ); + file_put_contents( $temp_file_name, base64_decode( $t_attachment->content ) ); + $file_data = array( 'name' => $t_attachment->filename, + 'type' => $t_attachment->file_type, + 'tmp_name' => $temp_file_name, + 'size' => filesize( $temp_file_name ) ); + // unfortunately we have no clue who has added the attachment (this could only be fetched from history -> feel free to implement this) + // also I have no clue where description should come from... + file_add( $this->new_id_, $file_data, 'bug', $t_attachment->title, $p_desc = '', $p_user_id = null, $t_attachment->date_added, true ); + unlink( $temp_file_name ); + } + } //echo "\nnew bug: $this->new_id_\n"; } - public function update_map( Mapper$mapper ) { + public function update_map( Mapper $mapper ) { $mapper->add( 'issue', $this->old_id_, $this->new_id_ ); } @@ -150,10 +269,14 @@ class ImportXml_Issue implements ImportXml_Interface { */ private function get_user_id( $username, $squash_userid = 0 ) { $t_user_id = user_get_id_by_name( $username ); - if( $t_user_id === false ) { - - //not found - $t_user_id = $squash_userid; + if ( $t_user_id === false ) { + // user not found by username -> check real name + // keep in mind that the setting config_get( 'show_realname' ) may differ between import and export system + $t_user_id = user_get_id_by_realname( $username ); + if ( $t_user_id === false ) { + //not found + $t_user_id = $squash_userid; + } } return $t_user_id; } diff --git a/plugins/XmlImportExport/pages/export.php b/plugins/XmlImportExport/pages/export.php index 135127e..ab1444c 100644 --- a/plugins/XmlImportExport/pages/export.php +++ b/plugins/XmlImportExport/pages/export.php @@ -146,6 +146,93 @@ foreach( $t_result as $t_row ) { $writer->writeElement( $t_element, $t_value ); } } + + # fetch and export custom fields + $t_custom_fields = custom_field_get_all_linked_fields( $t_row->id ); + if ( is_array( $t_custom_fields ) && count( $t_custom_fields ) > 0 ) { + $writer->startElement( 'custom_fields' ); + foreach ( $t_custom_fields as $custom_field_name => $t_custom_field ) { + $writer->startElement( 'custom_field' ); + # id + $writer->writeElement( 'id', custom_field_get_id_from_name( $custom_field_name ) ); + # title + $writer->writeElement( 'name', $custom_field_name ); + # filename + $writer->writeElement( 'type', $t_custom_field['type'] ); + # filesize + $writer->writeElement( 'value', $t_custom_field['value'] ); + # file_type + $writer->writeElement( 'access_level_r', $t_custom_field['access_level_r'] ); + + $writer->endElement(); # custom_field + } + $writer->endElement(); # custom_fields + } + + # fetch and export bugnotes + $t_bugnotes = bugnote_get_all_bugnotes( $t_row->id ); + if ( is_array( $t_bugnotes ) && count( $t_bugnotes ) > 0 ) { + $writer->startElement( 'bugnotes' ); + foreach ( $t_bugnotes as $t_bugnote ) { + $writer->startElement( 'bugnote' ); + # id + $writer->writeElement( 'id', $t_bugnote->id ); + # reporter + $writer->startElement( 'reporter' ); + $writer->writeAttribute( 'id', $t_bugnote->reporter_id ); + $writer->text( user_get_name( $t_bugnote->reporter_id ) ); + $writer->endElement( ); + # bug note + $writer->writeElement( 'note', $t_bugnote->note ); + # view state + $writer->startElement( 'view_state' ); + $writer->writeAttribute( 'id', $t_bugnote->view_state ); + $writer->text( get_enum_element( 'view_state', $t_bugnote->view_state ) ); + $writer->endElement( ); + # date submitted + $writer->writeElement( 'date_submitted', $t_bugnote->date_submitted ); + # last modified + $writer->writeElement( 'last_modified', $t_bugnote->last_modified ); + # note type + $writer->writeElement( 'note_type', $t_bugnote->note_type ); + # note attr + $writer->writeElement( 'note_attr', $t_bugnote->note_attr ); + # time tracking + $writer->writeElement( 'time_tracking', $t_bugnote->time_tracking ); + + $writer->endElement(); # bugnote + } + $writer->endElement(); # bugnotes + } + + # fetch and export attachments + $t_attachments = bug_get_attachments( $t_row->id ); + if ( is_array( $t_attachments ) && count( $t_attachments ) > 0 ) { + $writer->startElement( 'attachments' ); + foreach ( $t_attachments as $t_attachment ) { + $writer->startElement( 'attachment' ); + # id + $writer->writeElement( 'id', $t_attachment['id'] ); + # title + $writer->writeElement( 'title', $t_attachment['title'] ); + # filename + $writer->writeElement( 'filename', $t_attachment['filename'] ); + # filesize + $writer->writeElement( 'filesize', $t_attachment['filesize'] ); + # file_type + $writer->writeElement( 'file_type', $t_attachment['file_type'] ); + # last added + $writer->writeElement( 'date_added', $t_attachment['date_added'] ); + # content + $content = file_get_content( $t_attachment['id'] ); + + $writer->writeElement( 'content', base64_encode( $content['content'] ) ); + + $writer->endElement(); # attachment + } + $writer->endElement(); # bugnotes + } + $writer->endElement(); # issue // Save memory by clearing cache -- 1.7.0.2.msysgit.0 XmlImportExport_1.3.x.patch (24,841 bytes)
From 515cd551c8a2c3490a77b06b21602e5d4ef9ceec Mon Sep 17 00:00:00 2001 From: Dominik Blunk <dominik@blunk.ch> Date: Wed, 23 Jun 2010 15:29:37 +0200 Subject: [PATCH] Patch based on master (1.3.x) --- core/bug_api.php | 14 ++- core/bugnote_api.php | 15 ++- core/constant_inc.php | 1 + core/file_api.php | 115 +++++++++++++++++++++- lang/strings_english.txt | 1 + plugins/XmlImportExport/ImportXml.php | 31 +++++- plugins/XmlImportExport/ImportXml/Issue.php | 140 +++++++++++++++++++++++++-- plugins/XmlImportExport/pages/export.php | 87 +++++++++++++++++ 8 files changed, 380 insertions(+), 24 deletions(-) diff --git a/core/bug_api.php b/core/bug_api.php index ef12ccf..a8d974b 100644 --- a/core/bug_api.php +++ b/core/bug_api.php @@ -108,7 +108,7 @@ class BugData { # omitted: # var $bug_text_id - protected $profile_id; + protected $profile_id = 0; # extended info protected $description = ''; @@ -310,8 +310,15 @@ class BugData { # check due_date format if( is_blank( $this->due_date ) ) { - $this_due_date = date_get_null(); + $this->due_date = date_get_null(); + } + # check date submitted and last modified + if( is_blank( $this->date_submitted ) ) { + $this->date_submitted = db_now(); } + if( is_blank( $this->last_updated ) ) { + $this->last_updated = db_now(); + } $t_bug_table = db_get_table( 'bug' ); $t_bug_text_table = db_get_table( 'bug_text' ); @@ -374,7 +381,8 @@ class BugData { " . db_param() . ',' . db_param() . ',' . db_param() . ',' . db_param() . ", " . db_param() . ',' . db_param() . ',' . db_param() . ',' . db_param() . ')'; - db_query_bound( $query, Array( $this->project_id, $this->reporter_id, $this->handler_id, $this->duplicate_id, $this->priority, $this->severity, $this->reproducibility, $t_status, $this->resolution, $this->projection, $this->category_id, db_now(), db_now(), $this->eta, $t_text_id, $this->os, $this->os_build, $this->platform, $this->version, $this->build, $this->profile_id, $this->summary, $this->view_state, $this->sponsorship_total, $this->sticky, $this->fixed_in_version, $this->target_version, $this->due_date ) ); + db_query_bound( $query, Array( $this->project_id, $this->reporter_id, $this->handler_id, $this->duplicate_id, $this->priority, $this->severity, $this->reproducibility, $t_status, $this->resolution, $this->projection, $this->category_id, $this->date_submitted, $this->last_updated, $this->eta, $t_text_id, $this->os, $this->os_build, $this->platform, $this->version, $this->build, $this->profile_id, $this->summary, $this->view_state, $this->sponsorship_total, $this->sticky, $this->fixed_in_version, $this->target_version, $this->due_date ) ); + $this->id = db_insert_id( $t_bug_table ); diff --git a/core/bugnote_api.php b/core/bugnote_api.php index 88d3987..2b046a7 100644 --- a/core/bugnote_api.php +++ b/core/bugnote_api.php @@ -136,14 +136,19 @@ function bugnote_is_user_reporter( $p_bugnote_id, $p_user_id ) { * @param string $p_attr * @param int $p_user_id user id * @param bool $p_send_email generate email? + * @param int $p_date_submitted date submitted (defaults to now()) + * @param int $p_last_modified last modification date (defaults to now()) + * @param bool $p_skip_bug_update skip bug last modification update (useful when importing bugs/bugnotes) * @return false|int false or indicating bugnote id added * @access public */ -function bugnote_add( $p_bug_id, $p_bugnote_text, $p_time_tracking = '0:00', $p_private = false, $p_type = 0, $p_attr = '', $p_user_id = null, $p_send_email = TRUE ) { +function bugnote_add( $p_bug_id, $p_bugnote_text, $p_time_tracking = '0:00', $p_private = false, $p_type = 0, $p_attr = '', $p_user_id = null, $p_send_email = TRUE, $p_date_submitted = 0, $p_last_modified = 0, $p_skip_bug_update = FALSE ) { $c_bug_id = db_prepare_int( $p_bug_id ); $c_time_tracking = helper_duration_to_minutes( $p_time_tracking ); $c_private = db_prepare_bool( $p_private ); $c_type = db_prepare_int( $p_type ); + $c_date_submitted = $p_date_submitted <= 0 ? db_now() : db_prepare_int( $p_date_submitted ); + $c_last_modified = $p_last_modified <= 0 ? db_now() : db_prepare_int( $p_last_modified ); $t_bugnote_text_table = db_get_table( 'bugnote_text' ); $t_bugnote_table = db_get_table( 'bugnote' ); @@ -181,7 +186,7 @@ function bugnote_add( $p_bug_id, $p_bugnote_text, $p_time_tracking = '0:00', $p_ } # Check for private bugnotes. - if( $p_private && access_has_bug_level( config_get( 'set_view_status_threshold' ), $p_bug_id, $c_user_id ) ) { + if( $c_private && access_has_bug_level( config_get( 'set_view_status_threshold' ), $p_bug_id, $c_user_id ) ) { $t_view_state = VS_PRIVATE; } else { $t_view_state = VS_PUBLIC; @@ -192,13 +197,15 @@ function bugnote_add( $p_bug_id, $p_bugnote_text, $p_time_tracking = '0:00', $p_ (bug_id, reporter_id, bugnote_text_id, view_state, date_submitted, last_modified, note_type, note_attr, time_tracking ) VALUES (" . db_param() . ', ' . db_param() . ',' . db_param() . ', ' . db_param() . ', ' . db_param() . ',' . db_param() . ', ' . db_param() . ', ' . db_param() . ', ' . db_param() . ' )'; - db_query_bound( $query, Array( $c_bug_id, $c_user_id, $t_bugnote_text_id, $t_view_state, db_now(), db_now(), $c_type, $p_attr, $c_time_tracking ) ); + db_query_bound( $query, Array( $c_bug_id, $c_user_id, $t_bugnote_text_id, $t_view_state, $c_date_submitted, $c_last_modified, $c_type, $p_attr, $c_time_tracking ) ); # get bugnote id $t_bugnote_id = db_insert_id( $t_bugnote_table ); # update bug last updated - bug_update_date( $p_bug_id ); + if ( !$p_skip_bug_update ) { + bug_update_date( $p_bug_id ); + } # log new bug history_log_event_special( $p_bug_id, BUGNOTE_ADDED, bugnote_format_id( $t_bugnote_id ) ); diff --git a/core/constant_inc.php b/core/constant_inc.php index a598aa7..e906d2f 100644 --- a/core/constant_inc.php +++ b/core/constant_inc.php @@ -302,6 +302,7 @@ define( 'ERROR_CUSTOM_FIELD_NAME_NOT_UNIQUE', 1301 ); define( 'ERROR_CUSTOM_FIELD_IN_USE', 1302 ); define( 'ERROR_CUSTOM_FIELD_INVALID_VALUE', 1303 ); define( 'ERROR_CUSTOM_FIELD_INVALID_DEFINITION', 1304 ); +define( 'ERROR_CUSTOM_FIELD_NOT_LINKED_TO_PROJECT', 1305 ); # ERROR_LDAP_* define( 'ERROR_LDAP_AUTH_FAILED', 1400 ); diff --git a/core/file_api.php b/core/file_api.php index ca89e95..ed4a79d 100644 --- a/core/file_api.php +++ b/core/file_api.php @@ -628,12 +628,19 @@ function file_is_name_unique( $p_name, $p_bug_id ) { * * @param integer $p_bug_id the bug id * @param array $p_file the uploaded file info, as retrieved from gpc_get_file() + * @param string $p_table table ('bug' or 'doc') + * @param string $p_title file title + * @param string $p_desc file description + * @param int $p_user_id user id + * @param int $p_date_added date added + * @param bool $p_skip_bug_update skip bug last modification update (useful when importing bug attachments) */ -function file_add( $p_bug_id, $p_file, $p_table = 'bug', $p_title = '', $p_desc = '', $p_user_id = null ) { +function file_add( $p_bug_id, $p_file, $p_table = 'bug', $p_title = '', $p_desc = '', $p_user_id = null, $p_date_added = 0, $p_skip_bug_update = false ) { file_ensure_uploaded( $p_file ); $t_file_name = $p_file['name']; $t_tmp_file = $p_file['tmp_name']; + $c_date_added = $p_date_added <= 0 ? db_now() : db_prepare_int( $p_date_added ); if( !file_type_check( $t_file_name ) ) { trigger_error( ERROR_FILE_NOT_ALLOWED, ERROR ); @@ -728,13 +735,15 @@ function file_add( $p_bug_id, $p_file, $p_table = 'bug', $p_title = '', $p_desc $query = "INSERT INTO $t_file_table (" . $p_table . "_id, title, description, diskfile, filename, folder, filesize, file_type, date_added, content, user_id) VALUES - ($c_id, '$c_title', '$c_desc', '$c_unique_name', '$c_new_file_name', '$c_file_path', $c_file_size, '$c_file_type', '" . db_now() . "', $c_content, $c_user_id)"; + ($c_id, '$c_title', '$c_desc', '$c_unique_name', '$c_new_file_name', '$c_file_path', $c_file_size, '$c_file_type', '" . $c_date_added . "', $c_content, $c_user_id)"; db_query( $query ); if( 'bug' == $p_table ) { # updated the last_updated date - $result = bug_update_date( $p_bug_id ); + if ( !$p_skip_bug_update ) { + $result = bug_update_date( $p_bug_id ); + } # log new bug history_log_event_special( $p_bug_id, FILE_ADDED, $t_file_name ); @@ -868,3 +877,103 @@ function file_get_extension( $p_filename ) { } return $t_extension; } + +/** + * Get file content + * + * @param int $p_file_id file id + * @param string $p_type file type + * @return array file type and content + */ +function file_get_content( $p_file_id, $p_type = 'bug' ) { + # we handle the case where the file is attached to a bug + # or attached to a project as a project doc. + $query = ''; + switch ( $p_type ) { + case 'bug': + $t_bug_file_table = db_get_table( 'mantis_bug_file_table' ); + $query = "SELECT * + FROM $t_bug_file_table + WHERE id=" . db_param(); + break; + case 'doc': + $t_project_file_table = db_get_table( 'mantis_project_file_table' ); + $query = "SELECT * + FROM $t_project_file_table + WHERE id=" . db_param(); + break; + default: + return false; + } + $result = db_query_bound( $query, Array( $p_file_id ) ); + $row = db_fetch_array( $result ); + + if ( $f_type == 'bug' ) { + $t_project_id = bug_get_field( $row['bug_id'], 'project_id' ); + } else { + $t_project_id = $row['bug_id']; + } + + # If finfo is available (always true for PHP >= 5.3.0) we can use it to determine the MIME type of files + $finfo_available = false; + if ( class_exists( 'finfo' ) ) { + $t_info_file = config_get( 'fileinfo_magic_db_file' ); + + if ( is_blank( $t_info_file ) ) { + $finfo = new finfo( FILEINFO_MIME ); + } else { + $finfo = new finfo( FILEINFO_MIME, $t_info_file ); + } + + if ( $finfo ) { + $finfo_available = true; + } + } + + $t_content_type = $row['file_type']; + + # dump file content to the connection. + switch ( config_get( 'file_upload_method' ) ) { + case DISK: + $t_local_disk_file = file_normalize_attachment_path( $row['diskfile'], $t_project_id ); + + if ( file_exists( $t_local_disk_file ) ) { + if ( $finfo_available ) { + $t_file_info_type = $finfo->file( $t_local_disk_file ); + + if ( $t_file_info_type !== false ) { + $t_content_type = $t_file_info_type; + } + } + return array( 'type' => $t_content_type, 'content' => file_get_contents( $t_local_disk_file ) ); + } + break; + case FTP: + $t_local_disk_file = file_normalize_attachment_path( $row['diskfile'], $t_project_id ); + + if ( !file_exists( $t_local_disk_file ) ) { + $ftp = file_ftp_connect(); + file_ftp_get ( $ftp, $t_local_disk_file, $row['diskfile'] ); + file_ftp_disconnect( $ftp ); + } + + if ( $finfo_available ) { + $t_file_info_type = $finfo->file( $t_local_disk_file ); + + if ( $t_file_info_type !== false ) { + $t_content_type = $t_file_info_type; + } + } + return array( 'type' => $t_content_type, 'content' => file_get_contents( $t_local_disk_file ) ); + break; + default: + if ( $finfo_available ) { + $t_file_info_type = $finfo->buffer( $row['content'] ); + + if ( $t_file_info_type !== false ) { + $t_content_type = $t_file_info_type; + } + } + return array( 'type' => $t_content_type, 'content' => $row['content'] ); + } +} diff --git a/lang/strings_english.txt b/lang/strings_english.txt index 038c178..f4bb7a0 100644 --- a/lang/strings_english.txt +++ b/lang/strings_english.txt @@ -238,6 +238,7 @@ $MANTIS_ERROR[ERROR_CUSTOM_FIELD_NAME_NOT_UNIQUE] = 'This is a duplicate name.'; $MANTIS_ERROR[ERROR_CUSTOM_FIELD_IN_USE] = 'At least one project still uses this field.'; $MANTIS_ERROR[ERROR_CUSTOM_FIELD_INVALID_VALUE] = 'Invalid value for field "%1$s".'; $MANTIS_ERROR[ERROR_CUSTOM_FIELD_INVALID_DEFINITION] = 'Invalid custom field definition.'; +$MANTIS_ERROR[ERROR_CUSTOM_FIELD_NOT_LINKED_TO_PROJECT] = 'Custom field "%1$s" (id %2$s) not linked to currently active project.'; $MANTIS_ERROR[ERROR_LDAP_AUTH_FAILED] = 'LDAP Authentication Failed.'; $MANTIS_ERROR[ERROR_LDAP_SERVER_CONNECT_FAILED] = 'LDAP Server Connection Failed.'; $MANTIS_ERROR[ERROR_LDAP_UPDATE_FAILED] = 'LDAP Record Update has failed.'; diff --git a/plugins/XmlImportExport/ImportXml.php b/plugins/XmlImportExport/ImportXml.php index 203d35e..83f974d 100644 --- a/plugins/XmlImportExport/ImportXml.php +++ b/plugins/XmlImportExport/ImportXml.php @@ -102,23 +102,42 @@ class ImportXML { echo " Done\n"; + // replace references in bug description and additional information $importedIssues = $this->itemsMap_->getall( 'issue' ); printf( "Processing cross-references for %s issues...", count( $importedIssues ) ); foreach( $importedIssues as $oldId => $newId ) { $bugData = bug_get( $newId, true ); $bugLinkRegexp = '/(^|[^\w])(' . preg_quote( $this->source_->issuelink, '/' ) . ')(\d+)\b/e'; - $replacement = '"\\1" . $this->getReplacementString( "\\2", "\\3" )'; - - $bugData->description = preg_replace( $bugLinkRegexp, $replacement, $bugData->description ); - bug_update( $newId, $bugData, true, true ); - } + // replace links in description + preg_match_all( $bugLinkRegexp, $bugData->description, $matches ); + if ( is_array( $matches[3] && count( $matches[3] ) > 0 ) ) { + $content_replaced = true; + foreach ( $matches[3] as $old_id ) { + $bugData->description = str_replace( $this->source_->issuelink . $old_id, $this->getReplacementString( $this->source_->issuelink, $old_id ), $bugData->description); + } + } + // replace links in additional information + preg_match_all( $bugLinkRegexp, $bugData->additional_information, $matches ); + if ( is_array( $matches[3] && count( $matches[3] ) > 0 ) ) { + $content_replaced = true; + foreach ( $matches[3] as $old_id ) { + $bugData->additional_information = str_replace( $this->source_->issuelink . $old_id, $this->getReplacementString( $this->source_->issuelink, $old_id ), $bugData->additional_information); + } + } + if ( $content_replaced ) { + // only update bug if necessary (otherwise last update date would be unnecessarily overwritten) + $bugData->update( true ); + } + } + + // @todo: replace references within bugnotes echo " Done\n"; } /** * Compute and return the new link - * + * */ private function getReplacementString( $oldLinkTag, $oldId ) { $linkTag = config_get( 'bug_link_tag' ); diff --git a/plugins/XmlImportExport/ImportXml/Issue.php b/plugins/XmlImportExport/ImportXml/Issue.php index 78541ce..20d5a7e 100644 --- a/plugins/XmlImportExport/ImportXml/Issue.php +++ b/plugins/XmlImportExport/ImportXml/Issue.php @@ -37,7 +37,7 @@ class ImportXml_Issue implements ImportXml_Interface { // Read stream until current item finishes, processing // the data found - public function process( XMLreader$reader ) { + public function process( XMLreader $reader ) { //print "\nImportIssue process()\n"; $t_project_id = helper_get_current_project(); // TODO: category_get_id_by_name could work by default on current project @@ -109,9 +109,91 @@ class ImportXml_Issue implements ImportXml_Interface { case 'project'; - // ignore original value, use current project - $this->newbug_->project_id = $t_project_id; - break; + // ignore original value, use current project + $this->newbug_->project_id = $t_project_id; + break; + + case 'custom_fields': + // store custom fields + $i = -1; + $depth_cf = $reader->depth; + while ( $reader->read() && + ( $reader->depth > $depth_cf || + $reader->nodeType != XMLReader::END_ELEMENT ) ) + { + + if ( $reader->nodeType == XMLReader::ELEMENT ) { + if ($reader->localName == 'custom_field') { + $i++; + } + switch ( $reader->localName ) { + default: + $field = $reader->localName; + $reader->read( ); + $t_custom_fields[$i]->$field = $reader->value; + } + } + } + break; + + case 'bugnotes': + // store bug notes + $i = -1; + $depth_bn = $reader->depth; + while ( $reader->read() && + ( $reader->depth > $depth_bn || + $reader->nodeType != XMLReader::END_ELEMENT ) ) + { + + if ( $reader->nodeType == XMLReader::ELEMENT ) { + if ($reader->localName == 'bugnote') { + $i++; + } + switch ( $reader->localName ) { + case 'reporter': + $t_old_id = $reader->getAttribute( 'id' ); + $reader->read( ); + $t_bugnotes[$i]->reporter_id = $this->get_user_id( $reader->value, $userId ); + break; + + case 'view_state': + $t_old_id = $reader->getAttribute( 'id' ); + $reader->read( ); + $t_bugnotes[$i]->private = $reader->value == VS_PRIVATE ? true : false; + break; + + default: + $field = $reader->localName; + $reader->read( ); + $t_bugnotes[$i]->$field = $reader->value; + } + } + } + break; + + case 'attachments': + // store attachments + $i = -1; + $depth_att = $reader->depth; + while ( $reader->read() && + ( $reader->depth > $depth_att || + $reader->nodeType != XMLReader::END_ELEMENT ) ) + { + + if ( $reader->nodeType == XMLReader::ELEMENT ) { + if ($reader->localName == 'attachment') { + $i++; + } + switch ( $reader->localName ) { + default: + $field = $reader->localName; + $reader->read( ); + $t_attachments[$i]->$field = $reader->value; + } + } + } + break; + default: $field = $reader->localName; @@ -124,11 +206,49 @@ class ImportXml_Issue implements ImportXml_Interface { // now save the new bug $this->new_id_ = $this->newbug_->create(); + + // add custom fields + if ( $this->new_id_ > 0 && is_array( $t_custom_fields ) && count( $t_custom_fields ) > 0 ) { + foreach ( $t_custom_fields as $t_custom_field) { + $t_custom_field_id = custom_field_get_id_from_name( $t_custom_field->name ); + if ( custom_field_ensure_exists( $t_custom_field_id ) && custom_field_is_linked( $t_custom_field_id, $t_project_id ) ) { + custom_field_set_value( $t_custom_field->id, $this->new_id_, $t_custom_field->value ); + } + else { + error_parameters( $t_custom_field->name, $t_custom_field_id ); + trigger_error( ERROR_CUSTOM_FIELD_NOT_LINKED_TO_PROJECT, ERROR ); + } + } + } + + // add bugnotes + if ( $this->new_id_ > 0 && is_array( $t_bugnotes ) && count( $t_bugnotes ) > 0 ) { + foreach ( $t_bugnotes as $t_bugnote) { + bugnote_add( $this->new_id_, $t_bugnote->note, $t_bugnote->time_tracking, $t_bugnote->private, $t_bugnote->note_type, $t_bugnote_>note_attr, $t_bugnote->reporter_id, false, $t_bugnote->date_submitted, $t_bugnote->last_modified, true ); + } + } + + // add attachments + if ( $this->new_id_ > 0 && is_array( $t_attachments ) && count( $t_attachments ) > 0 ) { + foreach ( $t_attachments as $t_attachment) { + // Create a temporary file in the temporary files directory using sys_get_temp_dir() + $temp_file_name = tempnam( sys_get_temp_dir(), 'MantisImport' ); + file_put_contents( $temp_file_name, base64_decode( $t_attachment->content ) ); + $file_data = array( 'name' => $t_attachment->filename, + 'type' => $t_attachment->file_type, + 'tmp_name' => $temp_file_name, + 'size' => filesize( $temp_file_name ) ); + // unfortunately we have no clue who has added the attachment (this could only be fetched from history -> feel free to implement this) + // also I have no clue where description should come from... + file_add( $this->new_id_, $file_data, 'bug', $t_attachment->title, $p_desc = '', $p_user_id = null, $t_attachment->date_added, true ); + unlink( $temp_file_name ); + } + } //echo "\nnew bug: $this->new_id_\n"; } - public function update_map( Mapper$mapper ) { + public function update_map( Mapper $mapper ) { $mapper->add( 'issue', $this->old_id_, $this->new_id_ ); } @@ -151,9 +271,13 @@ class ImportXml_Issue implements ImportXml_Interface { private function get_user_id( $username, $squash_userid = 0 ) { $t_user_id = user_get_id_by_name( $username ); if( $t_user_id === false ) { - - //not found - $t_user_id = $squash_userid; + // user not found by username -> check real name + // keep in mind that the setting config_get( 'show_realname' ) may differ between import and export system! + $t_user_id = user_get_id_by_realname( $username ); + if ( $t_user_id === false ) { + //not found + $t_user_id = $squash_userid; + } } return $t_user_id; } diff --git a/plugins/XmlImportExport/pages/export.php b/plugins/XmlImportExport/pages/export.php index e9a2926..0eb024e 100644 --- a/plugins/XmlImportExport/pages/export.php +++ b/plugins/XmlImportExport/pages/export.php @@ -146,6 +146,93 @@ foreach( $t_result as $t_row ) { $writer->writeElement( $t_element, $t_value ); } } + + # fetch and export custom fields + $t_custom_fields = custom_field_get_all_linked_fields( $t_row->id ); + if ( is_array( $t_custom_fields ) && count( $t_custom_fields ) > 0 ) { + $writer->startElement( 'custom_fields' ); + foreach ( $t_custom_fields as $custom_field_name => $t_custom_field ) { + $writer->startElement( 'custom_field' ); + # id + $writer->writeElement( 'id', custom_field_get_id_from_name( $custom_field_name ) ); + # title + $writer->writeElement( 'name', $custom_field_name ); + # filename + $writer->writeElement( 'type', $t_custom_field['type'] ); + # filesize + $writer->writeElement( 'value', $t_custom_field['value'] ); + # file_type + $writer->writeElement( 'access_level_r', $t_custom_field['access_level_r'] ); + + $writer->endElement(); # custom_field + } + $writer->endElement(); # custom_fields + } + + # fetch and export bugnotes + $t_bugnotes = bugnote_get_all_bugnotes( $t_row->id ); + if ( is_array( $t_bugnotes ) && count( $t_bugnotes ) > 0 ) { + $writer->startElement( 'bugnotes' ); + foreach ( $t_bugnotes as $t_bugnote ) { + $writer->startElement( 'bugnote' ); + # id + $writer->writeElement( 'id', $t_bugnote->id ); + # reporter + $writer->startElement( 'reporter' ); + $writer->writeAttribute( 'id', $t_bugnote->reporter_id ); + $writer->text( user_get_name( $t_bugnote->reporter_id ) ); + $writer->endElement( ); + # bug note + $writer->writeElement( 'note', $t_bugnote->note ); + # view state + $writer->startElement( 'view_state' ); + $writer->writeAttribute( 'id', $t_bugnote->view_state ); + $writer->text( get_enum_element( 'view_state', $t_bugnote->view_state ) ); + $writer->endElement( ); + # date submitted + $writer->writeElement( 'date_submitted', $t_bugnote->date_submitted ); + # last modified + $writer->writeElement( 'last_modified', $t_bugnote->last_modified ); + # note type + $writer->writeElement( 'note_type', $t_bugnote->note_type ); + # note attr + $writer->writeElement( 'note_attr', $t_bugnote->note_attr ); + # time tracking + $writer->writeElement( 'time_tracking', $t_bugnote->time_tracking ); + + $writer->endElement(); # bugnote + } + $writer->endElement(); # bugnotes + } + + # fetch and export attachments + $t_attachments = bug_get_attachments( $t_row->id ); + if ( is_array( $t_attachments ) && count( $t_attachments ) > 0 ) { + $writer->startElement( 'attachments' ); + foreach ( $t_attachments as $t_attachment ) { + $writer->startElement( 'attachment' ); + # id + $writer->writeElement( 'id', $t_attachment['id'] ); + # title + $writer->writeElement( 'title', $t_attachment['title'] ); + # filename + $writer->writeElement( 'filename', $t_attachment['filename'] ); + # filesize + $writer->writeElement( 'filesize', $t_attachment['filesize'] ); + # file_type + $writer->writeElement( 'file_type', $t_attachment['file_type'] ); + # last added + $writer->writeElement( 'date_added', $t_attachment['date_added'] ); + # content + $content = file_get_content( $t_attachment['id'] ); + + $writer->writeElement( 'content', base64_encode( $content['content'] ) ); + + $writer->endElement(); # attachment + } + $writer->endElement(); # bugnotes + } + $writer->endElement(); # issue // Save memory by clearing cache -- 1.7.0.2.msysgit.0 | ||||
related to | 0015721 | closed | grangeway | Functionality to consider porting to master-2.0.x |
has duplicate | 0012316 | closed | dhx | XMLImport plug-in patch to allow custom_fields |
has duplicate | 0011876 | closed | atrol | XML Import/Export doesn't support custom fields. |
has duplicate | 0014694 | closed | dregad | Cannot import xml/csv files |
related to | 0012091 | closed | siebrand | Bugnotes not in CSV Export anymore |
related to | 0015739 | feedback | Improvements for plugin ImportExportXml to support import from Bugzilla | |
related to | 0016117 | acknowledged | XML Import never update my issues but create new one | |
related to | 0017075 | closed | vboctor | Import plugins should be able to set last_updated field to a date in the past |
related to | 0017725 | closed | dregad | CVE-2014-7146 : PHP Code Injection Vulnerability in XmlImportExport plugin |
related to | 0017774 | closed | dregad | XML import plugin does not process links |
related to | 0017775 | closed | dregad | XML import plugin does not replace links in 'steps to reproduce' |
Please forgive my ignorance. I have not patched Mantis before. So I just need to drop the XMLImportExport files into the plugins folder. What do i need to run to get the patch applied? I am running MAMP on my test system and will be running this off a Mac server in production. |
|
Well I ran the patch on the files and it seemed to change them properly. But then I tried to export or view the CSV file and there is no change to them. What else do I need to do? I didn't see any additional column options in the config pages. Thanks for your help. |
|
Sorry maybe I didnt point out this one properly: my patch does only affect the xml export/import. I used that one to move issues between two Mantis instances and that worked quite good... |
|
Thanks for the patchset, it looks good. I was going to commit it to the 1.3.x branch however I came across some merge conflicts. Plugin ImportXml enhanced with bugnotes, custom fields and attachments Is there any chance you could rebase your patchset against the latest 1.3.x head? If not I'll try and do it myself later when I have some free time. Thanks again for your help :) |
|
Uploaded patch for 1.3.x (actually I couldn't find any master-1.3.x do I assumed that "master" is master-1.3.x). I have not yet a test environment with Mantis 1.3.x available so this patch is NOT tested. |
|
Great patch Dominik! I apologise for the delay in responding to your latest patch. Your patch has been committed to the 1.3.x branch ready for the next major release. The only changes made were minor formatting fixes (hanging white space). Thank you for the contribution. |
|
Hi, I've installed mantis 1.2.4. I applied the patch 2010-06-09_improvements_for_plugin_ImportExportXml.patch, but nothing change. Whe I export an issue or a set of issues doesn't export the bug notes. I'm missing something? |
|
There is a Bug in the Patch within this line: case 'view_state': Correct: |
|
Thanks dominik, the patch works great in my 1.3.x! I have to make some modifications though. I explain them here cause I don't know how to make a patch. First in core/file_api.php, function file_get_content, I had to change line and line (Both of them removing mantis_ prefix and _table suffix in both of them). I also had to change ImportXml/Issue.php cause it was not finding the core api files By the way I upload the plugin modified with these modifications, it also includes schoppi71 comment (before mine, about private status i think), and of course with dominik patch for 1.3.x applied. Hope it's useful! |
|
Who is able to adjust the file "print_all_bug_page_excel.php" from a a previous version (1.1.x) to make it work with the current version 1.2.x? It seems that many people are missing the feature to export bugnotes to CSV or Excel. And this seems (to me) the easiest way. |
|
Hi the support for custom fields that you added, do we already need to have the particular custom fields in the project that we are importing the file into, or it will create automatically? |
|
Marking as 'acknowledged' not resolved/closed to track that change gets ported to master-2.0.x branch |
|
Reopening due to the fact that some additional fixes were submitted after dhx committed dominik's original patch. |
|
Hello, (I have no problem with the bug_notes, custum_field, date_submitted thanks to the patch) I would like to Import with an XML file a new issue (create) when the <id> is set to 0 and update an issue with an specific <id>. I saw that the function update() exist in the file : mantis/www/core/bug_api.php |
|
Hello, How could i apply this patch to the last release (1.2.15) ? It seems that the 2 patchs attached does not work for 1.2.15 Do I have to generate myself a dedicated patch with a diff between 1.2.15 source files and last ImportExportXML_V2.zip attached ? Thanks in advance. |
|
You're welcome to try (at your own risk), but I don't recommend backporting this patch due to the huge differences between the master and 1.2.x branches on some of the affected files (particularly bug_update.php). |
|
Thanks Degrad. We made a patch based on difference between first patch published (without bug_update) and last release (1.2.15). |
|
Hello, I am currently using the 1.2.17 version, and xml export is crashing when there is an attachment file. I updated the XmlImportExport with this file: I can see the bugnotes, this feature is working properly. But when there is a file attachment the following error occur: Thanks for helping me :) PS: For the moment I commented these lines so it is not crashing: #$content = file_get_content( $t_attachment['id'] ); #$writer->writeElement( 'content', base64_encode( $content['content'] ) ); |
|
Kulssaka, the issues will be fixed in version 1.3 and there is no plan to implement the changes in 1.2. Please use the forums, the mantisbt-help mailing list or IRC to discuss such kind of questions (refer to http://www.mantisbt.org/support.php for links and further details). |
|
As atrol said, you're patching 1.2 at your own risk, not to mention that said patch was not developed by us... I suggest you try and contact the original author for further assistance. That said, there is a standard php function called file_get_contents() (with an 's'), so it could just be a typo. |
|
Thanks for the advice. I will just disable the file attachment into the xml for now, and investigate further about it. I never did php before so I guess i should have a look at the documentation about function file_get_contents(). |
|
MantisBT: master 84017535 2010-06-23 09:29 Committer: dhx Details Diff |
Issue 0012013: Improved ImportExportXml plugin Improvements made to the importing and exporting features of this plugin: * Added support for custom fields, bugnotes and attachments * Added support for dates (date submitted, last updated) - keep dates as given in import file * Added function to easily retrieve the contents of a file (file_api.php) Signed-off-by: David Hicks <hickseydr@optusnet.com.au> |
Affected Issues 0012013 |
|
mod - core/bugnote_api.php | Diff File | ||
mod - core/bug_api.php | Diff File | ||
mod - plugins/XmlImportExport/pages/export.php | Diff File | ||
mod - plugins/XmlImportExport/ImportXml.php | Diff File | ||
mod - core/file_api.php | Diff File | ||
mod - plugins/XmlImportExport/ImportXml/Issue.php | Diff File | ||
mod - bug_update.php | Diff File | ||
mod - core/constant_inc.php | Diff File | ||
mod - lang/strings_english.txt | Diff File | ||
MantisBT: master de607de3 2013-10-27 15:59 Details Diff |
XML import/export plugin fixes This resolves a number of warnings and notices with the plugin when strict error reporting. As far as I can tell, the fixes mentioned in issue 0012013 after dhx committed the changes (84017535f8718685d755d58af7a39d80f52ffca8), i.e. bugnotes are not needed: - 0012013:0028391 by schoppi71: don't think this is a bug - 0012013:0028855 by ninguno: already included in dhx's commit |
Affected Issues 0012013 |
|
mod - core/file_api.php | Diff File | ||
mod - plugins/XmlImportExport/ImportXml.php | Diff File | ||
mod - plugins/XmlImportExport/ImportXml/Issue.php | Diff File | ||
mod - plugins/XmlImportExport/lang/strings_english.txt | Diff File | ||
mod - plugins/XmlImportExport/pages/import.php | Diff File | ||
mod - plugins/XmlImportExport/pages/import_action.php | Diff File |