I’m currently building a WordPress Theme that is very versatile and has a fairly high number of Theme options (although I’m trying to abstract many of them from the end-user to keep things simple).
I was very surprised to see that WordPress itself doesn’t include a way to easily create your own Settings panels, in the same way that for example it includes a class/library for creating admin list tables (like the ones for Posts or Links, for example) that you can extend to build your own list tables without having to write any markup.
There are however a couple of external libraries that you can use to abstract the creation of Options panels: the two most mature ones (at first glance) are Options Framework from Devin Price, and the UpThemes Framework from Themes developer UpThemes. Both look good, are well-coded, and have a decent following and activity on Github.
I chose to go for Options Framework because it is a little more “barebones”, its development is very iterative and ongoing, and most of all developer Devin seems to be really helpful and responsive to both Github pull requests and comments on his website.
There are actually two versions of Options Framework: a Plugin version, and a “Theme” version that you can actually use as a library. I am mostly interested in the Theme version because I want my theme to be self-contained and to be able to provide extra flexibility.
Which brings us to the core of this post: how to actually extend Options Framework to make it more customizable and be able to add more functionality?
First, Options Framework is already reasonably extendable in the sense that it uses filters (notably, for data sanitization) and WordPress-type pluggable functions (i.e., functions definitions are enclosed in if ( ! function_exists( 'optionsframework_mlu_init' ) ) { }
branchings).
However, for a few of my needs, I had to hack into Options Framework and add extensibility “hooks”. This post serves as a reference as to how I implemented these hooks.
First, I want to have my Theme Options WordPress submenu inside my Theme’s menu in the Admin panel sidebar, not under Appearance. This is actually documented by Devin (see Figure below), but still requires hacking the code directly, as there are several places where the appearance menu hook ('appearance_page_options-framework'
) is hardcoded. The way I propose to add extensibility on this point is through the use of a constant named OPTIONS_FRAMEWORK_ADMIN_PAGE
(Options Framework already uses constants like OPTIONS_FRAMEWORK_URL
and OPTIONS_FRAMEWORK_DIRECTORY
).
Then, there are things that I wanted to configure in the Frontend behavior of Options Framework, that were hardcoded in the JS file. I think it makes sense to be able to configure or extend the JS behavior from outside Options Framework. For example, I don’t like the fading effect on Options tabs, so I added a fadeDuration
setting (that you can set to 0
from outside the library), but kept its default value of 400
inside Options Framework.
To do this elegantly, I actually used the jQuery extend function, which is what jQuery plugins use to define defaults and options. In turn, I am able to configure the JS values from outside Options Framework, with code like the following:
add_action('optionsframework_custom_scripts', 'optionsframework_custom_scripts'); function optionsframework_custom_scripts() { // The following is my attempt at adding some (more) parametrability to Options Framework's JS. $of_options = array('fadeDuration' => 0, 'navTabSelector' => '.nav-popshop-settings a'); echo sprintf('<script type="text/javascript">/* <![CDATA[ */ var Of_options = %s; /* ]]> */</script>', json_encode($of_options)); }
In my theme, I also want to keep a toplevel set of tabs (“Dashboard”, “Orders”, “Settings”) on the Settings page, so I need to be able to specify to the Javascript file that the set of tabs generated by Options Framework is hooked on a different CSS selector. I also style the set of tabs a little differently (from outside the library as well):
My Theme also have a “Getting Started” page that is integrated with the Options Framework page. This “subpage” interacts with the options page (for example, it has a different styling), and I needed to know when tabs were clicked. I did this by triggering custom jQuery events (named of-tab-active
) from Options Framework. That way, I can hook on this event like this:
// Hook into Options Framework's custom tab selection event, in order to style specific options groups differently. $(".nav-popshop-settings a.nav-tab").bind('of-tab-active', function(){ $('#optionsframework').removeClass().addClass('postbox').addClass($(this).attr('id')); });
Another thing I did (but which didn’t require any change to Options Framework) was to duplicate the “Save Options” button in Javascript, and place it on top of each subpage. When the button is only at the bottom of the page, I’ve found that it’s easy to miss it and forget to save changes (in particular as more and more apps save settings “transparently”). This is pretty hackish, but this is how I did it:
// Copy "Save Options" submit buttons at the top of pages (Settings page) var submit_button = '<input class="button-primary top-button" type="submit" value="' + $("#optionsframework-submit input[name='update']").attr('value') + '" name="update">'; // Sadly there is no jQuery outerHTML function so we have to reconstruct the markup ourselves... // We also add a custom class ("top-button"). $("#optionsframework h3").append(submit_button); // We append into h3 tags, although this is not semantically ideal.
Another thing that I thought about (though I ended up not needing it at this point) was the ability to extend the types of interface elements used in Options Frameworks (text, textarea, select, etc.) and defined in optionframework_fields()
, so that plugins would be able to define their own interface elements (sliders, etc.) without having to hack into Options Framework.
This is basically what I did to extend Options Framework. All in all, this is a great library! And it has helped me develop my Theme considerably. My Pull request can be found on Github.