Skip to content

Instantly share code, notes, and snippets.

@wpexplorer
Last active April 4, 2023 00:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save wpexplorer/a222a8a579fb5c62c2dfe8741d7a59c3 to your computer and use it in GitHub Desktop.
Save wpexplorer/a222a8a579fb5c62c2dfe8741d7a59c3 to your computer and use it in GitHub Desktop.
WordPress Options Panel Helper Class
class WPEX_Options_Panel {
/**
* Options panel arguments.
*/
protected $args = [];
/**
* Options panel title.
*/
protected $title = '';
/**
* Options panel slug.
*/
protected $slug = '';
/**
* Option name to use for saving our options in the database.
*/
protected $option_name = '';
/**
* Option group name.
*/
protected $option_group_name = '';
/**
* User capability allowed to access the options page.
*/
protected $user_capability = '';
/**
* Our array of settings.
*/
protected $settings = [];
/**
* Our class constructor.
*/
public function __construct( array $args, array $settings ) {
$this->args = $args;
$this->settings = $settings;
$this->title = $this->args['title'] ?? esc_html__( 'Options', 'text_domain' );
$this->slug = $this->args['slug'] ?? sanitize_key( $this->title );
$this->option_name = $this->args['option_name'] ?? sanitize_key( $this->title );
$this->option_group_name = $this->option_name . '_group';
$this->user_capability = $args['user_capability'] ?? 'manage_options';
add_action( 'admin_menu', [ $this, 'register_menu_page' ] );
add_action( 'admin_init', [ $this, 'register_settings' ] );
}
/**
* Register the new menu page.
*/
public function register_menu_page() {
add_menu_page(
$this->title,
$this->title,
$this->user_capability,
$this->slug,
[ $this, 'render_options_page' ]
);
}
/**
* Register the settings.
*/
public function register_settings() {
register_setting( $this->option_group_name, $this->option_name, [
'sanitize_callback' => [ $this, 'sanitize_fields' ],
'default' => $this->get_defaults(),
] );
add_settings_section(
$this->option_name . '_sections',
false,
false,
$this->option_name
);
foreach ( $this->settings as $key => $args ) {
$type = $args['type'] ?? 'text';
$callback = "render_{$type}_field";
if ( method_exists( $this, $callback ) ) {
$tr_class = '';
if ( array_key_exists( 'tab', $args ) ) {
$tr_class .= 'wpex-tab-item wpex-tab-item--' . sanitize_html_class( $args['tab'] );
}
add_settings_field(
$key,
$args['label'],
[ $this, $callback ],
$this->option_name,
$this->option_name . '_sections',
[
'label_for' => $key,
'class' => $tr_class
]
);
}
}
}
/**
* Saves our fields.
*/
public function sanitize_fields( $value ) {
$value = (array) $value;
$new_value = [];
foreach ( $this->settings as $key => $args ) {
$field_type = $args['type'];
$new_option_value = $value[$key] ?? '';
if ( $new_option_value ) {
$sanitize_callback = $args['sanitize_callback'] ?? $this->get_sanitize_callback_by_type( $field_type );
$new_value[$key] = call_user_func( $sanitize_callback, $new_option_value, $args );
} elseif ( 'checkbox' === $field_type ) {
$new_value[$key] = 0;
}
}
return $new_value;
}
/**
* Returns sanitize callback based on field type.
*/
protected function get_sanitize_callback_by_type( $field_type ) {
switch ( $field_type ) {
case 'select':
return [ $this, 'sanitize_select_field' ];
break;
case 'textarea':
return 'wp_kses_post';
break;
case 'checkbox':
return [ $this, 'sanitize_checkbox_field' ];
break;
default:
case 'text':
return 'sanitize_text_field';
break;
}
}
/**
* Returns default values.
*/
protected function get_defaults() {
$defaults = [];
foreach ( $this->settings as $key => $args ) {
$defaults[$key] = $args['default'] ?? '';
}
return $defaults;
}
/**
* Sanitizes the checkbox field.
*/
protected function sanitize_checkbox_field( $value = '', $field_args = [] ) {
return ( 'on' === $value ) ? 1 : 0;
}
/**
* Sanitizes the select field.
*/
protected function sanitize_select_field( $value = '', $field_args = [] ) {
$choices = $field_args['choices'] ?? [];
if ( array_key_exists( $value, $choices ) ) {
return $value;
}
}
/**
* Renders the options page.
*/
public function render_options_page() {
if ( ! current_user_can( $this->user_capability ) ) {
return;
}
if ( isset( $_GET['settings-updated'] ) ) {
add_settings_error(
$this->option_name . '_mesages',
$this->option_name . '_message',
esc_html__( 'Settings Saved', 'text_domain' ),
'updated'
);
}
settings_errors( $this->option_name . '_mesages' );
?>
<div class="wrap">
<h1><?php echo esc_html( get_admin_page_title() ); ?></h1>
<?php $this->render_tabs(); ?>
<form action="options.php" method="post" class="wpex-options-form">
<?php
settings_fields( $this->option_group_name );
do_settings_sections( $this->option_name );
submit_button( 'Save Settings' );
?>
</form>
</div>
<?php
}
/**
* Renders options page tabs.
*/
protected function render_tabs() {
if ( empty( $this->args['tabs'] ) ) {
return;
}
$tabs = $this->args['tabs'];
?>
<style>.wpex-tab-item{ display: none; ?></style>
<h2 class="nav-tab-wrapper wpex-tabs"><?php
$first_tab = true;
foreach ( $tabs as $id => $label ) {?>
<a href="#" data-tab="<?php echo esc_attr( $id ); ?>" class="nav-tab<?php echo ( $first_tab ) ? ' nav-tab-active' : ''; ?>"><?php echo ucfirst( $label ); ?></a>
<?php
$first_tab = false;
}
?></h2>
<script>
( function() {
document.addEventListener( 'click', ( event ) => {
const target = event.target;
if ( ! target.closest( '.wpex-tabs a' ) ) {
return;
}
event.preventDefault();
document.querySelectorAll( '.wpex-tabs a' ).forEach( ( tablink ) => {
tablink.classList.remove( 'nav-tab-active' );
} );
target.classList.add( 'nav-tab-active' );
targetTab = target.getAttribute( 'data-tab' );
document.querySelectorAll( '.wpex-options-form .wpex-tab-item' ).forEach( ( item ) => {
if ( item.classList.contains( `wpex-tab-item--${targetTab}` ) ) {
item.style.display = 'block';
} else {
item.style.display = 'none';
}
} );
} );
document.addEventListener( 'DOMContentLoaded', function () {
document.querySelector( '.wpex-tabs .nav-tab' ).click();
}, false );
} )();
</script>
<?php
}
/**
* Returns an option value.
*/
protected function get_option_value( $option_name ) {
$option = get_option( $this->option_name );
if ( ! array_key_exists( $option_name, $option ) ) {
return array_key_exists( 'default', $this->settings[$option_name] ) ? $this->settings[$option_name]['default'] : '';
}
return $option[$option_name];
}
/**
* Renders a text field.
*/
public function render_text_field( $args ) {
$option_name = $args['label_for'];
$value = $this->get_option_value( $option_name );
$description = $this->settings[$option_name]['description'] ?? '';
?>
<input
type="text"
id="<?php echo esc_attr( $args['label_for'] ); ?>"
name="<?php echo $this->option_name; ?>[<?php echo esc_attr( $args['label_for'] ); ?>]"
value="<?php echo esc_attr( $value ); ?>">
<?php if ( $description ) { ?>
<p class="description"><?php echo esc_html( $description ); ?></p>
<?php } ?>
<?php
}
/**
* Renders a textarea field.
*/
public function render_textarea_field( $args ) {
$option_name = $args['label_for'];
$value = $this->get_option_value( $option_name );
$description = $this->settings[$option_name]['description'] ?? '';
$rows = $this->settings[$option_name]['rows'] ?? '4';
$cols = $this->settings[$option_name]['cols'] ?? '50';
?>
<textarea
type="text"
id="<?php echo esc_attr( $args['label_for'] ); ?>"
rows="<?php echo esc_attr( absint( $rows ) ); ?>"
cols="<?php echo esc_attr( absint( $cols ) ); ?>"
name="<?php echo $this->option_name; ?>[<?php echo esc_attr( $args['label_for'] ); ?>]"><?php echo esc_attr( $value ); ?></textarea>
<?php if ( $description ) { ?>
<p class="description"><?php echo esc_html( $description ); ?></p>
<?php } ?>
<?php
}
/**
* Renders a checkbox field.
*/
public function render_checkbox_field( $args ) {
$option_name = $args['label_for'];
$value = $this->get_option_value( $option_name );
$description = $this->settings[$option_name]['description'] ?? '';
?>
<input
type="checkbox"
id="<?php echo esc_attr( $args['label_for'] ); ?>"
name="<?php echo $this->option_name; ?>[<?php echo esc_attr( $args['label_for'] ); ?>]"
<?php checked( $value, 1, true ); ?>
>
<?php if ( $description ) { ?>
<p class="description"><?php echo esc_html( $description ); ?></p>
<?php } ?>
<?php
}
/**
* Renders a select field.
*/
public function render_select_field( $args ) {
$option_name = $args['label_for'];
$value = $this->get_option_value( $option_name );
$description = $this->settings[$option_name]['description'] ?? '';
$choices = $this->settings[$option_name]['choices'] ?? [];
?>
<select
id="<?php echo esc_attr( $args['label_for'] ); ?>"
name="<?php echo $this->option_name; ?>[<?php echo esc_attr( $args['label_for'] ); ?>]"
>
<?php foreach ( $choices as $choice_v => $label ) { ?>
<option value="<?php echo esc_attr( $choice_v ); ?>" <?php selected( $choice_v, $value, true ); ?>><?php echo esc_html( $label ); ?></option>
<?php } ?>
</select>
<?php if ( $description ) { ?>
<p class="description"><?php echo esc_html( $description ); ?></p>
<?php } ?>
<?php
}
}
Helper Class Usage:
Here is an example of how you can use the classname to register a custom options page with various fields. So easy! You can now create unlimited option panels and let the class do all the heavy lifting.
// Register new Options panel.
$panel_args = [
'title' => 'My Options',
'option_name' => 'my_options',
'slug' => 'my-options-panel',
'user_capability' => 'manage_options',
];
$panel_settings = [
'option_1' => [
'label' => esc_html__( 'Checkbox Option', 'text_domain' ),
'type' => 'checkbox',
'description' => 'My checkbox field description.',
],
'option_2' => [
'label' => esc_html__( 'Select Option', 'text_domain' ),
'type' => 'select',
'description' => 'My select field description.',
'choices' => [
'' => esc_html__( 'Select', 'text_domain' ),
'choice_1' => esc_html__( 'Choice 1', 'text_domain' ),
'choice_2' => esc_html__( 'Choice 2', 'text_domain' ),
'choice_3' => esc_html__( 'Choice 3', 'text_domain' ),
],
],
'option_3' => [
'label' => esc_html__( 'Text Option', 'text_domain' ),
'type' => 'text',
'description' => 'My field 1 description.',
],
'option_4' => [
'label' => esc_html__( 'Textarea Option', 'text_domain' ),
'type' => 'textarea',
'description' => 'My textarea field description.',
],
];
new WPEX_Options_Panel( $panel_args, $panel_settings );
@wpexplorer
Copy link
Author

wpexplorer commented Apr 2, 2023

Basic use (no tabs)

// Register new Options panel.
$panel_args = [
    'title'           => 'My Options',
    'option_name'     => 'my_options',
    'user_capability' => 'manage_options',
];

$panel_settings = [
    'option_1' => [
        'label'       => esc_html__( 'Checkbox Option', 'text_domain' ),
        'type'        => 'checkbox',
        'description' => 'My checkbox field description.',
    ],
    'option_2' => [
        'label'       => esc_html__( 'Select Option', 'text_domain' ),
        'type'        => 'select',
        'description' => 'My select field description.',
        'choices'     => [
            ''         => esc_html__( 'Select', 'text_domain' ),
            'choice_1' => esc_html__( 'Choice 1', 'text_domain' ),
            'choice_2' => esc_html__( 'Choice 2', 'text_domain' ),
            'choice_3' => esc_html__( 'Choice 3', 'text_domain' ),
        ],
    ],
    'option_3' => [
        'label'       => esc_html__( 'Text Option', 'text_domain' ),
        'type'        => 'text',
        'description' => 'My field 1 description.',
    ],
    'option_4' => [
        'label'       => esc_html__( 'Textarea Option', 'text_domain' ),
        'type'        => 'textarea',
        'description' => 'My textarea field description.',
    ],
];

new WPEX_Options_Panel( $panel_args, $panel_settings );

Advanced use (with tabs)

// Register new Options panel.
$panel_args = [
    'title'           => 'My Options',
    'option_name'     => 'my_options',
    'slug'            => 'my-options-panel',
    'user_capability' => 'manage_options',
    'tabs'            => [
        'tab-1' => esc_html__( 'Tab 1', 'text_domain' ),
        'tab-2' => esc_html__( 'Tab 2', 'text_domain' ),
    ],
];

$panel_settings = [
    // Tab 1
    'option_1' => [
        'label'       => esc_html__( 'Checkbox Option', 'text_domain' ),
        'type'        => 'checkbox',
        'description' => 'My checkbox field description.',
        'tab'         => 'tab-1',
    ],
    'option_2' => [
        'label'       => esc_html__( 'Select Option', 'text_domain' ),
        'type'        => 'select',
        'description' => 'My select field description.',
        'choices'     => [
            ''         => esc_html__( 'Select', 'text_domain' ),
            'choice_1' => esc_html__( 'Choice 1', 'text_domain' ),
            'choice_2' => esc_html__( 'Choice 2', 'text_domain' ),
            'choice_3' => esc_html__( 'Choice 3', 'text_domain' ),
        ],
        'tab'         => 'tab-1',
    ],
    // Tab 2
    'option_3' => [
        'label'       => esc_html__( 'Text Option', 'text_domain' ),
        'type'        => 'text',
        'description' => 'My field 1 description.',
        'tab'         => 'tab-2',
    ],
    'option_4' => [
        'label'       => esc_html__( 'Textarea Option', 'text_domain' ),
        'type'        => 'textarea',
        'description' => 'My textarea field description.',
        'tab'         => 'tab-2',
    ],
];

new WPEX_Options_Panel( $panel_args, $panel_settings );

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment