Completed
Push — feature/codesniffer-fixes ( 68e5c9...c1f438 )
by Scott Kingsley
06:14
created

PodsAPI::load_sister_fields()   C

Complexity

Conditions 16
Paths 41

Size

Total Lines 52
Code Lines 33

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 16
eloc 33
nc 41
nop 2
dl 0
loc 52
rs 5.9237
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
			$term_data['term_id'] = wp_insert_term( $term_name, $taxonomy, $term_data );
705
		} elseif ( 1 < count( $term_data ) ) {
706
			$term_data['term_id'] = wp_update_term( $term_data['term_id'], $taxonomy, $term_data );
707
		}
708
709
		if ( is_wp_error( $term_data['term_id'] ) ) {
710
			if ( ! $conflicted ) {
711
				pods_no_conflict_off( 'taxonomy' );
712
			}
713
714
			/**
715
			 * @var $term_error WP_Error
716
			 */
717
			$term_error = $term_data['term_id'];
718
719
			return pods_error( $term_error->get_error_message(), $this );
720
		} elseif ( is_array( $term_data['term_id'] ) ) {
721
			$term_data['term_id'] = $term_data['term_id']['term_id'];
722
		}
723
724
		$this->save_term_meta( $term_data['term_id'], $term_meta, $strict, $fields );
725
726
		if ( ! $conflicted ) {
727
			pods_no_conflict_off( 'taxonomy' );
728
		}
729
730
		return $term_data['term_id'];
731
	}
732
733
	/**
734
	 * Save a term's meta
735
	 *
736
	 * @param int   $id        Term ID
737
	 * @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...
738
	 * @param bool  $strict    Whether to delete previously saved meta not in $term_meta
739
	 * @param array $fields    (optional) The array of fields and their options, for further processing with
740
	 *
741
	 * @return int Id of the term with the meta
742
	 *
743
	 * @since 2.0
744
	 */
745
	public function save_term_meta( $id, $term_meta = null, $strict = false, $fields = array() ) {
746
747
		if ( ! function_exists( 'get_term_meta' ) ) {
748
			return $id;
749
		}
750
751
		$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...
752
753
		$conflicted = pods_no_conflict_check( 'taxonomy' );
754
755
		if ( ! $conflicted ) {
756
			pods_no_conflict_on( 'taxonomy' );
757
		}
758
759
		if ( ! is_array( $term_meta ) ) {
760
			$term_meta = array();
761
		}
762
763
		$id = (int) $id;
764
765
		$meta = get_term_meta( $id );
766
767
		foreach ( $meta as $k => $value ) {
768
			if ( is_array( $value ) && 1 == count( $value ) ) {
769
				$meta[ $k ] = current( $value );
770
			}
771
		}
772
773
		foreach ( $term_meta as $meta_key => $meta_value ) {
774
			if ( null === $meta_value || ( $strict && '' === $term_meta[ $meta_key ] ) ) {
775
				$old_meta_value = '';
776
777
				if ( isset( $meta[ $meta_key ] ) ) {
778
					$old_meta_value = $meta[ $meta_key ];
779
				}
780
781
				delete_term_meta( $id, $meta_key, $old_meta_value );
782
			} else {
783
				$simple = false;
784
785
				if ( isset( $fields[ $meta_key ] ) ) {
786
					$field_data = $fields[ $meta_key ];
787
788
					$simple = ( 'pick' === $field_data['type'] && in_array( pods_var( 'pick_object', $field_data ), $simple_tableless_objects ) );
789
				}
790
791
				if ( $simple ) {
792
					delete_term_meta( $id, $meta_key );
793
794
					update_term_meta( $id, '_pods_' . $meta_key, $meta_value );
795
796
					if ( ! is_array( $meta_value ) ) {
797
						$meta_value = array( $meta_value );
798
					}
799
800
					foreach ( $meta_value as $value ) {
801
						add_term_meta( $id, $meta_key, $value );
802
					}
803
				} else {
804
					update_term_meta( $id, $meta_key, $meta_value );
805
				}
806
			}
807
		}
808
809
		if ( $strict ) {
810
			foreach ( $meta as $meta_key => $meta_value ) {
811
				if ( ! isset( $term_meta[ $meta_key ] ) ) {
812
					delete_term_meta( $id, $meta_key, $meta_value );
813
				}
814
			}
815
		}
816
817
		if ( ! $conflicted ) {
818
			pods_no_conflict_off( 'taxonomy' );
819
		}
820
821
		return $id;
822
	}
823
824
	/**
825
	 * Save a set of options
826
	 *
827
	 * @param string $setting     Setting group name
828
	 * @param array  $option_data All option data to be saved
829
	 * @param bool   $sanitized   (optional) Will unsanitize the data, should be passed if the data is sanitized before
830
	 *                            sending.
831
	 *
832
	 * @return bool
833
	 *
834
	 * @since 2.3
835
	 */
836
	public function save_setting( $setting, $option_data, $sanitized = false ) {
837
838
		if ( ! is_array( $option_data ) || empty( $option_data ) ) {
839
			return pods_error( __( 'Setting data is required but is either invalid or empty', 'pods' ), $this );
840
		}
841
842
		$conflicted = pods_no_conflict_check( 'settings' );
843
844
		if ( ! $conflicted ) {
845
			pods_no_conflict_on( 'settings' );
846
		}
847
848
		if ( $sanitized ) {
849
			$option_data = pods_unsanitize( $option_data );
850
		}
851
852
		foreach ( $option_data as $option => $value ) {
853
			if ( ! empty( $setting ) ) {
854
				$option = $setting . '_' . $option;
855
			}
856
857
			update_option( $option, $value );
858
		}
859
860
		if ( ! $conflicted ) {
861
			pods_no_conflict_off( 'settings' );
862
		}
863
864
		return true;
865
	}
866
867
	/**
868
	 * Rename a WP object's type
869
	 *
870
	 * @param string $object_type Object type: post|taxonomy|comment|setting
871
	 * @param string $old_name    The old name
872
	 * @param string $new_name    The new name
873
	 *
874
	 * @return bool
875
	 *
876
	 * @since 2.0
877
	 */
878
	public function rename_wp_object_type( $object_type, $old_name, $new_name ) {
879
880
		/**
881
		 * @var $wpdb wpdb
882
		 */
883
		global $wpdb;
884
885
		if ( 'post_type' === $object_type ) {
886
			$object_type = 'post';
887
		}
888
889
		if ( 'post' === $object_type ) {
890
			pods_query( "UPDATE `{$wpdb->posts}` SET `post_type` = %s WHERE `post_type` = %s", array(
891
				$new_name,
892
				$old_name
893
			) );
894
		} elseif ( 'taxonomy' === $object_type ) {
895
			pods_query( "UPDATE `{$wpdb->term_taxonomy}` SET `taxonomy` = %s WHERE `taxonomy` = %s", array(
896
				$new_name,
897
				$old_name
898
			) );
899
		} elseif ( 'comment' === $object_type ) {
900
			pods_query( "UPDATE `{$wpdb->comments}` SET `comment_type` = %s WHERE `comment_type` = %s", array(
901
				$new_name,
902
				$old_name
903
			) );
904
		} elseif ( 'settings' === $object_type ) {
905
			pods_query( "UPDATE `{$wpdb->options}` SET `option_name` = REPLACE( `option_name`, %s, %s ) WHERE `option_name` LIKE '" . pods_sanitize_like( $old_name ) . "_%'", array(
906
				$new_name . '_',
907
				$old_name . '_'
908
			) );
909
		}
910
911
		return true;
912
	}
913
914
	/**
915
	 * Get a list of core WP object fields for a specific object
916
	 *
917
	 * @param string  $object  The pod type to look for, possible values: post_type, user, comment, taxonomy
918
	 * @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...
919
	 * @param boolean $refresh Whether to force refresh the information
920
	 *
921
	 * @return array Array of fields
922
	 *
923
	 * @since 2.0
924
	 */
925
	public function get_wp_object_fields( $object = 'post_type', $pod = null, $refresh = false ) {
926
927
		$pod_name = pods_var_raw( 'name', $pod, $object, null, true );
928
929
		if ( 'media' === $pod_name ) {
930
			$object   = 'post_type';
931
			$pod_name = 'attachment';
932
		}
933
934
		$fields = false;
935
936
		if ( pods_api_cache() ) {
937
			$fields = pods_transient_get( trim( 'pods_api_object_fields_' . $object . $pod_name . '_', '_' ) );
938
		}
939
940
		if ( false !== $fields && ! $refresh ) {
941
			return $this->do_hook( 'get_wp_object_fields', $fields, $object, $pod );
942
		}
943
944
		$fields = array();
945
946
		if ( 'post_type' === $object ) {
947
			$fields = array(
948
				'ID'                    => array(
949
					'name'    => 'ID',
950
					'label'   => 'ID',
951
					'type'    => 'number',
952
					'alias'   => array( 'id' ),
953
					'options' => array(
954
						'number_format' => '9999.99'
955
					)
956
				),
957
				'post_title'            => array(
958
					'name'    => 'post_title',
959
					'label'   => 'Title',
960
					'type'    => 'text',
961
					'alias'   => array( 'title', 'name' ),
962
					'options' => array(
963
						'display_filter'      => 'the_title',
964
						'display_filter_args' => array( 'post_ID' )
965
					)
966
				),
967
				'post_content'          => array(
968
					'name'    => 'post_content',
969
					'label'   => 'Content',
970
					'type'    => 'wysiwyg',
971
					'alias'   => array( 'content' ),
972
					'options' => array(
973
						'wysiwyg_allowed_html_tags' => '',
974
						'display_filter'            => 'the_content',
975
						'pre_save'                  => 0
976
					)
977
				),
978
				'post_excerpt'          => array(
979
					'name'    => 'post_excerpt',
980
					'label'   => 'Excerpt',
981
					'type'    => 'paragraph',
982
					'alias'   => array( 'excerpt' ),
983
					'options' => array(
984
						'paragraph_allow_html'        => 1,
985
						'paragraph_allowed_html_tags' => '',
986
						'display_filter'              => 'the_excerpt',
987
						'pre_save'                    => 0
988
					)
989
				),
990
				'post_author'           => array(
991
					'name'        => 'post_author',
992
					'label'       => 'Author',
993
					'type'        => 'pick',
994
					'alias'       => array( 'author' ),
995
					'pick_object' => 'user',
996
					'options'     => array(
997
						'pick_format_type'   => 'single',
998
						'pick_format_single' => 'autocomplete',
999
						'default_value'      => '{@user.ID}'
1000
					)
1001
				),
1002
				'post_date'             => array(
1003
					'name'  => 'post_date',
1004
					'label' => 'Publish Date',
1005
					'type'  => 'datetime',
1006
					'alias' => array( 'created', 'date' )
1007
				),
1008
				'post_date_gmt'         => array(
1009
					'name'   => 'post_date_gmt',
1010
					'label'  => 'Publish Date (GMT)',
1011
					'type'   => 'datetime',
1012
					'alias'  => array(),
1013
					'hidden' => true
1014
				),
1015
				'post_status'           => array(
1016
					'name'        => 'post_status',
1017
					'label'       => 'Status',
1018
					'type'        => 'pick',
1019
					'pick_object' => 'post-status',
1020
					'default'     => $this->do_hook( 'default_status_' . $pod_name, pods_var( 'default_status', pods_var_raw( 'options', $pod ), 'draft', null, true ), $pod ),
1021
					'alias'       => array( 'status' )
1022
				),
1023
				'comment_status'        => array(
1024
					'name'    => 'comment_status',
1025
					'label'   => 'Comment Status',
1026
					'type'    => 'text',
1027
					'default' => get_option( 'default_comment_status', 'open' ),
1028
					'alias'   => array(),
1029
					'data'    => array(
1030
						'open'   => __( 'Open', 'pods' ),
1031
						'closed' => __( 'Closed', 'pods' )
1032
					)
1033
				),
1034
				'ping_status'           => array(
1035
					'name'    => 'ping_status',
1036
					'label'   => 'Ping Status',
1037
					'default' => get_option( 'default_ping_status', 'open' ),
1038
					'type'    => 'text',
1039
					'alias'   => array(),
1040
					'data'    => array(
1041
						'open'   => __( 'Open', 'pods' ),
1042
						'closed' => __( 'Closed', 'pods' )
1043
					)
1044
				),
1045
				'post_password'         => array(
1046
					'name'  => 'post_password',
1047
					'label' => 'Password',
1048
					'type'  => 'text',
1049
					'alias' => array()
1050
				),
1051
				'post_name'             => array(
1052
					'name'  => 'post_name',
1053
					'label' => 'Permalink',
1054
					'type'  => 'slug',
1055
					'alias' => array( 'slug', 'permalink' )
1056
				),
1057
				'to_ping'               => array(
1058
					'name'   => 'to_ping',
1059
					'label'  => 'To Ping',
1060
					'type'   => 'text',
1061
					'alias'  => array(),
1062
					'hidden' => true
1063
				),
1064
				'pinged'                => array(
1065
					'name'   => 'pinged',
1066
					'label'  => 'Pinged',
1067
					'type'   => 'text',
1068
					'alias'  => array(),
1069
					'hidden' => true
1070
				),
1071
				'post_modified'         => array(
1072
					'name'   => 'post_modified',
1073
					'label'  => 'Last Modified Date',
1074
					'type'   => 'datetime',
1075
					'alias'  => array( 'modified' ),
1076
					'hidden' => true
1077
				),
1078
				'post_modified_gmt'     => array(
1079
					'name'   => 'post_modified_gmt',
1080
					'label'  => 'Last Modified Date (GMT)',
1081
					'type'   => 'datetime',
1082
					'alias'  => array(),
1083
					'hidden' => true
1084
				),
1085
				'post_content_filtered' => array(
1086
					'name'    => 'post_content_filtered',
1087
					'label'   => 'Content (filtered)',
1088
					'type'    => 'paragraph',
1089
					'alias'   => array(),
1090
					'hidden'  => true,
1091
					'options' => array(
1092
						'paragraph_allow_html'        => 1,
1093
						'paragraph_oembed'            => 1,
1094
						'paragraph_wptexturize'       => 1,
1095
						'paragraph_convert_chars'     => 1,
1096
						'paragraph_wpautop'           => 1,
1097
						'paragraph_allow_shortcode'   => 1,
1098
						'paragraph_allowed_html_tags' => ''
1099
					)
1100
				),
1101
				'post_parent'           => array(
1102
					'name'        => 'post_parent',
1103
					'label'       => 'Parent',
1104
					'type'        => 'pick',
1105
					'pick_object' => 'post_type',
1106
					'pick_val'    => '__current__',
1107
					'alias'       => array( 'parent' ),
1108
					'data'        => array(),
1109
					'hidden'      => true
1110
				),
1111
				'guid'                  => array(
1112
					'name'   => 'guid',
1113
					'label'  => 'GUID',
1114
					'type'   => 'text',
1115
					'alias'  => array(),
1116
					'hidden' => true
1117
				),
1118
				'menu_order'            => array(
1119
					'name'    => 'menu_order',
1120
					'label'   => 'Menu Order',
1121
					'type'    => 'number',
1122
					'alias'   => array(),
1123
					'options' => array(
1124
						'number_format' => '9999.99'
1125
					)
1126
				),
1127
				'post_type'             => array(
1128
					'name'   => 'post_type',
1129
					'label'  => 'Type',
1130
					'type'   => 'text',
1131
					'alias'  => array( 'type' ),
1132
					'hidden' => true
1133
				),
1134
				'post_mime_type'        => array(
1135
					'name'   => 'post_mime_type',
1136
					'label'  => 'Mime Type',
1137
					'type'   => 'text',
1138
					'alias'  => array(),
1139
					'hidden' => true
1140
				),
1141
				'comment_count'         => array(
1142
					'name'   => 'comment_count',
1143
					'label'  => 'Comment Count',
1144
					'type'   => 'number',
1145
					'alias'  => array(),
1146
					'hidden' => true
1147
				),
1148
				'comments'              => array(
1149
					'name'        => 'comments',
1150
					'label'       => 'Comments',
1151
					'type'        => 'comment',
1152
					'pick_object' => 'comment',
1153
					'pick_val'    => 'comment',
1154
					'alias'       => array(),
1155
					'hidden'      => true,
1156
					'options'     => array(
1157
						'comment_format_type' => 'multi'
1158
					)
1159
				)
1160
			);
1161
1162
			if ( ! empty( $pod ) ) {
1163
				$taxonomies = get_object_taxonomies( $pod_name, 'objects' );
1164
1165
				foreach ( $taxonomies as $taxonomy ) {
1166
					$fields[ $taxonomy->name ] = array(
1167
						'name'        => $taxonomy->name,
1168
						'label'       => $taxonomy->labels->name,
1169
						'type'        => 'taxonomy',
1170
						'pick_object' => 'taxonomy',
1171
						'pick_val'    => $taxonomy->name,
1172
						'alias'       => array(),
1173
						'hidden'      => true,
1174
						'options'     => array(
1175
							'taxonomy_format_type' => 'multi'
1176
						)
1177
					);
1178
				}
1179
			}
1180
		} elseif ( 'user' === $object ) {
1181
			$fields = array(
1182
				'ID'              => array(
1183
					'name'    => 'ID',
1184
					'label'   => 'ID',
1185
					'type'    => 'number',
1186
					'alias'   => array( 'id' ),
1187
					'options' => array(
1188
						'number_format' => '9999.99'
1189
					)
1190
				),
1191
				'user_login'      => array(
1192
					'name'    => 'user_login',
1193
					'label'   => 'Title',
1194
					'type'    => 'text',
1195
					'alias'   => array( 'login' ),
1196
					'options' => array(
1197
						'required' => 1
1198
					)
1199
				),
1200
				'user_nicename'   => array(
1201
					'name'  => 'user_nicename',
1202
					'label' => 'Permalink',
1203
					'type'  => 'slug',
1204
					'alias' => array( 'nicename', 'slug', 'permalink' )
1205
				),
1206
				'display_name'    => array(
1207
					'name'  => 'display_name',
1208
					'label' => 'Display Name',
1209
					'type'  => 'text',
1210
					'alias' => array( 'title', 'name' )
1211
				),
1212
				'user_pass'       => array(
1213
					'name'    => 'user_pass',
1214
					'label'   => 'Password',
1215
					'type'    => 'text',
1216
					'alias'   => array( 'password', 'pass' ),
1217
					'options' => array(
1218
						'required'         => 1,
1219
						'text_format_type' => 'password'
1220
					)
1221
				),
1222
				'user_email'      => array(
1223
					'name'    => 'user_email',
1224
					'label'   => 'E-mail',
1225
					'type'    => 'text',
1226
					'alias'   => array( 'email' ),
1227
					'options' => array(
1228
						'required'         => 1,
1229
						'text_format_type' => 'email'
1230
					)
1231
				),
1232
				'user_url'        => array(
1233
					'name'    => 'user_url',
1234
					'label'   => 'URL',
1235
					'type'    => 'text',
1236
					'alias'   => array( 'url', 'website' ),
1237
					'options' => array(
1238
						'required'            => 0,
1239
						'text_format_type'    => 'website',
1240
						'text_format_website' => 'normal'
1241
					)
1242
				),
1243
				'user_registered' => array(
1244
					'name'    => 'user_registered',
1245
					'label'   => 'Registration Date',
1246
					'type'    => 'date',
1247
					'alias'   => array( 'created', 'date', 'registered' ),
1248
					'options' => array(
1249
						'date_format_type' => 'datetime'
1250
					)
1251
				)
1252
			);
1253
		} elseif ( 'comment' === $object ) {
1254
			$fields = array(
1255
				'comment_ID'           => array(
1256
					'name'    => 'comment_ID',
1257
					'label'   => 'ID',
1258
					'type'    => 'number',
1259
					'alias'   => array( 'id', 'ID', 'comment_id' ),
1260
					'options' => array(
1261
						'number_format' => '9999.99'
1262
					)
1263
				),
1264
				'comment_content'      => array(
1265
					'name'  => 'comment_content',
1266
					'label' => 'Content',
1267
					'type'  => 'wysiwyg',
1268
					'alias' => array( 'content' )
1269
				),
1270
				'comment_approved'     => array(
1271
					'name'    => 'comment_approved',
1272
					'label'   => 'Approved',
1273
					'type'    => 'number',
1274
					'alias'   => array( 'approved' ),
1275
					'options' => array(
1276
						'number_format' => '9999.99'
1277
					)
1278
				),
1279
				'comment_post_ID'      => array(
1280
					'name'  => 'comment_post_ID',
1281
					'label' => 'Post',
1282
					'type'  => 'pick',
1283
					'alias' => array( 'post', 'post_id' ),
1284
					'data'  => array()
1285
				),
1286
				'user_id'              => array(
1287
					'name'        => 'user_id',
1288
					'label'       => 'Author',
1289
					'type'        => 'pick',
1290
					'alias'       => array( 'author' ),
1291
					'pick_object' => 'user',
1292
					'data'        => array()
1293
				),
1294
				'comment_date'         => array(
1295
					'name'    => 'comment_date',
1296
					'label'   => 'Date',
1297
					'type'    => 'date',
1298
					'alias'   => array( 'created', 'date' ),
1299
					'options' => array(
1300
						'date_format_type' => 'datetime'
1301
					)
1302
				),
1303
				'comment_author'       => array(
1304
					'name'  => 'comment_author',
1305
					'label' => 'Author',
1306
					'type'  => 'text',
1307
					'alias' => array( 'author' )
1308
				),
1309
				'comment_author_email' => array(
1310
					'name'  => 'comment_author_email',
1311
					'label' => 'Author E-mail',
1312
					'type'  => 'email',
1313
					'alias' => array( 'author_email' )
1314
				),
1315
				'comment_author_url'   => array(
1316
					'name'  => 'comment_author_url',
1317
					'label' => 'Author URL',
1318
					'type'  => 'text',
1319
					'alias' => array( 'author_url' )
1320
				),
1321
				'comment_author_IP'    => array(
1322
					'name'  => 'comment_author_IP',
1323
					'label' => 'Author IP',
1324
					'type'  => 'text',
1325
					'alias' => array( 'author_IP' )
1326
				),
1327
				'comment_type'         => array(
1328
					'name'   => 'comment_type',
1329
					'label'  => 'Type',
1330
					'type'   => 'text',
1331
					'alias'  => array( 'type' ),
1332
					'hidden' => true
1333
				),
1334
				'comment_parent'       => array(
1335
					'name'        => 'comment_parent',
1336
					'label'       => 'Parent',
1337
					'type'        => 'pick',
1338
					'pick_object' => 'comment',
1339
					'pick_val'    => '__current__',
1340
					'alias'       => array( 'parent' ),
1341
					'data'        => array(),
1342
					'hidden'      => true
1343
				)
1344
			);
1345
		} elseif ( 'taxonomy' === $object ) {
1346
			$fields = array(
1347
				'term_id'          => array(
1348
					'name'    => 'term_id',
1349
					'label'   => 'ID',
1350
					'type'    => 'number',
1351
					'alias'   => array( 'id', 'ID' ),
1352
					'options' => array(
1353
						'number_format' => '9999.99'
1354
					)
1355
				),
1356
				'name'             => array(
1357
					'name'  => 'name',
1358
					'label' => 'Title',
1359
					'type'  => 'text',
1360
					'alias' => array( 'title' )
1361
				),
1362
				'slug'             => array(
1363
					'name'  => 'slug',
1364
					'label' => 'Permalink',
1365
					'type'  => 'slug',
1366
					'alias' => array( 'permalink' )
1367
				),
1368
				'description'      => array(
1369
					'name'  => 'description',
1370
					'label' => 'Description',
1371
					'type'  => 'wysiwyg',
1372
					'alias' => array( 'content' )
1373
				),
1374
				'taxonomy'         => array(
1375
					'name'  => 'taxonomy',
1376
					'label' => 'Taxonomy',
1377
					'type'  => 'text',
1378
					'alias' => array()
1379
				),
1380
				'parent'           => array(
1381
					'name'        => 'parent',
1382
					'label'       => 'Parent',
1383
					'type'        => 'pick',
1384
					'pick_object' => 'taxonomy',
1385
					'pick_val'    => '__current__',
1386
					'alias'       => array( 'parent' ),
1387
					'data'        => array(),
1388
					'hidden'      => true
1389
				),
1390
				'term_taxonomy_id' => array(
1391
					'name'    => 'term_taxonomy_id',
1392
					'label'   => 'Term Taxonomy ID',
1393
					'type'    => 'number',
1394
					'alias'   => array(),
1395
					'hidden'  => true,
1396
					'options' => array(
1397
						'number_format' => '9999.99'
1398
					)
1399
				),
1400
				'term_group'       => array(
1401
					'name'    => 'term_group',
1402
					'label'   => 'Term Group',
1403
					'type'    => 'number',
1404
					'alias'   => array( 'group' ),
1405
					'hidden'  => true,
1406
					'options' => array(
1407
						'number_format' => '9999.99'
1408
					)
1409
				),
1410
				'count'            => array(
1411
					'name'    => 'count',
1412
					'label'   => 'Count',
1413
					'type'    => 'number',
1414
					'alias'   => array(),
1415
					'hidden'  => true,
1416
					'options' => array(
1417
						'number_format' => '9999.99'
1418
					)
1419
				)
1420
			);
1421
		}
1422
1423
		$fields = $this->do_hook( 'get_wp_object_fields', $fields, $object, $pod );
1424
1425
		foreach ( $fields as $field => $options ) {
1426
			if ( ! isset( $options['alias'] ) ) {
1427
				$options['alias'] = array();
1428
			} else {
1429
				$options['alias'] = (array) $options['alias'];
1430
			}
1431
1432
			if ( ! isset( $options['name'] ) ) {
1433
				$options['name'] = $field;
1434
			}
1435
1436
			$fields[ $field ] = $options;
1437
		}
1438
1439
		$fields = PodsForm::fields_setup( $fields );
1440
1441
		if ( did_action( 'init' ) && pods_api_cache() ) {
1442
			pods_transient_set( trim( 'pods_api_object_fields_' . $object . $pod_name . '_', '_' ), $fields );
1443
		}
1444
1445
		return $fields;
1446
	}
1447
1448
	/**
1449
	 *
1450
	 * @see   PodsAPI::save_pod
1451
	 *
1452
	 * Add a Pod via the Wizard
1453
	 *
1454
	 * $params['create_extend'] string Create or Extend a Content Type
1455
	 * $params['create_pod_type'] string Pod Type (for Creating)
1456
	 * $params['create_name'] string Pod Name (for Creating)
1457
	 * $params['create_label_plural'] string Plural Label (for Creating)
1458
	 * $params['create_label_singular'] string Singular Label (for Creating)
1459
	 * $params['create_storage'] string Storage Type (for Creating Post Types)
1460
	 * $params['create_storage_taxonomy'] string Storage Type (for Creating Taxonomies)
1461
	 * $params['extend_pod_type'] string Pod Type (for Extending)
1462
	 * $params['extend_post_type'] string Post Type (for Extending Post Types)
1463
	 * $params['extend_taxonomy'] string Taxonomy (for Extending Taxonomies)
1464
	 * $params['extend_storage'] string Storage Type (for Extending Post Types / Users / Comments)
1465
	 *
1466
	 * @param array $params An associative array of parameters
1467
	 *
1468
	 * @return bool|int Pod ID
1469
	 * @since 2.0
1470
	 */
1471
	public function add_pod( $params ) {
1472
1473
		$defaults = array(
1474
			'create_extend'   => 'create',
1475
			'create_pod_type' => 'post_type',
1476
1477
			'create_name'             => '',
1478
			'create_label_singular'   => '',
1479
			'create_label_plural'     => '',
1480
			'create_storage'          => 'meta',
1481
			'create_storage_taxonomy' => ( function_exists( 'get_term_meta' ) ? 'meta' : 'none' ),
1482
1483
			'create_setting_name'  => '',
1484
			'create_label_title'   => '',
1485
			'create_label_menu'    => '',
1486
			'create_menu_location' => 'settings',
1487
1488
			'extend_pod_type'         => 'post_type',
1489
			'extend_post_type'        => 'post',
1490
			'extend_taxonomy'         => 'category',
1491
			'extend_table'            => '',
1492
			'extend_storage'          => 'meta',
1493
			'extend_storage_taxonomy' => ( function_exists( 'get_term_meta' ) ? 'meta' : 'table' ),
1494
		);
1495
1496
		$params = (object) array_merge( $defaults, (array) $params );
1497
1498
		if ( empty( $params->create_extend ) || ! in_array( $params->create_extend, array( 'create', 'extend' ) ) ) {
1499
			return pods_error( __( 'Please choose whether to Create or Extend a Content Type', 'pods' ), $this );
1500
		}
1501
1502
		$pod_params = array(
1503
			'name'    => '',
1504
			'label'   => '',
1505
			'type'    => '',
1506
			'storage' => 'table',
1507
			'object'  => '',
1508
			'options' => array()
1509
		);
1510
1511
		if ( 'create' === $params->create_extend ) {
1512
			$label = ucwords( str_replace( '_', ' ', $params->create_name ) );
1513
1514
			if ( ! empty( $params->create_label_singular ) ) {
1515
				$label = $params->create_label_singular;
1516
			}
1517
1518
			$pod_params['name']    = $params->create_name;
1519
			$pod_params['label']   = ( ! empty( $params->create_label_plural ) ? $params->create_label_plural : $label );
1520
			$pod_params['type']    = $params->create_pod_type;
1521
			$pod_params['options'] = array(
1522
				'label_singular' => ( ! empty( $params->create_label_singular ) ? $params->create_label_singular : $pod_params['label'] ),
1523
				'public'         => 1,
1524
				'show_ui'        => 1
1525
			);
1526
1527
			// Auto-generate name if not provided
1528
			if ( empty( $pod_params['name'] ) && ! empty( $pod_params['options']['label_singular'] ) ) {
1529
				$pod_params['name'] = pods_clean_name( $pod_params['options']['label_singular'] );
1530
			}
1531
1532
			if ( 'post_type' === $pod_params['type'] ) {
1533
				if ( empty( $pod_params['name'] ) ) {
1534
					return pods_error( 'Please enter a Name for this Pod', $this );
1535
				}
1536
1537
				$pod_params['storage'] = $params->create_storage;
1538
1539
				if ( pods_tableless() ) {
1540
					$pod_params['storage'] = 'meta';
1541
				}
1542
			} elseif ( 'taxonomy' === $pod_params['type'] ) {
1543
				if ( empty( $pod_params['name'] ) ) {
1544
					return pods_error( 'Please enter a Name for this Pod', $this );
1545
				}
1546
1547
				$pod_params['storage'] = $params->create_storage_taxonomy;
1548
1549
				if ( pods_tableless() ) {
1550
					$pod_params['storage'] = ( function_exists( 'get_term_meta' ) ? 'meta' : 'none' );
1551
				}
1552
1553
				$pod_params['options']['hierarchical'] = 1;
1554
			} elseif ( 'pod' === $pod_params['type'] ) {
1555
				if ( empty( $pod_params['name'] ) ) {
1556
					return pods_error( 'Please enter a Name for this Pod', $this );
1557
				}
1558
1559
				if ( pods_tableless() ) {
1560
					$pod_params['type']    = 'post_type';
1561
					$pod_params['storage'] = 'meta';
1562
				}
1563
			} elseif ( 'settings' === $pod_params['type'] ) {
1564
				$pod_params['name']    = $params->create_setting_name;
1565
				$pod_params['label']   = ( ! empty( $params->create_label_title ) ? $params->create_label_title : ucwords( str_replace( '_', ' ', $params->create_setting_name ) ) );
1566
				$pod_params['options'] = array(
1567
					'menu_name'     => ( ! empty( $params->create_label_menu ) ? $params->create_label_menu : $pod_params['label'] ),
1568
					'menu_location' => $params->create_menu_location
1569
				);
1570
				$pod_params['storage'] = 'none';
1571
1572
				// Auto-generate name if not provided
1573
				if ( empty( $pod_params['name'] ) && ! empty( $pod_params['label'] ) ) {
1574
					$pod_params['name'] = pods_clean_name( $pod_params['label'] );
1575
				}
1576
1577
				if ( empty( $pod_params['name'] ) ) {
1578
					return pods_error( 'Please enter a Name for this Pod', $this );
1579
				}
1580
			}
1581
		} elseif ( 'extend' === $params->create_extend ) {
1582
			$pod_params['type'] = $params->extend_pod_type;
1583
1584
			if ( 'post_type' === $pod_params['type'] ) {
1585
				$pod_params['storage'] = $params->extend_storage;
1586
1587
				if ( pods_tableless() ) {
1588
					$pod_params['storage'] = 'meta';
1589
				}
1590
1591
				$pod_params['name'] = $params->extend_post_type;
1592
			} elseif ( 'taxonomy' === $pod_params['type'] ) {
1593
				$pod_params['storage'] = $params->extend_storage_taxonomy;
1594
1595
				if ( pods_tableless() ) {
1596
					$pod_params['storage'] = ( function_exists( 'get_term_meta' ) ? 'meta' : 'none' );
1597
				}
1598
1599
				$pod_params['name'] = $params->extend_taxonomy;
1600
			} elseif ( 'table' === $pod_params['type'] ) {
1601
				$pod_params['storage'] = 'table';
1602
				$pod_params['name']    = $params->extend_table;
1603
			} else {
1604
				$pod_params['storage'] = $params->extend_storage;
1605
1606
				if ( pods_tableless() ) {
1607
					$pod_params['storage'] = 'meta';
1608
				}
1609
1610
				$pod_params['name'] = $params->extend_pod_type;
1611
			}
1612
1613
			$pod_params['label']  = ucwords( str_replace( '_', ' ', $pod_params['name'] ) );
1614
			$pod_params['object'] = $pod_params['name'];
1615
		}
1616
1617
		if ( empty( $pod_params['object'] ) ) {
1618
			if ( 'post_type' === $pod_params['type'] ) {
1619
				$check = get_post_type_object( $pod_params['name'] );
1620
1621
				if ( ! empty( $check ) ) {
1622
					return pods_error( sprintf( __( 'Post Type %s already exists, try extending it instead', 'pods' ), $pod_params['name'] ), $this );
1623
				}
1624
1625
				$pod_params['options']['supports_title']  = 1;
1626
				$pod_params['options']['supports_editor'] = 1;
1627
			} elseif ( 'taxonomy' === $pod_params['type'] ) {
1628
				$check = get_taxonomy( $pod_params['name'] );
1629
1630
				if ( ! empty( $check ) ) {
1631
					return pods_error( sprintf( __( 'Taxonomy %s already exists, try extending it instead', 'pods' ), $pod_params['name'] ), $this );
1632
				}
1633
			}
1634
		}
1635
1636
		if ( ! empty( $pod_params ) ) {
1637
			return $this->save_pod( $pod_params );
1638
		}
1639
1640
		return false;
1641
	}
1642
1643
	/**
1644
	 * Add or edit a Pod
1645
	 *
1646
	 * $params['id'] int The Pod ID
1647
	 * $params['name'] string The Pod name
1648
	 * $params['label'] string The Pod label
1649
	 * $params['type'] string The Pod type
1650
	 * $params['object'] string The object being extended (if any)
1651
	 * $params['storage'] string The Pod storage
1652
	 * $params['options'] array Options
1653
	 *
1654
	 * @param array    $params    An associative array of parameters
1655
	 * @param bool     $sanitized (optional) Decides whether the params have been sanitized before being passed, will
1656
	 *                            sanitize them if false.
1657
	 * @param bool|int $db        (optional) Whether to save into the DB or just return Pod array.
1658
	 *
1659
	 * @return int Pod ID
1660
	 * @since 1.7.9
1661
	 */
1662
	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...
1663
1664
		$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...
1665
		$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...
1666
1667
		$load_params = (object) $params;
1668
1669
		if ( isset( $load_params->id ) && isset( $load_params->name ) ) {
1670
			unset( $load_params->name );
1671
		}
1672
1673
		if ( isset( $load_params->old_name ) ) {
1674
			$load_params->name = $load_params->old_name;
1675
		}
1676
1677
		$load_params->table_info = true;
1678
1679
		$pod = $this->load_pod( $load_params, false );
1680
1681
		$params = (object) $params;
1682
1683
		if ( false === $sanitized ) {
1684
			$params = pods_sanitize( $params );
1685
		}
1686
1687
		$old_id = $old_name = $old_storage = null;
1688
1689
		$old_fields = $old_options = array();
1690
1691
		if ( isset( $params->name ) && ! isset( $params->object ) ) {
1692
			$params->name = pods_clean_name( $params->name );
1693
		}
1694
1695
		if ( ! empty( $pod ) ) {
1696
			if ( isset( $params->id ) && 0 < $params->id ) {
1697
				$old_id = $params->id;
1698
			}
1699
1700
			$params->id = $pod['id'];
1701
1702
			$old_name    = $pod['name'];
1703
			$old_storage = $pod['storage'];
1704
			$old_fields  = $pod['fields'];
1705
			$old_options = $pod['options'];
1706
1707
			if ( ! isset( $params->name ) && empty( $params->name ) ) {
1708
				$params->name = $pod['name'];
1709
			}
1710
1711
			if ( $old_name !== $params->name && false !== $this->pod_exists( array( 'name' => $params->name ) ) ) {
1712
				return pods_error( sprintf( __( 'Pod %s already exists, you cannot rename %s to that', 'pods' ), $params->name, $old_name ), $this );
1713
			}
1714
1715
			if ( $old_name !== $params->name && in_array( $pod['type'], array(
1716
					'user',
1717
					'comment',
1718
					'media'
1719
				) ) && in_array( $pod['object'], array( 'user', 'comment', 'media' ) ) ) {
1720
				return pods_error( sprintf( __( 'Pod %s cannot be renamed, it extends an existing WP Object', 'pods' ), $old_name ), $this );
1721
			}
1722
1723
			if ( $old_name !== $params->name && in_array( $pod['type'], array(
1724
					'post_type',
1725
					'taxonomy'
1726
				) ) && ! empty( $pod['object'] ) && $pod['object'] == $old_name ) {
1727
				return pods_error( sprintf( __( 'Pod %s cannot be renamed, it extends an existing WP Object', 'pods' ), $old_name ), $this );
1728
			}
1729
1730
			if ( $old_id != $params->id ) {
1731
				if ( $params->type == $pod['type'] && isset( $params->object ) && $params->object == $pod['object'] ) {
1732
					return pods_error( sprintf( __( 'Pod using %s already exists, you can not reuse an object across multiple pods', 'pods' ), $params->object ), $this );
1733
				} else {
1734
					return pods_error( sprintf( __( 'Pod %s already exists', 'pods' ), $params->name ), $this );
1735
				}
1736
			}
1737
		} elseif ( in_array( $params->name, array(
1738
				'order',
1739
				'orderby',
1740
				'post_type'
1741
			) ) && 'post_type' === pods_var( 'type', $params ) ) {
1742
			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 );
1743
		} else {
1744
			$pod = array(
1745
				'id'          => 0,
1746
				'name'        => $params->name,
1747
				'label'       => $params->name,
1748
				'description' => '',
1749
				'type'        => 'pod',
1750
				'storage'     => 'table',
1751
				'object'      => '',
1752
				'alias'       => '',
1753
				'options'     => array(),
1754
				'fields'      => array()
1755
			);
1756
		}
1757
1758
		// Blank out fields and options for AJAX calls (everything should be sent to it for a full overwrite)
1759
		if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
1760
			$pod['fields']  = array();
1761
			$pod['options'] = array();
1762
		}
1763
1764
		// Setup options
1765
		$options = get_object_vars( $params );
1766
1767
		if ( isset( $options['method'] ) ) {
1768
			unset( $options['method'] );
1769
		}
1770
1771
		$options_ignore = array(
1772
			'object_type',
1773
			'object_name',
1774
			'table',
1775
			'meta_table',
1776
			'pod_table',
1777
			'field_id',
1778
			'field_index',
1779
			'field_slug',
1780
			'field_type',
1781
			'field_parent',
1782
			'field_parent_select',
1783
			'meta_field_id',
1784
			'meta_field_index',
1785
			'meta_field_value',
1786
			'pod_field_id',
1787
			'pod_field_index',
1788
			'object_fields',
1789
			'join',
1790
			'where',
1791
			'where_default',
1792
			'orderby',
1793
			'pod',
1794
			'recurse',
1795
			'table_info',
1796
			'attributes',
1797
			'group',
1798
			'grouped',
1799
			'developer_mode',
1800
			'dependency',
1801
			'depends-on',
1802
			'excludes-on'
1803
		);
1804
1805
		foreach ( $options_ignore as $ignore ) {
1806
			if ( isset( $options[ $ignore ] ) ) {
1807
				unset( $options[ $ignore ] );
1808
			}
1809
		}
1810
1811
		$exclude = array(
1812
			'id',
1813
			'name',
1814
			'label',
1815
			'description',
1816
			'type',
1817
			'storage',
1818
			'object',
1819
			'alias',
1820
			'options',
1821
			'fields'
1822
		);
1823
1824
		foreach ( $exclude as $k => $exclude_field ) {
1825
			$aliases = array( $exclude_field );
1826
1827
			if ( is_array( $exclude_field ) ) {
1828
				$aliases       = array_merge( array( $k ), $exclude_field );
1829
				$exclude_field = $k;
1830
			}
1831
1832
			foreach ( $aliases as $alias ) {
1833
				if ( isset( $options[ $alias ] ) ) {
1834
					$pod[ $exclude_field ] = pods_trim( $options[ $alias ] );
1835
1836
					unset( $options[ $alias ] );
1837
				}
1838
			}
1839
		}
1840
1841
		if ( pods_tableless() && ! in_array( $pod['type'], array( 'settings', 'table' ) ) ) {
1842
			if ( 'pod' === $pod['type'] ) {
1843
				$pod['type'] = 'post_type';
1844
			}
1845
1846
			if ( 'table' === $pod['storage'] ) {
1847
				if ( 'taxonomy' === $pod['type'] && ! function_exists( 'get_term_meta' ) ) {
1848
					$pod['storage'] = 'none';
1849
				} else {
1850
					$pod['storage'] = 'meta';
1851
				}
1852
			}
1853
		}
1854
1855
		$pod['options']['type']    = $pod['type'];
1856
		$pod['options']['storage'] = $pod['storage'];
1857
		$pod['options']['object']  = $pod['object'];
1858
		$pod['options']['alias']   = $pod['alias'];
1859
1860
		$pod['options'] = array_merge( $pod['options'], $options );
1861
1862
		/**
1863
		 * @var WP_Query
1864
		 */
1865
		global $wp_query;
1866
1867
		$reserved_query_vars = array(
1868
			'post_type',
1869
			'taxonomy',
1870
			'output'
1871
		);
1872
1873
		if ( is_object( $wp_query ) ) {
1874
			$reserved_query_vars = array_merge( $reserved_query_vars, array_keys( $wp_query->fill_query_vars( array() ) ) );
1875
		}
1876
1877
		if ( isset( $pod['options']['query_var_string'] ) ) {
1878
			if ( in_array( $pod['options']['query_var_string'], $reserved_query_vars ) ) {
1879
				$pod['options']['query_var_string'] = $pod['options']['type'] . '_' . $pod['options']['query_var_string'];
1880
			}
1881
		}
1882
1883
		if ( isset( $pod['options']['query_var'] ) ) {
1884
			if ( in_array( $pod['options']['query_var'], $reserved_query_vars ) ) {
1885
				$pod['options']['query_var'] = $pod['options']['type'] . '_' . $pod['options']['query_var'];
1886
			}
1887
		}
1888
1889
		if ( strlen( $pod['label'] ) < 1 ) {
1890
			$pod['label'] = $pod['name'];
1891
		}
1892
1893
		if ( 'post_type' === $pod['type'] ) {
1894
			// Max length for post types are 20 characters
1895
			$pod['name'] = substr( $pod['name'], 0, 20 );
1896
		} elseif ( 'taxonomy' === $pod['type'] ) {
1897
			// Max length for taxonomies are 32 characters
1898
			$pod['name'] = substr( $pod['name'], 0, 32 );
1899
		}
1900
1901
		$params->id   = $pod['id'];
1902
		$params->name = $pod['name'];
1903
1904
		if ( null !== $old_name && $old_name !== $params->name && empty( $pod['object'] ) ) {
1905
			if ( 'post_type' === $pod['type'] ) {
1906
				$check = get_post_type_object( $params->name );
1907
1908
				if ( ! empty( $check ) ) {
1909
					return pods_error( sprintf( __( 'Post Type %s already exists, you cannot rename %s to that', 'pods' ), $params->name, $old_name ), $this );
1910
				}
1911
			} elseif ( 'taxonomy' === $pod['type'] ) {
1912
				$check = get_taxonomy( $params->name );
1913
1914
				if ( ! empty( $check ) ) {
1915
					return pods_error( sprintf( __( 'Taxonomy %s already exists, you cannot rename %s to that', 'pods' ), $params->name, $old_name ), $this );
1916
				}
1917
			}
1918
		}
1919
1920
		$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...
1921
1922
		// Add new pod
1923
		if ( empty( $params->id ) ) {
1924
			if ( strlen( $params->name ) < 1 ) {
1925
				return pods_error( __( 'Pod name cannot be empty', 'pods' ), $this );
1926
			}
1927
1928
			$post_data = array(
1929
				'post_name'    => $pod['name'],
1930
				'post_title'   => $pod['label'],
1931
				'post_content' => $pod['description'],
1932
				'post_type'    => '_pods_pod',
1933
				'post_status'  => 'publish'
1934
			);
1935
1936
			if ( 'pod' === $pod['type'] && ( ! is_array( $pod['fields'] ) || empty( $pod['fields'] ) ) ) {
1937
				$pod['fields'] = array();
1938
1939
				$pod['fields']['name'] = array(
1940
					'name'    => 'name',
1941
					'label'   => 'Name',
1942
					'type'    => 'text',
1943
					'options' => array(
1944
						'required' => '1'
1945
					)
1946
				);
1947
1948
				$pod['fields']['created'] = array(
1949
					'name'    => 'created',
1950
					'label'   => 'Date Created',
1951
					'type'    => 'datetime',
1952
					'options' => array(
1953
						'datetime_format'      => 'ymd_slash',
1954
						'datetime_time_type'   => '12',
1955
						'datetime_time_format' => 'h_mm_ss_A'
1956
					)
1957
				);
1958
1959
				$pod['fields']['modified'] = array(
1960
					'name'    => 'modified',
1961
					'label'   => 'Date Modified',
1962
					'type'    => 'datetime',
1963
					'options' => array(
1964
						'datetime_format'      => 'ymd_slash',
1965
						'datetime_time_type'   => '12',
1966
						'datetime_time_format' => 'h_mm_ss_A'
1967
					)
1968
				);
1969
1970
				$pod['fields']['author'] = array(
1971
					'name'        => 'author',
1972
					'label'       => 'Author',
1973
					'type'        => 'pick',
1974
					'pick_object' => 'user',
1975
					'options'     => array(
1976
						'pick_format_type'   => 'single',
1977
						'pick_format_single' => 'autocomplete',
1978
						'default_value'      => '{@user.ID}'
1979
					)
1980
				);
1981
1982
				$pod['fields']['permalink'] = array(
1983
					'name'        => 'permalink',
1984
					'label'       => 'Permalink',
1985
					'type'        => 'slug',
1986
					'description' => 'Leave blank to auto-generate from Name'
1987
				);
1988
1989
				if ( ! isset( $pod['options']['pod_index'] ) ) {
1990
					$pod['options']['pod_index'] = 'name';
1991
				}
1992
			}
1993
1994
			$pod = $this->do_hook( 'save_pod_default_pod', $pod, $params, $sanitized, $db );
1995
1996
			$field_table_operation = false;
1997
		} else {
1998
			$post_data = array(
1999
				'ID'           => $pod['id'],
2000
				'post_name'    => $pod['name'],
2001
				'post_title'   => $pod['label'],
2002
				'post_content' => $pod['description'],
2003
				'post_status'  => 'publish'
2004
			);
2005
		}
2006
2007
		if ( true === $db ) {
2008
			if ( ! has_filter( 'wp_unique_post_slug', array( $this, 'save_slug_fix' ) ) ) {
2009
				add_filter( 'wp_unique_post_slug', array( $this, 'save_slug_fix' ), 100, 6 );
2010
			}
2011
2012
			$conflicted = false;
2013
2014
			// Headway compatibility fix
2015
			if ( has_filter( 'wp_insert_post_data', 'headway_clean_slug', 0 ) ) {
2016
				remove_filter( 'wp_insert_post_data', 'headway_clean_slug', 0 );
2017
2018
				$conflicted = true;
2019
			}
2020
2021
			$params->id = $this->save_wp_object( 'post', $post_data, $pod['options'], true, true );
2022
2023
			if ( $conflicted ) {
2024
				add_filter( 'wp_insert_post_data', 'headway_clean_slug', 0 );
2025
			}
2026
2027
			if ( false === $params->id ) {
2028
				return pods_error( __( 'Cannot save Pod', 'pods' ), $this );
2029
			}
2030
		} elseif ( empty( $params->id ) ) {
2031
			$params->id = (int) $db;
2032
		}
2033
2034
		$pod['id'] = $params->id;
2035
2036
		// Setup / update tables
2037
		if ( 'table' !== $pod['type'] && 'table' === $pod['storage'] && $old_storage !== $pod['storage'] && $db ) {
2038
			$definitions = array( "`id` BIGINT(20) UNSIGNED AUTO_INCREMENT PRIMARY KEY" );
2039
2040
			$defined_fields = array();
2041
2042
			foreach ( $pod['fields'] as $field ) {
2043
				if ( ! is_array( $field ) || ! isset( $field['name'] ) || in_array( $field['name'], $defined_fields ) ) {
2044
					continue;
2045
				}
2046
2047
				$defined_fields[] = $field['name'];
2048
2049
				if ( ! in_array( $field['type'], $tableless_field_types ) || ( 'pick' === $field['type'] && in_array( pods_var( 'pick_object', $field ), $simple_tableless_objects ) ) ) {
2050
					$definition = $this->get_field_definition( $field['type'], array_merge( $field, pods_var_raw( 'options', $field, array() ) ) );
2051
2052
					if ( 0 < strlen( $definition ) ) {
2053
						$definitions[] = "`{$field['name']}` " . $definition;
2054
					}
2055
				}
2056
			}
2057
2058
			pods_query( "DROP TABLE IF EXISTS `@wp_pods_{$params->name}`" );
2059
2060
			$result = pods_query( "CREATE TABLE `@wp_pods_{$params->name}` (" . implode( ', ', $definitions ) . ") DEFAULT CHARSET utf8", $this );
2061
2062
			if ( empty( $result ) ) {
2063
				return pods_error( __( 'Cannot add Database Table for Pod', 'pods' ), $this );
2064
			}
2065
2066
		} elseif ( 'table' !== $pod['type'] && 'table' === $pod['storage'] && $pod['storage'] == $old_storage && null !== $old_name && $old_name !== $params->name && $db ) {
2067
			$result = pods_query( "ALTER TABLE `@wp_pods_{$old_name}` RENAME `@wp_pods_{$params->name}`", $this );
2068
2069
			if ( empty( $result ) ) {
2070
				return pods_error( __( 'Cannot update Database Table for Pod', 'pods' ), $this );
2071
			}
2072
		}
2073
2074
		/**
2075
		 * @var $wpdb wpdb
2076
		 */
2077
		global $wpdb;
2078
2079
		if ( null !== $old_name && $old_name !== $params->name && $db ) {
2080
			// Rename items in the DB pointed at the old WP Object names
2081
			if ( 'post_type' === $pod['type'] && empty( $pod['object'] ) ) {
2082
				$this->rename_wp_object_type( 'post', $old_name, $params->name );
2083
			} elseif ( 'taxonomy' === $pod['type'] && empty( $pod['object'] ) ) {
2084
				$this->rename_wp_object_type( 'taxonomy', $old_name, $params->name );
2085
			} elseif ( 'comment' === $pod['type'] && empty( $pod['object'] ) ) {
2086
				$this->rename_wp_object_type( 'comment', $old_name, $params->name );
2087
			} elseif ( 'settings' === $pod['type'] ) {
2088
				$this->rename_wp_object_type( 'settings', $old_name, $params->name );
2089
			}
2090
2091
			// Sync any related fields if the name has changed
2092
			$fields = pods_query( "
2093
                SELECT `p`.`ID`
2094
                FROM `{$wpdb->posts}` AS `p`
2095
                LEFT JOIN `{$wpdb->postmeta}` AS `pm` ON `pm`.`post_id` = `p`.`ID`
2096
                LEFT JOIN `{$wpdb->postmeta}` AS `pm2` ON `pm2`.`post_id` = `p`.`ID`
2097
                WHERE
2098
                    `p`.`post_type` = '_pods_field'
2099
                    AND `pm`.`meta_key` = 'pick_object'
2100
                    AND (
2101
                    	`pm`.`meta_value` = 'pod'
2102
                    	OR `pm`.`meta_value` = '" . $pod['type'] . "'
2103
					)
2104
                    AND `pm2`.`meta_key` = 'pick_val'
2105
                    AND `pm2`.`meta_value` = '{$old_name}'
2106
            " );
2107
2108
			if ( ! empty( $fields ) ) {
2109
				foreach ( $fields as $field ) {
2110
					update_post_meta( $field->ID, 'pick_object', $pod['type'] );
2111
					update_post_meta( $field->ID, 'pick_val', $params->name );
2112
				}
2113
			}
2114
2115
			$fields = pods_query( "
2116
                SELECT `p`.`ID`
2117
                FROM `{$wpdb->posts}` AS `p`
2118
                LEFT JOIN `{$wpdb->postmeta}` AS `pm` ON `pm`.`post_id` = `p`.`ID`
2119
                WHERE
2120
                    `p`.`post_type` = '_pods_field'
2121
                    AND `pm`.`meta_key` = 'pick_object'
2122
                    AND (
2123
                    	`pm`.`meta_value` = 'pod-{$old_name}'
2124
                    	OR `pm`.`meta_value` = '" . $pod['type'] . "-{$old_name}'
2125
					)
2126
            " );
2127
2128
			if ( ! empty( $fields ) ) {
2129
				foreach ( $fields as $field ) {
2130
					update_post_meta( $field->ID, 'pick_object', $pod['type'] );
2131
					update_post_meta( $field->ID, 'pick_val', $params->name );
2132
				}
2133
			}
2134
		}
2135
2136
		// Sync built-in options for post types and taxonomies
2137
		if ( in_array( $pod['type'], array( 'post_type', 'taxonomy' ) ) && empty( $pod['object'] ) && $db ) {
2138
			// Build list of 'built_in' for later
2139
			$built_in = array();
2140
2141
			foreach ( $pod['options'] as $key => $val ) {
2142
				if ( false === strpos( $key, 'built_in_' ) ) {
2143
					continue;
2144
				} elseif ( false !== strpos( $key, 'built_in_post_types_' ) ) {
2145
					$built_in_type = 'post_type';
2146
				} elseif ( false !== strpos( $key, 'built_in_taxonomies_' ) ) {
2147
					$built_in_type = 'taxonomy';
2148
				} else {
2149
					continue;
2150
				}
2151
2152
				if ( $built_in_type == $pod['type'] ) {
2153
					continue;
2154
				}
2155
2156
				if ( ! isset( $built_in[ $built_in_type ] ) ) {
2157
					$built_in[ $built_in_type ] = array();
2158
				}
2159
2160
				$built_in_object = str_replace( array( 'built_in_post_types_', 'built_in_taxonomies_' ), '', $key );
2161
2162
				$built_in[ $built_in_type ][ $built_in_object ] = (int) $val;
2163
			}
2164
2165
			$lookup_option = $lookup_built_in = false;
2166
2167
			$lookup_name = $pod['name'];
2168
2169
			if ( 'post_type' === $pod['type'] ) {
2170
				$lookup_option   = 'built_in_post_types_' . $lookup_name;
2171
				$lookup_built_in = 'taxonomy';
2172
			} elseif ( 'taxonomy' === $pod['type'] ) {
2173
				$lookup_option   = 'built_in_taxonomies_' . $lookup_name;
2174
				$lookup_built_in = 'post_type';
2175
			}
2176
2177
			if ( ! empty( $lookup_option ) && ! empty( $lookup_built_in ) && isset( $built_in[ $lookup_built_in ] ) ) {
2178
				foreach ( $built_in[ $lookup_built_in ] as $built_in_object => $val ) {
2179
					$search_val = 1;
2180
2181
					if ( 1 == $val ) {
2182
						$search_val = 0;
2183
					}
2184
2185
					$query = "SELECT p.ID FROM {$wpdb->posts} AS p
2186
                                LEFT JOIN {$wpdb->postmeta} AS pm ON pm.post_id = p.ID AND pm.meta_key = '{$lookup_option}'
2187
                                LEFT JOIN {$wpdb->postmeta} AS pm2 ON pm2.post_id = p.ID AND pm2.meta_key = 'type' AND pm2.meta_value = '{$lookup_built_in}'
2188
                                LEFT JOIN {$wpdb->postmeta} AS pm3 ON pm3.post_id = p.ID AND pm3.meta_key = 'object' AND pm3.meta_value = ''
2189
                                WHERE p.post_type = '_pods_pod' AND p.post_name = '{$built_in_object}'
2190
                                    AND pm2.meta_id IS NOT NULL
2191
                                    AND ( pm.meta_id IS NULL OR pm.meta_value = {$search_val} )";
2192
2193
					$results = pods_query( $query );
2194
2195
					if ( ! empty( $results ) ) {
2196
						foreach ( $results as $the_pod ) {
2197
							delete_post_meta( $the_pod->ID, $lookup_option );
2198
2199
							add_post_meta( $the_pod->ID, $lookup_option, $val );
2200
						}
2201
					}
2202
				}
2203
			}
2204
		}
2205
2206
		$saved  = array();
2207
		$errors = array();
2208
2209
		$field_index_change = false;
2210
		$field_index_id     = 0;
2211
2212
		$id_required = false;
2213
2214
		$field_index = pods_var( 'pod_index', $pod['options'], 'id', null, true );
2215
2216
		if ( 'pod' === $pod['type'] && ! empty( $pod['fields'] ) && isset( $pod['fields'][ $field_index ] ) ) {
2217
			$field_index_id = $pod['fields'][ $field_index ];
2218
		}
2219
2220
		if ( isset( $params->fields ) || ( defined( 'DOING_AJAX' ) && DOING_AJAX ) ) {
2221
			$fields = array();
2222
2223
			if ( isset( $params->fields ) ) {
2224
				$params->fields = (array) $params->fields;
2225
2226
				$weight = 0;
2227
2228
				foreach ( $params->fields as $field ) {
2229
					if ( ! is_array( $field ) || ! isset( $field['name'] ) ) {
2230
						continue;
2231
					}
2232
2233
					if ( ! isset( $field['weight'] ) ) {
2234
						$field['weight'] = $weight;
2235
2236
						$weight ++;
2237
					}
2238
2239
					$fields[ $field['name'] ] = $field;
2240
				}
2241
			}
2242
2243
			$weight = 0;
2244
2245
			$saved_field_ids = array();
2246
2247
			foreach ( $pod['fields'] as $k => $field ) {
2248
				if ( ! empty( $old_id ) && ( ! is_array( $field ) || ! isset( $field['name'] ) || ! isset( $fields[ $field['name'] ] ) ) ) {
2249
					// Iterative change handling for setup-edit.php
2250
					if ( ! is_array( $field ) && isset( $old_fields[ $k ] ) ) {
2251
						$saved[ $old_fields[ $k ]['name'] ] = true;
2252
					}
2253
2254
					continue;
2255
				}
2256
2257
				if ( ! empty( $old_id ) ) {
2258
					$field = array_merge( $field, $fields[ $field['name'] ] );
2259
				}
2260
2261
				$field['pod'] = $pod;
2262
2263
				if ( ! isset( $field['weight'] ) ) {
2264
					$field['weight'] = $weight;
2265
2266
					$weight ++;
2267
				}
2268
2269
				if ( 0 < $field_index_id && pods_var( 'id', $field ) == $field_index_id ) {
2270
					$field_index_change = $field['name'];
2271
				}
2272
2273
				if ( 0 < pods_var( 'id', $field ) ) {
2274
					$id_required = true;
2275
				}
2276
2277
				if ( $id_required ) {
2278
					$field['id_required'] = true;
2279
				}
2280
2281
				$field_data = $field;
2282
2283
				$field = $this->save_field( $field_data, $field_table_operation, true, $db );
2284
2285
				if ( true !== $db ) {
2286
					$pod['fields'][ $k ] = $field;
2287
					$saved_field_ids[]   = $field['id'];
2288
				} else {
2289
					if ( ! empty( $field ) && 0 < $field ) {
2290
						$saved[ $field_data['name'] ] = true;
2291
						$saved_field_ids[]            = $field;
2292
					} else {
2293
						$errors[] = sprintf( __( 'Cannot save the %s field', 'pods' ), $field_data['name'] );
2294
					}
2295
				}
2296
			}
2297
2298
			if ( true === $db ) {
2299
				foreach ( $old_fields as $field ) {
2300
					if ( isset( $pod['fields'][ $field['name'] ] ) || isset( $saved[ $field['name'] ] ) || in_array( $field['id'], $saved_field_ids ) ) {
2301
						continue;
2302
					}
2303
2304
					if ( $field['id'] == $field_index_id ) {
2305
						$field_index_change = 'id';
2306
					} elseif ( $field['name'] == $field_index ) {
2307
						$field_index_change = 'id';
2308
					}
2309
2310
					$this->delete_field( array(
2311
						'id'   => (int) $field['id'],
2312
						'name' => $field['name'],
2313
						'pod'  => $pod
2314
					), $field_table_operation );
2315
				}
2316
			}
2317
2318
			// Update field index if the name has changed or the field has been removed
2319
			if ( false !== $field_index_change && true === $db ) {
2320
				update_post_meta( $pod['id'], 'pod_index', $field_index_change );
2321
			}
2322
		}
2323
2324
		if ( ! empty( $errors ) ) {
2325
			return pods_error( $errors, $this );
2326
		}
2327
2328
		$this->cache_flush_pods( $pod );
2329
2330
		$refresh_pod = $this->load_pod( array( 'name' => $pod['name'] ), false );
2331
2332
		if ( $refresh_pod ) {
2333
			$pod = $refresh_pod;
2334
		}
2335
2336
		if ( 'post_type' === $pod['type'] ) {
2337
			PodsMeta::$post_types[ $pod['id'] ] = $pod;
2338
		} elseif ( 'taxonomy' === $pod['type'] ) {
2339
			PodsMeta::$taxonomies[ $pod['id'] ] = $pod;
2340
		} elseif ( 'media' === $pod['type'] ) {
2341
			PodsMeta::$media[ $pod['id'] ] = $pod;
2342
		} elseif ( 'user' === $pod['type'] ) {
2343
			PodsMeta::$user[ $pod['id'] ] = $pod;
2344
		} elseif ( 'comment' === $pod['type'] ) {
2345
			PodsMeta::$comment[ $pod['id'] ] = $pod;
2346
		}
2347
2348
		// Register Post Types / Taxonomies post-registration from PodsInit
2349
		if ( ! empty( PodsInit::$content_types_registered ) && in_array( $pod['type'], array(
2350
				'post_type',
2351
				'taxonomy'
2352
			) ) && empty( $pod['object'] ) ) {
2353
			global $pods_init;
2354
2355
			$pods_init->setup_content_types( true );
2356
		}
2357
2358
		if ( true === $db ) {
2359
			return $pod['id'];
2360
		} else {
2361
			return $pod;
2362
		}
2363
	}
2364
2365
	/**
2366
	 * Add or edit a field within a Pod
2367
	 *
2368
	 * $params['id'] int Field ID (id OR pod_id+pod+name required)
2369
	 * $params['pod_id'] int Pod ID (id OR pod_id+pod+name required)
2370
	 * $params['pod'] string Pod name (id OR pod_id+pod+name required)
2371
	 * $params['name'] string Field name (id OR pod_id+pod+name required)
2372
	 * $params['label'] string (optional) Field label
2373
	 * $params['type'] string (optional) Field type (avatar, boolean, code, color, currency, date, datetime, email,
2374
	 * file, number, paragraph, password, phone, pick, slug, text, time, website, wysiwyg)
2375
	 * $params['pick_object'] string (optional) Related Object (for relationships)
2376
	 * $params['pick_val'] string (optional) Related Object name (for relationships)
2377
	 * $params['sister_id'] int (optional) Related Field ID (for bidirectional relationships)
2378
	 * $params['weight'] int (optional) Order in which the field appears
2379
	 * $params['options'] array (optional) Options
2380
	 *
2381
	 * @param array    $params          An associative array of parameters
2382
	 * @param bool     $table_operation (optional) Whether or not to handle table operations
2383
	 * @param bool     $sanitized       (optional) Decides wether the params have been sanitized before being passed,
2384
	 *                                  will sanitize them if false.
2385
	 * @param bool|int $db              (optional) Whether to save into the DB or just return field array.
2386
	 *
2387
	 * @return int|array The field ID or field array (if !$db)
2388
	 * @since 1.7.9
2389
	 */
2390
	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...
2391
2392
		/**
2393
		 * @var $wpdb wpdb
2394
		 */
2395
		global $wpdb;
2396
2397
		if ( true !== $db ) {
2398
			$table_operation = false;
2399
		}
2400
2401
		$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...
2402
		$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...
2403
2404
		$params = (object) $params;
2405
2406
		if ( false === $sanitized ) {
2407
			$params = pods_sanitize( $params );
2408
		}
2409
2410
		if ( isset( $params->pod_id ) ) {
2411
			$params->pod_id = pods_absint( $params->pod_id );
2412
		}
2413
2414
		if ( true !== $db ) {
2415
			$params->pod_id = (int) $db;
2416
		}
2417
2418
		$pod         = null;
2419
		$save_pod    = false;
2420
		$id_required = false;
2421
2422
		if ( isset( $params->id_required ) ) {
2423
			unset( $params->id_required );
2424
2425
			$id_required = true;
2426
		}
2427
2428
		if ( ( ! isset( $params->pod ) || empty( $params->pod ) ) && ( ! isset( $params->pod_id ) || empty( $params->pod_id ) ) ) {
2429
			return pods_error( __( 'Pod ID or name is required', 'pods' ), $this );
2430
		}
2431
2432
		if ( isset( $params->pod ) && is_array( $params->pod ) ) {
2433
			$pod = $params->pod;
2434
2435
			$save_pod = true;
2436
		} elseif ( ( ! isset( $params->pod_id ) || empty( $params->pod_id ) ) && ( true === $db || 0 < $db ) ) {
2437
			$pod = $this->load_pod( array( 'name' => $params->pod, 'table_info' => true ) );
2438
		} elseif ( ! isset( $params->pod ) && ( true === $db || 0 < $db ) ) {
2439
			$pod = $this->load_pod( array( 'id' => $params->pod_id, 'table_info' => true ) );
2440
		} elseif ( true === $db || 0 < $db ) {
2441
			$pod = $this->load_pod( array( 'id' => $params->pod_id, 'name' => $params->pod, 'table_info' => true ) );
2442
		}
2443
2444
		if ( empty( $pod ) && true === $db ) {
2445
			return pods_error( __( 'Pod not found', 'pods' ), $this );
2446
		}
2447
2448
		$params->pod_id   = $pod['id'];
2449
		$params->pod      = $pod['name'];
2450
		$params->pod_data = $pod;
2451
2452
		$params->name = pods_clean_name( $params->name, true, ( 'meta' === $pod['storage'] ? false : true ) );
2453
2454
		if ( ! isset( $params->id ) ) {
2455
			$params->id = 0;
2456
		}
2457
2458
		if ( empty( $params->name ) ) {
2459
			return pods_error( 'Pod field name is required', $this );
2460
		}
2461
2462
		$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 2961 which is incompatible with the return type documented by PodsAPI::save_field of type integer|array.
Loading history...
2463
2464
		unset( $params->pod_data );
2465
2466
		$old_id = $old_name = $old_type = $old_definition = $old_simple = $old_options = $old_sister_id = null;
2467
2468
		if ( ! empty( $field ) ) {
2469
			$old_id        = pods_var( 'id', $field );
2470
			$old_name      = pods_clean_name( $field['name'], true, ( 'meta' === $pod['storage'] ? false : true ) );
2471
			$old_type      = $field['type'];
2472
			$old_options   = $field['options'];
2473
			$old_sister_id = (int) pods_var( 'sister_id', $old_options, 0 );
2474
2475
			$old_simple = ( 'pick' === $old_type && in_array( pods_var( 'pick_object', $field ), $simple_tableless_objects ) );
2476
2477
			if ( isset( $params->name ) && ! empty( $params->name ) ) {
2478
				$field['name'] = $params->name;
2479
			}
2480
2481
			if ( $old_name !== $field['name'] && false !== $this->field_exists( $params ) ) {
2482
				return pods_error( sprintf( __( 'Field %s already exists, you cannot rename %s to that', 'pods' ), $field['name'], $old_name ), $this );
2483
			}
2484
2485
			if ( ( $id_required || ! empty( $params->id ) ) && ( empty( $old_id ) || $old_id != $params->id ) ) {
2486
				return pods_error( sprintf( __( 'Field %s already exists', 'pods' ), $field['name'] ), $this );
2487
			}
2488
2489
			if ( empty( $params->id ) ) {
2490
				$params->id = $old_id;
2491
			}
2492
2493
			if ( ! in_array( $old_type, $tableless_field_types ) || $old_simple ) {
2494
				$definition = $this->get_field_definition( $old_type, array_merge( $field, $old_options ) );
2495
2496
				if ( 0 < strlen( $definition ) ) {
2497
					$old_definition = "`{$old_name}` " . $definition;
2498
				}
2499
			}
2500
		} else {
2501
			$field = array(
2502
				'id'          => 0,
2503
				'pod_id'      => $params->pod_id,
2504
				'name'        => $params->name,
2505
				'label'       => $params->name,
2506
				'description' => '',
2507
				'type'        => 'text',
2508
				'pick_object' => '',
2509
				'pick_val'    => '',
2510
				'sister_id'   => '',
2511
				'weight'      => null,
2512
				'options'     => array()
2513
			);
2514
		}
2515
2516
		// Setup options
2517
		$options = get_object_vars( $params );
2518
2519
		$options_ignore = array(
2520
			'method',
2521
			'table_info',
2522
			'attributes',
2523
			'group',
2524
			'grouped',
2525
			'developer_mode',
2526
			'dependency',
2527
			'depends-on',
2528
			'excludes-on'
2529
		);
2530
2531
		foreach ( $options_ignore as $ignore ) {
2532
			if ( isset( $options[ $ignore ] ) ) {
2533
				unset( $options[ $ignore ] );
2534
			}
2535
		}
2536
2537
		if ( isset( $options['method'] ) ) {
2538
			unset( $options['method'] );
2539
		} elseif ( isset( $options['table_info'] ) ) {
2540
			unset( $options['table_info'] );
2541
		}
2542
2543
		$exclude = array(
2544
			'id',
2545
			'pod_id',
2546
			'pod',
2547
			'name',
2548
			'label',
2549
			'description',
2550
			'type',
2551
			'pick_object',
2552
			'pick_val',
2553
			'sister_id',
2554
			'weight',
2555
			'options'
2556
		);
2557
2558
		foreach ( $exclude as $k => $exclude_field ) {
2559
			$aliases = array( $exclude_field );
2560
2561
			if ( is_array( $exclude_field ) ) {
2562
				$aliases       = array_merge( array( $k ), $exclude_field );
2563
				$exclude_field = $k;
2564
			}
2565
2566
			foreach ( $aliases as $alias ) {
2567
				if ( isset( $options[ $alias ] ) ) {
2568
					$field[ $exclude_field ] = pods_trim( $options[ $alias ] );
2569
2570
					unset( $options[ $alias ] );
2571
				}
2572
			}
2573
		}
2574
2575
		if ( strlen( $field['label'] ) < 1 ) {
2576
			$field['label'] = $field['name'];
2577
		}
2578
2579
		$field['options']['type'] = $field['type'];
2580
2581
		if ( in_array( $field['options']['type'], $tableless_field_types ) ) {
2582
			// Clean up special drop-down in field editor and save out pick_val
2583
			$field['pick_object'] = pods_var( 'pick_object', $field, '', null, true );
2584
2585
			if ( 0 === strpos( $field['pick_object'], 'pod-' ) ) {
2586
				$field['pick_val']    = pods_str_replace( 'pod-', '', $field['pick_object'], 1 );
2587
				$field['pick_object'] = 'pod';
2588
			} elseif ( 0 === strpos( $field['pick_object'], 'post_type-' ) ) {
2589
				$field['pick_val']    = pods_str_replace( 'post_type-', '', $field['pick_object'], 1 );
2590
				$field['pick_object'] = 'post_type';
2591
			} elseif ( 0 === strpos( $field['pick_object'], 'taxonomy-' ) ) {
2592
				$field['pick_val']    = pods_str_replace( 'taxonomy-', '', $field['pick_object'], 1 );
2593
				$field['pick_object'] = 'taxonomy';
2594
			} elseif ( 'table' === $field['pick_object'] && 0 < strlen( pods_var_raw( 'pick_table', $field['options'] ) ) ) {
2595
				$field['pick_val']    = $field['options']['pick_table'];
2596
				$field['pick_object'] = 'table';
2597
			} elseif ( false === strpos( $field['pick_object'], '-' ) && ! in_array( $field['pick_object'], array(
2598
					'pod',
2599
					'post_type',
2600
					'taxonomy'
2601
				) ) ) {
2602
				$field['pick_val'] = '';
2603
			} elseif ( 'custom-simple' === $field['pick_object'] ) {
2604
				$field['pick_val'] = '';
2605
			}
2606
2607
			$field['options']['pick_object'] = $field['pick_object'];
2608
			$field['options']['pick_val']    = $field['pick_val'];
2609
			$field['options']['sister_id']   = pods_var( 'sister_id', $field );
2610
2611
			unset( $field['pick_object'] );
2612
			unset( $field['pick_val'] );
2613
2614
			if ( isset( $field['sister_id'] ) ) {
2615
				unset( $field['sister_id'] );
2616
			}
2617
		}
2618
2619
		$field['options'] = array_merge( $field['options'], $options );
2620
2621
		$object_fields = (array) pods_var_raw( 'object_fields', $pod, array(), null, true );
2622
2623
		if ( 0 < $old_id && defined( 'PODS_FIELD_STRICT' ) && ! PODS_FIELD_STRICT ) {
2624
			$params->id = $field['id'] = $old_id;
2625
		}
2626
2627
		// Add new field
2628
		if ( ! isset( $params->id ) || empty( $params->id ) || empty( $field ) ) {
2629
			if ( $table_operation && in_array( $field['name'], array(
2630
					'created',
2631
					'modified'
2632
				) ) && ! in_array( $field['type'], array(
2633
					'date',
2634
					'datetime'
2635
				) ) && ( ! defined( 'PODS_FIELD_STRICT' ) || PODS_FIELD_STRICT ) ) {
2636
				return pods_error( sprintf( __( '%s is reserved for internal Pods usage, please try a different name', 'pods' ), $field['name'] ), $this );
2637
			}
2638
2639
			if ( $table_operation && 'author' === $field['name'] && 'pick' !== $field['type'] && ( ! defined( 'PODS_FIELD_STRICT' ) || PODS_FIELD_STRICT ) ) {
2640
				return pods_error( sprintf( __( '%s is reserved for internal Pods usage, please try a different name', 'pods' ), $field['name'] ), $this );
2641
			}
2642
2643
			if ( in_array( $field['name'], array( 'id', 'ID' ) ) ) {
2644
				return pods_error( sprintf( __( '%s is reserved for internal Pods usage, please try a different name', 'pods' ), $field['name'] ), $this );
2645
			}
2646
2647
			foreach ( $object_fields as $object_field => $object_field_opt ) {
2648
				if ( $object_field == $field['name'] || in_array( $field['name'], $object_field_opt['alias'] ) ) {
2649
					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 );
2650
				}
2651
			}
2652
2653
			if ( in_array( $field['name'], array( 'rss' ) ) ) // Reserved post_name values that can't be used as field names
2654
			{
2655
				$field['name'] .= '2';
2656
			}
2657
2658
			if ( 'slug' === $field['type'] && true === $db ) {
2659
				if ( in_array( $pod['type'], array( 'post_type', 'taxonomy', 'user' ) ) ) {
2660
					return pods_error( __( 'This pod already has an internal WordPress permalink field', 'pods' ), $this );
2661
				}
2662
2663
				$slug_field = get_posts( array(
2664
					'post_type'      => '_pods_field',
2665
					'orderby'        => 'menu_order',
2666
					'order'          => 'ASC',
2667
					'posts_per_page' => 1,
2668
					'post_parent'    => $field['pod_id'],
2669
					'meta_query'     => array(
2670
						array(
2671
							'key'   => 'type',
2672
							'value' => 'slug'
2673
						)
2674
					)
2675
				) );
2676
2677
				if ( ! empty( $slug_field ) ) {
2678
					return pods_error( __( 'This pod already has a permalink field', 'pods' ), $this );
2679
				}
2680
			}
2681
2682
			// Sink the new field to the bottom of the list
2683
			if ( null === $field['weight'] ) {
2684
				$field['weight'] = 0;
2685
2686
				$bottom_most_field = get_posts( array(
2687
					'post_type'      => '_pods_field',
2688
					'orderby'        => 'menu_order',
2689
					'order'          => 'DESC',
2690
					'posts_per_page' => 1,
2691
					'post_parent'    => $field['pod_id']
2692
				) );
2693
2694
				if ( ! empty( $bottom_most_field ) ) {
2695
					$field['weight'] = pods_absint( $bottom_most_field[0]->menu_order ) + 1;
2696
				}
2697
			}
2698
2699
			$field['weight'] = pods_absint( $field['weight'] );
2700
2701
			$post_data = array(
2702
				'post_name'    => $field['name'],
2703
				'post_title'   => $field['label'],
2704
				'post_content' => $field['description'],
2705
				'post_type'    => '_pods_field',
2706
				'post_parent'  => $field['pod_id'],
2707
				'post_status'  => 'publish',
2708
				'menu_order'   => $field['weight']
2709
			);
2710
		} else {
2711
			if ( in_array( $field['name'], array( 'id', 'ID' ) ) ) {
2712
				if ( null !== $old_name ) {
2713
					return pods_error( sprintf( __( '%s is reserved for internal Pods usage, please try a different name', 'pods' ), $field['name'] ), $this );
2714
				} else {
2715
					return pods_error( sprintf( __( '%s is not editable', 'pods' ), $field['name'] ), $this );
2716
				}
2717
			}
2718
2719
			if ( null !== $old_name && $field['name'] !== $old_name && in_array( $field['name'], array(
2720
					'created',
2721
					'modified'
2722
				) ) && ! in_array( $field['type'], array(
2723
					'date',
2724
					'datetime'
2725
				) ) && ( ! defined( 'PODS_FIELD_STRICT' ) || PODS_FIELD_STRICT ) ) {
2726
				return pods_error( sprintf( __( '%s is reserved for internal Pods usage, please try a different name', 'pods' ), $field['name'] ), $this );
2727
			}
2728
2729
			if ( null !== $old_name && $field['name'] !== $old_name && 'author' === $field['name'] && 'pick' !== $field['type'] && ( ! defined( 'PODS_FIELD_STRICT' ) || PODS_FIELD_STRICT ) ) {
2730
				return pods_error( sprintf( __( '%s is reserved for internal Pods usage, please try a different name', 'pods' ), $field['name'] ), $this );
2731
			}
2732
2733
			foreach ( $object_fields as $object_field => $object_field_opt ) {
2734
				if ( $object_field !== $field['name'] && ! in_array( $field['name'], $object_field_opt['alias'] ) ) {
2735
					continue;
2736
				}
2737
2738
				if ( null !== $old_name ) {
2739
					return pods_error( sprintf( __( '%s is reserved for internal WordPress or Pods usage, please try a different name', 'pods' ), $field['name'] ), $this );
2740
				} else {
2741
					return pods_error( sprintf( __( '%s is not editable', 'pods' ), $field['name'] ), $this );
2742
				}
2743
			}
2744
2745
			$post_data = array(
2746
				'ID'           => $field['id'],
2747
				'post_name'    => $field['name'],
2748
				'post_title'   => $field['label'],
2749
				'post_content' => $field['description']
2750
			);
2751
2752
			if ( null !== $field['weight'] ) {
2753
				$field['weight'] = pods_absint( $field['weight'] );
2754
2755
				$post_data['menu_order'] = $field['weight'];
2756
			}
2757
		}
2758
2759
		if ( true === $db ) {
2760
			if ( ! has_filter( 'wp_unique_post_slug', array( $this, 'save_slug_fix' ) ) ) {
2761
				add_filter( 'wp_unique_post_slug', array( $this, 'save_slug_fix' ), 100, 6 );
2762
			}
2763
2764
			$conflicted = false;
2765
2766
			// Headway compatibility fix
2767
			if ( has_filter( 'wp_insert_post_data', 'headway_clean_slug', 0 ) ) {
2768
				remove_filter( 'wp_insert_post_data', 'headway_clean_slug', 0 );
2769
2770
				$conflicted = true;
2771
			}
2772
2773
			// Store the old field name
2774
			if ( $old_name && $old_name !== $post_data['post_name'] ) {
2775
				$field['options']['old_name'] = $old_name;
2776
			}
2777
2778
			$params->id = $this->save_wp_object( 'post', $post_data, $field['options'], true, true );
2779
2780
			if ( $conflicted ) {
2781
				add_filter( 'wp_insert_post_data', 'headway_clean_slug', 0 );
2782
			}
2783
2784
			if ( false === $params->id ) {
2785
				return pods_error( __( 'Cannot save Field', 'pods' ), $this );
2786
			}
2787
		} else {
2788
			$params->id = $field['name'];
2789
		}
2790
2791
		$field['id'] = $params->id;
2792
2793
		$simple = ( 'pick' === $field['type'] && in_array( pods_var( 'pick_object', $field['options'] ), $simple_tableless_objects ) );
2794
2795
		$definition = false;
2796
2797
		if ( ! in_array( $field['type'], $tableless_field_types ) || $simple ) {
2798
			$field_definition = $this->get_field_definition( $field['type'], array_merge( $field, $field['options'] ) );
2799
2800
			if ( 0 < strlen( $field_definition ) ) {
2801
				$definition = '`' . $field['name'] . '` ' . $field_definition;
2802
			}
2803
		}
2804
2805
		$sister_id = (int) pods_var( 'sister_id', $field['options'], 0 );
2806
2807
		if ( $table_operation && 'table' === $pod['storage'] && ! pods_tableless() ) {
2808
			if ( ! empty( $old_id ) ) {
2809
				if ( ( $field['type'] !== $old_type || $old_simple != $simple ) && empty( $definition ) ) {
2810
					pods_query( "ALTER TABLE `@wp_pods_{$params->pod}` DROP COLUMN `{$old_name}`", false );
2811
				} elseif ( 0 < strlen( $definition ) ) {
2812
					if ( $old_name !== $field['name'] || $old_simple != $simple ) {
2813
						$test = false;
2814
2815
						if ( 0 < strlen( $old_definition ) ) {
2816
							$test = pods_query( "ALTER TABLE `@wp_pods_{$params->pod}` CHANGE `{$old_name}` {$definition}", false );
2817
						}
2818
2819
						// If the old field doesn't exist, continue to add a new field
2820
						if ( false === $test ) {
2821
							pods_query( "ALTER TABLE `@wp_pods_{$params->pod}` ADD COLUMN {$definition}", __( 'Cannot create new field', 'pods' ) );
2822
						}
2823
					} elseif ( null !== $old_definition && $definition !== $old_definition ) {
2824
						$test = pods_query( "ALTER TABLE `@wp_pods_{$params->pod}` CHANGE `{$old_name}` {$definition}", false );
2825
2826
						// If the old field doesn't exist, continue to add a new field
2827
						if ( false === $test ) {
2828
							pods_query( "ALTER TABLE `@wp_pods_{$params->pod}` ADD COLUMN {$definition}", __( 'Cannot create new field', 'pods' ) );
2829
						}
2830
					}
2831
				}
2832
			} elseif ( 0 < strlen( $definition ) ) {
2833
				$test = false;
2834
2835
				if ( 0 < strlen( $old_definition ) ) {
2836
					$test = pods_query( "ALTER TABLE `@wp_pods_{$params->pod}` CHANGE `" . $field['name'] . "` {$definition}", false );
2837
				}
2838
2839
				// If the old field doesn't exist, continue to add a new field
2840
				if ( false === $test ) {
2841
					pods_query( "ALTER TABLE `@wp_pods_{$params->pod}` ADD COLUMN {$definition}", __( 'Cannot create new field', 'pods' ) );
2842
				}
2843
			}
2844
		}
2845
2846
		if ( ! empty( $old_id ) && 'meta' === $pod['storage'] && $old_name !== $field['name'] && $pod['meta_table'] !== $pod['table'] ) {
2847
			$prepare = array(
2848
				$field['name'],
2849
				$old_name
2850
			);
2851
2852
			// Users don't have a type
2853
			if ( ! empty( $pod['field_type'] ) ) {
2854
				$prepare[] = $pod['name'];
2855
			}
2856
2857
			$join_table = $pod['table'];
2858
2859
			// Taxonomies are the odd type out, terrible I know
2860
			if ( 'taxonomy' === $pod['type'] ) {
2861
				// wp_term_taxonomy has the 'taxonomy' field we need to limit by
2862
				$join_table = $wpdb->term_taxonomy;
2863
			}
2864
2865
			pods_query( "
2866
                UPDATE `{$pod[ 'meta_table' ]}` AS `m`
2867
                LEFT JOIN `{$join_table}` AS `t`
2868
                    ON `t`.`{$pod[ 'field_id' ]}` = `m`.`{$pod[ 'meta_field_id' ]}`
2869
                SET
2870
                    `m`.`{$pod[ 'meta_field_index' ]}` = %s
2871
                WHERE
2872
                    `m`.`{$pod[ 'meta_field_index' ]}` = %s
2873
            " . ( ! empty( $pod['field_type'] ) ? " AND `t`.`{$pod[ 'field_type' ]}` = %s" : "" ), $prepare );
2874
		}
2875
2876
		if ( $field['type'] !== $old_type && in_array( $old_type, $tableless_field_types ) ) {
2877
			delete_post_meta( $old_sister_id, 'sister_id' );
2878
2879
			if ( true === $db ) {
2880
				pods_query( "
2881
                        DELETE pm
2882
                        FROM {$wpdb->postmeta} AS pm
2883
                        LEFT JOIN {$wpdb->posts} AS p
2884
                            ON p.post_type = '_pods_field'
2885
                            AND p.ID = pm.post_id
2886
                        WHERE
2887
                            p.ID IS NOT NULL
2888
                            AND pm.meta_key = 'sister_id'
2889
                            AND pm.meta_value = %d
2890
                    ", array(
2891
						$params->id
2892
					) );
2893
2894
				if ( ! pods_tableless() ) {
2895
					pods_query( "DELETE FROM @wp_podsrel WHERE `field_id` = {$params->id}", false );
2896
2897
					pods_query( "
2898
                            UPDATE `@wp_podsrel`
2899
                            SET `related_field_id` = 0
2900
                            WHERE `field_id` = %d
2901
                        ", array(
2902
							$old_sister_id
2903
						) );
2904
				}
2905
			}
2906
		} elseif ( 0 < $sister_id ) {
2907
			update_post_meta( $sister_id, 'sister_id', $params->id );
2908
2909
			if ( true === $db && ( ! pods_tableless() ) ) {
2910
				pods_query( "
2911
                        UPDATE `@wp_podsrel`
2912
                        SET `related_field_id` = %d
2913
                        WHERE `field_id` = %d
2914
                    ", array(
2915
						$params->id,
2916
						$sister_id
2917
					) );
2918
			}
2919
		} elseif ( 0 < $old_sister_id ) {
2920
			delete_post_meta( $old_sister_id, 'sister_id' );
2921
2922
			if ( true === $db && ( ! pods_tableless() ) ) {
2923
				pods_query( "
2924
                        UPDATE `@wp_podsrel`
2925
                        SET `related_field_id` = 0
2926
                        WHERE `field_id` = %d
2927
                    ", array(
2928
						$old_sister_id
2929
					) );
2930
			}
2931
		}
2932
2933
		if ( ! empty( $old_id ) && $old_name !== $field['name'] && true === $db ) {
2934
			pods_query( "
2935
                    UPDATE `@wp_postmeta`
2936
                    SET `meta_value` = %s
2937
                    WHERE
2938
                        `post_id` = %d
2939
                        AND `meta_key` = 'pod_index'
2940
                        AND `meta_value` = %s
2941
                ", array(
2942
					$field['name'],
2943
					$pod['id'],
2944
					$old_name
2945
				) );
2946
		}
2947
2948
		if ( ! $save_pod ) {
2949
			$this->cache_flush_pods( $pod );
2950
		} else {
2951
			pods_transient_clear( 'pods_field_' . $pod['name'] . '_' . $field['name'] );
2952
2953
			if ( ! empty( $old_id ) && $old_name !== $field['name'] ) {
2954
				pods_transient_clear( 'pods_field_' . $pod['name'] . '_' . $old_name );
2955
			}
2956
		}
2957
2958
		if ( true === $db ) {
2959
			return $params->id;
2960
		} else {
2961
			return $field;
2962
		}
2963
	}
2964
2965
	/**
2966
	 * Fix Pod / Field post_name to ensure they are exactly as saved (allow multiple posts w/ same post_name)
2967
	 *
2968
	 * @param string $slug          Unique slug value
2969
	 * @param int    $post_ID       Post ID
2970
	 * @param string $post_status   Post Status
2971
	 * @param string $post_type     Post Type
2972
	 * @param int    $post_parent   Post Parent ID
2973
	 * @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...
2974
	 *
2975
	 * @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...
2976
	 *
2977
	 * @since 2.3.3
2978
	 */
2979
	public function save_slug_fix( $slug, $post_ID, $post_status, $post_type, $post_parent = 0, $original_slug = null ) {
2980
2981
		if ( in_array( $post_type, array( '_pods_field', '_pods_pod' ) ) && false !== strpos( $slug, '-' ) ) {
2982
			$slug = $original_slug;
2983
		}
2984
2985
		return $slug;
2986
	}
2987
2988
	/**
2989
	 * Add or Edit a Pods Object
2990
	 *
2991
	 * $params['id'] int The Object ID
2992
	 * $params['name'] string The Object name
2993
	 * $params['type'] string The Object type
2994
	 * $params['options'] Associative array of Object options
2995
	 *
2996
	 * @param array|object $params    An associative array of parameters
2997
	 * @param bool         $sanitized (optional) Decides whether the params have been sanitized before being passed,
2998
	 *                                will sanitize them if false.
2999
	 *
3000
	 * @return int The Object ID
3001
	 * @since 2.0
3002
	 */
3003
	public function save_object( $params, $sanitized = false ) {
3004
3005
		$params = (object) $params;
3006
3007
		if ( false === $sanitized ) {
3008
			$params = pods_sanitize( $params );
3009
		}
3010
3011
		if ( ! isset( $params->name ) || empty( $params->name ) ) {
3012
			return pods_error( __( 'Name must be given to save an Object', 'pods' ), $this );
3013
		}
3014
3015
		if ( ! isset( $params->type ) || empty( $params->type ) ) {
3016
			return pods_error( __( 'Type must be given to save an Object', 'pods' ), $this );
3017
		}
3018
3019
		$object = array(
3020
			'id'      => 0,
3021
			'name'    => $params->name,
3022
			'type'    => $params->type,
3023
			'code'    => '',
3024
			'options' => array()
3025
		);
3026
3027
		// Setup options
3028
		$options = get_object_vars( $params );
3029
3030
		if ( isset( $options['method'] ) ) {
3031
			unset( $options['method'] );
3032
		}
3033
3034
		$exclude = array(
3035
			'id',
3036
			'name',
3037
			'helper_type',
3038
			'code',
3039
			'options',
3040
			'status'
3041
		);
3042
3043
		foreach ( $exclude as $k => $exclude_field ) {
3044
			$aliases = array( $exclude_field );
3045
3046
			if ( is_array( $exclude_field ) ) {
3047
				$aliases       = array_merge( array( $k ), $exclude_field );
3048
				$exclude_field = $k;
3049
			}
3050
3051
			foreach ( $aliases as $alias ) {
3052
				if ( isset( $options[ $alias ] ) ) {
3053
					$object[ $exclude_field ] = pods_trim( $options[ $alias ] );
3054
3055
					unset( $options[ $alias ] );
3056
				}
3057
			}
3058
		}
3059
3060
		if ( 'helper' === $object['type'] ) {
3061
			$object['options']['helper_type'] = $object['helper_type'];
3062
		}
3063
3064
		if ( isset( $object['options']['code'] ) ) {
3065
			unset( $object['options']['code'] );
3066
		}
3067
3068
		$object['options'] = array_merge( $object['options'], $options );
3069
3070
		$post_data = array(
3071
			'post_name'    => pods_clean_name( $object['name'], true ),
3072
			'post_title'   => $object['name'],
3073
			'post_content' => $object['code'],
3074
			'post_type'    => '_pods_' . $object['type'],
3075
			'post_status'  => 'publish'
3076
		);
3077
3078
		if ( ! empty( $object['id'] ) ) {
3079
			$post_data['ID'] = $object['id'];
3080
		}
3081
3082
		if ( null !== pods_var( 'status', $object, null, null, true ) ) {
3083
			$post_data['post_status'] = pods_var( 'status', $object, null, null, true );
3084
		}
3085
3086
		remove_filter( 'content_save_pre', 'balanceTags', 50 );
3087
3088
		$post_data = pods_sanitize( $post_data );
3089
3090
		$params->id = $this->save_post( $post_data, $object['options'], true, true );
3091
3092
		pods_transient_clear( 'pods_objects_' . $params->type );
3093
		pods_transient_clear( 'pods_objects_' . $params->type . '_get' );
3094
3095
		return $params->id;
3096
	}
3097
3098
	/**
3099
	 * @see   PodsAPI::save_object
3100
	 *
3101
	 * Add or edit a Pod Template
3102
	 *
3103
	 * $params['id'] int The template ID
3104
	 * $params['name'] string The template name
3105
	 * $params['code'] string The template code
3106
	 *
3107
	 * @param array|object $params    An associative array of parameters
3108
	 * @param bool         $sanitized (optional) Decides wether the params have been sanitized before being passed,
3109
	 *                                will sanitize them if false.
3110
	 *
3111
	 * @return int The Template ID
3112
	 *
3113
	 * @since 1.7.9
3114
	 */
3115
	public function save_template( $params, $sanitized = false ) {
3116
3117
		$params = (object) $params;
3118
3119
		$params->type = 'template';
3120
3121
		return $this->save_object( $params, $sanitized );
3122
	}
3123
3124
	/**
3125
	 * @see   PodsAPI::save_object
3126
	 *
3127
	 * Add or edit a Pod Page
3128
	 *
3129
	 * $params['id'] int The page ID
3130
	 * $params['name'] string The page URI
3131
	 * $params['code'] string The page code
3132
	 *
3133
	 * @param array|object $params    An associative array of parameters
3134
	 * @param bool         $sanitized (optional) Decides wether the params have been sanitized before being passed,
3135
	 *                                will sanitize them if false.
3136
	 *
3137
	 * @return int The page ID
3138
	 * @since 1.7.9
3139
	 */
3140
	public function save_page( $params, $sanitized = false ) {
3141
3142
		$params = (object) $params;
3143
3144
		if ( ! isset( $params->name ) ) {
3145
			$params->name = $params->uri;
3146
			unset( $params->uri );
3147
		}
3148
3149
		if ( isset( $params->phpcode ) ) {
3150
			$params->code = $params->phpcode;
3151
			unset( $params->phpcode );
3152
		}
3153
3154
		$params->name = trim( $params->name, '/' );
3155
		$params->type = 'page';
3156
3157
		return $this->save_object( $params, $sanitized );
3158
	}
3159
3160
	/**
3161
	 * @see   PodsAPI::save_object
3162
	 *
3163
	 * Add or edit a Pod Helper
3164
	 *
3165
	 * $params['id'] int The helper ID
3166
	 * $params['name'] string The helper name
3167
	 * $params['helper_type'] string The helper type ("pre_save", "display", etc)
3168
	 * $params['code'] string The helper code
3169
	 *
3170
	 * @param array $params    An associative array of parameters
3171
	 * @param bool  $sanitized (optional) Decides wether the params have been sanitized before being passed, will
3172
	 *                         sanitize them if false.
3173
	 *
3174
	 * @return int The helper ID
3175
	 * @since 1.7.9
3176
	 */
3177
	public function save_helper( $params, $sanitized = false ) {
3178
3179
		$params = (object) $params;
3180
3181
		if ( isset( $params->phpcode ) ) {
3182
			$params->code = $params->phpcode;
3183
			unset( $params->phpcode );
3184
		}
3185
3186
		if ( isset( $params->type ) ) {
3187
			$params->helper_type = $params->type;
3188
			unset( $params->type );
3189
		}
3190
3191
		$params->type = 'helper';
3192
3193
		return $this->save_object( $params, $sanitized );
3194
	}
3195
3196
	/**
3197
	 * Add or edit a single pod item
3198
	 *
3199
	 * $params['pod'] string The Pod name (pod or pod_id is required)
3200
	 * $params['pod_id'] string The Pod ID (pod or pod_id is required)
3201
	 * $params['id'] int|array The item ID, or an array of item IDs to save data for
3202
	 * $params['data'] array (optional) Associative array of field names + values
3203
	 * $params['bypass_helpers'] bool Set to true to bypass running pre-save and post-save helpers
3204
	 * $params['track_changed_fields'] bool Set to true to enable tracking of saved fields via
3205
	 * PodsAPI::get_changed_fields()
3206
	 * $params['error_mode'] string Throw an 'exception', 'exit' with the message, return 'false', or return 'wp_error'
3207
	 *
3208
	 * @param array|object $params An associative array of parameters
3209
	 *
3210
	 * @return int|array The item ID, or an array of item IDs (if `id` is an array if IDs)
3211
	 *
3212
	 * @since 1.7.9
3213
	 */
3214
	public function save_pod_item( $params ) {
3215
3216
		global $wpdb;
3217
3218
		$params = (object) pods_str_replace( '@wp_', '{prefix}', $params );
3219
3220
		$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...
3221
		$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...
3222
		$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...
3223
3224
		$error_mode = $this->display_errors;
3225
3226
		if ( ! empty( $params->error_mode ) ) {
3227
			$error_mode = $params->error_mode;
3228
		}
3229
3230
		// @deprecated 2.0
3231
		if ( isset( $params->datatype ) ) {
3232
			pods_deprecated( '$params->pod instead of $params->datatype', '2.0' );
3233
3234
			$params->pod = $params->datatype;
3235
3236
			unset( $params->datatype );
3237
3238
			if ( isset( $params->pod_id ) ) {
3239
				pods_deprecated( '$params->id instead of $params->pod_id', '2.0' );
3240
3241
				$params->id = $params->pod_id;
3242
3243
				unset( $params->pod_id );
3244
			}
3245
3246
			if ( isset( $params->data ) && ! empty( $params->data ) && is_array( $params->data ) ) {
3247
				$check = current( $params->data );
3248
3249
				if ( is_array( $check ) ) {
3250
					pods_deprecated( 'PodsAPI::save_pod_items', '2.0' );
3251
3252
					return $this->save_pod_items( $params, $params->data );
3253
				}
3254
			}
3255
		}
3256
3257
		// @deprecated 2.0
3258
		if ( isset( $params->tbl_row_id ) ) {
3259
			pods_deprecated( '$params->id instead of $params->tbl_row_id', '2.0' );
3260
3261
			$params->id = $params->tbl_row_id;
3262
3263
			unset( $params->tbl_row_id );
3264
		}
3265
3266
		// @deprecated 2.0
3267
		if ( isset( $params->columns ) ) {
3268
			pods_deprecated( '$params->data instead of $params->columns', '2.0' );
3269
3270
			$params->data = $params->columns;
3271
3272
			unset( $params->columns );
3273
		}
3274
3275
		if ( ! isset( $params->pod ) ) {
3276
			$params->pod = false;
3277
		}
3278
		if ( isset( $params->pod_id ) ) {
3279
			$params->pod_id = pods_absint( $params->pod_id );
3280
		} else {
3281
			$params->pod_id = 0;
3282
		}
3283
3284
		if ( isset( $params->id ) ) {
3285
			$params->id = pods_absint( $params->id );
3286
		} else {
3287
			$params->id = 0;
3288
		}
3289
3290
		if ( ! isset( $params->from ) ) {
3291
			$params->from = 'save';
3292
		}
3293
3294
		if ( ! isset( $params->location ) ) {
3295
			$params->location = null;
3296
		}
3297
3298
		if ( ! isset( $params->track_changed_fields ) ) {
3299
			$params->track_changed_fields = false;
3300
		}
3301
3302
		/**
3303
		 * Override $params['track_changed_fields']
3304
		 *
3305
		 * Use for globally setting field change tracking.
3306
		 *
3307
		 * @param bool
3308
		 *
3309
		 * @since 2.3.19
3310
		 */
3311
		$track_changed_fields = apply_filters( 'pods_api_save_pod_item_track_changed_fields_' . $params->pod, (boolean) $params->track_changed_fields, $params );
3312
3313
		$changed_fields = array();
3314
3315
		if ( ! isset( $params->clear_slug_cache ) ) {
3316
			$params->clear_slug_cache = true;
3317
		}
3318
3319
		// Support for bulk edit
3320
		if ( isset( $params->id ) && ! empty( $params->id ) && is_array( $params->id ) ) {
3321
			$ids        = array();
3322
			$new_params = $params;
3323
3324
			foreach ( $params->id as $id ) {
3325
				$new_params->id = $id;
3326
3327
				$ids[] = $this->save_pod_item( $new_params );
3328
			}
3329
3330
			return $ids;
3331
		}
3332
3333
		// Allow Helpers to know what's going on, are we adding or saving?
3334
		$is_new_item = false;
3335
3336
		if ( empty( $params->id ) ) {
3337
			$is_new_item = true;
3338
		}
3339
3340
		if ( isset( $params->is_new_item ) ) {
3341
			$is_new_item = (boolean) $params->is_new_item;
3342
		}
3343
3344
		// Allow Helpers to bypass subsequent helpers in recursive save_pod_item calls
3345
		$bypass_helpers = false;
3346
3347
		if ( isset( $params->bypass_helpers ) && false !== $params->bypass_helpers ) {
3348
			$bypass_helpers = true;
3349
		}
3350
3351
		// Allow Custom Fields not defined by Pods to be saved
3352
		$allow_custom_fields = false;
3353
3354
		if ( isset( $params->allow_custom_fields ) && false !== $params->allow_custom_fields ) {
3355
			$allow_custom_fields = true;
3356
		}
3357
3358
		// Get array of Pods
3359
		$pod = $this->load_pod( array( 'id' => $params->pod_id, 'name' => $params->pod, 'table_info' => true ) );
3360
3361
		if ( false === $pod ) {
3362
			return pods_error( __( 'Pod not found', 'pods' ), $error_mode );
3363
		}
3364
3365
		$params->pod    = $pod['name'];
3366
		$params->pod_id = $pod['id'];
3367
3368
		if ( 'settings' === $pod['type'] ) {
3369
			$params->id = $pod['id'];
3370
		}
3371
3372
		$fields = $pod['fields'];
3373
3374
		$object_fields = (array) pods_var_raw( 'object_fields', $pod, array(), null, true );
3375
3376
		$fields_active = array();
3377
		$custom_data   = array();
3378
3379
		// Find the active fields (loop through $params->data to retain order)
3380
		if ( ! empty( $params->data ) && is_array( $params->data ) ) {
3381
			$custom_fields = array();
3382
3383
			foreach ( $params->data as $field => $value ) {
3384
				if ( isset( $object_fields[ $field ] ) ) {
3385
					$object_fields[ $field ]['value'] = $value;
3386
					$fields_active[]                  = $field;
3387
				} elseif ( isset( $fields[ $field ] ) ) {
3388
					if ( 'save' === $params->from || true === PodsForm::permission( $fields[ $field ]['type'], $field, $fields[ $field ], $fields, $pod, $params->id, $params ) ) {
3389
						$fields[ $field ]['value'] = $value;
3390
						$fields_active[]           = $field;
3391
					} elseif ( ! pods_has_permissions( $fields[ $field ]['options'] ) && pods_var( 'hidden', $fields[ $field ]['options'], false ) ) {
3392
						$fields[ $field ]['value'] = $value;
3393
						$fields_active[]           = $field;
3394
					}
3395
				} else {
3396
					$found = false;
3397
3398
					foreach ( $object_fields as $object_field => $object_field_opt ) {
3399
						if ( in_array( $field, $object_field_opt['alias'] ) ) {
3400
							$object_fields[ $object_field ]['value'] = $value;
3401
							$fields_active[]                         = $object_field;
3402
3403
							$found = true;
3404
3405
							break;
3406
						}
3407
					}
3408
3409
					if ( $allow_custom_fields && ! $found ) {
3410
						$custom_fields[] = $field;
3411
					}
3412
				}
3413
			}
3414
3415
			if ( $allow_custom_fields && ! empty( $custom_fields ) ) {
3416
				foreach ( $custom_fields as $field ) {
3417
					$custom_data[ $field ] = $params->data[ $field ];
3418
				}
3419
			}
3420
3421
			unset( $params->data );
3422
		}
3423
3424
		if ( empty( $params->id ) && ! in_array( 'created', $fields_active ) && isset( $fields['created'] ) && in_array( $fields['created']['type'], array(
3425
				'date',
3426
				'datetime'
3427
			) ) ) {
3428
			$fields['created']['value'] = current_time( 'mysql' );
3429
			$fields_active[]            = 'created';
3430
		}
3431
3432
		if ( ! in_array( 'modified', $fields_active ) && isset( $fields['modified'] ) && in_array( $fields['modified']['type'], array(
3433
				'date',
3434
				'datetime'
3435
			) ) ) {
3436
			$fields['modified']['value'] = current_time( 'mysql' );
3437
			$fields_active[]             = 'modified';
3438
		}
3439
3440
		if ( in_array( $pod['type'], array(
3441
				'pod',
3442
				'table'
3443
			) ) && 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'] ] ) ) {
3444
			$fields[ $pod['pod_field_slug'] ]['value'] = ''; // this will get picked up by slug pre_save method
3445
			$fields_active[]                           = $pod['pod_field_slug'];
3446
		}
3447
3448
		// Handle hidden fields
3449
		if ( empty( $params->id ) ) {
3450
			foreach ( $fields as $field => $field_data ) {
3451
				if ( in_array( $field, $fields_active ) ) {
3452
					continue;
3453
				}
3454
3455
				if ( in_array( $params->from, array(
3456
						'save',
3457
						'process_form'
3458
					) ) || true === PodsForm::permission( $fields[ $field ]['type'], $field, $fields[ $field ], $fields, $pod, $params->id, $params ) ) {
3459
					$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 );
3460
3461
					if ( null !== $value && '' !== $value && false !== $value ) {
3462
						$fields[ $field ]['value'] = $value;
3463
						$fields_active[]           = $field;
3464
					}
3465
				}
3466
			}
3467
3468
			// Set default field values for object fields
3469
			if ( ! empty( $object_fields ) ) {
3470
				foreach ( $object_fields as $field => $field_data ) {
3471
					if ( in_array( $field, $fields_active ) ) {
3472
						continue;
3473
					} elseif ( ! isset( $field_data['default'] ) || strlen( $field_data['default'] ) < 1 ) {
3474
						continue;
3475
					}
3476
3477
					$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 );
3478
3479
					if ( null !== $value && '' !== $value && false !== $value ) {
3480
						$object_fields[ $field ]['value'] = $value;
3481
						$fields_active[]                  = $field;
3482
					}
3483
				}
3484
			}
3485
3486
			// Set default field values for Pod fields
3487
			foreach ( $fields as $field => $field_data ) {
3488
				if ( in_array( $field, $fields_active ) ) {
3489
					continue;
3490
				} elseif ( ! isset( $field_data['default'] ) || strlen( $field_data['default'] ) < 1 ) {
3491
					continue;
3492
				}
3493
3494
				$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 );
3495
3496
				if ( null !== $value && '' !== $value && false !== $value ) {
3497
					$fields[ $field ]['value'] = $value;
3498
					$fields_active[]           = $field;
3499
				}
3500
			}
3501
		}
3502
3503
		$columns            =& $fields; // @deprecated 2.0
3504
		$active_columns     =& $fields_active; // @deprecated 2.0
3505
		$params->tbl_row_id =& $params->id; // @deprecated 2.0
3506
3507
		$pre_save_helpers = $post_save_helpers = array();
3508
3509
		$pieces = array(
3510
			'fields',
3511
			'params',
3512
			'pod',
3513
			'fields_active',
3514
			'object_fields',
3515
			'custom_fields',
3516
			'custom_data',
3517
			'track_changed_fields',
3518
			'changed_fields'
3519
		);
3520
3521
		if ( $track_changed_fields ) {
3522
			self::handle_changed_fields( $params->pod, $params->id, 'set' );
3523
		}
3524
3525
		if ( false === $bypass_helpers ) {
3526
			// Plugin hooks
3527
			$hooked = $this->do_hook( 'pre_save_pod_item', compact( $pieces ), $is_new_item, $params->id );
3528
3529
			if ( is_array( $hooked ) && ! empty( $hooked ) ) {
3530
				extract( $hooked );
3531
			}
3532
3533
			$hooked = $this->do_hook( "pre_save_pod_item_{$params->pod}", compact( $pieces ), $is_new_item, $params->id );
3534
3535
			if ( is_array( $hooked ) && ! empty( $hooked ) ) {
3536
				extract( $hooked );
3537
			}
3538
3539
			if ( $is_new_item ) {
3540
				$hooked = $this->do_hook( 'pre_create_pod_item', compact( $pieces ) );
3541
3542
				if ( is_array( $hooked ) && ! empty( $hooked ) ) {
3543
					extract( $hooked );
3544
				}
3545
3546
				$hooked = $this->do_hook( "pre_create_pod_item_{$params->pod}", compact( $pieces ) );
3547
3548
				if ( is_array( $hooked ) && ! empty( $hooked ) ) {
3549
					extract( $hooked );
3550
				}
3551
			} else {
3552
				$hooked = $this->do_hook( 'pre_edit_pod_item', compact( $pieces ), $params->id );
3553
3554
				if ( is_array( $hooked ) && ! empty( $hooked ) ) {
3555
					extract( $hooked );
3556
				}
3557
3558
				$hooked = $this->do_hook( "pre_edit_pod_item_{$params->pod}", compact( $pieces ), $params->id );
3559
3560
				if ( is_array( $hooked ) && ! empty( $hooked ) ) {
3561
					extract( $hooked );
3562
				}
3563
			}
3564
3565
			// Call any pre-save helpers (if not bypassed)
3566
			if ( ! defined( 'PODS_DISABLE_EVAL' ) || ! PODS_DISABLE_EVAL ) {
3567
				if ( ! empty( $pod['options'] ) && is_array( $pod['options'] ) ) {
3568
					$helpers = array( 'pre_save_helpers', 'post_save_helpers' );
3569
3570
					foreach ( $helpers as $helper ) {
3571
						if ( isset( $pod['options'][ $helper ] ) && ! empty( $pod['options'][ $helper ] ) ) {
3572
							${$helper} = explode( ',', $pod['options'][ $helper ] );
3573
						}
3574
					}
3575
				}
3576
3577
				if ( ! empty( $pre_save_helpers ) ) {
3578
					pods_deprecated( sprintf( __( 'Pre-save helpers are deprecated, use the action pods_pre_save_pod_item_%s instead', 'pods' ), $params->pod ), '2.0' );
3579
3580
					foreach ( $pre_save_helpers as $helper ) {
3581
						$helper = $this->load_helper( array( 'name' => $helper ) );
3582
3583
						if ( false !== $helper ) {
3584
							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...
3585
						}
3586
					}
3587
				}
3588
			}
3589
		}
3590
3591
		$table_data = $table_formats = $update_values = $rel_fields = $rel_field_ids = array();
3592
3593
		$object_type = $pod['type'];
3594
3595
		$object_ID = 'ID';
3596
3597
		if ( 'comment' === $object_type ) {
3598
			$object_ID = 'comment_ID';
3599
		} elseif ( 'taxonomy' === $object_type ) {
3600
			$object_ID = 'term_id';
3601
		}
3602
3603
		$object_data = $object_meta = $post_term_data = array();
3604
3605
		if ( 'settings' === $object_type ) {
3606
			$object_data['option_id'] = $pod['name'];
3607
		} elseif ( ! empty( $params->id ) ) {
3608
			$object_data[ $object_ID ] = $params->id;
3609
		}
3610
3611
		$fields_active = array_unique( $fields_active );
3612
3613
		// Loop through each active field, validating and preparing the table data
3614
		foreach ( $fields_active as $field ) {
3615
			if ( isset( $object_fields[ $field ] ) ) {
3616
				$field_data = $object_fields[ $field ];
3617
			} elseif ( isset( $fields[ $field ] ) ) {
3618
				$field_data = $fields[ $field ];
3619
			} else {
3620
				continue;
3621
			}
3622
3623
			$value   = $field_data['value'];
3624
			$type    = $field_data['type'];
3625
			$options = pods_var( 'options', $field_data, array() );
3626
3627
			// WPML AJAX compatibility
3628
			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' ) ) {
3629
				$options['unique'] = $fields[ $field ]['options']['unique'] = $options['required'] = $fields[ $field ]['options']['required'] = 0;
3630
			} else {
3631
				// Validate value
3632
				$validate = $this->handle_field_validation( $value, $field, $object_fields, $fields, $pod, $params );
3633
3634
				if ( false === $validate ) {
3635
					$validate = sprintf( __( 'There was an issue validating the field %s', 'pods' ), $field_data['label'] );
3636
				} elseif ( true !== $validate ) {
3637
					$validate = (array) $validate;
3638
				}
3639
3640
				if ( ! is_bool( $validate ) && ! empty( $validate ) ) {
3641
					return pods_error( $validate, $error_mode );
3642
				}
3643
			}
3644
3645
			$value = PodsForm::pre_save( $field_data['type'], $value, $params->id, $field, array_merge( $field_data, $options ), array_merge( $fields, $object_fields ), $pod, $params );
3646
3647
			$field_data['value'] = $value;
3648
3649
			if ( isset( $object_fields[ $field ] ) ) {
3650
				// @todo Eventually support 'comment' field type saving here too
3651
				if ( 'taxonomy' === $object_fields[ $field ]['type'] ) {
3652
					$post_term_data[ $field ] = $value;
3653
				} else {
3654
					$object_data[ $field ] = $value;
3655
				}
3656
			} else {
3657
				$simple = ( 'pick' === $type && in_array( pods_var( 'pick_object', $field_data ), $simple_tableless_objects ) );
3658
				$simple = (boolean) $this->do_hook( 'tableless_custom', $simple, $field_data, $field, $fields, $pod, $params );
3659
3660
				// Handle Simple Relationships
3661
				if ( $simple ) {
3662
					if ( ! is_array( $value ) ) {
3663
						$value = explode( ',', $value );
3664
					}
3665
3666
					$pick_limit = (int) pods_var_raw( 'pick_limit', $options, 0 );
3667
3668
					if ( 'single' === pods_var_raw( 'pick_format_type', $options ) ) {
3669
						$pick_limit = 1;
3670
					}
3671
3672
					if ( 'custom-simple' === pods_var( 'pick_object', $field_data ) ) {
3673
						$custom = pods_var_raw( 'pick_custom', $options, '' );
3674
3675
						$custom = apply_filters( 'pods_form_ui_field_pick_custom_values', $custom, $field_data['name'], $value, array_merge( $field_data, $options ), $pod, $params->id );
3676
3677
						if ( empty( $value ) || empty( $custom ) ) {
3678
							$value = '';
3679
						} elseif ( ! empty( $custom ) ) {
3680
							if ( ! is_array( $custom ) ) {
3681
								$custom = explode( "\n", $custom );
3682
3683
								$custom_values = array();
3684
3685
								foreach ( $custom as $c => $cv ) {
3686
									if ( 0 < strlen( $cv ) ) {
3687
										$custom_label = explode( '|', $cv );
3688
3689
										if ( ! isset( $custom_label[1] ) ) {
3690
											$custom_label[1] = $custom_label[0];
3691
										}
3692
3693
										$custom_label[0]                   = trim( (string) $custom_label[0] );
3694
										$custom_label[1]                   = trim( (string) $custom_label[1] );
3695
										$custom_values[ $custom_label[0] ] = $custom_label[1];
3696
									}
3697
								}
3698
							} else {
3699
								$custom_values = $custom;
3700
							}
3701
3702
							$values = array();
3703
3704
							foreach ( $value as $k => $v ) {
3705
								$v = pods_unsanitize( $v );
3706
3707
								if ( isset( $custom_values[ $v ] ) ) {
3708
									$values[ $k ] = $v;
3709
								}
3710
							}
3711
3712
							$value = $values;
3713
						}
3714
					}
3715
3716
					if ( 0 < $pick_limit && ! empty( $value ) ) {
3717
						$value = array_slice( $value, 0, $pick_limit );
3718
					}
3719
3720
					// Don't save an empty array, just make it an empty string
3721
					if ( empty( $value ) ) {
3722
						$value = '';
3723
					} elseif ( is_array( $value ) ) {
3724
						if ( 1 == $pick_limit || 1 == count( $value ) ) {
3725
							// If there's just one item, don't save as an array, save the string
3726
							$value = implode( '', $value );
3727
						} elseif ( 'table' === pods_var( 'storage', $pod ) ) {
3728
							// If storage is set to table, json encode, otherwise WP will serialize automatically
3729
							$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...
3730
						}
3731
					}
3732
				}
3733
3734
				// Prepare all table / meta data
3735
				if ( ! in_array( $type, $tableless_field_types ) || $simple ) {
3736
					if ( in_array( $type, $repeatable_field_types ) && 1 == pods_var( $type . '_repeatable', $field_data, 0 ) ) {
3737
						// Don't save an empty array, just make it an empty string
3738
						if ( empty( $value ) ) {
3739
							$value = '';
3740
						} elseif ( is_array( $value ) ) {
3741
							if ( 1 == count( $value ) ) {
3742
								// If there's just one item, don't save as an array, save the string
3743
								$value = implode( '', $value );
3744
							} elseif ( 'table' === pods_var( 'storage', $pod ) ) {
3745
								// If storage is set to table, json encode, otherwise WP will serialize automatically
3746
								$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...
3747
							}
3748
						}
3749
					}
3750
3751
					$table_data[ $field ] = str_replace( array( '{prefix}', '@wp_' ), array(
3752
						'{/prefix/}',
3753
						'{prefix}'
3754
					), $value ); // Fix for pods_query
3755
					$table_formats[]      = PodsForm::prepare( $type, $options );
3756
3757
					$object_meta[ $field ] = $value;
3758
				} else {
3759
					// Store relational field data to be looped through later
3760
					// Convert values from a comma-separated string into an array
3761
					if ( ! is_array( $value ) ) {
3762
						$value = explode( ',', $value );
3763
					}
3764
3765
					$rel_fields[ $type ][ $field ] = $value;
3766
					$rel_field_ids[]               = $field_data['id'];
3767
				}
3768
			}
3769
		}
3770
3771
		if ( in_array( $pod['type'], array( 'post_type', 'taxonomy' ) ) ) {
3772
			$object_name = $pod['name'];
3773
3774
			if ( ! empty( $pod['object'] ) ) {
3775
				$object_name = $pod['object'];
3776
			}
3777
3778
			$object_name_field = 'post_type';
3779
3780
			if ( 'taxonomy' === $pod['type'] ) {
3781
				$object_name_field = 'taxonomy';
3782
			}
3783
3784
			$object_data[ $object_name_field ] = $object_name;
3785
		}
3786
3787
		if ( ! in_array( $pod['type'], array( 'pod', 'table', '' ) ) ) {
3788
			$meta_fields = array();
3789
3790
			if ( 'meta' === $pod['storage'] || 'settings' === $pod['type'] || ( 'taxonomy' === $pod['type'] && 'none' === $pod['storage'] ) ) {
3791
				$meta_fields = $object_meta;
3792
			}
3793
3794
			if ( $allow_custom_fields && ! empty( $custom_data ) ) {
3795
				$meta_fields = array_merge( $custom_data, $meta_fields );
3796
			}
3797
3798
			$fields_to_send = array_flip( array_keys( $meta_fields ) );
3799
3800
			foreach ( $fields_to_send as $field => $field_data ) {
3801
				if ( isset( $object_fields[ $field ] ) ) {
3802
					$field_data = $object_fields[ $field ];
3803
				} elseif ( isset( $fields[ $field ] ) ) {
3804
					$field_data = $fields[ $field ];
3805
				} else {
3806
					unset( $fields_to_send[ $field ] );
3807
				}
3808
3809
				$fields_to_send[ $field ] = $field_data;
3810
			}
3811
3812
			$params->id = $this->save_wp_object( $object_type, $object_data, $meta_fields, false, true, $fields_to_send );
3813
3814
			if ( ! empty( $params->id ) && 'settings' === $pod['type'] ) {
3815
				$params->id = $pod['id'];
3816
			}
3817
		}
3818
3819
		if ( 'table' === $pod['storage'] ) {
3820
			// Every row should have an id set here, otherwise Pods with nothing
3821
			// but relationship fields won't get properly ID'd
3822
			if ( empty( $params->id ) ) {
3823
				$params->id = 0;
3824
			}
3825
3826
			$table_data = array( 'id' => $params->id ) + $table_data;
3827
			array_unshift( $table_formats, '%d' );
3828
3829
			if ( ! empty( $table_data ) ) {
3830
				$sql = pods_data()->insert_on_duplicate( "@wp_pods_{$params->pod}", $table_data, $table_formats );
3831
3832
				$id = pods_query( $sql, 'Cannot add/save table row' );
3833
3834
				if ( empty( $params->id ) ) {
3835
					$params->id = $id;
3836
				}
3837
			}
3838
		}
3839
3840
		$params->id = (int) $params->id;
3841
3842
		// Save terms for taxonomies associated to a post type
3843
		if ( 0 < $params->id && 'post_type' === $pod['type'] && ! empty( $post_term_data ) ) {
3844
			foreach ( $post_term_data as $post_taxonomy => $post_terms ) {
3845
				$post_terms = (array) $post_terms;
3846
3847
				foreach ( $post_terms as $k => $v ) {
3848
					if ( ! preg_match( '/[^0-9]/', $v ) ) {
3849
						$v = (int) $v;
3850
					}
3851
3852
					$post_terms[ $k ] = $v;
3853
				}
3854
3855
				wp_set_object_terms( $params->id, $post_terms, $post_taxonomy );
3856
			}
3857
		}
3858
3859
		$no_conflict = pods_no_conflict_check( $pod['type'] );
3860
3861
		if ( ! $no_conflict ) {
3862
			pods_no_conflict_on( $pod['type'] );
3863
		}
3864
3865
		// Save relationship / file data
3866
		if ( ! empty( $rel_fields ) ) {
3867
			foreach ( $rel_fields as $type => $data ) {
3868
				// Only handle tableless fields
3869
				if ( ! in_array( $type, $tableless_field_types ) ) {
3870
					continue;
3871
				}
3872
3873
				foreach ( $data as $field => $values ) {
3874
					$pick_val = pods_var( 'pick_val', $fields[ $field ] );
3875
3876
					if ( 'table' === pods_var( 'pick_object', $fields[ $field ] ) ) {
3877
						$pick_val = pods_var( 'pick_table', $fields[ $field ]['options'], $pick_val, null, true );
3878
					}
3879
3880
					if ( '__current__' === $pick_val ) {
3881
						if ( is_object( $pod ) ) {
3882
							$pick_val = $pod->pod;
3883
						} elseif ( is_array( $pod ) ) {
3884
							$pick_val = $pod['name'];
3885
						} elseif ( 0 < strlen( $pod ) ) {
3886
							$pick_val = $pod;
3887
						}
3888
					}
3889
3890
					$fields[ $field ]['options']['table_info'] = pods_api()->get_table_info( pods_var( 'pick_object', $fields[ $field ] ), $pick_val, null, null, $fields[ $field ]['options'] );
3891
3892
					if ( isset( $fields[ $field ]['options']['table_info']['pod'] ) && ! empty( $fields[ $field ]['options']['table_info']['pod'] ) && isset( $fields[ $field ]['options']['table_info']['pod']['name'] ) ) {
3893
						$search_data = pods( $fields[ $field ]['options']['table_info']['pod']['name'] );
3894
3895
						$data_mode = 'pods';
3896
					} else {
3897
						$search_data = pods_data();
3898
						$search_data->table( $fields[ $field ]['options']['table_info'] );
3899
3900
						$data_mode = 'data';
3901
					}
3902
3903
					$find_rel_params = array(
3904
						'select'     => "`t`.`{$search_data->field_id}`",
3905
						'where'      => "`t`.`{$search_data->field_slug}` = %s OR `t`.`{$search_data->field_index}` = %s",
3906
						'limit'      => 1,
3907
						'pagination' => false,
3908
						'search'     => false
3909
					);
3910
3911
					if ( empty( $search_data->field_slug ) && ! empty( $search_data->field_index ) ) {
3912
						$find_rel_params['where'] = "`t`.`{$search_data->field_index}` = %s";
3913
					} elseif ( empty( $search_data->field_slug ) && empty( $search_data->field_index ) ) {
3914
						$find_rel_params = false;
3915
					}
3916
3917
					$related_limit = (int) pods_var_raw( $type . '_limit', $fields[ $field ]['options'], 0 );
3918
3919
					if ( 'single' === pods_var_raw( $type . '_format_type', $fields[ $field ]['options'] ) ) {
3920
						$related_limit = 1;
3921
					}
3922
3923
					// Enforce integers / unique values for IDs
3924
					$value_ids = array();
3925
3926
					$is_file_field = in_array( $type, PodsForm::file_field_types() );
3927
					$is_taggable   = ( in_array( $type, PodsForm::tableless_field_types() ) && 1 == pods_v( $type . '_taggable', $fields[ $field ]['options'] ) );
3928
3929
					// @todo Handle simple relationships eventually
3930
					foreach ( $values as $v ) {
3931
						if ( ! empty( $v ) ) {
3932
							if ( ! is_array( $v ) ) {
3933
								if ( ! preg_match( '/[^0-9]/', $v ) ) {
3934
									$v = (int) $v;
3935
								} elseif ( $is_file_field ) {
3936
									// File handling
3937
									// Get ID from GUID
3938
									$v = pods_image_id_from_field( $v );
3939
3940
									// If file not found, add it
3941
									if ( empty( $v ) ) {
3942
										$v = pods_attachment_import( $v );
3943
									}
3944
								} else {
3945
									// Reference by slug
3946
									$v_data = false;
3947
3948
									if ( false !== $find_rel_params ) {
3949
										$rel_params          = $find_rel_params;
3950
										$rel_params['where'] = $wpdb->prepare( $rel_params['where'], array( $v, $v ) );
3951
3952
										$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...
3953
3954
										$v_data = $search_data->fetch( $v );
3955
									}
3956
3957
									if ( ! empty( $v_data ) && isset( $v_data[ $search_data->field_id ] ) ) {
3958
										$v = (int) $v_data[ $search_data->field_id ];
3959
									} elseif ( $is_taggable && 'pods' === $data_mode ) {
3960
										// Allow tagging for Pods objects
3961
										$tag_data = array(
3962
											$search_data->field_index => $v
3963
										);
3964
3965
										if ( 'post_type' === $search_data->pod_data['type'] ) {
3966
											$tag_data['post_status'] = 'publish';
3967
										}
3968
3969
										/**
3970
										 * Filter for changing tag before adding new item.
3971
										 *
3972
										 * @param array  $tag_data    Fields for creating new item.
3973
										 * @param int    $v           Field ID of tag.
3974
										 * @param Pods   $search_data Search object for tag.
3975
										 * @param string $field       Table info for field.
3976
										 * @param array  $pieces      Field array.
3977
										 *
3978
										 * @since 2.3.19
3979
										 */
3980
										$tag_data = apply_filters( 'pods_api_save_pod_item_taggable_data', $tag_data, $v, $search_data, $field, compact( $pieces ) );
3981
3982
										// Save $v to a new item on related object
3983
										$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...
3984
3985
										// @todo Support non-Pods for tagging
3986
									}
3987
								}
3988
							} elseif ( $is_file_field && isset( $v['id'] ) ) {
3989
								$v = (int) $v['id'];
3990
							} else {
3991
								continue;
3992
							}
3993
3994
							if ( ! empty( $v ) && ! in_array( $v, $value_ids ) ) {
3995
								$value_ids[] = $v;
3996
							}
3997
						}
3998
					}
3999
4000
					$value_ids = array_unique( array_filter( $value_ids ) );
4001
4002
					// Filter unique values not equal to false in case of a multidimensional array
4003
					$filtered_values          = $this->array_filter_walker( $values );
4004
					$serialized_values        = array_map( 'serialize', $filtered_values );
4005
					$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...
4006
4007
					$values = array_map( 'unserialize', $unique_serialized_values );
4008
4009
					// Limit values
4010
					if ( 0 < $related_limit && ! empty( $value_ids ) ) {
4011
						$value_ids = array_slice( $value_ids, 0, $related_limit );
4012
						$values    = array_slice( $values, 0, $related_limit );
4013
					}
4014
4015
					// Get current values
4016
					if ( 'pick' === $type && isset( PodsField_Pick::$related_data[ $fields[ $field ]['id'] ] ) && isset( PodsField_Pick::$related_data[ $fields[ $field ]['id'] ]['current_ids'] ) ) {
4017
						$related_ids = PodsField_Pick::$related_data[ $fields[ $field ]['id'] ]['current_ids'];
4018
					} else {
4019
						$related_ids = $this->lookup_related_items( $fields[ $field ]['id'], $pod['id'], $params->id, $fields[ $field ], $pod );
4020
					}
4021
4022
					// Get ids to remove
4023
					$remove_ids = array_diff( $related_ids, $value_ids );
4024
4025
					// Delete relationships
4026
					if ( ! empty( $remove_ids ) ) {
4027
						$this->delete_relationships( $params->id, $remove_ids, $pod, $fields[ $field ] );
4028
					}
4029
4030
					// Save relationships
4031
					if ( ! empty( $value_ids ) ) {
4032
						$this->save_relationships( $params->id, $value_ids, $pod, $fields[ $field ] );
4033
					}
4034
4035
					$field_save_values = $value_ids;
4036
4037
					if ( 'file' === $type ) {
4038
						$field_save_values = $values;
4039
					}
4040
4041
					// Run save function for field type (where needed)
4042
					PodsForm::save( $type, $field_save_values, $params->id, $field, array_merge( $fields[ $field ], $fields[ $field ]['options'] ), array_merge( $fields, $object_fields ), $pod, $params );
4043
				}
4044
4045
				// Unset data no longer needed
4046
				if ( 'pick' === $type ) {
4047
					foreach ( $data as $field => $values ) {
4048
						if ( isset( PodsField_Pick::$related_data[ $fields[ $field ]['id'] ] ) ) {
4049
							unset( PodsField_Pick::$related_data[ PodsField_Pick::$related_data[ $fields[ $field ]['id'] ]['related_field']['id'] ] );
4050
							unset( PodsField_Pick::$related_data[ $fields[ $field ]['id'] ] );
4051
						}
4052
					}
4053
				}
4054
			}
4055
		}
4056
4057
		if ( ! $no_conflict ) {
4058
			pods_no_conflict_off( $pod['type'] );
4059
		}
4060
4061
		// Clear cache
4062
		pods_cache_clear( $params->id, 'pods_items_' . $pod['name'] );
4063
4064
		if ( $params->clear_slug_cache && ! empty( $pod['field_slug'] ) ) {
4065
			$slug = pods( $pod['name'], $params->id )->field( $pod['field_slug'] );
4066
4067
			if ( 0 < strlen( $slug ) ) {
4068
				pods_cache_clear( $slug, 'pods_items_' . $pod['name'] );
4069
			}
4070
		}
4071
4072
		// Clear WP meta cache
4073
		if ( in_array( $pod['type'], array( 'post_type', 'media', 'taxonomy', 'user', 'comment' ) ) ) {
4074
			$meta_type = $pod['type'];
4075
4076
			if ( 'post_type' === $meta_type ) {
4077
				$meta_type = 'post';
4078
			}
4079
4080
			wp_cache_delete( $params->id, $meta_type . '_meta' );
4081
			wp_cache_delete( $params->id, 'pods_' . $meta_type . '_meta' );
4082
		}
4083
4084
		if ( false === $bypass_helpers ) {
4085
			if ( $track_changed_fields ) {
4086
				$changed_fields = self::handle_changed_fields( $params->pod, $params->id, 'get' );
4087
			}
4088
4089
			$compact_pieces = compact( $pieces );
4090
4091
			// Plugin hooks
4092
			$this->do_hook( 'post_save_pod_item', $compact_pieces, $is_new_item, $params->id );
4093
			$this->do_hook( "post_save_pod_item_{$params->pod}", $compact_pieces, $is_new_item, $params->id );
4094
4095
			if ( $is_new_item ) {
4096
				$this->do_hook( 'post_create_pod_item', $compact_pieces, $params->id );
4097
				$this->do_hook( "post_create_pod_item_{$params->pod}", $compact_pieces, $params->id );
4098
			} else {
4099
				$this->do_hook( 'post_edit_pod_item', $compact_pieces, $params->id );
4100
				$this->do_hook( "post_edit_pod_item_{$params->pod}", $compact_pieces, $params->id );
4101
			}
4102
4103
			// Call any post-save helpers (if not bypassed)
4104
			if ( ! defined( 'PODS_DISABLE_EVAL' ) || ! PODS_DISABLE_EVAL ) {
4105
				if ( ! empty( $post_save_helpers ) ) {
4106
					pods_deprecated( sprintf( __( 'Post-save helpers are deprecated, use the action pods_post_save_pod_item_%s instead', 'pods' ), $params->pod ), '2.0' );
4107
4108
					foreach ( $post_save_helpers as $helper ) {
4109
						$helper = $this->load_helper( array( 'name' => $helper ) );
4110
4111
						if ( false !== $helper && ( ! defined( 'PODS_DISABLE_EVAL' ) || ! PODS_DISABLE_EVAL ) ) {
4112
							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...
4113
						}
4114
					}
4115
				}
4116
			}
4117
		}
4118
4119
		// Success! Return the id
4120
		return $params->id;
4121
4122
	}
4123
4124
	/**
4125
	 * @see   PodsAPI::save_pod_item
4126
	 * Add multiple pod items
4127
	 *
4128
	 * $params['pod'] string The Pod name (pod or pod_id is required)
4129
	 * $params['pod_id'] string The Pod ID (pod or pod_id is required)
4130
	 * $params['bypass_helpers'] bool Set to true to bypass running pre-save and post-save helpers
4131
	 *
4132
	 * $data['id'] int The item ID (optional)
4133
	 * $data['data'] array An associative array of field names + values
4134
	 *
4135
	 * @param array|object $params An associative array of parameters, data excluded
4136
	 * @param array        $data   An associative array of pod ids and field names + values (arrays of field data)
4137
	 *
4138
	 * @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...
4139
	 * @since 2.0
4140
	 */
4141
	public function save_pod_items( $params, $data ) {
4142
4143
		$params = (object) $params;
4144
4145
		$ids = array();
4146
4147
		foreach ( $data as $fields ) {
4148
			$params->data = $fields;
4149
4150
			if ( isset( $fields['id'] ) && isset( $fields['data'] ) ) {
4151
				$params->id   = $fields['id'];
4152
				$params->data = $fields['data'];
4153
			}
4154
4155
			$ids[] = $this->save_pod_item( $params );
4156
		}
4157
4158
		return $ids;
4159
	}
4160
4161
	/**
4162
	 *Handle tracking changed fields or get them
4163
	 *
4164
	 * @param string $pod
4165
	 * @param int    $id
4166
	 * @param string $mode
4167
	 *
4168
	 * @return array List of changed fields (if $mode = 'get')
4169
	 */
4170
	public static function handle_changed_fields( $pod, $id, $mode = 'set' ) {
4171
4172
		static $changed_pods_cache = array();
4173
		static $old_fields_cache = array();
4174
		static $changed_fields_cache = array();
4175
4176
		$cache_key = $pod . '|' . $id;
4177
4178
		$export_params = array(
4179
			'depth' => 1,
4180
		);
4181
4182
		if ( in_array( $mode, array( 'set', 'reset' ), true ) ) {
4183
			if ( isset( $changed_fields_cache[ $cache_key ] ) ) {
4184
				unset( $changed_fields_cache[ $cache_key ] );
4185
			}
4186
4187
			if ( empty( $old_fields_cache[ $cache_key ] ) || 'reset' === $mode ) {
4188
				$old_fields_cache[ $cache_key ] = array();
4189
4190
				if ( ! empty( $id ) ) {
4191
					if ( ! isset( $changed_pods_cache[ $pod ] ) ) {
4192
						$changed_pods_cache[ $pod ] = pods( $pod );
4193
					}
4194
4195
					if ( $changed_pods_cache[ $pod ] && $changed_pods_cache[ $pod ]->valid() ) {
4196
						$changed_pods_cache[ $pod ]->fetch( $id );
4197
4198
						$old_fields_cache[ $cache_key ] = $changed_pods_cache[ $pod ]->export( $export_params );
4199
					}
4200
				}
4201
			}
4202
		}
4203
4204
		$changed_fields = array();
4205
4206
		if ( isset( $changed_fields_cache[ $cache_key ] ) ) {
4207
			$changed_fields = $changed_fields_cache[ $cache_key ];
4208
		} elseif ( isset( $old_fields_cache[ $cache_key ] ) ) {
4209
			$old_fields = $old_fields_cache[ $cache_key ];
4210
4211
			if ( 'get' === $mode ) {
4212
				$changed_fields_cache[ $cache_key ] = array();
4213
4214
				if ( ! empty( $changed_pods_cache[ $pod ] ) ) {
4215
					if ( $id != $changed_pods_cache[ $pod ]->id() ) {
4216
						$changed_pods_cache[ $pod ]->fetch( $id );
4217
					}
4218
4219
					$new_fields = $changed_pods_cache[ $pod ]->export( $export_params );
4220
4221
					foreach ( $new_fields as $field => $value ) {
4222
						if ( ! isset( $old_fields[ $field ] ) || $value != $old_fields[ $field ] ) {
4223
							$changed_fields[ $field ] = $value;
4224
						}
4225
					}
4226
4227
					$changed_fields_cache[ $cache_key ] = $changed_fields;
4228
				}
4229
			}
4230
		}
4231
4232
		return $changed_fields;
4233
4234
	}
4235
4236
	/**
4237
	 * Get the fields that have changed during a save
4238
	 *
4239
	 * @param array $pieces Pieces array from save_pod_item
4240
	 *
4241
	 * @return array Array of fields and values that have changed
4242
	 *
4243
	 * @deprecated Use PodsAPI::handle_changed_fields
4244
	 */
4245
	public function get_changed_fields( $pieces ) {
4246
4247
		return self::handle_changed_fields( $pieces['params']->pod, $pieces['params']->id, 'get' );
4248
4249
	}
4250
4251
	/**
4252
	 * Save relationships
4253
	 *
4254
	 * @param int       $id         ID of item
4255
	 * @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...
4256
	 * @param array     $pod        Pod data
4257
	 * @param array     $field      Field data
4258
	 */
4259
	public function save_relationships( $id, $related_ids, $pod, $field ) {
4260
4261
		// Get current values
4262
		if ( 'pick' === $field['type'] && isset( PodsField_Pick::$related_data[ $field['id'] ] ) && isset( PodsField_Pick::$related_data[ $field['id'] ]['current_ids'] ) ) {
4263
			$current_ids = PodsField_Pick::$related_data[ $field['id'] ]['current_ids'];
4264
		} else {
4265
			$current_ids = $this->lookup_related_items( $field['id'], $pod['id'], $id, $field, $pod );
4266
		}
4267
4268
		if ( isset( self::$related_item_cache[ $pod['id'] ][ $field['id'] ] ) ) {
4269
			// Delete relationship from cache
4270
			unset( self::$related_item_cache[ $pod['id'] ][ $field['id'] ] );
4271
		}
4272
4273
		if ( ! is_array( $related_ids ) ) {
4274
			$related_ids = implode( ',', $related_ids );
4275
		}
4276
4277
		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...
4278
			$related_ids[ $k ] = (int) $related_id;
4279
		}
4280
4281
		$related_ids = array_unique( array_filter( $related_ids ) );
4282
4283
		$related_limit = (int) pods_var_raw( $field['type'] . '_limit', $field['options'], 0 );
4284
4285
		if ( 'single' === pods_var_raw( $field['type'] . '_format_type', $field['options'] ) ) {
4286
			$related_limit = 1;
4287
		}
4288
4289
		// Limit values
4290
		if ( 0 < $related_limit && ! empty( $related_ids ) ) {
4291
			$related_ids = array_slice( $related_ids, 0, $related_limit );
4292
		}
4293
4294
		// Post Types, Media, Users, and Comments (meta-based)
4295
		if ( in_array( $pod['type'], array( 'post_type', 'media', 'taxonomy', 'user', 'comment' ) ) ) {
4296
			$object_type = $pod['type'];
4297
4298
			if ( in_array( $object_type, array( 'post_type', 'media' ) ) ) {
4299
				$object_type = 'post';
4300
			} elseif ( 'taxonomy' === $object_type ) {
4301
				$object_type = 'term';
4302
			}
4303
4304
			delete_metadata( $object_type, $id, $field['name'] );
4305
4306
			if ( ! empty( $related_ids ) ) {
4307
				update_metadata( $object_type, $id, '_pods_' . $field['name'], $related_ids );
4308
4309
				foreach ( $related_ids as $related_id ) {
4310
					add_metadata( $object_type, $id, $field['name'], $related_id );
4311
				}
4312
			} else {
4313
				delete_metadata( $object_type, $id, '_pods_' . $field['name'] );
4314
			}
4315
		} elseif ( 'settings' === $pod['type'] ) {
4316
			// Custom Settings Pages (options-based)
4317
			if ( ! empty( $related_ids ) ) {
4318
				update_option( $pod['name'] . '_' . $field['name'], $related_ids );
4319
			} else {
4320
				delete_option( $pod['name'] . '_' . $field['name'] );
4321
			}
4322
		}
4323
4324
		$related_pod_id = $related_field_id = 0;
4325
4326
		if ( 'pick' === $field['type'] && isset( PodsField_Pick::$related_data[ $field['id'] ] ) && ! empty( PodsField_Pick::$related_data[ $field['id'] ]['related_field'] ) ) {
4327
			$related_pod_id   = PodsField_Pick::$related_data[ $field['id'] ]['related_pod']['id'];
4328
			$related_field_id = PodsField_Pick::$related_data[ $field['id'] ]['related_field']['id'];
4329
		}
4330
4331
		// Relationships table
4332
		if ( ! pods_tableless() ) {
4333
			$related_weight = 0;
4334
4335
			foreach ( $related_ids as $related_id ) {
4336
				if ( in_array( $related_id, $current_ids ) ) {
4337
					pods_query( "
4338
                        UPDATE `@wp_podsrel`
4339
                        SET
4340
                            `pod_id` = %d,
4341
                            `field_id` = %d,
4342
                            `item_id` = %d,
4343
                            `related_pod_id` = %d,
4344
                            `related_field_id` = %d,
4345
                            `related_item_id` = %d,
4346
                            `weight` = %d
4347
                        WHERE
4348
                            `pod_id` = %d
4349
                            AND `field_id` = %d
4350
                            AND `item_id` = %d
4351
                            AND `related_item_id` = %d
4352
                    ", array(
4353
						$pod['id'],
4354
						$field['id'],
4355
						$id,
4356
						$related_pod_id,
4357
						$related_field_id,
4358
						$related_id,
4359
						$related_weight,
4360
4361
						$pod['id'],
4362
						$field['id'],
4363
						$id,
4364
						$related_id,
4365
					) );
4366
				} else {
4367
					pods_query( "
4368
                        INSERT INTO `@wp_podsrel`
4369
                            (
4370
                                `pod_id`,
4371
                                `field_id`,
4372
                                `item_id`,
4373
                                `related_pod_id`,
4374
                                `related_field_id`,
4375
                                `related_item_id`,
4376
                                `weight`
4377
                            )
4378
                        VALUES ( %d, %d, %d, %d, %d, %d, %d )
4379
                    ", array(
4380
						$pod['id'],
4381
						$field['id'],
4382
						$id,
4383
						$related_pod_id,
4384
						$related_field_id,
4385
						$related_id,
4386
						$related_weight
4387
					) );
4388
				}
4389
4390
				$related_weight ++;
4391
			}
4392
		}
4393
	}
4394
4395
	/**
4396
	 * Duplicate a Pod
4397
	 *
4398
	 * $params['id'] int The Pod ID
4399
	 * $params['name'] string The Pod name
4400
	 * $params['new_name'] string The new Pod name
4401
	 *
4402
	 * @param array $params An associative array of parameters
4403
	 * @param bool  $strict (optional) Makes sure a pod exists, if it doesn't throws an error
4404
	 *
4405
	 * @return int New Pod ID
4406
	 * @since 2.3
4407
	 */
4408
	public function duplicate_pod( $params, $strict = false ) {
4409
4410
		if ( ! is_object( $params ) && ! is_array( $params ) ) {
4411
			if ( is_numeric( $params ) ) {
4412
				$params = array( 'id' => $params );
4413
			} else {
4414
				$params = array( 'name' => $params );
4415
			}
4416
4417
			$params = (object) pods_sanitize( $params );
4418
		} else {
4419
			$params = (object) pods_sanitize( $params );
4420
		}
4421
4422
		$params->table_info = false;
4423
4424
		$pod = $this->load_pod( $params, $strict );
4425
4426
		if ( empty( $pod ) ) {
4427
			if ( false !== $strict ) {
4428
				return pods_error( __( 'Pod not found', 'pods' ), $this );
4429
			}
4430
4431
			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...
4432
		} elseif ( in_array( $pod['type'], array( 'media', 'user', 'comment' ) ) ) {
4433
			if ( false !== $strict ) {
4434
				return pods_error( __( 'Pod not allowed to be duplicated', 'pods' ), $this );
4435
			}
4436
4437
			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...
4438
		} elseif ( in_array( $pod['type'], array( 'post_type', 'taxonomy' ) ) && 0 < strlen( $pod['object'] ) ) {
4439
			$pod['object'] = '';
4440
		}
4441
4442
		unset( $pod['id'] );
4443
4444
		if ( isset( $params->new_name ) ) {
4445
			$pod['name'] = $params->new_name;
4446
		}
4447
4448
		$try = 1;
4449
4450
		$check_name = $pod['name'];
4451
		$new_label  = $pod['label'];
4452
4453
		while ( $this->load_pod( array( 'name' => $check_name, 'table_info' => false ), false ) ) {
4454
			$try ++;
4455
4456
			$check_name = $pod['name'] . $try;
4457
			$new_label  = $pod['label'] . $try;
4458
		}
4459
4460
		$pod['name']  = $check_name;
4461
		$pod['label'] = $new_label;
4462
4463
		foreach ( $pod['fields'] as $field => $field_data ) {
4464
			unset( $pod['fields'][ $field ]['id'] );
4465
		}
4466
4467
		return $this->save_pod( $pod );
4468
	}
4469
4470
	/**
4471
	 * Duplicate a Field
4472
	 *
4473
	 * $params['pod_id'] int The Pod ID
4474
	 * $params['pod'] string The Pod name
4475
	 * $params['id'] int The Field ID
4476
	 * $params['name'] string The Field name
4477
	 * $params['new_name'] string The new Field name
4478
	 *
4479
	 * @param array $params An associative array of parameters
4480
	 * @param bool  $strict (optional) Makes sure a field exists, if it doesn't throws an error
4481
	 *
4482
	 * @return int New Field ID
4483
	 * @since 2.3.10
4484
	 */
4485
	public function duplicate_field( $params, $strict = false ) {
4486
4487
		if ( ! is_object( $params ) && ! is_array( $params ) ) {
4488
			if ( is_numeric( $params ) ) {
4489
				$params = array( 'id' => $params );
4490
			} else {
4491
				$params = array( 'name' => $params );
4492
			}
4493
		}
4494
4495
		$params = (object) pods_sanitize( $params );
4496
4497
		$params->table_info = false;
4498
4499
		$field = $this->load_field( $params, $strict );
4500
4501
		if ( empty( $field ) ) {
4502
			if ( false !== $strict ) {
4503
				return pods_error( __( 'Field not found', 'pods' ), $this );
4504
			}
4505
4506
			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...
4507
		}
4508
4509
		unset( $field['id'] );
4510
4511
		if ( isset( $params->new_name ) ) {
4512
			$field['name'] = $params->new_name;
4513
		}
4514
4515
		$try = 1;
4516
4517
		$check_name = $field['name'];
4518
		$new_label  = $field['label'];
4519
4520
		while ( $this->load_field( array(
4521
			'pod_id'     => $field['pod_id'],
4522
			'name'       => $check_name,
4523
			'table_info' => false
4524
		), false ) ) {
4525
			$try ++;
4526
4527
			$check_name = $field['name'] . $try;
4528
			$new_label  = $field['label'] . $try;
4529
		}
4530
4531
		$field['name']  = $check_name;
4532
		$field['label'] = $new_label;
4533
4534
		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 4534 which is incompatible with the return type documented by PodsAPI::duplicate_field of type integer.
Loading history...
4535
4536
	}
4537
4538
	/**
4539
	 * @see   PodsAPI::save_pod_item
4540
	 *
4541
	 * Duplicate a pod item
4542
	 *
4543
	 * $params['pod'] string The Pod name
4544
	 * $params['id'] int The item's ID from the wp_pods_* table
4545
	 *
4546
	 * @param array $params An associative array of parameters
4547
	 *
4548
	 * @return int The table row ID
4549
	 *
4550
	 * @since 1.12
4551
	 */
4552
	public function duplicate_pod_item( $params ) {
4553
4554
		$params = (object) pods_sanitize( $params );
4555
4556
		$load_pod_params = array(
4557
			'name'       => $params->pod,
4558
			'table_info' => false,
4559
		);
4560
4561
		$pod = $this->load_pod( $load_pod_params );
4562
4563
		if ( false === $pod ) {
4564
			return pods_error( __( 'Pod not found', 'pods' ), $this );
4565
		}
4566
4567
		$pod = pods( $params->pod, $params->id );
4568
4569
		$params->pod    = $pod->pod;
4570
		$params->pod_id = $pod->pod_id;
4571
4572
		$fields        = (array) pods_var_raw( 'fields', $pod->pod_data, array(), null, true );
4573
		$object_fields = (array) pods_var_raw( 'object_fields', $pod->pod_data, array(), null, true );
4574
4575
		if ( ! empty( $object_fields ) ) {
4576
			$fields = array_merge( $object_fields, $fields );
4577
		}
4578
4579
		$save_params = array(
4580
			'pod'         => $params->pod,
4581
			'data'        => array(),
4582
			'is_new_item' => true,
4583
		);
4584
4585
		$ignore_fields = array(
4586
			$pod->pod_data['field_id'],
4587
			$pod->pod_data['field_slug'],
4588
		);
4589
4590
		if ( in_array( $pod->pod_data['type'], array( 'post_type', 'media' ) ) ) {
4591
			$ignore_fields = array(
4592
				'ID',
4593
				'post_name',
4594
				'post_date',
4595
				'post_date_gmt',
4596
				'post_modified',
4597
				'post_modified_gmt',
4598
				'guid',
4599
			);
4600
		} elseif ( 'term' === $pod->pod_data['type'] ) {
4601
			$ignore_fields = array(
4602
				'term_id',
4603
				'term_taxonomy_id',
4604
				'slug',
4605
			);
4606
		} elseif ( 'user' === $pod->pod_data['type'] ) {
4607
			$ignore_fields = array(
4608
				'ID',
4609
				'user_nicename',
4610
			);
4611
		} elseif ( 'comment' === $pod->pod_data['type'] ) {
4612
			$ignore_fields = array(
4613
				'comment_ID',
4614
			);
4615
		} elseif ( 'settings' === $pod->pod_data['type'] ) {
4616
			return pods_error( __( 'Settings do not support duplication.', 'pods' ), $this );
4617
		}
4618
4619
		/**
4620
		 * Filter the fields to ignore during duplication
4621
		 *
4622
		 * @since 2.6.6
4623
		 *
4624
		 * @param array  $ignore_fields Fields to ignore and not save during duplication
4625
		 * @param Pods   $pod           Pod object
4626
		 * @param array  $fields        Fields on the pod to duplicate
4627
		 * @param object $params        Params passed into duplicate_pod_item()
4628
		 */
4629
		$ignore_fields = apply_filters( 'pods_api_duplicate_pod_item_ignore_fields', $ignore_fields, $pod, $fields, $params );
4630
4631
		foreach ( $fields as $field ) {
4632
			if ( in_array( $field['name'], $ignore_fields ) ) {
4633
				continue;
4634
			}
4635
4636
			$field = array(
4637
				'name'   => $field['name'],
4638
				'output' => 'ids'
4639
			);
4640
4641
			$value = $pod->field( $field );
4642
4643
			// @todo Add post type compatibility to set unique post_title
4644
			// @todo Add term compatibility to set unique name
4645
			// @todo Add user compatibility to set unique user_login/user_email
4646
4647
			if ( ! empty( $value ) || ( ! is_array( $value ) && 0 < strlen( $value ) ) ) {
4648
				$save_params['data'][ $field['name'] ] = $value;
4649
			}
4650
		}
4651
4652
		$save_params = $this->do_hook( 'duplicate_pod_item', $save_params, $pod->pod, $pod->id(), $params );
4653
4654
		$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 4656 which is incompatible with the return type documented by PodsAPI::duplicate_pod_item of type integer.
Loading history...
4655
4656
		return $id;
4657
4658
	}
4659
4660
	/**
4661
	 * @see   pods()
4662
	 *
4663
	 * Export a pod item
4664
	 *
4665
	 * $params['pod'] string The Pod name
4666
	 * $params['id'] int The item's ID from the wp_pods_* table
4667
	 * $params['fields'] array The fields to export
4668
	 * $params['depth'] int How many levels deep to export data
4669
	 *
4670
	 * @param array  $params An associative array of parameters
4671
	 * @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...
4672
	 *
4673
	 * @return int The table row ID
4674
	 * @since 1.12
4675
	 */
4676
	public function export_pod_item( $params, $pod = null ) {
4677
4678
		if ( ! is_object( $pod ) || 'Pods' !== get_class( $pod ) ) {
4679
			if ( empty( $params ) ) {
4680
				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...
4681
			}
4682
4683
			if ( is_object( $params ) ) {
4684
				$params = get_object_vars( (object) $params );
4685
			}
4686
4687
			$params = pods_sanitize( $params );
4688
4689
			$pod = pods( $params['pod'], $params['id'], false );
4690
4691
			if ( empty( $pod ) ) {
4692
				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...
4693
			}
4694
		}
4695
4696
		$params['fields']        = (array) pods_v( 'fields', $params, array(), true );
4697
		$params['depth']         = (int) pods_v( 'depth', $params, 2, true );
4698
		$params['object_fields'] = (array) pods_v( 'object_fields', $pod->pod_data, array(), true );
4699
		$params['flatten']       = (boolean) pods_v( 'flatten', $params, false, true );
4700
		$params['context']       = pods_v( 'context', $params, null, true );
4701
4702
		if ( empty( $params['fields'] ) ) {
4703
			$params['fields'] = array_merge( $pod->fields, $params['object_fields'] );
4704
		}
4705
4706
		$data = $this->export_pod_item_level( $pod, $params );
4707
4708
		$data = $this->do_hook( 'export_pod_item', $data, $pod->pod, $pod->id(), $pod, $params['fields'], $params['depth'], $params['flatten'], $params );
4709
4710
		return $data;
4711
	}
4712
4713
	/**
4714
	 * Export a pod item by depth level
4715
	 *
4716
	 * @param Pods  $pod    Pods object
4717
	 * @param array $params Export params
4718
	 *
4719
	 * @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...
4720
	 *
4721
	 * @since 2.3
4722
	 */
4723
	private function export_pod_item_level( $pod, $params ) {
4724
4725
		$fields        = $params['fields'];
4726
		$depth         = $params['depth'];
4727
		$flatten       = $params['flatten'];
4728
		$current_depth = pods_v( 'current_depth', $params, 1, true );
4729
		$context       = $params['context'];
4730
4731
		$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...
4732
		$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...
4733
4734
		$object_fields = (array) pods_v( 'object_fields', $pod->pod_data, array(), true );
4735
4736
		$export_fields = array();
4737
4738
		$pod_type = $pod->pod_data['type'];
4739
4740
		if ( 'post_type' === $pod_type ) {
4741
			$pod_type = 'post';
4742
		} elseif ( 'taxonomy' === $pod_type ) {
4743
			$pod_type = 'term';
4744
		}
4745
4746
		$registered_meta_keys = false;
4747
4748
		if ( function_exists( 'get_registered_meta_keys' ) ) {
4749
			$registered_meta_keys = get_registered_meta_keys( $pod_type );
4750
		}
4751
4752
		$show_in_rest = false;
4753
4754
		// If in rest, check if this pod can be exposed
4755
		if ( 'rest' === $context ) {
4756
			$read_all = (int) pods_v( 'read_all', $pod->pod_data['options'], 0 );
4757
4758
			if ( 1 === $read_all ) {
4759
				$show_in_rest = true;
4760
			}
4761
		}
4762
4763
		foreach ( $fields as $k => $field ) {
4764
			if ( ! is_array( $field ) ) {
4765
				$field = array(
4766
					'id'   => 0,
4767
					'name' => $field
4768
				);
4769
			}
4770
4771
			if ( isset( $pod->fields[ $field['name'] ] ) ) {
4772
				// If in rest, check if this field can be exposed
4773
				if ( 'rest' === $context && false === $show_in_rest ) {
4774
					$show_in_rest = PodsRESTFields::field_allowed_to_extend( $field['name'], $pod, 'read' );
4775
4776
					if ( false === $show_in_rest ) {
4777
						// Fallback to checking $registered_meta_keys
4778
						if ( false !== $registered_meta_keys ) {
4779
							if ( ! isset( $registered_meta_keys[ $field['name'] ] ) ) {
4780
								continue;
4781
							} elseif ( empty( $registered_meta_keys[ $field['name'] ]['show_in_rest'] ) ) {
4782
								continue;
4783
							}
4784
						}
4785
					}
4786
				}
4787
4788
				$field                = $pod->fields[ $field['name'] ];
4789
				$field['lookup_name'] = $field['name'];
4790
4791
				if ( in_array( $field['type'], $tableless_field_types ) && ! in_array( pods_var( 'pick_object', $field ), $simple_tableless_objects ) ) {
4792
					if ( 'pick' === $field['type'] ) {
4793
						if ( empty( $field['table_info'] ) ) {
4794
							$field['table_info'] = $this->get_table_info( pods_var_raw( 'pick_object', $field ), pods_var_raw( 'pick_val', $field ), null, null, $field );
4795
						}
4796
4797
						if ( ! empty( $field['table_info'] ) ) {
4798
							$field['lookup_name'] .= '.' . $field['table_info']['field_id'];
4799
						}
4800
					} elseif ( in_array( $field['type'], PodsForm::file_field_types() ) ) {
4801
						$field['lookup_name'] .= '.guid';
4802
					}
4803
				}
4804
4805
				$export_fields[ $field['name'] ] = $field;
4806
			} elseif ( isset( $object_fields[ $field['name'] ] ) ) {
4807
				$field                = $object_fields[ $field['name'] ];
4808
				$field['lookup_name'] = $field['name'];
4809
4810
				$export_fields[ $field['name'] ] = $field;
4811
			} elseif ( $field['name'] == $pod->pod_data['field_id'] ) {
4812
				$field['type']        = 'number';
4813
				$field['lookup_name'] = $field['name'];
4814
4815
				$export_fields[ $field['name'] ] = $field;
4816
			}
4817
		}
4818
4819
		$data = array();
4820
4821
		foreach ( $export_fields as $field ) {
4822
			// Return IDs (or guid for files) if only one level deep
4823
			if ( 1 == $depth ) {
4824
				$data[ $field['name'] ] = $pod->field( array( 'name' => $field['lookup_name'], 'output' => 'arrays' ) );
4825
			} elseif ( ( - 1 == $depth || $current_depth < $depth ) && 'pick' === $field['type'] && ! in_array( pods_var( 'pick_object', $field ), $simple_tableless_objects ) ) {
4826
				// Recurse depth levels for pick fields if $depth allows
4827
				$related_data = array();
4828
4829
				$related_ids = $pod->field( array( 'name' => $field['name'], 'output' => 'ids' ) );
4830
4831
				if ( ! empty( $related_ids ) ) {
4832
					$related_ids = (array) $related_ids;
4833
4834
					$pick_object = pods_var_raw( 'pick_object', $field );
4835
4836
					$related_pod = pods( pods_var_raw( 'pick_val', $field ), null, false );
4837
4838
					// If this isn't a Pod, return data exactly as Pods does normally
4839
					if ( empty( $related_pod ) || ( 'pod' !== $pick_object && $pick_object !== $related_pod->pod_data['type'] ) || $related_pod->pod == $pod->pod ) {
4840
						$related_data = $pod->field( array( 'name' => $field['name'], 'output' => 'arrays' ) );
4841
					} else {
4842
						$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...
4843
4844
						$related_fields = array_merge( $related_pod->fields, $related_object_fields );
4845
4846
						foreach ( $related_ids as $related_id ) {
4847
							if ( $related_pod->fetch( $related_id ) ) {
4848
								$related_params = array(
4849
									'fields'        => $related_fields,
4850
									'depth'         => $depth,
4851
									'flatten'       => $flatten,
4852
									'current_depth' => $current_depth + 1,
4853
									'context'       => $context,
4854
								);
4855
4856
								$related_item = $this->export_pod_item_level( $related_pod, $related_params );
4857
4858
								$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 );
4859
							}
4860
						}
4861
4862
						if ( $flatten && ! empty( $related_data ) ) {
4863
							$related_data = pods_serial_comma( array_values( $related_data ), array(
4864
								'and'         => '',
4865
								'field_index' => $related_pod->pod_data['field_index']
4866
							) );
4867
						}
4868
					}
4869
				}
4870
4871
				$data[ $field['name'] ] = $related_data;
4872
			} else {
4873
				// Return data exactly as Pods does normally
4874
				$data[ $field['name'] ] = $pod->field( array( 'name' => $field['name'], 'output' => 'arrays' ) );
4875
			}
4876
4877
			if ( $flatten && is_array( $data[ $field['name'] ] ) ) {
4878
				$data[ $field['name'] ] = pods_serial_comma( $data[ $field['name'] ], array(
4879
					'field'  => $field['name'],
4880
					'fields' => $export_fields,
4881
					'and'    => ''
4882
				) );
4883
			}
4884
		}
4885
4886
		$data['id'] = (int) $pod->id();
4887
4888
		return $data;
4889
	}
4890
4891
	/**
4892
	 * Reorder a Pod
4893
	 *
4894
	 * $params['pod'] string The Pod name
4895
	 * $params['field'] string The field name of the field to reorder
4896
	 * $params['order'] array The key => value array of items to reorder (key should be an integer)
4897
	 *
4898
	 * @param array $params An associative array of parameters
4899
	 *
4900
	 * @return bool
4901
	 *
4902
	 * @since 1.9.0
4903
	 */
4904
	public function reorder_pod_item( $params ) {
4905
4906
		$params = (object) pods_sanitize( $params );
4907
4908
		// @deprecated 2.0
4909
		if ( isset( $params->datatype ) ) {
4910
			pods_deprecated( __( '$params->pod instead of $params->datatype', 'pods' ), '2.0' );
4911
4912
			$params->pod = $params->datatype;
4913
4914
			unset( $params->datatype );
4915
		}
4916
4917
		if ( null === pods_var_raw( 'pod', $params, null, null, true ) ) {
4918
			return pods_error( __( '$params->pod is required', 'pods' ), $this );
4919
		}
4920
4921
		if ( ! is_array( $params->order ) ) {
4922
			$params->order = explode( ',', $params->order );
4923
		}
4924
4925
		$pod = $this->load_pod( array( 'name' => $params->pod, 'table_info' => true ) );
4926
4927
		$params->name = $pod['name'];
4928
4929
		if ( false === $pod ) {
4930
			return pods_error( __( 'Pod is required', 'pods' ), $this );
4931
		}
4932
4933
		foreach ( $params->order as $order => $id ) {
4934
			if ( isset( $pod['fields'][ $params->field ] ) || isset( $pod['object_fields'][ $params->field ] ) ) {
4935
				if ( 'table' === $pod['storage'] && ( ! pods_tableless() ) ) {
4936
					if ( isset( $pod['fields'][ $params->field ] ) ) {
4937
						pods_query( "UPDATE `@wp_pods_{$params->name}` SET `{$params->field}` = " . pods_absint( $order ) . " WHERE `id` = " . pods_absint( $id ) . " LIMIT 1" );
4938
					} else {
4939
						pods_query( "UPDATE `{$pod['table']}` SET `{$params->field}` = " . pods_absint( $order ) . " WHERE `{$pod['field_id']}` = " . pods_absint( $id ) . " LIMIT 1" );
4940
					}
4941
				} else {
4942
					$this->save_pod_item( array(
4943
						'pod'    => $params->pod,
4944
						'pod_id' => $params->pod_id,
4945
						'id'     => $id,
4946
						'data'   => array( $params->field => pods_absint( $order ) )
4947
					) );
4948
				}
4949
			}
4950
		}
4951
4952
		return true;
4953
	}
4954
4955
	/**
4956
	 *
4957
	 * Delete all content for a Pod
4958
	 *
4959
	 * $params['id'] int The Pod ID
4960
	 * $params['name'] string The Pod name
4961
	 *
4962
	 * @param array $params An associative array of parameters
4963
	 * @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...
4964
	 *
4965
	 * @return bool
4966
	 *
4967
	 * @uses  pods_query
4968
	 * @uses  pods_cache_clear
4969
	 *
4970
	 * @since 1.9.0
4971
	 */
4972
	public function reset_pod( $params, $pod = false ) {
4973
4974
		$params = (object) pods_sanitize( $params );
4975
4976
		$params->table_info = true;
4977
4978
		if ( empty( $pod ) ) {
4979
			$pod = $this->load_pod( $params );
4980
		}
4981
4982
		if ( false === $pod ) {
4983
			return pods_error( __( 'Pod not found', 'pods' ), $this );
4984
		}
4985
4986
		$params->id   = $pod['id'];
4987
		$params->name = $pod['name'];
4988
4989
		if ( ! pods_tableless() ) {
4990
			if ( 'table' === $pod['storage'] ) {
4991
				try {
4992
					pods_query( "TRUNCATE `@wp_pods_{$params->name}`", false );
4993
				} catch ( Exception $e ) {
4994
					// Allow pod to be reset if the table doesn't exist
4995
					if ( false === strpos( $e->getMessage(), 'Unknown table' ) ) {
4996
						return pods_error( $e->getMessage(), $this );
4997
					}
4998
				}
4999
			}
5000
5001
			pods_query( "DELETE FROM `@wp_podsrel` WHERE `pod_id` = {$params->id} OR `related_pod_id` = {$params->id}", false );
5002
		}
5003
5004
		// @todo Delete relationships from tableless relationships
5005
5006
		// Delete all posts/revisions from this post type
5007
		if ( in_array( $pod['type'], array( 'post_type', 'media' ) ) ) {
5008
			$type = pods_var( 'object', $pod, $pod['name'], null, true );
5009
5010
			$sql = "
5011
                DELETE `t`, `r`, `m`
5012
                FROM `{$pod['table']}` AS `t`
5013
                LEFT JOIN `{$pod['meta_table']}` AS `m`
5014
                    ON `m`.`{$pod['meta_field_id']}` = `t`.`{$pod['field_id']}`
5015
                LEFT JOIN `{$pod['table']}` AS `r`
5016
                    ON `r`.`post_parent` = `t`.`{$pod['field_id']}` AND `r`.`post_status` = 'inherit'
5017
                WHERE `t`.`{$pod['field_type']}` = '{$type}'
5018
            ";
5019
5020
			pods_query( $sql, false );
5021
		} elseif ( 'taxonomy' === $pod['type'] ) {
5022
			// Delete all terms from this taxonomy
5023
			if ( function_exists( 'get_term_meta' ) ) {
5024
				$sql = "
5025
                    DELETE `t`, `m`, `tt`, `tr`
5026
                    FROM `{$pod['table']}` AS `t`
5027
                    LEFT JOIN `{$pod['meta_table']}` AS `m`
5028
                        ON `m`.`{$pod['meta_field_id']}` = `t`.`{$pod['field_id']}`
5029
                    " . $pod['join']['tt'] . "
5030
                    " . $pod['join']['tr'] . "
5031
                    WHERE " . implode( ' AND ', $pod['where'] ) . "
5032
                ";
5033
			} else {
5034
				$sql = "
5035
                    DELETE `t`, `tt`, `tr`
5036
                    FROM `{$pod['table']}` AS `t`
5037
                    " . $pod['join']['tt'] . "
5038
                    " . $pod['join']['tr'] . "
5039
                    WHERE " . implode( ' AND ', $pod['where'] ) . "
5040
                ";
5041
			}
5042
5043
			pods_query( $sql, false );
5044
		} elseif ( 'user' === $pod['type'] ) {
5045
			// Delete all users except the current one
5046
			$sql = "
5047
                DELETE `t`, `m`
5048
                FROM `{$pod['table']}` AS `t`
5049
                LEFT JOIN `{$pod['meta_table']}` AS `m`
5050
                    ON `m`.`{$pod['meta_field_id']}` = `t`.`{$pod['field_id']}`
5051
                WHERE `t`.`{$pod['field_id']}` != " . (int) get_current_user_id() . "
5052
            ";
5053
5054
			pods_query( $sql, false );
5055
		} elseif ( 'comment' === $pod['type'] ) {
5056
			// Delete all comments
5057
			$type = pods_var( 'object', $pod, $pod['name'], null, true );
5058
5059
			$sql = "
5060
                DELETE `t`, `m`
5061
                FROM `{$pod['table']}` AS `t`
5062
                LEFT JOIN `{$pod['meta_table']}` AS `m`
5063
                    ON `m`.`{$pod['meta_field_id']}` = `t`.`{$pod['field_id']}`
5064
                WHERE `t`.`{$pod['field_type']}` = '{$type}'
5065
            ";
5066
5067
			pods_query( $sql, false );
5068
		}
5069
5070
		pods_cache_clear( true ); // only way to reliably clear out cached data across an entire group
5071
5072
		return true;
5073
	}
5074
5075
	/**
5076
	 * Delete a Pod and all its content
5077
	 *
5078
	 * $params['id'] int The Pod ID
5079
	 * $params['name'] string The Pod name
5080
	 *
5081
	 * @param array $params     An associative array of parameters
5082
	 * @param bool  $strict     (optional) Makes sure a pod exists, if it doesn't throws an error
5083
	 * @param bool  $delete_all (optional) Whether to delete all content from a WP object
5084
	 *
5085
	 * @uses  PodsAPI::load_pod
5086
	 * @uses  wp_delete_post
5087
	 * @uses  pods_query
5088
	 *
5089
	 * @return bool
5090
	 * @since 1.7.9
5091
	 */
5092
	public function delete_pod( $params, $strict = false, $delete_all = false ) {
5093
5094
		/**
5095
		 * @var $wpdb wpdb
5096
		 */
5097
		global $wpdb;
5098
5099
		if ( ! is_object( $params ) && ! is_array( $params ) ) {
5100
			if ( is_numeric( $params ) ) {
5101
				$params = array( 'id' => $params );
5102
			} else {
5103
				$params = array( 'name' => $params );
5104
			}
5105
5106
			$params = (object) pods_sanitize( $params );
5107
		} else {
5108
			$params = (object) pods_sanitize( $params );
5109
		}
5110
5111
		if ( ! isset( $params->delete_all ) ) {
5112
			$params->delete_all = $delete_all;
5113
		}
5114
5115
		$params->table_info = false;
5116
5117
		$pod = $this->load_pod( $params, $strict );
5118
5119
		if ( empty( $pod ) ) {
5120
			if ( false !== $strict ) {
5121
				return pods_error( __( 'Pod not found', 'pods' ), $this );
5122
			}
5123
5124
			return false;
5125
		}
5126
5127
		$params->id   = (int) $pod['id'];
5128
		$params->name = $pod['name'];
5129
5130
		// Reset content
5131
		if ( true === $params->delete_all ) {
5132
			$this->reset_pod( $params, $pod );
5133
		}
5134
5135
		foreach ( $pod['fields'] as $field ) {
5136
			$field['pod'] = $pod;
5137
5138
			$this->delete_field( $field, false );
5139
		}
5140
5141
		// Only delete the post once the fields are taken care of, it's not required anymore
5142
		$success = wp_delete_post( $params->id );
5143
5144
		if ( ! $success ) {
5145
			return pods_error( __( 'Pod unable to be deleted', 'pods' ), $this );
5146
		}
5147
5148
		if ( ! pods_tableless() ) {
5149
			if ( 'table' === $pod['storage'] ) {
5150
				try {
5151
					pods_query( "DROP TABLE IF EXISTS `@wp_pods_{$params->name}`", false );
5152
				} catch ( Exception $e ) {
5153
					// Allow pod to be deleted if the table doesn't exist
5154
					if ( false === strpos( $e->getMessage(), 'Unknown table' ) ) {
5155
						return pods_error( $e->getMessage(), $this );
5156
					}
5157
				}
5158
			}
5159
5160
			pods_query( "DELETE FROM `@wp_podsrel` WHERE `pod_id` = {$params->id} OR `related_pod_id` = {$params->id}", false );
5161
		}
5162
5163
		// @todo Delete relationships from tableless relationships
5164
5165
		// Delete any relationship references
5166
		$sql = "
5167
            DELETE `pm`
5168
            FROM `{$wpdb->postmeta}` AS `pm`
5169
            LEFT JOIN `{$wpdb->posts}` AS `p`
5170
                ON `p`.`post_type` = '_pods_field'
5171
                    AND `p`.`ID` = `pm`.`post_id`
5172
            LEFT JOIN `{$wpdb->postmeta}` AS `pm2`
5173
                ON `pm2`.`meta_key` = 'pick_object'
5174
                    AND `pm2`.`meta_value` = 'pod'
5175
                    AND `pm2`.`post_id` = `pm`.`post_id`
5176
            WHERE
5177
                `p`.`ID` IS NOT NULL
5178
                AND `pm2`.`meta_id` IS NOT NULL
5179
                AND `pm`.`meta_key` = 'pick_val'
5180
                AND `pm`.`meta_value` = '{$params->name}'
5181
        ";
5182
5183
		pods_query( $sql );
5184
5185
		$this->cache_flush_pods( $pod );
5186
5187
		return true;
5188
	}
5189
5190
	/**
5191
	 * Drop a field within a Pod
5192
	 *
5193
	 * $params['id'] int The field ID
5194
	 * $params['name'] int The field name
5195
	 * $params['pod'] string The Pod name
5196
	 * $params['pod_id'] string The Pod name
5197
	 *
5198
	 * @param array $params          An associative array of parameters
5199
	 * @param bool  $table_operation Whether or not to handle table operations
5200
	 *
5201
	 * @uses  PodsAPI::load_field
5202
	 * @uses  wp_delete_post
5203
	 * @uses  pods_query
5204
	 *
5205
	 * @return bool
5206
	 * @since 1.7.9
5207
	 */
5208
	public function delete_field( $params, $table_operation = true ) {
5209
5210
		/**
5211
		 * @var $wpdb wpdb
5212
		 */
5213
		global $wpdb;
5214
5215
		$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...
5216
		$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...
5217
5218
		$params = (object) pods_sanitize( $params );
5219
5220
		if ( ! isset( $params->pod ) ) {
5221
			$params->pod = '';
5222
		}
5223
5224
		if ( ! isset( $params->pod_id ) ) {
5225
			$params->pod_id = 0;
5226
		}
5227
5228
		$pod = $params->pod;
5229
5230
		$save_pod = false;
5231
5232
		if ( ! is_array( $pod ) ) {
5233
			$pod = $this->load_pod( array( 'name' => $pod, 'id' => $params->pod_id, 'table_info' => false ) );
5234
		} else {
5235
			$save_pod = true;
5236
		}
5237
5238
		if ( empty( $pod ) ) {
5239
			return pods_error( __( 'Pod not found', 'pods' ), $this );
5240
		}
5241
5242
		$params->pod_id = $pod['id'];
5243
		$params->pod    = $pod['name'];
5244
5245
		if ( ! isset( $params->name ) ) {
5246
			$params->name = '';
5247
		}
5248
5249
		if ( ! isset( $params->id ) ) {
5250
			$params->id = 0;
5251
		}
5252
5253
		$field = $this->load_field( array(
5254
			'name'   => $params->name,
5255
			'id'     => $params->id,
5256
			'pod'    => $params->pod,
5257
			'pod_id' => $params->pod_id
5258
		) );
5259
5260
		if ( false === $field ) {
5261
			return pods_error( __( 'Field not found', 'pods' ), $this );
5262
		}
5263
5264
		$params->id   = $field['id'];
5265
		$params->name = $field['name'];
5266
5267
		$simple = ( 'pick' === $field['type'] && in_array( pods_var( 'pick_object', $field ), $simple_tableless_objects ) );
5268
		$simple = (boolean) $this->do_hook( 'tableless_custom', $simple, $field, $pod, $params );
5269
5270
		if ( $table_operation && 'table' === $pod['storage'] && ( ! in_array( $field['type'], $tableless_field_types ) || $simple ) ) {
5271
			pods_query( "ALTER TABLE `@wp_pods_{$params->pod}` DROP COLUMN `{$params->name}`", false );
5272
		}
5273
5274
		$success = wp_delete_post( $params->id );
5275
5276
		if ( ! $success ) {
5277
			return pods_error( __( 'Field unable to be deleted', 'pods' ), $this );
5278
		}
5279
5280
		$wpdb->query( $wpdb->prepare( "DELETE pm FROM {$wpdb->postmeta} AS pm
5281
            LEFT JOIN {$wpdb->posts} AS p
5282
                ON p.post_type = '_pods_field' AND p.ID = pm.post_id
5283
            WHERE p.ID IS NOT NULL AND pm.meta_key = 'sister_id' AND pm.meta_value = %d", $params->id ) );
5284
5285
		if ( ( ! pods_tableless() ) && $table_operation ) {
5286
			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 );
5287
		}
5288
5289
		// @todo Delete tableless relationship meta
5290
5291
		if ( true === $save_pod ) {
5292
			$this->cache_flush_pods( $pod );
5293
		}
5294
5295
		return true;
5296
	}
5297
5298
	/**
5299
	 * Drop a Pod Object
5300
	 *
5301
	 * $params['id'] int The object ID
5302
	 * $params['name'] string The object name
5303
	 * $params['type'] string The object type
5304
	 *
5305
	 * @param array|object $params An associative array of parameters
5306
	 *
5307
	 * @uses  wp_delete_post
5308
	 *
5309
	 * @return bool
5310
	 * @since 2.0
5311
	 */
5312
	public function delete_object( $params ) {
5313
5314
		$params = (object) $params;
5315
		$object = $this->load_object( $params );
5316
5317
		if ( empty( $object ) ) {
5318
			return pods_error( sprintf( __( "%s Object not found", 'pods' ), ucwords( $params->type ) ), $this );
5319
		}
5320
5321
		$success = wp_delete_post( $params->id );
5322
5323
		if ( ! $success ) {
5324
			return pods_error( sprintf( __( "%s Object not deleted", 'pods' ), ucwords( $params->type ) ), $this );
5325
		}
5326
5327
		pods_transient_clear( 'pods_objects_' . $params->type );
5328
5329
		return true;
5330
	}
5331
5332
	/**
5333
	 * @see   PodsAPI::delete_object
5334
	 *
5335
	 * Drop a Pod Template
5336
	 *
5337
	 * $params['id'] int The template ID
5338
	 * $params['name'] string The template name
5339
	 *
5340
	 * @param array $params An associative array of parameters
5341
	 *
5342
	 * @return bool
5343
	 * @since 1.7.9
5344
	 */
5345
	public function delete_template( $params ) {
5346
5347
		$params       = (object) $params;
5348
		$params->type = 'template';
5349
5350
		return $this->delete_object( $params );
5351
	}
5352
5353
	/**
5354
	 * @see   PodsAPI::delete_object
5355
	 *
5356
	 * Drop a Pod Page
5357
	 *
5358
	 * $params['id'] int The page ID
5359
	 * $params['uri'] string The page URI
5360
	 *
5361
	 * @param array $params An associative array of parameters
5362
	 *
5363
	 * @return bool
5364
	 * @since 1.7.9
5365
	 */
5366
	public function delete_page( $params ) {
5367
5368
		$params = (object) $params;
5369
		if ( isset( $params->uri ) ) {
5370
			$params->name = $params->uri;
5371
			unset( $params->uri );
5372
		}
5373
		if ( isset( $params->name ) ) {
5374
			$params->name = trim( $params->name, '/' );
5375
		}
5376
		$params->type = 'page';
5377
5378
		return $this->delete_object( $params );
5379
	}
5380
5381
	/**
5382
	 * @see   PodsAPI::delete_object
5383
	 *
5384
	 * Drop a Pod Helper
5385
	 *
5386
	 * $params['id'] int The helper ID
5387
	 * $params['name'] string The helper name
5388
	 *
5389
	 * @param array $params An associative array of parameters
5390
	 *
5391
	 * @return bool
5392
	 * @since 1.7.9
5393
	 */
5394
	public function delete_helper( $params ) {
5395
5396
		$params       = (object) $params;
5397
		$params->type = 'helper';
5398
5399
		return $this->delete_object( $params );
5400
	}
5401
5402
	/**
5403
	 * Drop a single pod item
5404
	 *
5405
	 * $params['id'] int (optional) The item's ID from the wp_pod_* table (used with datatype parameter)
5406
	 * $params['pod'] string (optional) The Pod name (used with id parameter)
5407
	 * $params['pod_id'] int (optional) The Pod ID (used with id parameter)
5408
	 * $params['bypass_helpers'] bool Set to true to bypass running pre-save and post-save helpers
5409
	 *
5410
	 * @param array $params An associative array of parameters
5411
	 * @param bool  $wp     Whether to run WP object delete action
5412
	 *
5413
	 * @return bool
5414
	 * @since 1.7.9
5415
	 */
5416
	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...
5417
5418
		$params = (object) pods_sanitize( $params );
5419
5420
		// @deprecated 2.0
5421
		if ( isset( $params->datatype_id ) || isset( $params->datatype ) || isset( $params->tbl_row_id ) ) {
5422
			if ( isset( $params->tbl_row_id ) ) {
5423
				pods_deprecated( __( '$params->id instead of $params->tbl_row_id', 'pods' ), '2.0' );
5424
				$params->id = $params->tbl_row_id;
5425
				unset( $params->tbl_row_id );
5426
			}
5427
5428
			if ( isset( $params->pod_id ) ) {
5429
				pods_deprecated( __( '$params->id instead of $params->pod_id', 'pods' ), '2.0' );
5430
				$params->id = $params->pod_id;
5431
				unset( $params->pod_id );
5432
			}
5433
5434
			if ( isset( $params->dataype_id ) ) {
5435
				pods_deprecated( __( '$params->pod_id instead of $params->datatype_id', 'pods' ), '2.0' );
5436
				$params->pod_id = $params->dataype_id;
5437
				unset( $params->dataype_id );
5438
			}
5439
5440
			if ( isset( $params->datatype ) ) {
5441
				pods_deprecated( __( '$params->pod instead of $params->datatype', 'pods' ), '2.0' );
5442
				$params->pod = $params->datatype;
5443
				unset( $params->datatype );
5444
			}
5445
		}
5446
5447
		if ( ! isset( $params->id ) ) {
5448
			return pods_error( __( 'Pod Item not found', 'pods' ), $this );
5449
		}
5450
5451
		$params->id = pods_absint( $params->id );
5452
5453
		if ( ! isset( $params->pod ) ) {
5454
			$params->pod = '';
5455
		}
5456
5457
		if ( ! isset( $params->pod_id ) ) {
5458
			$params->pod_id = 0;
5459
		}
5460
5461
		$pod = $this->load_pod( array( 'name' => $params->pod, 'id' => $params->pod_id, 'table_info' => false ) );
5462
5463
		if ( false === $pod ) {
5464
			return pods_error( __( 'Pod not found', 'pods' ), $this );
5465
		}
5466
5467
		$params->pod_id = $pod['id'];
5468
		$params->pod    = $pod['name'];
5469
5470
		// Allow Helpers to bypass subsequent helpers in recursive delete_pod_item calls
5471
		$bypass_helpers = false;
5472
5473
		if ( isset( $params->bypass_helpers ) && false !== $params->bypass_helpers ) {
5474
			$bypass_helpers = true;
5475
		}
5476
5477
		$pre_delete_helpers = $post_delete_helpers = array();
5478
5479
		if ( false === $bypass_helpers ) {
5480
			// Plugin hook
5481
			$this->do_hook( 'pre_delete_pod_item', $params, $pod );
5482
			$this->do_hook( "pre_delete_pod_item_{$params->pod}", $params, $pod );
5483
5484
			// Call any pre-save helpers (if not bypassed)
5485
			if ( ! defined( 'PODS_DISABLE_EVAL' ) || ! PODS_DISABLE_EVAL ) {
5486
				if ( ! empty( $pod['options'] ) && is_array( $pod['options'] ) ) {
5487
					$helpers = array( 'pre_delete_helpers', 'post_delete_helpers' );
5488
5489
					foreach ( $helpers as $helper ) {
5490
						if ( isset( $pod['options'][ $helper ] ) && ! empty( $pod['options'][ $helper ] ) ) {
5491
							${$helper} = explode( ',', $pod['options'][ $helper ] );
5492
						}
5493
					}
5494
				}
5495
5496
				if ( ! empty( $pre_delete_helpers ) ) {
5497
					pods_deprecated( sprintf( __( 'Pre-delete helpers are deprecated, use the action pods_pre_delete_pod_item_%s instead', 'pods' ), $params->pod ), '2.0' );
5498
5499
					foreach ( $pre_delete_helpers as $helper ) {
5500
						$helper = $this->load_helper( array( 'name' => $helper ) );
5501
5502
						if ( false !== $helper ) {
5503
							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...
5504
						}
5505
					}
5506
				}
5507
			}
5508
		}
5509
5510
		// Delete object from relationship fields
5511
		$this->delete_object_from_relationships( $params->id, $pod );
5512
5513
		if ( 'table' === $pod['storage'] ) {
5514
			pods_query( "DELETE FROM `@wp_pods_{$params->pod}` WHERE `id` = {$params->id} LIMIT 1" );
5515
		}
5516
5517
		if ( $wp ) {
5518
			if ( 'taxonomy' === $pod['type'] ) {
5519
				$taxonomy = $pod['name'];
5520
5521
				if ( ! empty( $pod['object'] ) ) {
5522
					$taxonomy = $pod['object'];
5523
				}
5524
5525
				wp_delete_term( $params->id, $taxonomy );
5526
			} elseif ( ! in_array( $pod['type'], array( 'pod', 'table', '', 'taxonomy' ) ) ) {
5527
				$this->delete_wp_object( $pod['type'], $params->id );
5528
			}
5529
		}
5530
5531
		if ( false === $bypass_helpers ) {
5532
			// Plugin hook
5533
			$this->do_hook( 'post_delete_pod_item', $params, $pod );
5534
			$this->do_hook( "post_delete_pod_item_{$params->pod}", $params, $pod );
5535
5536
			// Call any post-save helpers (if not bypassed)
5537
			if ( ! defined( 'PODS_DISABLE_EVAL' ) || ! PODS_DISABLE_EVAL ) {
5538
				if ( ! empty( $post_delete_helpers ) ) {
5539
					pods_deprecated( sprintf( __( 'Post-delete helpers are deprecated, use the action pods_post_delete_pod_item_%s instead', 'pods' ), $params->pod ), '2.0' );
5540
5541
					foreach ( $post_delete_helpers as $helper ) {
5542
						$helper = $this->load_helper( array( 'name' => $helper ) );
5543
5544
						if ( false !== $helper ) {
5545
							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...
5546
						}
5547
					}
5548
				}
5549
			}
5550
		}
5551
5552
		pods_cache_clear( $params->id, 'pods_items_' . $params->pod );
5553
5554
		return true;
5555
	}
5556
5557
	/**
5558
	 * Delete an object from tableless fields
5559
	 *
5560
	 * @param int    $id
5561
	 * @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...
5562
	 * @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...
5563
	 *
5564
	 * @return bool
5565
	 *
5566
	 * @since 2.3
5567
	 */
5568
	public function delete_object_from_relationships( $id, $object, $name = null ) {
5569
5570
		/**
5571
		 * @var $pods_init \PodsInit
5572
		 */
5573
		global $pods_init;
5574
5575
		$pod = false;
5576
5577
		// Run any bidirectional delete operations
5578
		if ( is_array( $object ) ) {
5579
			$pod = $object;
5580
		} elseif ( is_object( $pods_init ) ) {
5581
			$pod = PodsInit::$meta->get_object( $object, $name );
5582
		}
5583
5584
		if ( ! empty( $pod ) ) {
5585
			$object = $pod['type'];
5586
			$name   = $pod['name'];
5587
5588
			foreach ( $pod['fields'] as $field ) {
5589
				PodsForm::delete( $field['type'], $id, $field['name'], array_merge( $field, $field['options'] ), $pod );
5590
			}
5591
		}
5592
5593
		// Lookup related fields (non-bidirectional)
5594
		$params = array(
5595
			'where' => array(
5596
				array(
5597
					'key'   => 'type',
5598
					'value' => 'pick'
5599
				),
5600
				array(
5601
					'key'   => 'pick_object',
5602
					'value' => $object
5603
				)
5604
			)
5605
		);
5606
5607
		if ( ! empty( $name ) && $name !== $object ) {
5608
			$params['where'][] = array(
5609
				'key'   => 'pick_val',
5610
				'value' => $name
5611
			);
5612
		}
5613
5614
		$fields = $this->load_fields( $params, false );
5615
5616
		if ( ! empty( $pod ) && 'media' === $pod['type'] ) {
5617
			$params['where'] = array(
5618
				array(
5619
					'key'   => 'type',
5620
					'value' => 'file'
5621
				)
5622
			);
5623
5624
			$fields = array_merge( $fields, $this->load_fields( $params, false ) );
5625
		}
5626
5627
		if ( is_array( $fields ) && ! empty( $fields ) ) {
5628
			foreach ( $fields as $related_field ) {
5629
				$related_pod = $this->load_pod( array( 'id' => $related_field['pod_id'], 'fields' => false ), false );
5630
5631
				if ( empty( $related_pod ) ) {
5632
					continue;
5633
				}
5634
5635
				$related_from = $this->lookup_related_items_from( $related_field['id'], $related_pod['id'], $id, $related_field, $related_pod );
5636
5637
				$this->delete_relationships( $related_from, $id, $related_pod, $related_field );
5638
			}
5639
		}
5640
5641
		if ( ! empty( $pod ) && ! pods_tableless() ) {
5642
			pods_query( "
5643
                DELETE FROM `@wp_podsrel`
5644
                WHERE
5645
                (
5646
                    `pod_id` = %d
5647
                    AND `item_id` = %d
5648
                )
5649
                OR (
5650
                    `related_pod_id` = %d
5651
                    AND `related_item_id` = %d
5652
                )
5653
            ", array(
5654
				$pod['id'],
5655
				$id,
5656
5657
				$pod['id'],
5658
				$id
5659
			) );
5660
		}
5661
5662
		return true;
5663
	}
5664
5665
	/**
5666
	 * Delete relationships
5667
	 *
5668
	 * @param int|array $related_id    IDs for items to save
5669
	 * @param int|array $id            ID or IDs to remove
5670
	 * @param array     $related_pod   Pod data
5671
	 * @param array     $related_field Field data
5672
	 *
5673
	 * @return void
5674
	 *
5675
	 * @since 2.3
5676
	 */
5677
	public function delete_relationships( $related_id, $id, $related_pod, $related_field ) {
5678
5679
		if ( is_array( $related_id ) ) {
5680
			foreach ( $related_id as $rid ) {
5681
				$this->delete_relationships( $rid, $id, $related_pod, $related_field );
5682
			}
5683
5684
			return;
5685
		}
5686
5687
		if ( is_array( $id ) ) {
5688
			foreach ( $id as $rid ) {
5689
				$this->delete_relationships( $related_id, $rid, $related_pod, $related_field );
5690
			}
5691
5692
			return;
5693
		}
5694
5695
		$id = (int) $id;
5696
5697
		if ( empty( $id ) ) {
5698
			return;
5699
		}
5700
5701
		$related_ids = $this->lookup_related_items( $related_field['id'], $related_pod['id'], $related_id, $related_field, $related_pod );
5702
5703
		if ( empty( $related_ids ) ) {
5704
			return;
5705
		} elseif ( ! in_array( $id, $related_ids ) ) {
5706
			return;
5707
		}
5708
5709
		if ( isset( self::$related_item_cache[ $related_pod['id'] ][ $related_field['id'] ] ) ) {
5710
			// Delete relationship from cache
5711
			unset( self::$related_item_cache[ $related_pod['id'] ][ $related_field['id'] ] );
5712
		}
5713
5714
		// @codingStandardsIgnoreLine
5715
		unset( $related_ids[ array_search( $id, $related_ids ) ] );
5716
5717
		$no_conflict = pods_no_conflict_check( $related_pod['type'] );
5718
5719
		if ( ! $no_conflict ) {
5720
			pods_no_conflict_on( $related_pod['type'] );
5721
		}
5722
5723
		// Post Types, Media, Users, and Comments (meta-based)
5724
		if ( in_array( $related_pod['type'], array( 'post_type', 'media', 'taxonomy', 'user', 'comment' ) ) ) {
5725
			$object_type = $related_pod['type'];
5726
5727
			if ( in_array( $object_type, array( 'post_type', 'media' ) ) ) {
5728
				$object_type = 'post';
5729
			} elseif ( 'taxonomy' === $object_type ) {
5730
				$object_type = 'term';
5731
			}
5732
5733
			delete_metadata( $object_type, $related_id, $related_field['name'] );
5734
5735
			if ( ! empty( $related_ids ) ) {
5736
				update_metadata( $object_type, $related_id, '_pods_' . $related_field['name'], $related_ids );
5737
5738
				foreach ( $related_ids as $rel_id ) {
5739
					add_metadata( $object_type, $related_id, $related_field['name'], $rel_id );
5740
				}
5741
			} else {
5742
				delete_metadata( $object_type, $related_id, '_pods_' . $related_field['name'] );
5743
			}
5744
		} elseif ( 'settings' === $related_pod['type'] ) {
5745
			// Custom Settings Pages (options-based)
5746
			if ( ! empty( $related_ids ) ) {
5747
				update_option( $related_pod['name'] . '_' . $related_field['name'], $related_ids );
5748
			} else {
5749
				delete_option( $related_pod['name'] . '_' . $related_field['name'] );
5750
			}
5751
		}
5752
5753
		// Relationships table
5754
		if ( ! pods_tableless() ) {
5755
			pods_query( "
5756
                DELETE FROM `@wp_podsrel`
5757
                WHERE
5758
                (
5759
                    `pod_id` = %d
5760
                    AND `field_id` = %d
5761
                    AND `item_id` = %d
5762
                    AND `related_item_id` = %d
5763
                )
5764
                OR (
5765
                    `related_pod_id` = %d
5766
                    AND `related_field_id` = %d
5767
                    AND `related_item_id` = %d
5768
                    AND `item_id` = %d
5769
                )
5770
            ", array(
5771
				$related_pod['id'],
5772
				$related_field['id'],
5773
				$related_id,
5774
				$id,
5775
5776
				$related_pod['id'],
5777
				$related_field['id'],
5778
				$related_id,
5779
				$id
5780
			) );
5781
		}
5782
5783
		if ( ! $no_conflict ) {
5784
			pods_no_conflict_off( $related_pod['type'] );
5785
		}
5786
	}
5787
5788
	/**
5789
	 * Check if a Pod exists
5790
	 *
5791
	 * $params['id'] int Pod ID
5792
	 * $params['name'] string Pod name
5793
	 *
5794
	 * @param array $params An associative array of parameters
5795
	 *
5796
	 * @return bool True if exists
5797
	 *
5798
	 * @since 1.12
5799
	 */
5800
	public function pod_exists( $params, $type = null ) {
5801
5802
		if ( is_string( $params ) ) {
5803
			$params = array( 'name' => $params );
5804
		}
5805
5806
		$params = (object) pods_sanitize( $params );
5807
5808
		if ( ! empty( $params->id ) || ! empty( $params->name ) ) {
5809
			if ( ! isset( $params->name ) ) {
5810
				$pod = get_post( $dummy = (int) $params->id );
5811
			} else {
5812
				$pod = get_posts( array(
5813
					'name'           => $params->name,
5814
					'post_type'      => '_pods_pod',
5815
					'posts_per_page' => 1
5816
				) );
5817
5818
				if ( is_array( $pod ) && ! empty( $pod[0] ) ) {
5819
					$pod = $pod[0];
5820
				}
5821
			}
5822
5823
			if ( ! empty( $pod ) && ( empty( $type ) || $type == get_post_meta( $pod->ID, 'type', true ) ) ) {
5824
				return true;
5825
			}
5826
		}
5827
5828
		return false;
5829
	}
5830
5831
	/**
5832
	 * Get number of pods for a specific pod type
5833
	 *
5834
	 * @param string $type Type to get count
5835
	 *
5836
	 * @return int Total number of pods for a type
5837
	 *
5838
	 * @since 2.6.6
5839
	 */
5840
	public function get_pod_type_count( $type ) {
5841
5842
		$args = array(
5843
			'post_type'      => '_pods_pod',
5844
			'posts_per_page' => - 1,
5845
			'nopaging'       => true,
5846
			'fields'         => 'ids',
5847
			'meta_query'     => array(
5848
				array(
5849
					'key'   => 'type',
5850
					'value' => $type,
5851
				),
5852
			),
5853
		);
5854
5855
		$posts = get_posts( $args );
5856
5857
		$total = count( $posts );
5858
5859
		return $total;
5860
5861
	}
5862
5863
	/**
5864
	 * Load a Pod and all of its fields
5865
	 *
5866
	 * $params['id'] int The Pod ID
5867
	 * $params['name'] string The Pod name
5868
	 * $params['fields'] bool Whether to load fields (default is true)
5869
	 * $params['bypass_cache'] boolean Bypass the cache when getting data
5870
	 *
5871
	 * @param array|object $params An associative array of parameters or pod name as a string
5872
	 * @param bool         $strict Makes sure the pod exists, throws an error if it doesn't work
5873
	 *
5874
	 * @return array|bool|mixed|void
5875
	 * @since 1.7.9
5876
	 */
5877
	public function load_pod( $params, $strict = true ) {
5878
5879
		/**
5880
		 * @var $sitepress SitePress
5881
		 * @var $wpdb      wpdb
5882
		 */
5883
		global $wpdb;
5884
5885
		$current_language = false;
5886
		$load_fields      = true;
5887
		$bypass_cache     = false;
5888
5889
		// Get current language data
5890
		$lang_data = PodsInit::$i18n->get_current_language_data();
5891
5892
		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...
5893
			if ( ! empty( $lang_data['language'] ) ) {
5894
				$current_language = $lang_data['language'];
5895
			}
5896
		}
5897
5898
		if ( ! is_array( $params ) && ! is_object( $params ) ) {
5899
			$params = array( 'name' => $params, 'table_info' => false, 'fields' => true );
5900
		}
5901
5902
		if ( is_object( $params ) && ! is_a( $params, 'WP_Post' ) && isset( $params->fields ) && ! $params->fields ) {
5903
			$load_fields = false;
5904
		} elseif ( is_array( $params ) && isset( $params['fields'] ) && ! $params['fields'] ) {
5905
			$load_fields = false;
5906
		}
5907
5908
		$table_info = false;
5909
5910
		if ( is_object( $params ) && ! is_a( $params, 'WP_Post' ) && ! empty( $params->table_info ) ) {
5911
			$table_info = true;
5912
		} elseif ( is_array( $params ) && ! empty( $params['table_info'] ) ) {
5913
			$table_info = true;
5914
		}
5915
5916
		$transient = 'pods_' . $wpdb->prefix . '_pod';
5917
5918
		if ( ! empty( $current_language ) ) {
5919
			$transient .= '_' . $current_language;
5920
		}
5921
5922
		if ( ! $load_fields ) {
5923
			$transient .= '_nofields';
5924
		}
5925
5926
		if ( $table_info ) {
5927
			$transient .= '_tableinfo';
5928
		}
5929
5930
		$check_pod = $params;
5931
5932
		if ( is_object( $params ) && ! is_a( $params, 'WP_Post' ) && ! empty( $params->pod ) ) {
5933
			$check_pod = $params->pod;
5934
		} elseif ( is_array( $params ) && ! empty( $params['pod'] ) ) {
5935
			$check_pod = $params['pod'];
5936
		}
5937
5938
		if ( is_object( $check_pod ) && ( is_a( $check_pod, 'WP_Post' ) || isset( $check_pod->post_name ) ) ) {
5939
			$pod = false;
5940
5941
			if ( pods_api_cache() ) {
5942
				$pod = pods_transient_get( $transient . '_' . $check_pod->post_name );
5943
			}
5944
5945
			if ( false !== $pod && ( ! $table_info || isset( $pod['table'] ) ) ) {
5946
				// @todo Is this needed anymore for WPML?
5947
				if ( in_array( $pod['type'], array(
5948
						'post_type',
5949
						'taxonomy'
5950
					) ) && did_action( 'wpml_loaded' ) && apply_filters( 'wpml_setting', true, 'auto_adjust_ids' ) ) {
5951
					$pod = array_merge( $pod, $this->get_table_info( $pod['type'], $pod['object'], $pod['name'], $pod ) );
5952
				}
5953
5954
				return $pod;
5955
			}
5956
5957
			$_pod = get_object_vars( $check_pod );
5958
		} else {
5959
			$params = (object) pods_sanitize( $params );
5960
5961
			if ( ( ! isset( $params->id ) || empty( $params->id ) ) && ( ! isset( $params->name ) || empty( $params->name ) ) ) {
5962
				if ( $strict ) {
5963
					return pods_error( 'Either Pod ID or Name are required', $this );
5964
				}
5965
5966
				return false;
5967
			}
5968
5969
			if ( ! empty( $params->bypass_cache ) ) {
5970
				$bypass_cache = true;
5971
			}
5972
5973
			if ( isset( $params->name ) ) {
5974
				$pod = false;
5975
5976
				if ( '_pods_pod' === $params->name ) {
5977
					$pod = array(
5978
						'id'      => 0,
5979
						'name'    => $params->name,
5980
						'label'   => __( 'Pods', 'pods' ),
5981
						'type'    => 'post_type',
5982
						'storage' => 'meta',
5983
						'options' => array(
5984
							'label_singular' => __( 'Pod', 'pods' )
5985
						),
5986
						'fields'  => array()
5987
					);
5988
				} elseif ( '_pods_field' === $params->name ) {
5989
					$pod = array(
5990
						'id'      => 0,
5991
						'name'    => $params->name,
5992
						'label'   => __( 'Pod Fields', 'pods' ),
5993
						'type'    => 'post_type',
5994
						'storage' => 'meta',
5995
						'options' => array(
5996
							'label_singular' => __( 'Pod Field', 'pods' )
5997
						),
5998
						'fields'  => array()
5999
					);
6000
				} elseif ( ! $bypass_cache & pods_api_cache() ) {
6001
					$pod = pods_transient_get( $transient . '_' . $params->name );
6002
				}
6003
6004
				if ( false !== $pod && ( ! $table_info || isset( $pod['table'] ) ) ) {
6005
					if ( in_array( $pod['type'], array(
6006
							'post_type',
6007
							'taxonomy'
6008
						) ) && did_action( 'wpml_loaded' ) && apply_filters( 'wpml_setting', true, 'auto_adjust_ids' ) ) {
6009
						$pod = array_merge( $pod, $this->get_table_info( $pod['type'], $pod['object'], $pod['name'], $pod ) );
6010
					}
6011
6012
					return $pod;
6013
				}
6014
			}
6015
6016
			if ( ! isset( $params->name ) ) {
6017
				$pod = get_post( $dummy = (int) $params->id );
6018
			} else {
6019
				$pod = get_posts( array(
6020
					'name'           => $params->name,
6021
					'post_type'      => '_pods_pod',
6022
					'posts_per_page' => 1
6023
				) );
6024
			}
6025
6026
			if ( empty( $pod ) ) {
6027
				if ( $strict ) {
6028
					return pods_error( __( 'Pod not found', 'pods' ), $this );
6029
				}
6030
6031
				return false;
6032
			}
6033
6034
			if ( is_array( $pod ) && ! empty( $pod[0] ) ) {
6035
				$pod = $pod[0];
6036
			}
6037
6038
			$_pod = get_object_vars( $pod );
6039
		}
6040
6041
		$pod = false;
6042
6043
		if ( ! $bypass_cache || pods_api_cache() ) {
6044
			$pod = pods_transient_get( $transient . '_' . $_pod['post_name'] );
6045
		}
6046
6047
		if ( false !== $pod && ( ! $table_info || isset( $pod['table'] ) ) ) {
6048
			if ( in_array( $pod['type'], array(
6049
					'post_type',
6050
					'taxonomy'
6051
				) ) && did_action( 'wpml_loaded' ) && apply_filters( 'wpml_setting', true, 'auto_adjust_ids' ) ) {
6052
				$pod = array_merge( $pod, $this->get_table_info( $pod['type'], $pod['object'], $pod['name'], $pod ) );
6053
			}
6054
6055
			return $pod;
6056
		}
6057
6058
		$pod = array(
6059
			'id'          => $_pod['ID'],
6060
			'name'        => $_pod['post_name'],
6061
			'label'       => $_pod['post_title'],
6062
			'description' => $_pod['post_content']
6063
		);
6064
6065
		if ( strlen( $pod['label'] ) < 1 ) {
6066
			$pod['label'] = $pod['name'];
6067
		}
6068
6069
		// @todo update with a method to put all options in
6070
		$defaults = array(
6071
			'show_in_menu' => 1,
6072
			'type'         => 'post_type',
6073
			'storage'      => 'meta',
6074
			'object'       => '',
6075
			'alias'        => ''
6076
		);
6077
6078
		if ( $bypass_cache ) {
6079
			wp_cache_delete( $pod['id'], 'post_meta' );
6080
6081
			update_postmeta_cache( array( $pod['id'] ) );
6082
		}
6083
6084
		$pod['options'] = get_post_meta( $pod['id'] );
6085
6086
		foreach ( $pod['options'] as $option => $value ) {
6087
			if ( is_array( $value ) ) {
6088
				foreach ( $value as $k => $v ) {
6089
					if ( ! is_array( $v ) ) {
6090
						$value[ $k ] = maybe_unserialize( $v );
6091
					}
6092
				}
6093
6094
				if ( 1 == count( $value ) ) {
6095
					$value = current( $value );
6096
				}
6097
			} else {
6098
				$value = maybe_unserialize( $value );
6099
			}
6100
6101
			$pod['options'][ $option ] = $value;
6102
		}
6103
6104
		$pod['options'] = array_merge( $defaults, $pod['options'] );
6105
6106
		$pod['type']    = $pod['options']['type'];
6107
		$pod['storage'] = $pod['options']['storage'];
6108
		$pod['object']  = $pod['options']['object'];
6109
		$pod['alias']   = $pod['options']['alias'];
6110
6111
		unset( $pod['options']['type'] );
6112
		unset( $pod['options']['storage'] );
6113
		unset( $pod['options']['object'] );
6114
		unset( $pod['options']['alias'] );
6115
6116
		if ( $table_info ) {
6117
			$pod = array_merge( $this->get_table_info( $pod['type'], $pod['object'], $pod['name'], $pod ), $pod );
6118
		}
6119
6120
		// Override old 'none' storage type
6121
		if ( 'taxonomy' === $pod['type'] && 'none' === $pod['storage'] && function_exists( 'get_term_meta' ) ) {
6122
			$pod['storage'] = 'meta';
6123
		}
6124
6125
		if ( isset( $pod['pod'] ) ) {
6126
			unset( $pod['pod'] );
6127
		}
6128
6129
		$pod['fields'] = array();
6130
6131
		$pod['object_fields'] = array();
6132
6133
		if ( 'pod' !== $pod['type'] ) {
6134
			$pod['object_fields'] = $this->get_wp_object_fields( $pod['type'], $pod );
6135
		}
6136
6137
		$fields = get_posts( array(
6138
			'post_type'      => '_pods_field',
6139
			'posts_per_page' => - 1,
6140
			'nopaging'       => true,
6141
			'post_parent'    => $pod['id'],
6142
			'orderby'        => 'menu_order',
6143
			'order'          => 'ASC'
6144
		) );
6145
6146
		if ( ! empty( $fields ) ) {
6147
			foreach ( $fields as $field ) {
6148
				$field->pod          = $pod['name'];
6149
				$field->table_info   = $table_info;
6150
				$field->bypass_cache = $bypass_cache;
6151
6152
				if ( $load_fields ) {
6153
					$field = $this->load_field( $field );
6154
6155
					$field = PodsForm::field_setup( $field, null, $field['type'] );
6156
				} else {
6157
					if ( $bypass_cache ) {
6158
						wp_cache_delete( $field->ID, 'post_meta' );
6159
6160
						update_postmeta_cache( array( $field->ID ) );
6161
					}
6162
6163
					$field = array(
6164
						'id'    => $field->ID,
6165
						'name'  => $field->post_name,
6166
						'label' => $field->post_title,
6167
						'type'  => get_post_meta( $field->ID, 'type', true )
6168
					);
6169
				}
6170
6171
				$pod['fields'][ $field['name'] ] = $field;
6172
			}
6173
		}
6174
6175
		if ( did_action( 'init' ) && pods_api_cache() ) {
6176
			pods_transient_set( $transient . '_' . $pod['name'], $pod );
6177
		}
6178
6179
		return $pod;
6180
	}
6181
6182
	/**
6183
	 * Load a list of Pods based on filters specified.
6184
	 *
6185
	 * $params['type'] string/array Pod Type(s) to filter by
6186
	 * $params['object'] string/array Pod Object(s) to filter by
6187
	 * $params['options'] array Pod Option(s) key=>value array to filter by
6188
	 * $params['orderby'] string ORDER BY clause of query
6189
	 * $params['limit'] string Number of Pods to return
6190
	 * $params['where'] string WHERE clause of query
6191
	 * $params['ids'] string|array IDs of Objects
6192
	 * $params['count'] boolean Return only a count of Pods
6193
	 * $params['names'] boolean Return only an array of name => label
6194
	 * $params['ids'] boolean Return only an array of ID => label
6195
	 * $params['fields'] boolean Return pod fields with Pods (default is true)
6196
	 * $params['key_names'] boolean Return pods keyed by name
6197
	 * $params['bypass_cache'] boolean Bypass the cache when getting data
6198
	 *
6199
	 * @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...
6200
	 *
6201
	 * @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...
6202
	 *
6203
	 * @uses  PodsAPI::load_pod
6204
	 *
6205
	 * @since 2.0
6206
	 */
6207
	public function load_pods( $params = null ) {
6208
6209
		$current_language = false;
6210
6211
		// Get current language data
6212
		$lang_data = PodsInit::$i18n->get_current_language_data();
6213
6214
		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...
6215
			if ( ! empty( $lang_data['language'] ) ) {
6216
				$current_language = $lang_data['language'];
6217
			}
6218
		}
6219
6220
		$params = (object) pods_sanitize( $params );
6221
6222
		$order   = 'ASC';
6223
		$orderby = 'menu_order title';
6224
		$limit   = - 1;
6225
		$ids     = false;
6226
6227
		$meta_query = array();
6228
		$cache_key  = '';
6229
6230
		$bypass_cache = false;
6231
6232
		if ( ! empty( $params->bypass_cache ) ) {
6233
			$bypass_cache = true;
6234
		}
6235
6236
		if ( isset( $params->type ) && ! empty( $params->type ) ) {
6237
			if ( ! is_array( $params->type ) ) {
6238
				$params->type = array( trim( $params->type ) );
6239
			}
6240
6241
			sort( $params->type );
6242
6243
			$meta_query[] = array(
6244
				'key'     => 'type',
6245
				'value'   => $params->type,
6246
				'compare' => 'IN'
6247
			);
6248
6249
			if ( 0 < count( $params->type ) ) {
6250
				$cache_key .= '_type_' . trim( implode( '_', $params->type ) );
6251
			}
6252
		}
6253
6254
		if ( isset( $params->object ) && ! empty( $params->object ) ) {
6255
			if ( ! is_array( $params->object ) ) {
6256
				$params->object = array( $params->object );
6257
			}
6258
6259
			$params->object = pods_trim( $params->object );
6260
6261
			sort( $params->object );
6262
6263
			$meta_query[] = array(
6264
				'key'     => 'object',
6265
				'value'   => $params->object,
6266
				'compare' => 'IN'
6267
			);
6268
6269
			if ( 1 == count( $params->object ) ) {
6270
				$cache_key .= '_object_' . trim( implode( '', $params->object ) );
6271
			}
6272
		}
6273
6274
		if ( isset( $params->options ) && ! empty( $params->options ) && is_array( $params->options ) ) {
6275
			foreach ( $params->options as $option => $value ) {
6276
				if ( ! is_array( $value ) ) {
6277
					$value = array( $value );
6278
				}
6279
6280
				$value = pods_trim( $value );
6281
6282
				sort( $value );
6283
6284
				$meta_query[] = array(
6285
					'key'     => $option,
6286
					'value'   => pods_sanitize( $value ),
6287
					'compare' => 'IN'
6288
				);
6289
			}
6290
6291
			$cache_key = '';
6292
		}
6293
6294
		if ( isset( $params->where ) && is_array( $params->where ) ) {
6295
			$meta_query = array_merge( $meta_query, (array) $params->where );
6296
		}
6297
6298
		if ( isset( $params->order ) && ! empty( $params->order ) && in_array( strtoupper( $params->order ), array(
6299
				'ASC',
6300
				'DESC'
6301
			) ) ) {
6302
			$order = strtoupper( $params->order );
6303
		}
6304
6305
		if ( isset( $params->orderby ) && ! empty( $params->orderby ) ) {
6306
			$orderby = strtoupper( $params->orderby );
6307
		}
6308
6309
		if ( isset( $params->limit ) && ! empty( $params->limit ) ) {
6310
			$limit = pods_absint( $params->limit );
6311
		}
6312
6313
		if ( isset( $params->ids ) && ! empty( $params->ids ) ) {
6314
			$ids = $params->ids;
6315
6316
			if ( ! is_array( $ids ) ) {
6317
				$ids = explode( ',', $ids );
6318
			}
6319
		}
6320
6321
		if ( empty( $ids ) ) {
6322
			$ids = false;
6323
		}
6324
6325
		$pre_key = '';
6326
6327
		if ( ! empty( $current_language ) ) {
6328
			$pre_key .= '_' . $current_language;
6329
		}
6330
6331
		if ( isset( $params->count ) && $params->count ) {
6332
			$pre_key .= '_count';
6333
		}
6334
6335
		if ( isset( $params->ids ) && $params->ids && ! empty( $ids ) ) {
6336
			$pre_key .= '_ids_' . implode( '_', $ids );
6337
		}
6338
6339
		if ( isset( $params->names ) && $params->names ) {
6340
			$pre_key .= '_names';
6341
		} elseif ( isset( $params->names_ids ) && $params->names_ids ) {
6342
			$pre_key .= '_names_ids';
6343
		}
6344
6345
		if ( isset( $params->key_names ) && $params->key_names ) {
6346
			$pre_key .= '_namekeys';
6347
		}
6348
6349
		if ( isset( $params->fields ) && ! $params->fields ) {
6350
			$pre_key .= '_nofields';
6351
		}
6352
6353
		if ( isset( $params->table_info ) && $params->table_info ) {
6354
			$pre_key .= '_tableinfo';
6355
		}
6356
6357
		$pre_key .= '_get';
6358
6359
		if ( empty( $cache_key ) ) {
6360
			$cache_key = 'pods' . $pre_key . '_all';
6361
		} else {
6362
			$cache_key = 'pods' . $pre_key . $cache_key;
6363
		}
6364
6365
		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 ) ) {
6366
			$the_pods = pods_transient_get( $cache_key );
6367
6368
			if ( false === $the_pods ) {
6369
				$the_pods = pods_cache_get( $cache_key, 'pods' );
6370
			}
6371
6372
			if ( ! is_array( $the_pods ) && 'none' === $the_pods ) {
6373
				return array();
6374
			} elseif ( false !== $the_pods ) {
6375
				return $the_pods;
6376
			}
6377
		}
6378
6379
		$the_pods = array();
6380
6381
		$args = array(
6382
			'post_type'      => '_pods_pod',
6383
			'nopaging'       => true,
6384
			'posts_per_page' => $limit,
6385
			'order'          => $order,
6386
			'orderby'        => $orderby,
6387
			'meta_query'     => $meta_query,
6388
		);
6389
6390
		// Only set post__in if there are ids to filter (see https://core.trac.wordpress.org/ticket/28099)
6391
		if ( false !== $ids ) {
6392
			$args['post__in'] = $ids;
6393
		}
6394
6395
		$_pods = get_posts( $args );
6396
6397
		$export_ignore = array(
6398
			'object_type',
6399
			'object_name',
6400
			'table',
6401
			'meta_table',
6402
			'pod_table',
6403
			'field_id',
6404
			'field_index',
6405
			'field_slug',
6406
			'field_type',
6407
			'field_parent',
6408
			'field_parent_select',
6409
			'meta_field_id',
6410
			'meta_field_index',
6411
			'meta_field_value',
6412
			'pod_field_id',
6413
			'pod_field_index',
6414
			'object_fields',
6415
			'join',
6416
			'where',
6417
			'where_default',
6418
			'orderby',
6419
			'pod',
6420
			'recurse',
6421
			'table_info',
6422
			'attributes',
6423
			'group',
6424
			'grouped',
6425
			'developer_mode',
6426
			'dependency',
6427
			'depends-on',
6428
			'excludes-on'
6429
		);
6430
6431
		$total_fields = 0;
6432
6433
		if ( isset( $params->count ) && $params->count ) {
6434
			$the_pods = count( $_pods );
6435
		} else {
6436
			foreach ( $_pods as $pod ) {
6437
				if ( isset( $params->names ) && $params->names ) {
6438
					$the_pods[ $pod->post_name ] = $pod->post_title;
6439
				} elseif ( isset( $params->names_ids ) && $params->names_ids ) {
6440
					$the_pods[ $pod->ID ] = $pod->post_name;
6441
				} else {
6442
					if ( isset( $params->fields ) && ! $params->fields ) {
6443
						$pod->fields = false;
6444
					}
6445
6446
					$pod = $this->load_pod( array(
6447
						'pod'          => $pod,
6448
						'table_info'   => ! empty( $params->table_info ),
6449
						'bypass_cache' => $bypass_cache
6450
					) );
6451
6452
					// Remove extra data not needed
6453
					if ( pods_var( 'export', $params, false ) && ( ! isset( $params->fields ) || $params->fields ) ) {
6454
						foreach ( $export_ignore as $ignore ) {
6455
							if ( isset( $pod[ $ignore ] ) ) {
6456
								unset( $pod[ $ignore ] );
6457
							}
6458
						}
6459
6460
						foreach ( $pod['fields'] as $field => $field_data ) {
6461
							if ( isset( $pod['fields'][ $field ]['table_info'] ) ) {
6462
								unset( $pod['fields'][ $field ]['table_info'] );
6463
							}
6464
						}
6465
					}
6466
6467
					$total_fields += count( $pod['fields'] );
6468
6469
					if ( isset( $params->key_names ) && $params->key_names ) {
6470
						$the_pods[ $pod['name'] ] = $pod;
6471
					} else {
6472
						$the_pods[ $pod['id'] ] = $pod;
6473
					}
6474
				}
6475
			}
6476
		}
6477
6478
		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 ) ) {
6479
			$total_pods = (int) ( is_array( $the_pods ) ) ? count( $the_pods ) : $the_pods;
6480
			// Too many Pods can cause issues with the DB when caching is not enabled
6481
			if ( 15 < $total_pods || 75 < (int) $total_fields ) {
6482
				pods_transient_clear( $cache_key );
6483
6484
				if ( pods_api_cache() ) {
6485
					if ( empty( $the_pods ) && ( ! isset( $params->count ) || ! $params->count ) ) {
6486
						pods_cache_set( $cache_key, 'none', 'pods' );
6487
					} else {
6488
						pods_cache_set( $cache_key, $the_pods, 'pods' );
6489
					}
6490
				}
6491
			} else {
6492
				pods_cache_clear( $cache_key, 'pods' );
6493
6494
				if ( pods_api_cache() ) {
6495
					if ( empty( $the_pods ) && ( ! isset( $params->count ) || ! $params->count ) ) {
6496
						pods_transient_set( $cache_key, 'none' );
6497
					} else {
6498
						pods_transient_set( $cache_key, $the_pods );
6499
					}
6500
				}
6501
			}
6502
		}
6503
6504
		return $the_pods;
6505
	}
6506
6507
	/**
6508
	 * Check if a Pod's field exists
6509
	 *
6510
	 * $params['pod_id'] int The Pod ID
6511
	 * $params['id'] int The field ID
6512
	 * $params['name'] string The field name
6513
	 *
6514
	 * @param array $params An associative array of parameters
6515
	 *
6516
	 * @return bool
6517
	 *
6518
	 * @since 1.12
6519
	 */
6520
	public function field_exists( $params ) {
6521
6522
		$params = (object) pods_sanitize( $params );
6523
6524
		if ( ( ! empty( $params->id ) || ! empty( $params->name ) ) && isset( $params->pod_id ) && ! empty( $params->pod_id ) ) {
6525
			if ( ! isset( $params->name ) ) {
6526
				$field = get_post( $dummy = (int) $params->id );
6527
			} else {
6528
				$field = get_posts( array(
6529
					'name'           => $params->name,
6530
					'post_type'      => '_pods_field',
6531
					'posts_per_page' => 1,
6532
					'post_parent'    => $params->pod_id
6533
				) );
6534
			}
6535
6536
			if ( ! empty( $field ) ) {
6537
				return true;
6538
			}
6539
		}
6540
6541
		return false;
6542
	}
6543
6544
	/**
6545
	 * Load a field
6546
	 *
6547
	 * $params['pod_id'] int The Pod ID
6548
	 * $params['pod'] string The Pod name
6549
	 * $params['id'] int The field ID
6550
	 * $params['name'] string The field name
6551
	 * $params['table_info'] boolean Whether to lookup a pick field's table info
6552
	 * $params['bypass_cache'] boolean Bypass the cache when getting data
6553
	 *
6554
	 * @param array   $params An associative array of parameters
6555
	 * @param boolean $strict Whether to require a field exist or not when loading the info
6556
	 *
6557
	 * @return array|bool Array with field data, false if field not found
6558
	 * @since 1.7.9
6559
	 */
6560
	public function load_field( $params, $strict = false ) {
6561
6562
		$params = (object) $params;
6563
6564
		if ( ! isset( $params->table_info ) ) {
6565
			$params->table_info = false;
6566
		}
6567
6568
		$bypass_cache = false;
6569
6570
		if ( ! empty( $params->bypass_cache ) ) {
6571
			$bypass_cache = true;
6572
		}
6573
6574
		$pod   = array();
6575
		$field = array();
6576
6577
		if ( isset( $params->post_title ) ) {
6578
			$_field = $params;
6579
		} elseif ( isset( $params->id ) && ! empty( $params->id ) ) {
6580
			$_field = get_post( $dumb = (int) $params->id );
6581
		} else {
6582
			if ( ! isset( $params->pod ) ) {
6583
				$params->pod = '';
6584
			}
6585
6586
			if ( ! isset( $params->pod_id ) ) {
6587
				$params->pod_id = 0;
6588
			}
6589
6590
			if ( isset( $params->pod_data ) ) {
6591
				$pod = $params->pod_data;
6592
			} else {
6593
				$pod = $this->load_pod( array(
6594
					'name'         => $params->pod,
6595
					'id'           => $params->pod_id,
6596
					'table_info'   => false,
6597
					'bypass_cache' => $bypass_cache
6598
				), false );
6599
6600
				if ( false === $pod ) {
6601
					if ( $strict ) {
6602
						return pods_error( __( 'Pod not found', 'pods' ), $this );
6603
					}
6604
6605
					return false;
6606
				}
6607
			}
6608
6609
			$params->pod_id = $pod['id'];
6610
			$params->pod    = $pod['name'];
6611
6612
			if ( empty( $params->name ) && empty( $params->pod ) && empty( $params->pod_id ) ) {
6613
				return pods_error( __( 'Either Field Name or Field ID / Pod ID are required', 'pods' ), $this );
6614
			}
6615
6616
			$params->name = pods_clean_name( $params->name, true, ( 'meta' === $pod['storage'] ? false : true ) );
6617
6618
			if ( isset( $pod['fields'][ $params->name ] ) && isset( $pod['fields'][ $params->name ]['id'] ) ) {
6619
				return $pod['fields'][ $params->name ];
6620
			}
6621
6622
			$field = false;
6623
6624
			if ( ! $bypass_cache && pods_api_cache() ) {
6625
				$field = pods_transient_get( 'pods_field_' . $params->pod . '_' . $params->name );
6626
			}
6627
6628
			if ( empty( $field ) ) {
6629
				$field = get_posts( array(
6630
					'name'           => $params->name,
6631
					'post_type'      => '_pods_field',
6632
					'posts_per_page' => 1,
6633
					'post_parent'    => $params->pod_id
6634
				) );
6635
6636
				if ( empty( $field ) || empty( $field[0] ) ) {
6637
					if ( $strict ) {
6638
						return pods_error( __( 'Field not found', 'pods' ), $this );
6639
					}
6640
6641
					return false;
6642
				}
6643
6644
				$_field = $field[0];
6645
6646
				$field = array();
6647
			}
6648
		}
6649
6650
		if ( empty( $_field ) ) {
6651
			if ( $strict ) {
6652
				return pods_error( __( 'Field not found', 'pods' ), $this );
6653
			}
6654
6655
			return false;
6656
		}
6657
6658
		$_field = get_object_vars( $_field );
6659
6660
		if ( ! isset( $pod['name'] ) && ! isset( $_field['pod'] ) ) {
6661
			if ( 0 < $_field['post_parent'] ) {
6662
				$pod = $this->load_pod( array( 'id' => $_field['post_parent'], 'table_info' => false ), false );
6663
			}
6664
6665
			if ( empty( $pod ) ) {
6666
				if ( $strict ) {
6667
					return pods_error( __( 'Pod for field not found', 'pods' ), $this );
6668
				}
6669
6670
				return false;
6671
			}
6672
		}
6673
6674
		if ( empty( $field ) ) {
6675
			if ( ! $bypass_cache && pods_api_cache() && ( isset( $pod['name'] ) || isset( $_field['pod'] ) ) ) {
6676
				$field = pods_transient_get( 'pods_field_' . pods_var( 'name', $pod, pods_var( 'pod', $_field ), null, true ) . '_' . $_field['post_name'] );
6677
			}
6678
6679
			if ( empty( $field ) ) {
6680
				$defaults = array(
6681
					'type' => 'text'
6682
				);
6683
6684
				$field = array(
6685
					'id'          => $_field['ID'],
6686
					'name'        => $_field['post_name'],
6687
					'label'       => $_field['post_title'],
6688
					'description' => $_field['post_content'],
6689
					'weight'      => $_field['menu_order'],
6690
					'pod_id'      => $_field['post_parent'],
6691
					'pick_object' => '',
6692
					'pick_val'    => '',
6693
					'sister_id'   => '',
6694
					'table_info'  => array()
6695
				);
6696
6697
				if ( isset( $pod['name'] ) ) {
6698
					$field['pod'] = $pod['name'];
6699
				} elseif ( isset( $_field['pod'] ) ) {
6700
					$field['pod'] = $_field['pod'];
6701
				}
6702
6703
				if ( $bypass_cache ) {
6704
					wp_cache_delete( $field['id'], 'post_meta' );
6705
6706
					update_postmeta_cache( array( $field['id'] ) );
6707
				}
6708
6709
				$field['options'] = get_post_meta( $field['id'] );
6710
6711
				$options_ignore = array(
6712
					'method',
6713
					'table_info',
6714
					'attributes',
6715
					'group',
6716
					'grouped',
6717
					'developer_mode',
6718
					'dependency',
6719
					'depends-on',
6720
					'excludes-on'
6721
				);
6722
6723
				foreach ( $options_ignore as $ignore ) {
6724
					if ( isset( $field['options'][ $ignore ] ) ) {
6725
						unset( $field['options'][ $ignore ] );
6726
					}
6727
				}
6728
6729
				foreach ( $field['options'] as $option => $value ) {
6730
					if ( is_array( $value ) ) {
6731
						foreach ( $value as $k => $v ) {
6732
							if ( ! is_array( $v ) ) {
6733
								$value[ $k ] = maybe_unserialize( $v );
6734
							}
6735
						}
6736
6737
						if ( 1 == count( $value ) ) {
6738
							$value = current( $value );
6739
						}
6740
					} else {
6741
						$value = maybe_unserialize( $value );
6742
					}
6743
6744
					$field['options'][ $option ] = $value;
6745
				}
6746
6747
				$field['options'] = array_merge( $defaults, $field['options'] );
6748
6749
				$field['type'] = $field['options']['type'];
6750
6751
				unset( $field['options']['type'] );
6752
6753
				if ( isset( $field['options']['pick_object'] ) ) {
6754
					$field['pick_object'] = $field['options']['pick_object'];
6755
6756
					unset( $field['options']['pick_object'] );
6757
				}
6758
6759
				if ( isset( $field['options']['pick_val'] ) ) {
6760
					$field['pick_val'] = $field['options']['pick_val'];
6761
6762
					unset( $field['options']['pick_val'] );
6763
				}
6764
6765
				if ( isset( $field['options']['sister_id'] ) ) {
6766
					$field['sister_id'] = $field['options']['sister_id'];
6767
6768
					unset( $field['options']['sister_id'] );
6769
				}
6770
6771
				if ( isset( $field['options']['sister_field_id'] ) ) {
6772
					unset( $field['options']['sister_field_id'] );
6773
				}
6774
6775
				if ( pods_api_cache() && ( isset( $pod['name'] ) || isset( $_field['pod'] ) ) ) {
6776
					pods_transient_set( 'pods_field_' . pods_var( 'name', $pod, pods_var( 'pod', $_field ), null, true ) . '_' . $field['name'], $field );
6777
				}
6778
			}
6779
		}
6780
6781
		$field['table_info'] = array();
6782
6783
		if ( 'pick' === $field['type'] && $params->table_info ) {
6784
			$field['table_info'] = $this->get_table_info( $field['pick_object'], $field['pick_val'], null, null, $field );
6785
		}
6786
6787
		return $field;
6788
	}
6789
6790
	/**
6791
	 * Load fields by Pod, ID, Name, and/or Type
6792
	 *
6793
	 * $params['pod_id'] int The Pod ID
6794
	 * $params['pod'] string The Pod name
6795
	 * $params['id'] array The field IDs
6796
	 * $params['name'] array The field names
6797
	 * $params['type'] array The field types
6798
	 * $params['options'] array Field Option(s) key=>value array to filter by
6799
	 * $params['where'] string WHERE clause of query
6800
	 * $params['object_fields'] bool Whether to include the object fields for WP objects, default true
6801
	 *
6802
	 * @param array $params An associative array of parameters
6803
	 * @param bool  $strict Whether to require a field exist or not when loading the info
6804
	 *
6805
	 * @return array Array of field data.
6806
	 *
6807
	 * @since 1.7.9
6808
	 */
6809
	public function load_fields( $params, $strict = false ) {
6810
6811
		// @todo Get away from using md5/serialize, I'm sure we can cache specific parts
6812
		$cache_key = md5( serialize( $params ) );
6813
		if ( isset( $this->fields_cache[ $cache_key ] ) ) {
6814
			return $this->fields_cache[ $cache_key ];
6815
		}
6816
6817
		$params = (object) pods_sanitize( $params );
6818
6819
		if ( ! isset( $params->pod ) || empty( $params->pod ) ) {
6820
			$params->pod = '';
6821
		}
6822
6823
		if ( ! isset( $params->pod_id ) || empty( $params->pod_id ) ) {
6824
			$params->pod_id = 0;
6825
		}
6826
6827
		if ( ! isset( $params->name ) || empty( $params->name ) ) {
6828
			$params->name = array();
6829
		} else {
6830
			$params->name = (array) $params->name;
6831
		}
6832
6833
		if ( ! isset( $params->id ) || empty( $params->id ) ) {
6834
			$params->id = array();
6835
		} else {
6836
			$params->id = (array) $params->id;
6837
6838
			foreach ( $params->id as &$id ) {
6839
				$id = pods_absint( $id );
6840
			}
6841
		}
6842
6843
		if ( ! isset( $params->type ) || empty( $params->type ) ) {
6844
			$params->type = array();
6845
		} else {
6846
			$params->type = (array) $params->type;
6847
		}
6848
6849
		if ( ! isset( $params->object_fields ) ) {
6850
			$params->object_fields = true;
6851
		} else {
6852
			$params->object_fields = (boolean) $params->object_fields;
6853
		}
6854
6855
		$fields = array();
6856
6857
		if ( ! empty( $params->pod ) || ! empty( $params->pod_id ) ) {
6858
			$pod = $this->load_pod( array(
6859
				'name'       => $params->pod,
6860
				'id'         => $params->pod_id,
6861
				'table_info' => true
6862
			), false );
6863
6864
			if ( false === $pod ) {
6865
				if ( $strict ) {
6866
					return pods_error( __( 'Pod not found', 'pods' ), $this );
6867
				}
6868
6869
				return $fields;
6870
			}
6871
6872
			if ( $params->object_fields && ! empty( $pod['object_fields'] ) ) {
6873
				$pod['fields'] = array_merge( $pod['object_fields'], $pod['fields'] );
6874
			}
6875
6876
			foreach ( $pod['fields'] as $field ) {
6877
				if ( empty( $params->name ) && empty( $params->id ) && empty( $params->type ) ) {
6878
					$fields[ $field['name'] ] = $field;
6879
				} elseif ( in_array( $fields['name'], $params->name ) || in_array( $fields['id'], $params->id ) || in_array( $fields['type'], $params->type ) ) {
6880
					$fields[ $field['name'] ] = $field;
6881
				}
6882
			}
6883
		} elseif ( ( isset( $params->options ) && ! empty( $params->options ) && is_array( $params->options ) ) || ( isset( $params->where ) && ! empty( $params->where ) && is_array( $params->where ) ) ) {
6884
			$order   = 'ASC';
6885
			$orderby = 'menu_order title';
6886
			$limit   = - 1;
6887
			$ids     = false;
6888
6889
			$meta_query = array();
6890
6891
			if ( isset( $params->options ) && ! empty( $params->options ) && is_array( $params->options ) ) {
6892
				foreach ( $params->options as $option => $value ) {
6893
					if ( ! is_array( $value ) ) {
6894
						$value = array( $value );
6895
					}
6896
6897
					$value = pods_trim( $value );
6898
6899
					sort( $value );
6900
6901
					$meta_query[] = array(
6902
						'key'     => $option,
6903
						'value'   => pods_sanitize( $value ),
6904
						'compare' => 'IN'
6905
					);
6906
				}
6907
			}
6908
6909
			if ( isset( $params->where ) && ! empty( $params->where ) && is_array( $params->where ) ) {
6910
				$meta_query = array_merge( $meta_query, (array) $params->where );
6911
			}
6912
6913
			$args = array(
6914
				'post_type'      => '_pods_field',
6915
				'nopaging'       => true,
6916
				'posts_per_page' => $limit,
6917
				'order'          => $order,
6918
				'orderby'        => $orderby,
6919
				'meta_query'     => $meta_query,
6920
			);
6921
6922
			// Only set post__in if there are ids to filter (see https://core.trac.wordpress.org/ticket/28099)
6923
			if ( false !== $ids ) {
6924
				$args['post__in'] = $ids;
6925
			}
6926
6927
			$fields = array();
6928
6929
			$_fields = get_posts( $args );
6930
6931
			foreach ( $_fields as $field ) {
6932
				$field = $this->load_field( $field, false );
6933
6934
				if ( ! empty( $field ) ) {
6935
					$fields[ $field['id'] ] = $field;
6936
				}
6937
			}
6938
		} else {
6939
			if ( empty( $params->name ) && empty( $params->id ) && empty( $params->type ) ) {
6940
				return pods_error( __( 'Either Field Name / Field ID / Field Type, or Pod Name / Pod ID are required', 'pods' ), $this );
6941
			}
6942
6943
			$lookup = array();
6944
6945
			if ( ! empty( $params->name ) ) {
6946
				$fields = implode( "', '", $params->name );
6947
6948
				$lookup[] = "`post_name` IN ( '{$fields}' )";
6949
			}
6950
6951
			if ( ! empty( $params->id ) ) {
6952
				$fields = implode( ", ", $params->id );
6953
6954
				$lookup[] = "`ID` IN ( {$fields} )";
6955
			}
6956
6957
			$lookup = implode( ' AND ', $lookup );
6958
6959
			$result = pods_query( "SELECT `ID`, `post_name`, `post_parent` FROM `@wp_posts` WHERE `post_type` = '_pods_field' AND ( {$lookup} )" );
6960
6961
			$fields = array();
6962
6963
			if ( ! empty( $result ) ) {
6964
				foreach ( $result as $field ) {
6965
					$field = $this->load_field( array(
6966
						'id'     => $field->ID,
6967
						'name'   => $field->post_name,
6968
						'pod_id' => $field->post_parent
6969
					), false );
6970
6971
					if ( ! empty( $field ) && ( empty( $params->type ) || in_array( $field['type'], $params->type ) ) ) {
6972
						$fields[ $field['id'] ] = $field;
6973
					}
6974
				}
6975
			}
6976
		}
6977
		if ( isset( $cache_key ) ) {
6978
			$this->fields_cache[ $cache_key ] = $fields;
6979
		}
6980
6981
		return $fields;
6982
	}
6983
6984
	/**
6985
	 * Load a Pods Object
6986
	 *
6987
	 * $params['id'] int The Object ID
6988
	 * $params['name'] string The Object name
6989
	 * $params['type'] string The Object type
6990
	 *
6991
	 * @param array|object $params An associative array of parameters
6992
	 * @param bool         $strict
6993
	 *
6994
	 * @return array|bool
6995
	 * @since 2.0
6996
	 */
6997
	public function load_object( $params, $strict = false ) {
6998
6999
		if ( is_object( $params ) && isset( $params->post_title ) ) {
7000
			$_object = get_object_vars( $params );
7001
		} else {
7002
			$params = (object) pods_sanitize( $params );
7003
7004
			if ( ! isset( $params->type ) || empty( $params->type ) ) {
7005
				return pods_error( __( 'Object type is required', 'pods' ), $this );
7006
			}
7007
7008
			if ( ( ! isset( $params->id ) || empty( $params->id ) ) && ( ! isset( $params->name ) || empty( $params->name ) ) ) {
7009
				return pods_error( __( 'Either Object ID or Name are required', 'pods' ), $this );
7010
			}
7011
7012
			/**
7013
			 * @var $wpdb wpdb
7014
			 */
7015
			global $wpdb;
7016
7017
			if ( isset( $params->name ) ) {
7018
				$_object = pods_by_title( $params->name, ARRAY_A, '_pods_' . $params->type, 'publish' );
7019
			} else {
7020
				$object = $params->id;
7021
7022
				$_object = get_post( $object, ARRAY_A );
7023
			}
7024
7025
			if ( empty( $_object ) ) {
7026
				if ( $strict ) {
7027
					return pods_error( __( 'Object not found', 'pods' ), $this );
7028
				}
7029
7030
				return false;
7031
			}
7032
		}
7033
7034
		$object = array(
7035
			'id'   => $_object['ID'],
7036
			'name' => $_object['post_title'],
7037
			'code' => $_object['post_content'],
7038
			'type' => str_replace( '_pods_', '', $_object['post_type'] ),
7039
			'slug' => $_object['post_name']
7040
		);
7041
7042
		$object['options'] = get_post_meta( $object['id'] );
7043
7044
		foreach ( $object['options'] as $option => &$value ) {
7045
			if ( is_array( $value ) && 1 == count( $value ) ) {
7046
				$value = current( $value );
7047
			}
7048
		}
7049
7050
		return $object;
7051
	}
7052
7053
	/**
7054
	 * Load Multiple Pods Objects
7055
	 *
7056
	 * $params['type'] string The Object type
7057
	 * $params['options'] array Pod Option(s) key=>value array to filter by
7058
	 * $params['orderby'] string ORDER BY clause of query
7059
	 * $params['limit'] string Number of objects to return
7060
	 * $params['where'] string WHERE clause of query
7061
	 * $params['ids'] string|array IDs of Objects
7062
	 *
7063
	 * @param array|object $params An associative array of parameters
7064
	 *
7065
	 * @return array
7066
	 * @since 2.0
7067
	 */
7068
	public function load_objects( $params ) {
7069
7070
		$params = (object) pods_sanitize( $params );
7071
7072
		if ( ! isset( $params->type ) || empty( $params->type ) ) {
7073
			return pods_error( __( 'Pods Object type is required', 'pods' ), $this );
7074
		}
7075
7076
		$order   = 'ASC';
7077
		$orderby = 'menu_order';
7078
		$limit   = - 1;
7079
		$ids     = false;
7080
7081
		$meta_query = array();
7082
		$cache_key  = '';
7083
7084
		if ( isset( $params->options ) && ! empty( $params->options ) && is_array( $params->options ) ) {
7085
			foreach ( $params->options as $option => $value ) {
7086
				if ( ! is_array( $value ) ) {
7087
					$value = array( $value );
7088
				}
7089
7090
				$value = pods_trim( $value );
7091
7092
				sort( $value );
7093
7094
				$meta_query[] = array(
7095
					'key'     => $option,
7096
					'value'   => pods_sanitize( $value ),
7097
					'compare' => 'IN'
7098
				);
7099
			}
7100
		}
7101
7102
		if ( isset( $params->where ) && is_array( $params->where ) ) {
7103
			$meta_query = array_merge( $meta_query, (array) $params->where );
7104
		}
7105
7106
		if ( isset( $params->order ) && ! empty( $params->order ) && in_array( strtoupper( $params->order ), array(
7107
				'ASC',
7108
				'DESC'
7109
			) ) ) {
7110
			$order = strtoupper( $params->order );
7111
		}
7112
7113
		if ( isset( $params->orderby ) && ! empty( $params->orderby ) ) {
7114
			$orderby = strtoupper( $params->orderby );
7115
		}
7116
7117
		if ( isset( $params->limit ) && ! empty( $params->limit ) ) {
7118
			$limit = pods_absint( $params->limit );
7119
		}
7120
7121
		if ( isset( $params->ids ) && ! empty( $params->ids ) ) {
7122
			$ids = $params->ids;
7123
7124
			if ( ! is_array( $ids ) ) {
7125
				$ids = explode( ',', $ids );
7126
			}
7127
		}
7128
7129
		if ( empty( $ids ) ) {
7130
			$ids = false;
7131
		}
7132
7133
		if ( pods_api_cache() && empty( $meta_query ) && empty( $limit ) && ( empty( $orderby ) || 'menu_order' === $orderby ) && empty( $ids ) ) {
7134
			$cache_key = 'pods_objects_' . $params->type;
7135
7136
			$the_objects = pods_transient_get( $cache_key );
7137
7138
			if ( false !== $the_objects ) {
7139
				return $the_objects;
7140
			}
7141
		}
7142
7143
		$the_objects = array();
7144
7145
		$args = array(
7146
			'post_type'      => '_pods_' . $params->type,
7147
			'nopaging'       => true,
7148
			'posts_per_page' => $limit,
7149
			'order'          => $order,
7150
			'orderby'        => $orderby,
7151
			'meta_query'     => $meta_query,
7152
		);
7153
7154
		// Only set post__in if there are ids to filter (see https://core.trac.wordpress.org/ticket/28099)
7155
		if ( false !== $ids ) {
7156
			$args['post__in'] = $ids;
7157
		}
7158
7159
		$objects = get_posts( $args );
7160
7161
		foreach ( $objects as $object ) {
7162
			$object = $this->load_object( $object );
7163
7164
			$the_objects[ $object['name'] ] = $object;
7165
		}
7166
7167
		if ( pods_api_cache() && ! empty( $cache_key ) ) {
7168
			pods_transient_set( $cache_key, $the_objects );
7169
		}
7170
7171
		return $the_objects;
7172
	}
7173
7174
	/**
7175
	 * @see   PodsAPI::load_object
7176
	 *
7177
	 * Load a Pod Template
7178
	 *
7179
	 * $params['id'] int The template ID
7180
	 * $params['name'] string The template name
7181
	 *
7182
	 * @param array $params An associative array of parameters
7183
	 *
7184
	 * @return array|bool
7185
	 * @since 1.7.9
7186
	 */
7187
	public function load_template( $params ) {
7188
7189
		if ( ! class_exists( 'Pods_Templates' ) ) {
7190
			return false;
7191
		}
7192
7193
		$params       = (object) $params;
7194
		$params->type = 'template';
7195
7196
		return $this->load_object( $params );
7197
	}
7198
7199
	/**
7200
	 * @see   PodsAPI::load_objects
7201
	 *
7202
	 * Load Multiple Pod Templates
7203
	 *
7204
	 * $params['where'] string The WHERE clause of query
7205
	 * $params['options'] array Pod Option(s) key=>value array to filter by
7206
	 * $params['orderby'] string ORDER BY clause of query
7207
	 * $params['limit'] string Number of templates to return
7208
	 *
7209
	 * @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...
7210
	 *
7211
	 * @return array
7212
	 *
7213
	 * @since 2.0
7214
	 */
7215
	public function load_templates( $params = null ) {
7216
7217
		if ( ! class_exists( 'Pods_Templates' ) ) {
7218
			return array();
7219
		}
7220
7221
		$params       = (object) $params;
7222
		$params->type = 'template';
7223
7224
		return $this->load_objects( $params );
7225
	}
7226
7227
	/**
7228
	 * @see   PodsAPI::load_object
7229
	 *
7230
	 * Load a Pod Page
7231
	 *
7232
	 * $params['id'] int The page ID
7233
	 * $params['name'] string The page URI
7234
	 *
7235
	 * @param array $params An associative array of parameters
7236
	 *
7237
	 * @return array|bool
7238
	 *
7239
	 * @since 1.7.9
7240
	 */
7241
	public function load_page( $params ) {
7242
7243
		if ( ! class_exists( 'Pods_Pages' ) ) {
7244
			return false;
7245
		}
7246
7247
		$params = (object) $params;
7248
		if ( ! isset( $params->name ) && isset( $params->uri ) ) {
7249
			$params->name = $params->uri;
7250
			unset( $params->uri );
7251
		}
7252
		$params->type = 'page';
7253
7254
		return $this->load_object( $params );
7255
	}
7256
7257
	/**
7258
	 * @see   PodsAPI::load_objects
7259
	 *
7260
	 * Load Multiple Pod Pages
7261
	 *
7262
	 * $params['where'] string The WHERE clause of query
7263
	 * $params['options'] array Pod Option(s) key=>value array to filter by
7264
	 * $params['orderby'] string ORDER BY clause of query
7265
	 * $params['limit'] string Number of pages to return
7266
	 *
7267
	 * @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...
7268
	 *
7269
	 * @return array
7270
	 *
7271
	 * @since 2.0
7272
	 */
7273
	public function load_pages( $params = null ) {
7274
7275
		if ( ! class_exists( 'Pods_Pages' ) ) {
7276
			return array();
7277
		}
7278
7279
		$params       = (object) $params;
7280
		$params->type = 'page';
7281
7282
		return $this->load_objects( $params );
7283
	}
7284
7285
	/**
7286
	 * @see   PodsAPI::load_object
7287
	 *
7288
	 * Load a Pod Helper
7289
	 *
7290
	 * $params['id'] int The helper ID
7291
	 * $params['name'] string The helper name
7292
	 *
7293
	 * @param array $params An associative array of parameters
7294
	 *
7295
	 * @return array|bool
7296
	 *
7297
	 * @since 1.7.9
7298
	 */
7299
	public function load_helper( $params ) {
7300
7301
		if ( ! class_exists( 'Pods_Helpers' ) ) {
7302
			return false;
7303
		}
7304
7305
		$params       = (object) $params;
7306
		$params->type = 'helper';
7307
7308
		return $this->load_object( $params );
7309
	}
7310
7311
	/**
7312
	 * @see   PodsAPI::load_objects
7313
	 *
7314
	 * Load Multiple Pod Helpers
7315
	 *
7316
	 * $params['where'] string The WHERE clause of query
7317
	 * $params['options'] array Pod Option(s) key=>value array to filter by
7318
	 * $params['orderby'] string ORDER BY clause of query
7319
	 * $params['limit'] string Number of pages to return
7320
	 *
7321
	 * @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...
7322
	 *
7323
	 * @return array
7324
	 *
7325
	 * @since 2.0
7326
	 */
7327
	public function load_helpers( $params = null ) {
7328
7329
		if ( ! class_exists( 'Pods_Helpers' ) ) {
7330
			return array();
7331
		}
7332
7333
		$params       = (object) $params;
7334
		$params->type = 'helper';
7335
7336
		return $this->load_objects( $params );
7337
	}
7338
7339
	/**
7340
	 * Load the pod item object
7341
	 *
7342
	 * $params['pod'] string The datatype name
7343
	 * $params['id'] int (optional) The item's ID
7344
	 *
7345
	 * @param array $params An associative array of parameters
7346
	 *
7347
	 * @return bool|\Pods
7348
	 *
7349
	 * @uses  pods()
7350
	 *
7351
	 * @since 2.0
7352
	 */
7353
	public function load_pod_item( $params ) {
7354
7355
		$params = (object) pods_sanitize( $params );
7356
7357
		if ( ! isset( $params->pod ) || empty( $params->pod ) ) {
7358
			return pods_error( __( 'Pod name required', 'pods' ), $this );
7359
		}
7360
		if ( ! isset( $params->id ) || empty( $params->id ) ) {
7361
			return pods_error( __( 'Item ID required', 'pods' ), $this );
7362
		}
7363
7364
		$pod = false;
7365
7366
		if ( pods_api_cache() ) {
7367
			$pod = pods_cache_get( $params->id, 'pods_item_object_' . $params->pod );
7368
		}
7369
7370
		if ( false !== $pod ) {
7371
			return $pod;
7372
		}
7373
7374
		$pod = pods( $params->pod, $params->id );
7375
7376
		if ( pods_api_cache() ) {
7377
			pods_cache_set( $params->id, $pod, 'pods_item_object_' . $params->pod );
7378
		}
7379
7380
		return $pod;
7381
	}
7382
7383
	/**
7384
	 * Load potential sister fields for a specific field
7385
	 *
7386
	 * $params['pod'] int The Pod name
7387
	 * $params['related_pod'] string The related Pod name
7388
	 *
7389
	 * @param array $params An associative array of parameters
7390
	 * @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...
7391
	 *
7392
	 * @return array|bool
7393
	 *
7394
	 * @since 1.7.9
7395
	 *
7396
	 * @uses  PodsAPI::load_pod
7397
	 */
7398
	public function load_sister_fields( $params, $pod = null ) {
7399
7400
		$params = (object) pods_sanitize( $params );
7401
7402
		if ( empty( $pod ) ) {
7403
			$pod = $this->load_pod( array( 'name' => $params->pod, 'table_info' => false ), false );
7404
7405
			if ( false === $pod ) {
7406
				return pods_error( __( 'Pod not found', 'pods' ), $this );
7407
			}
7408
		}
7409
7410
		$params->pod_id = $pod['id'];
7411
		$params->pod    = $pod['name'];
7412
7413
		$type = false;
7414
7415
		if ( 0 === strpos( $params->related_pod, 'pod-' ) ) {
7416
			$params->related_pod = pods_str_replace( 'pod-', '', $params->related_pod, 1 );
7417
			$type                = 'pod';
7418
		} elseif ( 0 === strpos( $params->related_pod, 'post_type-' ) ) {
7419
			$params->related_pod = pods_str_replace( 'post_type-', '', $params->related_pod, 1 );
7420
			$type                = 'post_type';
7421
		} elseif ( 0 === strpos( $params->related_pod, 'taxonomy-' ) ) {
7422
			$params->related_pod = pods_str_replace( 'taxonomy-', '', $params->related_pod, 1 );
7423
			$type                = 'taxonomy';
7424
		} elseif ( 'comment' === $params->related_pod ) {
7425
			$type = $params->related_pod;
7426
		}
7427
7428
		$related_pod = $this->load_pod( array( 'name' => $params->related_pod, 'table_info' => false ), false );
7429
7430
		if ( false === $related_pod || ( false !== $type && 'pod' !== $type && $type !== $related_pod['type'] ) ) {
7431
			return pods_error( __( 'Related Pod not found', 'pods' ), $this );
7432
		}
7433
7434
		$params->related_pod_id = $related_pod['id'];
7435
		$params->related_pod    = $related_pod['name'];
7436
7437
		$sister_fields = array();
7438
7439
		foreach ( $related_pod['fields'] as $field ) {
7440
			if ( 'pick' === $field['type'] && in_array( $field['pick_object'], array(
7441
					$pod['type'],
7442
					'pod'
7443
				) ) && ( $params->pod == $field['pick_object'] || $params->pod == $field['pick_val'] ) ) {
7444
				$sister_fields[ $field['id'] ] = esc_html( $field['label'] . ' (' . $field['name'] . ')' );
7445
			}
7446
		}
7447
7448
		return $sister_fields;
7449
	}
7450
7451
	/**
7452
	 * Takes a sql field such as tinyint and returns the pods field type, such as num.
7453
	 *
7454
	 * @param string $sql_field The SQL field to look for
7455
	 *
7456
	 * @return string The field type
7457
	 *
7458
	 * @since 2.0
7459
	 */
7460
	public static function detect_pod_field_from_sql_data_type( $sql_field ) {
7461
7462
		$sql_field = strtolower( $sql_field );
7463
7464
		$field_to_field_map = array(
7465
			'tinyint'    => 'number',
7466
			'smallint'   => 'number',
7467
			'mediumint'  => 'number',
7468
			'int'        => 'number',
7469
			'bigint'     => 'number',
7470
			'float'      => 'number',
7471
			'double'     => 'number',
7472
			'decimal'    => 'number',
7473
			'date'       => 'date',
7474
			'datetime'   => 'datetime',
7475
			'timestamp'  => 'datetime',
7476
			'time'       => 'time',
7477
			'year'       => 'date',
7478
			'varchar'    => 'text',
7479
			'text'       => 'paragraph',
7480
			'mediumtext' => 'paragraph',
7481
			'longtext'   => 'paragraph'
7482
		);
7483
7484
		return ( array_key_exists( $sql_field, $field_to_field_map ) ) ? $field_to_field_map[ $sql_field ] : 'paragraph';
7485
	}
7486
7487
	/**
7488
	 * Gets all field types
7489
	 *
7490
	 * @return array Array of field types
7491
	 *
7492
	 * @uses  PodsForm::field_loader
7493
	 *
7494
	 * @since 2.0
7495
	 * @deprecated
7496
	 */
7497
	public function get_field_types() {
7498
7499
		return PodsForm::field_types();
7500
	}
7501
7502
	/**
7503
	 * Gets the schema definition of a field.
7504
	 *
7505
	 * @param string $type    Field type to look for
7506
	 * @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...
7507
	 *
7508
	 * @return array|bool|mixed|null
7509
	 *
7510
	 * @since 2.0
7511
	 */
7512
	private function get_field_definition( $type, $options = null ) {
7513
7514
		$definition = PodsForm::field_method( $type, 'schema', $options );
7515
7516
		return $this->do_hook( 'field_definition', $definition, $type, $options );
7517
	}
7518
7519
	/**
7520
	 * @see   PodsForm:validate
7521
	 *
7522
	 * Validates the value of a field.
7523
	 *
7524
	 * @param mixed        $value         The value to validate
7525
	 * @param string       $field         Field to use for validation
7526
	 * @param array        $object_fields Fields of the object we're validating
7527
	 * @param array        $fields        Array of all fields data
7528
	 * @param array|Pods   $pod           Array of pod data (or Pods object)
7529
	 * @param array|object $params        Extra parameters to pass to the validation function of the field.
7530
	 *
7531
	 * @return array|bool
7532
	 *
7533
	 * @uses  PodsForm::validate
7534
	 *
7535
	 * @since 2.0
7536
	 */
7537
	public function handle_field_validation( &$value, $field, $object_fields, $fields, $pod, $params ) {
7538
7539
		$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...
7540
7541
		$fields = array_merge( $fields, $object_fields );
7542
7543
		$options = $fields[ $field ];
7544
7545
		$id = ( is_object( $params ) ? $params->id : ( is_object( $pod ) ? $pod->id() : 0 ) );
7546
7547
		if ( is_object( $pod ) ) {
7548
			$pod = $pod->pod_data;
7549
		}
7550
7551
		$type  = $options['type'];
7552
		$label = $options['label'];
7553
		$label = empty( $label ) ? $field : $label;
7554
7555
		// Verify required fields
7556
		if ( 1 == pods_var( 'required', $options['options'], 0 ) && 'slug' !== $type ) {
7557
			if ( '' === $value || null === $value || array() === $value ) {
7558
				return pods_error( sprintf( __( '%s is empty', 'pods' ), $label ), $this );
7559
			}
7560
7561
			if ( 'multi' === pods_var( 'pick_format_type', $options['options'] ) && 'autocomplete' !== pods_var( 'pick_format_multi', $options['options'] ) ) {
7562
				$has_value = false;
7563
7564
				$check_value = (array) $value;
7565
7566
				foreach ( $check_value as $val ) {
7567
					if ( '' !== $val && null !== $val && 0 !== $val && '0' !== $val ) {
7568
						$has_value = true;
7569
7570
						continue;
7571
					}
7572
				}
7573
7574
				if ( ! $has_value ) {
7575
					return pods_error( sprintf( __( '%s is required', 'pods' ), $label ), $this );
7576
				}
7577
			}
7578
7579
		}
7580
7581
		// @todo move this to after pre-save preparations
7582
		// Verify unique fields
7583
		if ( 1 == pods_var( 'unique', $options['options'], 0 ) && '' !== $value && null !== $value && array() !== $value ) {
7584
			if ( empty( $pod ) ) {
7585
				return false;
7586
			}
7587
7588
			if ( ! in_array( $type, $tableless_field_types ) ) {
7589
				$exclude = '';
7590
7591
				if ( ! empty( $id ) ) {
7592
					$exclude = "AND `id` != {$id}";
7593
				}
7594
7595
				$check = false;
7596
7597
				$check_value = pods_sanitize( $value );
7598
7599
				// @todo handle meta-based fields
7600
				// Trigger an error if not unique
7601
				if ( 'table' === $pod['storage'] ) {
7602
					$check = pods_query( "SELECT `id` FROM `@wp_pods_" . $pod['name'] . "` WHERE `{$field}` = '{$check_value}' {$exclude} LIMIT 1", $this );
7603
				}
7604
7605
				if ( ! empty( $check ) ) {
7606
					return pods_error( sprintf( __( '%s needs to be unique', 'pods' ), $label ), $this );
7607
				}
7608
			} else {
7609
				// @todo handle tableless check
7610
			}
7611
		}
7612
7613
		$validate = PodsForm::validate( $options['type'], $value, $field, array_merge( $options, pods_var( 'options', $options, array() ) ), $fields, $pod, $id, $params );
7614
7615
		$validate = $this->do_hook( 'field_validation', $validate, $value, $field, $object_fields, $fields, $pod, $params );
7616
7617
		return $validate;
7618
	}
7619
7620
	/**
7621
	 * Find items related to a parent field
7622
	 *
7623
	 * @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...
7624
	 * @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...
7625
	 * @param mixed $ids      A comma-separated string (or array) of item IDs
7626
	 * @param array $field    Field data array
7627
	 * @param array $pod      Pod data array
7628
	 *
7629
	 * @return int[]
7630
	 *
7631
	 * @since 2.0
7632
	 *
7633
	 * @uses  pods_query()
7634
	 */
7635
	public function lookup_related_items( $field_id, $pod_id, $ids, $field = null, $pod = null ) {
7636
7637
		$related_ids = array();
7638
7639
		if ( ! is_array( $ids ) ) {
7640
			$ids = explode( ',', $ids );
7641
		}
7642
7643
		$ids = array_map( 'absint', $ids );
7644
7645
		$ids = array_unique( array_filter( $ids ) );
7646
7647
		$idstring = implode( ',', $ids );
7648
7649
		if ( 0 != $pod_id && 0 != $field_id && isset( self::$related_item_cache[ $pod_id ][ $field_id ][ $idstring ] ) ) {
7650
			// Check cache first, no point in running the same query multiple times
7651
			return self::$related_item_cache[ $pod_id ][ $field_id ][ $idstring ];
7652
		}
7653
7654
		$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...
7655
7656
		$field_type = pods_v( 'type', $field );
7657
7658
		if ( empty( $ids ) || ! in_array( $field_type, $tableless_field_types ) ) {
7659
			return array();
7660
		}
7661
7662
		$related_pick_limit = 0;
7663
7664
		if ( empty( $field ) ) {
7665
			$field = $this->load_field( array( 'id' => $field_id ) );
7666
		}
7667
7668
		if ( ! empty( $field ) ) {
7669
			$options = (array) pods_var_raw( 'options', $field, $field, null, true );
7670
7671
			$related_pick_limit = (int) pods_v( $field_type . '_limit', $options, 0 );
7672
7673
			if ( 'single' === pods_var_raw( $field_type . '_format_type', $options ) ) {
7674
				$related_pick_limit = 1;
7675
			}
7676
7677
			// Temporary hack until there's some better handling here
7678
			$related_pick_limit = $related_pick_limit * count( $ids );
7679
		}
7680
7681
		if ( 'taxonomy' === $field_type ) {
7682
			$related = wp_get_object_terms( $ids, pods_v( 'name', $field ), array( 'fields' => 'ids' ) );
7683
7684
			if ( ! is_wp_error( $related ) ) {
7685
				$related_ids = $related;
7686
			}
7687
		} elseif ( 'comment' === $field_type ) {
7688
			$comment_args = array(
7689
				'post__in' => $ids,
7690
				'fields'   => 'ids',
7691
			);
7692
7693
			$related = get_comments( $comment_args );
7694
7695
			if ( ! is_wp_error( $related ) ) {
7696
				$related_ids = $related;
7697
			}
7698
		} elseif ( ! pods_tableless() ) {
7699
			$ids = implode( ', ', $ids );
7700
7701
			$field_id  = (int) $field_id;
7702
			$sister_id = (int) pods_var_raw( 'sister_id', $field, 0 );
7703
7704
			$related_where = "
7705
                `field_id` = {$field_id}
7706
                AND `item_id` IN ( {$ids} )
7707
            ";
7708
7709
			$sql = "
7710
                SELECT item_id, related_item_id, related_field_id
7711
                FROM `@wp_podsrel`
7712
                WHERE
7713
                    {$related_where}
7714
                ORDER BY `weight`
7715
            ";
7716
7717
			$relationships = pods_query( $sql );
7718
7719
			if ( ! empty( $relationships ) ) {
7720
				foreach ( $relationships as $relation ) {
7721
					if ( ! in_array( $relation->related_item_id, $related_ids ) ) {
7722
						$related_ids[] = (int) $relation->related_item_id;
7723
					} elseif ( 0 < $sister_id && $field_id == $relation->related_field_id && ! in_array( $relation->item_id, $related_ids ) ) {
7724
						$related_ids[] = (int) $relation->item_id;
7725
					}
7726
				}
7727
			}
7728
		} else {
7729
			if ( ! is_array( $pod ) ) {
7730
				$pod = $this->load_pod( array( 'id' => $pod_id, 'table_info' => false ), false );
7731
			}
7732
7733
			if ( ! empty( $pod ) && in_array( $pod['type'], array(
7734
					'post_type',
7735
					'media',
7736
					'taxonomy',
7737
					'user',
7738
					'comment',
7739
					'settings'
7740
				) ) ) {
7741
				$meta_type = $pod['type'];
7742
7743
				if ( in_array( $meta_type, array( 'post_type', 'media' ) ) ) {
7744
					$meta_type = 'post';
7745
				} elseif ( 'taxonomy' === $meta_type ) {
7746
					$meta_type = 'term';
7747
				}
7748
7749
				$no_conflict = pods_no_conflict_check( ( 'term' === $meta_type ? 'taxonomy' : $meta_type ) );
7750
7751
				if ( ! $no_conflict ) {
7752
					pods_no_conflict_on( ( 'term' === $meta_type ? 'taxonomy' : $meta_type ) );
7753
				}
7754
7755
				foreach ( $ids as $id ) {
7756
					if ( 'settings' === $meta_type ) {
7757
						$related_id = get_option( '_pods_' . $pod['name'] . '_' . $field['name'] );
7758
7759
						if ( empty( $related_id ) ) {
7760
							$related_id = get_option( $pod['name'] . '_' . $field['name'] );
7761
						}
7762
7763
						if ( is_array( $related_id ) && ! empty( $related_id ) ) {
7764
							foreach ( $related_id as $related ) {
7765
								if ( is_array( $related ) && ! empty( $related ) ) {
7766
									if ( isset( $related['id'] ) ) {
7767
										$related_ids[] = (int) $related['id'];
7768
									} else {
7769
										foreach ( $related as $r ) {
7770
											$related_ids[] = (int) $r;
7771
										}
7772
									}
7773
								} else {
7774
									$related_ids[] = (int) $related;
7775
								}
7776
							}
7777
						}
7778
					} else {
7779
						$related_id = get_metadata( $meta_type, $id, '_pods_' . $field['name'], true );
7780
7781
						if ( empty( $related_id ) ) {
7782
							$related_id = get_metadata( $meta_type, $id, $field['name'] );
7783
						}
7784
7785
						if ( is_array( $related_id ) && ! empty( $related_id ) ) {
7786
							foreach ( $related_id as $related ) {
7787
								if ( is_array( $related ) && ! empty( $related ) ) {
7788
									if ( isset( $related['id'] ) ) {
7789
										$related_ids[] = (int) $related['id'];
7790
									} else {
7791
										foreach ( $related as $r ) {
7792
											if ( isset( $related['id'] ) ) {
7793
												$related_ids[] = (int) $r['id'];
7794
											} else {
7795
												$related_ids[] = (int) $r;
7796
											}
7797
										}
7798
									}
7799
								} else {
7800
									$related_ids[] = (int) $related;
7801
								}
7802
							}
7803
						}
7804
					}
7805
				}
7806
7807
				if ( ! $no_conflict ) {
7808
					pods_no_conflict_off( ( 'term' === $meta_type ? 'taxonomy' : $meta_type ) );
7809
				}
7810
			}
7811
		}
7812
7813
		if ( is_array( $related_ids ) ) {
7814
			$related_ids = array_unique( array_filter( $related_ids ) );
7815
7816
			if ( 0 < $related_pick_limit && ! empty( $related_ids ) ) {
7817
				$related_ids = array_slice( $related_ids, 0, $related_pick_limit );
7818
			}
7819
		}
7820
		if ( 0 != $pod_id && 0 != $field_id && ! empty( $related_ids ) ) {
7821
			// Only cache if $pod_id and $field_id were passed
7822
			self::$related_item_cache[ $pod_id ][ $field_id ][ $idstring ] = $related_ids;
7823
		}
7824
7825
		return $related_ids;
7826
	}
7827
7828
	/**
7829
	 * Find related items related to an item
7830
	 *
7831
	 * @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...
7832
	 * @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...
7833
	 * @param int   $id       Item ID to get related IDs from
7834
	 * @param array $field    Field data array
7835
	 * @param array $pod      Pod data array
7836
	 *
7837
	 * @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...
7838
	 *
7839
	 * @since 2.3
7840
	 *
7841
	 * @uses  pods_query()
7842
	 */
7843
	public function lookup_related_items_from( $field_id, $pod_id, $id, $field = null, $pod = null ) {
7844
7845
		$related_ids = false;
7846
7847
		$id = (int) $id;
7848
7849
		$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...
7850
7851
		if ( empty( $id ) || ! in_array( pods_v( 'type', $field ), $tableless_field_types ) ) {
7852
			return false;
7853
		}
7854
7855
		$related_pick_limit = 0;
7856
7857
		if ( ! empty( $field ) ) {
7858
			$options = (array) pods_var_raw( 'options', $field, $field, null, true );
7859
7860
			$related_pick_limit = (int) pods_v( 'pick_limit', $options, 0 );
7861
7862
			if ( 'single' === pods_var_raw( 'pick_format_type', $options ) ) {
7863
				$related_pick_limit = 1;
7864
			}
7865
		}
7866
7867
		if ( ! pods_tableless() ) {
7868
			$field_id  = (int) $field_id;
7869
			$sister_id = (int) pods_var_raw( 'sister_id', $field, 0 );
7870
7871
			$related_where = "
7872
                `field_id` = {$field_id}
7873
                AND `related_item_id` = {$id}
7874
            ";
7875
7876
			$sql = "
7877
                SELECT *
7878
                FROM `@wp_podsrel`
7879
                WHERE
7880
                    {$related_where}
7881
                ORDER BY `weight`
7882
            ";
7883
7884
			$relationships = pods_query( $sql );
7885
7886
			if ( ! empty( $relationships ) ) {
7887
				$related_ids = array();
7888
7889
				foreach ( $relationships as $relation ) {
7890
					if ( $field_id == $relation->field_id && ! in_array( $relation->item_id, $related_ids ) ) {
7891
						$related_ids[] = (int) $relation->item_id;
7892
					} elseif ( 0 < $sister_id && $field_id == $relation->related_field_id && ! in_array( $relation->related_item_id, $related_ids ) ) {
7893
						$related_ids[] = (int) $relation->related_item_id;
7894
					}
7895
				}
7896
			}
7897
		} else {
7898
			// @todo handle meta-based lookups
7899
			return false;
7900
7901
			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...
7902
				$pod = $this->load_pod( array( 'id' => $pod_id, 'table_info' => false ), false );
7903
			}
7904
7905
			if ( ! empty( $pod ) && in_array( $pod['type'], array(
7906
					'post_type',
7907
					'media',
7908
					'taxonomy',
7909
					'user',
7910
					'comment',
7911
					'settings'
7912
				) ) ) {
7913
				$related_ids = array();
7914
7915
				$meta_type = $pod['type'];
7916
7917
				if ( in_array( $meta_type, array( 'post_type', 'media' ) ) ) {
7918
					$meta_type = 'post';
7919
				} elseif ( 'taxonomy' === $meta_type ) {
7920
					$meta_type = 'term';
7921
				}
7922
7923
				$no_conflict = pods_no_conflict_check( ( 'term' === $meta_type ? 'taxonomy' : $meta_type ) );
7924
7925
				if ( ! $no_conflict ) {
7926
					pods_no_conflict_on( ( 'term' === $meta_type ? 'taxonomy' : $meta_type ) );
7927
				}
7928
7929
				if ( 'settings' === $meta_type ) {
7930
					$related_id = get_option( '_pods_' . $pod['name'] . '_' . $field['name'] );
7931
7932
					if ( empty( $related_id ) ) {
7933
						$related_id = get_option( $pod['name'] . '_' . $field['name'] );
7934
					}
7935
7936
					if ( is_array( $related_id ) && ! empty( $related_id ) ) {
7937
						foreach ( $related_id as $related ) {
7938
							if ( is_array( $related ) && ! empty( $related ) ) {
7939
								if ( isset( $related['id'] ) ) {
7940
									$related_ids[] = (int) $related['id'];
7941
								} else {
7942
									foreach ( $related as $r ) {
7943
										$related_ids[] = (int) $r;
7944
									}
7945
								}
7946
							} else {
7947
								$related_ids[] = (int) $related;
7948
							}
7949
						}
7950
					}
7951
				} else {
7952
					$related_id = get_metadata( $meta_type, $id, '_pods_' . $field['name'], true );
7953
7954
					if ( empty( $related_id ) ) {
7955
						$related_id = get_metadata( $meta_type, $id, $field['name'] );
7956
					}
7957
7958
					if ( is_array( $related_id ) && ! empty( $related_id ) ) {
7959
						foreach ( $related_id as $related ) {
7960
							if ( is_array( $related ) && ! empty( $related ) ) {
7961
								if ( isset( $related['id'] ) ) {
7962
									$related_ids[] = (int) $related['id'];
7963
								} else {
7964
									foreach ( $related as $r ) {
7965
										if ( isset( $related['id'] ) ) {
7966
											$related_ids[] = (int) $r['id'];
7967
										} else {
7968
											$related_ids[] = (int) $r;
7969
										}
7970
									}
7971
								}
7972
							} else {
7973
								$related_ids[] = (int) $related;
7974
							}
7975
						}
7976
					}
7977
				}
7978
7979
				if ( ! $no_conflict ) {
7980
					pods_no_conflict_off( ( 'term' === $meta_type ? 'taxonomy' : $meta_type ) );
7981
				}
7982
			}
7983
		}
7984
7985
		if ( is_array( $related_ids ) ) {
7986
			$related_ids = array_unique( array_filter( $related_ids ) );
7987
		}
7988
7989
		return $related_ids;
7990
	}
7991
7992
	/**
7993
	 *
7994
	 * Load the information about an objects MySQL table
7995
	 *
7996
	 * @param        $object_type
7997
	 * @param string $object The object to look for
7998
	 * @param null   $name   (optional) Name of the pod to load
7999
	 * @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...
8000
	 *
8001
	 * @return array
8002
	 *
8003
	 * @since 2.5
8004
	 */
8005
	public function get_table_info_load( $object_type, $object, $name = null, $pod = null ) {
8006
8007
		$info = array();
8008
8009
		if ( 'pod' === $object_type && null === $pod ) {
8010
			if ( empty( $name ) ) {
8011
				$prefix = 'pod-';
8012
8013
				// Make sure we actually have the prefix before trying anything with the name
8014
				if ( 0 === strpos( $object_type, $prefix ) ) {
8015
					$name = substr( $object_type, strlen( $prefix ), strlen( $object_type ) );
8016
				}
8017
			}
8018
8019
			if ( empty( $name ) && ! empty( $object ) ) {
8020
				$name = $object;
8021
			}
8022
8023
			$pod = $this->load_pod( array( 'name' => $name, 'table_info' => false ), false );
8024
8025
			if ( ! empty( $pod ) ) {
8026
				$object_type = $pod['type'];
8027
				$name        = $pod['name'];
8028
				$object      = $pod['object'];
8029
8030
				$info['pod'] = $pod;
8031
			}
8032
		} elseif ( null === $pod ) {
8033
			if ( empty( $name ) ) {
8034
				$prefix = $object_type . '-';
8035
8036
				// Make sure we actually have the prefix before trying anything with the name
8037
				if ( 0 === strpos( $object_type, $prefix ) ) {
8038
					$name = substr( $object_type, strlen( $prefix ), strlen( $object_type ) );
8039
				}
8040
			}
8041
8042
			if ( empty( $name ) && ! empty( $object ) ) {
8043
				$name = $object;
8044
			}
8045
8046
			if ( ! empty( $name ) ) {
8047
				$pod = $this->load_pod( array( 'name' => $name, 'table_info' => false ), false );
8048
8049
				if ( ! empty( $pod ) && ( null === $object_type || $object_type == $pod['type'] ) ) {
8050
					$object_type = $pod['type'];
8051
					$name        = $pod['name'];
8052
					$object      = $pod['object'];
8053
8054
					$info['pod'] = $pod;
8055
				}
8056
			}
8057
		} elseif ( ! empty( $pod ) ) {
8058
			$info['pod'] = $pod;
8059
		}
8060
8061
		if ( 0 === strpos( $object_type, 'pod' ) ) {
8062
			if ( empty( $name ) ) {
8063
				$prefix = 'pod-';
8064
8065
				// Make sure we actually have the prefix before trying anything with the name
8066
				if ( 0 === strpos( $object_type, $prefix ) ) {
8067
					$name = substr( $object_type, strlen( $prefix ), strlen( $object_type ) );
8068
				}
8069
			}
8070
8071
			$info['type'] = 'pod';
8072
			global $wpdb;
8073
8074
			$info['table'] = $info['meta_table'] = $wpdb->prefix . 'pods_' . ( empty( $object ) ? $name : $object );
8075
8076
			if ( is_array( $info['pod'] ) && 'pod' === pods_v( 'type', $info['pod'] ) ) {
8077
				$info['pod_field_index'] = $info['field_index'] = $info['meta_field_index'] = $info['meta_field_value'] = pods_v( 'pod_index', $info['pod']['options'], 'id', true );
8078
8079
				$slug_field = get_posts( array(
8080
					'post_type'      => '_pods_field',
8081
					'posts_per_page' => 1,
8082
					'nopaging'       => true,
8083
					'post_parent'    => $info['pod']['id'],
8084
					'orderby'        => 'menu_order',
8085
					'order'          => 'ASC',
8086
					'meta_query'     => array(
8087
						array(
8088
							'key'   => 'type',
8089
							'value' => 'slug',
8090
						)
8091
					)
8092
				) );
8093
8094
				if ( ! empty( $slug_field[0] ) ) {
8095
					$slug_field = $slug_field[0];
8096
8097
					$info['field_slug'] = $info['pod_field_slug'] = $slug_field->post_name;
8098
				}
8099
8100
				if ( 1 == pods_v( 'hierarchical', $info['pod']['options'], 0 ) ) {
8101
					$parent_field = pods_v( 'pod_parent', $info['pod']['options'], 'id', true );
8102
8103
					if ( ! empty( $parent_field ) && isset( $info['pod']['fields'][ $parent_field ] ) ) {
8104
						$info['object_hierarchical'] = true;
8105
8106
						$info['pod_field_parent']    = $info['field_parent'] = $parent_field . '_select';
8107
						$info['field_parent_select'] = '`' . $parent_field . '`.`id` AS `' . $info['field_parent'] . '`';
8108
					}
8109
				}
8110
			}
8111
		}
8112
8113
		return $info;
8114
	}
8115
8116
	/**
8117
	 * Get information about an objects MySQL table
8118
	 *
8119
	 * @param string $object_type
8120
	 * @param string $object The object to look for
8121
	 * @param null   $name   (optional) Name of the pod to load
8122
	 * @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...
8123
	 * @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...
8124
	 *
8125
	 * @return array|bool
8126
	 *
8127
	 * @since 2.0
8128
	 */
8129
	public function get_table_info( $object_type, $object, $name = null, $pod = null, $field = null ) {
8130
8131
		/**
8132
		 * @var $wpdb                         wpdb
8133
		 * @var $sitepress                    SitePress
8134
		 * @var $polylang                     object
8135
		 */
8136
		/*
8137
	     * @todo wpml-comp Remove global object usage
8138
	     */
8139
		global $wpdb, $sitepress, $polylang;
8140
8141
		// @todo Handle $object arrays for Post Types, Taxonomies, Comments (table pulled from first object in array)
8142
8143
		$info = array(
8144
			//'select' => '`t`.*',
8145
			'object_type'         => $object_type,
8146
			'type'                => null,
8147
			'object_name'         => $object,
8148
			'object_hierarchical' => false,
8149
8150
			'table'      => $object,
8151
			'meta_table' => $object,
8152
			'pod_table'  => $wpdb->prefix . 'pods_' . ( empty( $object ) ? $name : $object ),
8153
8154
			'field_id'            => 'id',
8155
			'field_index'         => 'name',
8156
			'field_slug'          => null,
8157
			'field_type'          => null,
8158
			'field_parent'        => null,
8159
			'field_parent_select' => null,
8160
8161
			'meta_field_id'    => 'id',
8162
			'meta_field_index' => 'name',
8163
			'meta_field_value' => 'name',
8164
8165
			'pod_field_id'     => 'id',
8166
			'pod_field_index'  => 'name',
8167
			'pod_field_slug'   => null,
8168
			'pod_field_parent' => null,
8169
8170
			'join' => array(),
8171
8172
			'where'         => null,
8173
			'where_default' => null,
8174
8175
			'orderby' => null,
8176
8177
			'pod'     => null,
8178
			'recurse' => false
8179
		);
8180
8181
		if ( empty( $object_type ) ) {
8182
			$object_type = 'post_type';
8183
			$object      = 'post';
8184
		} elseif ( empty( $object ) && in_array( $object_type, array( 'user', 'media', 'comment' ) ) ) {
8185
			$object = $object_type;
8186
		}
8187
8188
		$pod_name = $pod;
8189
8190
		if ( is_array( $pod_name ) ) {
8191
			$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...
8192
		} else {
8193
			$pod_name = $object;
8194
		}
8195
8196
		$field_name = $field;
8197
8198
		if ( is_array( $field_name ) ) {
8199
			$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...
8200
		}
8201
8202
		$transient = 'pods_' . $wpdb->prefix . '_get_table_info_' . md5( $object_type . '_object_' . $object . '_name_' . $name . '_pod_' . $pod_name . '_field_' . $field_name );
8203
8204
		$current_language      = false;
8205
		$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...
8206
8207
		// Get current language data
8208
		$lang_data = PodsInit::$i18n->get_current_language_data();
8209
8210
		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...
8211
			if ( ! empty( $lang_data['language'] ) ) {
8212
				$current_language = $lang_data['language'];
8213
			}
8214
8215
			if ( ! empty( $lang_data['t_id'] ) ) {
8216
				$current_language_t_id = $lang_data['t_id'];
8217
			}
8218
8219
			if ( ! empty( $lang_data['tt_id'] ) ) {
8220
				$current_language_tt_id = $lang_data['tt_id'];
8221
			}
8222
8223
			if ( ! empty( $lang_data['tl_t_id'] ) ) {
8224
				$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...
8225
			}
8226
8227
			if ( ! empty( $lang_data['tl_tt_id'] ) ) {
8228
				$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...
8229
			}
8230
		}
8231
8232
		if ( ! empty( $current_language ) ) {
8233
			$transient = 'pods_' . $wpdb->prefix . '_get_table_info_' . $current_language . '_' . md5( $object_type . '_object_' . $object . '_name_' . $name . '_pod_' . $pod_name . '_field_' . $field_name );
8234
		}
8235
8236
		$_info = false;
8237
8238
		if ( isset( self::$table_info_cache[ $transient ] ) ) {
8239
			// Prefer info from the object internal cache
8240
			$_info = self::$table_info_cache[ $transient ];
8241
		} elseif ( pods_api_cache() ) {
8242
			$_info = pods_transient_get( $transient );
8243
			if ( false === $_info && ! did_action( 'init' ) ) {
8244
				$_info = pods_transient_get( $transient . '_pre_init' );
8245
			}
8246
		}
8247
8248
		if ( false !== $_info && is_array( $_info ) ) {
8249
			// Data was cached, use that
8250
			$info = $_info;
8251
		} else {
8252
			// Data not cached, load it up
8253
			$_info = $this->get_table_info_load( $object_type, $object, $name, $pod );
8254
			if ( isset( $_info['type'] ) ) {
8255
				// Allow function to override $object_type
8256
				$object_type = $_info['type'];
8257
			}
8258
			$info = array_merge( $info, $_info );
8259
		}
8260
8261
		if ( 0 === strpos( $object_type, 'post_type' ) || 'media' === $object_type || in_array( pods_var_raw( 'type', $info['pod'] ), array(
8262
				'post_type',
8263
				'media'
8264
			) ) ) {
8265
			$info['table']      = $wpdb->posts;
8266
			$info['meta_table'] = $wpdb->postmeta;
8267
8268
			$info['field_id']            = 'ID';
8269
			$info['field_index']         = 'post_title';
8270
			$info['field_slug']          = 'post_name';
8271
			$info['field_type']          = 'post_type';
8272
			$info['field_parent']        = 'post_parent';
8273
			$info['field_parent_select'] = '`t`.`' . $info['field_parent'] . '`';
8274
8275
			$info['meta_field_id']    = 'post_id';
8276
			$info['meta_field_index'] = 'meta_key';
8277
			$info['meta_field_value'] = 'meta_value';
8278
8279
			if ( 'media' === $object_type ) {
8280
				$object = 'attachment';
8281
			}
8282
8283
			if ( empty( $name ) ) {
8284
				$prefix = 'post_type-';
8285
8286
				// Make sure we actually have the prefix before trying anything with the name
8287
				if ( 0 === strpos( $object_type, $prefix ) ) {
8288
					$name = substr( $object_type, strlen( $prefix ), strlen( $object_type ) );
8289
				}
8290
			}
8291
8292
			if ( 'media' !== $object_type ) {
8293
				$object_type = 'post_type';
8294
			}
8295
8296
			$post_type = pods_sanitize( ( empty( $object ) ? $name : $object ) );
8297
8298
			if ( 'attachment' === $post_type || 'media' === $object_type ) {
8299
				$info['pod_table'] = $wpdb->prefix . 'pods_media';
8300
			} else {
8301
				$info['pod_table'] = $wpdb->prefix . 'pods_' . pods_clean_name( $post_type, true, false );
8302
			}
8303
8304
			$post_type_object = get_post_type_object( $post_type );
8305
8306
			if ( is_object( $post_type_object ) && $post_type_object->hierarchical ) {
8307
				$info['object_hierarchical'] = true;
8308
			}
8309
8310
			// Post Status default
8311
			$post_status = array( 'publish' );
8312
8313
			// Pick field post_status option
8314
			if ( ! empty( $field['options']['pick_post_status'] ) ) {
8315
				$post_status = (array) $field['options']['pick_post_status'];
8316
			} elseif ( ! empty( $field['pick_post_status'] ) ) {
8317
				$post_status = (array) $field['pick_post_status'];
8318
			}
8319
8320
			/**
8321
			 * Default Post Status to query for.
8322
			 *
8323
			 * Use to change "default" post status from publish to any other status or statuses.
8324
			 *
8325
			 * @param  array  $post_status List of post statuses. Default is 'publish' or field setting (if available).
8326
			 * @param  string $post_type   Post type of current object.
8327
			 * @param  array  $info        Array of information about the object.
8328
			 * @param  string $object      Type of object.
8329
			 * @param  string $name        Name of pod to load.
8330
			 * @param  array  $pod         Array with Pod information. Result of PodsAPI::load_pod().
8331
			 * @param  array  $field       Array with field information.
8332
			 *
8333
			 * @since unknown
8334
			 */
8335
			$post_status = apply_filters( 'pods_api_get_table_info_default_post_status', $post_status, $post_type, $info, $object_type, $object, $name, $pod, $field );
8336
8337
			$info['where'] = array(
8338
				//'post_status' => '`t`.`post_status` IN ( "inherit", "publish" )', // @todo Figure out what statuses Attachments can be
8339
				'post_type' => '`t`.`' . $info['field_type'] . '` = "' . $post_type . '"'
8340
			);
8341
8342
			if ( 'post_type' === $object_type ) {
8343
				$info['where_default'] = '`t`.`post_status` IN ( "' . implode( '", "', $post_status ) . '" )';
8344
			}
8345
8346
			$info['orderby'] = '`t`.`menu_order`, `t`.`' . $info['field_index'] . '`, `t`.`post_date`';
8347
8348
			/*
8349
             * @todo wpml-comp Check if WPML filters can be applied afterwards
8350
             */
8351
			// WPML support
8352
			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' ) ) {
8353
				$info['join']['wpml_translations'] = "
8354
                        LEFT JOIN `{$wpdb->prefix}icl_translations` AS `wpml_translations`
8355
                            ON `wpml_translations`.`element_id` = `t`.`ID`
8356
                                AND `wpml_translations`.`element_type` = 'post_{$post_type}'
8357
                                AND `wpml_translations`.`language_code` = '{$current_language}'
8358
                    ";
8359
8360
				$info['join']['wpml_languages'] = "
8361
                        LEFT JOIN `{$wpdb->prefix}icl_languages` AS `wpml_languages`
8362
                            ON `wpml_languages`.`code` = `wpml_translations`.`language_code` AND `wpml_languages`.`active` = 1
8363
                    ";
8364
8365
				$info['where']['wpml_languages'] = "`wpml_languages`.`code` IS NOT NULL";
8366
			} elseif ( ( function_exists( 'PLL' ) || is_object( $polylang ) ) && ! empty( $current_language ) && function_exists( 'pll_is_translated_post_type' ) && pll_is_translated_post_type( $post_type ) ) {
8367
				// Polylang support
8368
				$info['join']['polylang_languages'] = "
8369
                        LEFT JOIN `{$wpdb->term_relationships}` AS `polylang_languages`
8370
                            ON `polylang_languages`.`object_id` = `t`.`ID`
8371
                                AND `polylang_languages`.`term_taxonomy_id` = {$current_language_tt_id}
8372
                    ";
8373
8374
				$info['where']['polylang_languages'] = "`polylang_languages`.`object_id` IS NOT NULL";
8375
			}
8376
8377
			$info['object_fields'] = $this->get_wp_object_fields( $object_type, $info['pod'] );
8378
		} elseif ( 0 === strpos( $object_type, 'taxonomy' ) || in_array( $object_type, array(
8379
				'nav_menu',
8380
				'post_format'
8381
			) ) || 'taxonomy' === pods_var_raw( 'type', $info['pod'] ) ) {
8382
			$info['table'] = $info['meta_table'] = $wpdb->terms;
8383
8384
			$info['join']['tt']          = "LEFT JOIN `{$wpdb->term_taxonomy}` AS `tt` ON `tt`.`term_id` = `t`.`term_id`";
8385
			$info['join']['tr']          = "LEFT JOIN `{$wpdb->term_relationships}` AS `tr` ON `tr`.`term_taxonomy_id` = `tt`.`term_taxonomy_id`";
8386
			$info['field_id']            = $info['meta_field_id'] = 'term_id';
8387
			$info['field_index']         = $info['meta_field_index'] = $info['meta_field_value'] = 'name';
8388
			$info['field_slug']          = 'slug';
8389
			$info['field_type']          = 'taxonomy';
8390
			$info['field_parent']        = 'parent';
8391
			$info['field_parent_select'] = '`tt`.`' . $info['field_parent'] . '`';
8392
8393
			if ( ! empty( $wpdb->termmeta ) ) {
8394
				$info['meta_table'] = $wpdb->termmeta;
8395
8396
				$info['meta_field_id']    = 'term_id';
8397
				$info['meta_field_index'] = 'meta_key';
8398
				$info['meta_field_value'] = 'meta_value';
8399
			}
8400
8401
			if ( 'nav_menu' === $object_type ) {
8402
				$object = 'nav_menu';
8403
			} elseif ( 'post_format' === $object_type ) {
8404
				$object = 'post_format';
8405
			}
8406
8407
			if ( empty( $name ) ) {
8408
				$prefix = 'taxonomy-';
8409
8410
				// Make sure we actually have the prefix before trying anything with the name
8411
				if ( 0 === strpos( $object_type, $prefix ) ) {
8412
					$name = substr( $object_type, strlen( $prefix ), strlen( $object_type ) );
8413
				}
8414
			}
8415
8416
			if ( ! in_array( $object_type, array( 'nav_menu', 'post_format' ) ) ) {
8417
				$object_type = 'taxonomy';
8418
			}
8419
8420
			$taxonomy = pods_sanitize( ( empty( $object ) ? $name : $object ) );
8421
8422
			$info['pod_table'] = $wpdb->prefix . 'pods_' . pods_clean_name( $taxonomy, true, false );
8423
8424
			$taxonomy_object = get_taxonomy( $taxonomy );
8425
8426
			if ( is_object( $taxonomy_object ) && $taxonomy_object->hierarchical ) {
8427
				$info['object_hierarchical'] = true;
8428
			}
8429
8430
			$info['where'] = array(
8431
				'tt.taxonomy' => '`tt`.`' . $info['field_type'] . '` = "' . $taxonomy . '"'
8432
			);
8433
8434
			/*
8435
             * @todo wpml-comp WPML API call for is_translated_taxononomy
8436
             * @todo wpml-comp Check if WPML filters can be applied afterwards
8437
             */
8438
			// WPML Support
8439
			if ( is_object( $sitepress ) && ! empty( $current_language ) && $sitepress->is_translated_taxonomy( $taxonomy ) && apply_filters( 'wpml_setting', true, 'auto_adjust_ids' ) ) {
8440
				$info['join']['wpml_translations'] = "
8441
                        LEFT JOIN `{$wpdb->prefix}icl_translations` AS `wpml_translations`
8442
                            ON `wpml_translations`.`element_id` = `tt`.`term_taxonomy_id`
8443
                                AND `wpml_translations`.`element_type` = 'tax_{$taxonomy}'
8444
                                AND `wpml_translations`.`language_code` = '{$current_language}'
8445
                    ";
8446
8447
				$info['join']['wpml_languages'] = "
8448
                        LEFT JOIN `{$wpdb->prefix}icl_languages` AS `wpml_languages`
8449
                            ON `wpml_languages`.`code` = `wpml_translations`.`language_code` AND `wpml_languages`.`active` = 1
8450
                    ";
8451
8452
				$info['where']['wpml_languages'] = "`wpml_languages`.`code` IS NOT NULL";
8453
			} 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 ) ) {
8454
				// Polylang support
8455
				$info['join']['polylang_languages'] = "
8456
					LEFT JOIN `{$wpdb->term_relationships}` AS `polylang_languages`
8457
						ON `polylang_languages`.`object_id` = `t`.`term_id`
8458
							AND `polylang_languages`.`term_taxonomy_id` = {$current_language_tl_tt_id}
8459
				";
8460
8461
				$info['where']['polylang_languages'] = "`polylang_languages`.`object_id` IS NOT NULL";
8462
			}
8463
8464
			$info['object_fields'] = $this->get_wp_object_fields( $object_type, $info['pod'] );
8465
		} elseif ( 'user' === $object_type || 'user' === pods_var_raw( 'type', $info['pod'] ) ) {
8466
			$info['table']      = $wpdb->users;
8467
			$info['meta_table'] = $wpdb->usermeta;
8468
			$info['pod_table']  = $wpdb->prefix . 'pods_user';
8469
8470
			$info['field_id']    = 'ID';
8471
			$info['field_index'] = 'display_name';
8472
			$info['field_slug']  = 'user_nicename';
8473
8474
			$info['meta_field_id']    = 'user_id';
8475
			$info['meta_field_index'] = 'meta_key';
8476
			$info['meta_field_value'] = 'meta_value';
8477
8478
			$info['where'] = array(
8479
				'user_status' => '`t`.`user_status` = 0'
8480
			);
8481
8482
			$info['object_fields'] = $this->get_wp_object_fields( $object_type, $info['pod'] );
8483
		} elseif ( 'comment' === $object_type || 'comment' === pods_var_raw( 'type', $info['pod'] ) ) {
8484
			//$info[ 'object_hierarchical' ] = true;
8485
8486
			$info['table']      = $wpdb->comments;
8487
			$info['meta_table'] = $wpdb->commentmeta;
8488
			$info['pod_table']  = $wpdb->prefix . 'pods_comment';
8489
8490
			$info['field_id']            = 'comment_ID';
8491
			$info['field_index']         = 'comment_date';
8492
			$info['field_type']          = 'comment_type';
8493
			$info['field_parent']        = 'comment_parent';
8494
			$info['field_parent_select'] = '`t`.`' . $info['field_parent'] . '`';
8495
8496
			$info['meta_field_id']    = 'comment_id';
8497
			$info['meta_field_index'] = 'meta_key';
8498
			$info['meta_field_value'] = 'meta_value';
8499
8500
			$object = 'comment';
8501
8502
			$comment_type = ( empty( $object ) ? $name : $object );
8503
8504
			$comment_type_clause = '`t`.`' . $info['field_type'] . '` = "' . $comment_type . '"';
8505
8506
			if ( 'comment' === $comment_type ) {
8507
				$comment_type_clause = '( ' . $comment_type_clause . ' OR `t`.`' . $info['field_type'] . '` = "" )';
8508
			}
8509
8510
			$info['where'] = array(
8511
				'comment_approved' => '`t`.`comment_approved` = 1',
8512
				'comment_type'     => $comment_type_clause
8513
			);
8514
8515
			$info['orderby'] = '`t`.`' . $info['field_index'] . '` DESC, `t`.`' . $info['field_id'] . '`';
8516
		} elseif ( in_array( $object_type, array(
8517
				'option',
8518
				'settings'
8519
			) ) || 'settings' === pods_var_raw( 'type', $info['pod'] ) ) {
8520
			$info['table']      = $wpdb->options;
8521
			$info['meta_table'] = $wpdb->options;
8522
8523
			$info['field_id']    = 'option_id';
8524
			$info['field_index'] = 'option_name';
8525
8526
			$info['meta_field_id']    = 'option_id';
8527
			$info['meta_field_index'] = 'option_name';
8528
			$info['meta_field_value'] = 'option_value';
8529
8530
			$info['orderby'] = '`t`.`' . $info['field_index'] . '` ASC';
8531
		} elseif ( is_multisite() && ( in_array( $object_type, array(
8532
					'site_option',
8533
					'site_settings'
8534
				) ) || 'site_settings' === pods_var_raw( 'type', $info['pod'] ) ) ) {
8535
			$info['table']      = $wpdb->sitemeta;
8536
			$info['meta_table'] = $wpdb->sitemeta;
8537
8538
			$info['field_id']    = 'site_id';
8539
			$info['field_index'] = 'meta_key';
8540
8541
			$info['meta_field_id']    = 'site_id';
8542
			$info['meta_field_index'] = 'meta_key';
8543
			$info['meta_field_value'] = 'meta_value';
8544
8545
			$info['orderby'] = '`t`.`' . $info['field_index'] . '` ASC';
8546
		} elseif ( is_multisite() && 'network' === $object_type ) { // Network = Site
8547
			$info['table']      = $wpdb->site;
8548
			$info['meta_table'] = $wpdb->sitemeta;
8549
8550
			$info['field_id']    = 'id';
8551
			$info['field_index'] = 'domain';
8552
8553
			$info['meta_field_id']    = 'site_id';
8554
			$info['meta_field_index'] = 'meta_key';
8555
			$info['meta_field_value'] = 'meta_value';
8556
8557
			$info['orderby'] = '`t`.`' . $info['field_index'] . '` ASC, `t`.`path` ASC, `t`.`' . $info['field_id'] . '`';
8558
		} elseif ( is_multisite() && 'site' === $object_type ) { // Site = Blog
8559
			$info['table'] = $wpdb->blogs;
8560
8561
			$info['field_id']    = 'blog_id';
8562
			$info['field_index'] = 'domain';
8563
			$info['field_type']  = 'site_id';
8564
8565
			$info['where'] = array(
8566
				'archived' => '`t`.`archived` = 0',
8567
				'spam'     => '`t`.`spam` = 0',
8568
				'deleted'  => '`t`.`deleted` = 0',
8569
				'site_id'  => '`t`.`' . $info['field_type'] . '` = ' . (int) get_current_site()->id
8570
			);
8571
8572
			$info['orderby'] = '`t`.`' . $info['field_index'] . '` ASC, `t`.`path` ASC, `t`.`' . $info['field_id'] . '`';
8573
		} elseif ( 'table' === $object_type || 'table' === pods_var_raw( 'type', $info['pod'] ) ) {
8574
			$info['table']     = ( empty( $object ) ? $name : $object );
8575
			$info['pod_table'] = $wpdb->prefix . 'pods_' . $info['table'];
8576
8577
			if ( ! empty( $field ) && is_array( $field ) ) {
8578
				$info['table']       = pods_var_raw( 'pick_table', pods_var_raw( 'options', $field, $field ) );
8579
				$info['field_id']    = pods_var_raw( 'pick_table_id', pods_var_raw( 'options', $field, $field ) );
8580
				$info['field_index'] = $info['meta_field_index'] = $info['meta_field_value'] = pods_var_raw( 'pick_table_index', pods_var_raw( 'options', $field, $field ) );
8581
			}
8582
		}
8583
8584
		$info['table']      = pods_clean_name( $info['table'], false, false );
8585
		$info['meta_table'] = pods_clean_name( $info['meta_table'], false, false );
8586
		$info['pod_table']  = pods_clean_name( $info['pod_table'], false, false );
8587
8588
		$info['field_id']    = pods_clean_name( $info['field_id'], false, false );
8589
		$info['field_index'] = pods_clean_name( $info['field_index'], false, false );
8590
		$info['field_slug']  = pods_clean_name( $info['field_slug'], false, false );
8591
8592
		$info['meta_field_id']    = pods_clean_name( $info['meta_field_id'], false, false );
8593
		$info['meta_field_index'] = pods_clean_name( $info['meta_field_index'], false, false );
8594
		$info['meta_field_value'] = pods_clean_name( $info['meta_field_value'], false, false );
8595
8596
		if ( empty( $info['orderby'] ) ) {
8597
			$info['orderby'] = '`t`.`' . $info['field_index'] . '`, `t`.`' . $info['field_id'] . '`';
8598
		}
8599
8600
		if ( 'table' === pods_var_raw( 'storage', $info['pod'] ) && ! in_array( $object_type, array(
8601
				'pod',
8602
				'table'
8603
			) ) ) {
8604
			$info['join']['d'] = 'LEFT JOIN `' . $info['pod_table'] . '` AS `d` ON `d`.`id` = `t`.`' . $info['field_id'] . '`';
8605
			//$info[ 'select' ] .= ', `d`.*';
8606
		}
8607
8608
		if ( ! empty( $info['pod'] ) && is_array( $info['pod'] ) ) {
8609
			$info['recurse'] = true;
8610
		}
8611
8612
		$info['type']        = $object_type;
8613
		$info['object_name'] = $object;
8614
8615
		if ( pods_api_cache() ) {
8616
			if ( ! did_action( 'init' ) ) {
8617
				$transient .= '_pre_init';
8618
			}
8619
			pods_transient_set( $transient, $info );
8620
		}
8621
8622
		self::$table_info_cache[ $transient ] = apply_filters( 'pods_api_get_table_info', $info, $object_type, $object, $name, $pod, $field, $this );
8623
8624
		return self::$table_info_cache[ $transient ];
8625
	}
8626
8627
	/**
8628
	 * Export a package
8629
	 *
8630
	 * $params['pods'] array Pod Type IDs to export
8631
	 * $params['templates'] array Template IDs to export
8632
	 * $params['pages'] array Pod Page IDs to export
8633
	 * $params['helpers'] array Helper IDs to export
8634
	 *
8635
	 * @param array $params An associative array of parameters
8636
	 *
8637
	 * @return array|bool
8638
	 *
8639
	 * @since      1.9.0
8640
	 * @deprecated 2.0
8641
	 */
8642
	public function export_package( $params ) {
8643
8644
		if ( class_exists( 'Pods_Migrate_Packages' ) ) {
8645
			return Pods_Migrate_Packages::export( $params );
8646
		}
8647
8648
		return false;
8649
	}
8650
8651
	/**
8652
	 * Replace an existing package
8653
	 *
8654
	 * @param mixed $data (optional) An associative array containing a package, or the json encoded package
8655
	 *
8656
	 * @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...
8657
	 *
8658
	 * @since      1.9.8
8659
	 * @deprecated 2.0
8660
	 */
8661
	public function replace_package( $data = false ) {
8662
8663
		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...
8664
	}
8665
8666
	/**
8667
	 * Import a package
8668
	 *
8669
	 * @param mixed $data    (optional) An associative array containing a package, or the json encoded package
8670
	 * @param bool  $replace (optional) Replace existing items when found
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.0
8675
	 * @deprecated 2.0
8676
	 */
8677
	public function import_package( $data = false, $replace = false ) {
8678
8679
		if ( class_exists( 'Pods_Migrate_Packages' ) ) {
8680
			return Pods_Migrate_Packages::import( $data, $replace );
8681
		}
8682
8683
		return false;
8684
	}
8685
8686
	/**
8687
	 * Validate a package
8688
	 *
8689
	 * @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...
8690
	 * @param bool         $output (optional)
8691
	 *
8692
	 * @return array|bool
8693
	 *
8694
	 * @since      1.9.0
8695
	 * @deprecated 2.0
8696
	 */
8697
	public function validate_package( $data = false, $output = false ) {
8698
8699
		return true;
8700
	}
8701
8702
	/**
8703
	 * Import data from an array or a CSV file.
8704
	 *
8705
	 * @param mixed  $import_data  PHP associative array or CSV input
8706
	 * @param bool   $numeric_mode Use IDs instead of the name field when matching
8707
	 * @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...
8708
	 *
8709
	 * @return array IDs of imported items
8710
	 *
8711
	 * @since 1.7.1
8712
	 * @todo  This needs some love and use of table_info etc for relationships
8713
	 */
8714
	public function import( $import_data, $numeric_mode = false, $format = null ) {
8715
8716
		/**
8717
		 * @var $wpdb wpdb
8718
		 */
8719
		global $wpdb;
8720
8721
		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...
8722
			$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...
8723
		}
8724
8725
		if ( 'csv' === $format && ! is_array( $import_data ) ) {
8726
			$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...
8727
8728
			$import_data = $data['items'];
8729
		}
8730
8731
		pods_query( "SET NAMES utf8" );
8732
		pods_query( "SET CHARACTER SET utf8" );
8733
8734
		// Loop through the array of items
8735
		$ids = array();
8736
8737
		// Test to see if it's an array of arrays
8738
		if ( ! is_array( @current( $import_data ) ) ) {
8739
			$import_data = array( $import_data );
8740
		}
8741
8742
		$pod = $this->load_pod( array( 'name' => $this->pod ) );
8743
8744
		if ( false === $pod ) {
8745
			return pods_error( __( 'Pod not found', 'pods' ), $this );
8746
		}
8747
8748
		$fields = array_merge( $pod['fields'], $pod['object_fields'] );
8749
8750
		$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...
8751
8752
		foreach ( $import_data as $key => $data_row ) {
8753
			$data = array();
8754
8755
			// Loop through each field (use $fields so only valid fields get parsed)
8756
			foreach ( $fields as $field_name => $field_data ) {
8757
				if ( ! isset( $data_row[ $field_name ] ) && ! isset( $data_row[ $field_data['label'] ] ) ) {
8758
					continue;
8759
				}
8760
8761
				$field_id    = $field_data['id'];
8762
				$type        = $field_data['type'];
8763
				$pick_object = isset( $field_data['pick_object'] ) ? $field_data['pick_object'] : '';
8764
				$pick_val    = isset( $field_data['pick_val'] ) ? $field_data['pick_val'] : '';
8765
8766
				if ( isset( $data_row[ $field_name ] ) ) {
8767
					$field_value = $data_row[ $field_name ];
8768
				} else {
8769
					$field_value = $data_row[ $field_data['label'] ];
8770
				}
8771
8772
				if ( null !== $field_value && false !== $field_value && '' !== $field_value ) {
8773
					if ( 'pick' === $type || in_array( $type, PodsForm::file_field_types() ) ) {
8774
						$field_values = is_array( $field_value ) ? $field_value : array( $field_value );
8775
						$pick_values  = array();
8776
8777
						foreach ( $field_values as $pick_value ) {
8778
							if ( in_array( $type, PodsForm::file_field_types() ) || 'media' === $pick_object ) {
8779
								$where = "`guid` = '" . pods_sanitize( $pick_value ) . "'";
8780
8781
								if ( 0 < pods_absint( $pick_value ) && false !== $numeric_mode ) {
8782
									$where = "`ID` = " . pods_absint( $pick_value );
8783
								}
8784
8785
								$result = pods_query( "SELECT `ID` AS `id` FROM `{$wpdb->posts}` WHERE `post_type` = 'attachment' AND {$where} ORDER BY `ID`", $this );
8786
8787
								if ( ! empty( $result ) ) {
8788
									$pick_values[] = $result[0]->id;
8789
								}
8790
							} elseif ( 'pick' === $type ) {
8791
								// @todo This could and should be abstracted better and simplified
8792
								$related_pod = false;
8793
8794
								if ( 'pod' === $pick_object ) {
8795
									$related_pod = $this->load_pod( array(
8796
										'name'       => $pick_val,
8797
										'table_info' => true
8798
									), false );
8799
								}
8800
8801
								if ( empty( $related_pod ) ) {
8802
									$related_pod = array(
8803
										'id'   => 0,
8804
										'type' => $pick_object
8805
									);
8806
								}
8807
8808
								if ( in_array( 'taxonomy', array( $pick_object, $related_pod['type'] ) ) ) {
8809
									$where = "`t`.`name` = '" . pods_sanitize( $pick_value ) . "'";
8810
8811
									if ( 0 < pods_absint( $pick_value ) && false !== $numeric_mode ) {
8812
										$where = "`tt`.`term_id` = " . pods_absint( $pick_value );
8813
									}
8814
8815
									$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 );
8816
8817
									if ( ! empty( $result ) ) {
8818
										$pick_values[] = $result[0]->id;
8819
									}
8820
								} elseif ( in_array( 'post_type', array(
8821
										$pick_object,
8822
										$related_pod['type']
8823
									) ) || in_array( 'media', array( $pick_object, $related_pod['type'] ) ) ) {
8824
									$where = "`post_title` = '" . pods_sanitize( $pick_value ) . "'";
8825
8826
									if ( 0 < pods_absint( $pick_value ) && false !== $numeric_mode ) {
8827
										$where = "`ID` = " . pods_absint( $pick_value );
8828
									}
8829
8830
									$result = pods_query( "SELECT `ID` AS `id` FROM `{$wpdb->posts}` WHERE `post_type` = '{$pick_val}' AND {$where} ORDER BY `ID` LIMIT 1", $this );
8831
8832
									if ( ! empty( $result ) ) {
8833
										$pick_values[] = $result[0]->id;
8834
									}
8835
								} elseif ( in_array( 'user', array( $pick_object, $related_pod['type'] ) ) ) {
8836
									$where = "`user_login` = '" . pods_sanitize( $pick_value ) . "'";
8837
8838
									if ( 0 < pods_absint( $pick_value ) && false !== $numeric_mode ) {
8839
										$where = "`ID` = " . pods_absint( $pick_value );
8840
									}
8841
8842
									$result = pods_query( "SELECT `ID` AS `id` FROM `{$wpdb->users}` WHERE {$where} ORDER BY `ID` LIMIT 1", $this );
8843
8844
									if ( ! empty( $result ) ) {
8845
										$pick_values[] = $result[0]->id;
8846
									}
8847
								} elseif ( in_array( 'comment', array( $pick_object, $related_pod['type'] ) ) ) {
8848
									$where = "`comment_ID` = " . pods_absint( $pick_value );
8849
8850
									$result = pods_query( "SELECT `comment_ID` AS `id` FROM `{$wpdb->comments}` WHERE {$where} ORDER BY `ID` LIMIT 1", $this );
8851
8852
									if ( ! empty( $result ) ) {
8853
										$pick_values[] = $result[0]->id;
8854
									}
8855
								} elseif ( in_array( $pick_object, $simple_tableless_objects ) ) {
8856
									$pick_values[] = $pick_value;
8857
								} elseif ( ! empty( $related_pod['id'] ) ) {
8858
									$where = "`" . $related_pod['field_index'] . "` = '" . pods_sanitize( $pick_value ) . "'";
8859
8860
									if ( 0 < pods_absint( $pick_value ) && false !== $numeric_mode ) {
8861
										$where = "`" . $related_pod['field_id'] . "` = " . pods_absint( $pick_value );
8862
									}
8863
8864
									$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 );
8865
8866
									if ( ! empty( $result ) ) {
8867
										$pick_values[] = $result[0]->id;
8868
									}
8869
								}
8870
							}
8871
						}
8872
8873
						$field_value = implode( ',', $pick_values );
8874
					}
8875
8876
					$data[ $field_name ] = $field_value;
8877
				}
8878
			}
8879
8880
			if ( ! empty( $data ) ) {
8881
				$params = array(
8882
					'pod'  => $this->pod,
8883
					'data' => $data
8884
				);
8885
8886
				$ids[] = $this->save_pod_item( $params );
8887
			}
8888
		}
8889
8890
		return $ids;
8891
	}
8892
8893
	/**
8894
	 * Export data from a Pod
8895
	 *
8896
	 * @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...
8897
	 * @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...
8898
	 *
8899
	 * @return array Data arrays of all exported pod items
8900
	 * @since 1.7.1
8901
	 */
8902
	public function export( $pod = null, $params = null ) {
8903
8904
		if ( empty( $pod ) ) {
8905
			$pod = $this->pod;
8906
		}
8907
8908
		$find = array(
8909
			'limit'      => - 1,
8910
			'search'     => false,
8911
			'pagination' => false
8912
		);
8913
8914
		if ( ! empty( $params ) && isset( $params['params'] ) ) {
8915
			$find = array_merge( $find, (array) $params['params'] );
8916
8917
			unset( $params['params'] );
8918
8919
			$pod = pods( $pod, $find );
8920
		} elseif ( ! is_object( $pod ) ) {
8921
			$pod = pods( $pod, $find );
8922
		}
8923
8924
		$data = array();
8925
8926
		while ( $pod->fetch() ) {
8927
			$data[ $pod->id() ] = $this->export_pod_item( $params, $pod );
8928
		}
8929
8930
		$data = $this->do_hook( 'export', $data, $pod->pod, $pod );
8931
8932
		return $data;
8933
	}
8934
8935
	/**
8936
	 * Convert CSV to a PHP array
8937
	 *
8938
	 * @param string $data The CSV input
8939
	 *
8940
	 * @return array
8941
	 * @since      1.7.1
8942
	 *
8943
	 * @deprecated 2.3.5
8944
	 */
8945
	public function csv_to_php( $data, $delimiter = ',' ) {
8946
8947
		pods_deprecated( "PodsAPI->csv_to_php", '2.3.5' );
8948
8949
		$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...
8950
8951
		return $data['items'];
8952
	}
8953
8954
	/**
8955
	 * Clear Pod-related cache
8956
	 *
8957
	 * @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...
8958
	 *
8959
	 * @return void
8960
	 *
8961
	 * @since 2.0
8962
	 */
8963
	public function cache_flush_pods( $pod = null ) {
8964
8965
		/**
8966
		 * @var $wpdb wpdb
8967
		 */
8968
		global $wpdb;
8969
8970
		pods_transient_clear( 'pods' );
8971
		pods_transient_clear( 'pods_components' );
8972
8973
		if ( null !== $pod && is_array( $pod ) ) {
8974
			pods_transient_clear( 'pods_pod_' . $pod['name'] );
8975
			pods_cache_clear( $pod['name'], 'pods-class' );
8976
8977
			foreach ( $pod['fields'] as $field ) {
8978
				pods_transient_clear( 'pods_field_' . $pod['name'] . '_' . $field['name'] );
8979
			}
8980
8981
			if ( in_array( $pod['type'], array( 'post_type', 'taxonomy' ) ) ) {
8982
				pods_transient_clear( 'pods_wp_cpt_ct' );
8983
			}
8984
		} else {
8985
			pods_transient_clear( 'pods_wp_cpt_ct' );
8986
		}
8987
8988
		// Delete transients in the database
8989
		$wpdb->query( "DELETE FROM `{$wpdb->options}` WHERE `option_name` LIKE '_transient_pods%'" );
8990
		$wpdb->query( "DELETE FROM `{$wpdb->options}` WHERE `option_name` LIKE '_transient_timeout_pods%'" );
8991
8992
		// Delete Pods Options Cache in the database
8993
		$wpdb->query( "DELETE FROM `{$wpdb->options}` WHERE `option_name` LIKE '_pods_option_%'" );
8994
8995
		pods_cache_clear( true );
8996
8997
		pods_transient_set( 'pods_flush_rewrites', 1 );
8998
8999
		do_action( 'pods_cache_flushed' );
9000
	}
9001
9002
	/**
9003
	 * Process a Pod-based form
9004
	 *
9005
	 * @param mixed  $params
9006
	 * @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...
9007
	 * @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...
9008
	 * @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...
9009
	 *
9010
	 * @return mixed
9011
	 *
9012
	 * @since 2.0
9013
	 */
9014
	public function process_form( $params, $obj = null, $fields = null, $thank_you = null ) {
9015
9016
		$this->display_errors = false;
9017
9018
		$form = null;
9019
9020
		$nonce    = pods_var( '_pods_nonce', $params );
9021
		$pod      = pods_var( '_pods_pod', $params );
9022
		$id       = pods_var( '_pods_id', $params );
9023
		$uri      = pods_var( '_pods_uri', $params );
9024
		$form     = pods_var( '_pods_form', $params );
9025
		$location = pods_var( '_pods_location', $params );
9026
9027
		if ( is_object( $obj ) ) {
9028
			$pod = $obj->pod;
9029
			$id  = $obj->id();
9030
		}
9031
9032
		if ( ! empty( $fields ) ) {
9033
			$fields = array_keys( $fields );
9034
			$form   = implode( ',', $fields );
9035
		} else {
9036
			$fields = explode( ',', $form );
9037
		}
9038
9039
		if ( empty( $nonce ) || empty( $pod ) || empty( $uri ) || empty( $fields ) ) {
9040
			return pods_error( __( 'Invalid submission', 'pods' ), $this );
9041
		}
9042
9043
		$uid = @session_id();
9044
9045
		if ( is_user_logged_in() ) {
9046
			$uid = 'user_' . get_current_user_id();
9047
		}
9048
9049
		$field_hash = wp_create_nonce( 'pods_fields_' . $form );
9050
9051
		$action = 'pods_form_' . $pod . '_' . $uid . '_' . $id . '_' . $uri . '_' . $field_hash;
9052
9053
		if ( empty( $uid ) ) {
9054
			return pods_error( __( 'Access denied for your session, please refresh and try again.', 'pods' ), $this );
9055
		}
9056
9057
		if ( false === wp_verify_nonce( $nonce, $action ) ) {
9058
			return pods_error( __( 'Access denied, please refresh and try again.', 'pods' ), $this );
9059
		}
9060
9061
		$data = array();
9062
9063
		foreach ( $fields as $field ) {
9064
			$data[ $field ] = pods_var_raw( 'pods_field_' . $field, $params, '' );
9065
		}
9066
9067
		$params = array(
9068
			'pod'      => $pod,
9069
			'id'       => $id,
9070
			'data'     => $data,
9071
			'from'     => 'process_form',
9072
			'location' => $location
9073
		);
9074
9075
		$id = $this->save_pod_item( $params );
9076
9077
		/**
9078
		 * Fires after the form has been processed and save_pod_item has run.
9079
		 *
9080
		 * @param int       $id     Item ID.
9081
		 * @param array     $params save_pod_item parameters.
9082
		 * @param null|Pods $obj    Pod object (if set).
9083
		 */
9084
		do_action( 'pods_api_processed_form', $id, $params, $obj );
9085
9086
		// Always return $id for AJAX requests.
9087
		if ( ! defined( 'DOING_AJAX' ) || ! DOING_AJAX ) {
9088
			if ( 0 < $id && ! empty( $thank_you ) ) {
9089
				$thank_you = str_replace( 'X_ID_X', $id, $thank_you );
9090
9091
				pods_redirect( $thank_you, 302, false );
9092
			}
9093
		}
9094
9095
		return $id;
9096
	}
9097
9098
	/**
9099
	 * Handle filters / actions for the class
9100
	 *
9101
	 * @since 2.0
9102
	 */
9103
	private function do_hook() {
9104
9105
		$args = func_get_args();
9106
		if ( empty( $args ) ) {
9107
			return false;
9108
		}
9109
		$name = array_shift( $args );
9110
9111
		return pods_do_hook( "api", $name, $args, $this );
9112
	}
9113
9114
	/**
9115
	 * Handle variables that have been deprecated
9116
	 *
9117
	 * @since 2.0
9118
	 */
9119
	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...
9120
9121
		$name = (string) $name;
9122
9123
		if ( ! isset( $this->deprecated ) ) {
9124
			require_once( PODS_DIR . 'deprecated/classes/PodsAPI.php' );
9125
			$this->deprecated = new PodsAPI_Deprecated( $this );
9126
		}
9127
9128
		$var = null;
9129
9130
		if ( isset( $this->deprecated->{$name} ) ) {
9131
			pods_deprecated( "PodsAPI->{$name}", '2.0' );
9132
9133
			$var = $this->deprecated->{$name};
9134
		} else {
9135
			pods_deprecated( "PodsAPI->{$name}", '2.0' );
9136
		}
9137
9138
		return $var;
9139
	}
9140
9141
	/**
9142
	 * Handle methods that have been deprecated
9143
	 *
9144
	 * @since 2.0
9145
	 */
9146
	public function __call( $name, $args ) {
9147
9148
		$name = (string) $name;
9149
9150
		if ( ! isset( $this->deprecated ) ) {
9151
			require_once( PODS_DIR . 'deprecated/classes/PodsAPI.php' );
9152
			$this->deprecated = new PodsAPI_Deprecated( $this );
9153
		}
9154
9155
		if ( method_exists( $this->deprecated, $name ) ) {
9156
			return call_user_func_array( array( $this->deprecated, $name ), $args );
9157
		} else {
9158
			pods_deprecated( "PodsAPI::{$name}", '2.0' );
9159
		}
9160
	}
9161
9162
	/**
9163
	 * Filter an array of arrays without causing PHP notices/warnings.
9164
	 *
9165
	 * @param array $values
9166
	 *
9167
	 * @return array
9168
	 *
9169
	 * @since 2.6.10
9170
	 */
9171
	private function array_filter_walker( $values = array() ) {
9172
9173
		$values = (array) $values;
9174
9175
		foreach ( $values as $k => $v ) {
9176
			if ( is_object( $v ) ) {
9177
				// Skip objects
9178
				continue;
9179
			} elseif ( is_array( $v ) ) {
9180
				if ( empty( $v ) ) {
9181
					// Filter values with empty arrays
9182
					unset( $values[ $k ] );
9183
				}
9184
			} else {
9185
				if ( ! $v ) {
9186
					// Filter empty values
9187
					unset( $values[ $k ] );
9188
				}
9189
			}
9190
		}
9191
9192
		return $values;
9193
9194
	}
9195
9196
}
9197