Completed
Pull Request — 2.x (#4771)
by Scott Kingsley
45:09 queued 35:13
created

PodsAPI::export_pod_item_level()   F

Complexity

Conditions 40
Paths 15678

Size

Total Lines 167
Code Lines 96

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 40
eloc 96
nc 15678
nop 2
dl 0
loc 167
rs 2
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * @package Pods
5
 */
6
class PodsAPI {
7
8
	/**
9
	 * @var PodsAPI
10
	 */
11
	static $instance = null;
12
13
	/**
14
	 * @var array PodsAPI
15
	 */
16
	static $instances = array();
17
18
	/**
19
	 * @var bool
20
	 */
21
	public $display_errors = false;
22
23
	/**
24
	 * @var array|bool|mixed|null|void
25
	 */
26
	public $pod_data;
27
28
	/**
29
	 * @var
30
	 */
31
	public $pod;
32
33
	/**
34
	 * @var
35
	 */
36
	public $pod_id;
37
38
	/**
39
	 * @var
40
	 */
41
	public $fields;
42
43
	/**
44
	 * @var
45
	 * @deprecated 2.0
46
	 */
47
	public $format = null;
48
49
	/**
50
	 * @var
51
	 */
52
	private $deprecated;
53
54
	/**
55
	 * @var array
56
	 * @since 2.5
57
	 */
58
	private $fields_cache = array();
59
60
	/**
61
	 * @var array
62
	 * @since 2.5
63
	 *
64
	 */
65
	private static $table_info_cache = array();
66
67
	/**
68
	 * @var array
69
	 * @since 2.5
70
	 *
71
	 */
72
	private static $related_item_cache = array();
73
74
	/**
75
	 * Singleton-ish handling for a basic pods_api() request
76
	 *
77
	 * @param string $pod    (optional) The pod name
0 ignored issues
show
Documentation introduced by
Should the type for parameter $pod not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
78
	 * @param string $format (deprecated) Format for import/export, "php" or "csv"
0 ignored issues
show
Documentation introduced by
Should the type for parameter $format not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
79
	 *
80
	 * @return \PodsAPI
81
	 *
82
	 * @since 2.3.5
83
	 */
84
	public static function init( $pod = null, $format = null ) {
85
86
		if ( null !== $pod || null !== $format ) {
87
			if ( ! isset( self::$instances[ $pod ] ) ) {
88
				// Cache API singleton per Pod
89
				self::$instances[ $pod ] = new PodsAPI( $pod, $format );
90
			}
91
92
			return self::$instances[ $pod ];
93
		} elseif ( ! is_object( self::$instance ) ) {
94
			self::$instance = new PodsAPI();
95
		}
96
97
		return self::$instance;
98
	}
99
100
	/**
101
	 * Store and retrieve data programatically
102
	 *
103
	 * @param string $pod    (optional) The pod name
0 ignored issues
show
Documentation introduced by
Should the type for parameter $pod not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
104
	 * @param string $format (deprecated) Format for import/export, "php" or "csv"
0 ignored issues
show
Documentation introduced by
Should the type for parameter $format not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
105
	 *
106
	 * @return \PodsAPI
0 ignored issues
show
Comprehensibility Best Practice introduced by
Adding a @return annotation to constructors is generally not recommended as a constructor does not have a meaningful return value.

Adding a @return annotation to a constructor is not recommended, since a constructor does not have a meaningful return value.

Please refer to the PHP core documentation on constructors.

Loading history...
107
	 *
108
	 * @license http://www.gnu.org/licenses/gpl-2.0.html
109
	 * @since   1.7.1
110
	 */
111
	public function __construct( $pod = null, $format = null ) {
112
113
		if ( null !== $pod && 0 < strlen( (string) $pod ) ) {
114
			if ( null !== $format ) {
115
				$this->format = $format;
0 ignored issues
show
Deprecated Code introduced by
The property PodsAPI::$format has been deprecated with message: 2.0

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
116
117
				pods_deprecated( 'pods_api( $pod, $format )', '2.0', 'pods_api( $pod )' );
118
			}
119
120
			$pod = pods_clean_name( $pod );
121
122
			$pod = $this->load_pod( array( 'name' => $pod, 'table_info' => true ), false );
123
124
			if ( ! empty( $pod ) ) {
125
				$this->pod_data = $pod;
126
				$this->pod      = $pod['name'];
127
				$this->pod_id   = $pod['id'];
128
				$this->fields   = $pod['fields'];
129
			}
130
		}
131
	}
132
133
	/**
134
	 * Save a WP object and its meta
135
	 *
136
	 * @param string $object_type Object type: post|taxonomy|user|comment|setting
137
	 * @param array  $data        All post data to be saved
138
	 * @param array  $meta        (optional) Associative array of meta keys and values
139
	 * @param bool   $strict      (optional) Decides whether the previous saved meta should be deleted or not
140
	 * @param bool   $sanitized   (optional) Will unsanitize the data, should be passed if the data is sanitized before
141
	 *                            sending.
142
	 * @param array  $fields      (optional) The array of fields and their options, for further processing with
143
	 *
144
	 * @return bool|mixed
145
	 *
146
	 * @since 2.0
147
	 */
148
	public function save_wp_object( $object_type, $data, $meta = array(), $strict = false, $sanitized = false, $fields = array() ) {
149
150
		if ( in_array( $object_type, array( 'post_type', 'media' ) ) ) {
151
			$object_type = 'post';
152
		}
153
154
		if ( 'taxonomy' === $object_type ) {
155
			$object_type = 'term';
156
		}
157
158
		if ( $sanitized ) {
159
			$data = pods_unsanitize( $data );
160
			$meta = pods_unsanitize( $meta );
161
		}
162
163
		if ( in_array( $object_type, array( 'post', 'term', 'user', 'comment' ) ) ) {
164
			return call_user_func( array( $this, 'save_' . $object_type ), $data, $meta, $strict, false, $fields );
165
		} elseif ( 'settings' === $object_type ) {
166
			// Nothing to save
167
			if ( empty( $meta ) ) {
168
				return true;
169
			}
170
171
			return $this->save_setting( pods_var( 'option_id', $data ), $meta, false );
172
		}
173
174
		return false;
175
	}
176
177
	/**
178
	 * Delete a WP object
179
	 *
180
	 * @param string $object_type  Object type: post|user|comment
181
	 * @param int    $id           Object ID
182
	 * @param bool   $force_delete (optional) Force deletion instead of trashing (post types only)
183
	 *
184
	 * @return bool|mixed
185
	 *
186
	 * @since 2.0
187
	 */
188
	public function delete_wp_object( $object_type, $id, $force_delete = true ) {
189
190
		if ( in_array( $object_type, array( 'post_type', 'media' ) ) ) {
191
			$object_type = 'post';
192
		}
193
194
		if ( 'taxonomy' === $object_type ) {
195
			$object_type = 'term';
196
		}
197
198
		if ( empty( $id ) ) {
199
			return false;
200
		}
201
202
		if ( in_array( $object_type, array( 'post' ) ) ) {
203
			return wp_delete_post( $id, $force_delete );
204
		}
205
206
		if ( function_exists( 'wp_delete_' . $object_type ) ) {
207
			return call_user_func( 'wp_delete_' . $object_type, $id );
208
		}
209
210
		return false;
211
	}
212
213
	/**
214
	 * Save a post and it's meta
215
	 *
216
	 * @param array $post_data All post data to be saved (using wp_insert_post / wp_update_post)
217
	 * @param array $post_meta (optional) All meta to be saved (set value to null to delete)
0 ignored issues
show
Documentation introduced by
Should the type for parameter $post_meta not be array|null? Also, consider making the array more specific, something like array<String>, or String[].

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive. In addition it looks for parameters that have the generic type array and suggests a stricter type like array<String>.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
218
	 * @param bool  $strict    (optional) Whether to delete previously saved meta not in $post_meta
219
	 * @param bool  $sanitized (optional) Will unsanitize the data, should be passed if the data is sanitized before
220
	 *                         sending.
221
	 * @param array $fields    (optional) The array of fields and their options, for further processing with
222
	 *
223
	 * @return mixed|void
224
	 *
225
	 * @since 2.0
226
	 */
227
	public function save_post( $post_data, $post_meta = null, $strict = false, $sanitized = false, $fields = array() ) {
228
229
		$conflicted = pods_no_conflict_check( 'post' );
230
231
		if ( ! $conflicted ) {
232
			pods_no_conflict_on( 'post' );
233
		}
234
235
		if ( ! is_array( $post_data ) || empty( $post_data ) ) {
236
			$post_data = array( 'post_title' => '' );
237
		}
238
239
		if ( ! is_array( $post_meta ) ) {
240
			$post_meta = array();
241
		}
242
243
		if ( $sanitized ) {
244
			$post_data = pods_unsanitize( $post_data );
245
			$post_meta = pods_unsanitize( $post_meta );
246
		}
247
248
		if ( ! isset( $post_data['ID'] ) || empty( $post_data['ID'] ) ) {
249
			$post_data['ID'] = wp_insert_post( $post_data, true );
250
		} elseif ( 2 < count( $post_data ) || ! isset( $post_data['post_type'] ) ) {
251
			$post_data['ID'] = wp_update_post( $post_data, true );
252
		}
253
254
		if ( is_wp_error( $post_data['ID'] ) ) {
255
			if ( ! $conflicted ) {
256
				pods_no_conflict_off( 'post' );
257
			}
258
259
			/**
260
			 * @var $post_error WP_Error
261
			 */
262
			$post_error = $post_data['ID'];
263
264
			return pods_error( $post_error->get_error_message(), $this );
265
		}
266
267
		$this->save_post_meta( $post_data['ID'], $post_meta, $strict, $fields );
268
269
		if ( ! $conflicted ) {
270
			pods_no_conflict_off( 'post' );
271
		}
272
273
		return $post_data['ID'];
274
	}
275
276
	/**
277
	 * Save a post's meta
278
	 *
279
	 * @param int   $id        Post ID
280
	 * @param array $post_meta All meta to be saved (set value to null to delete)
0 ignored issues
show
Documentation introduced by
Should the type for parameter $post_meta not be array|null? Also, consider making the array more specific, something like array<String>, or String[].

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive. In addition it looks for parameters that have the generic type array and suggests a stricter type like array<String>.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
281
	 * @param bool  $strict    Whether to delete previously saved meta not in $post_meta
282
	 * @param array $fields    (optional) The array of fields and their options, for further processing with
283
	 *
284
	 * @return int Id of the post with the meta
285
	 *
286
	 * @since 2.0
287
	 */
288
	public function save_post_meta( $id, $post_meta = null, $strict = false, $fields = array() ) {
289
290
		$simple_tableless_objects = PodsForm::simple_tableless_objects();
0 ignored issues
show
Comprehensibility Naming introduced by
The variable name $simple_tableless_objects exceeds the maximum configured length of 20.

Very long variable names usually make code harder to read. It is therefore recommended not to make variable names too verbose.

Loading history...
291
292
		$conflicted = pods_no_conflict_check( 'post' );
293
294
		if ( ! $conflicted ) {
295
			pods_no_conflict_on( 'post' );
296
		}
297
298
		if ( ! is_array( $post_meta ) ) {
299
			$post_meta = array();
300
		}
301
302
		$id = (int) $id;
303
304
		$meta = get_post_meta( $id );
305
306
		foreach ( $meta as $k => $value ) {
307
			if ( is_array( $value ) && 1 == count( $value ) ) {
308
				$meta[ $k ] = current( $value );
309
			}
310
		}
311
312
		foreach ( $post_meta as $meta_key => $meta_value ) {
313
			if ( null === $meta_value || ( $strict && '' === $post_meta[ $meta_key ] ) ) {
314
				$old_meta_value = '';
315
316
				if ( isset( $meta[ $meta_key ] ) ) {
317
					$old_meta_value = $meta[ $meta_key ];
318
				}
319
320
				delete_post_meta( $id, $meta_key, $old_meta_value );
321
			} else {
322
				$simple = false;
323
324
				if ( isset( $fields[ $meta_key ] ) ) {
325
					$field_data = $fields[ $meta_key ];
326
327
					$simple = ( 'pick' === $field_data['type'] && in_array( pods_var( 'pick_object', $field_data ), $simple_tableless_objects ) );
328
				}
329
330
				if ( $simple ) {
331
					delete_post_meta( $id, $meta_key );
332
333
					update_post_meta( $id, '_pods_' . $meta_key, $meta_value );
334
335
					if ( ! is_array( $meta_value ) ) {
336
						$meta_value = array( $meta_value );
337
					}
338
339
					foreach ( $meta_value as $value ) {
340
						add_post_meta( $id, $meta_key, $value );
341
					}
342
				} else {
343
					update_post_meta( $id, $meta_key, $meta_value );
344
				}
345
			}
346
		}
347
348
		if ( $strict ) {
349
			foreach ( $meta as $meta_key => $meta_value ) {
350
				if ( ! isset( $post_meta[ $meta_key ] ) ) {
351
					delete_post_meta( $id, $meta_key, $meta_value );
352
				}
353
			}
354
		}
355
356
		if ( ! $conflicted ) {
357
			pods_no_conflict_off( 'post' );
358
		}
359
360
		return $id;
361
	}
362
363
	/**
364
	 * Save a user and it's meta
365
	 *
366
	 * @param array $user_data All user data to be saved (using wp_insert_user / wp_update_user)
367
	 * @param array $user_meta (optional) All meta to be saved (set value to null to delete)
0 ignored issues
show
Documentation introduced by
Should the type for parameter $user_meta not be array|null? Also, consider making the array more specific, something like array<String>, or String[].

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive. In addition it looks for parameters that have the generic type array and suggests a stricter type like array<String>.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
368
	 * @param bool  $strict    (optional) Whether to delete previously saved meta not in $user_meta
369
	 * @param bool  $sanitized (optional) Will unsanitize the data, should be passed if the data is sanitized before
370
	 *                         sending.
371
	 * @param array $fields    (optional) The array of fields and their options, for further processing with
372
	 *
373
	 * @return int Returns user id on success
374
	 *
375
	 * @since 2.0
376
	 */
377
	public function save_user( $user_data, $user_meta = null, $strict = false, $sanitized = false, $fields = array() ) {
378
379
		if ( ! is_array( $user_data ) || empty( $user_data ) ) {
380
			return pods_error( __( 'User data is required but is either invalid or empty', 'pods' ), $this );
381
		}
382
383
		$conflicted = pods_no_conflict_check( 'user' );
384
385
		if ( ! $conflicted ) {
386
			pods_no_conflict_on( 'user' );
387
		}
388
389
		if ( ! is_array( $user_meta ) ) {
390
			$user_meta = array();
391
		}
392
393
		if ( $sanitized ) {
394
			$user_data = pods_unsanitize( $user_data );
395
			$user_meta = pods_unsanitize( $user_meta );
396
		}
397
398
		// Set role
399
		if ( isset( $user_meta['role'] ) ) {
400
			$user_data['role'] = $user_meta['role'];
401
402
			unset( $user_meta['role'] );
403
		}
404
405
		if ( ! isset( $user_data['ID'] ) || empty( $user_data['ID'] ) ) {
406
			$user_data['ID'] = wp_insert_user( $user_data );
407
		} elseif ( 1 < count( $user_data ) ) {
408
			wp_update_user( $user_data );
409
		}
410
411
		if ( is_wp_error( $user_data['ID'] ) ) {
412
			if ( ! $conflicted ) {
413
				pods_no_conflict_off( 'user' );
414
			}
415
416
			/**
417
			 * @var $user_error WP_Error
418
			 */
419
			$user_error = $user_data['ID'];
420
421
			return pods_error( $user_error->get_error_message(), $this );
422
		}
423
424
		$this->save_user_meta( $user_data['ID'], $user_meta, $strict, $fields );
425
426
		if ( ! $conflicted ) {
427
			pods_no_conflict_off( 'user' );
428
		}
429
430
		return $user_data['ID'];
431
	}
432
433
	/**
434
	 * Save a user meta
435
	 *
436
	 * @param int   $id        User ID
437
	 * @param array $user_meta (optional) All meta to be saved (set value to null to delete)
0 ignored issues
show
Documentation introduced by
Should the type for parameter $user_meta not be array|null? Also, consider making the array more specific, something like array<String>, or String[].

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive. In addition it looks for parameters that have the generic type array and suggests a stricter type like array<String>.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
438
	 * @param bool  $strict    (optional) Whether to delete previously saved meta not in $user_meta
439
	 * @param array $fields    (optional) The array of fields and their options, for further processing with
440
	 *
441
	 * @return int User ID
442
	 *
443
	 * @since 2.0
444
	 *
445
	 */
446
	public function save_user_meta( $id, $user_meta = null, $strict = false, $fields = array() ) {
447
448
		$simple_tableless_objects = PodsForm::simple_tableless_objects();
0 ignored issues
show
Comprehensibility Naming introduced by
The variable name $simple_tableless_objects exceeds the maximum configured length of 20.

Very long variable names usually make code harder to read. It is therefore recommended not to make variable names too verbose.

Loading history...
449
450
		$conflicted = pods_no_conflict_check( 'user' );
451
452
		if ( ! $conflicted ) {
453
			pods_no_conflict_on( 'user' );
454
		}
455
456
		if ( ! is_array( $user_meta ) ) {
457
			$user_meta = array();
458
		}
459
460
		$id = (int) $id;
461
462
		$meta = get_user_meta( $id );
463
464
		foreach ( $user_meta as $meta_key => $meta_value ) {
465
			if ( null === $meta_value ) {
466
				$old_meta_value = '';
467
468
				if ( isset( $meta[ $meta_key ] ) ) {
469
					$old_meta_value = $meta[ $meta_key ];
470
				}
471
472
				delete_user_meta( $id, $meta_key, $old_meta_value );
473
			} else {
474
				$simple = false;
475
476
				if ( isset( $fields[ $meta_key ] ) ) {
477
					$field_data = $fields[ $meta_key ];
478
479
					$simple = ( 'pick' === $field_data['type'] && in_array( pods_var( 'pick_object', $field_data ), $simple_tableless_objects ) );
480
				}
481
482
				if ( $simple ) {
483
					delete_user_meta( $id, $meta_key );
484
485
					if ( ! is_array( $meta_value ) ) {
486
						$meta_value = array( $meta_value );
487
					}
488
489
					foreach ( $meta_value as $value ) {
490
						add_user_meta( $id, $meta_key, $value );
491
					}
492
				} else {
493
					update_user_meta( $id, $meta_key, $meta_value );
494
				}
495
			}
496
		}
497
498
		if ( $strict ) {
499
			foreach ( $meta as $meta_key => $meta_value ) {
500
				if ( ! isset( $user_meta[ $meta_key ] ) ) {
501
					delete_user_meta( $id, $meta_key, $user_meta[ $meta_key ] );
502
				}
503
			}
504
		}
505
506
		if ( ! $conflicted ) {
507
			pods_no_conflict_off( 'user' );
508
		}
509
510
		return $id;
511
	}
512
513
	/**
514
	 * Save a comment and it's meta
515
	 *
516
	 * @param array $comment_data All comment data to be saved (using wp_insert_comment / wp_update_comment)
517
	 * @param array $comment_meta (optional) All meta to be saved (set value to null to delete)
0 ignored issues
show
Documentation introduced by
Should the type for parameter $comment_meta not be array|null? Also, consider making the array more specific, something like array<String>, or String[].

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive. In addition it looks for parameters that have the generic type array and suggests a stricter type like array<String>.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
518
	 * @param bool  $strict       (optional) Whether to delete previously saved meta not in $comment_meta
519
	 * @param bool  $sanitized    (optional) Will unsanitize the data, should be passed if the data is sanitized before
520
	 *                            sending.
521
	 * @param array $fields       (optional) The array of fields and their options, for further processing with
522
	 *
523
	 * @return int Comment ID
524
	 *
525
	 * @since 2.0
526
	 */
527
	public function save_comment( $comment_data, $comment_meta = null, $strict = false, $sanitized = false, $fields = array() ) {
528
529
		if ( ! is_array( $comment_data ) || empty( $comment_data ) ) {
530
			return pods_error( __( 'Comment data is required but is either invalid or empty', 'pods' ), $this );
531
		}
532
533
		$conflicted = pods_no_conflict_check( 'comment' );
534
535
		if ( ! $conflicted ) {
536
			pods_no_conflict_on( 'comment' );
537
		}
538
539
		if ( ! is_array( $comment_meta ) ) {
540
			$comment_meta = array();
541
		}
542
543
		if ( $sanitized ) {
544
			$comment_data = pods_unsanitize( $comment_data );
545
			$comment_meta = pods_unsanitize( $comment_meta );
546
		}
547
548
		if ( ! isset( $comment_data['comment_ID'] ) || empty( $comment_data['comment_ID'] ) ) {
549
			$comment_data['comment_ID'] = wp_insert_comment( pods_slash( $comment_data ) );
550
		} elseif ( 1 < count( $comment_data ) ) {
551
			// Expects slashed
552
			wp_update_comment( $comment_data );
553
		}
554
555
		if ( is_wp_error( $comment_data['comment_ID'] ) ) {
556
			if ( ! $conflicted ) {
557
				pods_no_conflict_off( 'comment' );
558
			}
559
560
			/**
561
			 * @var $comment_error WP_Error
562
			 */
563
			$comment_error = $comment_data['comment_ID'];
564
565
			return pods_error( $comment_error->get_error_message(), $this );
566
		}
567
568
		$this->save_comment_meta( $comment_data['comment_ID'], $comment_meta, $strict, $fields );
569
570
		if ( ! $conflicted ) {
571
			pods_no_conflict_off( 'comment' );
572
		}
573
574
		return $comment_data['comment_ID'];
575
	}
576
577
	/**
578
	 * Save a comment meta
579
	 *
580
	 * @param int   $id           Comment ID
581
	 * @param array $comment_meta (optional) All meta to be saved (set value to null to delete)
0 ignored issues
show
Documentation introduced by
Should the type for parameter $comment_meta not be array|null? Also, consider making the array more specific, something like array<String>, or String[].

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive. In addition it looks for parameters that have the generic type array and suggests a stricter type like array<String>.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
582
	 * @param bool  $strict       (optional) Whether to delete previously saved meta not in $comment_meta
583
	 * @param array $fields       (optional) The array of fields and their options, for further processing with
584
	 *
585
	 * @return int Comment ID
586
	 *
587
	 * @since 2.0
588
	 */
589
	public function save_comment_meta( $id, $comment_meta = null, $strict = false, $fields = array() ) {
590
591
		$simple_tableless_objects = PodsForm::simple_tableless_objects();
0 ignored issues
show
Comprehensibility Naming introduced by
The variable name $simple_tableless_objects exceeds the maximum configured length of 20.

Very long variable names usually make code harder to read. It is therefore recommended not to make variable names too verbose.

Loading history...
592
593
		$conflicted = pods_no_conflict_check( 'comment' );
594
595
		if ( ! $conflicted ) {
596
			pods_no_conflict_on( 'comment' );
597
		}
598
599
		if ( ! is_array( $comment_meta ) ) {
600
			$comment_meta = array();
601
		}
602
603
		$id = (int) $id;
604
605
		$meta = get_comment_meta( $id );
606
607
		foreach ( $comment_meta as $meta_key => $meta_value ) {
608
			if ( null === $meta_value ) {
609
				$old_meta_value = '';
610
611
				if ( isset( $meta[ $meta_key ] ) ) {
612
					$old_meta_value = $meta[ $meta_key ];
613
				}
614
615
				delete_comment_meta( $id, $meta_key, $old_meta_value );
616
			} else {
617
				$simple = false;
618
619
				if ( isset( $fields[ $meta_key ] ) ) {
620
					$field_data = $fields[ $meta_key ];
621
622
					$simple = ( 'pick' === $field_data['type'] && in_array( pods_var( 'pick_object', $field_data ), $simple_tableless_objects ) );
623
				}
624
625
				if ( $simple ) {
626
					delete_comment_meta( $id, $meta_key );
627
628
					if ( ! is_array( $meta_value ) ) {
629
						$meta_value = array( $meta_value );
630
					}
631
632
					foreach ( $meta_value as $value ) {
633
						add_comment_meta( $id, $meta_key, $value );
634
					}
635
				} else {
636
					update_comment_meta( $id, $meta_key, $meta_value );
637
				}
638
			}
639
		}
640
641
		if ( $strict ) {
642
			foreach ( $meta as $meta_key => $meta_value ) {
643
				if ( ! isset( $comment_meta[ $meta_key ] ) ) {
644
					delete_comment_meta( (int) $id, $meta_key, $comment_meta[ $meta_key ] );
645
				}
646
			}
647
		}
648
649
		if ( ! $conflicted ) {
650
			pods_no_conflict_off( 'comment' );
651
		}
652
653
		return $id;
654
	}
655
656
	/**
657
	 * Save a taxonomy's term
658
	 *
659
	 * @param array $term_data All term data to be saved (using wp_insert_term / wp_update_term)
660
	 * @param array $term_meta All meta to be saved (set value to null to delete)
661
	 * @param bool  $strict    (optional) Whether to delete previously saved meta not in $post_meta
662
	 * @param bool  $sanitized (optional) Will unsanitize the data, should be passed if the data is sanitized before
663
	 *                         sending.
664
	 * @param array $fields    (optional) The array of fields and their options, for further processing with
665
	 *
666
	 * @return int Term ID
667
	 *
668
	 * @since 2.0
669
	 */
670
	public function save_term( $term_data, $term_meta, $strict = false, $sanitized = false, $fields = array() ) {
671
672
		if ( empty( $term_data['taxonomy'] ) ) {
673
			return 0;
674
		}
675
676
		$conflicted = pods_no_conflict_check( 'taxonomy' );
677
678
		if ( ! is_array( $term_data ) || empty( $term_data ) ) {
679
			$term_data = array( 'name' => '' );
680
		}
681
682
		if ( ! $conflicted ) {
683
			pods_no_conflict_on( 'taxonomy' );
684
		}
685
686
		if ( ! is_array( $term_meta ) ) {
687
			$term_meta = array();
688
		}
689
690
		if ( $sanitized ) {
691
			$term_data = pods_unsanitize( $term_data );
692
			$term_meta = pods_unsanitize( $term_meta );
693
		}
694
695
		$taxonomy = $term_data['taxonomy'];
696
697
		unset( $term_data['taxonomy'] );
698
699
		if ( empty( $term_data['term_id'] ) ) {
700
			$term_name = $term_data['name'];
701
702
			unset( $term_data['name'] );
703
704
			// Check to be certain the 'new' term doesn't exist.
705
			if ( is_int( $term_name ) ) {
706
				$term = get_term_by( 'term_id', $term_name, $taxonomy );
707
			} else {
708
				$term = get_term_by( 'name', $term_name, $taxonomy );
709
710
				if ( ! $term ) {
711
					$term = get_term_by( 'slug', $term_name, $taxonomy );
712
				}
713
			}
714
715
			if ( $term ) {
716
				// Term already exists, no need to insert it.
717
				$term_data['term_id'] = $term->term_id;
718
			} else {
719
				$term_data['term_id'] = wp_insert_term( $term_name, $taxonomy, $term_data );
720
			}
721
		} elseif ( 1 < count( $term_data ) ) {
722
			$term_data['term_id'] = wp_update_term( $term_data['term_id'], $taxonomy, $term_data );
723
		}
724
725
		if ( is_wp_error( $term_data['term_id'] ) ) {
726
			if ( ! $conflicted ) {
727
				pods_no_conflict_off( 'taxonomy' );
728
			}
729
730
			/**
731
			 * @var $term_error WP_Error
732
			 */
733
			$term_error = $term_data['term_id'];
734
735
			return pods_error( $term_error->get_error_message(), $this );
736
		} elseif ( is_array( $term_data['term_id'] ) ) {
737
			$term_data['term_id'] = $term_data['term_id']['term_id'];
738
		}
739
740
		$this->save_term_meta( $term_data['term_id'], $term_meta, $strict, $fields );
741
742
		if ( ! $conflicted ) {
743
			pods_no_conflict_off( 'taxonomy' );
744
		}
745
746
		return $term_data['term_id'];
747
	}
748
749
	/**
750
	 * Save a term's meta
751
	 *
752
	 * @param int   $id        Term ID
753
	 * @param array $term_meta All meta to be saved (set value to null to delete)
0 ignored issues
show
Documentation introduced by
Should the type for parameter $term_meta not be array|null? Also, consider making the array more specific, something like array<String>, or String[].

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive. In addition it looks for parameters that have the generic type array and suggests a stricter type like array<String>.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
754
	 * @param bool  $strict    Whether to delete previously saved meta not in $term_meta
755
	 * @param array $fields    (optional) The array of fields and their options, for further processing with
756
	 *
757
	 * @return int Id of the term with the meta
758
	 *
759
	 * @since 2.0
760
	 */
761
	public function save_term_meta( $id, $term_meta = null, $strict = false, $fields = array() ) {
762
763
		if ( ! function_exists( 'get_term_meta' ) ) {
764
			return $id;
765
		}
766
767
		$simple_tableless_objects = PodsForm::simple_tableless_objects();
0 ignored issues
show
Comprehensibility Naming introduced by
The variable name $simple_tableless_objects exceeds the maximum configured length of 20.

Very long variable names usually make code harder to read. It is therefore recommended not to make variable names too verbose.

Loading history...
768
769
		$conflicted = pods_no_conflict_check( 'taxonomy' );
770
771
		if ( ! $conflicted ) {
772
			pods_no_conflict_on( 'taxonomy' );
773
		}
774
775
		if ( ! is_array( $term_meta ) ) {
776
			$term_meta = array();
777
		}
778
779
		$id = (int) $id;
780
781
		$meta = get_term_meta( $id );
782
783
		foreach ( $meta as $k => $value ) {
784
			if ( is_array( $value ) && 1 == count( $value ) ) {
785
				$meta[ $k ] = current( $value );
786
			}
787
		}
788
789
		foreach ( $term_meta as $meta_key => $meta_value ) {
790
			if ( null === $meta_value || ( $strict && '' === $term_meta[ $meta_key ] ) ) {
791
				$old_meta_value = '';
792
793
				if ( isset( $meta[ $meta_key ] ) ) {
794
					$old_meta_value = $meta[ $meta_key ];
795
				}
796
797
				delete_term_meta( $id, $meta_key, $old_meta_value );
798
			} else {
799
				$simple = false;
800
801
				if ( isset( $fields[ $meta_key ] ) ) {
802
					$field_data = $fields[ $meta_key ];
803
804
					$simple = ( 'pick' === $field_data['type'] && in_array( pods_var( 'pick_object', $field_data ), $simple_tableless_objects ) );
805
				}
806
807
				if ( $simple ) {
808
					delete_term_meta( $id, $meta_key );
809
810
					update_term_meta( $id, '_pods_' . $meta_key, $meta_value );
811
812
					if ( ! is_array( $meta_value ) ) {
813
						$meta_value = array( $meta_value );
814
					}
815
816
					foreach ( $meta_value as $value ) {
817
						add_term_meta( $id, $meta_key, $value );
818
					}
819
				} else {
820
					update_term_meta( $id, $meta_key, $meta_value );
821
				}
822
			}
823
		}
824
825
		if ( $strict ) {
826
			foreach ( $meta as $meta_key => $meta_value ) {
827
				if ( ! isset( $term_meta[ $meta_key ] ) ) {
828
					delete_term_meta( $id, $meta_key, $meta_value );
829
				}
830
			}
831
		}
832
833
		if ( ! $conflicted ) {
834
			pods_no_conflict_off( 'taxonomy' );
835
		}
836
837
		return $id;
838
	}
839
840
	/**
841
	 * Save a set of options
842
	 *
843
	 * @param string $setting     Setting group name
844
	 * @param array  $option_data All option data to be saved
845
	 * @param bool   $sanitized   (optional) Will unsanitize the data, should be passed if the data is sanitized before
846
	 *                            sending.
847
	 *
848
	 * @return bool
849
	 *
850
	 * @since 2.3
851
	 */
852
	public function save_setting( $setting, $option_data, $sanitized = false ) {
853
854
		if ( ! is_array( $option_data ) || empty( $option_data ) ) {
855
			return pods_error( __( 'Setting data is required but is either invalid or empty', 'pods' ), $this );
856
		}
857
858
		$conflicted = pods_no_conflict_check( 'settings' );
859
860
		if ( ! $conflicted ) {
861
			pods_no_conflict_on( 'settings' );
862
		}
863
864
		if ( $sanitized ) {
865
			$option_data = pods_unsanitize( $option_data );
866
		}
867
868
		foreach ( $option_data as $option => $value ) {
869
			if ( ! empty( $setting ) ) {
870
				$option = $setting . '_' . $option;
871
			}
872
873
			update_option( $option, $value );
874
		}
875
876
		if ( ! $conflicted ) {
877
			pods_no_conflict_off( 'settings' );
878
		}
879
880
		return true;
881
	}
882
883
	/**
884
	 * Rename a WP object's type
885
	 *
886
	 * @param string $object_type Object type: post|taxonomy|comment|setting
887
	 * @param string $old_name    The old name
888
	 * @param string $new_name    The new name
889
	 *
890
	 * @return bool
891
	 *
892
	 * @since 2.0
893
	 */
894
	public function rename_wp_object_type( $object_type, $old_name, $new_name ) {
895
896
		/**
897
		 * @var $wpdb wpdb
898
		 */
899
		global $wpdb;
900
901
		if ( 'post_type' === $object_type ) {
902
			$object_type = 'post';
903
		}
904
905
		if ( 'post' === $object_type ) {
906
			pods_query( "UPDATE `{$wpdb->posts}` SET `post_type` = %s WHERE `post_type` = %s", array(
907
				$new_name,
908
				$old_name
909
			) );
910
		} elseif ( 'taxonomy' === $object_type ) {
911
			pods_query( "UPDATE `{$wpdb->term_taxonomy}` SET `taxonomy` = %s WHERE `taxonomy` = %s", array(
912
				$new_name,
913
				$old_name
914
			) );
915
		} elseif ( 'comment' === $object_type ) {
916
			pods_query( "UPDATE `{$wpdb->comments}` SET `comment_type` = %s WHERE `comment_type` = %s", array(
917
				$new_name,
918
				$old_name
919
			) );
920
		} elseif ( 'settings' === $object_type ) {
921
			pods_query( "UPDATE `{$wpdb->options}` SET `option_name` = REPLACE( `option_name`, %s, %s ) WHERE `option_name` LIKE '" . pods_sanitize_like( $old_name ) . "_%'", array(
922
				$new_name . '_',
923
				$old_name . '_'
924
			) );
925
		}
926
927
		return true;
928
	}
929
930
	/**
931
	 * Get a list of core WP object fields for a specific object
932
	 *
933
	 * @param string  $object  The pod type to look for, possible values: post_type, user, comment, taxonomy
934
	 * @param array   $pod     Array of Pod data
0 ignored issues
show
Documentation introduced by
Should the type for parameter $pod not be array|null? Also, consider making the array more specific, something like array<String>, or String[].

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive. In addition it looks for parameters that have the generic type array and suggests a stricter type like array<String>.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
935
	 * @param boolean $refresh Whether to force refresh the information
936
	 *
937
	 * @return array Array of fields
938
	 *
939
	 * @since 2.0
940
	 */
941
	public function get_wp_object_fields( $object = 'post_type', $pod = null, $refresh = false ) {
942
943
		$pod_name = pods_var_raw( 'name', $pod, $object, null, true );
944
945
		if ( 'media' === $pod_name ) {
946
			$object   = 'post_type';
947
			$pod_name = 'attachment';
948
		}
949
950
		$fields = false;
951
952
		if ( pods_api_cache() ) {
953
			$fields = pods_transient_get( trim( 'pods_api_object_fields_' . $object . $pod_name . '_', '_' ) );
954
		}
955
956
		if ( false !== $fields && ! $refresh ) {
957
			return $this->do_hook( 'get_wp_object_fields', $fields, $object, $pod );
958
		}
959
960
		$fields = array();
961
962
		if ( 'post_type' === $object ) {
963
			$fields = array(
964
				'ID'                    => array(
965
					'name'    => 'ID',
966
					'label'   => 'ID',
967
					'type'    => 'number',
968
					'alias'   => array( 'id' ),
969
					'options' => array(
970
						'number_format' => '9999.99'
971
					)
972
				),
973
				'post_title'            => array(
974
					'name'    => 'post_title',
975
					'label'   => 'Title',
976
					'type'    => 'text',
977
					'alias'   => array( 'title', 'name' ),
978
					'options' => array(
979
						'display_filter'      => 'the_title',
980
						'display_filter_args' => array( 'post_ID' )
981
					)
982
				),
983
				'post_content'          => array(
984
					'name'    => 'post_content',
985
					'label'   => 'Content',
986
					'type'    => 'wysiwyg',
987
					'alias'   => array( 'content' ),
988
					'options' => array(
989
						'wysiwyg_allowed_html_tags' => '',
990
						'display_filter'            => 'the_content',
991
						'pre_save'                  => 0
992
					)
993
				),
994
				'post_excerpt'          => array(
995
					'name'    => 'post_excerpt',
996
					'label'   => 'Excerpt',
997
					'type'    => 'paragraph',
998
					'alias'   => array( 'excerpt' ),
999
					'options' => array(
1000
						'paragraph_allow_html'        => 1,
1001
						'paragraph_allowed_html_tags' => '',
1002
						'display_filter'              => 'the_excerpt',
1003
						'pre_save'                    => 0
1004
					)
1005
				),
1006
				'post_author'           => array(
1007
					'name'        => 'post_author',
1008
					'label'       => 'Author',
1009
					'type'        => 'pick',
1010
					'alias'       => array( 'author' ),
1011
					'pick_object' => 'user',
1012
					'options'     => array(
1013
						'pick_format_type'   => 'single',
1014
						'pick_format_single' => 'autocomplete',
1015
						'default_value'      => '{@user.ID}'
1016
					)
1017
				),
1018
				'post_date'             => array(
1019
					'name'  => 'post_date',
1020
					'label' => 'Publish Date',
1021
					'type'  => 'datetime',
1022
					'alias' => array( 'created', 'date' )
1023
				),
1024
				'post_date_gmt'         => array(
1025
					'name'   => 'post_date_gmt',
1026
					'label'  => 'Publish Date (GMT)',
1027
					'type'   => 'datetime',
1028
					'alias'  => array(),
1029
					'hidden' => true
1030
				),
1031
				'post_status'           => array(
1032
					'name'        => 'post_status',
1033
					'label'       => 'Status',
1034
					'type'        => 'pick',
1035
					'pick_object' => 'post-status',
1036
					'default'     => $this->do_hook( 'default_status_' . $pod_name, pods_var( 'default_status', pods_var_raw( 'options', $pod ), 'draft', null, true ), $pod ),
1037
					'alias'       => array( 'status' )
1038
				),
1039
				'comment_status'        => array(
1040
					'name'    => 'comment_status',
1041
					'label'   => 'Comment Status',
1042
					'type'    => 'text',
1043
					'default' => get_option( 'default_comment_status', 'open' ),
1044
					'alias'   => array(),
1045
					'data'    => array(
1046
						'open'   => __( 'Open', 'pods' ),
1047
						'closed' => __( 'Closed', 'pods' )
1048
					)
1049
				),
1050
				'ping_status'           => array(
1051
					'name'    => 'ping_status',
1052
					'label'   => 'Ping Status',
1053
					'default' => get_option( 'default_ping_status', 'open' ),
1054
					'type'    => 'text',
1055
					'alias'   => array(),
1056
					'data'    => array(
1057
						'open'   => __( 'Open', 'pods' ),
1058
						'closed' => __( 'Closed', 'pods' )
1059
					)
1060
				),
1061
				'post_password'         => array(
1062
					'name'  => 'post_password',
1063
					'label' => 'Password',
1064
					'type'  => 'text',
1065
					'alias' => array()
1066
				),
1067
				'post_name'             => array(
1068
					'name'  => 'post_name',
1069
					'label' => 'Permalink',
1070
					'type'  => 'slug',
1071
					'alias' => array( 'slug', 'permalink' )
1072
				),
1073
				'to_ping'               => array(
1074
					'name'   => 'to_ping',
1075
					'label'  => 'To Ping',
1076
					'type'   => 'text',
1077
					'alias'  => array(),
1078
					'hidden' => true
1079
				),
1080
				'pinged'                => array(
1081
					'name'   => 'pinged',
1082
					'label'  => 'Pinged',
1083
					'type'   => 'text',
1084
					'alias'  => array(),
1085
					'hidden' => true
1086
				),
1087
				'post_modified'         => array(
1088
					'name'   => 'post_modified',
1089
					'label'  => 'Last Modified Date',
1090
					'type'   => 'datetime',
1091
					'alias'  => array( 'modified' ),
1092
					'hidden' => true
1093
				),
1094
				'post_modified_gmt'     => array(
1095
					'name'   => 'post_modified_gmt',
1096
					'label'  => 'Last Modified Date (GMT)',
1097
					'type'   => 'datetime',
1098
					'alias'  => array(),
1099
					'hidden' => true
1100
				),
1101
				'post_content_filtered' => array(
1102
					'name'    => 'post_content_filtered',
1103
					'label'   => 'Content (filtered)',
1104
					'type'    => 'paragraph',
1105
					'alias'   => array(),
1106
					'hidden'  => true,
1107
					'options' => array(
1108
						'paragraph_allow_html'        => 1,
1109
						'paragraph_oembed'            => 1,
1110
						'paragraph_wptexturize'       => 1,
1111
						'paragraph_convert_chars'     => 1,
1112
						'paragraph_wpautop'           => 1,
1113
						'paragraph_allow_shortcode'   => 1,
1114
						'paragraph_allowed_html_tags' => ''
1115
					)
1116
				),
1117
				'post_parent'           => array(
1118
					'name'        => 'post_parent',
1119
					'label'       => 'Parent',
1120
					'type'        => 'pick',
1121
					'pick_object' => 'post_type',
1122
					'pick_val'    => '__current__',
1123
					'alias'       => array( 'parent' ),
1124
					'data'        => array(),
1125
					'hidden'      => true
1126
				),
1127
				'guid'                  => array(
1128
					'name'   => 'guid',
1129
					'label'  => 'GUID',
1130
					'type'   => 'text',
1131
					'alias'  => array(),
1132
					'hidden' => true
1133
				),
1134
				'menu_order'            => array(
1135
					'name'    => 'menu_order',
1136
					'label'   => 'Menu Order',
1137
					'type'    => 'number',
1138
					'alias'   => array(),
1139
					'options' => array(
1140
						'number_format' => '9999.99'
1141
					)
1142
				),
1143
				'post_type'             => array(
1144
					'name'   => 'post_type',
1145
					'label'  => 'Type',
1146
					'type'   => 'text',
1147
					'alias'  => array( 'type' ),
1148
					'hidden' => true
1149
				),
1150
				'post_mime_type'        => array(
1151
					'name'   => 'post_mime_type',
1152
					'label'  => 'Mime Type',
1153
					'type'   => 'text',
1154
					'alias'  => array(),
1155
					'hidden' => true
1156
				),
1157
				'comment_count'         => array(
1158
					'name'   => 'comment_count',
1159
					'label'  => 'Comment Count',
1160
					'type'   => 'number',
1161
					'alias'  => array(),
1162
					'hidden' => true
1163
				),
1164
				'comments'              => array(
1165
					'name'        => 'comments',
1166
					'label'       => 'Comments',
1167
					'type'        => 'comment',
1168
					'pick_object' => 'comment',
1169
					'pick_val'    => 'comment',
1170
					'alias'       => array(),
1171
					'hidden'      => true,
1172
					'options'     => array(
1173
						'comment_format_type' => 'multi'
1174
					)
1175
				)
1176
			);
1177
1178
			if ( ! empty( $pod ) ) {
1179
				$taxonomies = get_object_taxonomies( $pod_name, 'objects' );
1180
1181
				foreach ( $taxonomies as $taxonomy ) {
1182
					$fields[ $taxonomy->name ] = array(
1183
						'name'        => $taxonomy->name,
1184
						'label'       => $taxonomy->labels->name,
1185
						'type'        => 'taxonomy',
1186
						'pick_object' => 'taxonomy',
1187
						'pick_val'    => $taxonomy->name,
1188
						'alias'       => array(),
1189
						'hidden'      => true,
1190
						'options'     => array(
1191
							'taxonomy_format_type' => 'multi'
1192
						)
1193
					);
1194
				}
1195
			}
1196
		} elseif ( 'user' === $object ) {
1197
			$fields = array(
1198
				'ID'              => array(
1199
					'name'    => 'ID',
1200
					'label'   => 'ID',
1201
					'type'    => 'number',
1202
					'alias'   => array( 'id' ),
1203
					'options' => array(
1204
						'number_format' => '9999.99'
1205
					)
1206
				),
1207
				'user_login'      => array(
1208
					'name'    => 'user_login',
1209
					'label'   => 'Title',
1210
					'type'    => 'text',
1211
					'alias'   => array( 'login' ),
1212
					'options' => array(
1213
						'required' => 1
1214
					)
1215
				),
1216
				'user_nicename'   => array(
1217
					'name'  => 'user_nicename',
1218
					'label' => 'Permalink',
1219
					'type'  => 'slug',
1220
					'alias' => array( 'nicename', 'slug', 'permalink' )
1221
				),
1222
				'display_name'    => array(
1223
					'name'  => 'display_name',
1224
					'label' => 'Display Name',
1225
					'type'  => 'text',
1226
					'alias' => array( 'title', 'name' )
1227
				),
1228
				'user_pass'       => array(
1229
					'name'    => 'user_pass',
1230
					'label'   => 'Password',
1231
					'type'    => 'text',
1232
					'alias'   => array( 'password', 'pass' ),
1233
					'options' => array(
1234
						'required'         => 1,
1235
						'text_format_type' => 'password'
1236
					)
1237
				),
1238
				'user_email'      => array(
1239
					'name'    => 'user_email',
1240
					'label'   => 'E-mail',
1241
					'type'    => 'text',
1242
					'alias'   => array( 'email' ),
1243
					'options' => array(
1244
						'required'         => 1,
1245
						'text_format_type' => 'email'
1246
					)
1247
				),
1248
				'user_url'        => array(
1249
					'name'    => 'user_url',
1250
					'label'   => 'URL',
1251
					'type'    => 'text',
1252
					'alias'   => array( 'url', 'website' ),
1253
					'options' => array(
1254
						'required'            => 0,
1255
						'text_format_type'    => 'website',
1256
						'text_format_website' => 'normal'
1257
					)
1258
				),
1259
				'user_registered' => array(
1260
					'name'    => 'user_registered',
1261
					'label'   => 'Registration Date',
1262
					'type'    => 'date',
1263
					'alias'   => array( 'created', 'date', 'registered' ),
1264
					'options' => array(
1265
						'date_format_type' => 'datetime'
1266
					)
1267
				)
1268
			);
1269
		} elseif ( 'comment' === $object ) {
1270
			$fields = array(
1271
				'comment_ID'           => array(
1272
					'name'    => 'comment_ID',
1273
					'label'   => 'ID',
1274
					'type'    => 'number',
1275
					'alias'   => array( 'id', 'ID', 'comment_id' ),
1276
					'options' => array(
1277
						'number_format' => '9999.99'
1278
					)
1279
				),
1280
				'comment_content'      => array(
1281
					'name'  => 'comment_content',
1282
					'label' => 'Content',
1283
					'type'  => 'wysiwyg',
1284
					'alias' => array( 'content' )
1285
				),
1286
				'comment_approved'     => array(
1287
					'name'    => 'comment_approved',
1288
					'label'   => 'Approved',
1289
					'type'    => 'number',
1290
					'alias'   => array( 'approved' ),
1291
					'options' => array(
1292
						'number_format' => '9999.99'
1293
					)
1294
				),
1295
				'comment_post_ID'      => array(
1296
					'name'  => 'comment_post_ID',
1297
					'label' => 'Post',
1298
					'type'  => 'pick',
1299
					'alias' => array( 'post', 'post_id' ),
1300
					'data'  => array()
1301
				),
1302
				'user_id'              => array(
1303
					'name'        => 'user_id',
1304
					'label'       => 'Author',
1305
					'type'        => 'pick',
1306
					'alias'       => array( 'author' ),
1307
					'pick_object' => 'user',
1308
					'data'        => array()
1309
				),
1310
				'comment_date'         => array(
1311
					'name'    => 'comment_date',
1312
					'label'   => 'Date',
1313
					'type'    => 'date',
1314
					'alias'   => array( 'created', 'date' ),
1315
					'options' => array(
1316
						'date_format_type' => 'datetime'
1317
					)
1318
				),
1319
				'comment_author'       => array(
1320
					'name'  => 'comment_author',
1321
					'label' => 'Author',
1322
					'type'  => 'text',
1323
					'alias' => array( 'author' )
1324
				),
1325
				'comment_author_email' => array(
1326
					'name'  => 'comment_author_email',
1327
					'label' => 'Author E-mail',
1328
					'type'  => 'email',
1329
					'alias' => array( 'author_email' )
1330
				),
1331
				'comment_author_url'   => array(
1332
					'name'  => 'comment_author_url',
1333
					'label' => 'Author URL',
1334
					'type'  => 'text',
1335
					'alias' => array( 'author_url' )
1336
				),
1337
				'comment_author_IP'    => array(
1338
					'name'  => 'comment_author_IP',
1339
					'label' => 'Author IP',
1340
					'type'  => 'text',
1341
					'alias' => array( 'author_IP' )
1342
				),
1343
				'comment_type'         => array(
1344
					'name'   => 'comment_type',
1345
					'label'  => 'Type',
1346
					'type'   => 'text',
1347
					'alias'  => array( 'type' ),
1348
					'hidden' => true
1349
				),
1350
				'comment_parent'       => array(
1351
					'name'        => 'comment_parent',
1352
					'label'       => 'Parent',
1353
					'type'        => 'pick',
1354
					'pick_object' => 'comment',
1355
					'pick_val'    => '__current__',
1356
					'alias'       => array( 'parent' ),
1357
					'data'        => array(),
1358
					'hidden'      => true
1359
				)
1360
			);
1361
		} elseif ( 'taxonomy' === $object ) {
1362
			$fields = array(
1363
				'term_id'          => array(
1364
					'name'    => 'term_id',
1365
					'label'   => 'ID',
1366
					'type'    => 'number',
1367
					'alias'   => array( 'id', 'ID' ),
1368
					'options' => array(
1369
						'number_format' => '9999.99'
1370
					)
1371
				),
1372
				'name'             => array(
1373
					'name'  => 'name',
1374
					'label' => 'Title',
1375
					'type'  => 'text',
1376
					'alias' => array( 'title' )
1377
				),
1378
				'slug'             => array(
1379
					'name'  => 'slug',
1380
					'label' => 'Permalink',
1381
					'type'  => 'slug',
1382
					'alias' => array( 'permalink' )
1383
				),
1384
				'description'      => array(
1385
					'name'  => 'description',
1386
					'label' => 'Description',
1387
					'type'  => 'wysiwyg',
1388
					'alias' => array( 'content' )
1389
				),
1390
				'taxonomy'         => array(
1391
					'name'  => 'taxonomy',
1392
					'label' => 'Taxonomy',
1393
					'type'  => 'text',
1394
					'alias' => array()
1395
				),
1396
				'parent'           => array(
1397
					'name'        => 'parent',
1398
					'label'       => 'Parent',
1399
					'type'        => 'pick',
1400
					'pick_object' => 'taxonomy',
1401
					'pick_val'    => '__current__',
1402
					'alias'       => array( 'parent' ),
1403
					'data'        => array(),
1404
					'hidden'      => true
1405
				),
1406
				'term_taxonomy_id' => array(
1407
					'name'    => 'term_taxonomy_id',
1408
					'label'   => 'Term Taxonomy ID',
1409
					'type'    => 'number',
1410
					'alias'   => array(),
1411
					'hidden'  => true,
1412
					'options' => array(
1413
						'number_format' => '9999.99'
1414
					)
1415
				),
1416
				'term_group'       => array(
1417
					'name'    => 'term_group',
1418
					'label'   => 'Term Group',
1419
					'type'    => 'number',
1420
					'alias'   => array( 'group' ),
1421
					'hidden'  => true,
1422
					'options' => array(
1423
						'number_format' => '9999.99'
1424
					)
1425
				),
1426
				'count'            => array(
1427
					'name'    => 'count',
1428
					'label'   => 'Count',
1429
					'type'    => 'number',
1430
					'alias'   => array(),
1431
					'hidden'  => true,
1432
					'options' => array(
1433
						'number_format' => '9999.99'
1434
					)
1435
				)
1436
			);
1437
		}
1438
1439
		$fields = $this->do_hook( 'get_wp_object_fields', $fields, $object, $pod );
1440
1441
		foreach ( $fields as $field => $options ) {
1442
			if ( ! isset( $options['alias'] ) ) {
1443
				$options['alias'] = array();
1444
			} else {
1445
				$options['alias'] = (array) $options['alias'];
1446
			}
1447
1448
			if ( ! isset( $options['name'] ) ) {
1449
				$options['name'] = $field;
1450
			}
1451
1452
			$fields[ $field ] = $options;
1453
		}
1454
1455
		$fields = PodsForm::fields_setup( $fields );
1456
1457
		if ( did_action( 'init' ) && pods_api_cache() ) {
1458
			pods_transient_set( trim( 'pods_api_object_fields_' . $object . $pod_name . '_', '_' ), $fields );
1459
		}
1460
1461
		return $fields;
1462
	}
1463
1464
	/**
1465
	 *
1466
	 * @see   PodsAPI::save_pod
1467
	 *
1468
	 * Add a Pod via the Wizard
1469
	 *
1470
	 * $params['create_extend'] string Create or Extend a Content Type
1471
	 * $params['create_pod_type'] string Pod Type (for Creating)
1472
	 * $params['create_name'] string Pod Name (for Creating)
1473
	 * $params['create_label_plural'] string Plural Label (for Creating)
1474
	 * $params['create_label_singular'] string Singular Label (for Creating)
1475
	 * $params['create_storage'] string Storage Type (for Creating Post Types)
1476
	 * $params['create_storage_taxonomy'] string Storage Type (for Creating Taxonomies)
1477
	 * $params['extend_pod_type'] string Pod Type (for Extending)
1478
	 * $params['extend_post_type'] string Post Type (for Extending Post Types)
1479
	 * $params['extend_taxonomy'] string Taxonomy (for Extending Taxonomies)
1480
	 * $params['extend_storage'] string Storage Type (for Extending Post Types / Users / Comments)
1481
	 *
1482
	 * @param array $params An associative array of parameters
1483
	 *
1484
	 * @return bool|int Pod ID
1485
	 * @since 2.0
1486
	 */
1487
	public function add_pod( $params ) {
1488
1489
		$defaults = array(
1490
			'create_extend'   => 'create',
1491
			'create_pod_type' => 'post_type',
1492
1493
			'create_name'             => '',
1494
			'create_label_singular'   => '',
1495
			'create_label_plural'     => '',
1496
			'create_storage'          => 'meta',
1497
			'create_storage_taxonomy' => ( function_exists( 'get_term_meta' ) ? 'meta' : 'none' ),
1498
1499
			'create_setting_name'  => '',
1500
			'create_label_title'   => '',
1501
			'create_label_menu'    => '',
1502
			'create_menu_location' => 'settings',
1503
1504
			'extend_pod_type'         => 'post_type',
1505
			'extend_post_type'        => 'post',
1506
			'extend_taxonomy'         => 'category',
1507
			'extend_table'            => '',
1508
			'extend_storage'          => 'meta',
1509
			'extend_storage_taxonomy' => ( function_exists( 'get_term_meta' ) ? 'meta' : 'table' ),
1510
		);
1511
1512
		$params = (object) array_merge( $defaults, (array) $params );
1513
1514
		if ( empty( $params->create_extend ) || ! in_array( $params->create_extend, array( 'create', 'extend' ) ) ) {
1515
			return pods_error( __( 'Please choose whether to Create or Extend a Content Type', 'pods' ), $this );
1516
		}
1517
1518
		$pod_params = array(
1519
			'name'    => '',
1520
			'label'   => '',
1521
			'type'    => '',
1522
			'storage' => 'table',
1523
			'object'  => '',
1524
			'options' => array()
1525
		);
1526
1527
		if ( 'create' === $params->create_extend ) {
1528
			$label = ucwords( str_replace( '_', ' ', $params->create_name ) );
1529
1530
			if ( ! empty( $params->create_label_singular ) ) {
1531
				$label = $params->create_label_singular;
1532
			}
1533
1534
			$pod_params['name']    = $params->create_name;
1535
			$pod_params['label']   = ( ! empty( $params->create_label_plural ) ? $params->create_label_plural : $label );
1536
			$pod_params['type']    = $params->create_pod_type;
1537
			$pod_params['options'] = array(
1538
				'label_singular' => ( ! empty( $params->create_label_singular ) ? $params->create_label_singular : $pod_params['label'] ),
1539
				'public'         => 1,
1540
				'show_ui'        => 1
1541
			);
1542
1543
			// Auto-generate name if not provided
1544
			if ( empty( $pod_params['name'] ) && ! empty( $pod_params['options']['label_singular'] ) ) {
1545
				$pod_params['name'] = pods_clean_name( $pod_params['options']['label_singular'] );
1546
			}
1547
1548
			if ( 'post_type' === $pod_params['type'] ) {
1549
				if ( empty( $pod_params['name'] ) ) {
1550
					return pods_error( 'Please enter a Name for this Pod', $this );
1551
				}
1552
1553
				$pod_params['storage'] = $params->create_storage;
1554
1555
				if ( pods_tableless() ) {
1556
					$pod_params['storage'] = 'meta';
1557
				}
1558
			} elseif ( 'taxonomy' === $pod_params['type'] ) {
1559
				if ( empty( $pod_params['name'] ) ) {
1560
					return pods_error( 'Please enter a Name for this Pod', $this );
1561
				}
1562
1563
				$pod_params['storage'] = $params->create_storage_taxonomy;
1564
1565
				if ( pods_tableless() ) {
1566
					$pod_params['storage'] = ( function_exists( 'get_term_meta' ) ? 'meta' : 'none' );
1567
				}
1568
1569
				$pod_params['options']['hierarchical'] = 1;
1570
			} elseif ( 'pod' === $pod_params['type'] ) {
1571
				if ( empty( $pod_params['name'] ) ) {
1572
					return pods_error( 'Please enter a Name for this Pod', $this );
1573
				}
1574
1575
				if ( pods_tableless() ) {
1576
					$pod_params['type']    = 'post_type';
1577
					$pod_params['storage'] = 'meta';
1578
				}
1579
			} elseif ( 'settings' === $pod_params['type'] ) {
1580
				$pod_params['name']    = $params->create_setting_name;
1581
				$pod_params['label']   = ( ! empty( $params->create_label_title ) ? $params->create_label_title : ucwords( str_replace( '_', ' ', $params->create_setting_name ) ) );
1582
				$pod_params['options'] = array(
1583
					'menu_name'     => ( ! empty( $params->create_label_menu ) ? $params->create_label_menu : $pod_params['label'] ),
1584
					'menu_location' => $params->create_menu_location
1585
				);
1586
				$pod_params['storage'] = 'none';
1587
1588
				// Auto-generate name if not provided
1589
				if ( empty( $pod_params['name'] ) && ! empty( $pod_params['label'] ) ) {
1590
					$pod_params['name'] = pods_clean_name( $pod_params['label'] );
1591
				}
1592
1593
				if ( empty( $pod_params['name'] ) ) {
1594
					return pods_error( 'Please enter a Name for this Pod', $this );
1595
				}
1596
			}
1597
		} elseif ( 'extend' === $params->create_extend ) {
1598
			$pod_params['type'] = $params->extend_pod_type;
1599
1600
			if ( 'post_type' === $pod_params['type'] ) {
1601
				$pod_params['storage'] = $params->extend_storage;
1602
1603
				if ( pods_tableless() ) {
1604
					$pod_params['storage'] = 'meta';
1605
				}
1606
1607
				$pod_params['name'] = $params->extend_post_type;
1608
			} elseif ( 'taxonomy' === $pod_params['type'] ) {
1609
				$pod_params['storage'] = $params->extend_storage_taxonomy;
1610
1611
				if ( pods_tableless() ) {
1612
					$pod_params['storage'] = ( function_exists( 'get_term_meta' ) ? 'meta' : 'none' );
1613
				}
1614
1615
				$pod_params['name'] = $params->extend_taxonomy;
1616
			} elseif ( 'table' === $pod_params['type'] ) {
1617
				$pod_params['storage'] = 'table';
1618
				$pod_params['name']    = $params->extend_table;
1619
			} else {
1620
				$pod_params['storage'] = $params->extend_storage;
1621
1622
				if ( pods_tableless() ) {
1623
					$pod_params['storage'] = 'meta';
1624
				}
1625
1626
				$pod_params['name'] = $params->extend_pod_type;
1627
			}
1628
1629
			$pod_params['label']  = ucwords( str_replace( '_', ' ', $pod_params['name'] ) );
1630
			$pod_params['object'] = $pod_params['name'];
1631
		}
1632
1633
		if ( empty( $pod_params['object'] ) ) {
1634
			if ( 'post_type' === $pod_params['type'] ) {
1635
				$check = get_post_type_object( $pod_params['name'] );
1636
1637
				if ( ! empty( $check ) ) {
1638
					return pods_error( sprintf( __( 'Post Type %s already exists, try extending it instead', 'pods' ), $pod_params['name'] ), $this );
1639
				}
1640
1641
				$pod_params['options']['supports_title']  = 1;
1642
				$pod_params['options']['supports_editor'] = 1;
1643
			} elseif ( 'taxonomy' === $pod_params['type'] ) {
1644
				$check = get_taxonomy( $pod_params['name'] );
1645
1646
				if ( ! empty( $check ) ) {
1647
					return pods_error( sprintf( __( 'Taxonomy %s already exists, try extending it instead', 'pods' ), $pod_params['name'] ), $this );
1648
				}
1649
			}
1650
		}
1651
1652
		if ( ! empty( $pod_params ) ) {
1653
			return $this->save_pod( $pod_params );
1654
		}
1655
1656
		return false;
1657
	}
1658
1659
	/**
1660
	 * Add or edit a Pod
1661
	 *
1662
	 * $params['id'] int The Pod ID
1663
	 * $params['name'] string The Pod name
1664
	 * $params['label'] string The Pod label
1665
	 * $params['type'] string The Pod type
1666
	 * $params['object'] string The object being extended (if any)
1667
	 * $params['storage'] string The Pod storage
1668
	 * $params['options'] array Options
1669
	 *
1670
	 * @param array    $params    An associative array of parameters
1671
	 * @param bool     $sanitized (optional) Decides whether the params have been sanitized before being passed, will
1672
	 *                            sanitize them if false.
1673
	 * @param bool|int $db        (optional) Whether to save into the DB or just return Pod array.
1674
	 *
1675
	 * @return int Pod ID
1676
	 * @since 1.7.9
1677
	 */
1678
	public function save_pod( $params, $sanitized = false, $db = true ) {
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $db. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
1679
1680
		$tableless_field_types    = PodsForm::tableless_field_types();
0 ignored issues
show
Comprehensibility Naming introduced by
The variable name $tableless_field_types exceeds the maximum configured length of 20.

Very long variable names usually make code harder to read. It is therefore recommended not to make variable names too verbose.

Loading history...
1681
		$simple_tableless_objects = PodsForm::simple_tableless_objects();
0 ignored issues
show
Comprehensibility Naming introduced by
The variable name $simple_tableless_objects exceeds the maximum configured length of 20.

Very long variable names usually make code harder to read. It is therefore recommended not to make variable names too verbose.

Loading history...
1682
1683
		$load_params = (object) $params;
1684
1685
		if ( isset( $load_params->id ) && isset( $load_params->name ) ) {
1686
			unset( $load_params->name );
1687
		}
1688
1689
		if ( isset( $load_params->old_name ) ) {
1690
			$load_params->name = $load_params->old_name;
1691
		}
1692
1693
		$load_params->table_info = true;
1694
1695
		$pod = $this->load_pod( $load_params, false );
1696
1697
		$params = (object) $params;
1698
1699
		if ( false === $sanitized ) {
1700
			$params = pods_sanitize( $params );
1701
		}
1702
1703
		$old_id = $old_name = $old_storage = null;
1704
1705
		$old_fields = $old_options = array();
1706
1707
		if ( isset( $params->name ) && ! isset( $params->object ) ) {
1708
			$params->name = pods_clean_name( $params->name );
1709
		}
1710
1711
		if ( ! empty( $pod ) ) {
1712
			if ( isset( $params->id ) && 0 < $params->id ) {
1713
				$old_id = $params->id;
1714
			}
1715
1716
			$params->id = $pod['id'];
1717
1718
			$old_name    = $pod['name'];
1719
			$old_storage = $pod['storage'];
1720
			$old_fields  = $pod['fields'];
1721
			$old_options = $pod['options'];
1722
1723
			if ( ! isset( $params->name ) && empty( $params->name ) ) {
1724
				$params->name = $pod['name'];
1725
			}
1726
1727
			if ( $old_name !== $params->name && false !== $this->pod_exists( array( 'name' => $params->name ) ) ) {
1728
				return pods_error( sprintf( __( 'Pod %s already exists, you cannot rename %s to that', 'pods' ), $params->name, $old_name ), $this );
1729
			}
1730
1731
			if ( $old_name !== $params->name && in_array( $pod['type'], array(
1732
					'user',
1733
					'comment',
1734
					'media'
1735
				) ) && in_array( $pod['object'], array( 'user', 'comment', 'media' ) ) ) {
1736
				return pods_error( sprintf( __( 'Pod %s cannot be renamed, it extends an existing WP Object', 'pods' ), $old_name ), $this );
1737
			}
1738
1739
			if ( $old_name !== $params->name && in_array( $pod['type'], array(
1740
					'post_type',
1741
					'taxonomy'
1742
				) ) && ! empty( $pod['object'] ) && $pod['object'] == $old_name ) {
1743
				return pods_error( sprintf( __( 'Pod %s cannot be renamed, it extends an existing WP Object', 'pods' ), $old_name ), $this );
1744
			}
1745
1746
			if ( $old_id != $params->id ) {
1747
				if ( $params->type == $pod['type'] && isset( $params->object ) && $params->object == $pod['object'] ) {
1748
					return pods_error( sprintf( __( 'Pod using %s already exists, you can not reuse an object across multiple pods', 'pods' ), $params->object ), $this );
1749
				} else {
1750
					return pods_error( sprintf( __( 'Pod %s already exists', 'pods' ), $params->name ), $this );
1751
				}
1752
			}
1753
		} elseif ( in_array( $params->name, array(
1754
				'order',
1755
				'orderby',
1756
				'post_type'
1757
			) ) && 'post_type' === pods_var( 'type', $params ) ) {
1758
			return pods_error( sprintf( 'There are certain names that a Custom Post Types cannot be named and unfortunately, %s is one of them.', $params->name ), $this );
1759
		} else {
1760
			$pod = array(
1761
				'id'          => 0,
1762
				'name'        => $params->name,
1763
				'label'       => $params->name,
1764
				'description' => '',
1765
				'type'        => 'pod',
1766
				'storage'     => 'table',
1767
				'object'      => '',
1768
				'alias'       => '',
1769
				'options'     => array(),
1770
				'fields'      => array()
1771
			);
1772
		}
1773
1774
		// Blank out fields and options for AJAX calls (everything should be sent to it for a full overwrite)
1775
		if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
1776
			$pod['fields']  = array();
1777
			$pod['options'] = array();
1778
		}
1779
1780
		// Setup options
1781
		$options = get_object_vars( $params );
1782
1783
		if ( isset( $options['method'] ) ) {
1784
			unset( $options['method'] );
1785
		}
1786
1787
		$options_ignore = array(
1788
			'object_type',
1789
			'object_name',
1790
			'table',
1791
			'meta_table',
1792
			'pod_table',
1793
			'field_id',
1794
			'field_index',
1795
			'field_slug',
1796
			'field_type',
1797
			'field_parent',
1798
			'field_parent_select',
1799
			'meta_field_id',
1800
			'meta_field_index',
1801
			'meta_field_value',
1802
			'pod_field_id',
1803
			'pod_field_index',
1804
			'object_fields',
1805
			'join',
1806
			'where',
1807
			'where_default',
1808
			'orderby',
1809
			'pod',
1810
			'recurse',
1811
			'table_info',
1812
			'attributes',
1813
			'group',
1814
			'grouped',
1815
			'developer_mode',
1816
			'dependency',
1817
			'depends-on',
1818
			'excludes-on'
1819
		);
1820
1821
		foreach ( $options_ignore as $ignore ) {
1822
			if ( isset( $options[ $ignore ] ) ) {
1823
				unset( $options[ $ignore ] );
1824
			}
1825
		}
1826
1827
		$exclude = array(
1828
			'id',
1829
			'name',
1830
			'label',
1831
			'description',
1832
			'type',
1833
			'storage',
1834
			'object',
1835
			'alias',
1836
			'options',
1837
			'fields'
1838
		);
1839
1840
		foreach ( $exclude as $k => $exclude_field ) {
1841
			$aliases = array( $exclude_field );
1842
1843
			if ( is_array( $exclude_field ) ) {
1844
				$aliases       = array_merge( array( $k ), $exclude_field );
1845
				$exclude_field = $k;
1846
			}
1847
1848
			foreach ( $aliases as $alias ) {
1849
				if ( isset( $options[ $alias ] ) ) {
1850
					$pod[ $exclude_field ] = pods_trim( $options[ $alias ] );
1851
1852
					unset( $options[ $alias ] );
1853
				}
1854
			}
1855
		}
1856
1857
		if ( pods_tableless() && ! in_array( $pod['type'], array( 'settings', 'table' ) ) ) {
1858
			if ( 'pod' === $pod['type'] ) {
1859
				$pod['type'] = 'post_type';
1860
			}
1861
1862
			if ( 'table' === $pod['storage'] ) {
1863
				if ( 'taxonomy' === $pod['type'] && ! function_exists( 'get_term_meta' ) ) {
1864
					$pod['storage'] = 'none';
1865
				} else {
1866
					$pod['storage'] = 'meta';
1867
				}
1868
			}
1869
		}
1870
1871
		$pod['options']['type']    = $pod['type'];
1872
		$pod['options']['storage'] = $pod['storage'];
1873
		$pod['options']['object']  = $pod['object'];
1874
		$pod['options']['alias']   = $pod['alias'];
1875
1876
		$pod['options'] = array_merge( $pod['options'], $options );
1877
1878
		/**
1879
		 * @var WP_Query
1880
		 */
1881
		global $wp_query;
1882
1883
		$reserved_query_vars = array(
1884
			'post_type',
1885
			'taxonomy',
1886
			'output'
1887
		);
1888
1889
		if ( is_object( $wp_query ) ) {
1890
			$reserved_query_vars = array_merge( $reserved_query_vars, array_keys( $wp_query->fill_query_vars( array() ) ) );
1891
		}
1892
1893
		if ( isset( $pod['options']['query_var_string'] ) ) {
1894
			if ( in_array( $pod['options']['query_var_string'], $reserved_query_vars ) ) {
1895
				$pod['options']['query_var_string'] = $pod['options']['type'] . '_' . $pod['options']['query_var_string'];
1896
			}
1897
		}
1898
1899
		if ( isset( $pod['options']['query_var'] ) ) {
1900
			if ( in_array( $pod['options']['query_var'], $reserved_query_vars ) ) {
1901
				$pod['options']['query_var'] = $pod['options']['type'] . '_' . $pod['options']['query_var'];
1902
			}
1903
		}
1904
1905
		if ( strlen( $pod['label'] ) < 1 ) {
1906
			$pod['label'] = $pod['name'];
1907
		}
1908
1909
		if ( 'post_type' === $pod['type'] ) {
1910
			// Max length for post types are 20 characters
1911
			$pod['name'] = substr( $pod['name'], 0, 20 );
1912
		} elseif ( 'taxonomy' === $pod['type'] ) {
1913
			// Max length for taxonomies are 32 characters
1914
			$pod['name'] = substr( $pod['name'], 0, 32 );
1915
		}
1916
1917
		$params->id   = $pod['id'];
1918
		$params->name = $pod['name'];
1919
1920
		if ( null !== $old_name && $old_name !== $params->name && empty( $pod['object'] ) ) {
1921
			if ( 'post_type' === $pod['type'] ) {
1922
				$check = get_post_type_object( $params->name );
1923
1924
				if ( ! empty( $check ) ) {
1925
					return pods_error( sprintf( __( 'Post Type %s already exists, you cannot rename %s to that', 'pods' ), $params->name, $old_name ), $this );
1926
				}
1927
			} elseif ( 'taxonomy' === $pod['type'] ) {
1928
				$check = get_taxonomy( $params->name );
1929
1930
				if ( ! empty( $check ) ) {
1931
					return pods_error( sprintf( __( 'Taxonomy %s already exists, you cannot rename %s to that', 'pods' ), $params->name, $old_name ), $this );
1932
				}
1933
			}
1934
		}
1935
1936
		$field_table_operation = true;
0 ignored issues
show
Comprehensibility Naming introduced by
The variable name $field_table_operation exceeds the maximum configured length of 20.

Very long variable names usually make code harder to read. It is therefore recommended not to make variable names too verbose.

Loading history...
1937
1938
		// Add new pod
1939
		if ( empty( $params->id ) ) {
1940
			if ( strlen( $params->name ) < 1 ) {
1941
				return pods_error( __( 'Pod name cannot be empty', 'pods' ), $this );
1942
			}
1943
1944
			$post_data = array(
1945
				'post_name'    => $pod['name'],
1946
				'post_title'   => $pod['label'],
1947
				'post_content' => $pod['description'],
1948
				'post_type'    => '_pods_pod',
1949
				'post_status'  => 'publish'
1950
			);
1951
1952
			if ( 'pod' === $pod['type'] && ( ! is_array( $pod['fields'] ) || empty( $pod['fields'] ) ) ) {
1953
				$pod['fields'] = array();
1954
1955
				$pod['fields']['name'] = array(
1956
					'name'    => 'name',
1957
					'label'   => 'Name',
1958
					'type'    => 'text',
1959
					'options' => array(
1960
						'required' => '1'
1961
					)
1962
				);
1963
1964
				$pod['fields']['created'] = array(
1965
					'name'    => 'created',
1966
					'label'   => 'Date Created',
1967
					'type'    => 'datetime',
1968
					'options' => array(
1969
						'datetime_format'      => 'ymd_slash',
1970
						'datetime_time_type'   => '12',
1971
						'datetime_time_format' => 'h_mm_ss_A'
1972
					)
1973
				);
1974
1975
				$pod['fields']['modified'] = array(
1976
					'name'    => 'modified',
1977
					'label'   => 'Date Modified',
1978
					'type'    => 'datetime',
1979
					'options' => array(
1980
						'datetime_format'      => 'ymd_slash',
1981
						'datetime_time_type'   => '12',
1982
						'datetime_time_format' => 'h_mm_ss_A'
1983
					)
1984
				);
1985
1986
				$pod['fields']['author'] = array(
1987
					'name'        => 'author',
1988
					'label'       => 'Author',
1989
					'type'        => 'pick',
1990
					'pick_object' => 'user',
1991
					'options'     => array(
1992
						'pick_format_type'   => 'single',
1993
						'pick_format_single' => 'autocomplete',
1994
						'default_value'      => '{@user.ID}'
1995
					)
1996
				);
1997
1998
				$pod['fields']['permalink'] = array(
1999
					'name'        => 'permalink',
2000
					'label'       => 'Permalink',
2001
					'type'        => 'slug',
2002
					'description' => 'Leave blank to auto-generate from Name'
2003
				);
2004
2005
				if ( ! isset( $pod['options']['pod_index'] ) ) {
2006
					$pod['options']['pod_index'] = 'name';
2007
				}
2008
			}
2009
2010
			$pod = $this->do_hook( 'save_pod_default_pod', $pod, $params, $sanitized, $db );
2011
2012
			$field_table_operation = false;
2013
		} else {
2014
			$post_data = array(
2015
				'ID'           => $pod['id'],
2016
				'post_name'    => $pod['name'],
2017
				'post_title'   => $pod['label'],
2018
				'post_content' => $pod['description'],
2019
				'post_status'  => 'publish'
2020
			);
2021
		}
2022
2023
		if ( true === $db ) {
2024
			if ( ! has_filter( 'wp_unique_post_slug', array( $this, 'save_slug_fix' ) ) ) {
2025
				add_filter( 'wp_unique_post_slug', array( $this, 'save_slug_fix' ), 100, 6 );
2026
			}
2027
2028
			$conflicted = false;
2029
2030
			// Headway compatibility fix
2031
			if ( has_filter( 'wp_insert_post_data', 'headway_clean_slug', 0 ) ) {
2032
				remove_filter( 'wp_insert_post_data', 'headway_clean_slug', 0 );
2033
2034
				$conflicted = true;
2035
			}
2036
2037
			$params->id = $this->save_wp_object( 'post', $post_data, $pod['options'], true, true );
2038
2039
			if ( $conflicted ) {
2040
				add_filter( 'wp_insert_post_data', 'headway_clean_slug', 0 );
2041
			}
2042
2043
			if ( false === $params->id ) {
2044
				return pods_error( __( 'Cannot save Pod', 'pods' ), $this );
2045
			}
2046
		} elseif ( empty( $params->id ) ) {
2047
			$params->id = (int) $db;
2048
		}
2049
2050
		$pod['id'] = $params->id;
2051
2052
		// Setup / update tables
2053
		if ( 'table' !== $pod['type'] && 'table' === $pod['storage'] && $old_storage !== $pod['storage'] && $db ) {
2054
			$definitions = array( "`id` BIGINT(20) UNSIGNED AUTO_INCREMENT PRIMARY KEY" );
2055
2056
			$defined_fields = array();
2057
2058
			foreach ( $pod['fields'] as $field ) {
2059
				if ( ! is_array( $field ) || ! isset( $field['name'] ) || in_array( $field['name'], $defined_fields ) ) {
2060
					continue;
2061
				}
2062
2063
				$defined_fields[] = $field['name'];
2064
2065
				if ( ! in_array( $field['type'], $tableless_field_types ) || ( 'pick' === $field['type'] && in_array( pods_var( 'pick_object', $field ), $simple_tableless_objects ) ) ) {
2066
					$definition = $this->get_field_definition( $field['type'], array_merge( $field, pods_var_raw( 'options', $field, array() ) ) );
2067
2068
					if ( 0 < strlen( $definition ) ) {
2069
						$definitions[] = "`{$field['name']}` " . $definition;
2070
					}
2071
				}
2072
			}
2073
2074
			pods_query( "DROP TABLE IF EXISTS `@wp_pods_{$params->name}`" );
2075
2076
			$result = pods_query( "CREATE TABLE `@wp_pods_{$params->name}` (" . implode( ', ', $definitions ) . ") DEFAULT CHARSET utf8", $this );
2077
2078
			if ( empty( $result ) ) {
2079
				return pods_error( __( 'Cannot add Database Table for Pod', 'pods' ), $this );
2080
			}
2081
2082
		} elseif ( 'table' !== $pod['type'] && 'table' === $pod['storage'] && $pod['storage'] == $old_storage && null !== $old_name && $old_name !== $params->name && $db ) {
2083
			$result = pods_query( "ALTER TABLE `@wp_pods_{$old_name}` RENAME `@wp_pods_{$params->name}`", $this );
2084
2085
			if ( empty( $result ) ) {
2086
				return pods_error( __( 'Cannot update Database Table for Pod', 'pods' ), $this );
2087
			}
2088
		}
2089
2090
		/**
2091
		 * @var $wpdb wpdb
2092
		 */
2093
		global $wpdb;
2094
2095
		if ( null !== $old_name && $old_name !== $params->name && $db ) {
2096
			// Rename items in the DB pointed at the old WP Object names
2097
			if ( 'post_type' === $pod['type'] && empty( $pod['object'] ) ) {
2098
				$this->rename_wp_object_type( 'post', $old_name, $params->name );
2099
			} elseif ( 'taxonomy' === $pod['type'] && empty( $pod['object'] ) ) {
2100
				$this->rename_wp_object_type( 'taxonomy', $old_name, $params->name );
2101
			} elseif ( 'comment' === $pod['type'] && empty( $pod['object'] ) ) {
2102
				$this->rename_wp_object_type( 'comment', $old_name, $params->name );
2103
			} elseif ( 'settings' === $pod['type'] ) {
2104
				$this->rename_wp_object_type( 'settings', $old_name, $params->name );
2105
			}
2106
2107
			// Sync any related fields if the name has changed
2108
			$fields = pods_query( "
2109
                SELECT `p`.`ID`
2110
                FROM `{$wpdb->posts}` AS `p`
2111
                LEFT JOIN `{$wpdb->postmeta}` AS `pm` ON `pm`.`post_id` = `p`.`ID`
2112
                LEFT JOIN `{$wpdb->postmeta}` AS `pm2` ON `pm2`.`post_id` = `p`.`ID`
2113
                WHERE
2114
                    `p`.`post_type` = '_pods_field'
2115
                    AND `pm`.`meta_key` = 'pick_object'
2116
                    AND (
2117
                    	`pm`.`meta_value` = 'pod'
2118
                    	OR `pm`.`meta_value` = '" . $pod['type'] . "'
2119
					)
2120
                    AND `pm2`.`meta_key` = 'pick_val'
2121
                    AND `pm2`.`meta_value` = '{$old_name}'
2122
            " );
2123
2124
			if ( ! empty( $fields ) ) {
2125
				foreach ( $fields as $field ) {
2126
					update_post_meta( $field->ID, 'pick_object', $pod['type'] );
2127
					update_post_meta( $field->ID, 'pick_val', $params->name );
2128
				}
2129
			}
2130
2131
			$fields = pods_query( "
2132
                SELECT `p`.`ID`
2133
                FROM `{$wpdb->posts}` AS `p`
2134
                LEFT JOIN `{$wpdb->postmeta}` AS `pm` ON `pm`.`post_id` = `p`.`ID`
2135
                WHERE
2136
                    `p`.`post_type` = '_pods_field'
2137
                    AND `pm`.`meta_key` = 'pick_object'
2138
                    AND (
2139
                    	`pm`.`meta_value` = 'pod-{$old_name}'
2140
                    	OR `pm`.`meta_value` = '" . $pod['type'] . "-{$old_name}'
2141
					)
2142
            " );
2143
2144
			if ( ! empty( $fields ) ) {
2145
				foreach ( $fields as $field ) {
2146
					update_post_meta( $field->ID, 'pick_object', $pod['type'] );
2147
					update_post_meta( $field->ID, 'pick_val', $params->name );
2148
				}
2149
			}
2150
		}
2151
2152
		// Sync built-in options for post types and taxonomies
2153
		if ( in_array( $pod['type'], array( 'post_type', 'taxonomy' ) ) && empty( $pod['object'] ) && $db ) {
2154
			// Build list of 'built_in' for later
2155
			$built_in = array();
2156
2157
			foreach ( $pod['options'] as $key => $val ) {
2158
				if ( false === strpos( $key, 'built_in_' ) ) {
2159
					continue;
2160
				} elseif ( false !== strpos( $key, 'built_in_post_types_' ) ) {
2161
					$built_in_type = 'post_type';
2162
				} elseif ( false !== strpos( $key, 'built_in_taxonomies_' ) ) {
2163
					$built_in_type = 'taxonomy';
2164
				} else {
2165
					continue;
2166
				}
2167
2168
				if ( $built_in_type == $pod['type'] ) {
2169
					continue;
2170
				}
2171
2172
				if ( ! isset( $built_in[ $built_in_type ] ) ) {
2173
					$built_in[ $built_in_type ] = array();
2174
				}
2175
2176
				$built_in_object = str_replace( array( 'built_in_post_types_', 'built_in_taxonomies_' ), '', $key );
2177
2178
				$built_in[ $built_in_type ][ $built_in_object ] = (int) $val;
2179
			}
2180
2181
			$lookup_option = $lookup_built_in = false;
2182
2183
			$lookup_name = $pod['name'];
2184
2185
			if ( 'post_type' === $pod['type'] ) {
2186
				$lookup_option   = 'built_in_post_types_' . $lookup_name;
2187
				$lookup_built_in = 'taxonomy';
2188
			} elseif ( 'taxonomy' === $pod['type'] ) {
2189
				$lookup_option   = 'built_in_taxonomies_' . $lookup_name;
2190
				$lookup_built_in = 'post_type';
2191
			}
2192
2193
			if ( ! empty( $lookup_option ) && ! empty( $lookup_built_in ) && isset( $built_in[ $lookup_built_in ] ) ) {
2194
				foreach ( $built_in[ $lookup_built_in ] as $built_in_object => $val ) {
2195
					$search_val = 1;
2196
2197
					if ( 1 == $val ) {
2198
						$search_val = 0;
2199
					}
2200
2201
					$query = "SELECT p.ID FROM {$wpdb->posts} AS p
2202
                                LEFT JOIN {$wpdb->postmeta} AS pm ON pm.post_id = p.ID AND pm.meta_key = '{$lookup_option}'
2203
                                LEFT JOIN {$wpdb->postmeta} AS pm2 ON pm2.post_id = p.ID AND pm2.meta_key = 'type' AND pm2.meta_value = '{$lookup_built_in}'
2204
                                LEFT JOIN {$wpdb->postmeta} AS pm3 ON pm3.post_id = p.ID AND pm3.meta_key = 'object' AND pm3.meta_value = ''
2205
                                WHERE p.post_type = '_pods_pod' AND p.post_name = '{$built_in_object}'
2206
                                    AND pm2.meta_id IS NOT NULL
2207
                                    AND ( pm.meta_id IS NULL OR pm.meta_value = {$search_val} )";
2208
2209
					$results = pods_query( $query );
2210
2211
					if ( ! empty( $results ) ) {
2212
						foreach ( $results as $the_pod ) {
2213
							delete_post_meta( $the_pod->ID, $lookup_option );
2214
2215
							add_post_meta( $the_pod->ID, $lookup_option, $val );
2216
						}
2217
					}
2218
				}
2219
			}
2220
		}
2221
2222
		$saved  = array();
2223
		$errors = array();
2224
2225
		$field_index_change = false;
2226
		$field_index_id     = 0;
2227
2228
		$id_required = false;
2229
2230
		$field_index = pods_var( 'pod_index', $pod['options'], 'id', null, true );
2231
2232
		if ( 'pod' === $pod['type'] && ! empty( $pod['fields'] ) && isset( $pod['fields'][ $field_index ] ) ) {
2233
			$field_index_id = $pod['fields'][ $field_index ];
2234
		}
2235
2236
		if ( isset( $params->fields ) || ( defined( 'DOING_AJAX' ) && DOING_AJAX ) ) {
2237
			$fields = array();
2238
2239
			if ( isset( $params->fields ) ) {
2240
				$params->fields = (array) $params->fields;
2241
2242
				$weight = 0;
2243
2244
				foreach ( $params->fields as $field ) {
2245
					if ( ! is_array( $field ) || ! isset( $field['name'] ) ) {
2246
						continue;
2247
					}
2248
2249
					if ( ! isset( $field['weight'] ) ) {
2250
						$field['weight'] = $weight;
2251
2252
						$weight ++;
2253
					}
2254
2255
					$fields[ $field['name'] ] = $field;
2256
				}
2257
			}
2258
2259
			$weight = 0;
2260
2261
			$saved_field_ids = array();
2262
2263
			foreach ( $pod['fields'] as $k => $field ) {
2264
				if ( ! empty( $old_id ) && ( ! is_array( $field ) || ! isset( $field['name'] ) || ! isset( $fields[ $field['name'] ] ) ) ) {
2265
					// Iterative change handling for setup-edit.php
2266
					if ( ! is_array( $field ) && isset( $old_fields[ $k ] ) ) {
2267
						$saved[ $old_fields[ $k ]['name'] ] = true;
2268
					}
2269
2270
					continue;
2271
				}
2272
2273
				if ( ! empty( $old_id ) ) {
2274
					$field = array_merge( $field, $fields[ $field['name'] ] );
2275
				}
2276
2277
				$field['pod'] = $pod;
2278
2279
				if ( ! isset( $field['weight'] ) ) {
2280
					$field['weight'] = $weight;
2281
2282
					$weight ++;
2283
				}
2284
2285
				if ( 0 < $field_index_id && pods_var( 'id', $field ) == $field_index_id ) {
2286
					$field_index_change = $field['name'];
2287
				}
2288
2289
				if ( 0 < pods_var( 'id', $field ) ) {
2290
					$id_required = true;
2291
				}
2292
2293
				if ( $id_required ) {
2294
					$field['id_required'] = true;
2295
				}
2296
2297
				$field_data = $field;
2298
2299
				$field = $this->save_field( $field_data, $field_table_operation, true, $db );
2300
2301
				if ( true !== $db ) {
2302
					$pod['fields'][ $k ] = $field;
2303
					$saved_field_ids[]   = $field['id'];
2304
				} else {
2305
					if ( ! empty( $field ) && 0 < $field ) {
2306
						$saved[ $field_data['name'] ] = true;
2307
						$saved_field_ids[]            = $field;
2308
					} else {
2309
						$errors[] = sprintf( __( 'Cannot save the %s field', 'pods' ), $field_data['name'] );
2310
					}
2311
				}
2312
			}
2313
2314
			if ( true === $db ) {
2315
				foreach ( $old_fields as $field ) {
2316
					if ( isset( $pod['fields'][ $field['name'] ] ) || isset( $saved[ $field['name'] ] ) || in_array( $field['id'], $saved_field_ids ) ) {
2317
						continue;
2318
					}
2319
2320
					if ( $field['id'] == $field_index_id ) {
2321
						$field_index_change = 'id';
2322
					} elseif ( $field['name'] == $field_index ) {
2323
						$field_index_change = 'id';
2324
					}
2325
2326
					$this->delete_field( array(
2327
						'id'   => (int) $field['id'],
2328
						'name' => $field['name'],
2329
						'pod'  => $pod
2330
					), $field_table_operation );
2331
				}
2332
			}
2333
2334
			// Update field index if the name has changed or the field has been removed
2335
			if ( false !== $field_index_change && true === $db ) {
2336
				update_post_meta( $pod['id'], 'pod_index', $field_index_change );
2337
			}
2338
		}
2339
2340
		if ( ! empty( $errors ) ) {
2341
			return pods_error( $errors, $this );
2342
		}
2343
2344
		$this->cache_flush_pods( $pod );
2345
2346
		$refresh_pod = $this->load_pod( array( 'name' => $pod['name'] ), false );
2347
2348
		if ( $refresh_pod ) {
2349
			$pod = $refresh_pod;
2350
		}
2351
2352
		if ( 'post_type' === $pod['type'] ) {
2353
			PodsMeta::$post_types[ $pod['id'] ] = $pod;
2354
		} elseif ( 'taxonomy' === $pod['type'] ) {
2355
			PodsMeta::$taxonomies[ $pod['id'] ] = $pod;
2356
		} elseif ( 'media' === $pod['type'] ) {
2357
			PodsMeta::$media[ $pod['id'] ] = $pod;
2358
		} elseif ( 'user' === $pod['type'] ) {
2359
			PodsMeta::$user[ $pod['id'] ] = $pod;
2360
		} elseif ( 'comment' === $pod['type'] ) {
2361
			PodsMeta::$comment[ $pod['id'] ] = $pod;
2362
		}
2363
2364
		// Register Post Types / Taxonomies post-registration from PodsInit
2365
		if ( ! empty( PodsInit::$content_types_registered ) && in_array( $pod['type'], array(
2366
				'post_type',
2367
				'taxonomy'
2368
			) ) && empty( $pod['object'] ) ) {
2369
			global $pods_init;
2370
2371
			$pods_init->setup_content_types( true );
2372
		}
2373
2374
		if ( true === $db ) {
2375
			return $pod['id'];
2376
		} else {
2377
			return $pod;
2378
		}
2379
	}
2380
2381
	/**
2382
	 * Add or edit a field within a Pod
2383
	 *
2384
	 * $params['id'] int Field ID (id OR pod_id+pod+name required)
2385
	 * $params['pod_id'] int Pod ID (id OR pod_id+pod+name required)
2386
	 * $params['pod'] string Pod name (id OR pod_id+pod+name required)
2387
	 * $params['name'] string Field name (id OR pod_id+pod+name required)
2388
	 * $params['label'] string (optional) Field label
2389
	 * $params['type'] string (optional) Field type (avatar, boolean, code, color, currency, date, datetime, email,
2390
	 * file, number, paragraph, password, phone, pick, slug, text, time, website, wysiwyg)
2391
	 * $params['pick_object'] string (optional) Related Object (for relationships)
2392
	 * $params['pick_val'] string (optional) Related Object name (for relationships)
2393
	 * $params['sister_id'] int (optional) Related Field ID (for bidirectional relationships)
2394
	 * $params['weight'] int (optional) Order in which the field appears
2395
	 * $params['options'] array (optional) Options
2396
	 *
2397
	 * @param array    $params          An associative array of parameters
2398
	 * @param bool     $table_operation (optional) Whether or not to handle table operations
2399
	 * @param bool     $sanitized       (optional) Decides wether the params have been sanitized before being passed,
2400
	 *                                  will sanitize them if false.
2401
	 * @param bool|int $db              (optional) Whether to save into the DB or just return field array.
2402
	 *
2403
	 * @return int|array The field ID or field array (if !$db)
2404
	 * @since 1.7.9
2405
	 */
2406
	public function save_field( $params, $table_operation = true, $sanitized = false, $db = true ) {
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $db. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
2407
2408
		/**
2409
		 * @var $wpdb wpdb
2410
		 */
2411
		global $wpdb;
2412
2413
		if ( true !== $db ) {
2414
			$table_operation = false;
2415
		}
2416
2417
		$tableless_field_types    = PodsForm::tableless_field_types();
0 ignored issues
show
Comprehensibility Naming introduced by
The variable name $tableless_field_types exceeds the maximum configured length of 20.

Very long variable names usually make code harder to read. It is therefore recommended not to make variable names too verbose.

Loading history...
2418
		$simple_tableless_objects = PodsForm::simple_tableless_objects();
0 ignored issues
show
Comprehensibility Naming introduced by
The variable name $simple_tableless_objects exceeds the maximum configured length of 20.

Very long variable names usually make code harder to read. It is therefore recommended not to make variable names too verbose.

Loading history...
2419
2420
		$params = (object) $params;
2421
2422
		if ( false === $sanitized ) {
2423
			$params = pods_sanitize( $params );
2424
		}
2425
2426
		if ( isset( $params->pod_id ) ) {
2427
			$params->pod_id = pods_absint( $params->pod_id );
2428
		}
2429
2430
		if ( true !== $db ) {
2431
			$params->pod_id = (int) $db;
2432
		}
2433
2434
		$pod         = null;
2435
		$save_pod    = false;
2436
		$id_required = false;
2437
2438
		if ( isset( $params->id_required ) ) {
2439
			unset( $params->id_required );
2440
2441
			$id_required = true;
2442
		}
2443
2444
		if ( ( ! isset( $params->pod ) || empty( $params->pod ) ) && ( ! isset( $params->pod_id ) || empty( $params->pod_id ) ) ) {
2445
			return pods_error( __( 'Pod ID or name is required', 'pods' ), $this );
2446
		}
2447
2448
		if ( isset( $params->pod ) && is_array( $params->pod ) ) {
2449
			$pod = $params->pod;
2450
2451
			$save_pod = true;
2452
		} elseif ( ( ! isset( $params->pod_id ) || empty( $params->pod_id ) ) && ( true === $db || 0 < $db ) ) {
2453
			$pod = $this->load_pod( array( 'name' => $params->pod, 'table_info' => true ) );
2454
		} elseif ( ! isset( $params->pod ) && ( true === $db || 0 < $db ) ) {
2455
			$pod = $this->load_pod( array( 'id' => $params->pod_id, 'table_info' => true ) );
2456
		} elseif ( true === $db || 0 < $db ) {
2457
			$pod = $this->load_pod( array( 'id' => $params->pod_id, 'name' => $params->pod, 'table_info' => true ) );
2458
		}
2459
2460
		if ( empty( $pod ) && true === $db ) {
2461
			return pods_error( __( 'Pod not found', 'pods' ), $this );
2462
		}
2463
2464
		$params->pod_id   = $pod['id'];
2465
		$params->pod      = $pod['name'];
2466
		$params->pod_data = $pod;
2467
2468
		$params->name = pods_clean_name( $params->name, true, ( 'meta' === $pod['storage'] ? false : true ) );
2469
2470
		if ( ! isset( $params->id ) ) {
2471
			$params->id = 0;
2472
		}
2473
2474
		if ( empty( $params->name ) ) {
2475
			return pods_error( 'Pod field name is required', $this );
2476
		}
2477
2478
		$field = $this->load_field( $params );
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->load_field($params); of type array|boolean adds the type boolean to the return on line 2977 which is incompatible with the return type documented by PodsAPI::save_field of type integer|array.
Loading history...
2479
2480
		unset( $params->pod_data );
2481
2482
		$old_id = $old_name = $old_type = $old_definition = $old_simple = $old_options = $old_sister_id = null;
2483
2484
		if ( ! empty( $field ) ) {
2485
			$old_id        = pods_var( 'id', $field );
2486
			$old_name      = pods_clean_name( $field['name'], true, ( 'meta' === $pod['storage'] ? false : true ) );
2487
			$old_type      = $field['type'];
2488
			$old_options   = $field['options'];
2489
			$old_sister_id = (int) pods_var( 'sister_id', $old_options, 0 );
2490
2491
			$old_simple = ( 'pick' === $old_type && in_array( pods_var( 'pick_object', $field ), $simple_tableless_objects ) );
2492
2493
			if ( isset( $params->name ) && ! empty( $params->name ) ) {
2494
				$field['name'] = $params->name;
2495
			}
2496
2497
			if ( $old_name !== $field['name'] && false !== $this->field_exists( $params ) ) {
2498
				return pods_error( sprintf( __( 'Field %s already exists, you cannot rename %s to that', 'pods' ), $field['name'], $old_name ), $this );
2499
			}
2500
2501
			if ( ( $id_required || ! empty( $params->id ) ) && ( empty( $old_id ) || $old_id != $params->id ) ) {
2502
				return pods_error( sprintf( __( 'Field %s already exists', 'pods' ), $field['name'] ), $this );
2503
			}
2504
2505
			if ( empty( $params->id ) ) {
2506
				$params->id = $old_id;
2507
			}
2508
2509
			if ( ! in_array( $old_type, $tableless_field_types ) || $old_simple ) {
2510
				$definition = $this->get_field_definition( $old_type, array_merge( $field, $old_options ) );
2511
2512
				if ( 0 < strlen( $definition ) ) {
2513
					$old_definition = "`{$old_name}` " . $definition;
2514
				}
2515
			}
2516
		} else {
2517
			$field = array(
2518
				'id'          => 0,
2519
				'pod_id'      => $params->pod_id,
2520
				'name'        => $params->name,
2521
				'label'       => $params->name,
2522
				'description' => '',
2523
				'type'        => 'text',
2524
				'pick_object' => '',
2525
				'pick_val'    => '',
2526
				'sister_id'   => '',
2527
				'weight'      => null,
2528
				'options'     => array()
2529
			);
2530
		}
2531
2532
		// Setup options
2533
		$options = get_object_vars( $params );
2534
2535
		$options_ignore = array(
2536
			'method',
2537
			'table_info',
2538
			'attributes',
2539
			'group',
2540
			'grouped',
2541
			'developer_mode',
2542
			'dependency',
2543
			'depends-on',
2544
			'excludes-on'
2545
		);
2546
2547
		foreach ( $options_ignore as $ignore ) {
2548
			if ( isset( $options[ $ignore ] ) ) {
2549
				unset( $options[ $ignore ] );
2550
			}
2551
		}
2552
2553
		if ( isset( $options['method'] ) ) {
2554
			unset( $options['method'] );
2555
		} elseif ( isset( $options['table_info'] ) ) {
2556
			unset( $options['table_info'] );
2557
		}
2558
2559
		$exclude = array(
2560
			'id',
2561
			'pod_id',
2562
			'pod',
2563
			'name',
2564
			'label',
2565
			'description',
2566
			'type',
2567
			'pick_object',
2568
			'pick_val',
2569
			'sister_id',
2570
			'weight',
2571
			'options'
2572
		);
2573
2574
		foreach ( $exclude as $k => $exclude_field ) {
2575
			$aliases = array( $exclude_field );
2576
2577
			if ( is_array( $exclude_field ) ) {
2578
				$aliases       = array_merge( array( $k ), $exclude_field );
2579
				$exclude_field = $k;
2580
			}
2581
2582
			foreach ( $aliases as $alias ) {
2583
				if ( isset( $options[ $alias ] ) ) {
2584
					$field[ $exclude_field ] = pods_trim( $options[ $alias ] );
2585
2586
					unset( $options[ $alias ] );
2587
				}
2588
			}
2589
		}
2590
2591
		if ( strlen( $field['label'] ) < 1 ) {
2592
			$field['label'] = $field['name'];
2593
		}
2594
2595
		$field['options']['type'] = $field['type'];
2596
2597
		if ( in_array( $field['options']['type'], $tableless_field_types ) ) {
2598
			// Clean up special drop-down in field editor and save out pick_val
2599
			$field['pick_object'] = pods_var( 'pick_object', $field, '', null, true );
2600
2601
			if ( 0 === strpos( $field['pick_object'], 'pod-' ) ) {
2602
				$field['pick_val']    = pods_str_replace( 'pod-', '', $field['pick_object'], 1 );
2603
				$field['pick_object'] = 'pod';
2604
			} elseif ( 0 === strpos( $field['pick_object'], 'post_type-' ) ) {
2605
				$field['pick_val']    = pods_str_replace( 'post_type-', '', $field['pick_object'], 1 );
2606
				$field['pick_object'] = 'post_type';
2607
			} elseif ( 0 === strpos( $field['pick_object'], 'taxonomy-' ) ) {
2608
				$field['pick_val']    = pods_str_replace( 'taxonomy-', '', $field['pick_object'], 1 );
2609
				$field['pick_object'] = 'taxonomy';
2610
			} elseif ( 'table' === $field['pick_object'] && 0 < strlen( pods_var_raw( 'pick_table', $field['options'] ) ) ) {
2611
				$field['pick_val']    = $field['options']['pick_table'];
2612
				$field['pick_object'] = 'table';
2613
			} elseif ( false === strpos( $field['pick_object'], '-' ) && ! in_array( $field['pick_object'], array(
2614
					'pod',
2615
					'post_type',
2616
					'taxonomy'
2617
				) ) ) {
2618
				$field['pick_val'] = '';
2619
			} elseif ( 'custom-simple' === $field['pick_object'] ) {
2620
				$field['pick_val'] = '';
2621
			}
2622
2623
			$field['options']['pick_object'] = $field['pick_object'];
2624
			$field['options']['pick_val']    = $field['pick_val'];
2625
			$field['options']['sister_id']   = pods_var( 'sister_id', $field );
2626
2627
			unset( $field['pick_object'] );
2628
			unset( $field['pick_val'] );
2629
2630
			if ( isset( $field['sister_id'] ) ) {
2631
				unset( $field['sister_id'] );
2632
			}
2633
		}
2634
2635
		$field['options'] = array_merge( $field['options'], $options );
2636
2637
		$object_fields = (array) pods_var_raw( 'object_fields', $pod, array(), null, true );
2638
2639
		if ( 0 < $old_id && defined( 'PODS_FIELD_STRICT' ) && ! PODS_FIELD_STRICT ) {
2640
			$params->id = $field['id'] = $old_id;
2641
		}
2642
2643
		// Add new field
2644
		if ( ! isset( $params->id ) || empty( $params->id ) || empty( $field ) ) {
2645
			if ( $table_operation && in_array( $field['name'], array(
2646
					'created',
2647
					'modified'
2648
				) ) && ! in_array( $field['type'], array(
2649
					'date',
2650
					'datetime'
2651
				) ) && ( ! defined( 'PODS_FIELD_STRICT' ) || PODS_FIELD_STRICT ) ) {
2652
				return pods_error( sprintf( __( '%s is reserved for internal Pods usage, please try a different name', 'pods' ), $field['name'] ), $this );
2653
			}
2654
2655
			if ( $table_operation && 'author' === $field['name'] && 'pick' !== $field['type'] && ( ! defined( 'PODS_FIELD_STRICT' ) || PODS_FIELD_STRICT ) ) {
2656
				return pods_error( sprintf( __( '%s is reserved for internal Pods usage, please try a different name', 'pods' ), $field['name'] ), $this );
2657
			}
2658
2659
			if ( in_array( $field['name'], array( 'id', 'ID' ) ) ) {
2660
				return pods_error( sprintf( __( '%s is reserved for internal Pods usage, please try a different name', 'pods' ), $field['name'] ), $this );
2661
			}
2662
2663
			foreach ( $object_fields as $object_field => $object_field_opt ) {
2664
				if ( $object_field == $field['name'] || in_array( $field['name'], $object_field_opt['alias'] ) ) {
2665
					return pods_error( sprintf( __( '%s is reserved for internal WordPress or Pods usage, please try a different name. Also consider what WordPress and Pods provide you built-in.', 'pods' ), $field['name'] ), $this );
2666
				}
2667
			}
2668
2669
			if ( in_array( $field['name'], array( 'rss' ) ) ) // Reserved post_name values that can't be used as field names
2670
			{
2671
				$field['name'] .= '2';
2672
			}
2673
2674
			if ( 'slug' === $field['type'] && true === $db ) {
2675
				if ( in_array( $pod['type'], array( 'post_type', 'taxonomy', 'user' ) ) ) {
2676
					return pods_error( __( 'This pod already has an internal WordPress permalink field', 'pods' ), $this );
2677
				}
2678
2679
				$slug_field = get_posts( array(
2680
					'post_type'      => '_pods_field',
2681
					'orderby'        => 'menu_order',
2682
					'order'          => 'ASC',
2683
					'posts_per_page' => 1,
2684
					'post_parent'    => $field['pod_id'],
2685
					'meta_query'     => array(
2686
						array(
2687
							'key'   => 'type',
2688
							'value' => 'slug'
2689
						)
2690
					)
2691
				) );
2692
2693
				if ( ! empty( $slug_field ) ) {
2694
					return pods_error( __( 'This pod already has a permalink field', 'pods' ), $this );
2695
				}
2696
			}
2697
2698
			// Sink the new field to the bottom of the list
2699
			if ( null === $field['weight'] ) {
2700
				$field['weight'] = 0;
2701
2702
				$bottom_most_field = get_posts( array(
2703
					'post_type'      => '_pods_field',
2704
					'orderby'        => 'menu_order',
2705
					'order'          => 'DESC',
2706
					'posts_per_page' => 1,
2707
					'post_parent'    => $field['pod_id']
2708
				) );
2709
2710
				if ( ! empty( $bottom_most_field ) ) {
2711
					$field['weight'] = pods_absint( $bottom_most_field[0]->menu_order ) + 1;
2712
				}
2713
			}
2714
2715
			$field['weight'] = pods_absint( $field['weight'] );
2716
2717
			$post_data = array(
2718
				'post_name'    => $field['name'],
2719
				'post_title'   => $field['label'],
2720
				'post_content' => $field['description'],
2721
				'post_type'    => '_pods_field',
2722
				'post_parent'  => $field['pod_id'],
2723
				'post_status'  => 'publish',
2724
				'menu_order'   => $field['weight']
2725
			);
2726
		} else {
2727
			if ( in_array( $field['name'], array( 'id', 'ID' ) ) ) {
2728
				if ( null !== $old_name ) {
2729
					return pods_error( sprintf( __( '%s is reserved for internal Pods usage, please try a different name', 'pods' ), $field['name'] ), $this );
2730
				} else {
2731
					return pods_error( sprintf( __( '%s is not editable', 'pods' ), $field['name'] ), $this );
2732
				}
2733
			}
2734
2735
			if ( null !== $old_name && $field['name'] !== $old_name && in_array( $field['name'], array(
2736
					'created',
2737
					'modified'
2738
				) ) && ! in_array( $field['type'], array(
2739
					'date',
2740
					'datetime'
2741
				) ) && ( ! defined( 'PODS_FIELD_STRICT' ) || PODS_FIELD_STRICT ) ) {
2742
				return pods_error( sprintf( __( '%s is reserved for internal Pods usage, please try a different name', 'pods' ), $field['name'] ), $this );
2743
			}
2744
2745
			if ( null !== $old_name && $field['name'] !== $old_name && 'author' === $field['name'] && 'pick' !== $field['type'] && ( ! defined( 'PODS_FIELD_STRICT' ) || PODS_FIELD_STRICT ) ) {
2746
				return pods_error( sprintf( __( '%s is reserved for internal Pods usage, please try a different name', 'pods' ), $field['name'] ), $this );
2747
			}
2748
2749
			foreach ( $object_fields as $object_field => $object_field_opt ) {
2750
				if ( $object_field !== $field['name'] && ! in_array( $field['name'], $object_field_opt['alias'] ) ) {
2751
					continue;
2752
				}
2753
2754
				if ( null !== $old_name ) {
2755
					return pods_error( sprintf( __( '%s is reserved for internal WordPress or Pods usage, please try a different name', 'pods' ), $field['name'] ), $this );
2756
				} else {
2757
					return pods_error( sprintf( __( '%s is not editable', 'pods' ), $field['name'] ), $this );
2758
				}
2759
			}
2760
2761
			$post_data = array(
2762
				'ID'           => $field['id'],
2763
				'post_name'    => $field['name'],
2764
				'post_title'   => $field['label'],
2765
				'post_content' => $field['description']
2766
			);
2767
2768
			if ( null !== $field['weight'] ) {
2769
				$field['weight'] = pods_absint( $field['weight'] );
2770
2771
				$post_data['menu_order'] = $field['weight'];
2772
			}
2773
		}
2774
2775
		if ( true === $db ) {
2776
			if ( ! has_filter( 'wp_unique_post_slug', array( $this, 'save_slug_fix' ) ) ) {
2777
				add_filter( 'wp_unique_post_slug', array( $this, 'save_slug_fix' ), 100, 6 );
2778
			}
2779
2780
			$conflicted = false;
2781
2782
			// Headway compatibility fix
2783
			if ( has_filter( 'wp_insert_post_data', 'headway_clean_slug', 0 ) ) {
2784
				remove_filter( 'wp_insert_post_data', 'headway_clean_slug', 0 );
2785
2786
				$conflicted = true;
2787
			}
2788
2789
			// Store the old field name
2790
			if ( $old_name && $old_name !== $post_data['post_name'] ) {
2791
				$field['options']['old_name'] = $old_name;
2792
			}
2793
2794
			$params->id = $this->save_wp_object( 'post', $post_data, $field['options'], true, true );
2795
2796
			if ( $conflicted ) {
2797
				add_filter( 'wp_insert_post_data', 'headway_clean_slug', 0 );
2798
			}
2799
2800
			if ( false === $params->id ) {
2801
				return pods_error( __( 'Cannot save Field', 'pods' ), $this );
2802
			}
2803
		} else {
2804
			$params->id = $field['name'];
2805
		}
2806
2807
		$field['id'] = $params->id;
2808
2809
		$simple = ( 'pick' === $field['type'] && in_array( pods_var( 'pick_object', $field['options'] ), $simple_tableless_objects ) );
2810
2811
		$definition = false;
2812
2813
		if ( ! in_array( $field['type'], $tableless_field_types ) || $simple ) {
2814
			$field_definition = $this->get_field_definition( $field['type'], array_merge( $field, $field['options'] ) );
2815
2816
			if ( 0 < strlen( $field_definition ) ) {
2817
				$definition = '`' . $field['name'] . '` ' . $field_definition;
2818
			}
2819
		}
2820
2821
		$sister_id = (int) pods_var( 'sister_id', $field['options'], 0 );
2822
2823
		if ( $table_operation && 'table' === $pod['storage'] && ! pods_tableless() ) {
2824
			if ( ! empty( $old_id ) ) {
2825
				if ( ( $field['type'] !== $old_type || $old_simple != $simple ) && empty( $definition ) ) {
2826
					pods_query( "ALTER TABLE `@wp_pods_{$params->pod}` DROP COLUMN `{$old_name}`", false );
2827
				} elseif ( 0 < strlen( $definition ) ) {
2828
					if ( $old_name !== $field['name'] || $old_simple != $simple ) {
2829
						$test = false;
2830
2831
						if ( 0 < strlen( $old_definition ) ) {
2832
							$test = pods_query( "ALTER TABLE `@wp_pods_{$params->pod}` CHANGE `{$old_name}` {$definition}", false );
2833
						}
2834
2835
						// If the old field doesn't exist, continue to add a new field
2836
						if ( false === $test ) {
2837
							pods_query( "ALTER TABLE `@wp_pods_{$params->pod}` ADD COLUMN {$definition}", __( 'Cannot create new field', 'pods' ) );
2838
						}
2839
					} elseif ( null !== $old_definition && $definition !== $old_definition ) {
2840
						$test = pods_query( "ALTER TABLE `@wp_pods_{$params->pod}` CHANGE `{$old_name}` {$definition}", false );
2841
2842
						// If the old field doesn't exist, continue to add a new field
2843
						if ( false === $test ) {
2844
							pods_query( "ALTER TABLE `@wp_pods_{$params->pod}` ADD COLUMN {$definition}", __( 'Cannot create new field', 'pods' ) );
2845
						}
2846
					}
2847
				}
2848
			} elseif ( 0 < strlen( $definition ) ) {
2849
				$test = false;
2850
2851
				if ( 0 < strlen( $old_definition ) ) {
2852
					$test = pods_query( "ALTER TABLE `@wp_pods_{$params->pod}` CHANGE `" . $field['name'] . "` {$definition}", false );
2853
				}
2854
2855
				// If the old field doesn't exist, continue to add a new field
2856
				if ( false === $test ) {
2857
					pods_query( "ALTER TABLE `@wp_pods_{$params->pod}` ADD COLUMN {$definition}", __( 'Cannot create new field', 'pods' ) );
2858
				}
2859
			}
2860
		}
2861
2862
		if ( ! empty( $old_id ) && 'meta' === $pod['storage'] && $old_name !== $field['name'] && $pod['meta_table'] !== $pod['table'] ) {
2863
			$prepare = array(
2864
				$field['name'],
2865
				$old_name
2866
			);
2867
2868
			// Users don't have a type
2869
			if ( ! empty( $pod['field_type'] ) ) {
2870
				$prepare[] = $pod['name'];
2871
			}
2872
2873
			$join_table = $pod['table'];
2874
2875
			// Taxonomies are the odd type out, terrible I know
2876
			if ( 'taxonomy' === $pod['type'] ) {
2877
				// wp_term_taxonomy has the 'taxonomy' field we need to limit by
2878
				$join_table = $wpdb->term_taxonomy;
2879
			}
2880
2881
			pods_query( "
2882
                UPDATE `{$pod[ 'meta_table' ]}` AS `m`
2883
                LEFT JOIN `{$join_table}` AS `t`
2884
                    ON `t`.`{$pod[ 'field_id' ]}` = `m`.`{$pod[ 'meta_field_id' ]}`
2885
                SET
2886
                    `m`.`{$pod[ 'meta_field_index' ]}` = %s
2887
                WHERE
2888
                    `m`.`{$pod[ 'meta_field_index' ]}` = %s
2889
            " . ( ! empty( $pod['field_type'] ) ? " AND `t`.`{$pod[ 'field_type' ]}` = %s" : "" ), $prepare );
2890
		}
2891
2892
		if ( $field['type'] !== $old_type && in_array( $old_type, $tableless_field_types ) ) {
2893
			delete_post_meta( $old_sister_id, 'sister_id' );
2894
2895
			if ( true === $db ) {
2896
				pods_query( "
2897
                        DELETE pm
2898
                        FROM {$wpdb->postmeta} AS pm
2899
                        LEFT JOIN {$wpdb->posts} AS p
2900
                            ON p.post_type = '_pods_field'
2901
                            AND p.ID = pm.post_id
2902
                        WHERE
2903
                            p.ID IS NOT NULL
2904
                            AND pm.meta_key = 'sister_id'
2905
                            AND pm.meta_value = %d
2906
                    ", array(
2907
						$params->id
2908
					) );
2909
2910
				if ( ! pods_tableless() ) {
2911
					pods_query( "DELETE FROM @wp_podsrel WHERE `field_id` = {$params->id}", false );
2912
2913
					pods_query( "
2914
                            UPDATE `@wp_podsrel`
2915
                            SET `related_field_id` = 0
2916
                            WHERE `field_id` = %d
2917
                        ", array(
2918
							$old_sister_id
2919
						) );
2920
				}
2921
			}
2922
		} elseif ( 0 < $sister_id ) {
2923
			update_post_meta( $sister_id, 'sister_id', $params->id );
2924
2925
			if ( true === $db && ( ! pods_tableless() ) ) {
2926
				pods_query( "
2927
                        UPDATE `@wp_podsrel`
2928
                        SET `related_field_id` = %d
2929
                        WHERE `field_id` = %d
2930
                    ", array(
2931
						$params->id,
2932
						$sister_id
2933
					) );
2934
			}
2935
		} elseif ( 0 < $old_sister_id ) {
2936
			delete_post_meta( $old_sister_id, 'sister_id' );
2937
2938
			if ( true === $db && ( ! pods_tableless() ) ) {
2939
				pods_query( "
2940
                        UPDATE `@wp_podsrel`
2941
                        SET `related_field_id` = 0
2942
                        WHERE `field_id` = %d
2943
                    ", array(
2944
						$old_sister_id
2945
					) );
2946
			}
2947
		}
2948
2949
		if ( ! empty( $old_id ) && $old_name !== $field['name'] && true === $db ) {
2950
			pods_query( "
2951
                    UPDATE `@wp_postmeta`
2952
                    SET `meta_value` = %s
2953
                    WHERE
2954
                        `post_id` = %d
2955
                        AND `meta_key` = 'pod_index'
2956
                        AND `meta_value` = %s
2957
                ", array(
2958
					$field['name'],
2959
					$pod['id'],
2960
					$old_name
2961
				) );
2962
		}
2963
2964
		if ( ! $save_pod ) {
2965
			$this->cache_flush_pods( $pod );
2966
		} else {
2967
			pods_transient_clear( 'pods_field_' . $pod['name'] . '_' . $field['name'] );
2968
2969
			if ( ! empty( $old_id ) && $old_name !== $field['name'] ) {
2970
				pods_transient_clear( 'pods_field_' . $pod['name'] . '_' . $old_name );
2971
			}
2972
		}
2973
2974
		if ( true === $db ) {
2975
			return $params->id;
2976
		} else {
2977
			return $field;
2978
		}
2979
	}
2980
2981
	/**
2982
	 * Fix Pod / Field post_name to ensure they are exactly as saved (allow multiple posts w/ same post_name)
2983
	 *
2984
	 * @param string $slug          Unique slug value
2985
	 * @param int    $post_ID       Post ID
2986
	 * @param string $post_status   Post Status
2987
	 * @param string $post_type     Post Type
2988
	 * @param int    $post_parent   Post Parent ID
2989
	 * @param string $original_slug Original slug value
0 ignored issues
show
Documentation introduced by
Should the type for parameter $original_slug not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
2990
	 *
2991
	 * @return string Final slug value
0 ignored issues
show
Documentation introduced by
Should the return type not be string|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
2992
	 *
2993
	 * @since 2.3.3
2994
	 */
2995
	public function save_slug_fix( $slug, $post_ID, $post_status, $post_type, $post_parent = 0, $original_slug = null ) {
2996
2997
		if ( in_array( $post_type, array( '_pods_field', '_pods_pod' ) ) && false !== strpos( $slug, '-' ) ) {
2998
			$slug = $original_slug;
2999
		}
3000
3001
		return $slug;
3002
	}
3003
3004
	/**
3005
	 * Add or Edit a Pods Object
3006
	 *
3007
	 * $params['id'] int The Object ID
3008
	 * $params['name'] string The Object name
3009
	 * $params['type'] string The Object type
3010
	 * $params['options'] Associative array of Object options
3011
	 *
3012
	 * @param array|object $params    An associative array of parameters
3013
	 * @param bool         $sanitized (optional) Decides whether the params have been sanitized before being passed,
3014
	 *                                will sanitize them if false.
3015
	 *
3016
	 * @return int The Object ID
3017
	 * @since 2.0
3018
	 */
3019
	public function save_object( $params, $sanitized = false ) {
3020
3021
		$params = (object) $params;
3022
3023
		if ( false === $sanitized ) {
3024
			$params = pods_sanitize( $params );
3025
		}
3026
3027
		if ( ! isset( $params->name ) || empty( $params->name ) ) {
3028
			return pods_error( __( 'Name must be given to save an Object', 'pods' ), $this );
3029
		}
3030
3031
		if ( ! isset( $params->type ) || empty( $params->type ) ) {
3032
			return pods_error( __( 'Type must be given to save an Object', 'pods' ), $this );
3033
		}
3034
3035
		$object = array(
3036
			'id'      => 0,
3037
			'name'    => $params->name,
3038
			'type'    => $params->type,
3039
			'code'    => '',
3040
			'options' => array()
3041
		);
3042
3043
		// Setup options
3044
		$options = get_object_vars( $params );
3045
3046
		if ( isset( $options['method'] ) ) {
3047
			unset( $options['method'] );
3048
		}
3049
3050
		$exclude = array(
3051
			'id',
3052
			'name',
3053
			'helper_type',
3054
			'code',
3055
			'options',
3056
			'status'
3057
		);
3058
3059
		foreach ( $exclude as $k => $exclude_field ) {
3060
			$aliases = array( $exclude_field );
3061
3062
			if ( is_array( $exclude_field ) ) {
3063
				$aliases       = array_merge( array( $k ), $exclude_field );
3064
				$exclude_field = $k;
3065
			}
3066
3067
			foreach ( $aliases as $alias ) {
3068
				if ( isset( $options[ $alias ] ) ) {
3069
					$object[ $exclude_field ] = pods_trim( $options[ $alias ] );
3070
3071
					unset( $options[ $alias ] );
3072
				}
3073
			}
3074
		}
3075
3076
		if ( 'helper' === $object['type'] ) {
3077
			$object['options']['helper_type'] = $object['helper_type'];
3078
		}
3079
3080
		if ( isset( $object['options']['code'] ) ) {
3081
			unset( $object['options']['code'] );
3082
		}
3083
3084
		$object['options'] = array_merge( $object['options'], $options );
3085
3086
		$post_data = array(
3087
			'post_name'    => pods_clean_name( $object['name'], true ),
3088
			'post_title'   => $object['name'],
3089
			'post_content' => $object['code'],
3090
			'post_type'    => '_pods_' . $object['type'],
3091
			'post_status'  => 'publish'
3092
		);
3093
3094
		if ( ! empty( $object['id'] ) ) {
3095
			$post_data['ID'] = $object['id'];
3096
		}
3097
3098
		if ( null !== pods_var( 'status', $object, null, null, true ) ) {
3099
			$post_data['post_status'] = pods_var( 'status', $object, null, null, true );
3100
		}
3101
3102
		remove_filter( 'content_save_pre', 'balanceTags', 50 );
3103
3104
		$post_data = pods_sanitize( $post_data );
3105
3106
		$params->id = $this->save_post( $post_data, $object['options'], true, true );
3107
3108
		pods_transient_clear( 'pods_objects_' . $params->type );
3109
		pods_transient_clear( 'pods_objects_' . $params->type . '_get' );
3110
3111
		return $params->id;
3112
	}
3113
3114
	/**
3115
	 * @see   PodsAPI::save_object
3116
	 *
3117
	 * Add or edit a Pod Template
3118
	 *
3119
	 * $params['id'] int The template ID
3120
	 * $params['name'] string The template name
3121
	 * $params['code'] string The template code
3122
	 *
3123
	 * @param array|object $params    An associative array of parameters
3124
	 * @param bool         $sanitized (optional) Decides wether the params have been sanitized before being passed,
3125
	 *                                will sanitize them if false.
3126
	 *
3127
	 * @return int The Template ID
3128
	 *
3129
	 * @since 1.7.9
3130
	 */
3131
	public function save_template( $params, $sanitized = false ) {
3132
3133
		$params = (object) $params;
3134
3135
		$params->type = 'template';
3136
3137
		return $this->save_object( $params, $sanitized );
3138
	}
3139
3140
	/**
3141
	 * @see   PodsAPI::save_object
3142
	 *
3143
	 * Add or edit a Pod Page
3144
	 *
3145
	 * $params['id'] int The page ID
3146
	 * $params['name'] string The page URI
3147
	 * $params['code'] string The page code
3148
	 *
3149
	 * @param array|object $params    An associative array of parameters
3150
	 * @param bool         $sanitized (optional) Decides wether the params have been sanitized before being passed,
3151
	 *                                will sanitize them if false.
3152
	 *
3153
	 * @return int The page ID
3154
	 * @since 1.7.9
3155
	 */
3156
	public function save_page( $params, $sanitized = false ) {
3157
3158
		$params = (object) $params;
3159
3160
		if ( ! isset( $params->name ) ) {
3161
			$params->name = $params->uri;
3162
			unset( $params->uri );
3163
		}
3164
3165
		if ( isset( $params->phpcode ) ) {
3166
			$params->code = $params->phpcode;
3167
			unset( $params->phpcode );
3168
		}
3169
3170
		$params->name = trim( $params->name, '/' );
3171
		$params->type = 'page';
3172
3173
		return $this->save_object( $params, $sanitized );
3174
	}
3175
3176
	/**
3177
	 * @see   PodsAPI::save_object
3178
	 *
3179
	 * Add or edit a Pod Helper
3180
	 *
3181
	 * $params['id'] int The helper ID
3182
	 * $params['name'] string The helper name
3183
	 * $params['helper_type'] string The helper type ("pre_save", "display", etc)
3184
	 * $params['code'] string The helper code
3185
	 *
3186
	 * @param array $params    An associative array of parameters
3187
	 * @param bool  $sanitized (optional) Decides wether the params have been sanitized before being passed, will
3188
	 *                         sanitize them if false.
3189
	 *
3190
	 * @return int The helper ID
3191
	 * @since 1.7.9
3192
	 */
3193
	public function save_helper( $params, $sanitized = false ) {
3194
3195
		$params = (object) $params;
3196
3197
		if ( isset( $params->phpcode ) ) {
3198
			$params->code = $params->phpcode;
3199
			unset( $params->phpcode );
3200
		}
3201
3202
		if ( isset( $params->type ) ) {
3203
			$params->helper_type = $params->type;
3204
			unset( $params->type );
3205
		}
3206
3207
		$params->type = 'helper';
3208
3209
		return $this->save_object( $params, $sanitized );
3210
	}
3211
3212
	/**
3213
	 * Add or edit a single pod item
3214
	 *
3215
	 * $params['pod'] string The Pod name (pod or pod_id is required)
3216
	 * $params['pod_id'] string The Pod ID (pod or pod_id is required)
3217
	 * $params['id'] int|array The item ID, or an array of item IDs to save data for
3218
	 * $params['data'] array (optional) Associative array of field names + values
3219
	 * $params['bypass_helpers'] bool Set to true to bypass running pre-save and post-save helpers
3220
	 * $params['track_changed_fields'] bool Set to true to enable tracking of saved fields via
3221
	 * PodsAPI::get_changed_fields()
3222
	 * $params['error_mode'] string Throw an 'exception', 'exit' with the message, return 'false', or return 'wp_error'
3223
	 *
3224
	 * @param array|object $params An associative array of parameters
3225
	 *
3226
	 * @return int|array The item ID, or an array of item IDs (if `id` is an array if IDs)
3227
	 *
3228
	 * @since 1.7.9
3229
	 */
3230
	public function save_pod_item( $params ) {
3231
3232
		global $wpdb;
3233
3234
		$params = (object) pods_str_replace( '@wp_', '{prefix}', $params );
3235
3236
		$tableless_field_types    = PodsForm::tableless_field_types();
0 ignored issues
show
Comprehensibility Naming introduced by
The variable name $tableless_field_types exceeds the maximum configured length of 20.

Very long variable names usually make code harder to read. It is therefore recommended not to make variable names too verbose.

Loading history...
3237
		$repeatable_field_types   = PodsForm::repeatable_field_types();
0 ignored issues
show
Comprehensibility Naming introduced by
The variable name $repeatable_field_types exceeds the maximum configured length of 20.

Very long variable names usually make code harder to read. It is therefore recommended not to make variable names too verbose.

Loading history...
3238
		$simple_tableless_objects = PodsForm::simple_tableless_objects();
0 ignored issues
show
Comprehensibility Naming introduced by
The variable name $simple_tableless_objects exceeds the maximum configured length of 20.

Very long variable names usually make code harder to read. It is therefore recommended not to make variable names too verbose.

Loading history...
3239
3240
		$error_mode = $this->display_errors;
3241
3242
		if ( ! empty( $params->error_mode ) ) {
3243
			$error_mode = $params->error_mode;
3244
		}
3245
3246
		// @deprecated 2.0
3247
		if ( isset( $params->datatype ) ) {
3248
			pods_deprecated( '$params->pod instead of $params->datatype', '2.0' );
3249
3250
			$params->pod = $params->datatype;
3251
3252
			unset( $params->datatype );
3253
3254
			if ( isset( $params->pod_id ) ) {
3255
				pods_deprecated( '$params->id instead of $params->pod_id', '2.0' );
3256
3257
				$params->id = $params->pod_id;
3258
3259
				unset( $params->pod_id );
3260
			}
3261
3262
			if ( isset( $params->data ) && ! empty( $params->data ) && is_array( $params->data ) ) {
3263
				$check = current( $params->data );
3264
3265
				if ( is_array( $check ) ) {
3266
					pods_deprecated( 'PodsAPI::save_pod_items', '2.0' );
3267
3268
					return $this->save_pod_items( $params, $params->data );
3269
				}
3270
			}
3271
		}
3272
3273
		// @deprecated 2.0
3274
		if ( isset( $params->tbl_row_id ) ) {
3275
			pods_deprecated( '$params->id instead of $params->tbl_row_id', '2.0' );
3276
3277
			$params->id = $params->tbl_row_id;
3278
3279
			unset( $params->tbl_row_id );
3280
		}
3281
3282
		// @deprecated 2.0
3283
		if ( isset( $params->columns ) ) {
3284
			pods_deprecated( '$params->data instead of $params->columns', '2.0' );
3285
3286
			$params->data = $params->columns;
3287
3288
			unset( $params->columns );
3289
		}
3290
3291
		if ( ! isset( $params->pod ) ) {
3292
			$params->pod = false;
3293
		}
3294
		if ( isset( $params->pod_id ) ) {
3295
			$params->pod_id = pods_absint( $params->pod_id );
3296
		} else {
3297
			$params->pod_id = 0;
3298
		}
3299
3300
		if ( isset( $params->id ) ) {
3301
			$params->id = pods_absint( $params->id );
3302
		} else {
3303
			$params->id = 0;
3304
		}
3305
3306
		if ( ! isset( $params->from ) ) {
3307
			$params->from = 'save';
3308
		}
3309
3310
		if ( ! isset( $params->location ) ) {
3311
			$params->location = null;
3312
		}
3313
3314
		if ( ! isset( $params->track_changed_fields ) ) {
3315
			$params->track_changed_fields = false;
3316
		}
3317
3318
		/**
3319
		 * Override $params['track_changed_fields']
3320
		 *
3321
		 * Use for globally setting field change tracking.
3322
		 *
3323
		 * @param bool
3324
		 *
3325
		 * @since 2.3.19
3326
		 */
3327
		$track_changed_fields = apply_filters( 'pods_api_save_pod_item_track_changed_fields_' . $params->pod, (boolean) $params->track_changed_fields, $params );
3328
3329
		$changed_fields = array();
3330
3331
		if ( ! isset( $params->clear_slug_cache ) ) {
3332
			$params->clear_slug_cache = true;
3333
		}
3334
3335
		// Support for bulk edit
3336
		if ( isset( $params->id ) && ! empty( $params->id ) && is_array( $params->id ) ) {
3337
			$ids        = array();
3338
			$new_params = $params;
3339
3340
			foreach ( $params->id as $id ) {
3341
				$new_params->id = $id;
3342
3343
				$ids[] = $this->save_pod_item( $new_params );
3344
			}
3345
3346
			return $ids;
3347
		}
3348
3349
		// Allow Helpers to know what's going on, are we adding or saving?
3350
		$is_new_item = false;
3351
3352
		if ( empty( $params->id ) ) {
3353
			$is_new_item = true;
3354
		}
3355
3356
		if ( isset( $params->is_new_item ) ) {
3357
			$is_new_item = (boolean) $params->is_new_item;
3358
		}
3359
3360
		// Allow Helpers to bypass subsequent helpers in recursive save_pod_item calls
3361
		$bypass_helpers = false;
3362
3363
		if ( isset( $params->bypass_helpers ) && false !== $params->bypass_helpers ) {
3364
			$bypass_helpers = true;
3365
		}
3366
3367
		// Allow Custom Fields not defined by Pods to be saved
3368
		$allow_custom_fields = false;
3369
3370
		if ( isset( $params->allow_custom_fields ) && false !== $params->allow_custom_fields ) {
3371
			$allow_custom_fields = true;
3372
		}
3373
3374
		// Get array of Pods
3375
		$pod = $this->load_pod( array( 'id' => $params->pod_id, 'name' => $params->pod, 'table_info' => true ) );
3376
3377
		if ( false === $pod ) {
3378
			return pods_error( __( 'Pod not found', 'pods' ), $error_mode );
3379
		}
3380
3381
		$params->pod    = $pod['name'];
3382
		$params->pod_id = $pod['id'];
3383
3384
		if ( 'settings' === $pod['type'] ) {
3385
			$params->id = $pod['id'];
3386
		}
3387
3388
		$fields = $pod['fields'];
3389
3390
		$object_fields = (array) pods_var_raw( 'object_fields', $pod, array(), null, true );
3391
3392
		$fields_active = array();
3393
		$custom_data   = array();
3394
3395
		// Find the active fields (loop through $params->data to retain order)
3396
		if ( ! empty( $params->data ) && is_array( $params->data ) ) {
3397
			$custom_fields = array();
3398
3399
			foreach ( $params->data as $field => $value ) {
3400
				if ( isset( $object_fields[ $field ] ) ) {
3401
					$object_fields[ $field ]['value'] = $value;
3402
					$fields_active[]                  = $field;
3403
				} elseif ( isset( $fields[ $field ] ) ) {
3404
					if ( 'save' === $params->from || true === PodsForm::permission( $fields[ $field ]['type'], $field, $fields[ $field ], $fields, $pod, $params->id, $params ) ) {
3405
						$fields[ $field ]['value'] = $value;
3406
						$fields_active[]           = $field;
3407
					} elseif ( ! pods_has_permissions( $fields[ $field ]['options'] ) && pods_var( 'hidden', $fields[ $field ]['options'], false ) ) {
3408
						$fields[ $field ]['value'] = $value;
3409
						$fields_active[]           = $field;
3410
					}
3411
				} else {
3412
					$found = false;
3413
3414
					foreach ( $object_fields as $object_field => $object_field_opt ) {
3415
						if ( in_array( $field, $object_field_opt['alias'] ) ) {
3416
							$object_fields[ $object_field ]['value'] = $value;
3417
							$fields_active[]                         = $object_field;
3418
3419
							$found = true;
3420
3421
							break;
3422
						}
3423
					}
3424
3425
					if ( $allow_custom_fields && ! $found ) {
3426
						$custom_fields[] = $field;
3427
					}
3428
				}
3429
			}
3430
3431
			if ( $allow_custom_fields && ! empty( $custom_fields ) ) {
3432
				foreach ( $custom_fields as $field ) {
3433
					$custom_data[ $field ] = $params->data[ $field ];
3434
				}
3435
			}
3436
3437
			unset( $params->data );
3438
		}
3439
3440
		if ( empty( $params->id ) && ! in_array( 'created', $fields_active ) && isset( $fields['created'] ) && in_array( $fields['created']['type'], array(
3441
				'date',
3442
				'datetime'
3443
			) ) ) {
3444
			$fields['created']['value'] = current_time( 'mysql' );
3445
			$fields_active[]            = 'created';
3446
		}
3447
3448
		if ( ! in_array( 'modified', $fields_active ) && isset( $fields['modified'] ) && in_array( $fields['modified']['type'], array(
3449
				'date',
3450
				'datetime'
3451
			) ) ) {
3452
			$fields['modified']['value'] = current_time( 'mysql' );
3453
			$fields_active[]             = 'modified';
3454
		}
3455
3456
		if ( in_array( $pod['type'], array(
3457
				'pod',
3458
				'table'
3459
			) ) && empty( $params->id ) && ! empty( $pod['pod_field_index'] ) && in_array( $pod['pod_field_index'], $fields_active ) && ! in_array( $pod['pod_field_slug'], $fields_active ) && isset( $fields[ $pod['pod_field_slug'] ] ) ) {
3460
			$fields[ $pod['pod_field_slug'] ]['value'] = ''; // this will get picked up by slug pre_save method
3461
			$fields_active[]                           = $pod['pod_field_slug'];
3462
		}
3463
3464
		// Handle hidden fields
3465
		if ( empty( $params->id ) ) {
3466
			foreach ( $fields as $field => $field_data ) {
3467
				if ( in_array( $field, $fields_active ) ) {
3468
					continue;
3469
				}
3470
3471
				if ( in_array( $params->from, array(
3472
						'save',
3473
						'process_form'
3474
					) ) || true === PodsForm::permission( $fields[ $field ]['type'], $field, $fields[ $field ], $fields, $pod, $params->id, $params ) ) {
3475
					$value = PodsForm::default_value( pods_var_raw( $field, 'post' ), $field_data['type'], $field, pods_var_raw( 'options', $field_data, $field_data, null, true ), $pod, $params->id );
3476
3477
					if ( null !== $value && '' !== $value && false !== $value ) {
3478
						$fields[ $field ]['value'] = $value;
3479
						$fields_active[]           = $field;
3480
					}
3481
				}
3482
			}
3483
3484
			// Set default field values for object fields
3485
			if ( ! empty( $object_fields ) ) {
3486
				foreach ( $object_fields as $field => $field_data ) {
3487
					if ( in_array( $field, $fields_active ) ) {
3488
						continue;
3489
					} elseif ( ! isset( $field_data['default'] ) || strlen( $field_data['default'] ) < 1 ) {
3490
						continue;
3491
					}
3492
3493
					$value = PodsForm::default_value( pods_var_raw( $field, 'post' ), $field_data['type'], $field, pods_var_raw( 'options', $field_data, $field_data, null, true ), $pod, $params->id );
3494
3495
					if ( null !== $value && '' !== $value && false !== $value ) {
3496
						$object_fields[ $field ]['value'] = $value;
3497
						$fields_active[]                  = $field;
3498
					}
3499
				}
3500
			}
3501
3502
			// Set default field values for Pod fields
3503
			foreach ( $fields as $field => $field_data ) {
3504
				if ( in_array( $field, $fields_active ) ) {
3505
					continue;
3506
				} elseif ( ! isset( $field_data['default'] ) || strlen( $field_data['default'] ) < 1 ) {
3507
					continue;
3508
				}
3509
3510
				$value = PodsForm::default_value( pods_var_raw( $field, 'post' ), $field_data['type'], $field, pods_var_raw( 'options', $field_data, $field_data, null, true ), $pod, $params->id );
3511
3512
				if ( null !== $value && '' !== $value && false !== $value ) {
3513
					$fields[ $field ]['value'] = $value;
3514
					$fields_active[]           = $field;
3515
				}
3516
			}
3517
		}
3518
3519
		$columns            =& $fields; // @deprecated 2.0
3520
		$active_columns     =& $fields_active; // @deprecated 2.0
3521
		$params->tbl_row_id =& $params->id; // @deprecated 2.0
3522
3523
		$pre_save_helpers = $post_save_helpers = array();
3524
3525
		$pieces = array(
3526
			'fields',
3527
			'params',
3528
			'pod',
3529
			'fields_active',
3530
			'object_fields',
3531
			'custom_fields',
3532
			'custom_data',
3533
			'track_changed_fields',
3534
			'changed_fields'
3535
		);
3536
3537
		if ( $track_changed_fields ) {
3538
			self::handle_changed_fields( $params->pod, $params->id, 'set' );
3539
		}
3540
3541
		if ( false === $bypass_helpers ) {
3542
			// Plugin hooks
3543
			$hooked = $this->do_hook( 'pre_save_pod_item', compact( $pieces ), $is_new_item, $params->id );
3544
3545
			if ( is_array( $hooked ) && ! empty( $hooked ) ) {
3546
				extract( $hooked );
3547
			}
3548
3549
			$hooked = $this->do_hook( "pre_save_pod_item_{$params->pod}", compact( $pieces ), $is_new_item, $params->id );
3550
3551
			if ( is_array( $hooked ) && ! empty( $hooked ) ) {
3552
				extract( $hooked );
3553
			}
3554
3555
			if ( $is_new_item ) {
3556
				$hooked = $this->do_hook( 'pre_create_pod_item', compact( $pieces ) );
3557
3558
				if ( is_array( $hooked ) && ! empty( $hooked ) ) {
3559
					extract( $hooked );
3560
				}
3561
3562
				$hooked = $this->do_hook( "pre_create_pod_item_{$params->pod}", compact( $pieces ) );
3563
3564
				if ( is_array( $hooked ) && ! empty( $hooked ) ) {
3565
					extract( $hooked );
3566
				}
3567
			} else {
3568
				$hooked = $this->do_hook( 'pre_edit_pod_item', compact( $pieces ), $params->id );
3569
3570
				if ( is_array( $hooked ) && ! empty( $hooked ) ) {
3571
					extract( $hooked );
3572
				}
3573
3574
				$hooked = $this->do_hook( "pre_edit_pod_item_{$params->pod}", compact( $pieces ), $params->id );
3575
3576
				if ( is_array( $hooked ) && ! empty( $hooked ) ) {
3577
					extract( $hooked );
3578
				}
3579
			}
3580
3581
			// Call any pre-save helpers (if not bypassed)
3582
			if ( ! defined( 'PODS_DISABLE_EVAL' ) || ! PODS_DISABLE_EVAL ) {
3583
				if ( ! empty( $pod['options'] ) && is_array( $pod['options'] ) ) {
3584
					$helpers = array( 'pre_save_helpers', 'post_save_helpers' );
3585
3586
					foreach ( $helpers as $helper ) {
3587
						if ( isset( $pod['options'][ $helper ] ) && ! empty( $pod['options'][ $helper ] ) ) {
3588
							${$helper} = explode( ',', $pod['options'][ $helper ] );
3589
						}
3590
					}
3591
				}
3592
3593
				if ( ! empty( $pre_save_helpers ) ) {
3594
					pods_deprecated( sprintf( __( 'Pre-save helpers are deprecated, use the action pods_pre_save_pod_item_%s instead', 'pods' ), $params->pod ), '2.0' );
3595
3596
					foreach ( $pre_save_helpers as $helper ) {
3597
						$helper = $this->load_helper( array( 'name' => $helper ) );
3598
3599
						if ( false !== $helper ) {
3600
							eval( '?>' . $helper['code'] );
0 ignored issues
show
Coding Style introduced by
It is generally not recommended to use eval unless absolutely required.

On one hand, eval might be exploited by malicious users if they somehow manage to inject dynamic content. On the other hand, with the emergence of faster PHP runtimes like the HHVM, eval prevents some optimization that they perform.

Loading history...
3601
						}
3602
					}
3603
				}
3604
			}
3605
		}
3606
3607
		$table_data = $table_formats = $update_values = $rel_fields = $rel_field_ids = array();
3608
3609
		$object_type = $pod['type'];
3610
3611
		$object_ID = 'ID';
3612
3613
		if ( 'comment' === $object_type ) {
3614
			$object_ID = 'comment_ID';
3615
		} elseif ( 'taxonomy' === $object_type ) {
3616
			$object_ID = 'term_id';
3617
		}
3618
3619
		$object_data = $object_meta = $post_term_data = array();
3620
3621
		if ( 'settings' === $object_type ) {
3622
			$object_data['option_id'] = $pod['name'];
3623
		} elseif ( ! empty( $params->id ) ) {
3624
			$object_data[ $object_ID ] = $params->id;
3625
		}
3626
3627
		$fields_active = array_unique( $fields_active );
3628
3629
		// Loop through each active field, validating and preparing the table data
3630
		foreach ( $fields_active as $field ) {
3631
			if ( isset( $object_fields[ $field ] ) ) {
3632
				$field_data = $object_fields[ $field ];
3633
			} elseif ( isset( $fields[ $field ] ) ) {
3634
				$field_data = $fields[ $field ];
3635
			} else {
3636
				continue;
3637
			}
3638
3639
			$value   = $field_data['value'];
3640
			$type    = $field_data['type'];
3641
			$options = pods_var( 'options', $field_data, array() );
3642
3643
			// WPML AJAX compatibility
3644
			if ( is_admin() && isset( $_GET['page'] ) && false !== strpos( $_GET['page'], '/menu/languages.php' ) && isset( $_POST['icl_ajx_action'] ) && isset( $_POST['_icl_nonce'] ) && wp_verify_nonce( $_POST['_icl_nonce'], $_POST['icl_ajx_action'] . '_nonce' ) ) {
3645
				$options['unique'] = $fields[ $field ]['options']['unique'] = $options['required'] = $fields[ $field ]['options']['required'] = 0;
3646
			} else {
3647
				// Validate value
3648
				$validate = $this->handle_field_validation( $value, $field, $object_fields, $fields, $pod, $params );
3649
3650
				if ( false === $validate ) {
3651
					$validate = sprintf( __( 'There was an issue validating the field %s', 'pods' ), $field_data['label'] );
3652
				} elseif ( true !== $validate ) {
3653
					$validate = (array) $validate;
3654
				}
3655
3656
				if ( ! is_bool( $validate ) && ! empty( $validate ) ) {
3657
					return pods_error( $validate, $error_mode );
3658
				}
3659
			}
3660
3661
			$value = PodsForm::pre_save( $field_data['type'], $value, $params->id, $field, array_merge( $field_data, $options ), array_merge( $fields, $object_fields ), $pod, $params );
3662
3663
			$field_data['value'] = $value;
3664
3665
			if ( isset( $object_fields[ $field ] ) ) {
3666
				// @todo Eventually support 'comment' field type saving here too
3667
				if ( 'taxonomy' === $object_fields[ $field ]['type'] ) {
3668
					$post_term_data[ $field ] = $value;
3669
				} else {
3670
					$object_data[ $field ] = $value;
3671
				}
3672
			} else {
3673
				$simple = ( 'pick' === $type && in_array( pods_var( 'pick_object', $field_data ), $simple_tableless_objects ) );
3674
				$simple = (boolean) $this->do_hook( 'tableless_custom', $simple, $field_data, $field, $fields, $pod, $params );
3675
3676
				// Handle Simple Relationships
3677
				if ( $simple ) {
3678
					if ( ! is_array( $value ) ) {
3679
						$value = explode( ',', $value );
3680
					}
3681
3682
					$pick_limit = (int) pods_var_raw( 'pick_limit', $options, 0 );
3683
3684
					if ( 'single' === pods_var_raw( 'pick_format_type', $options ) ) {
3685
						$pick_limit = 1;
3686
					}
3687
3688
					if ( 'custom-simple' === pods_var( 'pick_object', $field_data ) ) {
3689
						$custom = pods_var_raw( 'pick_custom', $options, '' );
3690
3691
						$custom = apply_filters( 'pods_form_ui_field_pick_custom_values', $custom, $field_data['name'], $value, array_merge( $field_data, $options ), $pod, $params->id );
3692
3693
						if ( empty( $value ) || empty( $custom ) ) {
3694
							$value = '';
3695
						} elseif ( ! empty( $custom ) ) {
3696
							if ( ! is_array( $custom ) ) {
3697
								$custom = explode( "\n", $custom );
3698
3699
								$custom_values = array();
3700
3701
								foreach ( $custom as $c => $cv ) {
3702
									if ( 0 < strlen( $cv ) ) {
3703
										$custom_label = explode( '|', $cv );
3704
3705
										if ( ! isset( $custom_label[1] ) ) {
3706
											$custom_label[1] = $custom_label[0];
3707
										}
3708
3709
										$custom_label[0]                   = trim( (string) $custom_label[0] );
3710
										$custom_label[1]                   = trim( (string) $custom_label[1] );
3711
										$custom_values[ $custom_label[0] ] = $custom_label[1];
3712
									}
3713
								}
3714
							} else {
3715
								$custom_values = $custom;
3716
							}
3717
3718
							$values = array();
3719
3720
							foreach ( $value as $k => $v ) {
3721
								$v = pods_unsanitize( $v );
3722
3723
								if ( isset( $custom_values[ $v ] ) ) {
3724
									$values[ $k ] = $v;
3725
								}
3726
							}
3727
3728
							$value = $values;
3729
						}
3730
					}
3731
3732
					if ( 0 < $pick_limit && ! empty( $value ) ) {
3733
						$value = array_slice( $value, 0, $pick_limit );
3734
					}
3735
3736
					// Don't save an empty array, just make it an empty string
3737
					if ( empty( $value ) ) {
3738
						$value = '';
3739
					} elseif ( is_array( $value ) ) {
3740
						if ( 1 == $pick_limit || 1 == count( $value ) ) {
3741
							// If there's just one item, don't save as an array, save the string
3742
							$value = implode( '', $value );
3743
						} elseif ( 'table' === pods_var( 'storage', $pod ) ) {
3744
							// If storage is set to table, json encode, otherwise WP will serialize automatically
3745
							$value = version_compare( PHP_VERSION, '5.4.0', '>=' ) ? json_encode( $value, JSON_UNESCAPED_UNICODE ) : json_encode( $value );
0 ignored issues
show
Unused Code introduced by
The call to json_encode() has too many arguments starting with JSON_UNESCAPED_UNICODE.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
3746
						}
3747
					}
3748
				}
3749
3750
				// Prepare all table / meta data
3751
				if ( ! in_array( $type, $tableless_field_types ) || $simple ) {
3752
					if ( in_array( $type, $repeatable_field_types ) && 1 == pods_var( $type . '_repeatable', $field_data, 0 ) ) {
3753
						// Don't save an empty array, just make it an empty string
3754
						if ( empty( $value ) ) {
3755
							$value = '';
3756
						} elseif ( is_array( $value ) ) {
3757
							if ( 1 == count( $value ) ) {
3758
								// If there's just one item, don't save as an array, save the string
3759
								$value = implode( '', $value );
3760
							} elseif ( 'table' === pods_var( 'storage', $pod ) ) {
3761
								// If storage is set to table, json encode, otherwise WP will serialize automatically
3762
								$value = version_compare( PHP_VERSION, '5.4.0', '>=' ) ? json_encode( $value, JSON_UNESCAPED_UNICODE ) : json_encode( $value );
0 ignored issues
show
Unused Code introduced by
The call to json_encode() has too many arguments starting with JSON_UNESCAPED_UNICODE.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
3763
							}
3764
						}
3765
					}
3766
3767
					$table_data[ $field ] = str_replace( array( '{prefix}', '@wp_' ), array(
3768
						'{/prefix/}',
3769
						'{prefix}'
3770
					), $value ); // Fix for pods_query
3771
					$table_formats[]      = PodsForm::prepare( $type, $options );
3772
3773
					$object_meta[ $field ] = $value;
3774
				} else {
3775
					// Store relational field data to be looped through later
3776
					// Convert values from a comma-separated string into an array
3777
					if ( ! is_array( $value ) ) {
3778
						$value = explode( ',', $value );
3779
					}
3780
3781
					$rel_fields[ $type ][ $field ] = $value;
3782
					$rel_field_ids[]               = $field_data['id'];
3783
				}
3784
			}
3785
		}
3786
3787
		if ( in_array( $pod['type'], array( 'post_type', 'taxonomy' ) ) ) {
3788
			$object_name = $pod['name'];
3789
3790
			if ( ! empty( $pod['object'] ) ) {
3791
				$object_name = $pod['object'];
3792
			}
3793
3794
			$object_name_field = 'post_type';
3795
3796
			if ( 'taxonomy' === $pod['type'] ) {
3797
				$object_name_field = 'taxonomy';
3798
			}
3799
3800
			$object_data[ $object_name_field ] = $object_name;
3801
		}
3802
3803
		if ( ! in_array( $pod['type'], array( 'pod', 'table', '' ) ) ) {
3804
			$meta_fields = array();
3805
3806
			if ( 'meta' === $pod['storage'] || 'settings' === $pod['type'] || ( 'taxonomy' === $pod['type'] && 'none' === $pod['storage'] ) ) {
3807
				$meta_fields = $object_meta;
3808
			}
3809
3810
			if ( $allow_custom_fields && ! empty( $custom_data ) ) {
3811
				$meta_fields = array_merge( $custom_data, $meta_fields );
3812
			}
3813
3814
			$fields_to_send = array_flip( array_keys( $meta_fields ) );
3815
3816
			foreach ( $fields_to_send as $field => $field_data ) {
3817
				if ( isset( $object_fields[ $field ] ) ) {
3818
					$field_data = $object_fields[ $field ];
3819
				} elseif ( isset( $fields[ $field ] ) ) {
3820
					$field_data = $fields[ $field ];
3821
				} else {
3822
					unset( $fields_to_send[ $field ] );
3823
				}
3824
3825
				$fields_to_send[ $field ] = $field_data;
3826
			}
3827
3828
			$params->id = $this->save_wp_object( $object_type, $object_data, $meta_fields, false, true, $fields_to_send );
3829
3830
			if ( ! empty( $params->id ) && 'settings' === $pod['type'] ) {
3831
				$params->id = $pod['id'];
3832
			}
3833
		}
3834
3835
		if ( 'table' === $pod['storage'] ) {
3836
			// Every row should have an id set here, otherwise Pods with nothing
3837
			// but relationship fields won't get properly ID'd
3838
			if ( empty( $params->id ) ) {
3839
				$params->id = 0;
3840
			}
3841
3842
			$table_data = array( 'id' => $params->id ) + $table_data;
3843
			array_unshift( $table_formats, '%d' );
3844
3845
			if ( ! empty( $table_data ) ) {
3846
				$sql = pods_data()->insert_on_duplicate( "@wp_pods_{$params->pod}", $table_data, $table_formats );
3847
3848
				$id = pods_query( $sql, 'Cannot add/save table row' );
3849
3850
				if ( empty( $params->id ) ) {
3851
					$params->id = $id;
3852
				}
3853
			}
3854
		}
3855
3856
		$params->id = (int) $params->id;
3857
3858
		// Save terms for taxonomies associated to a post type
3859
		if ( 0 < $params->id && 'post_type' === $pod['type'] && ! empty( $post_term_data ) ) {
3860
			foreach ( $post_term_data as $post_taxonomy => $post_terms ) {
3861
				$post_terms = (array) $post_terms;
3862
3863
				foreach ( $post_terms as $k => $v ) {
3864
					if ( ! preg_match( '/[^0-9]/', $v ) ) {
3865
						$v = (int) $v;
3866
					}
3867
3868
					$post_terms[ $k ] = $v;
3869
				}
3870
3871
				wp_set_object_terms( $params->id, $post_terms, $post_taxonomy );
3872
			}
3873
		}
3874
3875
		$no_conflict = pods_no_conflict_check( $pod['type'] );
3876
3877
		if ( ! $no_conflict ) {
3878
			pods_no_conflict_on( $pod['type'] );
3879
		}
3880
3881
		// Save relationship / file data
3882
		if ( ! empty( $rel_fields ) ) {
3883
			foreach ( $rel_fields as $type => $data ) {
3884
				// Only handle tableless fields
3885
				if ( ! in_array( $type, $tableless_field_types ) ) {
3886
					continue;
3887
				}
3888
3889
				foreach ( $data as $field => $values ) {
3890
					$pick_val = pods_var( 'pick_val', $fields[ $field ] );
3891
3892
					if ( 'table' === pods_var( 'pick_object', $fields[ $field ] ) ) {
3893
						$pick_val = pods_var( 'pick_table', $fields[ $field ]['options'], $pick_val, null, true );
3894
					}
3895
3896
					if ( '__current__' === $pick_val ) {
3897
						if ( is_object( $pod ) ) {
3898
							$pick_val = $pod->pod;
3899
						} elseif ( is_array( $pod ) ) {
3900
							$pick_val = $pod['name'];
3901
						} elseif ( 0 < strlen( $pod ) ) {
3902
							$pick_val = $pod;
3903
						}
3904
					}
3905
3906
					$fields[ $field ]['options']['table_info'] = pods_api()->get_table_info( pods_var( 'pick_object', $fields[ $field ] ), $pick_val, null, null, $fields[ $field ]['options'] );
3907
3908
					if ( isset( $fields[ $field ]['options']['table_info']['pod'] ) && ! empty( $fields[ $field ]['options']['table_info']['pod'] ) && isset( $fields[ $field ]['options']['table_info']['pod']['name'] ) ) {
3909
						$search_data = pods( $fields[ $field ]['options']['table_info']['pod']['name'] );
3910
3911
						$data_mode = 'pods';
3912
					} else {
3913
						$search_data = pods_data();
3914
						$search_data->table( $fields[ $field ]['options']['table_info'] );
3915
3916
						$data_mode = 'data';
3917
					}
3918
3919
					$find_rel_params = array(
3920
						'select'     => "`t`.`{$search_data->field_id}`",
3921
						'where'      => "`t`.`{$search_data->field_slug}` = %s OR `t`.`{$search_data->field_index}` = %s",
3922
						'limit'      => 1,
3923
						'pagination' => false,
3924
						'search'     => false
3925
					);
3926
3927
					if ( empty( $search_data->field_slug ) && ! empty( $search_data->field_index ) ) {
3928
						$find_rel_params['where'] = "`t`.`{$search_data->field_index}` = %s";
3929
					} elseif ( empty( $search_data->field_slug ) && empty( $search_data->field_index ) ) {
3930
						$find_rel_params = false;
3931
					}
3932
3933
					$related_limit = (int) pods_var_raw( $type . '_limit', $fields[ $field ]['options'], 0 );
3934
3935
					if ( 'single' === pods_var_raw( $type . '_format_type', $fields[ $field ]['options'] ) ) {
3936
						$related_limit = 1;
3937
					}
3938
3939
					// Enforce integers / unique values for IDs
3940
					$value_ids = array();
3941
3942
					$is_file_field = in_array( $type, PodsForm::file_field_types() );
3943
					$is_taggable   = ( in_array( $type, PodsForm::tableless_field_types() ) && 1 == pods_v( $type . '_taggable', $fields[ $field ]['options'] ) );
3944
3945
					// @todo Handle simple relationships eventually
3946
					foreach ( $values as $v ) {
3947
						if ( ! empty( $v ) ) {
3948
							if ( ! is_array( $v ) ) {
3949
								if ( ! preg_match( '/[^0-9]/', $v ) ) {
3950
									$v = (int) $v;
3951
								} elseif ( $is_file_field ) {
3952
									// File handling
3953
									// Get ID from GUID
3954
									$v = pods_image_id_from_field( $v );
3955
3956
									// If file not found, add it
3957
									if ( empty( $v ) ) {
3958
										$v = pods_attachment_import( $v );
3959
									}
3960
								} else {
3961
									// Reference by slug
3962
									$v_data = false;
3963
3964
									if ( false !== $find_rel_params ) {
3965
										$rel_params          = $find_rel_params;
3966
										$rel_params['where'] = $wpdb->prepare( $rel_params['where'], array( $v, $v ) );
3967
3968
										$search_data->select( $rel_params );
0 ignored issues
show
Bug introduced by
The method select does only exist in PodsData, but not in Pods.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
3969
3970
										$v_data = $search_data->fetch( $v );
3971
									}
3972
3973
									if ( ! empty( $v_data ) && isset( $v_data[ $search_data->field_id ] ) ) {
3974
										$v = (int) $v_data[ $search_data->field_id ];
3975
									} elseif ( $is_taggable && 'pods' === $data_mode ) {
3976
										// Allow tagging for Pods objects
3977
										$tag_data = array(
3978
											$search_data->field_index => $v
3979
										);
3980
3981
										if ( 'post_type' === $search_data->pod_data['type'] ) {
3982
											$tag_data['post_status'] = 'publish';
3983
										}
3984
3985
										/**
3986
										 * Filter for changing tag before adding new item.
3987
										 *
3988
										 * @param array  $tag_data    Fields for creating new item.
3989
										 * @param int    $v           Field ID of tag.
3990
										 * @param Pods   $search_data Search object for tag.
3991
										 * @param string $field       Table info for field.
3992
										 * @param array  $pieces      Field array.
3993
										 *
3994
										 * @since 2.3.19
3995
										 */
3996
										$tag_data = apply_filters( 'pods_api_save_pod_item_taggable_data', $tag_data, $v, $search_data, $field, compact( $pieces ) );
3997
3998
										// Save $v to a new item on related object
3999
										$v = $search_data->add( $tag_data );
0 ignored issues
show
Bug introduced by
The method add does only exist in Pods, but not in PodsData.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
4000
4001
										// @todo Support non-Pods for tagging
4002
									}
4003
								}
4004
							} elseif ( $is_file_field && isset( $v['id'] ) ) {
4005
								$v = (int) $v['id'];
4006
							} else {
4007
								continue;
4008
							}
4009
4010
							if ( ! empty( $v ) && ! in_array( $v, $value_ids ) ) {
4011
								$value_ids[] = $v;
4012
							}
4013
						}
4014
					}
4015
4016
					$value_ids = array_unique( array_filter( $value_ids ) );
4017
4018
					// Filter unique values not equal to false in case of a multidimensional array
4019
					$filtered_values          = $this->array_filter_walker( $values );
4020
					$serialized_values        = array_map( 'serialize', $filtered_values );
4021
					$unique_serialized_values = array_unique( $serialized_values );
0 ignored issues
show
Comprehensibility Naming introduced by
The variable name $unique_serialized_values exceeds the maximum configured length of 20.

Very long variable names usually make code harder to read. It is therefore recommended not to make variable names too verbose.

Loading history...
4022
4023
					$values = array_map( 'unserialize', $unique_serialized_values );
4024
4025
					// Limit values
4026
					if ( 0 < $related_limit && ! empty( $value_ids ) ) {
4027
						$value_ids = array_slice( $value_ids, 0, $related_limit );
4028
						$values    = array_slice( $values, 0, $related_limit );
4029
					}
4030
4031
					// Get current values
4032
					if ( 'pick' === $type && isset( PodsField_Pick::$related_data[ $fields[ $field ]['id'] ] ) && isset( PodsField_Pick::$related_data[ $fields[ $field ]['id'] ]['current_ids'] ) ) {
4033
						$related_ids = PodsField_Pick::$related_data[ $fields[ $field ]['id'] ]['current_ids'];
4034
					} else {
4035
						$related_ids = $this->lookup_related_items( $fields[ $field ]['id'], $pod['id'], $params->id, $fields[ $field ], $pod );
4036
					}
4037
4038
					// Get ids to remove
4039
					$remove_ids = array_diff( $related_ids, $value_ids );
4040
4041
					// Delete relationships
4042
					if ( ! empty( $remove_ids ) ) {
4043
						$this->delete_relationships( $params->id, $remove_ids, $pod, $fields[ $field ] );
4044
					}
4045
4046
					// Save relationships
4047
					if ( ! empty( $value_ids ) ) {
4048
						$this->save_relationships( $params->id, $value_ids, $pod, $fields[ $field ] );
4049
					}
4050
4051
					$field_save_values = $value_ids;
4052
4053
					if ( 'file' === $type ) {
4054
						$field_save_values = $values;
4055
					}
4056
4057
					// Run save function for field type (where needed)
4058
					PodsForm::save( $type, $field_save_values, $params->id, $field, array_merge( $fields[ $field ], $fields[ $field ]['options'] ), array_merge( $fields, $object_fields ), $pod, $params );
4059
				}
4060
4061
				// Unset data no longer needed
4062
				if ( 'pick' === $type ) {
4063
					foreach ( $data as $field => $values ) {
4064
						if ( isset( PodsField_Pick::$related_data[ $fields[ $field ]['id'] ] ) ) {
4065
							unset( PodsField_Pick::$related_data[ PodsField_Pick::$related_data[ $fields[ $field ]['id'] ]['related_field']['id'] ] );
4066
							unset( PodsField_Pick::$related_data[ $fields[ $field ]['id'] ] );
4067
						}
4068
					}
4069
				}
4070
			}
4071
		}
4072
4073
		if ( ! $no_conflict ) {
4074
			pods_no_conflict_off( $pod['type'] );
4075
		}
4076
4077
		// Clear cache
4078
		pods_cache_clear( $params->id, 'pods_items_' . $pod['name'] );
4079
4080
		if ( $params->clear_slug_cache && ! empty( $pod['field_slug'] ) ) {
4081
			$slug = pods( $pod['name'], $params->id )->field( $pod['field_slug'] );
4082
4083
			if ( 0 < strlen( $slug ) ) {
4084
				pods_cache_clear( $slug, 'pods_items_' . $pod['name'] );
4085
			}
4086
		}
4087
4088
		// Clear WP meta cache
4089
		if ( in_array( $pod['type'], array( 'post_type', 'media', 'taxonomy', 'user', 'comment' ) ) ) {
4090
			$meta_type = $pod['type'];
4091
4092
			if ( 'post_type' === $meta_type ) {
4093
				$meta_type = 'post';
4094
			}
4095
4096
			wp_cache_delete( $params->id, $meta_type . '_meta' );
4097
			wp_cache_delete( $params->id, 'pods_' . $meta_type . '_meta' );
4098
		}
4099
4100
		if ( false === $bypass_helpers ) {
4101
			if ( $track_changed_fields ) {
4102
				$changed_fields = self::handle_changed_fields( $params->pod, $params->id, 'get' );
4103
			}
4104
4105
			$compact_pieces = compact( $pieces );
4106
4107
			// Plugin hooks
4108
			$this->do_hook( 'post_save_pod_item', $compact_pieces, $is_new_item, $params->id );
4109
			$this->do_hook( "post_save_pod_item_{$params->pod}", $compact_pieces, $is_new_item, $params->id );
4110
4111
			if ( $is_new_item ) {
4112
				$this->do_hook( 'post_create_pod_item', $compact_pieces, $params->id );
4113
				$this->do_hook( "post_create_pod_item_{$params->pod}", $compact_pieces, $params->id );
4114
			} else {
4115
				$this->do_hook( 'post_edit_pod_item', $compact_pieces, $params->id );
4116
				$this->do_hook( "post_edit_pod_item_{$params->pod}", $compact_pieces, $params->id );
4117
			}
4118
4119
			// Call any post-save helpers (if not bypassed)
4120
			if ( ! defined( 'PODS_DISABLE_EVAL' ) || ! PODS_DISABLE_EVAL ) {
4121
				if ( ! empty( $post_save_helpers ) ) {
4122
					pods_deprecated( sprintf( __( 'Post-save helpers are deprecated, use the action pods_post_save_pod_item_%s instead', 'pods' ), $params->pod ), '2.0' );
4123
4124
					foreach ( $post_save_helpers as $helper ) {
4125
						$helper = $this->load_helper( array( 'name' => $helper ) );
4126
4127
						if ( false !== $helper && ( ! defined( 'PODS_DISABLE_EVAL' ) || ! PODS_DISABLE_EVAL ) ) {
4128
							eval( '?>' . $helper['code'] );
0 ignored issues
show
Coding Style introduced by
It is generally not recommended to use eval unless absolutely required.

On one hand, eval might be exploited by malicious users if they somehow manage to inject dynamic content. On the other hand, with the emergence of faster PHP runtimes like the HHVM, eval prevents some optimization that they perform.

Loading history...
4129
						}
4130
					}
4131
				}
4132
			}
4133
		}
4134
4135
		// Success! Return the id
4136
		return $params->id;
4137
4138
	}
4139
4140
	/**
4141
	 * @see   PodsAPI::save_pod_item
4142
	 * Add multiple pod items
4143
	 *
4144
	 * $params['pod'] string The Pod name (pod or pod_id is required)
4145
	 * $params['pod_id'] string The Pod ID (pod or pod_id is required)
4146
	 * $params['bypass_helpers'] bool Set to true to bypass running pre-save and post-save helpers
4147
	 *
4148
	 * $data['id'] int The item ID (optional)
4149
	 * $data['data'] array An associative array of field names + values
4150
	 *
4151
	 * @param array|object $params An associative array of parameters, data excluded
4152
	 * @param array        $data   An associative array of pod ids and field names + values (arrays of field data)
4153
	 *
4154
	 * @return int The item ID
0 ignored issues
show
Documentation introduced by
Should the return type not be array? Also, consider making the array more specific, something like array<String>, or String[].

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

If the return type contains the type array, this check recommends the use of a more specific type like String[] or array<String>.

Loading history...
4155
	 * @since 2.0
4156
	 */
4157
	public function save_pod_items( $params, $data ) {
4158
4159
		$params = (object) $params;
4160
4161
		$ids = array();
4162
4163
		foreach ( $data as $fields ) {
4164
			$params->data = $fields;
4165
4166
			if ( isset( $fields['id'] ) && isset( $fields['data'] ) ) {
4167
				$params->id   = $fields['id'];
4168
				$params->data = $fields['data'];
4169
			}
4170
4171
			$ids[] = $this->save_pod_item( $params );
4172
		}
4173
4174
		return $ids;
4175
	}
4176
4177
	/**
4178
	 *Handle tracking changed fields or get them
4179
	 *
4180
	 * @param string $pod
4181
	 * @param int    $id
4182
	 * @param string $mode
4183
	 *
4184
	 * @return array List of changed fields (if $mode = 'get')
4185
	 */
4186
	public static function handle_changed_fields( $pod, $id, $mode = 'set' ) {
4187
4188
		static $changed_pods_cache = array();
4189
		static $old_fields_cache = array();
4190
		static $changed_fields_cache = array();
4191
4192
		$cache_key = $pod . '|' . $id;
4193
4194
		$export_params = array(
4195
			'depth' => 1,
4196
		);
4197
4198
		if ( in_array( $mode, array( 'set', 'reset' ), true ) ) {
4199
			if ( isset( $changed_fields_cache[ $cache_key ] ) ) {
4200
				unset( $changed_fields_cache[ $cache_key ] );
4201
			}
4202
4203
			if ( empty( $old_fields_cache[ $cache_key ] ) || 'reset' === $mode ) {
4204
				$old_fields_cache[ $cache_key ] = array();
4205
4206
				if ( ! empty( $id ) ) {
4207
					if ( ! isset( $changed_pods_cache[ $pod ] ) ) {
4208
						$changed_pods_cache[ $pod ] = pods( $pod );
4209
					}
4210
4211
					if ( $changed_pods_cache[ $pod ] && $changed_pods_cache[ $pod ]->valid() ) {
4212
						$changed_pods_cache[ $pod ]->fetch( $id );
4213
4214
						$old_fields_cache[ $cache_key ] = $changed_pods_cache[ $pod ]->export( $export_params );
4215
					}
4216
				}
4217
			}
4218
		}
4219
4220
		$changed_fields = array();
4221
4222
		if ( isset( $changed_fields_cache[ $cache_key ] ) ) {
4223
			$changed_fields = $changed_fields_cache[ $cache_key ];
4224
		} elseif ( isset( $old_fields_cache[ $cache_key ] ) ) {
4225
			$old_fields = $old_fields_cache[ $cache_key ];
4226
4227
			if ( 'get' === $mode ) {
4228
				$changed_fields_cache[ $cache_key ] = array();
4229
4230
				if ( ! empty( $changed_pods_cache[ $pod ] ) ) {
4231
					if ( $id != $changed_pods_cache[ $pod ]->id() ) {
4232
						$changed_pods_cache[ $pod ]->fetch( $id );
4233
					}
4234
4235
					$new_fields = $changed_pods_cache[ $pod ]->export( $export_params );
4236
4237
					foreach ( $new_fields as $field => $value ) {
4238
						if ( ! isset( $old_fields[ $field ] ) || $value != $old_fields[ $field ] ) {
4239
							$changed_fields[ $field ] = $value;
4240
						}
4241
					}
4242
4243
					$changed_fields_cache[ $cache_key ] = $changed_fields;
4244
				}
4245
			}
4246
		}
4247
4248
		return $changed_fields;
4249
4250
	}
4251
4252
	/**
4253
	 * Get the fields that have changed during a save
4254
	 *
4255
	 * @param array $pieces Pieces array from save_pod_item
4256
	 *
4257
	 * @return array Array of fields and values that have changed
4258
	 *
4259
	 * @deprecated Use PodsAPI::handle_changed_fields
4260
	 */
4261
	public function get_changed_fields( $pieces ) {
4262
4263
		return self::handle_changed_fields( $pieces['params']->pod, $pieces['params']->id, 'get' );
4264
4265
	}
4266
4267
	/**
4268
	 * Save relationships
4269
	 *
4270
	 * @param int       $id         ID of item
4271
	 * @param int|array $related_id ID or IDs to save
0 ignored issues
show
Documentation introduced by
There is no parameter named $related_id. Did you maybe mean $related_ids?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
4272
	 * @param array     $pod        Pod data
4273
	 * @param array     $field      Field data
4274
	 */
4275
	public function save_relationships( $id, $related_ids, $pod, $field ) {
4276
4277
		// Get current values
4278
		if ( 'pick' === $field['type'] && isset( PodsField_Pick::$related_data[ $field['id'] ] ) && isset( PodsField_Pick::$related_data[ $field['id'] ]['current_ids'] ) ) {
4279
			$current_ids = PodsField_Pick::$related_data[ $field['id'] ]['current_ids'];
4280
		} else {
4281
			$current_ids = $this->lookup_related_items( $field['id'], $pod['id'], $id, $field, $pod );
4282
		}
4283
4284
		if ( isset( self::$related_item_cache[ $pod['id'] ][ $field['id'] ] ) ) {
4285
			// Delete relationship from cache
4286
			unset( self::$related_item_cache[ $pod['id'] ][ $field['id'] ] );
4287
		}
4288
4289
		if ( ! is_array( $related_ids ) ) {
4290
			$related_ids = implode( ',', $related_ids );
4291
		}
4292
4293
		foreach ( $related_ids as $k => $related_id ) {
0 ignored issues
show
Bug introduced by
The expression $related_ids of type string|array is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
4294
			$related_ids[ $k ] = (int) $related_id;
4295
		}
4296
4297
		$related_ids = array_unique( array_filter( $related_ids ) );
4298
4299
		$related_limit = (int) pods_var_raw( $field['type'] . '_limit', $field['options'], 0 );
4300
4301
		if ( 'single' === pods_var_raw( $field['type'] . '_format_type', $field['options'] ) ) {
4302
			$related_limit = 1;
4303
		}
4304
4305
		// Limit values
4306
		if ( 0 < $related_limit && ! empty( $related_ids ) ) {
4307
			$related_ids = array_slice( $related_ids, 0, $related_limit );
4308
		}
4309
4310
		// Post Types, Media, Users, and Comments (meta-based)
4311
		if ( in_array( $pod['type'], array( 'post_type', 'media', 'taxonomy', 'user', 'comment' ) ) ) {
4312
			$object_type = $pod['type'];
4313
4314
			if ( in_array( $object_type, array( 'post_type', 'media' ) ) ) {
4315
				$object_type = 'post';
4316
			} elseif ( 'taxonomy' === $object_type ) {
4317
				$object_type = 'term';
4318
			}
4319
4320
			delete_metadata( $object_type, $id, $field['name'] );
4321
4322
			if ( ! empty( $related_ids ) ) {
4323
				update_metadata( $object_type, $id, '_pods_' . $field['name'], $related_ids );
4324
4325
				foreach ( $related_ids as $related_id ) {
4326
					add_metadata( $object_type, $id, $field['name'], $related_id );
4327
				}
4328
			} else {
4329
				delete_metadata( $object_type, $id, '_pods_' . $field['name'] );
4330
			}
4331
		} elseif ( 'settings' === $pod['type'] ) {
4332
			// Custom Settings Pages (options-based)
4333
			if ( ! empty( $related_ids ) ) {
4334
				update_option( $pod['name'] . '_' . $field['name'], $related_ids );
4335
			} else {
4336
				delete_option( $pod['name'] . '_' . $field['name'] );
4337
			}
4338
		}
4339
4340
		$related_pod_id = $related_field_id = 0;
4341
4342
		if ( 'pick' === $field['type'] && isset( PodsField_Pick::$related_data[ $field['id'] ] ) && ! empty( PodsField_Pick::$related_data[ $field['id'] ]['related_field'] ) ) {
4343
			$related_pod_id   = PodsField_Pick::$related_data[ $field['id'] ]['related_pod']['id'];
4344
			$related_field_id = PodsField_Pick::$related_data[ $field['id'] ]['related_field']['id'];
4345
		}
4346
4347
		// Relationships table
4348
		if ( ! pods_tableless() ) {
4349
			$related_weight = 0;
4350
4351
			foreach ( $related_ids as $related_id ) {
4352
				if ( in_array( $related_id, $current_ids ) ) {
4353
					pods_query( "
4354
                        UPDATE `@wp_podsrel`
4355
                        SET
4356
                            `pod_id` = %d,
4357
                            `field_id` = %d,
4358
                            `item_id` = %d,
4359
                            `related_pod_id` = %d,
4360
                            `related_field_id` = %d,
4361
                            `related_item_id` = %d,
4362
                            `weight` = %d
4363
                        WHERE
4364
                            `pod_id` = %d
4365
                            AND `field_id` = %d
4366
                            AND `item_id` = %d
4367
                            AND `related_item_id` = %d
4368
                    ", array(
4369
						$pod['id'],
4370
						$field['id'],
4371
						$id,
4372
						$related_pod_id,
4373
						$related_field_id,
4374
						$related_id,
4375
						$related_weight,
4376
4377
						$pod['id'],
4378
						$field['id'],
4379
						$id,
4380
						$related_id,
4381
					) );
4382
				} else {
4383
					pods_query( "
4384
                        INSERT INTO `@wp_podsrel`
4385
                            (
4386
                                `pod_id`,
4387
                                `field_id`,
4388
                                `item_id`,
4389
                                `related_pod_id`,
4390
                                `related_field_id`,
4391
                                `related_item_id`,
4392
                                `weight`
4393
                            )
4394
                        VALUES ( %d, %d, %d, %d, %d, %d, %d )
4395
                    ", array(
4396
						$pod['id'],
4397
						$field['id'],
4398
						$id,
4399
						$related_pod_id,
4400
						$related_field_id,
4401
						$related_id,
4402
						$related_weight
4403
					) );
4404
				}
4405
4406
				$related_weight ++;
4407
			}
4408
		}
4409
	}
4410
4411
	/**
4412
	 * Duplicate a Pod
4413
	 *
4414
	 * $params['id'] int The Pod ID
4415
	 * $params['name'] string The Pod name
4416
	 * $params['new_name'] string The new Pod name
4417
	 *
4418
	 * @param array $params An associative array of parameters
4419
	 * @param bool  $strict (optional) Makes sure a pod exists, if it doesn't throws an error
4420
	 *
4421
	 * @return int New Pod ID
4422
	 * @since 2.3
4423
	 */
4424
	public function duplicate_pod( $params, $strict = false ) {
4425
4426
		if ( ! is_object( $params ) && ! is_array( $params ) ) {
4427
			if ( is_numeric( $params ) ) {
4428
				$params = array( 'id' => $params );
4429
			} else {
4430
				$params = array( 'name' => $params );
4431
			}
4432
4433
			$params = (object) pods_sanitize( $params );
4434
		} else {
4435
			$params = (object) pods_sanitize( $params );
4436
		}
4437
4438
		$params->table_info = false;
4439
4440
		$pod = $this->load_pod( $params, $strict );
4441
4442
		if ( empty( $pod ) ) {
4443
			if ( false !== $strict ) {
4444
				return pods_error( __( 'Pod not found', 'pods' ), $this );
4445
			}
4446
4447
			return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type documented by PodsAPI::duplicate_pod of type integer.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
4448
		} elseif ( in_array( $pod['type'], array( 'media', 'user', 'comment' ) ) ) {
4449
			if ( false !== $strict ) {
4450
				return pods_error( __( 'Pod not allowed to be duplicated', 'pods' ), $this );
4451
			}
4452
4453
			return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type documented by PodsAPI::duplicate_pod of type integer.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
4454
		} elseif ( in_array( $pod['type'], array( 'post_type', 'taxonomy' ) ) && 0 < strlen( $pod['object'] ) ) {
4455
			$pod['object'] = '';
4456
		}
4457
4458
		unset( $pod['id'] );
4459
4460
		if ( isset( $params->new_name ) ) {
4461
			$pod['name'] = $params->new_name;
4462
		}
4463
4464
		$try = 1;
4465
4466
		$check_name = $pod['name'];
4467
		$new_label  = $pod['label'];
4468
4469
		while ( $this->load_pod( array( 'name' => $check_name, 'table_info' => false ), false ) ) {
4470
			$try ++;
4471
4472
			$check_name = $pod['name'] . $try;
4473
			$new_label  = $pod['label'] . $try;
4474
		}
4475
4476
		$pod['name']  = $check_name;
4477
		$pod['label'] = $new_label;
4478
4479
		foreach ( $pod['fields'] as $field => $field_data ) {
4480
			unset( $pod['fields'][ $field ]['id'] );
4481
		}
4482
4483
		return $this->save_pod( $pod );
4484
	}
4485
4486
	/**
4487
	 * Duplicate a Field
4488
	 *
4489
	 * $params['pod_id'] int The Pod ID
4490
	 * $params['pod'] string The Pod name
4491
	 * $params['id'] int The Field ID
4492
	 * $params['name'] string The Field name
4493
	 * $params['new_name'] string The new Field name
4494
	 *
4495
	 * @param array $params An associative array of parameters
4496
	 * @param bool  $strict (optional) Makes sure a field exists, if it doesn't throws an error
4497
	 *
4498
	 * @return int New Field ID
4499
	 * @since 2.3.10
4500
	 */
4501
	public function duplicate_field( $params, $strict = false ) {
4502
4503
		if ( ! is_object( $params ) && ! is_array( $params ) ) {
4504
			if ( is_numeric( $params ) ) {
4505
				$params = array( 'id' => $params );
4506
			} else {
4507
				$params = array( 'name' => $params );
4508
			}
4509
		}
4510
4511
		$params = (object) pods_sanitize( $params );
4512
4513
		$params->table_info = false;
4514
4515
		$field = $this->load_field( $params, $strict );
4516
4517
		if ( empty( $field ) ) {
4518
			if ( false !== $strict ) {
4519
				return pods_error( __( 'Field not found', 'pods' ), $this );
4520
			}
4521
4522
			return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type documented by PodsAPI::duplicate_field of type integer.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
4523
		}
4524
4525
		unset( $field['id'] );
4526
4527
		if ( isset( $params->new_name ) ) {
4528
			$field['name'] = $params->new_name;
4529
		}
4530
4531
		$try = 1;
4532
4533
		$check_name = $field['name'];
4534
		$new_label  = $field['label'];
4535
4536
		while ( $this->load_field( array(
4537
			'pod_id'     => $field['pod_id'],
4538
			'name'       => $check_name,
4539
			'table_info' => false
4540
		), false ) ) {
4541
			$try ++;
4542
4543
			$check_name = $field['name'] . $try;
4544
			$new_label  = $field['label'] . $try;
4545
		}
4546
4547
		$field['name']  = $check_name;
4548
		$field['label'] = $new_label;
4549
4550
		return $this->save_field( $field );
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->save_field($field); of type integer|array adds the type array to the return on line 4550 which is incompatible with the return type documented by PodsAPI::duplicate_field of type integer.
Loading history...
4551
4552
	}
4553
4554
	/**
4555
	 * @see   PodsAPI::save_pod_item
4556
	 *
4557
	 * Duplicate a pod item
4558
	 *
4559
	 * $params['pod'] string The Pod name
4560
	 * $params['id'] int The item's ID from the wp_pods_* table
4561
	 *
4562
	 * @param array $params An associative array of parameters
4563
	 *
4564
	 * @return int The table row ID
4565
	 *
4566
	 * @since 1.12
4567
	 */
4568
	public function duplicate_pod_item( $params ) {
4569
4570
		$params = (object) pods_sanitize( $params );
4571
4572
		$load_pod_params = array(
4573
			'name'       => $params->pod,
4574
			'table_info' => false,
4575
		);
4576
4577
		$pod = $this->load_pod( $load_pod_params );
4578
4579
		if ( false === $pod ) {
4580
			return pods_error( __( 'Pod not found', 'pods' ), $this );
4581
		}
4582
4583
		$pod = pods( $params->pod, $params->id );
4584
4585
		$params->pod    = $pod->pod;
4586
		$params->pod_id = $pod->pod_id;
4587
4588
		$fields        = (array) pods_var_raw( 'fields', $pod->pod_data, array(), null, true );
4589
		$object_fields = (array) pods_var_raw( 'object_fields', $pod->pod_data, array(), null, true );
4590
4591
		if ( ! empty( $object_fields ) ) {
4592
			$fields = array_merge( $object_fields, $fields );
4593
		}
4594
4595
		$save_params = array(
4596
			'pod'         => $params->pod,
4597
			'data'        => array(),
4598
			'is_new_item' => true,
4599
		);
4600
4601
		$ignore_fields = array(
4602
			$pod->pod_data['field_id'],
4603
			$pod->pod_data['field_slug'],
4604
		);
4605
4606
		if ( in_array( $pod->pod_data['type'], array( 'post_type', 'media' ) ) ) {
4607
			$ignore_fields = array(
4608
				'ID',
4609
				'post_name',
4610
				'post_date',
4611
				'post_date_gmt',
4612
				'post_modified',
4613
				'post_modified_gmt',
4614
				'guid',
4615
			);
4616
		} elseif ( 'term' === $pod->pod_data['type'] ) {
4617
			$ignore_fields = array(
4618
				'term_id',
4619
				'term_taxonomy_id',
4620
				'slug',
4621
			);
4622
		} elseif ( 'user' === $pod->pod_data['type'] ) {
4623
			$ignore_fields = array(
4624
				'ID',
4625
				'user_nicename',
4626
			);
4627
		} elseif ( 'comment' === $pod->pod_data['type'] ) {
4628
			$ignore_fields = array(
4629
				'comment_ID',
4630
			);
4631
		} elseif ( 'settings' === $pod->pod_data['type'] ) {
4632
			return pods_error( __( 'Settings do not support duplication.', 'pods' ), $this );
4633
		}
4634
4635
		/**
4636
		 * Filter the fields to ignore during duplication
4637
		 *
4638
		 * @since 2.6.6
4639
		 *
4640
		 * @param array  $ignore_fields Fields to ignore and not save during duplication
4641
		 * @param Pods   $pod           Pod object
4642
		 * @param array  $fields        Fields on the pod to duplicate
4643
		 * @param object $params        Params passed into duplicate_pod_item()
4644
		 */
4645
		$ignore_fields = apply_filters( 'pods_api_duplicate_pod_item_ignore_fields', $ignore_fields, $pod, $fields, $params );
4646
4647
		foreach ( $fields as $field ) {
4648
			if ( in_array( $field['name'], $ignore_fields ) ) {
4649
				continue;
4650
			}
4651
4652
			$field = array(
4653
				'name'   => $field['name'],
4654
				'output' => 'ids'
4655
			);
4656
4657
			$value = $pod->field( $field );
4658
4659
			// @todo Add post type compatibility to set unique post_title
4660
			// @todo Add term compatibility to set unique name
4661
			// @todo Add user compatibility to set unique user_login/user_email
4662
4663
			if ( ! empty( $value ) || ( ! is_array( $value ) && 0 < strlen( $value ) ) ) {
4664
				$save_params['data'][ $field['name'] ] = $value;
4665
			}
4666
		}
4667
4668
		$save_params = $this->do_hook( 'duplicate_pod_item', $save_params, $pod->pod, $pod->id(), $params );
4669
4670
		$id = $this->save_pod_item( $save_params );
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->save_pod_item($save_params); of type integer|array adds the type array to the return on line 4672 which is incompatible with the return type documented by PodsAPI::duplicate_pod_item of type integer.
Loading history...
4671
4672
		return $id;
4673
4674
	}
4675
4676
	/**
4677
	 * @see   pods()
4678
	 *
4679
	 * Export a pod item
4680
	 *
4681
	 * $params['pod'] string The Pod name
4682
	 * $params['id'] int The item's ID from the wp_pods_* table
4683
	 * $params['fields'] array The fields to export
4684
	 * $params['depth'] int How many levels deep to export data
4685
	 *
4686
	 * @param array  $params An associative array of parameters
4687
	 * @param object $pod    (optional) Pods object
0 ignored issues
show
Documentation introduced by
Should the type for parameter $pod not be object|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
4688
	 *
4689
	 * @return int The table row ID
4690
	 * @since 1.12
4691
	 */
4692
	public function export_pod_item( $params, $pod = null ) {
4693
4694
		if ( ! is_object( $pod ) || 'Pods' !== get_class( $pod ) ) {
4695
			if ( empty( $params ) ) {
4696
				return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type documented by PodsAPI::export_pod_item of type integer.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
4697
			}
4698
4699
			if ( is_object( $params ) ) {
4700
				$params = get_object_vars( (object) $params );
4701
			}
4702
4703
			$params = pods_sanitize( $params );
4704
4705
			$pod = pods( $params['pod'], $params['id'], false );
4706
4707
			if ( empty( $pod ) ) {
4708
				return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type documented by PodsAPI::export_pod_item of type integer.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
4709
			}
4710
		}
4711
4712
		$params['fields']        = (array) pods_v( 'fields', $params, array(), true );
4713
		$params['depth']         = (int) pods_v( 'depth', $params, 2, true );
4714
		$params['object_fields'] = (array) pods_v( 'object_fields', $pod->pod_data, array(), true );
4715
		$params['flatten']       = (boolean) pods_v( 'flatten', $params, false, true );
4716
		$params['context']       = pods_v( 'context', $params, null, true );
4717
4718
		if ( empty( $params['fields'] ) ) {
4719
			$params['fields'] = array_merge( $pod->fields, $params['object_fields'] );
4720
		}
4721
4722
		$data = $this->export_pod_item_level( $pod, $params );
4723
4724
		$data = $this->do_hook( 'export_pod_item', $data, $pod->pod, $pod->id(), $pod, $params['fields'], $params['depth'], $params['flatten'], $params );
4725
4726
		return $data;
4727
	}
4728
4729
	/**
4730
	 * Export a pod item by depth level
4731
	 *
4732
	 * @param Pods  $pod    Pods object
4733
	 * @param array $params Export params
4734
	 *
4735
	 * @return array Data array
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use array<string,integer>.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
4736
	 *
4737
	 * @since 2.3
4738
	 */
4739
	private function export_pod_item_level( $pod, $params ) {
4740
4741
		$fields        = $params['fields'];
4742
		$depth         = $params['depth'];
4743
		$flatten       = $params['flatten'];
4744
		$current_depth = pods_v( 'current_depth', $params, 1, true );
4745
		$context       = $params['context'];
4746
4747
		$tableless_field_types    = PodsForm::tableless_field_types();
0 ignored issues
show
Comprehensibility Naming introduced by
The variable name $tableless_field_types exceeds the maximum configured length of 20.

Very long variable names usually make code harder to read. It is therefore recommended not to make variable names too verbose.

Loading history...
4748
		$simple_tableless_objects = PodsForm::simple_tableless_objects();
0 ignored issues
show
Comprehensibility Naming introduced by
The variable name $simple_tableless_objects exceeds the maximum configured length of 20.

Very long variable names usually make code harder to read. It is therefore recommended not to make variable names too verbose.

Loading history...
4749
4750
		$object_fields = (array) pods_v( 'object_fields', $pod->pod_data, array(), true );
4751
4752
		$export_fields = array();
4753
4754
		$pod_type = $pod->pod_data['type'];
4755
4756
		if ( 'post_type' === $pod_type ) {
4757
			$pod_type = 'post';
4758
		} elseif ( 'taxonomy' === $pod_type ) {
4759
			$pod_type = 'term';
4760
		}
4761
4762
		$registered_meta_keys = false;
4763
4764
		if ( function_exists( 'get_registered_meta_keys' ) ) {
4765
			$registered_meta_keys = get_registered_meta_keys( $pod_type );
4766
		}
4767
4768
		$show_in_rest = false;
4769
4770
		// If in rest, check if this pod can be exposed
4771
		if ( 'rest' === $context ) {
4772
			$read_all = (int) pods_v( 'read_all', $pod->pod_data['options'], 0 );
4773
4774
			if ( 1 === $read_all ) {
4775
				$show_in_rest = true;
4776
			}
4777
		}
4778
4779
		foreach ( $fields as $k => $field ) {
4780
			if ( ! is_array( $field ) ) {
4781
				$field = array(
4782
					'id'   => 0,
4783
					'name' => $field
4784
				);
4785
			}
4786
4787
			if ( isset( $pod->fields[ $field['name'] ] ) ) {
4788
				// If in rest, check if this field can be exposed
4789
				if ( 'rest' === $context && false === $show_in_rest ) {
4790
					$show_in_rest = PodsRESTFields::field_allowed_to_extend( $field['name'], $pod, 'read' );
4791
4792
					if ( false === $show_in_rest ) {
4793
						// Fallback to checking $registered_meta_keys
4794
						if ( false !== $registered_meta_keys ) {
4795
							if ( ! isset( $registered_meta_keys[ $field['name'] ] ) ) {
4796
								continue;
4797
							} elseif ( empty( $registered_meta_keys[ $field['name'] ]['show_in_rest'] ) ) {
4798
								continue;
4799
							}
4800
						}
4801
					}
4802
				}
4803
4804
				$field                = $pod->fields[ $field['name'] ];
4805
				$field['lookup_name'] = $field['name'];
4806
4807
				if ( in_array( $field['type'], $tableless_field_types ) && ! in_array( pods_var( 'pick_object', $field ), $simple_tableless_objects ) ) {
4808
					if ( 'pick' === $field['type'] ) {
4809
						if ( empty( $field['table_info'] ) ) {
4810
							$field['table_info'] = $this->get_table_info( pods_var_raw( 'pick_object', $field ), pods_var_raw( 'pick_val', $field ), null, null, $field );
4811
						}
4812
4813
						if ( ! empty( $field['table_info'] ) ) {
4814
							$field['lookup_name'] .= '.' . $field['table_info']['field_id'];
4815
						}
4816
					} elseif ( in_array( $field['type'], PodsForm::file_field_types() ) ) {
4817
						$field['lookup_name'] .= '.guid';
4818
					}
4819
				}
4820
4821
				$export_fields[ $field['name'] ] = $field;
4822
			} elseif ( isset( $object_fields[ $field['name'] ] ) ) {
4823
				$field                = $object_fields[ $field['name'] ];
4824
				$field['lookup_name'] = $field['name'];
4825
4826
				$export_fields[ $field['name'] ] = $field;
4827
			} elseif ( $field['name'] == $pod->pod_data['field_id'] ) {
4828
				$field['type']        = 'number';
4829
				$field['lookup_name'] = $field['name'];
4830
4831
				$export_fields[ $field['name'] ] = $field;
4832
			}
4833
		}
4834
4835
		$data = array();
4836
4837
		foreach ( $export_fields as $field ) {
4838
			// Return IDs (or guid for files) if only one level deep
4839
			if ( 1 == $depth ) {
4840
				$data[ $field['name'] ] = $pod->field( array( 'name' => $field['lookup_name'], 'output' => 'arrays' ) );
4841
			} elseif ( ( - 1 == $depth || $current_depth < $depth ) && 'pick' === $field['type'] && ! in_array( pods_var( 'pick_object', $field ), $simple_tableless_objects ) ) {
4842
				// Recurse depth levels for pick fields if $depth allows
4843
				$related_data = array();
4844
4845
				$related_ids = $pod->field( array( 'name' => $field['name'], 'output' => 'ids' ) );
4846
4847
				if ( ! empty( $related_ids ) ) {
4848
					$related_ids = (array) $related_ids;
4849
4850
					$pick_object = pods_var_raw( 'pick_object', $field );
4851
4852
					$related_pod = pods( pods_var_raw( 'pick_val', $field ), null, false );
4853
4854
					// If this isn't a Pod, return data exactly as Pods does normally
4855
					if ( empty( $related_pod ) || ( 'pod' !== $pick_object && $pick_object !== $related_pod->pod_data['type'] ) || $related_pod->pod == $pod->pod ) {
4856
						$related_data = $pod->field( array( 'name' => $field['name'], 'output' => 'arrays' ) );
4857
					} else {
4858
						$related_object_fields = (array) pods_var_raw( 'object_fields', $related_pod->pod_data, array(), null, true );
0 ignored issues
show
Comprehensibility Naming introduced by
The variable name $related_object_fields exceeds the maximum configured length of 20.

Very long variable names usually make code harder to read. It is therefore recommended not to make variable names too verbose.

Loading history...
4859
4860
						$related_fields = array_merge( $related_pod->fields, $related_object_fields );
4861
4862
						foreach ( $related_ids as $related_id ) {
4863
							if ( $related_pod->fetch( $related_id ) ) {
4864
								$related_params = array(
4865
									'fields'        => $related_fields,
4866
									'depth'         => $depth,
4867
									'flatten'       => $flatten,
4868
									'current_depth' => $current_depth + 1,
4869
									'context'       => $context,
4870
								);
4871
4872
								$related_item = $this->export_pod_item_level( $related_pod, $related_params );
4873
4874
								$related_data[ $related_id ] = $this->do_hook( 'export_pod_item_level', $related_item, $related_pod->pod, $related_pod->id(), $related_pod, $related_fields, $depth, $flatten, ( $current_depth + 1 ), $params );
4875
							}
4876
						}
4877
4878
						if ( $flatten && ! empty( $related_data ) ) {
4879
							$related_data = pods_serial_comma( array_values( $related_data ), array(
4880
								'and'         => '',
4881
								'field_index' => $related_pod->pod_data['field_index']
4882
							) );
4883
						}
4884
					}
4885
				}
4886
4887
				$data[ $field['name'] ] = $related_data;
4888
			} else {
4889
				// Return data exactly as Pods does normally
4890
				$data[ $field['name'] ] = $pod->field( array( 'name' => $field['name'], 'output' => 'arrays' ) );
4891
			}
4892
4893
			if ( $flatten && is_array( $data[ $field['name'] ] ) ) {
4894
				$data[ $field['name'] ] = pods_serial_comma( $data[ $field['name'] ], array(
4895
					'field'  => $field['name'],
4896
					'fields' => $export_fields,
4897
					'and'    => ''
4898
				) );
4899
			}
4900
		}
4901
4902
		$data['id'] = (int) $pod->id();
4903
4904
		return $data;
4905
	}
4906
4907
	/**
4908
	 * Reorder a Pod
4909
	 *
4910
	 * $params['pod'] string The Pod name
4911
	 * $params['field'] string The field name of the field to reorder
4912
	 * $params['order'] array The key => value array of items to reorder (key should be an integer)
4913
	 *
4914
	 * @param array $params An associative array of parameters
4915
	 *
4916
	 * @return bool
4917
	 *
4918
	 * @since 1.9.0
4919
	 */
4920
	public function reorder_pod_item( $params ) {
4921
4922
		$params = (object) pods_sanitize( $params );
4923
4924
		// @deprecated 2.0
4925
		if ( isset( $params->datatype ) ) {
4926
			pods_deprecated( __( '$params->pod instead of $params->datatype', 'pods' ), '2.0' );
4927
4928
			$params->pod = $params->datatype;
4929
4930
			unset( $params->datatype );
4931
		}
4932
4933
		if ( null === pods_var_raw( 'pod', $params, null, null, true ) ) {
4934
			return pods_error( __( '$params->pod is required', 'pods' ), $this );
4935
		}
4936
4937
		if ( ! is_array( $params->order ) ) {
4938
			$params->order = explode( ',', $params->order );
4939
		}
4940
4941
		$pod = $this->load_pod( array( 'name' => $params->pod, 'table_info' => true ) );
4942
4943
		$params->name = $pod['name'];
4944
4945
		if ( false === $pod ) {
4946
			return pods_error( __( 'Pod is required', 'pods' ), $this );
4947
		}
4948
4949
		foreach ( $params->order as $order => $id ) {
4950
			if ( isset( $pod['fields'][ $params->field ] ) || isset( $pod['object_fields'][ $params->field ] ) ) {
4951
				if ( 'table' === $pod['storage'] && ( ! pods_tableless() ) ) {
4952
					if ( isset( $pod['fields'][ $params->field ] ) ) {
4953
						pods_query( "UPDATE `@wp_pods_{$params->name}` SET `{$params->field}` = " . pods_absint( $order ) . " WHERE `id` = " . pods_absint( $id ) . " LIMIT 1" );
4954
					} else {
4955
						pods_query( "UPDATE `{$pod['table']}` SET `{$params->field}` = " . pods_absint( $order ) . " WHERE `{$pod['field_id']}` = " . pods_absint( $id ) . " LIMIT 1" );
4956
					}
4957
				} else {
4958
					$this->save_pod_item( array(
4959
						'pod'    => $params->pod,
4960
						'pod_id' => $params->pod_id,
4961
						'id'     => $id,
4962
						'data'   => array( $params->field => pods_absint( $order ) )
4963
					) );
4964
				}
4965
			}
4966
		}
4967
4968
		return true;
4969
	}
4970
4971
	/**
4972
	 *
4973
	 * Delete all content for a Pod
4974
	 *
4975
	 * $params['id'] int The Pod ID
4976
	 * $params['name'] string The Pod name
4977
	 *
4978
	 * @param array $params An associative array of parameters
4979
	 * @param array $pod    Pod data
0 ignored issues
show
Documentation introduced by
Should the type for parameter $pod not be false|array? Also, consider making the array more specific, something like array<String>, or String[].

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive. In addition it looks for parameters that have the generic type array and suggests a stricter type like array<String>.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
4980
	 *
4981
	 * @return bool
4982
	 *
4983
	 * @uses  pods_query
4984
	 * @uses  pods_cache_clear
4985
	 *
4986
	 * @since 1.9.0
4987
	 */
4988
	public function reset_pod( $params, $pod = false ) {
4989
4990
		$params = (object) pods_sanitize( $params );
4991
4992
		$params->table_info = true;
4993
4994
		if ( empty( $pod ) ) {
4995
			$pod = $this->load_pod( $params );
4996
		}
4997
4998
		if ( false === $pod ) {
4999
			return pods_error( __( 'Pod not found', 'pods' ), $this );
5000
		}
5001
5002
		$params->id   = $pod['id'];
5003
		$params->name = $pod['name'];
5004
5005
		if ( ! pods_tableless() ) {
5006
			if ( 'table' === $pod['storage'] ) {
5007
				try {
5008
					pods_query( "TRUNCATE `@wp_pods_{$params->name}`", false );
5009
				} catch ( Exception $e ) {
5010
					// Allow pod to be reset if the table doesn't exist
5011
					if ( false === strpos( $e->getMessage(), 'Unknown table' ) ) {
5012
						return pods_error( $e->getMessage(), $this );
5013
					}
5014
				}
5015
			}
5016
5017
			pods_query( "DELETE FROM `@wp_podsrel` WHERE `pod_id` = {$params->id} OR `related_pod_id` = {$params->id}", false );
5018
		}
5019
5020
		// @todo Delete relationships from tableless relationships
5021
5022
		// Delete all posts/revisions from this post type
5023
		if ( in_array( $pod['type'], array( 'post_type', 'media' ) ) ) {
5024
			$type = pods_var( 'object', $pod, $pod['name'], null, true );
5025
5026
			$sql = "
5027
                DELETE `t`, `r`, `m`
5028
                FROM `{$pod['table']}` AS `t`
5029
                LEFT JOIN `{$pod['meta_table']}` AS `m`
5030
                    ON `m`.`{$pod['meta_field_id']}` = `t`.`{$pod['field_id']}`
5031
                LEFT JOIN `{$pod['table']}` AS `r`
5032
                    ON `r`.`post_parent` = `t`.`{$pod['field_id']}` AND `r`.`post_status` = 'inherit'
5033
                WHERE `t`.`{$pod['field_type']}` = '{$type}'
5034
            ";
5035
5036
			pods_query( $sql, false );
5037
		} elseif ( 'taxonomy' === $pod['type'] ) {
5038
			// Delete all terms from this taxonomy
5039
			if ( function_exists( 'get_term_meta' ) ) {
5040
				$sql = "
5041
                    DELETE `t`, `m`, `tt`, `tr`
5042
                    FROM `{$pod['table']}` AS `t`
5043
                    LEFT JOIN `{$pod['meta_table']}` AS `m`
5044
                        ON `m`.`{$pod['meta_field_id']}` = `t`.`{$pod['field_id']}`
5045
                    " . $pod['join']['tt'] . "
5046
                    " . $pod['join']['tr'] . "
5047
                    WHERE " . implode( ' AND ', $pod['where'] ) . "
5048
                ";
5049
			} else {
5050
				$sql = "
5051
                    DELETE `t`, `tt`, `tr`
5052
                    FROM `{$pod['table']}` AS `t`
5053
                    " . $pod['join']['tt'] . "
5054
                    " . $pod['join']['tr'] . "
5055
                    WHERE " . implode( ' AND ', $pod['where'] ) . "
5056
                ";
5057
			}
5058
5059
			pods_query( $sql, false );
5060
		} elseif ( 'user' === $pod['type'] ) {
5061
			// Delete all users except the current one
5062
			$sql = "
5063
                DELETE `t`, `m`
5064
                FROM `{$pod['table']}` AS `t`
5065
                LEFT JOIN `{$pod['meta_table']}` AS `m`
5066
                    ON `m`.`{$pod['meta_field_id']}` = `t`.`{$pod['field_id']}`
5067
                WHERE `t`.`{$pod['field_id']}` != " . (int) get_current_user_id() . "
5068
            ";
5069
5070
			pods_query( $sql, false );
5071
		} elseif ( 'comment' === $pod['type'] ) {
5072
			// Delete all comments
5073
			$type = pods_var( 'object', $pod, $pod['name'], null, true );
5074
5075
			$sql = "
5076
                DELETE `t`, `m`
5077
                FROM `{$pod['table']}` AS `t`
5078
                LEFT JOIN `{$pod['meta_table']}` AS `m`
5079
                    ON `m`.`{$pod['meta_field_id']}` = `t`.`{$pod['field_id']}`
5080
                WHERE `t`.`{$pod['field_type']}` = '{$type}'
5081
            ";
5082
5083
			pods_query( $sql, false );
5084
		}
5085
5086
		pods_cache_clear( true ); // only way to reliably clear out cached data across an entire group
5087
5088
		return true;
5089
	}
5090
5091
	/**
5092
	 * Delete a Pod and all its content
5093
	 *
5094
	 * $params['id'] int The Pod ID
5095
	 * $params['name'] string The Pod name
5096
	 *
5097
	 * @param array $params     An associative array of parameters
5098
	 * @param bool  $strict     (optional) Makes sure a pod exists, if it doesn't throws an error
5099
	 * @param bool  $delete_all (optional) Whether to delete all content from a WP object
5100
	 *
5101
	 * @uses  PodsAPI::load_pod
5102
	 * @uses  wp_delete_post
5103
	 * @uses  pods_query
5104
	 *
5105
	 * @return bool
5106
	 * @since 1.7.9
5107
	 */
5108
	public function delete_pod( $params, $strict = false, $delete_all = false ) {
5109
5110
		/**
5111
		 * @var $wpdb wpdb
5112
		 */
5113
		global $wpdb;
5114
5115
		if ( ! is_object( $params ) && ! is_array( $params ) ) {
5116
			if ( is_numeric( $params ) ) {
5117
				$params = array( 'id' => $params );
5118
			} else {
5119
				$params = array( 'name' => $params );
5120
			}
5121
5122
			$params = (object) pods_sanitize( $params );
5123
		} else {
5124
			$params = (object) pods_sanitize( $params );
5125
		}
5126
5127
		if ( ! isset( $params->delete_all ) ) {
5128
			$params->delete_all = $delete_all;
5129
		}
5130
5131
		$params->table_info = false;
5132
5133
		$pod = $this->load_pod( $params, $strict );
5134
5135
		if ( empty( $pod ) ) {
5136
			if ( false !== $strict ) {
5137
				return pods_error( __( 'Pod not found', 'pods' ), $this );
5138
			}
5139
5140
			return false;
5141
		}
5142
5143
		$params->id   = (int) $pod['id'];
5144
		$params->name = $pod['name'];
5145
5146
		// Reset content
5147
		if ( true === $params->delete_all ) {
5148
			$this->reset_pod( $params, $pod );
5149
		}
5150
5151
		foreach ( $pod['fields'] as $field ) {
5152
			$field['pod'] = $pod;
5153
5154
			$this->delete_field( $field, false );
5155
		}
5156
5157
		// Only delete the post once the fields are taken care of, it's not required anymore
5158
		$success = wp_delete_post( $params->id );
5159
5160
		if ( ! $success ) {
5161
			return pods_error( __( 'Pod unable to be deleted', 'pods' ), $this );
5162
		}
5163
5164
		if ( ! pods_tableless() ) {
5165
			if ( 'table' === $pod['storage'] ) {
5166
				try {
5167
					pods_query( "DROP TABLE IF EXISTS `@wp_pods_{$params->name}`", false );
5168
				} catch ( Exception $e ) {
5169
					// Allow pod to be deleted if the table doesn't exist
5170
					if ( false === strpos( $e->getMessage(), 'Unknown table' ) ) {
5171
						return pods_error( $e->getMessage(), $this );
5172
					}
5173
				}
5174
			}
5175
5176
			pods_query( "DELETE FROM `@wp_podsrel` WHERE `pod_id` = {$params->id} OR `related_pod_id` = {$params->id}", false );
5177
		}
5178
5179
		// @todo Delete relationships from tableless relationships
5180
5181
		// Delete any relationship references
5182
		$sql = "
5183
            DELETE `pm`
5184
            FROM `{$wpdb->postmeta}` AS `pm`
5185
            LEFT JOIN `{$wpdb->posts}` AS `p`
5186
                ON `p`.`post_type` = '_pods_field'
5187
                    AND `p`.`ID` = `pm`.`post_id`
5188
            LEFT JOIN `{$wpdb->postmeta}` AS `pm2`
5189
                ON `pm2`.`meta_key` = 'pick_object'
5190
                    AND `pm2`.`meta_value` = 'pod'
5191
                    AND `pm2`.`post_id` = `pm`.`post_id`
5192
            WHERE
5193
                `p`.`ID` IS NOT NULL
5194
                AND `pm2`.`meta_id` IS NOT NULL
5195
                AND `pm`.`meta_key` = 'pick_val'
5196
                AND `pm`.`meta_value` = '{$params->name}'
5197
        ";
5198
5199
		pods_query( $sql );
5200
5201
		$this->cache_flush_pods( $pod );
5202
5203
		return true;
5204
	}
5205
5206
	/**
5207
	 * Drop a field within a Pod
5208
	 *
5209
	 * $params['id'] int The field ID
5210
	 * $params['name'] int The field name
5211
	 * $params['pod'] string The Pod name
5212
	 * $params['pod_id'] string The Pod name
5213
	 *
5214
	 * @param array $params          An associative array of parameters
5215
	 * @param bool  $table_operation Whether or not to handle table operations
5216
	 *
5217
	 * @uses  PodsAPI::load_field
5218
	 * @uses  wp_delete_post
5219
	 * @uses  pods_query
5220
	 *
5221
	 * @return bool
5222
	 * @since 1.7.9
5223
	 */
5224
	public function delete_field( $params, $table_operation = true ) {
5225
5226
		/**
5227
		 * @var $wpdb wpdb
5228
		 */
5229
		global $wpdb;
5230
5231
		$tableless_field_types    = PodsForm::tableless_field_types();
0 ignored issues
show
Comprehensibility Naming introduced by
The variable name $tableless_field_types exceeds the maximum configured length of 20.

Very long variable names usually make code harder to read. It is therefore recommended not to make variable names too verbose.

Loading history...
5232
		$simple_tableless_objects = PodsForm::simple_tableless_objects();
0 ignored issues
show
Comprehensibility Naming introduced by
The variable name $simple_tableless_objects exceeds the maximum configured length of 20.

Very long variable names usually make code harder to read. It is therefore recommended not to make variable names too verbose.

Loading history...
5233
5234
		$params = (object) pods_sanitize( $params );
5235
5236
		if ( ! isset( $params->pod ) ) {
5237
			$params->pod = '';
5238
		}
5239
5240
		if ( ! isset( $params->pod_id ) ) {
5241
			$params->pod_id = 0;
5242
		}
5243
5244
		$pod = $params->pod;
5245
5246
		$save_pod = false;
5247
5248
		if ( ! is_array( $pod ) ) {
5249
			$pod = $this->load_pod( array( 'name' => $pod, 'id' => $params->pod_id, 'table_info' => false ) );
5250
		} else {
5251
			$save_pod = true;
5252
		}
5253
5254
		if ( empty( $pod ) ) {
5255
			return pods_error( __( 'Pod not found', 'pods' ), $this );
5256
		}
5257
5258
		$params->pod_id = $pod['id'];
5259
		$params->pod    = $pod['name'];
5260
5261
		if ( ! isset( $params->name ) ) {
5262
			$params->name = '';
5263
		}
5264
5265
		if ( ! isset( $params->id ) ) {
5266
			$params->id = 0;
5267
		}
5268
5269
		$field = $this->load_field( array(
5270
			'name'   => $params->name,
5271
			'id'     => $params->id,
5272
			'pod'    => $params->pod,
5273
			'pod_id' => $params->pod_id
5274
		) );
5275
5276
		if ( false === $field ) {
5277
			return pods_error( __( 'Field not found', 'pods' ), $this );
5278
		}
5279
5280
		$params->id   = $field['id'];
5281
		$params->name = $field['name'];
5282
5283
		$simple = ( 'pick' === $field['type'] && in_array( pods_var( 'pick_object', $field ), $simple_tableless_objects ) );
5284
		$simple = (boolean) $this->do_hook( 'tableless_custom', $simple, $field, $pod, $params );
5285
5286
		if ( $table_operation && 'table' === $pod['storage'] && ( ! in_array( $field['type'], $tableless_field_types ) || $simple ) ) {
5287
			pods_query( "ALTER TABLE `@wp_pods_{$params->pod}` DROP COLUMN `{$params->name}`", false );
5288
		}
5289
5290
		$success = wp_delete_post( $params->id );
5291
5292
		if ( ! $success ) {
5293
			return pods_error( __( 'Field unable to be deleted', 'pods' ), $this );
5294
		}
5295
5296
		$wpdb->query( $wpdb->prepare( "DELETE pm FROM {$wpdb->postmeta} AS pm
5297
            LEFT JOIN {$wpdb->posts} AS p
5298
                ON p.post_type = '_pods_field' AND p.ID = pm.post_id
5299
            WHERE p.ID IS NOT NULL AND pm.meta_key = 'sister_id' AND pm.meta_value = %d", $params->id ) );
5300
5301
		if ( ( ! pods_tableless() ) && $table_operation ) {
5302
			pods_query( "DELETE FROM `@wp_podsrel` WHERE (`pod_id` = {$params->pod_id} AND `field_id` = {$params->id}) OR (`related_pod_id` = {$params->pod_id} AND `related_field_id` = {$params->id})", false );
5303
		}
5304
5305
		// @todo Delete tableless relationship meta
5306
5307
		if ( true === $save_pod ) {
5308
			$this->cache_flush_pods( $pod );
5309
		}
5310
5311
		return true;
5312
	}
5313
5314
	/**
5315
	 * Drop a Pod Object
5316
	 *
5317
	 * $params['id'] int The object ID
5318
	 * $params['name'] string The object name
5319
	 * $params['type'] string The object type
5320
	 *
5321
	 * @param array|object $params An associative array of parameters
5322
	 *
5323
	 * @uses  wp_delete_post
5324
	 *
5325
	 * @return bool
5326
	 * @since 2.0
5327
	 */
5328
	public function delete_object( $params ) {
5329
5330
		$params = (object) $params;
5331
		$object = $this->load_object( $params );
5332
5333
		if ( empty( $object ) ) {
5334
			return pods_error( sprintf( __( "%s Object not found", 'pods' ), ucwords( $params->type ) ), $this );
5335
		}
5336
5337
		$success = wp_delete_post( $params->id );
5338
5339
		if ( ! $success ) {
5340
			return pods_error( sprintf( __( "%s Object not deleted", 'pods' ), ucwords( $params->type ) ), $this );
5341
		}
5342
5343
		pods_transient_clear( 'pods_objects_' . $params->type );
5344
5345
		return true;
5346
	}
5347
5348
	/**
5349
	 * @see   PodsAPI::delete_object
5350
	 *
5351
	 * Drop a Pod Template
5352
	 *
5353
	 * $params['id'] int The template ID
5354
	 * $params['name'] string The template name
5355
	 *
5356
	 * @param array $params An associative array of parameters
5357
	 *
5358
	 * @return bool
5359
	 * @since 1.7.9
5360
	 */
5361
	public function delete_template( $params ) {
5362
5363
		$params       = (object) $params;
5364
		$params->type = 'template';
5365
5366
		return $this->delete_object( $params );
5367
	}
5368
5369
	/**
5370
	 * @see   PodsAPI::delete_object
5371
	 *
5372
	 * Drop a Pod Page
5373
	 *
5374
	 * $params['id'] int The page ID
5375
	 * $params['uri'] string The page URI
5376
	 *
5377
	 * @param array $params An associative array of parameters
5378
	 *
5379
	 * @return bool
5380
	 * @since 1.7.9
5381
	 */
5382
	public function delete_page( $params ) {
5383
5384
		$params = (object) $params;
5385
		if ( isset( $params->uri ) ) {
5386
			$params->name = $params->uri;
5387
			unset( $params->uri );
5388
		}
5389
		if ( isset( $params->name ) ) {
5390
			$params->name = trim( $params->name, '/' );
5391
		}
5392
		$params->type = 'page';
5393
5394
		return $this->delete_object( $params );
5395
	}
5396
5397
	/**
5398
	 * @see   PodsAPI::delete_object
5399
	 *
5400
	 * Drop a Pod Helper
5401
	 *
5402
	 * $params['id'] int The helper ID
5403
	 * $params['name'] string The helper name
5404
	 *
5405
	 * @param array $params An associative array of parameters
5406
	 *
5407
	 * @return bool
5408
	 * @since 1.7.9
5409
	 */
5410
	public function delete_helper( $params ) {
5411
5412
		$params       = (object) $params;
5413
		$params->type = 'helper';
5414
5415
		return $this->delete_object( $params );
5416
	}
5417
5418
	/**
5419
	 * Drop a single pod item
5420
	 *
5421
	 * $params['id'] int (optional) The item's ID from the wp_pod_* table (used with datatype parameter)
5422
	 * $params['pod'] string (optional) The Pod name (used with id parameter)
5423
	 * $params['pod_id'] int (optional) The Pod ID (used with id parameter)
5424
	 * $params['bypass_helpers'] bool Set to true to bypass running pre-save and post-save helpers
5425
	 *
5426
	 * @param array $params An associative array of parameters
5427
	 * @param bool  $wp     Whether to run WP object delete action
5428
	 *
5429
	 * @return bool
5430
	 * @since 1.7.9
5431
	 */
5432
	public function delete_pod_item( $params, $wp = true ) {
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $wp. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
5433
5434
		$params = (object) pods_sanitize( $params );
5435
5436
		// @deprecated 2.0
5437
		if ( isset( $params->datatype_id ) || isset( $params->datatype ) || isset( $params->tbl_row_id ) ) {
5438
			if ( isset( $params->tbl_row_id ) ) {
5439
				pods_deprecated( __( '$params->id instead of $params->tbl_row_id', 'pods' ), '2.0' );
5440
				$params->id = $params->tbl_row_id;
5441
				unset( $params->tbl_row_id );
5442
			}
5443
5444
			if ( isset( $params->pod_id ) ) {
5445
				pods_deprecated( __( '$params->id instead of $params->pod_id', 'pods' ), '2.0' );
5446
				$params->id = $params->pod_id;
5447
				unset( $params->pod_id );
5448
			}
5449
5450
			if ( isset( $params->dataype_id ) ) {
5451
				pods_deprecated( __( '$params->pod_id instead of $params->datatype_id', 'pods' ), '2.0' );
5452
				$params->pod_id = $params->dataype_id;
5453
				unset( $params->dataype_id );
5454
			}
5455
5456
			if ( isset( $params->datatype ) ) {
5457
				pods_deprecated( __( '$params->pod instead of $params->datatype', 'pods' ), '2.0' );
5458
				$params->pod = $params->datatype;
5459
				unset( $params->datatype );
5460
			}
5461
		}
5462
5463
		if ( ! isset( $params->id ) ) {
5464
			return pods_error( __( 'Pod Item not found', 'pods' ), $this );
5465
		}
5466
5467
		$params->id = pods_absint( $params->id );
5468
5469
		if ( ! isset( $params->pod ) ) {
5470
			$params->pod = '';
5471
		}
5472
5473
		if ( ! isset( $params->pod_id ) ) {
5474
			$params->pod_id = 0;
5475
		}
5476
5477
		$pod = $this->load_pod( array( 'name' => $params->pod, 'id' => $params->pod_id, 'table_info' => false ) );
5478
5479
		if ( false === $pod ) {
5480
			return pods_error( __( 'Pod not found', 'pods' ), $this );
5481
		}
5482
5483
		$params->pod_id = $pod['id'];
5484
		$params->pod    = $pod['name'];
5485
5486
		// Allow Helpers to bypass subsequent helpers in recursive delete_pod_item calls
5487
		$bypass_helpers = false;
5488
5489
		if ( isset( $params->bypass_helpers ) && false !== $params->bypass_helpers ) {
5490
			$bypass_helpers = true;
5491
		}
5492
5493
		$pre_delete_helpers = $post_delete_helpers = array();
5494
5495
		if ( false === $bypass_helpers ) {
5496
			// Plugin hook
5497
			$this->do_hook( 'pre_delete_pod_item', $params, $pod );
5498
			$this->do_hook( "pre_delete_pod_item_{$params->pod}", $params, $pod );
5499
5500
			// Call any pre-save helpers (if not bypassed)
5501
			if ( ! defined( 'PODS_DISABLE_EVAL' ) || ! PODS_DISABLE_EVAL ) {
5502
				if ( ! empty( $pod['options'] ) && is_array( $pod['options'] ) ) {
5503
					$helpers = array( 'pre_delete_helpers', 'post_delete_helpers' );
5504
5505
					foreach ( $helpers as $helper ) {
5506
						if ( isset( $pod['options'][ $helper ] ) && ! empty( $pod['options'][ $helper ] ) ) {
5507
							${$helper} = explode( ',', $pod['options'][ $helper ] );
5508
						}
5509
					}
5510
				}
5511
5512
				if ( ! empty( $pre_delete_helpers ) ) {
5513
					pods_deprecated( sprintf( __( 'Pre-delete helpers are deprecated, use the action pods_pre_delete_pod_item_%s instead', 'pods' ), $params->pod ), '2.0' );
5514
5515
					foreach ( $pre_delete_helpers as $helper ) {
5516
						$helper = $this->load_helper( array( 'name' => $helper ) );
5517
5518
						if ( false !== $helper ) {
5519
							eval( '?>' . $helper['code'] );
0 ignored issues
show
Coding Style introduced by
It is generally not recommended to use eval unless absolutely required.

On one hand, eval might be exploited by malicious users if they somehow manage to inject dynamic content. On the other hand, with the emergence of faster PHP runtimes like the HHVM, eval prevents some optimization that they perform.

Loading history...
5520
						}
5521
					}
5522
				}
5523
			}
5524
		}
5525
5526
		// Delete object from relationship fields
5527
		$this->delete_object_from_relationships( $params->id, $pod );
5528
5529
		if ( 'table' === $pod['storage'] ) {
5530
			pods_query( "DELETE FROM `@wp_pods_{$params->pod}` WHERE `id` = {$params->id} LIMIT 1" );
5531
		}
5532
5533
		if ( $wp ) {
5534
			if ( 'taxonomy' === $pod['type'] ) {
5535
				$taxonomy = $pod['name'];
5536
5537
				if ( ! empty( $pod['object'] ) ) {
5538
					$taxonomy = $pod['object'];
5539
				}
5540
5541
				wp_delete_term( $params->id, $taxonomy );
5542
			} elseif ( ! in_array( $pod['type'], array( 'pod', 'table', '', 'taxonomy' ) ) ) {
5543
				$this->delete_wp_object( $pod['type'], $params->id );
5544
			}
5545
		}
5546
5547
		if ( false === $bypass_helpers ) {
5548
			// Plugin hook
5549
			$this->do_hook( 'post_delete_pod_item', $params, $pod );
5550
			$this->do_hook( "post_delete_pod_item_{$params->pod}", $params, $pod );
5551
5552
			// Call any post-save helpers (if not bypassed)
5553
			if ( ! defined( 'PODS_DISABLE_EVAL' ) || ! PODS_DISABLE_EVAL ) {
5554
				if ( ! empty( $post_delete_helpers ) ) {
5555
					pods_deprecated( sprintf( __( 'Post-delete helpers are deprecated, use the action pods_post_delete_pod_item_%s instead', 'pods' ), $params->pod ), '2.0' );
5556
5557
					foreach ( $post_delete_helpers as $helper ) {
5558
						$helper = $this->load_helper( array( 'name' => $helper ) );
5559
5560
						if ( false !== $helper ) {
5561
							eval( '?>' . $helper['code'] );
0 ignored issues
show
Coding Style introduced by
It is generally not recommended to use eval unless absolutely required.

On one hand, eval might be exploited by malicious users if they somehow manage to inject dynamic content. On the other hand, with the emergence of faster PHP runtimes like the HHVM, eval prevents some optimization that they perform.

Loading history...
5562
						}
5563
					}
5564
				}
5565
			}
5566
		}
5567
5568
		pods_cache_clear( $params->id, 'pods_items_' . $params->pod );
5569
5570
		return true;
5571
	}
5572
5573
	/**
5574
	 * Delete an object from tableless fields
5575
	 *
5576
	 * @param int    $id
5577
	 * @param string $type
0 ignored issues
show
Bug introduced by
There is no parameter named $type. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
5578
	 * @param string $name
0 ignored issues
show
Documentation introduced by
Should the type for parameter $name not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
5579
	 *
5580
	 * @return bool
5581
	 *
5582
	 * @since 2.3
5583
	 */
5584
	public function delete_object_from_relationships( $id, $object, $name = null ) {
5585
5586
		/**
5587
		 * @var $pods_init \PodsInit
5588
		 */
5589
		global $pods_init;
5590
5591
		$pod = false;
5592
5593
		// Run any bidirectional delete operations
5594
		if ( is_array( $object ) ) {
5595
			$pod = $object;
5596
		} elseif ( is_object( $pods_init ) ) {
5597
			$pod = PodsInit::$meta->get_object( $object, $name );
5598
		}
5599
5600
		if ( ! empty( $pod ) ) {
5601
			$object = $pod['type'];
5602
			$name   = $pod['name'];
5603
5604
			foreach ( $pod['fields'] as $field ) {
5605
				PodsForm::delete( $field['type'], $id, $field['name'], array_merge( $field, $field['options'] ), $pod );
5606
			}
5607
		}
5608
5609
		// Lookup related fields (non-bidirectional)
5610
		$params = array(
5611
			'where' => array(
5612
				array(
5613
					'key'   => 'type',
5614
					'value' => 'pick'
5615
				),
5616
				array(
5617
					'key'   => 'pick_object',
5618
					'value' => $object
5619
				)
5620
			)
5621
		);
5622
5623
		if ( ! empty( $name ) && $name !== $object ) {
5624
			$params['where'][] = array(
5625
				'key'   => 'pick_val',
5626
				'value' => $name
5627
			);
5628
		}
5629
5630
		$fields = $this->load_fields( $params, false );
5631
5632
		if ( ! empty( $pod ) && 'media' === $pod['type'] ) {
5633
			$params['where'] = array(
5634
				array(
5635
					'key'   => 'type',
5636
					'value' => 'file'
5637
				)
5638
			);
5639
5640
			$fields = array_merge( $fields, $this->load_fields( $params, false ) );
5641
		}
5642
5643
		if ( is_array( $fields ) && ! empty( $fields ) ) {
5644
			foreach ( $fields as $related_field ) {
5645
				$related_pod = $this->load_pod( array( 'id' => $related_field['pod_id'], 'fields' => false ), false );
5646
5647
				if ( empty( $related_pod ) ) {
5648
					continue;
5649
				}
5650
5651
				$related_from = $this->lookup_related_items_from( $related_field['id'], $related_pod['id'], $id, $related_field, $related_pod );
5652
5653
				$this->delete_relationships( $related_from, $id, $related_pod, $related_field );
5654
			}
5655
		}
5656
5657
		if ( ! empty( $pod ) && ! pods_tableless() ) {
5658
			pods_query( "
5659
                DELETE FROM `@wp_podsrel`
5660
                WHERE
5661
                (
5662
                    `pod_id` = %d
5663
                    AND `item_id` = %d
5664
                )
5665
                OR (
5666
                    `related_pod_id` = %d
5667
                    AND `related_item_id` = %d
5668
                )
5669
            ", array(
5670
				$pod['id'],
5671
				$id,
5672
5673
				$pod['id'],
5674
				$id
5675
			) );
5676
		}
5677
5678
		return true;
5679
	}
5680
5681
	/**
5682
	 * Delete relationships
5683
	 *
5684
	 * @param int|array $related_id    IDs for items to save
5685
	 * @param int|array $id            ID or IDs to remove
5686
	 * @param array     $related_pod   Pod data
5687
	 * @param array     $related_field Field data
5688
	 *
5689
	 * @return void
5690
	 *
5691
	 * @since 2.3
5692
	 */
5693
	public function delete_relationships( $related_id, $id, $related_pod, $related_field ) {
5694
5695
		if ( is_array( $related_id ) ) {
5696
			foreach ( $related_id as $rid ) {
5697
				$this->delete_relationships( $rid, $id, $related_pod, $related_field );
5698
			}
5699
5700
			return;
5701
		}
5702
5703
		if ( is_array( $id ) ) {
5704
			foreach ( $id as $rid ) {
5705
				$this->delete_relationships( $related_id, $rid, $related_pod, $related_field );
5706
			}
5707
5708
			return;
5709
		}
5710
5711
		$id = (int) $id;
5712
5713
		if ( empty( $id ) ) {
5714
			return;
5715
		}
5716
5717
		$related_ids = $this->lookup_related_items( $related_field['id'], $related_pod['id'], $related_id, $related_field, $related_pod );
5718
5719
		if ( empty( $related_ids ) ) {
5720
			return;
5721
		} elseif ( ! in_array( $id, $related_ids ) ) {
5722
			return;
5723
		}
5724
5725
		if ( isset( self::$related_item_cache[ $related_pod['id'] ][ $related_field['id'] ] ) ) {
5726
			// Delete relationship from cache
5727
			unset( self::$related_item_cache[ $related_pod['id'] ][ $related_field['id'] ] );
5728
		}
5729
5730
		// @codingStandardsIgnoreLine
5731
		unset( $related_ids[ array_search( $id, $related_ids ) ] );
5732
5733
		$no_conflict = pods_no_conflict_check( $related_pod['type'] );
5734
5735
		if ( ! $no_conflict ) {
5736
			pods_no_conflict_on( $related_pod['type'] );
5737
		}
5738
5739
		// Post Types, Media, Users, and Comments (meta-based)
5740
		if ( in_array( $related_pod['type'], array( 'post_type', 'media', 'taxonomy', 'user', 'comment' ) ) ) {
5741
			$object_type = $related_pod['type'];
5742
5743
			if ( in_array( $object_type, array( 'post_type', 'media' ) ) ) {
5744
				$object_type = 'post';
5745
			} elseif ( 'taxonomy' === $object_type ) {
5746
				$object_type = 'term';
5747
			}
5748
5749
			delete_metadata( $object_type, $related_id, $related_field['name'] );
5750
5751
			if ( ! empty( $related_ids ) ) {
5752
				update_metadata( $object_type, $related_id, '_pods_' . $related_field['name'], $related_ids );
5753
5754
				foreach ( $related_ids as $rel_id ) {
5755
					add_metadata( $object_type, $related_id, $related_field['name'], $rel_id );
5756
				}
5757
			} else {
5758
				delete_metadata( $object_type, $related_id, '_pods_' . $related_field['name'] );
5759
			}
5760
		} elseif ( 'settings' === $related_pod['type'] ) {
5761
			// Custom Settings Pages (options-based)
5762
			if ( ! empty( $related_ids ) ) {
5763
				update_option( $related_pod['name'] . '_' . $related_field['name'], $related_ids );
5764
			} else {
5765
				delete_option( $related_pod['name'] . '_' . $related_field['name'] );
5766
			}
5767
		}
5768
5769
		// Relationships table
5770
		if ( ! pods_tableless() ) {
5771
			pods_query( "
5772
                DELETE FROM `@wp_podsrel`
5773
                WHERE
5774
                (
5775
                    `pod_id` = %d
5776
                    AND `field_id` = %d
5777
                    AND `item_id` = %d
5778
                    AND `related_item_id` = %d
5779
                )
5780
                OR (
5781
                    `related_pod_id` = %d
5782
                    AND `related_field_id` = %d
5783
                    AND `related_item_id` = %d
5784
                    AND `item_id` = %d
5785
                )
5786
            ", array(
5787
				$related_pod['id'],
5788
				$related_field['id'],
5789
				$related_id,
5790
				$id,
5791
5792
				$related_pod['id'],
5793
				$related_field['id'],
5794
				$related_id,
5795
				$id
5796
			) );
5797
		}
5798
5799
		if ( ! $no_conflict ) {
5800
			pods_no_conflict_off( $related_pod['type'] );
5801
		}
5802
	}
5803
5804
	/**
5805
	 * Check if a Pod exists
5806
	 *
5807
	 * $params['id'] int Pod ID
5808
	 * $params['name'] string Pod name
5809
	 *
5810
	 * @param array $params An associative array of parameters
5811
	 *
5812
	 * @return bool True if exists
5813
	 *
5814
	 * @since 1.12
5815
	 */
5816
	public function pod_exists( $params, $type = null ) {
5817
5818
		if ( is_string( $params ) ) {
5819
			$params = array( 'name' => $params );
5820
		}
5821
5822
		$params = (object) pods_sanitize( $params );
5823
5824
		if ( ! empty( $params->id ) || ! empty( $params->name ) ) {
5825
			if ( ! isset( $params->name ) ) {
5826
				$pod = get_post( $dummy = (int) $params->id );
5827
			} else {
5828
				$pod = get_posts( array(
5829
					'name'           => $params->name,
5830
					'post_type'      => '_pods_pod',
5831
					'posts_per_page' => 1
5832
				) );
5833
5834
				if ( is_array( $pod ) && ! empty( $pod[0] ) ) {
5835
					$pod = $pod[0];
5836
				}
5837
			}
5838
5839
			if ( ! empty( $pod ) && ( empty( $type ) || $type == get_post_meta( $pod->ID, 'type', true ) ) ) {
5840
				return true;
5841
			}
5842
		}
5843
5844
		return false;
5845
	}
5846
5847
	/**
5848
	 * Get number of pods for a specific pod type
5849
	 *
5850
	 * @param string $type Type to get count
5851
	 *
5852
	 * @return int Total number of pods for a type
5853
	 *
5854
	 * @since 2.6.6
5855
	 */
5856
	public function get_pod_type_count( $type ) {
5857
5858
		$args = array(
5859
			'post_type'      => '_pods_pod',
5860
			'posts_per_page' => - 1,
5861
			'nopaging'       => true,
5862
			'fields'         => 'ids',
5863
			'meta_query'     => array(
5864
				array(
5865
					'key'   => 'type',
5866
					'value' => $type,
5867
				),
5868
			),
5869
		);
5870
5871
		$posts = get_posts( $args );
5872
5873
		$total = count( $posts );
5874
5875
		return $total;
5876
5877
	}
5878
5879
	/**
5880
	 * Load a Pod and all of its fields
5881
	 *
5882
	 * $params['id'] int The Pod ID
5883
	 * $params['name'] string The Pod name
5884
	 * $params['fields'] bool Whether to load fields (default is true)
5885
	 * $params['bypass_cache'] boolean Bypass the cache when getting data
5886
	 *
5887
	 * @param array|object $params An associative array of parameters or pod name as a string
5888
	 * @param bool         $strict Makes sure the pod exists, throws an error if it doesn't work
5889
	 *
5890
	 * @return array|bool|mixed|void
5891
	 * @since 1.7.9
5892
	 */
5893
	public function load_pod( $params, $strict = true ) {
5894
5895
		/**
5896
		 * @var $sitepress SitePress
5897
		 * @var $wpdb      wpdb
5898
		 */
5899
		global $wpdb;
5900
5901
		$current_language = false;
5902
		$load_fields      = true;
5903
		$bypass_cache     = false;
5904
5905
		// Get current language data
5906
		$lang_data = PodsInit::$i18n->get_current_language_data();
5907
5908
		if ( $lang_data ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $lang_data of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
5909
			if ( ! empty( $lang_data['language'] ) ) {
5910
				$current_language = $lang_data['language'];
5911
			}
5912
		}
5913
5914
		if ( ! is_array( $params ) && ! is_object( $params ) ) {
5915
			$params = array( 'name' => $params, 'table_info' => false, 'fields' => true );
5916
		}
5917
5918
		if ( is_object( $params ) && ! is_a( $params, 'WP_Post' ) && isset( $params->fields ) && ! $params->fields ) {
5919
			$load_fields = false;
5920
		} elseif ( is_array( $params ) && isset( $params['fields'] ) && ! $params['fields'] ) {
5921
			$load_fields = false;
5922
		}
5923
5924
		$table_info = false;
5925
5926
		if ( is_object( $params ) && ! is_a( $params, 'WP_Post' ) && ! empty( $params->table_info ) ) {
5927
			$table_info = true;
5928
		} elseif ( is_array( $params ) && ! empty( $params['table_info'] ) ) {
5929
			$table_info = true;
5930
		}
5931
5932
		$transient = 'pods_' . $wpdb->prefix . '_pod';
5933
5934
		if ( ! empty( $current_language ) ) {
5935
			$transient .= '_' . $current_language;
5936
		}
5937
5938
		if ( ! $load_fields ) {
5939
			$transient .= '_nofields';
5940
		}
5941
5942
		if ( $table_info ) {
5943
			$transient .= '_tableinfo';
5944
		}
5945
5946
		$check_pod = $params;
5947
5948
		if ( is_object( $params ) && ! is_a( $params, 'WP_Post' ) && ! empty( $params->pod ) ) {
5949
			$check_pod = $params->pod;
5950
		} elseif ( is_array( $params ) && ! empty( $params['pod'] ) ) {
5951
			$check_pod = $params['pod'];
5952
		}
5953
5954
		if ( is_object( $check_pod ) && ( is_a( $check_pod, 'WP_Post' ) || isset( $check_pod->post_name ) ) ) {
5955
			$pod = false;
5956
5957
			if ( pods_api_cache() ) {
5958
				$pod = pods_transient_get( $transient . '_' . $check_pod->post_name );
5959
			}
5960
5961
			if ( false !== $pod && ( ! $table_info || isset( $pod['table'] ) ) ) {
5962
				// @todo Is this needed anymore for WPML?
5963
				if ( in_array( $pod['type'], array(
5964
						'post_type',
5965
						'taxonomy'
5966
					) ) && did_action( 'wpml_loaded' ) && apply_filters( 'wpml_setting', true, 'auto_adjust_ids' ) ) {
5967
					$pod = array_merge( $pod, $this->get_table_info( $pod['type'], $pod['object'], $pod['name'], $pod ) );
5968
				}
5969
5970
				return $pod;
5971
			}
5972
5973
			$_pod = get_object_vars( $check_pod );
5974
		} else {
5975
			$params = (object) pods_sanitize( $params );
5976
5977
			if ( ( ! isset( $params->id ) || empty( $params->id ) ) && ( ! isset( $params->name ) || empty( $params->name ) ) ) {
5978
				if ( $strict ) {
5979
					return pods_error( 'Either Pod ID or Name are required', $this );
5980
				}
5981
5982
				return false;
5983
			}
5984
5985
			if ( ! empty( $params->bypass_cache ) ) {
5986
				$bypass_cache = true;
5987
			}
5988
5989
			if ( isset( $params->name ) ) {
5990
				$pod = false;
5991
5992
				if ( '_pods_pod' === $params->name ) {
5993
					$pod = array(
5994
						'id'      => 0,
5995
						'name'    => $params->name,
5996
						'label'   => __( 'Pods', 'pods' ),
5997
						'type'    => 'post_type',
5998
						'storage' => 'meta',
5999
						'options' => array(
6000
							'label_singular' => __( 'Pod', 'pods' )
6001
						),
6002
						'fields'  => array()
6003
					);
6004
				} elseif ( '_pods_field' === $params->name ) {
6005
					$pod = array(
6006
						'id'      => 0,
6007
						'name'    => $params->name,
6008
						'label'   => __( 'Pod Fields', 'pods' ),
6009
						'type'    => 'post_type',
6010
						'storage' => 'meta',
6011
						'options' => array(
6012
							'label_singular' => __( 'Pod Field', 'pods' )
6013
						),
6014
						'fields'  => array()
6015
					);
6016
				} elseif ( ! $bypass_cache & pods_api_cache() ) {
6017
					$pod = pods_transient_get( $transient . '_' . $params->name );
6018
				}
6019
6020
				if ( false !== $pod && ( ! $table_info || isset( $pod['table'] ) ) ) {
6021
					if ( in_array( $pod['type'], array(
6022
							'post_type',
6023
							'taxonomy'
6024
						) ) && did_action( 'wpml_loaded' ) && apply_filters( 'wpml_setting', true, 'auto_adjust_ids' ) ) {
6025
						$pod = array_merge( $pod, $this->get_table_info( $pod['type'], $pod['object'], $pod['name'], $pod ) );
6026
					}
6027
6028
					return $pod;
6029
				}
6030
			}
6031
6032
			if ( ! isset( $params->name ) ) {
6033
				$pod = get_post( $dummy = (int) $params->id );
6034
			} else {
6035
				$pod = get_posts( array(
6036
					'name'           => $params->name,
6037
					'post_type'      => '_pods_pod',
6038
					'posts_per_page' => 1
6039
				) );
6040
			}
6041
6042
			if ( empty( $pod ) ) {
6043
				if ( $strict ) {
6044
					return pods_error( __( 'Pod not found', 'pods' ), $this );
6045
				}
6046
6047
				return false;
6048
			}
6049
6050
			if ( is_array( $pod ) && ! empty( $pod[0] ) ) {
6051
				$pod = $pod[0];
6052
			}
6053
6054
			$_pod = get_object_vars( $pod );
6055
		}
6056
6057
		$pod = false;
6058
6059
		if ( ! $bypass_cache || pods_api_cache() ) {
6060
			$pod = pods_transient_get( $transient . '_' . $_pod['post_name'] );
6061
		}
6062
6063
		if ( false !== $pod && ( ! $table_info || isset( $pod['table'] ) ) ) {
6064
			if ( in_array( $pod['type'], array(
6065
					'post_type',
6066
					'taxonomy'
6067
				) ) && did_action( 'wpml_loaded' ) && apply_filters( 'wpml_setting', true, 'auto_adjust_ids' ) ) {
6068
				$pod = array_merge( $pod, $this->get_table_info( $pod['type'], $pod['object'], $pod['name'], $pod ) );
6069
			}
6070
6071
			return $pod;
6072
		}
6073
6074
		$pod = array(
6075
			'id'          => $_pod['ID'],
6076
			'name'        => $_pod['post_name'],
6077
			'label'       => $_pod['post_title'],
6078
			'description' => $_pod['post_content']
6079
		);
6080
6081
		if ( strlen( $pod['label'] ) < 1 ) {
6082
			$pod['label'] = $pod['name'];
6083
		}
6084
6085
		// @todo update with a method to put all options in
6086
		$defaults = array(
6087
			'show_in_menu' => 1,
6088
			'type'         => 'post_type',
6089
			'storage'      => 'meta',
6090
			'object'       => '',
6091
			'alias'        => ''
6092
		);
6093
6094
		if ( $bypass_cache ) {
6095
			wp_cache_delete( $pod['id'], 'post_meta' );
6096
6097
			update_postmeta_cache( array( $pod['id'] ) );
6098
		}
6099
6100
		$pod['options'] = get_post_meta( $pod['id'] );
6101
6102
		foreach ( $pod['options'] as $option => $value ) {
6103
			if ( is_array( $value ) ) {
6104
				foreach ( $value as $k => $v ) {
6105
					if ( ! is_array( $v ) ) {
6106
						$value[ $k ] = maybe_unserialize( $v );
6107
					}
6108
				}
6109
6110
				if ( 1 == count( $value ) ) {
6111
					$value = current( $value );
6112
				}
6113
			} else {
6114
				$value = maybe_unserialize( $value );
6115
			}
6116
6117
			$pod['options'][ $option ] = $value;
6118
		}
6119
6120
		$pod['options'] = array_merge( $defaults, $pod['options'] );
6121
6122
		$pod['type']    = $pod['options']['type'];
6123
		$pod['storage'] = $pod['options']['storage'];
6124
		$pod['object']  = $pod['options']['object'];
6125
		$pod['alias']   = $pod['options']['alias'];
6126
6127
		unset( $pod['options']['type'] );
6128
		unset( $pod['options']['storage'] );
6129
		unset( $pod['options']['object'] );
6130
		unset( $pod['options']['alias'] );
6131
6132
		if ( $table_info ) {
6133
			$pod = array_merge( $this->get_table_info( $pod['type'], $pod['object'], $pod['name'], $pod ), $pod );
6134
		}
6135
6136
		// Override old 'none' storage type
6137
		if ( 'taxonomy' === $pod['type'] && 'none' === $pod['storage'] && function_exists( 'get_term_meta' ) ) {
6138
			$pod['storage'] = 'meta';
6139
		}
6140
6141
		if ( isset( $pod['pod'] ) ) {
6142
			unset( $pod['pod'] );
6143
		}
6144
6145
		$pod['fields'] = array();
6146
6147
		$pod['object_fields'] = array();
6148
6149
		if ( 'pod' !== $pod['type'] ) {
6150
			$pod['object_fields'] = $this->get_wp_object_fields( $pod['type'], $pod );
6151
		}
6152
6153
		$fields = get_posts( array(
6154
			'post_type'      => '_pods_field',
6155
			'posts_per_page' => - 1,
6156
			'nopaging'       => true,
6157
			'post_parent'    => $pod['id'],
6158
			'orderby'        => 'menu_order',
6159
			'order'          => 'ASC'
6160
		) );
6161
6162
		if ( ! empty( $fields ) ) {
6163
			foreach ( $fields as $field ) {
6164
				$field->pod          = $pod['name'];
6165
				$field->table_info   = $table_info;
6166
				$field->bypass_cache = $bypass_cache;
6167
6168
				if ( $load_fields ) {
6169
					$field = $this->load_field( $field );
6170
6171
					$field = PodsForm::field_setup( $field, null, $field['type'] );
6172
				} else {
6173
					if ( $bypass_cache ) {
6174
						wp_cache_delete( $field->ID, 'post_meta' );
6175
6176
						update_postmeta_cache( array( $field->ID ) );
6177
					}
6178
6179
					$field = array(
6180
						'id'    => $field->ID,
6181
						'name'  => $field->post_name,
6182
						'label' => $field->post_title,
6183
						'type'  => get_post_meta( $field->ID, 'type', true )
6184
					);
6185
				}
6186
6187
				$pod['fields'][ $field['name'] ] = $field;
6188
			}
6189
		}
6190
6191
		if ( did_action( 'init' ) && pods_api_cache() ) {
6192
			pods_transient_set( $transient . '_' . $pod['name'], $pod );
6193
		}
6194
6195
		return $pod;
6196
	}
6197
6198
	/**
6199
	 * Load a list of Pods based on filters specified.
6200
	 *
6201
	 * $params['type'] string/array Pod Type(s) to filter by
6202
	 * $params['object'] string/array Pod Object(s) to filter by
6203
	 * $params['options'] array Pod Option(s) key=>value array to filter by
6204
	 * $params['orderby'] string ORDER BY clause of query
6205
	 * $params['limit'] string Number of Pods to return
6206
	 * $params['where'] string WHERE clause of query
6207
	 * $params['ids'] string|array IDs of Objects
6208
	 * $params['count'] boolean Return only a count of Pods
6209
	 * $params['names'] boolean Return only an array of name => label
6210
	 * $params['ids'] boolean Return only an array of ID => label
6211
	 * $params['fields'] boolean Return pod fields with Pods (default is true)
6212
	 * $params['key_names'] boolean Return pods keyed by name
6213
	 * $params['bypass_cache'] boolean Bypass the cache when getting data
6214
	 *
6215
	 * @param array $params An associative array of parameters
0 ignored issues
show
Documentation introduced by
Should the type for parameter $params not be array|null? Also, consider making the array more specific, something like array<String>, or String[].

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive. In addition it looks for parameters that have the generic type array and suggests a stricter type like array<String>.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
6216
	 *
6217
	 * @return array|mixed
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use array|object|integer|double|string|null|boolean.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
6218
	 *
6219
	 * @uses  PodsAPI::load_pod
6220
	 *
6221
	 * @since 2.0
6222
	 */
6223
	public function load_pods( $params = null ) {
6224
6225
		$current_language = false;
6226
6227
		// Get current language data
6228
		$lang_data = PodsInit::$i18n->get_current_language_data();
6229
6230
		if ( $lang_data ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $lang_data of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
6231
			if ( ! empty( $lang_data['language'] ) ) {
6232
				$current_language = $lang_data['language'];
6233
			}
6234
		}
6235
6236
		$params = (object) pods_sanitize( $params );
6237
6238
		$order   = 'ASC';
6239
		$orderby = 'menu_order title';
6240
		$limit   = - 1;
6241
		$ids     = false;
6242
6243
		$meta_query = array();
6244
		$cache_key  = '';
6245
6246
		$bypass_cache = false;
6247
6248
		if ( ! empty( $params->bypass_cache ) ) {
6249
			$bypass_cache = true;
6250
		}
6251
6252
		if ( isset( $params->type ) && ! empty( $params->type ) ) {
6253
			if ( ! is_array( $params->type ) ) {
6254
				$params->type = array( trim( $params->type ) );
6255
			}
6256
6257
			sort( $params->type );
6258
6259
			$meta_query[] = array(
6260
				'key'     => 'type',
6261
				'value'   => $params->type,
6262
				'compare' => 'IN'
6263
			);
6264
6265
			if ( 0 < count( $params->type ) ) {
6266
				$cache_key .= '_type_' . trim( implode( '_', $params->type ) );
6267
			}
6268
		}
6269
6270
		if ( isset( $params->object ) && ! empty( $params->object ) ) {
6271
			if ( ! is_array( $params->object ) ) {
6272
				$params->object = array( $params->object );
6273
			}
6274
6275
			$params->object = pods_trim( $params->object );
6276
6277
			sort( $params->object );
6278
6279
			$meta_query[] = array(
6280
				'key'     => 'object',
6281
				'value'   => $params->object,
6282
				'compare' => 'IN'
6283
			);
6284
6285
			if ( 1 == count( $params->object ) ) {
6286
				$cache_key .= '_object_' . trim( implode( '', $params->object ) );
6287
			}
6288
		}
6289
6290
		if ( isset( $params->options ) && ! empty( $params->options ) && is_array( $params->options ) ) {
6291
			foreach ( $params->options as $option => $value ) {
6292
				if ( ! is_array( $value ) ) {
6293
					$value = array( $value );
6294
				}
6295
6296
				$value = pods_trim( $value );
6297
6298
				sort( $value );
6299
6300
				$meta_query[] = array(
6301
					'key'     => $option,
6302
					'value'   => pods_sanitize( $value ),
6303
					'compare' => 'IN'
6304
				);
6305
			}
6306
6307
			$cache_key = '';
6308
		}
6309
6310
		if ( isset( $params->where ) && is_array( $params->where ) ) {
6311
			$meta_query = array_merge( $meta_query, (array) $params->where );
6312
		}
6313
6314
		if ( isset( $params->order ) && ! empty( $params->order ) && in_array( strtoupper( $params->order ), array(
6315
				'ASC',
6316
				'DESC'
6317
			) ) ) {
6318
			$order = strtoupper( $params->order );
6319
		}
6320
6321
		if ( isset( $params->orderby ) && ! empty( $params->orderby ) ) {
6322
			$orderby = strtoupper( $params->orderby );
6323
		}
6324
6325
		if ( isset( $params->limit ) && ! empty( $params->limit ) ) {
6326
			$limit = pods_absint( $params->limit );
6327
		}
6328
6329
		if ( isset( $params->ids ) && ! empty( $params->ids ) ) {
6330
			$ids = $params->ids;
6331
6332
			if ( ! is_array( $ids ) ) {
6333
				$ids = explode( ',', $ids );
6334
			}
6335
		}
6336
6337
		if ( empty( $ids ) ) {
6338
			$ids = false;
6339
		}
6340
6341
		$pre_key = '';
6342
6343
		if ( ! empty( $current_language ) ) {
6344
			$pre_key .= '_' . $current_language;
6345
		}
6346
6347
		if ( isset( $params->count ) && $params->count ) {
6348
			$pre_key .= '_count';
6349
		}
6350
6351
		if ( isset( $params->ids ) && $params->ids && ! empty( $ids ) ) {
6352
			$pre_key .= '_ids_' . implode( '_', $ids );
6353
		}
6354
6355
		if ( isset( $params->names ) && $params->names ) {
6356
			$pre_key .= '_names';
6357
		} elseif ( isset( $params->names_ids ) && $params->names_ids ) {
6358
			$pre_key .= '_names_ids';
6359
		}
6360
6361
		if ( isset( $params->key_names ) && $params->key_names ) {
6362
			$pre_key .= '_namekeys';
6363
		}
6364
6365
		if ( isset( $params->fields ) && ! $params->fields ) {
6366
			$pre_key .= '_nofields';
6367
		}
6368
6369
		if ( isset( $params->table_info ) && $params->table_info ) {
6370
			$pre_key .= '_tableinfo';
6371
		}
6372
6373
		$pre_key .= '_get';
6374
6375
		if ( empty( $cache_key ) ) {
6376
			$cache_key = 'pods' . $pre_key . '_all';
6377
		} else {
6378
			$cache_key = 'pods' . $pre_key . $cache_key;
6379
		}
6380
6381
		if ( ! $bypass_cache && pods_api_cache() && ! empty( $cache_key ) && ( 'pods' . ( ! empty( $current_language ) ? '_' . $current_language : '' ) . '_get_all' !== $cache_key || empty( $meta_query ) ) && $limit < 1 && ( empty( $orderby ) || 'menu_order title' === $orderby ) && empty( $ids ) ) {
6382
			$the_pods = pods_transient_get( $cache_key );
6383
6384
			if ( false === $the_pods ) {
6385
				$the_pods = pods_cache_get( $cache_key, 'pods' );
6386
			}
6387
6388
			if ( ! is_array( $the_pods ) && 'none' === $the_pods ) {
6389
				return array();
6390
			} elseif ( false !== $the_pods ) {
6391
				return $the_pods;
6392
			}
6393
		}
6394
6395
		$the_pods = array();
6396
6397
		$args = array(
6398
			'post_type'      => '_pods_pod',
6399
			'nopaging'       => true,
6400
			'posts_per_page' => $limit,
6401
			'order'          => $order,
6402
			'orderby'        => $orderby,
6403
			'meta_query'     => $meta_query,
6404
		);
6405
6406
		// Only set post__in if there are ids to filter (see https://core.trac.wordpress.org/ticket/28099)
6407
		if ( false !== $ids ) {
6408
			$args['post__in'] = $ids;
6409
		}
6410
6411
		$_pods = get_posts( $args );
6412
6413
		$export_ignore = array(
6414
			'object_type',
6415
			'object_name',
6416
			'table',
6417
			'meta_table',
6418
			'pod_table',
6419
			'field_id',
6420
			'field_index',
6421
			'field_slug',
6422
			'field_type',
6423
			'field_parent',
6424
			'field_parent_select',
6425
			'meta_field_id',
6426
			'meta_field_index',
6427
			'meta_field_value',
6428
			'pod_field_id',
6429
			'pod_field_index',
6430
			'object_fields',
6431
			'join',
6432
			'where',
6433
			'where_default',
6434
			'orderby',
6435
			'pod',
6436
			'recurse',
6437
			'table_info',
6438
			'attributes',
6439
			'group',
6440
			'grouped',
6441
			'developer_mode',
6442
			'dependency',
6443
			'depends-on',
6444
			'excludes-on'
6445
		);
6446
6447
		$total_fields = 0;
6448
6449
		if ( isset( $params->count ) && $params->count ) {
6450
			$the_pods = count( $_pods );
6451
		} else {
6452
			foreach ( $_pods as $pod ) {
6453
				if ( isset( $params->names ) && $params->names ) {
6454
					$the_pods[ $pod->post_name ] = $pod->post_title;
6455
				} elseif ( isset( $params->names_ids ) && $params->names_ids ) {
6456
					$the_pods[ $pod->ID ] = $pod->post_name;
6457
				} else {
6458
					if ( isset( $params->fields ) && ! $params->fields ) {
6459
						$pod->fields = false;
6460
					}
6461
6462
					$pod = $this->load_pod( array(
6463
						'pod'          => $pod,
6464
						'table_info'   => ! empty( $params->table_info ),
6465
						'bypass_cache' => $bypass_cache
6466
					) );
6467
6468
					// Remove extra data not needed
6469
					if ( pods_var( 'export', $params, false ) && ( ! isset( $params->fields ) || $params->fields ) ) {
6470
						foreach ( $export_ignore as $ignore ) {
6471
							if ( isset( $pod[ $ignore ] ) ) {
6472
								unset( $pod[ $ignore ] );
6473
							}
6474
						}
6475
6476
						foreach ( $pod['fields'] as $field => $field_data ) {
6477
							if ( isset( $pod['fields'][ $field ]['table_info'] ) ) {
6478
								unset( $pod['fields'][ $field ]['table_info'] );
6479
							}
6480
						}
6481
					}
6482
6483
					$total_fields += count( $pod['fields'] );
6484
6485
					if ( isset( $params->key_names ) && $params->key_names ) {
6486
						$the_pods[ $pod['name'] ] = $pod;
6487
					} else {
6488
						$the_pods[ $pod['id'] ] = $pod;
6489
					}
6490
				}
6491
			}
6492
		}
6493
6494
		if ( ( ! function_exists( 'pll_current_language' ) || ! empty( $params->refresh ) ) && ! empty( $cache_key ) && ( 'pods' !== $cache_key || empty( $meta_query ) ) && $limit < 1 && ( empty( $orderby ) || 'menu_order title' === $orderby ) && empty( $ids ) ) {
6495
			$total_pods = (int) ( is_array( $the_pods ) ) ? count( $the_pods ) : $the_pods;
6496
			// Too many Pods can cause issues with the DB when caching is not enabled
6497
			if ( 15 < $total_pods || 75 < (int) $total_fields ) {
6498
				pods_transient_clear( $cache_key );
6499
6500
				if ( pods_api_cache() ) {
6501
					if ( empty( $the_pods ) && ( ! isset( $params->count ) || ! $params->count ) ) {
6502
						pods_cache_set( $cache_key, 'none', 'pods' );
6503
					} else {
6504
						pods_cache_set( $cache_key, $the_pods, 'pods' );
6505
					}
6506
				}
6507
			} else {
6508
				pods_cache_clear( $cache_key, 'pods' );
6509
6510
				if ( pods_api_cache() ) {
6511
					if ( empty( $the_pods ) && ( ! isset( $params->count ) || ! $params->count ) ) {
6512
						pods_transient_set( $cache_key, 'none' );
6513
					} else {
6514
						pods_transient_set( $cache_key, $the_pods );
6515
					}
6516
				}
6517
			}
6518
		}
6519
6520
		return $the_pods;
6521
	}
6522
6523
	/**
6524
	 * Check if a Pod's field exists
6525
	 *
6526
	 * $params['pod_id'] int The Pod ID
6527
	 * $params['id'] int The field ID
6528
	 * $params['name'] string The field name
6529
	 *
6530
	 * @param array $params An associative array of parameters
6531
	 *
6532
	 * @return bool
6533
	 *
6534
	 * @since 1.12
6535
	 */
6536
	public function field_exists( $params ) {
6537
6538
		$params = (object) pods_sanitize( $params );
6539
6540
		if ( ( ! empty( $params->id ) || ! empty( $params->name ) ) && isset( $params->pod_id ) && ! empty( $params->pod_id ) ) {
6541
			if ( ! isset( $params->name ) ) {
6542
				$field = get_post( $dummy = (int) $params->id );
6543
			} else {
6544
				$field = get_posts( array(
6545
					'name'           => $params->name,
6546
					'post_type'      => '_pods_field',
6547
					'posts_per_page' => 1,
6548
					'post_parent'    => $params->pod_id
6549
				) );
6550
			}
6551
6552
			if ( ! empty( $field ) ) {
6553
				return true;
6554
			}
6555
		}
6556
6557
		return false;
6558
	}
6559
6560
	/**
6561
	 * Load a field
6562
	 *
6563
	 * $params['pod_id'] int The Pod ID
6564
	 * $params['pod'] string The Pod name
6565
	 * $params['id'] int The field ID
6566
	 * $params['name'] string The field name
6567
	 * $params['table_info'] boolean Whether to lookup a pick field's table info
6568
	 * $params['bypass_cache'] boolean Bypass the cache when getting data
6569
	 *
6570
	 * @param array   $params An associative array of parameters
6571
	 * @param boolean $strict Whether to require a field exist or not when loading the info
6572
	 *
6573
	 * @return array|bool Array with field data, false if field not found
6574
	 * @since 1.7.9
6575
	 */
6576
	public function load_field( $params, $strict = false ) {
6577
6578
		$params = (object) $params;
6579
6580
		if ( ! isset( $params->table_info ) ) {
6581
			$params->table_info = false;
6582
		}
6583
6584
		$bypass_cache = false;
6585
6586
		if ( ! empty( $params->bypass_cache ) ) {
6587
			$bypass_cache = true;
6588
		}
6589
6590
		$pod   = array();
6591
		$field = array();
6592
6593
		if ( isset( $params->post_title ) ) {
6594
			$_field = $params;
6595
		} elseif ( isset( $params->id ) && ! empty( $params->id ) ) {
6596
			$_field = get_post( $dumb = (int) $params->id );
6597
		} else {
6598
			if ( ! isset( $params->pod ) ) {
6599
				$params->pod = '';
6600
			}
6601
6602
			if ( ! isset( $params->pod_id ) ) {
6603
				$params->pod_id = 0;
6604
			}
6605
6606
			if ( isset( $params->pod_data ) ) {
6607
				$pod = $params->pod_data;
6608
			} else {
6609
				$pod = $this->load_pod( array(
6610
					'name'         => $params->pod,
6611
					'id'           => $params->pod_id,
6612
					'table_info'   => false,
6613
					'bypass_cache' => $bypass_cache
6614
				), false );
6615
6616
				if ( false === $pod ) {
6617
					if ( $strict ) {
6618
						return pods_error( __( 'Pod not found', 'pods' ), $this );
6619
					}
6620
6621
					return false;
6622
				}
6623
			}
6624
6625
			$params->pod_id = $pod['id'];
6626
			$params->pod    = $pod['name'];
6627
6628
			if ( empty( $params->name ) && empty( $params->pod ) && empty( $params->pod_id ) ) {
6629
				return pods_error( __( 'Either Field Name or Field ID / Pod ID are required', 'pods' ), $this );
6630
			}
6631
6632
			$params->name = pods_clean_name( $params->name, true, ( 'meta' === $pod['storage'] ? false : true ) );
6633
6634
			if ( isset( $pod['fields'][ $params->name ] ) && isset( $pod['fields'][ $params->name ]['id'] ) ) {
6635
				return $pod['fields'][ $params->name ];
6636
			}
6637
6638
			$field = false;
6639
6640
			if ( ! $bypass_cache && pods_api_cache() ) {
6641
				$field = pods_transient_get( 'pods_field_' . $params->pod . '_' . $params->name );
6642
			}
6643
6644
			if ( empty( $field ) ) {
6645
				$field = get_posts( array(
6646
					'name'           => $params->name,
6647
					'post_type'      => '_pods_field',
6648
					'posts_per_page' => 1,
6649
					'post_parent'    => $params->pod_id
6650
				) );
6651
6652
				if ( empty( $field ) || empty( $field[0] ) ) {
6653
					if ( $strict ) {
6654
						return pods_error( __( 'Field not found', 'pods' ), $this );
6655
					}
6656
6657
					return false;
6658
				}
6659
6660
				$_field = $field[0];
6661
6662
				$field = array();
6663
			}
6664
		}
6665
6666
		if ( empty( $_field ) ) {
6667
			if ( $strict ) {
6668
				return pods_error( __( 'Field not found', 'pods' ), $this );
6669
			}
6670
6671
			return false;
6672
		}
6673
6674
		$_field = get_object_vars( $_field );
6675
6676
		if ( ! isset( $pod['name'] ) && ! isset( $_field['pod'] ) ) {
6677
			if ( 0 < $_field['post_parent'] ) {
6678
				$pod = $this->load_pod( array( 'id' => $_field['post_parent'], 'table_info' => false ), false );
6679
			}
6680
6681
			if ( empty( $pod ) ) {
6682
				if ( $strict ) {
6683
					return pods_error( __( 'Pod for field not found', 'pods' ), $this );
6684
				}
6685
6686
				return false;
6687
			}
6688
		}
6689
6690
		if ( empty( $field ) ) {
6691
			if ( ! $bypass_cache && pods_api_cache() && ( isset( $pod['name'] ) || isset( $_field['pod'] ) ) ) {
6692
				$field = pods_transient_get( 'pods_field_' . pods_var( 'name', $pod, pods_var( 'pod', $_field ), null, true ) . '_' . $_field['post_name'] );
6693
			}
6694
6695
			if ( empty( $field ) ) {
6696
				$defaults = array(
6697
					'type' => 'text'
6698
				);
6699
6700
				$field = array(
6701
					'id'          => $_field['ID'],
6702
					'name'        => $_field['post_name'],
6703
					'label'       => $_field['post_title'],
6704
					'description' => $_field['post_content'],
6705
					'weight'      => $_field['menu_order'],
6706
					'pod_id'      => $_field['post_parent'],
6707
					'pick_object' => '',
6708
					'pick_val'    => '',
6709
					'sister_id'   => '',
6710
					'table_info'  => array()
6711
				);
6712
6713
				if ( isset( $pod['name'] ) ) {
6714
					$field['pod'] = $pod['name'];
6715
				} elseif ( isset( $_field['pod'] ) ) {
6716
					$field['pod'] = $_field['pod'];
6717
				}
6718
6719
				if ( $bypass_cache ) {
6720
					wp_cache_delete( $field['id'], 'post_meta' );
6721
6722
					update_postmeta_cache( array( $field['id'] ) );
6723
				}
6724
6725
				$field['options'] = get_post_meta( $field['id'] );
6726
6727
				$options_ignore = array(
6728
					'method',
6729
					'table_info',
6730
					'attributes',
6731
					'group',
6732
					'grouped',
6733
					'developer_mode',
6734
					'dependency',
6735
					'depends-on',
6736
					'excludes-on'
6737
				);
6738
6739
				foreach ( $options_ignore as $ignore ) {
6740
					if ( isset( $field['options'][ $ignore ] ) ) {
6741
						unset( $field['options'][ $ignore ] );
6742
					}
6743
				}
6744
6745
				foreach ( $field['options'] as $option => $value ) {
6746
					if ( is_array( $value ) ) {
6747
						foreach ( $value as $k => $v ) {
6748
							if ( ! is_array( $v ) ) {
6749
								$value[ $k ] = maybe_unserialize( $v );
6750
							}
6751
						}
6752
6753
						if ( 1 == count( $value ) ) {
6754
							$value = current( $value );
6755
						}
6756
					} else {
6757
						$value = maybe_unserialize( $value );
6758
					}
6759
6760
					$field['options'][ $option ] = $value;
6761
				}
6762
6763
				$field['options'] = array_merge( $defaults, $field['options'] );
6764
6765
				$field['type'] = $field['options']['type'];
6766
6767
				unset( $field['options']['type'] );
6768
6769
				if ( isset( $field['options']['pick_object'] ) ) {
6770
					$field['pick_object'] = $field['options']['pick_object'];
6771
6772
					unset( $field['options']['pick_object'] );
6773
				}
6774
6775
				if ( isset( $field['options']['pick_val'] ) ) {
6776
					$field['pick_val'] = $field['options']['pick_val'];
6777
6778
					unset( $field['options']['pick_val'] );
6779
				}
6780
6781
				if ( isset( $field['options']['sister_id'] ) ) {
6782
					$field['sister_id'] = $field['options']['sister_id'];
6783
6784
					unset( $field['options']['sister_id'] );
6785
				}
6786
6787
				if ( isset( $field['options']['sister_field_id'] ) ) {
6788
					unset( $field['options']['sister_field_id'] );
6789
				}
6790
6791
				if ( pods_api_cache() && ( isset( $pod['name'] ) || isset( $_field['pod'] ) ) ) {
6792
					pods_transient_set( 'pods_field_' . pods_var( 'name', $pod, pods_var( 'pod', $_field ), null, true ) . '_' . $field['name'], $field );
6793
				}
6794
			}
6795
		}
6796
6797
		$field['table_info'] = array();
6798
6799
		if ( 'pick' === $field['type'] && $params->table_info ) {
6800
			$field['table_info'] = $this->get_table_info( $field['pick_object'], $field['pick_val'], null, null, $field );
6801
		}
6802
6803
		return $field;
6804
	}
6805
6806
	/**
6807
	 * Load fields by Pod, ID, Name, and/or Type
6808
	 *
6809
	 * $params['pod_id'] int The Pod ID
6810
	 * $params['pod'] string The Pod name
6811
	 * $params['id'] array The field IDs
6812
	 * $params['name'] array The field names
6813
	 * $params['type'] array The field types
6814
	 * $params['options'] array Field Option(s) key=>value array to filter by
6815
	 * $params['where'] string WHERE clause of query
6816
	 * $params['object_fields'] bool Whether to include the object fields for WP objects, default true
6817
	 *
6818
	 * @param array $params An associative array of parameters
6819
	 * @param bool  $strict Whether to require a field exist or not when loading the info
6820
	 *
6821
	 * @return array Array of field data.
6822
	 *
6823
	 * @since 1.7.9
6824
	 */
6825
	public function load_fields( $params, $strict = false ) {
6826
6827
		// @todo Get away from using md5/serialize, I'm sure we can cache specific parts
6828
		$cache_key = md5( serialize( $params ) );
6829
		if ( isset( $this->fields_cache[ $cache_key ] ) ) {
6830
			return $this->fields_cache[ $cache_key ];
6831
		}
6832
6833
		$params = (object) pods_sanitize( $params );
6834
6835
		if ( ! isset( $params->pod ) || empty( $params->pod ) ) {
6836
			$params->pod = '';
6837
		}
6838
6839
		if ( ! isset( $params->pod_id ) || empty( $params->pod_id ) ) {
6840
			$params->pod_id = 0;
6841
		}
6842
6843
		if ( ! isset( $params->name ) || empty( $params->name ) ) {
6844
			$params->name = array();
6845
		} else {
6846
			$params->name = (array) $params->name;
6847
		}
6848
6849
		if ( ! isset( $params->id ) || empty( $params->id ) ) {
6850
			$params->id = array();
6851
		} else {
6852
			$params->id = (array) $params->id;
6853
6854
			foreach ( $params->id as &$id ) {
6855
				$id = pods_absint( $id );
6856
			}
6857
		}
6858
6859
		if ( ! isset( $params->type ) || empty( $params->type ) ) {
6860
			$params->type = array();
6861
		} else {
6862
			$params->type = (array) $params->type;
6863
		}
6864
6865
		if ( ! isset( $params->object_fields ) ) {
6866
			$params->object_fields = true;
6867
		} else {
6868
			$params->object_fields = (boolean) $params->object_fields;
6869
		}
6870
6871
		$fields = array();
6872
6873
		if ( ! empty( $params->pod ) || ! empty( $params->pod_id ) ) {
6874
			$pod = $this->load_pod( array(
6875
				'name'       => $params->pod,
6876
				'id'         => $params->pod_id,
6877
				'table_info' => true
6878
			), false );
6879
6880
			if ( false === $pod ) {
6881
				if ( $strict ) {
6882
					return pods_error( __( 'Pod not found', 'pods' ), $this );
6883
				}
6884
6885
				return $fields;
6886
			}
6887
6888
			if ( $params->object_fields && ! empty( $pod['object_fields'] ) ) {
6889
				$pod['fields'] = array_merge( $pod['object_fields'], $pod['fields'] );
6890
			}
6891
6892
			foreach ( $pod['fields'] as $field ) {
6893
				if ( empty( $params->name ) && empty( $params->id ) && empty( $params->type ) ) {
6894
					$fields[ $field['name'] ] = $field;
6895
				} elseif ( in_array( $fields['name'], $params->name ) || in_array( $fields['id'], $params->id ) || in_array( $fields['type'], $params->type ) ) {
6896
					$fields[ $field['name'] ] = $field;
6897
				}
6898
			}
6899
		} elseif ( ( isset( $params->options ) && ! empty( $params->options ) && is_array( $params->options ) ) || ( isset( $params->where ) && ! empty( $params->where ) && is_array( $params->where ) ) ) {
6900
			$order   = 'ASC';
6901
			$orderby = 'menu_order title';
6902
			$limit   = - 1;
6903
			$ids     = false;
6904
6905
			$meta_query = array();
6906
6907
			if ( isset( $params->options ) && ! empty( $params->options ) && is_array( $params->options ) ) {
6908
				foreach ( $params->options as $option => $value ) {
6909
					if ( ! is_array( $value ) ) {
6910
						$value = array( $value );
6911
					}
6912
6913
					$value = pods_trim( $value );
6914
6915
					sort( $value );
6916
6917
					$meta_query[] = array(
6918
						'key'     => $option,
6919
						'value'   => pods_sanitize( $value ),
6920
						'compare' => 'IN'
6921
					);
6922
				}
6923
			}
6924
6925
			if ( isset( $params->where ) && ! empty( $params->where ) && is_array( $params->where ) ) {
6926
				$meta_query = array_merge( $meta_query, (array) $params->where );
6927
			}
6928
6929
			$args = array(
6930
				'post_type'      => '_pods_field',
6931
				'nopaging'       => true,
6932
				'posts_per_page' => $limit,
6933
				'order'          => $order,
6934
				'orderby'        => $orderby,
6935
				'meta_query'     => $meta_query,
6936
			);
6937
6938
			// Only set post__in if there are ids to filter (see https://core.trac.wordpress.org/ticket/28099)
6939
			if ( false !== $ids ) {
6940
				$args['post__in'] = $ids;
6941
			}
6942
6943
			$fields = array();
6944
6945
			$_fields = get_posts( $args );
6946
6947
			foreach ( $_fields as $field ) {
6948
				$field = $this->load_field( $field, false );
6949
6950
				if ( ! empty( $field ) ) {
6951
					$fields[ $field['id'] ] = $field;
6952
				}
6953
			}
6954
		} else {
6955
			if ( empty( $params->name ) && empty( $params->id ) && empty( $params->type ) ) {
6956
				return pods_error( __( 'Either Field Name / Field ID / Field Type, or Pod Name / Pod ID are required', 'pods' ), $this );
6957
			}
6958
6959
			$lookup = array();
6960
6961
			if ( ! empty( $params->name ) ) {
6962
				$fields = implode( "', '", $params->name );
6963
6964
				$lookup[] = "`post_name` IN ( '{$fields}' )";
6965
			}
6966
6967
			if ( ! empty( $params->id ) ) {
6968
				$fields = implode( ", ", $params->id );
6969
6970
				$lookup[] = "`ID` IN ( {$fields} )";
6971
			}
6972
6973
			$lookup = implode( ' AND ', $lookup );
6974
6975
			$result = pods_query( "SELECT `ID`, `post_name`, `post_parent` FROM `@wp_posts` WHERE `post_type` = '_pods_field' AND ( {$lookup} )" );
6976
6977
			$fields = array();
6978
6979
			if ( ! empty( $result ) ) {
6980
				foreach ( $result as $field ) {
6981
					$field = $this->load_field( array(
6982
						'id'     => $field->ID,
6983
						'name'   => $field->post_name,
6984
						'pod_id' => $field->post_parent
6985
					), false );
6986
6987
					if ( ! empty( $field ) && ( empty( $params->type ) || in_array( $field['type'], $params->type ) ) ) {
6988
						$fields[ $field['id'] ] = $field;
6989
					}
6990
				}
6991
			}
6992
		}
6993
		if ( isset( $cache_key ) ) {
6994
			$this->fields_cache[ $cache_key ] = $fields;
6995
		}
6996
6997
		return $fields;
6998
	}
6999
7000
	/**
7001
	 * Load a Pods Object
7002
	 *
7003
	 * $params['id'] int The Object ID
7004
	 * $params['name'] string The Object name
7005
	 * $params['type'] string The Object type
7006
	 *
7007
	 * @param array|object $params An associative array of parameters
7008
	 * @param bool         $strict
7009
	 *
7010
	 * @return array|bool
7011
	 * @since 2.0
7012
	 */
7013
	public function load_object( $params, $strict = false ) {
7014
7015
		if ( is_object( $params ) && isset( $params->post_title ) ) {
7016
			$_object = get_object_vars( $params );
7017
		} else {
7018
			$params = (object) pods_sanitize( $params );
7019
7020
			if ( ! isset( $params->type ) || empty( $params->type ) ) {
7021
				return pods_error( __( 'Object type is required', 'pods' ), $this );
7022
			}
7023
7024
			if ( ( ! isset( $params->id ) || empty( $params->id ) ) && ( ! isset( $params->name ) || empty( $params->name ) ) ) {
7025
				return pods_error( __( 'Either Object ID or Name are required', 'pods' ), $this );
7026
			}
7027
7028
			/**
7029
			 * @var $wpdb wpdb
7030
			 */
7031
			global $wpdb;
7032
7033
			if ( isset( $params->name ) ) {
7034
				$_object = pods_by_title( $params->name, ARRAY_A, '_pods_' . $params->type, 'publish' );
7035
			} else {
7036
				$object = $params->id;
7037
7038
				$_object = get_post( $object, ARRAY_A );
7039
			}
7040
7041
			if ( empty( $_object ) ) {
7042
				if ( $strict ) {
7043
					return pods_error( __( 'Object not found', 'pods' ), $this );
7044
				}
7045
7046
				return false;
7047
			}
7048
		}
7049
7050
		$object = array(
7051
			'id'   => $_object['ID'],
7052
			'name' => $_object['post_title'],
7053
			'code' => $_object['post_content'],
7054
			'type' => str_replace( '_pods_', '', $_object['post_type'] ),
7055
			'slug' => $_object['post_name']
7056
		);
7057
7058
		$object['options'] = get_post_meta( $object['id'] );
7059
7060
		foreach ( $object['options'] as $option => &$value ) {
7061
			if ( is_array( $value ) && 1 == count( $value ) ) {
7062
				$value = current( $value );
7063
			}
7064
		}
7065
7066
		return $object;
7067
	}
7068
7069
	/**
7070
	 * Load Multiple Pods Objects
7071
	 *
7072
	 * $params['type'] string The Object type
7073
	 * $params['options'] array Pod Option(s) key=>value array to filter by
7074
	 * $params['orderby'] string ORDER BY clause of query
7075
	 * $params['limit'] string Number of objects to return
7076
	 * $params['where'] string WHERE clause of query
7077
	 * $params['ids'] string|array IDs of Objects
7078
	 *
7079
	 * @param array|object $params An associative array of parameters
7080
	 *
7081
	 * @return array
7082
	 * @since 2.0
7083
	 */
7084
	public function load_objects( $params ) {
7085
7086
		$params = (object) pods_sanitize( $params );
7087
7088
		if ( ! isset( $params->type ) || empty( $params->type ) ) {
7089
			return pods_error( __( 'Pods Object type is required', 'pods' ), $this );
7090
		}
7091
7092
		$order   = 'ASC';
7093
		$orderby = 'menu_order';
7094
		$limit   = - 1;
7095
		$ids     = false;
7096
7097
		$meta_query = array();
7098
		$cache_key  = '';
7099
7100
		if ( isset( $params->options ) && ! empty( $params->options ) && is_array( $params->options ) ) {
7101
			foreach ( $params->options as $option => $value ) {
7102
				if ( ! is_array( $value ) ) {
7103
					$value = array( $value );
7104
				}
7105
7106
				$value = pods_trim( $value );
7107
7108
				sort( $value );
7109
7110
				$meta_query[] = array(
7111
					'key'     => $option,
7112
					'value'   => pods_sanitize( $value ),
7113
					'compare' => 'IN'
7114
				);
7115
			}
7116
		}
7117
7118
		if ( isset( $params->where ) && is_array( $params->where ) ) {
7119
			$meta_query = array_merge( $meta_query, (array) $params->where );
7120
		}
7121
7122
		if ( isset( $params->order ) && ! empty( $params->order ) && in_array( strtoupper( $params->order ), array(
7123
				'ASC',
7124
				'DESC'
7125
			) ) ) {
7126
			$order = strtoupper( $params->order );
7127
		}
7128
7129
		if ( isset( $params->orderby ) && ! empty( $params->orderby ) ) {
7130
			$orderby = strtoupper( $params->orderby );
7131
		}
7132
7133
		if ( isset( $params->limit ) && ! empty( $params->limit ) ) {
7134
			$limit = pods_absint( $params->limit );
7135
		}
7136
7137
		if ( isset( $params->ids ) && ! empty( $params->ids ) ) {
7138
			$ids = $params->ids;
7139
7140
			if ( ! is_array( $ids ) ) {
7141
				$ids = explode( ',', $ids );
7142
			}
7143
		}
7144
7145
		if ( empty( $ids ) ) {
7146
			$ids = false;
7147
		}
7148
7149
		if ( pods_api_cache() && empty( $meta_query ) && empty( $limit ) && ( empty( $orderby ) || 'menu_order' === $orderby ) && empty( $ids ) ) {
7150
			$cache_key = 'pods_objects_' . $params->type;
7151
7152
			$the_objects = pods_transient_get( $cache_key );
7153
7154
			if ( false !== $the_objects ) {
7155
				return $the_objects;
7156
			}
7157
		}
7158
7159
		$the_objects = array();
7160
7161
		$args = array(
7162
			'post_type'      => '_pods_' . $params->type,
7163
			'nopaging'       => true,
7164
			'posts_per_page' => $limit,
7165
			'order'          => $order,
7166
			'orderby'        => $orderby,
7167
			'meta_query'     => $meta_query,
7168
		);
7169
7170
		// Only set post__in if there are ids to filter (see https://core.trac.wordpress.org/ticket/28099)
7171
		if ( false !== $ids ) {
7172
			$args['post__in'] = $ids;
7173
		}
7174
7175
		$objects = get_posts( $args );
7176
7177
		foreach ( $objects as $object ) {
7178
			$object = $this->load_object( $object );
7179
7180
			$the_objects[ $object['name'] ] = $object;
7181
		}
7182
7183
		if ( pods_api_cache() && ! empty( $cache_key ) ) {
7184
			pods_transient_set( $cache_key, $the_objects );
7185
		}
7186
7187
		return $the_objects;
7188
	}
7189
7190
	/**
7191
	 * @see   PodsAPI::load_object
7192
	 *
7193
	 * Load a Pod Template
7194
	 *
7195
	 * $params['id'] int The template ID
7196
	 * $params['name'] string The template name
7197
	 *
7198
	 * @param array $params An associative array of parameters
7199
	 *
7200
	 * @return array|bool
7201
	 * @since 1.7.9
7202
	 */
7203
	public function load_template( $params ) {
7204
7205
		if ( ! class_exists( 'Pods_Templates' ) ) {
7206
			return false;
7207
		}
7208
7209
		$params       = (object) $params;
7210
		$params->type = 'template';
7211
7212
		return $this->load_object( $params );
7213
	}
7214
7215
	/**
7216
	 * @see   PodsAPI::load_objects
7217
	 *
7218
	 * Load Multiple Pod Templates
7219
	 *
7220
	 * $params['where'] string The WHERE clause of query
7221
	 * $params['options'] array Pod Option(s) key=>value array to filter by
7222
	 * $params['orderby'] string ORDER BY clause of query
7223
	 * $params['limit'] string Number of templates to return
7224
	 *
7225
	 * @param array $params (optional) An associative array of parameters
0 ignored issues
show
Documentation introduced by
Should the type for parameter $params not be array|null? Also, consider making the array more specific, something like array<String>, or String[].

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive. In addition it looks for parameters that have the generic type array and suggests a stricter type like array<String>.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
7226
	 *
7227
	 * @return array
7228
	 *
7229
	 * @since 2.0
7230
	 */
7231
	public function load_templates( $params = null ) {
7232
7233
		if ( ! class_exists( 'Pods_Templates' ) ) {
7234
			return array();
7235
		}
7236
7237
		$params       = (object) $params;
7238
		$params->type = 'template';
7239
7240
		return $this->load_objects( $params );
7241
	}
7242
7243
	/**
7244
	 * @see   PodsAPI::load_object
7245
	 *
7246
	 * Load a Pod Page
7247
	 *
7248
	 * $params['id'] int The page ID
7249
	 * $params['name'] string The page URI
7250
	 *
7251
	 * @param array $params An associative array of parameters
7252
	 *
7253
	 * @return array|bool
7254
	 *
7255
	 * @since 1.7.9
7256
	 */
7257
	public function load_page( $params ) {
7258
7259
		if ( ! class_exists( 'Pods_Pages' ) ) {
7260
			return false;
7261
		}
7262
7263
		$params = (object) $params;
7264
		if ( ! isset( $params->name ) && isset( $params->uri ) ) {
7265
			$params->name = $params->uri;
7266
			unset( $params->uri );
7267
		}
7268
		$params->type = 'page';
7269
7270
		return $this->load_object( $params );
7271
	}
7272
7273
	/**
7274
	 * @see   PodsAPI::load_objects
7275
	 *
7276
	 * Load Multiple Pod Pages
7277
	 *
7278
	 * $params['where'] string The WHERE clause of query
7279
	 * $params['options'] array Pod Option(s) key=>value array to filter by
7280
	 * $params['orderby'] string ORDER BY clause of query
7281
	 * $params['limit'] string Number of pages to return
7282
	 *
7283
	 * @param array $params (optional) An associative array of parameters
0 ignored issues
show
Documentation introduced by
Should the type for parameter $params not be array|null? Also, consider making the array more specific, something like array<String>, or String[].

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive. In addition it looks for parameters that have the generic type array and suggests a stricter type like array<String>.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
7284
	 *
7285
	 * @return array
7286
	 *
7287
	 * @since 2.0
7288
	 */
7289
	public function load_pages( $params = null ) {
7290
7291
		if ( ! class_exists( 'Pods_Pages' ) ) {
7292
			return array();
7293
		}
7294
7295
		$params       = (object) $params;
7296
		$params->type = 'page';
7297
7298
		return $this->load_objects( $params );
7299
	}
7300
7301
	/**
7302
	 * @see   PodsAPI::load_object
7303
	 *
7304
	 * Load a Pod Helper
7305
	 *
7306
	 * $params['id'] int The helper ID
7307
	 * $params['name'] string The helper name
7308
	 *
7309
	 * @param array $params An associative array of parameters
7310
	 *
7311
	 * @return array|bool
7312
	 *
7313
	 * @since 1.7.9
7314
	 */
7315
	public function load_helper( $params ) {
7316
7317
		if ( ! class_exists( 'Pods_Helpers' ) ) {
7318
			return false;
7319
		}
7320
7321
		$params       = (object) $params;
7322
		$params->type = 'helper';
7323
7324
		return $this->load_object( $params );
7325
	}
7326
7327
	/**
7328
	 * @see   PodsAPI::load_objects
7329
	 *
7330
	 * Load Multiple Pod Helpers
7331
	 *
7332
	 * $params['where'] string The WHERE clause of query
7333
	 * $params['options'] array Pod Option(s) key=>value array to filter by
7334
	 * $params['orderby'] string ORDER BY clause of query
7335
	 * $params['limit'] string Number of pages to return
7336
	 *
7337
	 * @param array $params (optional) An associative array of parameters
0 ignored issues
show
Documentation introduced by
Should the type for parameter $params not be array|null? Also, consider making the array more specific, something like array<String>, or String[].

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive. In addition it looks for parameters that have the generic type array and suggests a stricter type like array<String>.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
7338
	 *
7339
	 * @return array
7340
	 *
7341
	 * @since 2.0
7342
	 */
7343
	public function load_helpers( $params = null ) {
7344
7345
		if ( ! class_exists( 'Pods_Helpers' ) ) {
7346
			return array();
7347
		}
7348
7349
		$params       = (object) $params;
7350
		$params->type = 'helper';
7351
7352
		return $this->load_objects( $params );
7353
	}
7354
7355
	/**
7356
	 * Load the pod item object
7357
	 *
7358
	 * $params['pod'] string The datatype name
7359
	 * $params['id'] int (optional) The item's ID
7360
	 *
7361
	 * @param array $params An associative array of parameters
7362
	 *
7363
	 * @return bool|\Pods
7364
	 *
7365
	 * @uses  pods()
7366
	 *
7367
	 * @since 2.0
7368
	 */
7369
	public function load_pod_item( $params ) {
7370
7371
		$params = (object) pods_sanitize( $params );
7372
7373
		if ( ! isset( $params->pod ) || empty( $params->pod ) ) {
7374
			return pods_error( __( 'Pod name required', 'pods' ), $this );
7375
		}
7376
		if ( ! isset( $params->id ) || empty( $params->id ) ) {
7377
			return pods_error( __( 'Item ID required', 'pods' ), $this );
7378
		}
7379
7380
		$pod = false;
7381
7382
		if ( pods_api_cache() ) {
7383
			$pod = pods_cache_get( $params->id, 'pods_item_object_' . $params->pod );
7384
		}
7385
7386
		if ( false !== $pod ) {
7387
			return $pod;
7388
		}
7389
7390
		$pod = pods( $params->pod, $params->id );
7391
7392
		if ( pods_api_cache() ) {
7393
			pods_cache_set( $params->id, $pod, 'pods_item_object_' . $params->pod );
7394
		}
7395
7396
		return $pod;
7397
	}
7398
7399
	/**
7400
	 * Load potential sister fields for a specific field
7401
	 *
7402
	 * $params['pod'] int The Pod name
7403
	 * $params['related_pod'] string The related Pod name
7404
	 *
7405
	 * @param array $params An associative array of parameters
7406
	 * @param array $pod    (optional) Array of Pod data to use (to avoid lookup)
0 ignored issues
show
Documentation introduced by
Should the type for parameter $pod not be array|null? Also, consider making the array more specific, something like array<String>, or String[].

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive. In addition it looks for parameters that have the generic type array and suggests a stricter type like array<String>.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
7407
	 *
7408
	 * @return array|bool
7409
	 *
7410
	 * @since 1.7.9
7411
	 *
7412
	 * @uses  PodsAPI::load_pod
7413
	 */
7414
	public function load_sister_fields( $params, $pod = null ) {
7415
7416
		$params = (object) pods_sanitize( $params );
7417
7418
		if ( empty( $pod ) ) {
7419
			$pod = $this->load_pod( array( 'name' => $params->pod, 'table_info' => false ), false );
7420
7421
			if ( false === $pod ) {
7422
				return pods_error( __( 'Pod not found', 'pods' ), $this );
7423
			}
7424
		}
7425
7426
		$params->pod_id = $pod['id'];
7427
		$params->pod    = $pod['name'];
7428
7429
		$type = false;
7430
7431
		if ( 0 === strpos( $params->related_pod, 'pod-' ) ) {
7432
			$params->related_pod = pods_str_replace( 'pod-', '', $params->related_pod, 1 );
7433
			$type                = 'pod';
7434
		} elseif ( 0 === strpos( $params->related_pod, 'post_type-' ) ) {
7435
			$params->related_pod = pods_str_replace( 'post_type-', '', $params->related_pod, 1 );
7436
			$type                = 'post_type';
7437
		} elseif ( 0 === strpos( $params->related_pod, 'taxonomy-' ) ) {
7438
			$params->related_pod = pods_str_replace( 'taxonomy-', '', $params->related_pod, 1 );
7439
			$type                = 'taxonomy';
7440
		} elseif ( 'comment' === $params->related_pod ) {
7441
			$type = $params->related_pod;
7442
		}
7443
7444
		$related_pod = $this->load_pod( array( 'name' => $params->related_pod, 'table_info' => false ), false );
7445
7446
		if ( false === $related_pod || ( false !== $type && 'pod' !== $type && $type !== $related_pod['type'] ) ) {
7447
			return pods_error( __( 'Related Pod not found', 'pods' ), $this );
7448
		}
7449
7450
		$params->related_pod_id = $related_pod['id'];
7451
		$params->related_pod    = $related_pod['name'];
7452
7453
		$sister_fields = array();
7454
7455
		foreach ( $related_pod['fields'] as $field ) {
7456
			if ( 'pick' === $field['type'] && in_array( $field['pick_object'], array(
7457
					$pod['type'],
7458
					'pod'
7459
				) ) && ( $params->pod == $field['pick_object'] || $params->pod == $field['pick_val'] ) ) {
7460
				$sister_fields[ $field['id'] ] = esc_html( $field['label'] . ' (' . $field['name'] . ')' );
7461
			}
7462
		}
7463
7464
		return $sister_fields;
7465
	}
7466
7467
	/**
7468
	 * Takes a sql field such as tinyint and returns the pods field type, such as num.
7469
	 *
7470
	 * @param string $sql_field The SQL field to look for
7471
	 *
7472
	 * @return string The field type
7473
	 *
7474
	 * @since 2.0
7475
	 */
7476
	public static function detect_pod_field_from_sql_data_type( $sql_field ) {
7477
7478
		$sql_field = strtolower( $sql_field );
7479
7480
		$field_to_field_map = array(
7481
			'tinyint'    => 'number',
7482
			'smallint'   => 'number',
7483
			'mediumint'  => 'number',
7484
			'int'        => 'number',
7485
			'bigint'     => 'number',
7486
			'float'      => 'number',
7487
			'double'     => 'number',
7488
			'decimal'    => 'number',
7489
			'date'       => 'date',
7490
			'datetime'   => 'datetime',
7491
			'timestamp'  => 'datetime',
7492
			'time'       => 'time',
7493
			'year'       => 'date',
7494
			'varchar'    => 'text',
7495
			'text'       => 'paragraph',
7496
			'mediumtext' => 'paragraph',
7497
			'longtext'   => 'paragraph'
7498
		);
7499
7500
		return ( array_key_exists( $sql_field, $field_to_field_map ) ) ? $field_to_field_map[ $sql_field ] : 'paragraph';
7501
	}
7502
7503
	/**
7504
	 * Gets all field types
7505
	 *
7506
	 * @return array Array of field types
7507
	 *
7508
	 * @uses  PodsForm::field_loader
7509
	 *
7510
	 * @since 2.0
7511
	 * @deprecated
7512
	 */
7513
	public function get_field_types() {
7514
7515
		return PodsForm::field_types();
7516
	}
7517
7518
	/**
7519
	 * Gets the schema definition of a field.
7520
	 *
7521
	 * @param string $type    Field type to look for
7522
	 * @param array  $options (optional) Options of the field to pass to the schema function.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $options not be array|null? Also, consider making the array more specific, something like array<String>, or String[].

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive. In addition it looks for parameters that have the generic type array and suggests a stricter type like array<String>.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
7523
	 *
7524
	 * @return array|bool|mixed|null
7525
	 *
7526
	 * @since 2.0
7527
	 */
7528
	private function get_field_definition( $type, $options = null ) {
7529
7530
		$definition = PodsForm::field_method( $type, 'schema', $options );
7531
7532
		return $this->do_hook( 'field_definition', $definition, $type, $options );
7533
	}
7534
7535
	/**
7536
	 * @see   PodsForm:validate
7537
	 *
7538
	 * Validates the value of a field.
7539
	 *
7540
	 * @param mixed        $value         The value to validate
7541
	 * @param string       $field         Field to use for validation
7542
	 * @param array        $object_fields Fields of the object we're validating
7543
	 * @param array        $fields        Array of all fields data
7544
	 * @param array|Pods   $pod           Array of pod data (or Pods object)
7545
	 * @param array|object $params        Extra parameters to pass to the validation function of the field.
7546
	 *
7547
	 * @return array|bool
7548
	 *
7549
	 * @uses  PodsForm::validate
7550
	 *
7551
	 * @since 2.0
7552
	 */
7553
	public function handle_field_validation( &$value, $field, $object_fields, $fields, $pod, $params ) {
7554
7555
		$tableless_field_types = PodsForm::tableless_field_types();
0 ignored issues
show
Comprehensibility Naming introduced by
The variable name $tableless_field_types exceeds the maximum configured length of 20.

Very long variable names usually make code harder to read. It is therefore recommended not to make variable names too verbose.

Loading history...
7556
7557
		$fields = array_merge( $fields, $object_fields );
7558
7559
		$options = $fields[ $field ];
7560
7561
		$id = ( is_object( $params ) ? $params->id : ( is_object( $pod ) ? $pod->id() : 0 ) );
7562
7563
		if ( is_object( $pod ) ) {
7564
			$pod = $pod->pod_data;
7565
		}
7566
7567
		$type  = $options['type'];
7568
		$label = $options['label'];
7569
		$label = empty( $label ) ? $field : $label;
7570
7571
		// Verify required fields
7572
		if ( 1 == pods_var( 'required', $options['options'], 0 ) && 'slug' !== $type ) {
7573
			if ( '' === $value || null === $value || array() === $value ) {
7574
				return pods_error( sprintf( __( '%s is empty', 'pods' ), $label ), $this );
7575
			}
7576
7577
			if ( 'multi' === pods_var( 'pick_format_type', $options['options'] ) && 'autocomplete' !== pods_var( 'pick_format_multi', $options['options'] ) ) {
7578
				$has_value = false;
7579
7580
				$check_value = (array) $value;
7581
7582
				foreach ( $check_value as $val ) {
7583
					if ( '' !== $val && null !== $val && 0 !== $val && '0' !== $val ) {
7584
						$has_value = true;
7585
7586
						continue;
7587
					}
7588
				}
7589
7590
				if ( ! $has_value ) {
7591
					return pods_error( sprintf( __( '%s is required', 'pods' ), $label ), $this );
7592
				}
7593
			}
7594
7595
		}
7596
7597
		// @todo move this to after pre-save preparations
7598
		// Verify unique fields
7599
		if ( 1 == pods_var( 'unique', $options['options'], 0 ) && '' !== $value && null !== $value && array() !== $value ) {
7600
			if ( empty( $pod ) ) {
7601
				return false;
7602
			}
7603
7604
			if ( ! in_array( $type, $tableless_field_types ) ) {
7605
				$exclude = '';
7606
7607
				if ( ! empty( $id ) ) {
7608
					$exclude = "AND `id` != {$id}";
7609
				}
7610
7611
				$check = false;
7612
7613
				$check_value = pods_sanitize( $value );
7614
7615
				// @todo handle meta-based fields
7616
				// Trigger an error if not unique
7617
				if ( 'table' === $pod['storage'] ) {
7618
					$check = pods_query( "SELECT `id` FROM `@wp_pods_" . $pod['name'] . "` WHERE `{$field}` = '{$check_value}' {$exclude} LIMIT 1", $this );
7619
				}
7620
7621
				if ( ! empty( $check ) ) {
7622
					return pods_error( sprintf( __( '%s needs to be unique', 'pods' ), $label ), $this );
7623
				}
7624
			} else {
7625
				// @todo handle tableless check
7626
			}
7627
		}
7628
7629
		$validate = PodsForm::validate( $options['type'], $value, $field, array_merge( $options, pods_var( 'options', $options, array() ) ), $fields, $pod, $id, $params );
7630
7631
		$validate = $this->do_hook( 'field_validation', $validate, $value, $field, $object_fields, $fields, $pod, $params );
7632
7633
		return $validate;
7634
	}
7635
7636
	/**
7637
	 * Find items related to a parent field
7638
	 *
7639
	 * @param int   $field_id The Field ID
0 ignored issues
show
Documentation introduced by
Should the type for parameter $field not be array|null? Also, consider making the array more specific, something like array<String>, or String[].

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive. In addition it looks for parameters that have the generic type array and suggests a stricter type like array<String>.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
7640
	 * @param int   $pod_id   The Pod ID
0 ignored issues
show
Documentation introduced by
Should the type for parameter $pod not be array|null? Also, consider making the array more specific, something like array<String>, or String[].

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive. In addition it looks for parameters that have the generic type array and suggests a stricter type like array<String>.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
7641
	 * @param mixed $ids      A comma-separated string (or array) of item IDs
7642
	 * @param array $field    Field data array
7643
	 * @param array $pod      Pod data array
7644
	 *
7645
	 * @return int[]
7646
	 *
7647
	 * @since 2.0
7648
	 *
7649
	 * @uses  pods_query()
7650
	 */
7651
	public function lookup_related_items( $field_id, $pod_id, $ids, $field = null, $pod = null ) {
7652
7653
		$related_ids = array();
7654
7655
		if ( ! is_array( $ids ) ) {
7656
			$ids = explode( ',', $ids );
7657
		}
7658
7659
		$ids = array_map( 'absint', $ids );
7660
7661
		$ids = array_unique( array_filter( $ids ) );
7662
7663
		$idstring = implode( ',', $ids );
7664
7665
		if ( 0 != $pod_id && 0 != $field_id && isset( self::$related_item_cache[ $pod_id ][ $field_id ][ $idstring ] ) ) {
7666
			// Check cache first, no point in running the same query multiple times
7667
			return self::$related_item_cache[ $pod_id ][ $field_id ][ $idstring ];
7668
		}
7669
7670
		$tableless_field_types = PodsForm::tableless_field_types();
0 ignored issues
show
Comprehensibility Naming introduced by
The variable name $tableless_field_types exceeds the maximum configured length of 20.

Very long variable names usually make code harder to read. It is therefore recommended not to make variable names too verbose.

Loading history...
7671
7672
		$field_type = pods_v( 'type', $field );
7673
7674
		if ( empty( $ids ) || ! in_array( $field_type, $tableless_field_types ) ) {
7675
			return array();
7676
		}
7677
7678
		$related_pick_limit = 0;
7679
7680
		if ( empty( $field ) ) {
7681
			$field = $this->load_field( array( 'id' => $field_id ) );
7682
		}
7683
7684
		if ( ! empty( $field ) ) {
7685
			$options = (array) pods_var_raw( 'options', $field, $field, null, true );
7686
7687
			$related_pick_limit = (int) pods_v( $field_type . '_limit', $options, 0 );
7688
7689
			if ( 'single' === pods_var_raw( $field_type . '_format_type', $options ) ) {
7690
				$related_pick_limit = 1;
7691
			}
7692
7693
			// Temporary hack until there's some better handling here
7694
			$related_pick_limit = $related_pick_limit * count( $ids );
7695
		}
7696
7697
		if ( 'taxonomy' === $field_type ) {
7698
			$related = wp_get_object_terms( $ids, pods_v( 'name', $field ), array( 'fields' => 'ids' ) );
7699
7700
			if ( ! is_wp_error( $related ) ) {
7701
				$related_ids = $related;
7702
			}
7703
		} elseif ( 'comment' === $field_type ) {
7704
			$comment_args = array(
7705
				'post__in' => $ids,
7706
				'fields'   => 'ids',
7707
			);
7708
7709
			$related = get_comments( $comment_args );
7710
7711
			if ( ! is_wp_error( $related ) ) {
7712
				$related_ids = $related;
7713
			}
7714
		} elseif ( ! pods_tableless() ) {
7715
			$ids = implode( ', ', $ids );
7716
7717
			$field_id  = (int) $field_id;
7718
			$sister_id = (int) pods_var_raw( 'sister_id', $field, 0 );
7719
7720
			$related_where = "
7721
                `field_id` = {$field_id}
7722
                AND `item_id` IN ( {$ids} )
7723
            ";
7724
7725
			$sql = "
7726
                SELECT item_id, related_item_id, related_field_id
7727
                FROM `@wp_podsrel`
7728
                WHERE
7729
                    {$related_where}
7730
                ORDER BY `weight`
7731
            ";
7732
7733
			$relationships = pods_query( $sql );
7734
7735
			if ( ! empty( $relationships ) ) {
7736
				foreach ( $relationships as $relation ) {
7737
					if ( ! in_array( $relation->related_item_id, $related_ids ) ) {
7738
						$related_ids[] = (int) $relation->related_item_id;
7739
					} elseif ( 0 < $sister_id && $field_id == $relation->related_field_id && ! in_array( $relation->item_id, $related_ids ) ) {
7740
						$related_ids[] = (int) $relation->item_id;
7741
					}
7742
				}
7743
			}
7744
		} else {
7745
			if ( ! is_array( $pod ) ) {
7746
				$pod = $this->load_pod( array( 'id' => $pod_id, 'table_info' => false ), false );
7747
			}
7748
7749
			if ( ! empty( $pod ) && in_array( $pod['type'], array(
7750
					'post_type',
7751
					'media',
7752
					'taxonomy',
7753
					'user',
7754
					'comment',
7755
					'settings'
7756
				) ) ) {
7757
				$meta_type = $pod['type'];
7758
7759
				if ( in_array( $meta_type, array( 'post_type', 'media' ) ) ) {
7760
					$meta_type = 'post';
7761
				} elseif ( 'taxonomy' === $meta_type ) {
7762
					$meta_type = 'term';
7763
				}
7764
7765
				$no_conflict = pods_no_conflict_check( ( 'term' === $meta_type ? 'taxonomy' : $meta_type ) );
7766
7767
				if ( ! $no_conflict ) {
7768
					pods_no_conflict_on( ( 'term' === $meta_type ? 'taxonomy' : $meta_type ) );
7769
				}
7770
7771
				foreach ( $ids as $id ) {
7772
					if ( 'settings' === $meta_type ) {
7773
						$related_id = get_option( '_pods_' . $pod['name'] . '_' . $field['name'] );
7774
7775
						if ( empty( $related_id ) ) {
7776
							$related_id = get_option( $pod['name'] . '_' . $field['name'] );
7777
						}
7778
7779
						if ( is_array( $related_id ) && ! empty( $related_id ) ) {
7780
							foreach ( $related_id as $related ) {
7781
								if ( is_array( $related ) && ! empty( $related ) ) {
7782
									if ( isset( $related['id'] ) ) {
7783
										$related_ids[] = (int) $related['id'];
7784
									} else {
7785
										foreach ( $related as $r ) {
7786
											$related_ids[] = (int) $r;
7787
										}
7788
									}
7789
								} else {
7790
									$related_ids[] = (int) $related;
7791
								}
7792
							}
7793
						}
7794
					} else {
7795
						$related_id = get_metadata( $meta_type, $id, '_pods_' . $field['name'], true );
7796
7797
						if ( empty( $related_id ) ) {
7798
							$related_id = get_metadata( $meta_type, $id, $field['name'] );
7799
						}
7800
7801
						if ( is_array( $related_id ) && ! empty( $related_id ) ) {
7802
							foreach ( $related_id as $related ) {
7803
								if ( is_array( $related ) && ! empty( $related ) ) {
7804
									if ( isset( $related['id'] ) ) {
7805
										$related_ids[] = (int) $related['id'];
7806
									} else {
7807
										foreach ( $related as $r ) {
7808
											if ( isset( $related['id'] ) ) {
7809
												$related_ids[] = (int) $r['id'];
7810
											} else {
7811
												$related_ids[] = (int) $r;
7812
											}
7813
										}
7814
									}
7815
								} else {
7816
									$related_ids[] = (int) $related;
7817
								}
7818
							}
7819
						}
7820
					}
7821
				}
7822
7823
				if ( ! $no_conflict ) {
7824
					pods_no_conflict_off( ( 'term' === $meta_type ? 'taxonomy' : $meta_type ) );
7825
				}
7826
			}
7827
		}
7828
7829
		if ( is_array( $related_ids ) ) {
7830
			$related_ids = array_unique( array_filter( $related_ids ) );
7831
7832
			if ( 0 < $related_pick_limit && ! empty( $related_ids ) ) {
7833
				$related_ids = array_slice( $related_ids, 0, $related_pick_limit );
7834
			}
7835
		}
7836
		if ( 0 != $pod_id && 0 != $field_id && ! empty( $related_ids ) ) {
7837
			// Only cache if $pod_id and $field_id were passed
7838
			self::$related_item_cache[ $pod_id ][ $field_id ][ $idstring ] = $related_ids;
7839
		}
7840
7841
		return $related_ids;
7842
	}
7843
7844
	/**
7845
	 * Find related items related to an item
7846
	 *
7847
	 * @param int   $field_id The Field ID
0 ignored issues
show
Documentation introduced by
Should the type for parameter $field not be array|null? Also, consider making the array more specific, something like array<String>, or String[].

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive. In addition it looks for parameters that have the generic type array and suggests a stricter type like array<String>.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
7848
	 * @param int   $pod_id   The Pod ID
0 ignored issues
show
Documentation introduced by
Should the type for parameter $pod not be array|null? Also, consider making the array more specific, something like array<String>, or String[].

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive. In addition it looks for parameters that have the generic type array and suggests a stricter type like array<String>.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
7849
	 * @param int   $id       Item ID to get related IDs from
7850
	 * @param array $field    Field data array
7851
	 * @param array $pod      Pod data array
7852
	 *
7853
	 * @return array|bool
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use false|array.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
7854
	 *
7855
	 * @since 2.3
7856
	 *
7857
	 * @uses  pods_query()
7858
	 */
7859
	public function lookup_related_items_from( $field_id, $pod_id, $id, $field = null, $pod = null ) {
7860
7861
		$related_ids = false;
7862
7863
		$id = (int) $id;
7864
7865
		$tableless_field_types = PodsForm::tableless_field_types();
0 ignored issues
show
Comprehensibility Naming introduced by
The variable name $tableless_field_types exceeds the maximum configured length of 20.

Very long variable names usually make code harder to read. It is therefore recommended not to make variable names too verbose.

Loading history...
7866
7867
		if ( empty( $id ) || ! in_array( pods_v( 'type', $field ), $tableless_field_types ) ) {
7868
			return false;
7869
		}
7870
7871
		$related_pick_limit = 0;
7872
7873
		if ( ! empty( $field ) ) {
7874
			$options = (array) pods_var_raw( 'options', $field, $field, null, true );
7875
7876
			$related_pick_limit = (int) pods_v( 'pick_limit', $options, 0 );
7877
7878
			if ( 'single' === pods_var_raw( 'pick_format_type', $options ) ) {
7879
				$related_pick_limit = 1;
7880
			}
7881
		}
7882
7883
		if ( ! pods_tableless() ) {
7884
			$field_id  = (int) $field_id;
7885
			$sister_id = (int) pods_var_raw( 'sister_id', $field, 0 );
7886
7887
			$related_where = "
7888
                `field_id` = {$field_id}
7889
                AND `related_item_id` = {$id}
7890
            ";
7891
7892
			$sql = "
7893
                SELECT *
7894
                FROM `@wp_podsrel`
7895
                WHERE
7896
                    {$related_where}
7897
                ORDER BY `weight`
7898
            ";
7899
7900
			$relationships = pods_query( $sql );
7901
7902
			if ( ! empty( $relationships ) ) {
7903
				$related_ids = array();
7904
7905
				foreach ( $relationships as $relation ) {
7906
					if ( $field_id == $relation->field_id && ! in_array( $relation->item_id, $related_ids ) ) {
7907
						$related_ids[] = (int) $relation->item_id;
7908
					} elseif ( 0 < $sister_id && $field_id == $relation->related_field_id && ! in_array( $relation->related_item_id, $related_ids ) ) {
7909
						$related_ids[] = (int) $relation->related_item_id;
7910
					}
7911
				}
7912
			}
7913
		} else {
7914
			// @todo handle meta-based lookups
7915
			return false;
7916
7917
			if ( ! is_array( $pod ) ) {
0 ignored issues
show
Unused Code introduced by
if (!is_array($pod)) { ...o' => false), false); } does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
7918
				$pod = $this->load_pod( array( 'id' => $pod_id, 'table_info' => false ), false );
7919
			}
7920
7921
			if ( ! empty( $pod ) && in_array( $pod['type'], array(
7922
					'post_type',
7923
					'media',
7924
					'taxonomy',
7925
					'user',
7926
					'comment',
7927
					'settings'
7928
				) ) ) {
7929
				$related_ids = array();
7930
7931
				$meta_type = $pod['type'];
7932
7933
				if ( in_array( $meta_type, array( 'post_type', 'media' ) ) ) {
7934
					$meta_type = 'post';
7935
				} elseif ( 'taxonomy' === $meta_type ) {
7936
					$meta_type = 'term';
7937
				}
7938
7939
				$no_conflict = pods_no_conflict_check( ( 'term' === $meta_type ? 'taxonomy' : $meta_type ) );
7940
7941
				if ( ! $no_conflict ) {
7942
					pods_no_conflict_on( ( 'term' === $meta_type ? 'taxonomy' : $meta_type ) );
7943
				}
7944
7945
				if ( 'settings' === $meta_type ) {
7946
					$related_id = get_option( '_pods_' . $pod['name'] . '_' . $field['name'] );
7947
7948
					if ( empty( $related_id ) ) {
7949
						$related_id = get_option( $pod['name'] . '_' . $field['name'] );
7950
					}
7951
7952
					if ( is_array( $related_id ) && ! empty( $related_id ) ) {
7953
						foreach ( $related_id as $related ) {
7954
							if ( is_array( $related ) && ! empty( $related ) ) {
7955
								if ( isset( $related['id'] ) ) {
7956
									$related_ids[] = (int) $related['id'];
7957
								} else {
7958
									foreach ( $related as $r ) {
7959
										$related_ids[] = (int) $r;
7960
									}
7961
								}
7962
							} else {
7963
								$related_ids[] = (int) $related;
7964
							}
7965
						}
7966
					}
7967
				} else {
7968
					$related_id = get_metadata( $meta_type, $id, '_pods_' . $field['name'], true );
7969
7970
					if ( empty( $related_id ) ) {
7971
						$related_id = get_metadata( $meta_type, $id, $field['name'] );
7972
					}
7973
7974
					if ( is_array( $related_id ) && ! empty( $related_id ) ) {
7975
						foreach ( $related_id as $related ) {
7976
							if ( is_array( $related ) && ! empty( $related ) ) {
7977
								if ( isset( $related['id'] ) ) {
7978
									$related_ids[] = (int) $related['id'];
7979
								} else {
7980
									foreach ( $related as $r ) {
7981
										if ( isset( $related['id'] ) ) {
7982
											$related_ids[] = (int) $r['id'];
7983
										} else {
7984
											$related_ids[] = (int) $r;
7985
										}
7986
									}
7987
								}
7988
							} else {
7989
								$related_ids[] = (int) $related;
7990
							}
7991
						}
7992
					}
7993
				}
7994
7995
				if ( ! $no_conflict ) {
7996
					pods_no_conflict_off( ( 'term' === $meta_type ? 'taxonomy' : $meta_type ) );
7997
				}
7998
			}
7999
		}
8000
8001
		if ( is_array( $related_ids ) ) {
8002
			$related_ids = array_unique( array_filter( $related_ids ) );
8003
		}
8004
8005
		return $related_ids;
8006
	}
8007
8008
	/**
8009
	 *
8010
	 * Load the information about an objects MySQL table
8011
	 *
8012
	 * @param        $object_type
8013
	 * @param string $object The object to look for
8014
	 * @param null   $name   (optional) Name of the pod to load
8015
	 * @param array  $pod    (optional) Array with pod information
0 ignored issues
show
Documentation introduced by
Should the type for parameter $pod not be array|null? Also, consider making the array more specific, something like array<String>, or String[].

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive. In addition it looks for parameters that have the generic type array and suggests a stricter type like array<String>.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
8016
	 *
8017
	 * @return array
8018
	 *
8019
	 * @since 2.5
8020
	 */
8021
	public function get_table_info_load( $object_type, $object, $name = null, $pod = null ) {
8022
8023
		$info = array();
8024
8025
		if ( 'pod' === $object_type && null === $pod ) {
8026
			if ( empty( $name ) ) {
8027
				$prefix = 'pod-';
8028
8029
				// Make sure we actually have the prefix before trying anything with the name
8030
				if ( 0 === strpos( $object_type, $prefix ) ) {
8031
					$name = substr( $object_type, strlen( $prefix ), strlen( $object_type ) );
8032
				}
8033
			}
8034
8035
			if ( empty( $name ) && ! empty( $object ) ) {
8036
				$name = $object;
8037
			}
8038
8039
			$pod = $this->load_pod( array( 'name' => $name, 'table_info' => false ), false );
8040
8041
			if ( ! empty( $pod ) ) {
8042
				$object_type = $pod['type'];
8043
				$name        = $pod['name'];
8044
				$object      = $pod['object'];
8045
8046
				$info['pod'] = $pod;
8047
			}
8048
		} elseif ( null === $pod ) {
8049
			if ( empty( $name ) ) {
8050
				$prefix = $object_type . '-';
8051
8052
				// Make sure we actually have the prefix before trying anything with the name
8053
				if ( 0 === strpos( $object_type, $prefix ) ) {
8054
					$name = substr( $object_type, strlen( $prefix ), strlen( $object_type ) );
8055
				}
8056
			}
8057
8058
			if ( empty( $name ) && ! empty( $object ) ) {
8059
				$name = $object;
8060
			}
8061
8062
			if ( ! empty( $name ) ) {
8063
				$pod = $this->load_pod( array( 'name' => $name, 'table_info' => false ), false );
8064
8065
				if ( ! empty( $pod ) && ( null === $object_type || $object_type == $pod['type'] ) ) {
8066
					$object_type = $pod['type'];
8067
					$name        = $pod['name'];
8068
					$object      = $pod['object'];
8069
8070
					$info['pod'] = $pod;
8071
				}
8072
			}
8073
		} elseif ( ! empty( $pod ) ) {
8074
			$info['pod'] = $pod;
8075
		}
8076
8077
		if ( 0 === strpos( $object_type, 'pod' ) ) {
8078
			if ( empty( $name ) ) {
8079
				$prefix = 'pod-';
8080
8081
				// Make sure we actually have the prefix before trying anything with the name
8082
				if ( 0 === strpos( $object_type, $prefix ) ) {
8083
					$name = substr( $object_type, strlen( $prefix ), strlen( $object_type ) );
8084
				}
8085
			}
8086
8087
			$info['type'] = 'pod';
8088
			global $wpdb;
8089
8090
			$info['table'] = $info['meta_table'] = $wpdb->prefix . 'pods_' . ( empty( $object ) ? $name : $object );
8091
8092
			if ( is_array( $info['pod'] ) && 'pod' === pods_v( 'type', $info['pod'] ) ) {
8093
				$info['pod_field_index'] = $info['field_index'] = $info['meta_field_index'] = $info['meta_field_value'] = pods_v( 'pod_index', $info['pod']['options'], 'id', true );
8094
8095
				$slug_field = get_posts( array(
8096
					'post_type'      => '_pods_field',
8097
					'posts_per_page' => 1,
8098
					'nopaging'       => true,
8099
					'post_parent'    => $info['pod']['id'],
8100
					'orderby'        => 'menu_order',
8101
					'order'          => 'ASC',
8102
					'meta_query'     => array(
8103
						array(
8104
							'key'   => 'type',
8105
							'value' => 'slug',
8106
						)
8107
					)
8108
				) );
8109
8110
				if ( ! empty( $slug_field[0] ) ) {
8111
					$slug_field = $slug_field[0];
8112
8113
					$info['field_slug'] = $info['pod_field_slug'] = $slug_field->post_name;
8114
				}
8115
8116
				if ( 1 == pods_v( 'hierarchical', $info['pod']['options'], 0 ) ) {
8117
					$parent_field = pods_v( 'pod_parent', $info['pod']['options'], 'id', true );
8118
8119
					if ( ! empty( $parent_field ) && isset( $info['pod']['fields'][ $parent_field ] ) ) {
8120
						$info['object_hierarchical'] = true;
8121
8122
						$info['pod_field_parent']    = $info['field_parent'] = $parent_field . '_select';
8123
						$info['field_parent_select'] = '`' . $parent_field . '`.`id` AS `' . $info['field_parent'] . '`';
8124
					}
8125
				}
8126
			}
8127
		}
8128
8129
		return $info;
8130
	}
8131
8132
	/**
8133
	 * Get information about an objects MySQL table
8134
	 *
8135
	 * @param string $object_type
8136
	 * @param string $object The object to look for
8137
	 * @param null   $name   (optional) Name of the pod to load
8138
	 * @param array  $pod    (optional) Array with pod information
0 ignored issues
show
Documentation introduced by
Should the type for parameter $pod not be array|null? Also, consider making the array more specific, something like array<String>, or String[].

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive. In addition it looks for parameters that have the generic type array and suggests a stricter type like array<String>.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
8139
	 * @param array  $field  (optional) Array with field information
0 ignored issues
show
Documentation introduced by
Should the type for parameter $field not be array|null? Also, consider making the array more specific, something like array<String>, or String[].

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive. In addition it looks for parameters that have the generic type array and suggests a stricter type like array<String>.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
8140
	 *
8141
	 * @return array|bool
8142
	 *
8143
	 * @since 2.0
8144
	 */
8145
	public function get_table_info( $object_type, $object, $name = null, $pod = null, $field = null ) {
8146
8147
		/**
8148
		 * @var $wpdb                         wpdb
8149
		 * @var $sitepress                    SitePress
8150
		 * @var $polylang                     object
8151
		 */
8152
		/*
8153
	     * @todo wpml-comp Remove global object usage
8154
	     */
8155
		global $wpdb, $sitepress, $polylang;
8156
8157
		// @todo Handle $object arrays for Post Types, Taxonomies, Comments (table pulled from first object in array)
8158
8159
		$info = array(
8160
			//'select' => '`t`.*',
8161
			'object_type'         => $object_type,
8162
			'type'                => null,
8163
			'object_name'         => $object,
8164
			'object_hierarchical' => false,
8165
8166
			'table'      => $object,
8167
			'meta_table' => $object,
8168
			'pod_table'  => $wpdb->prefix . 'pods_' . ( empty( $object ) ? $name : $object ),
8169
8170
			'field_id'            => 'id',
8171
			'field_index'         => 'name',
8172
			'field_slug'          => null,
8173
			'field_type'          => null,
8174
			'field_parent'        => null,
8175
			'field_parent_select' => null,
8176
8177
			'meta_field_id'    => 'id',
8178
			'meta_field_index' => 'name',
8179
			'meta_field_value' => 'name',
8180
8181
			'pod_field_id'     => 'id',
8182
			'pod_field_index'  => 'name',
8183
			'pod_field_slug'   => null,
8184
			'pod_field_parent' => null,
8185
8186
			'join' => array(),
8187
8188
			'where'         => null,
8189
			'where_default' => null,
8190
8191
			'orderby' => null,
8192
8193
			'pod'     => null,
8194
			'recurse' => false
8195
		);
8196
8197
		if ( empty( $object_type ) ) {
8198
			$object_type = 'post_type';
8199
			$object      = 'post';
8200
		} elseif ( empty( $object ) && in_array( $object_type, array( 'user', 'media', 'comment' ) ) ) {
8201
			$object = $object_type;
8202
		}
8203
8204
		$pod_name = $pod;
8205
8206
		if ( is_array( $pod_name ) ) {
8207
			$pod_name = pods_var_raw( 'name', $pod_name, ( version_compare( PHP_VERSION, '5.4.0', '>=' ) ? json_encode( $pod_name, JSON_UNESCAPED_UNICODE ) : json_encode( $pod_name ) ), null, true );
0 ignored issues
show
Unused Code introduced by
The call to json_encode() has too many arguments starting with JSON_UNESCAPED_UNICODE.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
8208
		} else {
8209
			$pod_name = $object;
8210
		}
8211
8212
		$field_name = $field;
8213
8214
		if ( is_array( $field_name ) ) {
8215
			$field_name = pods_var_raw( 'name', $field_name, ( version_compare( PHP_VERSION, '5.4.0', '>=' ) ? json_encode( $pod_name, JSON_UNESCAPED_UNICODE ) : json_encode( $field_name ) ), null, true );
0 ignored issues
show
Unused Code introduced by
The call to json_encode() has too many arguments starting with JSON_UNESCAPED_UNICODE.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
8216
		}
8217
8218
		$transient = 'pods_' . $wpdb->prefix . '_get_table_info_' . md5( $object_type . '_object_' . $object . '_name_' . $name . '_pod_' . $pod_name . '_field_' . $field_name );
8219
8220
		$current_language      = false;
8221
		$current_language_t_id = $current_language_tt_id = 0;
0 ignored issues
show
Comprehensibility Naming introduced by
The variable name $current_language_t_id exceeds the maximum configured length of 20.

Very long variable names usually make code harder to read. It is therefore recommended not to make variable names too verbose.

Loading history...
Comprehensibility Naming introduced by
The variable name $current_language_tt_id exceeds the maximum configured length of 20.

Very long variable names usually make code harder to read. It is therefore recommended not to make variable names too verbose.

Loading history...
8222
8223
		// Get current language data
8224
		$lang_data = PodsInit::$i18n->get_current_language_data();
8225
8226
		if ( $lang_data ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $lang_data of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
8227
			if ( ! empty( $lang_data['language'] ) ) {
8228
				$current_language = $lang_data['language'];
8229
			}
8230
8231
			if ( ! empty( $lang_data['t_id'] ) ) {
8232
				$current_language_t_id = $lang_data['t_id'];
8233
			}
8234
8235
			if ( ! empty( $lang_data['tt_id'] ) ) {
8236
				$current_language_tt_id = $lang_data['tt_id'];
8237
			}
8238
8239
			if ( ! empty( $lang_data['tl_t_id'] ) ) {
8240
				$current_language_tl_t_id = $lang_data['tl_t_id'];
0 ignored issues
show
Comprehensibility Naming introduced by
The variable name $current_language_tl_t_id exceeds the maximum configured length of 20.

Very long variable names usually make code harder to read. It is therefore recommended not to make variable names too verbose.

Loading history...
8241
			}
8242
8243
			if ( ! empty( $lang_data['tl_tt_id'] ) ) {
8244
				$current_language_tl_tt_id = $lang_data['tl_tt_id'];
0 ignored issues
show
Comprehensibility Naming introduced by
The variable name $current_language_tl_tt_id exceeds the maximum configured length of 20.

Very long variable names usually make code harder to read. It is therefore recommended not to make variable names too verbose.

Loading history...
8245
			}
8246
		}
8247
8248
		if ( ! empty( $current_language ) ) {
8249
			$transient = 'pods_' . $wpdb->prefix . '_get_table_info_' . $current_language . '_' . md5( $object_type . '_object_' . $object . '_name_' . $name . '_pod_' . $pod_name . '_field_' . $field_name );
8250
		}
8251
8252
		$_info = false;
8253
8254
		if ( isset( self::$table_info_cache[ $transient ] ) ) {
8255
			// Prefer info from the object internal cache
8256
			$_info = self::$table_info_cache[ $transient ];
8257
		} elseif ( pods_api_cache() ) {
8258
			$_info = pods_transient_get( $transient );
8259
			if ( false === $_info && ! did_action( 'init' ) ) {
8260
				$_info = pods_transient_get( $transient . '_pre_init' );
8261
			}
8262
		}
8263
8264
		if ( false !== $_info && is_array( $_info ) ) {
8265
			// Data was cached, use that
8266
			$info = $_info;
8267
		} else {
8268
			// Data not cached, load it up
8269
			$_info = $this->get_table_info_load( $object_type, $object, $name, $pod );
8270
			if ( isset( $_info['type'] ) ) {
8271
				// Allow function to override $object_type
8272
				$object_type = $_info['type'];
8273
			}
8274
			$info = array_merge( $info, $_info );
8275
		}
8276
8277
		if ( 0 === strpos( $object_type, 'post_type' ) || 'media' === $object_type || in_array( pods_var_raw( 'type', $info['pod'] ), array(
8278
				'post_type',
8279
				'media'
8280
			) ) ) {
8281
			$info['table']      = $wpdb->posts;
8282
			$info['meta_table'] = $wpdb->postmeta;
8283
8284
			$info['field_id']            = 'ID';
8285
			$info['field_index']         = 'post_title';
8286
			$info['field_slug']          = 'post_name';
8287
			$info['field_type']          = 'post_type';
8288
			$info['field_parent']        = 'post_parent';
8289
			$info['field_parent_select'] = '`t`.`' . $info['field_parent'] . '`';
8290
8291
			$info['meta_field_id']    = 'post_id';
8292
			$info['meta_field_index'] = 'meta_key';
8293
			$info['meta_field_value'] = 'meta_value';
8294
8295
			if ( 'media' === $object_type ) {
8296
				$object = 'attachment';
8297
			}
8298
8299
			if ( empty( $name ) ) {
8300
				$prefix = 'post_type-';
8301
8302
				// Make sure we actually have the prefix before trying anything with the name
8303
				if ( 0 === strpos( $object_type, $prefix ) ) {
8304
					$name = substr( $object_type, strlen( $prefix ), strlen( $object_type ) );
8305
				}
8306
			}
8307
8308
			if ( 'media' !== $object_type ) {
8309
				$object_type = 'post_type';
8310
			}
8311
8312
			$post_type = pods_sanitize( ( empty( $object ) ? $name : $object ) );
8313
8314
			if ( 'attachment' === $post_type || 'media' === $object_type ) {
8315
				$info['pod_table'] = $wpdb->prefix . 'pods_media';
8316
			} else {
8317
				$info['pod_table'] = $wpdb->prefix . 'pods_' . pods_clean_name( $post_type, true, false );
8318
			}
8319
8320
			$post_type_object = get_post_type_object( $post_type );
8321
8322
			if ( is_object( $post_type_object ) && $post_type_object->hierarchical ) {
8323
				$info['object_hierarchical'] = true;
8324
			}
8325
8326
			// Post Status default
8327
			$post_status = array( 'publish' );
8328
8329
			// Pick field post_status option
8330
			if ( ! empty( $field['options']['pick_post_status'] ) ) {
8331
				$post_status = (array) $field['options']['pick_post_status'];
8332
			} elseif ( ! empty( $field['pick_post_status'] ) ) {
8333
				$post_status = (array) $field['pick_post_status'];
8334
			}
8335
8336
			/**
8337
			 * Default Post Status to query for.
8338
			 *
8339
			 * Use to change "default" post status from publish to any other status or statuses.
8340
			 *
8341
			 * @param  array  $post_status List of post statuses. Default is 'publish' or field setting (if available).
8342
			 * @param  string $post_type   Post type of current object.
8343
			 * @param  array  $info        Array of information about the object.
8344
			 * @param  string $object      Type of object.
8345
			 * @param  string $name        Name of pod to load.
8346
			 * @param  array  $pod         Array with Pod information. Result of PodsAPI::load_pod().
8347
			 * @param  array  $field       Array with field information.
8348
			 *
8349
			 * @since unknown
8350
			 */
8351
			$post_status = apply_filters( 'pods_api_get_table_info_default_post_status', $post_status, $post_type, $info, $object_type, $object, $name, $pod, $field );
8352
8353
			$info['where'] = array(
8354
				//'post_status' => '`t`.`post_status` IN ( "inherit", "publish" )', // @todo Figure out what statuses Attachments can be
8355
				'post_type' => '`t`.`' . $info['field_type'] . '` = "' . $post_type . '"'
8356
			);
8357
8358
			if ( 'post_type' === $object_type ) {
8359
				$info['where_default'] = '`t`.`post_status` IN ( "' . implode( '", "', $post_status ) . '" )';
8360
			}
8361
8362
			$info['orderby'] = '`t`.`menu_order`, `t`.`' . $info['field_index'] . '`, `t`.`post_date`';
8363
8364
			/*
8365
             * @todo wpml-comp Check if WPML filters can be applied afterwards
8366
             */
8367
			// WPML support
8368
			if ( did_action( 'wpml_loaded' ) && ! empty( $current_language ) && apply_filters( 'wpml_is_translated_post_type', false, $post_type ) && apply_filters( 'wpml_setting', true, 'auto_adjust_ids' ) ) {
8369
				$info['join']['wpml_translations'] = "
8370
                        LEFT JOIN `{$wpdb->prefix}icl_translations` AS `wpml_translations`
8371
                            ON `wpml_translations`.`element_id` = `t`.`ID`
8372
                                AND `wpml_translations`.`element_type` = 'post_{$post_type}'
8373
                                AND `wpml_translations`.`language_code` = '{$current_language}'
8374
                    ";
8375
8376
				$info['join']['wpml_languages'] = "
8377
                        LEFT JOIN `{$wpdb->prefix}icl_languages` AS `wpml_languages`
8378
                            ON `wpml_languages`.`code` = `wpml_translations`.`language_code` AND `wpml_languages`.`active` = 1
8379
                    ";
8380
8381
				$info['where']['wpml_languages'] = "`wpml_languages`.`code` IS NOT NULL";
8382
			} elseif ( ( function_exists( 'PLL' ) || is_object( $polylang ) ) && ! empty( $current_language ) && function_exists( 'pll_is_translated_post_type' ) && pll_is_translated_post_type( $post_type ) ) {
8383
				// Polylang support
8384
				$info['join']['polylang_languages'] = "
8385
                        LEFT JOIN `{$wpdb->term_relationships}` AS `polylang_languages`
8386
                            ON `polylang_languages`.`object_id` = `t`.`ID`
8387
                                AND `polylang_languages`.`term_taxonomy_id` = {$current_language_tt_id}
8388
                    ";
8389
8390
				$info['where']['polylang_languages'] = "`polylang_languages`.`object_id` IS NOT NULL";
8391
			}
8392
8393
			$info['object_fields'] = $this->get_wp_object_fields( $object_type, $info['pod'] );
8394
		} elseif ( 0 === strpos( $object_type, 'taxonomy' ) || in_array( $object_type, array(
8395
				'nav_menu',
8396
				'post_format'
8397
			) ) || 'taxonomy' === pods_var_raw( 'type', $info['pod'] ) ) {
8398
			$info['table'] = $info['meta_table'] = $wpdb->terms;
8399
8400
			$info['join']['tt']          = "LEFT JOIN `{$wpdb->term_taxonomy}` AS `tt` ON `tt`.`term_id` = `t`.`term_id`";
8401
			$info['join']['tr']          = "LEFT JOIN `{$wpdb->term_relationships}` AS `tr` ON `tr`.`term_taxonomy_id` = `tt`.`term_taxonomy_id`";
8402
			$info['field_id']            = $info['meta_field_id'] = 'term_id';
8403
			$info['field_index']         = $info['meta_field_index'] = $info['meta_field_value'] = 'name';
8404
			$info['field_slug']          = 'slug';
8405
			$info['field_type']          = 'taxonomy';
8406
			$info['field_parent']        = 'parent';
8407
			$info['field_parent_select'] = '`tt`.`' . $info['field_parent'] . '`';
8408
8409
			if ( ! empty( $wpdb->termmeta ) ) {
8410
				$info['meta_table'] = $wpdb->termmeta;
8411
8412
				$info['meta_field_id']    = 'term_id';
8413
				$info['meta_field_index'] = 'meta_key';
8414
				$info['meta_field_value'] = 'meta_value';
8415
			}
8416
8417
			if ( 'nav_menu' === $object_type ) {
8418
				$object = 'nav_menu';
8419
			} elseif ( 'post_format' === $object_type ) {
8420
				$object = 'post_format';
8421
			}
8422
8423
			if ( empty( $name ) ) {
8424
				$prefix = 'taxonomy-';
8425
8426
				// Make sure we actually have the prefix before trying anything with the name
8427
				if ( 0 === strpos( $object_type, $prefix ) ) {
8428
					$name = substr( $object_type, strlen( $prefix ), strlen( $object_type ) );
8429
				}
8430
			}
8431
8432
			if ( ! in_array( $object_type, array( 'nav_menu', 'post_format' ) ) ) {
8433
				$object_type = 'taxonomy';
8434
			}
8435
8436
			$taxonomy = pods_sanitize( ( empty( $object ) ? $name : $object ) );
8437
8438
			$info['pod_table'] = $wpdb->prefix . 'pods_' . pods_clean_name( $taxonomy, true, false );
8439
8440
			$taxonomy_object = get_taxonomy( $taxonomy );
8441
8442
			if ( is_object( $taxonomy_object ) && $taxonomy_object->hierarchical ) {
8443
				$info['object_hierarchical'] = true;
8444
			}
8445
8446
			$info['where'] = array(
8447
				'tt.taxonomy' => '`tt`.`' . $info['field_type'] . '` = "' . $taxonomy . '"'
8448
			);
8449
8450
			/*
8451
             * @todo wpml-comp WPML API call for is_translated_taxononomy
8452
             * @todo wpml-comp Check if WPML filters can be applied afterwards
8453
             */
8454
			// WPML Support
8455
			if ( is_object( $sitepress ) && ! empty( $current_language ) && $sitepress->is_translated_taxonomy( $taxonomy ) && apply_filters( 'wpml_setting', true, 'auto_adjust_ids' ) ) {
8456
				$info['join']['wpml_translations'] = "
8457
                        LEFT JOIN `{$wpdb->prefix}icl_translations` AS `wpml_translations`
8458
                            ON `wpml_translations`.`element_id` = `tt`.`term_taxonomy_id`
8459
                                AND `wpml_translations`.`element_type` = 'tax_{$taxonomy}'
8460
                                AND `wpml_translations`.`language_code` = '{$current_language}'
8461
                    ";
8462
8463
				$info['join']['wpml_languages'] = "
8464
                        LEFT JOIN `{$wpdb->prefix}icl_languages` AS `wpml_languages`
8465
                            ON `wpml_languages`.`code` = `wpml_translations`.`language_code` AND `wpml_languages`.`active` = 1
8466
                    ";
8467
8468
				$info['where']['wpml_languages'] = "`wpml_languages`.`code` IS NOT NULL";
8469
			} elseif ( ( function_exists( 'PLL' ) || is_object( $polylang ) ) && ! empty( $current_language ) && ! empty( $current_language_tl_tt_id ) && function_exists( 'pll_is_translated_taxonomy' ) && pll_is_translated_taxonomy( $taxonomy ) ) {
8470
				// Polylang support
8471
				$info['join']['polylang_languages'] = "
8472
					LEFT JOIN `{$wpdb->term_relationships}` AS `polylang_languages`
8473
						ON `polylang_languages`.`object_id` = `t`.`term_id`
8474
							AND `polylang_languages`.`term_taxonomy_id` = {$current_language_tl_tt_id}
8475
				";
8476
8477
				$info['where']['polylang_languages'] = "`polylang_languages`.`object_id` IS NOT NULL";
8478
			}
8479
8480
			$info['object_fields'] = $this->get_wp_object_fields( $object_type, $info['pod'] );
8481
		} elseif ( 'user' === $object_type || 'user' === pods_var_raw( 'type', $info['pod'] ) ) {
8482
			$info['table']      = $wpdb->users;
8483
			$info['meta_table'] = $wpdb->usermeta;
8484
			$info['pod_table']  = $wpdb->prefix . 'pods_user';
8485
8486
			$info['field_id']    = 'ID';
8487
			$info['field_index'] = 'display_name';
8488
			$info['field_slug']  = 'user_nicename';
8489
8490
			$info['meta_field_id']    = 'user_id';
8491
			$info['meta_field_index'] = 'meta_key';
8492
			$info['meta_field_value'] = 'meta_value';
8493
8494
			$info['where'] = array(
8495
				'user_status' => '`t`.`user_status` = 0'
8496
			);
8497
8498
			$info['object_fields'] = $this->get_wp_object_fields( $object_type, $info['pod'] );
8499
		} elseif ( 'comment' === $object_type || 'comment' === pods_var_raw( 'type', $info['pod'] ) ) {
8500
			//$info[ 'object_hierarchical' ] = true;
8501
8502
			$info['table']      = $wpdb->comments;
8503
			$info['meta_table'] = $wpdb->commentmeta;
8504
			$info['pod_table']  = $wpdb->prefix . 'pods_comment';
8505
8506
			$info['field_id']            = 'comment_ID';
8507
			$info['field_index']         = 'comment_date';
8508
			$info['field_type']          = 'comment_type';
8509
			$info['field_parent']        = 'comment_parent';
8510
			$info['field_parent_select'] = '`t`.`' . $info['field_parent'] . '`';
8511
8512
			$info['meta_field_id']    = 'comment_id';
8513
			$info['meta_field_index'] = 'meta_key';
8514
			$info['meta_field_value'] = 'meta_value';
8515
8516
			$object = 'comment';
8517
8518
			$comment_type = ( empty( $object ) ? $name : $object );
8519
8520
			$comment_type_clause = '`t`.`' . $info['field_type'] . '` = "' . $comment_type . '"';
8521
8522
			if ( 'comment' === $comment_type ) {
8523
				$comment_type_clause = '( ' . $comment_type_clause . ' OR `t`.`' . $info['field_type'] . '` = "" )';
8524
			}
8525
8526
			$info['where'] = array(
8527
				'comment_approved' => '`t`.`comment_approved` = 1',
8528
				'comment_type'     => $comment_type_clause
8529
			);
8530
8531
			$info['orderby'] = '`t`.`' . $info['field_index'] . '` DESC, `t`.`' . $info['field_id'] . '`';
8532
		} elseif ( in_array( $object_type, array(
8533
				'option',
8534
				'settings'
8535
			) ) || 'settings' === pods_var_raw( 'type', $info['pod'] ) ) {
8536
			$info['table']      = $wpdb->options;
8537
			$info['meta_table'] = $wpdb->options;
8538
8539
			$info['field_id']    = 'option_id';
8540
			$info['field_index'] = 'option_name';
8541
8542
			$info['meta_field_id']    = 'option_id';
8543
			$info['meta_field_index'] = 'option_name';
8544
			$info['meta_field_value'] = 'option_value';
8545
8546
			$info['orderby'] = '`t`.`' . $info['field_index'] . '` ASC';
8547
		} elseif ( is_multisite() && ( in_array( $object_type, array(
8548
					'site_option',
8549
					'site_settings'
8550
				) ) || 'site_settings' === pods_var_raw( 'type', $info['pod'] ) ) ) {
8551
			$info['table']      = $wpdb->sitemeta;
8552
			$info['meta_table'] = $wpdb->sitemeta;
8553
8554
			$info['field_id']    = 'site_id';
8555
			$info['field_index'] = 'meta_key';
8556
8557
			$info['meta_field_id']    = 'site_id';
8558
			$info['meta_field_index'] = 'meta_key';
8559
			$info['meta_field_value'] = 'meta_value';
8560
8561
			$info['orderby'] = '`t`.`' . $info['field_index'] . '` ASC';
8562
		} elseif ( is_multisite() && 'network' === $object_type ) { // Network = Site
8563
			$info['table']      = $wpdb->site;
8564
			$info['meta_table'] = $wpdb->sitemeta;
8565
8566
			$info['field_id']    = 'id';
8567
			$info['field_index'] = 'domain';
8568
8569
			$info['meta_field_id']    = 'site_id';
8570
			$info['meta_field_index'] = 'meta_key';
8571
			$info['meta_field_value'] = 'meta_value';
8572
8573
			$info['orderby'] = '`t`.`' . $info['field_index'] . '` ASC, `t`.`path` ASC, `t`.`' . $info['field_id'] . '`';
8574
		} elseif ( is_multisite() && 'site' === $object_type ) { // Site = Blog
8575
			$info['table'] = $wpdb->blogs;
8576
8577
			$info['field_id']    = 'blog_id';
8578
			$info['field_index'] = 'domain';
8579
			$info['field_type']  = 'site_id';
8580
8581
			$info['where'] = array(
8582
				'archived' => '`t`.`archived` = 0',
8583
				'spam'     => '`t`.`spam` = 0',
8584
				'deleted'  => '`t`.`deleted` = 0',
8585
				'site_id'  => '`t`.`' . $info['field_type'] . '` = ' . (int) get_current_site()->id
8586
			);
8587
8588
			$info['orderby'] = '`t`.`' . $info['field_index'] . '` ASC, `t`.`path` ASC, `t`.`' . $info['field_id'] . '`';
8589
		} elseif ( 'table' === $object_type || 'table' === pods_var_raw( 'type', $info['pod'] ) ) {
8590
			$info['table']     = ( empty( $object ) ? $name : $object );
8591
			$info['pod_table'] = $wpdb->prefix . 'pods_' . $info['table'];
8592
8593
			if ( ! empty( $field ) && is_array( $field ) ) {
8594
				$info['table']       = pods_var_raw( 'pick_table', pods_var_raw( 'options', $field, $field ) );
8595
				$info['field_id']    = pods_var_raw( 'pick_table_id', pods_var_raw( 'options', $field, $field ) );
8596
				$info['field_index'] = $info['meta_field_index'] = $info['meta_field_value'] = pods_var_raw( 'pick_table_index', pods_var_raw( 'options', $field, $field ) );
8597
			}
8598
		}
8599
8600
		$info['table']      = pods_clean_name( $info['table'], false, false );
8601
		$info['meta_table'] = pods_clean_name( $info['meta_table'], false, false );
8602
		$info['pod_table']  = pods_clean_name( $info['pod_table'], false, false );
8603
8604
		$info['field_id']    = pods_clean_name( $info['field_id'], false, false );
8605
		$info['field_index'] = pods_clean_name( $info['field_index'], false, false );
8606
		$info['field_slug']  = pods_clean_name( $info['field_slug'], false, false );
8607
8608
		$info['meta_field_id']    = pods_clean_name( $info['meta_field_id'], false, false );
8609
		$info['meta_field_index'] = pods_clean_name( $info['meta_field_index'], false, false );
8610
		$info['meta_field_value'] = pods_clean_name( $info['meta_field_value'], false, false );
8611
8612
		if ( empty( $info['orderby'] ) ) {
8613
			$info['orderby'] = '`t`.`' . $info['field_index'] . '`, `t`.`' . $info['field_id'] . '`';
8614
		}
8615
8616
		if ( 'table' === pods_var_raw( 'storage', $info['pod'] ) && ! in_array( $object_type, array(
8617
				'pod',
8618
				'table'
8619
			) ) ) {
8620
			$info['join']['d'] = 'LEFT JOIN `' . $info['pod_table'] . '` AS `d` ON `d`.`id` = `t`.`' . $info['field_id'] . '`';
8621
			//$info[ 'select' ] .= ', `d`.*';
8622
		}
8623
8624
		if ( ! empty( $info['pod'] ) && is_array( $info['pod'] ) ) {
8625
			$info['recurse'] = true;
8626
		}
8627
8628
		$info['type']        = $object_type;
8629
		$info['object_name'] = $object;
8630
8631
		if ( pods_api_cache() ) {
8632
			if ( ! did_action( 'init' ) ) {
8633
				$transient .= '_pre_init';
8634
			}
8635
			pods_transient_set( $transient, $info );
8636
		}
8637
8638
		self::$table_info_cache[ $transient ] = apply_filters( 'pods_api_get_table_info', $info, $object_type, $object, $name, $pod, $field, $this );
8639
8640
		return self::$table_info_cache[ $transient ];
8641
	}
8642
8643
	/**
8644
	 * Export a package
8645
	 *
8646
	 * $params['pods'] array Pod Type IDs to export
8647
	 * $params['templates'] array Template IDs to export
8648
	 * $params['pages'] array Pod Page IDs to export
8649
	 * $params['helpers'] array Helper IDs to export
8650
	 *
8651
	 * @param array $params An associative array of parameters
8652
	 *
8653
	 * @return array|bool
8654
	 *
8655
	 * @since      1.9.0
8656
	 * @deprecated 2.0
8657
	 */
8658
	public function export_package( $params ) {
8659
8660
		if ( class_exists( 'Pods_Migrate_Packages' ) ) {
8661
			return Pods_Migrate_Packages::export( $params );
8662
		}
8663
8664
		return false;
8665
	}
8666
8667
	/**
8668
	 * Replace an existing package
8669
	 *
8670
	 * @param mixed $data (optional) An associative array containing a package, or the json encoded package
8671
	 *
8672
	 * @return bool
0 ignored issues
show
Documentation introduced by
Should the return type not be array|boolean? Also, consider making the array more specific, something like array<String>, or String[].

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

If the return type contains the type array, this check recommends the use of a more specific type like String[] or array<String>.

Loading history...
8673
	 *
8674
	 * @since      1.9.8
8675
	 * @deprecated 2.0
8676
	 */
8677
	public function replace_package( $data = false ) {
8678
8679
		return $this->import_package( $data, true );
0 ignored issues
show
Deprecated Code introduced by
The method PodsAPI::import_package() has been deprecated with message: 2.0

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
8680
	}
8681
8682
	/**
8683
	 * Import a package
8684
	 *
8685
	 * @param mixed $data    (optional) An associative array containing a package, or the json encoded package
8686
	 * @param bool  $replace (optional) Replace existing items when found
8687
	 *
8688
	 * @return bool
0 ignored issues
show
Documentation introduced by
Should the return type not be array|boolean? Also, consider making the array more specific, something like array<String>, or String[].

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

If the return type contains the type array, this check recommends the use of a more specific type like String[] or array<String>.

Loading history...
8689
	 *
8690
	 * @since      1.9.0
8691
	 * @deprecated 2.0
8692
	 */
8693
	public function import_package( $data = false, $replace = false ) {
8694
8695
		if ( class_exists( 'Pods_Migrate_Packages' ) ) {
8696
			return Pods_Migrate_Packages::import( $data, $replace );
8697
		}
8698
8699
		return false;
8700
	}
8701
8702
	/**
8703
	 * Validate a package
8704
	 *
8705
	 * @param array|string $data   (optional) An associative array containing a package, or the json encoded package
0 ignored issues
show
Documentation introduced by
Should the type for parameter $data not be false|array|string? Also, consider making the array more specific, something like array<String>, or String[].

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive. In addition it looks for parameters that have the generic type array and suggests a stricter type like array<String>.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
8706
	 * @param bool         $output (optional)
8707
	 *
8708
	 * @return array|bool
8709
	 *
8710
	 * @since      1.9.0
8711
	 * @deprecated 2.0
8712
	 */
8713
	public function validate_package( $data = false, $output = false ) {
8714
8715
		return true;
8716
	}
8717
8718
	/**
8719
	 * Import data from an array or a CSV file.
8720
	 *
8721
	 * @param mixed  $import_data  PHP associative array or CSV input
8722
	 * @param bool   $numeric_mode Use IDs instead of the name field when matching
8723
	 * @param string $format       Format of import data, options are php or csv
0 ignored issues
show
Documentation introduced by
Should the type for parameter $format not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
8724
	 *
8725
	 * @return array IDs of imported items
8726
	 *
8727
	 * @since 1.7.1
8728
	 * @todo  This needs some love and use of table_info etc for relationships
8729
	 */
8730
	public function import( $import_data, $numeric_mode = false, $format = null ) {
8731
8732
		/**
8733
		 * @var $wpdb wpdb
8734
		 */
8735
		global $wpdb;
8736
8737
		if ( null === $format && null !== $this->format ) {
0 ignored issues
show
Deprecated Code introduced by
The property PodsAPI::$format has been deprecated with message: 2.0

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
8738
			$format = $this->format;
0 ignored issues
show
Deprecated Code introduced by
The property PodsAPI::$format has been deprecated with message: 2.0

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
8739
		}
8740
8741
		if ( 'csv' === $format && ! is_array( $import_data ) ) {
8742
			$data = pods_migrate( 'sv', ',' )->parse( $import_data );
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $data is correct as pods_migrate('sv', ',')->parse($import_data) (which targets PodsMigrate::parse()) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
8743
8744
			$import_data = $data['items'];
8745
		}
8746
8747
		pods_query( "SET NAMES utf8" );
8748
		pods_query( "SET CHARACTER SET utf8" );
8749
8750
		// Loop through the array of items
8751
		$ids = array();
8752
8753
		// Test to see if it's an array of arrays
8754
		if ( ! is_array( @current( $import_data ) ) ) {
8755
			$import_data = array( $import_data );
8756
		}
8757
8758
		$pod = $this->load_pod( array( 'name' => $this->pod ) );
8759
8760
		if ( false === $pod ) {
8761
			return pods_error( __( 'Pod not found', 'pods' ), $this );
8762
		}
8763
8764
		$fields = array_merge( $pod['fields'], $pod['object_fields'] );
8765
8766
		$simple_tableless_objects = PodsForm::simple_tableless_objects();
0 ignored issues
show
Comprehensibility Naming introduced by
The variable name $simple_tableless_objects exceeds the maximum configured length of 20.

Very long variable names usually make code harder to read. It is therefore recommended not to make variable names too verbose.

Loading history...
8767
8768
		foreach ( $import_data as $key => $data_row ) {
8769
			$data = array();
8770
8771
			// Loop through each field (use $fields so only valid fields get parsed)
8772
			foreach ( $fields as $field_name => $field_data ) {
8773
				if ( ! isset( $data_row[ $field_name ] ) && ! isset( $data_row[ $field_data['label'] ] ) ) {
8774
					continue;
8775
				}
8776
8777
				$field_id    = $field_data['id'];
8778
				$type        = $field_data['type'];
8779
				$pick_object = isset( $field_data['pick_object'] ) ? $field_data['pick_object'] : '';
8780
				$pick_val    = isset( $field_data['pick_val'] ) ? $field_data['pick_val'] : '';
8781
8782
				if ( isset( $data_row[ $field_name ] ) ) {
8783
					$field_value = $data_row[ $field_name ];
8784
				} else {
8785
					$field_value = $data_row[ $field_data['label'] ];
8786
				}
8787
8788
				if ( null !== $field_value && false !== $field_value && '' !== $field_value ) {
8789
					if ( 'pick' === $type || in_array( $type, PodsForm::file_field_types() ) ) {
8790
						$field_values = is_array( $field_value ) ? $field_value : array( $field_value );
8791
						$pick_values  = array();
8792
8793
						foreach ( $field_values as $pick_value ) {
8794
							if ( in_array( $type, PodsForm::file_field_types() ) || 'media' === $pick_object ) {
8795
								$where = "`guid` = '" . pods_sanitize( $pick_value ) . "'";
8796
8797
								if ( 0 < pods_absint( $pick_value ) && false !== $numeric_mode ) {
8798
									$where = "`ID` = " . pods_absint( $pick_value );
8799
								}
8800
8801
								$result = pods_query( "SELECT `ID` AS `id` FROM `{$wpdb->posts}` WHERE `post_type` = 'attachment' AND {$where} ORDER BY `ID`", $this );
8802
8803
								if ( ! empty( $result ) ) {
8804
									$pick_values[] = $result[0]->id;
8805
								}
8806
							} elseif ( 'pick' === $type ) {
8807
								// @todo This could and should be abstracted better and simplified
8808
								$related_pod = false;
8809
8810
								if ( 'pod' === $pick_object ) {
8811
									$related_pod = $this->load_pod( array(
8812
										'name'       => $pick_val,
8813
										'table_info' => true
8814
									), false );
8815
								}
8816
8817
								if ( empty( $related_pod ) ) {
8818
									$related_pod = array(
8819
										'id'   => 0,
8820
										'type' => $pick_object
8821
									);
8822
								}
8823
8824
								if ( in_array( 'taxonomy', array( $pick_object, $related_pod['type'] ) ) ) {
8825
									$where = "`t`.`name` = '" . pods_sanitize( $pick_value ) . "'";
8826
8827
									if ( 0 < pods_absint( $pick_value ) && false !== $numeric_mode ) {
8828
										$where = "`tt`.`term_id` = " . pods_absint( $pick_value );
8829
									}
8830
8831
									$result = pods_query( "SELECT `t`.`term_id` AS `id` FROM `{$wpdb->term_taxonomy}` AS `tt` LEFT JOIN `{$wpdb->terms}` AS `t` ON `t`.`term_id` = `tt`.`term_id` WHERE `taxonomy` = '{$pick_val}' AND {$where} ORDER BY `t`.`term_id` LIMIT 1", $this );
8832
8833
									if ( ! empty( $result ) ) {
8834
										$pick_values[] = $result[0]->id;
8835
									}
8836
								} elseif ( in_array( 'post_type', array(
8837
										$pick_object,
8838
										$related_pod['type']
8839
									) ) || in_array( 'media', array( $pick_object, $related_pod['type'] ) ) ) {
8840
									$where = "`post_title` = '" . pods_sanitize( $pick_value ) . "'";
8841
8842
									if ( 0 < pods_absint( $pick_value ) && false !== $numeric_mode ) {
8843
										$where = "`ID` = " . pods_absint( $pick_value );
8844
									}
8845
8846
									$result = pods_query( "SELECT `ID` AS `id` FROM `{$wpdb->posts}` WHERE `post_type` = '{$pick_val}' AND {$where} ORDER BY `ID` LIMIT 1", $this );
8847
8848
									if ( ! empty( $result ) ) {
8849
										$pick_values[] = $result[0]->id;
8850
									}
8851
								} elseif ( in_array( 'user', array( $pick_object, $related_pod['type'] ) ) ) {
8852
									$where = "`user_login` = '" . pods_sanitize( $pick_value ) . "'";
8853
8854
									if ( 0 < pods_absint( $pick_value ) && false !== $numeric_mode ) {
8855
										$where = "`ID` = " . pods_absint( $pick_value );
8856
									}
8857
8858
									$result = pods_query( "SELECT `ID` AS `id` FROM `{$wpdb->users}` WHERE {$where} ORDER BY `ID` LIMIT 1", $this );
8859
8860
									if ( ! empty( $result ) ) {
8861
										$pick_values[] = $result[0]->id;
8862
									}
8863
								} elseif ( in_array( 'comment', array( $pick_object, $related_pod['type'] ) ) ) {
8864
									$where = "`comment_ID` = " . pods_absint( $pick_value );
8865
8866
									$result = pods_query( "SELECT `comment_ID` AS `id` FROM `{$wpdb->comments}` WHERE {$where} ORDER BY `ID` LIMIT 1", $this );
8867
8868
									if ( ! empty( $result ) ) {
8869
										$pick_values[] = $result[0]->id;
8870
									}
8871
								} elseif ( in_array( $pick_object, $simple_tableless_objects ) ) {
8872
									$pick_values[] = $pick_value;
8873
								} elseif ( ! empty( $related_pod['id'] ) ) {
8874
									$where = "`" . $related_pod['field_index'] . "` = '" . pods_sanitize( $pick_value ) . "'";
8875
8876
									if ( 0 < pods_absint( $pick_value ) && false !== $numeric_mode ) {
8877
										$where = "`" . $related_pod['field_id'] . "` = " . pods_absint( $pick_value );
8878
									}
8879
8880
									$result = pods_query( "SELECT `" . $related_pod['field_id'] . "` AS `id` FROM `" . $related_pod['table'] . "` WHERE {$where} ORDER BY `" . $related_pod['field_id'] . "` LIMIT 1", $this );
8881
8882
									if ( ! empty( $result ) ) {
8883
										$pick_values[] = $result[0]->id;
8884
									}
8885
								}
8886
							}
8887
						}
8888
8889
						$field_value = implode( ',', $pick_values );
8890
					}
8891
8892
					$data[ $field_name ] = $field_value;
8893
				}
8894
			}
8895
8896
			if ( ! empty( $data ) ) {
8897
				$params = array(
8898
					'pod'  => $this->pod,
8899
					'data' => $data
8900
				);
8901
8902
				$ids[] = $this->save_pod_item( $params );
8903
			}
8904
		}
8905
8906
		return $ids;
8907
	}
8908
8909
	/**
8910
	 * Export data from a Pod
8911
	 *
8912
	 * @param string|object $pod    The pod name or Pods object
0 ignored issues
show
Documentation introduced by
Should the type for parameter $pod not be string|object|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
8913
	 * @param array         $params An associative array of parameters
0 ignored issues
show
Documentation introduced by
Should the type for parameter $params not be array|null? Also, consider making the array more specific, something like array<String>, or String[].

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive. In addition it looks for parameters that have the generic type array and suggests a stricter type like array<String>.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
8914
	 *
8915
	 * @return array Data arrays of all exported pod items
8916
	 * @since 1.7.1
8917
	 */
8918
	public function export( $pod = null, $params = null ) {
8919
8920
		if ( empty( $pod ) ) {
8921
			$pod = $this->pod;
8922
		}
8923
8924
		$find = array(
8925
			'limit'      => - 1,
8926
			'search'     => false,
8927
			'pagination' => false
8928
		);
8929
8930
		if ( ! empty( $params ) && isset( $params['params'] ) ) {
8931
			$find = array_merge( $find, (array) $params['params'] );
8932
8933
			unset( $params['params'] );
8934
8935
			$pod = pods( $pod, $find );
8936
		} elseif ( ! is_object( $pod ) ) {
8937
			$pod = pods( $pod, $find );
8938
		}
8939
8940
		$data = array();
8941
8942
		while ( $pod->fetch() ) {
8943
			$data[ $pod->id() ] = $this->export_pod_item( $params, $pod );
8944
		}
8945
8946
		$data = $this->do_hook( 'export', $data, $pod->pod, $pod );
8947
8948
		return $data;
8949
	}
8950
8951
	/**
8952
	 * Convert CSV to a PHP array
8953
	 *
8954
	 * @param string $data The CSV input
8955
	 *
8956
	 * @return array
8957
	 * @since      1.7.1
8958
	 *
8959
	 * @deprecated 2.3.5
8960
	 */
8961
	public function csv_to_php( $data, $delimiter = ',' ) {
8962
8963
		pods_deprecated( "PodsAPI->csv_to_php", '2.3.5' );
8964
8965
		$data = pods_migrate( 'sv', $delimiter, $data )->parse();
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $data is correct as pods_migrate('sv', $delimiter, $data)->parse() (which targets PodsMigrate::parse()) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
8966
8967
		return $data['items'];
8968
	}
8969
8970
	/**
8971
	 * Clear Pod-related cache
8972
	 *
8973
	 * @param array $pod
0 ignored issues
show
Documentation introduced by
Should the type for parameter $pod not be array|null? Also, consider making the array more specific, something like array<String>, or String[].

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive. In addition it looks for parameters that have the generic type array and suggests a stricter type like array<String>.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
8974
	 *
8975
	 * @return void
8976
	 *
8977
	 * @since 2.0
8978
	 */
8979
	public function cache_flush_pods( $pod = null ) {
8980
8981
		/**
8982
		 * @var $wpdb wpdb
8983
		 */
8984
		global $wpdb;
8985
8986
		pods_transient_clear( 'pods' );
8987
		pods_transient_clear( 'pods_components' );
8988
8989
		if ( null !== $pod && is_array( $pod ) ) {
8990
			pods_transient_clear( 'pods_pod_' . $pod['name'] );
8991
			pods_cache_clear( $pod['name'], 'pods-class' );
8992
8993
			foreach ( $pod['fields'] as $field ) {
8994
				pods_transient_clear( 'pods_field_' . $pod['name'] . '_' . $field['name'] );
8995
			}
8996
8997
			if ( in_array( $pod['type'], array( 'post_type', 'taxonomy' ) ) ) {
8998
				pods_transient_clear( 'pods_wp_cpt_ct' );
8999
			}
9000
		} else {
9001
			pods_transient_clear( 'pods_wp_cpt_ct' );
9002
		}
9003
9004
		// Delete transients in the database
9005
		$wpdb->query( "DELETE FROM `{$wpdb->options}` WHERE `option_name` LIKE '_transient_pods%'" );
9006
		$wpdb->query( "DELETE FROM `{$wpdb->options}` WHERE `option_name` LIKE '_transient_timeout_pods%'" );
9007
9008
		// Delete Pods Options Cache in the database
9009
		$wpdb->query( "DELETE FROM `{$wpdb->options}` WHERE `option_name` LIKE '_pods_option_%'" );
9010
9011
		pods_cache_clear( true );
9012
9013
		pods_transient_set( 'pods_flush_rewrites', 1 );
9014
9015
		do_action( 'pods_cache_flushed' );
9016
	}
9017
9018
	/**
9019
	 * Process a Pod-based form
9020
	 *
9021
	 * @param mixed  $params
9022
	 * @param object $obj       Pod object
0 ignored issues
show
Documentation introduced by
Should the type for parameter $obj not be object|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
9023
	 * @param array  $fields    Fields being submitted in form ( key => settings )
0 ignored issues
show
Documentation introduced by
Should the type for parameter $fields not be array|null? Also, consider making the array more specific, something like array<String>, or String[].

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive. In addition it looks for parameters that have the generic type array and suggests a stricter type like array<String>.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
9024
	 * @param string $thank_you URL to send to upon success
0 ignored issues
show
Documentation introduced by
Should the type for parameter $thank_you not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
9025
	 *
9026
	 * @return mixed
9027
	 *
9028
	 * @since 2.0
9029
	 */
9030
	public function process_form( $params, $obj = null, $fields = null, $thank_you = null ) {
9031
9032
		$this->display_errors = false;
9033
9034
		$form = null;
9035
9036
		$nonce    = pods_var( '_pods_nonce', $params );
9037
		$pod      = pods_var( '_pods_pod', $params );
9038
		$id       = pods_var( '_pods_id', $params );
9039
		$uri      = pods_var( '_pods_uri', $params );
9040
		$form     = pods_var( '_pods_form', $params );
9041
		$location = pods_var( '_pods_location', $params );
9042
9043
		if ( is_object( $obj ) ) {
9044
			$pod = $obj->pod;
9045
			$id  = $obj->id();
9046
		}
9047
9048
		if ( ! empty( $fields ) ) {
9049
			$fields = array_keys( $fields );
9050
			$form   = implode( ',', $fields );
9051
		} else {
9052
			$fields = explode( ',', $form );
9053
		}
9054
9055
		if ( empty( $nonce ) || empty( $pod ) || empty( $uri ) || empty( $fields ) ) {
9056
			return pods_error( __( 'Invalid submission', 'pods' ), $this );
9057
		}
9058
9059
		$uid = @session_id();
9060
9061
		if ( is_user_logged_in() ) {
9062
			$uid = 'user_' . get_current_user_id();
9063
		}
9064
9065
		$field_hash = wp_create_nonce( 'pods_fields_' . $form );
9066
9067
		$action = 'pods_form_' . $pod . '_' . $uid . '_' . $id . '_' . $uri . '_' . $field_hash;
9068
9069
		if ( empty( $uid ) ) {
9070
			return pods_error( __( 'Access denied for your session, please refresh and try again.', 'pods' ), $this );
9071
		}
9072
9073
		if ( false === wp_verify_nonce( $nonce, $action ) ) {
9074
			return pods_error( __( 'Access denied, please refresh and try again.', 'pods' ), $this );
9075
		}
9076
9077
		$data = array();
9078
9079
		foreach ( $fields as $field ) {
9080
			$data[ $field ] = pods_var_raw( 'pods_field_' . $field, $params, '' );
9081
		}
9082
9083
		$params = array(
9084
			'pod'      => $pod,
9085
			'id'       => $id,
9086
			'data'     => $data,
9087
			'from'     => 'process_form',
9088
			'location' => $location
9089
		);
9090
9091
		$id = $this->save_pod_item( $params );
9092
9093
		/**
9094
		 * Fires after the form has been processed and save_pod_item has run.
9095
		 *
9096
		 * @param int       $id     Item ID.
9097
		 * @param array     $params save_pod_item parameters.
9098
		 * @param null|Pods $obj    Pod object (if set).
9099
		 */
9100
		do_action( 'pods_api_processed_form', $id, $params, $obj );
9101
9102
		// Always return $id for AJAX requests.
9103
		if ( ! defined( 'DOING_AJAX' ) || ! DOING_AJAX ) {
9104
			if ( 0 < $id && ! empty( $thank_you ) ) {
9105
				$thank_you = str_replace( 'X_ID_X', $id, $thank_you );
9106
9107
				pods_redirect( $thank_you, 302, false );
9108
			}
9109
		}
9110
9111
		return $id;
9112
	}
9113
9114
	/**
9115
	 * Handle filters / actions for the class
9116
	 *
9117
	 * @since 2.0
9118
	 */
9119
	private function do_hook() {
9120
9121
		$args = func_get_args();
9122
		if ( empty( $args ) ) {
9123
			return false;
9124
		}
9125
		$name = array_shift( $args );
9126
9127
		return pods_do_hook( "api", $name, $args, $this );
9128
	}
9129
9130
	/**
9131
	 * Handle variables that have been deprecated
9132
	 *
9133
	 * @since 2.0
9134
	 */
9135
	public function __get( $name ) {
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
9136
9137
		$name = (string) $name;
9138
9139
		if ( ! isset( $this->deprecated ) ) {
9140
			require_once( PODS_DIR . 'deprecated/classes/PodsAPI.php' );
9141
			$this->deprecated = new PodsAPI_Deprecated( $this );
9142
		}
9143
9144
		$var = null;
9145
9146
		if ( isset( $this->deprecated->{$name} ) ) {
9147
			pods_deprecated( "PodsAPI->{$name}", '2.0' );
9148
9149
			$var = $this->deprecated->{$name};
9150
		} else {
9151
			pods_deprecated( "PodsAPI->{$name}", '2.0' );
9152
		}
9153
9154
		return $var;
9155
	}
9156
9157
	/**
9158
	 * Handle methods that have been deprecated
9159
	 *
9160
	 * @since 2.0
9161
	 */
9162
	public function __call( $name, $args ) {
9163
9164
		$name = (string) $name;
9165
9166
		if ( ! isset( $this->deprecated ) ) {
9167
			require_once( PODS_DIR . 'deprecated/classes/PodsAPI.php' );
9168
			$this->deprecated = new PodsAPI_Deprecated( $this );
9169
		}
9170
9171
		if ( method_exists( $this->deprecated, $name ) ) {
9172
			return call_user_func_array( array( $this->deprecated, $name ), $args );
9173
		} else {
9174
			pods_deprecated( "PodsAPI::{$name}", '2.0' );
9175
		}
9176
	}
9177
9178
	/**
9179
	 * Filter an array of arrays without causing PHP notices/warnings.
9180
	 *
9181
	 * @param array $values
9182
	 *
9183
	 * @return array
9184
	 *
9185
	 * @since 2.6.10
9186
	 */
9187
	private function array_filter_walker( $values = array() ) {
9188
9189
		$values = (array) $values;
9190
9191
		foreach ( $values as $k => $v ) {
9192
			if ( is_object( $v ) ) {
9193
				// Skip objects
9194
				continue;
9195
			} elseif ( is_array( $v ) ) {
9196
				if ( empty( $v ) ) {
9197
					// Filter values with empty arrays
9198
					unset( $values[ $k ] );
9199
				}
9200
			} else {
9201
				if ( ! $v ) {
9202
					// Filter empty values
9203
					unset( $values[ $k ] );
9204
				}
9205
			}
9206
		}
9207
9208
		return $values;
9209
9210
	}
9211
9212
}
9213