Completed
Push — milestone/2_0/react-ui ( 6ff089...f420b0 )
by
unknown
05:40
created

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