*/ abstract class WP_Settings_API_Helper { /** * Option name to store data in the database * * @since 1.0.0 * * @var array * * @usage $settings_options = [[ * 'option_group', * 'option_name', * 'option_value' => [], * 'default'=>[] //default values for the option * ], * ]; */ protected $settings_options; /** * This Setting Section array adds settings sections/fields to Admin option page * * @since 1.0.0 * * @var array * * @usage * [[ * id => 'ID for the section', * title => 'Title for the section, * description => 'Description for the section', * menu_slug => 'menu slug for registering section', * option_name => 'the name of the variable to be saved to the Options database, * args => [ * before_section, * after_section, * section_class, * ], * fields => [ * type => (text|url|number|email|hidden|select|checkbox|checkboxes|slider-checkbox|radio|textarea|password|dropdown_pages|color|range|image|code-editor|callback), * title => 'Title for this field', * id => 'id attribute for this field', * name => 'input name attribute for this field', * value => '', * default => 'default value for this field', * min => 'int, for number input field only', * max => 'int, for number input field only', * step => 'int or float, for number input field only', * rows => 'int, for textarea and code-editor fields (default: 5)', * cols => 'int, for textarea and code-editor fields (default: 60)', * multiple => 'boolean, for select field only — promotes to '; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Attribute fragments are built from escaped values and $disable_el is hardcoded. if ( $desc ) { echo '

' . wp_kses_post( $desc ) . '

'; } break; case 'textarea': if ( '' === $value && '' !== $default ) { $value = $default; } echo ''; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- $disable_el is a hardcoded safe string. if ( $desc ) { echo '

' . wp_kses_post( $desc ) . '

'; } break; case 'select': $multiple_attr = $multiple ? ' multiple="multiple"' : ''; echo ''; if ( $desc ) { echo '

' . wp_kses_post( $desc ) . '

'; } break; case 'radio': foreach ( $choices as $cval => $clabel ) { $checked = '' === $value ? checked( $cval, $default, false ) : checked( $cval, $value, false ); echo '
'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- $checked is generated by checked() and $disable_el is hardcoded. } if ( $desc ) { echo '

' . wp_kses_post( $desc ) . '

'; } break; case 'hidden': echo ''; break; case 'checkbox': $default = ! empty( $default ) ? absint( $default ) : 0; $value = '' !== $value ? $value : $default; echo ''; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- $disable_el is a hardcoded safe string. if ( $desc ) { echo '

' . wp_kses_post( $desc ) . '

'; } break; case 'slider-checkbox': // CSS styling for .slider not included. $default = ! empty( $default ) ? absint( $default ) : 0; $value = '' !== $value ? $value : $default; $disable_slider_class = $disabled ? ' disabled' : ''; echo ''; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- $disable_el is a hardcoded safe string. if ( $desc ) { echo '

' . wp_kses_post( $desc ) . '

'; } break; case 'checkboxes': $cb_class = ! empty( $class ) ? ' class="' . esc_attr( $class ) . '"' : ''; $default_checked = ( ! empty( $default ) && is_array( $default ) ) ? array_map( 'sanitize_text_field', $default ) : []; foreach ( $choices as $ckey => $cval ) { if ( array_key_exists( $name, $option ) && '' !== $option[ $name ] ) { $checked = ( is_array( $option[ $name ] ) && in_array( $ckey, $option[ $name ], true ) ) ? ' checked="checked"' : ''; } else { $checked = in_array( $ckey, $default_checked, true ) ? ' checked="checked"' : ''; } echo '
'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Attribute fragments are built from escaped values or hardcoded strings. } if ( $desc ) { echo '

' . wp_kses_post( $desc ) . '

'; } break; case 'dropdown_pages': $value = ! empty( $value ) ? $value : 0; wp_dropdown_pages( [ 'echo' => 1, 'name' => esc_attr( $option_name . '[' . $name . ']' ), 'id' => esc_attr( $id ), 'selected' => esc_attr( $value ), 'show_option_none' => 'Choose a page', 'option_none_value' => '-1', ] ); if ( $desc ) { echo '

' . wp_kses_post( $desc ) . '

'; } break; case 'color': if ( '' === $value && '' !== $default ) { $value = $default; } echo ''; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- $disable_el is a hardcoded safe string. if ( $desc ) { echo '

' . wp_kses_post( $desc ) . '

'; } break; case 'range': $min_attr = '' !== $min ? ' min="' . esc_attr( (float) $min ) . '"' : ''; $max_attr = '' !== $max ? ' max="' . esc_attr( (float) $max ) . '"' : ''; $step_attr = ! empty( $step ) ? ' step="' . esc_attr( $step ) . '"' : ''; if ( '' === $value && '' !== $default ) { $value = $default; } echo ''; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Attribute fragments are built from escaped values and $disable_el is hardcoded. echo '' . esc_html( $value ) . ''; $this->render_range_script( $id ); if ( $desc ) { echo '

' . wp_kses_post( $desc ) . '

'; } break; case 'image': if ( '' === $value && '' !== $default ) { $value = $default; } $preview_url = ''; if ( ! empty( $value ) ) { $preview_url = ( 'id' === $image_return && is_numeric( $value ) ) ? wp_get_attachment_image_url( (int) $value, 'thumbnail' ) : $value; } echo '
'; echo ''; echo '
'; if ( $preview_url ) { echo ''; } echo '
'; echo ' '; echo ''; echo '
'; $this->render_image_script( $id, $image_return ); if ( $desc ) { echo '

' . wp_kses_post( $desc ) . '

'; } break; case 'code-editor': if ( '' === $value && '' !== $default ) { $value = $default; } $code_type = $this->normalize_code_editor_type( $code_type ); $code_editor = wp_enqueue_code_editor( [ 'type' => $code_type, 'codemirror' => [ 'inputStyle' => 'textarea', 'matchBrackets' => true, 'lint' => true, 'direction' => 'ltr', 'colorpicker' => [ 'mode' => 'edit' ], 'foldOptions' => [ 'widget' => '...' ], 'continueComments' => true, ], ] ); if ( false !== $code_editor ) { if ( ! empty( $code_theme ) ) { $code_editor['codemirror']['theme'] = $code_theme; } wp_add_inline_script( 'wp-codemirror', sprintf( 'jQuery( function() { wp.codeEditor.initialize( "' . esc_attr( $id ) . '", %s ); } );', wp_json_encode( $code_editor ) ) ); } echo ''; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- $disable_el is a hardcoded safe string. if ( $desc ) { echo '

' . wp_kses_post( $desc ) . '

'; } break; case 'callback': // Only invoke if $callback is explicitly a valid, callable reference. if ( ! empty( $callback ) && is_callable( $callback ) ) { if ( ! empty( $param ) ) { call_user_func( $callback, $args['field'], $param ); } else { call_user_func( $callback, $args['field'] ); } } break; } } /** * Render the range field live output script. * * @since 1.2.0 * * @param string $id Field id attribute. * * @return void */ protected function render_range_script( $id ) { $script = sprintf( '(function(){var r=document.getElementById(%s);if(!r){return;}var o=r.nextElementSibling;if(!o){return;}r.addEventListener("input",function(){o.value=r.value;o.textContent=r.value;});})();', wp_json_encode( $id ) ); echo ''; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- The only dynamic value is JSON encoded. } /** * Render the image picker script for a single field. * * @since 1.2.0 * * @param string $id Field id attribute. * @param string $image_return Whether to store the selected image URL or ID. * * @return void */ protected function render_image_script( $id, $image_return ) { $script = sprintf( "(function($){ var fieldId=%s; var returnType=%s; var selectButton=$('#'+fieldId+'_select'); var removeButton=$('#'+fieldId+'_remove'); var wrap=selectButton.closest('.image-field'); selectButton.on('click',function(){ if('undefined'===typeof wp || !wp.media){return;} var frame=wp.media({title:%s,button:{text:%s},multiple:false}); frame.on('select',function(){ var att=frame.state().get('selection').first().toJSON(); $('#'+fieldId).val('id'===returnType?att.id:att.url); var thumbUrl=(att.sizes&&att.sizes.thumbnail)?att.sizes.thumbnail.url:att.url; wrap.find('.image-preview').html($('',{src:thumbUrl,style:'max-width:150px;max-height:150px;display:block;'})); removeButton.show(); }); frame.open(); }); removeButton.on('click',function(){ $('#'+fieldId).val(''); wrap.find('.image-preview').html(''); removeButton.hide(); }); })(jQuery);", wp_json_encode( $id ), wp_json_encode( $image_return ), wp_json_encode( __( 'Select Image' ) ), wp_json_encode( __( 'Use this image' ) ) ); echo ''; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Dynamic values are JSON encoded. } /** * Render a standard single-line text input. * * @since 1.1.4 * * @param string $type Input type attribute (text|url|email|password). * @param string $option_name Option group name. * @param string $name Field name key. * @param string $id Field id attribute. * @param string $value Current field value. * @param string $placeholder Placeholder text. * @param string $css_class Additional CSS classes. * @param string $disable_el Pre-built disabled attribute string or empty. * * @return void */ protected function render_text_input( $type, $option_name, $name, $id, $value, $placeholder, $css_class, $disable_el ) { echo ''; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- $disable_el is a hardcoded safe string. } /** * Retrieve a single field value from a stored option array. * * @since 1.2.1 * * @param string $option_name The option name as registered with register_setting(). * @param string $key The field key within the option array. * @param mixed $default Value to return if the key is not set. * * @return mixed */ public function get_option_value( $option_name, $key, $default = null ) { $option = get_option( $option_name, [] ); return isset( $option[ $key ] ) ? $option[ $key ] : $default; } /** * Display settings on page * * @since 1.0.0 * * @param string $section All the Settings sections and fields configuration. * * @param array $other_attributes (Optional) Other attributes that should be output with the button, mapping attributes to their values, such as array( 'tabindex' => '1' ). These attributes will be output as attribute="value", such as tabindex="1". * * @return void */ public function render_settings_on_page( $section, $other_attributes = [] ) { if ( empty( $section ) ) { return; } // Default the id attribute to $section unless an id was specifically provided in $other_attributes. $id = $section; if ( isset( $other_attributes['id'] ) ) { $id = $other_attributes['id']; unset( $other_attributes['id'] ); } // Strip reserved and control keys before building HTML attributes. $add_submit_btn = ! isset( $other_attributes['remove_submit_button'] ) || ! (bool) $other_attributes['remove_submit_button']; unset( $other_attributes['action'], $other_attributes['method'], $other_attributes['remove_submit_button'] ); $attributes = ''; foreach ( $other_attributes as $attribute => $value ) { $attributes .= sanitize_key( $attribute ) . '="' . esc_attr( $value ) . '" '; // Trailing space is important. } $id_attr = $id ? ' id="' . esc_attr( $id ) . '"' : ''; $form = '
'; echo $form; // phpcs:ignore settings_fields( $section ); do_settings_sections( $section ); if ( $add_submit_btn ) { submit_button( 'Save Changes' ); } echo '
'; } /** * Normalize shorthand code editor types to WordPress-friendly MIME strings. * * @since 1.1.3 * * @param string $code_type Requested code type. * * @return string */ protected function normalize_code_editor_type( $code_type ) { $map = [ 'css' => 'text/css', 'scss' => 'text/x-scss', 'sass' => 'text/x-sass', 'less' => 'text/x-less', 'javascript' => 'text/javascript', 'js' => 'text/javascript', 'json' => 'application/json', 'html' => 'text/html', 'xml' => 'application/xml', 'markdown' => 'text/x-markdown', 'md' => 'text/x-markdown', 'php' => 'application/x-httpd-php', ]; if ( isset( $map[ $code_type ] ) ) { return $map[ $code_type ]; } if ( false !== strpos( $code_type, '/' ) ) { return $code_type; } return 'text/css'; } }