From 085a8c48fe8c700295704d288228f3057b0d5d53 Mon Sep 17 00:00:00 2001 From: Sagar Deshmukh Date: Mon, 10 Jun 2024 15:30:30 +0530 Subject: [PATCH] PLANET-7527 Remove blocks report functionality from blocks plugin Ref. https://jira.greenpeace.org/browse/PLANET-7527 --- classes/class-loader.php | 6 - classes/command/class-duplicated-postmeta.php | 86 --- .../menu/class-blocks-usage-controller.php | 219 ------ .../menu/class-classic-blocks-usage.php | 80 --- .../menu/class-postmeta-check-controller.php | 70 -- .../menu/class-reusable-blocks-controller.php | 40 -- .../menu/class-settings-controller.php | 38 -- classes/search/block/class-blockusage.php | 278 -------- classes/search/block/class-blockusageapi.php | 95 --- .../search/block/class-blockusagetable.php | 638 ------------------ classes/search/block/class-query.php | 22 - .../search/block/query/class-parameters.php | 189 ------ classes/search/block/sql/class-like.php | 48 -- classes/search/block/sql/class-regex.php | 48 -- classes/search/block/sql/class-sqlquery.php | 113 ---- classes/search/class-blocksearch.php | 47 -- classes/search/class-patternsearch.php | 170 ----- classes/search/class-rowactions.php | 76 --- .../search/pattern/class-contentstructure.php | 180 ----- classes/search/pattern/class-patterndata.php | 125 ---- classes/search/pattern/class-patternusage.php | 204 ------ .../search/pattern/class-patternusageapi.php | 96 --- .../pattern/class-patternusagetable.php | 401 ----------- .../search/pattern/query/class-parameters.php | 153 ----- templates/duplicate-postmeta-report.twig | 56 -- 25 files changed, 3478 deletions(-) delete mode 100644 classes/command/class-duplicated-postmeta.php delete mode 100644 classes/controller/menu/class-blocks-usage-controller.php delete mode 100644 classes/controller/menu/class-classic-blocks-usage.php delete mode 100644 classes/controller/menu/class-postmeta-check-controller.php delete mode 100644 classes/controller/menu/class-reusable-blocks-controller.php delete mode 100644 classes/controller/menu/class-settings-controller.php delete mode 100644 classes/search/block/class-blockusage.php delete mode 100644 classes/search/block/class-blockusageapi.php delete mode 100644 classes/search/block/class-blockusagetable.php delete mode 100644 classes/search/block/class-query.php delete mode 100644 classes/search/block/query/class-parameters.php delete mode 100644 classes/search/block/sql/class-like.php delete mode 100644 classes/search/block/sql/class-regex.php delete mode 100644 classes/search/block/sql/class-sqlquery.php delete mode 100644 classes/search/class-blocksearch.php delete mode 100644 classes/search/class-patternsearch.php delete mode 100644 classes/search/class-rowactions.php delete mode 100644 classes/search/pattern/class-contentstructure.php delete mode 100644 classes/search/pattern/class-patterndata.php delete mode 100644 classes/search/pattern/class-patternusage.php delete mode 100644 classes/search/pattern/class-patternusageapi.php delete mode 100644 classes/search/pattern/class-patternusagetable.php delete mode 100644 classes/search/pattern/query/class-parameters.php delete mode 100644 templates/duplicate-postmeta-report.twig diff --git a/classes/class-loader.php b/classes/class-loader.php index ddbb30acc..c120a09c5 100644 --- a/classes/class-loader.php +++ b/classes/class-loader.php @@ -11,7 +11,6 @@ use P4\MasterTheme\Features; use P4\MasterTheme\MigrationLog; use P4\MasterTheme\Migrations\M001EnableEnFormFeature; -use P4GBKS\Controllers; use P4GBKS\Patterns\Block_Pattern; use P4GBKS\Views\View; use WP_CLI; @@ -115,12 +114,7 @@ function ( $class_name ) { */ public function load_services(): void { $services = [ - Controllers\Menu\Settings_Controller::class, - Controllers\Menu\Blocks_Usage_Controller::class, - Controllers\Menu\Classic_Blocks_Usage::class, - Controllers\Menu\Reusable_Blocks_Controller::class, Controllers\Menu\Archive_Import::class, - Controllers\Menu\Postmeta_Check_Controller::class, ]; if ( ! $this->planet4_blocks_is_active() ) { diff --git a/classes/command/class-duplicated-postmeta.php b/classes/command/class-duplicated-postmeta.php deleted file mode 100644 index ace33ed18..000000000 --- a/classes/command/class-duplicated-postmeta.php +++ /dev/null @@ -1,86 +0,0 @@ -prepare( $sql, self::META_KEY_LIST ); - - return $wpdb->query( $prepared_sql ); - // phpcs:enable - } - - /** - * Detect duplicate postmeta records - * - * @return object - */ - public static function detect() { - - global $wpdb; - - // phpcs:disable - $sql = "SELECT `meta_key`, COUNT(post_id) AS all_count , COUNT(DISTINCT post_id) AS unique_count - FROM `wp_postmeta` - GROUP by `meta_key` - HAVING all_count <> unique_count - ORDER BY `all_count` DESC"; - - return $wpdb->get_results( $sql ); - // phpcs:enable - } - - /** - * Generate a bunch of placeholders for use in an IN query. - * - * @param array $items The items to generate placeholders for. - * @param int $start_index The start index to use for creating the placeholders. - * @return string The generated placeholders string. - */ - private static function generate_placeholders( array $items, int $start_index ): string { - $placeholders = []; - foreach ( range( $start_index, count( $items ) + $start_index - 1 ) as $i ) { - $placeholders[] = "'%$i\$s'"; - } - return implode( ',', $placeholders ); - } -} diff --git a/classes/controller/menu/class-blocks-usage-controller.php b/classes/controller/menu/class-blocks-usage-controller.php deleted file mode 100644 index f0ad6cad9..000000000 --- a/classes/controller/menu/class-blocks-usage-controller.php +++ /dev/null @@ -1,219 +0,0 @@ -hooks(); - } - - /** - * Class hooks. - */ - private function hooks() { - add_action( 'rest_api_init', [ $this, 'plugin_blocks_report_register_rest_route' ] ); - BlockUsageTable::set_hooks(); - PatternUsageTable::set_hooks(); - } - - /** - * Register API route for report of blocks usage in pages/posts. - */ - public function plugin_blocks_report_register_rest_route() { - register_rest_route( - 'plugin_blocks/v3', - '/plugin_blocks_report/', - [ - 'methods' => 'GET', - 'callback' => [ $this, 'plugin_blocks_report_rest_api' ], - 'permission_callback' => '__return_true', - ] - ); - } - - /** - * Generates blocks/pages report. - */ - public function plugin_blocks_report_rest_api() { - global $wpdb; - - $use_cache = ! current_user_can( 'manage_options' ); - $cache_key = 'plugin_blocks/v3/plugin_blocks_report'; - if ( $use_cache ) { - $report = wp_cache_get( $cache_key, 'api', false, $found ); - if ( $found ) { - return $report; - } - } - - $types = \get_post_types( - [ - 'public' => true, - 'exclude_from_search' => false, - ] - ); - - // Get posts types counts. - $params = new SqlParameters(); - $sql = 'SELECT post_type, count(ID) AS post_count - FROM ' . $params->identifier( $wpdb->posts ) . ' - WHERE post_status = ' . $params->string( 'publish' ) . ' - AND post_type IN ' . $params->string_list( $types ) . ' - GROUP BY post_type'; - $results = $wpdb->get_results( - // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared - $wpdb->prepare( $sql, $params->get_values() ), - \ARRAY_A - ); - - $post_types = array_combine( - array_column( $results, 'post_type' ), - array_map( 'intval', array_column( $results, 'post_count' ) ) - ); - - // Group results. - $block_api = new BlockUsageApi(); - $pattern_api = new PatternUsageApi(); - $report = [ - 'block_types' => $block_api->get_count(), - 'block_patterns' => $pattern_api->get_count(), - 'post_types' => $post_types, - ]; - wp_cache_set( $cache_key, $report, 'api', 60 * 5 ); - - return $report; - } - - /** - * Create menu/submenu entry. - */ - public function create_admin_menu() { - $current_user = wp_get_current_user(); - - if ( ( in_array( 'administrator', $current_user->roles, true ) || in_array( 'editor', $current_user->roles, true ) ) && current_user_can( 'edit_posts' ) ) { - add_submenu_page( - P4GBKS_PLUGIN_SLUG_NAME, - __( 'Report', 'planet4-blocks-backend' ), - __( 'Report', 'planet4-blocks-backend' ), - 'edit_posts', - 'plugin_blocks_report', - [ $this, 'plugin_blocks_report' ] - ); - - add_submenu_page( - P4GBKS_PLUGIN_SLUG_NAME, - __( 'Pattern Report', 'planet4-blocks-backend' ), - __( 'Pattern Report', 'planet4-blocks-backend' ), - 'edit_posts', - 'plugin_patterns_report', - [ $this, 'plugin_patterns_report' ] - ); - } - } - - /** - * Block usage report page. - */ - public function plugin_blocks_report() { - // Nonce verify. - if ( isset( $_REQUEST['filter_action'] ) ) { - check_admin_referer( 'bulk-' . BlockUsageTable::PLURAL ); - } - - // Create table. - $args = [ - 'block_usage' => new BlockUsage(), - 'block_registry' => WP_Block_Type_Registry::get_instance(), - ]; - $table = new BlockUsageTable( $args ); - - // Prepare data. - $special_filter = isset( $_REQUEST['unregistered'] ) ? 'unregistered' - : ( isset( $_REQUEST['unallowed'] ) ? 'unallowed' : null ); - $table->prepare_items( - BlockParameters::from_request( $_REQUEST ), - $_REQUEST['group'] ?? null, - $special_filter - ); - - // Display data. - echo '
-

Block usage

-
'; - - echo '
'; - $table->views(); - $table->search_box( 'Search in block attributes', 'blocks-report' ); - $table->display(); - echo ''; - echo '
'; - echo '
'; - } - - /** - * Pattern usage report page. - */ - public function plugin_patterns_report() { - // Nonce verify. - if ( isset( $_REQUEST['filter_action'] ) ) { - check_admin_referer( 'bulk-' . PatternUsageTable::PLURAL ); - } - - // Create table. - $args = [ - 'pattern_usage' => new PatternUsage(), - 'pattern_registry' => WP_Block_Patterns_Registry::get_instance(), - ]; - $table = new PatternUsageTable( $args ); - - // Prepare data. - $table->prepare_items( - PatternParameters::from_request( $_REQUEST ), - $_REQUEST['group'] ?? null - ); - - // Display data. - echo '
-

Pattern usage

-
'; - - echo '
'; - $table->views(); - $table->display(); - echo ''; - echo '
'; - echo '
'; - } - } -} diff --git a/classes/controller/menu/class-classic-blocks-usage.php b/classes/controller/menu/class-classic-blocks-usage.php deleted file mode 100644 index 57b8824bb..000000000 --- a/classes/controller/menu/class-classic-blocks-usage.php +++ /dev/null @@ -1,80 +0,0 @@ -roles, true ) && current_user_can( 'manage_options' ) ) { - add_submenu_page( - P4GBKS_PLUGIN_SLUG_NAME, - __( 'Classic block usage', 'planet4-blocks-backend' ), - __( 'Classic block usage', 'planet4-blocks-backend' ), - 'manage_options', - 'classic_block_usage', - [ $this, 'classic_block_usage' ] - ); - } - } - - /** - * Show all posts that use classic "blocks", which isn't really a block but the absence of one. - * All consecutive content that is not inside of a block comment is regarded as a single classic block when parsed. - * So in order to find classic blocks, we parse the content as blocks and then check for all blocks where the name - * is null and the content is more than just whitespace. - */ - public function classic_block_usage(): void { - $all_posts = get_posts( - [ - 'post_type' => [ 'post', 'page', 'campaign' ], - 'numberposts' => - 1, - ] - ); - - $has_classic_block = static function ( $post ) { - $blocks = parse_blocks( $post->post_content ); - foreach ( $blocks as $block ) { - if ( null === $block['blockName'] && '' !== trim( $block['innerHTML'] ) ) { - return true; - } - } - - return false; - }; - - $with_classic_blocks = array_filter( $all_posts, $has_classic_block ); - - // Copied this code from https://github.com/greenpeace/planet4-plugin-gutenberg-blocks/blob/add-classic-block-to-report/classes/controller/menu/class-blocks-usage-controller.php#L107-L107. - // So disabling CS here as well as this is only a temporary dev tool. - //phpcs:disable - echo '
'; - echo '

Posts using classic blocks

'; - echo '

Following posts are detected to use classic blocks. It is recommended to convert them to new blocks.

'; - echo '

You can convert classic blocks in the post editor by following the steps described in this article.

'; - echo ''; - echo ' - - - '; - foreach ( $with_classic_blocks as $post_with_classic ) { - echo ''; - echo ''; - } - echo '
' . __( 'ID', 'planet4-blocks-backend' ) . '' . __( 'Title', 'planet4-blocks-backend' ) . '
' . $post_with_classic->ID . '' . $post_with_classic->post_title . '
'; - //phpcs:enable - } -} diff --git a/classes/controller/menu/class-postmeta-check-controller.php b/classes/controller/menu/class-postmeta-check-controller.php deleted file mode 100644 index 979efed77..000000000 --- a/classes/controller/menu/class-postmeta-check-controller.php +++ /dev/null @@ -1,70 +0,0 @@ -roles, true ) ) { - add_submenu_page( - P4GBKS_PLUGIN_SLUG_NAME, - __( 'Postmeta Check', 'planet4-blocks-backend' ), - __( 'Postmeta Check', 'planet4-blocks-backend' ), - 'manage_options', - 'postmeta_report', - [ $this, 'postmeta_check' ] - ); - } - } - - /** - * Handle form submit. - * - * @param mixed[] $data The form data. - */ - public function handle_submit( &$data ) { - $remove_duplicate_postmeta = filter_input( INPUT_POST, 'delete_duplicate_postmeta', FILTER_SANITIZE_NUMBER_INT ); - if ( 'POST' === $_SERVER['REQUEST_METHOD'] && $remove_duplicate_postmeta ) { //phpcs:ignore WordPress.Security.NonceVerification.Recommended - try { - $deleted_rows = Duplicated_Postmeta::remove(); - } catch ( \Error $e ) { - $data['message'] = $e->getMessage(); - } catch ( \Exception $e ) { - $data['message'] = __( 'Exception: ', 'planet4-blocks-backend' ) . $e->getMessage(); - } - - if ( $deleted_rows ) { - // translators: %d = The duplicate postmeta count. - $data['message'] = sprintf( __( 'Remove %d duplicate postmeta records successfully.', 'planet4-blocks-backend' ), $deleted_rows ); - } else { - $data['message'] = __( 'No whitelisted duplicate postmeta records found.', 'planet4-blocks-backend' ); - } - } - } - - /** - * Render the admin page with duplicate postmeta details. - */ - public function postmeta_check() { - $data = []; - - $this->handle_submit( $data ); - $data['duplicate_postmeta'] = Duplicated_Postmeta::detect(); - $data['postmeta_keys'] = Duplicated_Postmeta::META_KEY_LIST; - - $this->view->block( 'duplicate-postmeta-report', $data, 'twig', '' ); - } -} diff --git a/classes/controller/menu/class-reusable-blocks-controller.php b/classes/controller/menu/class-reusable-blocks-controller.php deleted file mode 100644 index 8616a919f..000000000 --- a/classes/controller/menu/class-reusable-blocks-controller.php +++ /dev/null @@ -1,40 +0,0 @@ -roles, true ) || in_array( 'editor', $current_user->roles, true ) ) && current_user_can( 'edit_posts' ) ) { - add_submenu_page( - P4GBKS_PLUGIN_SLUG_NAME, - __( 'All Reusable blocks', 'planet4-blocks-backend' ), - __( 'All Reusable blocks', 'planet4-blocks-backend' ), - 'edit_posts', - 'edit.php?post_type=' . self::POST_TYPE - ); - } - } - } -} diff --git a/classes/controller/menu/class-settings-controller.php b/classes/controller/menu/class-settings-controller.php deleted file mode 100644 index 1c7690b6a..000000000 --- a/classes/controller/menu/class-settings-controller.php +++ /dev/null @@ -1,38 +0,0 @@ -roles, true ) || in_array( 'editor', $current_user->roles, true ) ) && current_user_can( 'edit_posts' ) ) { - - add_menu_page( - __( 'Blocks', 'planet4-blocks-backend' ), - __( 'Blocks', 'planet4-blocks-backend' ), - 'edit_posts', - P4GBKS_PLUGIN_SLUG_NAME, - null, - 'dashicons-layout' - ); - } - } - } -} diff --git a/classes/search/block/class-blockusage.php b/classes/search/block/class-blockusage.php deleted file mode 100644 index 2cb5e0d33..000000000 --- a/classes/search/block/class-blockusage.php +++ /dev/null @@ -1,278 +0,0 @@ -search = $search ?? new BlockSearch(); - $this->parser = $parser ?? new WP_Block_Parser(); - } - - /** - * @param Parameters $params Query parameters. - * @return array - */ - public function get_blocks( Parameters $params ): array { - $this->posts_ids = $this->search->get_posts( $params ); - - return $this->get_filtered_blocks( $this->posts_ids, $params ); - } - - /** - * @param Parameters $params Query parameters. - * @return int[] - */ - public function get_posts( Parameters $params ): array { - $block_list = $this->get_blocks( $this->posts_ids, $params ); - - return array_unique( array_column( $block_list, 'post_id' ) ); - } - - /** - * @param int[] $posts_ids Posts IDs. - * @param Parameters $params Query parameters. - * @return array - */ - private function get_filtered_blocks( $posts_ids, Parameters $params ) { - $this->fetch_blocks( $posts_ids, $params ); - $this->filter_blocks( $params ); - $this->sort_blocks( $params->order() ); - - return $this->blocks; - } - - /** - * @param int[] $posts_ids Posts IDs. - * @param Parameters $params Query parameters. - */ - private function fetch_blocks( array $posts_ids, Parameters $params ): void { - $posts_args = [ - 'include' => $posts_ids, - 'orderby' => empty( $params->order() ) ? null : array_fill_keys( $params->order(), 'ASC' ), - 'post_status' => $params->post_status(), - 'post_type' => $params->post_type(), - ]; - - $this->posts = get_posts( $posts_args ) ?? []; - - $block_listblock_list = []; - foreach ( $this->posts as $post ) { - $block_listblock_list = array_merge( $block_listblock_list, $this->parse_post( $post ) ); - } - $this->blocks = $block_listblock_list; - } - - /** - * Filter parsed search items - * - * @param Parameters $params Query parameters. - * @return array - */ - private function filter_blocks( Parameters $params ): void { - if ( - empty( $params->namespace() ) - && empty( $params->name() ) - && empty( $params->content() ) - ) { - return; - } - - $filtered = $this->blocks; - - $text = $params->content(); - $filters = [ - 'block_ns' => $params->namespace(), - 'block_type' => $params->name(), - 'local_name' => false !== strpos( $params->name(), '/' ) - ? explode( '/', $params->name() )[1] - : $params->name(), - ]; - - if ( ! empty( $filters['block_type'] ) ) { - $filtered = array_filter( - $filtered, - function ( $i ) use ( $filters ) { - return $i['block_type'] === $filters['block_type'] - || $i['local_name'] === $filters['local_name']; - } - ); - } elseif ( ! empty( $filters['block_ns'] ) ) { - $filtered = array_filter( - $filtered, - function ( $i ) use ( $filters ) { - return $i['block_ns'] === $filters['block_ns']; - } - ); - } - - if ( ! empty( $text ) ) { - $filtered = array_filter( - $filtered, - function ( $i ) use ( $text ) { - return strpos( $i['block_type'], $text ) !== false - //phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_serialize - || strpos( serialize( $i['block_attrs'] ), $text ) !== false; - } - ); - } - - $this->blocks = $filtered; - } - - /** - * Sort parsed blocks - * - * @param null|array $sort Sort dimensions. - * @return array - */ - private function sort_blocks( ?array $sort = [] ): void { - if ( empty( $sort ) ) { - return; - } - - $args = []; - $block_list = $this->blocks; - foreach ( $sort as $name ) { - $args[] = array_column( $block_list, $name ); - $args[] = SORT_NATURAL; - } - $args[] = &$block_list; - - array_multisort( ...$args ); - - $this->blocks = $block_list; - } - - /** - * Parse posts content to blocks. - * - * @param object $post WP_Post. - * @return array[] - */ - private function parse_post( $post ): array { - $output = $this->parser->parse( $post->post_content ); - - $block_list = array_filter( - $output, - function ( $block ) { - return ! empty( $block['blockName'] ); - } - ); - - $items = []; - while ( ! empty( $block_list ) ) { - $block = array_shift( $block_list ); - $items[] = $this->format_block_data( $block, $post ); - - if ( ! empty( $block['innerBlocks'] ) ) { - $block_list = array_merge( $block_list, $block['innerBlocks'] ); - } - } - - return $items; - } - - /** - * Format block information. - * - * @param array $block A block. - * @param object $post WP_Post. - * @return array[] - */ - private function format_block_data( array $block, $post ): array { - $type = $block['blockName']; - $attrs = $block['attrs'] ?? []; - $has_ns = strpos( $type, '/' ) !== false; - - [ $namespace, $local_name ] = $has_ns ? explode( '/', $type ) : [ 'core', $type ]; - - $classes = empty( $attrs['className'] ) ? [] : explode( ' ', $attrs['className'] ); - $styles = array_filter( - array_map( - function ( $c ): ?string { - return 'is-style-' === substr( $c, 0, 9 ) ? substr( $c, 9 ) : null; - }, - $classes - ) - ); - - return [ - 'post_id' => $post->ID, - 'post_title' => $post->post_title - ? $post->post_title : __( '(no title)', 'planet4-blocks-backend' ), - 'post_status' => $post->post_status, - 'post_type' => $post->post_type, - 'post_date' => $post->post_date, - 'post_modified' => $post->post_modified, - 'post_status' => $post->post_status, - 'guid' => $post->guid, - 'block_ns' => $namespace, - 'block_type' => $type, - 'local_name' => $local_name, - 'block_attrs' => $attrs, - 'block_styles' => $styles, - ]; - } - - /** - * Block count in search result. - * - * @return int - */ - public function block_count(): int { - return count( $this->blocks ); - } - - /** - * Post count in search result. - * - * @return int - */ - public function post_count(): int { - return count( $this->posts_ids ); - } -} diff --git a/classes/search/block/class-blockusageapi.php b/classes/search/block/class-blockusageapi.php deleted file mode 100644 index e217b0d73..000000000 --- a/classes/search/block/class-blockusageapi.php +++ /dev/null @@ -1,95 +0,0 @@ -usage = new BlockUsage(); - $this->params = ( new Parameters() ) - ->with_post_status( self::DEFAULT_POST_STATUS ) - ->with_post_type( - \get_post_types( - [ - 'public' => true, - 'exclude_from_search' => false, - ] - ) - ); - } - - /** - * Count blocks by type and style - * - * If style is not specified, an empty key 'n/a' is used. - */ - public function get_count(): array { - if ( null === $this->items ) { - $this->fetch_items(); - } - - $types = array_unique( - array_column( $this->items, 'block_type' ) - ); - $blocks = array_fill_keys( - $types, - [ - 'total' => 0, - 'styles' => [], - ] - ); - ksort( $blocks ); - - foreach ( $this->items as $item ) { - $styles = empty( $item['block_styles'] ) ? [ 'n/a' ] : $item['block_styles']; - foreach ( $styles as $style ) { - $type = $item['block_type']; - if ( ! isset( $blocks[ $type ]['styles'][ $style ] ) ) { - $blocks[ $type ]['styles'][ $style ] = 0; - } - $blocks[ $type ]['styles'][ $style ]++; - $blocks[ $type ]['total']++; - } - ksort( $blocks[ $type ]['styles'] ); - } - - return $blocks; - } - - /** - * Fetch parsed blocks - */ - private function fetch_items(): void { - $this->items = $this->usage->get_blocks( $this->params ); - } -} diff --git a/classes/search/block/class-blockusagetable.php b/classes/search/block/class-blockusagetable.php deleted file mode 100644 index c35ecf622..000000000 --- a/classes/search/block/class-blockusagetable.php +++ /dev/null @@ -1,638 +0,0 @@ - title. - */ - private $columns = null; - - /** - * @var string|null Latest row content displayed. - */ - private $latest_row = null; - - /** - * @var string[]|null Blocks namespaces. - */ - private $blocks_ns = null; - - /** - * @var string[]|null Blocks types. - */ - private $blocks_types = null; - - /** - * @var string[]|null Blocks registered. - */ - private $blocks_registered = null; - - /** - * @var string[]|null Blocks allowed. - */ - private $blocks_allowed = null; - - /** - * @var ?string Special filter. - */ - private $special = null; - - /** - * @param array $args Args. - * @throws InvalidArgumentException Throws on missing parameter. - * @see WP_List_Table::__construct() - */ - public function __construct( $args = [] ) { - $args['plural'] = self::PLURAL; - parent::__construct( $args ); - - $this->block_usage = $args['block_usage'] ?? null; - $this->block_registry = $args['block_registry'] ?? null; - - if ( ! ( $this->block_usage instanceof BlockUsage ) ) { - throw new InvalidArgumentException( - 'Table requires a BlockUsage instance.' - ); - } - if ( ! ( $this->block_registry instanceof WP_Block_Type_Registry ) ) { - throw new InvalidArgumentException( - 'Table requires a WP_Block_Type_Registry instance.' - ); - } - } - - /** - * Prepares table data. - * - * @param Parameters $search_params Search parameters. - * @param string $group_by Grouping dimension. - * @param ?string $special Unregistered blocks only. - */ - public function prepare_items( - ?Parameters $search_params = null, - ?string $group_by = null, - ?string $special = null - ): void { - if ( in_array( $group_by, $this->allowed_groups, true ) ) { - $this->group_by = $group_by; - } - - $this->search_params = $search_params - ->with_post_status( self::DEFAULT_POST_STATUS ) - ->with_post_type( - array_filter( - \get_post_types( [ 'show_in_rest' => true ] ), - fn ( $t ) => \post_type_supports( $t, 'editor' ) - ) - ) - ->with_order( array_merge( [ $this->group_by ], $this->sort_by ) ); - - $items = $this->block_usage->get_blocks( $this->search_params ); - - $this->special = $special; - if ( 'unregistered' === $this->special ) { - $items = $this->filter_for_unregistered( $items ); - } - if ( 'unallowed' === $this->special ) { - $items = $this->filter_for_unallowed( $items ); - } - - // Pagination handling. - $total_items = count( $items ); - $per_page = 50; - $current_page = $this->get_pagenum(); - $this->items = array_slice( $items, ( ( $current_page - 1 ) * $per_page ), $per_page ); - $this->set_pagination_args( - [ - 'total_items' => $total_items, - 'per_page' => $per_page, - 'total_pages' => ceil( $total_items / $per_page ), - ] - ); - - $this->set_block_filters(); - $this->_column_headers = $this->get_column_headers(); - } - - /** - * Filter items to keep unregistered blocks only. - * - * @param array $items Blocks not registered. - */ - private function filter_for_unregistered( array $items ): array { - $this->set_registered_blocks(); - return array_filter( - $items, - fn ( $i ) => ! in_array( $i['block_type'], $this->blocks_registered, true ) && 'core-embed' !== $i['block_ns'] - ); - } - - /** - * Filter items to keep unallowed blocks only. - * - * @param array $items Blocks not registered. - */ - private function filter_for_unallowed( array $items ): array { - $this->set_allowed_blocks(); - return array_filter( - $items, - fn ( $i ) => ! in_array( $i['block_type'], $this->blocks_allowed, true ) - ); - } - - /** - * Set dropdown filters content. - */ - private function set_block_filters(): void { - $this->set_registered_blocks(); - $this->set_allowed_blocks(); - $this->blocks_types = array_unique( - array_merge( - $this->blocks_registered, - $this->blocks_allowed - ) - ); - - $namespaces = array_filter( - array_unique( - array_map( - static function ( string $name ) { - return explode( '/', $name )[0] ?? null; - }, - $this->blocks_types - ) - ) - ); - sort( $namespaces ); - $this->blocks_ns = $namespaces; - } - - /** - * Set the registered blocks list. - */ - private function set_registered_blocks() { - $names = array_keys( - $this->block_registry->get_all_registered() - ); - sort( $names ); - $this->blocks_registered = $names; - } - - /** - * Set the allowed blocks list. - */ - private function set_allowed_blocks() { - $post_types = array_filter( - get_post_types( [ 'show_in_rest' => true ] ), - fn ( $t ) => post_type_supports( $t, 'editor' ) - ); - - $allowed = []; - foreach ( $post_types as $type ) { - $context = new \WP_Block_Editor_Context( - [ 'post' => (object) [ 'post_type' => $type ] ] - ); - $on_type = get_allowed_block_types( $context ); - if ( ! is_array( $on_type ) ) { - $on_type = $on_type ? $this->blocks_registered : []; - } - $allowed = array_merge( $allowed, array_values( $on_type ) ); - } - - $allowed = array_unique( $allowed ); - sort( $allowed ); - $this->blocks_allowed = $allowed; - } - - /** - * Columns list for table. - */ - public function get_columns() { - if ( null !== $this->columns ) { - return $this->columns; - } - - $default_columns = [ - 'post_title' => 'Title', - 'block_type' => 'Block', - 'block_styles' => 'Style', - 'block_attrs' => 'Attributes', - 'post_date' => 'Created', - 'post_modified' => 'Modified', - 'post_id' => 'ID', - 'post_status' => 'Status', - ]; - - $this->columns = array_merge( - [ $this->group_by => $default_columns[ $this->group_by ] ], - $default_columns - ); - - return $this->columns; - } - - /** - * All, hidden and sortable columns. - */ - private function get_column_headers() { - return [ - $this->get_columns(), - [], - [ 'post_title', 'post_date', 'post_modified' ], - ]; - } - - /** - * Available grouping as views. - */ - protected function get_views() { - $link_tpl = '%s'; - $active_link_tpl = '%s'; - return [ - 'block_type' => sprintf( - 'block_type' === $this->group_by ? $active_link_tpl : $link_tpl, - add_query_arg( 'group', 'block_type' ), - 'Group by block name' - ), - 'post_title' => sprintf( - 'post_title' === $this->group_by ? $active_link_tpl : $link_tpl, - add_query_arg( 'group', 'post_title' ), - 'Group by post title' - ), - 'post_id' => sprintf( - 'post_id' === $this->group_by ? $active_link_tpl : $link_tpl, - add_query_arg( 'group', 'post_id' ), - 'Group by post ID' - ), - ]; - } - - - /** - * Displays the list of views available on this table. - */ - public function views() { - parent::views(); - - $link_tpl = '%s'; - $active_link_tpl = '%s'; - $unique_views = [ - 'unregistered' => sprintf( - 'unregistered' === $this->special ? $active_link_tpl : $link_tpl, - 'unregistered' === $this->special - ? self::url() - : add_query_arg( [ 'unregistered' => '' ], self::url() ), - 'Not registered' - ), - 'unallowed' => sprintf( - 'unallowed' === $this->special ? $active_link_tpl : $link_tpl, - 'unallowed' === $this->special - ? self::url() - : add_query_arg( [ 'unallowed' => '' ], self::url() ), - 'Not allowed' - ), - ]; - - $views = []; - echo '
'; - } - - /** - * Select blocks namespaces. - */ - private function blockns_dropdown() { - sort( $this->blocks_ns ); - $filter = $this->search_params->namespace() ?? null; - - echo ''; - } - - /** - * Select blocks types. - */ - private function blocktype_dropdown() { - sort( $this->blocks_types ); - $filter = $this->search_params->name() ?? null; - - echo ''; - - echo ""; - } - - /** - * Add filters to table. - * - * @param string $which Tablenav identifier. - */ - protected function extra_tablenav( $which ) { - echo '
'; - $this->blockns_dropdown(); - $this->blocktype_dropdown(); - submit_button( - __( 'Filter', 'planet4-blocks-backend' ), - '', - 'filter_action', - false, - [ 'id' => 'block-query-submit' ] - ); - echo '
'; - } - - /** - * Add pagination information to table. - * - * @param string $which Tablenav identifier. - */ - protected function pagination( $which ) { - echo esc_html( parent::pagination( 'top' ) ); - } - - /** - * Default column value representation. - * - * @param array $item Item. - * @param string $column_name Column name. - * - * @return mixed - */ - public function column_default( $item, $column_name ) { - return $item[ $column_name ] ?? ''; - } - - /** - * Block option display. - * - * @param array $item Item. - * @return string - */ - public function column_block_attrs( $item ): string { - $content = $item['block_attrs'] ?? null; - if ( empty( $content ) ) { - return ''; - } - - //phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r - $content = print_r( $content, true ); - $content = trim( substr( $content, 5, strlen( $content ) ) ); - - return sprintf( - '%s', - esc_attr( $content ), - esc_html( - strlen( $content ) > 30 - ? substr( $content, 0, 30 ) . '...' - : $content - ) - ); - } - - /** - * Block styles display. - * - * @param array $item Item. - * @return string - */ - public function column_block_styles( $item ): string { - return sprintf( - '%s', - implode( ',', $item['block_styles'] ) - ); - } - - /** - * Post title display. - * - * @param array $item Item. - * @return string - */ - public function column_post_title( $item ): string { - $content = $item['post_title'] ?? null; - if ( empty( $content ) ) { - return ''; - } - - $title_tpl = '%2$s'; - $link_tpl = '%s'; - $page_uri = get_page_uri( $item['post_id'] ); - - return sprintf( - empty( $page_uri ) ? $title_tpl : $link_tpl, - $page_uri, - esc_attr( $content ), - ( strlen( $content ) > 45 ? substr( $content, 0, 45 ) . '...' : $content ) - ); - } - - /** - * Post ID display. - * - * @param array $item Item. - * @return string - */ - public function column_post_id( $item ): string { - return sprintf( - '%s', - get_edit_post_link( $item['post_id'] ), - $item['post_id'] - ); - } - - /** - * Full row display, edited for grouping functionality. - * - * @param array $item Item. - */ - public function single_row( $item ) { - $cols = $this->get_columns(); - $colspan = count( $cols ); - $first_col = array_key_first( $cols ); - - if ( $this->latest_row !== $item[ $first_col ] ) { - echo ''; - echo sprintf( - '%s', - esc_attr( $colspan ), - esc_html( $item[ $first_col ] ) - ); - echo ''; - } - - $this->latest_row = $item[ $first_col ]; - $item[ $first_col ] = ''; - parent::single_row( $item ); - } - - /** - * Add action links to a row - * - * @param array $item Item. - * @param string $column_name Current column name. - * @param string $primary Primary column name. - * - * phpcs:disable WordPress.WP.I18n.TextDomainMismatch - */ - protected function handle_row_actions( $item, $column_name, $primary ) { - return $this->row_actions( - ( new RowActions() )->get_post_actions( $item, $column_name, $primary ) - ); - } - - /** - * Show only top tablenav (duplicate form post bug) - * - * @param string $which Tablenav identifier. - */ - protected function display_tablenav( $which ) { - if ( 'bottom' === $which ) { - echo '
'; - echo esc_html( parent::pagination( $which ) ); - echo '
'; - return; - } - parent::display_tablenav( $which ); - } - - /** - * Search parameters - */ - public function get_search_params(): Parameters { - return $this->search_params; - } - - /** - * Table URL - */ - public static function url(): string { - return admin_url( 'admin.php?page=plugin_blocks_report' ); - } - - /** - * Set table hooks - */ - public static function set_hooks(): void { - // Add redirection for filter action. - add_action( - 'admin_action_' . self::ACTION_NAME, - function () { - $nonce = $_GET['_wpnonce'] ?? null; - if ( ! wp_verify_nonce( $nonce, 'bulk-' . self::PLURAL ) ) { - wp_safe_redirect( self::url() ); - exit; - } - - $redirect_query = remove_query_arg( - [ '_wp_http_referer', '_wpnonce', 'action', 'filter_action' ], - \wp_parse_url( $_SERVER['REQUEST_URI'], PHP_URL_QUERY ) - ); - \parse_str( $redirect_query, $args ); - $args = array_filter( - $args, - fn( $e ) => ! empty( $e ) && '0' !== $e - ); - - wp_safe_redirect( add_query_arg( $args, self::url() ) ); - exit; - }, - 10 - ); - } -} diff --git a/classes/search/block/class-query.php b/classes/search/block/class-query.php deleted file mode 100644 index e487f1438..000000000 --- a/classes/search/block/class-query.php +++ /dev/null @@ -1,22 +0,0 @@ - $value ) { - if ( null === $value ) { - continue; - } - - $query = $query->with( $field, $value ); - } - - return $query; - } - - /** - * Generate query param object from HTTP request. - * - * @param array $request The request parameters. - * - * @return self Parameters - */ - public static function from_request( array $request ): self { - $text_search = ! empty( $request['s'] ) ? $request['s'] : null; - - if ( ! empty( $request['name'] ) ) { - $request['namespace'] = null; - } - - return self::from_array( - [ - 'namespace' => $request['namespace'] ?? null, - 'name' => $request['name'] ?? null, - 'content' => $text_search, - 'attributes' => $request['attributes'] ?? [ $text_search ], - 'post_status' => $request['post_status'] ?? self::DEFAULT_POST_STATUS, - 'post_type' => $request['post_type'] ?? null, - 'order' => $request['order'] ?? null, - ] - ); - } - - /** - * - * - * @param string $name The name. - * @param array $args The arguments. - * - * @throws \BadMethodCallException Method does not exists. - * - * @return mixed - */ - public function __call( string $name, array $args ) { - if ( strpos( $name, 'with_' ) === 0 ) { - $property = substr( $name, 5 ); - return $this->with( $property, $args[0] ); - } - - throw new \BadMethodCallException( 'Method ' . $name . ' does not exist.' ); - } - - /** - * Sets parameter - * - * @param string $name The name. - * @param mixed $value The value. - * - * @throws \BadMethodCallException Property not allowed. - * - * @return self Immutable parameter object. - */ - public function with( string $name, $value = null ): self { - $allowed = [ 'namespace', 'name', 'attributes', 'content', 'post_status', 'post_type', 'order' ]; - if ( ! in_array( $name, $allowed, true ) ) { - throw new \BadMethodCallException( 'Property ' . $name . ' does not exist.' ); - } - - $this->$name = $value ?? null; - return $this; - } - - /** - * Block namespace. - */ - public function namespace(): ?string { - return $this->namespace; - } - - /** - * Full block name. - */ - public function name(): ?string { - return $this->name; - } - - /** - * Block attributes. - */ - public function attributes(): ?array { - return $this->attributes; - } - - /** - * Block options content. - */ - public function content(): ?string { - return $this->content; - } - - /** - * List of required post status. - */ - public function post_status(): ?array { - return $this->post_status ?? self::DEFAULT_POST_STATUS; - } - - /** - * List of required post types. - */ - public function post_type(): ?array { - return $this->post_type ?? null; - } - - /** - * Columns names to sort on. - */ - public function order(): ?array { - return $this->order; - } -} diff --git a/classes/search/block/sql/class-like.php b/classes/search/block/sql/class-like.php deleted file mode 100644 index 8e9d4b029..000000000 --- a/classes/search/block/sql/class-like.php +++ /dev/null @@ -1,48 +0,0 @@ -params = $params; - } - - /** - * @return string - * @todo: $params->attributes not implemented. - */ - public function __toString(): string { - $block_ns = $this->params->namespace(); - $block_name = $this->params->name(); - - if ( 'core' === $block_ns ) { - $block = '%'; - } elseif ( $block_name && 0 === strpos( $block_name, 'core/' ) ) { - $block = explode( '/', $block_name )[1]; - } else { - $name = $block_name ? $block_name : '%'; - $block = $block_ns ? $block_ns . '/%' : $name; - } - $attrs = $this->params->content() ? '%' . $this->params->content() . '%' : '%'; - - return sprintf( '%%.*', $block, $attrs ); - } -} diff --git a/classes/search/block/sql/class-sqlquery.php b/classes/search/block/sql/class-sqlquery.php deleted file mode 100644 index 0199c085c..000000000 --- a/classes/search/block/sql/class-sqlquery.php +++ /dev/null @@ -1,113 +0,0 @@ -db = $db ? $db : $wpdb; - } - - /** - * @param Block\Query\Parameters ...$params_list Query parameters. - * @return int[] List of posts IDs. - */ - public function get_posts( Block\Query\Parameters ...$params_list ): array { - $query = $this->get_sql_query( ...$params_list ); - $results = $this->db->get_results( $query ); - - return array_map( - function ( $r ) { - return (int) $r->ID; - }, - $results - ); - } - - /** - * @param Block\Query\Parameters ...$params_list Query parameters. - * @return string SQL query string - * @throws \UnexpectedValueException Empty prepared query. - */ - private function get_sql_query( Block\Query\Parameters ...$params_list ): string { - // Prepare query parameters. - $like = []; - $status = []; - $type = []; - $order = []; - foreach ( $params_list as $params ) { - $like[] = ( new Like( $params ) )->__toString(); - $status = array_merge( $status, $params->post_status() ?? [] ); - $type = array_merge( $type, $params->post_type() ?? [] ); - $order = array_merge( $order, $params->order() ?? [] ); - } - $status = array_unique( array_filter( $status ) ); - $type = array_unique( array_filter( $type ) ); - $order = $this->parse_order( array_unique( array_filter( $order ) ) ); - - // Prepare query. - $sql_params = new SqlParameters(); - $sql = 'SELECT ID - FROM ' . $sql_params->identifier( $this->db->posts ) . ' - WHERE post_status IN ' . $sql_params->string_list( $status ); - if ( ! empty( $type ) ) { - $sql .= ' AND post_type IN ' . $sql_params->string_list( $type ); - } - foreach ( $like as $l ) { - $sql .= ' AND post_content LIKE ' . $sql_params->string( $l ) . ' '; - } - if ( ! empty( $order ) ) { - $sql .= ' ORDER BY ' . implode( ',', $order ); - } - - $query = $this->db->prepare( $sql, $sql_params->get_values() ); - if ( empty( $query ) ) { - throw new \UnexpectedValueException( 'Search query is invalid' ); - } - - return $query; - } - - /** - * Parse and filter order parameter - * - * @param string[] $order List of sort columns. - * @return string[] - */ - private function parse_order( array $order ): array { - $parsed = []; - foreach ( $order as $k ) { - switch ( $k ) { - case 'block_type': - break; - case 'post_id': - $parsed[] = 'ID'; - break; - default: - $parsed[] = $k; - } - } - return $parsed; - } -} diff --git a/classes/search/class-blocksearch.php b/classes/search/class-blocksearch.php deleted file mode 100644 index 36644ecd5..000000000 --- a/classes/search/class-blocksearch.php +++ /dev/null @@ -1,47 +0,0 @@ -query = $query ?? new SqlQuery(); - } - - /** - * @param Parameters $params Query parameters. - * @return int[] list of posts IDs. - */ - public function get_posts( Parameters $params ): array { - return $this->query->get_posts( $params ); - } - - /** - * @param string $block_name Query parameters. - * @return int[] list of posts IDs. - */ - public function get_posts_with_block( string $block_name ): array { - return $this->get_posts( - ( new Parameters() )->with_name( $block_name ) - ); - } -} diff --git a/classes/search/class-patternsearch.php b/classes/search/class-patternsearch.php deleted file mode 100644 index 1f59da75e..000000000 --- a/classes/search/class-patternsearch.php +++ /dev/null @@ -1,170 +0,0 @@ - true, - 'use_class' => true, - 'use_templates' => true, - ], - $opts - ); - - $patterns = array_map( - fn ( $pattern ) => PatternData::from_name( $pattern ), - $params->name() - ); - - return array_unique( - array_filter( - array_merge( - $opts['use_class'] ? $this->query_by_pattern_classname( $params, ...$patterns ) : [], - $opts['use_struct'] ? $this->query_by_pattern_blocks( $params, ...$patterns ) : [], - $opts['use_templates'] ? $this->query_by_pattern_template( $params, ...$patterns ) : [] - ) - ) - ); - } - - /** - * Search templates by namespace to reduce query count. - * - * @param Parameters $params Search parameters. - * @param PatternData ...$pattern_data Pattern data. - * @return int[] List of posts IDs. - */ - private function query_by_pattern_template( - Parameters $params, - PatternData ...$pattern_data - ): array { - // Query posts with all blocks of tree. - $templates = PatternUsage::patterns_templates_lookup_table(); - $block_query = new BlockSqlQuery(); - - $pattern_names = array_map( fn( $p ) => $p->name, $pattern_data ); - $template_names = array_map( fn( $n ) => $templates[ $n ], $pattern_names ); - $template_ns = array_filter( array_unique( array_map( fn( $n ) => explode( '/', $n )[0] ?? null, $template_names ) ) ); - - $post_ids = []; - foreach ( $template_ns as $ns ) { - $block_params = BlockSearchParameters::from_array( - [ - 'namespace' => $ns, - 'post_status' => $params->post_status() ?? self::DEFAULT_POST_STATUS, - 'post_type' => $params->post_type() ?? self::DEFAULT_POST_TYPE, - ] - ); - - $post_ids = array_merge( $post_ids, $block_query->get_posts( $block_params ) ); - } - - return array_unique( $post_ids ); - } - - /** - * @param Parameters $params Search parameters. - * @param PatternData ...$pattern_data Pattern data. - * @return int[] List of posts IDs. - */ - private function query_by_pattern_blocks( - Parameters $params, - PatternData ...$pattern_data - ): array { - // Query posts with all blocks of tree. - $block_query = new BlockSqlQuery(); - - $post_ids = []; - foreach ( $pattern_data as $pattern ) { - $block_params = array_map( - fn ( $block_name ) => BlockSearchParameters::from_array( - [ - 'name' => $block_name, - 'post_status' => $params->post_status() ?? self::DEFAULT_POST_STATUS, - 'post_type' => $params->post_type() ?? self::DEFAULT_POST_TYPE, - ] - ), - $pattern->block_list - ); - - $post_ids = array_merge( $post_ids, $block_query->get_posts( ...$block_params ) ); - } - - return array_unique( $post_ids ); - } - - /** - * Query posts by pattern classname. - * - * @param Parameters $params Search parameters. - * @param PatternData ...$pattern_data Pattern data. - * @return int[] List of posts IDs. - */ - private function query_by_pattern_classname( - Parameters $params, - PatternData ...$pattern_data - ): array { - $classes = array_map( - fn ( $p ) => $p->classname, - $pattern_data - ); - if ( empty( $classes ) ) { - return []; - } - - global $wpdb; - - $like = array_map( fn ( $c ) => "post_content LIKE '%%$c%%'", $classes ); - $like = implode( ' OR ', $like ); - - $sql_params = new SqlParameters(); - $query = 'SELECT ID - FROM ' . $sql_params->identifier( $wpdb->posts ) . ' - WHERE post_status IN ' . $sql_params->string_list( - $params->post_status() ?? self::DEFAULT_POST_STATUS - ) . ' - AND post_type IN ' . $sql_params->string_list( - $params->post_type() ?? self::DEFAULT_POST_TYPE - ) . ' - AND ( ' . $like . ' )'; - - $results = $wpdb->get_results( - $wpdb->prepare( $query, $sql_params->get_values() ) // phpcs:ignore - ); - - if ( ! is_array( $results ) ) { - return []; - } - - return array_map( - fn ( $r ) => (int) $r->ID, - $results - ); - } -} diff --git a/classes/search/class-rowactions.php b/classes/search/class-rowactions.php deleted file mode 100644 index 57a26e324..000000000 --- a/classes/search/class-rowactions.php +++ /dev/null @@ -1,76 +0,0 @@ -%s', - get_edit_post_link( $item['post_id'] ), - /* translators: %s: Post title. */ - esc_attr( sprintf( __( 'Edit “%s”', 'default' ), $title ) ), - __( 'Edit', 'default' ) - ); - - if ( in_array( $item['post_status'], [ 'pending', 'draft', 'future' ], true ) ) { - $preview_link = get_preview_post_link( $id ); - $actions['view'] = sprintf( - '%s', - esc_url( $preview_link ), - /* translators: %s: Post title. */ - esc_attr( sprintf( __( 'Preview “%s”', 'default' ), $title ) ), - __( 'Preview', 'default' ) - ); - } elseif ( 'trash' !== $item['post_status'] ) { - $actions['view'] = sprintf( - '%s', - get_permalink( $item['post_id'] ), - /* translators: %s: Post title. */ - esc_attr( sprintf( __( 'View “%s”', 'default' ), $title ) ), - __( 'View', 'default' ) - ); - } - - $actions['clone'] = '' . - esc_html_x( 'Clone', 'verb', 'duplicate-post' ) . ''; - - $actions['edit_as_new_draft'] = '' . - esc_html__( 'New Draft', 'duplicate-post' ) . - ''; - - return $actions; - } -} diff --git a/classes/search/pattern/class-contentstructure.php b/classes/search/pattern/class-contentstructure.php deleted file mode 100644 index 9d62d4ba9..000000000 --- a/classes/search/pattern/class-contentstructure.php +++ /dev/null @@ -1,180 +0,0 @@ -parser = new WP_Block_Parser(); - } - - /** - * Parse content as structure. - * - * @param string $content Post content. - */ - public function parse_content( string $content ): void { - $this->content = $content; - $this->make_content_tree( $this->content ); - $this->make_structure_signature( $this->tree ); - } - - /** - * @return string|null Content - */ - public function get_content(): ?string { - return $this->content; - } - - /** - * @return array Content tree - */ - public function get_content_tree(): array { - return $this->tree; - } - - /** - * @return string Content signature - */ - public function get_content_signature(): string { - return $this->signature; - } - - /** - * Make tree structure from content. - * - * @param string $content Post content. - */ - public function make_content_tree( string $content ): void { - $parsed = $this->parser->parse( $content ); - $tree = []; - while ( ! empty( $parsed ) ) { - /** @var array $block */ - $block = array_shift( $parsed ); - $tree[] = $this->make_tree( $block ); - } - - $this->tree = array_values( array_filter( $tree ) ); - } - - /** - * Make signature from tree structure. - * - * @param array $tree Tree. - */ - public function make_structure_signature( array $tree ): void { - $this->normalize_tree_for_signature( $tree ); - $signature = json_encode( $tree ); // phpcs:ignore WordPress.WP.AlternativeFunctions - - $this->signature = trim( $signature, '[]' ); - } - - /** - * Normalize tree to remove duplications. - * - * @param array $tree Tree. - */ - public function normalize_tree_for_signature( array &$tree ) { - // No classes in content signature. - if ( isset( $tree['classes'] ) ) { - unset( $tree['classes'] ); - } - - foreach ( $tree as $key => &$node ) { - // No classes in content signature. - if ( isset( $node['classes'] ) ) { - unset( $node['classes'] ); - } - - if ( empty( $node['children'] ) || ! is_array( $node['children'] ) ) { - continue; - } - - if ( 'core/columns' === $node['name'] ) { - $columns_count = count( $node['children'] ); - $unique_columns = array_unique( $node['children'], \SORT_REGULAR ); - $unique_count = count( $unique_columns ); - - if ( 1 === $unique_count && $columns_count > $unique_count ) { - $node['children'] = $unique_columns; - } - } - - if ( 'core/group' === $node['name'] ) { - $subgroups_count = count( $node['children'] ); - $unique_subgroups = array_unique( $node['children'], \SORT_REGULAR ); - $unique_count = count( $unique_subgroups ); - - if ( 1 === $unique_count && $subgroups_count > $unique_count ) { - $node['children'] = $unique_subgroups; - } - } - - if ( ! empty( $node['children'] ) ) { - $this->normalize_tree_for_signature( $node['children'] ); - } - } - } - - /** - * Make blocks tree - * - * @param array $block Block. - * - * @return null|array Tree representation of block content. - */ - public function make_tree( array $block ): ?array { - if ( empty( $block['blockName'] ) ) { - return null; - } - - return [ - 'name' => $block['blockName'], - 'classes' => array_filter( - explode( ' ', $block['attrs']['className'] ?? '' ) - ), - 'children' => empty( $block['innerBlocks'] ) - ? null - : array_values( - array_filter( - array_map( - fn ( $b ) => $this->make_tree( $b ), - $block['innerBlocks'] - ) - ) - ), - ]; - } -} diff --git a/classes/search/pattern/class-patterndata.php b/classes/search/pattern/class-patterndata.php deleted file mode 100644 index a3768887c..000000000 --- a/classes/search/pattern/class-patterndata.php +++ /dev/null @@ -1,125 +0,0 @@ -get_registered( $name ) - ); - } - - /** - * @param array $pattern Pattern data from registry. - */ - public static function from_pattern( array $pattern ): self { - $data = new self(); - - $data->name = $pattern['name']; - $data->title = $pattern['title']; - $data->content = $pattern['content'] ?? ''; - - $data->description = $pattern['description'] ?? null; - $data->viewport_width = $pattern['viewportWidth'] ?? null; - $data->block_types = $pattern['blockTypes'] ?? null; - $data->post_types = $pattern['postTypes'] ?? null; - $data->keywords = $pattern['keywords'] ?? null; - - $struct = new ContentStructure(); - $struct->parse_content( $data->content ); - - $data->structure = $struct->get_content_tree(); - $data->signature = $struct->get_content_signature(); - $data->block_list = BlockList::parse_block_list( $data->content ); - $data->classname = self::make_classname( $data->name ); - - return $data; - } - - /** - * @param string $name Pattern name. - */ - public static function make_classname( string $name ): string { - return 'is-pattern-' . preg_replace( '#[^_a-zA-Z0-9-]#', '-', $name ); - } -} diff --git a/classes/search/pattern/class-patternusage.php b/classes/search/pattern/class-patternusage.php deleted file mode 100644 index 589a0b600..000000000 --- a/classes/search/pattern/class-patternusage.php +++ /dev/null @@ -1,204 +0,0 @@ -search = $search ?? new PatternSearch(); - $this->parser = $parser ?? new WP_Block_Parser(); - $this->registry = WP_Block_Patterns_Registry::get_instance(); - } - - /** - * @param Parameters $params Search parameters. - * @param array $opts Options. - * @return array - */ - public function get_patterns( Parameters $params, array $opts = [] ): array { - $opts = array_merge( - [ - 'use_struct' => true, - 'use_class' => true, - 'use_templates' => true, - ], - $opts - ); - - $posts_ids = $this->search->get_posts( $params, $opts ); - - return $this->get_filtered_patterns( $posts_ids, $params, $opts ); - } - - /** - * @param int[] $posts_ids Posts ids. - * @param Parameters $params Search parameters. - * @param array $opts Parsing options. - * @return array - */ - private function get_filtered_patterns( - array $posts_ids, - Parameters $params, - array $opts - ): array { - $chunks = array_chunk( $posts_ids, 50 ); - - $post_args = [ - 'orderby' => empty( $params->order() ) ? null : array_fill_keys( $params->order(), 'ASC' ), - 'post_status' => $params->post_status(), - 'post_type' => $params->post_type(), - ]; - - if ( $opts['use_templates'] ) { - $templates = self::patterns_templates_lookup_table(); - } - - $patterns = []; - foreach ( $chunks as $chunk ) { - /** @var WP_Post[] $posts */ - $posts = get_posts( array_merge( [ 'include' => $chunk ], $post_args ) ); - - foreach ( $posts as $post ) { - $post_struct = new ContentStructure(); - $post_struct->parse_content( $post->post_content ?? '' ); - - foreach ( $params->name() as $pattern_name ) { - $pattern = PatternData::from_name( $pattern_name ); - - // struct matches. - if ( $opts['use_struct'] ) { - $struct_occ = substr_count( - $post_struct->get_content_signature(), - $pattern->signature - ); - if ( $struct_occ > 0 ) { - $patterns[] = $this->format_pattern_data( - $pattern, - $post, - $struct_occ, - 'structure' - ); - } - } - - // class matches. - if ( $opts['use_class'] ) { - $class_occ = round( - substr_count( $post->post_content, $pattern->classname ) / 2 - ); - if ( $class_occ > 0 ) { - $patterns[] = $this->format_pattern_data( - $pattern, - $post, - $class_occ, - 'classname' - ); - } - } - - if ( $opts['use_templates'] && ! empty( $templates[ $pattern->name ] ) ) { - $template_occ = substr_count( - $post->post_content, - '