PluginProbe
Cool Timeline (Horizontal & Vertical Timeline) / trunk
Cool Timeline (Horizontal & Vertical Timeline) vtrunk
3.4.0 3.3.6 3.3.5 3.3.4 3.3.3 3.3.2 2.2.3 2.3.3 2.4 2.4.1 2.4.2 2.4.3 2.4.4 2.4.5 2.6.1 2.7.1 2.8.3 2.9.3 2.9.4 2.9.5 2.9.6 2.9.7 2.9.8 2.9.9 3.0.0 All 79 releases
cool-timeline / admin / codestar-framework / classes / admin-options.class.php

admin-options.class.php in Cool Timeline (Horizontal & Vertical Timeline) trunk, at admin/codestar-framework/classes/admin-options.class.php

765 lines 28.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php if ( ! defined( 'ABSPATH' ) ) { die; } // Cannot access directly.
2 /**
3 *
4 * Options Class
5 *
6 * @since 1.0.0
7 * @version 1.0.0
8 *
9 */
10 if ( ! class_exists( 'CSF_Options' ) ) {
11 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedClassFound
12 class CSF_Options extends CSF_Abstract {
13
14 // constans
15 public $unique = '';
16 public $notice = '';
17 public $abstract = 'options';
18 public $sections = array();
19 public $options = array();
20 public $errors = array();
21 public $pre_tabs = array();
22 public $pre_fields = array();
23 public $pre_sections = array();
24 public $args = array(
25
26 // framework title
27 'framework_title' => 'Codestar Framework <small>by Codestar</small>',
28 'framework_class' => '',
29
30 // menu settings
31 'menu_title' => '',
32 'menu_slug' => '',
33 'menu_type' => 'menu',
34 'menu_capability' => 'manage_options',
35 'menu_icon' => null,
36 'menu_position' => null,
37 'menu_hidden' => false,
38 'menu_parent' => '',
39 'sub_menu_title' => '',
40
41 // menu extras
42 'show_bar_menu' => true,
43 'show_sub_menu' => true,
44 'show_in_network' => true,
45 'show_in_customizer' => false,
46
47 'show_search' => true,
48 'show_reset_all' => true,
49 'show_reset_section' => true,
50 'show_footer' => true,
51 'show_all_options' => true,
52 'show_form_warning' => true,
53 'sticky_header' => true,
54 'save_defaults' => true,
55 'ajax_save' => true,
56 'form_action' => '',
57
58 // admin bar menu settings
59 'admin_bar_menu_icon' => '',
60 'admin_bar_menu_priority' => 50,
61
62 // footer
63 'footer_text' => 'Thank you for creating with Codestar Framework',
64 'footer_after' => '',
65 'footer_credit' => '',
66
67 // database model
68 'database' => '', // options, transient, theme_mod, network
69 'transient_time' => 0,
70
71 // contextual help
72 'contextual_help' => array(),
73 'contextual_help_sidebar' => '',
74
75 // typography options
76 'enqueue_webfont' => true,
77 'async_webfont' => false,
78
79 // others
80 'output_css' => true,
81
82 // theme
83 'nav' => 'normal',
84 'theme' => 'dark',
85 'class' => '',
86
87 // external default values
88 'defaults' => array(),
89
90 );
91
92 // run framework construct
93 public function __construct( $key, $params = array() ) {
94
95 $this->unique = $key;
96 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
97 $this->args = apply_filters( "csf_{$this->unique}_args", wp_parse_args( $params['args'], $this->args ), $this );
98 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
99 $this->sections = apply_filters( "csf_{$this->unique}_sections", $params['sections'], $this );
100
101 // run only is admin panel options, avoid performance loss
102 $this->pre_tabs = $this->pre_tabs( $this->sections );
103 $this->pre_fields = $this->pre_fields( $this->sections );
104 $this->pre_sections = $this->pre_sections( $this->sections );
105
106 $this->get_options();
107 $this->set_options();
108 $this->save_defaults();
109
110 add_action( 'admin_menu', array( $this, 'add_admin_menu' ) );
111 add_action( 'admin_bar_menu', array( $this, 'add_admin_bar_menu' ), $this->args['admin_bar_menu_priority'] );
112 add_action( 'wp_ajax_csf_'. $this->unique .'_ajax_save', array( $this, 'ajax_save' ) );
113
114 if ( $this->args['database'] === 'network' && ! empty( $this->args['show_in_network'] ) ) {
115 add_action( 'network_admin_menu', array( $this, 'add_admin_menu' ) );
116 }
117
118 // wp enqeueu for typography and output css
119 parent::__construct();
120
121 }
122
123 // instance
124 public static function instance( $key, $params = array() ) {
125 return new self( $key, $params );
126 }
127
128 public function pre_tabs( $sections ) {
129
130 $result = array();
131 $parents = array();
132 $count = 100;
133
134 foreach ( $sections as $key => $section ) {
135 if ( ! empty( $section['parent'] ) ) {
136 $section['priority'] = ( isset( $section['priority'] ) ) ? $section['priority'] : $count;
137 $parents[$section['parent']][] = $section;
138 unset( $sections[$key] );
139 }
140 $count++;
141 }
142
143 foreach ( $sections as $key => $section ) {
144 $section['priority'] = ( isset( $section['priority'] ) ) ? $section['priority'] : $count;
145 if ( ! empty( $section['id'] ) && ! empty( $parents[$section['id']] ) ) {
146 $section['subs'] = wp_list_sort( $parents[$section['id']], array( 'priority' => 'ASC' ), 'ASC', true );
147 }
148 $result[] = $section;
149 $count++;
150 }
151
152 return wp_list_sort( $result, array( 'priority' => 'ASC' ), 'ASC', true );
153 }
154
155 public function pre_fields( $sections ) {
156
157 $result = array();
158
159 foreach ( $sections as $key => $section ) {
160 if ( ! empty( $section['fields'] ) ) {
161 foreach ( $section['fields'] as $field ) {
162 $result[] = $field;
163 }
164 }
165 }
166
167 return $result;
168 }
169
170 public function pre_sections( $sections ) {
171
172 $result = array();
173
174 foreach ( $this->pre_tabs as $tab ) {
175 if ( ! empty( $tab['subs'] ) ) {
176 foreach ( $tab['subs'] as $sub ) {
177 $sub['ptitle'] = $tab['title'];
178 $result[] = $sub;
179 }
180 }
181 if ( empty( $tab['subs'] ) ) {
182 $result[] = $tab;
183 }
184 }
185
186 return $result;
187 }
188
189 // add admin bar menu
190 public function add_admin_bar_menu( $wp_admin_bar ) {
191
192 if ( ! current_user_can( $this->args['menu_capability'] ) ) {
193 return;
194 }
195
196 if ( is_network_admin() && ( $this->args['database'] !== 'network' || $this->args['show_in_network'] !== true ) ) {
197 return;
198 }
199
200 if ( ! empty( $this->args['show_bar_menu'] ) && empty( $this->args['menu_hidden'] ) ) {
201
202 global $submenu;
203
204 $menu_slug = $this->args['menu_slug'];
205 $menu_icon = ( ! empty( $this->args['admin_bar_menu_icon'] ) ) ? '<span class="csf-ab-icon ab-icon '. esc_attr( $this->args['admin_bar_menu_icon'] ) .'"></span>' : '';
206
207 $wp_admin_bar->add_node( array(
208 'id' => $menu_slug,
209 'title' => $menu_icon . esc_attr( $this->args['menu_title'] ),
210 'href' => esc_url( ( is_network_admin() ) ? network_admin_url( 'admin.php?page='. $menu_slug ) : admin_url( 'admin.php?page='. $menu_slug ) ),
211 ) );
212
213 if ( ! empty( $submenu[$menu_slug] ) ) {
214 foreach ( $submenu[$menu_slug] as $menu_key => $menu_value ) {
215 $wp_admin_bar->add_node( array(
216 'parent' => $menu_slug,
217 'id' => $menu_slug .'-'. $menu_key,
218 'title' => $menu_value[0],
219 'href' => esc_url( ( is_network_admin() ) ? network_admin_url( 'admin.php?page='. $menu_value[2] ) : admin_url( 'admin.php?page='. $menu_value[2] ) ),
220 ) );
221 }
222 }
223
224 }
225
226 }
227
228 public function ajax_save() {
229
230 // Check user capabilities
231 if ( ! current_user_can( 'manage_options' ) ) {
232 // phpcs:ignore WordPress.WP.I18n.TextDomainMismatch
233 return wp_send_json_error( array( 'error' => esc_html__( 'Unauthorized access.', 'csf' ) ) );
234
235 }
236
237 $result = $this->set_options( true );
238
239 if ( ! $result ) {
240 // phpcs:ignore WordPress.WP.I18n.TextDomainMismatch
241 return wp_send_json_error( array( 'error' => esc_html__( 'Error while saving the changes.', 'csf' ) ) );
242 } else {
243 wp_send_json_success( array( 'notice' => $this->notice, 'errors' => $this->errors ) );
244 }
245
246 }
247
248 // get default value
249 public function get_default( $field ) {
250
251 $default = ( isset( $field['default'] ) ) ? $field['default'] : '';
252 $default = ( isset( $this->args['defaults'][$field['id']] ) ) ? $this->args['defaults'][$field['id']] : $default;
253
254 return $default;
255
256 }
257
258 // save defaults and set new fields value to main options
259 public function save_defaults() {
260
261 $tmp_options = $this->options;
262
263 foreach ( $this->pre_fields as $field ) {
264 if ( ! empty( $field['id'] ) ) {
265 $this->options[$field['id']] = ( isset( $this->options[$field['id']] ) ) ? $this->options[$field['id']] : $this->get_default( $field );
266 }
267 }
268
269 if ( $this->args['save_defaults'] && empty( $tmp_options ) ) {
270 $this->save_options( $this->options );
271 }
272
273 }
274
275 // set options
276 public function set_options( $ajax = false ) {
277
278 // XSS ok.
279 // No worries, This "POST" requests is sanitizing in the below foreach. see #L337 - #L341
280 // phpcs:ignore WordPress.Security.NonceVerification.Missing,WordPress.Security.ValidatedSanitizedInput.MissingUnslash,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
281 $response = ( $ajax && ! empty( $_POST['data'] ) ) ? json_decode( wp_unslash( trim( $_POST['data'] ) ), true ) : $_POST;
282
283 // Set variables.
284 $data = array();
285 $noncekey = 'csf_options_nonce'. $this->unique;
286 $nonce = ( ! empty( $response[$noncekey] ) ) ? $response[$noncekey] : '';
287 $options = ( ! empty( $response[$this->unique] ) ) ? $response[$this->unique] : array();
288 $transient = ( ! empty( $response['csf_transient'] ) ) ? $response['csf_transient'] : array();
289
290 if ( wp_verify_nonce( $nonce, 'csf_options_nonce' ) ) {
291
292 $importing = false;
293 $section_id = ( ! empty( $transient['section'] ) ) ? $transient['section'] : '';
294
295 if ( ! $ajax && ! empty( $response[ 'csf_import_data' ] ) ) {
296
297 // XSS ok.
298 // No worries, This "POST" requests is sanitizing in the below foreach. see #L337 - #L341
299 $import_data = json_decode( wp_unslash( trim( $response[ 'csf_import_data' ] ) ), true );
300 $options = ( is_array( $import_data ) && ! empty( $import_data ) ) ? $import_data : array();
301 $importing = true;
302 // phpcs:ignore WordPress.WP.I18n.TextDomainMismatch
303 $this->notice = esc_html__( 'Settings successfully imported.', 'csf' );
304
305 }
306
307 if ( ! empty( $transient['reset'] ) ) {
308
309 foreach ( $this->pre_fields as $field ) {
310 if ( ! empty( $field['id'] ) ) {
311 $data[$field['id']] = $this->get_default( $field );
312 }
313 }
314
315 // phpcs:ignore WordPress.WP.I18n.TextDomainMismatch
316 $this->notice = esc_html__( 'Default settings restored.', 'csf' );
317
318 } else if ( ! empty( $transient['reset_section'] ) && ! empty( $section_id ) ) {
319
320 if ( ! empty( $this->pre_sections[$section_id-1]['fields'] ) ) {
321
322 foreach ( $this->pre_sections[$section_id-1]['fields'] as $field ) {
323 if ( ! empty( $field['id'] ) ) {
324 $data[$field['id']] = $this->get_default( $field );
325 }
326 }
327
328 }
329
330 $data = wp_parse_args( $data, $this->options );
331
332 // phpcs:ignore WordPress.WP.I18n.TextDomainMismatch
333 $this->notice = esc_html__( 'Default settings restored.', 'csf' );
334
335 } else {
336
337 // sanitize and validate
338 foreach ( $this->pre_fields as $field ) {
339
340 if ( ! empty( $field['id'] ) ) {
341
342 $field_id = $field['id'];
343 $field_value = isset( $options[$field_id] ) ? $options[$field_id] : '';
344
345 // Ajax and Importing doing wp_unslash already.
346 if ( ! $ajax && ! $importing ) {
347 $field_value = wp_unslash( $field_value );
348 }
349
350 // Sanitize "post" request of field.
351 if ( ! isset( $field['sanitize'] ) ) {
352
353 if( is_array( $field_value ) ) {
354
355 $data[$field_id] = wp_kses_post_deep( $field_value );
356
357 } else {
358
359 $data[$field_id] = wp_kses_post( $field_value );
360
361 }
362
363 } else if( isset( $field['sanitize'] ) && is_callable( $field['sanitize'] ) ) {
364
365 $data[$field_id] = call_user_func( $field['sanitize'], $field_value );
366
367 } else {
368
369 $data[$field_id] = $field_value;
370
371 }
372
373 // Validate "post" request of field.
374 if ( isset( $field['validate'] ) && is_callable( $field['validate'] ) ) {
375
376 $has_validated = call_user_func( $field['validate'], $field_value );
377
378 if ( ! empty( $has_validated ) ) {
379
380 $data[$field_id] = ( isset( $this->options[$field_id] ) ) ? $this->options[$field_id] : '';
381 $this->errors[$field_id] = $has_validated;
382
383 }
384
385 }
386
387 }
388
389 }
390
391 }
392
393 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
394 $data = apply_filters( "csf_{$this->unique}_save", $data, $this );
395
396 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
397 do_action( "csf_{$this->unique}_save_before", $data, $this );
398
399 $this->options = $data;
400
401 $this->save_options( $data );
402
403 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
404 do_action( "csf_{$this->unique}_save_after", $data, $this );
405
406 if ( empty( $this->notice ) ) {
407 // phpcs:ignore WordPress.WP.I18n.TextDomainMismatch
408 $this->notice = esc_html__( 'Settings saved.', 'csf' );
409 }
410
411 return true;
412
413 }
414
415 return false;
416
417 }
418
419 // save options database
420 public function save_options( $data ) {
421
422 if ( $this->args['database'] === 'transient' ) {
423 set_transient( $this->unique, $data, $this->args['transient_time'] );
424 } else if ( $this->args['database'] === 'theme_mod' ) {
425 set_theme_mod( $this->unique, $data );
426 } else if ( $this->args['database'] === 'network' ) {
427 update_site_option( $this->unique, $data );
428 } else {
429 update_option( $this->unique, $data );
430 }
431
432 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
433 do_action( "csf_{$this->unique}_saved", $data, $this );
434
435 }
436
437 // get options from database
438 public function get_options() {
439
440 if ( $this->args['database'] === 'transient' ) {
441 $this->options = get_transient( $this->unique );
442 } else if ( $this->args['database'] === 'theme_mod' ) {
443 $this->options = get_theme_mod( $this->unique );
444 } else if ( $this->args['database'] === 'network' ) {
445 $this->options = get_site_option( $this->unique );
446 } else {
447 $this->options = get_option( $this->unique );
448 }
449
450 if ( empty( $this->options ) ) {
451 $this->options = array();
452 }
453
454 return $this->options;
455
456 }
457
458 // admin menu
459 public function add_admin_menu() {
460
461 extract( $this->args );
462
463 if ( $menu_type === 'submenu' ) {
464
465 $menu_page = call_user_func( 'add_submenu_page', $menu_parent, esc_attr( $menu_title ), esc_attr( $menu_title ), $menu_capability, $menu_slug, array( $this, 'add_options_html' ) );
466
467 } else {
468
469 $menu_page = call_user_func( 'add_menu_page', esc_attr( $menu_title ), esc_attr( $menu_title ), $menu_capability, $menu_slug, array( $this, 'add_options_html' ), $menu_icon, $menu_position );
470
471 if ( ! empty( $sub_menu_title ) ) {
472 call_user_func( 'add_submenu_page', $menu_slug, esc_attr( $sub_menu_title ), esc_attr( $sub_menu_title ), $menu_capability, $menu_slug, array( $this, 'add_options_html' ) );
473 }
474
475 if ( ! empty( $this->args['show_sub_menu'] ) && count( $this->pre_tabs ) > 1 ) {
476
477 // create submenus
478 foreach ( $this->pre_tabs as $section ) {
479 call_user_func( 'add_submenu_page', $menu_slug, esc_attr( $section['title'] ), esc_attr( $section['title'] ), $menu_capability, $menu_slug .'#tab='. sanitize_title( $section['title'] ), '__return_null' );
480 }
481
482 remove_submenu_page( $menu_slug, $menu_slug );
483
484 }
485
486 if ( ! empty( $menu_hidden ) ) {
487 remove_menu_page( $menu_slug );
488 }
489
490 }
491
492 add_action( 'load-'. $menu_page, array( $this, 'add_page_on_load' ) );
493
494 }
495
496 public function add_page_on_load() {
497
498 if ( ! empty( $this->args['contextual_help'] ) ) {
499
500 $screen = get_current_screen();
501
502 foreach ( $this->args['contextual_help'] as $tab ) {
503 $screen->add_help_tab( $tab );
504 }
505
506 if ( ! empty( $this->args['contextual_help_sidebar'] ) ) {
507 $screen->set_help_sidebar( $this->args['contextual_help_sidebar'] );
508 }
509
510 }
511
512 if ( ! empty( $this->args['footer_credit'] ) ) {
513 add_filter( 'admin_footer_text', array( $this, 'add_admin_footer_text' ) );
514 }
515
516 }
517
518 public function add_admin_footer_text() {
519 echo wp_kses_post( $this->args['footer_credit'] );
520 }
521
522 public function error_check( $sections, $err = '' ) {
523
524 if ( ! $this->args['ajax_save'] ) {
525
526 if ( ! empty( $sections['fields'] ) ) {
527 foreach ( $sections['fields'] as $field ) {
528 if ( ! empty( $field['id'] ) ) {
529 if ( array_key_exists( $field['id'], $this->errors ) ) {
530 $err = '<span class="csf-label-error">!</span>';
531 }
532 }
533 }
534 }
535
536 if ( ! empty( $sections['subs'] ) ) {
537 foreach ( $sections['subs'] as $sub ) {
538 $err = $this->error_check( $sub, $err );
539 }
540 }
541
542 if ( ! empty( $sections['id'] ) && array_key_exists( $sections['id'], $this->errors ) ) {
543 $err = $this->errors[$sections['id']];
544 }
545
546 }
547
548 return $err;
549 }
550
551 // option page html output
552 public function add_options_html() {
553
554 $has_nav = ( count( $this->pre_tabs ) > 1 ) ? true : false;
555 $show_all = ( ! $has_nav ) ? ' csf-show-all' : '';
556 $ajax_class = ( $this->args['ajax_save'] ) ? ' csf-save-ajax' : '';
557 $sticky_class = ( $this->args['sticky_header'] ) ? ' csf-sticky-header' : '';
558 $wrapper_class = ( $this->args['framework_class'] ) ? ' '. $this->args['framework_class'] : '';
559 $theme = ( $this->args['theme'] ) ? ' csf-theme-'. $this->args['theme'] : '';
560 $class = ( $this->args['class'] ) ? ' '. $this->args['class'] : '';
561 $nav_type = ( $this->args['nav'] === 'inline' ) ? 'inline' : 'normal';
562 $form_action = ( $this->args['form_action'] ) ? $this->args['form_action'] : '';
563
564 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
565 do_action( 'csf_options_before' );
566
567 echo '<div class="csf csf-options'. esc_attr( $theme . $class . $wrapper_class ) .'" data-slug="'. esc_attr( $this->args['menu_slug'] ) .'" data-unique="'. esc_attr( $this->unique ) .'">';
568
569 echo '<div class="csf-container">';
570
571 echo '<form method="post" action="'. esc_attr( $form_action ) .'" enctype="multipart/form-data" id="csf-form" autocomplete="off" novalidate="novalidate">';
572
573 echo '<input type="hidden" class="csf-section-id" name="csf_transient[section]" value="1">';
574
575 wp_nonce_field( 'csf_options_nonce', 'csf_options_nonce'. $this->unique );
576
577 echo '<div class="csf-header'. esc_attr( $sticky_class ) .'">';
578 echo '<div class="csf-header-inner">';
579
580 echo '<div class="csf-header-left">';
581 echo '<h1>'. esc_html($this->args['framework_title']) .'</h1>';
582 echo '</div>';
583
584 echo '<div class="csf-header-right">';
585
586 $notice_class = ( ! empty( $this->notice ) ) ? 'csf-form-show' : '';
587 $notice_text = ( ! empty( $this->notice ) ) ? $this->notice : '';
588
589 echo '<div class="csf-form-result csf-form-success '. esc_attr( $notice_class ) .'">'.esc_html($notice_text) .'</div>';
590
591 // phpcs:ignore WordPress.WP.I18n.TextDomainMismatch
592 echo ( $this->args['show_form_warning'] ) ? '<div class="csf-form-result csf-form-warning">'. esc_html__( 'You have unsaved changes, save your changes!', 'csf' ) .'</div>' : '';
593
594 // phpcs:ignore WordPress.WP.I18n.TextDomainMismatch
595 echo ( $has_nav && $this->args['show_all_options'] ) ? '<div class="csf-expand-all" title="'. esc_html__( 'show all settings', 'csf' ) .'"><i class="fas fa-outdent"></i></div>' : '';
596
597 // phpcs:ignore WordPress.WP.I18n.TextDomainMismatch
598 echo ( $this->args['show_search'] ) ? '<div class="csf-search"><input type="text" name="csf-search" placeholder="'. esc_html__( 'Search...', 'csf' ) .'" autocomplete="off" /></div>' : '';
599
600 echo '<div class="csf-buttons">';
601 // phpcs:ignore WordPress.WP.I18n.TextDomainMismatch
602 echo '<input type="submit" name="'. esc_attr( $this->unique ) .'[_nonce][save]" class="button button-primary csf-top-save csf-save'. esc_attr( $ajax_class ) .'" value="'. esc_html__( 'Save', 'csf' ) .'" data-save="'. esc_html__( 'Saving...', 'csf' ) .'">';
603 // phpcs:ignore WordPress.WP.I18n.TextDomainMismatch
604 echo ( $this->args['show_reset_section'] ) ? '<input type="submit" name="csf_transient[reset_section]" class="button button-secondary csf-reset-section csf-confirm" value="'. esc_html__( 'Reset Section', 'csf' ) .'" data-confirm="'. esc_html__( 'Are you sure to reset this section options?', 'csf' ) .'">' : '';
605 // phpcs:ignore WordPress.WP.I18n.TextDomainMismatch
606 echo ( $this->args['show_reset_all'] ) ? '<input type="submit" name="csf_transient[reset]" class="button csf-warning-primary csf-reset-all csf-confirm" value="'. ( ( $this->args['show_reset_section'] ) ? esc_html__( 'Reset All', 'csf' ) : esc_html__( 'Reset', 'csf' ) ) .'" data-confirm="'. esc_html__( 'Are you sure you want to reset all settings to default values?', 'csf' ) .'">' : '';
607 echo '</div>';
608
609 echo '</div>';
610
611 echo '<div class="clear"></div>';
612 echo '</div>';
613 echo '</div>';
614
615 echo '<div class="csf-wrapper'. esc_attr( $show_all ) .'">';
616
617 if ( $has_nav ) {
618
619 echo '<div class="csf-nav csf-nav-'. esc_attr( $nav_type ) .' csf-nav-options">';
620
621 echo '<ul>';
622
623 foreach ( $this->pre_tabs as $tab ) {
624
625 $tab_id = sanitize_title( $tab['title'] );
626 $tab_error = $this->error_check( $tab );
627 $tab_icon = ( ! empty( $tab['icon'] ) ) ? '<i class="csf-tab-icon '. esc_attr( $tab['icon'] ) .'"></i>' : '';
628
629 if ( ! empty( $tab['subs'] ) ) {
630
631 echo '<li class="csf-tab-item">';
632
633 echo '<a href="#tab='. esc_attr( $tab_id ) .'" data-tab-id="'. esc_attr( $tab_id ) .'" class="csf-arrow">'.esc_html($tab_icon) . esc_html($tab['title']) . esc_html($tab_error) .'</a>';
634
635 echo '<ul>';
636
637 foreach ( $tab['subs'] as $sub ) {
638
639 $sub_id = $tab_id .'/'. sanitize_title( $sub['title'] );
640 $sub_error = $this->error_check( $sub );
641 $sub_icon = ( ! empty( $sub['icon'] ) ) ? '<i class="csf-tab-icon '. esc_attr( $sub['icon'] ) .'"></i>' : '';
642
643 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
644 echo '<li><a href="#tab='. esc_attr( $sub_id ) .'" data-tab-id="'. esc_attr( $sub_id ) .'">'. $sub_icon . $sub['title'] . $sub_error .'</a></li>';
645
646 }
647
648 echo '</ul>';
649
650 echo '</li>';
651
652 } else {
653
654 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
655 echo '<li class="csf-tab-item"><a href="#tab='. esc_attr( $tab_id ) .'" data-tab-id="'. esc_attr( $tab_id ) .'">'. $tab_icon . $tab['title'] . $tab_error .'</a></li>';
656
657 }
658
659 }
660
661 echo '</ul>';
662
663 echo '</div>';
664
665 }
666
667 echo '<div class="csf-content">';
668
669 echo '<div class="csf-sections">';
670
671 foreach ( $this->pre_sections as $section ) {
672
673 $section_onload = ( ! $has_nav ) ? ' csf-onload' : '';
674 $section_class = ( ! empty( $section['class'] ) ) ? ' '. $section['class'] : '';
675 $section_icon = ( ! empty( $section['icon'] ) ) ? '<i class="csf-section-icon '. esc_attr( $section['icon'] ) .'"></i>' : '';
676 $section_title = ( ! empty( $section['title'] ) ) ? $section['title'] : '';
677 $section_parent = ( ! empty( $section['ptitle'] ) ) ? sanitize_title( $section['ptitle'] ) .'/' : '';
678 $section_slug = ( ! empty( $section['title'] ) ) ? sanitize_title( $section_title ) : '';
679
680 echo '<div class="csf-section hidden'. esc_attr( $section_onload . $section_class ) .'" data-section-id="'. esc_attr( $section_parent . $section_slug ) .'">';
681 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
682 echo ( $has_nav ) ? '<div class="csf-section-title"><h3>'. $section_icon . $section_title .'</h3></div>' : '';
683 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
684 echo ( ! empty( $section['description'] ) ) ? '<div class="csf-field csf-section-description">'. $section['description'] .'</div>' : '';
685
686 if ( ! empty( $section['fields'] ) ) {
687
688 foreach ( $section['fields'] as $field ) {
689
690 $is_field_error = $this->error_check( $field );
691
692 if ( ! empty( $is_field_error ) ) {
693 $field['_error'] = $is_field_error;
694 }
695
696 if ( ! empty( $field['id'] ) ) {
697 $field['default'] = $this->get_default( $field );
698 }
699
700 $value = ( ! empty( $field['id'] ) && isset( $this->options[$field['id']] ) ) ? $this->options[$field['id']] : '';
701
702 CSF::field( $field, $value, $this->unique, 'options' );
703
704 }
705
706 } else {
707
708 // phpcs:ignore WordPress.WP.I18n.TextDomainMismatch
709 echo '<div class="csf-no-option">'. esc_html__( 'No data available.', 'csf' ) .'</div>';
710
711 }
712
713 echo '</div>';
714
715 }
716
717 echo '</div>';
718
719 echo '<div class="clear"></div>';
720
721 echo '</div>';
722
723 echo ( $has_nav && $nav_type === 'normal' ) ? '<div class="csf-nav-background"></div>' : '';
724
725 echo '</div>';
726
727 if ( ! empty( $this->args['show_footer'] ) ) {
728
729 echo '<div class="csf-footer">';
730
731 echo '<div class="csf-buttons">';
732 // phpcs:ignore WordPress.WP.I18n.TextDomainMismatch
733 echo '<input type="submit" name="csf_transient[save]" class="button button-primary csf-save'. esc_attr( $ajax_class ) .'" value="'. esc_html__( 'Save', 'csf' ) .'" data-save="'. esc_html__( 'Saving...', 'csf' ) .'">';
734 // phpcs:ignore WordPress.WP.I18n.TextDomainMismatch
735 echo ( $this->args['show_reset_section'] ) ? '<input type="submit" name="csf_transient[reset_section]" class="button button-secondary csf-reset-section csf-confirm" value="'. esc_html__( 'Reset Section', 'csf' ) .'" data-confirm="'. esc_html__( 'Are you sure to reset this section options?', 'csf' ) .'">' : '';
736 // phpcs:ignore WordPress.WP.I18n.TextDomainMismatch
737 echo ( $this->args['show_reset_all'] ) ? '<input type="submit" name="csf_transient[reset]" class="button csf-warning-primary csf-reset-all csf-confirm" value="'. ( ( $this->args['show_reset_section'] ) ? esc_html__( 'Reset All', 'csf' ) : esc_html__( 'Reset', 'csf' ) ) .'" data-confirm="'. esc_html__( 'Are you sure you want to reset all settings to default values?', 'csf' ) .'">' : '';
738 echo '</div>';
739
740 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
741 echo ( ! empty( $this->args['footer_text'] ) ) ? '<div class="csf-copyright">'. $this->args['footer_text'] .'</div>' : '';
742
743 echo '<div class="clear"></div>';
744 echo '</div>';
745
746 }
747
748 echo '</form>';
749
750 echo '</div>';
751
752 echo '<div class="clear"></div>';
753
754 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
755 echo ( ! empty( $this->args['footer_after'] ) ) ? $this->args['footer_after'] : '';
756
757 echo '</div>';
758
759 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
760 do_action( 'csf_options_after' );
761
762 }
763 }
764 }
765