Making theme options modular

Today I submitted version 1.1 of Museum Core to the WordPress theme repository for review. Originally this was going to just be a simple update with some bugfixes, but I had thought of a way to make it easier to build child themes that include some options settings from the parent theme (Museum Core) without having to include all of the options from the parent. It started out with this ticket I created on GitHub.

Now, the ticket evolved from that first sketch of an idea, and I ultimately abandoned the concept of using actions and remove_action to change what options get used. But the current approach is, I think, a pretty powerful way to approach theme options development so that things can be reused or abandoned in child themes and it isn’t something that I’ve seen other people doing. (To be fair, this is actually what’s described in the WordPress Codex, but I haven’t seen it in action on very many theme options pages, or any tutorials talking about how one would go about writing a theme options page, so I thought it was worth writing about.) And it reduces my entire theme options down to this code:

So let’s take a look at what we’re doing in ap_core_do_theme_options

First of all, I’ve pulled out all the code and put it into a new file called option-setup.php. This handles all the stuff that was previously where the _do_theme_options function is now but everything has been pretty drastically rewritten. First we have ap_core_do_theme_options which looks like this:

If you’ve tried or are currently using Museum Core, you’ll already know that the theme options page sports some tabs. So the first function we’re calling in _do_theme_options is the tab setup. This is a pretty straightforward function that just spits out the html needed to set up the tabs. The real meat is in the next three functions. We won’t look at every single function, we’ll just dig into the main highlights so you get an idea of what’s going on. Let’s peek at ap_core_general_settings which handles the, wait for it, general settings tab.

ap_core_general_settings looks an awful lot like ap_core_do_theme_options, with a bit of HTML thrown in to set up the table that’s going to hold the actual options. That uniformity is intentional, the idea is that you can swap functions in and out however you want. You can use your own modified version of ap_core_general_settings in your child theme, or you can do away with the tabs and just put all your options straight into ap_core_do_theme_options. Or you can mix and match options from the various settings tabs and create your own tabs, it’s up to you. Let’s take a look at what an individual option looks like now.

That’s got a bit more heft to it. But it’s still pretty basic. All I did here is pull the table row from the original theme-options.php and added it to this function. I’m using output buffering to store all the php/html as a variable and echoing the variable at the end which is what gets sent to the function calling this function (in this case ap_core_sidebar_option). I’m also calling the defaults and options from the database so our values are loaded and stored. This is done ad nauseum for each setting, so each setting gets called separately and can be isolated, grouped, cut out, whatever as needed by a child theme.

So what happens when I want to build a child theme and use some of these options? Here’s an example:

First what I’m going to do is start with a new theme options function. I’m keeping the 3 tabs from Core, but I’m adding a 4th tab. Since Core only supports 3 tabs, and I’ll need to set up the title for the fourth tab, I’m doing my own tabs. We’ll start by setting up the tabs. Remember, you don’t have to use the tabs at all; if you just want all of your options on the Theme Options page, you can just cut out the tabs function and then they’ll display normally, one on top of the other.

The two things that are changed are highlighted by the fact that the textdomain (‘museum-core’, ‘my-theme’) has changed. We don’t need to change the textdomain of the other tabs, because, assuming the text was translated in a language file, those would already be covered by the existing translation in Core — only the new translation strings need to be added (of course, none of this is necessary, really, if you don’t care about localization of your child theme). We changed “Typography & Fonts” to “Font Stuff” and added the fourth tab, “Custom Tab”. So far, so good. This is what it looks like:

20120511 8bgrj593pymrskawg7me5dky7y.medium Making theme options modular

Now that we’ve got the tabs, and we’ve added the options pages that we’re pulling from Core, it’s time to create our new settings page. We’ll do this really minimally and just add one option. We won’t add a bunch of function calls (although we could), we’ll just put our option straight into the new my_custom_theme_settings function. It looks something like this:

Now, in order for this to be actually functional, we’d need to define some new options for our theme so we could store them in the database, this is just meant to be an example and illustrate how we’re able to still pull functions from the parent theme and mix them into our custom theme functions. Here’s what that new tab looks like:

20120511 gjd8g2p9m9dw3a397wykaxw78g Making theme options modular

To summarize, what I’ve got is a modular way of calling individual settings that can be swapped in and out at will and replaced with custom settings (or not) however you want. If you have any thoughts or questions, feel free to post in the comments. If you’d like to dig into the source files, you can grab the latest version of Museum Core from GitHub. You can also take a look at the complete code from this tutorial on GitHub. And if you want to take a look at the files on your computer and hack them up, you can download the source files used to create the child theme in the screenshots here.

Create a site down notifier using iftt, Yahoo Pipes and ismyblogworking.com

This is a bit off the usual WordPress topic, but I discovered a handy tool to get automatic notifications sent to your email (or IM, or Twitter or whatever you want) if your site is down or broken.  This can be particularly helpful if you are a designer or webmaster or if you just maintain a bunch of different websites and don’t check each one everyday.  We’ll be leveraging several different sites/technologies, so I’ll go step by step.

ismyblogworking.com

The first tool we’ll be using is ismyblogworking.com.  This is a fantastic resource particularly for WordPress users as it will give you some valuable insight into how your site is working and if there is anything that needs your attention.  Types of things it can check on a WordPress site are things like what version of WordPress you’re running and if your RSS feeds are validating.  ismyblogworking.com will also give you a message if it thinks your site is loading slowly.  This is the crux of this tool since I just discovered that ismyblogworking.com can create an RSS feed for your status.

The RSS feed URL it generates looks like this:

http://ismyblogworking.com/YOURDOMAIN.com?feed=1

This is fairly easy to remember and if you have multiple sites, you can just copy and paste the URL and change the domain inside the URL, which is exactly what we’ll be doing.

Yahoo Pipes

If you’ve never used Yahoo Pipes before, it’s a fantastic tool that allows you to create complex operations or alerts based on different types of inputs.  I won’t go into all the potential uses here, but basically it allows non-programmers to do things that pretty much would only be possible otherwise with some complex scripting or programming knowledge.  What we’ll be doing today is joining a bunch of different RSS feeds, sorting them by date, and then filtering out updates that are just telling us that our blog is working.

Step 1 — Get your ismyblogworking.com RSS URL

First, from the Sources menu in Yahoo Pipes, select Fetch Feed and click the + to add it to the Pipe.  You can add up to 10 RSS URLs, so if you need more, you’ll need to create a new Fetch Feed module and join them together with a Union module (more on that later).

Step 2 — Add a sort module

We’ll want to sort them in descending order, so the most recent updates are at the top.  To do this, we’ll need to add a Sort module.  These are located in the Operators section.  To connect the two modules, simply click and drag from the circle at the bottom of your Fetch Feed source module and connect it to the top (input) of the Sort module.  You’ll see a friendly-looking “pipe” appear to connect the two.  Once they’re connected, you’ll get a number of different items to sort by.  You want to select the item.pubDate to sort by date, and then select Descending to put the newest first.

Step 3 — (Union and) Filter

I’ll be using a Union to combine a bunch of different feeds.  You may not need to, so you can skip this.  Unions are in the Operators menu as well and they’re pretty simple; they just provide a bunch of different connectors for sources and a single output connector.  I will join all my source feeds with a Union module and send that to a Filter module.

Filter is in the Operators menu as well (right above Sort).  Our filter is going to be on the item.title.  We want to Block all all items that are “Your blog is working.”  It’s important to have the period if you are going to use this filter, otherwise it won’t be an exact match.  The reason why we’re doing an exact match is that you may get a result like this “Your blog is working (but needs attention)!”  We don’t want to filter those out because if our blog needs attention, we probably should be aware of that as well, so we’re only going to filter out the “everything is A-OK” results.

This is what my Pipe looks like when I’m done:

ismyblogworking.com pipes 750x422 Create a site down notifier using iftt, Yahoo Pipes and ismyblogworking.com

If you click on the Pipe Output, you can see an example of the results.  You can do this on any module to debug specific parts of the pipe.

Step 4 — Create an RSS feed

Now we’ll want to create an RSS feed that we can send to iftt to create the alert.  You could use something other than iftt (like create a FeedBurner feed and then activate the subscribe by email feature, or create a Google or Yahoo Alert) but iftt provides the most options in terms of how you want to deliver the notifications.

First, save your Pipe.  Give it a name like “ismyblogworking.com site notifier” or something that you’ll recognize later.  When it’s saved, click Run Pipe.  After it does the initial run, you will have an option to get an RSS feed.  I’m not too worried right now about errors fetching the RSS feed.  All we’re getting at this point is just historical data and we can look at the actual pipe to find sites that might actually be having issues.

ismyblogworking pipe 750x463 Create a site down notifier using iftt, Yahoo Pipes and ismyblogworking.com

Step 5 — Create an iftt trigger

iftt stands for if this then that, and it is a system that allows you to take some trigger (an instant message, email, RSS update, weather, time, and a whole bunch of other things) and turn it into something else (a Tweet, an instant message, a post to your Facebook wall, an email, etc).  So we’ll be doing something pretty tame as far as iftt goes: taking an RSS feed and turning it into an email, but you could potentially use this to feed a Twitter account that posts status updates on all the sites you maintain or something like that.

The first step is to select the trigger “channel”.  In our case, it’s RSS.

iftt trigger 750x567 Create a site down notifier using iftt, Yahoo Pipes and ismyblogworking.com

We’re going to select “New feed item” but we could have done our filtering here if we wanted to.

Paste your RSS feed URL into the box and click the “Create Trigger” button.

Once it’s done, you should have something that looks like this:

Step 6 — Create the email alert (or whatever)

Click on the that link to create an “action channel” (the part of the if this then that that does the thing you want it to do).  I’m going to select Email to get an email alert.  If you want, you can customize what gets sent in the email.  I’m just going to leave everything at the default.

Click “Create Action,” give it a name, then click “Create task.”

The brilliant thing about this is that there are services that you pay a fee for that give you the same information you can get from this for free.  This may not be a perfect solution (basically limited by Yahoo Pipes’ ability to fetch the feed from ismyblogworking.com) but it couldn’t hurt, it’s free, and it’s much easier than checking 20 – 40 (or more) different websites every day.

WP_Query pagination problems? Here’s a hack

It happens every time I use WP_Query. I get a beautiful-looking custom WP_Query. Then I try to add pagination and everything goes to hell.

I’ve spent hours looking up solutions. Every time. Nine times out of ten, they don’t work. And for no discernible reason. Here’s a hack that worked for me, and is mostly for my own benefit so I don’t forget it next time I am in this situation, but maybe it will help you.

This hack requires Lester Chan‘s plugin WP-PageNavi. Go get it and come back.
For me, this applies specifically to using custom post types. If I wasn’t using CPTs, my WP_Query might not have any problems. Feel free to let me know in the comments if you spot an issue with the way I’m doing things. As I said, this is a hack — meaning it’s not the preferred way to do this — but it’s a functional hack at least.

Let’s start with the query:

			rewind_posts();
			global $wp_query, $paged;
			$temp = $wp_query;
			$wp_query = null;
			$wp_query = new WP_Query();

This is pretty basic. I’m running a rewind_posts() here because I have some custom queries going on in the sidebars, so I threw this in there just to be on the safe side. The next bit is where the hack starts coming in…

			$showposts = 12;
			$offset = ( ( $showposts * $page ) - $showposts );

Here we introduce two variables, both of which we will use in the arguments later. $showposts is a hard-coded rule for the posts_per_page argument. Yes, this is required. The reason will become evident by looking at the next line. Our $offset is being multiplied by the $showposts value (for me, this is 12) and then subtracted by that value again. This is how we are going to fix the issue of not being able to go into paged navigation because it only displays the first page worth of posts — by creating an offset that is multiplied by the current page you are looking at ($page is a global variable that is built into WordPress to display the current page).

Now the $args:

			$args = array(
				'post_type' => 'ap_products',
				'posts_per_page' => $showposts,
				'offset' => $offset,
			);
			query_posts($args);

This is pretty straightforward also. All we’re doing here is declaring the post type (which is just for my use, you can omit this if you aren’t using a custom post type, or replace with your own post type), and then feeding the $showposts and $offset values in here. Then we’re running our query. The full loop looks like this:

<?php the_post(); ?>
	<div <?php post_class(); ?> id="post-<?php the_ID(); ?>">	
	<?php 			
			rewind_posts();
			global $wp_query, $paged;
			$temp = $wp_query;
			$wp_query = null;
			$wp_query = new WP_Query();
			$showposts = 12;
			$offset = ( ( $showposts * $page ) - $showposts ); 
			$args = array(
				'post_type' => 'ap_products',
				'posts_per_page' => $showposts,
				'offset' => $offset,
			);
			query_posts($args); 	?>				
			<div class="entry">
				<?php while ( have_posts() ) : the_post(); ?>				
					<!-- do stuff -->
				<?php endwhile; ?>
				<?php if(function_exists('wp_pagenavi')) { echo '<div class="navigation"><span class="pre-navi"><a href="' . home_url() . '" />1</a></span>'; wp_pagenavi() . '</div>'; } ?>
				<?php $wp_query = null; $wp_query = $temp; ?>
			</div>
		</div>

Notice what I’m doing at the end with the wp_pagenavi function. I’ve added this into my style.css:

.current { display: none; }
.pre-navi { float: left; }

This makes it so that the current page doesn’t display at all. I’m doing this because the current page is always 1 with my fantabulous error, and this way I can replace the page 1 with one that actually links (if you’re on any page other than 1).

And that’s it. It’s not elegant, but it works, and the only real issue is that page 1 is always a link. If you like this solution (or if you hate it), let us know in the comments!