Completed
Push — issues/1038 ( 1df044...f8946f )
by Ravinder
19:18
created

Give_Fields_API::set_default_values()   D

Complexity

Conditions 8
Paths 128

Size

Total Lines 31
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 8
eloc 17
nc 128
nop 2
dl 0
loc 31
rs 4.6666
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * Fields API
5
 *
6
 * @package     Give
7
 * @subpackage  Classes/Give_Fields_API
8
 * @copyright   Copyright (c) 2016, WordImpress
9
 * @license     https://opensource.org/licenses/gpl-license GNU Public License
10
 * @since       1.9
11
 */
12
class Give_Fields_API {
0 ignored issues
show
Coding Style introduced by
Since you have declared the constructor as private, maybe you should also declare the class as final.
Loading history...
13
	/**
14
	 * Instance.
15
	 *
16
	 * @since  1.9
17
	 * @access private
18
	 * @var Give_Fields_API
19
	 */
20
	static private $instance;
21
22
	/**
23
	 * The defaults for all elements
24
	 *
25
	 * @since  1.9
26
	 * @access static
27
	 */
28
	static $field_defaults = array(
0 ignored issues
show
Coding Style introduced by
The visibility should be declared for property $field_defaults.

The PSR-2 coding standard requires that all properties in a class have their visibility explicitly declared. If you declare a property using

class A {
    var $property;
}

the property is implicitly global.

To learn more about the PSR-2, please see the PHP-FIG site on the PSR-2.

Loading history...
29
		'type'                 => '',
30
		'name'                 => '',
31
		'data_type'            => '',
32
		'value'                => '',
33
		'required'             => false,
34
		'options'              => array(),
35
36
		// Set default value to field.
37
		'default'              => '',
38
39
		// Add label, before and after field.
40
		'label'                => '',
41
		'label_position'       => 'before',
42
43
		// Add description to field as tooltip.
44
		'tooltip'              => '',
45
46
		// Show multiple fields in same row with in sub section.
47
		'sub_section_start'    => false,
48
		'sub_section_end'      => false,
49
50
		// Add custom attributes.
51
		'field_attributes'     => array(),
52
		'wrapper_attributes'   => array(),
53
54
		// Show/Hide field in before/after modal view.
55
		'show_without_modal'   => false,
56
		'show_within_modal'    => true,
57
58
		// Params to edit field html.
59
		'before_field'         => '',
60
		'after_field'          => '',
61
		'before_field_wrapper' => '',
62
		'after_field_wrapper'  => '',
63
		'before_label'         => '',
64
		'after_label'          => '',
65
66
		// Manually render field.
67
		'callback'             => '',
68
69
	);
70
71
	/**
72
	 * The defaults for all elements
73
	 *
74
	 * @since  1.9
75
	 * @access static
76
	 */
77
	static $section_defaults = array(
0 ignored issues
show
Coding Style introduced by
The visibility should be declared for property $section_defaults.

The PSR-2 coding standard requires that all properties in a class have their visibility explicitly declared. If you declare a property using

class A {
    var $property;
}

the property is implicitly global.

To learn more about the PSR-2, please see the PHP-FIG site on the PSR-2.

Loading history...
78
		'label'            => '',
79
		'name'             => '',
80
		'field_attributes' => array(),
81
82
		// Manually render section.
83
		'callback'         => '',
84
	);
85
86
87
	private function __construct() {
88
	}
89
90
91
	/**
92
	 * Get instance.
93
	 *
94
	 * @return static
95
	 */
96
	public static function get_instance() {
97
		if ( is_null( static::$instance ) ) {
0 ignored issues
show
Bug introduced by
Since $instance is declared private, accessing it with static will lead to errors in possible sub-classes; consider using self, or increasing the visibility of $instance to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return static::$someVariable;
    }
}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass { }

YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class SomeClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return self::$someVariable; // self works fine with private.
    }
}
Loading history...
98
			self::$instance = new static();
99
		}
100
101
		return self::$instance;
102
	}
103
104
	/**
105
	 * Initialize this module
106
	 *
107
	 * @since  1.9
108
	 * @access static
109
	 */
110
	public function init() {
111
		add_filter( 'give_form_api_render_form_tags', array( $this, 'render_tags' ), 10, 2 );
112
	}
113
114
115
	/**
116
	 * Render custom field.
117
	 *
118
	 * @since  1.0
119
	 * @access private
120
	 *
121
	 * @param array $field
122
	 *
123
	 * @return bool
124
	 */
125
	private function render_custom_field( $field ) {
126
		$field_html = '';
127
128
		if ( empty( $field['callback'] ) ) {
129
			$callback = $field['callback'];
130
131
			// Process callback to get field html.
132
			if ( is_string( $callback ) && function_exists( "$callback" ) ) {
133
				$field_html = $callback( $field );
134
			} elseif ( is_array( $callback ) && method_exists( $callback[0], "$callback[1]" ) ) {
135
				$field_html = $callback[0]->$callback[1]( $field );
136
			}
137
		}
138
139
		return $field_html;
140
	}
141
142
	/**
143
	 * Render tag
144
	 *
145
	 * @since   1.9
146
	 * @access  public
147
	 *
148
	 * @param $field
149
	 * @param $form
150
	 *
151
	 * @return string
152
	 */
153
	public static function render_tag( $field, $form = null ) {
154
		$field_html     = '';
155
		$functions_name = "render_{$field['type']}_field";
156
157
		if ( method_exists( self::$instance, $functions_name ) ) {
158
			$field_html .= self::$instance->{$functions_name}( $field );
159
		} else {
160
			$field_html .= apply_filters( "give_field_api_render_{$field['type']}_field", '', $field, $form );
161
		}
162
163
		return $field_html;
164
	}
165
166
167
	/**
168
	 * Render `{{form_fields}}` tag.
169
	 *
170
	 * @since  1.9
171
	 * @access private
172
	 *
173
	 * @param  string $form_html
174
	 * @param  array  $form
175
	 *
176
	 * @return string
177
	 */
178
	public function render_tags( $form_html, $form ) {
179
		// Bailout: If form does not contain any field.
180
		if ( empty( $form['fields'] ) ) {
181
			str_replace( '{{form_fields}}', '', $form_html );
182
183
			return $form_html;
184
		}
185
186
		$fields_html = '';
187
188
		// Set responsive fields.
189
		self::$instance->set_responsive_field( $form );
190
191
		// Render fields.
192
		foreach ( $form['fields'] as $key => $field ) {
193
			// Set default value.
194
			$field['name'] = empty( $field['name'] ) ? $key : $field['name'];
195
			$field         = self::$instance->set_default_values( $field, $form );
196
197
198
			// Render custom form with callback.
199
			if ( $field_html = self::$instance->render_custom_field( $field ) ) {
200
				$fields_html .= $field_html;
201
			}
202
203
			switch ( true ) {
204
				// Section.
205
				case array_key_exists( 'fields', $field ):
206
					// Set default values.
207
					foreach ( $field['fields'] as $section_field_index => $section_field ) {
208
						$section_field['name'] = empty( $section_field['name'] )
209
							? $section_field_index
210
							: $section_field['name'];
211
212
						$field['fields'][ $section_field_index ] = self::$instance->set_default_values( $section_field );
213
					}
214
215
					$fields_html .= self::$instance->render_section( $field, $form );
216
217
					break;
218
219
				// Field
220
				default:
221
					$fields_html .= self::render_tag( $field, $form );
222
			}
223
		}
224
225
		$form_html = str_replace( '{{form_fields}}', $fields_html, $form_html );
226
227
		return $form_html;
228
	}
229
230
231
	/**
232
	 * Render section.
233
	 *
234
	 * @since  1.9
235
	 * @access public
236
	 *
237
	 * @param array $section
238
	 * @param array $form
239
	 *
240
	 * @return string
241
	 */
242
	public static function render_section( $section, $form = null ) {
243
		ob_start();
244
		?>
245
		<fieldset <?php echo self::$instance->get_field_attributes( $section ); ?>>
246
			<?php
247
			// Legend.
248
			if ( ! empty( $section['label'] ) ) {
249
				echo "<legend>{$section['label']}</legend>";
250
			};
251
252
			// Fields.
253
			foreach ( $section['fields'] as $key => $field ) {
254
				$field['name'] = empty( $field['name'] ) ? $key : $field['name'];
255
				echo self::render_tag( $field, $form );
256
			}
257
			?>
258
		</fieldset>
259
		<?php
260
		return ob_get_clean();
261
	}
262
263
264
	/**
265
	 * Render text field.
266
	 *
267
	 * @since  1.9
268
	 * @access private
269
	 *
270
	 * @param  array $field
271
	 *
272
	 * @return string
273
	 */
274
	public static function render_text_field( $field ) {
275
		$field_wrapper = self::$instance->render_field_wrapper( $field );
276
		ob_start();
277
		?>
278
		<input
279
				type="<?php echo $field['type']; ?>"
280
				name="<?php echo $field['name']; ?>"
281
				value="<?php echo $field ['value']; ?>"
282
			<?php echo( $field['required'] ? 'required=""' : '' ); ?>
283
			<?php echo self::$instance->get_field_attributes( $field ); ?>
284
		>
285
		<?php
286
287
		return str_replace( '{{form_field}}', ob_get_clean(), $field_wrapper );
288
	}
289
290
	/**
291
	 * Render submit field.
292
	 *
293
	 * @since  1.9
294
	 * @access private
295
	 *
296
	 * @param  array $field
297
	 *
298
	 * @return string
299
	 */
300
	public static function render_submit_field( $field ) {
301
		return self::$instance->render_text_field( $field );
302
	}
303
304
	/**
305
	 * Render checkbox field.
306
	 *
307
	 * @since  1.9
308
	 * @access private
309
	 *
310
	 * @param  array $field
311
	 *
312
	 * @return string
313
	 */
314
	public static function render_checkbox_field( $field ) {
315
		return self::$instance->render_text_field( $field );
316
	}
317
318
	/**
319
	 * Render email field.
320
	 *
321
	 * @since  1.9
322
	 * @access private
323
	 *
324
	 * @param  array $field
325
	 *
326
	 * @return string
327
	 */
328
	public static function render_email_field( $field ) {
329
		return self::$instance->render_text_field( $field );
330
	}
331
332
	/**
333
	 * Render number field.
334
	 *
335
	 * @since  1.9
336
	 * @access private
337
	 *
338
	 * @param  array $field
339
	 *
340
	 * @return string
341
	 */
342
	public static function render_number_field( $field ) {
343
		return self::$instance->render_text_field( $field );
344
	}
345
346
	/**
347
	 * Render password field.
348
	 *
349
	 * @since  1.9
350
	 * @access private
351
	 *
352
	 * @param  array $field
353
	 *
354
	 * @return string
355
	 */
356
	public static function render_password_field( $field ) {
357
		return self::$instance->render_text_field( $field );
358
	}
359
360
	/**
361
	 * Render textarea field.
362
	 *
363
	 * @since  1.9
364
	 * @access private
365
	 *
366
	 * @param  array $field
367
	 *
368
	 * @return string
369
	 */
370
	public static function render_textarea_field( $field ) {
371
		$field_wrapper = self::$instance->render_field_wrapper( $field );
372
		ob_start();
373
		?>
374
		<textarea
375
				name="<?php echo $field['name']; ?>"
376
			<?php echo( $field['required'] ? 'required=""' : '' ); ?>
377
			<?php echo self::$instance->get_field_attributes( $field ); ?>
378
		><?php echo $field ['value']; ?></textarea>
379
380
381
		<?php
382
383
		return str_replace( '{{form_field}}', ob_get_clean(), $field_wrapper );
384
	}
385
386
	/**
387
	 * Render select field.
388
	 *
389
	 * @since  1.9
390
	 * @access private
391
	 *
392
	 * @param  array $field
393
	 *
394
	 * @return string
395
	 */
396
	public static function render_select_field( $field ) {
397
		$field_wrapper = self::$instance->render_field_wrapper( $field );
398
		ob_start();
399
400
		$options_html = '';
401
		foreach ( $field['options'] as $key => $option ) {
402
			$options_html .= "<option value=\"{$key}\">{$option}</option>";
403
		}
404
		?>
405
406
		<select
407
				name="<?php echo $field['name']; ?>"
408
			<?php echo( $field['required'] ? 'required=""' : '' ); ?>
409
			<?php echo self::$instance->get_field_attributes( $field ); ?>
410
		><?php echo $options_html; ?></select>
411
		<?php
412
413
		return str_replace( '{{form_field}}', ob_get_clean(), $field_wrapper );
414
	}
415
416
	/**
417
	 * Render multi select field.
418
	 *
419
	 * @since  1.9
420
	 * @access private
421
	 *
422
	 * @param  array $field
423
	 *
424
	 * @return string
425
	 */
426
	public static function render_multi_select_field( $field ) {
427
		$field['field_attributes'] = array_merge( $field['field_attributes'], array( 'multiple' => 'multiple' ) );
428
		$field['name']             = "{$field['name']}[]";
429
430
		return self::$instance->render_select_field( $field );
431
	}
432
433
	/**
434
	 * Render text field.
435
	 *
436
	 * @since  1.9
437
	 * @access private
438
	 *
439
	 * @param  array $field
440
	 *
441
	 * @return string
442
	 */
443
	public static function render_radio_field( $field ) {
444
		$field_wrapper = self::$instance->render_field_wrapper( $field );
445
		ob_start();
446
		foreach ( $field['options'] as $key => $option ) :
447
			// @todo id issue.
448
			?>
449
			<input
450
			type="<?php echo $field['type']; ?>"
451
			name="<?php echo $field['name']; ?>"
452
			value="<?php echo $key; ?>"
453
			<?php echo( $field['required'] ? 'required=""' : '' ); ?>
454
			<?php echo self::$instance->get_field_attributes( $field ); ?>
455
			><?php echo $option; ?>
456
			<?php
457
		endforeach;
458
459
		return str_replace( '{{form_field}}', ob_get_clean(), $field_wrapper );
460
	}
461
462
463
	/**
464
	 * Render wrapper
465
	 *
466
	 * @since  1.9
467
	 * @access private
468
	 *
469
	 * @param $field
470
	 *
471
	 * @return string
472
	 */
473
	private function render_field_wrapper( $field ) {
474
		ob_start();
475
476
		echo $field['before_field_wrapper'];
477
		?>
478
		<p <?php echo self::$instance->get_row_attributes( $field ); ?>>
479
			<?php
480
			// Label: before field.
481
			if ( 'before' === $field['label_position'] ) {
482
				echo self::$instance->render_label( $field );
483
			}
484
485
			echo $field['before_field'];
486
			?>
487
488
			{{form_field}}
489
490
			<?php
491
			echo $field['before_field'];
492
493
			// Label: before field.
494
			if ( 'after' === $field['label_position'] ) {
495
				echo self::$instance->render_label( $field );
496
			}
497
			?>
498
		</p>
499
		<?php
500
		echo $field['after_field_wrapper'];
501
502
		return ob_get_clean();
503
	}
504
505
506
	/**
507
	 * Render label
508
	 *
509
	 * @since  1.9
510
	 * @access private
511
	 *
512
	 * @param $field
513
	 *
514
	 * @return string
515
	 */
516
	private function render_label( $field ) {
517
		ob_start();
518
		?>
519
		<?php if ( ! empty( $field['label'] ) ) : ?>
520
			<?php echo $field['before_label']; ?>
521
			<label class="give-label" for="<?php echo $field['field_attributes']['id']; ?>">
522
523
				<?php echo $field['label']; ?>
524
525
				<?php if ( $field['required'] ) : ?>
526
					<span class="give-required-indicator">*</span>
527
				<?php endif; ?>
528
529
				<?php if ( $field['tooltip'] ) : ?>
530
					<span class="give-tooltip give-icon give-icon-question" data-tooltip="<?php echo $field['tooltip'] ?>"></span>
531
				<?php endif; ?>
532
			</label>
533
			<?php echo $field['after_label']; ?>
534
		<?php endif; ?>
535
		<?php
536
		return ob_get_clean();
537
	}
538
539
	/**
540
	 * Get field attribute string from field arguments.
541
	 *
542
	 * @since  1.9
543
	 * @access private
544
	 *
545
	 * @param array $attributes
546
	 *
547
	 * @return array|string
548
	 */
549
	private function get_attributes( $attributes ) {
550
		$field_attributes_val = '';
551
552
		if ( ! empty( $attributes ) ) {
553
			foreach ( $attributes as $attribute_name => $attribute_val ) {
554
				$field_attributes_val[] = "{$attribute_name}=\"{$attribute_val}\"";
555
			}
556
		}
557
558
		if ( ! empty( $field_attributes_val ) ) {
559
			$field_attributes_val = implode( ' ', $field_attributes_val );
560
		}
561
562
		return $field_attributes_val;
563
	}
564
565
	/**
566
	 * Get field attribute string from field arguments.
567
	 *
568
	 * @since  1.9
569
	 * @access private
570
	 *
571
	 * @param $field
572
	 *
573
	 * @return array|string
574
	 */
575
	private function get_field_attributes( $field ) {
576
		return self::$instance->get_attributes( $field['field_attributes'] );
577
	}
578
579
	/**
580
	 * Get row attribute string from field arguments.
581
	 *
582
	 * @since  1.9
583
	 * @access private
584
	 *
585
	 * @param $field
586
	 *
587
	 * @return array|string
588
	 */
589
	private function get_row_attributes( $field ) {
590
		return self::$instance->get_attributes( $field['wrapper_attributes'] );;
591
	}
592
593
	/**
594
	 * Set default values for fields
595
	 *
596
	 * @since  1.0
597
	 * @access private
598
	 *
599
	 * @param array $field
600
	 * @param array $form
601
	 *
602
	 * @return array
603
	 */
604
	private function set_default_values( $field, $form = null ) {
605
		$is_field = array_key_exists( 'fields', $field ) ? false : true;
606
607
		// Get default values for section or field.
608
		$default_values = ! $is_field
609
			? self::$section_defaults
610
			: self::$field_defaults;
611
612
		// Set default values for field or section.
613
		$field = wp_parse_args( $field, $default_values );
614
615
		// Default field classes.
616
		$default_class = ! $is_field ? 'give-form-section js-give-form-section give-clearfix' : "give-field give-field-js give-field-type-{$field['type']}";
617
618
		// Set ID.
619
		$field['field_attributes']['id'] = empty( $field['field_attributes']['id'] )
620
			? ( $is_field ? "give-{$field['name']}-field" : "give-{$field['name']}-section" )
621
			: $field['field_attributes']['id'];
622
623
		// Set class.
624
		$field['field_attributes']['class'] = empty( $field['field_attributes']['class'] )
625
			? $default_class
626
			: "{$default_class} {$field['field_attributes']['class']}";
627
628
		// Set wrapper class.
629
		$field['wrapper_attributes']['class'] = empty( $field['wrapper_attributes']['class'] )
630
			? 'give-field-row'
631
			: "give-field-row {$field['wrapper_attributes']['class']}";
632
633
		return apply_filters( 'give_field_api_set_default_values', $field, $form );
634
	}
635
636
637
	/**
638
	 * Set responsive fields.
639
	 *
640
	 * @since  1.9
641
	 * @access private
642
	 *
643
	 * @param $form
644
	 *
645
	 * @return mixed
646
	 */
647
	private function set_responsive_field( &$form ) {
648
649
		foreach ( $form['fields'] as $key => $field ) {
650
			switch ( true ) {
651
				case array_key_exists( 'fields', $field ):
652
					foreach ( $field['fields'] as $section_field_index => $section_field ) {
653
						if ( ! self::$instance->is_sub_section( $section_field ) ) {
654
							continue;
655
						}
656
657
						$form['fields'][ $key ]['fields'][ $section_field_index ]['wrapper_attributes']['class'] = 'give-form-col';
658
659
						if ( array_key_exists( 'sub_section_end', $section_field ) ) {
660
							$form['fields'][ $key ]['fields'][ $section_field_index ]['wrapper_attributes']['class'] = 'give-form-col give-form-col-end';
661
662
							// Clear float left for next field.
663
							$fields_keys = array_keys( $form['fields'][ $key ]['fields'] );
664
665
							if ( $next_field_key = array_search( $section_field_index, $fields_keys ) ) {
666
								if (
667
									! isset( $fields_keys[ $next_field_key + 1 ] )
668
									|| ! isset( $form['fields'][ $key ]['fields'][ $fields_keys[ $next_field_key + 1 ] ] )
669
								) {
670
									continue;
671
								}
672
673
								$next_field = $form['fields'][ $key ]['fields'][ $fields_keys[ $next_field_key + 1 ] ];
674
675
								$next_field['wrapper_attributes']['class'] = isset( $next_field['wrapper_attributes']['class'] )
676
									? $next_field['wrapper_attributes']['class'] . ' give-clearfix'
677
									: 'give-clearfix';
678
679
								$form['fields'][ $key ]['fields'][ $fields_keys[ $next_field_key + 1 ] ] = $next_field;
680
							}
681
						}
682
					}
683
684
					break;
685
686
				default:
687
					if ( ! self::$instance->is_sub_section( $field ) ) {
688
						continue;
689
					}
690
691
					$form['fields'][ $key ]['wrapper_attributes']['class'] = 'give-form-col';
692
693
					if ( array_key_exists( 'sub_section_end', $field ) ) {
694
						$form['fields'][ $key ]['wrapper_attributes']['class'] = 'give-form-col give-form-col-end';
695
696
						// Clear float left for next field.
697
						$fields_keys = array_keys( $form['fields'] );
698
699
						if ( $next_field_key = array_search( $key, $fields_keys ) ) {
700
							$form['fields'][ $fields_keys[ $next_field_key + 1 ] ]['wrapper_attributes']['class'] = 'give-clearfix';
701
						}
702
					}
703
			}
704
		}
705
	}
706
707
708
	/**
709
	 * Check if current feld is part of sub section or not.
710
	 *
711
	 * @since  1.9
712
	 * @access private
713
	 *
714
	 * @param $field
715
	 *
716
	 * @return bool
717
	 */
718
	private function is_sub_section( $field ) {
719
		$is_sub_section = false;
720
		if (
721
			array_key_exists( 'sub_section_start', $field )
722
			|| array_key_exists( 'sub_section_end', $field )
723
		) {
724
			$is_sub_section = true;
725
		}
726
727
		return $is_sub_section;
728
	}
729
730
	/**
731
	 * Is the element a button?
732
	 *
733
	 * @since  1.9
734
	 * @access static
735
	 *
736
	 * @param array $element
737
	 *
738
	 * @return bool
0 ignored issues
show
Documentation introduced by
Should the return type not be integer?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
739
	 */
740
	static function is_button( $element ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
741
		return preg_match( '/^button|submit$/', $element['#type'] );
742
	}
743
}
744
// @todo auto fill field values.
745
// @todo set clearfix class for section also