Completed
Push — milestone/2_0/react-ui ( b00e8d...52106c )
by
unknown
02:51
created

Field::admin_init()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 1
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

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