Skip to content

Commit

Permalink
PLANET-7527 Move blocks report into master theme
Browse files Browse the repository at this point in the history
  • Loading branch information
sagarsdeshmukh committed Jun 5, 2024
1 parent c5985de commit e220a29
Show file tree
Hide file tree
Showing 27 changed files with 3,659 additions and 0 deletions.
292 changes: 292 additions & 0 deletions src/BlockReportSearch/Block/BlockUsage.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,292 @@
<?php

/**
* Table displaying blocks usage
*
* @package P4BKS\Controllers
*/

namespace P4\MasterTheme\BlockReportSearch\Block;

use WP_Block_Parser;
use P4\MasterTheme\BlockReportSearch\BlockSearch;
use P4\MasterTheme\BlockReportSearch\Block\Query\Parameters;

/**
* Present block usage, using native WordPress table
*/
class BlockUsage
{
/**
* @var BlockSearch
*/
private $search;

/**
* @var WP_Block_Parser
*/
private $parser;

/**
* @var int[]
*/
private $posts_ids;

/**
* @var WP_Post[]
*/
private $posts;

/**
* @var array
*/
private $blocks;

/**
* @param BlockSearch $search Search class.
* @param WP_Block_Parser $parser Block parser.
*/
public function __construct(
?BlockSearch $search = null,
?WP_Block_Parser $parser = null
) {
$this->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'])) {
continue;
}

$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);
}
}
99 changes: 99 additions & 0 deletions src/BlockReportSearch/Block/BlockUsageApi.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
<?php

/**
* Table displaying blocks usage
*
* @package P4BKS\Controllers
*/

namespace P4\MasterTheme\BlockReportSearch\Block;

use P4\MasterTheme\BlockReportSearch\Block\Query\Parameters;

/**
* Block usage API
*/
class BlockUsageApi
{
public const DEFAULT_POST_STATUS = [ 'publish' ];

/**
* @var BlockUsage
*/
private $usage;

/**
* @var Parameters
*/
private $params;

/**
* @var array[] Blocks.
*/
private $items;

/**
* Constructor
*/
public function __construct()
{
$this->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);
}
}
Loading

0 comments on commit e220a29

Please sign in to comment.