Skip to content

Commit

Permalink
Merge pull request #2954 from 10up/feature/facet-meta-type
Browse files Browse the repository at this point in the history
Facet by Meta
  • Loading branch information
felipeelia authored Aug 26, 2022
2 parents ee38ce8 + 2b66636 commit fac9cf3
Show file tree
Hide file tree
Showing 15 changed files with 1,753 additions and 1 deletion.
1 change: 1 addition & 0 deletions .wp-env.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"https://downloads.wordpress.org/plugin/woocommerce.zip"
],
"mappings": {
"wp-content/mu-plugins/enable-meta-facet.php": "./tests/cypress/wordpress-files/test-mu-plugins/enable-meta-facet.php",
"wp-content/mu-plugins/unique-index-name.php": "./tests/cypress/wordpress-files/test-mu-plugins/unique-index-name.php",
"wp-content/plugins/cpt-and-custom-tax.php": "./tests/cypress/wordpress-files/test-plugins/cpt-and-custom-tax.php",
"wp-content/plugins/fake-new-activation.php": "./tests/cypress/wordpress-files/test-plugins/fake-new-activation.php",
Expand Down
34 changes: 34 additions & 0 deletions assets/js/blocks/facets/meta/block.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 2,
"title": "Facet by Meta (ElasticPress)",
"textdomain": "elasticpress",
"name": "elasticpress/facet-meta",
"icon": "feedback",
"category": "widgets",
"attributes": {
"searchPlaceholder": {
"type": "string",
"default": "Search"
},
"facet": {
"type": "string",
"default": ""
},
"orderby": {
"type" : "string",
"default": "count",
"enum" : [ "count", "name" ]
},
"order": {
"type": "string",
"default": "desc",
"enum": [ "desc", "asc" ]
}
},
"supports": {
"html": false
},
"editorScript": "file:/../../../../../dist/js/facets-meta-block-script.min.js",
"style": "file:/../../../../../dist/css/facets-styles.min.css"
}
105 changes: 105 additions & 0 deletions assets/js/blocks/facets/meta/edit.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import { InspectorControls, useBlockProps } from '@wordpress/block-editor';
import {
PanelBody,
RadioControl,
TextControl,
Spinner,
Placeholder,
SelectControl,
} from '@wordpress/components';
import { Fragment, useEffect, useState, useCallback } from '@wordpress/element';
import apiFetch from '@wordpress/api-fetch';
import { __ } from '@wordpress/i18n';

const FacetBlockEdit = (props) => {
const { attributes, setAttributes } = props;
const [metaKeys, setMetaKeys] = useState([]);
const [preview, setPreview] = useState('');
const [loading, setLoading] = useState(false);
const { searchPlaceholder, facet, orderby, order } = attributes;

const blockProps = useBlockProps();

const load = useCallback(async () => {
const metaKeys = await apiFetch({
path: '/elasticpress/v1/facets/meta/keys',
});
setMetaKeys(metaKeys);
}, [setMetaKeys]);

useEffect(load, [load]);

useEffect(() => {
setLoading(true);
const params = new URLSearchParams({
searchPlaceholder,
facet,
orderby,
order,
});
apiFetch({
path: `/elasticpress/v1/facets/meta/block-preview?${params}`,
})
.then((preview) => setPreview(preview))
.finally(() => setLoading(false));
}, [searchPlaceholder, facet, orderby, order]);

return (
<Fragment>
<InspectorControls>
<PanelBody title={__('Facet Settings', 'elasticpress')}>
<TextControl
label={__('Search Placeholder', 'elasticpress')}
value={searchPlaceholder}
onChange={(value) => setAttributes({ searchPlaceholder: value })}
/>
<SelectControl
label={__('Meta Field Key', 'elasticpress')}
help={__(
'This is the list of meta fields indexed in Elasticsearch. If you do not see your field here, you might need to sync your content.',
'elasticpress',
)}
value={facet}
options={[
...metaKeys.map((metaKey) => ({
label: metaKey,
value: metaKey,
})),
]}
onChange={(value) => setAttributes({ facet: value })}
/>
<RadioControl
label={__('Order By', 'elasticpress')}
help={__('The field used to order available options', 'elasticpress')}
selected={orderby}
options={[
{ label: __('Count', 'elasticpress'), value: 'count' },
{ label: __('Name', 'elasticpress'), value: 'name' },
]}
onChange={(value) => setAttributes({ orderby: value })}
/>
<RadioControl
label={__('Order', 'elasticpress')}
selected={order}
options={[
{ label: __('ASC', 'elasticpress'), value: 'asc' },
{ label: __('DESC', 'elasticpress'), value: 'desc' },
]}
onChange={(value) => setAttributes({ order: value })}
/>
</PanelBody>
</InspectorControls>

<div {...blockProps}>
{loading && (
<Placeholder>
<Spinner />
</Placeholder>
)}
{/* eslint-disable-next-line react/no-danger */}
{!loading && <div dangerouslySetInnerHTML={{ __html: preview }} />}
</div>
</Fragment>
);
};
export default FacetBlockEdit;
15 changes: 15 additions & 0 deletions assets/js/blocks/facets/meta/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/**
* WordPress dependencies.
*/
import { registerBlockType } from '@wordpress/blocks';

/**
* Internal dependencies.
*/
import edit from './edit';
import block from './block.json';

registerBlockType(block, {
edit,
save: () => {},
});
1 change: 1 addition & 0 deletions includes/classes/Elasticsearch.php
Original file line number Diff line number Diff line change
Expand Up @@ -466,6 +466,7 @@ public function query( $index, $type, $query, $query_args, $query_object = null
[
'found_documents' => $total_hits,
'documents' => $documents,
'aggregations' => $response['aggregations'] ?? [],
],
$response,
$query,
Expand Down
217 changes: 217 additions & 0 deletions includes/classes/Feature/Facets/Types/Meta/Block.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
<?php
/**
* Facets block
*
* @since 4.2.0
* @package elasticpress
*/

namespace ElasticPress\Feature\Facets\Types\Meta;

use ElasticPress\Features;

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

/**
* Facets block class
*/
class Block {
/**
* Hook block funcionality.
*/
public function setup() {
add_action( 'init', [ $this, 'register_block' ] );
add_action( 'rest_api_init', [ $this, 'setup_endpoints' ] );
}

/**
* Setup REST endpoints for the feature.
*/
public function setup_endpoints() {
register_rest_route(
'elasticpress/v1',
'facets/meta/keys',
[
'methods' => 'GET',
'permission_callback' => [ $this, 'check_facets_meta_rest_permission' ],
'callback' => [ $this, 'get_rest_registered_metakeys' ],
]
);
register_rest_route(
'elasticpress/v1',
'facets/meta/block-preview',
[
'methods' => 'GET',
'permission_callback' => [ $this, 'check_facets_meta_rest_permission' ],
'callback' => [ $this, 'render_block_preview' ],
'args' => [
'searchPlaceholder' => [
'sanitize_callback' => 'sanitize_text_field',
],
'facet' => [
'sanitize_callback' => 'sanitize_text_field',
],
'orderby' => [
'sanitize_callback' => 'sanitize_text_field',
],
'order' => [
'sanitize_callback' => 'sanitize_text_field',
],
],

]
);
}

/**
* Check permissions of the /facets/taxonomies REST endpoint.
*
* @return WP_Error|true
*/
public function check_facets_meta_rest_permission() {
if ( ! current_user_can( 'edit_theme_options' ) ) {
return new \WP_Error( 'ep_rest_forbidden', esc_html__( 'Sorry, you cannot view this resource.', 'elasticpress' ), array( 'status' => 401 ) );
}

return true;
}

/**
* Return an array of registered meta keys.
*
* @return array
*/
public function get_rest_registered_metakeys() {
$post_indexable = \ElasticPress\Indexables::factory()->get( 'post' );

try {
$meta_keys = $post_indexable->get_distinct_meta_field_keys();
} catch ( \Throwable $th ) {
$meta_keys = [];
}

return $meta_keys;
}

/**
* Register the block.
*/
public function register_block() {
register_block_type_from_metadata(
EP_PATH . 'assets/js/blocks/facets/meta',
[
'render_callback' => [ $this, 'render_block' ],
]
);
}

/**
* Render the block.
*
* @param array $attributes Block attributes.
*/
public function render_block( $attributes ) {
$attributes = $this->parse_attributes( $attributes );

/** This filter is documented in includes/classes/Feature/Facets/Types/Taxonomy/Block.php */
$renderer_class = apply_filters( 'ep_facet_renderer_class', __NAMESPACE__ . '\Renderer', 'meta', 'block', $attributes );
$renderer = new $renderer_class();

ob_start();
?>
<div class="wp-block-elasticpress-facet">
<?php $renderer->render( [], $attributes ); ?>
</div>
<?php
return ob_get_clean();
}

/**
* Outputs the block preview
*
* @param \WP_REST_Request $request REST request
* @return string
*/
public function render_block_preview( $request ) {
global $wp_query;

add_filter( 'ep_is_facetable', '__return_true' );

$search = Features::factory()->get_registered_feature( 'search' );

$attributes = $this->parse_attributes(
[
'searchPlaceholder' => $request->get_param( 'searchPlaceholder' ),
'facet' => $request->get_param( 'facet' ),
'orderby' => $request->get_param( 'orderby' ),
'order' => $request->get_param( 'order' ),
]
);

add_filter(
'ep_facet_meta_fields',
function ( $meta_fields ) use ( $attributes ) {
$meta_fields = [ $attributes['facet'] ];
return $meta_fields;
}
);

$wp_query = new \WP_Query(
[
'post_type' => $search->get_searchable_post_types(),
'per_page' => 1,
]
);

/** This filter is documented in includes/classes/Feature/Facets/Types/Taxonomy/Block.php */
$renderer_class = apply_filters( 'ep_facet_renderer_class', __NAMESPACE__ . '\Renderer', 'meta', 'block', $attributes );
$renderer = new $renderer_class();

ob_start();
$renderer->render( [], $attributes );
$block_content = ob_get_clean();

if ( empty( $block_content ) ) {
if ( empty( $attributes['facet'] ) ) {
return esc_html__( 'Preview not available', 'elasticpress' );
}

return sprintf(
/* translators: Meta field name */
esc_html__( 'Preview for %s not available', 'elasticpress' ),
esc_html( $request->get_param( 'facet' ) )
);
}

$block_content = preg_replace( '/href="(.*?)"/', 'href="#"', $block_content );
return '<div class="wp-block-elasticpress-facet">' . $block_content . '</div>';
}

/**
* Utilitary method to set default attributes.
*
* @param array $attributes Attributes passed
* @return array
*/
protected function parse_attributes( $attributes ) {
$attributes = wp_parse_args(
$attributes,
[
'searchPlaceholder' => esc_html_x( 'Search', 'Facet by meta search placeholder', 'elasticpress' ),
'facet' => '',
'orderby' => 'count',
'order' => 'desc',

]
);
if ( empty( $attributes['facet'] ) ) {
$registered_metakeys = $this->get_rest_registered_metakeys();
if ( ! empty( $registered_metakeys ) ) {
$attributes['facet'] = reset( $registered_metakeys );
}
}
return $attributes;
}
}
Loading

0 comments on commit fac9cf3

Please sign in to comment.