From 4f27dd5ff54fd5f26a560028fe71c69de6de32b7 Mon Sep 17 00:00:00 2001 From: Glenn Henshaw Date: Sun, 28 Apr 2013 22:01:09 -0400 Subject: backport XML importer improvements fix bug/ bugnote add to allow for setting dates directly updates to XML importer to: - add more fields - allow for translation between fields in the XML imported and Mantis fields add XSLT translation from Bugzilla export to Mantis - translate using xsltproc bugzilla2mantis.xslt bugzilla.xml > mantis.xml --- core/bug_api.php | 16 +- core/bugnote_api.php | 17 +- core/file_api.php | 6 +- plugins/XmlImportExport/ImportXml.php | 47 +++- plugins/XmlImportExport/ImportXml/Interface.php | 6 +- plugins/XmlImportExport/ImportXml/Issue.php | 264 +++++++++++++++++++-- plugins/XmlImportExport/ImportXml/Mapper.php | 12 +- plugins/XmlImportExport/ImportXml/Translation.php | 71 ++++++ plugins/XmlImportExport/XmlImportExport.php | 14 +- plugins/XmlImportExport/bugzilla2mantis.xslt | 225 ++++++++++++++++++ plugins/XmlImportExport/pages/export.php | 91 +++++++- plugins/XmlImportExport/pages/import.php | 4 +- plugins/XmlImportExport/pages/import_action.php | 4 +- 13 files changed, 705 insertions(+), 72 deletions(-) mode change 100644 => 100755 plugins/XmlImportExport/ImportXml.php mode change 100644 => 100755 plugins/XmlImportExport/ImportXml/Interface.php mode change 100644 => 100755 plugins/XmlImportExport/ImportXml/Mapper.php create mode 100755 plugins/XmlImportExport/ImportXml/Translation.php mode change 100644 => 100755 plugins/XmlImportExport/XmlImportExport.php create mode 100644 plugins/XmlImportExport/bugzilla2mantis.xslt mode change 100644 => 100755 plugins/XmlImportExport/mantis.dtd mode change 100644 => 100755 plugins/XmlImportExport/pages/export.php mode change 100644 => 100755 plugins/XmlImportExport/pages/import.php mode change 100644 => 100755 plugins/XmlImportExport/pages/import_action.php diff --git a/core/bug_api.php b/core/bug_api.php index 06bc514..fca372b 100644 --- a/core/bug_api.php +++ b/core/bug_api.php @@ -1,5 +1,4 @@ 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' ); $t_bug_text_table = db_get_table( 'mantis_bug_text_table' ); @@ -366,7 +372,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 ); @@ -831,8 +837,8 @@ function bug_is_readonly( $p_bug_id ) { * @uses config_api.php */ function bug_is_resolved( $p_bug_id ) { - $t_bug = bug_get( $p_bug_id ); - return( $t_bug->status >= config_get( 'bug_resolved_status_threshold', null, null, $t_bug->project_id ) ); + $t_status = bug_get_field( $p_bug_id, 'status' ); + return( $t_status >= config_get( 'bug_resolved_status_threshold' ) ); } /** diff --git a/core/bugnote_api.php b/core/bugnote_api.php index 13147ce..99bb694 100644 --- a/core/bugnote_api.php +++ b/core/bugnote_api.php @@ -121,13 +121,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 = BUGNOTE, $p_attr = '', $p_user_id = null, $p_send_email = TRUE, $p_log_history = TRUE) { +function bugnote_add( $p_bug_id, $p_bugnote_text, $p_time_tracking = '0:00', $p_private = false, $p_type = BUGNOTE, $p_attr = '', $p_user_id = null, $p_send_email = TRUE, $p_log_history = 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 ); if( REMINDER !== $p_type ) { # Check if this is a time-tracking note @@ -179,17 +185,18 @@ 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 - if ( TRUE == $p_log_history) - history_log_event_special( $p_bug_id, BUGNOTE_ADDED, bugnote_format_id( $t_bugnote_id ) ); + history_log_event_special( $p_bug_id, BUGNOTE_ADDED, bugnote_format_id( $t_bugnote_id ) ); # if it was FEEDBACK its NEW_ now if ( config_get( 'reassign_on_feedback' ) && diff --git a/core/file_api.php b/core/file_api.php index 877ac61..3125ba7 100644 --- a/core/file_api.php +++ b/core/file_api.php @@ -633,7 +633,7 @@ function file_is_name_unique( $p_name, $p_bug_id, $p_table = 'bug' ) { * @param integer $p_bug_id the bug id * @param array $p_file the uploaded file info, as retrieved from gpc_get_file() */ -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']; @@ -739,7 +739,9 @@ function file_add( $p_bug_id, $p_file, $p_table = 'bug', $p_title = '', $p_desc 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 ); diff --git a/plugins/XmlImportExport/ImportXml.php b/plugins/XmlImportExport/ImportXml.php old mode 100644 new mode 100755 index 98a724b..8ccba059 --- a/plugins/XmlImportExport/ImportXml.php +++ b/plugins/XmlImportExport/ImportXml.php @@ -1,6 +1,6 @@ element and it's attributes - while( $this->reader_->read( ) && $this->reader_->name == 'mantis' ) { + $this->reader_->read( ); + if ( $this->reader_->name == 'mantis' ) { $this->source_->version = $this->reader_->getAttribute( 'version' ); $this->source_->urlbase = $this->reader_->getAttribute( 'urlbase' ); $this->source_->issuelink = $this->reader_->getAttribute( 'issuelink' ); @@ -92,33 +94,53 @@ class ImportXML { /* element start */ $t_element_name = $this->reader_->localName; $t_importer = $this->get_importer_object( $t_element_name ); + echo "import " . $t_element_name . "\n"; if( !is_null( $t_importer ) ) { $t_importer->process( $this->reader_ ); $t_importer->update_map( $this->itemsMap_ ); } break; } - } + } 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 ); - $bugData->update( 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' ); @@ -160,7 +182,10 @@ class ImportXML { $importer = null; switch( $p_element_name ) { case 'issue': - $importer = new ImportXml_Issue( $this->keepCategory_, $this->defaultCategory_ ); + $importer = new ImportXml_Issue( $this->keepCategory_, $this->defaultCategory_, $this->itemsMap_ ); + break; + case 'translation': + $importer = new ImportXml_Translation( ); break; } return $importer; diff --git a/plugins/XmlImportExport/ImportXml/Interface.php b/plugins/XmlImportExport/ImportXml/Interface.php old mode 100644 new mode 100755 index 143e02f..5cf8a15 --- a/plugins/XmlImportExport/ImportXml/Interface.php +++ b/plugins/XmlImportExport/ImportXml/Interface.php @@ -1,5 +1,5 @@ new_id conversion map @@ -32,7 +32,7 @@ interface ImportXml_Interface { * Import Classes for items not needing this info can use an * empty implementation * - * @param ImportXml_Mapper $mapper + * @param Mapper $mapper */ public function update_map( ImportXml_Mapper $mapper ); } diff --git a/plugins/XmlImportExport/ImportXml/Issue.php b/plugins/XmlImportExport/ImportXml/Issue.php index 264faae..d96e6d6 100644 --- a/plugins/XmlImportExport/ImportXml/Issue.php +++ b/plugins/XmlImportExport/ImportXml/Issue.php @@ -1,5 +1,5 @@ . -require_once( 'bug_api.php' ); -require_once( 'user_api.php' ); +$t_core_path = config_get( 'core_path' ); +require_once( $t_core_path.'bug_api.php' ); +require_once( $t_core_path.'user_api.php' ); +require_once( $t_core_path.'utility_api.php' ); require_once( 'Interface.php' ); +require_once( 'Mapper.php' ); class ImportXml_Issue implements ImportXml_Interface { @@ -28,26 +31,50 @@ class ImportXml_Issue implements ImportXml_Interface { // import Issues options private $keepCategory_; private $defaultCategory_; + private $textTranslation_; - public function __construct( $keepCategory, $defaultCategory ) { + public function __construct( $keepCategory, $defaultCategory, Mapper$translation ) { $this->newbug_ = new BugData; $this->keepCategory_ = $keepCategory; $this->defaultCategory_ = $defaultCategory; + $this->textTranslation_ = $translation; } + // verify that date is numeric, and translate if necessary + private function parseDate( $p_time ) + { + $t_time = $p_time; + if ( !is_numeric($t_time) ) + { + // not a number, parse as a date + $t_time_comps = strptime($t_time, "%F %T %z"); + if ( false !== $t_time_comps ) + $t_time = mktime( $t_time_comps['tm_hour'], $t_time_comps['tm_min'], + $t_time_comps['tm_sec'], 1 , + $t_time_comps['tm_yday'] + 1, $t_time_comps['tm_year'] + 1900 ); + else + $t_time = time(); + } + //echo "date: " . $p_time . " = " . $t_time . "\n"; + return $t_time; + } + + // Read stream until current item finishes, processing // the data found 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( ); + $bugnotePtr = -1; + $cfPtr = -1; $depth = $reader->depth; while( $reader->read() && ($reader->depth > $depth || $reader->nodeType != XMLReader::END_ELEMENT)) { if( $reader->nodeType == XMLReader::ELEMENT ) { + //echo "process " . $reader->localName . "\n"; switch( $reader->localName ) { case 'reporter': $t_old_id = $reader->getAttribute( 'id' ); @@ -68,19 +95,15 @@ class ImportXml_Issue implements ImportXml_Interface { case 'category': $this->newbug_->category_id = $this->defaultCategory_; + // TODO: if we port the import/export code to 1.1.x, this needs to be + // improved to cope with the different cases (1.1 => 1.2, 1.2 => 1.1 etc) if( version_compare( MANTIS_VERSION, '1.2', '>' ) === true ) { $reader->read( ); if( $this->keepCategory_ ) { - # Check for the category's existence in the current project - # well as its parents (if any) - $t_projects_hierarchy = project_hierarchy_inheritance( $t_project_id ); - foreach( $t_projects_hierarchy as $t_project ) { - $t_category_id = category_get_id_by_name( $reader->value, $t_project, false ); - if( $t_category_id !== false ) { - $this->newbug_->category_id = $t_category_id; - break; - } + $t_category_id = category_get_id_by_name( $reader->value, $t_project_id ); + if( $t_category_id !== false ) { + $this->newbug_->category_id = $t_category_id; } } @@ -99,11 +122,27 @@ class ImportXml_Issue implements ImportXml_Interface { $t_field = $reader->localName; $t_id = $reader->getAttribute( 'id' ); $reader->read( ); - $t_value = $reader->value; - - // Here we assume ids have the same meaning in both installations - // TODO add a check for customized values - $this->newbug_->$t_field = $t_id; + $t_value = strtolower( trim( $reader->value )); + if ( (NULL === $t_id) && ("" != $t_value) ) + { + // id wasn't set, use value to find result + // remap using provided translations + //echo " got $t_value\n"; + $t_value = $this->textTranslation_->getNewID( $t_field, $t_value ); + //echo " remapping $t_value\n"; + + // reverse text from enum string + $t_enum = $t_field . '_enum_string'; + $t_id = MantisEnum::getValue( config_get( $t_enum ), $t_value ); + if (false !== $t_id) + { + $this->newbug_->$t_field = $t_id; + } + else + { + echo "failed to map \"$t_value\" in $t_enum\n"; + } + } break; case 'id': @@ -111,11 +150,131 @@ class ImportXml_Issue implements ImportXml_Interface { $this->old_id_ = $reader->value; break; - case 'project'; + case 'project_id'; + $reader->read( ); + $t_project = $reader->value; + $t_project_id = project_get_id_by_name( $t_project ); + // ignore original value, use current project + //echo " map project \"$t_project\" to id $t_project_id\n"; + $this->newbug_->project_id = $t_project_id; + break; + + case 'date_submitted': + $reader->read( ); + $t_time = $this->parseDate( $reader->value ); + $this->newbug_->date_submitted = $t_time; + break; + + case 'last_updated': + $reader->read( ); + $t_time = $this->parseDate( $reader->value ); + $this->newbug_->last_updated = $t_time; + break; - // ignore original value, use current project - $this->newbug_->project_id = $t_project_id; - break; + case 'custom_fields': + // store custom fields + $depth_cf = $reader->depth; + while ( $reader->read() && + ( $reader->depth > $depth_cf || + $reader->nodeType != XMLReader::END_ELEMENT ) ) + { + + if ( $reader->nodeType == XMLReader::ELEMENT ) { + switch ( $reader->localName ) { + case "custom_field": + case "custom_fields": + break; + default: + $t_field = $reader->localName; + $reader->read( ); + $t_value = $reader->value; + if (false !== $t_value) { + $cfPtr++; + $t_custom_fields[$cfPtr]->name = $t_field; + $t_custom_fields[$cfPtr]->value = $t_value; + } + break; + } + } + } + break; + + case 'bugnotes': + // ignore bugnote container for now + $bugnotePtr = -1; + break; + + case 'bugnote': + // store bug notes + $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') { + $bugnotePtr++; + $t_bugnotes[$bugnotePtr]->date_submitted = 0; + $t_bugnotes[$bugnotePtr]->last_updated = 0; + } + switch ( $reader->localName ) { + case 'reporter': + $t_old_id = $reader->getAttribute( 'id' ); + $reader->read( ); + $t_bugnotes[$bugnotePtr]->reporter_id = $this->get_user_id( $reader->value, $userId ); + break; + + case 'view_state': + $t_old_id = $reader->getAttribute( 'id' ); + $reader->read( ); + $t_bugnotes[$bugnotePtr]->private = $t_old_id == VS_PRIVATE ? true : false; + break; + + case 'date_submitted': + $reader->read( ); + $t_time = $this->parseDate( $reader->value ); + $t_bugnotes[$bugnotePtr]->date_submitted = $t_time; + break; + + case 'last_updated': + $reader->read( ); + $t_time = $this->parseDate( $reader->value ); + $t_bugnotes[$bugnotePtr]->last_updated = $t_time; + break; + + default: + $field = $reader->localName; + $reader->read( ); + $t_bugnotes[$bugnotePtr]->$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; @@ -126,8 +285,57 @@ class ImportXml_Issue implements ImportXml_Interface { } } + // pre-save checks + if ( is_blank($this->newbug_->description) ) + $this->newbug_->description = "see below"; + // now save the new bug + //var_dump($this->newbug_); $this->new_id_ = $this->newbug_->create(); + echo "created bug " . $this->new_id_ . "\n"; + + // add custom fields + var_dump($t_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_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 { + echo "CF $t_custom_field not linked to project\n"; + //error_parameters( $t_custom_field->name, $t_custom_field_id ); + //trigger_error( ERROR_CUSTOM_FIELD_NOT_LINKED_TO_PROJECT, ERROR ); + } + } + } + + // add bugnotes + echo " add " . count($t_bugnotes) . " notes\n"; + if ( $this->new_id_ > 0 && is_array( $t_bugnotes ) && count( $t_bugnotes ) > 0 ) { + foreach ( $t_bugnotes as $t_bugnote) { + if ($t_bugnote->last_updated == 0) + $t_bugnote->last_updated = $t_bugnote->date_submitted; + 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"; } @@ -155,9 +363,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/ImportXml/Mapper.php b/plugins/XmlImportExport/ImportXml/Mapper.php old mode 100644 new mode 100755 index e514dee..3fea5dc --- a/plugins/XmlImportExport/ImportXml/Mapper.php +++ b/plugins/XmlImportExport/ImportXml/Mapper.php @@ -1,5 +1,5 @@ {$type}[ $old ] = $new; } diff --git a/plugins/XmlImportExport/ImportXml/Translation.php b/plugins/XmlImportExport/ImportXml/Translation.php new file mode 100755 index 0000000..c14caae --- /dev/null +++ b/plugins/XmlImportExport/ImportXml/Translation.php @@ -0,0 +1,71 @@ +. + +require_once( 'Interface.php' ); + +class ImportXml_Translation implements ImportXml_Interface { + + private $translation_; + private $new_id_; + + private $mapping_name_; + + private $valid_mappings = array( + "eta", + "priority", + "projection", + "reproducibility", + "resolution", + "severity", + "status", + "view_state" + ); + + // Read stream until current item finishes, processing + // the data found + public function process( XMLreader $reader ) { + + $this->mapping_name_ = $reader->getAttribute( 'name' ); + + if (in_array($this->mapping_name_, $this->valid_mappings)) + { + echo "process translation for " . $this->mapping_name_ . "\n"; + $depth = $reader->depth; + while( $reader->read() && + ($reader->depth > $depth || + $reader->nodeType != XMLReader::END_ELEMENT)) { + if( $reader->nodeType == XMLReader::ELEMENT ) { + switch( $reader->localName ) { + case 'translate': + $t_id = $reader->getAttribute( 'id' ); + $reader->read( ); + $this->translation[$t_id] = $reader->value; + echo " translate " . $t_id . " to " . $reader->value . "\n"; + break; + + default: + } + } + } + + } + } + + public function update_map( ImportXml_Mapper $mapper ) { + foreach ($this->translation as $t_id => $t_value) + $mapper->add( $this->mapping_name_, $t_id, $t_value ); + } +} diff --git a/plugins/XmlImportExport/XmlImportExport.php b/plugins/XmlImportExport/XmlImportExport.php old mode 100644 new mode 100755 index c894de3..07905a5 --- a/plugins/XmlImportExport/XmlImportExport.php +++ b/plugins/XmlImportExport/XmlImportExport.php @@ -1,5 +1,5 @@ ' . plugin_lang_get( 'export' ) . '', ); } - - function install() { - $result = extension_loaded("xmlreader") && extension_loaded("xmlwriter"); - if ( ! $result ) { - #\todo returning false should trigger some error reporting, needs rethinking error_api - error_parameters( plugin_lang_get( 'error_no_xml' ) ); - trigger_error( ERROR_PLUGIN_INSTALL_FAILED, ERROR ); - } - return $result; - } } diff --git a/plugins/XmlImportExport/bugzilla2mantis.xslt b/plugins/XmlImportExport/bugzilla2mantis.xslt new file mode 100644 index 0000000..0366387 --- /dev/null +++ b/plugins/XmlImportExport/bugzilla2mantis.xslt @@ -0,0 +1,225 @@ + + + + + + + feature + minor + crash + + + normal + low + + + feedback + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/plugins/XmlImportExport/mantis.dtd b/plugins/XmlImportExport/mantis.dtd old mode 100644 new mode 100755 diff --git a/plugins/XmlImportExport/pages/export.php b/plugins/XmlImportExport/pages/export.php old mode 100644 new mode 100755 index 700a804..0eb024e --- a/plugins/XmlImportExport/pages/export.php +++ b/plugins/XmlImportExport/pages/export.php @@ -1,6 +1,6 @@ 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 diff --git a/plugins/XmlImportExport/pages/import.php b/plugins/XmlImportExport/pages/import.php old mode 100644 new mode 100755 index 14fe63d..429c64e --- a/plugins/XmlImportExport/pages/import.php +++ b/plugins/XmlImportExport/pages/import.php @@ -1,6 +1,6 @@