Extending Custom Post Types in WordPress 3.0

In a previous article about Custom Post Types in WordPress 3.0 I outlined one of the most exciting features coming up in 3.0 and I noticed that people have already started building websites upon these new features. It’s not a secret that I’m working on a project myself which involves 3.0 so just like everybody else, I can’t wait to see it being released!

Continue reading

Adding mod_rewrite Rules to .htaccess in WordPress

This is all about the Twitter Friendly Links plugin I’ve been working on lately. You might have noticed some 404 issues in the comments section on the plugin page, this in most cases is due to caching fail. I wrote about this a while ago when I was working on W3 Total Cache compatibility where all I had to do is set the wp_query object’s is_404 variable to false. Then again, the other caching plugins didn’t understand that, so I came up with my own links caching mechanism – direct cache with .htaccess ;)

The WP_Rewrite article in the Codex pretty much explains everything, except that it’s pretty difficult to add custom rewrite rules to .htaccess via WordPress, as even wp_query object’s non_wp_rules is handled like this:

RewriteRule ^Pattern /Substitution [QSA,L]

With the trailing slash and “Query String Append + Last Rule” hard-coded in the WordPress core. Well here’s how I managed to do Permanent 301 redirects to absolute URLs.

First of all add an action and a filter. My plugin code is wrapped up in a class (and I encourage you to do the same for maximum compatibility):

add_action('generate_rewrite_rules', array(&$this, 'generate_rewrite_rules'));
add_filter('mod_rewrite_rules', array(&$this, 'mod_rewrite_rules'));

The generate_rewrite_rules action is the place where we will add some rules to non_wp_rules, then modify (or customize) in the mod_rewrite_rules filter. Here’s the generate_rewrite_rules function which I’m hooking to:

function generate_rewrite_rules() {
	global $wp_rewrite;
	$non_wp_rules = array(
		'simple-redirect/?$plugin_name' => 'http://google.com',
		'one-more-redirect/?$plugin_name' => 'http://yahoo.com'
	);

	$wp_rewrite->non_wp_rules = $non_wp_rules + $wp_rewrite->non_wp_rules;
}

I used the string ‘plugin_name’ in every rule just to make sure that I don’t edit anyone else’s rewrite rules further on. You’ll understand what I mean once you read the next part of the code. Also make sure you use single quotes as double quotes would make $plugin_name a variable.

function mod_rewrite_rules($rules) {
	$rules = preg_replace('/^(RewriteRule ^.*+/?$)plugin_name (/)(.*) ([QSA,L])$/im', '1 3 [R=301,L]', $rules);
	return $rules;
}

There you go, so the rules are modified using a simple regular expression, which in .htaccess will turn out to be:

RewriteRule ^simple-redirect/?$ http://google.com [R=301,L]
RewriteRule ^one-more-redirect/?$ http://yahoo.com [R=301,L]

The regex is not perfect though, just a sketch. I believe it will not work if the RewriteBase is not set to / but you get my point ;) The rules are written to .htaccess when you access or save changes in the Permalinks section in the admin panel or whenever wp_rewrite object’s flush_rules is called. Now be careful, you cannot call flush_rules anywhere outside the admin panel (although you may try to link with a few includes – wp-admin/includes/misc.php and wp-admin/includes/files.php before flushing) but in my case I’ll just go with the publish_post hook.

WordPress: The template_redirect Hook Returns 404

This is a real quick one. I’ve started using the W3 Total Cache plugin a few days ago which I’m totally satisfied with. Unlike the WP Super Cache plugin, W3TC can keep the caches in memory via memcached, serve the static content through CDNs. The guys at W3 Edge promised to add Amazon CloudFront compatibility which I’m very excited about. As we all know (I guess) Mashable is running WordPress, and guess what! They’re using W3 Total Cache too, so if they could trust those guys, then I’m totally in. Their support over Twitter is awesome, you don’t even have to look for them! Just tweet your problem mentioning “W3 Total Cache” or “W3TC” – they’ll find you via search and help you solve whatever your issue is.

Anyways, that being said, after writing my post announcements to Twitter via Twitter Friendly Links some peeps shouted out that I was returning 404 errors on all of my short links. Strange, thought I, as I was able to see everything fine. Looking at the settings of the plugin I realized that I, as a logged in user, weren’t looking at cached pages, so I turned that off and guess what! I got those 404s. Creepy! After a little talk over Twitter and IM with the guys at W3 Edge, I started to look through the caching plugin code. It’s so well-written and I’ve actually learned a couple of techniques out there! Anyways, the reason I was getting 404s was because W3 TC looked at the $wp_query object and it’s $is_404 variable, which I don’t know why (Frederick from W3 Edge mentioned a redirection issue with WordPress) was returning true on all of my Twitter friendly links!

I figured out that the template_redirect hook (which is not documented in the WordPress Codex at all) didn’t quite do what I wanted it to. The easiest way out was adding a couple of lines before the redirect actually happened:

global $wp_query;
$wp_query->is_404 = false;

And guess what, it worked! This may be not the smartest decission but hey, it’s working! One more workaround would be adding a regular expression to the W3 Total Cache page settings, exclude “[0-9]+/?$” which would not cache any .com/1234 queries, but that’s not so reliable for a couple of reasons. First, why not cache if we can cache? And second, what if we’re using Twitter Friendly Links in alphanumeric mode? That would mean “[0-9a-zA-Z]+/?$” which would probably match for all the other top-level pages, thus we’re losing all the benefits from caching. Meh!

I guess this is not the only place where the template_redirect action would return a 404. Some plugins that define their own permalinks through the template_redirect hook (WP Polls for poll results for instance, not sure though) will also get 404s if they don’t set it to false in $wp_query. Many wordpress websites have profile pages which are not actual pages in the database, but are handled through the template_redirect action for .com/profile_name sort of permalinks which is indeed cool. I’m actually planning on using it with one of my upcoming projects and I’m really glad I sorted it out.

So the big shoutout goes to W3 Total Cache and the W3 Edge team!

Multiblog WordPress: Eeek! No Database Connection

I tried browsing to www.kovshenin.com this morning and was pretty sure I’ll get the usual redirect, but no. I got an “Error establishing a database connection”. Right, it seems that www.kovshenin.com is not defined in the wp-config.php file we created earlier this week (Multiple Sites Driven By One WordPress Installation Part II) so here’s a workaround (and we’re switching back to example.org and example-two.org):

$wp_multi = array(
    "example.org" => array(
        "DB_NAME" => "example",
        "DB_USER" => "example-user",
        "DB_PASSWORD" => "example-pass",
        "DB_HOST" => "example-host"
    ),

    "example-two.org" => array(
        "DB_NAME" => "example-two",
        "DB_USER" => "example-two-user",
        "DB_PASSWORD" => "example-two-pass",
        "DB_HOST" => "example-two-host"
    )
);
$server_name = str_replace("www." , "", strtolower($_SERVER["SERVER_NAME"]));
$wp_settings = $wp_multi[$server_name];

There. And we also got rid of an error we’d get if we typed eXamPle.org in the address bar.

I’m thinking of a way to wrap this up in some plugin or a little hack, or perhaps a super-short step-by-step tutorial, so that setting up multiple websites one a single wordpress bundle would be easier than ever. If you have any suggestions feel free to speak ;)