Completed
Push — milestone/2.0 ( 8ed296...84f65d )
by
unknown
03:34
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\App;
6
use Carbon_Fields\Datastore\Datastore_Interface;
7
use Carbon_Fields\Datastore\Datastore_Holder_Interface;
8
use Carbon_Fields\Value_Set\Value_Set;
9
use Carbon_Fields\Helper\Helper;
10
use Carbon_Fields\Exception\Incorrect_Syntax_Exception;
11
12
/**
13
 * Base field class.
14
 * Defines the key container methods and their default implementations.
15
 * Implements factory design pattern.
16
 **/
17
class Field implements Datastore_Holder_Interface {
18
	/**
19
	 * Stores all the field Backbone templates
20
	 *
21
	 * @see factory()
22
	 * @see add_template()
23
	 * @var array
24
	 */
25
	protected $templates = 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
	 * The width of the field.
156
	 *
157
	 * @see set_width()
158
	 * @var int
159
	 **/
160
	protected $width = 0;
161
162
	/**
163
	 * Custom CSS classes.
164
	 *
165
	 * @see add_class()
166
	 * @var array
167
	 **/
168
	protected $classes = array();
169
170
	/**
171
	 * Whether or not this field is required.
172
	 *
173
	 * @see set_required()
174
	 * @var bool
175
	 **/
176
	protected $required = false;
177
178
	/**
179
	 * Stores the field conditional logic rules.
180
	 *
181
	 * @var array
182
	 **/
183
	protected $conditional_logic = array();
184
185
	/**
186
	 * Whether the field should be included in the response of the requests to the REST API
187
	 *
188
	 * @see  set_visible_in_rest_api
189
	 * @see  get_visible_in_rest_api
190
	 * @var boolean
191
	 */
192
	protected $visible_in_rest_api = false;
193
194
	/**
195
	 * Clone the Value_Set object as well
196
	 *
197
	 * @var array
198
	 **/
199
	public function __clone() {
200
		$this->set_value_set( clone $this->get_value_set() );
201
	}
202
203
	/**
204
	 * Create a new field of type $type and name $name and label $label.
205
	 *
206
	 * @param string $type
207
	 * @param string $name lower case and underscore-delimited
208
	 * @param string $label (optional) Automatically generated from $name if not present
209
	 * @return Field
210
	 **/
211 14
	public static function factory( $type, $name, $label = null ) {
212 14
		$type = str_replace( ' ', '_', ucwords( str_replace( '_', ' ', $type ) ) );
213
214 14
		$class = __NAMESPACE__ . '\\' . $type . '_Field';
215
216 14 View Code Duplication
		if ( ! class_exists( $class ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

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