WordPress Theme Customizer Boilerplate
- 1. Introduction To The WordPress Theme Customizer
- 2. Interacting With WordPress Theme Customizer
- 3. Currently Reading: WordPress Theme Customizer Boilerplate
- 4. Extending The WordPress Theme Customizer Boilerplate
- 5. Theme Customizer: Conditionals, Child Themes and Plugins
Update: This article has been edited on February 19th 2013, to reflect the changes made to Theme Customizer Boilerplate.
Hopefully you read and enjoyed first two posts of Theme Customizer series — Introduction to WordPress Theme Customizer and Interacting With Theme Customizer. Now it’s time to move to main course and start assembling Theme Customizer boilerplate you’ll be able to use in your themes. This post contains a few long blocks of code, so pay attention to inline comments.
Note: If you’d rather just use the boilerplate right away, you can download it from Github and change fields in $options array by hooking into ‘thsp_cbp_options_array’ filter hook.
What We’re Creating
- customizer.php – This is the main Theme Customizer boilerplate file, the one that adds sections, settings and controls using data from options array
- custom-controls.php – Custom controls classes and an action hook that allows you to add your own custom controls
- helpers.php – Helper functions, used to retrieve theme options, option defaults etc.
- options.php – Sample options and a filter hook that allows you to edit the options array and use your own fields
- customizer-controls.css – Basic CSS for image replaced radio custom control
The whole idea is to be able to copy these files to a subdirectory in your theme directory, include customizer.php file from your functions.php and change anything you like, including and especially the options array, by using Theme Customizer Boilerplate hooks (explained in Part 4). Update: Previously, you’d just edit options.php, but using hooks makes things a lot cleaner.
Now let’s use a real example — we’ll add a text control to a new Theme Customizer section. The array is placed in a helper function and has a filter applied to it when returned. This way you can add more options from a child theme or plugin. Here it is:
/**
* Helper function that holds array of theme options.
*
* @return array $options Array of theme options
* @uses thsp_get_theme_customizer_fields() defined in customizer/helpers.php
*/
function thsp_get_theme_customizer_fields() {
/*
* Using helper function to get default required capability
*/
$required_capability = thsp_settings_page_capability();
$options = array(
// Section ID
'new_customizer_section' => array(
/*
* We're checking if this is an existing section
* or a new one that needs to be registered
*/
'existing_section' => false,
/*
* Section related arguments
* Codex - http://codex.wordpress.org/Class_Reference/WP_Customize_Manager/add_section
*/
'args' => array(
'title' => __( 'New Section Title', 'my_theme_textdomain' ),
'description' => __( 'New section description', 'my_theme_textdomain' ),
'priority' => 10
),
/*
* 'fields' array contains all the fields that need to be
* added to this section
*/
'fields' => array(
/*
* ==========
* ==========
* Text field
* ==========
* ==========
*/
// Field ID
'new_text_field' => array(
/*
* Setting related arguments
* Codex - http://codex.wordpress.org/Class_Reference/WP_Customize_Manager/add_setting
*/
'setting_args' => array(
'default' => __( 'Default text value', 'my_theme_textdomain' ),
'type' => 'option',
'capability' => $required_capability,
'transport' => 'refresh',
'sanitize_callback' => 'thsp_sanitize_cb',
),
/*
* Control related arguments
* Codex - http://codex.wordpress.org/Class_Reference/WP_Customize_Manager/add_control
*/
'control_args' => array(
'label' => __( 'New text control label', 'my_theme_textdomain' ),
'type' => 'text', // Text field control
'priority' => 1
)
)
)
),
);
/*
* 'thsp_customizer_options' filter hook will allow you to
* add/remove some of these options from a child theme
*/
return apply_filters( 'thsp_customizer_options', $options );
}
Update: The array stays the same, but now you can also pass the options array to a filter hook, see Part 4 for more details.
Looks complicated at first sight, I know, but let me explain.
The $options array consists of section(s) (only one in this case – new_customizer_section), each section has it’s arguments, fields and a boolean value (existing_section) to indicate if it’s a new section, or we’re just adding some fields to an existing one. Arguments array is identical to what you’d pass to $wp_customize->add_section method. Fields array is slightly more complex.
Update: choices array in control arguments is now a multi-dimensional array.
Each field consists of a Customizer setting and a Customizer control. That’s why we have setting_args and control_args arrays. They are almost exactly the same as arguments arrays you’d pass to $wp_customize->add_setting and $wp_customize->add_control methods. The only difference is ‘choices’ array in control arguments, $wp_customize->add_control requires it to be a simple key => value pair array and we’re using an multi-dimensional array instead.
This way it’s possible to pass more data to each one of the choices, so if you’re, for example, loading Google Fonts in your theme, you’ll be able to have strings that allow you to load the correct font inside choices array. You can see an example of this theme that uses Customizer Boilerplate.
So, if you’re already aware of those three methods, it’s all very familiar.
Adding a checkbox control is almost the same, you just need to change ‘type’ to ‘checkbox’ in ‘control_args’ array:
/*
* ==============
* Checkbox field
* ==============
*/
'new_checkbox_field' => array(
'setting_args' => array(
'default' => true,
'type' => 'option',
'capability' => $required_capability,
'transport' => 'refresh',
'sanitize_callback' => 'thsp_sanitize_cb',
),
'control_args' => array(
'label' => __( 'New radio control label', 'my_theme_textdomain' ),
'type' => 'checkbox', // Checkbox field control
'priority' => 2
)
),
Radio and select controls are almost the same, you just need to specify given choices:
/*
* ===========
* ===========
* Radio field
* ===========
* ===========
*/
'new_radio_field' => array(
'setting_args' => array(
'default' => 'option-2',
'type' => 'option',
'capability' => $thsp_cbp_capability,
'transport' => 'refresh',
),
'control_args' => array(
'label' => __( 'New radio control label', 'my_theme_textdomain' ),
'type' => 'radio', // Radio control
'choices' => array(
'option-1' => array(
'label' => __( 'Option 1', 'my_theme_textdomain' )
),
'option-2' => array(
'label' => __( 'Option 2', 'my_theme_textdomain' )
),
'option-3' => array(
'label' => __( 'Option 3', 'my_theme_textdomain' )
)
),
'priority' => 3
)
),
/*
* ============
* ============
* Select field
* ============
* ============
*/
'new_select_field' => array(
'setting_args' => array(
'default' => 'option-3',
'type' => 'option',
'capability' => $thsp_cbp_capability,
'transport' => 'refresh',
),
'control_args' => array(
'label' => __( 'New select field label', 'my_theme_textdomain' ),
'type' => 'select', // Select control
'choices' => array(
'option-1' => array(
'label' => __( 'Option 1', 'my_theme_textdomain' )
),
'option-2' => array(
'label' => __( 'Option 2', 'my_theme_textdomain' )
),
'option-3' => array(
'label' => __( 'Option 3', 'my_theme_textdomain' )
)
),
'priority' => 4
)
)
And finally, two complex control types that are built into WordPress — file upload and image upload:
/*
* ===========
* ===========
* File Upload
* ===========
* ===========
*/
'new_file_upload_field' => array(
'setting_args' => array(
'default' => '',
'type' => 'option',
'capability' => $thsp_cbp_capability,
'transport' => 'refresh',
),
'control_args' => array(
'label' => __( 'File upload', 'my_theme_textdomain' ),
'type' => 'upload', // File upload field control
'priority' => 5
)
),
/*
* ============
* ============
* Image Upload
* ============
* ============
*/
'new_image_upload_field' => array(
'setting_args' => array(
'default' => '',
'type' => 'option',
'capability' => $thsp_cbp_capability,
'transport' => 'refresh',
),
'control_args' => array(
'label' => __( 'Image upload', 'my_theme_textdomain' ),
'type' => 'image', // Image upload field control
'priority' => 6
)
)
Note that I used ‘type’ => ‘option’ in setting arguments for all of these fields. This will allow all the values to be stored as one value in your database. The alternative is ‘type’ => ‘theme_mod’ but for now let’s stick with ‘option’.
Using Options Array to Add Customizer Sections, Settings and Controls
If you’re not sure how to interact with WordPress Theme Customizer, go and check, because that’s what we’ll be doing now. The only difference is that instead of adding sections, settings and controls one at a time, we’ll automate the process using serialized array we created. Let’s just jump into it:
function thsp_cbp_customize_register( $wp_customize ) {
/**
* Custom controls
*/
require( dirname(__FILE__) . '/custom-controls.php' );
/*
* Get all the fields using a helper function
*/
$thsp_sections = thsp_cbp_get_fields();
/*
* Get name of DB entry under which options will be stored
*/
$thsp_cbp_option = thsp_cbp_option();
/**
* Loop through the array and add Customizer sections
*/
foreach( $thsp_sections as $thsp_section_key => $thsp_section_value ) {
/**
* Adds Customizer section, if needed
*/
if( ! $thsp_section_value['existing_section'] ) {
$thsp_section_args = $thsp_section_value['args'];
// Add section
$wp_customize->add_section(
$thsp_section_key,
$thsp_section_args
);
} // end if
/*
* Loop through 'fields' array in each section
* and add settings and controls
*/
$thsp_section_fields = $thsp_section_value['fields'];
foreach( $thsp_section_fields as $thsp_field_key => $thsp_field_value ) {
/*
* Check if 'option' or 'theme_mod' is used to store option
*
* If nothing is set, $wp_customize->add_setting method will default to 'theme_mod'
* If 'option' is used as setting type its value will be stored in an entry in
* {prefix}_options table. Option name is defined by thsp_cbp_option() function
*/
if ( isset( $thsp_field_value['setting_args']['type'] ) && 'option' == $thsp_field_value['setting_args']['type'] ) {
$setting_control_id = $thsp_cbp_option . '[' . $thsp_field_key . ']';
} else {
$setting_control_id = $thsp_field_key;
}
/*
* Add default callback function, if none is defined
*/
if ( ! isset( $thsp_field_value['setting_args']['sanitize_cb'] ) ) {
$thsp_field_value['setting_args']['sanitize_cb'] = 'thsp_cbp_sanitize_cb';
}
/**
* Adds Customizer settings
*/
$wp_customize->add_setting(
$setting_control_id,
$thsp_field_value['setting_args']
);
/**
* Adds Customizer control
*
* 'section' value must be added to 'control_args' array
* so control can get added to current section
*/
$thsp_field_value['control_args']['section'] = $thsp_section_key;
/*
* $wp_customize->add_control method requires 'choices' to be a simple key => value pair
*/
if ( isset( $thsp_field_value['control_args']['choices'] ) ) {
$thsp_cbp_choices = array();
foreach( $thsp_field_value['control_args']['choices'] as $thsp_cbp_choice_key => $thsp_cbp_choice_value ) {
$thsp_cbp_choices[$thsp_cbp_choice_key] = $thsp_cbp_choice_value['label'];
}
$thsp_field_value['control_args']['choices'] = $thsp_cbp_choices;
}
// Check control type
if ( 'color' == $thsp_field_value['control_args']['type'] ) {
$wp_customize->add_control(
new WP_Customize_Color_Control(
$wp_customize,
$setting_control_id,
$thsp_field_value['control_args']
)
);
} elseif ( 'image' == $thsp_field_value['control_args']['type'] ) {
$wp_customize->add_control(
new WP_Customize_Image_Control(
$wp_customize,
$setting_control_id,
$thsp_field_value['control_args']
)
);
} elseif ( 'upload' == $thsp_field_value['control_args']['type'] ) {
$wp_customize->add_control(
new WP_Customize_Upload_Control(
$wp_customize,
$setting_control_id,
$thsp_field_value['control_args']
)
);
} else {
$wp_customize->add_control(
$setting_control_id,
$thsp_field_value['control_args']
);
}
} // end foreach
} // end foreach
}
add_action( 'customize_register', 'thsp_cbp_customize_register' );
Going through all the sections, adding the ones that don’t already exist, then going through all the fields in each section, adding a setting and a control for each. That’s all that’s going on here.
Remember that we used ‘type’ => ‘option’ for all the setting? That’s why we now have “my_theme_options[$thsp_field_key]” for both settings and sections. This will store all values as one serialized array that you can retrieve by using get_option( ‘my_theme_options’ ). Or you can just use helper functions defined in helpers.php to retrieve both current values and default values for all fields:
/**
* Get Option Values
*
* Array that holds all of the options values
* Option's default value is used if user hasn't specified a value
*
* @uses thsp_get_theme_customizer_defaults() defined in /customizer/options.php
* @return array Current values for all options
* @since Theme_Customizer_Boilerplate 1.0
*/
function thsp_cbp_get_options_values() {
// Get the option defaults
$option_defaults = thsp_cbp_get_options_defaults();
// Parse the stored options with the defaults
$thsp_cbp_options = wp_parse_args( get_option( thsp_cbp_option(), array() ), $option_defaults );
// Return the parsed array
return $thsp_cbp_options;
}
/**
* Get Option Defaults
*
* Returns an array that holds default values for all options
*
* @uses thsp_get_theme_customizer_fields() defined in /customizer/options.php
* @return array $thsp_option_defaults Default values for all options
* @since Theme_Customizer_Boilerplate 1.0
*/
function thsp_cbp_get_options_defaults() {
// Get the array that holds all theme option fields
$thsp_sections = thsp_cbp_get_fields();
// Initialize the array to hold the default values for all theme options
$thsp_option_defaults = array();
// Loop through the option parameters array
foreach ( $thsp_sections as $thsp_section ) {
$thsp_section_fields = $thsp_section['fields'];
foreach ( $thsp_section_fields as $thsp_field_key => $thsp_field_value ) {
// Add an associative array key to the defaults array for each option in the parameters array
if( isset( $thsp_field_value['setting_args']['default'] ) ) {
$thsp_option_defaults[$thsp_field_key] = $thsp_field_value['setting_args']['default'];
} else {
$thsp_option_defaults[$thsp_field_key] = false;
}
}
}
// Return the defaults array
return $thsp_option_defaults;
}
There’s only one more thing I should mention — sanitization callback function that we specified in ‘setting_args’ array. Function is defined in extend.php and simply runs data through wp_kses_post function:
/**
* Theme Customizer sanitization callback function
*/
function thsp_sanitize_cb( $input ) {
return wp_kses_post( $input );
}
Where to From Here?
For now, you can use this Theme Customizer Boilerplate in your themes, all you need to do is download it from Github, copy into your theme’s directory and include the main file from functions.php, which is 100% functional and good enough for most themes.
Since your theme is not “like most themes”, next week we’ll see how to extend the boilerplate by using its filter and action hooks.
I would love to hear how you think this boilerplate could be improved or expanded, so please fire away in the comments.
Thanks for this awesome series, Slobodan! I believe the customizer is THE way to go for theme options management.
Since you’ll be discussing complex controls next…I’ve noticed that the background image selector and image uploader do not let you select files already in your media library. Any way to add that functionality? I’d love to see it discussed in a coming article.
Thanks, keep up the great work!
Jason
Jason, thanks!
Not sure if I’ll cover that in this series, but looks like this guy might have an answer:
I really think that this boilerplate is awesome. Just need to know if it also supports “Panel”, is it possible? Thanks!
This is an older post, I’m not sure if the author has updated the boilerplate yet, however, I’m sure it wouldn’t be hard to add if it doesn’t have support yet.
Hello Slobodan,
Thanks for these great posts.
I’m trying to implement your boilerplate into my theme but encounter some problems.
While trying to add a new section in the $options array, it doesn’t show up in the customizer. Just the first one.
It has it’s own ID and I reactivate the theme, but with no luck.
I’m also want one of my textfields show up as textarea.
That custom control is defined in extend.php and called via customizer.php, changing ‘type’ => ‘textarea’ just don’t work.
Any ideas/suggestions about this?
Thanks.
Ronald,
You can check the Github repo again, I’ve added support for textarea and number control.
For the section, if you want it to be added to Customizer, you must set ‘existing_section’ => false and make sure there are some fields in it.
Hi Slobodan,
Great thins. For years I was discovering Dreamweaver and before that GoLive … do anyone remember this product?
Now I’m quite impressed by WP and I would really like to know where to start developing themes for WP? Can you, as expert, let me (and maybe someone else) know which literature or source do you recommend?
Many thanks in advance.
Matej,
I sure remember Dreamweaver, used to have it installed way back when it was still Macromedia Dreamweaver.
Great to hear you’re interested in WordPress theme development, it’s an interesting and potentially very lucrative business. Here’s what I’d recommend for you, to start with:
It’s written by the Automattic (company behind WordPress) Theme Team and if you’re asking me, it’s the best resource to learn developing WordPress themes properly.
If you’re really serious about learning and committed to it (which I’m sure you are), feel free to get in touch with me, I’d love to help you learn. If you’re using Twitter, that’s probably the best way to do it, you can see my Twitter handle in the author box in this post.
I am really like this dreamweaver that’s why I install this for operating my activities. It’s really making comfortable my life.