Completed
Pull Request — master (#124)
by Asier
02:03
created

Complex_Field::collapse()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 3
c 1
b 0
f 0
nc 1
nop 1
dl 0
loc 5
rs 9.4285
1
<?php
2
3
namespace Carbon_Fields\Field;
4
5
use Carbon_Fields\Datastore\Datastore_Interface;
6
use Carbon_Fields\Helper\Helper;
7
use Carbon_Fields\Field\Field;
8
use Carbon_Fields\Field\Group_Field;
9
use Carbon_Fields\Exception\Incorrect_Syntax_Exception;
10
11
/**
12
 * Complex field class.
13
 * Allows nested repeaters with multiple field groups to be created.
14
 */
15
class Complex_Field extends Field {
16
	const LAYOUT_GRID = 'grid'; // default
17
	const LAYOUT_LIST = 'list'; // deprecated
18
	const LAYOUT_TABBED = 'tabbed'; // deprecated
19
	const LAYOUT_TABBED_HORIZONTAL = 'tabbed-horizontal';
20
	const LAYOUT_TABBED_VERTICAL = 'tabbed-vertical';
21
22
	protected $fields = array();
23
	protected $values = array();
24
	protected $groups = array();
25
26
	protected $layout = self::LAYOUT_GRID;
27
	protected $values_min = -1;
28
	protected $values_max = -1;
29
	protected $collapsed = false;
30
31
	public $labels = array(
32
		'singular_name' => 'Entry',
33
		'plural_name' => 'Entries',
34
	);
35
36
	/**
37
	 * Initialization tasks.
38
	 */
39
	public function init() {
40
		$this->labels = array(
41
			'singular_name' => __( 'Entry', 'carbon-fields' ),
42
			'plural_name' => __( 'Entries', 'carbon-fields' ),
43
		);
44
45
		// Include the complex group Underscore templates
46
		$this->add_template( 'Complex-Group', array( $this, 'template_group' ) );
47
		$this->add_template( 'Complex-Group-Tab-Item', array( $this, 'template_group_tab_item' ) );
48
49
		parent::init();
50
	}
51
52
	/**
53
	 * Add a set/group of fields.
54
	 *
55
	 * @return $this
56
	 */
57
	public function add_fields() {
58
		$argv = func_get_args();
59
		$argc = count( $argv );
60
61
		if ( $argc == 1 ) {
62
			$fields = $argv[0];
63
			$name = '';
64
			$label = null;
65
		} else if ( $argc == 2 ) {
66 View Code Duplication
			if ( is_array( $argv[0] ) ) {
1 ignored issue
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...
67
				list( $fields, $name ) = $argv;
68
			} else {
69
				list( $name, $fields ) = $argv;
70
			}
71
			$label = null;
72
		} else if ( $argc == 3 ) {
73 View Code Duplication
			if ( is_array( $argv[0] ) ) {
1 ignored issue
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...
74
				list( $fields, $name, $label ) = $argv;
75
			} else {
76
				list( $name, $label, $fields ) = $argv;
77
			}
78
		}
79
80
		if ( array_key_exists( '_' . $name, $this->groups ) ) {
81
			Incorrect_Syntax_Exception::raise( 'Group with name "' . $name . '" in Complex Field "' . $this->get_label() . '" already exists.' );
0 ignored issues
show
Bug introduced by
The variable $name does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
82
		}
83
84
		$group = new Group_Field($name, $label, $fields);
0 ignored issues
show
Bug introduced by
The variable $label does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
Bug introduced by
The variable $fields does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
Coding Style introduced by
Expected 1 spaces after opening bracket; 0 found
Loading history...
Coding Style introduced by
Expected 1 spaces before closing bracket; 0 found
Loading history...
85
86
		$this->groups[ $group->get_name() ] = $group;
87
88
		return $this;
89
	}
90
91
	/**
92
	 * Set the group label Underscore template.
93
	 *
94
	 * @param  string|callable $template
95
	 * @return $this
96
	 */
97
	public function set_header_template( $template ) {
98
		if ( count($this->groups) === 0 ) {
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after opening bracket; 0 found
Loading history...
Coding Style introduced by
Expected 1 spaces before closing bracket; 0 found
Loading history...
99
			Incorrect_Syntax_Exception::raise( "Can't set group label template. There are no present groups for Complex Field " . $this->get_label() . "." );
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal . does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
100
		}
101
102
		$template = is_callable( $template ) ? call_user_func( $template ) : $template;
103
104
		// Assign the template to the group that was added last
105
		$values = array_values( $this->groups );
106
		$group = end( $values );
107
		$group->set_label_template( $template );
108
109
		// Include the group label Underscore template
110
		$this->add_template( $group->get_group_id(), array( $group, 'template_label' ) );
111
112
		$this->groups[ $group->get_name() ] = $group;
113
114
		return $this;
115
	}
116
117
	/**
118
	 * Retrieve all groups of fields.
119
	 *
120
	 * @return array $fields
121
	 */
122
	public function get_fields() {
123
		$fields = array();
124
125
		foreach ( $this->groups as $group ) {
126
			$group_fields = $group->get_fields();
127
128
			$fields = array_merge( $fields, $group_fields );
129
		}
130
131
		return $fields;
132
	}
133
134
	/**
135
	 * Set the field labels.
136
	 * Currently supported values:
137
	 *  - singular_name - the singular entry label
138
	 *  - plural_name - the plural entries label
139
	 *
140
	 * @param  array $labels Labels
141
	 */
142
	public function setup_labels( $labels ) {
143
		$this->labels = array_merge( $this->labels, $labels );
144
		return $this;
145
	}
146
147
	/**
148
	 * Set the datastore of this field.
149
	 *
150
	 * @param Datastore_Interface $store
151
	 */
152
	public function set_datastore( Datastore_Interface $store ) {
153
		$this->store = $store;
154
155
		foreach ( $this->groups as $group ) {
156
			$group->set_datastore( $this->store );
157
		}
158
	}
159
160
	/**
161
	 * Load the field value from an input array based on it's name.
162
	 *
163
	 * @param array $input (optional) Array of field names and values. Defaults to $_POST
164
	 **/
165
	public function set_value_from_input( $input = null ) {
166
		$this->values = array();
167
168
		if ( is_null( $input ) ) {
169
			$input = $_POST;
0 ignored issues
show
introduced by
Detected access of super global var $_POST, probably need manual inspection.
Loading history...
introduced by
Detected usage of a non-sanitized input variable: $_POST
Loading history...
170
		}
171
172
		if ( ! isset( $input[ $this->get_name() ] ) ) {
173
			return;
174
		}
175
176
		$input_groups = $input[ $this->get_name() ];
177
		$index = 0;
178
0 ignored issues
show
Coding Style introduced by
Functions must not contain multiple empty lines in a row; found 2 empty lines
Loading history...
179
180
		foreach ( $input_groups as $values ) {
181
			$value_group = array();
182
			if ( ! isset( $values['group'] ) || ! isset( $this->groups[ $values['group'] ] ) ) {
183
				continue;
184
			}
185
186
			$group = $this->groups[ $values['group'] ];
187
			unset( $values['group'] );
188
189
			$group_fields = $group->get_fields();
190
191
			// trim input values to those used by the field
192
			$group_field_names = array_flip( $group->get_field_names() );
193
			$values = array_intersect_key( $values, $group_field_names );
194
195
			foreach ( $group_fields as $field ) {
196
				// set value from the group
197
				$tmp_field = clone $field;
198
				if ( is_a( $tmp_field, __NAMESPACE__ . '\\Complex_Field' ) ) {
199
					if ( ! isset( $values[ $tmp_field->get_name() ] ) ) {
200
						continue; // bail if the complex field is empty
201
					}
202
203
					$new_name = $this->get_name() . $group->get_name() . '-' . $field->get_name() . '_' . $index;
204
					$new_values = array( $new_name => $values[ $tmp_field->get_name() ] );
205
206
					$tmp_field->set_name( $new_name );
207
					$tmp_field->set_value_from_input( $new_values );
208
				} else {
209
					$tmp_field->set_value_from_input( $values );
210
				}
211
212
				// update name to group name
213
				$tmp_field->set_name( $this->get_name() . $group->get_name() . '-' . $field->get_name() . '_' . $index );
214
				$value_group[] = $tmp_field;
215
			}
216
217
			$this->values[] = $value_group;
218
			$index++;
219
		}
220
	}
221
222
	/**
223
	 * Load all groups of fields and their data.
224
	 */
225
	public function load() {
226
		// load existing groups
227
		$this->load_values();
228
	}
229
230
	/**
231
	 * Save all contained groups of fields.
232
	 */
233
	public function save() {
234
		$this->delete();
235
236
		foreach ( $this->values as $value ) {
237
			foreach ( $value as $field ) {
238
				$field->save();
239
			}
240
		}
241
	}
242
243
	/**
244
	 * Delete the values of all contained fields.
245
	 */
246
	public function delete() {
247
		return $this->store->delete_values( $this );
248
	}
249
250
	/**
251
	 * Load and parse the field data.
252
	 */
253
	public function load_values() {
254
		return $this->load_values_from_db();
255
	}
256
257
	/**
258
	 * Load and parse the field data from the database.
259
	 */
260
	public function load_values_from_db() {
261
		$this->values = array();
262
263
		$group_rows = $this->store->load_values( $this );
264
265
		return $this->process_loaded_values( $group_rows );
266
	}
267
268
	/**
269
	 * Load and parse a raw set of field data.
270
	 *
271
	 * @param  array $values Raw data entries
272
	 * @return array 		 Processed data entries
273
	 */
274
	public function load_values_from_array( $values ) {
275
		$this->values = array();
276
277
		$group_rows = array();
278
279
		$meta_key = $this->get_name();
280
281
		foreach ( $values as $key => $value ) {
282
			if ( strpos( $key, $meta_key ) !== 0 ) {
283
				continue;
284
			}
285
286
			$group_rows[] = array(
287
				'field_key' => preg_replace( '~^(' . preg_quote( $this->name, '~' ) . ')_\d+_~', '$1_', $key ),
288
				'field_value' => $value,
289
			);
290
		}
291
292
		return $this->process_loaded_values( $group_rows );
293
	}
294
295
	/**
296
	 * Parse groups of raw field data into the actual field hierarchy.
297
	 *
298
	 * @param  array $group_rows Group rows
299
	 */
300
	public function process_loaded_values( $group_rows ) {
301
		$input_groups = array();
302
303
		// Set default values
304
		$field_names = array();
305
		foreach ( $this->groups as $group ) {
306
			$group_fields = $group->get_fields();
307
			foreach ( $group_fields as $field ) {
308
				$field_names[] = $field->get_name();
309
				$field->set_value( $field->get_default_value() );
310
			}
311
		}
312
313
		if ( empty( $group_rows ) ) {
314
			return;
315
		}
316
317
		// load and parse values and group type
318
		foreach ( $group_rows as $row ) {
319
			if ( ! preg_match( Helper::get_complex_field_regex( $this->name, array_keys( $this->groups ), $field_names ), $row['field_key'], $field_name ) ) {
320
				continue;
321
			}
322
323
			$row['field_value'] = maybe_unserialize( $row['field_value'] );
324
			$input_groups[ $field_name['index'] ]['type'] = $field_name['group'];
325
326
			if ( ! empty( $field_name['trailing'] ) ) {
327
				$input_groups[ $field_name['index'] ][ $field_name['key'] . '_' . $field_name['sub'] . '-' . $field_name['trailing'] ] = $row['field_value'];
328 View Code Duplication
			} else if ( ! empty( $field_name['sub'] ) ) {
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...
329
				$input_groups[ $field_name['index'] ][ $field_name['key'] ][ $field_name['sub'] ] = $row['field_value'];
330
			} else {
331
				$input_groups[ $field_name['index'] ][ $field_name['key'] ] = $row['field_value'];
332
			}
333
		}
334
335
		// create groups list with loaded fields
336
		ksort( $input_groups );
337
338
		foreach ( $input_groups as $index => $values ) {
339
			$value_group = array( 'type' => $values['type'] );
340
			$group_fields = $this->groups[ $values['type'] ]->get_fields();
341
			unset( $values['type'] );
342
343
			foreach ( $group_fields as $field ) {
344
				// set value from the group
345
				$tmp_field = clone $field;
346
347
				if ( is_a( $field, __NAMESPACE__ . '\\Complex_Field' ) ) {
348
					$tmp_field->load_values_from_array( $values );
349
				} else {
350
					$tmp_field->set_value_from_input( $values );
351
				}
352
353
				$value_group[] = $tmp_field;
354
			}
355
356
			$this->values[] = $value_group;
357
		}
358
	}
359
360
	/**
361
	 * Retrieve the field values.
362
	 * @return array
363
	 */
364
	public function get_values() {
365
		return $this->values;
366
	}
367
368
	/**
369
	 * Generate and set the field prefix.
370
	 * @param string $prefix
371
	 */
372
	public function set_prefix( $prefix ) {
373
		parent::set_prefix( $prefix );
374
375
		foreach ( $this->groups as $group ) {
376
			$group->set_prefix( $prefix );
377
		}
378
	}
379
380
	/**
381
	 * Returns an array that holds the field data, suitable for JSON representation.
382
	 * This data will be available in the Underscore template and the Backbone Model.
383
	 *
384
	 * @param bool $load  Should the value be loaded from the database or use the value from the current instance.
385
	 * @return array
386
	 */
387
	public function to_json( $load ) {
388
		$complex_data = parent::to_json( $load );
389
390
		$groups_data = array();
391
		$values_data = array();
392
393
		foreach ( $this->groups as $group ) {
394
			$groups_data[] = $group->to_json( false );
395
		}
396
397
		foreach ( $this->values as $fields ) {
398
			$group = $this->get_group_by_name( $fields['type'] );
399
			unset( $fields['type'] );
400
401
			$data = array(
402
				'name' => $group->get_name(),
403
				'label' => $group->get_label(),
404
				'group_id' => $group->get_group_id(),
405
				'fields' => array(),
406
			);
407
408
			foreach ( $fields as $index => $field ) {
409
				$data['fields'][] = $field->to_json( false );
410
			}
411
412
			$values_data[] = $data;
413
		}
414
415
		$complex_data = array_merge( $complex_data, array(
416
			'layout' => $this->layout,
417
			'labels' => $this->labels,
418
			'min' => $this->get_min(),
419
			'max' => $this->get_max(),
420
			'multiple_groups' => count( $groups_data ) > 1,
421
			'groups' => $groups_data,
422
			'value' => $values_data,
423
			'collapsed' => $this->collapsed,
424
		) );
425
426
		return $complex_data;
427
	}
428
429
	/**
430
	 * The main Underscore template.
431
	 */
432
	public function template() {
433
		?>
434
		<div class="carbon-subcontainer carbon-grid {{ multiple_groups ? 'multiple-groups' : '' }}">
435
			<div class="carbon-empty-row carbon-empty-row-visible">
436
				{{{ crbl10n.complex_no_rows.replace('%s', labels.plural_name) }}}
437
			</div>
438
439
			<div class="groups-wrapper layout-{{ layout }}">
440
				<# if (layout === '<?php echo self::LAYOUT_TABBED_HORIZONTAL ?>' || layout === '<?php echo self::LAYOUT_TABBED_VERTICAL ?>' ) { #>
0 ignored issues
show
introduced by
Expected next thing to be a escaping function, not 'self'
Loading history...
441
					<div class="group-tabs-nav-holder">
442
						<ul class="group-tabs-nav"></ul>
443
444
						<div class="carbon-actions">
445
							<div class="carbon-button">
446
								<a href="#" class="button" data-group="{{{ multiple_groups ? '' : groups[0].name }}}">
447
									+
448
								</a>
449
450
								<# if (multiple_groups) { #>
451
									<ul>
452
										<# _.each(groups, function(group) { #>
453
											<li><a href="#" data-group="{{{ group.name }}}">{{{ group.label }}}</a></li>
454
										<# }); #>
455
									</ul>
456
								<# } #>
457
							</div>
458
						</div>
459
					</div><!-- /.group-tabs-nav-holder -->
460
				<# } #>
461
462
				<div class="carbon-groups-holder"></div>
463
				<div class="clear"></div>
464
			</div>
465
466
			<div class="carbon-actions">
467
				<div class="carbon-button">
468
					<a href="#" class="button" data-group="{{{ multiple_groups ? '' : groups[0].name }}}">
469
						{{{ crbl10n.complex_add_button.replace('%s', labels.singular_name) }}}
470
						{{{ multiple_groups ? '&#8681;' : '' }}}
471
					</a>
472
473
					<# if (multiple_groups) { #>
474
						<ul>
475
							<# _.each(groups, function(group) { #>
476
								<li><a href="#" data-group="{{{ group.name }}}">{{{ group.label }}}</a></li>
477
							<# }); #>
478
						</ul>
479
					<# } #>
480
				</div>
481
			</div>
482
		</div>
483
		<?php
484
	}
485
486
	/**
487
	 * Collapse the complex field entries.
488
	 */
489
	public function collapse( $state = true ) {
490
		$this->collapsed = $state;
491
492
		return $this;
493
	}
494
495
	/**
496
	 * The Underscore template for the complex field group.
497
	 */
498
	public function template_group() {
499
		?>
500
		<div id="carbon-{{{ complex_name }}}-complex-container" class="carbon-row carbon-group-row" data-group-id="{{ id }}">
501
			<input type="hidden" name="{{{ complex_name + '[' + index + ']' }}}[group]" value="{{ name }}" />
502
503
			<div class="carbon-drag-handle">
504
				<span class="group-number">{{{ order + 1 }}}</span><span class="group-name">{{{ label_template || label }}}</span>
505
			</div>
506
507
			<div class="carbon-group-actions carbon-group-actions-{{ layout }}">
508
				<a class="carbon-btn-duplicate" href="#" title="<?php esc_attr_e( 'Clone', 'carbon_fields' ); ?>">
509
					<?php _e( 'Clone', 'carbon_fields' ); ?>
510
				</a>
511
512
				<a class="carbon-btn-remove" href="#" title="<?php esc_attr_e( 'Remove', 'carbon_fields' ); ?>">
513
					<?php _e( 'Remove', 'carbon_fields' ); ?>
514
				</a>
515
516
				<a class="carbon-btn-collapse" href="#" title="<?php esc_attr_e( 'Collapse/Expand', 'carbon_fields' ); ?>">
517
					<?php _e( 'Collapse/Expand', 'carbon_fields' ); ?>
518
				</a>
519
			</div>
520
521
			<div class="fields-container">
522
				<# _.each(fields, function(field) { #>
523
					<div class="carbon-row carbon-subrow subrow-{{{ field.type }}} {{{ field.classes.join(' ') }}}">
524
						<label for="{{{ complex_id + '-' + field.id + '-' + index }}}">
525
							{{ field.label }}
526
527
							<# if (field.required) { #>
528
								 <span class="carbon-required">*</span>
529
							<# } #>
530
						</label>
531
532
						<div class="field-holder {{{ complex_id + '-' + field.id + '-' + index }}}"></div>
533
534
						<# if (field.help_text) { #>
535
							<em class="help-text">
536
								{{{ field.help_text }}}
537
							</em>
538
						<# } #>
539
540
						<em class="carbon-error"></em>
541
					</div>
542
				<# }) #>
543
			</div>
544
		</div>
545
		<?php
546
	}
547
548
	 /**
549
	 * The Underscore template for the group item tab.
550
	 */
551
	public function template_group_tab_item() {
552
		?>
553
		<li class="group-tab-item" data-group-id="{{ id }}">
554
			<a href="#">
555
				<span class="group-handle"></span>
556
557
				<# if (label_template || label) { #>
558
					<span class="group-name">{{{ label_template || label }}}</span>
559
				<# } #>
560
				<span class="group-number">{{{ order + 1 }}}</span>
561
				<span class="dashicons dashicons-warning carbon-complex-group-error-badge" ></span>
562
			</a>
563
		</li>
564
		<?php
565
	}
566
567
	/**
568
	 * Modify the layout of this field.
569
	 *
570
	 * @param string $layout
571
	 */
572
	public function set_layout( $layout ) {
573
		$available_layouts = array(
574
			self::LAYOUT_GRID,
575
			self::LAYOUT_TABBED_HORIZONTAL,
576
			self::LAYOUT_TABBED_VERTICAL,
577
			self::LAYOUT_LIST,
578
		);
579
580
		if ( $layout === self::LAYOUT_TABBED ) {
581
			// The library used to provide just one kind of tabs -- horizontal ones. Later vertical tabs were added.
582
			// So the "tabbed" name was renamed to "tabbed-horizontal" and "tabbed-vertical" layout was introduced.
583
			_doing_it_wrong( __METHOD__, sprintf( __( 'Complex field "%1$s" layout is deprecated, please use "%2$s" or "%3$s" instead.', 'carbon_fields' ), self::LAYOUT_TABBED, self::LAYOUT_TABBED_HORIZONTAL, self::LAYOUT_TABBED_VERTICAL ), null );
584
585
			$layout = self::LAYOUT_TABBED_HORIZONTAL;
586
		}
587
588 View Code Duplication
		if ( ! in_array( $layout,  $available_layouts ) ) {
1 ignored issue
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...
589
			$error_message = 'Incorrect layout ``' . $layout . '" specified. ' .
590
				'Available layouts: ' . implode( ', ', $available_layouts );
591
592
			Incorrect_Syntax_Exception::raise( $error_message );
593
		}
594
595
		if ( $layout === self::LAYOUT_LIST ) {
596
			_doing_it_wrong( __METHOD__, __( 'Complex field <code>' . self::LAYOUT_LIST . '</code> layout is deprecated, please use <code>set_width()</code> instead.', 'carbon_fields' ), null );
0 ignored issues
show
introduced by
Expected next thing to be a escaping function, not 'self'
Loading history...
597
		}
598
599
		$this->layout = $layout;
600
601
		return $this;
602
	}
603
604
	/**
605
	 * Set the minimum number of entries.
606
	 *
607
	 * @param int $min
608
	 */
609
	public function set_min( $min ) {
610
		$this->values_min = intval( $min );
611
		return $this;
612
	}
613
614
	/**
615
	 * Get the minimum number of entries.
616
	 *
617
	 * @return int $min
618
	 */
619
	public function get_min() {
620
		return $this->values_min;
621
	}
622
623
	/**
624
	 * Set the maximum number of entries.
625
	 *
626
	 * @param int $max
627
	 */
628
	public function set_max( $max ) {
629
		$this->values_max = intval( $max );
630
		return $this;
631
	}
632
633
	/**
634
	 * Get the maximum number of entries.
635
	 *
636
	 * @return int $max
637
	 */
638
	public function get_max() {
639
		return $this->values_max;
640
	}
641
642
	/**
643
	 * Retrieve the groups of this field.
644
	 *
645
	 * @return array
646
	 */
647
	public function get_group_names() {
648
		return array_keys( $this->groups );
649
	}
650
651
	/**
652
	 * Retrieve a group by its name.
653
	 *
654
	 * @param  string $group_name        Group name
655
	 * @return Group_Field $group_object Group object
656
	 */
657
	public function get_group_by_name( $group_name ) {
658
		$group_object = null;
659
660
		foreach ( $this->groups as $group ) {
661
			if ( $group->get_name() == $group_name ) {
662
				$group_object = $group;
663
			}
664
		}
665
666
		return $group_object;
667
	}
668
}
669