www.timothyfletcher.com http://http://www.timothyfletcher.com/feed Stuff about web development using PHP 2008-12-03 20:19:31 The www.timothyfletcher.com Blogging Engine en Simple Disjoint Image Swap With JQuery http://http://www.timothyfletcher.com/archives/posts/simple-disjoint-image-swap-with-jquery http://http://www.timothyfletcher.com/archives/posts/simple-disjoint-image-swap-with-jquery#comments 2008-11-25 07:26:10 Tim Fletcher Having spent a good 9 months intensively studying PHP and gettting to a pretty good standard, I'm onto Javascript now, specifically jQuery. Despite the many awesome and tempting effects, I'm starting at the beginning and solving the kinds of problems that you come across in a typical web project. Here I document how to perform a basic disjoint rollover or swap image. This script will allow you to hover over a thumbnail and display a larger preview of that image. DEMO - FILES

The HTML

The HTML is semantic and as simple as possible.

<div id="imageContainer">

    <img src="images/image-swap/image1.jpg" width="240" height="180" alt="Image1" id="image1">
    <img src="images/image-swap/image2.jpg" width="240" height="180" alt="Image2" id="image2">
    <img src="images/image-swap/image3.jpg" width="240" height="180" alt="Image3" id="image3">

</div>

<div id="thumbContainer">
    
    <a href="#image1"><img src="images/image-swap/thumb1.jpg" width="80" height="60" alt="Thumb1" id="thumb1"></a>
    <a href="#image2"><img src="images/image-swap/thumb2.jpg" width="80" height="60" alt="Thumb2" id="thumb2"></a>
    <a href="#image3"><img src="images/image-swap/thumb3.jpg" width="80" height="60" alt="Thumb3" id="thumb3"></a>
    
</div>

The CSS

#imageContainer         {width: 255px; height: 180px; overflow: hidden;}
#imageContainer img     {margin-bottom: 5px; display: block;}
#thumbContainer         {margin-top: 5px;}
#thumbContainer img     {display: block; float: left;}

The operation of this code is simple. The three identically sized large images are contained within the div#imageContainer which is limited in size via CSS to the size of a single image and has overflow: hidden to remove any scroll bars. This means that all the images exist on the page and are preloaded but hidden from view. Standards compliant browsers render images as inline elements which have an associated line-height. We must therefore use display:block; to remove the few pixels of gap at the bottom of div#imageContainer. Each thumbnail image is contained within an anchor link which associates it with the appropriate main image. When clicked, the div#imageContainer will scroll/jump to display the appropriate image.

It's important to make sure that your script manipulated HTML/CSS will still be usable if your users have Javascript turned off. This is why we are taking the XHTML/CSS approach first and then applying the Javascript to it later. It's not 100% perfect as each click takes you to a 'new' url which breaks the back button. It should be stressed that only users with Javascript turned off will see this.

The Javascript

Currently our XHTML/CSS needs the thumbnail to be clicked to get the large image to swap. What we need is for the swap to occur when the user mouses over the thumbnail. With jQuery this is simple. Place the following within your <head></head> tags or, more properly in an external script and link it in the head.

<script type="text/javascript" charset="utf-8">
    
    $(document).ready(function(){

        // Hide all large images except the first one
        $('#imageContainer img').hide().filter(':first').show();

        // Select all thumb links
        $('#thumbContainer a').hover(function(event) {

                // Hide all large images except for the one with the same hash as our thumb link
                $('#imageContainer img').hide().filter(this.hash).show();
            },
            function () {} // Because the hover method has a mouseout state we need to define too
        );
    });

</script>

I'll not explain the very basics as they're covered well on the jQuery site but what is happening here is that we are selecting all of the large images, hiding them and then displaying the first one. Next we select all of the links within #thumbContainer and attach a .hover action to them. The hover event accepts two callbacks as it's parameters: mouseover and mouseout, both of which need to be defined. When the link is hovered, all the large images are hidden except for the one which matches the hash link from the anchor we hovered. The currently selected element is represented by $(this) and you access it's methods using period notation.

Here's the code demo and download: DEMO - FILES

]]>
Changing Textmate Key Commands http://http://www.timothyfletcher.com/archives/posts/changing-textmate-key-commands http://http://www.timothyfletcher.com/archives/posts/changing-textmate-key-commands#comments 2008-11-21 20:15:19 Tim Fletcher I've found it a little annoying that Textmate's default key commands for switching tabs is different from that of Safari and Firefox. Textmate defaults to ⌥ ⌘ ← and ⌥ ⌘ → whereas the browsers are ⇑ ⌘ [ and ⇑ ⌘ ]. Every time I have to move my hand to the cursor keys it slows me down and is less comfortable. Strange then that it took me so long to get around to changing Textmate's defaults, particularly as it's so straightforward.

  1. System Preferences -> Keyboard & Mouse
  2. Click the + icon at the bottom left of the scrollable window
  3. Select Textmate as the Application
  4. Type "Next File Tab" as the Menu Title
  5. Hit ⇑ ⌘ [ whilst in the keyboard shortcut box
  6. Repeat using "Previous File Tab" and ⇑ ⌘ ]
]]>
Free 100% Cross Browser 2/3 Column Layouts http://http://www.timothyfletcher.com/archives/posts/free-100-cross-browser-23-column-layouts http://http://www.timothyfletcher.com/archives/posts/free-100-cross-browser-23-column-layouts#comments 2008-11-18 07:38:44 Tim Fletcher A couple of months ago I put together two cross-browser, pixel-perfect two and three column fixed width layouts. I'd found that when slicing Photoshop comps at work that the YUI Grids Framework was just too inflexible when having to specify precise widths. YUI sets everything in ems so you have to calculate widths. In fact you need to calculate twice as IE requires a different value.

I’d highly recommend having a go at building your own as it taught me a lot about cross-browser compatibility, particularly the quirks of IE5.5 and 6. However, if you just want to get going, I've provided them here for download. They're pixel-perfect in most (all?) browsers including IE 5.5.

Download: 2-column-fixed.zip
Download: 3-column-fixed.zip

]]>
Is There Such a Thing as a Simple Web App? http://http://www.timothyfletcher.com/archives/posts/is-there-such-a-thing-as-a-simple-web-app http://http://www.timothyfletcher.com/archives/posts/is-there-such-a-thing-as-a-simple-web-app#comments 2008-11-17 19:45:25 Tim Fletcher As a professional development exercise over the last couple of weeks, I've built myself a new blog using the PHP5 framework I've been putting together. I was getting a little frustrated with Wordpress and my reliance on the plugins of others. Inevitably they break after one of Wordpress's many critical security updates. What initially started as a small project with intentions of simplicity quickly grew into a fairly complex application. It's difficult to launch a modern web application of any sort without a level of complexity and it seems that managing that complexity is one of the skills of application development.

To give an example of how a seemingly simple feature can become a huge amount of work, consider the example of commenting. On the face of it you simply have a form that accepts input, writes it to a database and your page then pulls the comments from the database and displays them with the post. For an amateur job that would be fine. For a professional like myself we should consider the following:

Receiving comments

  • Filtering of $_POST input
  • Sanitising of $_POST input
  • Setting of cookies to show the comment only to its author until it is approved
  • Spam filtering
  • Alerting the administrator via email

Displaying comments

  • Are we accepting comments?
  • Is the current reader the comment author? How should we show the unapproved comment?
  • Is commenting on for this post?
  • Comment anchors

So you see it's not as simple to build a good application as it is to build a basic one. PHP's learning curve may be famously shallow but to build a well structured, efficient and secure application takes a lot of thought, time and effort. The barebones features I've integrated for this 1.0 release are:

  • Tagging
  • Commenting with specific allowed HTML tags using HTML Purifier
  • Syntax highlighting using Google Code Prettyfier
  • Post Administration
  • RSS Feeds
  • Secure login area
  • Toggle for post publishing on/off
  • Toggle for commenting on/off
  • Comment approval administration
  • Post writing and editing
  • Full tagging
]]>
Custom Block Widths with YUI Grids http://http://www.timothyfletcher.com/archives/posts/custom-block-widths-with-yui-grids http://http://www.timothyfletcher.com/archives/posts/custom-block-widths-with-yui-grids#comments 2008-08-21 06:14:26 Tim Fletcher Having just started my first job as a professional web developer, I'm now trying to integrate some of the site building techniques I've learned into the company's way of working. Something I've not done before is to slice up Photoshop compositions (I'm a programmer) and turn them into nice XHTML/CSS sites. For site structure I use Yahoo's User Interface suite, a highly researched, optimised and cross-browser set of tools designed to increase productivity for basic site structuring. However, it needs a bit of customisation to build sites that are not within one of Yahoo's standard bundled templates.

Custom Page Widths

The width of a page is easily customisable by specifying a width using a #custom-doc selector as in following CSS example. Page widths are specified in em to allow for zooming/accessibility should a user change the font size. The width in em is calculated by dividing the pixel width by 13 (for most browsers) or by 13.3333 for IE.

/* CSS for a custom page width of 600px */
#custom-doc {
    width:46.154em;    /* 600px/13 */
    *width:45.042em;   /* 600px/13.3333 */
}

Custom Template Block Widths

There are six grid templates for the YUI encompassing a variety of dual columns of varying widths. If somebody else is designing your sites these templates will almost certainly be inappropriate. To give an example, the .yui-t4 template class specifies a 180 pixel right block with the left (master) block taking up the remainder of the page width save for a 3 pixel 'safety' space in between the two column blocks. Here is the salient HTML and CSS taken from the grids.css YUI file.

<!-- HTML for a standard template with a custom width -->
<div id="custom-doc" class="yui-t4"> <!-- Custom set at 600px wide -->
    <div id="hd"></div>
    <div id="bd">
        <div id="yui-main">    <!-- Wrap .yui-b div in a #yui-main div to make it the main div -->
            <div class="yui-b leftColumn"></div>
        </div>
        <div class="yui-b rightColumn"></div>
    </div>
<div id="ft"></div>
</div>					
/* CSS for the yui-t4 template */
.yui-t4 .yui-b {
    float:right;
    width:13.8461em;    /* 190px/13 */
    *width:13.50em;     /* 190px/13.333 for IE */
}

.yui-t4 #yui-main .yui-b {
    margin-right:14.8456em;    /* 193px/13 */
    *margin-right:14.55em;     /* 193px/13.333 for IE */
}

Now to customise these block columns we simply need to respecify the widths of the .yui-t4 selector within our own css file. Here we are specifying a right column width of 190px and a spacer of 10px thus assigning the rest of the space (the left column) 400px in our 600px custom width layout. Make sure that when your css files are linked in your HTML file they are in the correct order - grids.css goes first.

/* Customisation of YUI grids for template*/
.yui-t4 .yui-b{
    float: right;
    width: 14.615em;       /* 190px/13 */
    *width: 14.250em;      /* 190px/13.333 for IE */
}

.yui-t4 #yui-main .yui-b {
    margin-right: 15.385em;      /* 200px/13 */
    *margin-right: 15.000em;     /* 200px/13.333 for IE*/
}

One note that needs adding is that it is probably useful to work in pixels and then change to em at the end of your designing/slicing. If you need to add padding to the block column divs then you'll also need to recalculate the ems which will get boring after...well...one time.

UPDATE: I no longer use YUI grids. In the long run I found them to be inflexible when slicing other designers' comps. I have moved to using some simple custom 2 and 3 column templates that I developed and heavily tested.

]]>
Making Zend Framework Applications Portable http://http://www.timothyfletcher.com/archives/posts/making-zend-framework-applications-portable http://http://www.timothyfletcher.com/archives/posts/making-zend-framework-applications-portable#comments 2008-08-10 08:04:07 Tim Fletcher I recently put my first Zend Framework application onto my Slicehost slice only to be greeted with the inevitable pages of errors generated by not found pages and the like. Clearly this wasted the time I value so much and so I thought I'd write a few notes on how to avoid this if you are transferring applications to production servers.

Database Configuration

It's highly likely that your local and production database configuration files are going to be different. The config.ini file in Zend Framework allows you to specify many different configurations for anything you like. Why not specify two database configurations and switch between them in your bootstrap based on the contents of the $_SERVER['SERVER_NAME'] variable? This is my config.ini.

[local]
;Local server database
db.adapter=PDO_MYSQL
db.host=localhost
db.username=localuser
db.password=localpassword
db.dbname=localdbname

[production]
;Production server database
db.adapter=PDO_MYSQL
db.host=localhost
db.username=productionuser
db.password=productionpassword
db.dbname=productiondbname

Now in my bootstrap file I use the following

// Load config - query server variables to get local / remote
$productionServer = array('dev.adeli.ca');
$localServer = array('dreamadelica.dev');
if (in_array($_SERVER['SERVER_NAME'], $localServer)) { $locale = 'local'; }
if (in_array($_SERVER['SERVER_NAME'], $productionServer)) { $locale = 'production'; }
$config = new Zend_Config_Ini(ROOT . DIRECTORY_SEPARATOR . 'config' . DIRECTORY_SEPARATOR . 'config.ini', $locale);

The $locale variable contains a string that is a parameter in the instantiation of the new Zend_Config_Ini() object created using whichever database configuration details are appropriate for our server location.

Case Sensitivity

I develop on a Mac which I though had a case sensitive file system in Leopard's Mac OS Extended (Journaled). It turns out that this is not the case and although volumes can be created with case sensitivity enabled, some 3rd party applications (like the recently fixed Adobe CS3 suite) do not seem to support it yet. I found out a class Forms_CommentForm() does not get found via the Zend_Loader class if it is located at forms/CommentForm.php. A simple enough problem to fix but bugs of this nature could arise if you develop on a case insensitive file system and then upload to a live Linux server which does feature case sensitivity.

PHP Short Tags

As noted in my <a href="http://www.timothyfletcher.com/archives/posts/syntax-error-when-creating-xmlrss-views">previous post</a> PHP's short tags doesn't play happily with the XML declaration ( <code>&lt;?xml</code> ) It thinks it's going into PHP and then throws syntax errors. You shouldn't be using short tags anyway. It's bad for code portability. Turn them off in php.ini.

Relative URLs

The thing that really caused me problems was my lack of use of dynamically generated URLs in my controllers and views. I used relative URLs (i.e. <code>&lt;a href=&quot;/link/to/somewhere&quot;&gt;Somewhere&lt;/a&gt;</code>) which is a really bad idea if you want to avoid code portability issues. The best policy is to obtain the base URL from within the request object which exists within each controller object. At the beginning of your controller class, use a <code>init()</code> (like a constructor) method to assign the base URL to a property of the controller class and make it accessible to all the action methods.
public function init()
{
    $this->view->baseUrl = $this->_request->getBaseUrl();
}

Now from within the views we can access the base URL with:


echo $this->baseUrl;

One final note is that if you're using background CSS images, you need to ensure that your URLs within the CSS file are relative to the current CSS file location. If your CSS is located in a folder and your images are located in a folder on the same level then you need to go up a level using .. before the path. For example:

#ft{background-image: url('../images/footer.png');}

Let me know if this saved you time. Best.

Update: After uploading my second app - Highlanders Rugby Club - I've come across a few more portability issues. They are:

  • Not copying the .htaccess file (files starting with a period are system files and do not show in the finder
  • I had capitals within my model class definitions but not on the file names.
  • File names for the admin module's controller classes were not capitalised.
  • I had written Zend_Form_Element_TextArea instead of Zend_Form_Element_Textarea
]]>
Syntax Error When Creating XML/RSS Views http://http://www.timothyfletcher.com/archives/posts/syntax-error-when-creating-xmlrss-views http://http://www.timothyfletcher.com/archives/posts/syntax-error-when-creating-xmlrss-views#comments 2008-08-02 19:35:14 Tim Fletcher I was recently trying to render an XML view in the Zend Framework for an RSS feed but couldn't get past a syntax error generated on line 1 of the XML view file. The answer is to turn off short tags in your PHP config file This is good practice anyway as if you use short tags you reduce the portability of your code. It was a frustrating one and took me longer to figure out than I would have liked, mainly because I thought it was me using the framework incorrectly! To solve this, simply turn off short tags in your php.ini.

The offending line was:

<?xml version="1.0" encoding="UTF-8" ?>

To fix this amend your php.ini (approx. line 76) file to...

short_open_tag = Off

Don't forget to restart Apache!

]]>
Finder Button to Show/Hide Hidden Files in OS X http://http://www.timothyfletcher.com/archives/posts/finder-button-to-showhide-hidden-files-in-os-x http://http://www.timothyfletcher.com/archives/posts/finder-button-to-showhide-hidden-files-in-os-x#comments 2008-07-19 22:27:38 Tim Fletcher I recently found Henrik Nyh's cool 'Open in Textmate' script which includes the ability to be added to the Finder toolbar in OS X. I also often find the need to show hidden files in the Finder and run Shane Duffy's AppleScript to do so. I though it would be great if I could have the 'ToggleHiddenFiles' script as a button on the Finder toolbar too so I created one.

Finder With Hide Files Button

Basically what I did is take Shane's script, open it in Leopard's Script Editor and save it as an application (such that it has the .app extension. I then took Henrik's script and extracted the droplet.icns icon file from it (Ctrl-Click -> Show Package Contents -> Contents -> Resources -> droplet.ics) and opened it in Adobe Fireworks. I then modified the icon and saved it as a .psd Photoshop file. Next I opened the .psd in Icon Composer (part of the XCode Tools) and saved as a .icns file. I wish there was an easier way to do this but I found lots of problems with other basic (read:free) icon manipulation tools.

Finally, in the .app package I created with Script Editor, I replaced applet.icns (which was named droplet.icns in Henrik's script) with my version of the icon, placed the .app file into /Applications/Scripts and dragged the script file to the Finder Toolbar. Voila!

If you want to create your own icon then use my instructions above. If you're happy with my handiwork then download my version of this great script and drag to your menu bar.

Download: ToggleHiddenFiles.zip

]]>
How to Code a Web App? http://http://www.timothyfletcher.com/archives/posts/how-to-code-a-web-app http://http://www.timothyfletcher.com/archives/posts/how-to-code-a-web-app#comments 2008-05-11 07:51:15 Tim Fletcher I've hit a bit of a problem with my study of PHP. Over the last 3-4 months I've become well up to speed with the syntax and understand the theory of object orientation. However, when I'm sitting in front of a text editor's blank page, I have no clue on how to go about building a well coded, extensible web application. Where do I begin? Yes, I could easily just start hacking away and creating code but as the site grows and new features are added it will eventually develop into the infamous 'spaghetti code' where I (or anyone else for that matter) will have to deal with a completely structureless and unmanageable mess. For me this is not acceptable and enough of a barrier to necessitate further study before beginning to code proper applications.

One of the solutions to this is to use a framework and my last few posts have documented my experiences with CodeIgniter's implementation of the Model-View-Controller design pattern. Great though frameworks are, I've come across two issues. Firstly, the proliferation of PHP 5 is not as widespread as I would like. I'm told that many shared hosts do not support it despite its release nearly four years ago - Would you pick a host that didn't support PHP 5!?!? Anyway, this means that the most popular frameworks are necessarily supporting adn thus coded to be compatible with PHP 4 and it's 'tacked on' implementation of object orientation. As a fledgling coder and someone who embraces new (or 4 year old) technologies I want to code in PHP 5, learn its 'new' object-oriented features and become a better developer. There are currently few frameworks that are PHP 5 only and in my opinion none of these have sufficient community support / documentation for me to dissect and learn effectively.

Secondly, though I hold firm in my beliefs that frameworks are an essential part of modern web development, I also believe that is important to understand their implementation, at least on a basic level. Trying to dissect a framework like CodeIgniter, CakePHP or Kohana (at least for a relative beginner like me) is a near impossible task. The issue is that if you blindly use a framework for every project you will ultimately become reliant upon it and your knowledge of the fundamental principals of development will be dulled. A very bad thing, particularly for a programming beginner.

So with these thoughts in mind I'm now moving onto a new book - PHP in Action: Objects, Design, Agility I've read the first chapter and a few Amazon reviews and it appears to be at my current level. I'll maybe post a review in a while if it's any good...

]]>
Simple PHP Twitter Status Badge http://http://www.timothyfletcher.com/archives/posts/simple-php-twitter-status-badge http://http://www.timothyfletcher.com/archives/posts/simple-php-twitter-status-badge#comments 2008-05-10 07:43:46 Tim Fletcher The concept of learning by doing is one I firmly believe in. The intention of 'rolling my own' blog is that inevitably I will happen upon and have to overcome many typical programming problems as I implement new features. One such problem was how to add the 'latest Tweet' feature that the twitterRSS plugin solved on my old Wordpress blog.

Reverse Engineering

Important though an understanding of the theory taught by books is, I find that reverse engineering somebody else's code, stripping it down to the absolute basics and then building it back up in a way that I can fully understand is a great method of making that knowledge stick. Yes, I could just find and use a third party class but I almost always find that it's features are way beyond my needs resulting in the obfuscation of the code I'm trying to learn from. Stripping it to the basics lets you simplify, analyse and understand its core processes. The code I stripped for this tutorial came from a the CodeIgniter RSSParser class although the resultant code is not framework dependant.

The first thing you need to do is find your Twitter RSS feed URL. Log into Twitter and click on the 'home' link in the upper menu bar. Then scroll to the bottom and click the RSS button. This will load your RSS feed in your browser or feed reader. Note that for the feed to work in the code the protocol must be http:// and not feed://. You username is your full twitter name. It's important to get this exactly correct else your tweet will have your username prepended at the start (we remove it later).

function getTweet() {
  // Set up variables
  $twitter_feed = 'http://twitter.com/statuses/user_timeline/13395012.rss';
  $twitter_username = 'timfletcher';

Next we need to get the RSS feed and write it into a string. Using the @ symbol before a function suppresses errors. Remember only to do this if you're implementing error handling in some other way.

  // Get the RSS file and put it into a string
  // Supress errors with the @ symbol
  $rawfeed = @file_get_contents($twitter_feed);

If the feed was successfully read (i.e. does not return FALSE) then we can process it by creating a new SimpleXMLElement object and passing the feed to it. Passing a variable when instantiating an object causes the object's constructor to create the object using that variable. To quote from the PHP manual:

The SimpleXML extension provides a very simple and easily usable toolset to convert XML to an object that can be processed with normal property selectors and array iterators.

The SimpleXML extension requires PHP5. Seriously, if your host doesn't yet support PHP5 then get another one!

if ($rawfeed != FALSE) {
  $xml = new SimpleXmlElement($rawfeed);

To get the latest tweet from the RSS file you can use (as stated in the manual) property selectors and array iterators. The way I worked all this out was by using the essential PHP function var_dump(). It basically spews the contents of a variable or object onto the display so you can see its structure. I have TextMate set up to throw a var_dump(variable); die(); into the code from a tab command when I type 'var'. It's invaluable for debugging.

So after a bit of using var_dump I worked out that you can display your latest tweet using this code.

$tweet = $xml->channel->item[0]->description;

As is the structure of Twitter's RSS stream, this line will output your Twitter name and a colon before the tweet. If you want this then fine but I'll remove by using str_replace() to change it to nothing. This is why $username needs to be exactly correct.

echo str_replace($twitter_username . ': ','', $tweet);

And that's pretty much it. All you need to do is call the getTweet() function and it will output your single, latest tweet as unformatted text. To make it all a bit easier to use here's the full code. Give me a shout with any comments or problems!

function getTweet() {
    
    // Set up variables
    $twitter_feed = 'http://twitter.com/statuses/user_timeline/13395012.rss';
    $twitter_username = 'timfletcher';
    
    // Get the RSS file and put it into a string
    // Supress errors with the @ symbol
    $rawfeed = @file_get_contents($twitter_feed);
    
    // if file_get_contents returned true...
    if ($rawfeed != FALSE) {
        
        $xml = new SimpleXmlElement($rawfeed);
        
        // Fetch the latest tweet
        $tweet = $xml->channel->item[0]->description;

        // Remove username from the front of the tweet and output
        echo str_replace(TWITTER_USERNAME . ': ','', $tweet);
        
    } else {
        
    echo 'There is a problem with the Twitter feed just now...';
    }
}

UPDATE: I've just realised that when Twitter (or at least its RSS) is unavailable (i.e. often), it takes over a minute for the page to load. Arrrgh!!!! Talk about learning the hard way!

This can be resolved by setting a timeout value on the file_get_contents(); function. From the php.net site for file_get_contents(), Change the above code to include the following.

// Setting the timeout properly without messing with ini values
$ctx = stream_context_create(array('http' => array('timeout' => 1)));
//Parse the document
$rawfeed = @file_get_contents(TWITTER_FEED, 0, $ctx);
]]>