Building a Wordpress 3 Mobilizer plugin

Here comes another chapter in my fights against Wordpress plugins' lack of quality code :)

For the small blog I have to toy with WP and try to optimize it, one of the tasks I had in the TODO list was setting up a mobile version. A while ago I did a quick search and found a few mobile plugins, both for administration ( which I'm not interested in) and for visitors browsing (let's call them 'mobile themes' although they require quite some coding).

I had three candidates, so this last nights I decided to test them, with terrible results: Some directly not work (or at least not render the homepage posts list), others were so ugly regarding source code, that once again I went the 'do it yourself' path (which usually is also the fun one).

My requisites:

  • Do not reinvent the wheel: I don't want to code any complex Javascript, nor reimplement Wordpress and/or JQuery functions.
  • Basic mobile site: Home/post list, individual posts, and as much as mobile placeholders for other "unsupported" pages (for example supporting rendering search results but not providing a mobile search page).
  • Being able to toggle normal site back: Simple user story: I want to browse the normal version using my iPad, but not using my Phone. And I want to be able to choose.
  • No UI from scratch: I am both bad and lazy when it comes to CSS.
  • Support at least most grade A mobile devices: If it works in an old BlackBerry, nice, but I want iOS, Android, Windows Phone, Kindle and other current devices supported with HTML5 and Javascript.

JQuery mobile 1.0 has just been released, so It solved me two of the hardest requisites: It comes with nice themes (including a dark one!), it supports actual mobile devices, and I don't have to write almost any javascript for request handling (it intercepts and handles requests nicely).

I am not going to dig into how it works, because the documentation and samples are soo easy to follow. I built the whole mobile part in like two-three hours (counting some CSS overrides to better fit my desires).

I had a few other framework alternatives but this one was blazing fast to implement and as I was already using JQuery due to Wordpress, the impact was small (but I must say that 80KB for a mobile FW is a lot!).

As for how to plug a mobile theme into WordPress without rewriting much code, I went for the typical approach: Creating a plugin that switches the theme to the mobile one if detects a mobile device.

If you manage to do this, is the best approach, as you will reuse huge parts of your code and just provide another UI (and typically another client-side logic layer), but is not always possible.

Luckily, I could do it in wordpress with just some research.

This are the hooks/actions/filters/whatever you have to capture to either let return the function parameter (normal/desktop) or override with the mobile one:

add_filter('stylesheet', array(&$this, 'GetStylesheetFolder'));
add_filter('theme_root', array(&$this, 'GetThemeRootPath'));
add_filter('theme_root_uri', array(&$this, 'GetThemeRootUri'));
add_filter('template', array(&$this, 'GetTemplatesFolder'));
add_action('wp_footer', array(&$this, 'AddFooterSwitcher'));

Credit from learning which ones to use goes to WPTouch plugin developers who found it.

The fifth hook is for my requisite of "Being able to toggle normal site back". I inject in the theme footer a small hyperlink and javascript o set a cookie to allow me to override the mobile theme if desired:

public function AddFooterSwitcher()

{
if ($this->mode == Mobilizer::MODE_MOBILE)
{
echo '<a href="
http://www.xxxxx.com',
$_SERVER['REQUEST_URI'],
'" onclick="Switch()" data-ajax="false">Switch to Desktop</a>',
$this->GetCookieModeSwitchJavascript();
}
else
{
echo '<a href="
http://www.xxxxx.com',
$_SERVER['REQUEST_URI'],
'" onclick="Switch()">Switch to Mobile</a>',
$this->GetCookieModeSwitchJavascript();
}
}

And this is the switcher code:

public function GetCookieModeSwitchJavascript()

{
return
'<script type="text/javascript">function Switch() {' .
'var expiration = new Date();expiration.setDate(expiration.getDate() + 365);' .
'document.cookie = "' . self::$cookieName . '=' .
($this->mode == Mobilizer::MODE_NORMAL ? Mobilizer::MODE_MOBILE : Mobilizer::MODE_NORMAL) .
'; expires="+expiration.toUTCString()+"; path=/";' .
'}</script>';
}

The logic to decide the mode is very simple:

  • If detected as mobile device*:
    • Override cookie present and set to normal mode -> leave normal
    • Override cookie not present or set to anything except normal mode ->set mobile
  • else -> leave normal

* I have also a dev IP override (a variable) to do local tests

Mobile detection is fairly simple using a simple regexp , I didn't wanted anything complex:

public function IsMobile()
{
if (self::$devIpOverride != null && strcmp($_SERVER['REMOTE_ADDR'], self::$devIpOverride) === 0)
{
return true;
}
if (isset($_SERVER['HTTP_X_WAP_PROFILE']) || isset($_SERVER['HTTP_PROFILE']))
{
return true;
}
if (strpos($_SERVER['HTTP_ACCEPT'], 'text/vnd.wap.wml') > 0 || strpos($_SERVER['HTTP_ACCEPT'], 'application/vnd.wap.xhtml+xml') > 0)
{
return true;
}
return (bool) preg_match("/(iphone|ipod|ipad|android|blackberry|kindle|iemobile|ppc|smartphone|windows phone|psp|symbian|smartphone|opera mini)/i",
strtolower($_SERVER['HTTP_USER_AGENT']));
}

To hook the plugin early enough so that no theme initialization code has been performed, I added this hook:

add_filter('plugins_loaded', 'MobilizerInit');

Code for the mobile pages is very simple HTML5 with the data-xxx attributes of JQuery mobile, for example this is the single post template:

<?php load_template(TEMPLATEPATH . '/header.php', false); ?>
<div data-role="page" data-theme="a">
<div data-role="header">
<a href="
http://www.darkmillenniumcodex.com" data-icon="home" data-iconpos="notext">Index</a>
<h1>Dark Millennium Codex</h1>
</div>
<div data-role="content">
<?php if (have_posts()) : while (have_posts()) : the_post(); ?>
<h4><?php the_title(); ?></h4>
<p>
<strong>Date:</strong> <?php the_time('m.d.Y') ?>
</p>
<p>
<strong>Categories:</strong> <?php the_category(', ') ?>
</p>
<?php the_content(); ?>
<?php comments_template(); ?>
<?php endwhile; ?>
<?php endif; ?>
</div>
<div data-role="footer">
<?php load_template(TEMPLATEPATH . '/footer.php', false); ?>
</div>
</div>
</body>
</html>

Some PHP fragments are really ugly, I know, but I haven't had time to properly clean the theme, I just grabbed a normal one, stripped out all CSS and markup and rebuilt a minimal mobile version. Wordpress is just so ugly by default.

And really there is not much else... One huge win with this approach is that all requests keep the same URLs, as we are only modifying the theme path and not the JS/PHP engines. The footer switcher function reloads exactly the same page you were visiting in the oppositve mode.

If you want to see it in action, just point a mobile phone, ipad or similar to http://www.darkmillenniumcodex.com and play with it (remember, in the footer you can always change the mode).

The full source code of the plugin and my theme is available here. Just read the notes and comments, should be easy to follow and setup.

Comments?

Posted by Kartones on 2012-01-04