PluginProbe ʕ •ᴥ•ʔ
Pods – Custom Content Types and Fields / 3.3.6
Pods – Custom Content Types and Fields v3.3.6
trunk 1.14.8 2.7.31.3 2.8.23.3 2.9.19.3 3.0.10.3 3.1.4.1 3.2.0 3.2.1 3.2.1.1 3.2.2 3.2.4 3.2.5 3.2.6 3.2.7 3.2.7.1 3.2.8 3.2.8.1 3.2.8.2 3.3.0 3.3.1 3.3.2 3.3.3 3.3.4 3.3.5 3.3.6 3.3.7 3.3.8 3.3.9
pods / classes / PodsMigrate.php
pods / classes Last commit date
cli 4 months ago fields 4 months ago widgets 4 months ago Pods.php 4 months ago PodsAPI.php 4 months ago PodsAdmin.php 4 months ago PodsArray.php 4 months ago PodsComponent.php 4 months ago PodsComponents.php 4 months ago PodsData.php 4 months ago PodsField.php 4 months ago PodsForm.php 4 months ago PodsI18n.php 4 months ago PodsInit.php 4 months ago PodsMeta.php 4 months ago PodsMigrate.php 4 months ago PodsRESTFields.php 4 months ago PodsRESTHandlers.php 4 months ago PodsTermSplitting.php 4 months ago PodsUI.php 4 months ago PodsView.php 4 months ago
PodsMigrate.php
1393 lines
1 <?php
2
3 // Don't load directly.
4 if ( ! defined( 'ABSPATH' ) ) {
5 die( '-1' );
6 }
7
8 /**
9 * @package Pods
10 */
11 class PodsMigrate {
12
13 /**
14 * @var null|string
15 */
16 public $type = 'php';
17
18 /**
19 * @var array
20 */
21 public $types = array( 'php', 'json', 'sv', 'xml' );
22
23 /**
24 * @var array
25 */
26 public $mimes = array(
27 'json' => 'application/json',
28 'csv' => 'text/csv',
29 'tsv' => 'text/tsv',
30 'xml' => 'text/xml',
31 );
32
33 /**
34 * @var null|string
35 */
36 public $delimiter = ',';
37
38 /**
39 * @var null
40 */
41 public $data = array(
42 'items' => array(),
43 'columns' => array(),
44 'fields' => array(),
45 'single' => false,
46 );
47
48 /**
49 * @var null
50 */
51 public $input;
52
53 /**
54 * @var
55 */
56 public $parsed;
57
58 /**
59 * @var
60 */
61 public $built;
62
63 /**
64 * Migrate Data to and from Pods
65 *
66 * @param string $type Export Type (php, json, sv, xml)
67 * @param string $delimiter Delimiter for export type 'sv'
68 * @param array $data Array of data settings
69 *
70 * @return \PodsMigrate
71 *
72 * @license http://www.gnu.org/licenses/gpl-2.0.html
73 * @since 2.0.0
74 */
75 public function __construct( $type = null, $delimiter = null, $data = null ) {
76
77 if ( ! empty( $type ) ) {
78 if ( 'csv' === $type ) {
79 $type = 'sv';
80
81 if ( null === $delimiter ) {
82 $delimiter = ',';
83 }
84 } elseif ( 'tsv' === $type ) {
85 $type = 'sv';
86
87 if ( null === $delimiter ) {
88 $delimiter = "\t";
89 }
90 }
91
92 if ( in_array( $type, $this->types, true ) ) {
93 $this->type = $type;
94 }
95 }
96
97 if ( ! empty( $delimiter ) ) {
98 $this->delimiter = $delimiter;
99 }
100
101 if ( ! empty( $data ) ) {
102 $this->set_data( $data );
103 }
104 }
105
106 /**
107 * @param $data
108 */
109 public function set_data( $data ) {
110
111 $defaults = array(
112 'items' => array(),
113 'columns' => array(),
114 'fields' => array(),
115 );
116
117 $this->data = array_merge( $defaults, (array) $data );
118 }
119
120 /**
121 * Get items.
122 *
123 * @since 2.7.17
124 *
125 * @return array List of data items.
126 */
127 private function get_items() {
128
129 return empty( $this->data['single'] ) ?
130 $this->data['items'] :
131 array( $this->data['items'] );
132
133 }
134
135 /**
136 * Importing / Parsing / Validating Code
137 *
138 * @param array $data Array of data
139 * @param string $type Export Type (php, json, sv, xml)
140 * @param string $delimiter Delimiter for export type 'sv'
141 *
142 * @return bool
143 */
144 public function import( $data = null, $type = null, $delimiter = null ) {
145
146 $this->parse( $data, $type, $delimiter );
147
148 return $this->import_pod_items();
149
150 }
151
152 /**
153 * @param array $data Array of data
154 * @param string $type Export Type (php, json, sv, xml)
155 *
156 * @return bool
157 */
158 public function import_pod_items( $data = null, $type = null ) {
159
160 if ( ! empty( $data ) ) {
161 $this->input = $data;
162 }
163
164 if ( ! empty( $type ) && in_array( $type, $this->types, true ) ) {
165 $this->type = $type;
166 }
167
168 return false;
169 }
170
171 /**
172 * @param array $data Array of data
173 * @param string $type Parse Type (php, json, sv, xml)
174 * @param string $delimiter Delimiter for export type 'sv'
175 *
176 * @return null
177 */
178 public function parse( $data = null, $type = null, $delimiter = null ) {
179
180 if ( ! empty( $data ) ) {
181 $this->input = $data;
182 }
183
184 if ( ! empty( $type ) && in_array( $type, $this->types, true ) ) {
185 $this->type = $type;
186 }
187
188 if ( !empty( $delimiter ) )
189 $this->delimiter = $delimiter;
190
191 if ( method_exists( $this, "parse_{$this->type}" ) ) {
192 return call_user_func( array( $this, 'parse_' . $this->type ) );
193 }
194
195 return $this->parsed;
196 }
197
198 /**
199 * @param array $data Array of data
200 *
201 * @return bool
202 */
203 public function parse_json( $data = null ) {
204
205 if ( ! empty( $data ) ) {
206 $this->input = $data;
207 }
208
209 $items = @json_decode( $this->input, true );
210
211 if ( ! is_array( $items ) ) {
212 return false;
213 }
214
215 // Only export to a basic object if building for a single item.
216 if ( ! empty( $this->data['single'] ) ) {
217 $data = $items;
218 } else {
219 $data = array(
220 'columns' => array(),
221 'items' => array(),
222 'fields' => array(),
223 );
224
225 foreach ( $items as $key => $item ) {
226 if ( ! is_array( $item ) ) {
227 continue;
228 }
229
230 foreach ( $item as $column => $value ) {
231 if ( ! in_array( $column, $data['columns'], true ) ) {
232 $data['columns'][] = $column;
233 }
234 }
235
236 $data['items'][ $key ] = $item;
237 }
238 }
239
240 $this->parsed = $data;
241
242 return $this->parsed;
243 }
244
245 /**
246 * @param array $data Array of data
247 * @param string $delimiter Delimiter for export type 'sv'
248 *
249 * @return bool
250 */
251 public function parse_sv( $data = null, $delimiter = null ) {
252
253 if ( ! empty( $data ) ) {
254 $this->input = $data;
255 }
256
257 if ( ! empty( $delimiter ) ) {
258 $this->delimiter = $delimiter;
259 }
260
261 $rows = $this->str_getcsv( $this->input, '\n' );
262
263 if ( empty( $rows ) || 2 > count( $rows ) ) {
264 return false;
265 }
266
267 $data = array(
268 'columns' => array(),
269 'items' => array(),
270 );
271
272 foreach ( $rows as $key => $row ) {
273 if ( 0 === $key ) {
274 $data['columns'] = $this->str_getcsv( $row, $this->delimiter );
275 } else {
276 $row = $this->str_getcsv( $row, $this->delimiter );
277
278 $data['items'][ $key ] = array();
279
280 foreach ( $data['columns'] as $ckey => $column ) {
281 $column_value = ( isset( $row[ $ckey ] ) ? $row[ $ckey ] : '' );
282
283 if ( 'NULL' === $column_value ) {
284 // Maybe set the value as null.
285 $column_value = null;
286 } elseif (
287 0 === strpos( $column_value, '\\=' )
288 || 0 === strpos( $column_value, '\\+' )
289 || 0 === strpos( $column_value, '\\-' )
290 || 0 === strpos( $column_value, '\\@' )
291 ) {
292 // Maybe remove the first backslash.
293 $column_value = substr( $column_value, 1 );
294 }
295
296 $data['items'][ $key ][ $column ] = $column_value;
297 }
298 }
299 }
300
301 $this->parsed = $data;
302
303 return $this->parsed;
304 }
305
306 /**
307 * Handle str_getcsv for cases where it's not set
308 *
309 * @param $line
310 * @param string $delimiter
311 * @param string $enclosure
312 * @param string $escape
313 *
314 * @return array|mixed
315 */
316 public function str_getcsv( $line, $delimiter = ',', $enclosure = '"', $escape = '\\' ) {
317
318 $line = str_replace( "\r\n", "\n", $line );
319 $line = str_replace( "\r", "\n", $line );
320
321 if ( '\n' !== $delimiter && function_exists( 'str_getcsv' ) ) {
322 return str_getcsv( $line, $delimiter, $enclosure, $escape );
323 }
324
325 $delimiter = str_replace( '/', '\/', $delimiter );
326 $enclosure = preg_quote( $enclosure, '/' );
327
328 $split = "/{$delimiter}(?=(?:[^{$enclosure}]*{$enclosure}[^{$enclosure}]*{$enclosure})*(?![^{$enclosure}]*{$enclosure}))/";
329
330 $data = preg_split( $split, trim( $line ), - 1, PREG_SPLIT_NO_EMPTY );
331
332 if ( '\n' !== $delimiter ) {
333 $data = preg_replace( "/^{$enclosure}(.*){$enclosure}$/s", '$1', $data );
334 }
335
336 return $data;
337 }
338
339 /**
340 * @param array $data Array of data
341 *
342 * @return bool
343 */
344 public function parse_xml( $data = null ) {
345
346 if ( ! empty( $data ) ) {
347 $this->input = $data;
348 }
349
350 $xml = new SimpleXMLElement( $this->input );
351
352 if ( ! isset( $xml->items ) ) {
353 return false;
354 }
355
356 $data = array(
357 'columns' => array(),
358 'items' => array(),
359 );
360
361 /**
362 * @var $child SimpleXMLElement
363 * @var $item_child SimpleXMLElement
364 * @var $data_child SimpleXMLElement
365 */
366
367 if ( isset( $xml->columns ) ) {
368 foreach ( $xml->columns->children() as $child ) {
369 $sub = $child->getName();
370
371 if ( empty( $sub ) || 'column' !== $sub ) {
372 continue;
373 }
374
375 if ( isset( $child->name ) ) {
376 if ( is_array( $child->name ) ) {
377 $column = $child->name[0];
378 } else {
379 $column = $child->name;
380 }
381
382 $data['columns'][] = $column;
383 }
384 }
385 }
386
387 foreach ( $xml->items->children() as $child ) {
388 $sub = $child->getName();
389
390 if ( empty( $sub ) || 'item' !== $sub ) {
391 continue;
392 }
393
394 $item = array();
395
396 $attributes = $child->attributes();
397
398 if ( ! empty( $attributes ) ) {
399 foreach ( $attributes as $column => $value ) {
400 if ( ! in_array( $column, $data['columns'], true ) ) {
401 $data['columns'][] = $column;
402 }
403
404 $item[ $column ] = $value;
405 }
406 }
407
408 $item_child = $child->children();
409
410 if ( ! empty( $item_child ) ) {
411 foreach ( $item_child->children() as $data_child ) {
412 $column = $data_child->getName();
413
414 if ( ! in_array( $column, $data['columns'], true ) ) {
415 $data['columns'][] = $column;
416 }
417
418 $item[ $column ] = $item_child->$column;
419 }
420 }
421
422 if ( ! empty( $item ) ) {
423 $data['items'][] = $item;
424 }
425 }//end foreach
426
427 $this->parsed = $data;
428
429 return $this->parsed;
430 }
431
432 /**
433 * @param array $data Array of data
434 *
435 * @return mixed
436 *
437 * @todo For much much later
438 */
439 public function parse_sql( $data = null ) {
440
441 if ( ! empty( $data ) ) {
442 $this->input = $data;
443 }
444
445 $this->parsed = $data;
446
447 return $this->parsed;
448 }
449
450 /**
451 * Exporting / Building Code
452 *
453 * @param array $data Array of data
454 * @param string $type Export Type (php, json, sv, xml)
455 * @param string $delimiter Delimiter for export type 'sv'
456 *
457 * @return mixed
458 */
459 public function export( $data = null, $type = null, $delimiter = null ) {
460
461 if ( ! empty( $data ) ) {
462 $this->set_data( $data );
463 }
464
465 if ( ! empty( $type ) && in_array( $type, $this->types, true ) ) {
466 $this->type = $type;
467 }
468
469 if ( ! empty( $delimiter ) ) {
470 $this->delimiter = $delimiter;
471 }
472
473 if ( method_exists( $this, "build_{$this->type}" ) ) {
474 call_user_func( array( $this, 'build_' . $this->type ) );
475 }
476
477 return $this->built;
478 }
479
480 /**
481 * @param array $data Array of data
482 */
483 public function export_pod_items( $data = null ) {
484
485 if ( ! empty( $data ) ) {
486 $this->set_data( $data );
487 }
488 }
489
490 /**
491 * @param array $data Array of data
492 * @param string $type Export Type (php, json, sv, xml)
493 *
494 * @return null
495 */
496 public function build( $data = null, $type = null ) {
497
498 if ( ! empty( $data ) ) {
499 $this->set_data( $data );
500 }
501
502 if ( ! empty( $type ) && in_array( $type, $this->types, true ) ) {
503 $this->type = $type;
504 }
505
506 if ( method_exists( $this, "build_{$this->type}" ) ) {
507 call_user_func( array( $this, 'build_' . $this->type ) );
508 }
509
510 return $this->data;
511 }
512
513 /**
514 * @param array $data Array of data
515 *
516 * @return bool
517 */
518 public function build_json( $data = null ) {
519
520 if ( ! empty( $data ) ) {
521 $this->set_data( $data );
522 }
523
524 if ( empty( $this->data ) || ! is_array( $this->data ) ) {
525 return false;
526 }
527
528 // Only export to a basic object if building for a single item.
529 if ( ! empty( $this->data['single'] ) ) {
530 $data = $this->data['items'];
531 } else {
532 $data = array(
533 'items' => array(
534 'count' => count( $this->data['items'] ),
535 'item' => array(),
536 ),
537 );
538
539 foreach ( $this->data['items'] as $item ) {
540 $row = array();
541
542 foreach ( $this->data['columns'] as $column => $label ) {
543 if ( is_numeric( $column ) && ( ( is_object( $item ) && ! isset( $item->$column ) ) || ( is_array( $item ) && ! isset( $item[ $column ] ) ) ) ) {
544 $column = $label;
545 }
546
547 $value = '';
548
549 if ( is_object( $item ) ) {
550 if ( ! isset( $item->$column ) ) {
551 $item->$column = '';
552 }
553
554 $value = $item->$column;
555 } elseif ( is_array( $item ) ) {
556 if ( ! isset( $item[ $column ] ) ) {
557 $item[ $column ] = '';
558 }
559
560 $value = $item[ $column ];
561 }
562
563 $row[ $column ] = $value;
564 }//end foreach
565
566 $data['items']['item'][] = $row;
567 }//end foreach
568 }
569
570 $this->built = @json_encode( $data );
571
572 return $this->built;
573 }
574
575 /**
576 * @param array $data Array of data
577 * @param string $delimiter Delimiter for export type 'sv'
578 *
579 * @return bool
580 */
581 public function build_sv( $data = null, $delimiter = null ) {
582
583 if ( ! empty( $data ) ) {
584 $this->set_data( $data );
585 }
586
587 if ( ! empty( $delimiter ) ) {
588 $this->delimiter = $delimiter;
589 }
590
591 if ( empty( $this->data ) || ! is_array( $this->data ) ) {
592 return false;
593 }
594
595 $head = '';
596 $lines = '';
597
598 foreach ( $this->data['columns'] as $column => $label ) {
599 $head .= '"' . $label . '"' . $this->delimiter;
600 }
601
602 $head = substr( $head, 0, - 1 );
603
604 $items = $this->get_items();
605
606 foreach ( $items as $item ) {
607 $line = '';
608
609 foreach ( $this->data['columns'] as $column => $label ) {
610 if ( is_numeric( $column ) && ( ( is_object( $item ) && ! isset( $item->$column ) ) || ( is_array( $item ) && ! isset( $item[ $column ] ) ) ) ) {
611 $column = $label;
612 }
613
614 $value = '';
615
616 if ( is_object( $item ) ) {
617 if ( ! isset( $item->$column ) ) {
618 $item->$column = '';
619 }
620
621 $value = $item->$column;
622 } elseif ( is_array( $item ) ) {
623 if ( ! isset( $item[ $column ] ) ) {
624 $item[ $column ] = '';
625 }
626
627 $value = $item[ $column ];
628 }
629
630 if ( is_array( $value ) || is_object( $value ) ) {
631 $value = pods_serial_comma(
632 $value, array(
633 'field' => $column,
634 'fields' => pods_var_raw( $column, $this->data['fields'] ),
635 'and' => '',
636 )
637 );
638 }
639
640 $value = str_replace( array( '"', "\r\n", "\r", "\n" ), array( '\\"', "\n", "\n", '\n' ), $value );
641
642 // Maybe escape the first character to prevent formulas from getting used when opening the file with a spreadsheet app.
643 if (
644 0 === strpos( $value, '=' )
645 || 0 === strpos( $value, '+' )
646 || 0 === strpos( $value, '-' )
647 || 0 === strpos( $value, '@' )
648 ) {
649 $value = '\\' . $value;
650 }
651
652 $line .= '"' . $value . '"' . $this->delimiter;
653 }//end foreach
654
655 $lines .= substr( $line, 0, - 1 ) . "\n";
656 }//end foreach
657
658 if ( ! empty( $lines ) ) {
659 $lines = "\n" . substr( $lines, 0, - 1 );
660 }
661
662 $this->built = $head . $lines;
663
664 return $this->built;
665 }
666
667 /**
668 * @param array $data Array of data
669 *
670 * @return bool
671 */
672 public function build_xml( $data = null ) {
673
674 if ( ! empty( $data ) ) {
675 $this->set_data( $data );
676 }
677
678 if ( empty( $this->data ) || ! is_array( $this->data ) ) {
679 return false;
680 }
681
682 $items = $this->get_items();
683
684 $head = '<' . '?' . 'xml version="1.0" encoding="utf-8" ' . '?' . '>' . "\r\n<items count=\"" . count( $items ) . "\">\r\n";
685 $lines = '';
686
687 foreach ( $items as $item ) {
688 $line = "\t<item>\r\n";
689
690 foreach ( $this->data['columns'] as $column => $label ) {
691 if ( is_numeric( $column ) && ( ( is_object( $item ) && ! isset( $item->$column ) ) || ( is_array( $item ) && ! isset( $item[ $column ] ) ) ) ) {
692 $column = $label;
693 }
694
695 $line .= $this->build_xml_level( $item, $column );
696 }
697
698 $line .= "\t</item>\r\n";
699 $lines .= $line;
700 }
701
702 $foot = '</items>';
703
704 $this->built = $head . $lines . $foot;
705
706 return $this->built;
707 }
708
709 /**
710 * @param array|object $item
711 * @param string $column
712 * @param int $level
713 * @param string $column_name
714 *
715 * @return string
716 */
717 public function build_xml_level( $item, $column, $level = 2, $column_name = '' ) {
718
719 $column = pods_clean_name( $column, false, false );
720
721 $line = '';
722
723 $value = '';
724
725 if ( is_object( $item ) ) {
726 if ( ! isset( $item->$column ) ) {
727 $item->$column = '';
728 }
729
730 $value = $item->$column;
731 } elseif ( is_array( $item ) ) {
732 if ( ! isset( $item[ $column ] ) ) {
733 $item[ $column ] = '';
734 }
735
736 $value = $item[ $column ];
737 }
738
739 if ( ! empty( $column_name ) ) {
740 $column = $column_name;
741 }
742
743 $tabs = str_repeat( "\t", $level );
744
745 $line .= $tabs . "<{$column}>";
746
747 if ( is_array( $value ) || is_object( $value ) ) {
748 if ( is_object( $value ) ) {
749 $value = get_object_vars( $value );
750 }
751
752 foreach ( $value as $k => $v ) {
753 if ( is_int( $k ) ) {
754 $line .= $this->build_xml_level( $value, $k, $level + 1, 'value' );
755 } else {
756 $line .= $this->build_xml_level( $value, $k, $level + 1 );
757 }
758 }
759 } elseif ( false !== strpos( $value, '<' ) ) {
760 $value = str_replace( array( '<![CDATA[', ']]>' ), array( '&lt;![CDATA[', ']]&gt;' ), $value );
761
762 $line .= '<![CDATA[' . $value . ']]>';
763 } else {
764 $line .= str_replace( '&', '&amp;', $value );
765 }
766
767 $line .= "</{$column}>\r\n";
768
769 return $line;
770 }
771
772 /**
773 * @param array $data Array of data
774 *
775 * @return mixed
776 */
777 public function build_sql( $data = null ) {
778
779 if ( ! empty( $data ) ) {
780 $this->set_data( $data );
781 }
782
783 $this->built = $data;
784
785 return $this->built;
786 }
787
788 /**
789 * Save export to a file.
790 *
791 * @param array $params Additional options for saving.
792 *
793 * @return string The URL of the saved file, a path if not attached.
794 */
795 public function save( $params = array() ) {
796
797 $defaults = array(
798 'file' => null,
799 'path' => null,
800 'attach' => false,
801 );
802
803 $params = array_merge( $defaults, $params );
804
805 $extension = 'txt';
806
807 if ( ! empty( $params['file'] ) ) {
808 $export_file = $params['file'];
809
810 if ( false !== strpos( $export_file, '.' ) ) {
811 $extension = explode( '.', $export_file );
812 $extension = end( $extension );
813 }
814 } else {
815 if ( 'sv' === $this->type ) {
816 if ( ',' === $this->delimiter ) {
817 $extension = 'csv';
818 } elseif ( "\t" === $this->delimiter ) {
819 $extension = 'tsv';
820 }
821 } else {
822 $extension = $this->type;
823 }
824
825 $export_file = sprintf(
826 'pods_export_%s.%s',
827 wp_create_nonce( date_i18n( 'm-d-Y_h-i-sa' ) ),
828 $extension
829 );
830 }
831
832 if ( ! empty( $params['path'] ) ) {
833 $new_file = sprintf(
834 '%s/%s',
835 untrailingslashit( $params['path'] ),
836 $export_file
837 );
838
839 $filename = $export_file;
840 } else {
841 $uploads = wp_upload_dir( current_time( 'mysql' ) );
842
843 if ( ! $uploads || false !== $uploads['error'] ) {
844 return pods_error( __( 'There was an issue saving the export file in your uploads folder.', 'pods' ), true );
845 }
846
847 // Generate unique file name
848 $filename = wp_unique_filename( $uploads['path'], $export_file );
849
850 // move the file to the uploads dir
851 $new_file = $uploads['path'] . '/' . $filename;
852 }
853
854 file_put_contents( $new_file, $this->built );
855
856 // Set correct file permissions
857 $stat = stat( dirname( $new_file ) );
858 $perms = $stat['mode'] & 0000666;
859
860 require_once ABSPATH . '/wp-admin/includes/file.php';
861 WP_Filesystem();
862
863 /** @var WP_Filesystem_Base $wp_filesystem */
864 global $wp_filesystem;
865
866 $wp_filesystem->chmod( $new_file, $perms );
867
868 // Only attach if we want to and don't have a custom path.
869 if ( $params['attach'] && empty( $params['path'] ) ) {
870 // Get the file type
871 $wp_filetype = wp_check_filetype( $filename, $this->mimes );
872
873 // construct the attachment array
874 $attachment = array(
875 'post_mime_type' => 'text/' . $extension,
876 'guid' => $uploads['url'] . '/' . $filename,
877 'post_parent' => null,
878 'post_title' => 'Pods Export (' . $export_file . ')',
879 'post_content' => '',
880 'post_status' => 'private'
881 );
882
883 if ( $wp_filetype['type'] ) {
884 $attachment['post_mime_type'] = $wp_filetype['type'];
885 }
886
887 // insert attachment
888 /* @var int|WP_Error $attachment_id */
889 $attachment_id = wp_insert_attachment( $attachment, $new_file );
890
891 // error!
892 if ( $attachment_id instanceof WP_Error ) {
893 return pods_error( __( 'There was an issue saving the export file in your uploads folder.', 'pods' ), true );
894 }
895
896 $url = $attachment['guid'];
897 } else {
898 $url = $new_file;
899 }
900
901 return $url;
902
903 }
904
905 /*
906 The real enchilada!
907
908 EXAMPLES
909 //// minimal import (if your fields match on both your pods and tables)
910 $import = array('my_pod' => array('table' => 'my_table')); // if your table name doesn't match the pod name
911 $import = array('my_pod'); // if your table name matches your pod name
912
913 //// advanced import
914 $import = array();
915 $import['my_pod'] = array();
916 $import['my_pod']['fields']['this_field'] = 'field_name_in_table'; // if the field name doesn't match on table and pod
917 $import['my_pod']['fields'][] = 'that_field'; // if the field name matches on table and pod
918 $import['my_pod']['fields']['this_other_field'] = array('filter' => 'wpautop'); // if you want the value to be different than is provided, set a filter function to use [filter uses = filter_name($value,$rowdata)]
919 $import['my_pod']['fields']['another_field'] = array('field' => 'the_real_field_in_table','filter' => 'my_custom_function'); // if you want the value to be filtered, and the field name doesn't match on the table and pod
920 $import[] = 'my_other_pod'; // if your table name matches your pod name
921 $import['another_pod'] = array('update_on' => 'main_field'); // you can update a pod item if the value of this field is the same on both tables
922 $import['another_pod'] = array('reset' => true); // you can choose to reset all data in a pod before importing
923
924 //// run import
925 pods_import_it($import);
926 */
927 /**
928 * @param $import
929 * @param bool $output
930 */
931 public function heres_the_beef( $import, $output = true ) {
932
933 global $wpdb;
934
935 $api = pods_api();
936
937 for ( $i = 0; $i < 40000; $i ++ ) {
938 echo " \t";
939 // extra spaces
940 }
941
942 $default_data = array(
943 'pod' => null,
944 'table' => null,
945 'reset' => null,
946 'update_on' => null,
947 'where' => null,
948 'fields' => array(),
949 'row_filter' => null,
950 'pre_save' => null,
951 'post_save' => null,
952 'sql' => null,
953 'sort' => null,
954 'limit' => null,
955 'page' => null,
956 'output' => null,
957 'page_var' => 'ipg',
958 'bypass_helpers' => false,
959 );
960
961 $default_field_data = array(
962 'field' => null,
963 'filter' => null,
964 );
965
966 if ( ! is_array( $import ) ) {
967 $import = array( $import );
968 } elseif ( empty( $import ) ) {
969 die( '<h1 style="color:red;font-weight:bold;">ERROR: No imports configured</h1>' );
970 }
971
972 $import_counter = 0;
973 $total_imports = count( $import );
974 $paginated = false;
975 $avg_time = - 1;
976 $total_time = 0;
977 $counter = 0;
978 $avg_unit = 100;
979 $avg_counter = 0;
980
981 foreach ( $import as $datatype => $data ) {
982 $import_counter ++;
983
984 flush();
985 @ob_end_flush();
986 usleep( 50000 );
987
988 if ( ! is_array( $data ) ) {
989 $datatype = $data;
990 $data = array( 'table' => $data );
991 }
992
993 if ( isset( $data[0] ) ) {
994 $data = array( 'table' => $data[0] );
995 }
996
997 $data = array_merge( $default_data, $data );
998
999 $data['limit'] = absint( $data['limit'] );
1000
1001 if ( null === $data['pod'] ) {
1002 $data['pod'] = array( 'name' => $datatype );
1003 }
1004
1005 if ( false !== $output ) {
1006 echo '<br />' . esc_html( date_i18n( 'Y-m-d h:i:sa' ) ) . ' - <em>' . esc_html( $data['pod']['name'] ) . '</em> - <em>' . esc_html( $data['pod']['name'] ) . '</em> - <strong>Loading Pod: ' . esc_html( $data['pod']['name'] ) . "</strong>\n";
1007 }
1008
1009 if ( 2 > count( $data['pod'] ) ) {
1010 $data['pod'] = $api->load_pod( array( 'name' => $data['pod']['name'] ) );
1011 }
1012
1013 if ( empty( $data['pod']['fields'] ) ) {
1014 continue;
1015 }
1016
1017 if ( null === $data['table'] ) {
1018 $data['table'] = $data['pod']['name'];
1019 }
1020
1021 if ( $data['reset'] === true ) {
1022 if ( false !== $output ) {
1023 echo '<br />' . esc_html( date_i18n( 'Y-m-d h:i:sa' ) ) . ' - <em>' . esc_html( $data['pod']['name'] ) . "</em> - <strong style='color:blue;'>Resetting Pod: " . esc_html( $data['pod']['name'] ) . "</strong>\n";
1024 }
1025
1026 $api->reset_pod(
1027 array(
1028 'id' => $data['pod']['id'],
1029 'name' => $data['pod']['name'],
1030 )
1031 );
1032 }
1033
1034 if ( null === $data['sort'] && null !== $data['update_on'] && isset( $data['fields'][ $data['update_on'] ] ) ) {
1035 if ( isset( $data['fields'][ $data['update_on'] ]['field'] ) ) {
1036 $data['sort'] = $data['fields'][ $data['update_on'] ]['field'];
1037 } else {
1038 $data['sort'] = $data['update_on'];
1039 }
1040 }
1041
1042 $page = 1;
1043
1044 if ( false !== $data['page_var'] && 0 < (int) pods_v( $data['page_var'] ) ) {
1045 $page = (int) pods_v( $data['page_var'] );
1046 }
1047
1048 if ( null === $data['sql'] ) {
1049 $data['sql'] = "SELECT * FROM {$data['table']}" . ( null !== $data['where'] ? " WHERE {$data['where']}" : '' ) . ( null !== $data['sort'] ? " ORDER BY {$data['sort']}" : '' ) . ( null !== $data['limit'] ? ' LIMIT ' . ( 1 < $page ? ( ( $page - 1 ) * $data['limit'] ) . ',' : '' ) . "{$data['limit']}" : '' );
1050 }
1051
1052 if ( false !== $output ) {
1053 echo '<br />' . esc_html( date_i18n( 'Y-m-d h:i:sa' ) ) . ' - <em>' . esc_html( $data['pod']['name'] ) . '</em> - Getting Results: ' . esc_html( $data['pod']['name'] ) . "\n";
1054 }
1055
1056 if ( false !== $output ) {
1057 echo '<br />' . esc_html( date_i18n( 'Y-m-d h:i:sa' ) ) . ' - <em>' . esc_html( $data['pod']['name'] ) . '</em> - Using Query: <small><code>' . esc_html( $data['sql'] ) . "</code></small>\n";
1058 }
1059
1060 $result = $wpdb->get_results( // phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter
1061 $data['sql'], // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
1062 ARRAY_A
1063 );
1064
1065 if ( false !== $output ) {
1066 echo '<br />' . esc_html( date_i18n( 'Y-m-d h:i:sa' ) ) . ' - <em>' . esc_html( $data['pod']['name'] ) . '</em> - Results Found: ' . count( $result ) . "\n";
1067 }
1068
1069 $avg_time = - 1;
1070 $total_time = 0;
1071 $counter = 0;
1072 $avg_unit = 100;
1073 $avg_counter = 0;
1074 $result_count = count( $result );
1075 $paginated = false;
1076
1077 if ( false !== $data['page_var'] && $result_count === $data['limit'] ) {
1078 $paginated = "<input type=\"button\" onclick=\"document.location=\'" . esc_js( add_query_arg( [ $data['page_var'] => $page + 1 ] ) ) . "\';\" value=\" Continue Import &raquo; \" />";
1079 }
1080
1081 if ( $result_count < $avg_unit && 5 < $result_count ) {
1082 $avg_unit = number_format( $result_count / 5, 0, '', '' );
1083 } elseif ( 2000 < $result_count && 10 < count( $data['pod']['fields'] ) ) {
1084 $avg_unit = 40;
1085 }
1086
1087 $data['count'] = $result_count;
1088 timer_start();
1089
1090 if ( false !== $output && 1 === $import_counter ) {
1091 echo "<div style='width:50%;background-color:navy;padding:10px 10px 30px 10px;color:#FFF;position:absolute;top:10px;left:25%;text-align:center;'><p id='progress_status' align='center'>" . esc_html( date_i18n( 'Y-m-d h:i:sa' ) ) . ' - <em>' . esc_html( $data['pod']['name'] ) . '</em> - Running Importer..</p><br /><small>This will automatically update every ' . esc_html( $avg_unit ) . " rows</small></div>\n";
1092 }
1093
1094 foreach ( $result as $k => $row ) {
1095 flush();
1096 @ob_end_flush();
1097 usleep( 50000 );
1098
1099 if ( false !== $output ) {
1100 echo '<br />' . esc_html( date_i18n( 'Y-m-d h:i:sa' ) ) . ' - <em>' . esc_html( $data['pod']['name'] ) . '</em> - Processing Row #' . esc_html( $k + 1 ) . "\n";
1101 }
1102
1103 if ( null !== $data['row_filter'] && function_exists( $data['row_filter'] ) ) {
1104 if ( false !== $output ) {
1105 echo '<br />' . esc_html( date_i18n( 'Y-m-d h:i:sa' ) ) . ' - <em>' . esc_html( $data['pod']['name'] ) . '</em> - Filtering <strong>' . esc_html( $data['row_filter'] ) . '</strong> on Row #' . esc_html( $k + 1 ) . "\n";
1106 }
1107
1108 $row = $data['row_filter']( $row, $data );
1109 }
1110
1111 if ( ! is_array( $row ) ) {
1112 continue;
1113 }
1114
1115 $params = array(
1116 'datatype' => $data['pod']['name'],
1117 'columns' => array(),
1118 'bypass_helpers' => $data['bypass_helpers'],
1119 );
1120
1121 foreach ( $data['pod']['fields'] as $fk => $field_info ) {
1122 $field = $field_info['name'];
1123
1124 if ( ! empty( $data['fields'] ) && ! isset( $data['fields'][ $field ] ) && ! in_array( $field, $data['fields'], true ) ) {
1125 continue;
1126 }
1127
1128 if ( isset( $data['fields'][ $field ] ) ) {
1129 if ( is_array( $data['fields'][ $field ] ) ) {
1130 $field_data = $data['fields'][ $field ];
1131 } else {
1132 $field_data = array( 'field' => $data['fields'][ $field ] );
1133 }
1134 } else {
1135 $field_data = array();
1136 }
1137
1138 if ( ! is_array( $field_data ) ) {
1139 $field = $field_data;
1140 $field_data = array();
1141 }
1142
1143 $field_data = pods_config_merge_data( $default_field_data, $field_data );
1144
1145 if ( null === $field_data['field'] ) {
1146 $field_data['field'] = $field;
1147 }
1148
1149 $data['fields'][ $field ] = $field_data;
1150 $value = '';
1151
1152 if ( isset( $row[ $field_data['field'] ] ) ) {
1153 $value = $row[ $field_data['field'] ];
1154 }
1155
1156 if ( null !== $field_data['filter'] ) {
1157 if ( function_exists( $field_data['filter'] ) ) {
1158 if ( false !== $output ) {
1159 echo '<br />' . esc_html( date_i18n( 'Y-m-d h:i:sa' ) ) . ' - <em>' . esc_html( $data['pod']['name'] ) . '</em> - Filtering <strong>' . esc_html( $field_data['filter'] ) . '</strong> on Field: ' . esc_html( $field ) . "\n";
1160 }
1161
1162 $value = $field_data['filter']( $value, $row, $data );
1163 } else {
1164 $value = '';
1165 }
1166 }
1167
1168 if ( 1 === $field_info['required'] && ( ! is_string( $value ) || strlen( $value ) < 1 ) ) {
1169 die( '<h1 style="color:red;font-weight:bold;">ERROR: Field Required for <strong>' . esc_html( $field ) . '</strong></h1>' );
1170 }
1171
1172 $params['columns'][ $field ] = $value;
1173
1174 unset( $value, $field_data, $field_info, $fk );
1175 }//end foreach
1176
1177 if ( empty( $params['columns'] ) ) {
1178 continue;
1179 }
1180
1181 $params['columns'] = pods_sanitize( $params['columns'] );
1182
1183 if ( null !== $data['update_on'] && isset( $params['columns'][ $data['update_on'] ] ) ) {
1184 if ( false !== $output ) {
1185 echo '<br />' . esc_html( date_i18n( 'Y-m-d h:i:sa' ) ) . ' - <em>' . esc_html( $data['pod']['name'] ) . "</em> - Checking for Existing Item\n";
1186 }
1187
1188 $check = pods( $data['pod']['name'] );
1189 $check->find(
1190 [
1191 'orderby' => 't.id',
1192 'limit' => 1,
1193 'where' => "t.{$data['update_on']} = '{$params['columns'][$data['update_on']]}'",
1194 'search' => false,
1195 'page' => 1,
1196 ]
1197 );
1198
1199 if ( $check->fetch() ) {
1200 $params['id'] = $check->id();
1201 $params['pod'] = $check->pod;
1202 $params['pod_id'] = $check->pod_id;
1203
1204 if ( false !== $output ) {
1205 echo '<br />' . esc_html( date_i18n( 'Y-m-d h:i:sa' ) ) . ' - <em>' . esc_html( $data['pod']['name'] ) . '</em> - Found Existing Item w/ ID: ' . esc_html( $params['id'] ) . "\n";
1206 }
1207
1208 unset( $check );
1209 }
1210
1211 if ( ! isset( $params['id'] ) && false !== $output ) {
1212 echo '<br />' . esc_html( date_i18n( 'Y-m-d h:i:sa' ) ) . ' - <em>' . esc_html( $data['pod']['name'] ) . "</em> - Existing item not found - Creating New\n";
1213 }
1214 }//end if
1215
1216 if ( null !== $data['pre_save'] && function_exists( $data['pre_save'] ) ) {
1217 if ( false !== $output ) {
1218 echo '<br />' . esc_html( date_i18n( 'Y-m-d h:i:sa' ) ) . ' - <em>' . esc_html( $data['pod']['name'] ) . '</em> - Running Pre Save <strong>' . esc_html( $data['pre_save'] ) . '</strong> on ' . esc_html( $data['pod']['name'] ) . "\n";
1219 }
1220
1221 $params = $data['pre_save']( $params, $row, $data );
1222 }
1223
1224 $id = $api->save_pod_item( $params );
1225
1226 if ( false !== $output ) {
1227 echo '<br />' . esc_html( date_i18n( 'Y-m-d h:i:sa' ) ) . ' - <em>' . esc_html( $data['pod']['name'] ) . '</em> - <strong>Saved Row #' . esc_html( $k + 1 ) . ' w/ ID: ' . esc_html( $id ) . "</strong>\n";
1228 }
1229
1230 $params['id'] = $id;
1231
1232 if ( null !== $data['post_save'] && function_exists( $data['post_save'] ) ) {
1233 if ( false !== $output ) {
1234 echo '<br />' . esc_html( date_i18n( 'Y-m-d h:i:sa' ) ) . ' - <em>' . esc_html( $data['pod']['name'] ) . '</em> - Running Post Save <strong>' . esc_html( $data['post_save'] ) . '</strong> on ' . esc_html( $data['pod']['name'] ) . "\n";
1235 }
1236
1237 $data['post_save']( $params, $row, $data );
1238 }
1239
1240 unset( $params, $result[ $k ], $row );
1241
1242 wp_cache_flush();
1243 $wpdb->queries = array();
1244
1245 $avg_counter ++;
1246 $counter ++;
1247
1248 if ( $avg_counter === $avg_unit && false !== $output ) {
1249 $avg_counter = 0;
1250 $avg_time = timer_stop( 0, 10 );
1251 $total_time += $avg_time;
1252 $rows_left = $result_count - $counter;
1253 $estimated_time_left = ( ( $total_time / $counter ) * $rows_left ) / 60;
1254 $percent_complete = 100 - ( ( $rows_left * 100 ) / $result_count );
1255
1256 echo "<script type='text/javascript'>document.getElementById('progress_status').innerHTML = '" . esc_js( date_i18n( 'Y-m-d h:i:sa' ) ) . ' - <em>' . esc_js( $data['pod']['name'] ) . '</em><br /><strong>' . esc_js( $percent_complete ) . '% Complete</strong><br /><strong>Estimated Time Left:</strong> ' . esc_js( $estimated_time_left ) . ' minute(s) or ' . esc_js( $estimated_time_left / 60 ) . ' hours(s)<br /><strong>Time Spent:</strong> ' . esc_js( $total_time / 60 ) . ' minute(s)<br /><strong>Rows Done:</strong> ' . esc_js( $result_count - $rows_left ) . '/' . esc_js( $result_count ) . '<br /><strong>Rows Left:</strong> ' . esc_js( $rows_left ) . "';</script>\n";
1257 echo '<br />' . esc_html( date_i18n( 'Y-m-d h:i:sa' ) ) . ' - <em>' . esc_html( $data['pod']['name'] ) . '</em> - <strong>Updated Status:</strong> ' . esc_html( $percent_complete ) . "% Complete</strong>\n";
1258 }
1259 }//end foreach
1260
1261 if ( false !== $output ) {
1262 $avg_counter = 0;
1263 $avg_time = timer_stop( 0, 10 );
1264 $total_time += $avg_time;
1265 $rows_left = $result_count - $counter;
1266 $estimated_time_left = ( ( $total_time / $counter ) * $rows_left ) / 60;
1267 $percent_complete = 100 - ( ( $rows_left * 100 ) / $result_count );
1268
1269 echo "<script type='text/javascript'>document.getElementById('progress_status').innerHTML = '" . esc_js( date_i18n( 'Y-m-d h:i:sa' ) ) . ' - <em>' . esc_html( $data['pod']['name'] ) . "</em><br /><strong style=\'color:green;\'>100% Complete</strong><br /><br /><strong>Time Spent:</strong> " . esc_html( $total_time / 60 ) . ' minute(s)<br /><strong>Rows Imported:</strong> ' . esc_html( $result_count ) . ( false !== $paginated ? '<br /><br />' . wp_kses_post( $paginated ) : '' ) . "';</script>\n";
1270 echo '<br />' . esc_html( date_i18n( 'Y-m-d h:i:sa' ) ) . ' - <em>' . esc_html( $data['pod']['name'] ) . "</em> - <strong style='color:green;'>Done Importing: " . esc_html( $data['pod']['name'] ) . "</strong>\n";
1271 }
1272
1273 unset( $result, $import[ $datatype ], $datatype, $data );
1274
1275 wp_cache_flush();
1276 $wpdb->queries = array();
1277 }//end foreach
1278
1279 if ( false !== $output ) {
1280 $avg_counter = 0;
1281 $avg_time = timer_stop( 0, 10 );
1282 $total_time += $avg_time;
1283 $rows_left = $result_count - $counter;
1284
1285 echo "<script type='text/javascript'>document.getElementById('progress_status').innerHTML = '" . esc_js( date_i18n( 'Y-m-d h:i:sa' ) ) . " - <strong style=\'color:green;\'>Import Complete</strong><br /><br /><strong>Time Spent:</strong> " . esc_html( $total_time / 60 ) . ' minute(s)<br /><strong>Rows Imported:</strong> ' . esc_html( $result_count ) . ( false !== $paginated ? '<br /><br />' . wp_kses_post( $paginated ) : '' ) . "';</script>\n";
1286 echo '<br />' . esc_html( date_i18n( 'Y-m-d h:i:sa' ) ) . " - <strong style='color:green;'>Import Complete</strong>\n";
1287 }
1288 }
1289
1290 /**
1291 * Export data to a file.
1292 *
1293 * @param string $file File to export to.
1294 * @param array $data Data to export.
1295 * @param bool $single Whether this is a single item export.
1296 *
1297 * @return mixed
1298 */
1299 public static function export_data_to_file( $file, $data, $single = false ) {
1300
1301 $path = ABSPATH;
1302
1303 // Detect path if it is set in the file param.
1304 if ( false !== strpos( $file, '/' ) ) {
1305 $path = dirname( $file );
1306 $file = basename( $file );
1307 }
1308
1309 $format = 'json';
1310
1311 // Detect the export format.
1312 if ( false !== strpos( $file, '.' ) ) {
1313 $format = explode( '.', $file );
1314 $format = end( $format );
1315 }
1316
1317 $migrate_data = array(
1318 'items' => $data,
1319 'single' => $single,
1320 );
1321
1322 // Try to guess the column labels based on the supplied data.
1323 $first_item = null;
1324
1325 if ( $single ) {
1326 $first_item = $data;
1327 } elseif ( is_array( $data ) ) {
1328 $first_item = reset( $data );
1329 }
1330
1331 if ( is_array( $first_item ) ) {
1332 $fields = array_keys( $first_item );
1333 $migrate_data['columns'] = array_combine( $fields, $fields );
1334 }
1335
1336 $migrate = new self( $format, null, $migrate_data );
1337
1338 // Handle processing the data into the format needed.
1339 $migrate->export();
1340
1341 $save_params = array(
1342 'path' => $path,
1343 'file' => $file,
1344 'attach' => true,
1345 );
1346
1347 return $migrate->save( $save_params );
1348
1349 }
1350
1351 /**
1352 * Get data from a file.
1353 *
1354 * @param string $file File to get data from.
1355 * @param bool $single Whether this is a single item.
1356 *
1357 * @return mixed
1358 */
1359 public static function get_data_from_file( $file, $single = false ) {
1360
1361 $path = ABSPATH;
1362
1363 // Detect path if it is set in the file param.
1364 if ( false !== strpos( $file, DIRECTORY_SEPARATOR ) ) {
1365 $path = dirname( $file );
1366 $file = basename( $file );
1367 }
1368
1369 $format = 'json';
1370
1371 // Detect the export format.
1372 if ( false !== strpos( $file, '.' ) ) {
1373 $format = explode( '.', $file );
1374 $format = end( $format );
1375 }
1376
1377 $migrate_data = array(
1378 'single' => $single,
1379 );
1380
1381 $migrate = new self( $format, null, $migrate_data );
1382
1383 $raw_data = file_get_contents( $path . DIRECTORY_SEPARATOR . $file );
1384
1385 // Handle processing the raw data from the format needed.
1386 $data = $migrate->parse( $raw_data );
1387
1388 return $data;
1389
1390 }
1391
1392 }
1393