Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[External content] Fixes, better docs, more filters, and unit tests #99

Merged
merged 1 commit into from
Feb 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 69 additions & 16 deletions includes/classes/Feature/ExternalContent.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ public function __construct() {

$this->title = esc_html__( 'External Content', 'elasticpress-labs' );

$this->summary = __(
'List meta keys containing a path or a URL, and ElasticPress will index the content of those path or URL. For example, for a meta key called <code>meta_key</code> with <code>https://wordpress.org/news/wp-json/wp/v2/posts/16837</code> as its value, the JSON returned by that REST API endpoint will be indexed in a meta key called <code>ep_external_content_meta_key</code>.',
'elasticpress-labs'
);

parent::__construct();
}

Expand All @@ -44,7 +49,7 @@ public function __construct() {
* @return void
*/
public function setup() {
add_filter( 'ep_prepare_meta_data', [ $this, 'append_external_content' ] );
add_filter( 'ep_prepare_meta_data', [ $this, 'append_external_content' ], 10, 2 );
add_filter( 'ep_prepare_meta_allowed_protected_keys', [ $this, 'allow_meta_keys' ], 10, 2 );

/**
Expand All @@ -71,10 +76,23 @@ public function requirements_status() {
* Set the `settings_schema` attribute
*/
public function set_settings_schema() {
$weighting_dashboard_url = ( ! defined( 'EP_IS_NETWORK' ) || ! EP_IS_NETWORK ) ?
admin_url( 'admin.php?page=elasticpress-weighting' ) :
admin_url( 'admin.php?page=elasticpress' );

$help_text = sprintf(
/* translators: Search Fields & Weighting Dashboard URL */
__(
'Add one field per line. Visit the <a href="%s">Search Fields & Weighting Dashboard</a> if you want to make their <code>ep_external_content_*</code> version searchable.',
'elasticpress-labs'
),
$weighting_dashboard_url
);

$this->settings_schema = [
[
'default' => '',
'help' => '<p>' . __( 'Add one field per line', 'elasticpress-labs' ) . '</p>',
'help' => '<p>' . $help_text . '</p>',
'key' => 'meta_fields',
'label' => __( 'Meta fields with external URLs', 'elasticpress-labs' ),
'type' => 'textarea',
Expand All @@ -85,15 +103,21 @@ public function set_settings_schema() {
/**
* Append external content to the document meta data
*
* @param array $post_meta Document's meta data
* @param array $post_meta Document's meta data
* @param \WP_Post|null $post Post object
* @return array
*/
public function append_external_content( $post_meta ) {
public function append_external_content( $post_meta, $post = null ) {
global $wp_filesystem;

require_once ABSPATH . '/wp-admin/includes/file.php';
WP_Filesystem();

$post_indexable = \ElasticPress\Indexables::factory()->get( 'post' );
$test_meta_value = method_exists( $post_indexable, 'get_test_meta_value' ) ?
$post_indexable->get_test_meta_value() :
'test-value';

$meta_keys = $this->get_meta_keys();
foreach ( $meta_keys as $meta_key ) {
if ( ! isset( $post_meta[ $meta_key ] ) ) {
Expand All @@ -103,6 +127,23 @@ public function append_external_content( $post_meta ) {
$meta_value = (array) $post_meta[ $meta_key ];
$meta_value = reset( $meta_value );

$should_skip = empty( $meta_value ) || $test_meta_value === $meta_value;

/**
* Filter if the meta value should be skipped
*
* @since 2.3.0
* @hook ep_external_content_should_skip
* @param {bool} $should_skip Whether the meta value should be skipped
* @param {mixed} $meta_value Meta value being analyzed
* @param {string} $meta_key Meta key being analyzed
* @param {WP_Post|null} $post Current post object
* @return {bool} Whether the meta value should be skipped
*/
if ( apply_filters( 'ep_external_content_should_skip', $should_skip, $meta_value, $meta_key, $post ) ) {
continue;
}

/**
* The field value can either be a simple string or a JSON array with a list of URLs.
*/
Expand Down Expand Up @@ -134,6 +175,10 @@ public function append_external_content( $post_meta ) {
*/
$request_url = apply_filters( 'ep_external_content_remote_request_url', $external_path_and_url );

if ( ! filter_var( $request_url, FILTER_VALIDATE_URL ) ) {
continue;
}

/**
* Filter the arguments of the remote request
*
Expand All @@ -149,7 +194,8 @@ public function append_external_content( $post_meta ) {
$post_meta,
$meta_key,
wp_remote_retrieve_body( $remote_get ),
$external_path_and_url
$external_path_and_url,
$remote_get
);
}

Expand Down Expand Up @@ -218,8 +264,13 @@ public function get_stored_meta_key( $meta_key ) {
* @return array
*/
public function allow_meta_keys( $meta_keys ) {
$external_meta_keys = $this->get_meta_keys();
if ( empty( $external_meta_keys ) ) {
return $meta_keys;
}

$stored_meta_keys = array_reduce(
$this->get_meta_keys(),
$external_meta_keys,
function ( $acc, $meta_key ) {
$acc[] = $this->get_stored_meta_key( $meta_key );
return $acc;
Expand Down Expand Up @@ -304,25 +355,27 @@ public function maybe_parse_js( $content, $path_or_url ) {
/**
* Add the content of external sources to the post meta array
*
* @param array $post_meta Array of all post meta
* @param string $meta_key Meta key
* @param string $content Contents of the external source
* @param string $path_or_url Path or URL of the external source
* @param array $post_meta Array of all post meta
* @param string $meta_key Meta key
* @param string $content Contents of the external source
* @param string $path_or_url Path or URL of the external source
* @param string $additional_data Additional data. Contains the HTTP response if the content was fetched remotely
* @return array
*/
protected function add_external_content_to_post_meta( $post_meta, $meta_key, $content, $path_or_url ) {
protected function add_external_content_to_post_meta( $post_meta, $meta_key, $content, $path_or_url, $additional_data = [] ) {
/**
* Filter the content.
*
* @since 2.3.0
* @hook ep_external_content_file_content
* @param {string} $content Content being processed
* @param {string} $path_or_url Path or URL
* @param {string} $meta_key The meta key that contains the path or URL
* @param {array} $post_meta Post meta
* @param {string} $content Content being processed
* @param {string} $path_or_url Path or URL
* @param {string} $meta_key The meta key that contains the path or URL
* @param {array} $post_meta Post meta
* @param {array} $additional_data Additional data. Contains the HTTP response if the content was fetched remotely
* @return {string} New $content
*/
$content = apply_filters( 'ep_external_content_file_content', $content, $path_or_url, $meta_key, $post_meta );
$content = apply_filters( 'ep_external_content_file_content', $content, $path_or_url, $meta_key, $post_meta, $additional_data );

if ( empty( $content ) ) {
return $post_meta;
Expand Down
80 changes: 58 additions & 22 deletions tests/phpunit/feature/TestExternalContent.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ class TestExternalContent extends \WP_UnitTestCase {
* Setup each test
*/
public function set_up() {
parent::set_up();

$instance = new ExternalContent();
Features::factory()->register_feature( $instance );
Features::factory()->activate_feature( 'external_content' );
Expand All @@ -36,14 +38,6 @@ public function set_up() {
add_filter( 'pre_http_request', [ $this, 'force_http_response' ] );
}

/**
* Clean up after each test
*/
public function tear_down() {
remove_filter( 'pre_option_ep_feature_settings', [ $this, 'set_settings' ] );
remove_filter( 'pre_http_request', [ $this, 'force_http_response' ] );
}

/**
* Get External Content feature
*
Expand Down Expand Up @@ -86,7 +80,7 @@ public function test_set_settings_schema() {

$expected_schema = [
'default' => '',
'help' => '<p>Add one field per line</p>',
'help' => '<p>Add one field per line. Visit the <a href="http://example.org/wp-admin/admin.php?page=elasticpress-weighting">Search Fields & Weighting Dashboard</a> if you want to make their <code>ep_external_content_*</code> version searchable.</p>',
'key' => 'meta_fields',
'label' => 'Meta fields with external URLs',
'type' => 'textarea',
Expand All @@ -111,11 +105,12 @@ public function test_append_external_content() {

$this->assertSame( $post_meta['ep_external_content_meta_key_1'], ' {"id":123,"content":"Lorem ipsum"}' );

$change_via_filter = function ( $content ) {
$change_via_filter = function ( $content, $path_or_url, $meta_key, $post_meta, $additional_data ) {
$this->assertSame( $content, '{"id":123,"content":"Lorem ipsum"}' );
$this->assertSame( $additional_data['code'], 123 );
return 'Something different';
};
add_filter( 'ep_external_content_file_content', $change_via_filter );
add_filter( 'ep_external_content_file_content', $change_via_filter, 10, 5 );

$post_meta = $this->get_feature()->append_external_content( $original_post_meta );
$this->assertSame( $post_meta['ep_external_content_meta_key_1'], ' Something different' );
Expand Down Expand Up @@ -173,10 +168,49 @@ public function test_append_external_content_remote_request_filters() {
];

$this->get_feature()->append_external_content( $original_post_meta );
}

remove_filter( 'ep_external_content_remote_request_url', $change_url );
remove_filter( 'ep_external_content_remote_request_args', $change_args );
remove_filter( 'pre_http_request', $check );
/**
* Test the ep_external_content_should_skip filter
*
* @group external-content
*/
public function test_ep_external_content_should_skip() {
$original_post_meta = [
'meta_key_1' => 'https://example.org/news/wp-json/wp/v2/posts/1',
];
$this->get_feature()->append_external_content( $original_post_meta );

$this->assertSame( 1, did_action( 'ep_external_content_item_processed' ) );

$skip_meta_value = function ( $should_skip, $meta_value, $meta_key, $post ) {
$this->assertFalse( $should_skip );
$this->assertSame( $meta_value, 'https://example.org/news/wp-json/wp/v2/posts/1' );
$this->assertSame( $meta_key, 'meta_key_1' );
$this->assertNull( $post );
return true;
};
add_filter( 'ep_external_content_should_skip', $skip_meta_value, 10, 4 );
$this->get_feature()->append_external_content( $original_post_meta );

$this->assertSame( 1, did_action( 'ep_external_content_item_processed' ) );
}

/**
* Test the append_external_content method with an invalid URL
*
* @group external-content
*/
public function test_append_external_content_invalid_url() {
$original_post_meta = [
'meta_key_1' => '/news/wp-json/wp/v2/posts/1',
];
$this->get_feature()->append_external_content( $original_post_meta );

$this->assertSame( 1, did_filter( 'ep_external_content_remote_request_url' ) );

// It will not even try to filter the args, as a request will not be sent.
$this->assertSame( 0, did_filter( 'ep_external_content_remote_request_args' ) );
}

/**
Expand All @@ -196,8 +230,6 @@ public function test_get_meta_keys() {
add_filter( 'ep_external_content_meta_keys', $change_via_filter );

$this->assertSame( $this->get_feature()->get_meta_keys(), array_merge( $expected, [ 'meta_key_3' ] ) );

remove_filter( 'ep_external_content_meta_keys', $change_via_filter );
}

/**
Expand All @@ -216,8 +248,6 @@ public function test_get_stored_meta_key() {
add_filter( 'ep_external_content_stored_meta_key', $change_via_filter, 10, 2 );

$this->assertSame( $this->get_feature()->get_stored_meta_key( 'meta_key' ), 'ep_external_content_meta_keychanged' );

remove_filter( 'ep_external_content_stored_meta_key', $change_via_filter );
}

/**
Expand All @@ -231,8 +261,13 @@ public function test_allow_meta_keys() {
'ep_external_content_meta_key_1',
'ep_external_content_meta_key_2',
];

$this->assertSame( $this->get_feature()->allow_meta_keys( [ 'some_other_key' ] ), $expected );

$expected = [
'ep_external_content_meta_key_1',
'ep_external_content_meta_key_2',
];
$this->assertSame( $this->get_feature()->allow_meta_keys( [] ), $expected );
}

/**
Expand Down Expand Up @@ -288,8 +323,6 @@ public function test_maybe_limit_size_filter_max_size() {
$post_meta = $this->get_feature()->append_external_content( $original_post_meta );

$this->assertStringNotContainsString( ' (trimmed)', $post_meta['ep_external_content_meta_key_1'] );

remove_filter( 'ep_external_content_max_size', $change_size );
}

/**
Expand Down Expand Up @@ -352,7 +385,10 @@ public function set_settings() {
* @return array
*/
public function force_http_response() {
return [ 'body' => $this->html_return ];
return [
'code' => 123,
'body' => $this->html_return,
];
}

/**
Expand Down
Loading