Completed
Push — milestone/2_0/react-ui ( 73b2ee...16415b )
by
unknown
27:57 queued 10:59
created

Container::and_when()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

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