PluginProbe
Reading Position Indicator / trunk
Reading Position Indicator vtrunk
1.2.3 trunk 1.0.1 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.0.7 1.0.8 1.0.9 1.1.2 1.2.0 1.2.1 1.2.2
reading-position-indicator / includes / iworks / options / options.php

options.php in Reading Position Indicator trunk, at includes/iworks/options/options.php

2,998 lines 80.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * iWorks Options Class.
4 *
5 * A comprehensive options management class for WordPress plugins and themes.
6 * Provides a flexible framework for creating admin pages, settings fields,
7 * and managing plugin/theme configuration options.
8 *
9 * @package Iworks\Options
10 * @version 3.1.1
11 * @author Marcin Pietrzak <marcin@iworks.pl>
12 * @link https://github.com/iworks/wordpress-options-class
13 * @license GPL-3.0-or-later
14 *
15 * Copyright 2011-2026 Marcin Pietrzak (marcin@iworks.pl)
16 *
17 * This program is free software; you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License, version 3, as
19 * published by the Free Software Foundation.
20 *
21 * This program is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 * GNU General Public License for more details.
25 *
26 * You should have received a copy of the GNU General Public License
27 * along with this program; if not, write to the Free Software
28 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
29 */
30
31 defined( 'ABSPATH' ) || exit; // Exit if accessed directly
32
33 if ( class_exists( 'iworks_options' ) ) {
34 return;
35 }
36
37 class iworks_options {
38
39 /**
40 * Plugin version.
41 *
42 * @since 1.0.0
43 * @var string
44 */
45 private string $version = '3.1.1';
46
47 /**
48 * Core options array.
49 *
50 * @since 1.0.0
51 * @var array
52 */
53 private array $options;
54
55 /**
56 * Option function name.
57 *
58 * @since 1.0.0
59 * @var string
60 */
61 private string $option_function_name = '';
62
63 /**
64 * Option group name.
65 *
66 * @since 1.0.0
67 * @var string
68 */
69 private string $option_group = 'index';
70
71 /**
72 * Option prefix.
73 *
74 * @since 1.0.0
75 * @var string
76 */
77 private string $option_prefix = '';
78
79 /**
80 * Registered page hooks.
81 *
82 * @since 1.0.0
83 * @var array
84 */
85 private array $pagehooks = array();
86
87 /**
88 * Enqueued scripts.
89 *
90 * @since 1.0.0
91 * @var array
92 */
93 private array $scripts_enqueued = array();
94 /**
95 * Control class mode.
96 *
97 * @since 2.6.5
98 * @var string
99 */
100 private string $mode = 'plugin';
101
102 /**
103 * Admin notices array.
104 *
105 * @since 1.0.0
106 * @var array
107 */
108 public array $notices = array();
109
110 /**
111 * Plugin identifier.
112 *
113 * @since 2.7.3
114 * @var string
115 */
116 private string $plugin = '-not-set-';
117
118 /**
119 * Files to enqueue.
120 *
121 * @since 2.8.4
122 * @var array
123 */
124 private array $files = array();
125
126 /**
127 * Logger instance.
128 *
129 * @since 3.1.0
130 * @var iworks_options_logger
131 */
132 private iworks_options_logger $logger;
133
134 /**
135 * Constructor.
136 *
137 * Sets up the options class with default values and registers hooks.
138 *
139 * @since 1.0.0
140 */
141 public function __construct() {
142
143 // Setup.
144 $this->files = $this->get_files();
145
146 // Register hooks.
147 add_action( 'admin_enqueue_scripts', array( $this, 'register_styles' ), 0 );
148 add_action( 'admin_head', array( $this, 'admin_head' ) );
149 add_action( 'admin_menu', array( $this, 'admin_menu' ) );
150 add_action( 'admin_notices', array( $this, 'admin_notices' ) );
151 add_filter( 'screen_layout_columns', array( $this, 'screen_layout_columns' ), 10, 2 );
152
153 // Include logger class.
154 require_once plugin_dir_path( __FILE__ ) . 'includes/class-iworks-options-logger.php';
155 $this->logger = new iworks_options_logger( $this );
156 }
157
158 /**
159 * Initialize the options class.
160 *
161 * @since 1.0.0
162 */
163 public function init() {
164 $this->get_option_array();
165 }
166
167 /**
168 * Set option class mode.
169 *
170 * @since 2.6.5
171 *
172 * @param string $mode Working mode, possible values "plugin", "theme".
173 */
174 public function set_mode( $mode ) {
175 if ( preg_match( '/^(plugin|theme)$/', $mode ) ) {
176 $this->mode = $mode;
177 }
178 }
179
180 /**
181 * Get option class mode.
182 *
183 * @since 3.1.0
184 *
185 * @return string Working mode, possible values "plugin", "theme".
186 */
187 public function get_mode() {
188 return $this->mode;
189 }
190
191 /**
192 * Get option group configuration.
193 *
194 * @since 2.6.7
195 *
196 * @param string|null $option_group Name of config group.
197 * @return array Group configuration array.
198 */
199 public function get_group( $option_group = null ) {
200 if ( null === $option_group ) {
201 $option_group = $this->option_group;
202 }
203 return $this->get_option_array( $option_group );
204 }
205
206 /**
207 * Add admin menu.
208 *
209 * @since 1.0.0
210 */
211 public function admin_menu() {
212 $data = $this->get_option_array();
213 if ( ! isset( $this->options ) ) {
214 return;
215 }
216 $pages = array();
217 $pages['index'] = $data;
218 if ( isset( $data['pages'] ) ) {
219 $pages = $data['pages'] + $pages;
220 }
221 foreach ( $pages as $key => $data ) {
222 /**
223 * Parse and sanitize admin menu arguments.
224 *
225 * @since 1.0.0
226 *
227 * @param array $data {
228 * Array of menu page arguments.
229 *
230 * @type string $menu The menu type. Default 'top_level'.
231 * @type string $capability The capability required for this menu. Default 'manage_options'.
232 * @type int $position The position in the menu order. Default 10.
233 * @type string $icon_url The URL to the icon to be used for this menu. Default null.
234 * @type string $parent The parent menu slug. Default null for top-level menu.
235 * @type string $page_title The text to be displayed in the title tags of the page.
236 * Default 'No Page Title'.
237 * }
238 */
239 $data = wp_parse_args(
240 $data,
241 array(
242 'menu' => 'top_level',
243 'capability' => 'manage_options',
244 'position' => 10,
245 'icon_url' => null,
246 'parent' => null,
247 'page_title' => esc_html__( 'No Page Title', 'reading-position-indicator' ),
248 )
249 );
250 /**
251 * Check callback
252 */
253 $callback = array( $this, 'show_page' );
254 if ( isset( $data['show_page_callback'] ) && is_callable( $data['show_page_callback'] ) ) {
255 $callback = $data['show_page_callback'];
256 }
257 if ( isset( $data['set_callback_to_null'] ) && $data['set_callback_to_null'] ) {
258 $callback = null;
259 }
260 /**
261 * Add menu or submenu
262 */
263 switch ( $data['menu'] ) {
264 case 'comments':
265 case 'dashboard':
266 case 'links':
267 case 'management':
268 case 'media':
269 case 'options':
270 case 'pages':
271 case 'plugins':
272 case 'posts':
273 case 'posts':
274 case 'theme':
275 case 'users':
276 $function = sprintf( 'add_%s_page', $data['menu'] );
277 $this->pagehooks[ $key ] = $function(
278 $data['page_title'],
279 isset( $data['menu_title'] ) ? $data['menu_title'] : $data['page_title'],
280 apply_filters( 'iworks_options_capability', 'manage_options', 'settings' ),
281 $this->get_option_name( $key ),
282 apply_filters( 'iworks_options_callback', $callback, $data, $this->options ),
283 isset( $data['position'] ) ? floatval( $data['position'] ) : null
284 );
285 add_action( 'load-' . $this->pagehooks[ $key ], array( $this, 'load_page' ) );
286 break;
287 case 'top_level':
288 $this->pagehooks[ $key ] = add_menu_page(
289 $data['page_title'],
290 isset( $data['menu_title'] ) ? $data['menu_title'] : $data['page_title'],
291 apply_filters( 'iworks_options_capability', 'manage_options', 'settings' ),
292 $this->get_option_name( $key ),
293 apply_filters( 'iworks_options_callback', $callback, $data, $this->options ),
294 apply_filters( 'iworks_options_icon_url', $data['icon_url'], $data ),
295 isset( $data['position'] ) ? floatval( $data['position'] ) : null
296 );
297 add_action( 'load-' . $this->pagehooks[ $key ], array( $this, 'load_page' ) );
298 break;
299 default:
300 if ( ! empty( $data['parent'] ) ) {
301 $this->pagehooks[ $key ] = add_submenu_page(
302 $data['parent'],
303 $data['page_title'],
304 isset( $data['menu_title'] ) ? $data['menu_title'] : $data['page_title'],
305 apply_filters( 'iworks_options_capability', 'manage_options', 'settings' ),
306 isset( $data['menu_slug'] ) ? $data['menu_slug'] : $this->get_option_name( $key ),
307 apply_filters( 'iworks_options_callback', $callback, $data, $this->options ),
308 isset( $data['position'] ) ? floatval( $data['position'] ) : null
309 );
310 add_action( 'load-' . $this->pagehooks[ $key ], array( $this, 'load_page' ) );
311 }
312 break;
313 }
314 }
315 }
316
317 /**
318 * Get the version of the options class.
319 *
320 * @since 1.0.0
321 *
322 * @return string The version of the options class.
323 */
324 public function get_version() {
325 return $this->version;
326 }
327
328 /**
329 * Set the option function name.
330 *
331 * @since 1.0.0
332 *
333 * @param string $option_function_name The option function name.
334 */
335 public function set_option_function_name( $option_function_name ) {
336 $this->option_function_name = $option_function_name;
337 }
338
339 /**
340 * Get the option function name.
341 *
342 * @since 3.1.0
343 *
344 * @return string The option function name.
345 */
346 public function get_option_function_name() {
347 return $this->option_function_name;
348 }
349
350 /**
351 * Set the option prefix.
352 *
353 * @since 1.0.0
354 *
355 * @param string $option_prefix The option prefix.
356 */
357 public function set_option_prefix( $option_prefix ) {
358 $this->option_prefix = $option_prefix;
359 }
360
361 /**
362 * Get the option array.
363 *
364 * @since 1.0.0
365 *
366 * @param string $option_group The option group.
367 *
368 * @return array The option array.
369 */
370 private function get_option_array( $option_group = null ) {
371 if ( null === $option_group ) {
372 $option_group = $this->option_group;
373 }
374 $options = array();
375 if ( is_callable( $this->option_function_name ) ) {
376 $options = apply_filters( $this->option_function_name, call_user_func( $this->option_function_name ) );
377 }
378 if ( array_key_exists( $option_group, $options ) && ! empty( $options[ $option_group ] ) ) {
379 $this->options[ $option_group ] = $options[ $option_group ];
380 return apply_filters( $this->option_function_name, $this->options[ $option_group ] );
381 }
382 return apply_filters( $this->option_function_name, array() );
383 }
384
385 /**
386 * Build the options.
387 *
388 * @since 1.0.0
389 *
390 * @param string $option_group The option group.
391 * @param bool $echo Whether to echo the options.
392 * @param int $term_id The term ID.
393 *
394 * @return void
395 */
396 public function build_options( $option_group = 'index', $echo = true, $term_id = false ) {
397 $this->option_group = $option_group;
398 $options = $this->get_option_array();
399 /**
400 * add some defaults
401 */
402 $options['show_submit_button'] = true;
403 $options['add_table'] = true;
404 if ( ! array_key_exists( 'type', $options ) ) {
405 $options['type'] = 'option';
406 }
407 /**
408 * add defaults for taxonomies
409 */
410 if ( 'taxonomy' == $options['type'] ) {
411 $options['show_submit_button'] = false;
412 $options['add_table'] = false;
413 }
414 /**
415 * check options exists?
416 */
417 if ( ! is_array( $options['options'] ) ) {
418 echo '<div class="below-h2 error"><p><strong>';
419 esc_html_e( 'An error occurred while getting the configuration.', 'reading-position-indicator' );
420 echo '</strong></p></div>';
421 return;
422 }
423 /**
424 * proceder
425 */
426 $is_simple = 'simple' == $this->get_option( 'configuration', 'index', 'advance' );
427 $content = '';
428 $hidden = '';
429 $top = '';
430 $use_tabs = isset( $options['use_tabs'] ) && $options['use_tabs'];
431 /**
432 * add last_used_tab field
433 */
434 if ( $use_tabs ) {
435 $field = array(
436 'type' => 'hidden',
437 'name' => 'last_used_tab',
438 'id' => 'last_used_tab',
439 'value' => $this->get_option( 'last_used_tab' ),
440 );
441 array_unshift( $options['options'], $field );
442 }
443 /**
444 * produce options
445 */
446 if ( $use_tabs ) {
447 $top .= sprintf(
448 '<div id="hasadmintabs" class="ui-tabs ui-widget ui-widget-content ui-corner-all" data-prefix="%s">',
449 esc_attr( $this->option_prefix )
450 );
451 }
452 $i = 0;
453 $label_index = 0;
454 $last_tab = null;
455 $related_to = array();
456 $configuration = 'all';
457 foreach ( $options['options'] as $option ) {
458 if ( isset( $option['capability'] ) ) {
459 if ( ! current_user_can( $option['capability'] ) ) {
460 continue;
461 }
462 }
463 /**
464 * add default type
465 */
466 if ( ! array_key_exists( 'type', $option ) ) {
467 $option['type'] = 'text';
468 }
469 /**
470 * check show option
471 */
472 $show_option = true;
473 if ( isset( $option['check_supports'] ) && is_array( $option['check_supports'] ) && count( $option['check_supports'] ) ) {
474 foreach ( $option['check_supports'] as $support_to_check ) {
475 if ( ! current_theme_supports( $support_to_check ) ) {
476 $show_option = false;
477 }
478 }
479 }
480 if ( ! $show_option ) {
481 continue;
482 }
483 /**
484 * dismiss on special type
485 */
486 if ( 'special' == $option['type'] ) {
487 continue;
488 }
489 /**
490 * get option name
491 */
492 $option_name = false;
493 if ( array_key_exists( 'name', $option ) && $option['name'] ) {
494 $option_name = $option['name'];
495 if ( 'taxonomy' == $options['type'] ) {
496 $option_name = sprintf(
497 '%s_%s_%s',
498 $option_group,
499 $term_id,
500 $option_name
501 );
502 }
503 }
504 /**
505 * dismiss if have "callback_to_show" and return false
506 */
507 if ( ! preg_match( '/^(heading|info)$/', $option['type'] ) && isset( $option['callback_to_show'] ) && is_callable( $option['callback_to_show'] ) ) {
508 if ( false === $option['callback_to_show']( $this->get_option( $option_name, $option_group ) ) ) {
509 continue;
510 }
511 }
512 /**
513 * heading
514 */
515 if ( preg_match( '/^(heading|page)$/', $option['type'] ) ) {
516 if ( isset( $option['configuration'] ) ) {
517 $configuration = $option['configuration'];
518 } else {
519 $configuration = 'all';
520 }
521 }
522 if ( ( $is_simple && $configuration == 'advance' ) || ( ! $is_simple && $configuration == 'simple' ) ) {
523 if ( isset( $option['configuration'] ) && 'both' == $option['configuration'] ) {
524 continue;
525 }
526 if ( in_array(
527 $option['type'],
528 array(
529 'checkbox',
530 'email',
531 'image',
532 'number',
533 'radio',
534 'text',
535 'textarea',
536 'url',
537 )
538 ) ) {
539 $html_element_name = $option_name ? $this->option_prefix . $option_name : '';
540 $content .= sprintf(
541 '<input type="hidden" name="%s" value="%s" /> %s',
542 esc_attr( $html_element_name ),
543 esc_attr( $this->get_option( $option_name, $option_group ) ),
544 PHP_EOL
545 );
546 }
547 continue;
548 }
549 $tr_classes = array(
550 'iworks-options-row',
551 sprintf( 'iworks-options-type-%s', esc_attr( strtolower( $option['type'] ) ) ),
552 );
553 if ( $option['type'] == 'heading' ) {
554 if ( $use_tabs ) {
555 if ( $last_tab != $option['label'] ) {
556 $last_tab = $option['label'];
557 if ( $options['add_table'] ) {
558 $content .= '</tbody></table>';
559 }
560 $content .= '</fieldset>';
561 }
562 $content .= sprintf(
563 '<fieldset id="iworks_%s" class="ui-tabs-panel ui-widget-content ui-corner-bottom"%s>',
564 esc_attr( crc32( $option['label'] ) ),
565 sprintf(
566 ( isset( $option['class'] ) && $option['class'] ) ?
567 sprintf( ' rel="%s"', esc_attr( $option['class'] ) ) : ''
568 )
569 );
570 if ( ! $use_tabs ) {
571 $content .= sprintf( '<h3>%s</h3>', esc_html( $option['label'] ) );
572 }
573 if ( $options['add_table'] ) {
574 $content .= sprintf(
575 '<table class="form-table%s" style="%s" role="presentation">',
576 esc_attr( isset( $options['widefat'] ) ? ' widefat' : '' ),
577 esc_attr( isset( $options['style'] ) ? $options['style'] : '' )
578 );
579 $content .= '<tbody>';
580 }
581 }
582 $content .= sprintf( '<tr class="%s"><td colspan="2">', esc_attr( implode( ' ', $tr_classes ) ) );
583 } elseif ( 'subheading' == $option['type'] ) {
584 $content .= '<tr><td colspan="2">';
585 } elseif ( 'hidden' != $option['type'] ) {
586 if ( isset( $option['related_to'] ) && isset( $related_to[ $option['related_to'] ] ) && $related_to[ $option['related_to'] ] == 0 ) {
587 $classes[] = 'hidden';
588 }
589 /**
590 * Allow to add code before the TR HTML tag.
591 *
592 * @since 2.8.3
593 *
594 * @param string $content Content, default empty string.
595 * @param array $option Current option array.
596 */
597 $content .= apply_filters( 'iworks/options/filter/tr/before/' . $option_name, '', $option );
598 $content .= PHP_EOL;
599 $content .= sprintf(
600 '<tr valign="top" id="tr_%s" class="%s">',
601 esc_attr( $option_name ? $option_name : '' ),
602 esc_attr( implode( ' ', $tr_classes ) )
603 );
604 $content .= PHP_EOL;
605 /**
606 * TH
607 */
608 $content .= '<th scope="row">';
609 /**
610 * Allow to add code before a content of the TH HTML tag.
611 *
612 * @since 2.8.3
613 *
614 * @param string $content Content, default empty string.
615 * @param array $option Current option array.
616 */
617 $content .= apply_filters( 'iworks/options/filter/th/begin/' . $option_name, '', $option );
618 $content .= isset( $option['dashicon'] ) && $option['dashicon'] ? sprintf( '<span class="dashicons dashicons-%s"></span>&nbsp;', esc_attr( $option['dashicon'] ) ) : '';
619 $content .= isset( $option['th'] ) && $option['th'] ? $option['th'] : '&nbsp;';
620 /**
621 * Allow to add code after a content of the TH HTML tag.
622 *
623 * @since 2.8.3
624 *
625 * @param string $content Content, default empty string.
626 * @param array $option Current option array.
627 */
628 $content .= apply_filters( 'iworks/options/filter/th/end/' . $option_name, '', $option );
629 $content .= '</th>';
630 /**
631 * TD
632 */
633 $content .= PHP_EOL;
634 $content .= '<td>';
635 /**
636 * Allow to add code before a content of the TD HTML tag.
637 *
638 * @since 2.8.3
639 *
640 * @param string $content Content, default empty string.
641 * @param array $option Current option array.
642 */
643 $content .= apply_filters( 'iworks/options/td/begin/' . $option_name, '', $option );
644 }
645 $html_element_name = $option_name ? $this->option_prefix . $option_name : '';
646 $filter_name = $html_element_name ? $option_group . '_' . $html_element_name : null;
647 /**
648 * classes
649 */
650 $classes = isset( $option['classes'] ) ? $option['classes'] : ( isset( $option['class'] ) ? explode( ' ', $option['class'] ) : array() );
651 $classes[] = sprintf( 'option-%s', $option['type'] );
652 /**
653 * data string
654 *
655 * @since 3.0.0
656 */
657 $data_string = '';
658 if ( isset( $option['data'] ) && is_array( $option['data'] ) ) {
659 foreach ( $option['data'] as $data_key => $data_value ) {
660 if ( $data_string ) {
661 $data_string .= ' ';
662 }
663 $data_string .= sprintf(
664 'data-%s="%s"',
665 esc_attr( $data_key ),
666 esc_attr( $data_value )
667 );
668 }
669 }
670 /**
671 * build
672 */
673 switch ( $option['type'] ) {
674 case 'hidden':
675 $hidden .= sprintf(
676 '<input type="hidden" name="%s" value="%s" id="%s"%s>',
677 esc_attr( $html_element_name ),
678 esc_attr( $this->get_option( $option_name, $option_group ) ),
679 esc_attr( isset( $option['id'] ) ? $option['id'] : '' ),
680 $data_string
681 );
682 break;
683 case 'number':
684 $args = array();
685 $args_keys = array( 'min', 'max', 'step' );
686 foreach ( $args_keys as $arg_key ) {
687 if ( isset( $option[ $arg_key ] ) ) {
688 $args[ $arg_key ] = $option[ $arg_key ];
689 }
690 }
691 if ( isset( $option['use_name_as_id'] ) && $option['use_name_as_id'] ) {
692 $args['id'] = sprintf( ' id="%s"', $html_element_name );
693 }
694 $content .= sprintf(
695 '<input type="%s" name="%s" value="%s" class="%s" %s%s> %s',
696 esc_attr( $option['type'] ),
697 esc_attr( $html_element_name ),
698 esc_attr( $this->get_option( $option_name, $option_group ) ),
699 esc_attr( implode( ' ', $classes ) ),
700 $this->build_field_attributes( $args ),
701 $data_string,
702 esc_html( isset( $option['label'] ) ? $option['label'] : '' )
703 );
704 break;
705 case 'email':
706 case 'password':
707 case 'text':
708 case 'url':
709 $id = '';
710 if ( isset( $option['use_name_as_id'] ) && $option['use_name_as_id'] ) {
711 $id = sprintf( ' id="%s"', $html_element_name );
712 }
713 $content .= sprintf(
714 '<input type="%s" name="%s" value="%s" class="%s"%s%s%s%s%s> %s',
715 esc_attr( $option['type'] ),
716 esc_attr( $html_element_name ),
717 esc_attr( $this->get_option( $option_name, $option_group ) ),
718 esc_attr( implode( ' ', $classes ) ),
719 esc_attr( $id ),
720 isset( $option['maxlength'] ) ? sprintf( ' maxlength="%d"', $option['maxlength'] ) : '',
721 isset( $option['placeholder'] ) ? sprintf( ' placeholder="%s"', esc_attr( $option['placeholder'] ) ) : '',
722 isset( $option['aria-label'] ) ? sprintf( ' aria-label="%s"', esc_attr( $option['aria-label'] ) ) : '',
723 $data_string,
724 isset( $option['label'] ) ? $option['label'] : ''
725 );
726 break;
727 case 'checkbox':
728 $related_to[ $option_name ] = $this->get_option( $option_name, $option_group );
729 $checkbox = sprintf(
730 '<label for="%s"><input type="checkbox" name="%s" id="%s" value="1"%s%s class="%s"%s> %s</label>',
731 esc_attr( $html_element_name ),
732 esc_attr( $html_element_name ),
733 esc_attr( $html_element_name ),
734 checked( $related_to[ $option_name ], true, false ),
735 ( ( isset( $option['disabled'] ) && $option['disabled'] ) or ( isset( $option['need_pro'] ) && $option['need_pro'] ) ) ? ' disabled="disabled"' : '',
736 esc_attr( implode( ' ', $classes ) ),
737 $data_string,
738 esc_html( isset( $option['label'] ) ? $option['label'] : '' )
739 );
740 $content .= apply_filters( $filter_name, $checkbox, $option );
741 break;
742 case 'checkbox_group':
743 $option_value = $this->get_option( $option_name, $option_group );
744 if ( empty( $option_value ) && isset( $option['defaults'] ) ) {
745 foreach ( $option['defaults'] as $default ) {
746 $option_value[ $default ] = $default;
747 }
748 }
749 $content .= '<ul>';
750 $i = 0;
751 if ( isset( $option['extra_options'] ) && is_callable( $option['extra_options'] ) ) {
752 $option['options'] = array_merge( $option['options'], $option['extra_options']() );
753 }
754 foreach ( $option['options'] as $value => $label ) {
755 $checked = false;
756 if ( is_array( $option_value ) && array_key_exists( $value, $option_value ) ) {
757 $checked = true;
758 }
759 $id = sprintf( '%s%d', $option_name, $i++ );
760 $content .= sprintf(
761 '<li><label for="%s"><input type="checkbox" name="%s[%s]" value="%s"%s id="%s"/> %s</label></li>',
762 esc_attr( $id ),
763 esc_attr( $html_element_name ),
764 esc_attr( $value ),
765 esc_attr( $value ),
766 checked( $checked, true, false ),
767 esc_attr( $id ),
768 esc_html( $label )
769 );
770 }
771 $content .= '</ul>';
772 break;
773 case 'radio':
774 $option_value = $this->get_option( $option_name, $option_group );
775 $i = 0;
776 /**
777 * check user add "radio" or "options".
778 */
779 $radio_options = array();
780 if ( array_key_exists( 'options', $option ) ) {
781 $radio_options = $option['options'];
782 } elseif ( array_key_exists( 'radio', $option ) ) {
783 $radio_options = $option['radio'];
784 }
785 if ( empty( $radio_options ) ) {
786 $content .= sprintf(
787 '<p>Error: no <strong>radio</strong> array key for option: <em>%s</em>.</p>',
788 esc_html( $option_name )
789 );
790 } else {
791 /**
792 * add extra options, maybe dynamic?
793 */
794 $radio_options = apply_filters( $filter_name . '_data', $radio_options, $option );
795 $radio = apply_filters( $filter_name . '_content', null, $radio_options, $html_element_name, $option_name, $option_value );
796 if ( empty( $radio ) ) {
797 foreach ( $radio_options as $value => $input ) {
798 $id = sprintf( '%s%d', $option_name, $i++ );
799 $disabled = '';
800 if ( preg_match( '/\-disabled$/', $value ) ) {
801 $disabled = 'disabled="disabled"';
802 } elseif ( isset( $input['disabled'] ) && $input['disabled'] ) {
803 $disabled = 'disabled="disabled"';
804 }
805 $classes[] = sanitize_title( $value );
806 $checked = $option_value == $value or ( empty( $option_value ) and isset( $option['default'] ) and $value == $option['default'] );
807 $radio .= sprintf(
808 '<li class="%s%s"><label for="%s"><input type="radio" name="%s" value="%s"%s id="%s" %s/> %s</label>',
809 esc_attr( implode( ' ', $classes ) ),
810 esc_attr( $disabled ? ' disabled' : '' ),
811 esc_attr( $id ),
812 esc_attr( $html_element_name ),
813 esc_attr( $value ),
814 checked( $checked, true, false ),
815 esc_attr( $id ),
816 $disabled,
817 esc_html( $input['label'] )
818 );
819 if ( isset( $input['description'] ) ) {
820 $radio .= sprintf(
821 '<br /><span class="description">%s</span>',
822 wp_kses_post( $input['description'] )
823 );
824 }
825 $radio .= '</li>';
826 }
827 if ( $radio ) {
828 $radio = sprintf( '<ul>%s</ul>', $radio );
829 }
830 } else {
831 $radio = apply_filters( $filter_name, $radio, $option );
832 if ( empty( $radio ) ) {
833 $content .= sprintf(
834 '<p>Error: no <strong>radio</strong> array key for option: <em>%s</em>.</p>',
835 esc_html( $option_name )
836 );
837 }
838 }
839 $content .= apply_filters( $filter_name, $radio, $option );
840 }
841 break;
842 case 'select':
843 case 'select2':
844 $extra = $name_sufix = '';
845 if ( 'select2' == $option['type'] ) {
846 $classes[] = 'select2';
847 if ( isset( $option['multiple'] ) && $option['multiple'] ) {
848 $extra = ' multiple="multiple"';
849 $name_sufix = '[]';
850 }
851 }
852 /**
853 * Sanitize options.
854 */
855 if ( ! isset( $option['options'] ) || ! is_array( $option['options'] ) ) {
856 $option['options'] = array();
857 }
858 $option_value = $this->get_option( $option_name, $option_group );
859 if ( isset( $option['extra_options'] ) && is_callable( $option['extra_options'] ) ) {
860 $option['options'] = array_merge( $option['options'], $option['extra_options']() );
861 }
862 $option['options'] = apply_filters( $filter_name . '_data', $option['options'], $option_name, $option_value );
863 $select = apply_filters( $filter_name . '_content', null, $option['options'], $html_element_name, $option_name, $option_value );
864 $select = apply_filters( 'iworks_options_' . $option_name . '_content', $select, $option['options'], $html_element_name, $option_name, $option_value );
865 if ( empty( $select ) ) {
866 foreach ( $option['options'] as $key => $value ) {
867 $disabled = '';
868 if ( preg_match( '/\-disabled$/', $value ) ) {
869 $disabled = 'disabled="disabled"';
870 } elseif ( isset( $input['disabled'] ) && $input['disabled'] ) {
871 $disabled = 'disabled="disabled"';
872 }
873 $selected = false;
874 if ( is_array( $option_value ) ) {
875 if ( empty( $option_value ) ) {
876 } else {
877 $selected = in_array( $key, $option_value );
878 }
879 } else {
880 $selected = ( $option_value == $key or ( empty( $option_value ) and isset( $option['default'] ) and $key == $option['default'] ) );
881 }
882 $select .= sprintf(
883 '<option %s value="%s" %s %s >%s</option>',
884 $disabled ? 'class="disabled"' : '',
885 esc_attr( $key ),
886 selected( $selected, true, false ),
887 $disabled,
888 esc_html( $value )
889 );
890 }
891 if ( $select ) {
892 $select = sprintf(
893 '<select id="%s" name="%s%s" class="%s" %s%s>%s</select>',
894 esc_attr( $html_element_name ),
895 esc_attr( $html_element_name ),
896 esc_attr( $name_sufix ),
897 esc_attr( implode( ' ', $classes ) ),
898 $extra,
899 $data_string,
900 $select
901 );
902 }
903 }
904 $content .= apply_filters( $filter_name, $select, $option );
905 break;
906 case 'textarea':
907 $value = $this->get_option( $option_name, $option_group );
908 $value = ( ! $value && isset( $option['default'] ) ) ? $option['default'] : $value;
909 $args = array(
910 'rows' => isset( $option['rows'] ) ? $option['rows'] : 3,
911 'class' => isset( $option['classes'] ) ? implode( ' ', $option['classes'] ) : '',
912 );
913 $content .= $this->textarea( $html_element_name, $value, $args );
914 break;
915 case 'heading':
916 if ( isset( $option['label'] ) && $option['label'] ) {
917 $classes = array();
918 if ( $this->get_option( 'last_used_tab' ) == $label_index ) {
919 $classes[] = 'selected';
920 }
921 $content .= sprintf(
922 '<h3 id="options-%s"%s>%s</h3>',
923 sanitize_title_with_dashes( remove_accents( $option['label'] ) ),
924 count( $classes ) ? ' class="' . implode( ' ', $classes ) . '"' : '',
925 esc_html( $option['label'] )
926 );
927 ++$label_index;
928 $i = 0;
929 }
930 break;
931 case 'info':
932 $content .= $option['value'];
933 break;
934 case 'serialize':
935 if ( isset( $option['callback'] ) && is_callable( $option['callback'] ) ) {
936 $content .= $option['callback']( $this->get_option( $option_name, $option_group ), $option_name );
937 } elseif ( isset( $option['call_user_func'] ) && isset( $option['call_user_data'] ) && is_callable( $option['call_user_func'] ) ) {
938 ob_start();
939 call_user_func_array( $option['call_user_func'], $option['call_user_data'] );
940 $content .= ob_get_contents();
941 ob_end_clean();
942 }
943 break;
944 case 'subheading':
945 $content .= sprintf( '<h2 class="title">%s</h2>', esc_html( $option['label'] ) );
946 break;
947 case 'wpColorPicker':
948 if ( is_admin() ) {
949 wp_enqueue_style( 'wp-color-picker' );
950 wp_enqueue_script( 'wp-color-picker' );
951 }
952 $id = '';
953 if ( isset( $option['use_name_as_id'] ) && $option['use_name_as_id'] ) {
954 $id = sprintf( ' id="%s"', esc_attr( $html_element_name ) );
955 }
956 $content .= apply_filters(
957 $filter_name,
958 sprintf(
959 '<input %s type="text" name="%s" value="%s" class="wpColorPicker %s" %s%s> %s',
960 esc_attr( $id ),
961 esc_attr( $html_element_name ),
962 esc_attr( $this->get_option( $option_name, $option_group ) ),
963 esc_attr( isset( $option['class'] ) && $option['class'] ? $option['class'] : '' ),
964 ( isset( $option['need_pro'] ) and $option['need_pro'] ) ? ' disabled="disabled"' : '',
965 $data_string,
966 isset( $option['label'] ) ? $option['label'] : ''
967 )
968 );
969 break;
970 case 'image':
971 if ( is_admin() ) {
972 wp_enqueue_media();
973 }
974 $src = $value = $this->get_option( $option_name, $option_group );
975 if (
976 $value
977 && preg_match( '/^\d+/', $value )
978 && 0 < intval( $value )
979 ) {
980 $src = wp_get_attachment_url( $value );
981 }
982 $content .= sprintf(
983 '<%s id="%s_img" src="%s" alt="" style="%s%s;margin-bottom:10px;"><br>',
984 esc_attr( 'img' ),
985 esc_attr( $html_element_name ),
986 esc_attr( $src ? $src : '' ),
987 array_key_exists( 'max-width', $option ) && is_integer( $option['max-width'] ) ? sprintf( 'max-width: %dpx;', $option['max-width'] ) : '',
988 array_key_exists( 'max-height', $option ) && is_integer( $option['max-height'] ) ? sprintf( 'max-height: %dpx;', $option['max-height'] ) : ''
989 );
990 $content .= sprintf(
991 '<input type="hidden" name="%s" value="%s" />',
992 esc_attr( $html_element_name ),
993 esc_attr( $this->get_option( $option_name, $option_group ) ),
994 esc_attr( $value )
995 );
996 $content .= sprintf(
997 ' <input type="button" class="button iworks_upload_button" value="%s" rel="#%s" />',
998 esc_attr__( 'Select Image', 'reading-position-indicator' ),
999 esc_attr( $html_element_name )
1000 );
1001 $content .= sprintf(
1002 ' <input type="button" class="button iworks_delete_button" value="%s" rel="#%s" %s/>',
1003 esc_attr__( 'Delete image', 'reading-position-indicator' ),
1004 esc_attr( $html_element_name ),
1005 empty( $value ) ? ' style="display:none"' : ''
1006 );
1007 break;
1008 /**
1009 * handle `button` field type
1010 *
1011 * @since 2.6.9
1012 */
1013 case 'button':
1014 $classes[] = 'button';
1015 $content .= sprintf(
1016 '<input type="button" name="%s" value="%s" class="%s" data-nonce="%s"%s>',
1017 esc_attr( $html_element_name ),
1018 esc_attr( $option['value'] ),
1019 esc_attr( implode( ' ', $classes ) ),
1020 wp_create_nonce( $html_element_name ),
1021 $data_string
1022 );
1023 break;
1024 default:
1025 $content .= sprintf( 'not implemented type: %s', esc_html( $option['type'] ) );
1026 }
1027 if ( 'hidden' !== $option['type'] ) {
1028 if ( isset( $option['description'] ) && $option['description'] ) {
1029 if ( isset( $option['label'] ) && $option['label'] && 'subheading' !== $option['type'] ) {
1030 $content .= '<br />';
1031 }
1032 $content .= sprintf( '<p class="description">%s</p>', wp_kses_post( $option['description'] ) );
1033 }
1034 /**
1035 * Allow to add code after a content of the TD HTML tag.
1036 *
1037 * @since 2.8.3
1038 *
1039 * @param string $content Content, default empty string.
1040 * @param array $option Current option array.
1041 */
1042 $content .= apply_filters( 'iworks/options/td/end/' . $option_name, '', $option );
1043 $content .= '</td>';
1044 $content .= '</tr>';
1045 $content .= PHP_EOL;
1046 /**
1047 * Allow to add code before the TR HTML tag.
1048 *
1049 * @since 2.8.3
1050 *
1051 * @param string $content Content, default empty string.
1052 * @param array $option Current option array.
1053 */
1054 $content .= apply_filters( 'iworks/options/filter/tr/after/' . $option_name, '', $option );
1055 }
1056 }
1057 /**
1058 * filter
1059 */
1060 if ( isset( $option['filter'] ) ) {
1061 $content .= apply_filters( $option['filter'], '' );
1062 }
1063 /**
1064 * content
1065 */
1066 if ( $content ) {
1067 if ( isset( $options['label'] ) && $options['label'] && ! $use_tabs ) {
1068 $top .= sprintf( '<h3>%s</h3>', esc_html( $options['label'] ) );
1069 }
1070 $top .= $hidden;
1071 if ( $use_tabs ) {
1072 if ( $options['add_table'] ) {
1073 $content .= '</tbody></table>';
1074 }
1075 $content .= '</fieldset>';
1076 $content = $top . $content;
1077 } else {
1078 if ( $options['add_table'] ) {
1079 $top .= sprintf(
1080 '<table class="form-table%s" style="%s" role="presentation">',
1081 esc_attr( isset( $options['widefat'] ) ? ' widefat' : '' ),
1082 esc_attr( isset( $options['style'] ) ? $options['style'] : '' )
1083 );
1084 if ( isset( $options['thead'] ) ) {
1085 $top .= sprintf( '<thead><tr class="%s">', esc_attr( implode( ' ', $tr_classes ) ) );
1086 foreach ( $options['thead'] as $text => $colspan ) {
1087 $top .= sprintf(
1088 '<th%s>%s</th>',
1089 $colspan > 1 ? ' colspan="' . intval( $colspan ) . '"' : '',
1090 esc_html( $text )
1091 );
1092 }
1093 $top .= '</tr></thead>';
1094 }
1095 $top .= '<tbody>';
1096 }
1097 $content = $top . $content;
1098 if ( $options['add_table'] ) {
1099 $content .= '</tbody></table>';
1100 }
1101 }
1102 }
1103 if ( $use_tabs ) {
1104 $content .= '</div>';
1105 }
1106 /**
1107 * submit button
1108 */
1109 if ( $options['show_submit_button'] ) {
1110 $content .= get_submit_button(
1111 esc_html__( 'Save Changes', 'reading-position-indicator' ),
1112 'primary',
1113 'submit_button'
1114 );
1115 }
1116 /**
1117 * add tags to wp_kses()
1118 */
1119 $tags = $this->get_allowed_tags();
1120 /**
1121 * iworks-options wrapper
1122 */
1123 $content = sprintf(
1124 '<div class="iworks-options">%s</div>',
1125 wp_kses( $content, $tags )
1126 );
1127 /* print ? */
1128 if ( $echo ) {
1129 /**
1130 * this is alredy escaped
1131 */
1132 echo wp_kses( $content, $tags );
1133 return;
1134 }
1135 return $content;
1136 }
1137
1138 /**
1139 * Register settings.
1140 *
1141 * @since 1.0.0
1142 *
1143 * @param array $options The options.
1144 * @param string $option_group The option group.
1145 *
1146 * @return void
1147 */
1148 private function register_setting( $options, $option_group ) {
1149 foreach ( $options as $option ) {
1150 /**
1151 * don't register setting without type and name
1152 */
1153 if ( ! is_array( $option ) || ! array_key_exists( 'type', $option ) || ! array_key_exists( 'name', $option ) ) {
1154 continue;
1155 }
1156 /**
1157 * don't register certain type setting or with empty name
1158 */
1159 if (
1160 empty( $option['name'] )
1161 || preg_match( '/^(sub)?heading$/', $option['type'] )
1162 ) {
1163 continue;
1164 }
1165 /**
1166 * register setting
1167 */
1168 $args = array(
1169 'type' => 'string',
1170 'sanitize_callback' => 'sanitize_text_field',
1171 'logger' => 'no',
1172 );
1173 /**
1174 * logger
1175 */
1176 if ( isset( $option['logger'] ) && $option['logger'] ) {
1177 $args['logger'] = $option['logger'];
1178 }
1179 /**
1180 * set type
1181 */
1182 if ( isset( $option['type'] ) ) {
1183 switch ( $option['type'] ) {
1184 case 'string':
1185 case 'boolean':
1186 case 'integer':
1187 case 'number':
1188 case 'array':
1189 case 'object':
1190 $args['type'] = $option['type'];
1191 break;
1192 case 'text':
1193 case 'textarea':
1194 case 'wpColorPicker':
1195 $args['type'] = 'string';
1196 break;
1197 case 'checkbox':
1198 $args['type'] = 'integer';
1199 break;
1200 case 'array':
1201 case 'object':
1202 case 'checkbox_group':
1203 $args['type'] = $option['type'];
1204 unset( $args['sanitize_callback'] );
1205 break;
1206 }
1207 }
1208 /**
1209 * set own callback
1210 */
1211 if ( isset( $option['sanitize_callback'] ) ) {
1212 $args['sanitize_callback'] = $option['sanitize_callback'];
1213 }
1214 /**
1215 * set description
1216 */
1217 if ( isset( $option['description'] ) ) {
1218 $args['description'] = wp_kses_post( $option['description'] );
1219 }
1220 /**
1221 * need to flush_rewrite_rules?
1222 */
1223 if ( isset( $option['flush_rewrite_rules'] ) ) {
1224 $action = sprintf( 'update_option_%s%s', $this->option_prefix, $option['name'] );
1225 add_action( $action, array( $this, 'flush_rewrite_rules' ) );
1226 }
1227 /**
1228 * remove sanitize_callback for complex object
1229 * https://github.com/iworks/wordpress-options-class/issues/4
1230 *
1231 * @since 2.9.9
1232 */
1233 if ( isset( $option['multiple'] ) && $option['multiple'] ) {
1234 unset( $args['sanitize_callback'] );
1235 }
1236 /**
1237 * register
1238 */
1239 register_setting(
1240 $this->option_prefix . $option_group,
1241 $this->option_prefix . $option['name'],
1242 $args
1243 );
1244 }
1245 }
1246
1247 public function options_init() {
1248 $options = $this->get_option_array();
1249 /**
1250 * register_setting last_used_tab field
1251 */
1252 if ( isset( $options['use_tabs'] ) && $options['use_tabs'] ) {
1253 register_setting(
1254 $this->option_prefix . 'index',
1255 $this->option_prefix . 'last_used_tab',
1256 array(
1257 'type' => 'string',
1258 'sanitize_callback' => 'sanitize_text_field',
1259 )
1260 );
1261 }
1262 /**
1263 * filter it
1264 */
1265 $options = apply_filters( $this->option_function_name, $options );
1266 /**
1267 * register_setting
1268 */
1269 foreach ( $options as $key => $data ) {
1270 if ( isset( $data['options'] ) && is_array( $data['options'] ) ) {
1271 $this->register_setting( $data['options'], $key );
1272 } elseif ( 'options' == $key ) {
1273 $key = $this->mode;
1274 if ( ! empty( $this->option_group ) ) {
1275 $key = $this->option_group;
1276 }
1277 $this->register_setting( $data, $key );
1278 }
1279 }
1280 }
1281
1282 /**
1283 * Get values.
1284 *
1285 * @since 1.0.0
1286 *
1287 * @param string $option_name The option name.
1288 * @param string $option_group The option group.
1289 *
1290 * @return mixed The values.
1291 */
1292 public function get_values( $option_name, $option_group = 'index' ) {
1293 $this->option_group = $option_group;
1294 $data = $this->get_option_array();
1295 $data = $data['options'];
1296 foreach ( $data as $one ) {
1297 if ( isset( $one['name'] ) && $one['name'] != $option_name ) {
1298 continue;
1299 }
1300 switch ( $one['type'] ) {
1301 case 'checkbox_group':
1302 return $one['options'];
1303 case 'radio':
1304 return $one['radio'];
1305 }
1306 }
1307 return;
1308 }
1309
1310 /**
1311 * Get the default value for an option.
1312 *
1313 * @since 1.0.0
1314 *
1315 * @param string $option_name The name of the option to get the default value for.
1316 * @param string $option_group The option group name. Default 'index'.
1317 *
1318 * @return mixed The default value of the option if set, null otherwise.
1319 */
1320 public function get_default_value( $option_name, $option_group = 'index' ) {
1321 $this->option_group = $option_group;
1322 $options = $this->get_option_array();
1323 /**
1324 * check options exists?
1325 */
1326 if ( ! array_key_exists( 'options', $options ) or ! is_array( $options['options'] ) ) {
1327 return null;
1328 }
1329 /**
1330 * default key name
1331 */
1332 $default_option_name = $option_name;
1333 /**
1334 * default name for taxonomies
1335 */
1336 if ( array_key_exists( 'type', $options ) && 'taxonomy' == $options['type'] ) {
1337 $re = sprintf( '/^%s_\d+_/', $option_group );
1338 $default_option_name = preg_replace( $re, '', $default_option_name );
1339 }
1340 foreach ( $options['options'] as $option ) {
1341 if ( isset( $option['name'] ) && $option['name'] == $default_option_name ) {
1342 return isset( $option['default'] ) ? $option['default'] : null;
1343 }
1344 }
1345 return null;
1346 }
1347
1348 /**
1349 * Save default options for the plugin.
1350 *
1351 * This method is typically called during plugin activation to store default values
1352 * for all options that have them defined.
1353 *
1354 * @since 1.0.0
1355 *
1356 * @return void
1357 */
1358 public function activate() {
1359 $options = apply_filters( $this->option_function_name, call_user_func( $this->option_function_name ) );
1360 foreach ( $options as $key => $data ) {
1361 if ( ! is_array( $data ) ) {
1362 continue;
1363 }
1364 if ( ! isset( $data['options'] ) ) {
1365 continue;
1366 }
1367 foreach ( $data['options'] as $option ) {
1368 if (
1369 ( isset( $option['type'] ) && $option['type'] == 'heading' )
1370 or ! isset( $option['name'] )
1371 or ! $option['name'] or ! isset( $option['default'] )
1372 ) {
1373 continue;
1374 }
1375 add_option( $this->option_prefix . $option['name'], $option['default'], '', isset( $option['autoload'] ) ? $option['autoload'] : 'yes' );
1376 }
1377 }
1378 add_option( $this->option_prefix . 'cache_stamp', gmdate( 'c' ) );
1379 }
1380
1381 /**
1382 * Delete options on deactivate.
1383 *
1384 * @since 1.0.0
1385 *
1386 * @return void
1387 */
1388 public function deactivate() {
1389 $options = apply_filters( $this->option_function_name, call_user_func( $this->option_function_name ) );
1390 foreach ( $options as $key => $data ) {
1391 if ( ! is_array( $data ) ) {
1392 continue;
1393 }
1394 if ( ! isset( $data['options'] ) ) {
1395 continue;
1396 }
1397 foreach ( $data['options'] as $option ) {
1398 if (
1399 ( isset( $option['type'] ) && 'heading' == $option['type'] )
1400 or ! isset( $option['name'] )
1401 or ! $option['name']
1402 ) {
1403 continue;
1404 }
1405 /**
1406 * prevent special options
1407 */
1408 if ( isset( $option['dont_deactivate'] ) && $option['dont_deactivate'] ) {
1409 continue;
1410 }
1411 delete_option( $this->option_prefix . $option['name'] );
1412 }
1413 }
1414 delete_option( $this->option_prefix . 'cache_stamp' );
1415 delete_option( $this->option_prefix . 'version' );
1416 delete_option( $this->option_prefix . 'flush_rules' );
1417 }
1418
1419 /**
1420 * Output settings fields for a specific option group.
1421 *
1422 * @since 1.0.0
1423 *
1424 * @param string $option_name The option name.
1425 * @param bool $use_prefix Whether to use the option prefix. Default true.
1426 */
1427 public function settings_fields( $option_name, $use_prefix = true ) {
1428 if ( $use_prefix ) {
1429 settings_fields( $this->option_prefix . $option_name );
1430 } else {
1431 settings_fields( $option_name );
1432 }
1433 }
1434
1435 /**
1436 * Output admin notices.
1437 *
1438 * @since 1.0.0
1439 *
1440 * @return void
1441 */
1442 public function admin_notices() {
1443 if ( empty( $this->notices ) ) {
1444 return;
1445 }
1446 foreach ( $this->notices as $notice ) {
1447 printf( '<div class="error"><p>%s</p></div>', esc_html( $notice ) );
1448 }
1449 }
1450
1451 /**
1452 * Add an option.
1453 *
1454 * @since 1.0.0
1455 *
1456 * @param string $option_name The option name.
1457 * @param mixed $option_value The option value.
1458 * @param bool $autoload Whether to autoload the option. Default true.
1459 *
1460 * @return void
1461 */
1462 public function add_option( $option_name, $option_value, $autoload = true ) {
1463 $autoload = $autoload ? 'yes' : 'no';
1464 add_option( $this->option_prefix . $option_name, $option_value, '', $autoload );
1465 }
1466
1467 /**
1468 * Get an option.
1469 *
1470 * @since 1.0.0
1471 *
1472 * @param string $option_name The option name.
1473 * @param string $option_group The option group.
1474 * @param mixed $default_value The default value.
1475 * @param bool $forece_default Whether to force the default value. Default false.
1476 *
1477 * @return mixed The option value.
1478 */
1479 public function get_option( $option_name, $option_group = 'index', $default_value = null, $forece_default = false ) {
1480 $option_value = get_option( $this->option_prefix . $option_name, null );
1481 $default_value = $this->get_default_value( $option_name, $option_group );
1482 if ( ( $default_value || $forece_default ) && is_null( $option_value ) ) {
1483 $option_value = $default_value;
1484 }
1485 return apply_filters(
1486 sprintf(
1487 'iworks/option/get/%s/%s/%s',
1488 $this->plugin,
1489 $option_group,
1490 $option_name
1491 ),
1492 $option_value
1493 );
1494 }
1495
1496 /**
1497 * Get all options.
1498 *
1499 * @since 1.0.0
1500 *
1501 * @return array The options.
1502 */
1503 public function get_all_options() {
1504 $data = array();
1505 $options = $this->get_option_array();
1506 foreach ( $options['options'] as $option ) {
1507 if ( ! array_key_exists( 'name', $option ) || ! $option['name'] ) {
1508 continue;
1509 }
1510 $value = $this->get_option( $option['name'] );
1511 if ( array_key_exists( 'sanitize_callback', $option ) && is_callable( $option['sanitize_callback'] ) ) {
1512 $value = call_user_func( $option['sanitize_callback'], $value );
1513 }
1514 $data[ $option['name'] ] = $value;
1515 }
1516 return $data;
1517 }
1518
1519 /**
1520 * Get option name by adding prefix.
1521 *
1522 * @since 1.0
1523 * @since 2.6.4 Added `hidden` argument.
1524 *
1525 * @param string $name Name of option.
1526 * @param boolean $hidden If this hidden value.
1527 *
1528 * @return string $option_name
1529 */
1530 public function get_option_name( $name, $hidden = false ) {
1531 $option_name = sprintf( '%s%s', $this->option_prefix, $name );
1532 if ( $hidden ) {
1533 $option_name = sprintf( '_%s', $option_name );
1534 }
1535 return $option_name;
1536 }
1537
1538 /**
1539 * Update an option.
1540 *
1541 * @since 1.0.0
1542 *
1543 * @param string $option_name The option name.
1544 * @param mixed $option_value The option value.
1545 *
1546 * @return void
1547 */
1548 public function update_option( $option_name, $option_value ) {
1549 /**
1550 * delete if option have a default value
1551 */
1552 $default_value = $this->get_default_value( $this->option_prefix . $option_name );
1553 if ( $option_value === $default_value ) {
1554 delete_option( $this->option_prefix . $option_name );
1555 return;
1556 }
1557 update_option( $this->option_prefix . $option_name, $option_value );
1558 }
1559
1560 /**
1561 * Update taxonomy options.
1562 *
1563 * @since 1.0.0
1564 *
1565 * @param string $option_group The option group.
1566 * @param int $term_id The term ID.
1567 *
1568 * @return void
1569 */
1570 public function update_taxonomy_options( $option_group, $term_id ) {
1571 /**
1572 * check for nonce
1573 */
1574 $nonce_value = $this->get_nonce_value();
1575 if (
1576 is_wp_error( $nonce_value )
1577 || ! wp_verify_nonce( $nonce_value, $this->get_nonce_name() ) ) {
1578 return;
1579 }
1580 /**
1581 * groups
1582 */
1583 $this->option_group = $option_group;
1584 $options = $this->get_option_array();
1585 /**
1586 * only for taxonomies
1587 */
1588 if ( ! array_key_exists( 'type', $options ) ) {
1589 return;
1590 }
1591 if ( 'taxonomy' != $options['type'] ) {
1592 return;
1593 }
1594 foreach ( $options['options'] as $option ) {
1595 if ( ! array_key_exists( 'name', $option ) || ! $option['name'] ) {
1596 continue;
1597 }
1598 $option_name = sprintf(
1599 '%s_%s_%s',
1600 $option_group,
1601 $term_id,
1602 $option['name']
1603 );
1604 /**
1605 * get & sanitize value
1606 *
1607 * @since 2.8.6 - added `sanitize_text_field`.
1608 */
1609 $value = false;
1610 if ( array_key_exists( $this->get_option_name( $option_name ), $_POST ) ) {
1611 $value = sanitize_text_field( wp_unslash( $_POST[ $this->get_option_name( $option_name ) ] ) );
1612 }
1613 /**
1614 * add custom sanitization
1615 *
1616 * @since 2.8.6
1617 */
1618 if ( array_key_exists( 'sanitize_callback', $option ) && is_callable( $option['sanitize_callback'] ) ) {
1619 $value = call_user_func( $option['sanitize_callback'], $value );
1620 }
1621 if ( $value ) {
1622 $this->update_option( $option_name, $value );
1623 } else {
1624 delete_option( $option_name );
1625 }
1626 }
1627 }
1628
1629 /**
1630 * Helper method to create page dropdown.
1631 *
1632 * Creates a WordPress page dropdown select element.
1633 *
1634 * @since 1.0.0
1635 *
1636 * @param string $name Option name.
1637 * @param bool $show_option_none Whether to show a "none" option. Default false.
1638 * @param string $post_type Post type to show. Default 'page'.
1639 * @return string HTML dropdown markup.
1640 */
1641 public function select_page_helper( $name, $show_option_none = false, $post_type = 'page' ) {
1642 $args = array(
1643 'echo' => false,
1644 'name' => esc_attr( $this->get_option_name( $name ) ),
1645 'selected' => esc_attr( $this->get_option( $name ) ),
1646 'show_option_none' => esc_attr( $show_option_none ),
1647 'post_type' => esc_attr( $post_type ),
1648 );
1649 return wp_dropdown_pages( $args ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
1650 }
1651
1652 /**
1653 * Helper method to create category dropdown.
1654 *
1655 * Creates a WordPress category dropdown select element.
1656 *
1657 * @since 1.0.0
1658 *
1659 * @param string $name Option name.
1660 * @param bool|null $hide_empty Whether to hide empty categories. Default null.
1661 * @param bool $show_option_none Whether to show a "none" option. Default false.
1662 * @return string HTML dropdown markup.
1663 */
1664 public function select_category_helper( $name, $hide_empty = null, $show_option_none = false ) {
1665 $args = array(
1666 'echo' => false,
1667 'name' => $this->get_option_name( $name ),
1668 'selected' => $this->get_option( $name ),
1669 'hierarchical' => true,
1670 'hide_empty' => $hide_empty,
1671 );
1672 if ( $show_option_none ) {
1673 $args['show_option_none'] = true;
1674 }
1675 return wp_dropdown_categories( $args );
1676 }
1677
1678 /**
1679 * Get the current option group.
1680 *
1681 * @since 1.0.0
1682 *
1683 * @return string The option group.
1684 */
1685 public function get_option_group() {
1686 return $this->option_group;
1687 }
1688
1689 /**
1690 * Get the option index from the current screen.
1691 *
1692 * @since 1.0.0
1693 *
1694 * @return false|string The option index or false if not found.
1695 */
1696 private function get_option_index_from_screen() {
1697 $screen = get_current_screen();
1698 $key = explode( $this->option_prefix, $screen->id );
1699 if ( 2 != count( $key ) ) {
1700 return false;
1701 }
1702 return $key[1];
1703 }
1704
1705 /**
1706 * Show the options page.
1707 *
1708 * @since 1.0.0
1709 *
1710 * @param bool $check_option_name Whether to check the option name. Default true.
1711 * @param string $url The URL to redirect to. Default 'options.php'.
1712 *
1713 * @return void
1714 */
1715 public function show_page( $check_option_name = true, $url = 'options.php' ) {
1716 $options = array();
1717 $option_name = 'index';
1718 if ( $check_option_name ) {
1719 $option_name = $this->get_option_index_from_screen();
1720 if ( ! $option_name ) {
1721 return;
1722 }
1723 $options = $this->options[ $option_name ];
1724 } else {
1725 $options = $this->get_option_array();
1726 }
1727 global $screen_layout_columns;
1728 $data = array();
1729 echo '<div class="wrap iworks_options">';
1730 printf( '<h1>%s</h1>', esc_html( $options['page_title'] ) );
1731 /**
1732 * Hook before form
1733 *
1734 * @since 3.1.0
1735 */
1736 do_action( 'iworks/options/show_page/form/before', $this->get_option_function_name() );
1737 ?>
1738 <form method="post" action="<?php echo esc_url( $url ); ?>" id="<?php echo esc_attr( $this->get_option_name( 'admin_index' ) ); ?>">
1739 <?php wp_nonce_field( 'closedpostboxes', 'closedpostboxesnonce', false ); ?>
1740 <?php wp_nonce_field( 'meta-box-order', 'meta-box-order-nonce', false ); ?>
1741 <input type="hidden" name="action" value="save_howto_metaboxes_general" />
1742 <div class="metabox-holder<?php echo esc_attr( empty( $screen_layout_columns ) || 2 == $screen_layout_columns ? ' has-right-sidebar' : '' ); ?>">
1743 <?php
1744 /**
1745 * check metaboxes for key
1746 */
1747 if ( array_key_exists( 'metaboxes', $this->options[ $option_name ] ) ) {
1748 ?>
1749 <div id="side-info-column" class="inner-sidebar">
1750 <?php do_meta_boxes( $this->pagehooks[ $option_name ], 'side', $this ); ?>
1751 </div>
1752 <?php } ?>
1753 <div id="post-body" class="has-sidebar">
1754 <div id="post-body-content" class="has-sidebar-content">
1755 <?php
1756 /**
1757 * Hook before settings fields
1758 *
1759 * @since 3.1.0
1760 */
1761 do_action( 'iworks/options/show_page/settings_fields/before', $this->get_option_function_name() );
1762 $this->settings_fields( $option_name );
1763 $this->build_options( $option_name );
1764 /**
1765 * Hook after build options
1766 *
1767 * @since 3.1.0
1768 */
1769 do_action( 'iworks/options/show_page/build_options/after', $this->get_option_function_name() );
1770 ?>
1771 </div>
1772 </div>
1773 <br class="clear"/>
1774 </div>
1775 </form>
1776 <?php
1777 /**
1778 * Hook after form
1779 *
1780 * @since 3.1.0
1781 */
1782 do_action( 'iworks/options/show_page/form/after', $this->get_option_function_name() );
1783 echo '</div>';
1784 }
1785
1786 /**
1787 * Load the options page.
1788 *
1789 * @since 1.0.0
1790 *
1791 * @return void
1792 */
1793 public function load_page() {
1794 $option_name = $this->get_option_index_from_screen();
1795 if ( ! $option_name ) {
1796 return;
1797 }
1798 /**
1799 * check options for key
1800 */
1801 if ( ! array_key_exists( $option_name, $this->options ) ) {
1802 return;
1803 }
1804 /**
1805 * check metaboxes for key
1806 */
1807 if (
1808 array_key_exists( 'metaboxes', $this->options[ $option_name ] )
1809 && count( $this->options[ $option_name ]['metaboxes'] )
1810 ) {
1811 /**
1812 * ensure, that the needed javascripts been loaded to allow drag/drop,
1813 * expand/collapse and hide/show of boxes
1814 */
1815 wp_enqueue_script( 'common' );
1816 wp_enqueue_script( 'wp-lists' );
1817 wp_enqueue_script( 'postbox' );
1818 foreach ( $this->options[ $option_name ]['metaboxes'] as $id => $data ) {
1819 add_meta_box(
1820 $id,
1821 $data['title'],
1822 $data['callback'],
1823 $this->pagehooks[ $option_name ],
1824 $data['context'],
1825 $data['priority']
1826 );
1827 }
1828 }
1829 /**
1830 * wp_enqueue_script
1831 */
1832 if ( array_key_exists( 'enqueue_scripts', $this->options[ $option_name ] ) ) {
1833 $scripts = array();
1834 if ( is_admin() && isset( $this->options[ $option_name ]['enqueue_scripts']['admin'] ) ) {
1835 $scripts = $this->options[ $option_name ]['enqueue_scripts']['admin'];
1836 } elseif ( ! is_admin() && isset( $this->options[ $option_name ]['enqueue_scripts']['frontend'] ) ) {
1837 $scripts = $this->options[ $option_name ]['enqueue_scripts']['frontend'];
1838 } else {
1839 $scripts = $this->options[ $option_name ]['enqueue_scripts'];
1840 }
1841 foreach ( $scripts as $script ) {
1842 wp_enqueue_script( $script );
1843 }
1844 }
1845 /**
1846 * wp_enqueue_style
1847 */
1848 if ( array_key_exists( 'enqueue_styles', $this->options[ $option_name ] ) ) {
1849 $styles = array();
1850 if ( is_admin() && isset( $this->options[ $option_name ]['enqueue_styles']['admin'] ) ) {
1851 $styles = $this->options[ $option_name ]['enqueue_styles']['admin'];
1852 } elseif ( ! is_admin() && isset( $this->options[ $option_name ]['enqueue_styles']['frontend'] ) ) {
1853 $styles = $this->options[ $option_name ]['enqueue_styles']['frontend'];
1854 } else {
1855 $styles = $this->options[ $option_name ]['enqueue_styles'];
1856 }
1857 foreach ( $styles as $style ) {
1858 wp_enqueue_style( $style );
1859 }
1860 }
1861 }
1862
1863 /**
1864 * Set the number of columns for the options page.
1865 *
1866 * @since 1.0.0
1867 *
1868 * @param array $columns The columns.
1869 * @param object $screen The screen.
1870 *
1871 * @return array The columns.
1872 */
1873 public function screen_layout_columns( $columns, $screen ) {
1874 foreach ( $this->pagehooks as $option_name => $pagehook ) {
1875 if ( $screen == $pagehook ) {
1876 $columns[ $pagehook ] = 2;
1877 }
1878 }
1879 return $columns;
1880 }
1881
1882 /**
1883 * Get options by group.
1884 *
1885 * @since 1.0.0
1886 *
1887 * @param string $group The group.
1888 *
1889 * @return array The options.
1890 */
1891 public function get_options_by_group( $group ) {
1892 $opts = array();
1893 $options = $this->get_option_array();
1894 if ( ! isset( $options['options'] ) || empty( $options['options'] ) ) {
1895 return $options;
1896 }
1897 foreach ( $options['options'] as $one ) {
1898 if ( ! isset( $one['name'] ) || ! isset( $one['type'] ) ) {
1899 continue;
1900 }
1901 if ( ! isset( $one['group'] ) || $group != $one['group'] ) {
1902 continue;
1903 }
1904 $opts[] = $one;
1905 }
1906 return $opts;
1907 }
1908
1909 /**
1910 * Get field by type.
1911 *
1912 * @since 1.0.0
1913 *
1914 * @param string $type The type.
1915 * @param string $name The name.
1916 * @param mixed $value The value.
1917 * @param array $args The arguments.
1918 *
1919 * @return string The field.
1920 */
1921 public function get_field_by_type( $type, $name, $value = '', $args = array() ) {
1922 if ( method_exists( $this, $type ) ) {
1923 wp_enqueue_style( __CLASS__ );
1924 if ( ! isset( $args['class'] ) ) {
1925 switch ( $type ) {
1926 case 'switch_button':
1927 wp_enqueue_script( __CLASS__ );
1928 wp_enqueue_style( 'switch_button' );
1929 break;
1930 case 'checkbox':
1931 case 'radio':
1932 break;
1933 default:
1934 $args['class'] = array( 'large-text' );
1935 break;
1936 }
1937 }
1938 $args['class'][] = sprintf( 'iworks-options-%s', preg_replace( '/_/', '-', esc_attr( $type ) ) );
1939 return $this->$type( $name, $value, $args );
1940 }
1941 return sprintf( 'wrong type: %s', esc_html( $type ) );
1942 }
1943
1944 /**
1945 * Build HTML attributes string from array.
1946 *
1947 * Converts an array of attributes to a properly formatted HTML attribute string.
1948 *
1949 * @since 1.0.0
1950 *
1951 * @param array $args Array of attributes.
1952 * @return string Formatted HTML attributes.
1953 */
1954 private function build_field_attributes( $args ) {
1955 $atts = '';
1956 foreach ( $args as $key => $value ) {
1957 if ( is_array( $value ) ) {
1958 $value = implode( ' ', $value );
1959 }
1960 $atts .= sprintf( ' %s="%s"', esc_html( $key ), esc_attr( trim( $value ) ) );
1961 }
1962 return $atts;
1963 }
1964
1965 /**
1966 * Print HTML select
1967 *
1968 * @since 1.0.0
1969 *
1970 * @since 3.0.8 handle multiple select
1971 *
1972 * @param string $name The name.
1973 * @param mixed $value The value.
1974 * @param array $args The arguments.
1975 *
1976 * @return void
1977 */
1978 private function select( $name, $value = '', $args = array() ) {
1979 /**
1980 * default value
1981 */
1982 if ( isset( $args['default'] ) ) {
1983 if ( empty( $value ) ) {
1984 $value = $args['default'];
1985 }
1986 unset( $args['default'] );
1987 }
1988 /**
1989 * options
1990 */
1991 $options = array();
1992 if ( isset( $args['options'] ) ) {
1993 $options = $args['options'];
1994 unset( $args['options'] );
1995 }
1996 if ( empty( $options ) && ! empty( $value ) ) {
1997 $options[ $value['value'] ] = $value['label'];
1998 }
1999 /**
2000 * value to check
2001 */
2002 $value_to_check = is_array( $value ) && isset( $value['value'] ) ? $value['value'] : $value;
2003 /**
2004 * is multiple?
2005 */
2006 $multiple = false;
2007 if ( isset( $args['multiple'] ) && $args['multiple'] ) {
2008 $multiple = true;
2009 if ( ! is_array( $value_to_check ) ) {
2010 $value_to_check = array( $value_to_check );
2011 }
2012 }
2013 $content = sprintf(
2014 '<select name="%s" %s>',
2015 esc_attr( $name ),
2016 $this->build_field_attributes( $args )
2017 );
2018 foreach ( $options as $val => $label ) {
2019 $checked = '';
2020 if ( $multiple ) {
2021 if ( is_array( $value_to_check ) && in_array( $val, $value_to_check ) ) {
2022 $checked = ' selected';
2023 }
2024 } else {
2025 if ( $val === $value_to_check ) {
2026 $checked = ' selected';
2027 }
2028 }
2029 $content .= sprintf(
2030 '<option value="%s"%s>%s</option>',
2031 esc_attr( $val ),
2032 $checked,
2033 esc_html( $label )
2034 );
2035 }
2036 $content .= '</select>';
2037 return $content;
2038 }
2039
2040 /**
2041 * Common input class.
2042 *
2043 * @since 1.0.0
2044 *
2045 * @param string $name The name.
2046 * @param mixed $value The value.
2047 * @param array $args The arguments.
2048 * @param string $type The type.
2049 *
2050 * @return void
2051 */
2052 private function input( $name, $value = '', $args = array(), $type = 'text' ) {
2053 /**
2054 * default value
2055 */
2056 if ( isset( $args['default'] ) ) {
2057 if ( empty( $value ) ) {
2058 $value = $args['default'];
2059 }
2060 unset( $args['default'] );
2061 }
2062 /**
2063 * turn off autocomplete
2064 */
2065 if ( 'text' == $type ) {
2066 if ( ! isset( $args['autocomplete'] ) ) {
2067 $args['autocomplete'] = 'off';
2068 }
2069 }
2070 /**
2071 * before & after
2072 */
2073 $keys = array( 'before', 'after' );
2074 foreach ( $keys as $key ) {
2075 $$key = '';
2076 if ( isset( $args[ $key ] ) ) {
2077 $$key = $args[ $key ];
2078 unset( $args[ $key ] );
2079 }
2080 }
2081 /**
2082 * produce
2083 */
2084 return sprintf(
2085 '%s<input type="%s" name="%s" value="%s" %s />%s',
2086 $before,
2087 esc_attr( $type ),
2088 esc_attr( $name ),
2089 esc_attr( $value ),
2090 $this->build_field_attributes( $args ),
2091 $after
2092 );
2093 }
2094
2095 /**
2096 * Switch button element (based on checkbox field).
2097 *
2098 * @since 2.6.4
2099 *
2100 * @param string $name The name.
2101 * @param mixed $value The value.
2102 * @param array $args The arguments.
2103 *
2104 * @return string The switch button.
2105 */
2106 private function switch_button( $name, $value = '', $args = array() ) {
2107 return $this->checkbox( $name, $value, $args );
2108 }
2109
2110 /**
2111 * Select2 input element.
2112 *
2113 * @since 2.6.4
2114 *
2115 * @param string $name The name.
2116 * @param mixed $value The value.
2117 * @param array $args The arguments.
2118 *
2119 * @return string The select2 input.
2120 */
2121 private function select2( $name, $value = '', $args = array() ) {
2122 if ( isset( $args['data-nonce-action'] ) ) {
2123 $args['data-nonce'] = wp_create_nonce( $args['data-nonce-action'] );
2124 unset( $args['data-nonce-action'] );
2125 }
2126 if ( ! isset( $args['class'] ) ) {
2127 $args['class'] = array();
2128 }
2129 $args['class'][] = 'select2';
2130 return $this->select( $name, $value, $args );
2131 }
2132
2133 /**
2134 * Textarea input element.
2135 *
2136 * @since 2.6.4
2137 *
2138 * @param string $name The name.
2139 * @param mixed $value The value.
2140 * @param array $args The arguments.
2141 * @param string $data_string The data string.
2142 *
2143 * @return string The textarea input.
2144 */
2145 private function textarea( $name, $value = '', $args = array(), $data_string = '' ) {
2146 if ( ! isset( $args['rows'] ) ) {
2147 $args['rows'] = 3;
2148 }
2149 return sprintf(
2150 '<textarea name="%s" %s%s>%s</textarea>',
2151 esc_attr( $name ),
2152 $this->build_field_attributes( $args ),
2153 $data_string,
2154 esc_html( $value )
2155 );
2156 }
2157
2158 /**
2159 * Description input element.
2160 *
2161 * @since 2.6.4
2162 *
2163 * @param string $name The name.
2164 * @param mixed $value The value.
2165 * @param array $args The arguments.
2166 *
2167 * @return string The description input.
2168 */
2169 private function description( $name, $value = '', $args = array() ) {
2170 if ( ! isset( $args['value'] ) || empty( $args['value'] ) ) {
2171 return '';
2172 }
2173 return sprintf( '<p class="description">%s</p>', wp_kses_post( $args['value'] ) );
2174 }
2175
2176 /**
2177 * Money input element.
2178 *
2179 * @since 2.6.4
2180 *
2181 * @param string $name The name.
2182 * @param mixed $value The value.
2183 * @param array $args The arguments.
2184 *
2185 * @return string The money input.
2186 */
2187 private function money( $name, $value = '', $args = array() ) {
2188 if ( empty( $value ) || ! is_array( $value ) ) {
2189 $value = array();
2190 }
2191 $value = wp_parse_args(
2192 $value,
2193 array(
2194 'integer' => 0,
2195 'fractional' => 0,
2196 'currency' => false,
2197 )
2198 );
2199 $args = wp_parse_args(
2200 $args,
2201 array(
2202 'kind' => 'complex',
2203 'currency' => false,
2204 'currency_default' => false,
2205 )
2206 );
2207 $content = '';
2208 /**
2209 * Integer
2210 */
2211 $n = sprintf( '%s[integer]', $name );
2212 $content .= $this->input( $n, $value['integer'], array( 'min' => 0 ), 'number' );
2213 if ( 'complex' === $args['kind'] ) {
2214 /**
2215 * fractional
2216 */
2217 $n = sprintf( '%s[fractional]', $name );
2218 $content .= $this->input(
2219 $n,
2220 $value['fractional'],
2221 array(
2222 'min' => 0,
2223 'max' => 99,
2224 ),
2225 'number'
2226 );
2227 }
2228 if ( is_array( $args['currency'] ) && ! empty( $args['currency'] ) ) {
2229 $n = sprintf( '%s[currency]', $name );
2230 $atts = array(
2231 'default' => $args['currency_default'],
2232 'options' => $args['currency'],
2233 );
2234 $content .= $this->select( $n, $value['currency'], $atts );
2235 }
2236 return $content;
2237 }
2238
2239 /**
2240 * Location input element.
2241 *
2242 * @since 2.6.4
2243 *
2244 * @param string $name The name.
2245 * @param mixed $value The value.
2246 * @param array $args The arguments.
2247 *
2248 * @return string The location input.
2249 */
2250 private function location( $name, $value = '', $args = array() ) {
2251 if ( empty( $value ) || ! is_array( $value ) ) {
2252 $value = array();
2253 }
2254 $defaults = array(
2255 'country' => '',
2256 'city' => '',
2257 'street' => '',
2258 'zip' => '',
2259 );
2260 $i18n = array(
2261 'country' => esc_html__( 'Country', 'reading-position-indicator' ),
2262 'city' => esc_html__( 'City', 'reading-position-indicator' ),
2263 'street' => esc_html__( 'Street', 'reading-position-indicator' ),
2264 'zip' => esc_html__( 'ZIP code', 'reading-position-indicator' ),
2265 );
2266 $value = wp_parse_args( $value, $defaults );
2267 /**
2268 * Content
2269 */
2270 $content = '';
2271 foreach ( array_keys( $defaults ) as $key ) {
2272 $content .= sprintf( '<div class="iworks-options-%s">', esc_attr( $key ) );
2273 $content .= '<label>';
2274 $content .= $i18n[ $key ];
2275 $content .= '<br />';
2276 $content .= $this->input(
2277 sprintf( '%s[%s]', esc_attr( $name ), esc_attr( $key ) ),
2278 $value[ $key ],
2279 array(
2280 'class' => 'large-text iworks-options-text',
2281 )
2282 );
2283 $content .= '</div>';
2284 }
2285 return $content;
2286 }
2287
2288 /**
2289 * Enqueue scripts and styles.
2290 *
2291 * @since 2.6.4
2292 *
2293 * @return void
2294 */
2295 public function admin_head() {
2296 if ( false === $this->check_hooks_to_load_asses() ) {
2297 return;
2298 }
2299 $files = $this->get_files();
2300 foreach ( $files as $data ) {
2301 if ( $data['style'] ) {
2302 wp_enqueue_style( $data['handle'] );
2303 } else {
2304 wp_enqueue_script( $data['handle'] );
2305 }
2306 }
2307 }
2308
2309 /**
2310 * Convert color to rgb
2311 *
2312 * @since 2.4.1
2313 *
2314 * @param string $hex Hex value of color
2315 * @return array RGB array.
2316 */
2317 public function hex2rgb( $hex ) {
2318 $hex = str_replace( '#', '', $hex );
2319 if ( strlen( $hex ) == 3 ) {
2320 $r = hexdec( substr( $hex, 0, 1 ) . substr( $hex, 0, 1 ) );
2321 $g = hexdec( substr( $hex, 1, 1 ) . substr( $hex, 1, 1 ) );
2322 $b = hexdec( substr( $hex, 2, 1 ) . substr( $hex, 2, 1 ) );
2323 } else {
2324 $r = hexdec( substr( $hex, 0, 2 ) );
2325 $g = hexdec( substr( $hex, 2, 2 ) );
2326 $b = hexdec( substr( $hex, 4, 2 ) );
2327 }
2328 $rgb = array( $r, $g, $b );
2329 return $rgb; // returns an array with the rgb values
2330 }
2331
2332 /**
2333 * Register styles and scripts.
2334 *
2335 * @since 2.6.4
2336 *
2337 * @return void
2338 */
2339 public function register_styles() {
2340 if ( false === $this->check_hooks_to_load_asses() ) {
2341 return;
2342 }
2343 $files = $this->get_files();
2344 foreach ( $files as $data ) {
2345 $file = sprintf( 'assets/%s/%s', $data['style'] ? 'styles' : 'scripts', $data['file'] );
2346 if ( 'theme' == $this->mode ) {
2347 $url = str_replace( get_template_directory(), '', __DIR__ );
2348 $file = get_template_directory_uri() . $url . '/' . $file;
2349 } else {
2350 $file = plugins_url( $file, __FILE__ );
2351 }
2352 $version = isset( $data['version'] ) ? $data['version'] : $this->version;
2353 $deps = isset( $data['deps'] ) ? $data['deps'] : array();
2354 $in_footer = isset( $data['in_footer'] ) ? $data['in_footer'] : true;
2355 if ( $data['style'] ) {
2356 wp_register_style( $data['handle'], $file, $deps, $version );
2357 } else {
2358 wp_register_script( $data['handle'], $file, $deps, $version, $in_footer );
2359 if ( isset( $data['wp_localize_script'] ) ) {
2360 wp_localize_script( $data['handle'], $data['handle'], $data['wp_localize_script'] );
2361 }
2362 }
2363 }
2364 }
2365
2366 /**
2367 * Get files.
2368 *
2369 * @since 2.6.4
2370 *
2371 * @return array The files.
2372 */
2373 public function get_files() {
2374 $f = array(
2375 /**
2376 * iworks_options core files
2377 */
2378 array(
2379 'handle' => __CLASS__,
2380 'file' => 'jquery-ui.min.css',
2381 ),
2382 array(
2383 'handle' => __CLASS__,
2384 'file' => 'common.js',
2385 'deps' => array( 'jquery', 'switch_button', 'jquery-ui-tabs' ),
2386 'wp_localize_script' => array(
2387 'buttons' => array(
2388 'select_media' => esc_html__( 'Select Image', 'reading-position-indicator' ),
2389 ),
2390 ),
2391 ),
2392 /**
2393 * switch checkbox
2394 */
2395 array(
2396 'handle' => 'switch_button',
2397 'file' => 'jquery.switch_button.css',
2398 'version' => '1.0',
2399 ),
2400 array(
2401 'handle' => 'switch_button',
2402 'file' => 'jquery.switch_button.js',
2403 'version' => '1.0',
2404 'deps' => array( 'jquery', 'jquery-effects-core', 'jquery-ui-widget' ),
2405 'wp_localize_script' => $this->get_switch_button_data(),
2406 ),
2407 /**
2408 * select2
2409 */
2410 array(
2411 'handle' => 'select2',
2412 'file' => 'select2.min.css',
2413 'version' => '4.0.13',
2414 ),
2415 array(
2416 'handle' => 'select2',
2417 'file' => 'select2.min.js',
2418 'version' => '4.0.13',
2419 'deps' => array( 'jquery' ),
2420 ),
2421 /**
2422 * options
2423 */
2424 array(
2425 'handle' => 'iworks-options',
2426 'file' => 'options-admin.css',
2427 'version' => $this->version,
2428 ),
2429 );
2430 $files = array();
2431 foreach ( $f as $data ) {
2432 $data['style'] = preg_match( '/css$/', $data['file'] );
2433 $files[] = $data;
2434 }
2435 return $files;
2436 }
2437
2438 /**
2439 * Get switch button data.
2440 *
2441 * @since 2.6.4
2442 *
2443 * @return array The switch button data.
2444 */
2445 public function get_switch_button_data() {
2446 $data = array(
2447 'labels' => array(
2448 'off_label' => esc_html__( 'OFF', 'reading-position-indicator' ),
2449 'on_label' => esc_html__( 'ON', 'reading-position-indicator' ),
2450 ),
2451 );
2452 return $data;
2453 }
2454
2455 /**
2456 * Get option page
2457 *
2458 * @since 2.6.0
2459 */
2460 public function get_pagehook() {
2461 return $this->option_prefix . $this->option_group;
2462 }
2463
2464 /**
2465 * Flush rewrite roles when it is configured
2466 *
2467 * @since 2.6.7
2468 */
2469 public function flush_rewrite_rules() {
2470 flush_rewrite_rules();
2471 }
2472
2473 /**
2474 * check to register or load assets
2475 *
2476 * check to register or load assets to avoid loading when it is not needed
2477 *
2478 * @since 2.8.0
2479 */
2480 private function check_hooks_to_load_asses() {
2481 if ( ! function_exists( 'get_current_screen' ) ) {
2482 return false;
2483 }
2484 $screen = get_current_screen();
2485 if ( ! is_object( $screen ) ) {
2486 return false;
2487 }
2488 return in_array( $screen->id, $this->pagehooks );
2489 }
2490
2491 /**
2492 * Set plugin value
2493 *
2494 * @since 2.7.3
2495 *
2496 * @param string $plugin Plugin file.
2497 */
2498 public function set_plugin( $plugin ) {
2499 $this->plugin = $plugin;
2500 }
2501
2502 /**
2503 * get nonce value
2504 *
2505 * @since 2.8.6
2506 */
2507 private function get_nonce_value() {
2508 $nonce_names = array( $this->get_nonce_name(), '_wpnonce' );
2509 foreach ( $nonce_names as $nonce_name ) {
2510 if ( isset( $_REQUEST[ $nonce_name ] ) ) {
2511 return sanitize_text_field( wp_unslash( $_REQUEST[ $nonce_value ] ) );
2512 }
2513 }
2514 return new WP_Error( 'security', esc_html__( 'Failed Security Check', 'reading-position-indicator' ) );
2515 }
2516
2517 /**
2518 * get nonce name
2519 *
2520 * @since 2.8.6
2521 */
2522 private function get_nonce_name() {
2523 return apply_filters( 'iworks_options_nonce_name', 'iworks_options' );
2524 }
2525
2526 /**
2527 * get allowed tags
2528 *
2529 * @since 2.9.5
2530 */
2531 private function get_allowed_tags() {
2532 $tags = array(
2533 'input' => array(
2534 'accept' => true,
2535 'alt' => true,
2536 'aria-*' => true,
2537 'autocomplete' => true,
2538 'autofocus' => true,
2539 'checked' => true,
2540 'class' => true,
2541 'data-*' => true,
2542 'dirname' => true,
2543 'disabled' => true,
2544 'form' => true,
2545 'formaction' => true,
2546 'formenctype' => true,
2547 'formmethod' => true,
2548 'formnovalidate' => true,
2549 'formtarget' => true,
2550 'height' => true,
2551 'id' => true,
2552 'list' => true,
2553 'max' => true,
2554 'maxlength' => true,
2555 'min' => true,
2556 'minlength' => true,
2557 'multiple' => true,
2558 'name' => true,
2559 'pattern' => true,
2560 'placeholder' => true,
2561 'popovertarget' => true,
2562 'popovertargetaction' => true,
2563 'readonly' => true,
2564 'rel' => true,
2565 'required' => true,
2566 'size' => true,
2567 'src' => true,
2568 'step' => true,
2569 'type' => true,
2570 'value' => true,
2571 'width' => true,
2572 ),
2573 'button' => array(
2574 'accept' => true,
2575 'alt' => true,
2576 'aria-*' => true,
2577 'autocomplete' => true,
2578 'autofocus' => true,
2579 'checked' => true,
2580 'class' => true,
2581 'data-*' => true,
2582 'dirname' => true,
2583 'disabled' => true,
2584 'form' => true,
2585 'formaction' => true,
2586 'formenctype' => true,
2587 'formmethod' => true,
2588 'formnovalidate' => true,
2589 'formtarget' => true,
2590 'height' => true,
2591 'id' => true,
2592 'list' => true,
2593 'max' => true,
2594 'maxlength' => true,
2595 'min' => true,
2596 'minlength' => true,
2597 'multiple' => true,
2598 'name' => true,
2599 'pattern' => true,
2600 'placeholder' => true,
2601 'popovertarget' => true,
2602 'popovertargetaction' => true,
2603 'readonly' => true,
2604 'rel' => true,
2605 'required' => true,
2606 'size' => true,
2607 'src' => true,
2608 'step' => true,
2609 'type' => true,
2610 'value' => true,
2611 'width' => true,
2612 ),
2613 'optgroup' => array(
2614 'label' => true,
2615 'class' => true,
2616 'data-*' => true,
2617 'aria-*' => true,
2618 'id' => true,
2619 ),
2620 'script' => array(
2621 'aria-*' => true,
2622 'async' => true,
2623 'charset' => true,
2624 'class' => true,
2625 'crossorigin' => true,
2626 'data-*' => true,
2627 'defer' => true,
2628 'disabled ' => true,
2629 'id' => true,
2630 'integrity' => true,
2631 'language' => true,
2632 'name' => true,
2633 'nomodule' => true,
2634 'src' => true,
2635 'type' => true,
2636 ),
2637 'style' => array(
2638 'aria-*' => true,
2639 'class' => true,
2640 'data-*' => true,
2641 'disabled ' => true,
2642 'id' => true,
2643 'media' => true,
2644 'name' => true,
2645 'scoped' => true,
2646 'type' => true,
2647 ),
2648 'select' => array(
2649 'autocomplete' => true,
2650 'autofocus' => true,
2651 'disabled ' => true,
2652 'form' => true,
2653 'multiple' => true,
2654 'name' => true,
2655 'class' => true,
2656 'data-*' => true,
2657 'aria-*' => true,
2658 'id' => true,
2659 'required' => true,
2660 'size' => true,
2661 ),
2662 'option' => array(
2663 'label' => true,
2664 'disabled ' => true,
2665 'value' => true,
2666 'selected' => true,
2667 'class' => true,
2668 'data-*' => true,
2669 'aria-*' => true,
2670 'id' => true,
2671 ),
2672 'textarea' => array(
2673 'autocomplete' => true,
2674 'autofocus' => true,
2675 'cols' => true,
2676 'dirname' => true,
2677 'disabled' => true,
2678 'form' => true,
2679 'maxlength' => true,
2680 'minlength' => true,
2681 'name' => true,
2682 'placeholder' => true,
2683 'readonly' => true,
2684 'required' => true,
2685 'rows' => true,
2686 'wrap' => true,
2687 'class' => true,
2688 'data-*' => true,
2689 'aria-*' => true,
2690 'id' => true,
2691 ),
2692 'noscript' => array(
2693 'class' => true,
2694 'data-*' => true,
2695 'aria-*' => true,
2696 'id' => true,
2697 ),
2698 'iframe' => array(
2699 'allow' => true,
2700 'allowfullscreen' => true,
2701 'allowtransparency' => true,
2702 'aria-*' => true,
2703 'class' => true,
2704 'data-*' => true,
2705 'id' => true,
2706 'height' => true,
2707 'name' => true,
2708 'sandbox' => true,
2709 'scrolling' => true,
2710 'src' => true,
2711 'srcdoc' => true,
2712 'style' => true,
2713 'title' => true,
2714 'width' => true,
2715 ),
2716 );
2717 return apply_filters(
2718 'iworks/options/wp_kses_allowed_html',
2719 wp_parse_args(
2720 $tags,
2721 wp_kses_allowed_html( 'post' )
2722 )
2723 );
2724 }
2725
2726 /**
2727 * Get the array of registered page hooks
2728 *
2729 * Retrieves all registered admin page hooks that have been added through this class.
2730 *
2731 * @since 3.0.3
2732 *
2733 * @return array Associative array of page hooks where keys are page slugs
2734 * and values are the corresponding WordPress hook suffixes.
2735 */
2736 public function get_pagehooks() {
2737 return $this->pagehooks;
2738 }
2739
2740
2741 /**
2742 * HTML INPUT ELEMENTS
2743 */
2744
2745 /**
2746 * Button input element.
2747 *
2748 * @since 2.6.4
2749 *
2750 * @param string $name The name.
2751 * @param mixed $value The value.
2752 * @param array $args The arguments.
2753 *
2754 * @return string The button input.
2755 */
2756 private function button( $name, $value = '', $args = array() ) {
2757 return $this->input( $name, $value, $args, __FUNCTION__ );
2758 }
2759
2760 /**
2761 * Checkbox HTML element.
2762 *
2763 * @since 2.6.4
2764 *
2765 * @param string $name The name.
2766 * @param mixed $value The value.
2767 * @param array $args The arguments.
2768 *
2769 * @return string The checkbox.
2770 */
2771 private function checkbox( $name, $value = '', $args = array() ) {
2772 if ( ! empty( $value ) ) {
2773 $args['checked'] = 'checked';
2774 }
2775 return $this->input( $name, $value, $args, __FUNCTION__ );
2776 }
2777
2778 /**
2779 * Input type="color" element.
2780 *
2781 * @since 3.0.9
2782 *
2783 * @param string $name The name.
2784 * @param mixed $value The value.
2785 * @param array $args The arguments.
2786 *
2787 * @return string The number input.
2788 */
2789 private function color( $name, $value = '', $args = array() ) {
2790 return $this->input( $name, $value, $args, __FUNCTION__ );
2791 }
2792
2793 /**
2794 * Date input element.
2795 *
2796 * @since 2.6.4
2797 *
2798 * @param string $name The name.
2799 * @param mixed $value The value.
2800 * @param array $args The arguments.
2801 *
2802 * @return string The date input.
2803 */
2804 private function date( $name, $value = '', $args = array() ) {
2805 if ( ! isset( $args['class'] ) ) {
2806 $args['class'] = array();
2807 }
2808 $args['class'][] = 'datepicker';
2809 return $this->input( $name, $value, $args );
2810 }
2811
2812 /**
2813 * Input type="email" element.
2814 *
2815 * @since 3.0.9
2816 *
2817 * @param string $name The name.
2818 * @param mixed $value The value.
2819 * @param array $args The arguments.
2820 *
2821 * @return string The number input.
2822 */
2823 private function email( $name, $value = '', $args = array() ) {
2824 return $this->input( $name, $value, $args, __FUNCTION__ );
2825 }
2826
2827 /**
2828 * Hidden input element.
2829 *
2830 * @since 2.6.4
2831 *
2832 * @param string $name The name.
2833 * @param mixed $value The value.
2834 * @param array $args The arguments.
2835 *
2836 * @return string The hidden input.
2837 */
2838 private function hidden( $name, $value = '', $args = array() ) {
2839 return $this->input( $name, $value, $args, __FUNCTION__ );
2840 }
2841
2842 /**
2843 * Input type="month" element.
2844 *
2845 * @since 3.0.9
2846 *
2847 * @param string $name The name.
2848 * @param mixed $value The value.
2849 * @param array $args The arguments.
2850 *
2851 * @return string The number input.
2852 */
2853 private function month( $name, $value = '', $args = array() ) {
2854 return $this->input( $name, $value, $args, __FUNCTION__ );
2855 }
2856
2857 /**
2858 * Number input element.
2859 *
2860 * @since 2.6.4
2861 *
2862 * @param string $name The name.
2863 * @param mixed $value The value.
2864 * @param array $args The arguments.
2865 *
2866 * @return string The number input.
2867 */
2868 private function number( $name, $value = '', $args = array() ) {
2869 return $this->input( $name, $value, $args, __FUNCTION__ );
2870 }
2871
2872 /**
2873 * Radio input element.
2874 *
2875 * @since 2.6.4
2876 *
2877 * @param string $name The name.
2878 * @param mixed $value The value.
2879 * @param array $args The arguments.
2880 *
2881 * @return string The radio input.
2882 */
2883 private function radio( $name, $value = '', $args = array() ) {
2884 $radio = '';
2885 $options = $args['options'];
2886 unset( $args['options'] );
2887 /**
2888 * default value
2889 */
2890 if ( isset( $args['default'] ) && '' == $value ) {
2891 $value = $args['default'];
2892 }
2893 $i = 0;
2894 foreach ( $options as $option_value => $input ) {
2895 $id = sprintf( '%s%d', $name, $i++ );
2896 $radio .= sprintf(
2897 '<li class="%s"><label for="%s"><input type="radio" name="%s" value="%s"%s id="%s"/> %s</label>',
2898 esc_attr( sanitize_title( $value ) ),
2899 esc_attr( $id ),
2900 esc_attr( $name ),
2901 esc_attr( $option_value ),
2902 checked( $option_value, $value, false ),
2903 esc_attr( $id ),
2904 esc_html( is_string( $input ) ? $input : $input['label'] )
2905 );
2906 if ( isset( $input['description'] ) ) {
2907 $radio .= '<br>';
2908 $radio .= $this->description( '', '', array( 'description' => wp_kses_post( $input['description'] ) ) );
2909 }
2910 $radio .= '</li>';
2911 }
2912 if ( $radio ) {
2913 $radio = sprintf( '<ul>%s</ul>', $radio );
2914 }
2915 return $radio;
2916 }
2917
2918 /**
2919 * Submit input element.
2920 *
2921 * @since 2.6.4
2922 *
2923 * @param string $name The name.
2924 * @param mixed $value The value.
2925 * @param array $args The arguments.
2926 *
2927 * @return string The submit input.
2928 */
2929 private function submit( $name, $value = '', $args = array() ) {
2930 return $this->input( $name, $value, $args, __FUNCTION__ );
2931 }
2932
2933 /**
2934 * Input type="tel" element.
2935 *
2936 * @since 3.0.9
2937 *
2938 * @param string $name The name.
2939 * @param mixed $value The value.
2940 * @param array $args The arguments.
2941 *
2942 * @return string The number input.
2943 */
2944 private function tel( $name, $value = '', $args = array() ) {
2945 return $this->input( $name, $value, $args, __FUNCTION__ );
2946 }
2947
2948 /**
2949 * Text input element.
2950 *
2951 * @since 2.6.4
2952 *
2953 * @param string $name The name.
2954 * @param mixed $value The value.
2955 * @param array $args The arguments.
2956 *
2957 * @return string The text input.
2958 */
2959 private function text( $name, $value = '', $args = array() ) {
2960 return $this->input( $name, $value, $args, __FUNCTION__ );
2961 }
2962
2963 /**
2964 * Input type="url" element.
2965 *
2966 * @since 3.0.9
2967 *
2968 * @param string $name The name.
2969 * @param mixed $value The value.
2970 * @param array $args The arguments.
2971 *
2972 * @return string The number input.
2973 */
2974 private function url( $name, $value = '', $args = array() ) {
2975 return $this->input( $name, $value, $args, __FUNCTION__ );
2976 }
2977
2978 /**
2979 * Input type="week" element.
2980 *
2981 * @since 3.0.9
2982 *
2983 * @param string $name The name.
2984 * @param mixed $value The value.
2985 * @param array $args The arguments.
2986 *
2987 * @return string The number input.
2988 */
2989 private function week( $name, $value = '', $args = array() ) {
2990 return $this->input( $name, $value, $args, __FUNCTION__ );
2991 }
2992
2993 /**
2994 * @TODO: datetime-local, file, password, range, reset, search, time
2995 */
2996 }
2997
2998