From 515cd551c8a2c3490a77b06b21602e5d4ef9ceec Mon Sep 17 00:00:00 2001 From: Dominik Blunk Date: Wed, 23 Jun 2010 15:29:37 +0200 Subject: [PATCH] Patch based on master (1.3.x) --- core/bug_api.php | 14 ++- core/bugnote_api.php | 15 ++- core/constant_inc.php | 1 + core/file_api.php | 115 +++++++++++++++++++++- lang/strings_english.txt | 1 + plugins/XmlImportExport/ImportXml.php | 31 +++++- plugins/XmlImportExport/ImportXml/Issue.php | 140 +++++++++++++++++++++++++-- plugins/XmlImportExport/pages/export.php | 87 +++++++++++++++++ 8 files changed, 380 insertions(+), 24 deletions(-) diff --git a/core/bug_api.php b/core/bug_api.php index ef12ccf..a8d974b 100644 --- a/core/bug_api.php +++ b/core/bug_api.php @@ -108,7 +108,7 @@ class BugData { # omitted: # var $bug_text_id - protected $profile_id; + protected $profile_id = 0; # extended info protected $description = ''; @@ -310,8 +310,15 @@ class BugData { # check due_date format if( is_blank( $this->due_date ) ) { - $this_due_date = date_get_null(); + $this->due_date = date_get_null(); + } + # check date submitted and last modified + if( is_blank( $this->date_submitted ) ) { + $this->date_submitted = db_now(); } + if( is_blank( $this->last_updated ) ) { + $this->last_updated = db_now(); + } $t_bug_table = db_get_table( 'bug' ); $t_bug_text_table = db_get_table( 'bug_text' ); @@ -374,7 +381,8 @@ class BugData { " . db_param() . ',' . db_param() . ',' . db_param() . ',' . db_param() . ", " . db_param() . ',' . db_param() . ',' . db_param() . ',' . db_param() . ')'; - db_query_bound( $query, Array( $this->project_id, $this->reporter_id, $this->handler_id, $this->duplicate_id, $this->priority, $this->severity, $this->reproducibility, $t_status, $this->resolution, $this->projection, $this->category_id, db_now(), db_now(), $this->eta, $t_text_id, $this->os, $this->os_build, $this->platform, $this->version, $this->build, $this->profile_id, $this->summary, $this->view_state, $this->sponsorship_total, $this->sticky, $this->fixed_in_version, $this->target_version, $this->due_date ) ); + db_query_bound( $query, Array( $this->project_id, $this->reporter_id, $this->handler_id, $this->duplicate_id, $this->priority, $this->severity, $this->reproducibility, $t_status, $this->resolution, $this->projection, $this->category_id, $this->date_submitted, $this->last_updated, $this->eta, $t_text_id, $this->os, $this->os_build, $this->platform, $this->version, $this->build, $this->profile_id, $this->summary, $this->view_state, $this->sponsorship_total, $this->sticky, $this->fixed_in_version, $this->target_version, $this->due_date ) ); + $this->id = db_insert_id( $t_bug_table ); diff --git a/core/bugnote_api.php b/core/bugnote_api.php index 88d3987..2b046a7 100644 --- a/core/bugnote_api.php +++ b/core/bugnote_api.php @@ -136,14 +136,19 @@ function bugnote_is_user_reporter( $p_bugnote_id, $p_user_id ) { * @param string $p_attr * @param int $p_user_id user id * @param bool $p_send_email generate email? + * @param int $p_date_submitted date submitted (defaults to now()) + * @param int $p_last_modified last modification date (defaults to now()) + * @param bool $p_skip_bug_update skip bug last modification update (useful when importing bugs/bugnotes) * @return false|int false or indicating bugnote id added * @access public */ -function bugnote_add( $p_bug_id, $p_bugnote_text, $p_time_tracking = '0:00', $p_private = false, $p_type = 0, $p_attr = '', $p_user_id = null, $p_send_email = TRUE ) { +function bugnote_add( $p_bug_id, $p_bugnote_text, $p_time_tracking = '0:00', $p_private = false, $p_type = 0, $p_attr = '', $p_user_id = null, $p_send_email = TRUE, $p_date_submitted = 0, $p_last_modified = 0, $p_skip_bug_update = FALSE ) { $c_bug_id = db_prepare_int( $p_bug_id ); $c_time_tracking = helper_duration_to_minutes( $p_time_tracking ); $c_private = db_prepare_bool( $p_private ); $c_type = db_prepare_int( $p_type ); + $c_date_submitted = $p_date_submitted <= 0 ? db_now() : db_prepare_int( $p_date_submitted ); + $c_last_modified = $p_last_modified <= 0 ? db_now() : db_prepare_int( $p_last_modified ); $t_bugnote_text_table = db_get_table( 'bugnote_text' ); $t_bugnote_table = db_get_table( 'bugnote' ); @@ -181,7 +186,7 @@ function bugnote_add( $p_bug_id, $p_bugnote_text, $p_time_tracking = '0:00', $p_ } # Check for private bugnotes. - if( $p_private && access_has_bug_level( config_get( 'set_view_status_threshold' ), $p_bug_id, $c_user_id ) ) { + if( $c_private && access_has_bug_level( config_get( 'set_view_status_threshold' ), $p_bug_id, $c_user_id ) ) { $t_view_state = VS_PRIVATE; } else { $t_view_state = VS_PUBLIC; @@ -192,13 +197,15 @@ function bugnote_add( $p_bug_id, $p_bugnote_text, $p_time_tracking = '0:00', $p_ (bug_id, reporter_id, bugnote_text_id, view_state, date_submitted, last_modified, note_type, note_attr, time_tracking ) VALUES (" . db_param() . ', ' . db_param() . ',' . db_param() . ', ' . db_param() . ', ' . db_param() . ',' . db_param() . ', ' . db_param() . ', ' . db_param() . ', ' . db_param() . ' )'; - db_query_bound( $query, Array( $c_bug_id, $c_user_id, $t_bugnote_text_id, $t_view_state, db_now(), db_now(), $c_type, $p_attr, $c_time_tracking ) ); + db_query_bound( $query, Array( $c_bug_id, $c_user_id, $t_bugnote_text_id, $t_view_state, $c_date_submitted, $c_last_modified, $c_type, $p_attr, $c_time_tracking ) ); # get bugnote id $t_bugnote_id = db_insert_id( $t_bugnote_table ); # update bug last updated - bug_update_date( $p_bug_id ); + if ( !$p_skip_bug_update ) { + bug_update_date( $p_bug_id ); + } # log new bug history_log_event_special( $p_bug_id, BUGNOTE_ADDED, bugnote_format_id( $t_bugnote_id ) ); diff --git a/core/constant_inc.php b/core/constant_inc.php index a598aa7..e906d2f 100644 --- a/core/constant_inc.php +++ b/core/constant_inc.php @@ -302,6 +302,7 @@ define( 'ERROR_CUSTOM_FIELD_NAME_NOT_UNIQUE', 1301 ); define( 'ERROR_CUSTOM_FIELD_IN_USE', 1302 ); define( 'ERROR_CUSTOM_FIELD_INVALID_VALUE', 1303 ); define( 'ERROR_CUSTOM_FIELD_INVALID_DEFINITION', 1304 ); +define( 'ERROR_CUSTOM_FIELD_NOT_LINKED_TO_PROJECT', 1305 ); # ERROR_LDAP_* define( 'ERROR_LDAP_AUTH_FAILED', 1400 ); diff --git a/core/file_api.php b/core/file_api.php index ca89e95..ed4a79d 100644 --- a/core/file_api.php +++ b/core/file_api.php @@ -628,12 +628,19 @@ function file_is_name_unique( $p_name, $p_bug_id ) { * * @param integer $p_bug_id the bug id * @param array $p_file the uploaded file info, as retrieved from gpc_get_file() + * @param string $p_table table ('bug' or 'doc') + * @param string $p_title file title + * @param string $p_desc file description + * @param int $p_user_id user id + * @param int $p_date_added date added + * @param bool $p_skip_bug_update skip bug last modification update (useful when importing bug attachments) */ -function file_add( $p_bug_id, $p_file, $p_table = 'bug', $p_title = '', $p_desc = '', $p_user_id = null ) { +function file_add( $p_bug_id, $p_file, $p_table = 'bug', $p_title = '', $p_desc = '', $p_user_id = null, $p_date_added = 0, $p_skip_bug_update = false ) { file_ensure_uploaded( $p_file ); $t_file_name = $p_file['name']; $t_tmp_file = $p_file['tmp_name']; + $c_date_added = $p_date_added <= 0 ? db_now() : db_prepare_int( $p_date_added ); if( !file_type_check( $t_file_name ) ) { trigger_error( ERROR_FILE_NOT_ALLOWED, ERROR ); @@ -728,13 +735,15 @@ function file_add( $p_bug_id, $p_file, $p_table = 'bug', $p_title = '', $p_desc $query = "INSERT INTO $t_file_table (" . $p_table . "_id, title, description, diskfile, filename, folder, filesize, file_type, date_added, content, user_id) VALUES - ($c_id, '$c_title', '$c_desc', '$c_unique_name', '$c_new_file_name', '$c_file_path', $c_file_size, '$c_file_type', '" . db_now() . "', $c_content, $c_user_id)"; + ($c_id, '$c_title', '$c_desc', '$c_unique_name', '$c_new_file_name', '$c_file_path', $c_file_size, '$c_file_type', '" . $c_date_added . "', $c_content, $c_user_id)"; db_query( $query ); if( 'bug' == $p_table ) { # updated the last_updated date - $result = bug_update_date( $p_bug_id ); + if ( !$p_skip_bug_update ) { + $result = bug_update_date( $p_bug_id ); + } # log new bug history_log_event_special( $p_bug_id, FILE_ADDED, $t_file_name ); @@ -868,3 +877,103 @@ function file_get_extension( $p_filename ) { } return $t_extension; } + +/** + * Get file content + * + * @param int $p_file_id file id + * @param string $p_type file type + * @return array file type and content + */ +function file_get_content( $p_file_id, $p_type = 'bug' ) { + # we handle the case where the file is attached to a bug + # or attached to a project as a project doc. + $query = ''; + switch ( $p_type ) { + case 'bug': + $t_bug_file_table = db_get_table( 'mantis_bug_file_table' ); + $query = "SELECT * + FROM $t_bug_file_table + WHERE id=" . db_param(); + break; + case 'doc': + $t_project_file_table = db_get_table( 'mantis_project_file_table' ); + $query = "SELECT * + FROM $t_project_file_table + WHERE id=" . db_param(); + break; + default: + return false; + } + $result = db_query_bound( $query, Array( $p_file_id ) ); + $row = db_fetch_array( $result ); + + if ( $f_type == 'bug' ) { + $t_project_id = bug_get_field( $row['bug_id'], 'project_id' ); + } else { + $t_project_id = $row['bug_id']; + } + + # If finfo is available (always true for PHP >= 5.3.0) we can use it to determine the MIME type of files + $finfo_available = false; + if ( class_exists( 'finfo' ) ) { + $t_info_file = config_get( 'fileinfo_magic_db_file' ); + + if ( is_blank( $t_info_file ) ) { + $finfo = new finfo( FILEINFO_MIME ); + } else { + $finfo = new finfo( FILEINFO_MIME, $t_info_file ); + } + + if ( $finfo ) { + $finfo_available = true; + } + } + + $t_content_type = $row['file_type']; + + # dump file content to the connection. + switch ( config_get( 'file_upload_method' ) ) { + case DISK: + $t_local_disk_file = file_normalize_attachment_path( $row['diskfile'], $t_project_id ); + + if ( file_exists( $t_local_disk_file ) ) { + if ( $finfo_available ) { + $t_file_info_type = $finfo->file( $t_local_disk_file ); + + if ( $t_file_info_type !== false ) { + $t_content_type = $t_file_info_type; + } + } + return array( 'type' => $t_content_type, 'content' => file_get_contents( $t_local_disk_file ) ); + } + break; + case FTP: + $t_local_disk_file = file_normalize_attachment_path( $row['diskfile'], $t_project_id ); + + if ( !file_exists( $t_local_disk_file ) ) { + $ftp = file_ftp_connect(); + file_ftp_get ( $ftp, $t_local_disk_file, $row['diskfile'] ); + file_ftp_disconnect( $ftp ); + } + + if ( $finfo_available ) { + $t_file_info_type = $finfo->file( $t_local_disk_file ); + + if ( $t_file_info_type !== false ) { + $t_content_type = $t_file_info_type; + } + } + return array( 'type' => $t_content_type, 'content' => file_get_contents( $t_local_disk_file ) ); + break; + default: + if ( $finfo_available ) { + $t_file_info_type = $finfo->buffer( $row['content'] ); + + if ( $t_file_info_type !== false ) { + $t_content_type = $t_file_info_type; + } + } + return array( 'type' => $t_content_type, 'content' => $row['content'] ); + } +} diff --git a/lang/strings_english.txt b/lang/strings_english.txt index 038c178..f4bb7a0 100644 --- a/lang/strings_english.txt +++ b/lang/strings_english.txt @@ -238,6 +238,7 @@ $MANTIS_ERROR[ERROR_CUSTOM_FIELD_NAME_NOT_UNIQUE] = 'This is a duplicate name.'; $MANTIS_ERROR[ERROR_CUSTOM_FIELD_IN_USE] = 'At least one project still uses this field.'; $MANTIS_ERROR[ERROR_CUSTOM_FIELD_INVALID_VALUE] = 'Invalid value for field "%1$s".'; $MANTIS_ERROR[ERROR_CUSTOM_FIELD_INVALID_DEFINITION] = 'Invalid custom field definition.'; +$MANTIS_ERROR[ERROR_CUSTOM_FIELD_NOT_LINKED_TO_PROJECT] = 'Custom field "%1$s" (id %2$s) not linked to currently active project.'; $MANTIS_ERROR[ERROR_LDAP_AUTH_FAILED] = 'LDAP Authentication Failed.'; $MANTIS_ERROR[ERROR_LDAP_SERVER_CONNECT_FAILED] = 'LDAP Server Connection Failed.'; $MANTIS_ERROR[ERROR_LDAP_UPDATE_FAILED] = 'LDAP Record Update has failed.'; diff --git a/plugins/XmlImportExport/ImportXml.php b/plugins/XmlImportExport/ImportXml.php index 203d35e..83f974d 100644 --- a/plugins/XmlImportExport/ImportXml.php +++ b/plugins/XmlImportExport/ImportXml.php @@ -102,23 +102,42 @@ class ImportXML { echo " Done\n"; + // replace references in bug description and additional information $importedIssues = $this->itemsMap_->getall( 'issue' ); printf( "Processing cross-references for %s issues...", count( $importedIssues ) ); foreach( $importedIssues as $oldId => $newId ) { $bugData = bug_get( $newId, true ); $bugLinkRegexp = '/(^|[^\w])(' . preg_quote( $this->source_->issuelink, '/' ) . ')(\d+)\b/e'; - $replacement = '"\\1" . $this->getReplacementString( "\\2", "\\3" )'; - - $bugData->description = preg_replace( $bugLinkRegexp, $replacement, $bugData->description ); - bug_update( $newId, $bugData, true, true ); - } + // replace links in description + preg_match_all( $bugLinkRegexp, $bugData->description, $matches ); + if ( is_array( $matches[3] && count( $matches[3] ) > 0 ) ) { + $content_replaced = true; + foreach ( $matches[3] as $old_id ) { + $bugData->description = str_replace( $this->source_->issuelink . $old_id, $this->getReplacementString( $this->source_->issuelink, $old_id ), $bugData->description); + } + } + // replace links in additional information + preg_match_all( $bugLinkRegexp, $bugData->additional_information, $matches ); + if ( is_array( $matches[3] && count( $matches[3] ) > 0 ) ) { + $content_replaced = true; + foreach ( $matches[3] as $old_id ) { + $bugData->additional_information = str_replace( $this->source_->issuelink . $old_id, $this->getReplacementString( $this->source_->issuelink, $old_id ), $bugData->additional_information); + } + } + if ( $content_replaced ) { + // only update bug if necessary (otherwise last update date would be unnecessarily overwritten) + $bugData->update( true ); + } + } + + // @todo: replace references within bugnotes echo " Done\n"; } /** * Compute and return the new link - * + * */ private function getReplacementString( $oldLinkTag, $oldId ) { $linkTag = config_get( 'bug_link_tag' ); diff --git a/plugins/XmlImportExport/ImportXml/Issue.php b/plugins/XmlImportExport/ImportXml/Issue.php index 78541ce..20d5a7e 100644 --- a/plugins/XmlImportExport/ImportXml/Issue.php +++ b/plugins/XmlImportExport/ImportXml/Issue.php @@ -37,7 +37,7 @@ class ImportXml_Issue implements ImportXml_Interface { // Read stream until current item finishes, processing // the data found - public function process( XMLreader$reader ) { + public function process( XMLreader $reader ) { //print "\nImportIssue process()\n"; $t_project_id = helper_get_current_project(); // TODO: category_get_id_by_name could work by default on current project @@ -109,9 +109,91 @@ class ImportXml_Issue implements ImportXml_Interface { case 'project'; - // ignore original value, use current project - $this->newbug_->project_id = $t_project_id; - break; + // ignore original value, use current project + $this->newbug_->project_id = $t_project_id; + break; + + case 'custom_fields': + // store custom fields + $i = -1; + $depth_cf = $reader->depth; + while ( $reader->read() && + ( $reader->depth > $depth_cf || + $reader->nodeType != XMLReader::END_ELEMENT ) ) + { + + if ( $reader->nodeType == XMLReader::ELEMENT ) { + if ($reader->localName == 'custom_field') { + $i++; + } + switch ( $reader->localName ) { + default: + $field = $reader->localName; + $reader->read( ); + $t_custom_fields[$i]->$field = $reader->value; + } + } + } + break; + + case 'bugnotes': + // store bug notes + $i = -1; + $depth_bn = $reader->depth; + while ( $reader->read() && + ( $reader->depth > $depth_bn || + $reader->nodeType != XMLReader::END_ELEMENT ) ) + { + + if ( $reader->nodeType == XMLReader::ELEMENT ) { + if ($reader->localName == 'bugnote') { + $i++; + } + switch ( $reader->localName ) { + case 'reporter': + $t_old_id = $reader->getAttribute( 'id' ); + $reader->read( ); + $t_bugnotes[$i]->reporter_id = $this->get_user_id( $reader->value, $userId ); + break; + + case 'view_state': + $t_old_id = $reader->getAttribute( 'id' ); + $reader->read( ); + $t_bugnotes[$i]->private = $reader->value == VS_PRIVATE ? true : false; + break; + + default: + $field = $reader->localName; + $reader->read( ); + $t_bugnotes[$i]->$field = $reader->value; + } + } + } + break; + + case 'attachments': + // store attachments + $i = -1; + $depth_att = $reader->depth; + while ( $reader->read() && + ( $reader->depth > $depth_att || + $reader->nodeType != XMLReader::END_ELEMENT ) ) + { + + if ( $reader->nodeType == XMLReader::ELEMENT ) { + if ($reader->localName == 'attachment') { + $i++; + } + switch ( $reader->localName ) { + default: + $field = $reader->localName; + $reader->read( ); + $t_attachments[$i]->$field = $reader->value; + } + } + } + break; + default: $field = $reader->localName; @@ -124,11 +206,49 @@ class ImportXml_Issue implements ImportXml_Interface { // now save the new bug $this->new_id_ = $this->newbug_->create(); + + // add custom fields + if ( $this->new_id_ > 0 && is_array( $t_custom_fields ) && count( $t_custom_fields ) > 0 ) { + foreach ( $t_custom_fields as $t_custom_field) { + $t_custom_field_id = custom_field_get_id_from_name( $t_custom_field->name ); + if ( custom_field_ensure_exists( $t_custom_field_id ) && custom_field_is_linked( $t_custom_field_id, $t_project_id ) ) { + custom_field_set_value( $t_custom_field->id, $this->new_id_, $t_custom_field->value ); + } + else { + error_parameters( $t_custom_field->name, $t_custom_field_id ); + trigger_error( ERROR_CUSTOM_FIELD_NOT_LINKED_TO_PROJECT, ERROR ); + } + } + } + + // add bugnotes + if ( $this->new_id_ > 0 && is_array( $t_bugnotes ) && count( $t_bugnotes ) > 0 ) { + foreach ( $t_bugnotes as $t_bugnote) { + bugnote_add( $this->new_id_, $t_bugnote->note, $t_bugnote->time_tracking, $t_bugnote->private, $t_bugnote->note_type, $t_bugnote_>note_attr, $t_bugnote->reporter_id, false, $t_bugnote->date_submitted, $t_bugnote->last_modified, true ); + } + } + + // add attachments + if ( $this->new_id_ > 0 && is_array( $t_attachments ) && count( $t_attachments ) > 0 ) { + foreach ( $t_attachments as $t_attachment) { + // Create a temporary file in the temporary files directory using sys_get_temp_dir() + $temp_file_name = tempnam( sys_get_temp_dir(), 'MantisImport' ); + file_put_contents( $temp_file_name, base64_decode( $t_attachment->content ) ); + $file_data = array( 'name' => $t_attachment->filename, + 'type' => $t_attachment->file_type, + 'tmp_name' => $temp_file_name, + 'size' => filesize( $temp_file_name ) ); + // unfortunately we have no clue who has added the attachment (this could only be fetched from history -> feel free to implement this) + // also I have no clue where description should come from... + file_add( $this->new_id_, $file_data, 'bug', $t_attachment->title, $p_desc = '', $p_user_id = null, $t_attachment->date_added, true ); + unlink( $temp_file_name ); + } + } //echo "\nnew bug: $this->new_id_\n"; } - public function update_map( Mapper$mapper ) { + public function update_map( Mapper $mapper ) { $mapper->add( 'issue', $this->old_id_, $this->new_id_ ); } @@ -151,9 +271,13 @@ class ImportXml_Issue implements ImportXml_Interface { private function get_user_id( $username, $squash_userid = 0 ) { $t_user_id = user_get_id_by_name( $username ); if( $t_user_id === false ) { - - //not found - $t_user_id = $squash_userid; + // user not found by username -> check real name + // keep in mind that the setting config_get( 'show_realname' ) may differ between import and export system! + $t_user_id = user_get_id_by_realname( $username ); + if ( $t_user_id === false ) { + //not found + $t_user_id = $squash_userid; + } } return $t_user_id; } diff --git a/plugins/XmlImportExport/pages/export.php b/plugins/XmlImportExport/pages/export.php index e9a2926..0eb024e 100644 --- a/plugins/XmlImportExport/pages/export.php +++ b/plugins/XmlImportExport/pages/export.php @@ -146,6 +146,93 @@ foreach( $t_result as $t_row ) { $writer->writeElement( $t_element, $t_value ); } } + + # fetch and export custom fields + $t_custom_fields = custom_field_get_all_linked_fields( $t_row->id ); + if ( is_array( $t_custom_fields ) && count( $t_custom_fields ) > 0 ) { + $writer->startElement( 'custom_fields' ); + foreach ( $t_custom_fields as $custom_field_name => $t_custom_field ) { + $writer->startElement( 'custom_field' ); + # id + $writer->writeElement( 'id', custom_field_get_id_from_name( $custom_field_name ) ); + # title + $writer->writeElement( 'name', $custom_field_name ); + # filename + $writer->writeElement( 'type', $t_custom_field['type'] ); + # filesize + $writer->writeElement( 'value', $t_custom_field['value'] ); + # file_type + $writer->writeElement( 'access_level_r', $t_custom_field['access_level_r'] ); + + $writer->endElement(); # custom_field + } + $writer->endElement(); # custom_fields + } + + # fetch and export bugnotes + $t_bugnotes = bugnote_get_all_bugnotes( $t_row->id ); + if ( is_array( $t_bugnotes ) && count( $t_bugnotes ) > 0 ) { + $writer->startElement( 'bugnotes' ); + foreach ( $t_bugnotes as $t_bugnote ) { + $writer->startElement( 'bugnote' ); + # id + $writer->writeElement( 'id', $t_bugnote->id ); + # reporter + $writer->startElement( 'reporter' ); + $writer->writeAttribute( 'id', $t_bugnote->reporter_id ); + $writer->text( user_get_name( $t_bugnote->reporter_id ) ); + $writer->endElement( ); + # bug note + $writer->writeElement( 'note', $t_bugnote->note ); + # view state + $writer->startElement( 'view_state' ); + $writer->writeAttribute( 'id', $t_bugnote->view_state ); + $writer->text( get_enum_element( 'view_state', $t_bugnote->view_state ) ); + $writer->endElement( ); + # date submitted + $writer->writeElement( 'date_submitted', $t_bugnote->date_submitted ); + # last modified + $writer->writeElement( 'last_modified', $t_bugnote->last_modified ); + # note type + $writer->writeElement( 'note_type', $t_bugnote->note_type ); + # note attr + $writer->writeElement( 'note_attr', $t_bugnote->note_attr ); + # time tracking + $writer->writeElement( 'time_tracking', $t_bugnote->time_tracking ); + + $writer->endElement(); # bugnote + } + $writer->endElement(); # bugnotes + } + + # fetch and export attachments + $t_attachments = bug_get_attachments( $t_row->id ); + if ( is_array( $t_attachments ) && count( $t_attachments ) > 0 ) { + $writer->startElement( 'attachments' ); + foreach ( $t_attachments as $t_attachment ) { + $writer->startElement( 'attachment' ); + # id + $writer->writeElement( 'id', $t_attachment['id'] ); + # title + $writer->writeElement( 'title', $t_attachment['title'] ); + # filename + $writer->writeElement( 'filename', $t_attachment['filename'] ); + # filesize + $writer->writeElement( 'filesize', $t_attachment['filesize'] ); + # file_type + $writer->writeElement( 'file_type', $t_attachment['file_type'] ); + # last added + $writer->writeElement( 'date_added', $t_attachment['date_added'] ); + # content + $content = file_get_content( $t_attachment['id'] ); + + $writer->writeElement( 'content', base64_encode( $content['content'] ) ); + + $writer->endElement(); # attachment + } + $writer->endElement(); # bugnotes + } + $writer->endElement(); # issue // Save memory by clearing cache -- 1.7.0.2.msysgit.0