*/ private static $post_count_cache = array(); /** * Register blocks with WordPress. */ public function __construct() { Hook_Registry::add_action( 'init', array( $this, 'register_blocks' ) ); Hook_Registry::add_action( 'enqueue_block_editor_assets', array( $this, 'enqueue_block_editor_assets' ) ); Hook_Registry::add_filter( 'block_editor_rest_api_preload_paths', array( $this, 'add_custom_preload_paths' ) ); } /** * Add custom preload paths for the REST API. * * @since 4.5.0 * * @param array $preload_paths Existing preload paths. * @return array Modified preload paths. */ public static function add_custom_preload_paths( $preload_paths ) { $preload_paths[] = '/wp/v2/top-10/v1/counter'; return $preload_paths; } /** * Registers the block using the metadata loaded from the `block.json` file. * Behind the scenes, it registers also all assets so they can be enqueued * through the block editor in the corresponding context. * * @since 3.1.0 */ public function register_blocks() { // Define an array of blocks with their paths and optional render callbacks. $blocks = array( 'popular-posts' => array( 'path' => __DIR__ . '/build/popular-posts/', 'render_callback' => array( $this, 'render_block_popular_posts' ), ), 'post-count' => array( 'path' => __DIR__ . '/build/post-count/', 'render_callback' => array( $this, 'render_block_post_count' ), ), ); $default_blocks = $blocks; /** * Filters the blocks registered by the plugin. * * @since 4.0.0 * * @param array $blocks Array of blocks registered by the plugin. */ $blocks = apply_filters( 'tptn_register_blocks', $blocks ); $manifest = __DIR__ . '/build/blocks-manifest.php'; if ( function_exists( 'wp_register_block_types_from_metadata_collection' ) && file_exists( $manifest ) ) { wp_register_block_types_from_metadata_collection( __DIR__ . '/build', $manifest ); // Register any blocks added or changed by the filter that fall outside the manifest. foreach ( $blocks as $block_name => $block_data ) { if ( isset( $default_blocks[ $block_name ] ) && $default_blocks[ $block_name ] === $block_data ) { continue; } $args = array(); if ( isset( $block_data['render_callback'] ) ) { $args['render_callback'] = $block_data['render_callback']; } register_block_type( $block_data['path'], $args ); } return; } // Loop through each block and register it. foreach ( $blocks as $block_name => $block_data ) { $args = array(); // If a render callback is provided, add it to the args. if ( isset( $block_data['render_callback'] ) ) { $args['render_callback'] = $block_data['render_callback']; } register_block_type( $block_data['path'], $args ); } } /** * Renders the `top-10/popular-posts` block on server. * * @since 3.0.0 * @param array $attributes The block attributes. * * @return string Returns the post content with popular posts added. */ public static function render_block_popular_posts( $attributes ) { $attributes['extra_class'] = isset( $attributes['className'] ) ? esc_attr( $attributes['className'] ) : ''; $defaults = array_merge( \tptn_get_settings_with_defaults(), array( 'is_block' => 1, ) ); $arguments = wp_parse_args( $attributes, $defaults ); if ( isset( $attributes['other_attributes'] ) ) { $arguments = wp_parse_args( $attributes['other_attributes'], $arguments ); } /** * Filters arguments passed to get_tptn for the block. * * @since 3.0.0 * * @param array $arguments Top 10 block options array. * @param array $attributes Block attributes array. */ $arguments = apply_filters( 'tptn_block_options', $arguments, $attributes ); // Enqueue the stylesheet for the selected style for this block. $style_array = Styles_Handler::get_style( $arguments['tptn_styles'] ); if ( ! empty( $style_array['name'] ) ) { $style = $style_array['name']; $extra_css = $style_array['extra_css']; $is_rtl = is_rtl(); $handle = "tptn-style-{$style}"; $already_enqueued = wp_style_is( $handle, 'enqueued' ); wp_register_style( $handle, plugins_url( Styles_Handler::get_stylesheet_path( $style, $is_rtl ), TOP_TEN_PLUGIN_FILE ), array(), TOP_TEN_VERSION ); wp_enqueue_style( $handle ); if ( ! $already_enqueued && ! empty( $extra_css ) ) { wp_add_inline_style( $handle, $extra_css ); } } return \WebberZone\Top_Ten\Frontend\Display::pop_posts( $arguments ); } /** * Renders the `core/post-title` block on the server. * * @since 4.0.0 * * @param array $attributes Block attributes. * @param string $content Block default content. * @param \WP_Block $block Block instance. * * @return string Returns the post count. */ public static function render_block_post_count( $attributes, $content, $block ) { if ( ! isset( $block->context['postId'] ) ) { return ''; } $output = ''; $args = array(); $post_id = absint( $block->context['postId'] ); $counter_type = isset( $attributes['counter'] ) ? $attributes['counter'] : 'total'; $blog_id = isset( $attributes['blogId'] ) ? absint( $attributes['blogId'] ) : get_current_blog_id(); $from_date = isset( $attributes['fromDate'] ) ? $attributes['fromDate'] : ''; $to_date = isset( $attributes['toDate'] ) ? $attributes['toDate'] : ''; $text_before = isset( $attributes['textBefore'] ) ? $attributes['textBefore'] : ''; $text_after = isset( $attributes['textAfter'] ) ? $attributes['textAfter'] : ''; $text_advanced = isset( $attributes['textAdvanced'] ) ? $attributes['textAdvanced'] : ''; $advanced_mode = isset( $attributes['advancedMode'] ) ? $attributes['advancedMode'] : false; $svg_code = isset( $attributes['svgCode'] ) ? $attributes['svgCode'] : ''; $svg_icon_size = isset( $attributes['svgIconSize'] ) ? $attributes['svgIconSize'] : '1'; $svg_icon_size_unit = isset( $attributes['svgIconSizeUnit'] ) ? $attributes['svgIconSizeUnit'] : 'em'; $svg_padding_values = isset( $attributes['svgPaddingValues'] ) ? $attributes['svgPaddingValues'] : array( 0, 0, 0, 0 ); $svg_padding_units = isset( $attributes['svgPaddingUnits'] ) ? $attributes['svgPaddingUnits'] : array( 'px', 'px', 'px', 'px' ); $svg_icon_location = isset( $attributes['svgIconLocation'] ) ? $attributes['svgIconLocation'] : 'before'; $args['format_number'] = isset( $attributes['numberFormat'] ) ? $attributes['numberFormat'] : false; if ( ! empty( $from_date ) ) { $args['from_date'] = $from_date; } if ( ! empty( $to_date ) ) { $args['to_date'] = $to_date; } $classes = array(); $classes[] = 'wp-block-tptn-post-count'; $classes[] = 'tptn-post-count'; if ( isset( $attributes['textAlign'] ) ) { $classes[] = 'has-text-align-' . $attributes['textAlign']; } if ( isset( $attributes['style']['elements']['link']['color']['text'] ) ) { $classes[] = 'has-link-color'; } $counter = null; if ( ! $advanced_mode || empty( $text_advanced ) ) { $counter = self::get_cached_post_count( $post_id, $counter_type, $blog_id, $args ); $output = sprintf( '%1$s%2$s%3$s', wp_kses_post( (string) $text_before ), esc_html( (string) $counter ), wp_kses_post( (string) $text_after ) ); } else { $classes[] = 'tptn-advanced-mode'; $output = $text_advanced; foreach ( array( 'total', 'daily', 'overall' ) as $type ) { if ( false !== strpos( $text_advanced, "%{$type}count%" ) ) { $count = self::get_cached_post_count( $post_id, $type, $blog_id, $args ); $output = str_replace( "%{$type}count%", (string) $count, $output ); } } } $output = sprintf( '%s', $output ); // Add the icon to the output. $icon = ''; if ( ! empty( $svg_code ) ) { $padding_style = sprintf( 'padding:%s%s %s%s %s%s %s%s;', esc_attr( $svg_padding_values[0] ), esc_attr( $svg_padding_units[0] ), esc_attr( $svg_padding_values[1] ), esc_attr( $svg_padding_units[1] ), esc_attr( $svg_padding_values[2] ), esc_attr( $svg_padding_units[2] ), esc_attr( $svg_padding_values[3] ), esc_attr( $svg_padding_units[3] ) ); $svg_style = sprintf( 'width: %1$s%2$s; height: %1$s%2$s; %3$s', esc_attr( $svg_icon_size ), esc_attr( $svg_icon_size_unit ), $padding_style ); $svg_style = esc_attr( $svg_style ); if ( preg_match( '/]*style="([^"]*)"/i', $svg_code, $matches ) ) { $existing_style = $matches[1]; $new_style = $svg_style . $existing_style; $svg_code_with_style = preg_replace( '/(]*style=")[^"]*(")/i', '${1}' . $new_style . '${2}', $svg_code, 1 ); } else { $svg_code_with_style = preg_replace( '/%1$s', wp_kses( $svg_code_with_style, self::get_allowed_svg_tags() ) ); } if ( ! empty( $icon ) ) { if ( 'before' === $svg_icon_location ) { $output = $icon . $output; } else { $output = $output . $icon; } } $wrapper_attributes = get_block_wrapper_attributes( array( 'class' => implode( ' ', $classes ) ) ); return sprintf( '%2$s', $wrapper_attributes, wp_kses( $output, self::get_allowed_svg_tags() ) ); } /** * Get a request-cached post count. * * @param int $post_id Post ID. * @param string $counter Counter type. * @param int $blog_id Blog ID. * @param array $args Additional counter arguments. * @return int|string Post count. */ private static function get_cached_post_count( $post_id, $counter, $blog_id, $args ) { $cache_key = md5( (string) wp_json_encode( array( $post_id, $counter, $blog_id, $args ) ) ); if ( ! array_key_exists( $cache_key, self::$post_count_cache ) ) { self::$post_count_cache[ $cache_key ] = Counter::get_post_count_only( $post_id, $counter, $blog_id, $args ); } return self::$post_count_cache[ $cache_key ]; } /** * Get allowed SVG tags for wp_kses. * * @since 4.0.0 * * @return array Allowed SVG tags and attributes. */ private static function get_allowed_svg_tags() { $allowed_tags = wp_kses_allowed_html( 'post' ); $svg_tags = array( 'svg' => array( 'class' => true, 'aria-hidden' => true, 'aria-labelledby' => true, 'role' => true, 'xmlns' => true, 'width' => true, 'height' => true, 'fill' => true, 'id' => true, 'data-name' => true, 'viewbox' => true, 'style' => true, ), 'g' => array( 'fill' => true, 'stroke-width' => true, 'stroke-linecap' => true, 'stroke-linejoin' => true, 'id' => true, ), 'defs' => array(), 'style' => array( 'type' => true, ), 'line' => array( 'class' => true, 'x1' => true, 'y1' => true, 'x2' => true, 'y2' => true, 'fill' => true, 'stroke' => true, 'stroke-width' => true, 'stroke-miterlimit' => true, ), 'path' => array( 'class' => true, 'd' => true, 'fill' => true, 'stroke' => true, 'stroke-linecap' => true, 'stroke-linejoin' => true, 'stroke-width' => true, ), 'polygon' => array( 'class' => true, 'points' => true, 'fill' => true, 'stroke' => true, 'stroke-width' => true, 'stroke-linecap' => true, 'stroke-linejoin' => true, 'stroke-miterlimit' => true, ), 'polyline' => array( 'class' => true, 'points' => true, 'fill' => true, 'stroke' => true, 'stroke-width' => true, 'stroke-linecap' => true, 'stroke-linejoin' => true, 'stroke-miterlimit' => true, ), 'circle' => array( 'class' => true, 'cx' => true, 'cy' => true, 'r' => true, 'fill' => true, 'stroke' => true, 'stroke-width' => true, ), 'rect' => array( 'class' => true, 'x' => true, 'y' => true, 'width' => true, 'height' => true, 'rx' => true, 'ry' => true, 'fill' => true, 'stroke' => true, 'stroke-width' => true, ), 'ellipse' => array( 'class' => true, 'cx' => true, 'cy' => true, 'rx' => true, 'ry' => true, 'fill' => true, 'stroke' => true, 'stroke-width' => true, ), 'use' => array( 'class' => true, 'xlink:href' => true, ), 'text' => array( 'class' => true, 'x' => true, 'y' => true, 'dy' => true, 'text-anchor' => true, 'fill' => true, ), 'tspan' => array( 'class' => true, 'x' => true, 'y' => true, 'dy' => true, 'text-anchor' => true, 'fill' => true, ), 'symbol' => array( 'id' => true, 'viewBox' => true, 'preserveAspectRatio' => true, ), 'clipPath' => array( 'id' => true, ), 'mask' => array( 'id' => true, ), 'image' => array( 'x' => true, 'y' => true, 'width' => true, 'height' => true, 'xlink:href' => true, ), 'foreignObject' => array( 'x' => true, 'y' => true, 'width' => true, 'height' => true, ), ); return array_merge( $allowed_tags, $svg_tags ); } /** * Enqueue scripts and styles for the block editor. * * @since 3.1.0 */ public static function enqueue_block_editor_assets() { $styles = Settings::get_styles(); $file_prefix = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? '' : '.min'; foreach ( $styles as $style ) { $style_array = Styles_Handler::get_style( $style['id'] ); if ( ! empty( $style_array['name'] ) ) { $style = $style_array['name']; $extra_css = $style_array['extra_css']; $base_path = 'includes/frontend/css/'; if ( false !== strpos( $style, '-pro' ) ) { $base_path = 'includes/pro/frontend/css/'; } wp_enqueue_style( "popular-posts-block-editor-{$style}", plugins_url( "{$base_path}{$style}{$file_prefix}.css", TOP_TEN_PLUGIN_FILE ), array( 'wp-edit-blocks' ), TOP_TEN_VERSION ); wp_add_inline_style( "popular-posts-block-editor-{$style}", $extra_css ); } } } }