Completed
Push — milestone/2_0/container-condit... ( d923a5...7e2a86 )
by
unknown
04:07
created

Container::_is_valid_save()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

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