PluginProbe
UpStream: a Project Management Plugin for WordPress / 2.1.0
UpStream: a Project Management Plugin for WordPress v2.1.0
trunk 1.39.0 1.39.1 1.39.2 1.39.3 2.0.7 2.1.0
upstream / includes / class-upstream-import.php

class-upstream-import.php in UpStream: a Project Management Plugin for WordPress 2.1.0, at includes/class-upstream-import.php

472 lines 11.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Setup message asking for review.
4 *
5 * @author UpStream
6 * @category Admin
7 * @package UpStream/Admin
8 * @version 1.0.0
9 */
10
11 // Exit if accessed directly or already defined.
12 if ( ! defined( 'ABSPATH' ) || class_exists( 'UpStream_Import' ) ) {
13 return;
14 }
15
16 /**
17 * Class UpStream_Import
18 */
19 class UpStream_Import {
20
21 /**
22 * Option created by
23 *
24 * @var int
25 */
26 protected $option_created_by = 1;
27
28 /**
29 * Columns
30 *
31 * @var array
32 */
33 protected $columns = array();
34
35 /**
36 * Model manager
37 *
38 * @var object
39 */
40 protected $model_manager;
41
42 /**
43 * UpStream_Admin_Import constructor.
44 */
45 public function __construct() {
46 $this->option_created_by = wp_get_current_user()->ID;
47 $this->model_manager = \UpStream_Model_Manager::get_instance();
48 $this->model_manager->loadAll();
49 }
50
51 /**
52 * Set project column
53 *
54 * @param int $project_column Project column.
55 */
56 public function set_project_column( $project_column ) {
57 $this->project_column = $project_column;
58 }
59
60 /**
61 * Import file.
62 *
63 * @param string $file File path.
64 * @param int $line_start Line start.
65 * @return string|null
66 */
67 public static function import_file( $file, $line_start ) {
68 $error = '';
69 $importer = new UpStream_Import();
70
71 ini_set( 'auto_detect_line_endings', true );
72 $handle = fopen( $file, 'r' );
73
74 try {
75 $line_no = 0;
76 while ( ( $data = fgetcsv( $handle ) ) !== false ) {
77
78 if ( 0 === $line_no || $line_no >= $line_start ) {
79 $importer->import_table_line( $data, $line_no );
80 }
81 $line_no++;
82 if ( $line_no >= $line_start + 100 ) {
83 break;
84 }
85 }
86 } catch ( \Exception $e ) {
87 $error = __( 'Error loading file: line ', 'upstream' ) . ( $line_no + 1 ) . ' ' . $e->getMessage();
88 }
89
90 fclose( $handle );
91 ini_set( 'auto_detect_line_endings', false );
92
93 return $error;
94 }
95
96 /**
97 * Prepare file.
98 *
99 * @param string $file File path.
100 */
101 public static function prepare_file( $file ) {
102 $message = '';
103 $importer = new UpStream_Import();
104
105 ini_set( 'auto_detect_line_endings', true );
106 $handle = fopen( $file, 'r' );
107
108 try {
109 $line_no = 0;
110 while ( ( $data = fgetcsv( $handle ) ) !== false ) {
111 $line_no++;
112 }
113 } catch ( \Exception $e ) {
114 $message = __( 'Error loading file: line ', 'upstream' ) . ( $line_no + 1 ) . ' ' . $e->getMessage();
115 }
116
117 fclose( $handle );
118 ini_set( 'auto_detect_line_endings', false );
119
120 return array(
121 'message' => $message,
122 'lines' => $line_no,
123 );
124 }
125
126 /**
127 * Clean line.
128 *
129 * @param array $line Line.
130 * @return array
131 */
132 protected function clean_line( &$line ) {
133 $newline = array();
134
135 foreach ( $line as $l ) {
136 $newline[] = trim( $l );
137 }
138
139 return $newline;
140 }
141
142 /**
143 * Import table line
144 *
145 * @param array $arr Array line.
146 * @param int $line_no Line no.
147 * @throws UpStream_Import_Exception Exception.
148 */
149 protected function import_table_line( &$arr, $line_no ) {
150 if ( 0 === $line_no ) {
151 $this->load_header( $arr );
152 } else {
153 $line = $this->clean_line( $arr );
154
155 // load project.
156 $project_id = $this->find_item_field( UPSTREAM_ITEM_TYPE_PROJECT, 'id', $line );
157 if ( ! $project_id ) {
158 $title = $this->find_item_field( UPSTREAM_ITEM_TYPE_PROJECT, 'title', $line );
159 if ( $title ) {
160 $project_id = $this->find_or_create_item_by_title( UPSTREAM_ITEM_TYPE_PROJECT, $title );
161 }
162 }
163
164 $project = null;
165 if ( $project_id ) {
166 try {
167 $project = $this->model_manager->getByID( UPSTREAM_ITEM_TYPE_PROJECT, $project_id );
168 } catch ( \UpStream_Model_ArgumentException $e ) {
169 throw new UpStream_Import_Exception(
170 sprintf(
171 // translators: %s: Project ID.
172 __( 'Project with ID %s does not exist.', 'upstream' ),
173 $project_id
174 )
175 );
176 }
177 }
178
179 if ( $project ) {
180 $this->set_fields( $line, $project );
181 }
182
183 // load milestone.
184 $milestone_id = $this->find_item_field( UPSTREAM_ITEM_TYPE_MILESTONE, 'id', $line );
185 if ( ! $milestone_id ) {
186 $title = $this->find_item_field( UPSTREAM_ITEM_TYPE_MILESTONE, 'title', $line );
187 if ( $title ) {
188 $milestone_id = $this->find_or_create_item_by_title( UPSTREAM_ITEM_TYPE_MILESTONE, $title, $project );
189 }
190 }
191
192 $milestone = null;
193 if ( $milestone_id ) {
194 try {
195 $milestone = $this->model_manager->getByID( UPSTREAM_ITEM_TYPE_MILESTONE, $milestone_id );
196 } catch ( \UpStream_Model_ArgumentException $e ) {
197 throw new UpStream_Import_Exception(
198 sprintf(
199 // translators: %s: Milestone ID.
200 __( 'Milestone with ID %s does not exist.', 'upstream' ),
201 $milestone_id
202 )
203 );
204 }
205 }
206
207 if ( $milestone ) {
208 $this->set_fields( $line, $milestone );
209 }
210
211 $this->import_children_of_type( UPSTREAM_ITEM_TYPE_TASK, $project, $milestone, $line );
212 $this->import_children_of_type( UPSTREAM_ITEM_TYPE_FILE, $project, $milestone, $line );
213 $this->import_children_of_type( UPSTREAM_ITEM_TYPE_BUG, $project, $milestone, $line );
214
215 }
216 }
217
218 /**
219 * Find child item.
220 *
221 * @param string $type Child type.
222 * @param object $project Project data.
223 * @param int $item_id Item id.
224 * @return mixed
225 */
226 protected function find_child_item( $type, &$project, $item_id ) {
227 if ( $project ) {
228 $pid = $project->id;
229
230 return $this->model_manager->getByID( $type, $item_id, UPSTREAM_ITEM_TYPE_PROJECT, $project->id );
231 }
232 }
233
234
235 /**
236 * Import child of type.
237 *
238 * @param string $type Child type.
239 * @param object $project Project data.
240 * @param object $milestone Milestone data.
241 * @param int $line Line number.
242 * @throws UpStream_Import_Exception Exception.
243 */
244 protected function import_children_of_type( $type, &$project, &$milestone, &$line ) {
245 // look for tasks.
246 $item_id = $this->find_item_field( $type, 'id', $line );
247 if ( ! $item_id ) {
248 $title = $this->find_item_field( $type, 'title', $line );
249 if ( $title ) {
250 $item_id = $this->find_or_create_item_by_title( $type, $title, $project, $milestone );
251 }
252 }
253
254 if ( $item_id ) {
255 try {
256 $item = $this->find_child_item( $type, $project, $item_id );
257 } catch ( \UpStream_Model_ArgumentException $e ) {
258 throw new UpStream_Import_Exception(
259 sprintf(
260 // translators: %1$s: Item type, %2$s: Item id.
261 __( 'Item %1$s with ID %2$s does not exist.', 'upstream' ),
262 $type,
263 $item_id
264 )
265 );
266 }
267
268 $this->set_fields( $line, $item );
269 }
270
271 }
272
273 /**
274 * Find or create item by title.
275 *
276 * @param string $type Child type.
277 * @param string $title Item title.
278 * @param mixed $project Project data.
279 * @param mixed $milestone Milestone data.
280 * @return |null
281 */
282 protected function find_or_create_item_by_title( $type, $title, $project = null, $milestone = null ) {
283 if ( UPSTREAM_ITEM_TYPE_PROJECT === $type ) {
284
285 $matches = $this->model_manager->findAllByCallback(
286 function( $item ) use ( $title ) {
287 return UPSTREAM_ITEM_TYPE_PROJECT === $item->type && $item->title == $title;
288 }
289 );
290
291 $obj = null;
292
293 if ( count( $matches ) > 0 ) {
294 $obj = $matches[0];
295 } else {
296 $obj = $this->model_manager->createObject( $type, $title, $this->option_created_by );
297 $obj->store();
298 }
299
300 return $obj->id;
301 }
302
303 if ( ! $project ) {
304 return null;
305 }
306
307 $matches = $this->model_manager->findAllByCallback(
308 function( $item ) use ( $title, $type, $project ) {
309 return $item->type === $type && $item->parentId === $project->id && $item->title == $title; // phpcs:ignore
310 }
311 );
312
313 if ( count( $matches ) > 0 ) {
314 $obj = $matches[0];
315 } else {
316 $obj = $this->model_manager->createObject( $type, $title, $this->option_created_by, $project->id );
317 }
318
319 if ( UPSTREAM_ITEM_TYPE_TASK === $type && $milestone ) {
320 $obj->milestone = $milestone;
321 }
322
323 $obj->store();
324
325 return $obj->id;
326 }
327
328 /**
329 * Find item field.
330 *
331 * @param string $type Item type.
332 * @param string $field Field name.
333 * @param array $line Line.
334 * @return mixed|null
335 */
336 protected function find_item_field( $type, $field, &$line ) {
337 $column_count = count( $this->columns );
338
339 for ( $i = 0; $i < $column_count; $i++ ) {
340
341 if ( $this->columns[ $i ]->itemType === $type && $this->columns[ $i ]->fieldName === $field ) {
342 return $line[ $i ];
343 }
344 }
345
346 return null;
347 }
348
349
350 /**
351 * Sets the fields of the object based on the fields in the table.
352 *
353 * @param array $line Line data.
354 * @param array $item Item data.
355 * @throws UpStream_Import_Exception Exception.
356 */
357 protected function set_fields( &$line, &$item ) {
358 $changed = false;
359
360 if ( ! $item ) {
361 return;
362 }
363
364 $line_count = count( $line );
365
366 for ( $i = 0; $i < $line_count; $i++ ) {
367
368 if ( ! $this->columns[ $i ] ) {
369 continue;
370 }
371
372 if ( $this->columns[ $i ]->itemType === $item->type ) {
373 $val = null;
374 try {
375 $val = $item->{$this->columns[ $i ]->fieldName};
376 } catch ( \UpStream_Model_ArgumentException $e ) {
377 throw new UpStream_Import_Exception( 'Error.' );
378 }
379
380 if ( $line[ $i ] && $val != $line[ $i ] ) {
381 try {
382 $item->{$this->columns[ $i ]->fieldName} = htmlentities( iconv( 'cp1252', 'utf-8', trim( $line[ $i ] ) ), ENT_IGNORE, 'UTF-8' );
383 $changed = true;
384 } catch ( \UpStream_Model_ArgumentException $e ) {
385 throw new UpStream_Import_Exception(
386 sprintf(
387 // translators: %1$s: column number, %2$s: field name.
388 __( '(column %1$s, field %2$s)', 'upstream' ),
389 $i + 1,
390 $this->columns[ $i ]->fieldName
391 ) . ' ' . $e->getMessage()
392 );
393 }
394 }
395 }
396 }
397
398 if ( $changed ) {
399 $item->store();
400 }
401
402 return $changed;
403 }
404
405
406 /**
407 * Load header.
408 *
409 * @param array $header Header.
410 * @throws UpStream_Import_Exception Exception.
411 */
412 protected function load_header( &$header ) {
413 $header_count = count( $header );
414
415 for ( $i = 0; $i < $header_count; $i++ ) {
416
417 $header[ $i ] = trim( $header[ $i ] );
418 $header[ $i ] = trim( $header[ $i ], chr( 239 ) . chr( 187 ) . chr( 191 ) );
419 $s = null;
420
421 if ( $header[ $i ] ) {
422 $parts = explode( '.', $header[ $i ] );
423
424 if ( count( $parts ) < 2 ) {
425 throw new UpStream_Import_Exception(
426 sprintf(
427 // translators: %s: column name.
428 __( 'Header column %s must be of the form item.field (example: project.title).', 'upstream' ),
429 $header[ $i ]
430 )
431 );
432 }
433
434 $item_type = $parts[0];
435 $field_name = $parts[1];
436
437 if ( ! in_array(
438 $item_type,
439 array(
440 UPSTREAM_ITEM_TYPE_PROJECT,
441 UPSTREAM_ITEM_TYPE_BUG,
442 UPSTREAM_ITEM_TYPE_MILESTONE,
443 UPSTREAM_ITEM_TYPE_TASK,
444 UPSTREAM_ITEM_TYPE_FILE,
445 )
446 ) ) {
447 throw new UpStream_Import_Exception(
448 sprintf(
449 // translators: %s Item type.
450 __( 'Item type %s is not valid.', 'upstream' ),
451 $item_type
452 )
453 );
454 }
455
456 // TODO: check if field is valid.
457 $s = new stdClass();
458 $s->itemType = $item_type; // phpcs:ignore
459 $s->fieldName = $field_name; // phpcs:ignore
460 }
461
462 $this->columns[] = $s;
463 }
464 }
465
466 }
467
468 /**
469 * Class UpStream_Import_Exception
470 */
471 class UpStream_Import_Exception extends Exception {}
472