Completed
Push — master ( 521f2f...44aca2 )
by
unknown
41s
created
inc/extensions/custom_fonts/class-redux-extension-custom-fonts.php 1 patch
Indentation   +963 added lines, -963 removed lines patch added patch discarded remove patch
@@ -16,967 +16,967 @@
 block discarded – undo
16 16
 
17 17
 if ( ! class_exists( 'Redux_Extension_Custom_Fonts' ) ) {
18 18
 
19
-	/**
20
-	 * Class Redux_Extension_Custom_Fonts
21
-	 */
22
-	class Redux_Extension_Custom_Fonts extends Redux_Extension_Abstract {
23
-
24
-		/**
25
-		 * Extension version.
26
-		 *
27
-		 * @var string
28
-		 */
29
-		public static $version = '4.5.6';
30
-
31
-		/**
32
-		 * Extension friendly name.
33
-		 *
34
-		 * @var string
35
-		 */
36
-		public string $extension_name = 'Custom Fonts';
37
-		/**
38
-		 * Class instance.
39
-		 *
40
-		 * @var object|null
41
-		 */
42
-		public static ?object $instance;
43
-
44
-		/**
45
-		 * Custom fonts array.
46
-		 *
47
-		 * @var array|null
48
-		 */
49
-		public ?array $custom_fonts = array();
50
-
51
-		/**
52
-		 * WordPress upload directory.
53
-		 *
54
-		 * @var string|null
55
-		 */
56
-		public ?string $upload_dir = '';
57
-
58
-		/**
59
-		 * WordPress upload URI.
60
-		 *
61
-		 * @var string|null
62
-		 */
63
-		public ?string $upload_url = '';
64
-
65
-		/**
66
-		 * Subfolder name.
67
-		 *
68
-		 * @var string
69
-		 */
70
-		public string $subfolder = 'custom/';
71
-
72
-		/**
73
-		 * Font folder.
74
-		 *
75
-		 * @var string|null
76
-		 */
77
-		public ?string $font_folder = '';
78
-
79
-		/**
80
-		 * Font Filename.
81
-		 *
82
-		 * @var string|null
83
-		 */
84
-		public ?string $font_filename = '';
85
-
86
-		/**
87
-		 * File selected in media upload.
88
-		 *
89
-		 * @var string|null
90
-		 */
91
-		public ?string $selected_file = '';
92
-
93
-		/**
94
-		 * Is font conversation service available?
95
-		 *
96
-		 * @var bool
97
-		 */
98
-		private bool $can_convert;
99
-
100
-		/**
101
-		 * Class Constructor. Defines the args for the extensions class
102
-		 *
103
-		 * @param ReduxFramework $redux ReduxFramework pointer.
104
-		 *
105
-		 * @return      void
106
-		 * @since       1.0.0
107
-		 * @access      public
108
-		 */
109
-		public function __construct( $redux ) {
110
-			if ( false === $redux->args['custom_fonts'] ) {
111
-				return;
112
-			}
113
-
114
-			parent::__construct( $redux, __FILE__ );
115
-
116
-			self::$instance = parent::get_instance();
117
-
118
-			$this->add_field( 'custom_fonts' );
119
-
120
-			$this->upload_dir = Redux_Core::$upload_dir . 'custom-fonts/';
121
-			$this->upload_url = Redux_Core::$upload_url . 'custom-fonts/';
122
-
123
-			if ( ! is_dir( $this->upload_dir ) ) {
124
-				Redux_Core::$filesystem->execute( 'mkdir', $this->upload_dir );
125
-			}
126
-
127
-			if ( ! is_dir( $this->upload_dir . '/custom' ) ) {
128
-				Redux_Core::$filesystem->execute( 'mkdir', $this->upload_dir . '/custom' );
129
-			}
130
-
131
-			$this->get_fonts();
132
-
133
-			if ( file_exists( $this->upload_dir . 'fonts.css' ) ) {
134
-				if ( filemtime( $this->upload_dir . 'custom' ) > ( filemtime( $this->upload_dir . 'fonts.css' ) + 10 ) ) {
135
-					$this->generate_css();
136
-				}
137
-			} else {
138
-				$this->generate_css();
139
-			}
140
-
141
-			add_action( 'wp_ajax_redux_custom_fonts', array( $this, 'ajax' ) );
142
-			add_action( 'wp_ajax_redux_custom_font_timer', array( $this, 'timer' ) );
143
-
144
-			add_filter( "redux/{$this->parent->args['opt_name']}/field/typography/custom_fonts", array( $this, 'add_custom_fonts' ) );
145
-
146
-			// phpcs:disable
147
-			// $this->is_field = Redux_Helpers::is_field_in_use( $parent, 'custom_fonts' );
148
-
149
-			// if ( ! $this->is_field ) {
150
-			// 	$this->add_section();
151
-			// }
152
-
153
-			add_filter( "redux/options/{$this->parent->args['opt_name']}/section/redux_dynamic_font_control", array( $this, 'remove_dynamic_section' ) ); // phpcs:ignore WordPress.NamingConventions.ValidHookName
154
-			add_filter( 'upload_mimes', array( $this, 'custom_upload_mimes' ) );
155
-			add_action( 'wp_head', array( $this, 'enqueue_output' ), 150 );
156
-			add_filter( 'tiny_mce_before_init', array( $this, 'extend_tinymce_dropdown' ) );
157
-
158
-			$this->can_convert = true; // has_filter( 'redux/' . $this->parent->args['opt_name'] . '/extensions/custom_fonts/api_url' );
159
-			// phpcs:enable
160
-		}
161
-
162
-		/**
163
-		 * Timer.
164
-		 */
165
-		public function timer() {
166
-			$name = get_option( 'redux_custom_font_current' );
167
-
168
-			if ( ! empty( $name ) ) {
169
-				echo esc_html( $name );
170
-			}
171
-
172
-			die();
173
-		}
174
-
175
-		/**
176
-		 * Remove the dynamically added section if the field was used elsewhere
177
-		 *
178
-		 * @param array $section Section array.
179
-		 *
180
-		 * @return array
181
-		 * @since  Redux_Framework 3.1.1
182
-		 */
183
-		public function remove_dynamic_section( array $section ): array {
184
-			if ( isset( $this->parent->field_types['custom_fonts'] ) ) {
185
-				$section = array();
186
-			}
187
-
188
-			return $section;
189
-		}
190
-
191
-		/**
192
-		 * Adds FontMeister fonts to the TinyMCE drop-down.
193
-		 * Typekit's fonts don't render properly in the drop-down and in the editor,
194
-		 * because Typekit needs JS and TinyMCE doesn't support that.
195
-		 *
196
-		 * @param array $opt Option array.
197
-		 *
198
-		 * @return array
199
-		 */
200
-		public function extend_tinymce_dropdown( array $opt ): array {
201
-			if ( ! is_admin() ) {
202
-				return $opt;
203
-			}
204
-
205
-			if ( file_exists( $this->upload_dir . 'fonts.css' ) ) {
206
-				$theme_advanced_fonts = $opt['font_formats'] ?? 'Andale Mono=andale mono,times;Arial=arial,helvetica,sans-serif;Arial Black=arial black,avant garde;Book Antiqua=book antiqua,palatino;Comic Sans MS=comic sans ms,sans-serif;Courier New=courier new,courier;Georgia=georgia,palatino;Helvetica=helvetica;Impact=impact,chicago;Symbol=symbol;Tahoma=tahoma,arial,helvetica,sans-serif;Terminal=terminal,monaco;Times New Roman=times new roman,times;Trebuchet MS=trebuchet ms,geneva;Verdana=verdana,geneva;Webdings=webdings;Wingdings=wingdings,zapf dingbats';
207
-				$custom_fonts         = '';
208
-
209
-				$stylesheet = $this->upload_url . 'fonts.css';
210
-
211
-				if ( empty( $opt['content_css'] ) ) {
212
-					$opt['content_css'] = $stylesheet;
213
-				} else {
214
-					$opt['content_css'] = $opt['content_css'] . ',' . $stylesheet;
215
-				}
216
-
217
-				foreach ( $this->custom_fonts as $arr ) {
218
-					foreach ( $arr as $font => $pieces ) {
219
-						$custom_fonts .= ';' . $font . '=' . $font;
220
-					}
221
-				}
222
-
223
-				$opt['font_formats'] = $theme_advanced_fonts . $custom_fonts;
224
-			}
225
-
226
-			return $opt;
227
-		}
228
-
229
-
230
-		/**
231
-		 * Function to enqueue the custom fonts css
232
-		 */
233
-		public function enqueue_output() {
234
-			if ( file_exists( $this->upload_dir . 'fonts.css' ) ) {
235
-				wp_enqueue_style(
236
-					'redux-custom-fonts',
237
-					$this->upload_url . 'fonts.css',
238
-					array(),
239
-					filemtime( $this->upload_dir . 'fonts.css' )
240
-				);
241
-			}
242
-		}
243
-
244
-		/**
245
-		 * Adds the appropriate mime types to WordPress
246
-		 *
247
-		 * @param array $existing_mimes Mine array.
248
-		 *
249
-		 * @return array
250
-		 */
251
-		public function custom_upload_mimes( array $existing_mimes = array() ): array {
252
-			$existing_mimes['ttf']   = 'font/ttf';
253
-			$existing_mimes['otf']   = 'font/otf';
254
-			$existing_mimes['eot']   = 'application/vnd.ms-fontobject';
255
-			$existing_mimes['woff']  = 'application/font-woff';
256
-			$existing_mimes['woff2'] = 'application/font-woff2';
257
-			$existing_mimes['svg']   = 'image/svg+xml';
258
-			$existing_mimes['zip']   = 'application/zip';
259
-
260
-			return $existing_mimes;
261
-		}
262
-
263
-		/**
264
-		 * Gets all the fonts in the custom_fonts directory
265
-		 */
266
-		public function get_fonts() {
267
-			if ( empty( $this->custom_fonts ) ) {
268
-				$params = array(
269
-					'include_hidden' => false,
270
-					'recursive'      => true,
271
-				);
272
-
273
-				$fonts = Redux_Core::$filesystem->execute( 'dirlist', $this->upload_dir, $params );
274
-
275
-				if ( ! empty( $fonts ) ) {
276
-					foreach ( $fonts as $section ) {
277
-						if ( 'd' === $section['type'] && ! empty( $section['name'] ) ) {
278
-							if ( 'custom' === $section['name'] ) {
279
-								$section['name'] = esc_html__( 'Custom Fonts', 'redux-framework' );
280
-							}
281
-
282
-							if ( empty( $section['files'] ) ) {
283
-								continue;
284
-							}
285
-
286
-							$this->custom_fonts[ $section['name'] ] = $this->custom_fonts[ $section['name'] ] ?? array();
287
-
288
-							foreach ( $section['files'] as $font ) {
289
-								if ( ! empty( $font['name'] ) ) {
290
-									if ( empty( $font['files'] ) ) {
291
-										continue;
292
-									}
293
-
294
-									$kinds = array();
295
-
296
-									foreach ( $font['files'] as $f ) {
297
-										$valid = $this->check_font_name( $f );
298
-										if ( $valid ) {
299
-											$kinds[] = $valid;
300
-										}
301
-									}
302
-
303
-									$this->custom_fonts[ $section['name'] ][ $font['name'] ] = $kinds;
304
-								}
305
-							}
306
-						}
307
-					}
308
-				}
309
-			}
310
-		}
311
-
312
-		/**
313
-		 * Add custom fonts.
314
-		 *
315
-		 * @param mixed $custom_fonts Custom fonts.
316
-		 *
317
-		 * @return array
318
-		 */
319
-		public function add_custom_fonts( $custom_fonts ): array {
320
-			if ( empty( $custom_fonts ) ) {
321
-				$custom_fonts = array();
322
-			}
323
-
324
-			return wp_parse_args( $custom_fonts, $this->custom_fonts );
325
-		}
326
-
327
-		/**
328
-		 * Ajax used within the panel to add and process the fonts
329
-		 */
330
-		public function ajax() {
331
-			if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_key( wp_unslash( $_POST['nonce'] ) ), 'redux_custom_fonts' ) ) {
332
-				die( 0 );
333
-			}
334
-
335
-			if ( isset( $_POST['type'] ) && 'delete' === $_POST['type'] ) {
336
-				if ( isset( $_POST['section'] ) ) {
337
-					if ( esc_html__( 'Custom Fonts', 'redux-framework' ) === $_POST['section'] ) {
338
-						$_POST['section'] = 'custom';
339
-					}
340
-				}
341
-
342
-				try {
343
-					if ( isset( $_POST['section'] ) || isset( $_POST['name'] ) ) {
344
-						$ret = Redux_Core::$filesystem->execute( 'rmdir', $this->upload_dir . sanitize_file_name( wp_unslash( $_POST['section'] ) ) . '/' . sanitize_file_name( wp_unslash( $_POST['name'] ) ) . '/', array( 'recursive' => true ) );
345
-
346
-						if ( true === $ret ) {
347
-							$result = array( 'type' => 'success' );
348
-						} else {
349
-							$result = array(
350
-								'type' => 'error',
351
-								'msg'  => esc_html__( 'File system failure. Could not delete temp dir.', 'redux-framework' ),
352
-							);
353
-						}
354
-
355
-						echo wp_json_encode( $result );
356
-					}
357
-				} catch ( Exception $e ) {
358
-					echo wp_json_encode(
359
-						array(
360
-							'type' => 'error',
361
-							'msg'  => esc_html__( 'Unable to delete font file(s).', 'redux-framework' ),
362
-						)
363
-					);
364
-				}
365
-
366
-				die();
367
-			}
368
-
369
-			if ( ! isset( $_POST['title'] ) ) {
370
-				$_POST['title'] = '';
371
-			}
372
-
373
-			if ( ! isset( $_POST['filename'] ) ) {
374
-				$_POST['filename'] = '';
375
-			}
376
-
377
-			$this->font_folder   = sanitize_file_name( wp_unslash( $_POST['title'] ) );
378
-			$this->font_filename = sanitize_file_name( wp_unslash( $_POST['filename'] ) );
379
-
380
-			if ( ! empty( $_POST['attachment_id'] ) ) {
381
-				if ( isset( $_POST['title'] ) || isset( $_POST['mime'] ) ) {
382
-					$msg = $this->process_web_font( sanitize_key( wp_unslash( $_POST['attachment_id'] ) ), sanitize_text_field( wp_unslash( $_POST['mime'] ) ) );
383
-
384
-					if ( empty( $msg ) ) {
385
-						$msg = '';
386
-					}
387
-
388
-					$result = array(
389
-						'type' => 'success',
390
-						'msg'  => $msg,
391
-					);
392
-
393
-					echo wp_json_encode( $result );
394
-				}
395
-			}
396
-
397
-			die();
398
-		}
399
-
400
-		/**
401
-		 * Get only valid files. Ensure everything is proper for processing.
402
-		 *
403
-		 * @param string $path Path.
404
-		 *
405
-		 * @return array
406
-		 */
407
-		public function get_valid_files( string $path ): array {
408
-			$output = array();
409
-			$path   = trailingslashit( $path );
410
-
411
-			$params = array(
412
-				'include_hidden' => false,
413
-				'recursive'      => true,
414
-			);
415
-
416
-			$files = Redux_Core::$filesystem->execute( 'dirlist', $path, $params );
417
-
418
-			foreach ( $files as $file ) {
419
-				if ( 'd' === $file['type'] ) {
420
-					$output = array_merge( $output, $this->get_valid_files( $path . $file['name'] ) );
421
-				} elseif ( 'f' === $file['type'] ) {
422
-					$valid = $this->check_font_name( $file );
423
-					if ( $valid ) {
424
-						$output[ $valid ] = trailingslashit( $path ) . $file['name'];
425
-					}
426
-				}
427
-			}
428
-
429
-			return $output;
430
-		}
431
-
432
-		/**
433
-		 * Take a valid web font and process the missing pieces.
434
-		 *
435
-		 * @param string $attachment_id ID.
436
-		 * @param string $mime_type     Mine type.
437
-		 */
438
-		public function process_web_font( string $attachment_id, string $mime_type ) {
439
-			// phpcs:ignore WordPress.Security.NonceVerification
440
-			if ( ! isset( $_POST['conversion'] ) ) {
441
-				$_POST['conversion'] = 'false';
442
-			}
443
-
444
-			// phpcs:ignore WordPress.Security.NonceVerification
445
-			$conversion = sanitize_text_field( wp_unslash( $_POST['conversion'] ) );
446
-
447
-			$missing = array();
448
-
449
-			$complete = array(
450
-				'ttf',
451
-				'woff',
452
-				'woff2',
453
-				'eot',
454
-				'svg',
455
-				'otf',
456
-			);
457
-
458
-			$subtype = explode( '/', $mime_type );
459
-			$subtype = trim( max( $subtype ) );
460
-
461
-			if ( ! is_dir( $this->upload_dir ) ) {
462
-				Redux_Core::$filesystem->execute( 'mkdir', $this->upload_dir );
463
-			}
464
-
465
-			if ( ! is_dir( $this->upload_dir . $this->subfolder ) ) {
466
-				Redux_Core::$filesystem->execute( 'mkdir', $this->upload_dir . $this->subfolder );
467
-			}
468
-
469
-			$temp                = $this->upload_dir . 'temp';
470
-			$this->selected_file = get_attached_file( $attachment_id );
471
-
472
-			if ( empty( $this->selected_file ) ) {
473
-				echo wp_json_encode(
474
-					array(
475
-						'type' => 'error',
476
-						'msg'  => esc_html__( 'Attachment does not exist.', 'redux-framework' ),
477
-					)
478
-				);
479
-
480
-				die();
481
-			}
482
-
483
-			$filename = explode( '/', $this->selected_file );
484
-
485
-			$filename = $filename[ ( count( $filename ) - 1 ) ];
486
-
487
-			$fontname = ucfirst(
488
-				str_replace(
489
-					array(
490
-						'.zip',
491
-						'.ttf',
492
-						'.woff',
493
-						'.woff2',
494
-						'.eot',
495
-						'.svg',
496
-						'.otf',
497
-					),
498
-					'',
499
-					strtolower( $filename )
500
-				)
501
-			);
502
-
503
-			if ( empty( $this->font_folder ) ) {
504
-				$this->font_folder = $fontname;
505
-			}
506
-
507
-			$ret = array();
508
-
509
-			if ( ! is_dir( $temp ) ) {
510
-				Redux_Core::$filesystem->execute( 'mkdir', $temp );
511
-			}
512
-
513
-			if ( 'zip' === $subtype ) {
514
-				$unzipfile = unzip_file( $this->selected_file, $temp );
515
-
516
-				if ( is_wp_error( $unzipfile ) ) {
517
-					echo wp_json_encode(
518
-						array(
519
-							'type' => 'error',
520
-							'msg'  => $unzipfile->get_error_message() . '<br><br>' . esc_html__( 'Unzipping failed.', 'redux-framework' ),
521
-						)
522
-					);
523
-
524
-					die();
525
-				}
526
-
527
-				$output = $this->get_valid_files( $temp );
528
-
529
-				if ( ! empty( $output ) ) {
530
-					foreach ( $complete as $test ) {
531
-						if ( ! isset( $output[ $test ] ) ) {
532
-							$missing[] = $test;
533
-						}
534
-					}
535
-
536
-					if ( ! is_dir( $this->upload_dir . $this->subfolder . $this->font_folder . '/' ) ) {
537
-						Redux_Core::$filesystem->execute( 'mkdir', $this->upload_dir . $this->subfolder . $this->font_folder . '/' );
538
-					}
539
-
540
-					foreach ( $output as $key => $value ) {
541
-						$param_array = array(
542
-							'destination' => $this->upload_dir . $this->subfolder . $this->font_folder . '/' . $fontname . '.' . $key,
543
-							'overwrite'   => true,
544
-							'chmod'       => 755,
545
-						);
546
-
547
-						Redux_Core::$filesystem->execute( 'copy', $value, $param_array );
548
-					}
549
-
550
-					if ( true === $this->can_convert && 'true' === $conversion ) {
551
-						$ret = $this->get_missing_files( $fontname, $missing, $output );
552
-					}
553
-				}
554
-
555
-				Redux_Core::$filesystem->execute( 'rmdir', $temp, array( 'recursive' => true ) );
556
-
557
-				$this->generate_css();
558
-
559
-				wp_delete_attachment( $attachment_id, true );
560
-			} elseif ( 'svg+xml' === $subtype || 'vnd.ms-fontobject' === $subtype || 'x-font-ttf' === $subtype || 'ttf' === $subtype || 'otf' === $subtype || 'font-woff' === $subtype || 'font-woff2' === $subtype || 'application-octet-stream' === $subtype || 'octet-stream' === $subtype ) {
561
-				foreach ( $complete as $test ) {
562
-					if ( $subtype !== $test ) {
563
-						if ( ! isset( $output[ $test ] ) ) {
564
-							$missing[] = $test;
565
-						}
566
-					}
567
-				}
568
-
569
-				if ( ! is_dir( $this->upload_dir . $this->subfolder . $this->font_folder . '/' ) ) {
570
-					Redux_Core::$filesystem->execute( 'mkdir', $this->upload_dir . $this->subfolder . $this->font_folder . '/' );
571
-				}
572
-
573
-				$output = array( $subtype => $this->selected_file );
574
-
575
-				if ( true === $this->can_convert && 'true' === $conversion ) {
576
-					$ret = $this->get_missing_files( $fontname, $missing, $output );
577
-
578
-					if ( false === $ret ) {
579
-						if ( false === $this->convert_local_font() ) {
580
-							echo wp_json_encode(
581
-								array(
582
-									'type' => 'error',
583
-									'msg'  => esc_html__( 'File permission error. Local file could not be installed.', 'redux-framework' ) . ' ' . $subtype,
584
-								)
585
-							);
586
-
587
-							die;
588
-						}
589
-					}
590
-				} elseif ( false === $this->convert_local_font() ) {
591
-						echo wp_json_encode(
592
-							array(
593
-								'type' => 'error',
594
-								'msg'  => esc_html__( 'File permission error. Local file could not be installed.', 'redux-framework' ) . ' ' . $subtype,
595
-							)
596
-						);
597
-
598
-						die;
599
-				}
600
-
601
-				Redux_Core::$filesystem->execute( 'rmdir', $temp, array( 'recursive' => true ) );
602
-
603
-				$this->generate_css();
604
-
605
-				wp_delete_attachment( $attachment_id, true );
606
-			} else {
607
-				echo wp_json_encode(
608
-					array(
609
-						'type' => 'error',
610
-						'msg'  => esc_html__( 'File type not recognized.', 'redux-framework' ) . ' ' . $subtype,
611
-					)
612
-				);
613
-
614
-				die();
615
-			}
616
-
617
-			if ( is_array( $ret ) && ! empty( $ret ) ) {
618
-				$msg = esc_html__( 'Unidentified error.', 'redux-framework' );
619
-
620
-				if ( isset( $ret['msg'] ) ) {
621
-					$msg = $ret['msg'];
622
-				}
623
-
624
-				return $msg;
625
-			}
626
-
627
-			return '';
628
-		}
629
-
630
-		/**
631
-		 * Install selected file into Custom Fonts.
632
-		 *
633
-		 * @return bool
634
-		 */
635
-		private function convert_local_font(): bool {
636
-			$param_array = array(
637
-				'destination' => $this->upload_dir . $this->subfolder . '/' . $this->font_folder . '/' . $this->font_filename,
638
-				'overwrite'   => true,
639
-				'chmod'       => 755,
640
-			);
641
-
642
-			return Redux_Core::$filesystem->execute( 'copy', $this->selected_file, $param_array );
643
-		}
644
-
645
-		/**
646
-		 * Ping the WebFontOMatic API to get the missing files.
647
-		 *
648
-		 * @param string $fontname  Font name.
649
-		 * @param array  $missing   Missing.
650
-		 * @param array  $output    Output.
651
-		 */
652
-		private function get_missing_files( string $fontname, array $missing, array $output ) {
653
-			if ( ! empty( $this->font_folder ) && ! empty( $missing ) ) {
654
-				$temp = $this->upload_dir . 'temp';
655
-
656
-				$font_ext = pathinfo( $this->font_filename, PATHINFO_EXTENSION );
657
-
658
-				$unsupported = array( 'eot', 'woff', 'woff2' );
659
-
660
-				// Find a file to convert from.
661
-				foreach ( $output as $key => $value ) {
662
-					if ( 'eot' === $key ) {
663
-						continue;
664
-					} else {
665
-						$main = $key;
666
-						break;
667
-					}
668
-				}
669
-
670
-				if ( ! isset( $main ) ) {
671
-					echo wp_json_encode(
672
-						array(
673
-							'type' => 'error',
674
-							'msg'  => esc_html__( 'No valid font file was found.', 'redux-framework' ),
675
-						)
676
-					);
677
-
678
-					Redux_Core::$filesystem->execute( 'rmdir', $temp, array( 'recursive' => true ) );
679
-					Redux_Core::$filesystem->execute( 'rmdir', $this->upload_dir . $this->subfolder . $this->font_folder . '/', array( 'recursive' => true ) );
680
-
681
-					die();
682
-				}
683
-
684
-				update_option( 'redux_custom_font_current', $this->font_folder . '.zip' );
685
-
686
-				$boundary = wp_generate_password( 24 );
687
-
688
-				$headers = array(
689
-					'content-type' => 'multipart/form-data; boundary=' . $boundary,
690
-					'user-agent'   => 'redux-custom-fonts-' . self::$version . ' using ' . wp_get_theme(),
691
-				);
692
-
693
-				$payload  = '--' . $boundary;
694
-				$payload .= "\r\n";
695
-				$payload .= 'Content-Disposition: form-data; name="md5"' . "\r\n\r\n";
696
-				$payload .= md5( 'redux_custom_font' );
697
-				$payload .= "\r\n";
698
-
699
-				if ( $output[ $main ] ) {
700
-					$payload .= '--' . $boundary;
701
-					$payload .= "\r\n";
702
-					$payload .= 'Content-Disposition: form-data; name="convert"; filename="' . basename( $output[ $main ] ) . '"' . "\r\n";
703
-					$payload .= "\r\n";
704
-					$payload .= Redux_Core::$filesystem->execute( 'get_contents', $output[ $main ] );
705
-					$payload .= "\r\n";
706
-				}
707
-
708
-				$payload .= '--' . $boundary . '--';
709
-
710
-				$args = array(
711
-					'headers'    => $headers,
712
-					'body'       => $payload,
713
-					'user-agent' => $headers['user-agent'],
714
-					'timeout'    => 300,
715
-					'sslverify'  => true,
716
-				);
717
-
718
-				// phpcs:disable WordPress.NamingConventions.ValidHookName
719
-				$api_url = apply_filters( 'redux/' . $this->parent->args['opt_name'] . '/extensions/custom_fonts/api_url', 'https://redux.io/fonts' );
720
-
721
-				$response = wp_remote_post( $api_url, $args );
722
-
723
-				if ( is_wp_error( $response ) ) {
724
-					return array(
725
-						'type' => 'error',
726
-						'msg'  => $response->get_error_message() . '<br><br>' . esc_html__( 'Your font could not be converted at this time. Please try again later.', 'redux-framework' ),
727
-					);
728
-				} elseif ( isset( $response['body'] ) ) {
729
-					if ( null !== json_decode( $response['body'] ) ) {
730
-						return json_decode( $response['body'], true );
731
-					}
732
-				}
733
-
734
-				$param_array = array(
735
-					'content'   => $response['body'],
736
-					'overwrite' => true,
737
-					'chmod'     => FS_CHMOD_FILE,
738
-				);
739
-
740
-				$zip_file = $temp . DIRECTORY_SEPARATOR . $fontname . '.zip';
741
-
742
-				Redux_Core::$filesystem->execute( 'put_contents', $zip_file, $param_array );
743
-
744
-				if ( 0 === filesize( $zip_file ) ) {
745
-					return false;
746
-				}
747
-
748
-				$zip = unzip_file( $zip_file, $temp );
749
-
750
-				if ( ! is_wp_error( $zip ) ) {
751
-					$params = array(
752
-						'include_hidden' => false,
753
-						'recursive'      => false,
754
-					);
755
-
756
-					$files = Redux_Core::$filesystem->execute( 'dirlist', $temp . DIRECTORY_SEPARATOR . 'fonts' . DIRECTORY_SEPARATOR, $params );
757
-
758
-					foreach ( $files as $file ) {
759
-						$param_array = array(
760
-							'destination' => $this->upload_dir . $this->subfolder . $this->font_folder . DIRECTORY_SEPARATOR . sanitize_file_name( $file['name'] ),
761
-							'overwrite'   => true,
762
-							'chmod'       => 755,
763
-						);
764
-
765
-						Redux_Core::$filesystem->execute( 'move', $temp . DIRECTORY_SEPARATOR . 'fonts' . DIRECTORY_SEPARATOR . ( $file['name'] ), $param_array );
766
-					}
767
-				} else {
768
-					$path_parts = pathinfo( $output[ $main ] );
769
-
770
-					$param_array = array(
771
-						'destination' => $this->upload_dir . $this->subfolder . $this->font_folder . DIRECTORY_SEPARATOR . sanitize_file_name( $path_parts['basename'] ),
772
-						'overwrite'   => true,
773
-						'chmod'       => 755,
774
-					);
775
-
776
-					Redux_Core::$filesystem->execute( 'move', $output[ $main ], $param_array );
777
-
778
-					if ( in_array( $font_ext, $unsupported, true ) ) {
779
-						return array(
780
-							'type' => 'error',
781
-							// translators: %s = font extension.
782
-							'msg'  => $zip->get_error_message() . '<br><br>' . sprintf( esc_html__( 'The font converter does not support %s fonts.', 'redux-framework' ), $font_ext ),
783
-						);
784
-					} else {
785
-						return array(
786
-							'type' => 'error',
787
-							'msg'  => $zip->get_error_message() . '<br><br>' . esc_html__( 'ZIP error. Your font could not be converted at this time. Please try again later.', 'redux-framework' ),
788
-						);
789
-					}
790
-				}
791
-
792
-				delete_option( 'redux_custom_font_current' );
793
-			}
794
-
795
-			return true;
796
-		}
797
-
798
-		/**
799
-		 * Check if the file name is a valid font file.
800
-		 *
801
-		 * @param array $file File.
802
-		 *
803
-		 * @return bool|string
804
-		 */
805
-		private function check_font_name( array $file ) {
806
-			if ( '.woff' === strtolower( substr( $file['name'], - 5 ) ) ) {
807
-				return 'woff';
808
-			}
809
-
810
-			if ( '.woff2' === strtolower( substr( $file['name'], - 6 ) ) ) {
811
-				return 'woff2';
812
-			}
813
-
814
-			$sub = strtolower( substr( $file['name'], - 4 ) );
815
-
816
-			if ( '.ttf' === $sub ) {
817
-				return 'ttf';
818
-			}
819
-
820
-			if ( '.eot' === $sub ) {
821
-				return 'eot';
822
-			}
823
-
824
-			if ( '.svg' === $sub ) {
825
-				return 'svg';
826
-			}
827
-
828
-			if ( '.otf' === $sub ) {
829
-				return 'otf';
830
-			}
831
-
832
-			return false;
833
-		}
834
-
835
-		/**
836
-		 * Generate a new custom CSS file for enqueuing on the frontend and backend.
837
-		 */
838
-		private function generate_css() {
839
-			$params = array(
840
-				'include_hidden' => false,
841
-				'recursive'      => true,
842
-			);
843
-
844
-			$fonts = Redux_Core::$filesystem->execute( 'dirlist', $this->upload_dir . 'custom' . DIRECTORY_SEPARATOR, $params );
845
-
846
-			if ( empty( $fonts ) || ! is_array( $fonts ) ) {
847
-				return;
848
-			}
849
-
850
-			foreach ( $fonts as $font ) {
851
-				if ( 'd' === $font['type'] ) {
852
-					break;
853
-				}
854
-
855
-				if ( file_exists( $this->upload_dir . 'fonts.css' ) ) {
856
-					Redux_Core::$filesystem->execute( 'delete', $this->upload_dir . 'fonts.css' );
857
-				}
858
-
859
-				return;
860
-			}
861
-
862
-			$css = '';
863
-
864
-			foreach ( $fonts as $font ) {
865
-				if ( 'd' === $font['type'] ) {
866
-					$css .= $this->generate_font_css( $font['name'], $this->upload_dir . 'custom' . DIRECTORY_SEPARATOR );
867
-				}
868
-			}
869
-
870
-			if ( '' !== $css ) {
871
-				$param_array = array(
872
-					'content' => $css,
873
-					'chmod'   => FS_CHMOD_FILE,
874
-				);
875
-
876
-				Redux_Core::$filesystem->execute( 'put_contents', $this->upload_dir . 'fonts.css', $param_array );
877
-			}
878
-		}
879
-
880
-		/**
881
-		 * Process to actually construct the custom font css file.
882
-		 *
883
-		 * @param string $name Name.
884
-		 * @param string $dir  Directory.
885
-		 *
886
-		 * @return string
887
-		 */
888
-		private function generate_font_css( string $name, string $dir ): ?string {
889
-			$path = $dir . $name;
890
-
891
-			$params = array(
892
-				'include_hidden' => false,
893
-				'recursive'      => true,
894
-			);
895
-
896
-			$files = Redux_Core::$filesystem->execute( 'dirlist', $path, $params );
897
-
898
-			if ( empty( $files ) ) {
899
-				return null;
900
-			}
901
-
902
-			$output = array();
903
-
904
-			foreach ( $files as $file ) {
905
-				$output[ $this->check_font_name( $file ) ] = $file['name'];
906
-			}
907
-
908
-			$css = '@font-face {';
909
-
910
-			$css .= 'font-family:"' . $name . '";';
911
-
912
-			$src = array();
913
-
914
-			if ( isset( $output['eot'] ) ) {
915
-				$src[] = "url('{$this->upload_url}custom/$name/{$output['eot']}?#iefix') format('embedded-opentype')";
916
-			}
917
-
918
-			if ( isset( $output['woff'] ) ) {
919
-				$src[] = "url('{$this->upload_url}custom/$name/{$output['woff']}') format('woff')";
920
-			}
921
-
922
-			if ( isset( $output['woff2'] ) ) {
923
-				$src[] = "url('{$this->upload_url}custom/$name/{$output['woff2']}') format('woff2')";
924
-			}
925
-
926
-			if ( isset( $output['ttf'] ) ) {
927
-				$src[] = "url('{$this->upload_url}custom/$name/{$output['ttf']}') format('truetype')";
928
-			}
929
-
930
-			if ( isset( $output['svg'] ) ) {
931
-				$src[] = "url('{$this->upload_url}custom/$name/{$output['svg']}#svg$name') format('svg')";
932
-			}
933
-
934
-			if ( ! empty( $src ) ) {
935
-				$css .= 'src:' . implode( ', ', $src ) . ';';
936
-			}
937
-
938
-			// Replace font weight and style with sub-sets.
939
-			$css .= 'font-weight: normal;';
940
-
941
-			$css .= 'font-style: normal;';
942
-
943
-			$css .= '}';
944
-
945
-			return $css;
946
-		}
947
-
948
-		/**
949
-		 * Custom function for filtering the section array.
950
-		 * Good for child themes to override or add to the sections.
951
-		 * Simply include this function in the child themes functions.php file.
952
-		 * NOTE: the defined constants for URLs and directories will NOT be available at this point in a child theme,
953
-		 * so you must use get_template_directory_uri() if you want to use any of the built-in icons
954
-		 */
955
-		public function add_section() {
956
-			if ( ! isset( $this->parent->fontControl ) ) {
957
-				$this->parent->sections[] = array(
958
-					'title'  => esc_html__( 'Font Control', 'redux-framework' ),
959
-					'desc'   => '<p class="description"></p>',
960
-					'icon'   => 'el-icon-font',
961
-					'id'     => 'redux_dynamic_font_control',
962
-					// Leave this as a blank section, no options just some intro text set above.
963
-					'fields' => array(),
964
-				);
965
-
966
-				for ( $i = count( $this->parent->sections ); $i >= 1; $i-- ) {
967
-					if ( isset( $this->parent->sections[ $i ] ) && isset( $this->parent->sections[ $i ]['title'] ) && esc_html__( 'Font Control', 'redux-framework' ) === $this->parent->sections[ $i ]['title'] ) {
968
-						$this->parent->fontControl                                        = $i;
969
-						$this->parent->sections[ $this->parent->fontControl ]['fields'][] = array(
970
-							'id'   => 'redux_font_control',
971
-							'type' => 'custom_fonts',
972
-						);
973
-
974
-						break;
975
-					}
976
-				}
977
-			}
978
-		}
979
-	}
980
-
981
-	class_alias( Redux_Extension_Custom_Fonts::class, 'ReduxFramework_Extension_custom_fonts' );
19
+    /**
20
+     * Class Redux_Extension_Custom_Fonts
21
+     */
22
+    class Redux_Extension_Custom_Fonts extends Redux_Extension_Abstract {
23
+
24
+        /**
25
+         * Extension version.
26
+         *
27
+         * @var string
28
+         */
29
+        public static $version = '4.5.6';
30
+
31
+        /**
32
+         * Extension friendly name.
33
+         *
34
+         * @var string
35
+         */
36
+        public string $extension_name = 'Custom Fonts';
37
+        /**
38
+         * Class instance.
39
+         *
40
+         * @var object|null
41
+         */
42
+        public static ?object $instance;
43
+
44
+        /**
45
+         * Custom fonts array.
46
+         *
47
+         * @var array|null
48
+         */
49
+        public ?array $custom_fonts = array();
50
+
51
+        /**
52
+         * WordPress upload directory.
53
+         *
54
+         * @var string|null
55
+         */
56
+        public ?string $upload_dir = '';
57
+
58
+        /**
59
+         * WordPress upload URI.
60
+         *
61
+         * @var string|null
62
+         */
63
+        public ?string $upload_url = '';
64
+
65
+        /**
66
+         * Subfolder name.
67
+         *
68
+         * @var string
69
+         */
70
+        public string $subfolder = 'custom/';
71
+
72
+        /**
73
+         * Font folder.
74
+         *
75
+         * @var string|null
76
+         */
77
+        public ?string $font_folder = '';
78
+
79
+        /**
80
+         * Font Filename.
81
+         *
82
+         * @var string|null
83
+         */
84
+        public ?string $font_filename = '';
85
+
86
+        /**
87
+         * File selected in media upload.
88
+         *
89
+         * @var string|null
90
+         */
91
+        public ?string $selected_file = '';
92
+
93
+        /**
94
+         * Is font conversation service available?
95
+         *
96
+         * @var bool
97
+         */
98
+        private bool $can_convert;
99
+
100
+        /**
101
+         * Class Constructor. Defines the args for the extensions class
102
+         *
103
+         * @param ReduxFramework $redux ReduxFramework pointer.
104
+         *
105
+         * @return      void
106
+         * @since       1.0.0
107
+         * @access      public
108
+         */
109
+        public function __construct( $redux ) {
110
+            if ( false === $redux->args['custom_fonts'] ) {
111
+                return;
112
+            }
113
+
114
+            parent::__construct( $redux, __FILE__ );
115
+
116
+            self::$instance = parent::get_instance();
117
+
118
+            $this->add_field( 'custom_fonts' );
119
+
120
+            $this->upload_dir = Redux_Core::$upload_dir . 'custom-fonts/';
121
+            $this->upload_url = Redux_Core::$upload_url . 'custom-fonts/';
122
+
123
+            if ( ! is_dir( $this->upload_dir ) ) {
124
+                Redux_Core::$filesystem->execute( 'mkdir', $this->upload_dir );
125
+            }
126
+
127
+            if ( ! is_dir( $this->upload_dir . '/custom' ) ) {
128
+                Redux_Core::$filesystem->execute( 'mkdir', $this->upload_dir . '/custom' );
129
+            }
130
+
131
+            $this->get_fonts();
132
+
133
+            if ( file_exists( $this->upload_dir . 'fonts.css' ) ) {
134
+                if ( filemtime( $this->upload_dir . 'custom' ) > ( filemtime( $this->upload_dir . 'fonts.css' ) + 10 ) ) {
135
+                    $this->generate_css();
136
+                }
137
+            } else {
138
+                $this->generate_css();
139
+            }
140
+
141
+            add_action( 'wp_ajax_redux_custom_fonts', array( $this, 'ajax' ) );
142
+            add_action( 'wp_ajax_redux_custom_font_timer', array( $this, 'timer' ) );
143
+
144
+            add_filter( "redux/{$this->parent->args['opt_name']}/field/typography/custom_fonts", array( $this, 'add_custom_fonts' ) );
145
+
146
+            // phpcs:disable
147
+            // $this->is_field = Redux_Helpers::is_field_in_use( $parent, 'custom_fonts' );
148
+
149
+            // if ( ! $this->is_field ) {
150
+            // 	$this->add_section();
151
+            // }
152
+
153
+            add_filter( "redux/options/{$this->parent->args['opt_name']}/section/redux_dynamic_font_control", array( $this, 'remove_dynamic_section' ) ); // phpcs:ignore WordPress.NamingConventions.ValidHookName
154
+            add_filter( 'upload_mimes', array( $this, 'custom_upload_mimes' ) );
155
+            add_action( 'wp_head', array( $this, 'enqueue_output' ), 150 );
156
+            add_filter( 'tiny_mce_before_init', array( $this, 'extend_tinymce_dropdown' ) );
157
+
158
+            $this->can_convert = true; // has_filter( 'redux/' . $this->parent->args['opt_name'] . '/extensions/custom_fonts/api_url' );
159
+            // phpcs:enable
160
+        }
161
+
162
+        /**
163
+         * Timer.
164
+         */
165
+        public function timer() {
166
+            $name = get_option( 'redux_custom_font_current' );
167
+
168
+            if ( ! empty( $name ) ) {
169
+                echo esc_html( $name );
170
+            }
171
+
172
+            die();
173
+        }
174
+
175
+        /**
176
+         * Remove the dynamically added section if the field was used elsewhere
177
+         *
178
+         * @param array $section Section array.
179
+         *
180
+         * @return array
181
+         * @since  Redux_Framework 3.1.1
182
+         */
183
+        public function remove_dynamic_section( array $section ): array {
184
+            if ( isset( $this->parent->field_types['custom_fonts'] ) ) {
185
+                $section = array();
186
+            }
187
+
188
+            return $section;
189
+        }
190
+
191
+        /**
192
+         * Adds FontMeister fonts to the TinyMCE drop-down.
193
+         * Typekit's fonts don't render properly in the drop-down and in the editor,
194
+         * because Typekit needs JS and TinyMCE doesn't support that.
195
+         *
196
+         * @param array $opt Option array.
197
+         *
198
+         * @return array
199
+         */
200
+        public function extend_tinymce_dropdown( array $opt ): array {
201
+            if ( ! is_admin() ) {
202
+                return $opt;
203
+            }
204
+
205
+            if ( file_exists( $this->upload_dir . 'fonts.css' ) ) {
206
+                $theme_advanced_fonts = $opt['font_formats'] ?? 'Andale Mono=andale mono,times;Arial=arial,helvetica,sans-serif;Arial Black=arial black,avant garde;Book Antiqua=book antiqua,palatino;Comic Sans MS=comic sans ms,sans-serif;Courier New=courier new,courier;Georgia=georgia,palatino;Helvetica=helvetica;Impact=impact,chicago;Symbol=symbol;Tahoma=tahoma,arial,helvetica,sans-serif;Terminal=terminal,monaco;Times New Roman=times new roman,times;Trebuchet MS=trebuchet ms,geneva;Verdana=verdana,geneva;Webdings=webdings;Wingdings=wingdings,zapf dingbats';
207
+                $custom_fonts         = '';
208
+
209
+                $stylesheet = $this->upload_url . 'fonts.css';
210
+
211
+                if ( empty( $opt['content_css'] ) ) {
212
+                    $opt['content_css'] = $stylesheet;
213
+                } else {
214
+                    $opt['content_css'] = $opt['content_css'] . ',' . $stylesheet;
215
+                }
216
+
217
+                foreach ( $this->custom_fonts as $arr ) {
218
+                    foreach ( $arr as $font => $pieces ) {
219
+                        $custom_fonts .= ';' . $font . '=' . $font;
220
+                    }
221
+                }
222
+
223
+                $opt['font_formats'] = $theme_advanced_fonts . $custom_fonts;
224
+            }
225
+
226
+            return $opt;
227
+        }
228
+
229
+
230
+        /**
231
+         * Function to enqueue the custom fonts css
232
+         */
233
+        public function enqueue_output() {
234
+            if ( file_exists( $this->upload_dir . 'fonts.css' ) ) {
235
+                wp_enqueue_style(
236
+                    'redux-custom-fonts',
237
+                    $this->upload_url . 'fonts.css',
238
+                    array(),
239
+                    filemtime( $this->upload_dir . 'fonts.css' )
240
+                );
241
+            }
242
+        }
243
+
244
+        /**
245
+         * Adds the appropriate mime types to WordPress
246
+         *
247
+         * @param array $existing_mimes Mine array.
248
+         *
249
+         * @return array
250
+         */
251
+        public function custom_upload_mimes( array $existing_mimes = array() ): array {
252
+            $existing_mimes['ttf']   = 'font/ttf';
253
+            $existing_mimes['otf']   = 'font/otf';
254
+            $existing_mimes['eot']   = 'application/vnd.ms-fontobject';
255
+            $existing_mimes['woff']  = 'application/font-woff';
256
+            $existing_mimes['woff2'] = 'application/font-woff2';
257
+            $existing_mimes['svg']   = 'image/svg+xml';
258
+            $existing_mimes['zip']   = 'application/zip';
259
+
260
+            return $existing_mimes;
261
+        }
262
+
263
+        /**
264
+         * Gets all the fonts in the custom_fonts directory
265
+         */
266
+        public function get_fonts() {
267
+            if ( empty( $this->custom_fonts ) ) {
268
+                $params = array(
269
+                    'include_hidden' => false,
270
+                    'recursive'      => true,
271
+                );
272
+
273
+                $fonts = Redux_Core::$filesystem->execute( 'dirlist', $this->upload_dir, $params );
274
+
275
+                if ( ! empty( $fonts ) ) {
276
+                    foreach ( $fonts as $section ) {
277
+                        if ( 'd' === $section['type'] && ! empty( $section['name'] ) ) {
278
+                            if ( 'custom' === $section['name'] ) {
279
+                                $section['name'] = esc_html__( 'Custom Fonts', 'redux-framework' );
280
+                            }
281
+
282
+                            if ( empty( $section['files'] ) ) {
283
+                                continue;
284
+                            }
285
+
286
+                            $this->custom_fonts[ $section['name'] ] = $this->custom_fonts[ $section['name'] ] ?? array();
287
+
288
+                            foreach ( $section['files'] as $font ) {
289
+                                if ( ! empty( $font['name'] ) ) {
290
+                                    if ( empty( $font['files'] ) ) {
291
+                                        continue;
292
+                                    }
293
+
294
+                                    $kinds = array();
295
+
296
+                                    foreach ( $font['files'] as $f ) {
297
+                                        $valid = $this->check_font_name( $f );
298
+                                        if ( $valid ) {
299
+                                            $kinds[] = $valid;
300
+                                        }
301
+                                    }
302
+
303
+                                    $this->custom_fonts[ $section['name'] ][ $font['name'] ] = $kinds;
304
+                                }
305
+                            }
306
+                        }
307
+                    }
308
+                }
309
+            }
310
+        }
311
+
312
+        /**
313
+         * Add custom fonts.
314
+         *
315
+         * @param mixed $custom_fonts Custom fonts.
316
+         *
317
+         * @return array
318
+         */
319
+        public function add_custom_fonts( $custom_fonts ): array {
320
+            if ( empty( $custom_fonts ) ) {
321
+                $custom_fonts = array();
322
+            }
323
+
324
+            return wp_parse_args( $custom_fonts, $this->custom_fonts );
325
+        }
326
+
327
+        /**
328
+         * Ajax used within the panel to add and process the fonts
329
+         */
330
+        public function ajax() {
331
+            if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_key( wp_unslash( $_POST['nonce'] ) ), 'redux_custom_fonts' ) ) {
332
+                die( 0 );
333
+            }
334
+
335
+            if ( isset( $_POST['type'] ) && 'delete' === $_POST['type'] ) {
336
+                if ( isset( $_POST['section'] ) ) {
337
+                    if ( esc_html__( 'Custom Fonts', 'redux-framework' ) === $_POST['section'] ) {
338
+                        $_POST['section'] = 'custom';
339
+                    }
340
+                }
341
+
342
+                try {
343
+                    if ( isset( $_POST['section'] ) || isset( $_POST['name'] ) ) {
344
+                        $ret = Redux_Core::$filesystem->execute( 'rmdir', $this->upload_dir . sanitize_file_name( wp_unslash( $_POST['section'] ) ) . '/' . sanitize_file_name( wp_unslash( $_POST['name'] ) ) . '/', array( 'recursive' => true ) );
345
+
346
+                        if ( true === $ret ) {
347
+                            $result = array( 'type' => 'success' );
348
+                        } else {
349
+                            $result = array(
350
+                                'type' => 'error',
351
+                                'msg'  => esc_html__( 'File system failure. Could not delete temp dir.', 'redux-framework' ),
352
+                            );
353
+                        }
354
+
355
+                        echo wp_json_encode( $result );
356
+                    }
357
+                } catch ( Exception $e ) {
358
+                    echo wp_json_encode(
359
+                        array(
360
+                            'type' => 'error',
361
+                            'msg'  => esc_html__( 'Unable to delete font file(s).', 'redux-framework' ),
362
+                        )
363
+                    );
364
+                }
365
+
366
+                die();
367
+            }
368
+
369
+            if ( ! isset( $_POST['title'] ) ) {
370
+                $_POST['title'] = '';
371
+            }
372
+
373
+            if ( ! isset( $_POST['filename'] ) ) {
374
+                $_POST['filename'] = '';
375
+            }
376
+
377
+            $this->font_folder   = sanitize_file_name( wp_unslash( $_POST['title'] ) );
378
+            $this->font_filename = sanitize_file_name( wp_unslash( $_POST['filename'] ) );
379
+
380
+            if ( ! empty( $_POST['attachment_id'] ) ) {
381
+                if ( isset( $_POST['title'] ) || isset( $_POST['mime'] ) ) {
382
+                    $msg = $this->process_web_font( sanitize_key( wp_unslash( $_POST['attachment_id'] ) ), sanitize_text_field( wp_unslash( $_POST['mime'] ) ) );
383
+
384
+                    if ( empty( $msg ) ) {
385
+                        $msg = '';
386
+                    }
387
+
388
+                    $result = array(
389
+                        'type' => 'success',
390
+                        'msg'  => $msg,
391
+                    );
392
+
393
+                    echo wp_json_encode( $result );
394
+                }
395
+            }
396
+
397
+            die();
398
+        }
399
+
400
+        /**
401
+         * Get only valid files. Ensure everything is proper for processing.
402
+         *
403
+         * @param string $path Path.
404
+         *
405
+         * @return array
406
+         */
407
+        public function get_valid_files( string $path ): array {
408
+            $output = array();
409
+            $path   = trailingslashit( $path );
410
+
411
+            $params = array(
412
+                'include_hidden' => false,
413
+                'recursive'      => true,
414
+            );
415
+
416
+            $files = Redux_Core::$filesystem->execute( 'dirlist', $path, $params );
417
+
418
+            foreach ( $files as $file ) {
419
+                if ( 'd' === $file['type'] ) {
420
+                    $output = array_merge( $output, $this->get_valid_files( $path . $file['name'] ) );
421
+                } elseif ( 'f' === $file['type'] ) {
422
+                    $valid = $this->check_font_name( $file );
423
+                    if ( $valid ) {
424
+                        $output[ $valid ] = trailingslashit( $path ) . $file['name'];
425
+                    }
426
+                }
427
+            }
428
+
429
+            return $output;
430
+        }
431
+
432
+        /**
433
+         * Take a valid web font and process the missing pieces.
434
+         *
435
+         * @param string $attachment_id ID.
436
+         * @param string $mime_type     Mine type.
437
+         */
438
+        public function process_web_font( string $attachment_id, string $mime_type ) {
439
+            // phpcs:ignore WordPress.Security.NonceVerification
440
+            if ( ! isset( $_POST['conversion'] ) ) {
441
+                $_POST['conversion'] = 'false';
442
+            }
443
+
444
+            // phpcs:ignore WordPress.Security.NonceVerification
445
+            $conversion = sanitize_text_field( wp_unslash( $_POST['conversion'] ) );
446
+
447
+            $missing = array();
448
+
449
+            $complete = array(
450
+                'ttf',
451
+                'woff',
452
+                'woff2',
453
+                'eot',
454
+                'svg',
455
+                'otf',
456
+            );
457
+
458
+            $subtype = explode( '/', $mime_type );
459
+            $subtype = trim( max( $subtype ) );
460
+
461
+            if ( ! is_dir( $this->upload_dir ) ) {
462
+                Redux_Core::$filesystem->execute( 'mkdir', $this->upload_dir );
463
+            }
464
+
465
+            if ( ! is_dir( $this->upload_dir . $this->subfolder ) ) {
466
+                Redux_Core::$filesystem->execute( 'mkdir', $this->upload_dir . $this->subfolder );
467
+            }
468
+
469
+            $temp                = $this->upload_dir . 'temp';
470
+            $this->selected_file = get_attached_file( $attachment_id );
471
+
472
+            if ( empty( $this->selected_file ) ) {
473
+                echo wp_json_encode(
474
+                    array(
475
+                        'type' => 'error',
476
+                        'msg'  => esc_html__( 'Attachment does not exist.', 'redux-framework' ),
477
+                    )
478
+                );
479
+
480
+                die();
481
+            }
482
+
483
+            $filename = explode( '/', $this->selected_file );
484
+
485
+            $filename = $filename[ ( count( $filename ) - 1 ) ];
486
+
487
+            $fontname = ucfirst(
488
+                str_replace(
489
+                    array(
490
+                        '.zip',
491
+                        '.ttf',
492
+                        '.woff',
493
+                        '.woff2',
494
+                        '.eot',
495
+                        '.svg',
496
+                        '.otf',
497
+                    ),
498
+                    '',
499
+                    strtolower( $filename )
500
+                )
501
+            );
502
+
503
+            if ( empty( $this->font_folder ) ) {
504
+                $this->font_folder = $fontname;
505
+            }
506
+
507
+            $ret = array();
508
+
509
+            if ( ! is_dir( $temp ) ) {
510
+                Redux_Core::$filesystem->execute( 'mkdir', $temp );
511
+            }
512
+
513
+            if ( 'zip' === $subtype ) {
514
+                $unzipfile = unzip_file( $this->selected_file, $temp );
515
+
516
+                if ( is_wp_error( $unzipfile ) ) {
517
+                    echo wp_json_encode(
518
+                        array(
519
+                            'type' => 'error',
520
+                            'msg'  => $unzipfile->get_error_message() . '<br><br>' . esc_html__( 'Unzipping failed.', 'redux-framework' ),
521
+                        )
522
+                    );
523
+
524
+                    die();
525
+                }
526
+
527
+                $output = $this->get_valid_files( $temp );
528
+
529
+                if ( ! empty( $output ) ) {
530
+                    foreach ( $complete as $test ) {
531
+                        if ( ! isset( $output[ $test ] ) ) {
532
+                            $missing[] = $test;
533
+                        }
534
+                    }
535
+
536
+                    if ( ! is_dir( $this->upload_dir . $this->subfolder . $this->font_folder . '/' ) ) {
537
+                        Redux_Core::$filesystem->execute( 'mkdir', $this->upload_dir . $this->subfolder . $this->font_folder . '/' );
538
+                    }
539
+
540
+                    foreach ( $output as $key => $value ) {
541
+                        $param_array = array(
542
+                            'destination' => $this->upload_dir . $this->subfolder . $this->font_folder . '/' . $fontname . '.' . $key,
543
+                            'overwrite'   => true,
544
+                            'chmod'       => 755,
545
+                        );
546
+
547
+                        Redux_Core::$filesystem->execute( 'copy', $value, $param_array );
548
+                    }
549
+
550
+                    if ( true === $this->can_convert && 'true' === $conversion ) {
551
+                        $ret = $this->get_missing_files( $fontname, $missing, $output );
552
+                    }
553
+                }
554
+
555
+                Redux_Core::$filesystem->execute( 'rmdir', $temp, array( 'recursive' => true ) );
556
+
557
+                $this->generate_css();
558
+
559
+                wp_delete_attachment( $attachment_id, true );
560
+            } elseif ( 'svg+xml' === $subtype || 'vnd.ms-fontobject' === $subtype || 'x-font-ttf' === $subtype || 'ttf' === $subtype || 'otf' === $subtype || 'font-woff' === $subtype || 'font-woff2' === $subtype || 'application-octet-stream' === $subtype || 'octet-stream' === $subtype ) {
561
+                foreach ( $complete as $test ) {
562
+                    if ( $subtype !== $test ) {
563
+                        if ( ! isset( $output[ $test ] ) ) {
564
+                            $missing[] = $test;
565
+                        }
566
+                    }
567
+                }
568
+
569
+                if ( ! is_dir( $this->upload_dir . $this->subfolder . $this->font_folder . '/' ) ) {
570
+                    Redux_Core::$filesystem->execute( 'mkdir', $this->upload_dir . $this->subfolder . $this->font_folder . '/' );
571
+                }
572
+
573
+                $output = array( $subtype => $this->selected_file );
574
+
575
+                if ( true === $this->can_convert && 'true' === $conversion ) {
576
+                    $ret = $this->get_missing_files( $fontname, $missing, $output );
577
+
578
+                    if ( false === $ret ) {
579
+                        if ( false === $this->convert_local_font() ) {
580
+                            echo wp_json_encode(
581
+                                array(
582
+                                    'type' => 'error',
583
+                                    'msg'  => esc_html__( 'File permission error. Local file could not be installed.', 'redux-framework' ) . ' ' . $subtype,
584
+                                )
585
+                            );
586
+
587
+                            die;
588
+                        }
589
+                    }
590
+                } elseif ( false === $this->convert_local_font() ) {
591
+                        echo wp_json_encode(
592
+                            array(
593
+                                'type' => 'error',
594
+                                'msg'  => esc_html__( 'File permission error. Local file could not be installed.', 'redux-framework' ) . ' ' . $subtype,
595
+                            )
596
+                        );
597
+
598
+                        die;
599
+                }
600
+
601
+                Redux_Core::$filesystem->execute( 'rmdir', $temp, array( 'recursive' => true ) );
602
+
603
+                $this->generate_css();
604
+
605
+                wp_delete_attachment( $attachment_id, true );
606
+            } else {
607
+                echo wp_json_encode(
608
+                    array(
609
+                        'type' => 'error',
610
+                        'msg'  => esc_html__( 'File type not recognized.', 'redux-framework' ) . ' ' . $subtype,
611
+                    )
612
+                );
613
+
614
+                die();
615
+            }
616
+
617
+            if ( is_array( $ret ) && ! empty( $ret ) ) {
618
+                $msg = esc_html__( 'Unidentified error.', 'redux-framework' );
619
+
620
+                if ( isset( $ret['msg'] ) ) {
621
+                    $msg = $ret['msg'];
622
+                }
623
+
624
+                return $msg;
625
+            }
626
+
627
+            return '';
628
+        }
629
+
630
+        /**
631
+         * Install selected file into Custom Fonts.
632
+         *
633
+         * @return bool
634
+         */
635
+        private function convert_local_font(): bool {
636
+            $param_array = array(
637
+                'destination' => $this->upload_dir . $this->subfolder . '/' . $this->font_folder . '/' . $this->font_filename,
638
+                'overwrite'   => true,
639
+                'chmod'       => 755,
640
+            );
641
+
642
+            return Redux_Core::$filesystem->execute( 'copy', $this->selected_file, $param_array );
643
+        }
644
+
645
+        /**
646
+         * Ping the WebFontOMatic API to get the missing files.
647
+         *
648
+         * @param string $fontname  Font name.
649
+         * @param array  $missing   Missing.
650
+         * @param array  $output    Output.
651
+         */
652
+        private function get_missing_files( string $fontname, array $missing, array $output ) {
653
+            if ( ! empty( $this->font_folder ) && ! empty( $missing ) ) {
654
+                $temp = $this->upload_dir . 'temp';
655
+
656
+                $font_ext = pathinfo( $this->font_filename, PATHINFO_EXTENSION );
657
+
658
+                $unsupported = array( 'eot', 'woff', 'woff2' );
659
+
660
+                // Find a file to convert from.
661
+                foreach ( $output as $key => $value ) {
662
+                    if ( 'eot' === $key ) {
663
+                        continue;
664
+                    } else {
665
+                        $main = $key;
666
+                        break;
667
+                    }
668
+                }
669
+
670
+                if ( ! isset( $main ) ) {
671
+                    echo wp_json_encode(
672
+                        array(
673
+                            'type' => 'error',
674
+                            'msg'  => esc_html__( 'No valid font file was found.', 'redux-framework' ),
675
+                        )
676
+                    );
677
+
678
+                    Redux_Core::$filesystem->execute( 'rmdir', $temp, array( 'recursive' => true ) );
679
+                    Redux_Core::$filesystem->execute( 'rmdir', $this->upload_dir . $this->subfolder . $this->font_folder . '/', array( 'recursive' => true ) );
680
+
681
+                    die();
682
+                }
683
+
684
+                update_option( 'redux_custom_font_current', $this->font_folder . '.zip' );
685
+
686
+                $boundary = wp_generate_password( 24 );
687
+
688
+                $headers = array(
689
+                    'content-type' => 'multipart/form-data; boundary=' . $boundary,
690
+                    'user-agent'   => 'redux-custom-fonts-' . self::$version . ' using ' . wp_get_theme(),
691
+                );
692
+
693
+                $payload  = '--' . $boundary;
694
+                $payload .= "\r\n";
695
+                $payload .= 'Content-Disposition: form-data; name="md5"' . "\r\n\r\n";
696
+                $payload .= md5( 'redux_custom_font' );
697
+                $payload .= "\r\n";
698
+
699
+                if ( $output[ $main ] ) {
700
+                    $payload .= '--' . $boundary;
701
+                    $payload .= "\r\n";
702
+                    $payload .= 'Content-Disposition: form-data; name="convert"; filename="' . basename( $output[ $main ] ) . '"' . "\r\n";
703
+                    $payload .= "\r\n";
704
+                    $payload .= Redux_Core::$filesystem->execute( 'get_contents', $output[ $main ] );
705
+                    $payload .= "\r\n";
706
+                }
707
+
708
+                $payload .= '--' . $boundary . '--';
709
+
710
+                $args = array(
711
+                    'headers'    => $headers,
712
+                    'body'       => $payload,
713
+                    'user-agent' => $headers['user-agent'],
714
+                    'timeout'    => 300,
715
+                    'sslverify'  => true,
716
+                );
717
+
718
+                // phpcs:disable WordPress.NamingConventions.ValidHookName
719
+                $api_url = apply_filters( 'redux/' . $this->parent->args['opt_name'] . '/extensions/custom_fonts/api_url', 'https://redux.io/fonts' );
720
+
721
+                $response = wp_remote_post( $api_url, $args );
722
+
723
+                if ( is_wp_error( $response ) ) {
724
+                    return array(
725
+                        'type' => 'error',
726
+                        'msg'  => $response->get_error_message() . '<br><br>' . esc_html__( 'Your font could not be converted at this time. Please try again later.', 'redux-framework' ),
727
+                    );
728
+                } elseif ( isset( $response['body'] ) ) {
729
+                    if ( null !== json_decode( $response['body'] ) ) {
730
+                        return json_decode( $response['body'], true );
731
+                    }
732
+                }
733
+
734
+                $param_array = array(
735
+                    'content'   => $response['body'],
736
+                    'overwrite' => true,
737
+                    'chmod'     => FS_CHMOD_FILE,
738
+                );
739
+
740
+                $zip_file = $temp . DIRECTORY_SEPARATOR . $fontname . '.zip';
741
+
742
+                Redux_Core::$filesystem->execute( 'put_contents', $zip_file, $param_array );
743
+
744
+                if ( 0 === filesize( $zip_file ) ) {
745
+                    return false;
746
+                }
747
+
748
+                $zip = unzip_file( $zip_file, $temp );
749
+
750
+                if ( ! is_wp_error( $zip ) ) {
751
+                    $params = array(
752
+                        'include_hidden' => false,
753
+                        'recursive'      => false,
754
+                    );
755
+
756
+                    $files = Redux_Core::$filesystem->execute( 'dirlist', $temp . DIRECTORY_SEPARATOR . 'fonts' . DIRECTORY_SEPARATOR, $params );
757
+
758
+                    foreach ( $files as $file ) {
759
+                        $param_array = array(
760
+                            'destination' => $this->upload_dir . $this->subfolder . $this->font_folder . DIRECTORY_SEPARATOR . sanitize_file_name( $file['name'] ),
761
+                            'overwrite'   => true,
762
+                            'chmod'       => 755,
763
+                        );
764
+
765
+                        Redux_Core::$filesystem->execute( 'move', $temp . DIRECTORY_SEPARATOR . 'fonts' . DIRECTORY_SEPARATOR . ( $file['name'] ), $param_array );
766
+                    }
767
+                } else {
768
+                    $path_parts = pathinfo( $output[ $main ] );
769
+
770
+                    $param_array = array(
771
+                        'destination' => $this->upload_dir . $this->subfolder . $this->font_folder . DIRECTORY_SEPARATOR . sanitize_file_name( $path_parts['basename'] ),
772
+                        'overwrite'   => true,
773
+                        'chmod'       => 755,
774
+                    );
775
+
776
+                    Redux_Core::$filesystem->execute( 'move', $output[ $main ], $param_array );
777
+
778
+                    if ( in_array( $font_ext, $unsupported, true ) ) {
779
+                        return array(
780
+                            'type' => 'error',
781
+                            // translators: %s = font extension.
782
+                            'msg'  => $zip->get_error_message() . '<br><br>' . sprintf( esc_html__( 'The font converter does not support %s fonts.', 'redux-framework' ), $font_ext ),
783
+                        );
784
+                    } else {
785
+                        return array(
786
+                            'type' => 'error',
787
+                            'msg'  => $zip->get_error_message() . '<br><br>' . esc_html__( 'ZIP error. Your font could not be converted at this time. Please try again later.', 'redux-framework' ),
788
+                        );
789
+                    }
790
+                }
791
+
792
+                delete_option( 'redux_custom_font_current' );
793
+            }
794
+
795
+            return true;
796
+        }
797
+
798
+        /**
799
+         * Check if the file name is a valid font file.
800
+         *
801
+         * @param array $file File.
802
+         *
803
+         * @return bool|string
804
+         */
805
+        private function check_font_name( array $file ) {
806
+            if ( '.woff' === strtolower( substr( $file['name'], - 5 ) ) ) {
807
+                return 'woff';
808
+            }
809
+
810
+            if ( '.woff2' === strtolower( substr( $file['name'], - 6 ) ) ) {
811
+                return 'woff2';
812
+            }
813
+
814
+            $sub = strtolower( substr( $file['name'], - 4 ) );
815
+
816
+            if ( '.ttf' === $sub ) {
817
+                return 'ttf';
818
+            }
819
+
820
+            if ( '.eot' === $sub ) {
821
+                return 'eot';
822
+            }
823
+
824
+            if ( '.svg' === $sub ) {
825
+                return 'svg';
826
+            }
827
+
828
+            if ( '.otf' === $sub ) {
829
+                return 'otf';
830
+            }
831
+
832
+            return false;
833
+        }
834
+
835
+        /**
836
+         * Generate a new custom CSS file for enqueuing on the frontend and backend.
837
+         */
838
+        private function generate_css() {
839
+            $params = array(
840
+                'include_hidden' => false,
841
+                'recursive'      => true,
842
+            );
843
+
844
+            $fonts = Redux_Core::$filesystem->execute( 'dirlist', $this->upload_dir . 'custom' . DIRECTORY_SEPARATOR, $params );
845
+
846
+            if ( empty( $fonts ) || ! is_array( $fonts ) ) {
847
+                return;
848
+            }
849
+
850
+            foreach ( $fonts as $font ) {
851
+                if ( 'd' === $font['type'] ) {
852
+                    break;
853
+                }
854
+
855
+                if ( file_exists( $this->upload_dir . 'fonts.css' ) ) {
856
+                    Redux_Core::$filesystem->execute( 'delete', $this->upload_dir . 'fonts.css' );
857
+                }
858
+
859
+                return;
860
+            }
861
+
862
+            $css = '';
863
+
864
+            foreach ( $fonts as $font ) {
865
+                if ( 'd' === $font['type'] ) {
866
+                    $css .= $this->generate_font_css( $font['name'], $this->upload_dir . 'custom' . DIRECTORY_SEPARATOR );
867
+                }
868
+            }
869
+
870
+            if ( '' !== $css ) {
871
+                $param_array = array(
872
+                    'content' => $css,
873
+                    'chmod'   => FS_CHMOD_FILE,
874
+                );
875
+
876
+                Redux_Core::$filesystem->execute( 'put_contents', $this->upload_dir . 'fonts.css', $param_array );
877
+            }
878
+        }
879
+
880
+        /**
881
+         * Process to actually construct the custom font css file.
882
+         *
883
+         * @param string $name Name.
884
+         * @param string $dir  Directory.
885
+         *
886
+         * @return string
887
+         */
888
+        private function generate_font_css( string $name, string $dir ): ?string {
889
+            $path = $dir . $name;
890
+
891
+            $params = array(
892
+                'include_hidden' => false,
893
+                'recursive'      => true,
894
+            );
895
+
896
+            $files = Redux_Core::$filesystem->execute( 'dirlist', $path, $params );
897
+
898
+            if ( empty( $files ) ) {
899
+                return null;
900
+            }
901
+
902
+            $output = array();
903
+
904
+            foreach ( $files as $file ) {
905
+                $output[ $this->check_font_name( $file ) ] = $file['name'];
906
+            }
907
+
908
+            $css = '@font-face {';
909
+
910
+            $css .= 'font-family:"' . $name . '";';
911
+
912
+            $src = array();
913
+
914
+            if ( isset( $output['eot'] ) ) {
915
+                $src[] = "url('{$this->upload_url}custom/$name/{$output['eot']}?#iefix') format('embedded-opentype')";
916
+            }
917
+
918
+            if ( isset( $output['woff'] ) ) {
919
+                $src[] = "url('{$this->upload_url}custom/$name/{$output['woff']}') format('woff')";
920
+            }
921
+
922
+            if ( isset( $output['woff2'] ) ) {
923
+                $src[] = "url('{$this->upload_url}custom/$name/{$output['woff2']}') format('woff2')";
924
+            }
925
+
926
+            if ( isset( $output['ttf'] ) ) {
927
+                $src[] = "url('{$this->upload_url}custom/$name/{$output['ttf']}') format('truetype')";
928
+            }
929
+
930
+            if ( isset( $output['svg'] ) ) {
931
+                $src[] = "url('{$this->upload_url}custom/$name/{$output['svg']}#svg$name') format('svg')";
932
+            }
933
+
934
+            if ( ! empty( $src ) ) {
935
+                $css .= 'src:' . implode( ', ', $src ) . ';';
936
+            }
937
+
938
+            // Replace font weight and style with sub-sets.
939
+            $css .= 'font-weight: normal;';
940
+
941
+            $css .= 'font-style: normal;';
942
+
943
+            $css .= '}';
944
+
945
+            return $css;
946
+        }
947
+
948
+        /**
949
+         * Custom function for filtering the section array.
950
+         * Good for child themes to override or add to the sections.
951
+         * Simply include this function in the child themes functions.php file.
952
+         * NOTE: the defined constants for URLs and directories will NOT be available at this point in a child theme,
953
+         * so you must use get_template_directory_uri() if you want to use any of the built-in icons
954
+         */
955
+        public function add_section() {
956
+            if ( ! isset( $this->parent->fontControl ) ) {
957
+                $this->parent->sections[] = array(
958
+                    'title'  => esc_html__( 'Font Control', 'redux-framework' ),
959
+                    'desc'   => '<p class="description"></p>',
960
+                    'icon'   => 'el-icon-font',
961
+                    'id'     => 'redux_dynamic_font_control',
962
+                    // Leave this as a blank section, no options just some intro text set above.
963
+                    'fields' => array(),
964
+                );
965
+
966
+                for ( $i = count( $this->parent->sections ); $i >= 1; $i-- ) {
967
+                    if ( isset( $this->parent->sections[ $i ] ) && isset( $this->parent->sections[ $i ]['title'] ) && esc_html__( 'Font Control', 'redux-framework' ) === $this->parent->sections[ $i ]['title'] ) {
968
+                        $this->parent->fontControl                                        = $i;
969
+                        $this->parent->sections[ $this->parent->fontControl ]['fields'][] = array(
970
+                            'id'   => 'redux_font_control',
971
+                            'type' => 'custom_fonts',
972
+                        );
973
+
974
+                        break;
975
+                    }
976
+                }
977
+            }
978
+        }
979
+    }
980
+
981
+    class_alias( Redux_Extension_Custom_Fonts::class, 'ReduxFramework_Extension_custom_fonts' );
982 982
 }
Please login to merge, or discard this patch.
redux-core/inc/classes/class-redux-args.php 1 patch
Indentation   +386 added lines, -386 removed lines patch added patch discarded remove patch
@@ -11,390 +11,390 @@
 block discarded – undo
11 11
 
12 12
 if ( ! class_exists( 'Redux_Args', false ) ) {
13 13
 
14
-	/**
15
-	 * Class Redux_Args
16
-	 */
17
-	class Redux_Args {
18
-
19
-		/**
20
-		 * Returns entire arguments array.
21
-		 *
22
-		 * @var array|mixed
23
-		 */
24
-		public $get = array();
25
-
26
-		/**
27
-		 * ReduxFramework object.
28
-		 *
29
-		 * @var ReduxFramework|null
30
-		 */
31
-		private ?ReduxFramework $parent;
32
-
33
-		/**
34
-		 * Switch to omit social icons if dev_mode is set to true and Redux defaults are used.
35
-		 *
36
-		 * @var bool
37
-		 */
38
-		public bool $omit_icons = false;
39
-
40
-		/**
41
-		 * Switch to omit support menu items if dev_mode is set to true and redux defaults are used.
42
-		 *
43
-		 * @var bool
44
-		 */
45
-		public bool $omit_items = false;
46
-
47
-		/**
48
-		 * Flag to force dev_mod to true if in localhost or WP_DEBUG is set to true.
49
-		 *
50
-		 * @var bool
51
-		 */
52
-		public bool $dev_mode_forced = false;
53
-
54
-		/**
55
-		 * Redux_Args constructor.
56
-		 *
57
-		 * @param ReduxFramework $redux ReduxFramework object.
58
-		 * @param array          $args  Global arguments array.
59
-		 */
60
-		public function __construct( ReduxFramework $redux, array $args ) {
61
-			$this->parent = $redux;
62
-
63
-			$default = array(
64
-				'opt_name'                         => '',
65
-				'last_tab'                         => '',
66
-				'menu_icon'                        => '',
67
-				'menu_title'                       => '',
68
-				'page_title'                       => '',
69
-				'page_slug'                        => '',
70
-				'page_permissions'                 => 'manage_options',
71
-				'menu_type'                        => 'menu',
72
-				'page_parent'                      => 'themes.php',
73
-				'page_priority'                    => null,
74
-				'allow_sub_menu'                   => true,
75
-				'save_defaults'                    => true,
76
-				'footer_credit'                    => '',
77
-				'async_typography'                 => false,
78
-				'disable_google_fonts_link'        => false,
79
-				'class'                            => '',
80
-				'admin_bar'                        => true,
81
-				'admin_bar_priority'               => 999,
82
-				'admin_bar_icon'                   => '',
83
-				'help_tabs'                        => array(),
84
-				'help_sidebar'                     => '',
85
-				'database'                         => '',
86
-				'customizer'                       => false,
87
-				'global_variable'                  => '',
88
-				'output'                           => true,
89
-				'output_variables_prefix'          => '--',
90
-				'compiler_output_variables_prefix' => '$',
91
-				'compiler'                         => true,
92
-				'output_tag'                       => true,
93
-				'output_location'                  => array( 'frontend' ),
94
-				'transient_time'                   => '',
95
-				'default_show'                     => false,
96
-				'default_mark'                     => '',
97
-				'disable_save_warn'                => false,
98
-				'open_expanded'                    => false,
99
-				'hide_expand'                      => false,
100
-				'network_admin'                    => false,
101
-				'network_sites'                    => true,
102
-				'hide_reset'                       => false,
103
-				'hide_save'                        => false,
104
-				'hints'                            => array(
105
-					'icon'          => 'el el-question-sign',
106
-					'icon_position' => 'right',
107
-					'icon_color'    => 'lightgray',
108
-					'icon_size'     => 'normal',
109
-					'tip_style'     => array(
110
-						'color'   => 'light',
111
-						'shadow'  => true,
112
-						'rounded' => false,
113
-						'style'   => '',
114
-					),
115
-					'tip_position'  => array(
116
-						'my' => 'top_left',
117
-						'at' => 'bottom_right',
118
-					),
119
-					'tip_effect'    => array(
120
-						'show' => array(
121
-							'effect'   => 'slide',
122
-							'duration' => '500',
123
-							'event'    => 'mouseover',
124
-						),
125
-						'hide' => array(
126
-							'effect'   => 'fade',
127
-							'duration' => '500',
128
-							'event'    => 'click mouseleave',
129
-						),
130
-					),
131
-				),
132
-				'font_weights'                     => array(
133
-					array(
134
-						'id'   => '400',
135
-						'name' => __( 'Regular 400', 'redux-framework' ),
136
-					),
137
-					array(
138
-						'id'   => '400italic',
139
-						'name' => __( 'Regular 400 Italic', 'redux-framework' ),
140
-					),
141
-					array(
142
-						'id'   => '700',
143
-						'name' => __( 'Bold 700', 'redux-framework' ),
144
-					),
145
-					array(
146
-						'id'   => '700italic',
147
-						'name' => __( 'Bold 700 Italic', 'redux-framework' ),
148
-					),
149
-				),
150
-				'show_import_export'               => true,
151
-				'show_options_object'              => true,
152
-				'dev_mode'                         => true,
153
-				'templates_path'                   => '',
154
-				'ajax_save'                        => true,
155
-				'use_cdn'                          => true,
156
-				'cdn_check_time'                   => 1440,
157
-				'options_api'                      => true,
158
-				'allow_tracking'                   => true,
159
-				'admin_theme'                      => 'wp',
160
-				'elusive_frontend'                 => false,
161
-				'fontawesome_frontend'             => false,
162
-				'flyout_submenus'                  => true,
163
-				'font_display'                     => 'swap', // block|swap|fallback|optional.
164
-				'load_on_cron'                     => false,
165
-				'search'                           => false,
166
-				'widget_area'                      => false,
167
-				'custom_fonts'                     => true,
168
-			);
169
-
170
-			$args = Redux_Functions::parse_args( $args, $default );
171
-
172
-			$args = $this->args( $args );
173
-
174
-			$args = $this->default_cleanup( $args );
175
-
176
-			if ( ! in_array( $args['font_display'], array( 'block', 'swap', 'fallback', 'optional' ), true ) ) {
177
-				$args['font_display'] = 'swap';
178
-			}
179
-
180
-			if ( isset( $args['async_typography'] ) && $args['async_typography'] ) {
181
-				$args['async_typography'] = false;
182
-			}
183
-
184
-			$this->get = $args;
185
-
186
-			$this->parent->args = $args;
187
-
188
-			if ( 'redux_extensions_demo' !== $args['opt_name'] && 'redux_demo' !== $args['opt_name'] ) {
189
-				$this->change_demo_defaults( $args );
190
-			}
191
-		}
192
-
193
-		/**
194
-		 * Builds and sanitizes a global args array.
195
-		 *
196
-		 * @param     array $args Global args.
197
-		 *
198
-		 * @return array
199
-		 */
200
-		private function args( array $args ): array {
201
-			$args = $this->no_errors_please( $args );
202
-
203
-			$this->parent->old_opt_name = $args['opt_name'];
204
-
205
-			$args = $this->filters( $args );
206
-
207
-			if ( ! function_exists( 'wp_rand' ) ) {
208
-				require_once ABSPATH . '/wp-includes/pluggable.php';
209
-			}
210
-
211
-			if ( $args['opt_name'] === $this->parent->old_opt_name ) {
212
-				$this->parent->old_opt_name = null;
213
-				unset( $this->parent->old_opt_name );
214
-			}
215
-
216
-			// Do not save the defaults if we're on a live preview!
217
-			if ( 'customize' === $GLOBALS['pagenow'] && isset( $_GET['customize_theme'] ) && ! empty( $_GET['customize_theme'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
218
-				$args['save_defaults'] = false;
219
-			}
220
-
221
-			return $this->shim( $args );
222
-		}
223
-
224
-		/**
225
-		 * Apply filters to arg data.
226
-		 *
227
-		 * @param     array $args Global args.
228
-		 *
229
-		 * @return mixed|void
230
-		 */
231
-		private function filters( array $args ) {
232
-			/**
233
-			 * Filter 'redux/args/{opt_name}'
234
-			 *
235
-			 * @param     array     $args ReduxFramework configuration
236
-			 */
237
-
238
-			// phpcs:ignore WordPress.NamingConventions.ValidHookName
239
-			$args = apply_filters( "redux/args/{$args['opt_name']}", $args );
240
-
241
-			/**
242
-			 * Filter 'redux/options/{opt_name}/args'
243
-			 *
244
-			 * @param     array     $args ReduxFramework configuration
245
-			 */
246
-
247
-			// phpcs:ignore WordPress.NamingConventions.ValidHookName
248
-			return apply_filters( "redux/options/{$args['opt_name']}/args", $args );
249
-		}
250
-
251
-		/**
252
-		 * Sanitize args that should not be empty.
253
-		 *
254
-		 * @param     array $args Global args.
255
-		 *
256
-		 * @return array
257
-		 */
258
-		private function no_errors_please( array $args ): array {
259
-			if ( empty( $args['transient_time'] ) ) {
260
-				$args['transient_time'] = 60 * MINUTE_IN_SECONDS;
261
-			}
262
-
263
-			if ( empty( $args['footer_credit'] ) ) {
264
-
265
-				$footer_text = sprintf(
266
-				/* translators: 1: Redux, 2: Link to plugin review */
267
-					__( 'Enjoyed %1$s? Please leave us a %2$s rating. We really appreciate your support!', 'redux-framework' ),
268
-					'<strong>' . __( 'Redux', 'redux-framework' ) . '</strong>',
269
-					'<a href="https://wordpress.org/support/plugin/redux-framework/reviews/?filter=5/#new-post" target="_blank">&#9733;&#9733;&#9733;&#9733;&#9733;</a>'
270
-				);
271
-				$args['footer_credit'] = '<span id="footer-thankyou">' . $footer_text . '</span>';
272
-			}
273
-
274
-			if ( empty( $args['menu_title'] ) ) {
275
-				$args['menu_title'] = esc_html__( 'Options', 'redux-framework' );
276
-			}
277
-
278
-			if ( empty( $args['page_title'] ) ) {
279
-				$args['page_title'] = esc_html__( 'Options', 'redux-framework' );
280
-			}
281
-
282
-			// Auto creates the page_slug appropriately.
283
-			if ( empty( $args['page_slug'] ) ) {
284
-				if ( ! empty( $args['display_name'] ) ) {
285
-					$args['page_slug'] = sanitize_html_class( $args['display_name'] );
286
-				} elseif ( ! empty( $args['page_title'] ) ) {
287
-					$args['page_slug'] = sanitize_html_class( $args['page_title'] );
288
-				} elseif ( ! empty( $args['menu_title'] ) ) {
289
-					$args['page_slug'] = sanitize_html_class( $args['menu_title'] );
290
-				} else {
291
-					$args['page_slug'] = str_replace( '-', '_', $args['opt_name'] );
292
-				}
293
-			}
294
-
295
-			return $args;
296
-		}
297
-
298
-		/**
299
-		 * Shims for much older v3 configs.
300
-		 *
301
-		 * @param     array $args Global args.
302
-		 *
303
-		 * @return array
304
-		 */
305
-		private function shim( array $args ): array {
306
-			/**
307
-			 * SHIM SECTION
308
-			 * Old variables and ways of doing things that need correcting.  ;)
309
-			 * */
310
-			// Variable name change.
311
-			if ( ! empty( $args['page_cap'] ) ) {
312
-				$args['page_permissions'] = $args['page_cap'];
313
-				unset( $args['page_cap'] );
314
-			}
315
-
316
-			if ( ! empty( $args['page_position'] ) ) {
317
-				$args['page_priority'] = $args['page_position'];
318
-				unset( $args['page_position'] );
319
-			}
320
-
321
-			if ( ! empty( $args['page_type'] ) ) {
322
-				$args['menu_type'] = $args['page_type'];
323
-				unset( $args['page_type'] );
324
-			}
325
-
326
-			return $args;
327
-		}
328
-
329
-		/**
330
-		 * Verify to see if dev has bothered to change admin bar links and share icons from demo data to their own.
331
-		 *
332
-		 * @param array $args Global args.
333
-		 */
334
-		private function change_demo_defaults( array $args ) {
335
-			if ( $args['dev_mode'] || true === Redux_Helpers::is_local_host() ) {
336
-				if ( ! empty( $args['admin_bar_links'] ) ) {
337
-					foreach ( $args['admin_bar_links'] as $arr ) {
338
-						if ( is_array( $arr ) && ! empty( $arr ) ) {
339
-							foreach ( $arr as $y ) {
340
-								if ( strpos( Redux_Core::strtolower( $y ), 'redux' ) !== false ) {
341
-									$this->omit_items = true;
342
-									break;
343
-								}
344
-							}
345
-						}
346
-					}
347
-				}
348
-
349
-				if ( ! empty( $args['share_icons'] ) ) {
350
-					foreach ( $args['share_icons'] as $arr ) {
351
-						if ( is_array( $arr ) && ! empty( $arr ) ) {
352
-							foreach ( $arr as $y ) {
353
-								if ( strpos( Redux_Core::strtolower( $y ), 'redux' ) !== false ) {
354
-									$this->omit_icons = true;
355
-								}
356
-							}
357
-						}
358
-					}
359
-				}
360
-			}
361
-		}
362
-
363
-		/**
364
-		 * Fix other arg criteria that sometimes gets hosed up.
365
-		 *
366
-		 * @param array $args Global args.
367
-		 *
368
-		 * @return array
369
-		 * @noinspection PhpStrictComparisonWithOperandsOfDifferentTypesInspection
370
-		 */
371
-		private function default_cleanup( array $args ): array {
372
-
373
-			// Fix the global variable name.
374
-			if ( '' === $args['global_variable'] && false !== $args['global_variable'] ) {
375
-				$args['global_variable'] = str_replace( '-', '_', $args['opt_name'] );
376
-			}
377
-
378
-			if ( isset( $args['customizer_only'] ) && $args['customizer_only'] ) {
379
-				$args['menu_type']      = 'hidden';
380
-				$args['customizer']     = true;
381
-				$args['admin_bar']      = false;
382
-				$args['allow_sub_menu'] = false;
383
-			}
384
-
385
-			// Check if the Airplane Mode plugin is installed.
386
-			if ( class_exists( 'Airplane_Mode_Core' ) ) {
387
-				$airplane = Airplane_Mode_Core::getInstance();
388
-				if ( method_exists( $airplane, 'enabled' ) ) {
389
-					if ( $airplane->enabled() ) {
390
-						$args['use_cdn'] = false;
391
-					}
392
-				} elseif ( 'on' === $airplane->check_status() ) {
393
-					$args['use_cdn'] = false;
394
-				}
395
-			}
396
-
397
-			return $args;
398
-		}
399
-	}
14
+    /**
15
+     * Class Redux_Args
16
+     */
17
+    class Redux_Args {
18
+
19
+        /**
20
+         * Returns entire arguments array.
21
+         *
22
+         * @var array|mixed
23
+         */
24
+        public $get = array();
25
+
26
+        /**
27
+         * ReduxFramework object.
28
+         *
29
+         * @var ReduxFramework|null
30
+         */
31
+        private ?ReduxFramework $parent;
32
+
33
+        /**
34
+         * Switch to omit social icons if dev_mode is set to true and Redux defaults are used.
35
+         *
36
+         * @var bool
37
+         */
38
+        public bool $omit_icons = false;
39
+
40
+        /**
41
+         * Switch to omit support menu items if dev_mode is set to true and redux defaults are used.
42
+         *
43
+         * @var bool
44
+         */
45
+        public bool $omit_items = false;
46
+
47
+        /**
48
+         * Flag to force dev_mod to true if in localhost or WP_DEBUG is set to true.
49
+         *
50
+         * @var bool
51
+         */
52
+        public bool $dev_mode_forced = false;
53
+
54
+        /**
55
+         * Redux_Args constructor.
56
+         *
57
+         * @param ReduxFramework $redux ReduxFramework object.
58
+         * @param array          $args  Global arguments array.
59
+         */
60
+        public function __construct( ReduxFramework $redux, array $args ) {
61
+            $this->parent = $redux;
62
+
63
+            $default = array(
64
+                'opt_name'                         => '',
65
+                'last_tab'                         => '',
66
+                'menu_icon'                        => '',
67
+                'menu_title'                       => '',
68
+                'page_title'                       => '',
69
+                'page_slug'                        => '',
70
+                'page_permissions'                 => 'manage_options',
71
+                'menu_type'                        => 'menu',
72
+                'page_parent'                      => 'themes.php',
73
+                'page_priority'                    => null,
74
+                'allow_sub_menu'                   => true,
75
+                'save_defaults'                    => true,
76
+                'footer_credit'                    => '',
77
+                'async_typography'                 => false,
78
+                'disable_google_fonts_link'        => false,
79
+                'class'                            => '',
80
+                'admin_bar'                        => true,
81
+                'admin_bar_priority'               => 999,
82
+                'admin_bar_icon'                   => '',
83
+                'help_tabs'                        => array(),
84
+                'help_sidebar'                     => '',
85
+                'database'                         => '',
86
+                'customizer'                       => false,
87
+                'global_variable'                  => '',
88
+                'output'                           => true,
89
+                'output_variables_prefix'          => '--',
90
+                'compiler_output_variables_prefix' => '$',
91
+                'compiler'                         => true,
92
+                'output_tag'                       => true,
93
+                'output_location'                  => array( 'frontend' ),
94
+                'transient_time'                   => '',
95
+                'default_show'                     => false,
96
+                'default_mark'                     => '',
97
+                'disable_save_warn'                => false,
98
+                'open_expanded'                    => false,
99
+                'hide_expand'                      => false,
100
+                'network_admin'                    => false,
101
+                'network_sites'                    => true,
102
+                'hide_reset'                       => false,
103
+                'hide_save'                        => false,
104
+                'hints'                            => array(
105
+                    'icon'          => 'el el-question-sign',
106
+                    'icon_position' => 'right',
107
+                    'icon_color'    => 'lightgray',
108
+                    'icon_size'     => 'normal',
109
+                    'tip_style'     => array(
110
+                        'color'   => 'light',
111
+                        'shadow'  => true,
112
+                        'rounded' => false,
113
+                        'style'   => '',
114
+                    ),
115
+                    'tip_position'  => array(
116
+                        'my' => 'top_left',
117
+                        'at' => 'bottom_right',
118
+                    ),
119
+                    'tip_effect'    => array(
120
+                        'show' => array(
121
+                            'effect'   => 'slide',
122
+                            'duration' => '500',
123
+                            'event'    => 'mouseover',
124
+                        ),
125
+                        'hide' => array(
126
+                            'effect'   => 'fade',
127
+                            'duration' => '500',
128
+                            'event'    => 'click mouseleave',
129
+                        ),
130
+                    ),
131
+                ),
132
+                'font_weights'                     => array(
133
+                    array(
134
+                        'id'   => '400',
135
+                        'name' => __( 'Regular 400', 'redux-framework' ),
136
+                    ),
137
+                    array(
138
+                        'id'   => '400italic',
139
+                        'name' => __( 'Regular 400 Italic', 'redux-framework' ),
140
+                    ),
141
+                    array(
142
+                        'id'   => '700',
143
+                        'name' => __( 'Bold 700', 'redux-framework' ),
144
+                    ),
145
+                    array(
146
+                        'id'   => '700italic',
147
+                        'name' => __( 'Bold 700 Italic', 'redux-framework' ),
148
+                    ),
149
+                ),
150
+                'show_import_export'               => true,
151
+                'show_options_object'              => true,
152
+                'dev_mode'                         => true,
153
+                'templates_path'                   => '',
154
+                'ajax_save'                        => true,
155
+                'use_cdn'                          => true,
156
+                'cdn_check_time'                   => 1440,
157
+                'options_api'                      => true,
158
+                'allow_tracking'                   => true,
159
+                'admin_theme'                      => 'wp',
160
+                'elusive_frontend'                 => false,
161
+                'fontawesome_frontend'             => false,
162
+                'flyout_submenus'                  => true,
163
+                'font_display'                     => 'swap', // block|swap|fallback|optional.
164
+                'load_on_cron'                     => false,
165
+                'search'                           => false,
166
+                'widget_area'                      => false,
167
+                'custom_fonts'                     => true,
168
+            );
169
+
170
+            $args = Redux_Functions::parse_args( $args, $default );
171
+
172
+            $args = $this->args( $args );
173
+
174
+            $args = $this->default_cleanup( $args );
175
+
176
+            if ( ! in_array( $args['font_display'], array( 'block', 'swap', 'fallback', 'optional' ), true ) ) {
177
+                $args['font_display'] = 'swap';
178
+            }
179
+
180
+            if ( isset( $args['async_typography'] ) && $args['async_typography'] ) {
181
+                $args['async_typography'] = false;
182
+            }
183
+
184
+            $this->get = $args;
185
+
186
+            $this->parent->args = $args;
187
+
188
+            if ( 'redux_extensions_demo' !== $args['opt_name'] && 'redux_demo' !== $args['opt_name'] ) {
189
+                $this->change_demo_defaults( $args );
190
+            }
191
+        }
192
+
193
+        /**
194
+         * Builds and sanitizes a global args array.
195
+         *
196
+         * @param     array $args Global args.
197
+         *
198
+         * @return array
199
+         */
200
+        private function args( array $args ): array {
201
+            $args = $this->no_errors_please( $args );
202
+
203
+            $this->parent->old_opt_name = $args['opt_name'];
204
+
205
+            $args = $this->filters( $args );
206
+
207
+            if ( ! function_exists( 'wp_rand' ) ) {
208
+                require_once ABSPATH . '/wp-includes/pluggable.php';
209
+            }
210
+
211
+            if ( $args['opt_name'] === $this->parent->old_opt_name ) {
212
+                $this->parent->old_opt_name = null;
213
+                unset( $this->parent->old_opt_name );
214
+            }
215
+
216
+            // Do not save the defaults if we're on a live preview!
217
+            if ( 'customize' === $GLOBALS['pagenow'] && isset( $_GET['customize_theme'] ) && ! empty( $_GET['customize_theme'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
218
+                $args['save_defaults'] = false;
219
+            }
220
+
221
+            return $this->shim( $args );
222
+        }
223
+
224
+        /**
225
+         * Apply filters to arg data.
226
+         *
227
+         * @param     array $args Global args.
228
+         *
229
+         * @return mixed|void
230
+         */
231
+        private function filters( array $args ) {
232
+            /**
233
+             * Filter 'redux/args/{opt_name}'
234
+             *
235
+             * @param     array     $args ReduxFramework configuration
236
+             */
237
+
238
+            // phpcs:ignore WordPress.NamingConventions.ValidHookName
239
+            $args = apply_filters( "redux/args/{$args['opt_name']}", $args );
240
+
241
+            /**
242
+             * Filter 'redux/options/{opt_name}/args'
243
+             *
244
+             * @param     array     $args ReduxFramework configuration
245
+             */
246
+
247
+            // phpcs:ignore WordPress.NamingConventions.ValidHookName
248
+            return apply_filters( "redux/options/{$args['opt_name']}/args", $args );
249
+        }
250
+
251
+        /**
252
+         * Sanitize args that should not be empty.
253
+         *
254
+         * @param     array $args Global args.
255
+         *
256
+         * @return array
257
+         */
258
+        private function no_errors_please( array $args ): array {
259
+            if ( empty( $args['transient_time'] ) ) {
260
+                $args['transient_time'] = 60 * MINUTE_IN_SECONDS;
261
+            }
262
+
263
+            if ( empty( $args['footer_credit'] ) ) {
264
+
265
+                $footer_text = sprintf(
266
+                /* translators: 1: Redux, 2: Link to plugin review */
267
+                    __( 'Enjoyed %1$s? Please leave us a %2$s rating. We really appreciate your support!', 'redux-framework' ),
268
+                    '<strong>' . __( 'Redux', 'redux-framework' ) . '</strong>',
269
+                    '<a href="https://wordpress.org/support/plugin/redux-framework/reviews/?filter=5/#new-post" target="_blank">&#9733;&#9733;&#9733;&#9733;&#9733;</a>'
270
+                );
271
+                $args['footer_credit'] = '<span id="footer-thankyou">' . $footer_text . '</span>';
272
+            }
273
+
274
+            if ( empty( $args['menu_title'] ) ) {
275
+                $args['menu_title'] = esc_html__( 'Options', 'redux-framework' );
276
+            }
277
+
278
+            if ( empty( $args['page_title'] ) ) {
279
+                $args['page_title'] = esc_html__( 'Options', 'redux-framework' );
280
+            }
281
+
282
+            // Auto creates the page_slug appropriately.
283
+            if ( empty( $args['page_slug'] ) ) {
284
+                if ( ! empty( $args['display_name'] ) ) {
285
+                    $args['page_slug'] = sanitize_html_class( $args['display_name'] );
286
+                } elseif ( ! empty( $args['page_title'] ) ) {
287
+                    $args['page_slug'] = sanitize_html_class( $args['page_title'] );
288
+                } elseif ( ! empty( $args['menu_title'] ) ) {
289
+                    $args['page_slug'] = sanitize_html_class( $args['menu_title'] );
290
+                } else {
291
+                    $args['page_slug'] = str_replace( '-', '_', $args['opt_name'] );
292
+                }
293
+            }
294
+
295
+            return $args;
296
+        }
297
+
298
+        /**
299
+         * Shims for much older v3 configs.
300
+         *
301
+         * @param     array $args Global args.
302
+         *
303
+         * @return array
304
+         */
305
+        private function shim( array $args ): array {
306
+            /**
307
+             * SHIM SECTION
308
+             * Old variables and ways of doing things that need correcting.  ;)
309
+             * */
310
+            // Variable name change.
311
+            if ( ! empty( $args['page_cap'] ) ) {
312
+                $args['page_permissions'] = $args['page_cap'];
313
+                unset( $args['page_cap'] );
314
+            }
315
+
316
+            if ( ! empty( $args['page_position'] ) ) {
317
+                $args['page_priority'] = $args['page_position'];
318
+                unset( $args['page_position'] );
319
+            }
320
+
321
+            if ( ! empty( $args['page_type'] ) ) {
322
+                $args['menu_type'] = $args['page_type'];
323
+                unset( $args['page_type'] );
324
+            }
325
+
326
+            return $args;
327
+        }
328
+
329
+        /**
330
+         * Verify to see if dev has bothered to change admin bar links and share icons from demo data to their own.
331
+         *
332
+         * @param array $args Global args.
333
+         */
334
+        private function change_demo_defaults( array $args ) {
335
+            if ( $args['dev_mode'] || true === Redux_Helpers::is_local_host() ) {
336
+                if ( ! empty( $args['admin_bar_links'] ) ) {
337
+                    foreach ( $args['admin_bar_links'] as $arr ) {
338
+                        if ( is_array( $arr ) && ! empty( $arr ) ) {
339
+                            foreach ( $arr as $y ) {
340
+                                if ( strpos( Redux_Core::strtolower( $y ), 'redux' ) !== false ) {
341
+                                    $this->omit_items = true;
342
+                                    break;
343
+                                }
344
+                            }
345
+                        }
346
+                    }
347
+                }
348
+
349
+                if ( ! empty( $args['share_icons'] ) ) {
350
+                    foreach ( $args['share_icons'] as $arr ) {
351
+                        if ( is_array( $arr ) && ! empty( $arr ) ) {
352
+                            foreach ( $arr as $y ) {
353
+                                if ( strpos( Redux_Core::strtolower( $y ), 'redux' ) !== false ) {
354
+                                    $this->omit_icons = true;
355
+                                }
356
+                            }
357
+                        }
358
+                    }
359
+                }
360
+            }
361
+        }
362
+
363
+        /**
364
+         * Fix other arg criteria that sometimes gets hosed up.
365
+         *
366
+         * @param array $args Global args.
367
+         *
368
+         * @return array
369
+         * @noinspection PhpStrictComparisonWithOperandsOfDifferentTypesInspection
370
+         */
371
+        private function default_cleanup( array $args ): array {
372
+
373
+            // Fix the global variable name.
374
+            if ( '' === $args['global_variable'] && false !== $args['global_variable'] ) {
375
+                $args['global_variable'] = str_replace( '-', '_', $args['opt_name'] );
376
+            }
377
+
378
+            if ( isset( $args['customizer_only'] ) && $args['customizer_only'] ) {
379
+                $args['menu_type']      = 'hidden';
380
+                $args['customizer']     = true;
381
+                $args['admin_bar']      = false;
382
+                $args['allow_sub_menu'] = false;
383
+            }
384
+
385
+            // Check if the Airplane Mode plugin is installed.
386
+            if ( class_exists( 'Airplane_Mode_Core' ) ) {
387
+                $airplane = Airplane_Mode_Core::getInstance();
388
+                if ( method_exists( $airplane, 'enabled' ) ) {
389
+                    if ( $airplane->enabled() ) {
390
+                        $args['use_cdn'] = false;
391
+                    }
392
+                } elseif ( 'on' === $airplane->check_status() ) {
393
+                    $args['use_cdn'] = false;
394
+                }
395
+            }
396
+
397
+            return $args;
398
+        }
399
+    }
400 400
 }
Please login to merge, or discard this patch.
sample/sample-config.php 1 patch
Indentation   +425 added lines, -425 removed lines patch added patch discarded remove patch
@@ -10,7 +10,7 @@  discard block
 block discarded – undo
10 10
 defined( 'ABSPATH' ) || exit;
11 11
 
12 12
 if ( ! class_exists( 'Redux' ) ) {
13
-	return;
13
+    return;
14 14
 }
15 15
 
16 16
 // This is your option name where all the Redux data is stored.
@@ -31,32 +31,32 @@  discard block
 block discarded – undo
31 31
 $sample_patterns      = array();
32 32
 
33 33
 if ( is_dir( $sample_patterns_path ) ) {
34
-	$sample_patterns_dir = opendir( $sample_patterns_path );
35
-
36
-	if ( $sample_patterns_dir ) {
37
-
38
-		// phpcs:ignore Generic.CodeAnalysis.AssignmentInCondition.FoundInWhileCondition
39
-		while ( false !== ( $sample_patterns_file = readdir( $sample_patterns_dir ) ) ) {
40
-			if ( stristr( $sample_patterns_file, '.png' ) !== false || stristr( $sample_patterns_file, '.jpg' ) !== false ) {
41
-				$name              = explode( '.', $sample_patterns_file );
42
-				$name              = str_replace( '.' . end( $name ), '', $sample_patterns_file );
43
-				$sample_patterns[] = array(
44
-					'alt' => $name,
45
-					'img' => $sample_patterns_url . $sample_patterns_file,
46
-				);
47
-			}
48
-		}
49
-	}
34
+    $sample_patterns_dir = opendir( $sample_patterns_path );
35
+
36
+    if ( $sample_patterns_dir ) {
37
+
38
+        // phpcs:ignore Generic.CodeAnalysis.AssignmentInCondition.FoundInWhileCondition
39
+        while ( false !== ( $sample_patterns_file = readdir( $sample_patterns_dir ) ) ) {
40
+            if ( stristr( $sample_patterns_file, '.png' ) !== false || stristr( $sample_patterns_file, '.jpg' ) !== false ) {
41
+                $name              = explode( '.', $sample_patterns_file );
42
+                $name              = str_replace( '.' . end( $name ), '', $sample_patterns_file );
43
+                $sample_patterns[] = array(
44
+                    'alt' => $name,
45
+                    'img' => $sample_patterns_url . $sample_patterns_file,
46
+                );
47
+            }
48
+        }
49
+    }
50 50
 }
51 51
 
52 52
 // Used to except HTML tags in description arguments where esc_html would remove.
53 53
 $kses_exceptions = array(
54
-	'a'      => array(
55
-		'href' => array(),
56
-	),
57
-	'strong' => array(),
58
-	'br'     => array(),
59
-	'code'   => array(),
54
+    'a'      => array(
55
+        'href' => array(),
56
+    ),
57
+    'strong' => array(),
58
+    'br'     => array(),
59
+    'code'   => array(),
60 60
 );
61 61
 
62 62
 /*
@@ -71,155 +71,155 @@  discard block
 block discarded – undo
71 71
 
72 72
 // TYPICAL → Change these values as you need/desire.
73 73
 $args = array(
74
-	// This is where your data is stored in the database and also becomes your global variable name.
75
-	'opt_name'                  => $opt_name,
74
+    // This is where your data is stored in the database and also becomes your global variable name.
75
+    'opt_name'                  => $opt_name,
76 76
 
77
-	// Name that appears at the top of your panel.
78
-	'display_name'              => $theme->get( 'Name' ),
77
+    // Name that appears at the top of your panel.
78
+    'display_name'              => $theme->get( 'Name' ),
79 79
 
80
-	// Version that appears at the top of your panel.
81
-	'display_version'           => $theme->get( 'Version' ),
80
+    // Version that appears at the top of your panel.
81
+    'display_version'           => $theme->get( 'Version' ),
82 82
 
83
-	// Specify if the admin menu should appear or not. Options: menu or submenu (Under appearance only).
84
-	'menu_type'                 => 'menu',
83
+    // Specify if the admin menu should appear or not. Options: menu or submenu (Under appearance only).
84
+    'menu_type'                 => 'menu',
85 85
 
86
-	// Show the sections below the admin menu item or not.
87
-	'allow_sub_menu'            => true,
86
+    // Show the sections below the admin menu item or not.
87
+    'allow_sub_menu'            => true,
88 88
 
89
-	// The text to appear in the admin menu.
90
-	'menu_title'                => esc_html__( 'Sample Options', 'your-textdomain-here' ),
89
+    // The text to appear in the admin menu.
90
+    'menu_title'                => esc_html__( 'Sample Options', 'your-textdomain-here' ),
91 91
 
92
-	// The text to appear on the page title.
93
-	'page_title'                => esc_html__( 'Sample Options', 'your-textdomain-here' ),
92
+    // The text to appear on the page title.
93
+    'page_title'                => esc_html__( 'Sample Options', 'your-textdomain-here' ),
94 94
 
95
-	// Disable to create your own Google fonts loader.
96
-	'disable_google_fonts_link' => false,
95
+    // Disable to create your own Google fonts loader.
96
+    'disable_google_fonts_link' => false,
97 97
 
98
-	// Show the panel pages on the admin bar.
99
-	'admin_bar'                 => true,
98
+    // Show the panel pages on the admin bar.
99
+    'admin_bar'                 => true,
100 100
 
101
-	// Icon for the admin bar menu.
102
-	'admin_bar_icon'            => 'dashicons-portfolio',
101
+    // Icon for the admin bar menu.
102
+    'admin_bar_icon'            => 'dashicons-portfolio',
103 103
 
104
-	// Priority for the admin bar menu.
105
-	'admin_bar_priority'        => 50,
104
+    // Priority for the admin bar menu.
105
+    'admin_bar_priority'        => 50,
106 106
 
107
-	// Sets a different name for your global variable other than the opt_name.
108
-	'global_variable'           => $opt_name,
107
+    // Sets a different name for your global variable other than the opt_name.
108
+    'global_variable'           => $opt_name,
109 109
 
110
-	// Show the time the page took to load, etc. (forced on while on localhost or when WP_DEBUG is enabled).
111
-	'dev_mode'                  => true,
110
+    // Show the time the page took to load, etc. (forced on while on localhost or when WP_DEBUG is enabled).
111
+    'dev_mode'                  => true,
112 112
 
113
-	// Enable basic customizer support.
114
-	'customizer'                => true,
113
+    // Enable basic customizer support.
114
+    'customizer'                => true,
115 115
 
116
-	// Allow the panel to open expanded.
117
-	'open_expanded'             => false,
116
+    // Allow the panel to open expanded.
117
+    'open_expanded'             => false,
118 118
 
119
-	// Disable the save warning when a user changes a field.
120
-	'disable_save_warn'         => false,
119
+    // Disable the save warning when a user changes a field.
120
+    'disable_save_warn'         => false,
121 121
 
122
-	// Order where the menu appears in the admin area. If there is any conflict, something will not show. Warning.
123
-	'page_priority'             => 90,
122
+    // Order where the menu appears in the admin area. If there is any conflict, something will not show. Warning.
123
+    'page_priority'             => 90,
124 124
 
125
-	// For a full list of options, visit: https://codex.wordpress.org/Function_Reference/add_submenu_page#Parameters.
126
-	'page_parent'               => 'themes.php',
125
+    // For a full list of options, visit: https://codex.wordpress.org/Function_Reference/add_submenu_page#Parameters.
126
+    'page_parent'               => 'themes.php',
127 127
 
128
-	// Permissions needed to access the options panel.
129
-	'page_permissions'          => 'manage_options',
128
+    // Permissions needed to access the options panel.
129
+    'page_permissions'          => 'manage_options',
130 130
 
131
-	// Specify a custom URL to an icon.
132
-	'menu_icon'                 => '',
131
+    // Specify a custom URL to an icon.
132
+    'menu_icon'                 => '',
133 133
 
134
-	// Force your panel to always open to a specific tab (by id).
135
-	'last_tab'                  => '',
134
+    // Force your panel to always open to a specific tab (by id).
135
+    'last_tab'                  => '',
136 136
 
137
-	// Icon displayed in the admin panel next to your menu_title.
138
-	'page_icon'                 => 'icon-themes',
137
+    // Icon displayed in the admin panel next to your menu_title.
138
+    'page_icon'                 => 'icon-themes',
139 139
 
140
-	// Page slug used to denote the panel, will be based off page title, then menu title, then opt_name if not provided.
141
-	'page_slug'                 => $opt_name,
140
+    // Page slug used to denote the panel, will be based off page title, then menu title, then opt_name if not provided.
141
+    'page_slug'                 => $opt_name,
142 142
 
143
-	// On load save the defaults to DB before user clicks save.
144
-	'save_defaults'             => true,
143
+    // On load save the defaults to DB before user clicks save.
144
+    'save_defaults'             => true,
145 145
 
146
-	// Display the default value next to each field when not set to the default value.
147
-	'default_show'              => false,
146
+    // Display the default value next to each field when not set to the default value.
147
+    'default_show'              => false,
148 148
 
149
-	// What to print by the field's title if the value shown is default.
150
-	'default_mark'              => '*',
149
+    // What to print by the field's title if the value shown is default.
150
+    'default_mark'              => '*',
151 151
 
152
-	// Shows the Import/Export panel when not used as a field.
153
-	'show_import_export'        => true,
152
+    // Shows the Import/Export panel when not used as a field.
153
+    'show_import_export'        => true,
154 154
 
155
-	// Shows the Options Object for debugging purposes. Show be set to false before deploying.
156
-	'show_options_object'       => true,
155
+    // Shows the Options Object for debugging purposes. Show be set to false before deploying.
156
+    'show_options_object'       => true,
157 157
 
158
-	// The time transients will expire when the 'database' arg is set.
159
-	'transient_time'            => 60 * MINUTE_IN_SECONDS,
158
+    // The time transients will expire when the 'database' arg is set.
159
+    'transient_time'            => 60 * MINUTE_IN_SECONDS,
160 160
 
161
-	// Global shut-off for dynamic CSS output by the framework. Will also disable google fonts output.
162
-	'output'                    => true,
161
+    // Global shut-off for dynamic CSS output by the framework. Will also disable google fonts output.
162
+    'output'                    => true,
163 163
 
164
-	// Allows dynamic CSS to be generated for customizer and google fonts,
165
-	// but stops the dynamic CSS from going to the page head.
166
-	'output_tag'                => true,
164
+    // Allows dynamic CSS to be generated for customizer and google fonts,
165
+    // but stops the dynamic CSS from going to the page head.
166
+    'output_tag'                => true,
167 167
 
168
-	// Disable the footer credit of Redux. Please leave if you can help it.
169
-	'footer_credit'             => '',
168
+    // Disable the footer credit of Redux. Please leave if you can help it.
169
+    'footer_credit'             => '',
170 170
 
171
-	// If you prefer not to use the CDN for ACE Editor.
172
-	// You may download the Redux Vendor Support plugin to run locally or embed it in your code.
173
-	'use_cdn'                   => true,
171
+    // If you prefer not to use the CDN for ACE Editor.
172
+    // You may download the Redux Vendor Support plugin to run locally or embed it in your code.
173
+    'use_cdn'                   => true,
174 174
 
175
-	// Set the theme of the option panel.  Use 'wp' to use a more modern style, default is classic.
176
-	'admin_theme'               => 'wp',
175
+    // Set the theme of the option panel.  Use 'wp' to use a more modern style, default is classic.
176
+    'admin_theme'               => 'wp',
177 177
 
178
-	// Enable or disable flyout menus when hovering over a menu with submenus.
179
-	'flyout_submenus'           => true,
178
+    // Enable or disable flyout menus when hovering over a menu with submenus.
179
+    'flyout_submenus'           => true,
180 180
 
181
-	// Mode to display fonts (auto|block|swap|fallback|optional)
182
-	// See: https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display.
183
-	'font_display'              => 'swap',
181
+    // Mode to display fonts (auto|block|swap|fallback|optional)
182
+    // See: https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display.
183
+    'font_display'              => 'swap',
184 184
 
185
-	// HINTS.
186
-	'hints'                     => array(
187
-		'icon'          => 'el el-question-sign',
188
-		'icon_position' => 'right',
189
-		'icon_color'    => 'lightgray',
190
-		'icon_size'     => 'normal',
191
-		'tip_style'     => array(
192
-			'color'   => 'red',
193
-			'shadow'  => true,
194
-			'rounded' => false,
195
-			'style'   => '',
196
-		),
197
-		'tip_position'  => array(
198
-			'my' => 'top left',
199
-			'at' => 'bottom right',
200
-		),
201
-		'tip_effect'    => array(
202
-			'show' => array(
203
-				'effect'   => 'slide',
204
-				'duration' => '500',
205
-				'event'    => 'mouseover',
206
-			),
207
-			'hide' => array(
208
-				'effect'   => 'slide',
209
-				'duration' => '500',
210
-				'event'    => 'click mouseleave',
211
-			),
212
-		),
213
-	),
214
-
215
-	// FUTURE → Not in use yet, but reserved or partially implemented.
216
-	// Use at your own risk.
217
-	// Possible: options, theme_mods, theme_mods_expanded, transient. Not fully functional, warning!
218
-	'database'                  => '',
219
-	'network_admin'             => true,
220
-	'search'                    => true,
221
-	'widget_area'               => true,
222
-	'custom_fonts'              => true,
185
+    // HINTS.
186
+    'hints'                     => array(
187
+        'icon'          => 'el el-question-sign',
188
+        'icon_position' => 'right',
189
+        'icon_color'    => 'lightgray',
190
+        'icon_size'     => 'normal',
191
+        'tip_style'     => array(
192
+            'color'   => 'red',
193
+            'shadow'  => true,
194
+            'rounded' => false,
195
+            'style'   => '',
196
+        ),
197
+        'tip_position'  => array(
198
+            'my' => 'top left',
199
+            'at' => 'bottom right',
200
+        ),
201
+        'tip_effect'    => array(
202
+            'show' => array(
203
+                'effect'   => 'slide',
204
+                'duration' => '500',
205
+                'event'    => 'mouseover',
206
+            ),
207
+            'hide' => array(
208
+                'effect'   => 'slide',
209
+                'duration' => '500',
210
+                'event'    => 'click mouseleave',
211
+            ),
212
+        ),
213
+    ),
214
+
215
+    // FUTURE → Not in use yet, but reserved or partially implemented.
216
+    // Use at your own risk.
217
+    // Possible: options, theme_mods, theme_mods_expanded, transient. Not fully functional, warning!
218
+    'database'                  => '',
219
+    'network_admin'             => true,
220
+    'search'                    => true,
221
+    'widget_area'               => true,
222
+    'custom_fonts'              => true,
223 223
 );
224 224
 
225 225
 
@@ -227,53 +227,53 @@  discard block
 block discarded – undo
227 227
 // PLEASE CHANGE THESE SETTINGS IN YOUR THEME BEFORE RELEASING YOUR PRODUCT!!
228 228
 // If these are left unchanged, they will not display in your panel!
229 229
 $args['admin_bar_links'][] = array(
230
-	'id'    => 'redux-docs',
231
-	'href'  => '//devs.redux.io/',
232
-	'title' => __( 'Documentation', 'your-textdomain-here' ),
230
+    'id'    => 'redux-docs',
231
+    'href'  => '//devs.redux.io/',
232
+    'title' => __( 'Documentation', 'your-textdomain-here' ),
233 233
 );
234 234
 
235 235
 $args['admin_bar_links'][] = array(
236
-	'id'    => 'redux-support',
237
-	'href'  => '//github.com/ReduxFramework/redux-framework/issues',
238
-	'title' => __( 'Support', 'your-textdomain-here' ),
236
+    'id'    => 'redux-support',
237
+    'href'  => '//github.com/ReduxFramework/redux-framework/issues',
238
+    'title' => __( 'Support', 'your-textdomain-here' ),
239 239
 );
240 240
 
241 241
 // SOCIAL ICONS → Set up custom links in the footer for quick links in your panel footer icons.
242 242
 // PLEASE CHANGE THESE SETTINGS IN YOUR THEME BEFORE RELEASING YOUR PRODUCT!!
243 243
 // If these are left unchanged, they will not display in your panel!
244 244
 $args['share_icons'][] = array(
245
-	'url'   => '//github.com/ReduxFramework/ReduxFramework',
246
-	'title' => 'Visit us on GitHub',
247
-	'icon'  => 'el el-github',
245
+    'url'   => '//github.com/ReduxFramework/ReduxFramework',
246
+    'title' => 'Visit us on GitHub',
247
+    'icon'  => 'el el-github',
248 248
 );
249 249
 $args['share_icons'][] = array(
250
-	'url'   => '//www.facebook.com/pages/Redux-Framework/243141545850368',
251
-	'title' => 'Like us on Facebook',
252
-	'icon'  => 'el el-facebook',
250
+    'url'   => '//www.facebook.com/pages/Redux-Framework/243141545850368',
251
+    'title' => 'Like us on Facebook',
252
+    'icon'  => 'el el-facebook',
253 253
 );
254 254
 $args['share_icons'][] = array(
255
-	'url'   => '//twitter.com/reduxframework',
256
-	'title' => 'Follow us on Twitter',
257
-	'icon'  => 'el el-twitter',
255
+    'url'   => '//twitter.com/reduxframework',
256
+    'title' => 'Follow us on Twitter',
257
+    'icon'  => 'el el-twitter',
258 258
 );
259 259
 $args['share_icons'][] = array(
260
-	'url'   => '//www.linkedin.com/company/redux-framework',
261
-	'title' => 'Find us on LinkedIn',
262
-	'icon'  => 'el el-linkedin',
260
+    'url'   => '//www.linkedin.com/company/redux-framework',
261
+    'title' => 'Find us on LinkedIn',
262
+    'icon'  => 'el el-linkedin',
263 263
 );
264 264
 
265 265
 // Panel Intro text → before the form.
266 266
 if ( ! isset( $args['global_variable'] ) || false !== $args['global_variable'] ) {
267
-	if ( ! empty( $args['global_variable'] ) ) {
268
-		$v = $args['global_variable'];
269
-	} else {
270
-		$v = str_replace( '-', '_', $args['opt_name'] );
271
-	}
272
-
273
-	// translators:  Panel opt_name.
274
-	$args['intro_text'] = '<p>' . sprintf( esc_html__( 'Did you know that Redux sets a global variable for you? To access any of your saved options from within your code you can use your global variable: $%1$s', 'your-textdomain-here' ), '<strong>' . $v . '</strong>' ) . '<p>';
267
+    if ( ! empty( $args['global_variable'] ) ) {
268
+        $v = $args['global_variable'];
269
+    } else {
270
+        $v = str_replace( '-', '_', $args['opt_name'] );
271
+    }
272
+
273
+    // translators:  Panel opt_name.
274
+    $args['intro_text'] = '<p>' . sprintf( esc_html__( 'Did you know that Redux sets a global variable for you? To access any of your saved options from within your code you can use your global variable: $%1$s', 'your-textdomain-here' ), '<strong>' . $v . '</strong>' ) . '<p>';
275 275
 } else {
276
-	$args['intro_text'] = '<p>' . esc_html__( 'This text is displayed above the options panel. It isn\'t required, but more info is always better! The intro_text field accepts all HTML.', 'your-textdomain-here' ) . '</p>';
276
+    $args['intro_text'] = '<p>' . esc_html__( 'This text is displayed above the options panel. It isn\'t required, but more info is always better! The intro_text field accepts all HTML.', 'your-textdomain-here' ) . '</p>';
277 277
 }
278 278
 
279 279
 // Add content after the form.
@@ -289,16 +289,16 @@  discard block
 block discarded – undo
289 289
  * ---> START HELP TABS
290 290
  */
291 291
 $help_tabs = array(
292
-	array(
293
-		'id'      => 'redux-help-tab-1',
294
-		'title'   => esc_html__( 'Theme Information 1', 'your-textdomain-here' ),
295
-		'content' => '<p>' . esc_html__( 'This is the tab content, HTML is allowed.', 'your-textdomain-here' ) . '</p>',
296
-	),
297
-	array(
298
-		'id'      => 'redux-help-tab-2',
299
-		'title'   => esc_html__( 'Theme Information 2', 'your-textdomain-here' ),
300
-		'content' => '<p>' . esc_html__( 'This is the tab content, HTML is allowed.', 'your-textdomain-here' ) . '</p>',
301
-	),
292
+    array(
293
+        'id'      => 'redux-help-tab-1',
294
+        'title'   => esc_html__( 'Theme Information 1', 'your-textdomain-here' ),
295
+        'content' => '<p>' . esc_html__( 'This is the tab content, HTML is allowed.', 'your-textdomain-here' ) . '</p>',
296
+    ),
297
+    array(
298
+        'id'      => 'redux-help-tab-2',
299
+        'title'   => esc_html__( 'Theme Information 2', 'your-textdomain-here' ),
300
+        'content' => '<p>' . esc_html__( 'This is the tab content, HTML is allowed.', 'your-textdomain-here' ) . '</p>',
301
+    ),
302 302
 );
303 303
 Redux::set_help_tab( $opt_name, $help_tabs );
304 304
 
@@ -317,14 +317,14 @@  discard block
 block discarded – undo
317 317
 
318 318
 // -> START Basic Fields
319 319
 Redux::set_section(
320
-	$opt_name,
321
-	array(
322
-		'title'            => esc_html__( 'Basic Fields', 'your-textdomain-here' ),
323
-		'id'               => 'basic',
324
-		'desc'             => esc_html__( 'These are really basic fields!', 'your-textdomain-here' ),
325
-		'customizer_width' => '400px',
326
-		'icon'             => 'el el-home',
327
-	)
320
+    $opt_name,
321
+    array(
322
+        'title'            => esc_html__( 'Basic Fields', 'your-textdomain-here' ),
323
+        'id'               => 'basic',
324
+        'desc'             => esc_html__( 'These are really basic fields!', 'your-textdomain-here' ),
325
+        'customizer_width' => '400px',
326
+        'icon'             => 'el el-home',
327
+    )
328 328
 );
329 329
 
330 330
 require_once Redux_Core::$dir . '../sample/sections/basic-fields/checkbox.php';
@@ -337,13 +337,13 @@  discard block
 block discarded – undo
337 337
 
338 338
 // -> START Editors.
339 339
 Redux::set_section(
340
-	$opt_name,
341
-	array(
342
-		'title'            => esc_html__( 'Editors', 'your-textdomain-here' ),
343
-		'id'               => 'editor',
344
-		'customizer_width' => '500px',
345
-		'icon'             => 'el el-edit',
346
-	)
340
+    $opt_name,
341
+    array(
342
+        'title'            => esc_html__( 'Editors', 'your-textdomain-here' ),
343
+        'id'               => 'editor',
344
+        'customizer_width' => '500px',
345
+        'icon'             => 'el el-edit',
346
+    )
347 347
 );
348 348
 
349 349
 require_once Redux_Core::$dir . '../sample/sections/editors/wordpress-editor.php';
@@ -351,12 +351,12 @@  discard block
 block discarded – undo
351 351
 
352 352
 // -> START Color Selection.
353 353
 Redux::set_section(
354
-	$opt_name,
355
-	array(
356
-		'title' => esc_html__( 'Color Selection', 'your-textdomain-here' ),
357
-		'id'    => 'color',
358
-		'icon'  => 'el el-brush',
359
-	)
354
+    $opt_name,
355
+    array(
356
+        'title' => esc_html__( 'Color Selection', 'your-textdomain-here' ),
357
+        'id'    => 'color',
358
+        'icon'  => 'el el-brush',
359
+    )
360 360
 );
361 361
 
362 362
 require_once Redux_Core::$dir . '../sample/sections/color-selection/color.php';
@@ -368,12 +368,12 @@  discard block
 block discarded – undo
368 368
 
369 369
 // -> START Design Fields.
370 370
 Redux::set_section(
371
-	$opt_name,
372
-	array(
373
-		'title' => esc_html__( 'Design Fields', 'your-textdomain-here' ),
374
-		'id'    => 'design',
375
-		'icon'  => 'el el-wrench',
376
-	)
371
+    $opt_name,
372
+    array(
373
+        'title' => esc_html__( 'Design Fields', 'your-textdomain-here' ),
374
+        'id'    => 'design',
375
+        'icon'  => 'el el-wrench',
376
+    )
377 377
 );
378 378
 
379 379
 require_once Redux_Core::$dir . '../sample/sections/design-fields/background.php';
@@ -384,12 +384,12 @@  discard block
 block discarded – undo
384 384
 
385 385
 // -> START Media Uploads.
386 386
 Redux::set_section(
387
-	$opt_name,
388
-	array(
389
-		'title' => esc_html__( 'Media Uploads', 'your-textdomain-here' ),
390
-		'id'    => 'media',
391
-		'icon'  => 'el el-picture',
392
-	)
387
+    $opt_name,
388
+    array(
389
+        'title' => esc_html__( 'Media Uploads', 'your-textdomain-here' ),
390
+        'id'    => 'media',
391
+        'icon'  => 'el el-picture',
392
+    )
393 393
 );
394 394
 
395 395
 require_once Redux_Core::$dir . '../sample/sections/media-uploads/gallery.php';
@@ -399,12 +399,12 @@  discard block
 block discarded – undo
399 399
 
400 400
 // -> START Presentation Fields.
401 401
 Redux::set_section(
402
-	$opt_name,
403
-	array(
404
-		'title' => esc_html__( 'Presentation Fields', 'your-textdomain-here' ),
405
-		'id'    => 'presentation',
406
-		'icon'  => 'el el-screen',
407
-	)
402
+    $opt_name,
403
+    array(
404
+        'title' => esc_html__( 'Presentation Fields', 'your-textdomain-here' ),
405
+        'id'    => 'presentation',
406
+        'icon'  => 'el el-screen',
407
+    )
408 408
 );
409 409
 
410 410
 require_once Redux_Core::$dir . '../sample/sections/presentation-fields/divide.php';
@@ -413,21 +413,21 @@  discard block
 block discarded – undo
413 413
 require_once Redux_Core::$dir . '../sample/sections/presentation-fields/section.php';
414 414
 
415 415
 Redux::set_section(
416
-	$opt_name,
417
-	array(
418
-		'id'   => 'presentation-divide-sample',
419
-		'type' => 'divide',
420
-	)
416
+    $opt_name,
417
+    array(
418
+        'id'   => 'presentation-divide-sample',
419
+        'type' => 'divide',
420
+    )
421 421
 );
422 422
 
423 423
 // → START Switch & Button Set.
424 424
 Redux::set_section(
425
-	$opt_name,
426
-	array(
427
-		'title' => esc_html__( 'Switch / Button Set', 'your-textdomain-here' ),
428
-		'id'    => 'switch_buttonset',
429
-		'icon'  => 'el el-cogs',
430
-	)
425
+    $opt_name,
426
+    array(
427
+        'title' => esc_html__( 'Switch / Button Set', 'your-textdomain-here' ),
428
+        'id'    => 'switch_buttonset',
429
+        'icon'  => 'el el-cogs',
430
+    )
431 431
 );
432 432
 
433 433
 require_once Redux_Core::$dir . '../sample/sections/switch-button/button-set.php';
@@ -435,12 +435,12 @@  discard block
 block discarded – undo
435 435
 
436 436
 // -> START Select Fields.
437 437
 Redux::set_section(
438
-	$opt_name,
439
-	array(
440
-		'title' => esc_html__( 'Select Fields', 'your-textdomain-here' ),
441
-		'id'    => 'select',
442
-		'icon'  => 'el el-list-alt',
443
-	)
438
+    $opt_name,
439
+    array(
440
+        'title' => esc_html__( 'Select Fields', 'your-textdomain-here' ),
441
+        'id'    => 'select',
442
+        'icon'  => 'el el-list-alt',
443
+    )
444 444
 );
445 445
 
446 446
 require_once Redux_Core::$dir . '../sample/sections/select-fields/select.php';
@@ -449,12 +449,12 @@  discard block
 block discarded – undo
449 449
 
450 450
 // -> START Slider / Spinner.
451 451
 Redux::set_section(
452
-	$opt_name,
453
-	array(
454
-		'title' => esc_html__( 'Slider / Spinner', 'your-textdomain-here' ),
455
-		'id'    => 'slider_spinner',
456
-		'icon'  => 'el el-adjust-alt',
457
-	)
452
+    $opt_name,
453
+    array(
454
+        'title' => esc_html__( 'Slider / Spinner', 'your-textdomain-here' ),
455
+        'id'    => 'slider_spinner',
456
+        'icon'  => 'el el-adjust-alt',
457
+    )
458 458
 );
459 459
 
460 460
 require_once Redux_Core::$dir . '../sample/sections/slider-spinner/slider.php';
@@ -465,12 +465,12 @@  discard block
 block discarded – undo
465 465
 
466 466
 // -> START Additional Types.
467 467
 Redux::set_section(
468
-	$opt_name,
469
-	array(
470
-		'title' => esc_html__( 'Additional Types', 'your-textdomain-here' ),
471
-		'id'    => 'additional',
472
-		'icon'  => 'el el-magic',
473
-	)
468
+    $opt_name,
469
+    array(
470
+        'title' => esc_html__( 'Additional Types', 'your-textdomain-here' ),
471
+        'id'    => 'additional',
472
+        'icon'  => 'el el-magic',
473
+    )
474 474
 );
475 475
 
476 476
 require_once Redux_Core::$dir . '../sample/sections/additional-types/date.php';
@@ -479,11 +479,11 @@  discard block
 block discarded – undo
479 479
 require_once Redux_Core::$dir . '../sample/sections/additional-types/raw.php';
480 480
 
481 481
 Redux::set_section(
482
-	$opt_name,
483
-	array(
484
-		'title' => esc_html__( 'Advanced Features', 'your-textdomain-here' ),
485
-		'icon'  => 'el el-thumbs-up',
486
-	)
482
+    $opt_name,
483
+    array(
484
+        'title' => esc_html__( 'Advanced Features', 'your-textdomain-here' ),
485
+        'icon'  => 'el el-thumbs-up',
486
+    )
487 487
 );
488 488
 
489 489
 require_once Redux_Core::$dir . '../sample/sections/advanced-features/callback.php';
@@ -501,11 +501,11 @@  discard block
 block discarded – undo
501 501
 
502 502
 // -> START Disabling.
503 503
 Redux::set_section(
504
-	$opt_name,
505
-	array(
506
-		'title' => esc_html__( 'Disabling', 'your-textdomain-here' ),
507
-		'icon'  => 'el el-lock',
508
-	)
504
+    $opt_name,
505
+    array(
506
+        'title' => esc_html__( 'Disabling', 'your-textdomain-here' ),
507
+        'icon'  => 'el el-lock',
508
+    )
509 509
 );
510 510
 
511 511
 require_once Redux_Core::$dir . '../sample/sections/disabling/disable-field.php';
@@ -513,14 +513,14 @@  discard block
 block discarded – undo
513 513
 
514 514
 // -> START Extensions.
515 515
 Redux::set_section(
516
-	$opt_name,
517
-	array(
518
-		'title' => esc_html__( 'Redux Extensions', 'your-textdomain-here' ),
519
-		'id'    => 'redux-extensions',
520
-		'icon'  => 'el el-redux',
521
-		'class' => 'pro_highlight',
522
-		'desc'  => esc_html__( 'For full documentation on this field, visit: ', 'your-textdomain-here' ) . '<a href="https://devs.redux.io/core-extensions/" target="_blank">https://devs.redux.io/core-extensions/</a>',
523
-	)
516
+    $opt_name,
517
+    array(
518
+        'title' => esc_html__( 'Redux Extensions', 'your-textdomain-here' ),
519
+        'id'    => 'redux-extensions',
520
+        'icon'  => 'el el-redux',
521
+        'class' => 'pro_highlight',
522
+        'desc'  => esc_html__( 'For full documentation on this field, visit: ', 'your-textdomain-here' ) . '<a href="https://devs.redux.io/core-extensions/" target="_blank">https://devs.redux.io/core-extensions/</a>',
523
+    )
524 524
 );
525 525
 
526 526
 require_once Redux_Core::$dir . '../sample/sections/extensions/accordion.php';
@@ -546,46 +546,46 @@  discard block
 block discarded – undo
546 546
  * Raw README
547 547
  */
548 548
 if ( file_exists( $dir . '/../README.md' ) ) {
549
-	$section = array(
550
-		'icon'   => 'el el-list-alt',
551
-		'title'  => esc_html__( 'Documentation', 'your-textdomain-here' ),
552
-		'fields' => array(
553
-			array(
554
-				'id'           => 'opt-raw-documentation',
555
-				'type'         => 'raw',
556
-				'markdown'     => true,
557
-				'content_path' => __DIR__ . '/../README.md', // FULL PATH, not relative, please.
558
-			),
559
-		),
560
-	);
561
-
562
-	Redux::set_section( $opt_name, $section );
549
+    $section = array(
550
+        'icon'   => 'el el-list-alt',
551
+        'title'  => esc_html__( 'Documentation', 'your-textdomain-here' ),
552
+        'fields' => array(
553
+            array(
554
+                'id'           => 'opt-raw-documentation',
555
+                'type'         => 'raw',
556
+                'markdown'     => true,
557
+                'content_path' => __DIR__ . '/../README.md', // FULL PATH, not relative, please.
558
+            ),
559
+        ),
560
+    );
561
+
562
+    Redux::set_section( $opt_name, $section );
563 563
 }
564 564
 
565 565
 Redux::set_section(
566
-	$opt_name,
567
-	array(
568
-		'icon'            => 'el el-list-alt',
569
-		'title'           => esc_html__( 'Customizer Only', 'your-textdomain-here' ),
570
-		'desc'            => '<p class="description">' . esc_html__( 'This Section should be visible only in Customizer', 'your-textdomain-here' ) . '</p>',
571
-		'customizer_only' => true,
572
-		'fields'          => array(
573
-			array(
574
-				'id'              => 'opt-customizer-only',
575
-				'type'            => 'select',
576
-				'title'           => esc_html__( 'Customizer Only Option', 'your-textdomain-here' ),
577
-				'subtitle'        => esc_html__( 'The subtitle is NOT visible in customizer', 'your-textdomain-here' ),
578
-				'desc'            => esc_html__( 'The field desc is NOT visible in customizer.', 'your-textdomain-here' ),
579
-				'customizer_only' => true,
580
-				'options'         => array(
581
-					'1' => esc_html__( 'Opt 1', 'your-textdomain-here' ),
582
-					'2' => esc_html__( 'Opt 2', 'your-textdomain-here' ),
583
-					'3' => esc_html__( 'Opt 3', 'your-textdomain-here' ),
584
-				),
585
-				'default'         => '2',
586
-			),
587
-		),
588
-	)
566
+    $opt_name,
567
+    array(
568
+        'icon'            => 'el el-list-alt',
569
+        'title'           => esc_html__( 'Customizer Only', 'your-textdomain-here' ),
570
+        'desc'            => '<p class="description">' . esc_html__( 'This Section should be visible only in Customizer', 'your-textdomain-here' ) . '</p>',
571
+        'customizer_only' => true,
572
+        'fields'          => array(
573
+            array(
574
+                'id'              => 'opt-customizer-only',
575
+                'type'            => 'select',
576
+                'title'           => esc_html__( 'Customizer Only Option', 'your-textdomain-here' ),
577
+                'subtitle'        => esc_html__( 'The subtitle is NOT visible in customizer', 'your-textdomain-here' ),
578
+                'desc'            => esc_html__( 'The field desc is NOT visible in customizer.', 'your-textdomain-here' ),
579
+                'customizer_only' => true,
580
+                'options'         => array(
581
+                    '1' => esc_html__( 'Opt 1', 'your-textdomain-here' ),
582
+                    '2' => esc_html__( 'Opt 2', 'your-textdomain-here' ),
583
+                    '3' => esc_html__( 'Opt 3', 'your-textdomain-here' ),
584
+                ),
585
+                'default'         => '2',
586
+            ),
587
+        ),
588
+    )
589 589
 );
590 590
 
591 591
 /*
@@ -615,150 +615,150 @@  discard block
 block discarded – undo
615 615
 // add_filter('redux/options/' . $opt_name . '/sections', 'dynamic_section');
616 616
 // .
617 617
 if ( ! function_exists( 'compiler_action' ) ) {
618
-	/**
619
-	 * This is a test function that will let you see when the compiler hook occurs.
620
-	 * It only runs if a field's value has changed and compiler => true is set.
621
-	 *
622
-	 * @param array  $options        Options values.
623
-	 * @param string $css            Compiler selector CSS values  compiler => array( CSS SELECTORS ).
624
-	 * @param array  $changed_values Any values that have changed since last save.
625
-	 */
626
-	function compiler_action( array $options, string $css, array $changed_values ) {
627
-		echo '<h1>The compiler hook has run!</h1>';
628
-		echo '<pre>';
629
-		// phpcs:ignore WordPress.PHP.DevelopmentFunctions
630
-		print_r( $changed_values ); // Values that have changed since the last save.
631
-		// echo '<br/>';
632
-		// print_r($options); //Option values.
633
-		// echo '<br/>';
634
-		// print_r($css); // Compiler selector CSS values compiler => array( CSS SELECTORS ).
635
-		echo '</pre>';
636
-	}
618
+    /**
619
+     * This is a test function that will let you see when the compiler hook occurs.
620
+     * It only runs if a field's value has changed and compiler => true is set.
621
+     *
622
+     * @param array  $options        Options values.
623
+     * @param string $css            Compiler selector CSS values  compiler => array( CSS SELECTORS ).
624
+     * @param array  $changed_values Any values that have changed since last save.
625
+     */
626
+    function compiler_action( array $options, string $css, array $changed_values ) {
627
+        echo '<h1>The compiler hook has run!</h1>';
628
+        echo '<pre>';
629
+        // phpcs:ignore WordPress.PHP.DevelopmentFunctions
630
+        print_r( $changed_values ); // Values that have changed since the last save.
631
+        // echo '<br/>';
632
+        // print_r($options); //Option values.
633
+        // echo '<br/>';
634
+        // print_r($css); // Compiler selector CSS values compiler => array( CSS SELECTORS ).
635
+        echo '</pre>';
636
+    }
637 637
 }
638 638
 
639 639
 if ( ! function_exists( 'redux_validate_callback_function' ) ) {
640
-	/**
641
-	 * Custom function for the callback validation referenced above
642
-	 *
643
-	 * @param array $field          Field array.
644
-	 * @param mixed $value          New value.
645
-	 * @param mixed $existing_value Existing value.
646
-	 *
647
-	 * @return array
648
-	 */
649
-	function redux_validate_callback_function( array $field, $value, $existing_value ): array {
650
-		$error   = false;
651
-		$warning = false;
652
-
653
-		// Do your validation.
654
-		if ( 1 === (int) $value ) {
655
-			$error = true;
656
-			$value = $existing_value;
657
-		} elseif ( 2 === (int) $value ) {
658
-			$warning = true;
659
-			$value   = $existing_value;
660
-		}
661
-
662
-		$return['value'] = $value;
663
-
664
-		if ( true === $error ) {
665
-			$field['msg']    = 'your custom error message';
666
-			$return['error'] = $field;
667
-		}
668
-
669
-		if ( true === $warning ) {
670
-			$field['msg']      = 'your custom warning message';
671
-			$return['warning'] = $field;
672
-		}
673
-
674
-		return $return;
675
-	}
640
+    /**
641
+     * Custom function for the callback validation referenced above
642
+     *
643
+     * @param array $field          Field array.
644
+     * @param mixed $value          New value.
645
+     * @param mixed $existing_value Existing value.
646
+     *
647
+     * @return array
648
+     */
649
+    function redux_validate_callback_function( array $field, $value, $existing_value ): array {
650
+        $error   = false;
651
+        $warning = false;
652
+
653
+        // Do your validation.
654
+        if ( 1 === (int) $value ) {
655
+            $error = true;
656
+            $value = $existing_value;
657
+        } elseif ( 2 === (int) $value ) {
658
+            $warning = true;
659
+            $value   = $existing_value;
660
+        }
661
+
662
+        $return['value'] = $value;
663
+
664
+        if ( true === $error ) {
665
+            $field['msg']    = 'your custom error message';
666
+            $return['error'] = $field;
667
+        }
668
+
669
+        if ( true === $warning ) {
670
+            $field['msg']      = 'your custom warning message';
671
+            $return['warning'] = $field;
672
+        }
673
+
674
+        return $return;
675
+    }
676 676
 }
677 677
 
678 678
 
679 679
 if ( ! function_exists( 'dynamic_section' ) ) {
680
-	/**
681
-	 * Custom function for filtering the section array.
682
-	 * Good for child themes to override or add to the sections.
683
-	 * Simply include this function in the child themes functions.php file.
684
-	 * NOTE: the defined constants for URLs and directories will NOT be available at this point in a child theme,
685
-	 * so you must use get_template_directory_uri() if you want to use any of the built-in icons.
686
-	 *
687
-	 * @param array $sections Section array.
688
-	 *
689
-	 * @return array
690
-	 */
691
-	function dynamic_section( array $sections ): array {
692
-		$sections[] = array(
693
-			'title'  => esc_html__( 'Section via hook', 'your-textdomain-here' ),
694
-			'desc'   => '<p class="description">' . esc_html__( 'This is a section created by adding a filter to the sections array. Can be used by child themes to add/remove sections from the options.', 'your-textdomain-here' ) . '</p>',
695
-			'icon'   => 'el el-paper-clip',
696
-
697
-			// Leave this as a blank section, no options just some intro text set above.
698
-			'fields' => array(),
699
-		);
700
-
701
-		return $sections;
702
-	}
680
+    /**
681
+     * Custom function for filtering the section array.
682
+     * Good for child themes to override or add to the sections.
683
+     * Simply include this function in the child themes functions.php file.
684
+     * NOTE: the defined constants for URLs and directories will NOT be available at this point in a child theme,
685
+     * so you must use get_template_directory_uri() if you want to use any of the built-in icons.
686
+     *
687
+     * @param array $sections Section array.
688
+     *
689
+     * @return array
690
+     */
691
+    function dynamic_section( array $sections ): array {
692
+        $sections[] = array(
693
+            'title'  => esc_html__( 'Section via hook', 'your-textdomain-here' ),
694
+            'desc'   => '<p class="description">' . esc_html__( 'This is a section created by adding a filter to the sections array. Can be used by child themes to add/remove sections from the options.', 'your-textdomain-here' ) . '</p>',
695
+            'icon'   => 'el el-paper-clip',
696
+
697
+            // Leave this as a blank section, no options just some intro text set above.
698
+            'fields' => array(),
699
+        );
700
+
701
+        return $sections;
702
+    }
703 703
 }
704 704
 
705 705
 if ( ! function_exists( 'change_arguments' ) ) {
706
-	/**
707
-	 * Filter hook for filtering the args.
708
-	 * Good for child themes to override or add to the args array.
709
-	 * It can also be used in other functions.
710
-	 *
711
-	 * @param array $args Global arguments array.
712
-	 *
713
-	 * @return array
714
-	 */
715
-	function change_arguments( array $args ): array {
716
-		$args['dev_mode'] = true;
717
-
718
-		return $args;
719
-	}
706
+    /**
707
+     * Filter hook for filtering the args.
708
+     * Good for child themes to override or add to the args array.
709
+     * It can also be used in other functions.
710
+     *
711
+     * @param array $args Global arguments array.
712
+     *
713
+     * @return array
714
+     */
715
+    function change_arguments( array $args ): array {
716
+        $args['dev_mode'] = true;
717
+
718
+        return $args;
719
+    }
720 720
 }
721 721
 
722 722
 if ( ! function_exists( 'change_defaults' ) ) {
723
-	/**
724
-	 * Filter hook for filtering the default value of any given field. Very useful in development mode.
725
-	 *
726
-	 * @param array $defaults Default value array.
727
-	 *
728
-	 * @return array
729
-	 */
730
-	function change_defaults( array $defaults ): array {
731
-		$defaults['str_replace'] = esc_html__( 'Testing filter hook!', 'your-textdomain-here' );
732
-
733
-		return $defaults;
734
-	}
723
+    /**
724
+     * Filter hook for filtering the default value of any given field. Very useful in development mode.
725
+     *
726
+     * @param array $defaults Default value array.
727
+     *
728
+     * @return array
729
+     */
730
+    function change_defaults( array $defaults ): array {
731
+        $defaults['str_replace'] = esc_html__( 'Testing filter hook!', 'your-textdomain-here' );
732
+
733
+        return $defaults;
734
+    }
735 735
 }
736 736
 
737 737
 if ( ! function_exists( 'redux_custom_sanitize' ) ) {
738
-	/**
739
-	 * Function to be used if the field sanitizes argument.
740
-	 * Return value MUST be formatted or cleaned text to display.
741
-	 *
742
-	 * @param string $value Value to evaluate or clean.  Required.
743
-	 *
744
-	 * @return string
745
-	 */
746
-	function redux_custom_sanitize( string $value ): string {
747
-		$return = '';
748
-
749
-		foreach ( explode( ' ', $value ) as $w ) {
750
-			foreach ( str_split( $w ) as $k => $v ) {
751
-				if ( ( $k + 1 ) % 2 !== 0 && ctype_alpha( $v ) ) {
752
-					$return .= mb_strtoupper( $v );
753
-				} else {
754
-					$return .= $v;
755
-				}
756
-			}
757
-
758
-			$return .= ' ';
759
-		}
760
-
761
-		return $return;
762
-	}
738
+    /**
739
+     * Function to be used if the field sanitizes argument.
740
+     * Return value MUST be formatted or cleaned text to display.
741
+     *
742
+     * @param string $value Value to evaluate or clean.  Required.
743
+     *
744
+     * @return string
745
+     */
746
+    function redux_custom_sanitize( string $value ): string {
747
+        $return = '';
748
+
749
+        foreach ( explode( ' ', $value ) as $w ) {
750
+            foreach ( str_split( $w ) as $k => $v ) {
751
+                if ( ( $k + 1 ) % 2 !== 0 && ctype_alpha( $v ) ) {
752
+                    $return .= mb_strtoupper( $v );
753
+                } else {
754
+                    $return .= $v;
755
+                }
756
+            }
757
+
758
+            $return .= ' ';
759
+        }
760
+
761
+        return $return;
762
+    }
763 763
 }
764 764
 // phpcs:enable
Please login to merge, or discard this patch.