0, 'key' => '', 'title' => '', 'fields' => array(), 'location' => array(), 'menu_order' => 0, 'position' => 'normal', 'style' => 'default', 'label_placement' => 'top', 'instruction_placement' => 'label', 'hide_on_screen' => array(), 'active' => true, 'description' => '', 'show_in_rest' => false, 'display_title' => '', 'allow_ai_access' => false, 'ai_description' => '', ); } /** * Get an ACF CPT object as an array. * * @since 6.1 * * @param integer|WP_Post $id The post ID being queried. * @return array|boolean The main ACF array for the post, or false on failure. */ public function get_post( $id = 0 ) { // Allow WP_Post to be passed. if ( is_object( $id ) ) { $id = $id->ID; } // Check store. $store = acf_get_store( $this->store ); if ( $store->has( $id ) ) { return $store->get( $id ); } if ( acf_is_local_field_group( $id ) ) { $post = acf_get_local_field_group( $id ); } else { $post = $this->get_raw_post( $id ); } // Bail early if no post. if ( ! $post ) { return false; } $post = $this->validate_post( $post ); /** * Filters the post array after it has been loaded. * * @date 12/02/2014 * @since 5.0.0 * * @param array $post The post array. */ $post = apply_filters( "acf/load_{$this->hook_name}", $post ); // Store field group using aliasses to also find via key, ID and name. $store->set( $post['key'], $post ); $store->alias( $post['key'], $post['ID'] ); return $post; } /** * Filter the posts returned by $this->get_posts(). * * @since 6.1 * * @param array $posts An array of posts to filter. * @param array $args An array of args to filter by. * @return array */ public function filter_posts( $posts, $args = array() ) { // Loop over field groups and check visibility. $filtered = array(); if ( $posts ) { foreach ( $posts as $post ) { if ( acf_get_field_group_visibility( $post, $args ) ) { $filtered[] = $post; } } } return $filtered; } /** * Filters the field group data before it is updated in the database. * * @since 6.1 * * @param array $field_group The field group being updated. * @return array */ public function pre_update_field_group( $field_group ) { // Remove empty values and convert to associated array. if ( $field_group['location'] ) { $field_group['location'] = array_filter( $field_group['location'] ); $field_group['location'] = array_values( $field_group['location'] ); $field_group['location'] = array_map( 'array_filter', $field_group['location'] ); $field_group['location'] = array_map( 'array_values', $field_group['location'] ); } return $field_group; } /** * Deletes an ACF field group and related fields. * * @since 6.1 * * @param integer|string $id The ID of the field group to delete. * @return boolean */ public function delete_post( $id = 0 ) { // Disable filters to ensure ACF loads data from DB. acf_disable_filters(); // Get the post. $post = $this->get_post( $id ); // Bail early if post was not found. if ( ! $post || ! $post['ID'] ) { return false; } // Delete the fields. $fields = acf_get_fields( $post ); if ( $fields ) { foreach ( $fields as $field ) { acf_delete_field( $field['ID'] ); } } // Delete post and flush cache. wp_delete_post( $post['ID'], true ); $this->flush_post_cache( $post ); /** * Fires immediately after an ACF post has been deleted. * * @date 12/02/2014 * @since 5.0.0 * * @param array $post The ACF post array. */ do_action( "acf/delete_{$this->hook_name}", $post ); return true; } /** * Trashes an ACF field group and related fields. * * @since 6.1 * * @param integer|string $id The ID of the field group to trash. * @return boolean */ public function trash_post( $id = 0 ) { // Disable filters to ensure ACF loads data from DB. acf_disable_filters(); $post = $this->get_post( $id ); if ( ! $post || ! $post['ID'] ) { return false; } // Trash fields. $fields = acf_get_fields( $post ); if ( $fields ) { foreach ( $fields as $field ) { acf_trash_field( $field['ID'] ); } } wp_trash_post( $post['ID'] ); $this->flush_post_cache( $post ); /** * Fires immediately after a field_group has been trashed. * * @date 12/02/2014 * @since 5.0.0 * * @param array $post The ACF post array. */ do_action( "acf/trash_{$this->hook_name}", $post ); return true; } /** * Restores an ACF field group and related fields from the trash. * * @since 6.1 * * @param integer|string $id The ID of the ACF post to untrash. * @return boolean */ public function untrash_post( $id = 0 ) { // Disable filters to ensure ACF loads data from DB. acf_disable_filters(); $post = $this->get_post( $id ); if ( ! $post || ! $post['ID'] ) { return false; } $fields = acf_get_fields( $post ); if ( $fields ) { foreach ( $fields as $field ) { acf_untrash_field( $field['ID'] ); } } wp_untrash_post( $post['ID'] ); $this->flush_post_cache( $post ); /** * Fires immediately after an ACF post has been untrashed. * * @date 12/02/2014 * @since 5.0.0 * * @param array $post The ACF post array. */ do_action( "acf/untrash_{$this->hook_name}", $post ); return true; } /** * Duplicates an ACF post. * * @since 6.1 * * @param integer|string $id The ID of the post to duplicate. * @param integer $new_post_id Optional post ID to override. * @return array The new ACF post array. */ public function duplicate_post( $id = 0, $new_post_id = 0 ) { // Disable filters to ensure ACF loads data from DB. acf_disable_filters(); $post = $this->get_post( $id ); if ( ! $post || ! $post['ID'] ) { return false; } // Get fields before updating field group attributes. $fields = acf_get_fields( $post['ID'] ); // Update attributes. $post['ID'] = $new_post_id; $post['key'] = uniqid( 'group_' ); // Add (copy) to title when appropriate. if ( ! $new_post_id ) { $post['title'] .= ' (' . __( 'copy', 'acf' ) . ')'; } // When duplicating a field group, insert a temporary post and set the field group's ID. // This allows fields to be updated before the field group (field group ID is needed for field parent setting). if ( ! $post['ID'] ) { $post['ID'] = wp_insert_post( array( 'post_title' => $post['key'], 'post_type' => $this->post_type, ) ); } // Duplicate fields and update post. acf_duplicate_fields( $fields, $post['ID'] ); $post = $this->update_post( $post ); /** * Fires immediately after an ACF post has been duplicated. * * @date 12/02/2014 * @since 5.0.0 * * @param array $post The ACF post array. */ do_action( "acf/duplicate_{$this->hook_name}", $post ); return $post; } /** * Returns a modified ACF field group array ready for export. * * @since 6.1 * * @param array $post The ACF post array. * @return array */ public function prepare_post_for_export( $post = array() ) { // Remove args. acf_extract_vars( $post, array( 'ID', 'local', '_valid' ) ); // Prepare fields. $post['fields'] = acf_prepare_fields_for_export( $post['fields'] ); /** * Filters the ACF post array before being returned to the export tool. * * @date 12/02/2014 * @since 5.0.0 * * @param array $post The ACF post array. */ return apply_filters( "acf/prepare_{$this->hook_name}_for_export", $post ); } /** * Prepares an ACF field group for import. * * @since 6.1 * * @param array $post The ACF field group array. * @return array */ public function prepare_post_for_import( $post ) { // Update parent and menu_order properties for all fields. if ( ! empty( $post['fields'] ) ) { foreach ( $post['fields'] as $i => &$field ) { $field['parent'] = $post['key']; $field['menu_order'] = $i; } } /** * Filters the ACF post array before being returned to the import tool. * * @date 21/11/19 * @since 5.8.8 * * @param array $post The ACF post array. */ return apply_filters( "acf/prepare_{$this->hook_name}_for_import", $post ); } /** * Returns a string that can be used to create a field group with PHP. * * @since 6.1 * * @param array $post The main field group array. * @return string */ public function export_post_as_php( $post = array() ) { $return = ''; if ( empty( $post ) ) { return $return; } $code = var_export( $post, true ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions -- Used for PHP export. if ( ! $code ) { return $return; } $code = $this->format_code_for_export( $code ); $return .= "acf_add_local_field_group( {$code} );\r\n"; return esc_textarea( $return ); } /** * Imports an ACF post into the database. * * @since 6.1 * * @param array $post The ACF post array. * @return array */ public function import_post( $post ) { // Disable filters to ensure data is not modified by local, clone, etc. $filters = acf_disable_filters(); // Validate the post (ensures all settings exist). $post = $this->get_valid_post( $post ); // Prepare post for import (modifies settings). $post = $this->prepare_post_for_import( $post ); // Prepare fields for import (modifies settings). $fields = acf_prepare_fields_for_import( $post['fields'] ); // Stores a map of field "key" => "ID". $ids = array(); // If the field group has an ID, review and delete stale fields in the database. if ( $post['ID'] ) { // Load database fields. $db_fields = acf_prepare_fields_for_import( acf_get_fields( $post ) ); // Generate map of "index" => "key" data. $keys = wp_list_pluck( $fields, 'key' ); // Loop over db fields and delete those who don't exist in $new_fields. foreach ( $db_fields as $field ) { // Add field data to $ids map. $ids[ $field['key'] ] = $field['ID']; // Delete field if not in $keys. if ( ! in_array( $field['key'], $keys, true ) ) { acf_delete_field( $field['ID'] ); } } } // When importing a new field group, insert a temporary post and set the field group's ID. // This allows fields to be updated before the field group (field group ID is needed for field parent setting). if ( ! $post['ID'] ) { $post['ID'] = wp_insert_post( array( 'post_title' => $post['key'], 'post_type' => $this->post_type, ) ); } // Add field group data to $ids map. $ids[ $post['key'] ] = $post['ID']; // Loop over and add fields. if ( $fields ) { foreach ( $fields as $field ) { // Replace any "key" references with "ID". if ( isset( $ids[ $field['key'] ] ) ) { $field['ID'] = $ids[ $field['key'] ]; } if ( isset( $ids[ $field['parent'] ] ) ) { $field['parent'] = $ids[ $field['parent'] ]; } // Save field. $field = acf_update_field( $field ); // Add field data to $ids map for children. $ids[ $field['key'] ] = $field['ID']; } } // Save field group. $post = $this->update_post( $post ); // Enable filters again. acf_enable_filters( $filters ); /** * Fires immediately after an ACF post has been imported. * * @date 12/02/2014 * @since 5.0.0 * * @param array $post The ACF post array. */ do_action( "acf/import_{$this->hook_name}", $post ); return $post; } } } acf_new_instance( 'ACF_Field_Group' );