PluginProbe ʕ •ᴥ•ʔ
Secure Custom Fields / 6.4.2
Secure Custom Fields v6.4.2
6.9.2 6.9.1 6.9.0 6.8.9 6.8.7 6.8.8 6.8.6 6.8.4 6.8.5 trunk 6.4.0-beta1 6.4.0-beta2 6.4.1 6.4.1-beta3 6.4.1-beta4 6.4.1-beta5 6.4.1-beta6 6.4.1-beta7 6.4.2 6.5.0 6.5.1 6.5.2 6.5.3 6.5.4 6.5.5 6.5.6 6.5.7 6.6.0 6.7.0 6.7.1 6.8.0 6.8.1 6.8.2 6.8.3
secure-custom-fields / includes / post-types / class-acf-field-group.php
secure-custom-fields / includes / post-types Last commit date
class-acf-field-group.php 1 year ago class-acf-post-type.php 1 year ago class-acf-taxonomy.php 1 year ago class-acf-ui-options-page.php 1 year ago index.php 1 year ago
class-acf-field-group.php
547 lines
1 <?php
2
3 if ( ! defined( 'ABSPATH' ) ) {
4 exit; // Exit if accessed directly.
5 }
6
7 if ( ! class_exists( 'ACF_Field_Group' ) ) {
8 class ACF_Field_Group extends ACF_Internal_Post_Type {
9
10 /**
11 * The ACF internal post type name.
12 *
13 * @var string
14 */
15 public $post_type = 'acf-field-group';
16
17 /**
18 * The prefix for the key used in the main post array.
19 *
20 * @var string
21 */
22 public $post_key_prefix = 'group_';
23
24 /**
25 * The cache key for a singular post.
26 *
27 * @var string
28 */
29 public $cache_key = 'acf_get_field_group_post:key:';
30
31 /**
32 * The cache key for a collection of posts.
33 *
34 * @var string
35 */
36 public $cache_key_plural = 'acf_get_field_group_posts';
37
38 /**
39 * The hook name for a singular post.
40 *
41 * @var string
42 */
43 public $hook_name = 'field_group';
44
45 /**
46 * The hook name for a collection of posts.
47 *
48 * @var string
49 */
50 public $hook_name_plural = 'field_groups';
51
52 /**
53 * The name of the store used for the post type.
54 *
55 * @var string
56 */
57 public $store = 'field-groups';
58
59 /**
60 * Constructs the class.
61 */
62 public function __construct() {
63 // Include admin classes in admin.
64 if ( is_admin() ) {
65 acf_include( 'includes/admin/admin-internal-post-type-list.php' );
66 acf_include( 'includes/admin/admin-internal-post-type.php' );
67 acf_include( 'includes/admin/post-types/admin-field-group.php' );
68 acf_include( 'includes/admin/post-types/admin-field-groups.php' );
69 }
70
71 parent::__construct();
72 add_filter( 'acf/pre_update_field_group', array( $this, 'pre_update_field_group' ), 1 );
73 }
74
75 /**
76 * Gets the default settings array for an ACF field group.
77 *
78 * @return array
79 */
80 public function get_settings_array() {
81 return array(
82 'ID' => 0,
83 'key' => '',
84 'title' => '',
85 'fields' => array(),
86 'location' => array(),
87 'menu_order' => 0,
88 'position' => 'normal',
89 'style' => 'default',
90 'label_placement' => 'top',
91 'instruction_placement' => 'label',
92 'hide_on_screen' => array(),
93 'active' => true,
94 'description' => '',
95 'show_in_rest' => false,
96 );
97 }
98
99 /**
100 * Get an ACF CPT object as an array.
101 *
102 * @since ACF 6.1
103 *
104 * @param integer|WP_Post $id The post ID being queried.
105 * @return array|boolean The main ACF array for the post, or false on failure.
106 */
107 public function get_post( $id = 0 ) {
108 // Allow WP_Post to be passed.
109 if ( is_object( $id ) ) {
110 $id = $id->ID;
111 }
112
113 // Check store.
114 $store = acf_get_store( $this->store );
115 if ( $store->has( $id ) ) {
116 return $store->get( $id );
117 }
118
119 if ( acf_is_local_field_group( $id ) ) {
120 $post = acf_get_local_field_group( $id );
121 } else {
122 $post = $this->get_raw_post( $id );
123 }
124
125 // Bail early if no post.
126 if ( ! $post ) {
127 return false;
128 }
129
130 $post = $this->validate_post( $post );
131
132 /**
133 * Filters the post array after it has been loaded.
134 *
135 * @date 12/02/2014
136 * @since ACF 5.0.0
137 *
138 * @param array $post The post array.
139 */
140 $post = apply_filters( "acf/load_{$this->hook_name}", $post );
141
142 // Store field group using aliasses to also find via key, ID and name.
143 $store->set( $post['key'], $post );
144 $store->alias( $post['key'], $post['ID'] );
145
146 return $post;
147 }
148
149 /**
150 * Filter the posts returned by $this->get_posts().
151 *
152 * @since ACF 6.1
153 *
154 * @param array $posts An array of posts to filter.
155 * @param array $args An array of args to filter by.
156 * @return array
157 */
158 public function filter_posts( $posts, $args = array() ) {
159 // Loop over field groups and check visibility.
160 $filtered = array();
161 if ( $posts ) {
162 foreach ( $posts as $post ) {
163 if ( acf_get_field_group_visibility( $post, $args ) ) {
164 $filtered[] = $post;
165 }
166 }
167 }
168
169 return $filtered;
170 }
171
172 /**
173 * Filters the field group data before it is updated in the database.
174 *
175 * @since ACF 6.1
176 *
177 * @param array $field_group The field group being updated.
178 * @return array
179 */
180 public function pre_update_field_group( $field_group ) {
181 // Remove empty values and convert to associated array.
182 if ( $field_group['location'] ) {
183 $field_group['location'] = array_filter( $field_group['location'] );
184 $field_group['location'] = array_values( $field_group['location'] );
185 $field_group['location'] = array_map( 'array_filter', $field_group['location'] );
186 $field_group['location'] = array_map( 'array_values', $field_group['location'] );
187 }
188
189 return $field_group;
190 }
191
192 /**
193 * Deletes an ACF field group and related fields.
194 *
195 * @since ACF 6.1
196 *
197 * @param integer|string $id The ID of the field group to delete.
198 * @return boolean
199 */
200 public function delete_post( $id = 0 ) {
201 // Disable filters to ensure ACF loads data from DB.
202 acf_disable_filters();
203
204 // Get the post.
205 $post = $this->get_post( $id );
206
207 // Bail early if post was not found.
208 if ( ! $post || ! $post['ID'] ) {
209 return false;
210 }
211
212 // Delete the fields.
213 $fields = acf_get_fields( $post );
214 if ( $fields ) {
215 foreach ( $fields as $field ) {
216 acf_delete_field( $field['ID'] );
217 }
218 }
219
220 // Delete post and flush cache.
221 wp_delete_post( $post['ID'], true );
222 $this->flush_post_cache( $post );
223
224 /**
225 * Fires immediately after an ACF post has been deleted.
226 *
227 * @date 12/02/2014
228 * @since ACF 5.0.0
229 *
230 * @param array $post The ACF post array.
231 */
232 do_action( "acf/delete_{$this->hook_name}", $post );
233
234 return true;
235 }
236
237 /**
238 * Trashes an ACF field group and related fields.
239 *
240 * @since ACF 6.1
241 *
242 * @param integer|string $id The ID of the field group to trash.
243 * @return boolean
244 */
245 public function trash_post( $id = 0 ) {
246 // Disable filters to ensure ACF loads data from DB.
247 acf_disable_filters();
248
249 $post = $this->get_post( $id );
250 if ( ! $post || ! $post['ID'] ) {
251 return false;
252 }
253
254 // Trash fields.
255 $fields = acf_get_fields( $post );
256 if ( $fields ) {
257 foreach ( $fields as $field ) {
258 acf_trash_field( $field['ID'] );
259 }
260 }
261
262 wp_trash_post( $post['ID'] );
263 $this->flush_post_cache( $post );
264
265 /**
266 * Fires immediately after a field_group has been trashed.
267 *
268 * @date 12/02/2014
269 * @since ACF 5.0.0
270 *
271 * @param array $post The ACF post array.
272 */
273 do_action( "acf/trash_{$this->hook_name}", $post );
274
275 return true;
276 }
277
278 /**
279 * Restores an ACF field group and related fields from the trash.
280 *
281 * @since ACF 6.1
282 *
283 * @param integer|string $id The ID of the ACF post to untrash.
284 * @return boolean
285 */
286 public function untrash_post( $id = 0 ) {
287 // Disable filters to ensure ACF loads data from DB.
288 acf_disable_filters();
289
290 $post = $this->get_post( $id );
291 if ( ! $post || ! $post['ID'] ) {
292 return false;
293 }
294
295 $fields = acf_get_fields( $post );
296 if ( $fields ) {
297 foreach ( $fields as $field ) {
298 acf_untrash_field( $field['ID'] );
299 }
300 }
301
302 wp_untrash_post( $post['ID'] );
303 $this->flush_post_cache( $post );
304
305 /**
306 * Fires immediately after an ACF post has been untrashed.
307 *
308 * @date 12/02/2014
309 * @since ACF 5.0.0
310 *
311 * @param array $post The ACF post array.
312 */
313 do_action( "acf/untrash_{$this->hook_name}", $post );
314
315 return true;
316 }
317
318 /**
319 * Duplicates an ACF post.
320 *
321 * @since ACF 6.1
322 *
323 * @param integer|string $id The ID of the post to duplicate.
324 * @param integer $new_post_id Optional post ID to override.
325 * @return array The new ACF post array.
326 */
327 public function duplicate_post( $id = 0, $new_post_id = 0 ) {
328 // Disable filters to ensure ACF loads data from DB.
329 acf_disable_filters();
330
331 $post = $this->get_post( $id );
332 if ( ! $post || ! $post['ID'] ) {
333 return false;
334 }
335
336 // Get fields before updating field group attributes.
337 $fields = acf_get_fields( $post['ID'] );
338
339 // Update attributes.
340 $post['ID'] = $new_post_id;
341 $post['key'] = uniqid( 'group_' );
342
343 // Add (copy) to title when appropriate.
344 if ( ! $new_post_id ) {
345 $post['title'] .= ' (' . __( 'copy', 'secure-custom-fields' ) . ')';
346 }
347
348 // When importing a new field group, insert a temporary post and set the field group's ID.
349 // This allows fields to be updated before the field group (field group ID is needed for field parent setting).
350 if ( ! $post['ID'] ) {
351 $post['ID'] = wp_insert_post( array( 'post_title' => $post['key'] ) );
352 }
353
354 // Duplicate fields and update post.
355 acf_duplicate_fields( $fields, $post['ID'] );
356 $post = $this->update_post( $post );
357
358 /**
359 * Fires immediately after an ACF post has been duplicated.
360 *
361 * @date 12/02/2014
362 * @since ACF 5.0.0
363 *
364 * @param array $post The ACF post array.
365 */
366 do_action( "acf/duplicate_{$this->hook_name}", $post );
367
368 return $post;
369 }
370
371 /**
372 * Returns a modified ACF field group array ready for export.
373 *
374 * @since ACF 6.1
375 *
376 * @param array $post The ACF post array.
377 * @return array
378 */
379 public function prepare_post_for_export( $post = array() ) {
380 // Remove args.
381 acf_extract_vars( $post, array( 'ID', 'local', '_valid' ) );
382
383 // Prepare fields.
384 $post['fields'] = acf_prepare_fields_for_export( $post['fields'] );
385
386 /**
387 * Filters the ACF post array before being returned to the export tool.
388 *
389 * @date 12/02/2014
390 * @since ACF 5.0.0
391 *
392 * @param array $post The ACF post array.
393 */
394 return apply_filters( "acf/prepare_{$this->hook_name}_for_export", $post );
395 }
396
397 /**
398 * Prepares an ACF field group for import.
399 *
400 * @since ACF 6.1
401 *
402 * @param array $post The ACF field group array.
403 * @return array
404 */
405 public function prepare_post_for_import( $post ) {
406 // Update parent and menu_order properties for all fields.
407 if ( ! empty( $post['fields'] ) ) {
408 foreach ( $post['fields'] as $i => &$field ) {
409 $field['parent'] = $post['key'];
410 $field['menu_order'] = $i;
411 }
412 }
413
414 /**
415 * Filters the ACF post array before being returned to the import tool.
416 *
417 * @date 21/11/19
418 * @since ACF 5.8.8
419 *
420 * @param array $post The ACF post array.
421 */
422 return apply_filters( "acf/prepare_{$this->hook_name}_for_import", $post );
423 }
424
425 /**
426 * Returns a string that can be used to create a field group with PHP.
427 *
428 * @since ACF 6.1
429 *
430 * @param array $post The main field group array.
431 * @return string
432 */
433 public function export_post_as_php( $post = array() ) {
434 $return = '';
435 if ( empty( $post ) ) {
436 return $return;
437 }
438
439 $code = var_export( $post, true ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions -- Used for PHP export.
440 if ( ! $code ) {
441 return $return;
442 }
443
444 $code = $this->format_code_for_export( $code );
445 $return .= "acf_add_local_field_group( {$code} );\r\n";
446
447 return esc_textarea( $return );
448 }
449
450 /**
451 * Imports an ACF post into the database.
452 *
453 * @since ACF 6.1
454 *
455 * @param array $post The ACF post array.
456 * @return array
457 */
458 public function import_post( $post ) {
459 // Disable filters to ensure data is not modified by local, clone, etc.
460 $filters = acf_disable_filters();
461
462 // Validate the post (ensures all settings exist).
463 $post = $this->get_valid_post( $post );
464
465 // Prepare post for import (modifies settings).
466 $post = $this->prepare_post_for_import( $post );
467
468 // Prepare fields for import (modifies settings).
469 $fields = acf_prepare_fields_for_import( $post['fields'] );
470
471 // Stores a map of field "key" => "ID".
472 $ids = array();
473
474 // If the field group has an ID, review and delete stale fields in the database.
475 if ( $post['ID'] ) {
476
477 // Load database fields.
478 $db_fields = acf_prepare_fields_for_import( acf_get_fields( $post ) );
479
480 // Generate map of "index" => "key" data.
481 $keys = wp_list_pluck( $fields, 'key' );
482
483 // Loop over db fields and delete those who don't exist in $new_fields.
484 foreach ( $db_fields as $field ) {
485 // Add field data to $ids map.
486 $ids[ $field['key'] ] = $field['ID'];
487
488 // Delete field if not in $keys.
489 if ( ! in_array( $field['key'], $keys, true ) ) {
490 acf_delete_field( $field['ID'] );
491 }
492 }
493 }
494
495 // When importing a new field group, insert a temporary post and set the field group's ID.
496 // This allows fields to be updated before the field group (field group ID is needed for field parent setting).
497 if ( ! $post['ID'] ) {
498 $post['ID'] = wp_insert_post( array( 'post_title' => $post['key'] ) );
499 }
500
501 // Add field group data to $ids map.
502 $ids[ $post['key'] ] = $post['ID'];
503
504 // Loop over and add fields.
505 if ( $fields ) {
506 foreach ( $fields as $field ) {
507
508 // Replace any "key" references with "ID".
509 if ( isset( $ids[ $field['key'] ] ) ) {
510 $field['ID'] = $ids[ $field['key'] ];
511 }
512 if ( isset( $ids[ $field['parent'] ] ) ) {
513 $field['parent'] = $ids[ $field['parent'] ];
514 }
515
516 // Save field.
517 $field = acf_update_field( $field );
518
519 // Add field data to $ids map for children.
520 $ids[ $field['key'] ] = $field['ID'];
521 }
522 }
523
524 // Save field group.
525 $post = $this->update_post( $post );
526
527 // Enable filters again.
528 acf_enable_filters( $filters );
529
530 /**
531 * Fires immediately after an ACF post has been imported.
532 *
533 * @date 12/02/2014
534 * @since ACF 5.0.0
535 *
536 * @param array $post The ACF post array.
537 */
538 do_action( "acf/import_{$this->hook_name}", $post );
539
540 return $post;
541 }
542 }
543
544 }
545
546 acf_new_instance( 'ACF_Field_Group' );
547