View Issue Details
ID | Project | Category | View Status | Date Submitted | Last Update |
---|---|---|---|---|---|
0015739 | mantisbt | plug-ins | public | 2013-04-09 18:44 | 2014-01-01 22:12 |
Reporter | thraxisp | Assigned To | |||
Priority | normal | Severity | minor | Reproducibility | always |
Status | feedback | Resolution | open | ||
Product Version | 1.2.14 | ||||
Summary | 0015739: Improvements for plugin ImportExportXml to support import from Bugzilla | ||||
Description | Issue 0012013 provides a number of enhancements for the XML import. Unfortunately it is targeted for the non-functional 1.3.x branch. Improvements needed:
| ||||
Additional Information | A patch will follow shortly | ||||
Tags | patch | ||||
Attached Files | mantis_bug_15739.txt (42,521 bytes)
From 4f27dd5ff54fd5f26a560028fe71c69de6de32b7 Mon Sep 17 00:00:00 2001 From: Glenn Henshaw <thraxisp@logicaloutcome.ca> 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 @@ <?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 @@ -302,8 +301,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( '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 @@ <?php -# MantisBT - a php based bugtracking system -# Copyright (C) 2002 - 2013 MantisBT Team - mantisbt-dev@lists.sourceforge.net +# MantisBT - A PHP based bugtracking system +# Copyright (C) 2002 - 2010 MantisBT Team - mantisbt-dev@lists.sourceforge.net # 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 @@ -16,6 +16,7 @@ require_once( 'ImportXml' . DIRECTORY_SEPARATOR . 'Mapper.php' ); require_once( 'ImportXml' . DIRECTORY_SEPARATOR . 'Issue.php' ); +require_once( 'ImportXml' . DIRECTORY_SEPARATOR . 'Translation.php' ); class SourceData { public $version; @@ -74,7 +75,8 @@ class ImportXML { */ public function import( ) { // Read the <mantis> 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 @@ <?php -# MantisBT - a php based bugtracking system +# MantisBT - A PHP based bugtracking system # Copyright (C) 2002 - 2013 MantisBT Team - mantisbt-dev@lists.sourceforge.net # MantisBT is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -22,7 +22,7 @@ interface ImportXml_Interface { * * @param XMLreader $reader */ - public function process( XMLreader $reader ); + public function process( XMLreader$reader ); /** * Update the old_id => 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 @@ <?php -# MantisBT - a php based bugtracking system +# MantisBT - A PHP based bugtracking system # Copyright (C) 2002 - 2013 MantisBT Team - mantisbt-dev@lists.sourceforge.net # MantisBT is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -14,9 +14,12 @@ # You should have received a copy of the GNU General Public License # along with MantisBT. If not, see <http://www.gnu.org/licenses/>. -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 @@ <?php -# MantisBT - a php based bugtracking system +# MantisBT - A PHP based bugtracking system # Copyright (C) 2002 - 2013 MantisBT Team - mantisbt-dev@lists.sourceforge.net # MantisBT is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -21,7 +21,15 @@ */ class ImportXml_Mapper { private $issue = array( ); - + private $eta = array( ); + private $priority = array( ); + private $projection = array( ); + private $reproducibility = array( ); + private $resolution = array( ); + private $severity = array( ); + private $status = array( ); + private $view_state = array( ); + public function add( $type, $old, $new ) { $this->{$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 @@ +<?php +# MantisBT - A PHP based bugtracking system +# Copyright (C) 2002 - 2013 MantisBT Team - mantisbt-dev@lists.sourceforge.net +# 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_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 @@ <?php -# MantisBT - a php based bugtracking system +# 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 @@ -19,7 +19,7 @@ * @package MantisPlugin * @subpackage MantisPlugin * @copyright Copyright (C) 2000 - 2002 Kenzaburo Ito - kenito@300baud.org - * @copyright Copyright (C) 2002 - 2013 MantisBT Team - mantisbt-dev@lists.sourceforge.net + * @copyright Copyright (C) 2002 - 2010 MantisBT Team - mantisbt-dev@lists.sourceforge.net * @link http://www.mantisbt.org */ @@ -69,14 +69,4 @@ class XmlImportExportPlugin extends MantisPlugin { function export_issues_menu( ) { return array( '<a href="' . plugin_page( 'export' ) . '">' . plugin_lang_get( 'export' ) . '</a>', ); } - - 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 @@ +<xsl:stylesheet version="1.0" + xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> + <xsl:output omit-xml-declaration="yes" indent="yes"/> + + <xsl:template match="/*"> + <mantis + version="1.2.15dev" + urlbase="http://localhost:10088/~thraxisp/mantisbt/" + issuelink="#" + notelink="~" + format="1"> + <translation name="severity"> + <translate id="enhancement">feature</translate> + <translate id="normal">minor</translate> + <translate id="critical">crash</translate> + </translation> + <translation name="priority"> + <translate id="---">normal</translate> + <translate id="lowest">low</translate> + </translation> + <translation name="status"> + <translate id="reopened">feedback</translate> + </translation> + <xsl:apply-templates/> + </mantis> + </xsl:template> + + <xsl:template match="bug"> + <issue> + <xsl:apply-templates/> + </issue> + </xsl:template> + + <xsl:template match="bug_id"> + <bug> + <xsl:apply-templates/> + </bug> + </xsl:template> + + <xsl:template match="short_desc"> + <summary> + <xsl:value-of select="."/> + </summary> + <description> + <xsl:value-of select="../long_desc[1]/thetext"/> + </description> + </xsl:template> + + <xsl:template match="creation_ts"> + <date_submitted> + <xsl:apply-templates/> + </date_submitted> + </xsl:template> + + <xsl:template match="delta_ts"> + <last_updated> + <xsl:apply-templates/> + </last_updated> + </xsl:template> + + <xsl:template match="product"> + <project_id> + <xsl:apply-templates/> + </project_id> + </xsl:template> + + <xsl:template match="priority"> + <priority> + <xsl:apply-templates/> + </priority> + </xsl:template> + + <xsl:template match="bug_severity"> + <severity> + <xsl:apply-templates/> + </severity> + </xsl:template> + + <xsl:template match="bug_status"> + <status> + <xsl:apply-templates/> + </status> + </xsl:template> + + <xsl:template match="resolution"> + <resolution> + <xsl:apply-templates/> + </resolution> + </xsl:template> + + <xsl:template match="component"> + <category> + <xsl:apply-templates/> + </category> + </xsl:template> + + <xsl:template match="version"> + <version> + <xsl:apply-templates/> + </version> + </xsl:template> + + <xsl:template match="target_milestone"> + <target_version> + <xsl:apply-templates/> + </target_version> + </xsl:template> + + <xsl:template match="reporter"> + <reporter> + <xsl:value-of select="@name"/> + </reporter> + </xsl:template> + + <xsl:template match="assigned_to"> + <handler> + <xsl:value-of select="@name"/> + </handler> + </xsl:template> + + <xsl:template match="assigned_to"> + <handler> + <xsl:value-of select="@name"/> + </handler> + </xsl:template> + + <xsl:template match="cc"> + </xsl:template> + + <xsl:template match="op_sys"> + </xsl:template> + + <xsl:template match="rep_platform"> + </xsl:template> + + <xsl:template match="keywords"> + </xsl:template> + + <xsl:template match="status_whiteboard"> + </xsl:template> + + <xsl:template match="group"> + </xsl:template> + + <xsl:template match="reporter_accessible"> + </xsl:template> + + <xsl:template match="cclist_accessible"> + </xsl:template> + + <xsl:template match="classification_id"> + </xsl:template> + + <xsl:template match="classification"> + </xsl:template> + + <xsl:template match="bug_file_loc"> + </xsl:template> + + <xsl:template match="everconfirmed"> + </xsl:template> + + <xsl:template match="estimated_time"> + </xsl:template> + + <xsl:template match="remaining_time"> + </xsl:template> + + <xsl:template match="actual_time"> + </xsl:template> + + <xsl:template match="token"> + </xsl:template> + + <xsl:template match="cf_revision"> + <custom_fields> + <revision> + <xsl:apply-templates/> + </revision> + </custom_fields> + </xsl:template> + + <xsl:template match="cf_iteration"> + <custom_fields> + <iteration> + <xsl:apply-templates/> + </iteration> + </custom_fields> + </xsl:template> + + <!-- attachments --> + <!-- drop --> + <xsl:template match="attachment" /> + + <!-- bug notes --> + <!-- drop first note --> + <xsl:template match="long_desc[1]" /> + <xsl:template match="long_desc"> + <bugnote> + <xsl:apply-templates/> + </bugnote> + </xsl:template> + + <xsl:template match="commentid"> + </xsl:template> + + <xsl:template match="who"> + <reporter> + <xsl:value-of select="@name"/> + </reporter> + </xsl:template> + + <xsl:template match="bug_when"> + <date_submitted> + <xsl:apply-templates/> + </date_submitted> + </xsl:template> + + <xsl:template match="thetext"> + <note> + <xsl:apply-templates/> + </note> + </xsl:template> + +</xsl:stylesheet> \ 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 @@ <?php -# MantisBT - a php based bugtracking system -# Copyright (C) 2002 - 2013 MantisBT Team - mantisbt-dev@lists.sourceforge.net +# MantisBT - A PHP based bugtracking system +# Copyright (C) 2002 - 2010 MantisBT Team - mantisbt-dev@lists.sourceforge.net # 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 @@ -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 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 @@ <?php -# MantisBT - a php based bugtracking system -# Copyright (C) 2002 - 2013 MantisBT Team - mantisbt-dev@lists.sourceforge.net +# MantisBT - A PHP based bugtracking system +# Copyright (C) 2002 - 2010 MantisBT Team - mantisbt-dev@lists.sourceforge.net # 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 diff --git a/plugins/XmlImportExport/pages/import_action.php b/plugins/XmlImportExport/pages/import_action.php old mode 100644 new mode 100755 index 7ef387b..8966594 --- a/plugins/XmlImportExport/pages/import_action.php +++ b/plugins/XmlImportExport/pages/import_action.php @@ -1,6 +1,6 @@ <?php -# MantisBT - a php based bugtracking system -# Copyright (C) 2002 - 2013 MantisBT Team - mantisbt-dev@lists.sourceforge.net +# MantisBT - A PHP based bugtracking system +# Copyright (C) 2002 - 2010 MantisBT Team - mantisbt-dev@lists.sourceforge.net # 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 -- 1.7.3.5 | ||||
Removed assignment. dhx will not contribute to this issue in near future. |
|
Patch for 1.2.x added. Given the recent flux on the next version, I am unsure as to where else to port it to. |
|
Hi Glenn, Can you detail what this patch exactly is ?
|
|
Both. |
|
In that case, I think it would be much better to have your patch split into distinct bits & pieces, reflecting a) the back-ported stuff, either as
b) the new features as distinct commits (as there are 3 "feature" bullets in your patch commit message) This way, we clearly document what was done and also have traceability in the tracker on back-ported issues After doing a), we would hopefully have identical code in both 1.2.x and master branches, so b) should be applicable cleanly (or with minor issues) on both sides |
|
Did a quick comparison of the commits between 1.2.x and master; it's actually mainly just one issue (0012013) which has not been ported. The rest are either translation updates, or minor things not worth documenting indivudually. Note that 0011563 is only in 1.2.x but it looks like a similar change was made in master so can probably be ignored |
|