Tuesday, April 17, 2007

Syntax Highlighting on Blogger

Since this is a Technical Blog, concerning topics that have to do with programming mostly I wanted to add some sort of syntax highlighting library to this website. Blogger allows you do fully edit your HTML (actually XML) template, so I did the following:

  1. Looked up syntax highlighting on blogger
  2. Ended up here: http://www.sitepoint.com/blogs/2006/08/29/flickr-mysql-dba-blog/
    Note how someone states in the comments that it SHOULD be possible to add dp.SyntaxHighlighter since the template is editable.
  3. I did as instructed.. I upload the library to my personal website, and editted the template so that the files were included.
  4. I surrounded code blocks as instructed by dp.SyntaxHighligher...
The end result: I ended up with each block of code showing as one line of code, with a <br /> tag at the beginning and at the end.

What I discovered was that blogger prints a post on a SINGLE LINE. All new line are omitted (even if you didn't use 'Compose' mode, but editted the HTML directly). SyntaxHighlighter can't cope with this (expects newline characters, etc.).

Blogger, this is seriously messed up and I would REALLY appreciate native syntax highlighting support, or if that is not going to happen a way to include third party libraries and make it work properly!

Furthermore I do like to recommend dp.SyntaxHighlighter if you can't syntax highligh server side! The library is very extensive, easy to use and produces very nice syntax!

Monday, April 16, 2007

An easy way to add (web 2.0 like) tags to your posts

One of the features that is closely linked to the web 2.0 developments is the use of Tags. Tags are short phrases (/words) that describe the article they accompany. Tags are great for categorizing posts and give the reader of an article information on it's subject instantly.

You see, for Tags to be effective it would be best to minimize the variety being used. If all tags concerning programming have a tag 'programming' attached to them, we could use that tag to effectively categorize those posts. Using standard tags for posts helps us create a more structured and better organized category system. The problem however is that you can't remember all the tags you already use and you probably don't have enough room (or want for that matter) to show all the tags in a row.

Using script.aculo.us however this problem is solved very easily! First of all, if you didn't know this already, Scripaculous is an open source Javascript framework. It used the Prototype framework and it provided many graphical effects and other powerful tools to it's users. One of the things it offers is an Autocompletion class, which we are going to use to make our lives a lot easier. Basically autocompletion presents matching options while the user is typing. The user can click the options (or use his keyboard) to select an option after which the text is inserted in his input field. Go to this Scriptaculous demonstration to see what I'm talking about.

First of all we need a database that contains the tags. This database must at least contain these fields:
- id (int, primary key, auto_increment)
- tag (varchar, unique)

Then we need a table to link tags to posts:
- tag_id (int)
- post_id (int)

Next, let's set up the input page. First of all we need an INPUT field, e.g.:
<input name="tags" id="tags" value="" style="width: 300px;" type="text">

Next we need an invisible DIV that Scriptaculous will use to show it's autocompletion results, e.g.:

<div id="auto_complete" style="display: none;"></div>

Now on to the Javascript!
For the sake of easy testing, let's add a local array containing the tags that are already used on the website (the tags the autocompleter will use):
var tags = new Array('news', 'websites', 'links', 'programming', 'php', 'mysql', 'apache', 'games', 'hardware', 'holiday', 'business', 'ICT', 'music');

Be sure to add that inside a Javascript block!

Here comes the magic! Below the form field containing the tags, add the following Javascript function (inside a javascript block:
new Autocompleter.Local('tags', 'auto_complete', tags, {});

This function will add the autocompleter. It will use the INPUT field 'tags', fill the DIV 'auto_complete' with results and use Javascript array 'tags' as it's source. The {} at the end signify the options array (empty for now).

The above works! However there is a small problem with it.. It will only allow you to add one tag! After that, autocompletion won't work.. Luckily, Scriptaculous can fix that! In the empty brackets ({}) at the end, add the following: tokens: ','

Or make it like this:
new Autocompleter.Local('tags', 'auto_complete', tags,
{
tokens: ','
});

Now you can add multiple tags and seperate them using commas! How neat is that! You can also set more options (for instance partial searching, minimum characters for autocomplete to start, etc), which are described here.

Of course it's also possible to load the autocompletion tags using Ajax. I think however it would be better to render the tags array using PHP at page load, because of performance (each Ajax request takes time, while the tags won't change..).

Click here to view my demonstration page for the above!

Next is the PHP part!

I won't go into much detail here.. This part basically requires 2 steps:

  1. Split up the tags;
  2. Save each one. If they already exist in your table, only add a link from the new post to the tag, if they didn't exist add the new tag and link the post to it.
Splitting up requires a small 'heads-up': scriptaculous will allow whitespace after the seperating comma, so you need to watch out for that aswell. Here's how:

// Strips whitespace at the beginning of a string
$_POST['tags'] = ltrim($_POST['tags']);

// Splits up the tags
$tags = preg_split('/[ \t]*,[ \t]*/', $_POST['tags']);

Now all you need to to is loop through $tags and store the tags (as described in point #2). Make sure you first check if the tag already exists! If it does, only store the link from this post to the tag! If it doesn't, add the tag, then get it's id (mysql_insert_id()) and then store the post->tag link.

On great thing to do with tags is create a (Web 2.0) tag cloud!

Also be sure to check out my demonstration for the above!

Happy web 2.0'ing!

Thursday, April 12, 2007

CakePHP Routing Explained

I'm currently playing around a bit more with CakePHP. I'm rewriting our CMS system based on the CakePHP framework. It's a great learning exercise because you constantly have to deal with real life problems!

Just now I was wondering about the url structure of the website. The CMS has two different "areas" designed for two different end users. First there is the site itself (displayed to the visitor of the website) and second there is the CMS administration panel. Because I started designing the system from the administration panel's perspective, I soon discovered that thát part of the website was going to be accessed at the root (/). This of course isn't a logical structure, as it would require the pages the visitors visit to have a prefix or something.. So I looked into CakePHP's routing system and came up with some solutions to my problem.

First of all, there is CakePHP's CAKE_ADMIN config setting. This is a routing solution that allows a function called: index() in controller: users to be accessed like this: /admin/users/index. All you need to do is rename the method to: admin_index() and set the CAKE_ADMIN variable to 'admin'. This seemed like a nice solution, but frankly it wasn't. You can still access the controller without the /admin/ prefix, but it will then give out errors (view for admin_index() could not be found). So I went to play a bit more, and found out the following:

  1. You can easily reconstruct Cake's default routing (i.e.: /controller/action/param1/param2/...) by using the following route:
    $Route->connect('/:controller/:action/*', array());
  2. The order in which routes are set matter: if one route is successful, Cake will not bother to look at the rest of the routes.
So I designed my own /admin/ prefix, like so:
$Route->connect('/admin/:controller/:action/*', array());

Then by changing the default route (for '/') to the following line:
$Route->connect('/:page/*', array('controller' => 'posts', 'action' => 'output'));

I can have nice page structures, which are rerouted to /posts/output... Using this structure /posts/output knows which page to load by looking at $this->params['page'] (:page is a custom parameter!). Even better, secondary parameters are still passed (thanks to the trailing /*)!

Since CakePHP uses ordering in the routing, it stops routing after a route was successful. So if you place the /admin/ route above the /:page/* route, administration should be fine!
Also, you might want /admin/ to be accessible without passing on controller/action, etc. (i.e. you would end up at the start page for the administration panel). To do this, include the following route (place it at the top):
$Route->connect('/admin*', array('controller' => 'posts', 'action' => 'index', 'page'));

This will reroute a request to /admin to a default routing location (for me: /posts/index/page)

What I find strange is the fact that the above route doesn't stop Cake from looking on (it will still find the more elabore one; /admin/:controller/:action/*). This is possibly because of the trailing Asterix (*)?

You can of course use these techniques to set up different "areas" for your website (while physically all files are located in the same Cake folder). For instance you could have the following areas:
/blog/
/shop/
/customer_service/

simply by setting up your routings properly.

Please note the 'standard' routing variables:
/somedir/:controller/:action

Here: :controller sets the controller Cake will use and :action sets the action that will be invoked. Furthermore you can set custom parameters with this:
/blog/:page
Now the value for page will be available by using: $this->params['page'].
By the way, other parameters are available in the following array: $this->params['pass']!

I know the post is a bit messy, still I hope it might help some people out! Good luck!