knightly

Blog Archives

Writing a simple WordPress plugin

In the spirit of Ideas for March i should really be blogging more. Somehow i just can’t think of anything interesting at the moment. Will give it a shot anyway.

Last week i created my first ever WordPress plugin. It’s an extremely simple one. And for sure nothing new or special. But it was a fun experience. I have always dreaded the WP code base. The mixture of OO and procedural programming just looks plain ugly to me. But i don’t want to be bash WP. I use it myself. So i took the dive and spend a few hours hacking and reading. And the result is my DWOTD (Dutch Word Of The Day) plugin.

To get some inspiration for this i took a look at some plugin code available on the internet. This gave me a basic understanding of how the plugin system works. Together with the docs writing a plugin wasn’t that hard. The code that i saw however was pretty bad. And i wanted something that at least looked better then all the global statements swarming around. So i created a small OO wrapper for the plugin. The basic plugin structure looks something like this

– plugin
—– css
———- dwotd.css
—– lib
———- Dwotd.php
———- Widget.php
—– template
———- Widget.php
– dwotd.php

The file in the root is the bootstrap file. It includes the main plugin class and registers the widget class. The two files in lib contain the plugin and Widget classes. The rest is self explanatory i think.

The first lines of the bootstrap file contain a comment block that is used by WP to show some more information about the plugin inside the admin.

/*
Plugin Name: DWOTD
Plugin URI: http://lenss.nl/projects/dwotd-plugin/
Description: Dutch Word Of The Day including English translation
Author: Thijs Lensselink
Author URI: http://lenss.nl/
Version: 1.2
*/

Then the plugin class file is loaded and the a plugin object instantiated.

require_once(dirname(__FILE__).'/lib/Dwotd.php');

$DWOTD = new DWOTD_Plugin();
wp_register_style('dwotd', "{$DWOTD->getPluginDir()}/css/dwotd.css");
wp_enqueue_style('dwotd', "{$DWOTD->getPluginDir()}/css/dwotd.css");

If the logged in user is a admin hooks for installing and uninstalling the plugin will be activated.

if ( is_admin() ) {
	register_activation_hook(__FILE__, array($DWOTD, 'install'));
	register_deactivation_hook(__FILE__, array($DWOTD, 'uninstall'));
}

And finally register the Widget class

add_action('widgets_init', create_function('', 'return register_widget("DWOTD_Widget");'));

The Widget class is a simple wrapper around the plugin class. It basically instantiates the Plugin class and after that the widget() method gets called. This method calls the Plugin and finally renders a view for the widget box.

class DWOTD_Widget extends WP_Widget
{
	protected $_dwotd;

	public function DWOTD_Widget()
	{
		$this->_dwotd = new DWOTD_Plugin();
		parent::WP_Widget(false, $name = 'DWOTD Plugin');
	}

	public function widget($args, $instance)
	{
		$result = $this->_dwotd->getFromCache();
		if (!$result || $result->used_on != date('Y-m-d')) {
			try {
				$result = $this->_dwotd->fetchRandomWord();
				$this->_dwotd->writeToCache($result);
			} catch (Exception $e) {
				return new WP_Error('broke', __($e->getMessage()));
			}
		}

		$this->_renderView($result);
	}

	protected function _renderView($data)
	{
		ob_start();
		include $this->_dwotd->getTemplate();
		ob_get_contents();
	}

	public function update($new_instance, $old_instance)
	{
		$instance = $old_instance;
		return $instance;
	}
}

All real work is done inside the the Plugin class. This has all methods for talking to the database instance. But also contains the hooks for the WP install and uninstall actions. Some of the contents of this method are listed below.

require_once(ABSPATH.'wp-admin/includes/upgrade.php');
require_once(dirname(__FILE__).'/Widget.php');

Class DWOTD_Plugin
{
	public function __construct()
	{
		global $wpdb, $table_prefix;
                // set some class vars and include some files
	}

	public function install()
	{
                // create database and insert data
		add_option('dwotd_quote','1');
	}	

	public function uninstall()
	{
		$this->_dbh->query("DROP TABLE IF EXISTS `{$this->_dbName}`");
		delete_option('dwotd_quote');
	}

	public function getPluginDir()
	{
		return $this->_wpContent.'/plugins/dwotd';
	}
}

The code is just a small part of the real code. Which you can download here. After that it’s quite easy. Create a folder for the plugin in wp-content/plugins and copy the files there. Enable it from the plugin manager and enable it in the widget menu (which didn’t work for me because my theme has no sidebar.. well not the wp one :)). So i had to use a manual approach.

After copying the files to the plugin folder login to wp-admin and browse to Plugins/Plugins. Search for the plugin and click activate.

Enabling the widget from the admin panel is easy. Browse to Appearance/Widgets. And drag the widget from the left to the sidebar panel on the right.


Enabling it the manual way is also easy. But required a bit more work. For this to work the correct theme file needs to be edited somewhere in wp-content/themes. And the following code needs to be added in the spot the widget should appear.

$args = array ( 'title' => '' );
$instance = array ( 'title' => 'DWOTD', 'number' => 1 );
$widget = new DWOTD_Widget();
$widget->widget($args,$instance);

That’s all. The result can be viewed on the right top side. I build the plugin for fun and use on an Intranet. So don’t go blaming me if it breaks something. Besides that everybody is free to do with the code as they please.

A new look for lenss.nl

This long Easter weekend gave me some time to create a new theme for this blog. So after a day of work this is the result. I was a bit tired of the dark unreadable format. At the moment i am still tweaking here and there but it looks fine!

Fixing wp-e-commerce for iDEAL payments

Last Friday a friend approached me with a problem he was having. He was trying to setup a small webshop in a existing WordPress site. For the webshop he was using a plug-in called wp-e-commerce. He chose this plug-in because it is one of few that supports iDEAL payments. Because this shop only serves Holland the only payment option they need is iDEAL.

The iDEAL plug-in seemed to function properly. But the bank portal didn’t respond as expected. The first error i spotted was the mis configured referrer. The error code for this was.

unknown order/0/r

This didn’t solve the problem though. The message change from the previous to

unknown order/1/s

So i spend the next hours reading the manual he got from his bank. And came to the conclusion they do it just a bit different then for what this plug-in was written. The bank expects a hash to be send along each order made. This hash is build up from parts of the order and a secret string. This combined is hashed with the SHA-1 algorithm And added to the form as a hidden field. I wrote a small function to create hash and changed a few other small things in the order form.

The original form looks like this:

<script type="text/javascript">
var Amount = ;
var PSPID = "";
var AM;
if (isNaN(Amount)) {
	alert("Amount not a number: " + Amount + " !");
	AM = "";
} else {
	AM = Math.round(parseFloat(Amount)*100);
}
</script>
<form method='post' action='' id='ideal_form' name='ideal_form'>
<script type="text/javascript">
document.write("
");
document.write("
");
</script>
<INPUT TYPE="hidden" NAME="SHASign" VALUE="4FF8C2FB03B0AA45EA5DE9503AEACB6B603DCFCC">
<input type="hidden" NAME="orderID" value="" />
<input type="hidden" name="currency" value="" />
<input type="hidden" name="language" value="" />
<input type="hidden" name="accepturl" value="">
<input type="hidden" name="cancelurl" value="">
<!--customer information starts-->
<input type="hidden" name="CN" value="">
<input type="hidden" name="EMAIL" value="">
<input type="hidden" name="ownerZIP" value="">
<input type="hidden" name="owneraddress" value="">
<input type="hidden" name="ownercty" value="">
<input type="hidden" name="ownertown" value="">
<input type="hidden" name="ownertelno" value="">
<!--customer information ends-->
<input type="hidden" name="PM" value="iDEAL" />

I didn’t really understand why some values were written by JavaScript. So i removed the JavaScript lines and added the fields to the form. And after adding the hash function statement it looks like this.

<form method='post' action='' id='ideal_form' name='ideal_form'>

<input type="hidden" NAME="PSPID" value="" />
<input type="hidden" NAME="orderID" value="" />
<input type="hidden" NAME="amount" value="" />
<input type="hidden" name="currency" value="" />
<input type="hidden" name="language" value="" />
<input type="hidden" name="accepturl" value="">
<input type="hidden" name="cancelurl" value="">
<!--customer information starts-->
<input type="hidden" name="CN" value="">
<input type="hidden" name="EMAIL" value="">
<input type="hidden" name="ownerZIP" value="">
<input type="hidden" name="owneraddress" value="">
<input type="hidden" name="ownercty" value="">
<input type="hidden" name="ownertown" value="">
<input type="hidden" name="ownertelno" value="">
<!--customer information ends-->
<input type="hidden" name="PM" value="iDEAL" />
echo createSHA1Hash(array(
		$purchase_log[0]['id'],
		($amount*100),
		get_option('ideal_currency'),
		get_option('ideal_id'),
		'[SHA1-IN-HASH]'
	));
</form>

The function i can be placed anywhere in the page. Or a include file. Here’s the code. The only thing that has to be done is replace [SHA1-IN-HASH] with the Hash configured in the bank’s ideal admin.

function createSHA1Hash($hashOptions) {
        $str = implode('', $hashOptions);

        return '
';
    }

While doing some searches i noticed there are more people having issues with this plug-in. So maybe this will save somebody a bit of time.

WordPress and NO-WWW rewrite

On almost all of my domains i rewrite the WWW sub domain to the no-WWW version. This however went wrong on my own site. And i didn’t even notice it. My friend Alex pointed out that he couldn’t post any comment to the site anymore. So what went wrong?

Normally i use the following lines to do the rewrite.

RewriteCond %{HTTP_HOST} ^www\.lenss\.nl$ [NC]
RewriteRule ^(.*)$ http://lenss.nl/$1 [R=301,L]

My own site however still runs WordPress and this package comes with it’s own set of rewrite rules. And i just dropped my new lines under the WordPress rewrite rules.

RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} !^.*[^/]$
RewriteCond %{REQUEST_URI} !^.*//.*$
RewriteRule . index.php [L]

The result was that the WWW sub domain got rewritten to the no-www version. But everything after the trailing slash got dismissed. So rewrites for blog posts didn’t work. And all request would land on the main index. So i did some testing and combined the two sets of rewrite rules. This seems to function properly :)

RewriteEngine On
RewriteBase /
RewriteCond %{HTTP_HOST} ^www\.lenss\.nl$ [NC]
RewriteRule ^(.*)$ http://lenss.nl/$1 [R=301,L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} !^.*[^/]$
RewriteCond %{REQUEST_URI} !^.*//.*$
RewriteRule . index.php [L]

Next time i just need to check my changes more thoroughly. And thank you Alex for point that out to me.