PluginProbe
Kit (formerly ConvertKit) – Email Newsletter, Email Marketing, Membership, Subscribers and Landing Pages / 3.0.6
Kit (formerly ConvertKit) – Email Newsletter, Email Marketing, Membership, Subscribers and Landing Pages v3.0.6
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 2.3.3 All 194 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.0.6, at includes/blocks/class-convertkit-block.php

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