PluginProbe ʕ •ᴥ•ʔ
Jetpack – WP Security, Backup, Speed, & Growth / 16.0-beta
Jetpack – WP Security, Backup, Speed, & Growth v16.0-beta
16.0 16.0-beta 16.0-a.7 16.0-a.5 15.9.1 16.0-a.3 16.0-a.1 15.9 15.9-beta 15.9-a.7 15.9-a.5 15.9-a.3 15.9-a.1 15.8 15.8-beta 15.8-a.7 15.8-a.5 5.2.5 5.3.4 5.4.4 5.5.5 5.6.5 5.7.5 5.8.4 5.9.4 6.0.4 6.1 6.1.1 6.1.2 6.1.3 6.1.4 6.1.5 6.2 6.2.1 6.2.2 6.2.3 6.2.4 6.2.5 6.3 6.3.1 6.3.2 6.3.3 6.3.4 6.3.5 6.3.6 6.3.7 6.4 6.4.1 6.4.2 6.4.3 6.4.4 6.4.5 6.4.6 6.5 6.5.1 6.5.2 6.5.3 6.5.4 6.6 6.6.1 6.6.2 6.6.3 6.6.4 6.6.5 6.7 6.7.1 6.7.2 6.7.3 6.7.4 6.8 6.8.1 6.8.2 6.8.3 6.8.4 6.8.5 6.9 6.9.1 6.9.2 6.9.3 6.9.4 7.0 7.0.1 7.0.2 7.0.3 7.0.4 7.0.5 7.1 7.1.1 7.1.2 7.1.3 7.1.4 7.1.5 7.2 7.2.1 7.2.1.1 7.2.2 7.2.3 7.2.4 7.2.5 7.3 7.3.0.1 7.3.1 7.3.1.1 7.3.2 7.3.3 7.3.4 7.3.5 7.4 7.4.1 7.4.2 7.4.3 7.4.4 7.4.5 7.5 7.5.0.1 7.5.1 7.5.2 7.5.3 7.5.4 7.5.5 7.5.6 7.5.7 7.6 7.6.1 7.6.2 7.6.3 7.6.4 7.7 7.7.1 7.7.2 7.7.3 7.7.4 7.7.5 7.7.6 7.8 7.8.1 7.8.2 7.8.3 7.8.4 7.9 7.9.1 7.9.2 7.9.3 7.9.4 8.0 8.0.1 8.0.2 8.0.3 8.1 8.1.1 8.1.2 8.1.3 8.1.4 8.2 8.2.0.1 8.2.1 8.2.2 8.2.3 8.2.4 8.2.5 8.2.6 8.3 8.3.1 8.3.2 8.3.3 8.4 8.4.1 8.4.2 8.4.3 8.4.4 8.4.5 8.5 8.5.1 8.5.2 8.5.3 8.6 8.6.1 8.6.2 8.6.3 8.6.4 8.7 8.7.0.1 8.7.1 8.7.2 8.7.3 8.7.4 8.8 8.8.1 8.8.2 8.8.3 8.8.4 8.8.5 8.9 8.9.1 8.9.2 8.9.3 8.9.4 9.0 9.0.1 9.0.2 9.0.3 9.0.4 9.0.5 9.1 9.1.1 9.1.2 9.1.3 9.2 9.2.1 9.2.2 9.2.3 9.2.4 9.3 9.3.1 9.3.2 9.3.3 9.3.4 9.3.5 9.4 9.4.1 9.4.2 9.4.3 9.4.4 9.5 9.5.1 9.5.2 9.5.3 9.5.4 9.5.5 9.6 9.6.1 9.6.2 9.6.3 9.6.4 9.7 9.7.1 9.7.2 15.7-beta.2 9.7.3 15.7.1 9.8 15.8-a.1 9.8.1 15.8-a.3 9.8.2 2.0.9 9.8.3 2.1.7 9.9 2.2.10 9.9.1 2.3.10 9.9.2 2.4.7 9.9.3 2.5.5 2.6.6 2.7.5 2.8.5 2.9.6 3.0.6 3.1.5 3.2.5 3.3.6 3.4.6 3.5.6 3.6.4 3.7.5 3.8.5 3.9.10 4.0.7 4.1.4 4.2.5 4.3.5 4.4.5 4.5.3 4.6.3 4.7.4 4.8.5 4.9.3 5.0.3 5.1.4 trunk 10.0 10.0.1 10.0.2 10.1 10.1.1 10.1.2 10.2 10.2.1 10.2.2 10.2.3 10.3 10.3.1 10.3.2 10.4 10.4.1 10.4.2 10.5 10.5.1 10.5.2 10.5.3 10.6 10.6.1 10.6.2 10.7 10.7.1 10.7.2 10.8 10.8.1 10.8.2 10.9 10.9.1 10.9.2 10.9.3 11.0 11.0.1 11.0.2 11.1 11.1.1 11.1.2 11.1.3 11.1.4 11.2 11.2.1 11.2.2 11.3 11.3.1 11.3.2 11.3.3 11.3.4 11.4 11.4.1 11.4.2 11.5 11.5.1 11.5.2 11.5.3 11.6 11.6.1 11.6.2 11.7 11.7.1 11.7.2 11.7.3 11.8 11.8.3 11.8.4 11.8.5 11.8.6 11.9 11.9.1 11.9.2 11.9.3 12.0 12.0.1 12.0.2 12.1 12.1.1 12.1.2 12.2 12.2.1 12.2.2 12.3 12.3.1 12.4 12.4.1 12.5 12.5.1 12.6 12.6.1 12.6.2 12.6.3 12.7 12.7.1 12.7.2 12.8 12.8.1 12.8.2 12.9 12.9.1 12.9.2 12.9.3 12.9.4 13.0 13.0.1 13.1 13.1.1 13.1.2 13.1.3 13.1.4 13.2 13.2.1 13.2.2 13.2.3 13.3 13.3.1 13.3.2 13.4 13.4.1 13.4.2 13.4.3 13.4.4 13.5 13.5.1 13.6 13.6.1 13.7 13.7.1 13.8 13.8.1 13.8.2 13.9 13.9.1 14.0 14.1 14.2 14.2.1 14.3 14.4 14.4.1 14.5 14.6 14.7 14.8 14.9 14.9.1 15.0 15.0.1 15.0.2 15.1 15.1.1 15.2 15.3 15.3.1 15.4 15.5 15.6 15.7 15.7-a.1 15.7-a.3 15.7-a.5 15.7-a.7 15.7-beta
jetpack / extensions / blocks / button / button.php
jetpack / extensions / blocks / button Last commit date
button.php 2 weeks ago
button.php
437 lines
1 <?php
2 /**
3 * Button Block.
4 *
5 * @since 8.5.0
6 *
7 * @package automattic/jetpack
8 */
9
10 namespace Automattic\Jetpack\Extensions\Button;
11
12 use Automattic\Jetpack\Blocks;
13 use Jetpack_Gutenberg;
14
15 if ( ! defined( 'ABSPATH' ) ) {
16 exit( 0 );
17 }
18
19 const FEATURE_NAME = 'button';
20 const BLOCK_NAME = 'jetpack/' . FEATURE_NAME;
21
22 /**
23 * Registers the block for use in Gutenberg
24 * This is done via an action so that we can disable
25 * registration if we need to.
26 */
27 function register_block() {
28 Blocks::jetpack_register_block(
29 BLOCK_NAME,
30 array(
31 'render_callback' => __NAMESPACE__ . '\render_block',
32 'render_email_callback' => __NAMESPACE__ . '\render_email',
33 'uses_context' => array( 'jetpack/parentBlockWidth' ),
34 'selectors' => array(
35 'border' => '.wp-block-jetpack-button .wp-block-button__link',
36 ),
37 )
38 );
39 }
40 add_action( 'init', __NAMESPACE__ . '\register_block' );
41
42 /**
43 * Button block render callback.
44 *
45 * @param array $attributes Array containing the Button block attributes.
46 * @param string $content The Button block content.
47 *
48 * @return string
49 */
50 function render_block( $attributes, $content ) {
51 $save_in_post_content = get_attribute( $attributes, 'saveInPostContent' );
52
53 // The Jetpack Button block depends on the core button block styles.
54 // The following ensures that those styles are enqueued when rendering this block.
55 enqueue_existing_button_style_dependency( 'core/button' );
56 enqueue_existing_button_style_dependency( 'core/buttons' );
57
58 Jetpack_Gutenberg::load_styles_as_required( FEATURE_NAME );
59
60 if ( $save_in_post_content || ! class_exists( 'DOMDocument' ) ) {
61 return $content;
62 }
63
64 $element = get_attribute( $attributes, 'element' );
65 $text = wp_kses_post( get_attribute( $attributes, 'text' ) );
66 $unique_id = get_attribute( $attributes, 'uniqueId' );
67 $url = get_attribute( $attributes, 'url' );
68 $classes = Blocks::classes( FEATURE_NAME, $attributes, array( 'wp-block-button' ) );
69
70 $button_classes = get_button_classes( $attributes );
71 $button_styles = get_button_styles( $attributes );
72 $wrapper_styles = get_button_wrapper_styles( $attributes );
73
74 $wrapper_attributes = sprintf( ' class="%s" style="%s"', esc_attr( $classes ), esc_attr( $wrapper_styles ) );
75 $button_attributes = sprintf( ' class="%s" style="%s"', esc_attr( $button_classes ), esc_attr( $button_styles ) );
76
77 if ( empty( $unique_id ) ) {
78 $button_attributes .= ' data-id-attr="placeholder"';
79 } else {
80 $button_attributes .= sprintf( ' data-id-attr="%1$s" id="%1$s"', esc_attr( $unique_id ) );
81 }
82
83 if ( ! in_array( $element, array( 'a', 'button', 'input' ), true ) ) {
84 $element = 'a';
85 }
86
87 if ( 'a' === $element ) {
88 $button_attributes .= sprintf( ' href="%s" target="_blank" role="button" rel="noopener noreferrer"', esc_url( $url ) );
89 } elseif ( 'button' === $element ) {
90 $button_attributes .= ' type="submit"';
91 } elseif ( 'input' === $element ) {
92 $button_attributes .= sprintf( ' type="submit" value="%s"', esc_attr( wp_strip_all_tags( $text, true ) ) );
93 }
94
95 $button_attributes .= ' data-wp-class--is-submitting="state.isSubmitting" data-wp-bind--aria-disabled="state.isAriaDisabled"';
96
97 $svg = '<svg width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z" opacity=".25"/><path d="M10.14,1.16a11,11,0,0,0-9,8.92A1.59,1.59,0,0,0,2.46,12,1.52,1.52,0,0,0,4.11,10.7a8,8,0,0,1,6.66-6.61A1.42,1.42,0,0,0,12,2.69h0A1.57,1.57,0,0,0,10.14,1.16Z"><animateTransform attributeName="transform" type="rotate" dur="0.75s" values="0 12 12;360 12 12" repeatCount="indefinite"/></path></svg>';
98 $form_submitting_text = '<span class="is-visually-hidden">' . __( 'Submitting form', 'jetpack' ) . '</span>';
99 $spinner = '<span class="spinner" aria-hidden="true">' . $svg . $form_submitting_text . '</span>';
100
101 $button = 'input' === $element
102 ? '<' . $element . $button_attributes . ' />'
103 : '<' . $element . $button_attributes . '>' . $text . $spinner . '</' . $element . '>';
104
105 return '<div' . $wrapper_attributes . '>' . $button . '</div>';
106 }
107
108 /**
109 * WooCommerce Email Editor render callback for the button block.
110 *
111 * @param string $block_content The block content.
112 * @param array $parsed_block The parsed block data.
113 * @param object $rendering_context The email rendering context.
114 *
115 * @return string
116 */
117 function render_email( $block_content, array $parsed_block, $rendering_context ) {
118 // Validate input parameters and required dependencies
119 if ( ! isset( $parsed_block['attrs'] ) || ! is_array( $parsed_block['attrs'] ) ||
120 ! class_exists( '\Automattic\WooCommerce\EmailEditor\Integrations\Core\Renderer\Blocks\Button' ) ) {
121 return '';
122 }
123
124 $attributes = $parsed_block['attrs'];
125
126 // Create a mock innerHTML that WooCommerce's button renderer can parse
127 $button_text = ! empty( $attributes['text'] ) ? sanitize_text_field( $attributes['text'] ) : __( 'Click here', 'jetpack' );
128 $button_url = ! empty( $attributes['url'] ) ? $attributes['url'] : '#';
129
130 // Create the innerHTML that WooCommerce's button renderer expects
131 $inner_html = sprintf(
132 '<div class="wp-block-button"><a class="wp-block-button__link" href="%s">%s</a></div>',
133 esc_url( $button_url ),
134 esc_html( $button_text )
135 );
136
137 // Format attributes for WooCommerce's Styles_Helper
138 $formatted_attributes = format_attributes_for_woocommerce( $attributes );
139
140 // Create a mock parsed block that WooCommerce's button renderer can handle
141 $mock_parsed_block = array(
142 'innerHTML' => $inner_html,
143 'attrs' => $formatted_attributes,
144 );
145
146 // Use WooCommerce's core button renderer
147 $woo_button_renderer = new \Automattic\WooCommerce\EmailEditor\Integrations\Core\Renderer\Blocks\Button();
148
149 return $woo_button_renderer->render( $block_content, $mock_parsed_block, $rendering_context );
150 }
151
152 /**
153 * Format button attributes for WooCommerce's Styles_Helper.
154 *
155 * @param array $attributes The original attributes.
156 * @return array The formatted attributes.
157 */
158 function format_attributes_for_woocommerce( $attributes ) {
159 $formatted = array();
160
161 // Handle background colors (prioritize subscription attributes)
162 // Named colors go in backgroundColor, hex colors go in style.color.background
163 if ( ! empty( $attributes['buttonBackgroundColor'] ) ) {
164 $formatted['backgroundColor'] = $attributes['buttonBackgroundColor'];
165 } elseif ( ! empty( $attributes['backgroundColor'] ) ) {
166 $formatted['backgroundColor'] = $attributes['backgroundColor'];
167 }
168
169 if ( ! empty( $attributes['customButtonBackgroundColor'] ) ) {
170 $formatted['style']['color']['background'] = $attributes['customButtonBackgroundColor'];
171 } elseif ( ! empty( $attributes['customBackgroundColor'] ) ) {
172 $formatted['style']['color']['background'] = $attributes['customBackgroundColor'];
173 }
174
175 // Named colors go in textColor, hex colors go in style.color.text
176 if ( ! empty( $attributes['textColor'] ) ) {
177 $formatted['textColor'] = $attributes['textColor'];
178 }
179 if ( ! empty( $attributes['customTextColor'] ) ) {
180 $formatted['style']['color']['text'] = $attributes['customTextColor'];
181 }
182
183 // Handle typography (font size)
184 if ( ! empty( $attributes['fontSize'] ) ) {
185 $formatted['style']['typography']['fontSize'] = $attributes['fontSize'];
186 }
187 if ( ! empty( $attributes['customFontSize'] ) ) {
188 $formatted['style']['typography']['fontSize'] = $attributes['customFontSize'];
189 }
190
191 // Handle borders
192 if ( ! empty( $attributes['borderRadius'] ) ) {
193 $formatted['style']['border']['radius'] = $attributes['borderRadius'] . 'px';
194 }
195 // Named colors go in borderColor, hex colors go in style.color.border
196 if ( ! empty( $attributes['borderColor'] ) ) {
197 $formatted['borderColor'] = $attributes['borderColor'];
198 }
199 if ( ! empty( $attributes['customBorderColor'] ) ) {
200 $formatted['style']['border']['color'] = $attributes['customBorderColor'];
201 }
202 // Handle border weight (subscription block uses borderWeight, button block expects style.border.width)
203 if ( ! empty( $attributes['borderWeight'] ) ) {
204 $formatted['style']['border']['width'] = $attributes['borderWeight'] . 'px';
205 }
206
207 // Handle padding
208 if ( ! empty( $attributes['padding'] ) ) {
209 $formatted['style']['spacing']['padding'] = $attributes['padding'] . 'px';
210 }
211
212 return $formatted;
213 }
214
215 /**
216 * Get the Button block classes.
217 *
218 * @param array $attributes Array containing the block attributes.
219 *
220 * @return string
221 */
222 function get_button_classes( $attributes ) {
223 $classes = array( 'wp-block-button__link' );
224 $has_class_name = array_key_exists( 'className', $attributes );
225 $has_named_text_color = array_key_exists( 'textColor', $attributes );
226 $has_custom_text_color = array_key_exists( 'customTextColor', $attributes );
227 $has_named_background_color = array_key_exists( 'backgroundColor', $attributes );
228 $has_custom_background_color = array_key_exists( 'customBackgroundColor', $attributes );
229 $has_named_gradient = array_key_exists( 'gradient', $attributes );
230 $has_custom_gradient = array_key_exists( 'customGradient', $attributes );
231 $has_border_radius = array_key_exists( 'borderRadius', $attributes );
232 $has_font_size = array_key_exists( 'fontSize', $attributes );
233 $has_named_border_color = array_key_exists( 'borderColor', $attributes );
234
235 if ( $has_font_size ) {
236 $classes[] = 'has-' . $attributes['fontSize'] . '-font-size';
237 $classes[] = 'has-custom-font-size';
238 }
239
240 if ( $has_class_name ) {
241 $classes[] = $attributes['className'];
242 }
243
244 if ( $has_named_text_color || $has_custom_text_color ) {
245 $classes[] = 'has-text-color';
246 }
247 if ( $has_named_text_color ) {
248 $classes[] = sprintf( 'has-%s-color', $attributes['textColor'] );
249 }
250
251 if ( $has_named_border_color ) {
252 $classes[] = sprintf( 'has-%s-border-color', $attributes['borderColor'] );
253 }
254
255 if (
256 $has_named_background_color ||
257 $has_custom_background_color ||
258 $has_named_gradient ||
259 $has_custom_gradient
260 ) {
261 $classes[] = 'has-background';
262 }
263 if ( $has_named_background_color && ! $has_custom_gradient ) {
264 $classes[] = sprintf( 'has-%s-background-color', $attributes['backgroundColor'] );
265 }
266 if ( $has_named_gradient ) {
267 $classes[] = sprintf( 'has-%s-gradient-background', $attributes['gradient'] );
268 }
269
270 // phpcs:ignore Universal.Operators.StrictComparisons.LooseEqual
271 if ( $has_border_radius && 0 == $attributes['borderRadius'] ) {
272 $classes[] = 'no-border-radius';
273 }
274
275 return implode( ' ', $classes );
276 }
277
278 /**
279 * Get the Button block styles.
280 *
281 * @param array $attributes Array containing the block attributes.
282 *
283 * @return string
284 */
285 function get_button_styles( $attributes ) {
286 $styles = array();
287 $has_named_text_color = array_key_exists( 'textColor', $attributes );
288 $has_custom_text_color = array_key_exists( 'customTextColor', $attributes );
289 $has_named_background_color = array_key_exists( 'backgroundColor', $attributes );
290 $has_custom_background_color = array_key_exists( 'customBackgroundColor', $attributes );
291 $has_named_gradient = array_key_exists( 'gradient', $attributes );
292 $has_custom_gradient = array_key_exists( 'customGradient', $attributes );
293 $has_border_radius = array_key_exists( 'borderRadius', $attributes );
294 $has_font_family = array_key_exists( 'fontFamily', $attributes );
295 $has_typography_styles = array_key_exists( 'style', $attributes ) && array_key_exists( 'typography', $attributes['style'] );
296 $has_custom_font_size = $has_typography_styles && array_key_exists( 'fontSize', $attributes['style']['typography'] );
297 $has_custom_text_transform = $has_typography_styles && array_key_exists( 'textTransform', $attributes['style']['typography'] );
298 $border_styles = array();
299 $border_attribute = $attributes['style']['border'] ?? null;
300 $is_border_style_array = is_array( $border_attribute );
301
302 $has_custom_border_color = $is_border_style_array && isset( $border_attribute['color'] );
303 $has_border_style = $is_border_style_array && isset( $border_attribute['style'] );
304 $has_border_width = $is_border_style_array && isset( $border_attribute['width'] );
305 $has_individual_borders = $is_border_style_array && (
306 isset( $border_attribute['top'] ) ||
307 isset( $border_attribute['right'] ) ||
308 isset( $border_attribute['bottom'] ) ||
309 isset( $border_attribute['left'] )
310 );
311
312 if ( $has_font_family ) {
313 $styles[] = sprintf( 'font-family: %s;', $attributes['fontFamily'] );
314 }
315
316 if ( $has_custom_font_size ) {
317 $styles[] = sprintf( 'font-size: %s;', $attributes['style']['typography']['fontSize'] );
318 }
319
320 if ( $has_custom_text_transform ) {
321 $styles[] = sprintf( 'text-transform: %s;', $attributes['style']['typography']['textTransform'] );
322 }
323
324 if ( ! $has_named_text_color && $has_custom_text_color ) {
325 $styles[] = sprintf( 'color: %s;', $attributes['customTextColor'] );
326 }
327
328 if ( ! $has_named_background_color && ! $has_named_gradient && $has_custom_gradient ) {
329 $styles[] = sprintf( 'background: %s;', $attributes['customGradient'] );
330 }
331
332 if (
333 $has_custom_background_color &&
334 ! $has_named_background_color &&
335 ! $has_named_gradient &&
336 ! $has_custom_gradient
337 ) {
338 $styles[] = sprintf( 'background-color: %s;', $attributes['customBackgroundColor'] );
339 }
340
341 // phpcs:ignore Universal.Operators.StrictComparisons.LooseNotEqual
342 if ( $has_border_radius && 0 != $attributes['borderRadius'] ) {
343 $styles[] = sprintf( 'border-radius: %spx;', $attributes['borderRadius'] );
344 }
345
346 if ( $has_custom_border_color ) {
347 $border_styles['color'] = $attributes['style']['border']['color'];
348 }
349
350 if ( $has_border_style ) {
351 $border_styles['style'] = $attributes['style']['border']['style'];
352 }
353
354 if ( $has_border_width ) {
355 $border_styles['width'] = $attributes['style']['border']['width'];
356 }
357
358 if ( $has_individual_borders ) {
359 foreach ( array( 'top', 'right', 'bottom', 'left' ) as $side ) {
360 $border = $attributes['style']['border'][ $side ] ?? null;
361 if ( is_array( $border ) ) {
362 $border_side_values = array(
363 'width' => $border['width'] ?? null,
364 'color' => $border['color'] ?? null,
365 'style' => $border['style'] ?? null,
366 );
367 $border_styles[ $side ] = $border_side_values;
368 }
369 }
370 }
371
372 $border_styles = wp_style_engine_get_styles( array( 'border' => $border_styles ) );
373 if ( isset( $border_styles['css'] ) ) {
374 $styles[] = $border_styles['css'];
375 }
376
377 return implode( ' ', $styles );
378 }
379
380 /**
381 * Get the Button wrapper block styles.
382 *
383 * @param array $attributes Array containing the block attributes.
384 *
385 * @return string
386 */
387 function get_button_wrapper_styles( $attributes ) {
388 $styles = array();
389 $has_width = array_key_exists( 'width', $attributes );
390
391 if ( $has_width && ! empty( $attributes['width'] ) ) {
392 $styles[] = sprintf( 'width: %s;', $attributes['width'] );
393 }
394
395 return implode( ' ', $styles );
396 }
397
398 /**
399 * Get filtered attributes.
400 *
401 * @param array $attributes Array containing the Button block attributes.
402 * @param string $attribute_name String containing the attribute name to get.
403 *
404 * @return string
405 */
406 function get_attribute( $attributes, $attribute_name ) {
407 if ( isset( $attributes[ $attribute_name ] ) ) {
408 return $attributes[ $attribute_name ];
409 }
410
411 $default_attributes = array(
412 'url' => '#',
413 'element' => 'a',
414 'saveInPostContent' => false,
415 );
416
417 if ( isset( $default_attributes[ $attribute_name ] ) ) {
418 return $default_attributes[ $attribute_name ];
419 }
420 }
421
422 /**
423 * Enqueue style for an existing block.
424 *
425 * The Jetpack Button block depends on styles from the core button block.
426 * In case that block is not already within the post content, we can use
427 * this function to ensure the block's style assets are enqueued.
428 *
429 * @param string $block_name Block type name including namespace.
430 */
431 function enqueue_existing_button_style_dependency( $block_name ) {
432 $existing_block = \WP_Block_Type_Registry::get_instance()->get_registered( $block_name );
433 if ( isset( $existing_block ) && ! empty( $existing_block->style ) ) {
434 wp_enqueue_style( $existing_block->style );
435 }
436 }
437