PluginProbe
WebberZone Top 10 — Popular Posts / 4.4.0
WebberZone Top 10 — Popular Posts v4.4.0
4.5.1 4.5.0 4.4.3 4.4.2 4.4.1 4.4.0 4.3.4 4.3.3 4.3.2 4.3.1 4.3.0 trunk 1.0 1.0.1 1.1 1.2 1.3 1.4 1.4.1 1.5 1.5.1 1.5.2 1.5.3 1.6 1.6.1 All 117 releases
top-10 / includes / frontend / blocks / class-blocks.php

class-blocks.php in WebberZone Top 10 — Popular Posts 4.4.0, at includes/frontend/blocks/class-blocks.php

484 lines 13.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Blocks class.
4 *
5 * @package WebberZone\Top_Ten\Frontend\Blocks
6 */
7
8 namespace WebberZone\Top_Ten\Frontend\Blocks;
9
10 use WebberZone\Top_Ten\Admin\Settings;
11 use WebberZone\Top_Ten\Counter;
12 use WebberZone\Top_Ten\Frontend\Styles_Handler;
13 use WebberZone\Top_Ten\Util\Hook_Registry;
14
15 if ( ! defined( 'WPINC' ) ) {
16 die;
17 }
18
19 /**
20 * Blocks class.
21 *
22 * @since 3.3.0
23 */
24 class Blocks {
25
26 /**
27 * Register blocks with WordPress.
28 */
29 public function __construct() {
30 Hook_Registry::add_action( 'init', array( $this, 'register_blocks' ) );
31 Hook_Registry::add_action( 'enqueue_block_editor_assets', array( $this, 'enqueue_block_editor_assets' ) );
32 Hook_Registry::add_filter( 'block_editor_rest_api_preload_paths', array( $this, 'add_custom_preload_paths' ) );
33 }
34
35 /**
36 * Registers the block using the metadata loaded from the `block.json` file.
37 * Behind the scenes, it registers also all assets so they can be enqueued
38 * through the block editor in the corresponding context.
39 *
40 * @since 3.1.0
41 */
42 public function register_blocks() {
43 // Define an array of blocks with their paths and optional render callbacks.
44 $blocks = array(
45 'popular-posts' => array(
46 'path' => __DIR__ . '/build/popular-posts/',
47 'render_callback' => array( $this, 'render_block_popular_posts' ),
48 ),
49 'post-count' => array(
50 'path' => __DIR__ . '/build/post-count/',
51 'render_callback' => array( $this, 'render_block_post_count' ),
52 ),
53 );
54
55 /**
56 * Filters the blocks registered by the plugin.
57 *
58 * @since 4.0.0
59 *
60 * @param array $blocks Array of blocks registered by the plugin.
61 */
62 $blocks = apply_filters( 'tptn_register_blocks', $blocks );
63
64 // Loop through each block and register it.
65 foreach ( $blocks as $block_name => $block_data ) {
66 $args = array();
67
68 // If a render callback is provided, add it to the args.
69 if ( isset( $block_data['render_callback'] ) ) {
70 $args['render_callback'] = $block_data['render_callback'];
71 }
72
73 register_block_type( $block_data['path'], $args );
74 }
75 }
76
77 /**
78 * Renders the `top-10/popular-posts` block on server.
79 *
80 * @since 3.0.0
81 * @param array $attributes The block attributes.
82 *
83 * @return string Returns the post content with popular posts added.
84 */
85 public static function render_block_popular_posts( $attributes ) {
86
87 $attributes['extra_class'] = isset( $attributes['className'] ) ? esc_attr( $attributes['className'] ) : '';
88
89 $defaults = array_merge(
90 \tptn_get_settings_with_defaults(),
91 array(
92 'is_block' => 1,
93 )
94 );
95
96 $arguments = wp_parse_args( $attributes, $defaults );
97
98 if ( isset( $attributes['other_attributes'] ) ) {
99 $arguments = wp_parse_args( $attributes['other_attributes'], $arguments );
100 }
101
102 /**
103 * Filters arguments passed to get_tptn for the block.
104 *
105 * @since 3.0.0
106 *
107 * @param array $arguments Top 10 block options array.
108 * @param array $attributes Block attributes array.
109 */
110 $arguments = apply_filters( 'tptn_block_options', $arguments, $attributes );
111
112 // Enqueue the stylesheet for the selected style for this block.
113 $style_array = Styles_Handler::get_style( $arguments['tptn_styles'] );
114
115 if ( ! empty( $style_array['name'] ) ) {
116 $style = $style_array['name'];
117 $extra_css = $style_array['extra_css'];
118 $is_rtl = is_rtl();
119 $handle = "tptn-style-{$style}";
120
121 $already_enqueued = wp_style_is( $handle, 'enqueued' );
122
123 wp_register_style(
124 $handle,
125 plugins_url( Styles_Handler::get_stylesheet_path( $style, $is_rtl ), TOP_TEN_PLUGIN_FILE ),
126 array(),
127 TOP_TEN_VERSION
128 );
129 wp_enqueue_style( $handle );
130
131 if ( ! $already_enqueued && ! empty( $extra_css ) ) {
132 wp_add_inline_style( $handle, $extra_css );
133 }
134 }
135
136 return \WebberZone\Top_Ten\Frontend\Display::pop_posts( $arguments );
137 }
138
139 /**
140 * Renders the `core/post-title` block on the server.
141 *
142 * @since 4.0.0
143 *
144 * @param array $attributes Block attributes.
145 * @param string $content Block default content.
146 * @param \WP_Block $block Block instance.
147 *
148 * @return string Returns the post count.
149 */
150 public static function render_block_post_count( $attributes, $content, $block ) {
151 if ( ! isset( $block->context['postId'] ) ) {
152 return '';
153 }
154 $output = '';
155 $args = array();
156
157 $post_id = absint( $block->context['postId'] );
158 $counter = isset( $attributes['counter'] ) ? $attributes['counter'] : 'total';
159 $blog_id = isset( $attributes['blogId'] ) ? $attributes['blogId'] : get_current_blog_id();
160 $from_date = isset( $attributes['fromDate'] ) ? $attributes['fromDate'] : '';
161 $to_date = isset( $attributes['toDate'] ) ? $attributes['toDate'] : '';
162 $text_before = isset( $attributes['textBefore'] ) ? $attributes['textBefore'] : '';
163 $text_after = isset( $attributes['textAfter'] ) ? $attributes['textAfter'] : '';
164 $text_advanced = isset( $attributes['textAdvanced'] ) ? $attributes['textAdvanced'] : '';
165 $advanced_mode = isset( $attributes['advancedMode'] ) ? $attributes['advancedMode'] : false;
166 $svg_code = isset( $attributes['svgCode'] ) ? $attributes['svgCode'] : '';
167 $svg_icon_size = isset( $attributes['svgIconSize'] ) ? $attributes['svgIconSize'] : '1';
168 $svg_icon_size_unit = isset( $attributes['svgIconSizeUnit'] ) ? $attributes['svgIconSizeUnit'] : 'em';
169 $svg_padding_values = isset( $attributes['svgPaddingValues'] ) ? $attributes['svgPaddingValues'] : array( 0, 0, 0, 0 );
170 $svg_padding_units = isset( $attributes['svgPaddingUnits'] ) ? $attributes['svgPaddingUnits'] : array( 'px', 'px', 'px', 'px' );
171 $svg_icon_location = isset( $attributes['svgIconLocation'] ) ? $attributes['svgIconLocation'] : 'before';
172
173 $args['format_number'] = isset( $attributes['numberFormat'] ) ? $attributes['numberFormat'] : false;
174
175 if ( ! empty( $from_date ) ) {
176 $args['from_date'] = $from_date;
177 }
178 if ( ! empty( $to_date ) ) {
179 $args['to_date'] = $to_date;
180 }
181
182 $counter = Counter::get_post_count_only( $post_id, $counter, $blog_id, $args );
183
184 $classes = array();
185 $classes[] = 'wp-block-tptn-post-count';
186 $classes[] = 'tptn-post-count';
187
188 if ( isset( $attributes['textAlign'] ) ) {
189 $classes[] = 'has-text-align-' . $attributes['textAlign'];
190 }
191 if ( isset( $attributes['style']['elements']['link']['color']['text'] ) ) {
192 $classes[] = 'has-link-color';
193 }
194
195 if ( ! $advanced_mode ) {
196 $output = sprintf(
197 '%1$s%2$s%3$s',
198 wp_kses_post( (string) $text_before ),
199 esc_html( (string) $counter ),
200 wp_kses_post( (string) $text_after )
201 );
202 } elseif ( ! empty( $text_advanced ) ) {
203 $classes[] = 'tptn-advanced-mode';
204 $output = $text_advanced;
205
206 foreach ( array( 'total', 'daily', 'overall' ) as $type ) {
207 if ( false !== strpos( $text_advanced, "%{$type}count%" ) ) {
208 $count = Counter::get_post_count_only( $post_id, $type, $blog_id, $args );
209
210 $output = str_replace(
211 "%{$type}count%",
212 (string) $count,
213 $output
214 );
215 }
216 }
217 }
218
219 $output = sprintf(
220 '<span class="tptn-post-count-text">%s</span>',
221 $output
222 );
223
224 // Add the icon to the output.
225 $icon = '';
226 if ( ! empty( $svg_code ) ) {
227 $padding_style = sprintf(
228 'padding:%s%s %s%s %s%s %s%s;',
229 esc_attr( $svg_padding_values[0] ),
230 esc_attr( $svg_padding_units[0] ),
231 esc_attr( $svg_padding_values[1] ),
232 esc_attr( $svg_padding_units[1] ),
233 esc_attr( $svg_padding_values[2] ),
234 esc_attr( $svg_padding_units[2] ),
235 esc_attr( $svg_padding_values[3] ),
236 esc_attr( $svg_padding_units[3] )
237 );
238
239 $svg_style = sprintf(
240 'width: %1$s%2$s; height: %1$s%2$s; %3$s',
241 esc_attr( $svg_icon_size ),
242 esc_attr( $svg_icon_size_unit ),
243 $padding_style
244 );
245
246 $svg_style = esc_attr( $svg_style );
247
248 if ( preg_match( '/<svg[^>]*style="([^"]*)"/i', $svg_code, $matches ) ) {
249 $existing_style = $matches[1];
250 $new_style = $svg_style . $existing_style;
251
252 $svg_code_with_style = preg_replace( '/(<svg[^>]*style=")[^"]*(")/i', '${1}' . $new_style . '${2}', $svg_code, 1 );
253 } else {
254 $svg_code_with_style = preg_replace( '/<svg /', '<svg style="' . $svg_style . '" ', $svg_code, 1 );
255 }
256
257 $icon = sprintf(
258 '<span class="tptn-post-count-icon">%1$s</span>',
259 wp_kses( $svg_code_with_style, self::get_allowed_svg_tags() )
260 );
261 $icon = '<span class="tptn-post-count-icon">' . $svg_code_with_style . '</span>';
262 }
263 if ( ! empty( $icon ) ) {
264 if ( 'before' === $svg_icon_location ) {
265 $output = $icon . $output;
266 } else {
267 $output = $output . $icon;
268 }
269 }
270
271 $wrapper_attributes = get_block_wrapper_attributes( array( 'class' => implode( ' ', $classes ) ) );
272
273 return sprintf(
274 '<span %1$s>%2$s</span>',
275 $wrapper_attributes,
276 wp_kses( $output, self::get_allowed_svg_tags() )
277 );
278 }
279
280 /**
281 * Get allowed SVG tags for wp_kses.
282 *
283 * @since 4.0.0
284 *
285 * @return array Allowed SVG tags and attributes.
286 */
287 private static function get_allowed_svg_tags() {
288 $allowed_tags = wp_kses_allowed_html( 'post' );
289
290 $svg_tags = array(
291 'svg' => array(
292 'class' => true,
293 'aria-hidden' => true,
294 'aria-labelledby' => true,
295 'role' => true,
296 'xmlns' => true,
297 'width' => true,
298 'height' => true,
299 'fill' => true,
300 'id' => true,
301 'data-name' => true,
302 'viewbox' => true,
303 'style' => true,
304 ),
305 'g' => array(
306 'fill' => true,
307 'stroke-width' => true,
308 'stroke-linecap' => true,
309 'stroke-linejoin' => true,
310 'id' => true,
311 ),
312 'defs' => array(),
313 'style' => array(
314 'type' => true,
315 ),
316 'line' => array(
317 'class' => true,
318 'x1' => true,
319 'y1' => true,
320 'x2' => true,
321 'y2' => true,
322 'fill' => true,
323 'stroke' => true,
324 'stroke-width' => true,
325 'stroke-miterlimit' => true,
326 ),
327 'path' => array(
328 'class' => true,
329 'd' => true,
330 'fill' => true,
331 'stroke' => true,
332 'stroke-linecap' => true,
333 'stroke-linejoin' => true,
334 'stroke-width' => true,
335 ),
336 'polygon' => array(
337 'class' => true,
338 'points' => true,
339 'fill' => true,
340 'stroke' => true,
341 'stroke-width' => true,
342 'stroke-linecap' => true,
343 'stroke-linejoin' => true,
344 'stroke-miterlimit' => true,
345 ),
346 'polyline' => array(
347 'class' => true,
348 'points' => true,
349 'fill' => true,
350 'stroke' => true,
351 'stroke-width' => true,
352 'stroke-linecap' => true,
353 'stroke-linejoin' => true,
354 'stroke-miterlimit' => true,
355 ),
356 'circle' => array(
357 'class' => true,
358 'cx' => true,
359 'cy' => true,
360 'r' => true,
361 'fill' => true,
362 'stroke' => true,
363 'stroke-width' => true,
364 ),
365 'rect' => array(
366 'class' => true,
367 'x' => true,
368 'y' => true,
369 'width' => true,
370 'height' => true,
371 'rx' => true,
372 'ry' => true,
373 'fill' => true,
374 'stroke' => true,
375 'stroke-width' => true,
376 ),
377 'ellipse' => array(
378 'class' => true,
379 'cx' => true,
380 'cy' => true,
381 'rx' => true,
382 'ry' => true,
383 'fill' => true,
384 'stroke' => true,
385 'stroke-width' => true,
386 ),
387 'use' => array(
388 'class' => true,
389 'xlink:href' => true,
390 ),
391 'text' => array(
392 'class' => true,
393 'x' => true,
394 'y' => true,
395 'dy' => true,
396 'text-anchor' => true,
397 'fill' => true,
398 ),
399 'tspan' => array(
400 'class' => true,
401 'x' => true,
402 'y' => true,
403 'dy' => true,
404 'text-anchor' => true,
405 'fill' => true,
406 ),
407 'symbol' => array(
408 'id' => true,
409 'viewBox' => true,
410 'preserveAspectRatio' => true,
411 ),
412 'clipPath' => array(
413 'id' => true,
414 ),
415 'mask' => array(
416 'id' => true,
417 ),
418 'image' => array(
419 'x' => true,
420 'y' => true,
421 'width' => true,
422 'height' => true,
423 'xlink:href' => true,
424 ),
425 'foreignObject' => array(
426 'x' => true,
427 'y' => true,
428 'width' => true,
429 'height' => true,
430 ),
431 );
432
433 return array_merge( $allowed_tags, $svg_tags );
434 }
435
436 /**
437 * Enqueue scripts and styles for the block editor.
438 *
439 * @since 3.1.0
440 */
441 public static function enqueue_block_editor_assets() {
442
443 $styles = Settings::get_styles();
444 $file_prefix = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? '' : '.min';
445
446 foreach ( $styles as $style ) {
447
448 $style_array = Styles_Handler::get_style( $style['id'] );
449
450 if ( ! empty( $style_array['name'] ) ) {
451 $style = $style_array['name'];
452 $extra_css = $style_array['extra_css'];
453
454 $base_path = 'includes/frontend/css/';
455 if ( false !== strpos( $style, '-pro' ) ) {
456 $base_path = 'includes/pro/frontend/css/';
457 }
458
459 wp_enqueue_style(
460 "popular-posts-block-editor-{$style}",
461 plugins_url( "{$base_path}{$style}{$file_prefix}.css", TOP_TEN_PLUGIN_FILE ),
462 array( 'wp-edit-blocks' ),
463 TOP_TEN_VERSION
464 );
465 wp_add_inline_style( "popular-posts-block-editor-{$style}", $extra_css );
466 }
467 }
468 }
469
470 /**
471 * Add custom preload paths for the REST API.
472 *
473 * @since 4.0.0
474 *
475 * @param array $preload_paths Existing preload paths.
476 * @return array Modified preload paths.
477 */
478 public static function add_custom_preload_paths( $preload_paths ) {
479 $preload_paths[] = '/wp/v2/top-10/v1/counter';
480
481 return $preload_paths;
482 }
483 }
484