Issues (2873)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

classes/PodsAPI.php (106 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

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