Archive for April 2010

For those of you helping me with testing out SFC trunk, there’s a new one in there for you.

[fb-activity]

The activity feed widget is one of Facebook’s recent addition to their social widgets. Basically, it shows who’s liked and shared your posts recently. The idea is to encourage liking, I suppose.

The latest plugin adds this to WordPress as a sidebar widget, as a shortcode (fb-activity), and enables it for direct theme usage with the sfc_activity_feed() function.

As always, this is trunk beta code here, so if you do use it, don’t expect it to be 100% yet. Still, it works well enough. πŸ™‚

Get it here: http://plugins.svn.wordpress.org/simple-facebook-connect/trunk/.

Shortlink:

I sort of snuck this into Simple Facebook Connect 0.18 for you all.

If you go to http://facebook.com/insights, then one of the new things they have is “Insights for your Domain”. Basically, this lets you hook the Facebook Insights system up to your own domain.

They give you meta info to add to your site so as to define who has control over that site. This lets them know who should be able to see the insights and such.

Well, if you use SFC, that information is already there for you. Just go over to the insights page, put in your domain, then “Link With” your SFC Application. No need to add anything else to your site, the base SFC plugin is adding that fb:app-id meta tag for you automatically (once you’ve configured SFC itself, of course).

Note that you may have to link it a few times before it actually takes. Facebook has to retrieve your webpage to verify the connection, and it seems to time out rather easily. Took me several tries and I kept getting messages like “Bad Domain” and such. But it finally worked.

Since I just added it, I don’t have any data yet to show you, but basically it will keep track of the “Shares” from your site, so those Share buttons will now give you some useful information and feedback on the Insights page.

Enjoy.

Additional: Note that since I wrote this post, Insights never actually updated or worked at all. Facebook basically dropped the ball and never actually finished this code or turned it on or something. I seriously wonder exactly WTF they are doing over there sometimes. Anyway, here’s the bug if you want to track it: http://bugs.developers.facebook.com/show_bug.cgi?id=10174

Shortlink:

What with Android making more headway in the world, I’ve been seeing more and more QR codes lately. If you don’t know what a QR code is, then here is an example:

QR code

Scan this to buy Otto a beer. Seriously.

Android phones have this sort of thing built in, as it’s used for their version of the “app store”. iPhones can download apps to read them and act on them (NeoReader is a free and easy one to use).

Basically, QR Codes are like visual links. You take a picture of them using the application in question, and it either gives you some text or sends you to a website.

Anyway, while reading Google Reader today, I saw somebody mentioning “Portapayments”. A quick search and test showed me how it worked, but I don’t really care for their implementation. I prefer to roll my own, sort of thing. So here’s how you do it.

Make the Donation Link

Let’s assume you’re using PayPal. It’s quite possible to do this with any payment service that can provide you with a payment link, but PayPal is relatively easy.

First, log into Paypal, and go to their “Merchant Center”. There, you’ll click on “Website Payments Standard”. Finally, you’ll make a “Donate button”.

Fill in the bits, and you can ignore most of them. In this case, we’re making a link, not a button, so we don’t care how it really looks. Put in the fixed amount you want the link to be for, then make the button.

On the final screen, there’s a tab that says “Email”. The purpose of this is to give you a link you can email to other people. When clicked, it goes to PayPal with the info already filled in. All the user has to do is confirm the donation. That’s the link we need. Copy it somewhere for now. You can test it out too, if you like, to make sure it works.

Shortlink it

Now we’ll need to turn that into a shortlink. Why? Well, QR codes work better with shorter links. As the amount of information the code holds gets longer, it gets more pixels in it, and thus it gets harder for the readers to read. Thus we want to keep the amount of data in it as short as possible.

You can use any shortlink service you like for this purpose. bit.ly is the most common. I have my domain on Google Apps and I used their shortlink system to allow me to keep it under my own domain’s control that way. So my shortlink ended up being http://sl.ottodestruct.com/beer . πŸ™‚

Note: There is an advantage to using a shortlink system you control entirely here. If you want to change the payment link, then you can do it without changing the shortlink. This is useful because changing the shortlink after you generate the QR code may not be an option. What if you make t-shirts with the QR code on it? That link that you put into the code needs to be pretty darned permanent and not subject to change. If you control where the link goes, then you can change the content at that link easily anytime.

Whatever you prefer, just make that shortlink.

Make the QR Code

Finally, the easy part. Take your final shortlink and put it at the end of this URL:

http://chart.apis.google.com/chart?cht=qr&chs=192×192&choe=UTF-8&chl=

That’s using the Google Chart API to generate the QR code for you. The result is an image. You can put it on your pages however you like.

Example of my final image link:

http://chart.apis.google.com/chart?cht=qr&chs=192×192&choe=UTF-8&chl=http://sl.ottodestruct.com/beer

So it’s pretty straightforward, really. When somebody scans that with any code reading app, then it’ll send them to Paypal to complete the donation for the amount you set in the first place.

Neat.

Shortlink:

Took a look at @anywhere a little while ago. With my experience working with the Facebook javascript methods, it was rather trivial to incorporate the new javascript functionality from Twitter into Simple Twitter Connect, so that was exactly what I did.

Simple Twitter Connect 0.7 now automatically enables your site to run the new @anywhere code. With zero extra configuration steps.

All the example javascript code they give on the @anywhere documentation site will work immediately.

I even added an example plugin (STC-Linkify) to demonstrate how it works. Activate that plugin and it will automatically link any Twitter usernames on your page to Twitter. So when I type @ottodestruct or @otto42, they’re automatically linked. Neat, eh?

I’ll be adding several more @anywhere plugins to the package soon, but those will be in the next update. If you want them in advance, then you can see the trunk version of the plugin and pick and choose which you want. I’ve already added the Follow Button widget to there, so you can go get it now if you like. I’m already using it here in my sidebar. πŸ™‚

Shortlink:


I’m working on a Simple Google Connect for the future. But for now, here’s a simple Buzz Button using the functionality they announced today.

sgc-buzz.php

<?php
/*
Plugin Name: SGC - Buzz Button  
Plugin URI: http://ottopress.com/wordpress-plugins/simple-google-connect
Description: Adds a Google Buzz button to your content.
Author: Otto
Version: 0.1
Author URI: http://ottodestruct.com
License: GPL2

    Copyright 2010  Samuel Wood  (email : otto@ottodestruct.com)

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License version 2, 
    as published by the Free Software Foundation. 
    
    You may NOT assume that you can use any other version of the GPL.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
    
    The license for this software can likely be found here: 
    http://www.gnu.org/licenses/gpl-2.0.html
    
*/

wp_enqueue_script( 'google-buzz-button', 'http://www.google.com/buzz/api/button.js', array(), '1', true );

/**
 * Simple GB button
 *
 * @param int $post_id An optional post ID.
 */
function get_buzz_button($id = 0) {
	$url = get_permalink($id);
	$out = '<a title="Post on Google Buzz" class="google-buzz-button" href="http://www.google.com/buzz/post" data-button-style="normal-count" data-url="';
	$out .= $url;
	$out .= '"></a>';
	return $out;
}

function buzz_button($source = '', $id = 0) {
	echo get_buzz_button($id);
}

/**
 * Simple buzz button as a shortcode
 *
 * Example use: buzz id="123"
 */
function buzz_button_shortcode($atts) {
	extract(shortcode_atts(array(
		'id' => 0,
	), $atts));
	return get_buzz_button($id);
}

add_shortcode('buzz', 'buzz_button_shortcode');

function buzz_button_automatic($content) {
	$button = get_buzz_button();
	$content = $content . $button;
	return $content;
}
add_filter('the_content', 'buzz_button_automatic', 30);
Shortlink:

The Custom Background screen

The Custom Background screen is easy to add to any theme

Quick and simple way to add the new custom background selector to your WordPress 3.0 theme.

add_custom_background();

Seriously. That’s it. Just add that to the theme’s functions.php file.

Details

Okay, so your theme does need to have the normal wp_head() call in it. For those of you more CSS inclined, this basically creates CSS code for the body and adds that code to the head output directly. Voila, your theme gets styled.

Note that you will need to also not define your own background stuff in the theme for this to work. If the user tries to put in a solid color background and you define an image background, then the color won’t work or be visible over your image. Best to not put anything background related onto the body at all, in fact.

Customization

For those more inclined to customize things, there’s actually three parameters you can use:

function add_custom_background($header_callback = '', $admin_header_callback = '', $admin_image_div_callback = '')

Each of these are references to a callback function. If you use them, then you need to define your own callback functions to replace the default ones.

The header_callback function is what builds and outputs the CSS. The function takes no parameters, but it can use get_background_image() and get_background_color() to retrieve the necessary information. From this information, the function should produce and output (echo) the necessary <style> block to show the image.

The admin_header_callback function is called in the head section of the admin side of things; in the Background section to be specific. The admin_image_div_callback is similar, called immediately after displaying “This is your current background” on that page, where the image is displayed. If used, the admin_image_div_callback replaces the display of the current background image, so you custom callback should produce that instead.

These two admin callbacks can be used to modify the Background admin page, to add custom text or information, etc.

But generally, most themes won’t need this level of customization. Just add the basic code to the theme and the defaults are good to go. πŸ™‚

Shortlink:

Seems that some people don’t know much about kses. It’s really not all that complicated, but there doesn’t seem to be a lot of documentation around for it, so what the hell.

The kses package is short for “kses strips evil scripts”, and it is basically an HTML filtering mechanism. It can read HTML code, no matter how malformed it is, and filter out undesirable bits. The idea is to allow some safe subset of HTML through, so as to prevent various forms of attacks.

However, by necessity, it also includes a passable HTML parser, albeit not a complete one. Bits of it can be used for your own plugins and to make things a bit easier all around.

Note that the kses included in WordPress is a modified version of the original kses. I’ll only be discussing it here, not the original package.

Filtering

The basic use of kses is as a filter. It will eliminate any HTML that is not allowed. Here’s how it works:

$filtered = wp_kses($unfiltered, $allowed_html, $allowed_protocols);

Simple, no?

The allowed html parameter is an array of HTML that you want to allow through. The array can look sorta like this:

$allowed_html = array(
	'a' => array(
		'href' => array (),
		'title' => array ()),
	'abbr' => array(
		'title' => array ()),
	'acronym' => array(
		'title' => array ()),
	'b' => array(),
	'blockquote' => array(
		'cite' => array ()),
	'cite' => array (),
	'code' => array(),
	'del' => array(
		'datetime' => array ()),
	'em' => array (), 'i' => array (),
	'q' => array(
		'cite' => array ()),
	'strike' => array(),
	'strong' => array(),
);

As you can see, it’s rather simple. The main array is a list of HTML tags. Each of those points to an array of allowable attributes for those tags. Each of those points to an empty array, because kses is somewhat recursive in this manner.

Any HTML that is not in that list will get stripped out of the string.

The allowed protocols is basically a list of protocols for links that it will allow through. The default is this:

array ('http', 'https', 'ftp', 'ftps', 'mailto', 'news', 'irc', 'gopher', 'nntp', 'feed', 'telnet')

Anything else goes away.

That $allowed_html I gave before may look familiar. It’s the default set of allowed HTML in comments on WordPress. This is stored in a WordPress global called $allowedtags. So you can use this easily like so:

global $allowedtags;
$filtered = wp_kses($unfiltered, $allowedtags);

This is so useful that WordPress 2.9 makes it even easier:

$filtered = wp_kses_data($unfiltered);
$filtered = wp_filter_kses($unfiltered); // does the same, but will also slash escape the data

That uses the default set of allowed tags automatically. There’s another set of defaults, the allowed post tags. This is the set that is allowed to be put into Posts by non-admin users (admins have the “unfiltered_html” capability, and can put anything they like in). There’s easy ways to use that too:

$filtered = wp_kses_post($unfiltered);
$filtered = wp_filter_post_kses($unfiltered); // does the same, but will also slash escape the data

Note that because of the way they are written, they make perfect WordPress filters as well.

add_filter('the_title','wp_kses_data');

This is exactly how WordPress uses them for several internal safety checks.

Now, this is all very handy, but what if I’m not filtering? What if I’m trying to get some useful information out of HTML? Well, kses can help you there too.

Parsing

As part of the filtering mechanism, kses includes a lot of functions to parse the data and to try to find HTML in there, no matter how mangled up and weird looking it might be.

One of these functions is wp_kses_split. It’s not something that is useful directly, but it is useful to understand how kses works. The wp_kses_split function basically finds anything that looks like an HTML tag, then passes it off to wp_kses_split2.

The wp_kses_split2 function takes that tag, cleans it up a bit, and perhaps even recursively calls kses on it again, just in case. But eventually, it calls wp_kses_attr. The wp_kses_attr is what parses the attributes of any HTML tag into chunks and then removes them according to your set of allowed rules. But here’s where we finally find something useful: wp_kses_hair.

The wp_kses_hair function can parse attributes of tags into PHP lists. Here’s how you can use it.

Let’s say we’ve got a post with a bunch of images in it. We’d like to find the source (src) of all those images. This code will do it:

global $post;
if ( preg_match_all('/<img (.+?)>/', $post->post_content, $matches) ) {
        foreach ($matches[1] as $match) {
                foreach ( wp_kses_hair($match, array('http')) as $attr)
                	$img[$attr['name']] = $attr['value'];
                echo $img['src'];
        }
}

What happened there? Well, quite a bit, actually.

First we used preg_match_all to find all the img tags in a post. The regular expression in preg_match_all gave us all the attributes in the img tags, in the form of a string (that is what the “(.+?)” was for). Next, we loop through our matches, and pass each one through wp_kses_hair. It returns an array of name and value pairs. A quick loop through that to set up a more useful $img array, and voila, all we have to do is to reference $img[‘src’] to get the content of the src attribute. Equally accessible is every other attribute, such as $img[‘class’] or $img[‘id’].

Here’s an example piece of code, showing how kses rejects nonsense:

$content = 'This is a test. <img src="test.jpg" class="testclass another" id="testid" fake fake... / > More';
if ( preg_match_all('/<img (.+?)>/', $content, $matches) ) {
        foreach ($matches[1] as $match) {
                foreach ( wp_kses_hair($match, array('http')) as $attr)
                	$img[$attr['name']] = $attr['value'];
                print_r($img); // show what we got
        }
}

The resulting output from the above:

Array
(
    [src] => test.jpg
    [class] => testclass another
    [id] => testid
    [fake] =>
)

Very nice and easy way to parse selected pieces of HTML, don’t you think?

Overriding kses

Want to apply some kind of filter of your own to things? WordPress kindly adds a filter hook to all wp_kses calls: pre_kses.

function my_filter($string) {
	// do stuff to string
	return $string;
}
add_filter('pre_kses', 'my_filter');

Or maybe you want to add your own tags to the allowed list? Like, what if you wanted comments to be able to have images in them, but (sorta) safely?

global $allowedtags;
$allowedtags['img'] = array( 'src' => array () );

What if you want total control? Well, there’s a CUSTOM_TAGS define. If you set that to true, then the $allowedposttags, $allowedtags, and $allowedentitynames variables won’t get set at all. Feel free to define your own globals. I recommend copying them out of kses.php and then editing them if you want to do this.

And of course, if you only want to do a small bit of quick filtering, this sort of thing is always a valid option as well:

// only allow a hrefs through
$filtered = wp_kses($unfiltered, array( 'a' => array( 'href' => array() ) ));

Hopefully that answers some kses questions. It’s not a complete HTML parser by any means, but for quick and simple tasks, it can come in very handy.

Note: kses is NOT 100% safe. It’s very good, but it’s not a full-fledged HTML parser. It’s just safer than not using it. There’s always the possibility that somebody can figure out a way to sneak bad code through. It’s just a lot harder for them to do it.

Shortlink:

(Note to future readers: This is fixed in WordPress 3.3, so this article is now out-of-date. See http://core.trac.wordpress.org/changeset/18541Β for the patch.)

I was not aware that other people didn’t know about this until recently, but since it seems to be little known, I thought I’d write a post on the topic.

Chain LinksIn WordPress, you should never start the custom permalink string with any of these %postname%, %category%, %tag%, or %author%. (Unless you know what you’re doing, of course. πŸ™‚ )

Meaning that “%category%/%postname% ” is a bad custom permalink string. So is just “%postname%” for that matter.

Why? Well, it has to do with how the WordPress Rewrite system works.

Rewriting Explained

See, when you request a URL from a WordPress site, WordPress gets the URL and then has to parse it to determine what it is that you’re actually asking for.

It does this by using a series of rules that are built whenever you add new content to WordPress. Generally the list of rules is pretty small, but there are specific cases that can cause it to balloon way out of control.

Normal Rules

Let’s say you’re using a normal permalink string, like my preferred “%year%/%postname%”. The rules that are generated will look like this:

/robots.txt (for the privacy settings)
/feed/* (for normal feeds of any kind)
/comments/* (for comments feeds)
/search/* (pretty url for searches, not often used)
/category/%category% (category archives)
/tag/%tag% (tag archives)
/author/%author% (author archives)
/%year%/%month%/%day% (with each of those after year being optional)
/%year%/%postname% (this is the permalink string you define)
/%pagename% (any Page)

The way that system works is that it compares the URL it has to each of those in turn, from the top to the bottom. When one of them fits, then WordPress knows what to display and how to do it.

Note that the order I listed those in is is significant. Each one from the top down is less specific than the previous one.Β  For example, “robots.txt” matches only that, while “/feed/*” matches anything starting with /feed/. And so on down the list. The %postname%, %category%, %tag%, %author%, and %pagename% will match any string, but the other WordPress % ones will only match numeric fields. Like %year% is always a number.

Notice that the last one is %pagename%. This basically matches everything, because %pagename% can be anything at all. Even hierarchical pages like /plugins/whatever/something will cause this to match. It’s the fall-through position. And then, if that page doesn’t actually exist on your site, then this causes the query to trigger the 404 condition internally, which causes your theme’s 404.php to load up.

Pretty simple and straightforward, really.

Problem Rules

The problem comes in when you try to use a non-number for the beginning of your permalink string. Let’s examine those last two rules closer:

/%year%/%postname% (this is the permalink string you define)
/%pagename% (any Page)

What if you used “%category%/%postname%” for your custom permalink string? Now those last two rules are these:

/%category%/%postname% (this is the permalink string you define)
/%pagename% (any Page)

That violates our main rule, doesn’t it? That each one should be less specific than the one above it? Because %category% can match any string too, just like the %pagename% can… With this set of rules, there’s no way to view any of the Pages. Not good.

So, WordPress detects this condition and works around it. Internally, this sets a flag called “use_verbose_page_rules”, and that triggers the rewrite rebuild to make this set of rules instead:

/robots.txt (for the privacy settings)
/AAA
/BBB
/CCC (one of these for each of your Pages)
/feed/* (for normal feeds of any kind)
/comments/* (for comments feeds)
/search/* (pretty url for searches, not often used)
/category/%category% (category archives)
/tag/%tag% (tag archives)
/author/%author% (author archives)
/%year%/%month%/%day% (with each of those after year being optional)
/%category%/%postname% (this is the permalink string you define)

Now we have basically the same set of rules, except for those new ones at the top. Every Page now gets its own very specific rule, and this satisfies our main condition once again.

Pages

But what if you have a lot of Pages? I once read a post by a person who had over 50,000 Pages on his site. That is a special case obviously, but consider our lookup system. We’re going through these rules one at a time. With our first method, our rule list was only 10 rules, maximum. With this new method, you add a rule for every single Page you make. Going through 50,000 rules takes a lot longer than going through 10. And even just building that list of rules can take a long time.

Basically you’ve created a performance issue. Your Pages now won’t scale to unlimited numbers. Your site’s speed is linearly dependent on the number of Pages you have.

This is a bad thing.

Conclusion

Firstly, it’s really not any better for SEO to have the category in there, or to have just the postname there by itself. And anybody who tells you differently is wrong. If you disagree with me, then no, I’m not interested in arguing this point with you; you’re just wrong, period, end of discussion.

Secondly, shorter links are great and all, but hell, why not use a real shortlink? WordPress 3.0 now has a ShortLink API that defaults to using ?p=number links on your own domain. These will actually work for any WordPress site, even ye back unto WordPress 2.5. WordPress 3.0 just makes it nicer and easier to use these with the Shortlink API (as well as allowing plugins to make this automagically use services like wp.me or bit.ly). So use that instead.

The conclusion is, in general, just don’t do it. Leave a number, or something static, at the beginning of your permalink string and you’ll never have any sort of problems. But if you really MUST do this sort of thing, then keep your number of Pages low. Don’t try it when you have more than, say, 30-50 Pages.

Addendum

Okay, so I actually simplified things for this post. It’s actually worse than this, as verbose page rules can add much more than one rule per page, as this post demonstrates (he gets 11 per extra page!).

Shortlink:

Never thought I’d say this… But my recommendation for people thinking about upgrading from a single-user WordPress 2.9 installation to WordPress 3.0: Don’t do it… yet.

WordPress 3.0 is such a major update, that for 95% of the single-user blogs out there, I would recommend putting it off and not upgrading at all. Not until the issues are worked out.

See, everybody is talking about the features of 3.0, and those are indeed many and varied. But not many people are talking about the problems of 3.0. And while it is only in beta, it’s not too early to consider some of the design choices here and think about them in a larger scope.

Theming

Firstly, understand that a lot of the new features require theme support. Meaning that your existing theme, while it will work, won’t give you the new menuing system, it won’t have the comment form enhancement, the new editor styling tricks won’t work, etc…

So if you’re going to upgrade to 3.0 without modifying your theme as well, you’re not going to get the full experience.

The new Twenty Ten theme, while it is neat and shows off what can be done, is very, very complicated (and IMO, it also does a lot of things very badly). It’s somewhat difficult for even an experienced theme editor and developer to understand, so expecting a normal blogger to read this as an example and then change their own theme as needed is somewhat nutty. So if you want these new features, you’ll probably need professional assistance to get it all up and working.

Custom Post Types

They’re neat, they’re cool, and they’re insanely difficult to use properly. What’s going to happen here is that people will use them badly at first. Incredibly badly. Some of the examples I’ve seen thrown around are surprising just for the sheer crazy. Yes, you can make a “Podcast” post type, but do you really want to? Does it really make sense in the context of what you’re doing? The answer is probably not, actually.

And using them changes so much internally to the system that things start behaving in ways you totally don’t expect (or see). For example, custom post types don’t show up in feeds by default. That might make your podcasting a little bit, shall we say, broken?

Now, custom post types have to be enabled by plugins and such, so those plugins might indeed take care of this sort of thing properly. But frankly, I wouldn’t want to bet on it. People are going to have all sorts of weird problems with this until they work out that they really didn’t need custom post types after all.

IMO, only a very limited subset of people need this, or think they do, and those people are very likely already using them in some manner.

Plugin incompatibility

Any plugin that deals with posts being published is likely to be affected by custom post types. And this won’t be limited to your custom posts.

I’ve already had to edit 4 different plugins I use (2 of them are mine, admittedly) because of the new menu system. It stores the menu items in it as posts with custom post types of “nav-menu-item”. So before I fixed it, making a new menu item would cause the name of that menu item to appear on my Twitter feed. Simple Twitter Connect was seeing a new post get created, and it dutifully published that fact. It simply didn’t expect a “post” to be a “menu item”.

I’ve currently hardcoded the plugins to limit them to seeing actual posts and pages, but that seems a poor solution to me. What if somebody wants to make that “podcast” post type? How is a plugin supposed to know what is really a post and what is not? Overloading the wp_posts table for storage of “not-posts” in this way is confusing and strange and it’s going to break a lot of plugins.

So at the very least, don’t upgrade until your plugins explicitly support 3.0.

Menus

The new menu system is clever, but ultimately very limited. I tried it, but I eventually opted to not use it at all.

The problem is that it’s wholly static. Every menu item is a name and a URL, basically. There’s nothing there to make dynamic menus from your content. Like my WordPress Plugins dropdown at the top of the page there. That’s generated by a call to wp_list_pages. Can’t do that with the menu system. And generating a dropdown menu of a given internal list seems, to me, to be a strangely missing piece of the whole thing.

You can make that same menu manually, but if you add a new page, you have to go and manually add it to the menu as well. And that’s all well and good for people who want to micro-manage their site. I’m sure they’ll love it. But I can’t say that I do.

Now, I have been having this argument on the wp-hackers list, so it’s entirely possible that the final release will address more than a few of my above objections. So plugins might be created to fix this issue. Hell, I might create a few of them myself. But in the end, I still find that the underlying design is fundamentally flawed. The system stores menu items as metadata attached to fake posts. In my opinion, that’s just nine kinds of crazy. They should have leveraged the existing widgets system instead, and perhaps have made a new table to hold widget and menu item data together (thus solving the widget data problem and having a place to put menu-items all in one shot). Not everything should be a post.

Multi-Site

Okay, yeah, Multi-Site is damn cool, for those of us that run more than one site. But it’s got issues too. Mainly, it’s just complicated.

It’s complicated in that there’s several types of choices to be done. You have to know server configuration. You have to understand DNS and static IP addressing. Knowledge of Apache and VHOSTing helps. And if you’re using multiple domains, which seems like a rather common setup, then you’re into mu-plugin territory, which is wholly new to anybody familiar only with WordPress and not WordPress MU.

Setting up Multi-Site for my two domains required the following steps:
1. Move WordPress to the root of my domain (it really doesn’t work in a subdirectory. Just trust me on this).
2. Enable multisite by editing wp-config.
3. Enable it in the interface, making choices that I didn’t understand. The difference between a subdomain and a subdirectory installation is not immediately apparent, and later turns out to be wholly irrelevant when you use the Domain Mapping plugin anyway. Answer for everybody else: Subdirectory is simpler and requires less configuration, subdomain only works if you have more server control than the average cheap hosting account gives you (static IP, wildcard DNS, VHOST, etc).
4. Copy and paste a whole bunch of stuff that WP spits out into my wp-config and htaccess files.
5. Install the Domain Mapping plugin (manually install, you can’t do this through the happy plugin install interface, because it requires putting files into various directories).
6. Configure the mapping. This is confusing at first, because after you get it working and give it the server IP, you have to actually go to each site’s individual wp-admin in order to map it to a domain. Not hard, but very unclear. Oh, and before you map the domain, make sure that the domain is actually pointing to your server IP, because the mapping takes effect instantly, with no way to undo it. I had to manually edit my database to undo it after I screwed this up the first time.

After all that, there’s still a few bugs. Minor stuff though. The links on dashboard on the main site aren’t quite right. My main site shows individual posts in the /blog directory, with no real way to remove it (it’s hardcoded and deep in the heart of things there’s probably a good reason for it, but it’s still annoying). There’s some other limitations which are too boring to go into.

BTW, if you’re already running MU, then go nuts. This upgrade is perfect for you. It’ll get you onto the main line and that is more important than anything I’m talking about here.

Note that I’m not sure that this is fixable. Multi-Site is complicated by its very nature. Integration of Domain Mapping into the core would help make this a lot simpler and would have the side benefit of eliminating the unnecessary subdomain/subdirectory choice. But ultimately, you do have to have some server-side config to make this sort of thing work, and that’s never going to change.

Not everything is bad

Don’t get me wrong, there’s lots and lots of good stuff here. The Shortlinks API is great. The plugin and theme updaters have been enhanced and moved around proper. A lot of the new theme stuff like the header trickery and the automatic-feed-links and such are very cool.

I like the direction WordPress is heading. I just think that this particular stop on the tracks is not a great one. Too much still unfinished and only halfway thought-out, IMO.

And again, I realize that this is beta code, and I’m not criticizing any of the developers or talented individuals working on it. I get that WP 3.0 is unfinished as of this writing. I’m just trying to look at this from a perspective of an end-user. What’s in this upgrade for the end-user? Ultimately, not very much.

So while 3.0 is a very, very good step in the right direction, it’s a step that that end-users might want to skip over for now. If you’re a WordPress hacker, then go nuts. You’ll be fine with 3.0. But if not, then you might want to hold up a while and wait for the next release instead.

Note that this won’t stop me from upgrading, nor from posting tips/tricks for 3.0 users. Just saying. πŸ™‚

Shortlink:

For ages, theme authors have been adding code like this to their theme’s header.php files:

<link rel="alternate" type="application/rss+xml" title="<?php bloginfo('name'); ?> RSS Feed" href="<?php bloginfo('rss2_url'); ?>" />

No need for that any more. Remove that stuff, make sure you’ve got the wp_head() call in the header (like you should anyway), then add this to the theme’s functions.php file instead:

add_theme_support( 'automatic-feed-links' );

This automatically adds the relevant feed links everywhere on the whole site. Standard feed, comments links, category and tag archives, everything as it should be.

Shortlink: