Completed
Push — milestone/2_0/react-ui ( 2ba23e...94c03b )
by
unknown
03:12
created

Field::help_text()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
ccs 0
cts 2
cp 0
crap 2
1
<?php
2
3
namespace Carbon_Fields\Field;
4
5
use Carbon_Fields\Carbon_Fields;
6
use Carbon_Fields\Pimple\Container as PimpleContainer;
7
use Carbon_Fields\Datastore\Datastore_Interface;
8
use Carbon_Fields\Datastore\Datastore_Holder_Interface;
9
use Carbon_Fields\Value_Set\Value_Set;
10
use Carbon_Fields\Helper\Helper;
11
use Carbon_Fields\Exception\Incorrect_Syntax_Exception;
12
13
/**
14
 * Base field class.
15
 * Defines the key container methods and their default implementations.
16
 * Implements factory design pattern.
17
 */
18
class Field implements Datastore_Holder_Interface {
19
	
20
	/**
21
	 * Array of field class names that have had their activation method called
22
	 *
23
	 * @var array<string>
24
	 */
25
	protected static $activated_field_types = array();
26
27
	/**
28
	 * Globally unique field identificator. Generated randomly
29
	 *
30
	 * @var string
31
	 */
32
	protected $id;
33
34
	/**
35
	 * Stores the initial <kbd>$type</kbd> variable passed to the <code>factory()</code> method
36
	 *
37
	 * @see factory
38
	 * @var string
39
	 */
40
	public $type;
41
42
	/**
43
	 * Array of ancestor field names
44
	 *
45
	 * @var array
46
	 */
47
	protected $hierarchy = array();
48
49
	/**
50
	 * Array of complex entry ids
51
	 *
52
	 * @var array
53
	 */
54
	protected $hierarchy_index = array();
55
56
	/**
57
	 * Field value
58
	 *
59
	 * @var Value_Set
60
	 */
61
	protected $value_set;
62
63
	/**
64
	 * Default field value
65
	 *
66
	 * @var mixed
67
	 */
68
	protected $default_value = '';
69
70
	/**
71
	 * Sanitized field name used as input name attribute during field render
72
	 *
73
	 * @see factory()
74
	 * @see set_name()
75
	 * @var string
76
	 */
77
	protected $name;
78
79
	/**
80
	 * Field name prefix
81
	 *
82
	 * @see set_name()
83
	 * @var string
84
	 */
85
	protected $name_prefix = '_';
86
87
	/**
88
	 * The base field name which is used in the container.
89
	 *
90
	 * @see set_base_name()
91
	 * @var string
92
	 */
93
	protected $base_name;
94
95
	/**
96
	 * Field name used as label during field render
97
	 *
98
	 * @see factory()
99
	 * @see set_label()
100
	 * @var string
101
	 */
102
	protected $label;
103
104
	/**
105
	 * Additional text containing information and guidance for the user
106
	 *
107
	 * @see help_text()
108
	 * @var string
109
	 */
110
	protected $help_text;
111
112
	/**
113
	 * Field DataStore instance to which save, load and delete calls are delegated
114
	 *
115
	 * @see set_datastore()
116
	 * @see get_datastore()
117
	 * @var Datastore_Interface
118
	 */
119
	protected $datastore;
120
121
	/**
122
	 * Flag whether the datastore is the default one or replaced with a custom one
123
	 *
124
	 * @see set_datastore()
125
	 * @see get_datastore()
126
	 * @var boolean
127
	 */
128
	protected $has_default_datastore = true;
129
130
	/**
131
	 * The type of the container this field is in
132
	 *
133
	 * @see get_context()
134
	 * @var string
135
	 */
136
	protected $context;
137
138
	/**
139
	 * Whether or not this value should be auto loaded. Applicable to theme options only.
140
	 *
141
	 * @see set_autoload()
142
	 * @var bool
143
	 */
144
	protected $autoload = false;
145
146
	/**
147
	 * Whether or not this field will be initialized when the field is in the viewport (visible).
148
	 *
149
	 * @see set_lazyload()
150
	 * @var bool
151
	 */
152
	protected $lazyload = false;
153
154
	/**
155
	 * Key-value array of attribtues and their values
156
	 * 
157
	 * @var array
158
	 */
159
	protected $attributes = array();
160
161
	/**
162
	 * Array of attributes the user is allowed to change
163
	 * 
164
	 * @var array<string>
165
	 */
166
	protected $allowed_attributes = array( 'max', 'maxLength', 'min', 'pattern', 'placeholder', 'readOnly', 'step', 'type' );
167
168
	/**
169
	 * The width of the field.
170
	 *
171
	 * @see set_width()
172
	 * @var int
173
	 */
174
	protected $width = 0;
175
176
	/**
177
	 * Custom CSS classes.
178
	 *
179
	 * @see set_classes()
180
	 * @var array
181
	 */
182
	protected $classes = array();
183
184
	/**
185
	 * Whether or not this field is required.
186
	 *
187
	 * @see set_required()
188
	 * @var bool
189
	 */
190
	protected $required = false;
191
192
	/**
193
	 * Stores the field conditional logic rules.
194
	 *
195
	 * @var array
196
	 */
197
	protected $conditional_logic = array();
198
199
	/**
200
	 * Whether the field should be included in the response of the requests to the REST API
201
	 *
202
	 * @see  set_visible_in_rest_api
203
	 * @see  get_visible_in_rest_api
204
	 * @var boolean
205
	 */
206
	protected $visible_in_rest_api = false;
207
208
	/**
209
	 * Clone the Value_Set object as well
210
	 *
211
	 * @var array
212
	 */
213
	public function __clone() {
214
		$this->set_value_set( clone $this->get_value_set() );
215
	}
216
217
	/**
218
	 * Create a new field of type $raw_type and name $name and label $label.
219
	 *
220
	 * @param string $raw_type
221
	 * @param string $name lower case and underscore-delimited
222
	 * @param string $label (optional) Automatically generated from $name if not present
223
	 * @return Field
224
	 */
225 15
	public static function factory( $raw_type, $name, $label = null ) {
226 15
		$type = Helper::normalize_type( $raw_type );
227
228
		// stop hidden symbol support when the end user is creating fields ][
229
		// @see Field::set_name()
230 15
		if ( ! Helper::is_valid_entity_id( $name ) ) {
231 5
			Incorrect_Syntax_Exception::raise( 'Field names can only contain lowercase alphanumeric characters, dashes and underscores ("' . $name . '" passed).' );
232
			return null;
233
		}
234
235 10
		if ( Carbon_Fields::has( $type, 'fields' ) ) {
236
			return Carbon_Fields::resolve_with_arguments( $type, array(
237
				'type' => $type,
238
				'name' => $name,
239
				'label' => $label,
240
			), 'fields' );
241
		}
242
243
		// Fallback to class name-based resolution
244 10
		$class = Helper::type_to_class( $type, __NAMESPACE__, '_Field' );
245 10
		if ( ! class_exists( $class ) ) {
246 4
			Incorrect_Syntax_Exception::raise( 'Unknown field type "' . $raw_type . '".' );
247 1
			$class = __NAMESPACE__ . '\\Broken_Field';
248 1
		}
249
250 7
		$field = new $class( $type, $name, $label );
251 7
		return $field;
252
	}
253
254
	/**
255
	 * An alias of factory().
256
	 *
257
	 * @see Field::factory()
258
	 * @return Field
259
	 */
260 15
	public static function make( $type, $name, $label = null ) {
261 15
		return static::factory( $type, $name, $label );
262
	}
263
264
	/**
265
	 * Create a field from a certain type with the specified label.
266
	 * 
267
	 * @param string $type  Field type
268
	 * @param string $name  Field name
269
	 * @param string $label Field label
270
	 */
271 7
	public function __construct( $type, $name, $label ) {
272 7
		Carbon_Fields::verify_boot();
273
		
274 7
		$this->type = $type;
275 7
		$this->set_base_name( $name );
276 7
		$this->set_name( $name );
277 7
		$this->set_label( $label );
278
279
		// Pick random ID
280 7
		$random_string = md5( mt_rand() . $this->get_name() . $this->get_label() );
281 7
		$random_string = substr( $random_string, 0, 5 ); // 5 chars should be enough
282 7
		$this->id = 'carbon-' . $random_string;
283
284 7
		$this->init();
285 7
	}
286
287
	/**
288
	 * Returns the type of the field based on the class.
289
	 * The class is stripped by the "CarbonFields" prefix.
290
	 * Also the "Field" suffix is removed.
291
	 * Then underscores and backslashes are removed.
292
	 *
293
	 * @return string
294
	 */
295
	public function get_type() {
296
		return Helper::class_to_type( get_class( $this ), '_Field' );
297
	}
298
299
	/**
300
	 * Activate the field once the container is attached.
301
	 */
302
	public function activate() {
303
		$this->admin_init();
304
305
		add_action( 'admin_print_footer_scripts', array( get_class(), 'admin_hook_scripts' ), 5 );
306
		add_action( 'admin_print_footer_scripts', array( get_class(), 'admin_hook_styles' ), 5 );
307
		static::activate_field_type( get_class( $this ) );
308
309
		do_action( 'carbon_fields_field_activated', $this );
310
	}
311
312
	/**
313
	 * Activate a field type
314
	 * 
315
	 * @param string $class_name
316
	 */
317
	public static function activate_field_type( $class_name ) {
318
		if ( in_array( $class_name, static::$activated_field_types ) ) {
319
			return;
320
		}
321
322
		add_action( 'admin_print_footer_scripts', array( $class_name, 'admin_enqueue_scripts' ), 5 );
323
		call_user_func( array( $class_name, 'field_type_activated' ) );
324
325
		static::$activated_field_types[] = $class_name;
326
	}
327
328
	/**
329
	 * Prepare the field type for use
330
	 * Called once per field type when activated
331
	 */
332
	public static function field_type_activated() {}
333
334
	/**
335
	 * Get array of hierarchy field names
336
	 *
337
	 * @return array
338
	 */
339
	public function get_hierarchy() {
340
		return $this->hierarchy;
341
	}
342
343
	/**
344
	 * Set array of hierarchy field names
345
	 * 
346
	 * @return Field $this
347
	 */
348
	public function set_hierarchy( $hierarchy ) {
349
		$this->hierarchy = $hierarchy;
350
		return $this;
351
	}
352
353
	/**
354
	 * Get array of hierarchy indexes
355
	 *
356
	 * @return array
357
	 */
358
	public function get_hierarchy_index() {
359
		return $this->hierarchy_index;
360
	}
361
362
	/**
363
	 * Set array of hierarchy indexes
364
	 * 
365
	 * @return Field $this
366
	 */
367
	public function set_hierarchy_index( $hierarchy_index ) {
368
		$hierarchy_index = ( ! empty( $hierarchy_index ) ) ? $hierarchy_index : array();
369
		$this->hierarchy_index = $hierarchy_index;
370
		return $this;
371
	}
372
373
	/**
374
	 * Return whether the field is a root field and holds a single value
375
	 *
376
	 * @return bool
377
	 */
378 3
	public function is_simple_root_field() {
379 3
		$hierarchy = $this->get_hierarchy();
380
		return (
381 3
			empty( $hierarchy )
382 3
			&&
383 2
			in_array( $this->get_value_set()->get_type(), array( Value_Set::TYPE_SINGLE_VALUE, Value_Set::TYPE_MULTIPLE_PROPERTIES ) )
384 3
		);
385
	}
386
387
	/**
388
	 * Perform instance initialization
389
	 */
390
	public function init() {}
391
392
	/**
393
	 * Instance initialization when in the admin area
394
	 * Called during field boot
395
	 */
396
	public function admin_init() {}
397
398
	/**
399
	 * Enqueue scripts and styles in admin
400
	 * Called once per field type
401
	 */
402
	public static function admin_enqueue_scripts() {}
403
404
	/**
405
	 * Get value from datastore
406
	 *
407
	 * @param bool $fallback_to_default
408
	 * @return mixed
409
	 */
410
	protected function get_value_from_datastore( $fallback_to_default = true ) {
411
		$value = $this->get_datastore()->load( $this );
412
413
		if ( $value === null && $fallback_to_default ) {
414
			$value = $this->get_default_value();
415
		}
416
417
		return $value;
418
	}
419
420
	/**
421
	 * Load value from datastore
422
	 */
423 2
	public function load() {
424 2
		$this->set_value( $this->get_value_from_datastore() );
425 2
	}
426
427
	/**
428
	 * Save value to storage
429
	 */
430 1
	public function save() {
431 1
		$delete_on_save = apply_filters( 'carbon_fields_should_delete_field_value_on_save', true, $this );
432 1
		if ( $delete_on_save ) {
433 1
			$this->delete();
434 1
		}
435
436 1
		$save = apply_filters( 'carbon_fields_should_save_field_value', true, $this->get_value(), $this );
437 1
		if ( $save ) {
438 1
			$this->get_datastore()->save( $this );
439 1
		}
440 1
	}
441
442
	/**
443
	 * Delete value from storage
444
	 */
445 1
	public function delete() {
446 1
		$this->get_datastore()->delete( $this );
447 1
	}
448
449
	/**
450
	 * Load the field value from an input array based on it's name
451
	 *
452
	 * @param  array $input Array of field names and values.
453
	 * @return Field $this
454
	 */
455 2
	public function set_value_from_input( $input ) {
456 2
		if ( isset( $input[ $this->get_name() ] ) ) {
457 1
			$this->set_value( $input[ $this->get_name() ] );
458 1
		} else {
459 1
			$this->clear_value();
460
		}
461 2
		return $this;
462
	}
463
464
	/**
465
	 * Return whether the datastore instance is the default one or has been overriden
466
	 *
467
	 * @return boolean
468
	 */
469
	public function has_default_datastore() {
470
		return $this->has_default_datastore;
471
	}
472
473
	/**
474
	 * Get the DataStore instance
475
	 *
476
	 * @return Datastore_Interface $datastore
477
	 */
478 1
	public function get_datastore() {
479 1
		return $this->datastore;
480
	}
481
482
	/**
483
	 * Set datastore instance
484
	 *
485
	 * @param  Datastore_Interface $datastore
486
	 * @param  boolean             $set_as_default
487
	 * @return Field               $this
488
	 */
489 1
	public function set_datastore( Datastore_Interface $datastore, $set_as_default = false ) {
490 1
		if ( $set_as_default && ! $this->has_default_datastore() ) {
491
			return $this; // datastore has been overriden with a custom one - abort changing to a default one
492
		}
493 1
		$this->datastore = $datastore;
494 1
		$this->has_default_datastore = $set_as_default;
495 1
		return $this;
496
	}
497
498
	/**
499
	 * Return the type of the container this field is in
500
	 *
501
	 * @return string
502
	 */
503
	public function get_context() {
504
		return $this->context;
505
	}
506
507
	/**
508
	 * Assign the type of the container this field is in
509
	 *
510
	 * @param  string $context
511
	 * @return Field  $this
512
	 */
513
	public function set_context( $context ) {
514
		$this->context = $context;
515
		return $this;
516
	}
517
518
	/**
519
	 * Get the Value_Set object
520
	 *
521
	 * @return Value_Set
522
	 */
523 2
	public function get_value_set() {
524 2
		if ( $this->value_set === null ) {
525 1
			$this->set_value_set( new Value_Set() );
526 1
		}
527 2
		return $this->value_set;
528
	}
529
530
	/**
531
	 * Set the Value_Set object
532
	 *
533
	 * @param  Value_Set $value_set
534
	 * @return Field     $this
535
	 */
536 1
	public function set_value_set( $value_set ) {
537 1
		$this->value_set = $value_set;
538 1
		return $this;
539
	}
540
541
	/**
542
	 * Alias for $this->get_value_set()->get(); with fallback to default value
543
	 *
544
	 * @return mixed
545
	 */
546 3
	public function get_value() {
547 3
		if ( $this->get_value_set()->get() === null ) {
548 1
			$this->set_value( $this->get_default_value() );
549 1
		}
550 3
		return $this->get_value_set()->get();
551
	}
552
553
	/**
554
	 * Alias for $this->get_value_set()->get_set(); with fallback to default value
555
	 *
556
	 * @return array<array>
557
	 */
558
	public function get_full_value() {
559
		if ( $this->get_value_set()->get_set() === null ) {
560
			$this->set_value( $this->get_default_value() );
561
		}
562
		return $this->get_value_set()->get_set();
563
	}
564
565
	/**
566
	 * Return a differently formatted value for end-users
567
	 *
568
	 * @return mixed
569
	 */
570 2
	public function get_formatted_value() {
571 2
		return $this->get_value();
572
	}
573
574
	/**
575
	 * Alias for $this->get_value_set()->set( $value );
576
	 */
577 1
	public function set_value( $value ) {
578 1
		$this->get_value_set()->set( $value );
579 1
		return $this;
580
	}
581
582
	/**
583
	 * Clear the field value to a blank one (but not the default one)
584
	 */
585
	public function clear_value() {
586
		$this->get_value_set()->clear();
587
	}
588
589
	/**
590
	 * Get default field value
591
	 *
592
	 * @return mixed
593
	 */
594 1
	public function get_default_value() {
595 1
		return $this->default_value;
596
	}
597
598
	/**
599
	 * Set default field value
600
	 *
601
	 * @param  mixed $default_value
602
	 * @return Field $this
603
	 */
604 1
	public function set_default_value( $default_value ) {
605 1
		$this->default_value = $default_value;
606 1
		return $this;
607
	}
608
609
	/**
610
	 * Return the field base name.
611
	 *
612
	 * @return string
613
	 */
614
	public function get_base_name() {
615
		return $this->base_name;
616
	}
617
618
	/**
619
	 * Set field base name as defined in the container.
620
	 * 
621
	 * @return Field $this
622
	 */
623
	public function set_base_name( $name ) {
624
		$this->base_name = $name;
625
		return $this;
626
	}
627
628
	/**
629
	 * Return the field name
630
	 *
631
	 * @return string
632
	 */
633 2
	public function get_name() {
634 2
		return $this->name;
635
	}
636
637
	/**
638
	 * Set field name.
639
	 * Use only if you are completely aware of what you are doing.
640
	 *
641
	 * @param  string $name Field name, either sanitized or not
642
	 * @return Field  $this
643
	 */
644 2
	public function set_name( $name ) {
645 2
		if ( empty( $name ) ) {
646
			Incorrect_Syntax_Exception::raise( 'Field name can\'t be empty' );
647
			return $this;
648
		}
649
650
		// symbols ][ are supported in a hidden way - required for widgets to work (WP imposes dashes and square brackets on field names)
651 2
		$regex = '/\A[a-z0-9_\-\[\]]+\z/';
652 2
		if ( ! preg_match( $regex, $name ) ) {
653
			Incorrect_Syntax_Exception::raise( 'Field names  can only contain lowercase alphanumeric characters, dashes and underscores ("' . $name . '" passed).' );
654
			return $this;
655
		}
656
657 2
		$name_prefix = $this->get_name_prefix();
658 2
		$name = ( substr( $name, 0, strlen( $name_prefix ) ) !== $name_prefix ? $name_prefix . $name : $name );
659
660 2
		$this->name = $name;
661 2
		return $this;
662
	}
663
664
	/**
665
	 * Return the field name prefix
666
	 *
667
	 * @return string
668
	 */
669 3
	public function get_name_prefix() {
670 3
		return $this->name_prefix;
671
	}
672
673
	/**
674
	 * Set field name prefix
675
	 * Use only if you are completely aware of what you are doing.
676
	 *
677
	 * @param  string $name_prefix
678
	 * @return Field  $this
679
	 */
680 3
	public function set_name_prefix( $name_prefix ) {
681 3
		$name_prefix = strval( $name_prefix );
682 3
		$old_prefix_length = strlen( $this->name_prefix );
683 3
		$this->name_prefix = '';
684 3
		$this->set_name( substr( $this->get_name(), $old_prefix_length ) );
685
686 3
		$this->name_prefix = $name_prefix;
687 3
		$this->set_name( $this->name_prefix . $this->get_name() );
688 3
		return $this;
689
	}
690
691
	/**
692
	 * Return field label.
693
	 *
694
	 * @return string
695
	 */
696
	public function get_label() {
697
		return $this->label;
698
	}
699
700
	/**
701
	 * Set field label.
702
	 *
703
	 * @param  string $label If null, the label will be generated from the field name
704
	 * @return Field  $this
705
	 */
706 View Code Duplication
	public function set_label( $label ) {
707
		if ( is_null( $label ) ) {
708
			// Try to guess field label from it's name
709
			$label = Helper::normalize_label( $this->get_name() );
710
		}
711
712
		$this->label = $label;
713
		return $this;
714
	}
715
716
	/**
717
	 * Get a key-value array of attributes
718
	 * 
719
	 * @return array
720
	 */
721
	public function get_attributes() {
722
		return $this->attributes;
723
	}
724
725
	/**
726
	 * Get an attribute value
727
	 * 
728
	 * @param  string $name
729
	 * @return string
730
	 */
731
	public function get_attribute( $name ) {
732
		return isset( $this->attributes[ $name ] ) ? $this->attributes[ $name ] : '';
733
	}
734
735
	/**
736
	 * Set an attribute and it's value
737
	 * 
738
	 * @param  string $name
739
	 * @param  string $value
740
	 * @return Field  $this
741
	 */
742
	public function set_attribute( $name, $value = '' ) {
743
		if ( ! in_array( $name, $this->allowed_attributes ) ) {
744
			Incorrect_Syntax_Exception::raise( 'Only the following attributes are allowed: ' . implode( ', ', $this->allowed_attributes ) . '.' );
745
			return $this;
746
		}
747
		$this->attributes[ $name ] = $value;
748
		return $this;
749
	}
750
751
	/**
752
	 * Return the field help text
753
	 *
754
	 * @return object $this
755
	 */
756
	public function get_help_text() {
757
		return $this->help_text;
758
	}
759
760
	/**
761
	 * Set additional text to be displayed during field render,
762
	 * containing information and guidance for the user
763
	 *
764
	 * @return Field $this
765
	 */
766
	public function set_help_text( $help_text ) {
767
		$this->help_text = $help_text;
768
		return $this;
769
	}
770
771
	/**
772
	 * Alias for set_help_text()
773
	 *
774
	 * @see set_help_text()
775
	 * @return object $this
776
	 */
777
	public function help_text( $help_text ) {
778
		return $this->set_help_text( $help_text );
779
	}
780
781
	/**
782
	 * Return whether or not this value should be auto loaded.
783
	 *
784
	 * @return bool
785
	 */
786
	public function get_autoload() {
787
		return $this->autoload;
788
	}
789
790
	/**
791
	 * Whether or not this value should be auto loaded. Applicable to theme options only.
792
	 *
793
	 * @param  bool  $autoload
794
	 * @return Field $this
795
	 */
796
	public function set_autoload( $autoload ) {
797
		$this->autoload = $autoload;
798
		return $this;
799
	}
800
801
	/**
802
	 * Return whether or not this field should be lazyloaded.
803
	 *
804
	 * @return bool
805
	 */
806
	public function get_lazyload() {
807
		return $this->lazyload;
808
	}
809
810
	/**
811
	 * Whether or not this field will be initialized when the field is in the viewport (visible).
812
	 *
813
	 * @param  bool  $lazyload
814
	 * @return Field $this
815
	 */
816
	public function set_lazyload( $lazyload ) {
817
		$this->lazyload = $lazyload;
818
		return $this;
819
	}
820
821
	/**
822
	 * Get the field width.
823
	 *
824
	 * @return int $width
825
	 */
826
	public function get_width() {
827
		return $this->width;
828
	}
829
830
	/**
831
	 * Set the field width.
832
	 *
833
	 * @param  int   $width
834
	 * @return Field $this
835
	 */
836
	public function set_width( $width ) {
837
		$this->width = (int) $width;
838
		return $this;
839
	}
840
841
	/**
842
	 * Get custom CSS classes.
843
	 *
844
	 * @return array<string>
845
	 */
846
	public function get_classes() {
847
		return $this->classes;
848
	}
849
850
	/**
851
	 * Set CSS classes that the container should use.
852
	 *
853
	 * @param  string|array<string> $classes
854
	 * @return Field                $this
855
	 */
856
	public function set_classes( $classes ) {
857
		$this->classes = Helper::sanitize_classes( $classes );
858
		return $this;
859
	}
860
861
	/**
862
	 * Whether this field is mandatory for the user
863
	 *
864
	 * @param  bool  $required
865
	 * @return Field $this
866
	 */
867
	public function set_required( $required = true ) {
868
		$this->required = $required;
869
		return $this;
870
	}
871
872
	/**
873
	 * Return whether this field is mandatory for the user
874
	 *
875
	 * @return bool
876
	 */
877
	public function is_required() {
878
		return $this->required;
879
	}
880
881
	/**
882
	 * HTML id attribute getter.
883
	 * @return string
884
	 */
885 1
	public function get_id() {
886 1
		return $this->id;
887
	}
888
889
	/**
890
	 * HTML id attribute setter
891
	 * 
892
	 * @param  string $id
893
	 * @return Field  $this
894
	 */
895 1
	public function set_id( $id ) {
896 1
		$this->id = $id;
897 1
		return $this;
898
	}
899
900
	/**
901
	 * Set the field visibility conditional logic.
902
	 *
903
	 * @param  array
904
	 * @return Field $this
905
	 */
906 8
	public function set_conditional_logic( $rules ) {
907 8
		$this->conditional_logic = $this->parse_conditional_rules( $rules );
908 3
		return $this;
909
	}
910
911
	/**
912
	 * Get the conditional logic rules
913
	 *
914
	 * @return array
915
	 */
916 3
	public function get_conditional_logic() {
917 3
		return $this->conditional_logic;
918
	}
919
920
	/**
921
	 * Validate and parse the conditional logic rules.
922
	 *
923
	 * @param array $rules
924
	 * @return array
925
	 */
926
	protected function parse_conditional_rules( $rules ) {
927
		if ( ! is_array( $rules ) ) {
928
			Incorrect_Syntax_Exception::raise( 'Conditional logic rules argument should be an array.' );
929
			return array();
930
		}
931
932
		$allowed_operators = array( '=', '!=', '>', '>=', '<', '<=', 'IN', 'NOT IN', 'INCLUDES', 'EXCLUDES' );
933
934
		$parsed_rules = array(
935
			'relation' => Helper::get_relation_type_from_array( $rules ),
936
			'rules' => array(),
937
		);
938
939
		foreach ( $rules as $key => $rule ) {
940
			if ( $key === 'relation' ) {
941
				continue; // Skip the relation key as it is already handled above
942
			}
943
944
			// Check if the rule is valid
945
			if ( ! is_array( $rule ) || empty( $rule['field'] ) ) {
946
				Incorrect_Syntax_Exception::raise( 'Invalid conditional logic rule format. The rule should be an array with the "field" key set.' );
947
				return array();
948
			}
949
950
			// Fill in optional keys with defaults
951
			$rule = array_merge( array(
952
				'compare' => '=',
953
				'value' => '',
954
			), $rule );
955
956 View Code Duplication
			if ( ! in_array( $rule['compare'], $allowed_operators ) ) {
957
				Incorrect_Syntax_Exception::raise( 'Invalid conditional logic compare operator: <code>' . $rule['compare'] . '</code><br>Allowed operators are: <code>' .
958
				implode( ', ', $allowed_operators ) . '</code>' );
959
				return array();
960
			}
961
962
			if ( in_array( $rule['compare'], array( 'IN', 'NOT IN' ) ) && ! is_array( $rule['value'] ) ) {
963
				Incorrect_Syntax_Exception::raise( 'Invalid conditional logic value format. An array is expected, when using the "' . $rule['compare'] . '" operator.' );
964
				return array();
965
			}
966
967
			$parsed_rules['rules'][] = $rule;
968
		}
969
970
		return $parsed_rules;
971
	}
972
973
	/**
974
	 * Set the REST visibility of the field
975
	 * 
976
	 * @param  bool  $visible
977
	 * @return Field $this
978
	 */
979
	public function set_visible_in_rest_api( $visible = true ) {
980
		$this->visible_in_rest_api = $visible;
981
		return $this;
982
	}
983
	
984
	/**
985
	 * Get the REST visibility of the field
986
	 * 
987
	 * @return bool
988
	 */
989
	public function get_visible_in_rest_api() {
990
		return $this->visible_in_rest_api;
991
	}
992
993
	/**
994
	 * Returns an array that holds the field data, suitable for JSON representation.
995
	 *
996
	 * @param bool $load  Should the value be loaded from the database or use the value from the current instance.
997
	 * @return array
998
	 */
999
	public function to_json( $load ) {
1000
		if ( $load ) {
1001
			$this->load();
1002
		}
1003
1004
		$field_data = array(
1005
			'id' => $this->get_id(),
1006
			'type' => $this->get_type(),
1007
			'label' => $this->get_label(),
1008
			'name' => $this->get_name(),
1009
			'base_name' => $this->get_base_name(),
1010
			'value' => $this->get_formatted_value(),
1011
			'default_value' => $this->get_default_value(),
1012
			'attributes' => (object) $this->get_attributes(),
1013
			'help_text' => $this->get_help_text(),
1014
			'context' => $this->get_context(),
1015
			'required' => $this->is_required(),
1016
			'lazyload' => $this->get_lazyload(),
1017
			'width' => $this->get_width(),
1018
			'classes' => $this->get_classes(),
1019
			'conditional_logic' => $this->get_conditional_logic(),
1020
		);
1021
1022
		return $field_data;
1023
	}
1024
1025
	/**
1026
	 * Hook administration scripts.
1027
	 */
1028
	public static function admin_hook_scripts() {
1029
		wp_enqueue_media();
1030
		wp_enqueue_script( 'thickbox' );
1031
		wp_enqueue_script( 'media-upload' );
1032
	}
1033
1034
	/**
1035
	 * Hook administration styles.
1036
	 */
1037
	public static function admin_hook_styles() {
1038
		wp_enqueue_style( 'thickbox' );
1039
	}
1040
}
1041