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 / modules / block-parsers / field-data-parser.php

field-data-parser.php in JetFormBuilder — Dynamic Blocks Form Builder 3.6.6, at modules/block-parsers/field-data-parser.php

610 lines 13.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3
4 namespace JFB_Modules\Block_Parsers;
5
6 use Jet_Form_Builder\Classes\Arrayable\Array_Tools;
7 use Jet_Form_Builder\Classes\Resources\Media_Block_Value;
8 use Jet_Form_Builder\Classes\Resources\Uploaded_File_Path;
9 use Jet_Form_Builder\Classes\Tools;
10 use Jet_Form_Builder\Exceptions\Parse_Exception;
11 use Jet_Form_Builder\Exceptions\Repository_Exception;
12 use Jet_Form_Builder\Request\Exceptions\Plain_Value_Exception;
13 use Jet_Form_Builder\Request\Parser_Context;
14 use JFB_Components\Repository\Repository_Item_Instance_Trait;
15 use Jet_Form_Builder\Classes\Resources\File;
16 use Jet_Form_Builder\Classes\Resources\File_Collection;
17 use Jet_Form_Builder\Request\Exceptions\Exclude_Field_Exception;
18
19 // If this file is called directly, abort.
20 if ( ! defined( 'WPINC' ) ) {
21 die;
22 }
23
24 /**
25 * @property bool is_required
26 *
27 * Class Field_Data_Parser
28 * @package Jet_Form_Builder\Request
29 */
30 abstract class Field_Data_Parser implements Repository_Item_Instance_Trait {
31
32 /**
33 * @var string
34 */
35 protected $name;
36 /**
37 * @var string
38 */
39 protected $type;
40 /**
41 * @var array
42 */
43 protected $settings = array();
44
45 protected $value;
46
47 /** @var File|File_Collection|bool */
48 protected $file;
49
50 /** @var Parser_Context */
51 protected $context;
52
53 /** @var Parser_Context[] */
54 public $inner_contexts;
55
56 /** @var Parser_Context */
57 protected $inner_template;
58
59 protected $with_inner = true;
60
61 /**
62 * @var \WP_Error
63 */
64 protected $wp_error;
65
66 /**
67 * @since 3.3.0
68 * @var bool
69 */
70 private $complete_apply = false;
71
72 /**
73 * Second part in blockName (after "jet-forms/")
74 *
75 * Or return "default" to replace the default parser that applies
76 * to all fields that do not have a personal parser
77 *
78 * @return string
79 */
80 abstract public function type();
81
82 /**
83 * @return string
84 */
85 public function rep_item_id() {
86 return $this->type();
87 }
88
89 /**
90 * Used for internal purposes. To get the true value, use the method
91 * \JFB_Modules\Block_Parsers\Field_Data_Parser::get_value
92 *
93 * @return mixed
94 */
95 public function get_response() {
96 return $this->value;
97 }
98
99 public function update_request() {
100 if ( $this->is_complete_apply() ) {
101 return;
102 }
103
104 $this->wp_error = new \WP_Error();
105
106 try {
107 $this->is_field_visible();
108 $this->set_request();
109 } catch ( Exclude_Field_Exception $exception ) {
110 $this->get_context()->remove( $this->get_name() );
111
112 return;
113 } catch ( Parse_Exception $exception ) {
114 $this->get_context()->remove( $this->get_name() );
115
116 foreach ( $exception->get_inner() as $key => $value ) {
117 $this->get_context()->update_request( $value, $key );
118 }
119
120 return;
121 }
122
123 $this->check_response();
124
125 /**
126 * To prevent repeatable execution of this method
127 *
128 * @since 3.3.0
129 */
130 $this->set_complete_apply( true );
131
132 do_action( 'jet-form-builder/validate-field', $this );
133 }
134
135 /**
136 * @throws Exclude_Field_Exception|Parse_Exception
137 */
138 final public function set_request() {
139 // reset
140 $this->inner_contexts = array();
141
142 $this->set_value( $this->get_context()->get_request( $this->name ) );
143 $this->set_file( $this->get_context()->get_files( $this->name ) );
144
145 $this->value = $this->get_response();
146 }
147
148 public function parse_value( $value ) {
149 if ( is_array( $value ) && ! $this->allows_array_value() ) {
150 $this->collect_error( 'invalid_value' );
151
152 return '';
153 }
154
155 return $value;
156 }
157
158 protected function allows_array_value(): bool {
159 return false;
160 }
161
162 public function check_response() {
163 if (
164 $this->is_inside_conditional() ||
165 ( Tools::is_empty( $this->value ) && ! $this->is_required ) ||
166 ! Tools::is_empty( $this->value )
167 ) {
168 return;
169 }
170
171 $this->collect_error( 'empty_field' );
172 }
173
174 /**
175 * @throws Exclude_Field_Exception
176 */
177 protected function is_field_visible() {
178 $visibility = $this->settings['visibility'] ?? true;
179
180 // If is visible for logged in users and user is logged in - show field.
181 if ( 'logged_id' === $visibility && ! is_user_logged_in() ) {
182 throw new Exclude_Field_Exception();
183 }
184
185 // If is visible for not logged in users and user is not logged in - show field.
186 if ( 'not_logged_in' === $visibility && is_user_logged_in() ) {
187 throw new Exclude_Field_Exception();
188 }
189 }
190
191 public function set_context( Parser_Context $context ): Field_Data_Parser {
192 $this->context = $context;
193
194 return $this;
195 }
196
197 public function set_value( $value ) {
198 $this->value = $this->parse_value( $value );
199 }
200
201 /**
202 * @param bool|Media_Block_Value|Uploaded_File_Path $file
203 */
204 public function set_file( $file ) {
205 $this->file = $file;
206 }
207
208 public function set_type( string $type ) {
209 $this->type = $type;
210 }
211
212 /**
213 * @param string $name
214 */
215 public function set_name( string $name ) {
216 $this->name = $name;
217 }
218
219 /**
220 * @param array $settings
221 *
222 * @since 3.1.0
223 */
224 public function set_settings( array $settings ) {
225 $this->settings = $settings;
226 }
227
228 public function is_secure(): bool {
229 return 'password' === $this->get_setting( 'field_type' );
230 }
231
232 public function make_secure() {
233 $this->set_setting( 'field_type', 'password' );
234 }
235
236 /**
237 * @param string $attr
238 * @param $value
239 *
240 * @since 3.1.0
241 */
242 public function set_setting( string $attr, $value ) {
243 $this->settings[ $attr ] = $value;
244 }
245
246 /**
247 * @return Parser_Context
248 * @since 3.0.4
249 */
250 public function get_context(): Parser_Context {
251 return $this->context;
252 }
253
254 public function iterate_file(): \Generator {
255 yield $this->name => $this->get_file();
256 }
257
258 /**
259 * @return bool|File|File_Collection|array
260 */
261 public function get_file() {
262 if ( empty( $this->inner_contexts ) ) {
263 return $this->file;
264 }
265
266 return iterator_to_array( $this->iterate_inner_files() );
267 }
268
269 public function iterate_self( bool $break_on_first = true ): \Generator {
270 yield $this->get_scoped_name() => $this;
271
272 if ( empty( $this->inner_contexts ) && empty( $this->get_inner_template() ) ) {
273 return;
274 }
275
276 if ( empty( $this->inner_contexts ) ) {
277 yield from $this->get_inner_template()->iterate_parsers_list( $break_on_first );
278
279 return;
280 }
281
282 foreach ( $this->inner_contexts as $context ) {
283 yield from $context->iterate_parsers_list( $break_on_first );
284
285 if ( $break_on_first ) {
286 break;
287 }
288 }
289 }
290
291 /**
292 * @return string
293 */
294 public function get_type(): string {
295 return $this->type;
296 }
297
298 /**
299 * @return string
300 */
301 public function get_name(): string {
302 return $this->name;
303 }
304
305 public function iterate_value(): \Generator {
306 yield $this->name => $this->get_value();
307 }
308
309 public function iterate_row_value(): \Generator {
310 if ( ! $this->get_inner_template() ) {
311 yield $this->get_scoped_name() => $this->get_value();
312
313 return;
314 }
315
316 if ( empty( $this->inner_contexts ) ) {
317 return;
318 }
319
320 $inner = current( $this->inner_contexts );
321
322 if ( ! $inner ) {
323 $inner = end( $this->inner_contexts );
324
325 // current() should always return false, if it has reached end once
326 next( $this->inner_contexts );
327 }
328
329 yield from $inner->iterate_values_row();
330 }
331
332 public function is_inner_over(): bool {
333 return empty( $this->inner_contexts ) || key( $this->inner_contexts ) === null;
334 }
335
336 public function next_inner() {
337 if ( empty( $this->inner_contexts ) ) {
338 return;
339 }
340 next( $this->inner_contexts );
341 }
342
343 /**
344 * @return mixed
345 */
346 public function get_value() {
347 if ( empty( $this->inner_contexts ) || ! $this->with_inner ) {
348 return $this->value;
349 }
350
351 return iterator_to_array( $this->iterate_inner_values() );
352 }
353
354 public function reset() {
355 $this->value = null;
356 $this->file = null;
357 $this->inner_contexts = null;
358 }
359
360 public function get_setting( string $attr_name ) {
361 return $this->settings[ $attr_name ] ?? false;
362 }
363
364 /**
365 * @return array
366 */
367 public function get_settings(): array {
368 return $this->settings;
369 }
370
371 public function __get( $name ) {
372 switch ( $name ) {
373 case 'is_required':
374 return $this->settings['required'] ?? false;
375 default:
376 return null;
377 }
378 }
379
380 /**
381 * @param string|int $index
382 */
383 public function remove_context( $index ) {
384 if ( ! array_key_exists( $index, $this->inner_contexts ) ||
385 ! ( $this->inner_contexts[ $index ] instanceof Parser_Context )
386 ) {
387 return;
388 }
389
390 unset( $this->inner_contexts[ $index ] );
391 }
392
393 public function add_inner( Parser_Context $context, $key = '' ) {
394 if ( '' === $key ) {
395 $this->inner_contexts[] = $context;
396 } else {
397 $this->inner_contexts[ $key ] = $context;
398 }
399
400 return Array_Tools::last_key( $this->inner_contexts );
401 }
402
403 /**
404 * @return Parser_Context|null
405 */
406 public function get_inner_template() {
407 return $this->inner_template;
408 }
409
410 /**
411 * @param Parser_Context $inner_template
412 */
413 public function set_inner_template( Parser_Context $inner_template ) {
414 $this->inner_template = $inner_template;
415 }
416
417 protected function iterate_inner_values(): \Generator {
418 foreach ( $this->inner_contexts as $index => $parser_context ) {
419 yield $index => $parser_context->get_value( array() );
420 }
421 }
422
423 protected function iterate_inner_files(): \Generator {
424 foreach ( $this->inner_contexts as $index => $parser_context ) {
425 yield $index => $parser_context->get_file( array() );
426 }
427 }
428
429 /**
430 * @param array $inner_blocks
431 */
432 public function set_inner_contexts( array $inner_blocks ) {
433 if ( empty( $inner_blocks ) ) {
434 return;
435 }
436
437 $context = new Parser_Context();
438 $context->set_parent( $this );
439 $context->set_parsers( $inner_blocks );
440
441 if ( ! count( iterator_to_array( $context->iterate_parsers() ) ) ) {
442 return;
443 }
444
445 $this->set_inner_template( $context );
446 }
447
448 /**
449 * This applies to the case where the parser is inside a repeater string template.
450 * The difference between the template and the actual context is that the template
451 * cannot contain fields with values, so there is nothing to sanitize there
452 *
453 * @return bool
454 * @since 3.2.0
455 */
456 public function is_in_template(): bool {
457 if ( ! $this->get_context()->get_parent_field() ) {
458 return false;
459 }
460
461 return '' === $this->get_context()->get_index_in_parent();
462 }
463
464 public function get_scoped_name(): string {
465 return trim( $this->get_context()->get_parent_name() . '.' . $this->get_name(), '.' );
466 }
467
468 public function get_scoped_label(): string {
469 return trim( $this->get_context()->get_parent_label() . ':' . $this->get_label(), ':' );
470 }
471
472 public function get_label(): string {
473 $label = $this->settings['label'] ?? '';
474
475 return empty( $label ) ? $this->get_name() : $label;
476 }
477
478 /**
479 * @param bool $with_inner
480 */
481 public function set_with_inner( bool $with_inner ) {
482 $this->with_inner = $with_inner;
483 }
484
485 public function iterate_inner(): \Generator {
486 if ( empty( $this->inner_contexts ) ) {
487 return;
488 }
489
490 foreach ( $this->inner_contexts as $context ) {
491 yield $context;
492 }
493 }
494
495 /**
496 * @param string|\WP_Error $error
497 */
498 public function collect_error( $error, $message = '', $data = '' ) {
499 if ( ! is_wp_error( $this->wp_error ) ) {
500 $this->wp_error = new \WP_Error();
501 }
502
503 if ( is_string( $error ) ) {
504 $this->wp_error->add( $error, $message, $data );
505 }
506 if ( is_wp_error( $error ) ) {
507 $this->wp_error->merge_from( $error );
508 }
509 }
510
511 public function is_collected_error( string $error_code ): bool {
512 if ( ! is_wp_error( $this->wp_error ) ) {
513 return false;
514 }
515 $codes = $this->wp_error->get_error_codes();
516
517 return in_array( $error_code, $codes, true );
518 }
519
520 public function get_errors(): array {
521 return is_wp_error( $this->wp_error ) ? $this->wp_error->get_error_codes() : array();
522 }
523
524 public function set_inside_conditional( bool $is_inside ) {
525 $this->set_setting( '_inside_conditional', $is_inside );
526 }
527
528 public function is_inside_conditional(): bool {
529 return (bool) $this->get_setting( '_inside_conditional' );
530 }
531
532 /**
533 * @param array $path
534 *
535 * @return Field_Data_Parser|Parser_Context
536 * @throws Plain_Value_Exception|Repository_Exception
537 */
538 public function resolve( array $path ) {
539 if ( empty( $this->inner_contexts ) ) {
540 throw new Repository_Exception();
541 }
542
543 $index = array_shift( $path );
544
545 if ( ! array_key_exists( $index, $this->inner_contexts ) ||
546 ! ( $this->inner_contexts[ $index ] instanceof Parser_Context )
547 ) {
548 throw new Repository_Exception();
549 }
550
551 return empty( $path )
552 ? $this->inner_contexts[ $index ]
553 : $this->inner_contexts[ $index ]->resolve( $path );
554 }
555
556 /**
557 * @param string $name
558 *
559 * @return Field_Data_Parser
560 * @throws Plain_Value_Exception|Repository_Exception
561 */
562 public function resolve_to_up( string $name ): Field_Data_Parser {
563 $parser = $this->get_context()->resolve_to_up( $name );
564
565 if ( ! ( $parser instanceof Field_Data_Parser ) ) {
566 throw new Repository_Exception();
567 }
568
569 return $parser;
570 }
571
572 /**
573 * @return bool
574 */
575 public function is_complete_apply(): bool {
576 return $this->complete_apply;
577 }
578
579 /**
580 * @param bool $complete_apply
581 */
582 public function set_complete_apply( bool $complete_apply ) {
583 $this->complete_apply = $complete_apply;
584 }
585
586 public function __clone() {
587 if ( is_array( $this->inner_contexts ) ) {
588 foreach ( $this->inner_contexts as $key => $context ) {
589 $this->inner_contexts[ $key ] = clone $context;
590 }
591 }
592
593 if ( ! is_wp_error( $this->wp_error ) ) {
594 return;
595 }
596
597 $this->wp_error = clone $this->wp_error;
598 }
599
600 public function __debugInfo(): array {
601 $current = clone $this;
602
603 unset( $current->context );
604 $current->wp_error = $current->get_errors();
605
606 return get_object_vars( $current );
607 }
608
609 }
610