Remove extra RSS feeds in WordPress

Here’s a WordPress snippet to remove RSS feeds for comments while keeping the main one available.

RSS Icon

RSS is super nifty. It’s a great way to keep on top of breaking news and articles from your favourite blogs and/or aggregators (I’ve got over 100 subscriptions in my feedly reader).

So it’s a wonder then that WordPress offers RSS feeds for comments on all your posts. I presume there’s a group of people who really like to track conversations, but in my experience, it’s the minority. This functionality should really be an additional (and not core) part of WordPress. If you want to prevent your site serving pointless additional RSS feeds, you’ll need to remove them from wp_head.

Removing them, however, is a two-step process. Add the following to your functions.php file:

1
2
3
4
5
6
7
8
9
10
11
12
13
 
  // First, we remove all the RSS feed links from wp_head using remove_action
  remove_action( 'wp_head','feed_links', 2 );
  remove_action( 'wp_head','feed_links_extra', 3 );
 
  // We then need to reinsert the main RSS feed by using add_action to call our function
  add_action( 'wp_head', 'reinsert_rss_feed', 1 );
 
  // This function will reinsert the main RSS feed *after* the others have been removed
  function reinsert_rss_feed() {
      echo '<link rel="alternate" type="application/rss+xml" title="' . get_bloginfo('sitename') . ' &raquo; RSS Feed" href="' . get_bloginfo('rss2_url') . '" />';
  }
  //

And that’s it. The first two actions strip the RSS using remove_action with a high priority. The final add_action call tells WordPress to load our function when processing wp_head. This will go through and reinsert the main feed, leaving the comment feed out.

Congratulations! Your post conversations are now untrackable.

WordPress: Add page slug to body class (including parents!)

Are you using WordPress as a CMS? Do you want to style pages with CSS logically? You might want to try this.

By default, WordPress will inject the page and its ID into the body class. That’s great if you’re only running a single install, or if you really really don’t care what your CSS looks like.

Let’s face it – you care. Otherwise, you wouldn’t be here. Here’s a snippet that will inject the current page’s slug and all slugs of its parents into the body class of your HTML.

Add this to functions.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
 
function mytheme_add_body_class($classes) {
    global $post;
 
    // You can modify this check so it will run on every post type
    if (is_page()) {
        global $post;
 
        // If we *do* have an ancestors list, process it
        // http://codex.wordpress.org/Function_Reference/get_post_ancestors
        if ($parents = get_post_ancestors($post->ID)) {
            foreach ((array)$parents as $parent) {
                // As the array contains IDs only, we need to get each page
                if ($page = get_page($parent)) {
                    // Add the current ancestor to the body class array
                    $classes[] = "{$page->post_type}-{$page->post_name}";
                }
            }
        }
 
        // Add the current page to our body class array
        $classes[] = "{$post->post_type}-{$post->post_name}";
    }
 
    return $classes;
}
// Add a filter for WP
add_filter('body_class', 'mytheme_add_body_class');

And that’s it! You can then start chaining together page classes to target little overrides that your clients are probably hassling you about.

Help! register_activation_hook isn’t working!

If you’re developing a plugin, chances are you’ve been caught out by the WordPress activation/deactivation hooks.

I’ve been working on an Instagram plugin for WordPress, and in doing so have needed to use the functions register_activation_hook and register_deactivation_hook.

However, I – like many others, it seems – can’t get the hooked functions to execute. They simply don’t fire. To illustrate the problem, here’s an example from the code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
define('MY_INSTAGRAM_PLUGIN_PATH', plugin_dir_path(__FILE__));
require_once MY_INSTAGRAM_PLUGIN_PATH . 'my-instagram-widget.php';
 
$my_instagram = new My_Instagram();
 
class My_Instagram {
 
    /**
     * Standard constructor to initalise hooks and options
     *
     * @return void
     */
    public function __construct()
    {
        register_activation_hook(__FILE__, array($this, 'install'));
        register_deactivation_hook(__FILE__, array($this, 'uninstall'));
    }
 
    /**
     * Set up the table and options
     *
     * @return void
     */
    public function install()
    {
        ...
    }
 
    /**
     * Drop the table and options associated with the plugin
     *
     * @return void
     */
    public function uninstall()
    {
        ...
    }
}

I would fully expect the system to execute the functions hooked within the constructor. Had that been the case, I wouldn’t have lost a day debugging a non-existant problem.

Finding a solution

Of course, everyone’s setup is different. There were many suggestions for fixing the problem. For the sake of usefulness, here’s the other major suggestion:

Your global variables aren’t global

If (like me), your plugin is a global, you actually need to explicitly define it as such with the global keyword. For example, the snippet

$my_instagram = new My_Instagram();

should instead by written this way:

global $my_instagram;
$my_instagram = new My_Instagram();

Why? Well, for this, we need to consider how the plugin has its code loaded. After installation, your plugin has its code included “normally” with standard includes. This creates the global scope that you would expect. However, when you register a function with register_(de)activation_hook, it is called from within another function’s scope, effectively hiding your non-explicit globals.

Confused? Consider the plugin example from earlier. The variable $my_instagram is declared in the global scope, with global visibility. When your code executes, your plugin is included at the same scope, meaning you will have access to the variable (as they share the same suitably-high scope). Due to the way the hook/filter system in WordPress works, your activation/deactivation hooks are executed within a localised (not global) scope.

Screen beans - the only thing worse than scope.

Screen beans – the only thing worse than scope.

This scope-confusion applies to all functions created using create_function, or (in our specific example), functions called with call_user_func (and call_user_func_array). These functions have their own scope and are thus shielded from the cheap-and-nasty pile of implicit “globals” that haven’t been properly declared as such.

You can find a suitably more technical (and probably correct) write-up at the WordPress Codex page for register_activation_hook.

tl;dr: Explicitly declare your globals if you want them to work everywhere.

Ok, that’s great, but what’s the answer?

After having accidentally (and rather unwillingly) been given a very rough refresher on scope, I found that this was not my problem at all. Even running proxy functions (which eliminates scope issues) failed to call the functions specified in the hook.

After much trial and error (and I mean much), I found that the paths within register_(de)activation_hook were not matching the path found in plugin_dir_path. In fact, __FILE__ was returning a completely different path.

The problem? I had symlinked the theme directory in my install, meaning that __FILE__ returned a different path to plugin_dir_path. This is especially important, as the file path specified by the first parameter of register_(de)activation_hook is included before the function specified by the second parameter is called. So, if the path in the first parameter is wrong, you probably aren’t going to be executing many functions today.

The Fix

This is always my favourite part. To fix the symlinking problem, you just have to be very specific when you’re asking WordPress to execute a hook for you.

Fixing the first example, we get:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
define('MY_INSTAGRAM_PLUGIN_PATH', WP_PLUGIN_DIR . '/my-instagram/');
require_once MY_INSTAGRAM_PLUGIN_PATH . 'my-instagram-widget.php';
 
global $my_instagram;
$my_instagram = new My_Instagram();
 
class My_Instagram {
 
    /**
     * Standard constructor to initalise hooks and options
     *
     * @return void
     */
    public function __construct()
    {
        register_activation_hook(MY_INSTAGRAM_PLUGIN_PATH . 'my-instagram.php', array($this, 'install'));
        register_deactivation_hook(MY_INSTAGRAM_PLUGIN_PATH . 'my-instagram.php', array($this, 'uninstall'));
    }
 
    /**
     * Set up the table and options
     *
     * @return void
     */
    public function install()
    {
        ...
    }
 
    /**
     * Drop the table and options associated with the plugin
     *
     * @return void
     */
    public function uninstall()
    {
        ...
    }
}

The most important changes occur on lines 1, 16, and 17. The first line fixes the definition by manually specifying the path using the base plugin directory (instead of an automagic WordPress search). Lines 16 and 17 specify the main plugin file itself as the path (instead of relying on __FILE__).

This allowed WordPress to find the plugin files without a hitch, which is especially important in the installation/uninstallation phases.

Moral of the story? Check your paths first.

Paths

Help! My photos are missing hover text!

If you’re an avid Tumblr blogger and have suddenly realised your hover text (tooltips) are missing from your photos, your day is about to get better.

Something brought to my attention recently is the lack of tooltips that appear for photos posted on Tumblr, even when a caption/description is provided using the editor.

Tumblr image uploader

The image thingamajigger from Tumblr

Code-Monkey explanation

What’s actually happening here is that Tumblr/Tiny MCE is incorrectly assigning the alt text of the image. The specification outlines the use of alt text as a replacement for the image in the case that it fails to load or if the client is, in-fact, using a screen-reader, not as a tooltip (hence “alternative text”). Browsers have been using alt text as a fallback for title text for years, and this incorrect implementation is finally catching up to developers.

The tooltip text should instead be assigned to the title attribute. This is the text that shows on mouse over.

Ok, can you fix it for me?

For the blog-(and not code-)savvy, you can add a tooltip by fixing your image tags, like so:

<img src="/images/some_image.png" alt="My Tooltip Text" />

becomes:

<img src="/images/some_image.png" alt="Some Image" title="My Tooltip Text" />

I’ve noticed this behaviour most recently in Google Chrome, but I suspect other browsers will follow suit. It is in accordance with the spec, after all.

I hope this cleared things up for all you Chrome-wielding bloggers out there.

Pink Ribbon Breakfast

I don’t normally plug work that I do in my daily grind, but this one is for super-great cause. Instead of our usual advertising selves, we’re using our powers for good and completely upgrading the National Breast Cancer Foundation‘s Pink Ribbon Breakfast site. Their goal is to have 0 deaths by 2030, which is a pretty nice goal all-round.

We built the site using Zend Framework, CSS3, and a healthy dose of jQuery-inspired Javascript.

Jump over to the site and check out our relatively-quiet first phase launch while we get ready for the donation-crazy second phase. And be sure to visit the donate link if you know someone with breasts.

Using a single controller in Zend Framework

Are you using Zend? Do you have only a core controller and don’t need extraneous URL parameters? Try this.

Most of the time, I am using Zend Framework to its (mostly) full capacity. Models, Controllers, View Helpers – the whole box-and-dice. However, there has been several occasions where I’ve only needed a single controller serving up mostly static pages, or pages leveraging an external service.

The biggest problem that arises in this case when using the default Zend Router, is that action names are treated as controller names. I don’t know about you, but I personally hate seeing http://example.com/index/my-action when what I actually want to see is http://example.com/my-action.

Here’s a nice way to completely tidy up all your actions, serving them from a single controller. (I use a Bootstrap file to initialise my application):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
 * Set up routers for the system
 *
 * @return void
**/
protected function _initRouters()
{
    $router = Zend_Controller_Front::getInstance()->getRouter();
 
    $route = new Zend_Controller_Router_Route(
        ':action/*',
        array(
            'controller' => 'index',
            'action' => 'index'
        )
    );
 
    $router->addRoute('default', $route);
}

This basically says that my first parameter is always going to be an action name, and everything else is to be parsed as normal. I set request defaults in the array. In this case, the default controller is “index”. This allows everything to be passed straight through to my IndexController.

Addtionally, if you are using modules and want to retain routing for these also (for example, a one-controller site with an admin module), you can add this route rule to your system:

1
2
3
4
5
6
7
8
$route = new Zend_Controller_Router_Route(
    'admin/*',
    array(
        'module' => 'admin'
    )
);
 
$router->addRoute('admin', $route);

This says that anything starting with “admin” needs to be routed to the admin module, and the rest of the URL is to be parsed as normal.

That’s it! This will allow you to route all requests to a single controller for the front-end, and still maintain a complete module for the administration on the back-end.

Hopefully this cleared up some things for others, as I found the Zend Framework documentation to be quite dismal.

Using memcache and Zend_Cache

Probably one of the more useful things you can do with memcache is drop it into Zend Framework. Here’s how I did that.

Following on from the previous post, here is an example of how you can set up memcache to serve as a cache for your Zend project.

I like to use the method style of bootstrapping my application, so here’s the function that builds the cache for me.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
/**
 * Set up caching using memcache
 *
 * @return void
 **/
protected function _initMemcache()
{
    if (extension_loaded('memcache'))
    {
        // Configure caching backend strategy
        $cacheBackend = new Zend_Cache_Backend_Memcached(
            array(
                'servers' => array(
                    array(
                        'host' => '127.0.0.1',
                        'port' => '11211'
                    )
                    // Other servers here
                ),
                'compression' => true,
                'compatibility' => true
            )
        );
 
        // Configure caching frontend strategy
        $cacheFrontend = new Zend_Cache_Core(
            array(
                'caching' => true,
                'cache_id_prefix' => 'MyApp_',
                'write_control' => true,
                'automatic_serialization' => true,
                'ignore_user_abort' => true
            )
        );
 
        // Build a caching object
        $memcache = Zend_Cache::factory($cacheFrontend, $cacheBackend);
 
        Zend_Registry::set('cache', $memcache);
    } else {
        // Handle a non-existent extension here
    }
}

Originally, I was getting the “Memcache::addServer function expects at most 6 parameters, 8 given” warning. To force Zend to play nice with memcache (and send the right parameter count), you need to make sure you have the compatibility option set to true in the backend options array.

Here’s an example on how to use:

1
2
3
4
5
6
7
8
$cache = Zend_Registry::get('cache');
 
if (!$request = $cache->load('dataset')) {
    $request = LargeQueryOrDataset();
    $cache->save($request, 'dataset');
}
 
var_dump($request);

Hope this helps in getting started with Zend_Cache (even on a technology as old as memcache).

Enabling memcache under MAMP

I know the tech is a bit old, but sometimes you need to make do with the tools you’ve been given. Here’s how I enabled memcache under MAMP.

First, grab the precompiled binary file (memcache.so) from here. There were no problems using this on Snow Leopard (for me, at least).

Second, copy the file to your MAMP extensions directory. You should be able to find it here: /Applications/MAMP/bin/php5/lib/php/extensions/no-debug-non-zts-[date].

Next, you’ll need to enable the extension in MAMP. Switch it off, then open up php5.ini by going to File > Edit Template > PHP5 php.ini

Find the extensions section (about line 540 or so) and add this line to the list of extensions:

extension=memcache.so

Once that’s done, save php.ini and reboot MAMP. You should be able to see an entry for memcache when opening your phpinfo page.

Reclaiming your favicons when using MAMP

I use MAMP Pro to manage my local virtual hosts. It’s about a hundred times more convenient than Apple’s “native” Apache/PHP install (unfortunately a sad truth).

One annoying facet, though, is that MAMP will force its favicon when you’re using a MAMP-managed virtual host. Here’s how to get rid of this “feature” and reclaim your presumably awesome favicon.

First, make sure MAMP is not running Apache. Then, go to File > Edit Template > Apache httpd.conf

Once you’ve got the configuration file open, look for the line (roughly 625 for me):

Alias /favicon.ico "/Applications/MAMP/bin/favicon.ico"

and comment it out with a hash (#). Save your configuration, then reboot MAMP’s Apache server. You might have to flush your browser’s cache before it recognises your own favicons.

This took way too long to fix, so I hope this helped someone else.

Simple Remote Requests with Titanium Appcelerator

Titanium Appcelerator is a pretty nifty tool. Here’s a small data manager object that looked after my external requests.

I was tinkering with Appcelerator recently, and came upon the need to build a data manager to shoot off requests in a bit of a “fire-and-forget” sort of way. I created this global object I could channel requests into. My particular server always replied with JSON, so you can tweak this for whatever data your remote server will return.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
var DataManager = {
    send: function(toWhere, data, callback, context)
    {
        // Fail
            if (!toWhere) {
            return false;
        }
 
        // Defaults
        data     = data     || {}; // The data to send to the server
        callback = callback || function(_r) { return _r; }; // The callback. If none is supplied, will just return the result
        context  = context  || null; // The object that 'this' will refer to in the callback
 
        if (Ti.Network.online) {
            var XHR = Ti.Network.createHTTPClient({
                onload: function() {
                    var result = JSON.parse(this.responseText);
 
                    return callback.apply(context, [result]);
                },
                onerror: function() {
                    var result = JSON.parse(this.responseText);
                    alert('There was an error with the connection. Please try again later.');
 
                    return result;
                }
            });
 
            XHR.setTimeout(15000); // 15s is my timeout. Use your own here
 
            XHR.open('POST', 'http://example.com/' + toWhere, false);
            XHR.send(data);
        } else {
            // Handle offline here
            alert('You are offline');
        }
    }
};

Using this object in the global scope, you can just type:

1
DataManager.send('user/new', { myVar: 'some value' }, this.processXHR(), this);

Appcelerator has sped up development in key areas tremendously, but at the cost of some flexibility and code bloat. It’s worth keeping an eye on, as each release is leaps and bounds ahead of its predecessor.