Created
February 28, 2014 08:37
-
-
Save hlashbrooke/9267467 to your computer and use it in GitHub Desktop.
A complete, versatile options page class for any WordPress plugin
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
if ( ! defined( 'ABSPATH' ) ) exit; | |
class WordPress_Plugin_Template_Settings { | |
private $dir; | |
private $file; | |
private $assets_dir; | |
private $assets_url; | |
private $settings_base; | |
private $settings; | |
public function __construct( $file ) { | |
$this->file = $file; | |
$this->dir = dirname( $this->file ); | |
$this->assets_dir = trailingslashit( $this->dir ) . 'assets'; | |
$this->assets_url = esc_url( trailingslashit( plugins_url( '/assets/', $this->file ) ) ); | |
$this->settings_base = 'wpt_'; | |
// Initialise settings | |
add_action( 'admin_init', array( $this, 'init' ) ); | |
// Register plugin settings | |
add_action( 'admin_init' , array( $this, 'register_settings' ) ); | |
// Add settings page to menu | |
add_action( 'admin_menu' , array( $this, 'add_menu_item' ) ); | |
// Add settings link to plugins page | |
add_filter( 'plugin_action_links_' . plugin_basename( $this->file ) , array( $this, 'add_settings_link' ) ); | |
} | |
/** | |
* Initialise settings | |
* @return void | |
*/ | |
public function init() { | |
$this->settings = $this->settings_fields(); | |
} | |
/** | |
* Add settings page to admin menu | |
* @return void | |
*/ | |
public function add_menu_item() { | |
$page = add_options_page( __( 'Plugin Settings', 'plugin_textdomain' ) , __( 'Plugin Settings', 'plugin_textdomain' ) , 'manage_options' , 'plugin_settings' , array( $this, 'settings_page' ) ); | |
add_action( 'admin_print_styles-' . $page, array( $this, 'settings_assets' ) ); | |
} | |
/** | |
* Load settings JS & CSS | |
* @return void | |
*/ | |
public function settings_assets() { | |
// We're including the farbtastic script & styles here because they're needed for the colour picker | |
// If you're not including a colour picker field then you can leave these calls out as well as the farbtastic dependency for the wpt-admin-js script below | |
wp_enqueue_style( 'farbtastic' ); | |
wp_enqueue_script( 'farbtastic' ); | |
// We're including the WP media scripts here because they're needed for the image upload field | |
// If you're not including an image upload then you can leave this function call out | |
wp_enqueue_media(); | |
wp_register_script( 'wpt-admin-js', $this->assets_url . 'js/settings.js', array( 'farbtastic', 'jquery' ), '1.0.0' ); | |
wp_enqueue_script( 'wpt-admin-js' ); | |
} | |
/** | |
* Add settings link to plugin list table | |
* @param array $links Existing links | |
* @return array Modified links | |
*/ | |
public function add_settings_link( $links ) { | |
$settings_link = '<a href="options-general.php?page=plugin_settings">' . __( 'Settings', 'plugin_textdomain' ) . '</a>'; | |
array_push( $links, $settings_link ); | |
return $links; | |
} | |
/** | |
* Build settings fields | |
* @return array Fields to be displayed on settings page | |
*/ | |
private function settings_fields() { | |
$settings['standard'] = array( | |
'title' => __( 'Standard', 'plugin_textdomain' ), | |
'description' => __( 'These are fairly standard form input fields.', 'plugin_textdomain' ), | |
'fields' => array( | |
array( | |
'id' => 'text_field', | |
'label' => __( 'Some Text' , 'plugin_textdomain' ), | |
'description' => __( 'This is a standard text field.', 'plugin_textdomain' ), | |
'type' => 'text', | |
'default' => '', | |
'placeholder' => __( 'Placeholder text', 'plugin_textdomain' ) | |
), | |
array( | |
'id' => 'password_field', | |
'label' => __( 'A Password' , 'plugin_textdomain' ), | |
'description' => __( 'This is a standard password field.', 'plugin_textdomain' ), | |
'type' => 'password', | |
'default' => '', | |
'placeholder' => __( 'Placeholder text', 'plugin_textdomain' ) | |
), | |
array( | |
'id' => 'secret_text_field', | |
'label' => __( 'Some Secret Text' , 'plugin_textdomain' ), | |
'description' => __( 'This is a secret text field - any data saved here will not be displayed after the page has reloaded, but it will be saved.', 'plugin_textdomain' ), | |
'type' => 'text_secret', | |
'default' => '', | |
'placeholder' => __( 'Placeholder text', 'plugin_textdomain' ) | |
), | |
array( | |
'id' => 'text_block', | |
'label' => __( 'A Text Block' , 'plugin_textdomain' ), | |
'description' => __( 'This is a standard text area.', 'plugin_textdomain' ), | |
'type' => 'textarea', | |
'default' => '', | |
'placeholder' => __( 'Placeholder text for this textarea', 'plugin_textdomain' ) | |
), | |
array( | |
'id' => 'single_checkbox', | |
'label' => __( 'An Option', 'plugin_textdomain' ), | |
'description' => __( 'A standard checkbox - if you save this option as checked then it will store the option as \'on\', otherwise it will be an empty string.', 'plugin_textdomain' ), | |
'type' => 'checkbox', | |
'default' => '' | |
), | |
array( | |
'id' => 'select_box', | |
'label' => __( 'A Select Box', 'plugin_textdomain' ), | |
'description' => __( 'A standard select box.', 'plugin_textdomain' ), | |
'type' => 'select', | |
'options' => array( 'drupal' => 'Drupal', 'joomla' => 'Joomla', 'wordpress' => 'WordPress' ), | |
'default' => 'wordpress' | |
), | |
array( | |
'id' => 'radio_buttons', | |
'label' => __( 'Some Options', 'plugin_textdomain' ), | |
'description' => __( 'A standard set of radio buttons.', 'plugin_textdomain' ), | |
'type' => 'radio', | |
'options' => array( 'superman' => 'Superman', 'batman' => 'Batman', 'ironman' => 'Iron Man' ), | |
'default' => 'batman' | |
), | |
array( | |
'id' => 'multiple_checkboxes', | |
'label' => __( 'Some Items', 'plugin_textdomain' ), | |
'description' => __( 'You can select multiple items and they will be stored as an array.', 'plugin_textdomain' ), | |
'type' => 'checkbox_multi', | |
'options' => array( 'square' => 'Square', 'circle' => 'Circle', 'rectangle' => 'Rectangle', 'triangle' => 'Triangle' ), | |
'default' => array( 'circle', 'triangle' ) | |
) | |
) | |
); | |
$settings['extra'] = array( | |
'title' => __( 'Extra', 'plugin_textdomain' ), | |
'description' => __( 'These are some extra input fields that maybe aren\'t as common as the others.', 'plugin_textdomain' ), | |
'fields' => array( | |
array( | |
'id' => 'number_field', | |
'label' => __( 'A Number' , 'plugin_textdomain' ), | |
'description' => __( 'This is a standard number field - if this field contains anything other than numbers then the form will not be submitted.', 'plugin_textdomain' ), | |
'type' => 'number', | |
'default' => '', | |
'placeholder' => __( '42', 'plugin_textdomain' ) | |
), | |
array( | |
'id' => 'colour_picker', | |
'label' => __( 'Pick a colour', 'plugin_textdomain' ), | |
'description' => __( 'This uses WordPress\' built-in colour picker - the option is stored as the colour\'s hex code.', 'plugin_textdomain' ), | |
'type' => 'color', | |
'default' => '#21759B' | |
), | |
array( | |
'id' => 'an_image', | |
'label' => __( 'An Image' , 'plugin_textdomain' ), | |
'description' => __( 'This will upload an image to your media library and store the attachment ID in the option field. Once you have uploaded an imge the thumbnail will display above these buttons.', 'plugin_textdomain' ), | |
'type' => 'image', | |
'default' => '', | |
'placeholder' => '' | |
), | |
array( | |
'id' => 'multi_select_box', | |
'label' => __( 'A Multi-Select Box', 'plugin_textdomain' ), | |
'description' => __( 'A standard multi-select box - the saved data is stored as an array.', 'plugin_textdomain' ), | |
'type' => 'select_multi', | |
'options' => array( 'linux' => 'Linux', 'mac' => 'Mac', 'windows' => 'Windows' ), | |
'default' => array( 'linux' ) | |
) | |
) | |
); | |
$settings = apply_filters( 'plugin_settings_fields', $settings ); | |
return $settings; | |
} | |
/** | |
* Register plugin settings | |
* @return void | |
*/ | |
public function register_settings() { | |
if( is_array( $this->settings ) ) { | |
foreach( $this->settings as $section => $data ) { | |
// Add section to page | |
add_settings_section( $section, $data['title'], array( $this, 'settings_section' ), 'plugin_settings' ); | |
foreach( $data['fields'] as $field ) { | |
// Validation callback for field | |
$validation = ''; | |
if( isset( $field['callback'] ) ) { | |
$validation = $field['callback']; | |
} | |
// Register field | |
$option_name = $this->settings_base . $field['id']; | |
register_setting( 'plugin_settings', $option_name, $validation ); | |
// Add field to page | |
add_settings_field( $field['id'], $field['label'], array( $this, 'display_field' ), 'plugin_settings', $section, array( 'field' => $field ) ); | |
} | |
} | |
} | |
} | |
public function settings_section( $section ) { | |
$html = '<p> ' . $this->settings[ $section['id'] ]['description'] . '</p>' . "\n"; | |
echo $html; | |
} | |
/** | |
* Generate HTML for displaying fields | |
* @param array $args Field data | |
* @return void | |
*/ | |
public function display_field( $args ) { | |
$field = $args['field']; | |
$html = ''; | |
$option_name = $this->settings_base . $field['id']; | |
$option = get_option( $option_name ); | |
$data = ''; | |
if( isset( $field['default'] ) ) { | |
$data = $field['default']; | |
if( $option ) { | |
$data = $option; | |
} | |
} | |
switch( $field['type'] ) { | |
case 'text': | |
case 'password': | |
case 'number': | |
$html .= '<input id="' . esc_attr( $field['id'] ) . '" type="' . $field['type'] . '" name="' . esc_attr( $option_name ) . '" placeholder="' . esc_attr( $field['placeholder'] ) . '" value="' . $data . '"/>' . "\n"; | |
break; | |
case 'text_secret': | |
$html .= '<input id="' . esc_attr( $field['id'] ) . '" type="text" name="' . esc_attr( $option_name ) . '" placeholder="' . esc_attr( $field['placeholder'] ) . '" value=""/>' . "\n"; | |
break; | |
case 'textarea': | |
$html .= '<textarea id="' . esc_attr( $field['id'] ) . '" rows="5" cols="50" name="' . esc_attr( $option_name ) . '" placeholder="' . esc_attr( $field['placeholder'] ) . '">' . $data . '</textarea><br/>'. "\n"; | |
break; | |
case 'checkbox': | |
$checked = ''; | |
if( $option && 'on' == $option ){ | |
$checked = 'checked="checked"'; | |
} | |
$html .= '<input id="' . esc_attr( $field['id'] ) . '" type="' . $field['type'] . '" name="' . esc_attr( $option_name ) . '" ' . $checked . '/>' . "\n"; | |
break; | |
case 'checkbox_multi': | |
foreach( $field['options'] as $k => $v ) { | |
$checked = false; | |
if( in_array( $k, $data ) ) { | |
$checked = true; | |
} | |
$html .= '<label for="' . esc_attr( $field['id'] . '_' . $k ) . '"><input type="checkbox" ' . checked( $checked, true, false ) . ' name="' . esc_attr( $option_name ) . '[]" value="' . esc_attr( $k ) . '" id="' . esc_attr( $field['id'] . '_' . $k ) . '" /> ' . $v . '</label> '; | |
} | |
break; | |
case 'radio': | |
foreach( $field['options'] as $k => $v ) { | |
$checked = false; | |
if( $k == $data ) { | |
$checked = true; | |
} | |
$html .= '<label for="' . esc_attr( $field['id'] . '_' . $k ) . '"><input type="radio" ' . checked( $checked, true, false ) . ' name="' . esc_attr( $option_name ) . '" value="' . esc_attr( $k ) . '" id="' . esc_attr( $field['id'] . '_' . $k ) . '" /> ' . $v . '</label> '; | |
} | |
break; | |
case 'select': | |
$html .= '<select name="' . esc_attr( $option_name ) . '" id="' . esc_attr( $field['id'] ) . '">'; | |
foreach( $field['options'] as $k => $v ) { | |
$selected = false; | |
if( $k == $data ) { | |
$selected = true; | |
} | |
$html .= '<option ' . selected( $selected, true, false ) . ' value="' . esc_attr( $k ) . '">' . $v . '</option>'; | |
} | |
$html .= '</select> '; | |
break; | |
case 'select_multi': | |
$html .= '<select name="' . esc_attr( $option_name ) . '[]" id="' . esc_attr( $field['id'] ) . '" multiple="multiple">'; | |
foreach( $field['options'] as $k => $v ) { | |
$selected = false; | |
if( in_array( $k, $data ) ) { | |
$selected = true; | |
} | |
$html .= '<option ' . selected( $selected, true, false ) . ' value="' . esc_attr( $k ) . '" />' . $v . '</label> '; | |
} | |
$html .= '</select> '; | |
break; | |
case 'image': | |
$image_thumb = ''; | |
if( $data ) { | |
$image_thumb = wp_get_attachment_thumb_url( $data ); | |
} | |
$html .= '<img id="' . $option_name . '_preview" class="image_preview" src="' . $image_thumb . '" /><br/>' . "\n"; | |
$html .= '<input id="' . $option_name . '_button" type="button" data-uploader_title="' . __( 'Upload an image' , 'plugin_textdomain' ) . '" data-uploader_button_text="' . __( 'Use image' , 'plugin_textdomain' ) . '" class="image_upload_button button" value="'. __( 'Upload new image' , 'plugin_textdomain' ) . '" />' . "\n"; | |
$html .= '<input id="' . $option_name . '_delete" type="button" class="image_delete_button button" value="'. __( 'Remove image' , 'plugin_textdomain' ) . '" />' . "\n"; | |
$html .= '<input id="' . $option_name . '" class="image_data_field" type="hidden" name="' . $option_name . '" value="' . $data . '"/><br/>' . "\n"; | |
break; | |
case 'color': | |
?><div class="color-picker" style="position:relative;"> | |
<input type="text" name="<?php esc_attr_e( $option_name ); ?>" class="color" value="<?php esc_attr_e( $data ); ?>" /> | |
<div style="position:absolute;background:#FFF;z-index:99;border-radius:100%;" class="colorpicker"></div> | |
</div> | |
<?php | |
break; | |
} | |
switch( $field['type'] ) { | |
case 'checkbox_multi': | |
case 'radio': | |
case 'select_multi': | |
$html .= '<br/><span class="description">' . $field['description'] . '</span>'; | |
break; | |
default: | |
$html .= '<label for="' . esc_attr( $field['id'] ) . '"><span class="description">' . $field['description'] . '</span></label>' . "\n"; | |
break; | |
} | |
echo $html; | |
} | |
/** | |
* Validate individual settings field | |
* @param string $data Inputted value | |
* @return string Validated value | |
*/ | |
public function validate_field( $data ) { | |
if( $data && strlen( $data ) > 0 && $data != '' ) { | |
$data = urlencode( strtolower( str_replace( ' ' , '-' , $data ) ) ); | |
} | |
return $data; | |
} | |
/** | |
* Load settings page content | |
* @return void | |
*/ | |
public function settings_page() { | |
// Build page HTML | |
$html = '<div class="wrap" id="plugin_settings">' . "\n"; | |
$html .= '<h2>' . __( 'Plugin Settings' , 'plugin_textdomain' ) . '</h2>' . "\n"; | |
$html .= '<form method="post" action="options.php" enctype="multipart/form-data">' . "\n"; | |
// Setup navigation | |
$html .= '<ul id="settings-sections" class="subsubsub hide-if-no-js">' . "\n"; | |
$html .= '<li><a class="tab all current" href="#all">' . __( 'All' , 'plugin_textdomain' ) . '</a></li>' . "\n"; | |
foreach( $this->settings as $section => $data ) { | |
$html .= '<li>| <a class="tab" href="#' . $section . '">' . $data['title'] . '</a></li>' . "\n"; | |
} | |
$html .= '</ul>' . "\n"; | |
$html .= '<div class="clear"></div>' . "\n"; | |
// Get settings fields | |
ob_start(); | |
settings_fields( 'plugin_settings' ); | |
do_settings_sections( 'plugin_settings' ); | |
$html .= ob_get_clean(); | |
$html .= '<p class="submit">' . "\n"; | |
$html .= '<input name="Submit" type="submit" class="button-primary" value="' . esc_attr( __( 'Save Settings' , 'plugin_textdomain' ) ) . '" />' . "\n"; | |
$html .= '</p>' . "\n"; | |
$html .= '</form>' . "\n"; | |
$html .= '</div>' . "\n"; | |
echo $html; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
$settings = new WordPress_Plugin_Template_Settings( __FILE__ ); | |
?> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
jQuery(document).ready(function($) { | |
/***** Colour picker *****/ | |
$('.colorpicker').hide(); | |
$('.colorpicker').each( function() { | |
$(this).farbtastic( $(this).closest('.color-picker').find('.color') ); | |
}); | |
$('.color').click(function() { | |
$(this).closest('.color-picker').find('.colorpicker').fadeIn(); | |
}); | |
$(document).mousedown(function() { | |
$('.colorpicker').each(function() { | |
var display = $(this).css('display'); | |
if ( display == 'block' ) | |
$(this).fadeOut(); | |
}); | |
}); | |
/***** Uploading images *****/ | |
var file_frame; | |
jQuery.fn.uploadMediaFile = function( button, preview_media ) { | |
var button_id = button.attr('id'); | |
var field_id = button_id.replace( '_button', '' ); | |
var preview_id = button_id.replace( '_button', '_preview' ); | |
// If the media frame already exists, reopen it. | |
if ( file_frame ) { | |
file_frame.open(); | |
return; | |
} | |
// Create the media frame. | |
file_frame = wp.media.frames.file_frame = wp.media({ | |
title: jQuery( this ).data( 'uploader_title' ), | |
button: { | |
text: jQuery( this ).data( 'uploader_button_text' ), | |
}, | |
multiple: false | |
}); | |
// When an image is selected, run a callback. | |
file_frame.on( 'select', function() { | |
attachment = file_frame.state().get('selection').first().toJSON(); | |
jQuery("#"+field_id).val(attachment.id); | |
if( preview_media ) { | |
jQuery("#"+preview_id).attr('src',attachment.sizes.thumbnail.url); | |
} | |
}); | |
// Finally, open the modal | |
file_frame.open(); | |
} | |
jQuery('.image_upload_button').click(function() { | |
jQuery.fn.uploadMediaFile( jQuery(this), true ); | |
}); | |
jQuery('.image_delete_button').click(function() { | |
jQuery(this).closest('td').find( '.image_data_field' ).val( '' ); | |
jQuery( '.image_preview' ).remove(); | |
return false; | |
}); | |
/***** Navigation for settings page *****/ | |
// Make sure each heading has a unique ID. | |
jQuery( 'ul#settings-sections.subsubsub' ).find( 'a' ).each( function ( i ) { | |
var id_value = jQuery( this ).attr( 'href' ).replace( '#', '' ); | |
jQuery( 'h3:contains("' + jQuery( this ).text() + '")' ).attr( 'id', id_value ).addClass( 'section-heading' ); | |
}); | |
// Create nav links for settings page | |
jQuery( '#plugin_settings .subsubsub a.tab' ).click( function ( e ) { | |
// Move the "current" CSS class. | |
jQuery( this ).parents( '.subsubsub' ).find( '.current' ).removeClass( 'current' ); | |
jQuery( this ).addClass( 'current' ); | |
// If "All" is clicked, show all. | |
if ( jQuery( this ).hasClass( 'all' ) ) { | |
jQuery( '#plugin_settings h3, #plugin_settings form p, #plugin_settings table.form-table, p.submit' ).show(); | |
return false; | |
} | |
// If the link is a tab, show only the specified tab. | |
var toShow = jQuery( this ).attr( 'href' ); | |
// Remove the first occurance of # from the selected string (will be added manually below). | |
toShow = toShow.replace( '#', '', toShow ); | |
jQuery( '#plugin_settings h3, #plugin_settings form > p:not(".submit"), #plugin_settings table' ).hide(); | |
jQuery( 'h3#' + toShow ).show().nextUntil( 'h3.section-heading', 'p, table, table p' ).show(); | |
return false; | |
}); | |
}); |
Thanks for the code, I use the example for several plugins. I've noticed that the settings page is generated for every page in the dashboard.
I use this code at the top of the method "settings_page"
$screen = get_current_screen(); if ( $screen->id != 'settings_page_plugin_settings' ) { return; }
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This would break the tabs right? tried it?