Update: We have added a link to a short youtube video on how to install the plugin. It explains each the options as well.
Over at my personal blog, I recently posted about the new Bing Search API wrapper I wrote for Microsoft. In the post, I said that I would talk about some of the uses of the wrapper. This post is the the first of two I have planned.
php|architect, The Accidental Businessman and Postcards From My Life are all three WordPress blogs. Personally, I have three others as well because it's just so easy to setup and get running that I really have to have some serious requirements that absolutely cannot be met within WordPress to consider another option.
These days, being more of a market-teer than a code monkey, I worry about thinks like my site being "sticky", people being able to find my site, and generally the things that programmers laugh at but are important to the rest of us that build out sites.
One of the things I've always hated about WordPress is the stock 404 error message. Out of the box, WordPress just tells users "I'm sorry, I can't find what you are looking for." Granted it is better than the stock Apache 404 page but it isn't much more helpful. Having just finished working on the Bing Search Wrapper for PHP, the best use case I could think of for the wrapper was to solve this particular problem. This is how I helped Microsoft's Interoperability team to put together the Bing 404 Plugin for WordPress.
Quick Start
Ok, if you are in a hurry you can either watch this video or follow the 4 steps below.- Install the Bing 404 Plugin from the wordpres.org repository
- Get a Bing App ID
- Configure the plugin and give it your App Id
- Test it out by trying to go to an invalid URL on your blog.
All the gory details
The goal of the plugin was to give useful 404 page. However, the definition of useful is different for different people. To be specific, I wanted to see if we could find what the user was looking for on the site and if not, then at the very least I wanted to suggest a few of the most popular articles on the blog. To further narrow the search, I wanted to be able to plug in "standard" search terms for which to search.Abbreviated Workflow
Get the URL, strip off the domain. Everything else is considered the query. If the url that led them to the 404 page ishttp://phparch.com/bing-rocks/ then I want to strip off http://phparch.com/ and be left with /bing-rocks/. Granted it is not very useful by itself but it gives us a hint at what the user was looking for.
Get the standard search terms.
Just in case the query string doesn't return enough results (or any at all) we have the option of specifying some generic terms. In the case of phparch.com, we specify php, because that is what most people will be looking for here.
Run both queries, combine the arrays and pop off the top x results
In the setup, you can specify how many entries you want displayed on your page. If the query using the user's original query string returns enough results, we use only those. However, if that doesn't return enough to flesh out the entire x, the results of the second query to Bing, using the specified default keywords, will round out the list. Both queries honor the localized domain option so if you put in your domain name there, it will limit the results of both queries to results from your domain.
Code
Counting comments and all, the entire plugin (sans the API library) is 500+ lines. I am not going to paste them all in here but I will show the heart of the plugin and a couple of the bits of magic that allow it to work./**
* perform the actual search
*
* This is the heart of the plugin. This function is called from the 404.php
* template. No parameters are necessary. It returns the properly formatted
* HTML to either display a list of potential links or an error message.
*
* @return string html to output.
*/
function bing404_search_bing() {
include 'Msft/Exception.php';
include 'Msft/Bing/Exception.php';
include 'Msft/Bing/Search/Exception.php';
include 'Msft/Bing/Search.php';
/*
* Create the Bing Search object.
*/
$search = new Msft_Bing_Search( get_option( 'b4_apiKey' ) );
$search->setWebCount( get_option( 'b4_count' ) );
$search->setSource( 'Web' );
$search->setAdult( get_option( 'b4_adult' ) );
/*
* If requested, make this a site specific search
*/
if ( $localsite = get_option( 'b4_site' ) ) {
$search->setSite( $localsite );
}
/*
* If set, set the local market
*/
$localMarket = get_option( 'b4_market' );
if ( !empty( $localMarket ) && $localMarket != 'NONE' ) {
$search->setMarket( $localMarket );
}
/*
* Build the query to execute
*/
$queryTerms = str_replace( '/', ' ', html_entity_decode( urldecode( $_SERVER['REQUEST_URI'] ) ) );
$localQuery = get_option( 'b4_query' );
/*
* Try to pull the site-wide from cache. Otherwise, pull from bing
*/
$cacheKey = md5( $localQuery );
$raw = wp_cache_get( $cacheKey );
if ( $raw === false ) {
$search->setQuery( $localQuery );
$raw = $search->search();
wp_cache_set( $cacheKey, $raw,'',86400 );
}
$siteResults = json_decode( $raw );
/*
* Try to pull the regular query from cache. Otherwise, pull from bing
*/
$localQuery = trim( $queryTerms );
$cacheKey = md5( $localQuery );
$raw = wp_cache_get( $cacheKey );
if ( $raw === false ) {
$search->setQuery( $localQuery );
$raw = $search->search();
wp_cache_set( $cacheKey, $raw,'',86400 );
}
$results = json_decode( $raw );
/*
* Now merge the resultsets
*/
$finalResults = bing404_merge_results( $results, $siteResults );
/*
* Finally, prepare the output
*/
$output = '
';
foreach ( $finalResults as $value ) {
$output .= sprintf( '
- %s
', $value->Url, $value->Title );
}
$output .= '
';
$bing404_dirname = WP_PLUGIN_URL . '/' . ( basename( dirname( __FILE__ ) ) );
switch ( get_option( 'b4_poweredByBing' ) ) {
case 'Banner':
$output .= '
';
break;
case 'Text':
$output .= '
Powered by Bing
';
break;
case 'Off':
break;
}
return $output;
A few of the code highlights:
- Line 19 - Create the Bing search object.
- Line 27 - If you have specified a local domain, localize the search to only that domain.
- Line 42 - Get the query terms to search for. Note, 2 queries are actually made.
- Line 48 - Execute the query pulling back the most popular items for the blog optionally using your specified query term.
- Line 59 - Execute the query trying to find the page for which the user was actually looking
A bit of WordPress magic
Intercepting the 404 page on WordPress took a bit of digging. It turns out however to be simple, once you find the right hook./**
* Register the 404 hook.
*
* Only register the 404 hook if the user wants to use our included template.
*/
if ( get_option( 'b4_useIncludedTemplate' ) ) {
add_action( '404_template','bing404_use_included_template_hook' );
}
/**
* include the standard template.
*
* If the user has opted to use the included template then include it for use.
*/
function bing404_use_included_template_hook() {
include dirname( __FILE__ ). '/default-404.php';
exit;
}
The plugin works in one of two ways.
- The plugin includes a 404 page. If you check the option, this will be the one that is used. This should work in 75%-80% of the cases and requires no code changes to get the plugin working.
- If you want to roll your own, include this line in your template's 404.php.
<?php if(function_exists('bing404_search_bing')) { echo bing404_search_bing(); } ?>
bing404_search_bing() and the results are displayed.
