Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save jimratliff/a9a762a8eff66b43071e2c90ffa7bf2a to your computer and use it in GitHub Desktop.
Save jimratliff/a9a762a8eff66b43071e2c90ffa7bf2a to your computer and use it in GitHub Desktop.
<?php
/*
Based on AJ Clarke, "How to Create a Simple WordPress Theme Options Page," WPExplorer,
November 21, 2016, https://www.wpexplorer.com/wordpress-theme-options/
I started with AJ's code. Like many other examples of creating a theme-options page,
AJ sometimes used the same string for more than one distinct purpose, creating some
ambiguity about what role each occurrence of a string plays in its context.
To address this, I've modified the code to use a unique string for each distinct role.
This allows one to (a) understand better what's going on and (b) modify the string for
a particular role with a global search/replace without affecting the strings used for
any other roles.
For example, AJ had:
register_setting( 'theme_options', 'theme_options', array( 'WPEX_Theme_Options', 'sanitize' ) );
where the same string ('theme_options') is used for both of the first two arguments,
which have distinct meanings:
argument #1: $option_group: "A settings group name. This must match the group name used in register_setting(),
which is the page slug name on which the form is to appear."
<https://codex.wordpress.org/Settings_API>
argument #2: $option_name: The name of an option to sanitize and save.
A similar remarks applies to the first two arguments of add_menu_page().
I also changed the names of some of the functions/methods defined within the class to more
obviously signal that these are not standard-WordPress functions. (E.g., I changed the name of
register_settings(), to distinguish it from the standard-WordPress function register_setting().)
AJ's code used "WPEX_" as a prefix, corresponding to WPExplorer. I changed the prefix to "zzyzx"
to be more generic and a good candidate for global search/replace.
*
*/
/* Some WordPress functions have an argument for a callable function. While a PHP function
can be passed by its name as a string, "A method of an instantiated object is passed
as an array containing an object at index 0 and the method name at index 1."
See <https://www.php.net/manual/en/language.types.callable.php>.
This structure/syntax is used below.
*/
/*
Jim's notes
<div class="wrap"> apparently matches the look and feel of existing WordPress options pages.
See Codex, "Creating Options Pages," <https://codex.wordpress.org/Creating_Options_Pages>
zzyzx_Theme_Options_Class : class
zzyzx-custom-admin-screen-background-section: CSS class
zzyzx-custom-admin-login-table : CSS class
zzyzx_add_admin_menu : method Add sub menu page
zzyzx_register_settings : method Register a setting and its sanitization callback
zzyzx_get_theme_option : method Returns single theme option
zzyzx_get_theme_option_array : method Returns all theme options
zzyzx_sanitize : method Sanitization callback
zzyzx_create_admin_page : method Settings page output
'zzyzx_theme_options_array' : id in database for array of theme options
'zzyzx_theme_options_group' : id of options group. Should match between
(a) first argument of register_setting and
(b) argument of settings_fields
'zzyzx-theme-settings-menu-slug' : slug name to refer to menu. Should be
unique for this menu page and only include
lowercase-alpha, dashes, and underscores
'text-domain'
zzyzx_get_this_theme_option : method Helper function to use in
your theme to return a
theme option value
*/
// Exit if accessed directly
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
// Start Class
if ( ! class_exists( 'zzyzx_Theme_Options_Class' ) ) {
class zzyzx_Theme_Options_Class {
/* Start things up */
public function __construct() {
// We only need to register the admin panel on the back-end
if ( is_admin() ) {
add_action( 'admin_menu', array( 'zzyzx_Theme_Options_Class', 'zzyzx_add_admin_menu' ) );
add_action( 'admin_init', array( 'zzyzx_Theme_Options_Class', 'zzyzx_register_settings' ) );
}
}
/* Returns all theme options */
public static function zzyzx_get_theme_option_array() {
return get_option( 'zzyzx_theme_options_array' );
}
/* Returns single theme option */
public static function zzyzx_get_theme_option( $name_of_particular_option ) {
$options = self::zzyzx_get_theme_option_array();
if ( isset( $options[$name_of_particular_option] ) ) {
return $options[$name_of_particular_option];
}
}
/* Adds a top-level menu page */
public static function zzyzx_add_admin_menu() {
// add_menu_page() adds a top-level menu page
add_menu_page(
// Note: argument is browser-window title, not the <h1> header
esc_html__( 'Zzyzx Theme Page Browser Window Title', 'text-domain' ),
// Note: argument is label that appears in the admin sidebar, clicking on
// which opens the options page
esc_html__( 'Zzyzx Theme Menu Label', 'text-domain' ),
'manage_options', // Capability required in order to see this menu
'zzyzx-theme-settings-menu-slug', // slug for resultant options page
// Callable function to be called to output the content of the page.
// See note at top about a method being passed as an array
array( 'zzyzx_Theme_Options_Class', 'zzyzx_create_admin_page' )
);
}
/**
* Register a setting and its sanitization callback.
*
* We are only registering 1 setting so we can store all options in a single option as
* an array. You could, however, register a new setting for each option
*/
public static function zzyzx_register_settings() {
register_setting(
'zzyzx_theme_options_group',
'zzyzx_theme_options_array',
// Notes:
// (1) See note at top about a method being passed as an array
// (2) The third argument of register_setting() is $args, an array of "Data
// used to describe the setting when registered." Although I don't understand
// perfectly how the following jibes with the documentation, it seems
// to be OK to just pass a callable function in that argument.
array( 'zzyzx_Theme_Options_Class', 'zzyzx_sanitize' )
);
}
/**
* Sanitization callback
*/
public static function zzyzx_sanitize( $options ) {
// If we have options let's sanitize them
if ( $options ) {
// Checkbox
if ( ! empty( $options['checkbox_example'] ) ) {
$options['checkbox_example'] = 'on';
} else {
// Remove from options if not checked
unset( $options['checkbox_example'] );
}
// Input
if ( ! empty( $options['input_example'] ) ) {
$options['input_example'] = sanitize_text_field( $options['input_example'] );
} else {
unset( $options['input_example'] ); // Remove from options if empty
}
// Select
if ( ! empty( $options['select_example'] ) ) {
$options['select_example'] = sanitize_text_field( $options['select_example'] );
}
}
// Return sanitized options
return $options;
}
/* Outputs the settings page */
public static function zzyzx_create_admin_page() { ?>
<div class="wrap"> <!--matches the look and feel of existing WordPress options pages-->
<h1><?php esc_html_e( 'Zzyzx Theme Options Page header', 'text-domain' ); ?></h1>
<form method="post" action="options.php">
<?php
// settings_fields() outputs nonce, action, and option_page fields for a settings page
// Argument should match the group name used in register_setting()
settings_fields( 'zzyzx_theme_options_group' );
?>
<table class="form-table zzyzx-custom-admin-login-table">
<?php // Checkbox example ?>
<tr valign="top">
<th scope="row"><?php esc_html_e( 'Checkbox Example Label', 'text-domain' ); ?></th>
<td>
<?php $value = self::zzyzx_get_theme_option( 'checkbox_example' ); ?>
<input type="checkbox" name="zzyzx_theme_options_array[checkbox_example]"<?php checked( $value, 'on' ); ?>>
<?php esc_html_e( 'Checkbox example description.', 'text-domain' ); ?>
</td>
</tr>
<?php // Text input example ?>
<tr valign="top">
<th scope="row"><?php esc_html_e( 'Input Example Label', 'text-domain' ); ?></th>
<td>
<?php $value = self::zzyzx_get_theme_option( 'input_example' ); ?>
<input type="text" name="zzyzx_theme_options_array[input_example]" value="<?php echo esc_attr( $value ); ?>">
</td>
</tr>
<?php // Select example ?>
<tr valign="top" class="zzyzx-custom-admin-screen-background-section">
<th scope="row"><?php esc_html_e( 'Select Example Label', 'text-domain' ); ?></th>
<td>
<?php $value = self::zzyzx_get_theme_option( 'select_example' ); ?>
<select name="zzyzx_theme_options_array[select_example]">
<?php
$options = array(
'1' => esc_html__( 'Option 1', 'text-domain' ),
'2' => esc_html__( 'Option 2', 'text-domain' ),
'3' => esc_html__( 'Option 3', 'text-domain' ),
);
foreach ( $options as $id => $label ) { ?>
<option value="<?php echo esc_attr( $id ); ?>" <?php selected( $value, $id, true ); ?>>
<?php echo strip_tags( $label ); ?>
</option>
<?php } ?>
</select>
</td>
</tr>
</table>
<?php submit_button(); ?>
</form>
</div><!-- .wrap -->
<?php }
}
}
new zzyzx_Theme_Options_Class();
// Helper function to use in your theme to return a theme option value
function zzyzx_get_this_theme_option( $name_of_particular_option = '' ) {
return zzyzx_Theme_Options_Class::zzyzx_get_theme_option( $name_of_particular_option );
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment