18 Jul

jQuery 1.3 With PHP: chp7, image manipulation

I’ve submitted chapter 7 of jQuery 1.3 with PHP to Packt, which involves image manipulation. The editor I build in that chapter allows you to non-destructively manipulate an image in the most commonly needed ways – resize, rotate and crop.

The idea is that when you upload an image, it’s usually not yet right to include in a website. People tend to upload massive photos (3000×2000 or so) and resize it down using the <img> attributes, which is the wrong way to do it (see here for a solution to that particular problem).

However, once you’ve resized an image down, you can’t change it back.

The solution is to make copies of the image with your manipulations applied to them. That lets you make multiple versions of the same image.

But again, there’s the problem that if you change your mind about the original image, you can replace that, but the others all need to be redone.

Yet another problem is that unless you’ve kept records, there’s no link from the new images back to the original, so if you need to make space on the server by removing files that might not be in use anymore, you can’t be certain what thumbnails are still relevant and what are not.

The solution is to not actually create copies, but to record the changes applied to the original image in the URL of your manipulated image. When the server is asked for those URLs, it runs the manipulations on the original image and gives you back the new image. Of course, we will cache the new manipulations so this doesn’t need to be done every time, but this now allows you to replace the original image, then clear the cache, forcing all the variants of it to refresh, without you needing to do it again.

What this means, is that when the manipulation is recorded in the URL, no new copy is actually created until some browser actually requests it. This means that you can periodically clear your old thumbnails, safe in the knowledge that if they were still relevant, they will be recreated from the archived originals automatically the next time they’re needed.

Enough talk.


This is, as usual, just a demo. It’s not designed to be impressive, but just to show how to do manipulations.

A permanent URL is provided, in the same manner as google maps – it updates itself as you manipulate it. To see the actual cropped, resized and rotated image, try opening that permanent URL in a different tab.

The GUI here is a means to an end (the permanent URL). In your own CMS, you might force a certain width/height for uploaded images automatically using the URL for example.

The example image I manipulated in the chapter is the IMG_0134.JPG in chp7. Try rotating, resizing, and cropping (drag your mouse on the image).


Little thing to note about that URL – there’s no query indicator. Standards-compliant browsers treat the ‘?’ mark in a URL as an indication that the result should never be cached, but that would be pretty expensive for the server, so we remove that. The get.php file rebuilds the query from the $_SERVER['REQUEST_URI'] and sends out the manipulated image with caching headers.

The graphics manipulation was done using Imagick, which is a PECL extension that allows PHP to run ImageMagick functions internally.

In Fedora, you can install Imagick with yum install php-pecl-imagick. In CentOS, follow these instructions.

CentOS users might have a problem with the latest Imagick (2.2.2), so change the pecl install imagick line to pecl install imagick-2.2.1.

download the demo

Make sure to edit the images_libs.php file to point to your own image repositories. The images don’t need to be in a web-accessible directory.

I’ve a bit of work to do now, and then I’ll be applying the tricks in this chapter to our own CMS, WebME.

20 Jan

hosting multiple sites from the same CMS engine

I posted about webwork.ie‘s free website engine a few days ago. In the comments, Mickster asked how he would go about doing it himself.

I haven’t studied how other engines do it, but here’s how I do it.

First off, some benefits to sharing the CMS across separate sites:

  • Reduced resource usage. Because a single PHP engine is used, it is easier to optimise using RAM-disks, PHP caching, and other optimisations.
  • Easier upgrades – upgrade one system, upgrade them all!
  • Easier bug-fixing. One of the banes of my life is discovering a bug in a CMS that you know exists in at least ten other instances of that CMS. Now, you fix the bug in one place, and it’s fixed everywhere!

Convinced yet? Of course you are. Here’s how you do it.

First, separate all site-specific files from your engine files. To do this, you need to strictly keep your uploaded files and site-specific CSS in separate from your executable PHP. After doing this step, you should be able to clearly point out directories in your CMS and say “those are the engine’s executables and other resources, these are the design files and other uploadables which are site-specific.”

Second, all site-specific resources should be served through a script. The reason for this is that we are going to be moving the files away from the perceived directory that they’re in. Instead of, say, a web root of /home/webme/public_html/ with an image located at /home/webme/public_html/i/image.jpg, you might have the image located at /home/webme/sites/webme.eu/i/image.jpg, and another image from a different site located at /home/webme/sites/an-other-site.com/i/image.jpg. The choice of which image to show when /i/image.jpg is referenced depends then on the domain-name the browser requested the file from.

Then you need to over-ride the config at the time of its request. In the WebME CMS, the config file is located at /.private/config.php. What we want is to replace that with a “proxy” config file which, when included, checks the domain name and includes the real config file.

So how does the proxy config know where the real one is located? the way I handle this is to have a directory named after the site, located in /home/webme/sites/$NAME. If, for example, blah.webme.eu or www.blah.webme.eu is requested, you strip off any leading www. strings and trailing webme.eu strings with a regexp, and check to see if the config (/home/webme/sites/blah/config.php) exists. If so, include it.

To handle aliases, for example, if you wanted www.blah.com to load the blah.webme.eu site, you create a directory /home/webme/sites.aliases/$NAME, where $NAME (blah.com) is a symbolic link pointing to the correct directory.

It’s not a difficult trick, but it works.

Here’s my proxy config file:

Here is a copy of the proxy file that I use in webme.eu’s multi-user engine.

                header('Location: http://webme.eu/');
require $cfile;
17 Jan

running Thief in Wine


WOOT! Finally got Thief: The Dark Project running in Linux

One of the reasons I finally removed my Windows partition was that it had become absolutely useless to me.

The only reasons I’ve used Windows at all in the last few years are to play games or to test something in IE. Absolutely nothing else. And these days, most of the games I like to play simply do not work on Windows anymore. Vista, for example, refused to play Thief 2, and I haven’t been able to play Thief 1 since XP. For IE, I have another machine in the office in work, so there’s absolutely no reason for me to run Windows.

So anyway, I removed the partition with the reasoning that if Windows can’t play the games, and Wine can, then Wine is better. Ironic, really.

Thief is one of those game series that is just so brilliant that you need to buy it a few times, because you keep lending your copy to someone else and forgetting exactly who (and they like it so much they simply “forget” to give it back). Dungeon Keeper is another brilliant series like that.

Thief finally works in Wine 1.1.12, which was freshly released via Yum on the Fedora network just today. This is great news! The game did not work in Wine 1.1.11, when I tested last week. This week it’s perfect.

I fired up my previously failing installation, and after a few changes, was able to get it running.

Christos, in the bug tracker, says he re-installed the game and codecs to get it working. I didn’t have to reinstall anything. The only changes I had to make are:

  • use winecfg to change the emulation to Win98, and the graphics to use a virtual desktop. My laptop is widescreen, and Thief starts in 640×480 mode – for some stupid reason, x.org chops off the bottom of the screen when you request 640×480, and makes it 640×400. This makes Thief unplayable, so you need to use a virtual desktop.
  • write a small script to run the game in single-CPU mode – I created a file bin/thief containing this:
    cd ~/.wine/drive_c/Games/Thief/
    schedtool -a 0x2 -e wine THIEF.EXE

That was it – when I run thief from the console I get to play the game in full glory. It’s even better than the first time because this time my machine vastly beats the minimum requirements!

15 Jan

aw man…. 7 things

I thought I’d escaped this one. Everyone in the PHP world appears to have been “tagged” by now. Ken passed the buck to me, though, so I guess I’d better play.

Hmm… 7 things that people don’t already know about me? Hard task…

  1. I used to be into piercings, and if the piercing was common, then I wasn’t into it. Mostly, I was into surface piercings, and had a number of them down my sides and arms. I had planned on building a platform for a watch which would then be “embedded” in the arm, but never got around to it.
  2. I used to do “side-show” tricks, and have performed them on stage. This includes tricks such as “skewer through face” (horizontal – never got around to learning vertical), “human blockhead”, “face in glass”, eating glass, fire-eating, fire-blowing, and other weird stuff. Each of these is surprisingly safe when you know the trick.
  3. My first computer was a Commodore Vic 20 which I got when I was possibly 7 or 8. The highlight of that machine was writing (in BASIC) an animation of a bird which flew across the screen and “pooped” on a tank. I didn’t really consider that as “programming”, though – just playing. That machine was followed a year or two later with a Spectrum 48k+, which is where I really started – with 3d wire-frame graphics and text adventures. My first “real” computer was an Amstrad PPC640, where I learned QBASIC. My first CGI experience was writing applications (forums, etc) in C. That was followed by Perl, and finally I started writing php in about 1999 or so. Now I’m secretary of the Irish PHP User’s Group. Yay me! Oh, and Go Linux! I’ve been using Linux as my main OS since the 20th century as well.
  4. I can play a number of instruments. I played violin (rather, fiddle) for my school in a number of Feis Ceoil events and still have an electric violin that I pick up every now and then. I played Bagpipes in two separate pipe bands in Dublin – anecdote: in school, a music teacher was evaluating our playing of the tin whistle, and when he came to me, he saw immediately from the way I was holding it that I had training in a “pipe” instrument (uillean pipes and bag pipes are fingered similarly). I’ve played guitar in a few bands and like to get on stage at the Monaghan Blues Jam whenever I can (last compliment I got at one of them was “I like your playing – it’s /clean/ – very 70s”). I am looking for a classical guitar teacher at the moment. I also play keyboards, and have written music before (example).
  5. I’ve been vegetarian for more than half my life. This is not because of any hippy feelings towards animals. The two main reasons are 1) it’s disgusting to put dead animals in your mouth, and 2) it’s illogical, inefficient and cruel to kill animals for your food when you really don’t need to. I have no problem with people eating meat as long as they have no problems with me (and my family) not eating meat.
  6. I believe that the universe doesn’t really exist, but is a sort of shared illusion (look, it’s complex, ok!). Basically, it works like this – if the equation 2x-2x=0 is true, then you might consider that ‘x’ is a “real” object even though the sum of the equation is ‘0’ (ie; from nothing, comes something). This maps onto reality if you swap ‘x’ with ‘particle’, and ‘-x’ with ‘anti-particle’. I am always coming across situations where physicists discover this is true. Hawking radiation, for example, is an example of energy/matter spontaneously bursting into existence,, and is an exact analogy because you end up with ‘x’ (the radiation coming out of the black hole) and ‘-x’ (the radiation going into the black hole). Oh, and if you’re thinking it’s mad to think that ‘x’ could be real… in a computer game, all characters are “real” to each other, but not to an outsider looking in. In our case, we are those characters, and in an equation, each element is one of those characters. In the context of the equation/universe, all parts are real relative to all other parts, even if from the outside, they’re not.
  7. I used to do contact juggling. In fact, I founded the biggest contact juggling website. My son, Jareth, is named after David Bowie’s character from The Labyrinth where the goblin king (with the help of Michael Moschen) does some contact juggling (called “crystal ball manipulation” in the credits). I even wrote a book on the subject.
  8. Everyone in the world appears to have been tagged for this already, so I won’t bother tagging anyone.
    edit: suppose I’d better…

    1. Conor Mac Aoidh – local chap. Bright and enthusiastic. I feel that he’ll be producing sterling work soon and will be a credit to the Irish PHP community.
    2. St̩phane Lambert Рtireless compiler of PHP Whitebook trivia, and host of a damned good neo-classical metal radio show.

    I can’t think of any other PHP developers that I know that haven’t already been tagged. Really! Now go away and read something else!


    • Link your original tagger(s), and list these rules on your blog.
    • Share seven facts about yourself in the post – some random, some weird.
    • Tag seven people at the end of your post by leaving their names and the links to their blogs.
    • Let them know they’ve been tagged by leaving a comment on their blogs and/or Twitter
22 Nov

hooking up with an SD21

I bought some bits and pieces for the robot project. After a lot of consideration, I chose to go the servo route instead of the DC motor route. The difference is that with DC motors, you just turn them on and off they go, but with servos, you can tell the motors how far to go, and at what speed. I like the idea of having that fine control (which would probably be needed if I’m picking up twigs etc).

Unfortunately, the LPT port is pretty rare these days. In the first incarnation of my robot, I used the LPT port to directly turn motors on or off, using a home-built h-bridge to tell the motors to go forward or backward depending on what pins of the LPT I turned on.

I opted instead to use USB and I2C. I2C is a cool little standard – it lets you chain a load of devices into one connection. There is no direct I2C port on the outside of a laptop though, so i bought a USB-I2C convertor.

I found a shop online that sold all the necessary parts. Robot Electronics appears to be the shop-front for Devantech, who make a load of brilliant gadgets such as ultra-sonic rangers (not related to power rangers), on-chip compasses, and what I was interested in – servo controllers.

I bought the SD21 servo controller (controls up to 21 servos), a USB-I2C interface module so I could talk to the SD21, and 2 HS-55 Micro Lite servos so the other stuff actually did something, as well as some leads to connect the interface module to the motor controller.

By the way, the word ‘micro’ is a very apt description – they’re tiny! The image on the right shows the two of them with my mouse in the background.

The stuff finally arrived. Next was the tricky part – figuring out how they all go together.

Using the tech-specs for the usb-i2c and the sd21, it was easy to figure out. The only tricky part was that the USB-I2C module has 5 pins on it, although I2C actually only uses 4. in my photo above-left, the white cable is not used at all (it’s a test pin used by Devantech). So hook the USB-I2C and SD21 together connecting to one of the I2C connectors on the board.

in my case, I got a 5-way and a 4-way cable and just stuck the ends together. Connect black to black, red to red. But the blue in each cable connects to the yellow in the other. Then stick them onto the boards.

Power is provided by hooking 7.2v into the green block. I used 6 1.2v rechargeable batteries. Actually, 4 of them are 1.25v, but the board didn’t complain much. Be very careful that you hook them into the SD21 the right way around! I put the power in the wrong way once and had to yank them apart when I smelled burning parts! Luckily, there wasn’t anything wrong afterwards, but beware!

After all that, the hard part was figuring out how to actually talk to the thing. It took me some reading to figure out the obvious… you don’t speak directly to the SD21. You speak to the USB-I2C and it then talks to the SD21.

Here’s a small C program that turns the motors first in one direction, and then in the other. BTW: it’s been a century since I wrote C, so if I’ve done anything obviously wrong (especially that int to bytes conversion bit…) please tell me.

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <termios.h>

struct termios options;

int open_port(void) {
  // this function taken from http://www.robot-electronics.co.uk/forum/viewtopic.php?f=5&t=165
  int fd; // File descriptor for the port
  fd = open("/dev/ttyUSB0", O_RDWR | O_NOCTTY);
  if (fd == -1) {
    perror("open_port: Unable to open /dev/ttyUSB0 - "); // Could not open the port.
  else {
    fcntl(fd, F_SETFL, 0);

    // Get the current options for the port...
    tcgetattr(fd, &options);

    // Set the baud rates to 19200...
    cfsetispeed(&options, B19200);

    // Enable the receiver and set local mode...
    options.c_cflag |= (CLOCAL | CREAD);

    // Set no parity bit
    options.c_cflag &= ~PARENB;

    // Set 2 stop bits
    options.c_cflag &= ~CSTOPB;

    // Set the character size
    options.c_cflag &= ~CSIZE;
    options.c_cflag |= CS8;

    // Set the new options for the port...
    tcsetattr(fd, TCSANOW, &options);

    fcntl(fd, F_SETFL, FNDELAY);
  return (fd);

void set_servo(unsigned char reg, int pos) {
  // sets servo 'reg' to position 'pos'
  unsigned char sbuf[7];
  unsigned char lbyte, hbyte;
  int fd;

  hbyte=(unsigned char) (pos >> 8);
  lbyte=(unsigned char) (pos);

  sbuf[0] = 0x55;  // ftdi mode (multibyte device)
  sbuf[1] = 0xC2;  // address of the i2c device
  sbuf[2] = reg*3; // each servo has three registers
  sbuf[3] = 0x03;  // bytes to send
  sbuf[4] = 0x00;  // set to full speed
  sbuf[5] = lbyte; // position low byte
  sbuf[6] = hbyte; // position high byte

  fd = open_port();
  write(fd, sbuf, 7);

int main() {
  // take a step to the left
  set_servo(0, 800);
  set_servo(1, 800);


  // and then a jump to the right
  set_servo(0, 2200);
  set_servo(1, 2200);

  return 0;
29 Oct

argh! fedora 10

I should know better.

Every six months a new version of Fedora comes out, and every six months, I upgrade before it’s stable and screw up my computer.

For the sake of anyone else who encounters the same problem (bloody nothing loads – Gnome fails to load and Firefox fails as well), just downgrade to the Pango and Cairo libraries from Fedora 9.

update: if your machine boots, but only after you hit the keyboard a load of times, then try booting using the FC9 kernel instead.

btw: for some sad parties (myself included 😉 ): geansai gorm

28 Oct

codeweavers triggers global depression, offers software in compensation

Got to love it!

Giveaway Triggered in CodeWeaver’s Great American Lame Duck Presidential Challenge; “We take full responsibility for global economic collapse,” says CEO

Codeweavers, who make CrossOver office and CrossOver Games (applications which allow Windows programs to run in Linux), are giving away licenses for free, today, because Bush actually delivered on something – the price of petrol in Minneapolis fell to $2.79 on October 14th.

Of course, Bush had to destroy the global economy to do that, but hey, free software!

03 Oct

case-insensitive authentication in postfix-mysql

Just a quick note. If you find that your customers can log into their postfix account ok, but can’t read their email, check the directory their email is in:

[root@postfix /]# cd /home/vmail/domainname.ie/
[root@postfix domainname.ie]# ls -l
drwx------  5 postfix postfix 4096 Sep  4 11:52 joe.smith
drwx------  9 postfix postfix 4096 Sep  9 09:30 jane.smyth
drwx------  9 postfix postfix 4096 Sep  9 21:09 Jane.Smyth

The problem appears to be that postfix authenticates using MySQL, which is case-insensitive by default, then creates a default email skeleton directory named after the login username if it doesn’t find one already.

You can see in the above example that our user has logged in using uppercase letters in the username, MySQL verified the user had a right to be there, then postfix created the user’s directory using the login credentials, even though a lower-case version of the directory name already existed…

Obviously, this incorrect account will not receive email – email will be sent to the correct one.

There are a few solutions:

  • you can remove the Jane.Smyth directory and tell the user to change their authentication to use a lowercase username.
  • you can remove the Jane.Smyth directory then create a symbolic link from jane.smyth to Jane.Smyth.
  • change your MySQL installation to use a case-sensitive collation.

Personally, i go for the first solution.

05 Sep

moving email from qmail to postfixadmin

Yesterday we had to move about 300 domains from one machine to another. We bought a new machine recently and are taking this opportunity to move from Qmail (difficult to use, in my opinion) towards Postfix.

After doing one or two by hand, i decided that’s stupid – why not just automate the whole thing.

So I whipped up a script that reads details from vqadmin and uses those details to create postfix emails using mailadmin.

Please note that this does not handle forwards and other weirdness – just plain old email accounts. After running it, you need to check the accounts for forwards. I /could/ adapt it, but am too lazy.




// { get all domains
	curl_setopt($ch, CURLOPT_USERPWD,$vqadmin_auth);
	curl_setopt($ch, CURLOPT_POST, true );
	curl_setopt($ch, CURLOPT_POSTFIELDS, 'nav=list_domains' );
	curl_setopt($ch, CURLOPT_RETURNTRANSFER, true );
	preg_match_all('#<a href=vqadmin.cgi\?nav=view_domain&dname=([^>]*)>[^<]*</a>#',$page,$domains);
// }

$numdomains=0; $numemails=0;
foreach($domains[1] as $domain){
	echo '<h2>'.$numdomains.' - '.$domain.'</h2>';
	// { get password page
		curl_setopt($ch, CURLOPT_USERPWD,$vqadmin_auth);
		curl_setopt($ch, CURLOPT_POST, true );
		curl_setopt($ch, CURLOPT_POSTFIELDS, 'dname='.$domain.'&Submit=Show Users&nav=show_users' );
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, true );
	// }
	// { extract passwords
		preg_match_all('#<tr><td><FONT face=Verdana color="\#FFFFFF"><a href=[^>]*>[^<]*</a></FONT></td><td align=middle><FONT face=Verdana color="\#FFFFFF">[^<]*</FONT></td><td align=middle><FONT face=Verdana color="\#FFFFFF">[^<]*</FONT></td><td align=middle><FONT face=Verdana color="\#FFFFFF">[^<]*</FONT></td><td align=middle><FONT face=Verdana color="\#FFFFFF">[^<]*</FONT></td><td align=middle><FONT face=Verdana color=\#FFFFFF>(<B>)?[^<]*(</B>)?</FONT></td><td align=middle><FONT face=Verdana color=\#FFFFFF>[^<]*</font></td></tr>#',$page,$matches);
		foreach($matches as $match){
			$username=preg_replace('#<tr><td><FONT face=Verdana color="\#FFFFFF"><a href=[^>]*>([^<]*)<.*#','$1',$match);
			$password=preg_replace('#<tr><td><FONT face=Verdana color="\#FFFFFF"><a href=[^>]*>[^<]*</a></FONT></td><td align=middle><FONT face=Verdana color="\#FFFFFF">([^<]*)<.*#','$1',$match);
	// }
	// { log into postfix mailadmin
		curl_setopt($ch, CURLOPT_POST, true );
		curl_setopt($ch, CURLOPT_POSTFIELDS, 'fUsername='.$postfixmailadmin_username.'&fPassword='.urlencode($postfixmailadmin_password).'&lang=en&submit=Login');
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, true );
		curl_setopt($ch, CURLOPT_AUTOREFERER, true );
		curl_setopt($ch, CURLOPT_COOKIEJAR, 'tmp/cookies.txt');
	// }
	// { create domain
		curl_setopt($ch, CURLOPT_COOKIEFILE, 'tmp/cookies.txt');
		curl_setopt($ch, CURLOPT_POST, true );
		curl_setopt($ch, CURLOPT_POSTFIELDS, 'fDomain='.$domain.'&fAliases=25&fMailboxes=25&submit=Add Domain');
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, true );
	// }
	// { create email accounts
		foreach($emails as $email){
			curl_setopt($ch, CURLOPT_COOKIEFILE, 'tmp/cookies.txt');
			curl_setopt($ch, CURLOPT_POST, true );
			curl_setopt($ch, CURLOPT_POSTFIELDS, 'fDomain='.$domain.'&fUsername='.$email['username'].'&fPassword='.$email['password'].'&fPassword2='.$email['password'].'&fActive=on&fMail=on&submit=Add Mailbox');
			curl_setopt($ch, CURLOPT_RETURNTRANSFER, true );
			echo $numemails.':'.$email['username'].'@'.$domain.' ';
	// }

Make sure to up the timeout length of your PHP scripts – it took me about 10 minutes to transfer 1607 emails.

Also, be aware that this does not transfer the /contents/ of the email accounts – just recreates them on the other server – I don’t even want to touch that particular problem…

16 Jul

mounting an LVM volume partition

Let’s say you have a hard-drive to mount, and you try it:

mount /dev/sdc2 /mnt/hdc2

…and it reports an error.

mount: unknown filesystem type 'lvm2pv'

Took me a while to get this, but basically, you’re trying to load a partition from a volume, which is a pseudo file system. What you see in /dev (/dev/sdc2) is not actually the partition you think it is – it’s a container. the partition is contained /inside/ it (please correct me if I’m wrong).

So, the way you do this is:

[root@localhost ~]# pvscan
  PV /dev/sdc2   VG VolGroup01   lvm2 [74.41 GB / 32.00 MB free]
  Total: 1 [74.41 GB] / in use: 1 [74.41 GB] / in no VG: 0 [0   ]
[root@localhost ~]# lvchange --available y /dev/VolGroup01/LogVol00
[root@localhost ~]#

Then the mount is done using the name noted above (LogVol00 is obtained by looking in /dev/VolGroup01/).

[root@localhost ~]# mount /dev/VolGroup01/LogVol00 /mnt/hdc2
[root@localhost ~]#

et violin, you’re set.