1<?php 2/** 3 * MantisBT - A PHP based bugtracking system 4 * 5 * MantisBT is free software: you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation, either version 2 of the License, or 8 * (at your option) any later version. 9 * 10 * MantisBT is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with MantisBT. If not, see <http://www.gnu.org/licenses/>. 17 * 18 * @copyright Copyright 2002 MantisBT Team - mantisbt-dev@lists.sourceforge.net 19 */ 20 21/** 22 * Import XML issue class 23 */ 24 25 26require_api( 'bug_api.php' ); 27require_api( 'user_api.php' ); 28require_once( 'Interface.php' ); 29 30/** 31 * Import XML issue class 32 */ 33class ImportXml_Issue implements ImportXml_Interface { 34 /** 35 * old issue id 36 */ 37 private $old_id_; 38 /** 39 * new issue id 40 */ 41 private $new_id_; 42 43 /** 44 * new bug object 45 */ 46 private $newbug_; 47 48 /** 49 * keep existing category 50 * @var bool 51 */ 52 private $keepCategory_; 53 /** 54 * default category 55 * @var int 56 */ 57 private $defaultCategory_; 58 59 /** 60 * Default Constructor 61 * @param boolean $p_keep_category Whether to keep existing category. 62 * @param integer $p_default_category Identifier of default category. 63 */ 64 public function __construct( $p_keep_category, $p_default_category ) { 65 $this->newbug_ = new BugData; 66 $this->keepCategory_ = $p_keep_category; 67 $this->defaultCategory_ = $p_default_category; 68 } 69 70 /** 71 * Read stream until current item finishes, processing the data found 72 * @param XMLreader $t_reader XMLReader being processed. 73 * @return void 74 */ 75 public function process( XMLreader $t_reader ) { 76 # print "\nImportIssue process()\n"; 77 $t_project_id = helper_get_current_project(); # TODO: category_get_id_by_name could work by default on current project 78 $t_user_id = auth_get_current_user_id( ); 79 80 $t_custom_fields = array(); 81 $t_bugnotes = array(); 82 $t_attachments = array(); 83 84 $t_depth = $t_reader->depth; 85 while( $t_reader->read() && 86 ($t_reader->depth > $t_depth || 87 $t_reader->nodeType != XMLReader::END_ELEMENT)) { 88 if( $t_reader->nodeType == XMLReader::ELEMENT ) { 89 switch( $t_reader->localName ) { 90 case 'reporter': 91 $t_old_id = $t_reader->getAttribute( 'id' ); 92 $t_reader->read( ); 93 $this->newbug_->reporter_id = $this->get_user_id( $t_reader->value, $t_user_id ); 94 95 # echo "reporter: old id = $t_old_id - new id = {$this->newbug_->reporter_id}\n"; 96 break; 97 98 case 'handler': 99 $t_old_id = $t_reader->getAttribute( 'id' ); 100 $t_reader->read( ); 101 $this->newbug_->handler_id = $this->get_user_id( $t_reader->value, $t_user_id ); 102 103 # echo "handler: old id = $t_old_id - new id = {$this->newbug_->handler_id}\n"; 104 break; 105 106 case 'category': 107 $this->newbug_->category_id = $this->defaultCategory_; 108 109 if( version_compare( MANTIS_VERSION, '1.2', '>' ) === true ) { 110 $t_reader->read( ); 111 112 if( $this->keepCategory_ ) { 113 # Check for the category's existence in the current project 114 # well as its parents (if any) 115 $t_projects_hierarchy = project_hierarchy_inheritance( $t_project_id ); 116 foreach( $t_projects_hierarchy as $t_project ) { 117 $t_category_id = category_get_id_by_name( $t_reader->value, $t_project, false ); 118 if( $t_category_id !== false ) { 119 $this->newbug_->category_id = $t_category_id; 120 break; 121 } 122 } 123 } 124 125 # echo "new id = {$this->newbug_->category_id}\n"; 126 } 127 break; 128 129 case 'eta': 130 case 'priority': 131 case 'projection': 132 case 'reproducibility': 133 case 'resolution': 134 case 'severity': 135 case 'status': 136 case 'view_state': 137 $t_field = $t_reader->localName; 138 $t_id = $t_reader->getAttribute( 'id' ); 139 $t_reader->read( ); 140 $t_value = $t_reader->value; 141 142 # Here we assume ids have the same meaning in both installations 143 # TODO add a check for customized values 144 $this->newbug_->$t_field = $t_id; 145 break; 146 147 case 'id': 148 $t_reader->read( ); 149 $this->old_id_ = $t_reader->value; 150 break; 151 152 case 'project'; 153 # ignore original value, use current project 154 $this->newbug_->project_id = $t_project_id; 155 break; 156 157 case 'custom_fields': 158 # store custom fields 159 $i = -1; 160 $t_depth_cf = $t_reader->depth; 161 while( $t_reader->read() && 162 ( $t_reader->depth > $t_depth_cf || 163 $t_reader->nodeType != XMLReader::END_ELEMENT ) ) { 164 if( $t_reader->nodeType == XMLReader::ELEMENT ) { 165 if( $t_reader->localName == 'custom_field' ) { 166 $t_custom_fields[++$i] = new stdClass(); 167 } 168 switch( $t_reader->localName ) { 169 default: 170 $t_field = $t_reader->localName; 171 $t_reader->read( ); 172 $t_custom_fields[$i]->$t_field = $t_reader->value; 173 } 174 } 175 } 176 break; 177 178 case 'bugnotes': 179 # store bug notes 180 $i = -1; 181 $t_depth_bn = $t_reader->depth; 182 while( $t_reader->read() && 183 ( $t_reader->depth > $t_depth_bn || 184 $t_reader->nodeType != XMLReader::END_ELEMENT ) ) { 185 if( $t_reader->nodeType == XMLReader::ELEMENT ) { 186 if( $t_reader->localName == 'bugnote' ) { 187 $t_bugnotes[++$i] = new stdClass(); 188 } 189 switch( $t_reader->localName ) { 190 case 'reporter': 191 $t_old_id = $t_reader->getAttribute( 'id' ); 192 $t_reader->read( ); 193 $t_bugnotes[$i]->reporter_id = $this->get_user_id( $t_reader->value, $t_user_id ); 194 break; 195 196 case 'view_state': 197 $t_old_id = $t_reader->getAttribute( 'id' ); 198 $t_reader->read( ); 199 $t_bugnotes[$i]->private = $t_reader->value == VS_PRIVATE ? true : false; 200 break; 201 202 default: 203 $t_field = $t_reader->localName; 204 $t_reader->read( ); 205 $t_bugnotes[$i]->$t_field = $t_reader->value; 206 } 207 } 208 } 209 break; 210 211 case 'attachments': 212 # store attachments 213 $i = -1; 214 $t_depth_att = $t_reader->depth; 215 while( $t_reader->read() && 216 ( $t_reader->depth > $t_depth_att || 217 $t_reader->nodeType != XMLReader::END_ELEMENT ) ) { 218 if( $t_reader->nodeType == XMLReader::ELEMENT ) { 219 if( $t_reader->localName == 'attachment' ) { 220 $t_attachments[++$i] = new stdClass(); 221 } 222 switch( $t_reader->localName ) { 223 default: 224 $t_field = $t_reader->localName; 225 $t_reader->read( ); 226 $t_attachments[$i]->$t_field = $t_reader->value; 227 } 228 } 229 } 230 break; 231 default: 232 $t_field = $t_reader->localName; 233 234 # echo "using default handler for field: $field\n"; 235 $t_reader->read( ); 236 $this->newbug_->$t_field = $t_reader->value; 237 } 238 } 239 } 240 241 # now save the new bug 242 $this->new_id_ = $this->newbug_->create(); 243 244 # add custom fields 245 if( $this->new_id_ > 0 && is_array( $t_custom_fields ) && count( $t_custom_fields ) > 0 ) { 246 foreach( $t_custom_fields as $t_custom_field ) { 247 $t_custom_field_id = custom_field_get_id_from_name( $t_custom_field->name ); 248 if( custom_field_ensure_exists( $t_custom_field_id ) && custom_field_is_linked( $t_custom_field_id, $t_project_id ) ) { 249 custom_field_set_value( $t_custom_field->id, $this->new_id_, $t_custom_field->value ); 250 } else { 251 error_parameters( $t_custom_field->name, $t_custom_field_id ); 252 trigger_error( ERROR_CUSTOM_FIELD_NOT_LINKED_TO_PROJECT, ERROR ); 253 } 254 } 255 } 256 257 # add bugnotes 258 if( $this->new_id_ > 0 && is_array( $t_bugnotes ) && count( $t_bugnotes ) > 0 ) { 259 foreach( $t_bugnotes as $t_bugnote ) { 260 bugnote_add( 261 $this->new_id_, 262 $t_bugnote->note, 263 $t_bugnote->time_tracking, 264 $t_bugnote->private, 265 $t_bugnote->note_type, 266 $t_bugnote->note_attr, 267 $t_bugnote->reporter_id, 268 false, 269 $t_bugnote->date_submitted, 270 $t_bugnote->last_modified, 271 true ); 272 } 273 } 274 275 # add attachments 276 if( $this->new_id_ > 0 && is_array( $t_attachments ) && count( $t_attachments ) > 0 ) { 277 foreach ( $t_attachments as $t_attachment ) { 278 # Create a temporary file in the temporary files directory using sys_get_temp_dir() 279 $t_temp_file_name = tempnam( sys_get_temp_dir(), 'MantisImport' ); 280 file_put_contents( $t_temp_file_name, base64_decode( $t_attachment->content ) ); 281 $t_file_data = array( 282 'name' => $t_attachment->filename, 283 'type' => $t_attachment->file_type, 284 'tmp_name' => $t_temp_file_name, 285 'size' => filesize( $t_temp_file_name ), 286 'error' => UPLOAD_ERR_OK, 287 ); 288 # unfortunately we have no clue who has added the attachment (this could only be fetched from history -> feel free to implement this) 289 # also I have no clue where description should come from... 290 file_add( $this->new_id_, $t_file_data, 'bug', $t_attachment->title, $p_desc = '', $p_user_id = null, $t_attachment->date_added, true ); 291 unlink( $t_temp_file_name ); 292 } 293 } 294 295 #echo "\nnew bug: $this->new_id_\n"; 296 } 297 298 /** 299 * update mapper 300 * @param ImportXml_Mapper $p_mapper Mapper. 301 * @return void 302 */ 303 public function update_map( ImportXml_Mapper $p_mapper ) { 304 $p_mapper->add( 'issue', $this->old_id_, $this->new_id_ ); 305 } 306 307 /** 308 * Dump Diagnostic information 309 * @return void 310 */ 311 public function dumpbug() { 312 var_dump( $this->newbug_ ); 313 var_dump( $this->issueMap ); 314 } 315 316 /** 317 * Return the user id in the destination tracker 318 * 319 * Current logic is: try to find the same user by username; 320 * if it fails, use $p_squash_userid 321 * 322 * @param string $p_username Username as imported. 323 * @param integer $p_squash_userid Fallback userid. 324 * @return integer 325 */ 326 private function get_user_id( $p_username, $p_squash_userid = 0 ) { 327 $t_user_id = user_get_id_by_name( $p_username ); 328 if( $t_user_id === false ) { 329 # user not found by username -> check real name 330 # keep in mind that the setting config_get( 'show_user_realname_threshold' ) may differ between import and export system! 331 $t_user_id = user_get_id_by_realname( $p_username ); 332 if( $t_user_id === false ) { 333 # not found 334 $t_user_id = $p_squash_userid; 335 } 336 } 337 return $t_user_id; 338 } 339} 340