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

