boogdesign posts

Longer posts on standards based web design, portable web development and Linux, intermingled with some stuff on my other nerd interests.

Rob Crowther, London, UK based Blogger, Web Developer, Web Designer and System Administrator - read my Curriculum Vitae

Buy my book!

Book CoverHello! HTML5 and CSS3 available now

Buy my other book!

Book CoverEarly access to HTML5 in Action available now

Category: Server Side Web Development

03/08/09

03:10:22 pm Permalink CSS with PHP

Categories: Front End Web Development, Server Side Web Development, Standards, HTML and CSS

Last week I re-designed the home page of my personal website as the previous version was quite plain looking, mostly because I'd never gotten around to updating it. I had a visual theme for most of the sub-pages, a strong graphic as a background 'column' on the left and side, with the graphic differing by section. My idea was to reuse these graphics on the front page, except randomly select two of them each time the page loaded.

Since they were background images, I couldn't add a dynamic element in the page itself to contain them. I could perhaps have defined several new CSS classes to apply to the body element and then inserted a class name with PHP, but with six images, picking two, that means 30 class names which seemed inelegant, plus would be a bit of a pain if I later added an new section. So instead of making the page itself dynamic, I decided to make the CSS dynamic instead.

From a coding point of view the implementation is very straightforward. You rename your CSS file to be .php and make sure you return the correct content type in the header by adding this as your first line:

<?php header("Content-type: text/css"); ?>

I then just declared an array with all my images in and use a built in PHP function to give me two random images:

$bg_images = array('vertical_essays.jpg','vertical_default.jpg','vertical_books.jpg','vertical_football.jpg','vertical_writing.jpg','vertical_blog.jpg');
$bg_keys = array_rand($bg_images, 2);

Then in the CSS itself I just select the appropriate image:

html {
	background-image: url('/image/<?= $bg_images[$bg_keys[0]] ?>');
	background-repeat: no-repeat;
	background-attachment: fixed;
	background-position: bottom left;
}
body {
	background-image: url('/image/<?= $bg_images[$bg_keys[1]] ?>');
	background-repeat: no-repeat;
	background-attachment: fixed;
	background-position: bottom right;
}

After getting this working I then decided it would be fun to have my main navigation boxes have a background colour related to the images which are shown. The DeGraeve.com Color Palette Generator lets you generate a palette of colours from any image, so I put each of my background images into it and got a set of 'vibrant' and 'dull' colours for each image. I stuck this into another array, using the image names as keys and simply alternating vibrant and dull colours into an array value:

$colours = array(
    'vertical_essays.jpg' => array('#440','#553','#670','#332','#9b7','#eee'), 
    'vertical_default.jpg' => array('#560','#452','#02d','#36a','#8a7','#995'), 
    'vertical_books.jpg' => array('#720','#532','#d10','#a64','#d90','#a85'),
    'vertical_football.jpg' => array('#581','#564','#8a3','#785','#9c5','#eee'),
    'vertical_writing.jpg' => array('#644','#c73','#f20','#e82','#f30','#ded'),
    'vertical_blog.jpg' => array('#732','#644','#c30','#965','#c96','#cbb')
);

I could then set the background and border colours of the navigation boxes using the two random keys I'd already generated. To keep it simple I just had the left two boxes take colours from the left image, and the right box take colours from the right image:

#nav li.cv {
	background-color: <?= $colours[$bg_images[$bg_keys[0]]][0] ?>
	border-color: <?= $colours[$bg_images[$bg_keys[0]]][1] ?>
}
#nav li.writing {
	background-color: <?= $colours[$bg_images[$bg_keys[0]]][2] ?>
	border-color: <?= $colours[$bg_images[$bg_keys[0]]][3] ?>
}
#nav li.gameplan {
	background-color: <?= $colours[$bg_images[$bg_keys[1]]][4] ?>
	border-color: <?= $colours[$bg_images[$bg_keys[1]]][5] ?>
}

Here is the end result:

dotrob.com screenshot

As you can see, generating your CSS with PHP is just as easy as generating HTML with PHP. Even with my fairly simple minded implementation of the idea you can achieve some nice tricks. However, there are some drawbacks I ran into:

  • Because you're now editing a PHP file, you might have some trouble persuading your auto-completing CSS editor to recognise what's going on
  • You're now going to have to handle caching yourself, as your CSS is no longer a static file - not much of an issue for a small site like mine, but could be an unnecessary overhead on a large site
  • Once you've added background colours to the mix you have to consider your text and link foreground colours, I just settled for a light grey with light blue links which worked OK (but not great) on all the colours - could be a bit of a maintenance problem in the future (and a readability problem now)

Fortunately, there are possible ways round all these issues, especially if you were considering this technique for a serious site rather than a one page vanity domain &amp;#58;&amp;#41; You could always use your PHP to generate the appropriate set of colours for you from a given base colour - this saves you having to work out text, non-visited and visited link colours for each background colour. For bonus points, you could probably add the code which extract the base colour from the image in the first place to end up with a completely flexible solution. The first two issues both have the same solution - use an actual CSS file as a template, thus preserving the editor features, but use a server-side pre-processor to deliver the file to the web. This allows you, among other thing, to extend CSS with variables as well as taking care of all the cache issues for you.


Tweet this!
Send feedback »PermalinkPermalink

27/06/08

03:12:01 pm Permalink SearchMonkey Developer Event

Categories: Web Develop, Server Side Web Development, Semantic Web and Microformats

Review: SearchMonkey Developer Event at Wallacespace, 2 Dryden Street, Covent Garden, London, England. WC2E 9NA. 18:30 to 20:00

I'd heard quite a lot about SearchMonkey, Yahoo!'s new search platform but not got around to checking it out, so I was hoping this event would be a useful introduction. The evening involved a couple of presentations from Yahoo! developers coupled with free drinks and food - a promising combination!

The first presenter (sorry, didn't make a note of the name - a Yahoo! developer from the US) talked about the motivations behind SearchMonkey and some of the technical features. One of the main obstacles to more semantic data appearing on the web is that there's a chicken and egg problem. It's not worth having features supporting semantic standards like RDF and Microformats on search engines until there's content available, and it's not worth making content available until you get benefits on things like search results. SearchMonkey cuts through that whole issue by allowing users to add features to the search engine to make use of the semantic data they've added to their website. It allows you to modify the presentation of search results from particular sites (not just your own) using semantic data taken either directly from the page (if there are Microformats or RDFa available) or through an XSLT transformation which you can provide.

Next we had Neil Crosby walk us through building a couple of example SearchMonkey applications. It really is fairly straightforward - you pick a URL pattern for your application to apply to and then it's more or less point and click (especially if you have standard Microformats or RDFa available). A couple of noteworthy points came up:

  • You can ask SearchMonkey to automatically generate ten sample URLs as you go through the process, but it's best to pick the first one by hand as that's the one used in all the previews once your monkey is done
  • You can use XSLT to extract data if your target page doesn't have pre-parsed Microformats or RDFa available, but that will be slower (though it will at least get cached for 15 minutes after the first call)
  • The presentation part of the monkey is entirely separate from the data source, multiple monkeys can use the one data source, and you can use data sources provided by other developers in your monkey

At present all SearchMonkeys are opt-in on the part of the user, which means if you're not targeting a big site it's unlikely your finished application will see a lot of use. However, we were promised that 'within a month' there would be options to set a default SearchMonkey for a website if you're the registered owner of the site (ie. you've registered yourself on site explorer) - so I have that long to think of something clever to do with my search results &amp;#58;&amp;#41;

The evening ended with free pizza, hand prepared in the kitchen behind us, and there were free drinks throughout. We also got a nice SearchMonkey hat, a sticker and a rather weird USB stick (Grolsch style opening mechanism). All in all an excellent evening, 5 out of 5. It has taken me nearly two weeks to get the review finished off, but some folk were a bit quicker off the mark if you'd like an alternative perspective.

Technorati tags for this review:    

Tweet this!
Send feedback »PermalinkPermalink

10/06/08

07:41:02 pm Permalink Fixing WackoWiki for PHP 5

Categories: Web Develop, Server Side Web Development

For a long time now, I've had a WackoWiki implementation on my (not so technical) personal site. It was upgraded from a WikkaWiki which was installed back in March 2003, which makes it about four months older than this blog &amp;#58;&amp;#41;

Anyway, the point is it's a legacy app, which has never been a problem for a little used website until I discovered (last night) that the hosting company was doing an upgrade on all the servers which would remove PHP 4 to leave only PHP 5. A quick check indicated Wacko R4.2 was supposed to be PHP 5 compatible, so I didn't worry too much about it and went to bed. Of course, when I checked this morning, it was broken, just a blank page...

I downloaded the error log and found a few entries like this:

[error] [client www.xxx.yyy.zzz] PHP Catchable fatal error: Object of class Wacko could not be converted to string in wakka/wakka.php on line 254

A quick Google revealed a number of other WackoWiki installs with a similar problem, but also this more useful result which explained the change in PHP 5.2 which was causing the problem:

Changed __toString() to be called wherever applicable. The magic method __toString() will now be called in a string context, that is, anywhere an object is used as a string. The fallback of returning a string that contains the object identifier was dropped in PHP 5.2.0. It became problematic because an object identifier cannot be considered unique. This change will mean that your application is flawed if you have relied on the object identifier as a return value. An attempt to use that value as a string will now result in a catchable fatal error.

Line 254 of wakka.php is:

$cache->Log("Before Run wacko=".$wacko);

I'm not sure what it's supposed to be doing, but it didn't seem too critical, so I just removed the object reference:

$cache->Log("Before Run wacko=");

Seems to work for me!


Tweet this!
6 feedbacks »PermalinkPermalink

03/03/08

11:37:49 pm Permalink Fixing Jack's formmail.php for register_globals = off

Categories: Server Side Web Development

For many years now, I've used various versions of Jack's FormMail.php to handle simple form submissions on various websites, mostly 'contact us' forms. The script has gotten a little more sophisticated with time, but it's still one of the simpler ways of making a website 'interactive.'

The website at work uses it to send the marketing department emails when someone fills in the contact us form. The form is somewhat sophisticated, in that it presents different information depending on a few parameters in the URL, and passes formmail.php a different subject for the email depending on what configuration it's in. It also passes a redirect URL in a hidden field, so the user is redirected to a 'thanks for contacting us' page after submitting the form. Google Analytics is set up so that any hits on the 'thanks' page after viewing the contact page counts as a Goal. And this is where I spotted the problem - as I checked through the Analytics stats I noticed we hadn't achieved any goals since the middle of last week, although a day or two here and there without any is fairly common five on the trot is pretty rare.

My first step was to go to the contact form and try submitting it, the email duly arrived in the correct mailbox but the script didn't redirect to the thanks page. So first mystery solved, no hits on the thanks page means no goals on Analytics, but the marketing department hadn't noticed because they were still getting emails. I messed around with some of the different configurations of the contact page but none of them redirected as intended. Then I realised that all the emails had the same subject, and that was the default one given by formmail.php when it doesn't get passed a subject, though the values in the form fields were appearing in the email correctly, so it wasn't like the information wasn't getting passed though.

I checked the web host's page and discovered that they had recently updated the PHP config on the server, this in order to support PHP v5. There was a new option in CPanel to select the default version of PHP, so I went and set it to v4 in case formmail was incompatible with the newer release. It didn't make any difference though, so I started looking at formmail.php itself in detail.

The first thing I noticed was that the redirect functionality required register_globals to be enabled. Although my PHP configuration page claimed that register_globals was enabled, the behaviour of the script indicated that it wasn't. Unsurprisingly, the subject field also expected register_globals to be enabled, as did several other fields which I wasn't using, but the code which builds the email uses $HTTP_POST_VARS so works fine without.

If you've managed to read this far without falling asleep and you know what register_globals is, then you now have enough information to fix the problem yourself and there's no need to torture yourself any further. If you currently have no idea what I'm on about, I'm now going to try and explain and then show you how to edit your script to fix the problem. register_globals is a configuration setting for the PHP interpreter, if it's set to on then PHP will helpfully make any parameters passed into the script global variables. Parameters are the bits in the URL after the question mark, or fields in a form. This can be quite handy when you're slapping together a quick script, but is also very dangerous because, if you're not careful, you can create easy opportunities for hackers to do nasty things. For this reason, register_globals = on has long been discouraged in the PHP community, unfortunately many useful scripts were written in the days before anyone realised it was an issue, so web hosts usually provide a way turn it back on even if it's not enabled by default.

However, today we're going to make formmail.php work properly with register_globals = off. You're going to need to open the formmail.php script in a text editor - Notepad will do fine. First we shall fix the redirect issue, find the following lines near the end of the file:

if ($redirect) {
   header("Location: $redirect");
   exit; 

We're going to replace $redirect, which is trying to access the global variable, with $_POST['redirect'], like so:

if (isset($_POST['redirect'])) {
   header("Location: ". $_POST['redirect']);
   exit;

The same trick works with the subject and email address, scroll up a few lines and find this:

mail_it(stripslashes($content), ($subject)?stripslashes($subject):"Form Submission", $email, $recipient); 

Replace it with:

mail_it(stripslashes($content), isset($_POST['subject'])?stripslashes($_POST['subject']):"Form Submission", $_POST['email'], $recipient);

Now upload your new version to the server and you should find things start working as before. As you can see the general rule is to replace $variable with $_POST['variable'], notice that I didn't change $recipient because I always hard code that value at the top of the script as a security precaution.


Tweet this!
57 feedbacks »PermalinkPermalink

16/10/07

03:02:19 pm Permalink My First Website for Mobiles

Categories: Web Design, Front End Web Development, Server Side Web Development

At work we decided to purchase a few .mobi domains, not because we have any particularly compelling mobile content, but to boost the web presence of our mobile product and, mostly, to make sure no-one else snapped up the domain names we wanted. However this did give me the opportunity to build my first website explicitly for mobile devices.

I'd checked our main website on ready.mobi, because I have written a mobile stylesheet for it, but it got a pretty crappy score mostly because of the sheer size of the content. So my main goals for this first site were to:

  • Produce a website which validated
  • Get a 'Good' score on the ready.mobi evaluator

My first task was some background reading, I had a look through the DotMobi Mobile Web Developer's Guide and Luca Passani's Global Authoring Practices for the Mobile Web. Following that, I grabbed a page off the main website and broke the content up into three XHTML-MP pages. This was easy enough to do after reading the guides, not much different from creating regular XHTML pages. The only bit I wasn't too familiar with was sorting out the navigation and access keys. It was easy on the home page (actual links changed for brevity):

<ol>
<li><a href="page1.php" accesskey="1">One</a></li>
<li><a href="page2.php" accesskey="2">Two</a></li>
<li><a href="page3.php" accesskey="3">Three</a></li>
<li><a href="page4.php" accesskey="4">Four</a></li>
</ol>

But on the other pages I wanted to avoid having a link to the current page, but keep a consistent numbering scheme, from 'page3.php':

<ol>
<li><a href="page1.php" accesskey="1">One</a></li>
<li><a href="page2.php" accesskey="2">Two</a></li>
<li value="4"><a href="page4.php" accesskey="4">Four</a></li>
</ol>

So that was the actual content sorted out, most of the issues I was seeing in the ready.mobi validator were to do with server side configuration. Some mobile browsers require a application/vnd.wap.xhtml+xml MIME type, which Apache isn't going to do by default, but is easy enough to configure. However, I wanted the website to display on my boss's desktop browser too and IE will have some difficulties with that, so I decided to use PHP to do some lightweight browser detection:

<?php
header("Cache-Control: no-transform, max-age=86400");
header("Vary: User-Agent, Accept");
if (strpos(strtolower($_SERVER['HTTP_ACCEPT']),'application/vnd.wap.xhtml+xml')>0) {
header("Content-type: application/vnd.wap.xhtml+xml");
} elseif (strpos(strtolower($_SERVER['HTTP_ACCEPT']),'application/xhtml+xml')>0) {
header("Content-type: application/xhtml+xml");
} else {
header("Content-type: text/html");
}
echo "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
?>
<!DOCTYPE html PUBLIC "-//WAPFORUM//DTD XHTML Mobile 1.0//EN" "http://www.wapforum.org/DTD/xhtml-mobile10.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">

This works by looking to see what MIME types the browser says it can accept, then giving it the best one it can handle. Note that I'm also advising transcoding proxies not to transform this content.

The final setup step was to set appropriate cache headers on all the static content. This is easy to do in .htaccess:

<IfModule mod_expires.c>
ExpiresActive on
ExpiresDefault "access plus 1 day"
ExpiresByType image/gif "access plus 1 month"
ExpiresByType image/jpeg "access plus 1 month"
ExpiresByType image/png "access plus 1 month"
ExpiresByType text/css "access plus 1 month"
ExpiresByType application/x-javascript "access plus 1 month"
ExpiresByType text/html "access plus 1 week"
ExpiresByType application/x-shockwave-flash "access plus 1 month"
<FilesMatch "\.(pl|php|cgi|spl|scgi|fcgi)$">
ExpiresActive Off
</FilesMatch>
</IfModule>

Note that I'm ignoring PHP files in this directive, as I'm already handling the headers in the PHP files.

One final note, as I discovered a few times while working on this, you need make sure your error pages also handle all the MIME type stuff and are valid XHTML-MP, otherwise the experience could be confusing for mobile users. This applies to 404 pages (I just made it a PHP page) and your formmail.php script (if you're using one).

And there you have it, my first real, live, mobile website!


Tweet this!
Send feedback »PermalinkPermalink

10/06/07

08:56:43 pm Permalink GDD07UK: Google Data APIs (Part 1)

Categories: Web Develop, Server Side Web Development

Review: Google Data APIs at GDD07UK, The Brewery in London, 52 Chiswell Street, London 14:45 to 15:30

The GData APIs are a uniform interface into almost all the Google applications (Blogger, Mail, Calendar, Spreadsheets etc.). They're all based on an extended version of Atom Publishing Protocol, a REST style interface (though there is some difference of opinion on whether or not it's a best practice implementation of APP (see also some discussions on Reddit and this followup). In general this is an exciting development and I could imagine a number of practical applications for my day job, and having a unified API across all the Google Apps should make it easy to build applications which integrate with all of it.

Unfortunately, I didn't really enjoy this talk much at the time, mainly I felt there weren't enough code examples. Watching the video of the talk again now I see far more example code than I remember which indicates that I wasn't really taking it in. Most of the example code was in XML files, most of which were pretty hard to read from where I was sitting, or logs of HTTP interactions for high level descriptions of how it would work, but none of the code shown was associated with the demos in the second half of the talk. I think my expectation going into the talk was more along the lines of "here's some lines of Java code which insert an event in the calendar." Perhaps that was a little unrealistic, given the number of languages Google supply libraries for, not wanting to either show examples in several different languages or stick to one and risk alienating people, but it coloured my impressions. Though the fact that the talk was also the shortest one of the day indicates that perhaps there was room for some analysis of the code associated with the examples. Generally, unlike the previous talk, I didn't feel like I came away with enough information from this talk to dive right in and start trying stuff, so 2 out of 5. On the plus side, because this talk ended early I was able to catch the end of the KML / Google Earth talk.

Technorati tags for this review:    

Tweet this!
Send feedback »PermalinkPermalink

07/02/07

04:53:00 pm Permalink WebDD07: Ruby On Rails for .NET Developers (Dave Verwer)

Categories: Web Develop, Server Side Web Development

Review: Ruby On Rails for .NET Developers at WebDD Conference 07 14:30 to 15:30

After a brief spell writing a Ruby on Rails app last summer I always meant to do more stuff with it, but somehow I never got around to it. I was hoping this talk would re-ignite my enthusiasm and give me some good pointers.

Dave started off with some general background for the benefit of the .Net crowd, positioning C#/VB as comparable with Ruby and ASP.Net as Rails equivalent in the Microsoft world. He then moved on to an overview of Ruby:

  • Ruby is Object Oriented - a lot of languages claim to be, but not many are as aggressively object oriented as Ruby, even null is an object
  • Ruby is Readable - there were some good examples: 2.weeks.from_now.utc for the date two weeks from today; punctuation for tests as in x.null?; punctuation for destructive and non-destructive methods as in arr.sort compared to arr.sort!
  • Ruby is Dynamic - and this is taken full advantage of in Rails, there were some more examples using method_missing and the Linguistics add on, which extends built in objects

Next we moved on to Rails, and after some general discussion of convention over configuration the bulk of the presentation was a live demonstration of building a web application. The eye opening part of this for me was that Dave approached this in a completely different way to how I'd previously done it (in my admittedly limited experience). He started by defining his model and then adding fields using Rake migrations. I'd never paid any attention to Rake before but it looks very useful once you know what to do with it, basically you define your database upgrades and downgrades in Ruby so then it's easy to roll databases backwards and forwards.

The talk finished up with some references to how Microsoft is 'borrowing' some of the ideas in Rails, LINQ being quite similar to ActiveRecord and BLINQ performing similar tasks to the Rails scaffolding.

I really enjoyed this talk, what's not really coming across in my dry description of the talk above is what engaging presenter Dave Verwer really is, if you have a chance to go see him present I can highly recommend it. Overall this was my favourite talk of the day, 5 out of 5.

Technorati tags for this review:    

Tweet this!
3 feedbacks »PermalinkPermalink