PluginProbe ʕ •ᴥ•ʔ
Pods – Custom Content Types and Fields / 3.3.5
Pods – Custom Content Types and Fields v3.3.5
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
1392 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 WP_Filesystem();
861
862 /** @var WP_Filesystem_Base $wp_filesystem */
863 global $wp_filesystem;
864
865 $wp_filesystem->chmod( $new_file, $perms );
866
867 // Only attach if we want to and don't have a custom path.
868 if ( $params['attach'] && empty( $params['path'] ) ) {
869 // Get the file type
870 $wp_filetype = wp_check_filetype( $filename, $this->mimes );
871
872 // construct the attachment array
873 $attachment = array(
874 'post_mime_type' => 'text/' . $extension,
875 'guid' => $uploads['url'] . '/' . $filename,
876 'post_parent' => null,
877 'post_title' => 'Pods Export (' . $export_file . ')',
878 'post_content' => '',
879 'post_status' => 'private'
880 );
881
882 if ( $wp_filetype['type'] ) {
883 $attachment['post_mime_type'] = $wp_filetype['type'];
884 }
885
886 // insert attachment
887 /* @var int|WP_Error $attachment_id */
888 $attachment_id = wp_insert_attachment( $attachment, $new_file );
889
890 // error!
891 if ( $attachment_id instanceof WP_Error ) {
892 return pods_error( __( 'There was an issue saving the export file in your uploads folder.', 'pods' ), true );
893 }
894
895 $url = $attachment['guid'];
896 } else {
897 $url = $new_file;
898 }
899
900 return $url;
901
902 }
903
904 /*
905 The real enchilada!
906
907 EXAMPLES
908 //// minimal import (if your fields match on both your pods and tables)
909 $import = array('my_pod' => array('table' => 'my_table')); // if your table name doesn't match the pod name
910 $import = array('my_pod'); // if your table name matches your pod name
911
912 //// advanced import
913 $import = array();
914 $import['my_pod'] = array();
915 $import['my_pod']['fields']['this_field'] = 'field_name_in_table'; // if the field name doesn't match on table and pod
916 $import['my_pod']['fields'][] = 'that_field'; // if the field name matches on table and pod
917 $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)]
918 $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
919 $import[] = 'my_other_pod'; // if your table name matches your pod name
920 $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
921 $import['another_pod'] = array('reset' => true); // you can choose to reset all data in a pod before importing
922
923 //// run import
924 pods_import_it($import);
925 */
926 /**
927 * @param $import
928 * @param bool $output
929 */
930 public function heres_the_beef( $import, $output = true ) {
931
932 global $wpdb;
933
934 $api = pods_api();
935
936 for ( $i = 0; $i < 40000; $i ++ ) {
937 echo " \t";
938 // extra spaces
939 }
940
941 $default_data = array(
942 'pod' => null,
943 'table' => null,
944 'reset' => null,
945 'update_on' => null,
946 'where' => null,
947 'fields' => array(),
948 'row_filter' => null,
949 'pre_save' => null,
950 'post_save' => null,
951 'sql' => null,
952 'sort' => null,
953 'limit' => null,
954 'page' => null,
955 'output' => null,
956 'page_var' => 'ipg',
957 'bypass_helpers' => false,
958 );
959
960 $default_field_data = array(
961 'field' => null,
962 'filter' => null,
963 );
964
965 if ( ! is_array( $import ) ) {
966 $import = array( $import );
967 } elseif ( empty( $import ) ) {
968 die( '<h1 style="color:red;font-weight:bold;">ERROR: No imports configured</h1>' );
969 }
970
971 $import_counter = 0;
972 $total_imports = count( $import );
973 $paginated = false;
974 $avg_time = - 1;
975 $total_time = 0;
976 $counter = 0;
977 $avg_unit = 100;
978 $avg_counter = 0;
979
980 foreach ( $import as $datatype => $data ) {
981 $import_counter ++;
982
983 flush();
984 @ob_end_flush();
985 usleep( 50000 );
986
987 if ( ! is_array( $data ) ) {
988 $datatype = $data;
989 $data = array( 'table' => $data );
990 }
991
992 if ( isset( $data[0] ) ) {
993 $data = array( 'table' => $data[0] );
994 }
995
996 $data = array_merge( $default_data, $data );
997
998 $data['limit'] = absint( $data['limit'] );
999
1000 if ( null === $data['pod'] ) {
1001 $data['pod'] = array( 'name' => $datatype );
1002 }
1003
1004 if ( false !== $output ) {
1005 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";
1006 }
1007
1008 if ( 2 > count( $data['pod'] ) ) {
1009 $data['pod'] = $api->load_pod( array( 'name' => $data['pod']['name'] ) );
1010 }
1011
1012 if ( empty( $data['pod']['fields'] ) ) {
1013 continue;
1014 }
1015
1016 if ( null === $data['table'] ) {
1017 $data['table'] = $data['pod']['name'];
1018 }
1019
1020 if ( $data['reset'] === true ) {
1021 if ( false !== $output ) {
1022 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";
1023 }
1024
1025 $api->reset_pod(
1026 array(
1027 'id' => $data['pod']['id'],
1028 'name' => $data['pod']['name'],
1029 )
1030 );
1031 }
1032
1033 if ( null === $data['sort'] && null !== $data['update_on'] && isset( $data['fields'][ $data['update_on'] ] ) ) {
1034 if ( isset( $data['fields'][ $data['update_on'] ]['field'] ) ) {
1035 $data['sort'] = $data['fields'][ $data['update_on'] ]['field'];
1036 } else {
1037 $data['sort'] = $data['update_on'];
1038 }
1039 }
1040
1041 $page = 1;
1042
1043 if ( false !== $data['page_var'] && 0 < (int) pods_v( $data['page_var'] ) ) {
1044 $page = (int) pods_v( $data['page_var'] );
1045 }
1046
1047 if ( null === $data['sql'] ) {
1048 $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']}" : '' );
1049 }
1050
1051 if ( false !== $output ) {
1052 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";
1053 }
1054
1055 if ( false !== $output ) {
1056 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";
1057 }
1058
1059 $result = $wpdb->get_results( // phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter
1060 $data['sql'], // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
1061 ARRAY_A
1062 );
1063
1064 if ( false !== $output ) {
1065 echo '<br />' . esc_html( date_i18n( 'Y-m-d h:i:sa' ) ) . ' - <em>' . esc_html( $data['pod']['name'] ) . '</em> - Results Found: ' . count( $result ) . "\n";
1066 }
1067
1068 $avg_time = - 1;
1069 $total_time = 0;
1070 $counter = 0;
1071 $avg_unit = 100;
1072 $avg_counter = 0;
1073 $result_count = count( $result );
1074 $paginated = false;
1075
1076 if ( false !== $data['page_var'] && $result_count === $data['limit'] ) {
1077 $paginated = "<input type=\"button\" onclick=\"document.location=\'" . esc_js( add_query_arg( [ $data['page_var'] => $page + 1 ] ) ) . "\';\" value=\" Continue Import &raquo; \" />";
1078 }
1079
1080 if ( $result_count < $avg_unit && 5 < $result_count ) {
1081 $avg_unit = number_format( $result_count / 5, 0, '', '' );
1082 } elseif ( 2000 < $result_count && 10 < count( $data['pod']['fields'] ) ) {
1083 $avg_unit = 40;
1084 }
1085
1086 $data['count'] = $result_count;
1087 timer_start();
1088
1089 if ( false !== $output && 1 === $import_counter ) {
1090 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";
1091 }
1092
1093 foreach ( $result as $k => $row ) {
1094 flush();
1095 @ob_end_flush();
1096 usleep( 50000 );
1097
1098 if ( false !== $output ) {
1099 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";
1100 }
1101
1102 if ( null !== $data['row_filter'] && function_exists( $data['row_filter'] ) ) {
1103 if ( false !== $output ) {
1104 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";
1105 }
1106
1107 $row = $data['row_filter']( $row, $data );
1108 }
1109
1110 if ( ! is_array( $row ) ) {
1111 continue;
1112 }
1113
1114 $params = array(
1115 'datatype' => $data['pod']['name'],
1116 'columns' => array(),
1117 'bypass_helpers' => $data['bypass_helpers'],
1118 );
1119
1120 foreach ( $data['pod']['fields'] as $fk => $field_info ) {
1121 $field = $field_info['name'];
1122
1123 if ( ! empty( $data['fields'] ) && ! isset( $data['fields'][ $field ] ) && ! in_array( $field, $data['fields'], true ) ) {
1124 continue;
1125 }
1126
1127 if ( isset( $data['fields'][ $field ] ) ) {
1128 if ( is_array( $data['fields'][ $field ] ) ) {
1129 $field_data = $data['fields'][ $field ];
1130 } else {
1131 $field_data = array( 'field' => $data['fields'][ $field ] );
1132 }
1133 } else {
1134 $field_data = array();
1135 }
1136
1137 if ( ! is_array( $field_data ) ) {
1138 $field = $field_data;
1139 $field_data = array();
1140 }
1141
1142 $field_data = pods_config_merge_data( $default_field_data, $field_data );
1143
1144 if ( null === $field_data['field'] ) {
1145 $field_data['field'] = $field;
1146 }
1147
1148 $data['fields'][ $field ] = $field_data;
1149 $value = '';
1150
1151 if ( isset( $row[ $field_data['field'] ] ) ) {
1152 $value = $row[ $field_data['field'] ];
1153 }
1154
1155 if ( null !== $field_data['filter'] ) {
1156 if ( function_exists( $field_data['filter'] ) ) {
1157 if ( false !== $output ) {
1158 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";
1159 }
1160
1161 $value = $field_data['filter']( $value, $row, $data );
1162 } else {
1163 $value = '';
1164 }
1165 }
1166
1167 if ( 1 === $field_info['required'] && ( ! is_string( $value ) || strlen( $value ) < 1 ) ) {
1168 die( '<h1 style="color:red;font-weight:bold;">ERROR: Field Required for <strong>' . esc_html( $field ) . '</strong></h1>' );
1169 }
1170
1171 $params['columns'][ $field ] = $value;
1172
1173 unset( $value, $field_data, $field_info, $fk );
1174 }//end foreach
1175
1176 if ( empty( $params['columns'] ) ) {
1177 continue;
1178 }
1179
1180 $params['columns'] = pods_sanitize( $params['columns'] );
1181
1182 if ( null !== $data['update_on'] && isset( $params['columns'][ $data['update_on'] ] ) ) {
1183 if ( false !== $output ) {
1184 echo '<br />' . esc_html( date_i18n( 'Y-m-d h:i:sa' ) ) . ' - <em>' . esc_html( $data['pod']['name'] ) . "</em> - Checking for Existing Item\n";
1185 }
1186
1187 $check = pods( $data['pod']['name'] );
1188 $check->find(
1189 [
1190 'orderby' => 't.id',
1191 'limit' => 1,
1192 'where' => "t.{$data['update_on']} = '{$params['columns'][$data['update_on']]}'",
1193 'search' => false,
1194 'page' => 1,
1195 ]
1196 );
1197
1198 if ( $check->fetch() ) {
1199 $params['id'] = $check->id();
1200 $params['pod'] = $check->pod;
1201 $params['pod_id'] = $check->pod_id;
1202
1203 if ( false !== $output ) {
1204 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";
1205 }
1206
1207 unset( $check );
1208 }
1209
1210 if ( ! isset( $params['id'] ) && false !== $output ) {
1211 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";
1212 }
1213 }//end if
1214
1215 if ( null !== $data['pre_save'] && function_exists( $data['pre_save'] ) ) {
1216 if ( false !== $output ) {
1217 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";
1218 }
1219
1220 $params = $data['pre_save']( $params, $row, $data );
1221 }
1222
1223 $id = $api->save_pod_item( $params );
1224
1225 if ( false !== $output ) {
1226 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";
1227 }
1228
1229 $params['id'] = $id;
1230
1231 if ( null !== $data['post_save'] && function_exists( $data['post_save'] ) ) {
1232 if ( false !== $output ) {
1233 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";
1234 }
1235
1236 $data['post_save']( $params, $row, $data );
1237 }
1238
1239 unset( $params, $result[ $k ], $row );
1240
1241 wp_cache_flush();
1242 $wpdb->queries = array();
1243
1244 $avg_counter ++;
1245 $counter ++;
1246
1247 if ( $avg_counter === $avg_unit && false !== $output ) {
1248 $avg_counter = 0;
1249 $avg_time = timer_stop( 0, 10 );
1250 $total_time += $avg_time;
1251 $rows_left = $result_count - $counter;
1252 $estimated_time_left = ( ( $total_time / $counter ) * $rows_left ) / 60;
1253 $percent_complete = 100 - ( ( $rows_left * 100 ) / $result_count );
1254
1255 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";
1256 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";
1257 }
1258 }//end foreach
1259
1260 if ( false !== $output ) {
1261 $avg_counter = 0;
1262 $avg_time = timer_stop( 0, 10 );
1263 $total_time += $avg_time;
1264 $rows_left = $result_count - $counter;
1265 $estimated_time_left = ( ( $total_time / $counter ) * $rows_left ) / 60;
1266 $percent_complete = 100 - ( ( $rows_left * 100 ) / $result_count );
1267
1268 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";
1269 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";
1270 }
1271
1272 unset( $result, $import[ $datatype ], $datatype, $data );
1273
1274 wp_cache_flush();
1275 $wpdb->queries = array();
1276 }//end foreach
1277
1278 if ( false !== $output ) {
1279 $avg_counter = 0;
1280 $avg_time = timer_stop( 0, 10 );
1281 $total_time += $avg_time;
1282 $rows_left = $result_count - $counter;
1283
1284 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";
1285 echo '<br />' . esc_html( date_i18n( 'Y-m-d h:i:sa' ) ) . " - <strong style='color:green;'>Import Complete</strong>\n";
1286 }
1287 }
1288
1289 /**
1290 * Export data to a file.
1291 *
1292 * @param string $file File to export to.
1293 * @param array $data Data to export.
1294 * @param bool $single Whether this is a single item export.
1295 *
1296 * @return mixed
1297 */
1298 public static function export_data_to_file( $file, $data, $single = false ) {
1299
1300 $path = ABSPATH;
1301
1302 // Detect path if it is set in the file param.
1303 if ( false !== strpos( $file, '/' ) ) {
1304 $path = dirname( $file );
1305 $file = basename( $file );
1306 }
1307
1308 $format = 'json';
1309
1310 // Detect the export format.
1311 if ( false !== strpos( $file, '.' ) ) {
1312 $format = explode( '.', $file );
1313 $format = end( $format );
1314 }
1315
1316 $migrate_data = array(
1317 'items' => $data,
1318 'single' => $single,
1319 );
1320
1321 // Try to guess the column labels based on the supplied data.
1322 $first_item = null;
1323
1324 if ( $single ) {
1325 $first_item = $data;
1326 } elseif ( is_array( $data ) ) {
1327 $first_item = reset( $data );
1328 }
1329
1330 if ( is_array( $first_item ) ) {
1331 $fields = array_keys( $first_item );
1332 $migrate_data['columns'] = array_combine( $fields, $fields );
1333 }
1334
1335 $migrate = new self( $format, null, $migrate_data );
1336
1337 // Handle processing the data into the format needed.
1338 $migrate->export();
1339
1340 $save_params = array(
1341 'path' => $path,
1342 'file' => $file,
1343 'attach' => true,
1344 );
1345
1346 return $migrate->save( $save_params );
1347
1348 }
1349
1350 /**
1351 * Get data from a file.
1352 *
1353 * @param string $file File to get data from.
1354 * @param bool $single Whether this is a single item.
1355 *
1356 * @return mixed
1357 */
1358 public static function get_data_from_file( $file, $single = false ) {
1359
1360 $path = ABSPATH;
1361
1362 // Detect path if it is set in the file param.
1363 if ( false !== strpos( $file, DIRECTORY_SEPARATOR ) ) {
1364 $path = dirname( $file );
1365 $file = basename( $file );
1366 }
1367
1368 $format = 'json';
1369
1370 // Detect the export format.
1371 if ( false !== strpos( $file, '.' ) ) {
1372 $format = explode( '.', $file );
1373 $format = end( $format );
1374 }
1375
1376 $migrate_data = array(
1377 'single' => $single,
1378 );
1379
1380 $migrate = new self( $format, null, $migrate_data );
1381
1382 $raw_data = file_get_contents( $path . DIRECTORY_SEPARATOR . $file );
1383
1384 // Handle processing the raw data from the format needed.
1385 $data = $migrate->parse( $raw_data );
1386
1387 return $data;
1388
1389 }
1390
1391 }
1392