Completed
Pull Request — master (#1930)
by Aristeides
08:20 queued 04:16
created

Kirki_Control_Repeater::row_label()   C

Complexity

Conditions 12
Paths 9

Size

Total Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 12
nc 9
nop 1
dl 0
loc 21
rs 6.9666
c 0
b 0
f 0

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * Customizer Control: repeater.
4
 *
5
 * @package     Kirki
6
 * @subpackage  Controls
7
 * @copyright   Copyright (c) 2017, Aristeides Stathopoulos
8
 * @license     http://opensource.org/licenses/https://opensource.org/licenses/MIT
9
 * @since       2.0
10
 */
11
12
// Exit if accessed directly.
13
if ( ! defined( 'ABSPATH' ) ) {
14
	exit;
15
}
16
17
/**
18
 * Repeater control
19
 */
20
class Kirki_Control_Repeater extends Kirki_Control_Base {
21
22
	/**
23
	 * The control type.
24
	 *
25
	 * @access public
26
	 * @var string
27
	 */
28
	public $type = 'repeater';
29
30
	/**
31
	 * The fields that each container row will contain.
32
	 *
33
	 * @access public
34
	 * @var array
35
	 */
36
	public $fields = array();
37
38
	/**
39
	 * Will store a filtered version of value for advenced fields (like images).
40
	 *
41
	 * @access protected
42
	 * @var array
43
	 */
44
	protected $filtered_value = array();
45
46
	/**
47
	 * The row label
48
	 *
49
	 * @access public
50
	 * @var array
51
	 */
52
	public $row_label = array();
53
54
	/**
55
	 * The button label
56
	 *
57
	 * @access public
58
	 * @var string
59
	 */
60
	public $button_label = '';
61
62
	/**
63
	 * Constructor.
64
	 * Supplied `$args` override class property defaults.
65
	 * If `$args['settings']` is not defined, use the $id as the setting ID.
66
	 *
67
	 * @param WP_Customize_Manager $manager Customizer bootstrap instance.
68
	 * @param string               $id      Control ID.
69
	 * @param array                $args    {@see WP_Customize_Control::__construct}.
70
	 */
71
	public function __construct( $manager, $id, $args = array() ) {
72
73
		parent::__construct( $manager, $id, $args );
74
75
		// Set up defaults for row labels.
76
		$this->row_label = array(
77
			'type'  => 'text',
78
			'value' => esc_attr__( 'row', 'kirki' ),
79
			'field' => false,
80
		);
81
82
		// Validate row-labels.
83
		$this->row_label( $args );
84
85
		if ( empty( $this->button_label ) ) {
86
			/* translators: %s represents the label of the row. */
87
			$this->button_label = sprintf( esc_attr__( 'Add new %s', 'kirki' ), $this->row_label['value'] );
88
		}
89
90
		if ( empty( $args['fields'] ) || ! is_array( $args['fields'] ) ) {
91
			$args['fields'] = array();
92
		}
93
94
		// An array to store keys of fields that need to be filtered.
95
		$media_fields_to_filter = array();
96
97
		foreach ( $args['fields'] as $key => $value ) {
98
			if ( ! isset( $value['default'] ) ) {
99
				$args['fields'][ $key ]['default'] = '';
100
			}
101
			if ( ! isset( $value['label'] ) ) {
102
				$args['fields'][ $key ]['label'] = '';
103
			}
104
			$args['fields'][ $key ]['id'] = $key;
105
106
			// We check if the filed is an uploaded media ( image , file, video, etc.. ).
107
			if ( isset( $value['type'] ) ) {
108
				switch ( $value['type'] ) {
109
					case 'image':
110
					case 'cropped_image':
111
					case 'upload':
112
						// We add it to the list of fields that need some extra filtering/processing.
113
						$media_fields_to_filter[ $key ] = true;
114
						break;
115
116
					case 'dropdown-pages':
117
						// If the field is a dropdown-pages field then add it to args.
118
						$dropdown = wp_dropdown_pages(
119
							array(
120
								'name'              => '',
121
								'echo'              => 0,
122
								'show_option_none'  => esc_attr__( 'Select a Page', 'kirki' ),
123
								'option_none_value' => '0',
124
								'selected'          => '',
125
							)
126
						);
127
						// Hackily add in the data link parameter.
128
						$dropdown = str_replace( '<select', '<select data-field="' . esc_attr( $args['fields'][ $key ]['id'] ) . '"' . $this->get_link(), $dropdown ); // phpcs:ignore Generic.Formatting.MultipleStatementAlignment.NotSameWarning
129
						$args['fields'][ $key ]['dropdown'] = $dropdown;
130
						break;
131
				}
132
			}
133
		} // End foreach().
134
135
		$this->fields = $args['fields'];
136
137
		// Now we are going to filter the fields.
138
		// First we create a copy of the value that would be used otherwise.
139
		$this->filtered_value = $this->value();
140
141
		if ( is_array( $this->filtered_value ) && ! empty( $this->filtered_value ) ) {
142
143
			// We iterate over the list of fields.
144
			foreach ( $this->filtered_value as &$filtered_value_field ) {
145
146
				if ( is_array( $filtered_value_field ) && ! empty( $filtered_value_field ) ) {
147
148
					// We iterate over the list of properties for this field.
149
					foreach ( $filtered_value_field as $key => &$value ) {
150
151
						// We check if this field was marked as requiring extra filtering (in this case image, cropped_images, upload).
152
						if ( array_key_exists( $key, $media_fields_to_filter ) ) {
153
154
							// What follows was made this way to preserve backward compatibility.
155
							// The repeater control use to store the URL for images instead of the attachment ID.
156
							// We check if the value look like an ID (otherwise it's probably a URL so don't filter it).
157
							if ( is_numeric( $value ) ) {
158
159
								// "sanitize" the value.
160
								$attachment_id = (int) $value;
161
162
								// Try to get the attachment_url.
163
								$url = wp_get_attachment_url( $attachment_id );
164
165
								$filename = basename( get_attached_file( $attachment_id ) );
166
167
								// If we got a URL.
168
								if ( $url ) {
169
170
									// 'id' is needed for form hidden value, URL is needed to display the image.
171
									$value = array(
172
										'id'       => $attachment_id,
173
										'url'      => $url,
174
										'filename' => $filename,
175
									);
176
								}
177
							}
178
						}
179
					}
180
				}
181
			} // End foreach().
182
		} // End if().
183
	}
184
185
	/**
186
	 * Refresh the parameters passed to the JavaScript via JSON.
187
	 *
188
	 * @access public
189
	 */
190
	public function to_json() {
191
		parent::to_json();
192
193
		$fields = $this->fields;
194
195
		$this->json['fields']    = $fields;
196
		$this->json['row_label'] = $this->row_label;
197
198
		// If filtered_value has been set and is not empty we use it instead of the actual value.
199
		if ( is_array( $this->filtered_value ) && ! empty( $this->filtered_value ) ) {
200
			$this->json['value'] = $this->filtered_value;
201
		}
202
		$this->json['value'] = apply_filters( "kirki_controls_repeater_value_{$this->id}", $this->json['value'] );
203
	}
204
205
	/**
206
	 * Render the control's content.
207
	 * Allows the content to be overriden without having to rewrite the wrapper in $this->render().
208
	 *
209
	 * @access protected
210
	 */
211
	protected function render_content() {
212
		?>
213
		<label>
214
			<?php if ( ! empty( $this->label ) ) : ?>
215
				<span class="customize-control-title"><?php echo esc_html( $this->label ); ?></span>
216
			<?php endif; ?>
217
			<?php if ( ! empty( $this->description ) ) : ?>
218
				<span class="description customize-control-description"><?php echo wp_kses_post( $this->description ); ?></span>
219
			<?php endif; ?>
220
			<input type="hidden" {{{ data.inputAttrs }}} value="" <?php echo wp_kses_post( $this->get_link() ); ?> />
221
		</label>
222
223
		<ul class="repeater-fields"></ul>
224
225
		<?php if ( isset( $this->choices['limit'] ) ) : ?>
226
			<?php // @codingStandardsIgnoreLine ?>
227
			<?php /* translators: %s represents the number of rows we're limiting the repeater to allow. */ ?>
228
			<p class="limit"><?php printf( esc_attr__( 'Limit: %s rows', 'kirki' ), esc_html( $this->choices['limit'] ) ); ?></p>
229
		<?php endif; ?>
230
		<button class="button-secondary repeater-add"><?php echo esc_html( $this->button_label ); ?></button>
231
232
		<?php
233
234
		$this->repeater_js_template();
235
236
	}
237
238
	/**
239
	 * An Underscore (JS) template for this control's content (but not its container).
240
	 * Class variables for this control class are available in the `data` JS object.
241
	 *
242
	 * @access public
243
	 */
244
	public function repeater_js_template() {
245
		?>
246
		<script type="text/html" class="customize-control-repeater-content">
247
			<# var field; var index = data.index; #>
248
249
			<li class="repeater-row minimized" data-row="{{{ index }}}">
250
251
				<div class="repeater-row-header">
252
					<span class="repeater-row-label"></span>
253
					<i class="dashicons dashicons-arrow-down repeater-minimize"></i>
254
				</div>
255
				<div class="repeater-row-content">
256
					<# _.each( data, function( field, i ) { #>
257
258
						<div class="repeater-field repeater-field-{{{ field.type }}} repeater-field-{{ field.id }}">
259
260
							<# if ( 'text' === field.type || 'url' === field.type || 'link' === field.type || 'email' === field.type || 'tel' === field.type || 'date' === field.type || 'number' === field.type ) { #>
261
								<# var fieldExtras = ''; #>
262
								<# if ( 'link' === field.type ) { #>
263
									<# field.type = 'url' #>
264
								<# } #>
265
266
								<# if ( 'number' === field.type ) { #>
267
									<# if ( ! _.isUndefined( field.choices ) && ! _.isUndefined( field.choices.min ) ) { #>
268
										<# fieldExtras += ' min="' + field.choices.min + '"'; #>
269
									<# } #>
270
									<# if ( ! _.isUndefined( field.choices ) && ! _.isUndefined( field.choices.max ) ) { #>
271
										<# fieldExtras += ' max="' + field.choices.max + '"'; #>
272
									<# } #>
273
									<# if ( ! _.isUndefined( field.choices ) && ! _.isUndefined( field.choices.step ) ) { #>
274
										<# fieldExtras += ' step="' + field.choices.step + '"'; #>
275
									<# } #>
276
								<# } #>
277
278
								<label>
279
									<# if ( field.label ) { #><span class="customize-control-title">{{{ field.label }}}</span><# } #>
280
									<# if ( field.description ) { #><span class="description customize-control-description">{{{ field.description }}}</span><# } #>
281
									<input type="{{field.type}}" name="" value="{{{ field.default }}}" data-field="{{{ field.id }}}"{{ fieldExtras }}>
282
								</label>
283
284
							<# } else if ( 'number' === field.type ) { #>
285
286
								<label>
287
									<# if ( field.label ) { #><span class="customize-control-title">{{{ field.label }}}</span><# } #>
288
									<# if ( field.description ) { #><span class="description customize-control-description">{{{ field.description }}}</span><# } #>
289
									<input type="{{ field.type }}" name="" value="{{{ field.default }}}" data-field="{{{ field.id }}}"{{ numberFieldExtras }}>
290
								</label>
291
292
							<# } else if ( 'hidden' === field.type ) { #>
293
294
								<input type="hidden" data-field="{{{ field.id }}}" <# if ( field.default ) { #> value="{{{ field.default }}}" <# } #> />
295
296
							<# } else if ( 'checkbox' === field.type ) { #>
297
298
								<label>
299
									<input type="checkbox" value="{{{ field.default }}}" data-field="{{{ field.id }}}" <# if ( field.default ) { #> checked="checked" <# } #> /> {{{ field.label }}}
300
									<# if ( field.description ) { #>{{{ field.description }}}<# } #>
301
								</label>
302
303
							<# } else if ( 'select' === field.type ) { #>
304
305
								<label>
306
									<# if ( field.label ) { #><span class="customize-control-title">{{{ field.label }}}</span><# } #>
307
									<# if ( field.description ) { #><span class="description customize-control-description">{{{ field.description }}}</span><# } #>
308
									<select data-field="{{{ field.id }}}"<# if ( ! _.isUndefined( field.multiple ) && false !== field.multiple ) { #> multiple="multiple" data-multiple="{{ field.multiple }}"<# } #>>
309
										<# _.each( field.choices, function( choice, i ) { #>
310
											<option value="{{{ i }}}" <# if ( -1 !== jQuery.inArray( i, field.default ) || field.default == i ) { #> selected="selected" <# } #>>{{ choice }}</option>
311
										<# }); #>
312
									</select>
313
								</label>
314
315
							<# } else if ( 'dropdown-pages' === field.type ) { #>
316
317
								<label>
318
									<# if ( field.label ) { #><span class="customize-control-title">{{{ data.label }}}</span><# } #>
319
									<# if ( field.description ) { #><span class="description customize-control-description">{{{ field.description }}}</span><# } #>
320
									<div class="customize-control-content repeater-dropdown-pages">{{{ field.dropdown }}}</div>
321
								</label>
322
323
							<# } else if ( 'radio' === field.type ) { #>
324
325
								<label>
326
									<# if ( field.label ) { #><span class="customize-control-title">{{{ field.label }}}</span><# } #>
327
									<# if ( field.description ) { #><span class="description customize-control-description">{{{ field.description }}}</span><# } #>
328
329
									<# _.each( field.choices, function( choice, i ) { #>
330
										<label><input type="radio" name="{{{ field.id }}}{{ index }}" data-field="{{{ field.id }}}" value="{{{ i }}}" <# if ( field.default == i ) { #> checked="checked" <# } #>> {{ choice }} <br/></label>
331
									<# }); #>
332
								</label>
333
334
							<# } else if ( 'radio-image' === field.type ) { #>
335
336
								<label>
337
									<# if ( field.label ) { #><span class="customize-control-title">{{{ field.label }}}</span><# } #>
338
									<# if ( field.description ) { #><span class="description customize-control-description">{{{ field.description }}}</span><# } #>
339
340
									<# _.each( field.choices, function( choice, i ) { #>
341
										<input type="radio" id="{{{ field.id }}}_{{ index }}_{{{ i }}}" name="{{{ field.id }}}{{ index }}" data-field="{{{ field.id }}}" value="{{{ i }}}" <# if ( field.default == i ) { #> checked="checked" <# } #>>
342
											<label for="{{{ field.id }}}_{{ index }}_{{{ i }}}"><img src="{{ choice }}"></label>
343
										</input>
344
									<# }); #>
345
								</label>
346
347
							<# } else if ( 'color' === field.type ) { #>
348
349
								<label>
350
									<# if ( field.label ) { #><span class="customize-control-title">{{{ field.label }}}</span><# } #>
351
									<# if ( field.description ) { #><span class="description customize-control-description">{{{ field.description }}}</span><# } #>
352
								</label>
353
								<# var defaultValue = '';
354
								if ( field.default ) {
355
									if ( -1 === field.default.indexOf( 'rgba' ) ) {
356
										defaultValue = ( '#' !== field.default.substring( 0, 1 ) ) ? '#' + field.default : field.default;
357
										defaultValue = ' data-default-color=' + defaultValue; // Quotes added automatically.
358
									} else {
359
										defaultValue = ' data-default-color="' + defaultValue + '" data-alpha="true"';
360
									}
361
								} #>
362
								<input class="color-picker-hex" type="text" maxlength="7" placeholder="<?php esc_attr_e( 'Hex Value', 'kirki' ); ?>"  value="{{{ field.default }}}" data-field="{{{ field.id }}}" {{ defaultValue }} />
363
364
							<# } else if ( 'textarea' === field.type ) { #>
365
366
								<# if ( field.label ) { #><span class="customize-control-title">{{{ field.label }}}</span><# } #>
367
								<# if ( field.description ) { #><span class="description customize-control-description">{{{ field.description }}}</span><# } #>
368
								<textarea rows="5" data-field="{{{ field.id }}}">{{ field.default }}</textarea>
369
370
							<# } else if ( field.type === 'image' || field.type === 'cropped_image' ) { #>
371
372
								<label>
373
									<# if ( field.label ) { #><span class="customize-control-title">{{{ field.label }}}</span><# } #>
374
									<# if ( field.description ) { #><span class="description customize-control-description">{{{ field.description }}}</span><# } #>
375
								</label>
376
377
								<figure class="kirki-image-attachment" data-placeholder="<?php esc_attr_e( 'No Image Selected', 'kirki' ); ?>" >
378
									<# if ( field.default ) { #>
379
										<# var defaultImageURL = ( field.default.url ) ? field.default.url : field.default; #>
380
										<img src="{{{ defaultImageURL }}}">
381
									<# } else { #>
382
										<?php esc_attr_e( 'No Image Selected', 'kirki' ); ?>
383
									<# } #>
384
								</figure>
385
386
								<div class="actions">
387
									<button type="button" class="button remove-button<# if ( ! field.default ) { #> hidden<# } #>"><?php esc_attr_e( 'Remove', 'kirki' ); ?></button>
388
									<button type="button" class="button upload-button" data-label=" <?php esc_attr_e( 'Add Image', 'kirki' ); ?>" data-alt-label="<?php echo esc_attr_e( 'Change Image', 'kirki' ); ?>" >
389
										<# if ( field.default ) { #>
390
											<?php esc_attr_e( 'Change Image', 'kirki' ); ?>
391
										<# } else { #>
392
											<?php esc_attr_e( 'Add Image', 'kirki' ); ?>
393
										<# } #>
394
									</button>
395
									<# if ( field.default.id ) { #>
396
										<input type="hidden" class="hidden-field" value="{{{ field.default.id }}}" data-field="{{{ field.id }}}" >
397
									<# } else { #>
398
										<input type="hidden" class="hidden-field" value="{{{ field.default }}}" data-field="{{{ field.id }}}" >
399
									<# } #>
400
								</div>
401
402
							<# } else if ( field.type === 'upload' ) { #>
403
404
								<label>
405
									<# if ( field.label ) { #><span class="customize-control-title">{{{ field.label }}}</span><# } #>
406
									<# if ( field.description ) { #><span class="description customize-control-description">{{{ field.description }}}</span><# } #>
407
								</label>
408
409
								<figure class="kirki-file-attachment" data-placeholder="<?php esc_attr_e( 'No File Selected', 'kirki' ); ?>" >
410
									<# if ( field.default ) { #>
411
										<# var defaultFilename = ( field.default.filename ) ? field.default.filename : field.default; #>
412
										<span class="file"><span class="dashicons dashicons-media-default"></span> {{ defaultFilename }}</span>
413
									<# } else { #>
414
										<?php esc_attr_e( 'No File Selected', 'kirki' ); ?>
415
									<# } #>
416
								</figure>
417
418
								<div class="actions">
419
									<button type="button" class="button remove-button<# if ( ! field.default ) { #> hidden<# } #>"></button>
420
									<button type="button" class="button upload-button" data-label="<?php esc_attr_e( 'Add File', 'kirki' ); ?>" data-alt-label="<?php esc_attr_e( 'Change File', 'kirki' ); ?>">
421
										<# if ( field.default ) { #>
422
											<?php esc_attr_e( 'Change File', 'kirki' ); ?>
423
										<# } else { #>
424
											<?php esc_attr_e( 'Add File', 'kirki' ); ?>
425
										<# } #>
426
									</button>
427
									<# if ( field.default.id ) { #>
428
										<input type="hidden" class="hidden-field" value="{{{ field.default.id }}}" data-field="{{{ field.id }}}" >
429
									<# } else { #>
430
										<input type="hidden" class="hidden-field" value="{{{ field.default }}}" data-field="{{{ field.id }}}" >
431
									<# } #>
432
								</div>
433
434
							<# } else if ( 'custom' === field.type ) { #>
435
436
								<# if ( field.label ) { #><span class="customize-control-title">{{{ field.label }}}</span><# } #>
437
								<# if ( field.description ) { #><span class="description customize-control-description">{{{ field.description }}}</span><# } #>
438
								<div data-field="{{{ field.id }}}">{{{ field.default }}}</div>
439
440
							<# } #>
441
442
						</div>
443
					<# }); #>
444
					<button type="button" class="button-link repeater-row-remove"><?php esc_attr_e( 'Remove', 'kirki' ); ?></button>
445
				</div>
446
			</li>
447
		</script>
448
		<?php
449
	}
450
451
	/**
452
	 * Validate row-labels.
453
	 *
454
	 * @access protected
455
	 * @since 3.0.0
456
	 * @param array $args {@see WP_Customize_Control::__construct}.
457
	 */
458
	protected function row_label( $args ) {
459
460
		// Validating args for row labels.
461
		if ( isset( $args['row_label'] ) && is_array( $args['row_label'] ) && ! empty( $args['row_label'] ) ) {
462
463
			// Validating row label type.
464
			if ( isset( $args['row_label']['type'] ) && ( 'text' === $args['row_label']['type'] || 'field' === $args['row_label']['type'] ) ) {
465
				$this->row_label['type'] = $args['row_label']['type'];
466
			}
467
468
			// Validating row label type.
469
			if ( isset( $args['row_label']['value'] ) && ! empty( $args['row_label']['value'] ) ) {
470
				$this->row_label['value'] = esc_attr( $args['row_label']['value'] );
471
			}
472
473
			// Validating row label field.
474
			if ( isset( $args['row_label']['field'] ) && ! empty( $args['row_label']['field'] ) && isset( $args['fields'][ esc_attr( $args['row_label']['field'] ) ] ) ) {
475
				$this->row_label['field'] = esc_attr( $args['row_label']['field'] );
476
			} else {
477
				// If from field is not set correctly, making sure standard is set as the type.
478
				$this->row_label['type'] = 'text';
479
			}
480
		}
481
	}
482
}
483