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

Facet by post type #3473

Merged
merged 11 commits into from
Jun 5, 2023
Next Next commit
Facet by post type
  • Loading branch information
oscarssanchez committed May 26, 2023
commit f943258e180a535fb5ee091cb9c69f196ecb79b4
38 changes: 38 additions & 0 deletions assets/js/blocks/facets/post-type/block.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 2,
"title": "Facet by Post Type (ElasticPress)",
"textdomain": "elasticpress",
"name": "elasticpress/facet-post-type",
"icon": "feedback",
"category": "widgets",
"attributes": {
"searchPlaceholder": {
"type": "string",
"default": "Search"
},
"facet": {
"type": "string",
"default": ""
},
"displayCount": {
"type": "boolean",
"default": false
},
"orderby": {
"type" : "string",
"default": "count",
"enum" : [ "count", "name" ]
},
"order": {
"type": "string",
"default": "desc",
"enum": [ "desc", "asc" ]
}
},
"supports": {
"html": false
},
"editorScript": "ep-facets-post-type-block-script",
"style": "elasticpress-facets"
}
86 changes: 86 additions & 0 deletions assets/js/blocks/facets/post-type/edit.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import { InspectorControls, useBlockProps } from '@wordpress/block-editor';
import {
PanelBody,
RadioControl,
TextControl,
ToggleControl,
Spinner,
Placeholder,
} from '@wordpress/components';
import { Fragment, useEffect, useState } from '@wordpress/element';
import apiFetch from '@wordpress/api-fetch';
import { __ } from '@wordpress/i18n';

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

const blockProps = useBlockProps();

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

return (
<Fragment>
<InspectorControls>
<PanelBody title={__('Facet Settings', 'elasticpress')}>
<TextControl
label={__('Search Placeholder', 'elasticpress')}
value={searchPlaceholder}
onChange={(value) => setAttributes({ searchPlaceholder: value })}
/>
<ToggleControl
checked={displayCount}
onChange={(value) => setAttributes({ displayCount: value })}
label={__('Display Term Count', 'elasticpress')}
/>
<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/post-type/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/Feature/Facets/Facets.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ public function __construct() {
if ( version_compare( get_bloginfo( 'version' ), '5.8', '>=' ) ) {
$types['meta'] = __NAMESPACE__ . '\Types\Meta\FacetType';
$types['meta-range'] = __NAMESPACE__ . '\Types\MetaRange\FacetType';
$types['post-type'] = __NAMESPACE__ . '\Types\PostType\FacetType';
}

/**
Expand Down
197 changes: 197 additions & 0 deletions includes/classes/Feature/Facets/Types/PostType/Block.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
<?php
/**
* Facets block
*
* @since 4.6.0
* @package elasticpress
*/

namespace ElasticPress\Feature\Facets\Types\PostType;

use ElasticPress\Features;
use ElasticPress\Utils;

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

/**
* Facets block class
*/
class Block {
/**
* Hook block functionality.
*/
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/post-type/block-preview',
[
'methods' => 'GET',
'permission_callback' => [ $this, 'check_facets_rest_permission' ],
'callback' => [ $this, 'render_block_preview' ],
'args' => [
'searchPlaceholder' => [
'sanitize_callback' => 'sanitize_text_field',
],
'displayCount' => [
'sanitize_callback' => 'rest_sanitize_boolean',
],
'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.
burhandodhy marked this conversation as resolved.
Show resolved Hide resolved
*
* @return WP_Error|true
*/
public function check_facets_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;
}

/**
* Register the block.
*/
public function register_block() {
/**
* Registering it here so translation works
*
* @see https://core.trac.wordpress.org/ticket/54797#comment:20
*/
wp_register_script(
'ep-facets-post-type-block-script',
EP_URL . 'dist/js/facets-post-type-block-script.js',
Utils\get_asset_info( 'facets-post-type-block-script', 'dependencies' ),
Utils\get_asset_info( 'facets-post-type-block-script', 'version' ),
true
);

wp_set_script_translations( 'ep-facets-post-type-block-script', 'elasticpress' );

register_block_type_from_metadata(
EP_PATH . 'assets/js/blocks/facets/post-type',
[
'render_callback' => [ $this, 'render_block' ],
]
);
}

/**
* Render the block.
*
* @param array $attributes Block attributes.
*/
burhandodhy marked this conversation as resolved.
Show resolved Hide resolved
public function render_block( $attributes ) {
$attributes = $this->parse_attributes( $attributes );

$renderer_class = apply_filters( 'ep_facet_renderer_class', __NAMESPACE__ . '\Renderer', 'post-type', 'block', $attributes );
burhandodhy marked this conversation as resolved.
Show resolved Hide resolved
$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' ),
'displayCount' => $request->get_param( 'displayCount' ),
'facet' => $request->get_param( 'facet' ),
'orderby' => $request->get_param( 'orderby' ),
'order' => $request->get_param( 'order' ),
]
);

$args = [
'post_type' => $search->get_searchable_post_types(),
'posts_per_page' => 1,
];

$wp_query->query( $args );

/** This filter is documented in includes/classes/Feature/Facets/Types/Taxonomy/Block.php */
$renderer_class = apply_filters( 'ep_facet_renderer_class', __NAMESPACE__ . '\Renderer', 'post-type', '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 */
felipeelia marked this conversation as resolved.
Show resolved Hide resolved
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' ),
felipeelia marked this conversation as resolved.
Show resolved Hide resolved
'facet' => '',
'displayCount' => false,
'orderby' => 'count',
'order' => 'desc',
]
);

return $attributes;
}
}
Loading