Skip to content

Commit

Permalink
Merge pull request #4075 from 10up/feature/issue-3763
Browse files Browse the repository at this point in the history
ACF Repeater feature
  • Loading branch information
felipeelia authored Feb 11, 2025
2 parents 35f1037 + b5f9e0a commit 52ab6c7
Show file tree
Hide file tree
Showing 14 changed files with 1,028 additions and 12 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/cypress-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ jobs:
run: npm run build

- name: Set up database
run: npm run cypress:setup -- --wp-version=${{ matrix.core.version }} --wc-version=${{ matrix.core.wcVersion }}
run: npm run cypress:setup -- --wp-version=${{ matrix.core.version }} --wc-version=${{ matrix.core.wcVersion }} --acf-pro-license='${{ secrets.ACF_PRO_LICENSE_KEY }}'

- name: Test
run: npm run cypress:run -- --env grepTags=${{ matrix.testGroup }}
Expand Down Expand Up @@ -166,7 +166,7 @@ jobs:
run: npm run build

- name: Set up database
run: npm run cypress:setup -- --ep-host=${{ secrets.EPIO_HOST }} --es-shield='${{ secrets.EPIO_SHIELD }}' --ep-index-prefix=${{ secrets.EPIO_INDEX_PREFIX }} --wp-version=${{ matrix.core.version }} --wc-version=${{ matrix.core.wcVersion }}
run: npm run cypress:setup -- --ep-host=${{ secrets.EPIO_HOST }} --es-shield='${{ secrets.EPIO_SHIELD }}' --ep-index-prefix=${{ secrets.EPIO_INDEX_PREFIX }} --wp-version=${{ matrix.core.version }} --wc-version=${{ matrix.core.wcVersion }} --acf-pro-license='${{ secrets.ACF_PRO_LICENSE_KEY }}'

- name: Test
run: npm run cypress:run -- --env grepTags=${{ matrix.testGroup }}
Expand Down
1 change: 1 addition & 0 deletions .wp-env.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
],
"mappings": {
".htaccess": "./tests/cypress/wordpress-files/.htaccess",
"wp-content/composer.json": "./tests/cypress/wordpress-files/composer.json",
"wp-content/mu-plugins/disable-welcome-guide.php": "./tests/cypress/wordpress-files/test-mu-plugins/disable-welcome-guide.php",
"wp-content/mu-plugins/skip-wp-lookup.php": "./tests/cypress/wordpress-files/test-mu-plugins/skip-wp-lookup.php",
"wp-content/mu-plugins/unique-index-name.php": "./tests/cypress/wordpress-files/test-mu-plugins/unique-index-name.php",
Expand Down
33 changes: 23 additions & 10 deletions bin/setup-cypress-env.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,26 @@

# cat ./bin/2022-02-15-12-49.sql | ./bin/wp-env-cli tests-wordpress "wp --allow-root db import -"

ACF_PRO_LICENSE_KEY=""
DISPLAY_HELP=0
EP_HOST=""
ES_SHIELD=""
EP_INDEX_PREFIX=""
WP_VERSION=""
WC_VERSION=""
DISPLAY_HELP=0

for opt in "$@"; do
case $opt in
-h=*|--ep-host=*)
--acf-pro-license=*)
ACF_PRO_LICENSE_KEY="${opt#*=}"
;;
-H=*|--ep-host=*)
EP_HOST="${opt#*=}"
;;
-s=*|--es-shield=*)
-S=*|--es-shield=*)
ES_SHIELD="${opt#*=}"
;;
-u=*|--ep-index-prefix=*)
-p=*|--ep-index-prefix=*)
EP_INDEX_PREFIX="${opt#*=}"
;;
-wp=*|--wp-version=*)
Expand All @@ -39,12 +43,13 @@ if [ $DISPLAY_HELP -eq 1 ]; then
echo "Usage: ${0##*/} [OPTIONS...]"
echo
echo "Optional parameters:"
echo "-h=*, --ep-host=* The remote Elasticsearch Host URL."
echo "-s=*, --es-shield=* The Elasticsearch credentials, used in the ES_SHIELD constant."
echo "-u=*, --ep-index-prefix=* The Elasticsearch credentials, used in the EP_INDEX_PREFIX constant."
echo "-W=*, --wp-version=* WordPress Core version."
echo "-w=*, --wc-version=* WooCommerce version."
echo "-h|--help Display this help screen"
echo "--acf-pro-license=* ACF Pro License Key."
echo "-H=*, --ep-host=* The remote Elasticsearch Host URL."
echo "-S=*, --es-shield=* The Elasticsearch credentials, used in the ES_SHIELD constant."
echo "-p=*, --ep-index-prefix=* The Elasticsearch credentials, used in the EP_INDEX_PREFIX constant."
echo "-W=*, --wp-version=* WordPress Core version."
echo "-w=*, --wc-version=* WooCommerce version."
echo "-h|--help Display this help screen"
exit
fi

Expand Down Expand Up @@ -90,6 +95,14 @@ if [ ! -z $EP_INDEX_PREFIX ]; then
./bin/wp-env-cli tests-wordpress "wp --allow-root config set EP_INDEX_PREFIX ${EP_INDEX_PREFIX}"
fi

if [ ! -z $ACF_PRO_LICENSE_KEY ]; then
./bin/wp-env-cli tests-wordpress "composer --working-dir=./wp-content config http-basic.connect.advancedcustomfields.com ${ACF_PRO_LICENSE_KEY} https://elasticpress.test"
./bin/wp-env-cli tests-wordpress "composer --working-dir=./wp-content install"
./bin/wp-env-cli tests-wordpress "rm wp-content/auth.json"
./bin/wp-env-cli tests-wordpress "wp --allow-root plugin activate advanced-custom-fields-pro"
./bin/wp-env-cli tests-wordpress "wp --allow-root config set ACF_PRO_LICENSE ${ACF_PRO_LICENSE_KEY}"
fi

./bin/wp-env-cli tests-wordpress "wp --allow-root core multisite-convert"

SITES_COUNT=$(./bin/wp-env-cli tests-wordpress "wp --allow-root site list --format=count")
Expand Down
4 changes: 4 additions & 0 deletions elasticpress.php
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,10 @@ function register_indexable_posts() {
new Feature\Documents\Documents()
);

Features::factory()->register_feature(
new Feature\AcfRepeater\AcfRepeater()
);

Features::factory()->register_feature(
new Feature\Comments\Comments()
);
Expand Down
210 changes: 210 additions & 0 deletions includes/classes/Feature/AcfRepeater/AcfRepeater.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
<?php
/**
* ACF Repeater Field Compatibility feature
*
* @since 5.2.0
* @package elasticpress
*/

namespace ElasticPress\Feature\AcfRepeater;

use ElasticPress\Feature;
use ElasticPress\FeatureRequirementsStatus;
use ElasticPress\Utils;

if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}

/**
* ACF Repeater Field Compatibility feature class
*/
class AcfRepeater extends Feature {
/**
* List of ACF functions we use
*
* @var array
*/
protected $acf_functions = [
'acf_get_field_groups',
'acf_render_field_setting',
'acf_get_fields',
'acf_get_field',
'get_field',
];

/**
* Initialize feature setting it's config
*/
public function __construct() {
$this->slug = 'acf_repeater';

parent::__construct();
}

/**
* Sets i18n strings.
*/
public function set_i18n_strings(): void {
$this->title = esc_html__( 'ACF Repeater Field Compatibility', 'elasticpress' );

$this->short_title = esc_html__( 'ACF Repeater Field', 'elasticpress' );

$this->summary = '<p>' . __( 'Index your ACF Repeater fields as a JSON object and, optionally, make it searchable in the Search Fields & Weighting dashboard.', 'elasticpress' ) . '</p>';

$this->docs_url = __( 'https://www.elasticpress.io/documentation/article/acf-repeater-field-compatibility-feature/', 'elasticpress' );
}

/**
* Determine WC feature reqs status
*
* @return FeatureRequirementsStatus
*/
public function requirements_status() {
$status = new FeatureRequirementsStatus( 0 );

foreach ( $this->acf_functions as $function ) {
if ( ! function_exists( $function ) ) {
$status->code = 2;
$status->message = esc_html__( 'ACF Pro not installed.', 'elasticpress' );
break;
}
}

return $status;
}

/**
* Setup feature functionality
*/
public function setup() {
add_action( 'acf/render_field_settings', [ $this, 'render_field_settings' ] );
add_filter( 'ep_prepare_meta_allowed_protected_keys', [ $this, 'allow_meta_keys' ], 10, 2 );
add_filter( 'ep_prepare_meta_data', [ $this, 'add_meta_keys' ], 10, 2 );
}

/**
* Render field in the ACF group admin screen
*
* @param array $field ACF Field array.
* @return void
*/
public function render_field_settings( $field ): void {
// We only want repeaters and fields that are not children of repeaters.
if ( 'repeater' !== $field['type'] || ! empty( $field['parent_repeater'] ) ) {
return;
}

// Root level fields are children of the post object.
$post_parent = ! empty( $field['parent'] ) ? get_post( $field['parent'] ) : false;
if ( ! $post_parent ) {
return;
}

/**
* Filter whether EP should or not display the field setting in ACF
*
* @hook ep_acf_repeater_should_display_field_setting
* @since 5.3.0
* @param {bool} $should_display Whether should or not display the field setting in ACF
* @param {array} $field The ACF Field array
* @return {bool} New value of $should_display
*/
if ( ! apply_filters( 'ep_acf_repeater_should_display_field_setting', true, $field ) ) {
return;
}

$instructions = wp_kses_post(
sprintf(
/* translators: %s: post type name */
__( 'Index this field as a JSON object. If you want to make it searchable, do not forget to enable it under the related post types in the <a href="%1$s">Search Fields & Weighting dashboard</a>. To index existent content you can either manually save posts with this field or <a href="%2$s">run a sync</a>.', 'elasticpress' ),
esc_url( admin_url( 'admin.php?page=elasticpress-weighting' ) ),
Utils\get_sync_url()
)
);

\acf_render_field_setting(
$field,
[
'label' => esc_html__( 'Index in ElasticPress', 'elasticpress' ),
'instructions' => $instructions,
'name' => 'ep_acf_repeater_index_field',
'type' => 'true_false',
'ui' => 1,
]
);
}

/**
* Add to the weighting dashboard all the ACF Repeater fields that were checked to be indexed.
*
* @param array $meta List of allowed meta keys
* @param \WP_Post $post The post object.
* @return array
*/
public function allow_meta_keys( $meta, $post ) {
$field_groups = acf_get_field_groups(
array(
'post_id' => $post->ID,
'post_type' => $post->post_type,
)
);

if ( empty( $field_groups ) ) {
return $meta;
}

$ep_fields = [];

foreach ( $field_groups as $field_group ) {
$fields = acf_get_fields( $field_group );
foreach ( $fields as $field ) {
if ( empty( $field['ep_acf_repeater_index_field'] ) ) {
continue;
}

$ep_fields[] = $field['name'];
}
}

$meta = array_unique( array_merge( $meta, $ep_fields ) );

return $meta;
}

/**
* Add the ACF Repeater fields to the ES document meta data.
*
* @param array $meta All post meta data
* @param \WP_Post $post The post object
* @return array
*/
public function add_meta_keys( $meta, $post ) {
$meta_keys = array_keys( $meta );
foreach ( $meta_keys as $key ) {
$field = acf_get_field( $key );

if ( ! $field || empty( $field['ep_acf_repeater_index_field'] ) || 'repeater' !== $field['type'] ) {
continue;
}

$value_field = get_field( $key, $post->ID );
$value_encoded = wp_json_encode( $value_field );

/**
* Filter the ACF Repeater field value before it is indexed
*
* @hook ep_acf_repeater_meta_value
* @since 5.3.0
* @param {string} $value_encoded Repeater field value encoded
* @param {array} $value_field Original field value
* @param {string} $key The meta field key
* @param {WP_Post} $post The Post object
* @return {mixed} New value of $value_encoded
*/
$meta[ $key ] = apply_filters( 'ep_acf_repeater_meta_value', $value_encoded, $value_field, $key, $post );
}

return $meta;
}
}
Loading

0 comments on commit 52ab6c7

Please sign in to comment.