PluginProbe
Kit (formerly ConvertKit) – Email Newsletter, Email Marketing, Membership, Subscribers and Landing Pages / 3.2.2
Kit (formerly ConvertKit) – Email Newsletter, Email Marketing, Membership, Subscribers and Landing Pages v3.2.2
3.4.2 3.4.1 3.4.0 3.3.9 3.3.8 3.3.7 3.3.6 3.3.5 3.3.4 3.3.3 3.3.2 3.3.1 2.2.0 2.2.1 2.2.2 2.2.3 2.2.4 2.2.5 2.2.6 2.2.7 2.2.8 2.2.9 2.3.0 2.3.1 2.3.2 All 195 releases
convertkit / includes / blocks / class-convertkit-block.php

class-convertkit-block.php in Kit (formerly ConvertKit) – Email Newsletter, Email Marketing, Membership, Subscribers and Landing Pages 3.2.2, at includes/blocks/class-convertkit-block.php

535 lines 13.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * ConvertKit Block class.
4 *
5 * @package ConvertKit
6 * @author ConvertKit
7 */
8
9 /**
10 * ConvertKit Block definition for Gutenberg and Shortcode.
11 *
12 * @package ConvertKit
13 * @author ConvertKit
14 */
15 class ConvertKit_Block {
16
17 /**
18 * Registers this block with the ConvertKit Plugin.
19 *
20 * @since 1.9.6
21 *
22 * @param array $blocks Blocks to Register.
23 * @return array Blocks to Register
24 */
25 public function register( $blocks ) {
26
27 // If the request is for the frontend, return the minimum block definition required
28 // to register and render the block on the frontend site using register_block_type().
29 if ( ! $this->is_admin_frontend_editor_or_admin_rest_request() ) {
30 $blocks[ $this->get_name() ] = array(
31 'title' => $this->get_title(),
32 'icon' => $this->get_icon(),
33 'attributes' => $this->get_attributes(),
34 'render_callback' => array( $this, 'render' ),
35 );
36
37 return $blocks;
38 }
39
40 // Request is for the WordPress Administration, frontend editor or REST API request.
41 // Register the full block definition, including fields, panels, default values and supports.
42 $blocks[ $this->get_name() ] = array_merge(
43 $this->get_overview(),
44 array(
45 'name' => $this->get_name(),
46 'fields' => $this->get_fields(),
47 'attributes' => $this->get_attributes(),
48 'supports' => $this->get_supports(),
49 'panels' => $this->get_panels(),
50 'default_values' => $this->get_default_values(),
51 )
52 );
53
54 return $blocks;
55
56 }
57
58 /**
59 * Returns this block's programmatic name, excluding the convertkit- prefix.
60 *
61 * @since 1.9.6
62 */
63 public function get_name() {
64
65 /**
66 * This will register as:
67 * - a shortcode, with the name [convertkit_form].
68 * - a shortcode, with the name [convertkit], for backward compat.
69 * - a Gutenberg block, with the name convertkit/form.
70 */
71 return '';
72
73 }
74
75 /**
76 * Returns this block's title.
77 *
78 * @since 3.1.1
79 */
80 public function get_title() {
81
82 return '';
83
84 }
85
86 /**
87 * Returns this block's icon.
88 *
89 * @since 3.1.1
90 */
91 public function get_icon() {
92
93 return '';
94
95 }
96
97 /**
98 * Returns this block's Title, Icon, Categories, Keywords and properties.
99 *
100 * @since 1.9.6
101 *
102 * @return array
103 */
104 public function get_overview() {
105
106 return array();
107
108 }
109
110 /**
111 * Returns this block's Attributes
112 *
113 * @since 1.9.6.5
114 *
115 * @return array
116 */
117 public function get_attributes() {
118
119 return array();
120
121 }
122
123 /**
124 * Gutenberg: Returns supported built in attributes, such as
125 * className, color etc.
126 *
127 * @since 1.9.7.4
128 *
129 * @return array Supports
130 */
131 public function get_supports() {
132
133 return array(
134 'className' => true,
135 );
136
137 }
138
139 /**
140 * Returns this block's Fields
141 *
142 * @since 1.9.6
143 *
144 * @return array
145 */
146 public function get_fields() {
147
148 return array();
149
150 }
151
152 /**
153 * Returns this block's UI panels / sections.
154 *
155 * @since 1.9.6
156 *
157 * @return array
158 */
159 public function get_panels() {
160
161 return array();
162
163 }
164
165 /**
166 * Returns this block's Default Values
167 *
168 * @since 1.9.6
169 *
170 * @return array
171 */
172 public function get_default_values() {
173
174 return array();
175
176 }
177
178 /**
179 * Returns the given block's field's Default Value
180 *
181 * @since 1.9.6
182 *
183 * @param string $field Field Name.
184 * @return string
185 */
186 public function get_default_value( $field ) {
187
188 $defaults = $this->get_default_values();
189 if ( isset( $defaults[ $field ] ) ) {
190 return $defaults[ $field ];
191 }
192
193 return '';
194
195 }
196
197 /**
198 * Performs several transformation on a block's attributes, including:
199 * - sanitization
200 * - adding attributes with default values are missing but registered by the block
201 * - cast attribute values based on their defined type
202 *
203 * These steps are performed because the attributes may be defined by a shortcode,
204 * block or third party widget/page builder's block, each of which handle attributes
205 * slightly differently.
206 *
207 * Returns a standardised attributes array.
208 *
209 * @since 1.9.7.4
210 *
211 * @param array $atts Declared attributes.
212 * @return array All attributes, standardised.
213 */
214 public function sanitize_and_declare_atts( $atts ) {
215
216 // Sanitize attributes, merging with default values so that the array
217 // of attributes contains all expected keys for this block.
218 $atts = shortcode_atts(
219 $this->get_default_values(),
220 $this->sanitize_atts( $atts ),
221 $this->get_name()
222 );
223
224 // Fetch attribute definitions.
225 $atts_definitions = $this->get_attributes();
226
227 // Iterate through attributes, casting them based on their attribute definition.
228 foreach ( $atts as $att => $value ) {
229 // Skip if no definition exists for this attribute.
230 if ( ! array_key_exists( $att, $atts_definitions ) ) {
231 continue;
232 }
233
234 // Skip if no type exists for this attribute.
235 if ( ! array_key_exists( 'type', $atts_definitions[ $att ] ) ) {
236 continue;
237 }
238
239 // Cast, depending on the attribute type.
240 switch ( $atts_definitions[ $att ]['type'] ) {
241 case 'number':
242 $atts[ $att ] = (int) $value;
243 break;
244
245 case 'boolean':
246 $atts[ $att ] = (bool) $value;
247 break;
248
249 case 'string':
250 // If the attribute's value is empty, check if the default attribute has a value.
251 // If so, apply it now.
252 // shortcode_atts() will only do this if the attribute key isn't specified.
253 if ( empty( $value ) && ! empty( $this->get_default_value( $att ) ) ) {
254 $atts[ $att ] = $this->get_default_value( $att );
255 }
256 break;
257 }
258 }
259
260 // Remove some unused attributes, now they're declared above.
261 unset( $atts['style'], $atts['backgroundColor'], $atts['textColor'], $atts['className'] );
262
263 return $atts;
264
265 }
266
267 /**
268 * Removes any HTML that might be wrongly included in the shorcode attribute's values
269 * due to e.g. copy and pasting from Documentation or other examples.
270 *
271 * @since 1.9.6
272 *
273 * @param array $atts Block or shortcode attributes.
274 * @return array
275 */
276 public function sanitize_atts( $atts ) {
277
278 foreach ( $atts as $key => $value ) {
279 if ( is_array( $value ) ) {
280 continue;
281 }
282
283 $atts[ $key ] = wp_strip_all_tags( $value );
284 }
285
286 return $atts;
287
288 }
289
290 /**
291 * Builds CSS class(es) that might need to be added to the top level element's `class` attribute
292 * when using Gutenberg, to honor the block's styles and layout settings.
293 *
294 * @since 2.8.3
295 *
296 * @param array $additional_classes Additional classes to add to the block.
297 * @return array
298 */
299 public function get_css_classes( $additional_classes = array() ) {
300
301 // To avoid errors in get_block_wrapper_attributes() in non-block themes using the shortcode,
302 // tell WordPress that a block is being rendered.
303 // The attributes don't matter, as we send them to the render() function.
304 if ( class_exists( 'WP_Block_Supports' ) && is_null( WP_Block_Supports::$block_to_render ) ) { // @phpstan-ignore-line
305 WP_Block_Supports::$block_to_render = array(
306 'blockName' => 'convertkit/' . $this->get_name(),
307 'attrs' => array(),
308 'innerBlocks' => array(),
309 'innerHTML' => '',
310 'innerContent' => array(),
311 );
312 }
313
314 // Get the block wrapper attributes string.
315 $wrapper_attributes = get_block_wrapper_attributes(
316 array(
317 'class' => implode(
318 ' ',
319 array_merge(
320 array(
321 'convertkit-' . $this->get_name(),
322 ),
323 $additional_classes
324 )
325 ),
326 )
327 );
328
329 // Extract the class attribute from the wrapper attributes string, returning as an array.
330 // Extract just the class attribute value from the wrapper attributes string.
331 $classes = array();
332 if ( preg_match( '/class="([^"]*)"/', $wrapper_attributes, $matches ) ) {
333 $classes = explode( ' ', $matches[1] );
334 } else {
335 $classes = array(
336 'convertkit-' . $this->get_name(),
337 );
338 }
339
340 // Remove some classes WordPress adds that we don't want, as they break the layout.
341 $classes = array_diff( $classes, array( 'alignfull', 'wp-block-post-content' ) );
342
343 return $classes;
344
345 }
346
347 /**
348 * Builds inline CSS style(s) that might need to be added to the top level element's `style` attribute
349 * when using Gutenberg, a shortcode or third party page builder module / widget.
350 *
351 * @since 2.8.3
352 *
353 * @param array $atts Block or shortcode attributes.
354 * @return array
355 */
356 public function get_css_styles( $atts ) {
357
358 // To avoid errors in get_block_wrapper_attributes() in non-block themes using the shortcode,
359 // tell WordPress that a block is being rendered.
360 // The attributes don't matter, as we send them to the render() function.
361 if ( class_exists( 'WP_Block_Supports' ) && is_null( WP_Block_Supports::$block_to_render ) ) { // @phpstan-ignore-line
362 WP_Block_Supports::$block_to_render = array(
363 'blockName' => 'convertkit/' . $this->get_name(),
364 'attrs' => array(),
365 'innerBlocks' => array(),
366 'innerHTML' => '',
367 'innerContent' => array(),
368 );
369 }
370
371 $styles = array();
372
373 // Get the block wrapper attributes string, extracting any styles that the block has set,
374 // such as margin, padding or block spacing.
375 $wrapper_attributes = get_block_wrapper_attributes();
376 if ( preg_match( '/style="([^"]*)"/', $wrapper_attributes, $matches ) ) {
377 return array_filter( explode( ';', $matches[1] ) );
378 }
379
380 // If here, no block styles were found.
381 // This might be a shortcode or third party page builder module / widget that has
382 // specific attributes set.
383 if ( isset( $atts['text_color'] ) && ! empty( $atts['text_color'] ) ) {
384 $styles[] = 'color:' . $atts['text_color'];
385 }
386 if ( isset( $atts['background_color'] ) && ! empty( $atts['background_color'] ) ) {
387 $styles[] = 'background-color:' . $atts['background_color'];
388 }
389
390 return $styles;
391
392 }
393
394 /**
395 * Returns the given block / shortcode attributes array as HTML data-* attributes, which can be output
396 * in a block's container.
397 *
398 * @since 1.9.7.6
399 *
400 * @param array $atts Block or shortcode attributes.
401 * @return string Block or shortcode attributes
402 */
403 public function get_atts_as_html_data_attributes( $atts ) {
404
405 // Define attributes provided by Gutenberg, which will be skipped, such as
406 // styling.
407 $skip_keys = array(
408 'backgroundColor',
409 'textColor',
410 '_css_styles',
411 );
412
413 // Define a blank string to build the data-* attributes in.
414 $data = '';
415
416 foreach ( $atts as $key => $value ) {
417 // Skip built in attributes provided by Gutenberg.
418 if ( in_array( $key, $skip_keys, true ) ) {
419 continue;
420 }
421
422 // Skip empty values.
423 if ( empty( $value ) ) {
424 continue;
425 }
426
427 // Append to data string, replacing underscores with hyphens in the key name.
428 $data .= ' data-' . strtolower( str_replace( '_', '-', $key ) ) . '="' . esc_attr( $value ) . '"';
429 }
430
431 return trim( $data );
432
433 }
434
435 /**
436 * Determines if the request is a WordPress REST API request
437 * made by a logged in WordPress user who has the capability to edit posts.
438 *
439 * @since 3.1.0
440 *
441 * @return bool
442 */
443 public function is_admin_rest_request() {
444
445 return defined( 'REST_REQUEST' ) && REST_REQUEST && current_user_can( 'edit_posts' );
446
447 }
448
449 /**
450 * Determines if the request is for the WordPress Administration, frontend editor or REST API request.
451 *
452 * @since 3.1.0
453 *
454 * @return bool
455 */
456 public function is_admin_frontend_editor_or_admin_rest_request() {
457
458 return WP_ConvertKit()->is_admin_or_frontend_editor() || $this->is_admin_rest_request();
459
460 }
461
462 /**
463 * Determines if the request for the block is from the block editor or the frontend site.
464 *
465 * @since 1.9.8.5
466 *
467 * @return bool
468 */
469 public function is_block_editor_request() {
470
471 // Return false if not a WordPress REST API request, which Gutenberg uses.
472 if ( ! $this->is_admin_rest_request() ) {
473 return false;
474 }
475
476 // Return false if the context parameter isn't edit.
477 if ( ! filter_has_var( INPUT_GET, 'context' ) ) {
478 return false;
479 }
480 if ( filter_input( INPUT_GET, 'context', FILTER_SANITIZE_FULL_SPECIAL_CHARS ) !== 'edit' ) {
481 return false;
482 }
483
484 // Request is for the block editor.
485 return true;
486
487 }
488
489 /**
490 * If the Block Visiblity Plugin is active, run the block through its conditions now.
491 * We don't wait for Block Visibility to do this, as it performs this on the
492 * `render_block` filter, by which time the code in this method has fully executed,
493 * meaning any non-inline Forms will have had their scripts added to the
494 * `convertkit_output_scripts_footer` hook.
495 * As a result, the non-inline Form will always display, regardless of whether
496 * Block Visibility's conditions are met.
497 * We deliberately don't output non-inline Forms in their block, instead deferring
498 * to the `convertkit_output_scripts_footer` hook, to ensure the non-inline Forms
499 * styling are not constrained by the Theme's width, layout or other properties.
500 *
501 * @since 2.6.6
502 *
503 * @param array $atts Block Attributes.
504 * @return bool Display Block
505 */
506 public function is_block_visible( $atts ) {
507
508 // Display the block if the Block Visibility Plugin isn't active.
509 if ( ! function_exists( '\BlockVisibility\Frontend\render_with_visibility' ) ) {
510 return true;
511 }
512
513 // Determine whether the block should display.
514 $display_block = \BlockVisibility\Frontend\render_with_visibility(
515 'block',
516 array(
517 'blockName' => 'convertkit-' . $this->get_name(),
518 'attrs' => $atts,
519 )
520 );
521
522 // If the content returned is a blank string, conditions on this block set
523 // by the user in the Block Visibility Plugin resulted in the block not displaying.
524 // Don't display it.
525 if ( empty( $display_block ) ) {
526 return false;
527 }
528
529 // If here, the block can be displayed.
530 return true;
531
532 }
533
534 }
535