PluginProbe
JetFormBuilder — Dynamic Blocks Form Builder / 3.6.6
JetFormBuilder — Dynamic Blocks Form Builder v3.6.6
3.6.6.1 3.6.6 3.6.5.4 3.6.5.3 3.6.5.2 3.6.5.1 3.6.5 3.6.4.2 3.6.4.1 3.6.4 3.6.3.1 3.6.3 3.6.2.2 3.6.2.1 3.6.2 3.6.1.1 3.6.1 3.6.0.1 trunk 1.0.0 1.0.1 1.0.2 1.0.3 1.1.0 1.1.1 All 134 releases
jetformbuilder / includes / blocks / block-helper.php

block-helper.php in JetFormBuilder — Dynamic Blocks Form Builder 3.6.6, at includes/blocks/block-helper.php

402 lines 9.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3
4 namespace Jet_Form_Builder\Blocks;
5
6 use Jet_Form_Builder\Blocks\Types\Base;
7 use Jet_Form_Builder\Form_Manager;
8 use JFB_Modules\Post_Type\Module as Post_Type_Module;
9
10 // If this file is called directly, abort.
11 if ( ! defined( 'WPINC' ) ) {
12 die;
13 }
14
15 /**
16 * Class Helper
17 *
18 * @package Jet_Form_Builder\Blocks
19 */
20 class Block_Helper {
21
22 const PREVIEW_NONCE_FIELD = 'jfb_preview_nonce';
23 const PREVIEW_NONCE_ACTION = 'jfb-preview-form';
24
25 /**
26 * @since 3.1.1
27 *
28 * @param $parsed_block
29 *
30 * @return string
31 * @throws \Exception
32 */
33 public static function resolve_name( $parsed_block ): string {
34 $name = is_string( $parsed_block ) ? $parsed_block : $parsed_block['blockName'] ?? '';
35
36 // is has 'jet-forms/' namespace in 'blockName' property
37 if ( ! is_string( $name ) || ! self::is_field( $name ) ) {
38 throw new \Exception( 'empty_or_not_field' );
39 }
40
41 if ( ! \WP_Block_Type_Registry::get_instance()->is_registered( $name ) ) {
42 throw new \Exception( 'not_block_type' );
43 }
44
45 return self::delete_namespace( $name );
46 }
47
48 /**
49 * @param $value
50 * @param $blocks
51 *
52 * @return array
53 */
54 public static function find_block_by_name( $value, $blocks ): array {
55 return self::find_block_by_attr( $value, 'name', $blocks );
56 }
57
58 /**
59 * @param $value
60 * @param $attr_name
61 * @param $blocks
62 *
63 * @return array
64 */
65 public static function find_block_by_attr( $value, $attr_name, $blocks ): array {
66 return self::find_block(
67 function ( $block ) use ( $value, $attr_name ) {
68 if ( empty( $block['blockName'] ) || ! self::is_field( $block['blockName'] ) ) {
69 return false;
70 }
71 return ( ( $block['attrs'][ $attr_name ] ?? false ) === $value );
72 },
73 $blocks
74 );
75 }
76
77 public static function find_by_block_name( array $blocks, string $block_name ): array {
78 return self::find_block(
79 function ( $block ) use ( $block_name ) {
80 return ( ( $block['blockName'] ?? false ) === $block_name );
81 },
82 $blocks
83 );
84 }
85
86 public static function get_form_field_names( $blocks ): array {
87 $names = array();
88
89 self::find_block(
90 function ( $block ) use ( &$names ) {
91 if ( empty( $block['blockName'] ) || ! self::is_field( $block['blockName'] ) ) {
92 return false;
93 }
94 if ( ! empty( $block['attrs']['name'] ) ) {
95 $names[ $block['attrs']['name'] ] = 1;
96 }
97
98 return false;
99 },
100 $blocks
101 );
102
103 return $names;
104 }
105
106 public static function find_block( $callback, $blocks ): array {
107 if ( ! is_callable( $callback ) ) {
108 return array();
109 }
110 foreach ( $blocks as $block ) {
111 if ( ! isset( $block['blockName'] ) && ! isset( $block['attrs'] ) ) {
112 continue;
113 }
114 if ( call_user_func( $callback, $block ) ) {
115 return $block;
116 }
117
118 if ( 0 < count( $block['innerBlocks'] ?? array() ) ) {
119 $find = self::find_block( $callback, $block['innerBlocks'] );
120
121 if ( $find ) {
122 return $find;
123 }
124 }
125 }
126
127 return array();
128 }
129
130
131 /**
132 * @param $blocks
133 * @param string $scope
134 *
135 * @return array
136 */
137 public static function filter_blocks_by_namespace( $blocks, string $scope = Form_Manager::NAMESPACE_FIELDS ): array {
138 $fields = array();
139 $generator = self::generate_blocks_in_space( $blocks, $scope );
140
141 foreach ( $generator as $block ) {
142 $fields[] = $block;
143 }
144
145 return $fields;
146 }
147
148 /**
149 * @since 3.1.0
150 *
151 * @param array $blocks
152 * @param string $scope
153 *
154 * @return \Generator
155 */
156 public static function generate_blocks_in_space(
157 array $blocks,
158 string $scope = Form_Manager::NAMESPACE_FIELDS
159 ): \Generator {
160 return self::generate_blocks(
161 static function ( $block ) use ( $scope ) {
162 return ( false !== stripos( $block['blockName'], $scope ) );
163 },
164 $blocks
165 );
166 }
167
168 /**
169 * @param $callback
170 * @param array $source
171 *
172 * @return \Generator
173 * @since 3.1.0
174 */
175 public static function generate_blocks( $callback, array $source ): \Generator {
176 foreach ( $source as $index => $block ) {
177 if ( ! isset( $block['blockName'] ) ) {
178 continue;
179 }
180 if ( call_user_func( $callback, $block ) ) {
181 yield $block;
182 }
183
184 if ( ! empty( $block['innerBlocks'] ) ) {
185 yield from self::generate_blocks( $callback, $block['innerBlocks'] );
186 }
187 }
188 }
189
190 public static function is_valid_form_post( $post_id, bool $allow_preview = false ): bool {
191 $post = get_post( $post_id );
192
193 if ( ! is_a( $post, \WP_Post::class ) ) {
194 return false;
195 }
196
197 if (
198 Post_Type_Module::SLUG === $post->post_type
199 && 'publish' === $post->post_status
200 ) {
201 return true;
202 }
203
204 return $allow_preview && self::is_valid_preview_form_post( $post );
205 }
206
207 public static function is_valid_preview_form_post( $post_id ): bool {
208 $post = get_post( $post_id );
209
210 if ( ! is_a( $post, \WP_Post::class ) ) {
211 return false;
212 }
213
214 $nonce = self::get_preview_nonce();
215
216 if ( ! $nonce || ! wp_verify_nonce( $nonce, self::PREVIEW_NONCE_ACTION ) ) {
217 return false;
218 }
219
220 if ( Post_Type_Module::SLUG === $post->post_type ) {
221 return current_user_can( 'edit_jet_fb_form', $post->ID );
222 }
223
224 if ( 'revision' !== $post->post_type ) {
225 return false;
226 }
227
228 $parent_form_id = (int) $post->post_parent;
229 $parent_form = $parent_form_id ? get_post( $parent_form_id ) : null;
230
231 if ( ! $parent_form || Post_Type_Module::SLUG !== $parent_form->post_type ) {
232 return false;
233 }
234
235 return current_user_can( 'edit_jet_fb_form', $parent_form_id );
236 }
237
238 public static function get_preview_nonce(): string {
239 // phpcs:ignore WordPress.Security.NonceVerification.Recommended
240 if ( empty( $_REQUEST[ self::PREVIEW_NONCE_FIELD ] ) ) {
241 return '';
242 }
243
244 // phpcs:ignore WordPress.Security.NonceVerification.Recommended
245 return sanitize_text_field( wp_unslash( $_REQUEST[ self::PREVIEW_NONCE_FIELD ] ) );
246 }
247
248 public static function get_blocks_by_post(
249 $post_id,
250 bool $require_form_post = true,
251 bool $allow_preview = false
252 ): array {
253 $post = get_post( $post_id );
254
255 if ( ! is_a( $post, \WP_Post::class ) ) {
256 return array();
257 }
258
259 if ( $require_form_post && ! self::is_valid_form_post( $post, $allow_preview ) ) {
260 return array();
261 }
262
263 return self::get_blocks_from_content( $post->post_content );
264 }
265
266 /**
267 * Parses block content and expands reusable (`core/block`) references in exactly the
268 * same way as `get_blocks_by_post()`.
269 *
270 * @param string $post_content
271 *
272 * @return array
273 */
274 public static function get_blocks_from_content( string $post_content ): array {
275 return array_map(
276 function ( $block ) {
277 self::walk_by_reusable( $block, array() );
278
279 return $block;
280 },
281 parse_blocks( $post_content )
282 );
283 }
284
285 /**
286 * @param array $block
287 * @param array $visited_reusable_ids IDs already visited in the current reference path.
288 */
289 private static function walk_by_reusable( array &$block, array $visited_reusable_ids ) {
290 if ( ! empty( $block['innerBlocks'] ) ) {
291 foreach ( $block['innerBlocks'] as &$current ) {
292 self::walk_by_reusable( $current, $visited_reusable_ids );
293 }
294
295 return;
296 }
297
298 if ( 'core/block' !== $block['blockName'] ) {
299 return;
300 }
301
302 $reusable_id = (int) ( $block['attrs']['ref'] ?? 0 );
303
304 if ( ! $reusable_id || isset( $visited_reusable_ids[ $reusable_id ] ) ) {
305 $block['innerBlocks'] = array();
306
307 return;
308 }
309
310 $reusable = get_post( $reusable_id );
311
312 if ( ! $reusable instanceof \WP_Post ) {
313 $block['innerBlocks'] = array();
314
315 return;
316 }
317
318 $visited_reusable_ids[ $reusable_id ] = true;
319 $block['innerBlocks'] = array_map(
320 function ( $inner_block ) use ( $visited_reusable_ids ) {
321 self::walk_by_reusable( $inner_block, $visited_reusable_ids );
322
323 return $inner_block;
324 },
325 parse_blocks( $reusable->post_content )
326 );
327 }
328
329 public static function delete_namespace( $block ): string {
330 if ( is_array( $block ) ) {
331 $block = $block['blockName'] ?? '';
332 }
333
334 if ( stripos( $block, '/' ) === false ) {
335 return $block;
336 }
337
338 return explode( '/', $block )[1] ?? '';
339 }
340
341 public static function is_field( string $block_name ): bool {
342 return ( stripos( $block_name, Form_Manager::NAMESPACE_FIELDS ) !== false );
343 }
344
345 public static function render_with_context( $block, $context ) {
346 return ( new \WP_Block( $block, $context ) )->render();
347 }
348
349 public static function pref( string $block_name ): string {
350 return Form_Manager::NAMESPACE_FIELDS . self::delete_namespace( $block_name );
351 }
352
353 public static function get_attrs_from_block( array $block, array $attrs_list ): array {
354 $source = $block['attrs'] ?? $block;
355 $attrs = array();
356
357 foreach ( $attrs_list as $attr_name ) {
358 if ( ! isset( $source[ $attr_name ] ) ) {
359 continue;
360 }
361 $attrs[ $attr_name ] = $source[ $attr_name ];
362 }
363
364 return $attrs;
365 }
366
367 /**
368 * @param $names
369 *
370 * @return array|string
371 */
372 public static function get_block_names( $names ) {
373 if ( ! is_array( $names ) ) {
374 $block = jet_form_builder()->blocks->get_field_by_name( $names );
375
376 return $block->get_name();
377 }
378
379 return array_map(
380 array( static::class, 'get_block_names' ),
381 $names
382 );
383 }
384
385 public static function current_block(): array {
386 return is_array( \WP_Block_Supports::$block_to_render )
387 ? \WP_Block_Supports::$block_to_render
388 : array();
389 }
390
391
392 /**
393 * @return \WP_Block_Type|null
394 */
395 public static function current_block_type() {
396 $name = self::current_block()['blockName'] ?? '';
397
398 return \WP_Block_Type_Registry::get_instance()->get_registered( $name );
399 }
400
401 }
402