A complete, versatile options page class for any WordPress plugin

Building an options page for a plugin can be a daunting prospect – you want to build it in a way that works for your plugin, but also so that it works well within the WordPress UI. Many plugin authors get this painfully wrong and build incredibly unfriendly options pages that look terrible and really don’t fit into the WordPress dashboard design at all.

After putting loads of work into refactoring Seriously Simple Podcasting for the v2.0 release, I’ve developed a single class that will help you to create a versatile and user-friendly options page for your plugin that fits neatly into the WordPress dashboard. All you need to do is add this class to your plugin and modify the array of settings to handle the data that your plugin needs.

This isn’t a tutorial post, so I’m not going to walk you through this step by step, but if you use the class below you can create a WordPress options page with every type of field possible with almost no effort on your part. The field types that this class can create are:

  • Text field
  • Password field
  • Secret text field (where the saved data is not displayed on the page)
  • Text area
  • Single checkbox
  • Multiple checkboxes
  • Select box
  • Multiple select box
  • Radio buttons
  • Number field
  • Colour picker (using WordPress’ built-in colour picker script)
  • Image upload (that saves the image to your site’s media library)

I have included demo fields in the class so you can see how each field type works and what parameters you need to define for each field type – all you need to do is delete these fields and create your own.

The class uses all the WordPress Settings API functions, so it does things in the “correct” way. It also includes a mini-navigation that uses Javascript to show & hide the relevant sections.

You will find the class, along with its associated Javascript file below, but if you want to see it all in action in the context of a full plugin you can check out my WordPress Plugin Template that includes this same code. For a quick overview of what the resulting options page will look like, here’s a handy screenshot (click to enlarge):

Options page
The options page that is generated by this class

Here is the class in its entirety along with the relevant Javascript and include code:

<?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;
}
}

view raw
class.php
hosted with ❤ by GitHub

<?php
$settings = new WordPress_Plugin_Template_Settings( __FILE__ );
?>

view raw
index.php
hosted with ❤ by GitHub

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;
});
});

view raw
settings.js
hosted with ❤ by GitHub

Once you have added all this code you will find the new options page in the dashboard menu by going to Settings > Plugin Settings.

The options that are saved from this page use the $settings_base variable in the class (in this case, ‘wpt_‘)followed by the id field of each setting for their name. So the option name for the first field would be ‘wpt_text_field‘, which you can fetch using get_option( 'wpt_text_field' );.

If you can think of any other field types that you think should be included in this class then let me know in the comments.

28 Thoughts

  1. Great work, just came in handy for my very first plugin. I learned a lot about WP from friendly people like you who are willing to share their knowledge. As for your question about enhancements: how about having some sort of repeater? This would give us the ability for having “sets of settings”. Example: a playlist for an mp3 player, where each entry could for example have a title, a cover picture, etc.

    Another thought: how about storing all settings in an array into the db, instead of having multiple db calls?

    Grettings from germany

    1. In addition to the above: just found your fantastic wordpress plugin template on Github and came up with another idea on this: why not separate the field output logic? So we could use the different field types for the custom post type metaboxes as well.

      1. Thanks for the great feedback Andre! I’ll look into the idea of a ‘set’ of settings like you mention – might be a nice little enhancement. As for storing the options in a single array instead of multiple DB calls, I chose not to do that because when you are calling the options on the frontend it’s not always beneficial to call every single option when all you need is a single option. I’m not sure of the numbers with regards to performance, but I generally favour multiple options instead of one global array.

        I really like the idea of separating out the field logic so it’s usable for custom post types as well – I’ll look into enhancing the template to handle that as I think it would be a very valuable upgrade 🙂

        1. Glad you like my ideas. I think your template is a valuable foundation for every plugin coder, especially for beginners like me. I definitive stick with this post and will refractor my mp3 player plugin with your template as a base, especially after your next update 😉

          For the database calls: I just was a bit concerned about the amount of database calls for each plugin. Since I love the progress in my WP coding skills, i am quite sure that there will be a lot more plugin stuff from me in the future. Right now I am working on a versatile solution to play mp3 files in a WP site, comes with different flavors like single player, footer player ith playlist, etc. Complete with custom post type. I am damn proud about my first plugin. At the moment I am struggling with a single bit: I somehow need to hook into the file upload process. First for ensuring that there are only mp3 files ready, second because I want to create preview files of the uploaded mp3 files. I have a fantastic class for hat in my arsenal, that I have allready used for another project.

          Thanks for your effort, can’t wait to see the progress in your template. Keep me informed please 😉

  2. Hi. Thanks for this. I have a question about multi-select options.

    I need to generate the options dynamically according to the given site’s user roles. So:

    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’ )
    )

    … the options array would need to be built from another foreach and stored in a variable I guess. Here’s the foreach to generate the user roles:

    global $wp_roles;
    $all_roles = $wp_roles->roles;
    $editable_roles = apply_filters(‘editable_roles’, $all_roles);
    foreach($editable_roles as $role=>$theroles){
    echo ”.$wp_roles->role_names[$role].”;
    }

    How could I do this and plug it into the options array? Thanks for your time.

    1. Hi Thom,

      All you need to do is save the array of user roles as a variable in this format array( 'role' => 'Role Name' ). If you’re not sure how to do that then Google should be able to help you out 🙂

  3. Hi,
    Thanks for this great plugin template. I am starting my first plugin with your template. I have a question though. What the “validation callback for field” is doing? Can you explain it for me?

    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’ ), ‘html_data_table_settings’ );

    foreach( $data[‘fields’] as $field ) {

    // Validation callback for field
    $validation = ”;
    if( isset( $field[‘callback’] ) ) {
    $validation = $field[‘callback’];
    }

    // Register field
    $option_name = $this->base . $field[‘id’];
    register_setting( ‘html_data_table_settings’, $option_name, $validation );

    // Add field to page
    add_settings_field( $field[‘id’], $field[‘label’], array( $this, ‘display_field’ ), ‘html_data_table_settings’, $section, array( ‘field’ => $field ) );
    }
    }
    }
    }

    1. The validation callback is a way of validating the submitted form data before saving it. You can create a function that checks the content of the submitted field and performs any action you like on that content before saving it to the database. A good example of this would be if you are asking people to save a URL in one of the fields – using the validation callback you can run the esc_url() function on the submitted text to make sure that it is saved as a properly formatted URL.

  4. Hey, nice job, this is fantastic!
    Im new in this whole backend hardcore programming, and I have a problem with your template. When I click “upload an Image” it just doesnt add it, if I choose a file in my pc it sends an error, and if I choose the “Use one in wordpress” or something like that option, it keeps “buffering” forever. I dont know if there is an issue with your code, i dont know perhaps some function got deprecated, or if I made a mistake. But I followed every step, and the other things are working wonderfully, hope you read this and know how to solve it.

    And of course, Thank you for sharing this, this is amazin!
    Free-source!

    GM.

  5. When I call for option value in plugin file using get_option(‘id’); this is not work means value does not show. please help how can i get option value right way. Thank you!

  6. Thanks Hugh, it saved time.
    JS code was giving me error when I tried to upload an attachment. Adding a “var” before attachment variable fixed it.

    In case we have plenty of options to offer, we should save them as an array. Do you have any idea how can I change your code to save all options in a single array i.e. one entry in wp_options table with serialized data?

  7. Hi,

    I am more of a front-end developer with some php-knowledge. I know how to normally register a custom post type, but with your plugin and your explanation I’m a bit lost where and how to register the custom post types.

    In the ‘lib’-folder I see cpt and taxomomy files, but I think I shouldn’t modify these files as the contain all sorts of variables?

    I would like to register multiple cpt’s, and the ability to switch them ‘on’ or ‘off’ using checkboxes in the settings page. How can I do this?

    Your plugin would be a huge time-saver when it comes to developing client-websites and being flexible with custom post types.

    Thanks in advance!

  8. Sir,I have used your plugin class in one of my plugin.When I active that plugin it’s gives the following error message

    ” The plugin generated 3 characters of unexpected output during activation. If you notice “headers already sent” messages, problems with syndication feeds or other issues, try deactivating or removing this plugin. ”

    Please give solution!

  9. Hello,

    I know this was an old post, but I thought I’d ask if you had any suggestions for multiple image upload fields. In other words, it is returning the same value no matter which image uploader I use, on the first field, and cannot distinguish the difference in fields. I have gotten around this in the past by using class rather than id, but it doesn’t seem to work here.

    Thanks in advance, if you’re still listening.

  10. Nice Work! Im trying to understand how use the template :/ have you some kind of simple plugin where can i see how you use it? for example im trying to add some code in public function install () but cant get it working, at least if have some plugin already made with this template i can understand how it works!

  11. Good Job. 🙂

    It would be a great enhancemet, if there’s a repeating field aswell – or repeating fieldset. So the Admins can create multiple sets of fields.

  12. LOVE IT!
    how would you change it to save options as an array? Thats easier when turning off the plugin. i want to offer removing the options information from the database 😉 – ideas? (p.s: thanks a lot – i really love it – gr8 job).

    1. i think there are some errors… in this line “if( isset( $field[‘default’] ) ) {” you are basiclly blocking assigment of data=option if no default was set which means that later on, radio and select wont show selected options.

  13. (1) January 2017 and WP 4.7.1. Is this still a compatible option? I hope so.
    (2) As a previous post pointed out, the link to the example image is broken.

    Thanks.

  14. Hugh – thanks for the template and notes. I’ve been customizing it a bit for a couple projects, nothing worth contributing the GH repo yet. But one thing that seems to be “the next step” is for an uninstall.php to unregister_setting of everything that’s been registered. Surprisingly (or not, because who can imagine their plugin getting unistalled, yah rite?) I haven’t yet found code anywhere to completely cleanup all settings in a specific setting group. I’d prefer not to copy the code from template register_settings. This means (maybe?) abstracting out the definition of $settings into a static method which can then be used during the uninstallation in a function unregister_settings that looks similar to register_settings.

    Is anyone doing this? Any links to existing code? If I do this post a PR to the repo. Thanks!

Leave a Reply