Completed
Pull Request — development (#615)
by
unknown
02:22
created

Container::get_nonce_name()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
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\Container;
4
5
use Carbon_Fields\Carbon_Fields;
6
use Carbon_Fields\Helper\Helper;
7
use Carbon_Fields\Field\Field;
8
use Carbon_Fields\Field\Group_Field;
9
use Carbon_Fields\Container\Fulfillable\Fulfillable_Collection;
10
use Carbon_Fields\Datastore\Datastore_Interface;
11
use Carbon_Fields\Datastore\Datastore_Holder_Interface;
12
use Carbon_Fields\Exception\Incorrect_Syntax_Exception;
13
14
/**
15
 * Base container class.
16
 * Defines the key container methods and their default implementations.
17
 *
18
 * @mixin Block_Container|Comment_Meta_Container|Nav_Menu_Item_Container|Network_Container|Post_Meta_Container|Term_Meta_Container|Theme_Options_Container|User_Meta_Container|Widget_Container
19
 */
20
abstract class Container implements Datastore_Holder_Interface {
21
	/**
22
	 * Where to put a particular tab -- at the head or the tail. Tail by default
23
	 */
24
	const TABS_TAIL = 1;
25
	const TABS_HEAD = 2;
26
27
	/**
28
	 * Separator signifying field hierarchy relation
29
	 * Used when searching for fields in a specific complex field
30
	 */
31
	const HIERARCHY_FIELD_SEPARATOR = '/';
32
33
	/**
34
	 * Separator signifying complex_field->group relation
35
	 * Used when searching for fields in a specific complex field group
36
	 */
37
	const HIERARCHY_GROUP_SEPARATOR = ':';
38
39
	/**
40
	 * Stores if the container is active on the current page
41
	 *
42
	 * @see activate()
43
	 * @var bool
44
	 */
45
	protected $active = false;
46
47
	/**
48
	 * List of registered unique field names for this container instance
49
	 *
50
	 * @see register_field_name()
51
	 * @var array
52
	 */
53
	protected $registered_field_names = array();
54
55
	/**
56
	 * Tabs available
57
	 */
58
	protected $tabs = array();
59
60
	/**
61
	 * List of default container settings
62
	 *
63
	 * @see init()
64
	 * @var array
65
	 */
66
	public $settings = array();
67
68
	/**
69
	 * Title of the container
70
	 *
71
	 * @var string
72
	 */
73
	public $title = '';
74
75
	/**
76
	 * List of notification messages to be displayed on the front-end
77
	 *
78
	 * @var array
79
	 */
80
	protected $notifications = array();
81
82
	/**
83
	 * List of error messages to be displayed on the front-end
84
	 *
85
	 * @var array
86
	 */
87
	protected $errors = array();
88
89
	/**
90
	 * List of container fields
91
	 *
92
	 * @see add_fields()
93
	 * @var array
94
	 */
95
	protected $fields = array();
96
97
	/**
98
	 * Array of custom CSS classes.
99
	 *
100
	 * @see set_classes()
101
	 * @see get_classes()
102
	 * @var array<string>
103
	 */
104
	protected $classes = array();
105
106
	/**
107
	 * Container datastores. Propagated to all container fields
108
	 *
109
	 * @see set_datastore()
110
	 * @see get_datastore()
111
	 * @var object
112
	 */
113
	protected $datastore;
114
115
	/**
116
	 * Flag whether the datastore is the default one or replaced with a custom one
117
	 *
118
	 * @see set_datastore()
119
	 * @see get_datastore()
120
	 * @var boolean
121
	 */
122
	protected $has_default_datastore = true;
123
124
	/**
125
	 * Fulfillable_Collection to use when checking attachment/saving conditions
126
	 *
127
	 * @var Fulfillable_Collection
128
	 */
129
	protected $condition_collection;
130
131
	/**
132
	 * Translator to use when translating conditions to json
133
	 *
134
	 * @var Carbon_Fields\Container\Fulfillable\Translator\Translator
135
	 */
136
	protected $condition_translator;
137
138
	/**
139
	 * Create a new container of type $type and name $name.
140
	 *
141
	 * @param  string    $raw_type
142
	 * @param  string    $id        Unique id for the container. Optional
143
	 * @param  string    $name      Human-readable name of the container
144
	 * @return Container $container
145
	 */
146 8
	public static function factory( $raw_type, $id, $name = '' ) {
147
		// no name provided - switch input around as the id is optionally generated based on the name
148 8
		if ( $name === '' ) {
149 8
			$name = $id;
150 8
			$id = '';
151 8
		}
152
153 8
		$type = Helper::normalize_type( $raw_type );
154 8
		$repository = Carbon_Fields::resolve( 'container_repository' );
155 8
		$id = $repository->get_unique_container_id( ( $id !== '' ) ? $id : $name );
156
157 8
		if ( ! Helper::is_valid_entity_id( $id ) ) {
158
			Incorrect_Syntax_Exception::raise( 'Container IDs can only contain lowercase alphanumeric characters, dashes and underscores ("' . $id . '" passed).' );
159
			return null;
160
		}
161
162 8
		if ( ! $repository->is_unique_container_id( $id ) ) {
163
			Incorrect_Syntax_Exception::raise( 'The passed container id is already taken ("' . $id . '" passed).' );
164
			return null;
165
		}
166
167 8
		$container = null;
168 8
		if ( Carbon_Fields::has( $type, 'containers' ) ) {
169
			$container = Carbon_Fields::resolve_with_arguments( $type, array(
170
				'id' => $id,
171
				'name' => $name,
172
				'type' => $type,
173
			), 'containers' );
174
		} else {
175
			// Fallback to class name-based resolution
176 8
			$class = Helper::type_to_class( $type, __NAMESPACE__, '_Container' );
177
178 8
			if ( ! class_exists( $class ) ) {
179 3
				Incorrect_Syntax_Exception::raise( 'Unknown container "' . $raw_type . '".' );
180 1
				$class = __NAMESPACE__ . '\\Broken_Container';
181 1
			}
182
183 6
			$fulfillable_collection = Carbon_Fields::resolve( 'container_condition_fulfillable_collection' );
184 6
			$condition_translator = Carbon_Fields::resolve( 'container_condition_translator_json' );
185 6
			$container = new $class( $id, $name, $type, $fulfillable_collection, $condition_translator );
186
		}
187
188 6
		$repository->register_container( $container );
189
190 6
		return $container;
191
	}
192
193
	/**
194
	 * An alias of factory().
195
	 *
196
	 * @see    Container::factory()
197
	 * @return Container
198
	 */
199
	public static function make() {
200
		return call_user_func_array( array( get_class(), 'factory' ), func_get_args() );
201
	}
202
203
	/**
204
	 * Create a new container
205
	 *
206
	 * @param string                 $id                   Unique id of the container
207
	 * @param string                 $title                Title of the container
208
	 * @param string                 $type                 Type of the container
209
	 * @param Fulfillable_Collection $condition_collection
210
	 * @param Carbon_Fields\Container\Fulfillable\Translator\Translator $condition_translator
211
	 */
212 2
	public function __construct( $id, $title, $type, $condition_collection, $condition_translator ) {
213 2
		Carbon_Fields::verify_boot();
214
215 2
		if ( empty( $title ) ) {
216 1
			Incorrect_Syntax_Exception::raise( 'Empty container title is not supported' );
217
		}
218
219 1
		$this->id = $id;
220 1
		$this->title = $title;
221 1
		$this->type = $type;
222 1
		$this->condition_collection = $condition_collection;
223 1
		$this->condition_collection->set_condition_type_list(
224 1
			array_merge( $this->get_condition_types( true ), $this->get_condition_types( false ) ),
225
			true
226 1
		);
227 1
		$this->condition_translator = $condition_translator;
228 1
	}
229
230
	/**
231
	 * Return the container id
232
	 *
233
	 * @return string
234
	 */
235
	public function get_id() {
236
		return $this->id;
237
	}
238
239
	/**
240
	 * Get array of all static condition types
241
	 *
242
	 * @param  boolean       $static
243
	 * @return array<string>
244
	 */
245
	protected function get_condition_types( $static ) {
246
		$group = $static ? 'static' : 'dynamic';
247
		$container_type = Helper::class_to_type( get_class( $this ), '_Container' );
248
249
		$condition_types = array();
250
		$condition_types = apply_filters( 'carbon_fields_' . $container_type . '_container_' . $group . '_condition_types', $condition_types, $container_type, $this );
251
		$condition_types = apply_filters( 'carbon_fields_container_' . $group . '_condition_types', $condition_types, $container_type, $this );
252
253
		return $condition_types;
254
	}
255
256
	/**
257
	 * Return whether the container is active
258
	 */
259
	public function is_active() {
260
		return $this->active;
261
	}
262
263
	/**
264
	 * Activate the container and trigger an action
265
	 */
266
	protected function activate() {
267
		$this->active = true;
268
		$this->boot();
269
		do_action( 'carbon_fields_container_activated', $this );
270
271
		$fields = $this->get_fields();
272
		foreach ( $fields as $field ) {
273
			$field->activate();
274
		}
275
	}
276
277
	/**
278
	 * Perform instance initialization
279
	 */
280
	abstract public function init();
281
282
	/**
283
	 * Boot the container once it's attached.
284
	 */
285
	protected function boot() {
286
287
	}
288
289
	/**
290
	 * Load the value for each field in the container.
291
	 * Could be used internally during container rendering
292
	 */
293
	public function load() {
294
		foreach ( $this->fields as $field ) {
295
			$field->load();
296
		}
297
	}
298
299
	/**
300
	 * Called first as part of the container save procedure.
301
	 * Responsible for checking the request validity and
302
	 * calling the container-specific save() method
303
	 *
304
	 * @see save()
305
	 * @see is_valid_save()
306
	 */
307
	public function _save() {
308
		$param = func_get_args();
309
		if ( call_user_func_array( array( $this, '_is_valid_save' ), $param ) ) {
310
			call_user_func_array( array( $this, 'save' ), $param );
311
		}
312
	}
313
314
	/**
315
	 * Load submitted data and save each field in the container
316
	 *
317
	 * @see is_valid_save()
318
	 */
319
	public function save( $data = null ) {
320
		foreach ( $this->fields as $field ) {
321
			$field->set_value_from_input( Helper::input() );
322
			$field->save();
323
		}
324
	}
325
326
	/**
327
	 * Checks whether the current save request is valid
328
	 *
329
	 * @return bool
330
	 */
331
	final protected function _is_valid_save() {
332
		$params = func_get_args();
333
		$is_valid_save = call_user_func_array( array( $this, 'is_valid_save' ), $params );
334
		return apply_filters( 'carbon_fields_container_is_valid_save', $is_valid_save, $this );
335
	}
336
337
	/**
338
	 * Checks whether the current save request is valid
339
	 *
340
	 * @return bool
341
	 */
342
	abstract protected function is_valid_save();
343
344
	/**
345
	 * Called first as part of the container attachment procedure.
346
	 * Responsible for checking it's OK to attach the container
347
	 * and if it is, calling the container-specific attach() method
348
	 *
349
	 * @see attach()
350
	 * @see is_valid_attach()
351
	 */
352
	public function _attach() {
353
		if ( ! $this->is_valid_attach() ) {
354
			return;
355
		}
356
357
		$param = func_get_args();
358
		call_user_func_array( array( $this, 'attach' ), $param );
359
360
		// Allow containers to initialize but not activate (useful in cases such as theme options)
361
		if ( $this->should_activate() ) {
362
			$this->activate();
363
		}
364
	}
365
366
	/**
367
	 * Attach the container rendering and helping methods
368
	 * to concrete WordPress Action hooks
369
	 */
370
	public function attach() {}
371
372
	/**
373
	 * Perform checks whether the container should be attached during the current request
374
	 *
375
	 * @return bool True if the container is allowed to be attached
376
	 */
377
	final public function is_valid_attach() {
378
		$is_valid_attach = $this->is_valid_attach_for_request();
379
		return apply_filters( 'carbon_fields_container_is_valid_attach', $is_valid_attach, $this );
380
	}
381
382
	/**
383
	 * Get environment array for page request (in admin)
384
	 *
385
	 * @return array
386
	 */
387
	abstract protected function get_environment_for_request();
388
389
	/**
390
	 * Check container attachment rules against current page request (in admin)
391
	 *
392
	 * @return bool
393
	 */
394
	abstract protected function is_valid_attach_for_request();
395
396
	/**
397
	 * Check if conditions pass for request
398
	 *
399
	 * @return bool
400
	 */
401
	protected function static_conditions_pass() {
402
		$environment = $this->get_environment_for_request();
403
		$static_condition_collection = $this->condition_collection->evaluate(
404
			$this->get_condition_types( false ),
405
			true
406
		);
407
		return $static_condition_collection->is_fulfilled( $environment );
408
	}
409
410
	/**
411
	 * Get environment array for object id
412
	 *
413
	 * @param integer $object_id
414
	 * @return array
415
	 */
416
	abstract protected function get_environment_for_object( $object_id );
417
418
	/**
419
	 * Check container attachment rules against object id
420
	 *
421
	 * @param int $object_id
422
	 * @return bool
423
	 */
424
	abstract public function is_valid_attach_for_object( $object_id );
425
426
	/**
427
	 * Check if all conditions pass for object
428
	 *
429
	 * @param  integer $object_id
430
	 * @return bool
431
	 */
432
	protected function all_conditions_pass( $object_id ) {
433
		$environment = $this->get_environment_for_object( $object_id );
434
		return $this->condition_collection->is_fulfilled( $environment );
435
	}
436
437
	/**
438
	 * Whether this container is currently viewed.
439
	 */
440
	public function should_activate() {
441
		return true;
442
	}
443
444
	/**
445
	 * Perform a check whether the current container has fields
446
	 *
447
	 * @return bool
448
	 */
449
	public function has_fields() {
450
		return (bool) $this->fields;
451
	}
452
453
	/**
454
	 * Returns the private container array of fields.
455
	 * Use only if you are completely aware of what you are doing.
456
	 *
457
	 * @return array
458
	 */
459
	public function get_fields() {
460
		return $this->fields;
461
	}
462
463
	/**
464
	 * Return root field from container with specified name
465
	 *
466
	 * @example crb_complex
467
	 *
468
	 * @param string $field_name
469
	 * @return Field
470
	 */
471
	public function get_root_field_by_name( $field_name ) {
472
		$fields = $this->get_fields();
473
		foreach ( $fields as $field ) {
474
			if ( $field->get_base_name() === $field_name ) {
475
				return $field;
476
			}
477
		}
478
		return null;
479
	}
480
481
	/**
482
	 * Get a regex to match field name patterns used to fetch specific fields
483
	 *
484
	 * @return string
485
	 */
486
	protected function get_field_pattern_regex() {
487
		$field_name_characters = Helper::get_field_name_characters_pattern();
488
489
		// matches:
490
		// field_name
491
		// field_name[0]
492
		// field_name[0]:group_name
493
		// field_name:group_name
494
		$regex = '/
495
			\A
496
			(?P<field_name>[' . $field_name_characters . ']+)
497
			(?:\[(?P<group_index>\d+)\])?
498
			(?:' .  preg_quote( static::HIERARCHY_GROUP_SEPARATOR, '/' ). '(?P<group_name>[' . $field_name_characters . ']+))?
499
			\z
500
		/x';
501
		return $regex;
502
	}
503
504
	/**
505
	 * Return field from container with specified name
506
	 *
507
	 * @example $field_name = 'crb_complex/text_field'
508
	 * @example $field_name = 'crb_complex/complex_2'
509
	 * @example $field_name = 'crb_complex/complex_2:text_group/text_field'
510
	 * @example $field_name = 'crb_complex[3]/complex_2[1]:text_group/text_field'
511
	 *
512
	 * @param string $field_name
513
	 * @return Field
514
	 */
515
	public function get_field_by_name( $field_name ) {
516
		$hierarchy = array_filter( explode( static::HIERARCHY_FIELD_SEPARATOR, $field_name ) );
517
		$field = null;
518
519
		$field_group = $this->get_fields();
520
		$hierarchy_left = $hierarchy;
521
		$field_pattern_regex = $this->get_field_pattern_regex();
522
		$hierarchy_index = array();
523
524
		while ( ! empty( $hierarchy_left ) ) {
525
			$segment = array_shift( $hierarchy_left );
526
			$segment_pieces = array();
527
			if ( ! preg_match( $field_pattern_regex, $segment, $segment_pieces ) ) {
528
				return null;
529
			}
530
531
			$segment_field_name = $segment_pieces['field_name'];
532
			$segment_group_index = isset( $segment_pieces['group_index'] ) ? $segment_pieces['group_index'] : 0;
533
			$segment_group_name = isset( $segment_pieces['group_name'] ) ? $segment_pieces['group_name'] : Group_Field::DEFAULT_GROUP_NAME;
534
535
			foreach ( $field_group as $f ) {
536
				if ( $f->get_base_name() !== $segment_field_name ) {
537
					continue;
538
				}
539
540
				if ( empty( $hierarchy_left ) ) {
541
					$field = clone $f;
542
					$field->set_hierarchy_index( $hierarchy_index );
543
				} else {
544
					if ( ! is_a( $f, 'Carbon_Fields\\Field\\Complex_Field' ) ) {
545
						return null;
546
					}
547
548
					$group = $f->get_group_by_name( $segment_group_name );
549
					if ( ! $group ) {
550
						return null;
551
					}
552
					$field_group = $group->get_fields();
553
					$hierarchy_index[] = $segment_group_index;
554
				}
555
				break;
556
			}
557
		}
558
559
		return $field;
560
	}
561
562
	/**
563
	 * Perform checks whether there is a field registered with the name $name.
564
	 * If not, the field name is recorded.
565
	 *
566
	 * @param string $name
567
	 * @return boolean
568
	 */
569 View Code Duplication
	protected function register_field_name( $name ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
570
		if ( in_array( $name, $this->registered_field_names ) ) {
571
			Incorrect_Syntax_Exception::raise( 'Field name "' . $name . '" already registered' );
572
			return false;
573
		}
574
575
		$this->registered_field_names[] = $name;
576
		return true;
577
	}
578
579
	/**
580
	 * Return whether the datastore instance is the default one or has been overriden
581
	 *
582
	 * @return boolean
583
	 */
584 6
	public function has_default_datastore() {
585 6
		return $this->has_default_datastore;
586
	}
587
588
	/**
589
	 * Set datastore instance
590
	 *
591
	 * @param Datastore_Interface $datastore
592
	 * @return Container $this
593
	 */
594 6 View Code Duplication
	public function set_datastore( Datastore_Interface $datastore, $set_as_default = false ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
595 6
		if ( $set_as_default && ! $this->has_default_datastore() ) {
596 1
			return $this; // datastore has been overriden with a custom one - abort changing to a default one
597
		}
598 6
		$this->datastore = $datastore;
599 6
		$this->has_default_datastore = $set_as_default;
600
601 6
		foreach ( $this->fields as $field ) {
602
			$field->set_datastore( $this->get_datastore(), true );
603 6
		}
604 6
		return $this;
605
	}
606
607
	/**
608
	 * Get the DataStore instance
609
	 *
610
	 * @return Datastore_Interface $datastore
611
	 */
612 6
	public function get_datastore() {
613 6
		return $this->datastore;
614
	}
615
616
	/**
617
	 * Return WordPress nonce name used to identify the current container instance
618
	 *
619
	 * @return string
620
	 */
621
	protected function get_nonce_name() {
622
		return $this->get_id() . '_nonce';
623
	}
624
625
	/**
626
	 * Return WordPress nonce name used to identify the current container instance
627
	 *
628
	 * @return string
629
	 */
630
	protected function get_nonce_value() {
631
		return wp_create_nonce( $this->get_nonce_name() );
632
	}
633
634
	/**
635
	 * Check if the nonce is present in the request and that it is verified
636
	 *
637
	 * @return bool
638
	 */
639
	protected function verified_nonce_in_request() {
640
		$input = Helper::input();
641
		$nonce_name = $this->get_nonce_name();
642
		$nonce_value = isset( $input[ $nonce_name ] ) ? $input[ $nonce_name ] : '';
643
		return wp_verify_nonce( $nonce_value, $nonce_name );
644
	}
645
646
	/**
647
	 * Internal function that creates the tab and associates it with particular field set
648
	 *
649
	 * @param string $tab_name
650
	 * @param array $fields
651
	 * @param int $queue_end
652
	 * @return object $this
653
	 */
654
	private function create_tab( $tab_name, $fields, $queue_end = self::TABS_TAIL ) {
655
		if ( isset( $this->tabs[ $tab_name ] ) ) {
656
			Incorrect_Syntax_Exception::raise( "Tab name duplication for $tab_name" );
657
		}
658
659
		if ( $queue_end === static::TABS_TAIL ) {
660
			$this->tabs[ $tab_name ] = array();
661
		} else if ( $queue_end === static::TABS_HEAD ) {
662
			$this->tabs = array_merge(
663
				array( $tab_name => array() ),
664
				$this->tabs
665
			);
666
		}
667
668
		foreach ( $fields as $field ) {
669
			$field_name = $field->get_name();
670
			$this->tabs[ $tab_name ][ $field_name ] = $field;
671
		}
672
673
		$this->settings['tabs'] = $this->get_tabs_json();
674
	}
675
676
	/**
677
	 * Whether the container is tabbed or not
678
	 *
679
	 * @return bool
680
	 */
681
	public function is_tabbed() {
682
		return (bool) $this->tabs;
683
	}
684
685
	/**
686
	 * Retrieve all fields that are not defined under a specific tab
687
	 *
688
	 * @return array
689
	 */
690
	protected function get_untabbed_fields() {
691
		$tabbed_fields_names = array();
692
		foreach ( $this->tabs as $tab_fields ) {
693
			$tabbed_fields_names = array_merge( $tabbed_fields_names, array_keys( $tab_fields ) );
694
		}
695
696
		$untabbed_fields = array_filter( $this->fields, function( $field ) use ( $tabbed_fields_names ) {
697
			return ! in_array( $field->get_name(), $tabbed_fields_names );
698
		} );
699
700
		return $untabbed_fields;
701
	}
702
703
	/**
704
	 * Retrieve all tabs.
705
	 * Create a default tab if there are any untabbed fields.
706
	 *
707
	 * @return array
708
	 */
709
	protected function get_tabs() {
710
		$untabbed_fields = $this->get_untabbed_fields();
711
712
		if ( ! empty( $untabbed_fields ) ) {
713
			$this->create_tab( __( 'General', 'carbon-fields' ), $untabbed_fields, static::TABS_HEAD );
714
		}
715
716
		return $this->tabs;
717
	}
718
719
	/**
720
	 * Build the tabs JSON
721
	 *
722
	 * @return array
723
	 */
724
	protected function get_tabs_json() {
725
		$tabs_json = array();
726
		$tabs = $this->get_tabs();
727
728
		foreach ( $tabs as $tab_name => $fields ) {
729
			foreach ( $fields as $field_name => $field ) {
730
				$tabs_json[ $tab_name ][] = $field_name;
731
			}
732
		}
733
734
		return $tabs_json;
735
	}
736
737
	/**
738
	 * Get custom CSS classes.
739
	 *
740
	 * @return array<string>
741
	 */
742
	public function get_classes() {
743
		return $this->classes;
744
	}
745
746
	/**
747
	 * Set CSS classes that the container should use.
748
	 *
749
	 * @param string|array<string> $classes
750
	 * @return Container $this
751
	 */
752
	public function set_classes( $classes ) {
753
		$this->classes = Helper::sanitize_classes( $classes );
754
		return $this;
755
	}
756
757
	/**
758
	 * Returns an array that holds the container data, suitable for JSON representation.
759
	 *
760
	 * @param bool $load  Should the value be loaded from the database or use the value from the current instance.
761
	 * @return array
762
	 */
763
	public function to_json( $load ) {
764
		$conditions = $this->condition_collection->evaluate( $this->get_condition_types( true ), $this->get_environment_for_request(), array( 'CUSTOM' ) );
765
		$conditions = $this->condition_translator->fulfillable_to_foreign( $conditions );
766
767
		$container_data = array(
768
			'id' => $this->get_id(),
769
			'type' => $this->type,
770
			'title' => $this->title,
771
			'classes' => $this->get_classes(),
772
			'settings' => $this->settings,
773
			'conditions' => $conditions,
774
			'fields' => array(),
775
			'nonce' => array(
776
				'name' => $this->get_nonce_name(),
777
				'value' => $this->get_nonce_value(),
778
			),
779
		);
780
781
		$fields = $this->get_fields();
782
		foreach ( $fields as $field ) {
783
			$field_data = $field->to_json( $load );
784
			$container_data['fields'][] = $field_data;
785
		}
786
787
		return $container_data;
788
	}
789
790
	/**
791
	 * COMMON USAGE METHODS
792
	 */
793
794
	/**
795
	 * Append array of fields to the current fields set. All items of the array
796
	 * must be instances of Field and their names should be unique for all
797
	 * Carbon containers.
798
	 * If a field does not have DataStore already, the container datastore is
799
	 * assigned to them instead.
800
	 *
801
	 * @param array $fields
802
	 * @return Container $this
803
	 */
804
	public function add_fields( $fields ) {
805
		foreach ( $fields as $field ) {
806
			if ( ! is_a( $field, 'Carbon_Fields\\Field\\Field' ) ) {
807
				Incorrect_Syntax_Exception::raise( 'Object must be of type Carbon_Fields\\Field\\Field' );
808
				return $this;
809
			}
810
811
			$unique = $this->register_field_name( $field->get_name() );
812
			if ( ! $unique ) {
813
				return $this;
814
			}
815
816
			$field->set_context( $this->type );
817
			if ( ! $field->get_datastore() ) {
818
				$field->set_datastore( $this->get_datastore(), $this->has_default_datastore() );
819
			}
820
		}
821
822
		$this->fields = array_merge( $this->fields, $fields );
823
824
		return $this;
825
	}
826
827
	/**
828
	 * Configuration function for adding tab with fields
829
	 *
830
	 * @param string $tab_name
831
	 * @param array $fields
832
	 * @return Container $this
833
	 */
834
	public function add_tab( $tab_name, $fields ) {
835
		$this->add_fields( $fields );
836
		$this->create_tab( $tab_name, $fields );
837
		return $this;
838
	}
839
840
	/**
841
	 * Proxy function to set attachment conditions
842
	 *
843
	 * @see    Fulfillable_Collection::where()
844
	 * @return Container $this
845
	 */
846
	public function where() {
847
		call_user_func_array( array( $this->condition_collection, 'where' ), func_get_args() );
848
		return $this;
849
	}
850
851
	/**
852
	 * Proxy function to set attachment conditions
853
	 *
854
	 * @see    Fulfillable_Collection::or_where()
855
	 * @return Container $this
856
	 */
857
	public function or_where() {
858
		call_user_func_array( array( $this->condition_collection, 'or_where' ), func_get_args() );
859
		return $this;
860
	}
861
}
862