— tomauger.com

Archive
Web Development

During one of the wrap-up talks at WordPress Community Summit 2012, @nacin dropped the hint that a crack team was working on modifying the i18n/l10n system within WordPress to allow incremental / on-demand deployment of “language packs” with new .mo files, independently of the core update cycle.

This is a huge step toward enabling complete localization of WordPress, but the scary-cool part is for plugin developers – opening the door for third-party translations to be easily pushed out without necessitating a full dot revision.

[Skip all the verbage and take me to the pretty UI pictures]

Much (okay, all) of the details were clouded in much mystery, but it seems that there would be some kind of server / repo component (I envision something like Trac, only not like Trac) where designated community translators can log in, create translations for core, plugins, themes, whatever, and then push them out into language packs, which would then pop up in your Updates panel whenever your wp-cron did an update check.

Cool. But There’s a Bottleneck.

The bottleneck of course are the community translators themselves. Having the responsibility to translate all those strings from all those plugins, particularly if the plugin addresses functionality from some knowledge domain outside the purview of the translator’s expertise. This could be the case of building a state-of-the-art sports arena that never gets filled to capacity.

Read More

For a recent client, we found ourselves with 11,000+ registered WordPress users with a boatload of meta data that we wanted to be searchable in the Admin backend (All Users screen). Additionally, we wanted to be able to search on a user’s email address as well, to help the client’s Customer Support folk locate a user record quickly.

Now, out of the box, WordPress only searches the following fields:

  • user_login
  • user_nicename

This short post walks through the process of extending the WordPress Admin’s user search without mucking about in any core files.

Read More

Okay, so this is a pretty edge case, I’ll admit it, but recently found myself having to do a manual swap of a taxonomy through direct manipulation of the database. Short story: we had used the default post category instead of a custom taxonomy, and needed to preserve all the existing terms, so we registered the new custom taxonomy and then went into the DB and did a quick UPDATE, setting the ‘taxonomy’ field of wp_term_taxonomy to the new slug. All appeared well.

Problems with hierarchical taxonomies

Strangely though, when we tried to do a tax query using get_terms( array( ‘parent’ => $parent_id ) ), we kept getting empty result sets. Checking the database that made no sense because the hierarchy was there, the parent_ids were set, all was good in $wpdb-land. But each query yielded no results, even with ‘hide_empty’ set to false so we got the entire list, rather than just the terms that were actually in use. Sigh.

After much digging through core, the culprit was _get_term_hierarchy() defined in wp-includes/taxonomy.php. This core function, marked as ‘private’ through the use of the underscore in front of its name, is responsible for drafting up an array where the keys are the term_ids of all the terms in the taxonomy, and the value is the parent_id of that term. It’s just a shortcut, really, and in my opinion is probably a bit redundant. EXCEPT, that it was decided to use a primitive form of cacheing here. This must predate the $wp_cache because that probably should have been used instead. What it does is it stores the resulting hierarchy within the wp_options table of the database, with the option_name of {$taxonomy}_children.

So the _get_term_hierarchy() function first checks to see whether such an option record exists. If it does, it just uses that and returns the result. If it does not, it performs a proper query on the DB and stores that in the wp_options record for future use, before returning the new result.

So here’s where things fell apart. I suppose this option gets updated every time you at a term using the admin interface. Clearly, if you short-circuit this by writing directly to the DB, you won’t be thinking of updating the serialized array in wp_options. And when you register the taxonomy for the first time, it does write that option, so it will be there from the get-go, an empty array waiting to be filled with happy children like Mother Hubbard’s shoe. And that short-circuits the _get_term_hierarchy() call, which is used whenever you specify ‘parent’ or ‘child_of’ in your args to get_terms().

The solution in this case was simply to delete the option from the database and then run the query again. This forces _get_term_hierarchy() to re-build the list and cache it in wp_options, and all is well with the world.

So remember this little gotcha whenever you’re manipulating a hierarchical taxonomy directly on the database – not that this should happen very often, but I could see it cropping up during an import of terms from another DB, or, as in my case, a switch of taxonomy name.

Read More

I just had two real-world examples that I wanted to share on record to remind us all why, as much as we absolutely loathe doing it (as designers), we still have to design for the utter, most horribly worst-case scenario.

IE6 is dead?

We at Zeitguys jumped on the pyre and enthusiastically joined the IE6 funeral, referred customers to Microsoft’s own website encouraging users to switch to more modern browsers, and decided that in 2011 we would no longer be designing for IE6. Guess what? Every single site we’ve designed has had an IE6 version either from the get-go, or retroactively tacked on. Or we wished we had.

Case Study #1

We were designing a low-budget website for a non-profit community-level organization. Accessibility was our primary concern and we focused all our energies in a simple design and some pretty advanced accessibility stuff. I instructed the team to “forget IE6″. Then I went to present to the client in their boardroom, with all stakeholders present, on their laptop (big mistake). Guess what was the only web browser Corporate IT supported? IE6. The presentation was a disaster.

Case Study #2

We’re in the process of designing what will be our flagship web project for the interior specialist Amovo, full of all sorts of truly remarkable bells and whistles. We knew this was a high-end site for high-end clients and to do what we wanted to do we couldn’t be bogged down with IE6 concerns. At our initial training session with the client to bring them up to speed on a vanilla WordPress install we discovered that the CEO’s laptop was only equipped with IE6.

Sigh. The Moral of the Story?

This is 2011. IE6 is still out there.

As designers, you need to have a very frank up-front conversation (a conversation, not a line in your Statement of Work somewhere) with your client about designing for IE6, costs, timelines and limitations. Just remember that no matter how great the site looks on your high-end Macs at the studio, when the client is pulling the site up at home on his home laptop which was an office hand-me-down back in 2004, he is very likely running some out of date shit.

960… by what?

Ever since the 960 Grid System  became a household (or studiohold) term, and even before, we’ve all sort of settled down to a reasonably common set of minimum width guidelines for web design. In 1996 Jakob Nielsen answered the quintessential question of “What Screen Resolution should I design for” with 1024 x 768 and that hasn’t really changed all that much in the better part of a decade. Hence the popularity of 960 grid systems and the like.

At Zeitguys we use a 960 pixel width as our minimum worst-case scenario for width. This gives a decent margin for browsers, once you take into account the browser chrome, the scrollbars and the fact that the window may not be maximized.

But then there’s all this talk about “the Fold” and staying above it (and all the usability luminaries who attemptto debunk this myth), and we’re still left trying to figure out how much vertical content of that initial web page view your “average” user will see. Regardless of how you weigh in on The Fold, we all agree that as designers, it’s that initial view of the website when it first loads the ‘index’ page, that sets the tone and the first impressions of the site.

Case Study #3

I was at home, in a pinch, my laptop was at the office, the home computer is still fritzing out, so I flamed up my wife’s Acer laptop. Yeah, it’s a 1280 x 800 display from 2005 but you know, it still runs GuildWars (one) just fine. I popped into Explorer (8, thankfully!) to check out an update to this website we’ve been working on. Take a look at what I saw:

 

You’ll want to click on the image to see it full-size.

Is that 5 Toolbars?

Yep. Five. Plus a menu bar and a tab. Each clocking in at about 30 pixels apiece. That 800 pixel real-estate shrank down to just over 500 pixels real fast. And here’s the scary part: my wife is actually reasonably technical. She’s the “IT” contact at her office, and she’s a gamer (hence the GuildWars reference earlier). And yet. I totally blame companies like Adobe, who, when you install fricken Flash Player, sneakily also installs Google Toolbar (unless you notice and uncheck the option).

Double Sigh. The Moral of this Tale?

Just because we savvy, aesthetic-oriented designers (who, according to a recent article I read are 30% more likely to customize their browsing environments) like a nice, clean browser and have huge monitors, does not mean the same is true for our wives, clients or most importantly our clients’ clients.

So design for the worst case my friend. And then think of an even worse case than that.

Read More

Okay, this has been a holy grail for me for a while, and just came to a head today as I accidentally copied a database containing about 15 separate WordPress installations (with different table prefixes) into what was supposed to be a dedicated single WP database. Ouch.

A little while ago I blogged about how you can avoid this situation altogether by only mysqldumping selected tables based on a pattern match of their prefix. Well, guess who didn’t drink his own milk (is that even an expression)?

So there I was, with a database full of wp_ and wpclientA_ wpclientB_ zg_ etc. All I wanted was to keep the original wp_ tables and just drop the hell out of the rest of ‘em.

Read More

If you’re not familiar with the fancybox jQuery plugin, I highly recommend you check it out. Lightweight, portable and powerful it is your basic Lightbox-style gallery viewer that supports embedding of images, inline content, iframe content, AJAX derived content and even SWF files (without the need for an external library like SWFObject). Very sweet.

Recently on a Zeitguys project, we had a situation where we had a gallery of dozens of SWFs (thankfully) all the same size. We wanted to display a static graphic in place of each SWF as the page loaded, and then on click, load the appropriate SWF in place. All of this is pretty easy to do right out-of-the-fancybox, so to speak. But there was an added catch: the whole thing had to be built on progressive enchancement principles, so we needed a structure that first worked perfectly with JavaScript disabled. In other words, each anchor tag in our gallery has to have a link to a static HTML page that contains just that SWF embedded on it. Only once that structure was working, could we overlay the fancybox functionality.

The challenge is that the way fancybox (and most of its other “competitors”) work is by hijacking the content of the href attribute of the anchor tag to which fancybox is attached. This is a Beautiful Thing if you have a bunch of IMGs wrapped inside A tags. The anchor’s href points to the IMG source and BAM, if JavaScript is enabled and you’ve attached fancybox to those anchor tags, fancybox reads the href on click and generates the necessary DOM elements to display it in a nicely-formatted, centered box.

But with a gallery of “poster frame” images that have to point to legitimate HTML pages for when JavaScript (or fancybox) are unavailable, we need to roll up our sleeves.

Read More

Here’s a quickie. For those of you finger hackers out there, this one-liner will look through all available logs (including backups) and come up with a count of unique visitors. Just in case your built-in server stats aren’t working and you haven’t signed up with Google Analytics.

zcat access.log*| awk '{print $1}' | sort | uniq -c |sort -n | wc -l
Read More

Okay, I’ll admit that I’m no friend to Adobe Dreamweaver, preferring a “real” programmer’s IDE (though to be fair, I haven’t yet settled on which that’s going to be), but hey, we all work in teams and depending on the project, you may need to jump in bed with the enemy.

What Dreamweaver does (reasonably) well is a sort of version / ownership control (though it can’t handle multiple edits to a single file by multiple users). The principle: if you’re going to edit a file, you put a “lock” on it (ironically, removing the visual “lock” icon that shows that a file is NOT being used by anyone #uxfail). This is called “checking out” the file, and it becomes tagged with your name to let everyone else on the team know: “back off, work on something else.” When you’re done making changes on your local, you “check in” the file, which Puts it to the remote version and then unlocks it (ironically putting the “lock” icon back on it – good job Adobe UX team) so that anyone else can edit it (by checking it out themselves).

Great. So you work on the site for a number of hours, and have now checked out a pile of files all over the place. It’s time to check them back in and relinquish control. But there’s no “check in all my checked out files” menu item (as far as I can tell – comment if there is!). So what’s the solution?

Read More

Depending on your web hosting situation, you may find yourself having to share a database among multiple WordPress installations, or perhaps other tables that have nothing to do with WordPress whatsoever. This is usually not a significant issue for your day-to-day because WordPress allows you to prefix your WP tables to be anything you like.

The challenge comes with backup and migration, particularly if you’re doing it manually via the shell command line.

Suppose you have a whole bunch of WP installations on a staging server awaiting review, all sharing a database. Now client XYZ has approved the site and it’s time to go live. We’ll take a mysqldump of just those tables and migrate that to the new server.

mysqldump takes, in addition to the authentication (-u and -p – and possibly -h) and database arguments, can take a list of tables to be included in the export. Once you supply even one table, any table not named is ignored. The problem is, particularly if you use plugins that create their own tables, the fixed list of tables can be a moving target. Can’t we select all tables with a given prefix?

Read More

A real quickie for any Arras theme users out there. Okay, Arras is overly complicated and its actions are just plain convoluted. However, they were clever about implementing filter hooks where you need them to be.

Have you created a custom post type in WordPress and now you want to include it, along with other regular posts, in your home.php’s slideshow and featured articles list? Add this little snippet to your functions.php (or wherever you are defining your filters and actions):

add_filter('arras_slideshow_query', 'add_cpt_to_arras_queries'); 
add_filter('arras_featured_query', 'add_cpt_to_arras_queries'); 
 
function hdb_add_blog_to_arras_queries( $query_string ){ 
  // Arras gives us the query string in the &key=value format 
  parse_str( $query_string, $query_args );
  // but we need it in the Array format so we can add multiple post types 
  $query_args['post_type'] = array( 'post', OPINION_TYPE );
  // arras_render_posts accepts either format so we're good to go! 
  return $query_args; 
}

 

Read More
Flickr Photos Plugin screenshot

A styled version of the Simple Flickr Photos plugin

We recently implemented Onur Kocatas’ Simple Flickr Photos plugin, that displays a nice little widget of your Flickr photo stream. After testing everything on our development box, when we moved to our testing / staging site on a shared hosting environment, we started throwing errors like this lovely piece of work here:

Warning: file_get_contents() [function.file-get-contents.html]: URL file-access is
disabled in the server configuration in
/wp-content/plugins/simple-flickr-plugin.3.0/simpleflickrphotos.php on line 106

After a little digging, it appears that file_get_contents() has a dependency to the allow_url_fopen php.ini setting. If you are throwing this error and you have access to editing your php.ini file, just do a quick search for ‘fopen’ and set the allow_url_fopen to ‘ON’.

For those of us schmucks in shared hosting situations, this will not do. However, WordPress has been providing an alternative to file_get_contents() for some time, called wp_remote_fopen(). This helper function, defined as part of the WP_Http class replaces the lower-level fopen(0 or cUrl() calls that may or may not be supported on a given server. In fact, as of this writing, there are at least 5 different transports that are tested and may be automatically selected by the WP_Http class. This, along with some robust error checking (the methods will return a WP_Error instance which can be checked with is_wp_error()), makes this the only reliable solution for remote gets.

Updating the plugin to work is trivial, as of the version I’m using (billed as 3.0, but internally actually named 2.9.9.7.1 – seriously – how many decimals do you need in a versioning scheme?).

Replace this line on line 106 of simpleflickrphotos.php:

$html = file_get_contents($url);

with this:

$html = wp_remote_fopen($url);

I would have sent a request or a fork to the author to get his plugin updated (he should really be using built-in WordPress functions whenever possible), but have you seen his “plugin support site”? I’m not even going to do him the favour of providing a link here. Let’s just say, if you want a site dedicated to photos from Turkey… well, don’t go there. Just. Don’t go there. Period.

I sympathize with the guy. I mean, who wants to create a separate support site for a free plugin? It’s just begging for more work than you want. Which is why I haven’t released a plugin to the community yet. Ah well. Hope this fix helps someone out. Of course, once you manually make this fix, you have to be careful that the plugin is not updated, otherwise you’ll have to do it all over again, unless the author implements this fix in a future version (2.9.9.7.1.4.23.6.5.7 perhaps?)

Read More

Oh, lately I’ve been having a blast accommodating web servers that are not running PHP 5.3+. Amongst other gotchas, you can’t use the handy class ‘const’ keyword to define constants, you must use define().

Anyway, here’s a quick RegExp you can use in your favorite text editor or IDE to search out all those oh-so-convenient ‘const’ declarations and replace them with their ugly cousin:

Search pattern:

\bconst\s+(\w+)\s+=\s+(['"])([^\\]*)\2;

Replace pattern:

define ('$1', '$3');

Happy RegExping!

Read More