PluginProbe
bBlocks – Essential Gutenberg Blocks & Patterns Collection / 2.1.7
bBlocks – Essential Gutenberg Blocks & Patterns Collection v2.1.7
2.1.7 2.1.6 2.1.5 2.1.4 2.1.3 2.1.2 2.1.1 2.1.0 2.0.43 2.0.42 2.0.41 2.0.40 2.0.39 2.0.38 trunk 1.0 1.1 1.2 1.3 1.4 1.5 1.5.1 1.5.2 1.5.3 1.5.4 All 107 releases
b-blocks / build / timeline-item / render.php

render.php in bBlocks – Essential Gutenberg Blocks & Patterns Collection 2.1.7, at build/timeline-item/render.php

145 lines 5.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Timeline Item Block server-side render.
4 *
5 * One entry on a Timeline: the card holding the inner blocks, the SVG marker
6 * that sits on the line, and the date beside it. Like its parent the markup is
7 * rendered here rather than on the client — the card's content is InnerBlocks
8 * — and view.js only mounts the generated <style> into the empty host below.
9 *
10 * The side an entry sits on is normally decided in CSS from its position in
11 * the list, so nothing about ordering needs to be known at render time. A
12 * `side` set in the sidebar adds a class that outranks that rule; it mirrors
13 * utils/functions.js.
14 *
15 * @package bBlocks
16 *
17 * @var array $attributes Block attributes.
18 * @var string $content Rendered inner blocks.
19 */
20
21 if ( ! defined( 'ABSPATH' ) ) {
22 exit;
23 }
24
25 $id = wp_unique_id( 'timeline-item-' );
26
27 $item = isset( $attributes['item'] ) && is_array( $attributes['item'] ) ? $attributes['item'] : array();
28
29 $side = isset( $item['side'] ) ? sanitize_key( (string) $item['side'] ) : '';
30 $side = in_array( $side, array( 'left', 'right' ), true ) ? $side : '';
31
32 // Empty means follow the parent Timeline, so only an explicit choice emits a
33 // class. Mirrors getTimelineItemClasses in the parent's utils/functions.js.
34 $arrowAlign = isset( $item['arrowAlign'] ) ? sanitize_key( (string) $item['arrowAlign'] ) : '';
35 $arrowAlign = in_array( $arrowAlign, array( 'start', 'center', 'end' ), true ) ? $arrowAlign : '';
36
37 // The date is a RichText field, so it can carry inline formatting — bold,
38 // links, colour. wp_kses_post keeps exactly what a post is allowed to hold
39 // and strips the rest, matching how every other RichText in the plugin is
40 // rendered.
41 $date = isset( $attributes['date'] ) ? wp_kses_post( (string) $attributes['date'] ) : '';
42
43 /**
44 * The marker icon is an inline SVG from the bpl-tools icon library rather
45 * than a font class, so it carries no webfont dependency — but it also means
46 * raw markup reaches the page and has to be filtered.
47 *
48 * The allowlist below is not guesswork: it is every element and attribute
49 * that actually occurs across all 4,354 icons in the three bundled libraries
50 * (Font Awesome, Bootstrap, Lucide). Anything outside it — script, style, on*
51 * handlers, foreignObject — is stripped by wp_kses.
52 */
53 $svgAttr = array(
54 'xmlns' => true,
55 'viewbox' => true,
56 'width' => true,
57 'height' => true,
58 'fill' => true,
59 'fill-rule' => true,
60 'fill-opacity' => true,
61 'stroke' => true,
62 'stroke-width' => true,
63 'stroke-linecap' => true,
64 'stroke-linejoin' => true,
65 'class' => true,
66 'id' => true,
67 'transform' => true,
68 'aria-hidden' => true,
69 'focusable' => true,
70 );
71
72 $shapeAttr = array(
73 'd' => true,
74 'points' => true,
75 'cx' => true,
76 'cy' => true,
77 'r' => true,
78 'rx' => true,
79 'ry' => true,
80 'x' => true,
81 'y' => true,
82 'x1' => true,
83 'y1' => true,
84 'x2' => true,
85 'y2' => true,
86 'width' => true,
87 'height' => true,
88 'fill' => true,
89 'fill-rule' => true,
90 'fill-opacity' => true,
91 'stroke' => true,
92 'stroke-width' => true,
93 'stroke-linecap' => true,
94 'stroke-linejoin' => true,
95 'transform' => true,
96 );
97
98 $iconAllowed = array(
99 'svg' => $svgAttr,
100 'g' => $shapeAttr,
101 'path' => $shapeAttr,
102 'circle' => $shapeAttr,
103 'ellipse' => $shapeAttr,
104 'rect' => $shapeAttr,
105 'line' => $shapeAttr,
106 'polyline' => $shapeAttr,
107 'polygon' => $shapeAttr,
108 );
109
110 $icon = isset( $attributes['icon'] ) ? wp_kses( (string) $attributes['icon'], $iconAllowed ) : '';
111
112 $itemClasses = trim(
113 'b-blocks-timeline-item'
114 . ( $side ? ' b-blocks-timeline-side-' . $side : '' )
115 . ( $arrowAlign ? ' b-blocks-timeline-item-arrow-' . $arrowAlign : '' )
116 );
117 ?>
118 <div
119 <?php // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- get_block_wrapper_attributes() is properly escaped ?>
120 <?php echo get_block_wrapper_attributes( array( 'class' => $itemClasses ) ); ?>
121 id='<?php echo esc_attr( $id ); ?>'
122 data-attributes='<?php echo esc_attr( wp_json_encode( $attributes ) ); ?>'
123 >
124 <div class='b-blocks-timeline-item-style'></div>
125
126 <div class='b-blocks-timeline-content'>
127 <div class='b-blocks-timeline-card'>
128 <?php
129 // $content is this entry's inner blocks, each of which rendered
130 // and escaped its own markup.
131 echo $content; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- inner blocks render their own escaped markup
132 ?>
133 </div>
134 </div>
135
136 <div class='b-blocks-timeline-marker' aria-hidden='true'>
137 <?php
138 // Sanitized with wp_kses against the icon allowlist above.
139 echo $icon; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- filtered by wp_kses above
140 ?>
141 </div>
142
143 <div class='b-blocks-timeline-date'><?php echo $date; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- sanitized with wp_kses_post above ?></div>
144 </div>
145