web-design - GreenAsh Poignant wit and hippie ramblings that are pertinent to web-design https://greenash.net.au/thoughts/topics/web-design/ 2022-04-10T00:00:00Z Introducing: Hack Your Bradfield Vote 2022-04-10T00:00:00Z 2022-04-10T00:00:00Z Jaza https://greenash.net.au/thoughts/2022/04/introducing-hack-your-bradfield-vote/ I built a tiny site, that I humbly hope makes a tiny difference in my home electorate of Bradfield, this 2022 federal election. Check out Hack Your Bradfield Vote.

How
How "Hack Your Bradfield Vote" looks on desktop and mobile

I'm not overly optimistic, here in what is one of the safest Liberal seats in Australia. But you never know, this may finally be the year when the winds of change rustle the verdant treescape of Sydney's leafy North Shore.

]]>
Introducing GreenAsh 5 2022-03-14T00:00:00Z 2022-03-14T00:00:00Z Jaza https://greenash.net.au/thoughts/2022/03/introducing-greenash-5/ After a solid run of twelve years, I've put GreenAsh v4 out to pasture, and I've launched v5 to fill its plush shoes.

Sporting a simple, readable, mobile-friendly design.
Sporting a simple, readable, mobile-friendly design.

GreenAsh v5 marks the culmination of my continuing mission, to convert over all of my personal sites, and all of the other sites that I still host slash maintain, to use a Static Site Generator (SSG). As with some other sites of mine, GreenAsh is now powered by Eleventy, and is now hosted on Netlify.

As was the case with v4, this new version isn't a complete redesign, it's a realign. First and foremost, the new design's aim is for the thought-reading experience to be a delightful one, with improved text legibility and better formatting of in-article elements. The new design is also (long overdue for GreenAsh!) fully responsive from the ground up, catering for mobile display just as much as desktop.

After nearly 18 years, this is the first ever version of GreenAsh to lack a database-powered back-end. 'Tis a bittersweet parting for me. The initial years of GreenAsh, powered by the One True™ LAMP Stack – originally, albeit briefly, using a home-grown PHP app, and then, for much longer, using Drupal – were (for me) exciting times that I will always remember fondly.

The past decade (and a bit) of the GreenAsh chronicles, powered by Django, has seen the site mature, both technology-wise and content-wise. In this, the latest chapter of The Life of GreenAsh, I hope not just to find some juniper bushes, but also to continue nurturing the site, particularly by penning thoughts of an ever higher calibre.

The most noteworthy feature that I've built in this new version, is a comment moderation and publishing system powered mainly by Netlify Functions. I'm quite proud of what I've cobbled together, and I'll be expounding upon it, in prose coming soon to a thought near you. Watch this space!

Some of the things that I had previously whinged about as being a real pain in Hugo, such as a tag cloud and a monthly / yearly archive, I've gotten built quite nicely here, using Eleventy, just as I had hoped I would. Some of the functionality that I had manually ported from Drupal to Django (i.e. from PHP to Python), back in the day, such as the autop filter, and the inline image filter, I have now ported from Django to Eleventy (i.e. from Python to Node.js).

As a side effect of the site now being hosted on Netlify, the site's source code is (for the first time) publicly available on GitHub, and even has an open-source license. So feel free to use it as you will.

All of the SSG-powered sites that I've built over the past year, have their media assets (mainly consisting of images) stored in S3 and served by CloudFront (and, in some cases, the site itself is also stored in S3 and is served by CloudFront, rather than being hosted on Netlify). GreenAsh v5 is no exception.

On account of the source code now being public, and of there no longer being any traditional back-end server, I've had to move some functionality out of GreenAsh, that I previously had bundled in to Django. In particular, I migrated my invoice data for freelance work – which had been defined as Django models, and stored in the sites's database, and exposed in the Django admin – to a simple Google Sheet, which, honestly (considering how little work I do on the side these days), will do, for the foreseeable future. And I migrated my résumé – which had been a password-protected Django view – to its own little password-protected S3 / CloudFront site.

The only big feature of v4 that's currently missing in v5, is site search. This is, of course, much easier to implement for a traditional back-end-powered site, than it is for an SSG-powered site. I previously used Whoosh with Django. Anyway, site search is only a nice-to-have feature, and this is only a small site that's easily browsable, and (in the meantime) folks can just use Google with the site: operator instead. And I hear it's not that hard to implement search for Eleventy these days, so maybe I'll whack that on to GreenAsh v5 sometime soon too.

I've been busy, SSG-ifying all my old sites, and GreenAsh is the lucky last. Now that GreenAsh v5 is live (and now that I've migrated various other non-web-facing things – mainly migrating backups of things to S3 buckets), that means I don't need a VPS anymore! I'll be writing a separate thought, sometime soon, about the pros and cons of still having a VPS in this day and age.

Hope y'all like the new décor.

]]>
Introducing: Is Pacific Highway Upgraded Yet? 2021-06-08T00:00:00Z 2021-06-08T00:00:00Z Jaza https://greenash.net.au/thoughts/2021/06/introducing-is-pacific-highway-upgraded-yet/ Check out this fun little site that I just built: Is Pacific Highway Upgraded Yet?

Spoiler alert: no it's not!
Spoiler alert: no it's not!

I got thinking about this, in light of the government's announcement at the end of 2020 that the Pacific Highway upgrade is finished. I was like, hang on, no it's not! How about a web site to tell people how long we've already been waiting for this (spoiler alert: ages!), and how much longer we'll probably be waiting?

Complete with a countdown timer, which is currently set to 1 Jan 2030, a date that I arbitrarily and fairly optimistically picked as the target completion date of the Hexham bypass (but that project is still in the planning stage, no construction dates have currently been announced).

Fellow Australians, enjoy!

]]>
Introducing Flask Editable Site 2015-10-27T00:00:00Z 2015-10-27T00:00:00Z Jaza https://greenash.net.au/thoughts/2015/10/introducing-flask-editable-site/ I'd like to humbly present Flask Editable Site, a template for building a small marketing web site in Flask where all content is live editable. Here's a demo of the app in action.

Text and image block editing with Flask Editable Site.
Text and image block editing with Flask Editable Site.

The aim of this app is to demonstrate that, with the help of modern JS libraries, and with some well-thought-out server-side snippets, it's now perfectly possible to "bake in" live in-place editing for virtually every content element in a typical brochureware site.

This app is not a CMS. On the contrary, think of it as a proof-of-concept alternative to a CMS. An alternative where there's no "admin area", there's no "editing mode", and there's no "preview button". There's only direct manipulation.

"Template" means that this is a sample app. It comes with a bunch of models that work out-of-the-box (e.g. text content block, image content block, gallery item, event). However, these are just a starting point: you can and should define your own models when building a real site. Same with the front-end templates: the home page layout and the CSS styles are just examples.

About that "template" idea

I can't stress enough that this is not a CMS. There are of course plenty of CMSes out there already, in Python and in every other language under the sun. Several of those CMSes I have used extensively. I've even been paid to build web sites with them, for most of my professional life so far. I desire neither to add to that list, nor to take on the heavy maintenance burden that doing so would entail.

What I have discovered as a web developer, and what I'm sure that all web developers discover sooner or later, is that there's no such thing as the perfect CMS. Possibly, there isn't even such thing as a good CMS! If you want to build a web site with a content management experience that's highly tailored to the project in question, then really, you have to build a unique custom CMS just for that site. Deride me as a perfectionist if you want, but that's my opinion.

There is such a thing as a good framework. Flask Editable Site, as its name suggests, uses the Flask framework, which has the glorious honour of being my favourite framework these days. And there is definitely such a thing as a good library. Flask Editable Site uses a number of both front-end and back-end libraries. The best libraries can be easily mashed up together in different configurations, on top of different frameworks, to help power a variety of different apps.

Flask Editable Site is not a CMS. It's a sample app, which is a template for building a unique CMS-like app tailor-made for a given project. If you're doing it right, then no two projects based on Flask Editable Site will be the same app. Every project has at least slightly different data models, users / permissions, custom forms, front-end widgets, and so on.

So, there's the practical aim of demonstrating direct manipulation / live editing. However, Flask Editable Site has a philosophical aim, too. The traditional "building a super one-size-fits-all app to power 90% of sites" approach isn't necessarily a good one. You inevitably end up fighting the super-app, and hacking around things to make it work for you. Instead, how about "building and sharing a template for making each site its own tailored app"? How about accepting that "every site is a hack", and embracing that instead of fighting it?

Thanks and acknowledgements

Thanks to all the libraries that Flask Editable Site uses; in each case, I tried to choose the best library available at the present time, for achieving a given purpose:

  • Dante contenteditable WYSIWYG editor, a Medium editor clone. I had previously used MediumEditor, and I recommend it too, but I feel that Dante gives a more polished out-of-the-box experience for now. I think the folks at Medium have done a great job in setting the bar high for beautiful rich-text editing, which is an important part of the admin experience for many web sites / apps.
  • Dropzone.js image upload widget. C'mon, people, it's 2015. Death to HTML file fields for uploads. Drag and drop with image preview, bring it on. From my limited research, Dropzone.js seems to be the clear leader of this pack at the moment.
  • Bootstrap datetimepicker for calendar picker and hour/minute selector.
  • Bootstrap 3 for pretty CSS styles and grid layouts. I admit I've become a bit of a Bootstrap addict lately. For developers with non-existent artistic ability, like myself, it's impossible to resist. Font Awesome is rather nice, too.
  • Markovify for random text generation. I discovered this one (and several alternative implementations of it) while building Flask Editable Site, and I'm hooked. Adios, Lorem Ipsum, and don't hit the door on your way out.
  • Bootstrap Freelancer theme by Start Bootstrap. Although Flask Editable Site uses vanilla Bootstrap, I borrowed various snippets of CSS / JS from this theme, as well as the overall layout.
  • cookiecutter-flask, a Flask app template. I highly recommend this as a guide to best-practice directory layout, configuration management, and use of patterns in a Flask app. Thanks to these best practices, Flask Editable Site is also reasonably Twelve-Factor compliant, especially in terms of config and backing services.

Flask Editable Site began as the codebase for The Daydream Believers Performers web site, which I built pro-bono as a side project recently. So, acknowledgements to that group for helping to make Flask Editable Site happen.

For the live editing UX, I acknowledge that I drew inspiration from several examples. First and foremost, from Mezzanine, a CMS (based on Django) which I've used on occasion. Mezzanine puts "edit" buttons in-place next to most text fields on a site, and pops up a traditional (i.e. non contenteditable) WYSIWYG editor when these are clicked.

I also had a peek at Create.js, which takes care of the front-end side of live content editing quite similarly to the way I've cobbled it together. In Flask Editable Site, the combo of Dante editor and my custom "autosave" JS could easily be replaced with Create.js (particularly when using Hallo editor, which is quite minimalist like Dante); I guess it's just a question of personal taste.

Sir Trevor JS is an interesting new kid on the block. I'm quite impressed with Sir Trevor, but its philosophy of "adding blocks of anything down the page" isn't such a great fit for Flask Editable Site, where the idea is that site admins can only add / edit content within specific constraints for each block on the page. However, for sites with no structured content models, where it's OK for each page to be a free canvas (or for a "free canvas" within, say, each blog post on a site), I can see Sir Trevor being a real game-changer.

There's also X-editable, which is the only JS solution that I've come across for nice live editing of list-type content (i.e. checkoxes, radio buttons, tag fields, autocomplete boxes, etc). I haven't used X-editable in Flask Editable Site, because I'm mainly dealing with text and image fields (and for date / time fields, I prefer a proper calendar widget). But if I needed live editing of list fields, X-editable would be my first choice.

Final thoughts

I must stress that, as I said above, Flask Editable site is a proof-of-concept. It doesn't have all the features you're going to need for your project foo. In particular, it doesn't support very many field types: only text ("short text" and "rich text"), date, time, and image. It should also support inline images and (YouTube / Vimeo) videos out-of-the-box, as this is included with Dante, but I haven't tested it. For other field types, forks / pull requests / sister projects are welcome.

If you look at the code (particularly the settings.py file and the home view), you should be able to add live editing of new content models quite easily, with just a bit of copy-pasting and tweaking. The idea is that the editable.views code is generic enough, that you won't need to change it at all when adding new models / fields in your back-end. At least, that's the idea.

Quite a lot of the code in Flask Editable Site is more complex than it strictly needs to be, in order to support "session store mode", where all content is saved to the current user's session instead of to the database (preferably using something like Memcached or temp files, rather than cookies, although that depends on what settings you use). I developed "session store mode" in order to make the demo site work without requiring any hackery such as a scheduled DB refresh (which is the usual solution in such cases). However, I can see it also being useful for sandbox environments, for UAT, and for reviewing design / functionality changes without "real" content getting in the way.

The app also includes a fair bit of code for random generation and selection of sample text and image content. This was also done primarily for the purposes of the demo site. But, upon reflection, I think that a robust solution for randomly populating a site's content is really something that all CMS-like apps should consider more seriously. The exact algorithms and sample content pools for this, of course, are a matter of taste. But the point is that it's not just about pretty pictures and amusing Dickensian text. It's about the mindset of treating content dynamically, and of recognising the bounds and the parameters of each placeholder area on the page. And what better way to enforce that mindset, than by seeing a different random set of content every time you restart the app?

I decided to make this project a good opportunity for getting my hands dirty with thorough unit / functional testing. As such, Flask Editable Site is my first open-source effort that features automated testing via Travis CI, as well as test coverage reporting via Coveralls. As you can see on the GitHub page, tests are passing and coverage is pretty good. The tests are written in pytest, with significant help from webtest, too. I hope that the tests also serve as a template for other projects; all too often, with small brochureware sites, formal testing is done sparingly if at all.

Regarding the "no admin area" principle, Flask Editable Site has taken quite a purist approach to this. Personally, I think that radically reducing the role of "admin areas" in web site administration will lead to better UX. Anything that's publicly visible on the site, should be editable first and foremost via direct manipulation. However, in reality there will always be things that aren't publicly visible, and that admins still need to edit. For example, sites will always need user / role CRUD pages (unless you're happy to only manage users via shell commands). So, if you do add admin pages to a project based on Flask Editable Site, please don't feel as though you're breaking some golden rule.

Hope you enjoy playing around with the app. Who knows, maybe you'll even build something useful based on it. Feedback, bug reports, pull requests, all welcome.

]]>
An inline image Django template filter 2010-06-06T00:00:00Z 2010-06-06T00:00:00Z Jaza https://greenash.net.au/thoughts/2010/06/an-inline-image-django-template-filter/ Adding image fields to a Django model is easy, thanks to the built-in ImageField class. Auto-resizing uploaded images is also a breeze, courtesy of sorl-thumbnail and its forks/variants. But what about embedding resized images inline within text content? This is a very common use case for bloggers, and it's a final step that seems to be missing in Django at the moment.

Having recently migrated this site over from Drupal, my old blog posts had inline images embedded using image assist. Images could be inserted into an arbitrary spot within a text field by entering a token, with a syntax of [img_assist nid=123 ... ]. I wanted to be able to continue embedding images in roughly the same fashion, using a syntax as closely matching the old one as possible.

So, I've written a simple template filter that parses a text block for tokens with a syntax of [thumbnail image-identifier], and that replaces every such token with the image matching the given identifier, resized according to a pre-determined width and height (by sorl-thumbnail), and formatted as an image tag with a caption underneath. The code for the filter is below.

import re

from django import template
from django.template.defaultfilters import stringfilter

from sorl.thumbnail.main import DjangoThumbnail

from models import InlineImage

register = template.Library()

regex = re.compile(r'\[thumbnail (?P<identifier>[\-\w]+)\]')


@register.filter
@stringfilter
def inline_thumbnails(value):
    new_value = value
    it = regex.finditer(value)
    for m in it:
        try:
            image = InlineImage.objects.get(identifier=identifier)
            thumbnail = DjangoThumbnail(image.image, (500, 500))
            new_value = new_value.replace(m.group(), '<img src="%s%s" width="%d" height="%d" alt="%s" /><p><em>%s</em></p>' % ('http://mysite.com', thumbnail.absolute_url, thumbnail.width(), thumbnail.height(), image.title, image.title))
        except InlineImage.DoesNotExist:
            pass
    return new_value

This code belongs in a file such as appname/templatetags/inline_thumbnails.py within your Django project directory. It also assumes that you have an InlineImage model that looks something like this (in your app's models.py file):

from django.db import models

class InlineImage(models.Model):
    created         = models.DateTimeField(auto_now_add=True)
    modified        = models.DateTimeField(auto_now=True)

    title           = models.CharField(max_length=100)

    image           = models.ImageField(upload_to='uploads/images')
    identifier      = models.SlugField(unique=True)

    def __unicode__(self):
        return self.title
        ordering = ('-created',)

Say you have a model for your site's blog posts, called Entry. The main body text field for this model is content. You could upload an InlineImage with identifier hokey-pokey. You'd then embed the image into the body text of a blog post like so:

<p>You put your left foot in,
You put your left foot out,
You put your left foot in,
And you shake it all about.</p>

[thumbnail hokey-pokey]

<p>You do the Hokey Pokey and you turn around,
That's what it's all about.</p>

To render the blog post content with the thumbnail tokens converted into actual images, simply filter the variable in your template, like so:

{% load inline_thumbnails %}

{{ entry.content|inline_thumbnails|safe }}

The code here is just a simple example — if you copy it and adapt it to your own needs, you'll probably want to add a bit more functionality to it. For example, the token could be extended to support specifying image alignment (left/right), width/height per image, caption override, etc. But I didn't particularly need any of these things, and I wanted to keep my code simple, so I've omitted those features from my filter.

]]>
An autop Django template filter 2010-05-30T00:00:00Z 2010-05-30T00:00:00Z Jaza https://greenash.net.au/thoughts/2010/05/an-autop-django-template-filter/ autop is a script that was first written for WordPress by Matt Mullenweg (the WordPress founder). All WordPress blog posts are filtered using wpautop() (unless you install an additional plug-in to disable the filter). The function was also ported to Drupal, and it's enabled by default when entering body text into Drupal nodes. As far as I'm aware, autop has never been ported to a language other than PHP. Until now.

In the process of migrating this site from Drupal to Django, I was surprised to discover that not only Django, but also Python in general, lacks any linebreak filtering function (official or otherwise) that's anywhere near as intelligent as autop. The built-in Django linebreaks filter converts all single newlines to <br /> tags, and all double newlines to <p> tags, completely irrespective of HTML block elements such as <code> and <script>. This was a fairly major problem for me, as I was migrating a lot of old content over from Drupal, and that content was all formatted in autop style. Plus, I'm used to writing content in that way, and I'd like to continue writing content in that way, whether I'm in a PHP environment or not.

Therefore, I've ported Drupal's _filter_autop() function to Python, and implemented it as a Django template filter. From the limited testing I've done, the function appears to be working just as well in Django as it does in Drupal. You can find the function below.

import re
from django import template
from django.template.defaultfilters import force_escape, stringfilter
from django.utils.encoding import force_unicode
from django.utils.functional import allow_lazy
from django.utils.safestring import mark_safe


register = template.Library()


def autop_function(value):
    """
    Convert line breaks into <p> and <br> in an intelligent fashion.
    Originally based on: http://photomatt.net/scripts/autop

    Ported directly from the Drupal _filter_autop() function:
    http://api.drupal.org/api/function/_filter_autop
    """

    # All block level tags
    block = '(?:table|thead|tfoot|caption|colgroup|tbody|tr|td|th|div|dl|dd|dt|ul|ol|li|pre|select|form|blockquote|address|p|h[1-6]|hr)'

    # Split at <pre>, <script>, <style> and </pre>, </script>, </style> tags.
    # We don't apply any processing to the contents of these tags to avoid messing
    # up code. We look for matched pairs and allow basic nesting. For example:
    # "processed <pre> ignored <script> ignored </script> ignored </pre> processed"
    chunks = re.split('(</?(?:pre|script|style|object)[^>]*>)', value)
    ignore = False
    ignoretag = ''
    output = ''

    for i, chunk in zip(range(len(chunks)), chunks):
        prev_ignore = ignore

        if i % 2:
            # Opening or closing tag?
            is_open = chunk[1] != '/'
            tag = re.split('[ >]', chunk[2-is_open:], 2)[0]
            if not ignore:
                if is_open:
                    ignore = True
                    ignoretag = tag

            # Only allow a matching tag to close it.
            elif not is_open and ignoretag == tag:
                ignore = False
                ignoretag = ''

        elif not ignore:
            chunk = re.sub('\n*$', '', chunk) + "\n\n" # just to make things a little easier, pad the end
            chunk = re.sub('<br />\s*<br />', "\n\n", chunk)
            chunk = re.sub('(<'+ block +'[^>]*>)', r"\n\1", chunk) # Space things out a little
            chunk = re.sub('(</'+ block +'>)', r"\1\n\n", chunk) # Space things out a little
            chunk = re.sub("\n\n+", "\n\n", chunk) # take care of duplicates
            chunk = re.sub('\n?(.+?)(?:\n\s*\n|$)', r"<p>\1</p>\n", chunk) # make paragraphs, including one at the end
            chunk = re.sub("<p>(<li.+?)</p>", r"\1", chunk) # problem with nested lists
            chunk = re.sub('<p><blockquote([^>]*)>', r"<blockquote\1><p>", chunk)
            chunk = chunk.replace('</blockquote></p>', '</p></blockquote>')
            chunk = re.sub('<p>\s*</p>\n?', '', chunk) # under certain strange conditions it could create a P of entirely whitespace
            chunk = re.sub('<p>\s*(</?'+ block +'[^>]*>)', r"\1", chunk)
            chunk = re.sub('(</?'+ block +'[^>]*>)\s*</p>', r"\1", chunk)
            chunk = re.sub('(?<!<br />)\s*\n', "<br />\n", chunk) # make line breaks
            chunk = re.sub('(</?'+ block +'[^>]*>)\s*<br />', r"\1", chunk)
            chunk = re.sub('<br />(\s*</?(?:p|li|div|th|pre|td|ul|ol)>)', r'\1', chunk)
            chunk = re.sub('&([^#])(?![A-Za-z0-9]{1,8};)', r'&amp;\1', chunk)

        # Extra (not ported from Drupal) to escape the contents of code blocks.
        code_start = re.search('^<code>', chunk)
        code_end = re.search(r'(.*?)<\/code>$', chunk)
        if prev_ignore and ignore:
            if code_start:
                chunk = re.sub('^<code>(.+)', r'\1', chunk)
            if code_end:
                chunk = re.sub(r'(.*?)<\/code>$', r'\1', chunk)
            chunk = chunk.replace('<\\/pre>', '</pre>')
            chunk = force_escape(chunk)
            if code_start:
                chunk = '<code>' + chunk
            if code_end:
                chunk += '</code>'

        output += chunk

    return output

autop_function = allow_lazy(autop_function, unicode)

@register.filter
def autop(value, autoescape=None):
    return mark_safe(autop_function(value))
autop.is_safe = True
autop.needs_autoescape = True
autop = stringfilter(autop)

Update (31 May 2010): added the "Extra (not ported from Drupal) to escape the contents of code blocks" part of the code.

To use this filter in your Django templates, simply save the code above in a file called autop.py (or anything else you want) in a templatetags directory within one of your installed apps. Then, just declare {% load autop %} at the top of your templates, and filter your markup variables with something like {{ object.content|autop }}.

Note that this is pretty much a direct port of the Drupal / PHP function into Django / Python. As such, it's probably not as efficient nor as Pythonic as it could be. However, it seems to work quite well. Feedback and comments are welcome.

]]>
Introducing GreenAsh 4 2010-05-25T00:00:00Z 2010-05-25T00:00:00Z Jaza https://greenash.net.au/thoughts/2010/05/introducing-greenash-4/ It's that time again. GreenAsh makeover time!

What can I say? It's been a while. I was bored. The old site and the old server were getting crusty. And my technology preferences have shifted rather dramatically of late. Hence, it is with great pride that I present to you the splendiferously jaw-dropping 4th edition of GreenAsh, my personal and professional web site all rolled into one (oh yes, I love to mix business and pleasure).

New design

The latest incarnation of GreenAsh sports a design with plenty of fresh goodness. I've always gone for quite a minimalist look with GreenAsh (mainly because my design prowess is modest to say the least). The new design is, in my opinion, even more minimalist than its predecessors. For example, I've gotten rid of the CSS background images that previously sat on the home page, and the new background header image is a far smaller and less conspicuous alternative than the giant fluoro-green monstrosity before it.

Front page of the new GreenAsh 4
Front page of the new GreenAsh 4

Elements such as dates submitted, tags, and authors are some of the finer details that have been tuned up visually for this edition. I've also added some very narrow sidebars, which are being used for these elements among other things. The thoughts index page now has a tag cloud.

I've tried to focus on the details rather than the devil. After all, that old devil likes to hang out in the details anyway. For example, the portfolio section has received a pretty extensive visual overhaul. Portfolio entries can now be browsed from a single page, using a funky jQuery-powered filtering interface. The experience degrades nicely without JavaScript, of course. The site's static informational pages — the services, about, and contact pages — have also had their details spruced up. These pages now have distinctively styled subtitles, stand-out text blocks, and call-to-action buttons.

I'd like to think of this edition as more of a realign than a redesign. I've maintained the main page area width of 740px, which existing content is already optimised for. However, the addition of the narrow sidebars has upped the overall width to 960px, and I'm now making use of the excellent 960 grid system for the overall layout. Thoughts are still fresh (on the front page). Colours haven't changed too radically. The new header is not all that dissimilar to the one before it. And so it goes on.

Crusty content purge

It's not just the design that's been cleaned up this time. I've also taken the liberty of getting rid of quite a lot of content (and functionality relating to that content), as part of this spring-clean.

In particular, the old GreenAsh resources library is no more. It was a key component of GreenAsh back in the day. Back in 2004, I'd just finished high school, I had a lot of fresh and useful study notes to share with the world, and I was working part-time as a tutor. But much has changed since then. I never ended up transferring all my notes into soft copy (although I did get through a surprising number of them). I've long retired from the tutoring business. My notes are out-of-date and of little use to anyone. Plus, I was sick of maintaining the convoluted registration and access-control system I'd set up for the resource library, and I was sick of whinging school-kids e-mailing me and complaining that they couldn't view the notes.

Speaking of registration, that's completely gone too. The only reason anyone registered was in order to access the resource library, so there's no point maintaining user accounts here anymore. All comments from registered users have been transformed into anonymous comments (with the author's name recorded in plain text).

The news archive is also history. I hardly ever posted news items. It was designed for announcing site news and such, but the truth is that most changes to the site aren't worth announcing, and/or aren't things that anyone besides myself would care about. Having scrapped the news section once and for all, I was also able to scrap the 'posts' section (which originally contained 'deals', 'thoughts', and 'news'), and to finally make thoughts a top-level section.

Some other crusty old bits of content, such as polls, image galleries, and a few random menu callbacks, are now also gone forever. I know the polls were kinda fun, but seriously — GreenAsh has grown up since its infancy, and pure gimmickry such as polls no longer has a place here.

The switch to Django

After being powered by Drupal for over 5 years, I made the big decision to re-develop GreenAsh in Django for this edition. The decision to switch was not an easy one, but it was one that I felt I had to make. Over the past year or so, I've been doing my professional coding work increasingly in Python/Django rather than in PHP/Drupal. On a personal level (related but separate), I've also been feeling increasingly disillusioned with PHP/Drupal; and increasingly excited by, and impressed with, Python/Django. In short, Drupal is no fun for me anymore, and Django feels like a breath of fresh air in a musty cellar.

This version of GreenAsh, therefore, is the first ever version that hasn't been built with PHP. GreenAsh is now powered by Django. The thoughts archive and the portfolio archive were migrated over from Drupal using a custom import script. Standard Django apps — e.g. comments, syndication, taggit, thumbnail — are being used behind the scenes (particularly to power the thoughts section). All is now being handled with Django models, views, and templates — not that much to talk about, really, as the back-end is by-and-large pretty simple for Django to handle. Although there are a few cool bits 'n' pieces at work, and I'll be blogging about them here as time permits.

Jaza's World, the Category Module web site, and Jaza's World Trip remain Drupal-powered as always. I have no plans to migrate them away from Drupal at this stage.

New server

The switch to Django also necessitated migrating GreenAsh to a new hosting solution. After 6 years on the same shared hosting account, GreenAsh has now (finally) moved to a VPS. All requests are now being served by Nginx as the first port of call, and most are then being proxied through to Apache. Having a VPS is pretty cool in general, because it means I have total control over the server, so I get to install whatever I want.

Loose ends

As always, this launch has seen the bulk of the changes on my to-do list done, but there are still plenty of loose ends to tie up. The thoughts index page needs a 'browse by year' block. Thought detail pages could use 'previous (newer) thought' and 'next (older) thought' links. The portfolio JavaScript browsing interface could use a few rough edges cleaned up, such as accordion opening-closing for each facet (year, type, paid), and fading instead of hiding/showing for transitions. The static informational pages should be editable as content blocks on the back-end (currently hard-coded in templates). The sitemap.xml and robots.txt files need to be restored. Some global redirection (e.g. from www to non-www) needs to take place. I need a smarter linebreak filter (one that's aware of big code blocks).

The new content could also still use work in some places. In particular, my portfolio is horrendously out-of-date, and I'll be updating it with my newer work as soon as I can. The 'about' page and the other static pages could still use a bit more work.

I hope you like the new GreenAsh. Relax, enjoy, and happy perusing.

]]>
Taking PHP Fat-Free Framework for a test drive 2010-05-06T00:00:00Z 2010-05-06T00:00:00Z Jaza https://greenash.net.au/thoughts/2010/05/taking-php-fat-free-framework-for-a-test-drive/ Fat-Free is a brand-new PHP framework, and it's one of the coolest PHP projects I've seen in quite a long time. In stark contrast to the PHP tool that I use most often (Drupal), Fat-Free is truly miniscule, and it has no plans to get bigger. It also requires PHP 5.3, which is one version ahead of what most folks are currently running (PHP 5.3 is also required by FLOW3, another framework on my test-drive to-do list). A couple of weeks back, I decided to take Fat-Free for a quick spin and to have a look under its hood. I wanted to see how good its architecture is, how well it performs, and (most of all) whether it offers enough to actually be of use to a developer in getting a real-life project out the door.

I'm going to be comparing Fat-Free mainly with Django and Drupal, because they're the two frameworks / CMSes that I use the most these days. The comparison may at many times feel like comparing a cockroach to an elephant. But like Django and Drupal, Fat-Free claims to be a complete foundation for building a dynamic web site. It wants to compete with the big boys. So, I say, let's bring it on.

Installation

Even if you're a full-time PHP developer, chances are that you don't have PHP 5.3 installed. On Windows, latest stable 5.3 is available to download as an auto-installer (just like latest stable 5.2, which is also still available). On Mac, 5.3 is bundled with Snow Leopard (OS 10.6), but only 5.2 is bundled with Leopard (10.5). As I've written about before, PHP on Mac has a lot of installation issues and annoyances in general. If possible, avoid anything remotely out-of-the-ordinary with PHP on Mac. On Ubuntu, PHP is not bundled, but can be installed with a one-line apt-get command. In Karmic (9.10) and earlier recent versions, the php5 apt package links to 5.2, and the php5-devel apt package links to 5.3 (either way, it's just a quick apt-get to install). In the brand-new Lucid (10.04), the php5 apt package now links to 5.3. Why do I know about installing PHP on all three of these different systems? Let's just say that if you previously used Windows for coding at home, but you've now switched to Ubuntu for coding at home, and you use Mac for coding at work, then you too would be a fruit-loop schizophrenic.

Upgrading from 5.2 to 5.3 shouldn't be a big hurdle for you. Unfortunately, I happened to be in pretty much the worst possible situation. I wanted to install 5.3 on Mac OS 10.5, and I wanted to keep 5.2 installed and running as my default version of PHP (because the bulk of my PHP work is in Drupal, and Drupal 6 isn't 100% compatible with PHP 5.3). This proved to be possible, but only just — it was a nightmare. Please, don't try and do what I did. Totally not worth it.

After I got PHP 5.3 up and running, installing Fat-Free itself proved to be pretty trivial. However, I encountered terrible performance when trying out a simple "Hello, World" demo, off the bat with Fat-Free (page loads of 10+ seconds). This was a disheartening start. Nevertheless, it didn't put me off — I tracked down the source of the crazy lag to a bug with Fat-Free's blacklist system, which I reported and submitted a patch for. A fix was committed the next day. How refreshing! Also felt pretty cool to be trying out a project where it's so new and experimental, you have to fix a bug before you can take it for a test drive.

Routing

As with every web framework, the page routing system is Fat-Free's absolute core functionality. Fat-Free makes excellent use of PHP 5.3's new JavaScript-like support for functions as first-class objects in its routing system (including anonymous functions). In a very Django-esque style, you can pass anonymous functions (along with regular functions and class methods) directly to Fat-Free's route() method (or you can specify callbacks with strings).

Wildcard and token support in routes is comparable to that of the Drupal 6 menu callback system, although routes in Fat-Free are not full-fledged regular expressions, and hence aren't quite as flexible as Django's URL routing system. There's also the ability to specify multiple callbacks/handlers for a single route. When you do this, all the handlers for that route get executed (in the order they're defined in the callback). This is an interesting feature, and it's actually one that I can think of several uses for in Django (in particular).

In the interests of RESTful-ness, Fat-Free has decided that HTTP request methods (GET, POST, etc) must be explicitly specified for every route definition. E.g. to define a simple GET route, you must write:

<?php
F3::route('GET /','home');
?>

I think that GET should be the default request method, and that you shouldn't have to explicitly specify it for every route in your site. Or (in following Django's "configuration over convention" rule, which Fat-Free also espouses), at least have a setting variable called DEFAULT_REQUEST_METHOD, which itself defaults to GET. There's also much more to RESTful-ness than just properly using HTTP request methods, including many aspects of the response — HTTP response codes, MIME types, and XML/JSON response formats spring to mind as the obvious ones. And Fat-Free offers no help for these aspects, per se (although PHP does, for all of them, so Fat-Free doesn't really need to).

Templates

Can't say that Fat-Free's template engine has me over the moon. Variable passing and outputting is simple enough, and the syntax (while a bit verbose) is passable. The other key elements (described below) would have to be one of Fat-Free's weaker points.

Much like Django (and in stark contrast to Drupal), Fat-Free has its own template parser built-in, and you cannot execute arbitrary PHP within a template. In my opinion, this is a good approach (and Drupal's approach is a mess). However, you can more-or-less directly execute a configurable subset of PHP core functions, with Fat-Free's allow() method. You can, for example, allow all date and pcre functions to be called within templates, but nothing else. This strikes me as an ugly compromise: a template engine should either allow direct code execution, or it shouldn't (and I'd say that it always shouldn't). Seems like a poor substitute for a proper, Django-style custom filter system (which Fat-Free is lacking). Of course, Django's template system isn't perfect, either.

Fat-Free's template "directives" (include, exclude, check, and repeat) have an ugly, XML-style syntax. Reminds me of the bad old XTemplate days in Drupal theming. This is more a matter of taste, but nevertheless, I feel that the reasoning behinnd XML-style template directives is flawed (allows template markup to be easily edited in tools like Dreamweaver … *shudder*), and that the reasoning behind custom-style template directives is valid (allows template directives to be clearly distinguished from markup in most good text editors). What's more, the four directives are hard-coded into Fat-Free's serve() function — no chance whatsoever of having custom directives. Much like the function-calling in templates, this seems like a poor substitue for a proper, Django-style custom tag system.

ORM

Straight off the bat, my biggest and most obvious criticism of Axon, the Fat-Free ORM, is that it has no model classes as such, and that it has no database table generation based on model classes. All that Axon does is generate a model class that corresponds to a simple database table (which it analyses on-the-fly). You can subclass Axon, and explicitly define model classes that way — although with no field types as such, there's little to be gained. This is very much Axon's greatest strength (so simple! no cruft attached!) and its greatest weakness (makes it so bare-bones, it only just meets the definition of an ORM). Axon also makes no attempt to support relationships, and the front-page docs justify this pretty clearly:

Axon is designed to be a record-centric ORM and does not pretend to be more than that … By design, the Axon ORM does not provide methods for directly connecting Axons to each other, i.e. SQL joins – because this opens up a can of worms.

Axon pretty much does nothing but let you CRUD a single table. It can be wrangled into doing some fancier things — e.g. the docs have an example of creating simple pagination using a few lines of Axon code — but not a great deal. If you need more than that, SQL is your friend. Personally, I agree with the justification, and I think it's a charming and well-designed micro-ORM.

Bells and whistles

  • Page cache: Good. Just specify a cache period, in seconds, as an argument to route(). Pages get cached to a file server-side (by default — using stream wrappers, you could specify pretty much any "file" as a cache source). Page expiry also gets set as an HTTP response header.
  • Query cache: Good. Just specify a cache period, in seconds, when calling sql(). Query only gets executed once in that time frame.
  • JS and CSS compressor: Good. Minifies all files you pass to it. Drupal-style.
  • GZip: all responses are GZipped using PHP's built-in capabilities, whenever possible. Also Drupal-style.
  • XML sitemap: Good. Super-light sitemap generator. Incredible that in such a lightweight framework, this comes bundled (not bundled with Drupal, although it is with Django). But, considering that every site should have one of these, this is very welcome indeed.
  • Image resizing: Good. Drupal 7 will finally bundle this (still an add-on in Django). This is one thing, more than perhaps anything else, that gets left out of web frameworks when it shouldn't be. In Fat-Free, thumb() is your friend.
  • HTTP request utility: Good. Analogous to drupal_http_request(), and similar stuff can be done in Django with Python's httplib/urllib. Server-side requests, remote service calls, here we come.
  • Static file handler: Good. Similar to Drupal's private file download mode, and (potentially) Django's static media serving. Not something you particularly want to worry about as a developer.
  • Benchmarking: Good. profile() is your friend. Hopefully, your Fat-Free apps will be so light, that all this will ever do is confirm that everything's lightning-fast.
  • Throttle: Good. This was removed from Drupal core, and it's absent entirely from Django. Another one of those things that you wouldn't be thinking about for every lil web project, but that could come in pretty handy for your next DDoS incident.
  • Unit testing: Good. This framework is tiny, but it still has pretty decent unit test support. In contrast to the Drupal 6 to 7 bloat, this just goes to show that unit testing support doesn't have to double your framework's codebase.
  • Debug / production modes: Good. For hiding those all-too-revealing error messages, mainly.
  • Error handling: Good. Default 404 / etc callback, can be customised.
  • Autoload: OK. Very thin wrapper around PHP 5.3's autoloading system. Not particularly needed, since autoload is so quick and easy anyway.
  • Form handler: OK. Basic validation system, value passing system, and sanitation / XSS protection system. Nice that it's light, but I can't help but yearn for a proper API, like what Drupal or Django has.
  • Captcha: OK. But considering that the usefulness and suitability of captchas is being increasingly questioned these days, seems a strange choice to include this in such a lightweight framework. Not bundled with Drupal or Django.
  • Spammer blacklisting: Seems a bit excessive, having it built-in to the core framework that all requests are by default checked against a third-party spam blacklist database. Plus, wasn't until my patch that the EXEMPT setting was added for 127.0.0.1. Nevertheless, this is probably more of a Good Idea™ than it is anything bad.
  • Fake images: Gimmick, in my opinion. Useful, sure. But really, creating a div with fixed dimensions, specifying fixed dimensions for an existing image, or even just creating real images manually — these are just some of your other layout testing options available. Also, you'll want to create your own custom 'no image specified' image for most sites anyway.
  • Identicons: Total gimmick. I've never built a site with these (actually, I've never even heard the word 'identicon' before). Total waste of 134 lines of code (but hey, at least it's only 134 — after all, this is Fat-Free).

What's missing?

Apart from the issues that I've already mentioned about various aspects of Fat-Free (e.g. with the template engine, with the form handler, with the ORM), the following things are completely absent from Fat-Free, and they're present in both Drupal and Django, and in my opinion they're sorely missed:

  • Authentication
  • Session management
  • E-mail sending utility
  • File upload / storage utility
  • Link / base URL / route reverse utility
  • CSRF protection
  • Locales / i18n
  • Admin interface
  • RSS / Atom

The verdict

Would I use it for a real project? Probably not.

I love that it's so small and simple. I love that it assists with so many useful tasks in such a straightforward way.

But.

It's missing too many things that I consider essential. Lack of authentication and session management is a showstopper for me. Sure, there are some projects where these things aren't needed at all. But if I do need them, there's no way I'm going to build them myself. Not when 10,000 other frameworks have already built them for me. Same with e-mail sending. No way that any web developer, in the year 2010, should be expected to concern his or her self with MIME header, line ending, or encoding issues.

It's not flexible or extensible enough. A template engine that supports 4 tags, and that has no way of supporting more, is really unacceptable. An ORM that guesses my table structure, and that has no way of being corrected if its guess is wrong, is unacceptable.

It includes some things that are just stupid. I'm sorry, but I'd find it very hard to use a framework that had built-in identicon generation, and to still walk out my front door every day and hold my head up proudly as a mature and responsible developer. OK, maybe I'm dramatising a bit there. But, seriously … do I not have a point?

Its coding style bothers me. In particular, I've already mentioned my qualms re: the XML-style templating. The general PHP 5.3 syntax doesn't particularly appeal to me, either. I've been uninspired for some time by the C++-style :: OO syntax that was introduced in PHP 5.0. Now, the use of the backslash character as a namespace delimiter is the icing on the cake. Yuck! Ever heard of the dot character, PHP? They're used for namespaces / packages in every other programming language in the 'hood. Oh, that's right, you can't use the dot, because it's your string concatenation operator (gee, wasn't that a smart move?). And failing the dot, why the backslash? Could you not have at least used the forward slash instead? Or do you prefer specifying your paths MS-DOS style? Plus the backslash is the universal escaping operator within string literals.

I'm a big fan of the new features in PHP 5.3. However, that doesn't change the fact that those features have already existed for years in other languages, and with much more elegant syntax. I've been getting much more into Python of late, and having become fairly accustomed by now with that elusive, almost metaphysical ideal of "Pythonic code", what I've observed with PHP 5.3 in Fat-Free is really not impressing me.

]]>
Refugee Buddy: a project of OzSiCamp Sydney 2010 2010-03-10T00:00:00Z 2010-03-10T00:00:00Z Jaza https://greenash.net.au/thoughts/2010/03/refugee-buddy-a-project-of-ozsicamp-sydney-2010/ Last weekend, I attended Social Innovation Camp Sydney 2010. SiCamp is an event where several teams have one weekend in which to take an idea for an online social innovation technology, and to make something of it. Ideally, the technology gets built and deployed by the end of the camp, but if a team doesn't reach that stage, simply developing the concept is an acceptable outcome as well.

I was part of a team of seven (including our team leader), and we were the team that built Refugee Buddy. As the site's slogan says: "Refugee Buddy is a way for you to welcome people to your community from other cultures and countries." It allows regular Australians to sign up and become volunteers to help out people in our community who are refugees from overseas. It then allows refugee welfare organisations (both governmnent and independent) to search the database of volunteers, and to match "buddies" with people in need.

Of the eight teams present at this OzSiCamp, we won! Big congratulations to everyone on the team: Oz, Alex, James, Daniela, Tom, (and Jeremy — that's me!) and most of all Joy, who came to the camp with a great concept, and who provided sound leadership to the rest of us. Personally, I really enjoyed working on Refugee Buddy, and I felt that the team had a great vibe and the perfect mix of skills.

OzSiCamp Sydney 2010 was the first "build a site in one weekend" event in which I've participated. It was hectic, but fun. I may have overdosed on Mentos refreshments on the Saturday night (in fact, I never want to eat another Mentos again). All up, I think it was a great experience — and in our case, one with a demonstrable concrete result — and I hope to attend similar events in the future.

For building Refugee Buddy, our team decided to use Django, a Python web framework. This was basically the decision of Oz and myself: we were the two programmers on the team; and we both have solid experience with developing sites in Django, primarily from using it at Digital Eskimo (where we both work). Oz is a Django junkie; and I've been getting increasingly proficient in it. Other teams built their sites using Ruby on Rails, Drupal, MediaWiki, and various other platforms.

Going with a Django team rather than a Drupal team (and pushing for Django rather than Drupal) was a step in a new direction for me. It surprised my fellow members of the Sydney Drupal community who were also in attendance. And, to tell the truth, I also surprised myself. Anyway, I think Django was a superior match for the project compared to Drupal, and the fact that we were able to build the most fully-functioning end product out of all the teams, pretty much speaks for itself.

Refugee Buddy is an open source project, and the full code is available on GitHub. Feel free to get involved: we need design and dev help for the long-term maintenance and nurturing of the site. But most of all, I encourage you all to visit Refugee Buddy, and to sign up as a buddy.

]]>
GreenAsh 3.0: how did they do it? 2006-10-04T00:00:00Z 2006-10-04T00:00:00Z Jaza https://greenash.net.au/thoughts/2006/10/greenash-3-0-how-did-they-do-it/ After much delay, the stylish new 3rd edition of GreenAsh has finally hit the web! This is the first major upgrade that GreenAsh has had in almost 2 years, since it was ported over from home-grown CMS (v1) to Drupal (v2). I am proud to say that I honestly believe this to be the cleanest (inside and out), the most mature, and the sexiest edition to date. And as you can see, it is also one of the biggest upgrades that the site has ever had, and possibly the last really big upgrade that it will have for quite some time.

The site has been upgraded from its decaying and zealously hacked Drupal 4.5 code base, to the latest stable (and much-less-hacked) 4.7 code base. This fixes a number of security vulnerabilities that previously existed, as well as bringing the site back into the cutting edge of the Drupal world, and making it compatible with all the latest goodies that Drupal has to offer.

New theme

GreenAsh 3.0 sports a snazzy new theme, complete with fresh branding, graphics, and content layout. The new theme is called Lorien, and as with previous site designs, it gives GreenAsh a public face using nothing but accessible, validated, and standards-compliant markup:

GreenAsh 3.0
GreenAsh 3.0

GreenAsh 2.0 was styled with the Mithrandir theme:

GreenAsh 2.0
GreenAsh 2.0

And GreenAsh 1.0, which was not Drupal-powered, did not technically have a 'theme' at all; but for historical purposes, let's call its design the GreenAsh theme:

GreenAsh 1.0
GreenAsh 1.0

New modules

GreenAsh 3.0 is also using quite a few new modules that are only available in more recent versions of Drupal. The views module is being used to generate custom node listings in a number of places on the site, including for all of the various bits that make up the new front page, and also for the revamped recent posts page. The pathauto module is now generating human-readable URLs for all new pages of the site, in accordance with the site's navigation structure. Because the URL format of the site's pages has changed, a number of old URLs are now obselete, and these are being redirected to their new equivalents, with the help of the path redirect module.

The improved captcha module is providing some beginner-level maths questions to all aspiring new users, anonymous commenters, and anonymous contact form submitters, and is proving to be an effective combatant of spam. Also, the very nifty nice menus module is being used for administrator-only menus, to provide a simple and unobtrusive navigational addition to the site when needed.

Best of all, the site has finally been switched over to the category module, which was built and documented by myself, and which had the purpose from the very beginning of being installed right here, in order to meet the hefty navigational and user experience demands that I have placed upon this site. Switching to the category module was no small task: it required many hours of careful data migration, site re-structuring, and menu item mayhem. I certainly do not envy anyone who is making the switch from a taxonomy or book installation to a category module installation. But, despite the migration being not-for-the-faint-hearted, everything seems to be running smoothly now, and the new system is providing a vastly improved end-user and site administrator experience.

New user experience

I have implemented a number of important changes to the user experience of the site, which will hopefully make your visit to the site more enjoyable and useful:

  • Re-designed front page layout
  • Switch to a simple and uncluttered 1-column layout for all other pages
  • New category-module-powered navigation links and table-of-contents links
  • Automatic new user account approval after 72 hours
  • Removed a number of pages from the site's hierarchy, to reduce unnecessary information overload

Ongoing effort

Obviously, there are still a number of loose ends to tie up. For example, the content on some pages may still require updating. Additionally, some pages are still yet to be added (such as the listings in the sites folio). Further tweaks and adjustments will probably also be made to the look and feel of the site in the near future, to continue to improve it and to make it distinctly branded.

Feedback, comments, suggestions, criticisms, cookies, cakes, donations, and lingerie are all welcome to be thrown my way. Throwing apple cores is discouraged: if you think the new site stinks that badly, why not be constructive and throw a can of deodorant instead? Anyway, I really do hope you all like the site post-upgrade, and I wish you all the best in your exploration of the new and improved GreenAsh 3.0!

]]>
Drupal lite: Drupal minus its parts 2006-06-28T00:00:00Z 2006-06-28T00:00:00Z Jaza https://greenash.net.au/thoughts/2006/06/drupal-lite-drupal-minus-its-parts/ It is said that a house is the sum of its parts. If you take away all the things that make it a house, then it is no longer a house. But how much can you remove, before it can no longer be called a house? This is an age-old question in the school of philosophy, and it is one for which every person has a different answer. If you take away the doors, the windows, the roof, the floorboads, the inside walls, the power lines, and the water pipes, is it still a house? In developing Drupal Lite, I hope to have answered this question in relation to Drupal. What are the absolute essentials, without which Drupal simply cannot be called Drupal? If you remove nodes, users, and the entire database system from Drupal, is it still Drupal?

Drupal Lite is, as its name suggests, a very lightweight version of Drupal, that I whipped up in about two hours last night. I developed it because I've been asked to develop a new site, which will consist mainly of static brochureware pages, with a contact form or two, and perhaps a few other little bits of functionality; but which will have to run with no database. Of course, I could just do it the ol' fashioned way, with static HTML pages, and with a CGI script for the contacts forms. Or I could whip up some very basic PHP for template file inclusion.

But muuum, I wanna use Druuupal!

Too bad, right? No database, no Drupal - right? Wrong.

Drupal Lite would have to be the lightest version of Drupal known to man. It's even lighter than what I came up with the last time that I rewrote Drupal to be lighter. And unlike with my previous attempt, I didn't waste time doing anything stupid, like attempting to rewrite Drupal in an object-oriented fashion. In fact, I barely did any hacking at all. I just removed an enormous amount of code from Drupal core, and I made some small modifications to a few little bits (such as the module loader, and the static variable system), to make them database-unreliant.

This has all been done for a practical purpose. But it brings up the interesting question: just how much can you remove from Drupal, before what you've got left can no longer be called Drupal? The answer, in my opinion, is: lots. Everything that is database-reliant has been removed from Drupal Lite. This includes: nodes; users; blocks; filters; logs; access control (for all practical purposes); caching (at present - until file-based caching gets into core); file management; image handling; localization; search; URL aliasing (could have been rewritten to work with conf variables - but I didn't bother); and, of course, the DB abstraction system itself.

This proves, yet again, that Drupal is a hacker's paradise. It really is so solid - you can cut so much out of it, and if you (sort of) know what you're doing, it still works. There are just endless ways that you can play with Drupal, and endless needs that you can bend it to.

So what's left, you ask? What can Drupal still do, after it is so savagely crippled, and so unjustly robbed of many of its best-known features? Here's what Drupal Lite can offer you:

  • Persistent variables (by setting the $conf variable in your settings.php file)
  • Module loading
  • Hook system
  • Menu callback
  • Theming
  • Forms API (although there's not much you can do with submitted form data, except for emailing it, or saving it to a text file)
  • Clean URLs
  • Breadcrumbs
  • User messages
  • 404 / 403 handling
  • Error handling
  • Page redirection
  • Site offline mode
  • Basic formatting, validation, filtering, and security handling
  • Link and base URL handling
  • Unicode handling
  • Multisite setup (in theory - not tested)

This is more than enough for most brochureware sites. I also wrote a simple little module called 'static', that lets you define menu callbacks for static pages, and to include the content of such pages automatically, from separate template-ish files. This isn't as good as pages that are editable by non-geeky site admins (for which you need the DB and the node system), but it still allows you to cleanly define/write the content for each page; and the content of the template file for each page is virtually identical to the content of a node's body, meaning that such pages could easily be imported into a real Drupal site in future.

Speaking of which, compatibility with the real Drupal is a big feature of Drupal Lite. Any modules that are developed for Drupal Lite should work with Drupal as well. PHPTemplate (or other) themes written for Drupal Lite should work with a real Drupal site, except that themes don't have any block or region handling in Drupal Lite. Overall, converting a Drupal Lite site to the Real Deal™ should be a very easy task; and this is important for me, since I'd probably do that conversion for the site I'll be working on, if more server resources ever become available.

Drupal Lite is not going to be maintained (much), and I most certainly didn't write it as a replacement for Drupal. I just developed it for a specific need that I have, and I'm making it publicly available for anyone else who has a similar need. Setting up Drupal Lite basically consists of setting a few $conf values in settings.php, and creating your static page template files.

If you're interested, download Drupal Lite and have a peek under the hood. The zipped file is a teeny 81k - so when you do look under the hood, don't be surprised at how little you'll find! Otherwise, I'd be interested to just hear your thoughts.

A note to my dear friend Googlebot: in your infinite wisdom, I hope that you'll understand the context in which I used the phrase "hacker's paradise" in the text above. Please forgive me for this small travesty, and try to avoid indexing this page under the keyword "Windows 98". ;-)

]]>
An IE AJAX gotcha: page caching 2006-03-13T00:00:00Z 2006-03-13T00:00:00Z Jaza https://greenash.net.au/thoughts/2006/03/an-ie-ajax-gotcha-page-caching/ While doing some AJAX programming, I discovered a serious and extremely frustrating bug when using XMLHTTP in Internet Explorer. It appears that IE is prone to malfunctioning, unless a document accessed through AJAX has its HTTP header set to disallow caching. Beware!

I was working on the Drupal activeselect module, which allows one select box on a form to update the options in another form dynamically, through AJAX. I was having a very strange problem, where the AJAX worked fine when I first loaded the page in IE, but then refused to work properly whenever I refreshed or re-accessed the page. Only closing and re-opening the browser window would make it work again. Past the first time / first page load, everything went haywire.

I was tearing my hair out trying to work out the cause of this problem. I was searching on Google for anything I could think of to help me solve the problem. One page suggested setting all JavaScript variables that reference page elements to null (Google cache version), as soon as the user leaves the page. I tried this: no effect.

Another page led me to believe that changing the encoding from UTF-8 to iso-8859-1 (Google HTML version) would end my woes. Once again, no cigar.

Finally, I found a page suggesting that I set the HTTP response headers to disallow caching. It worked! My beautiful AJAX is now working in IE, just as well as it is working in decent browsers (i.e. Firefox et al). What I did was put the following response headers in the page (using the PHP header() function):

<?php header("Expires: Sun, 19 Nov 1978 05:00:00 GMT");
header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
header("Cache-Control: no-store, no-cache, must-revalidate");
header("Cache-Control: post-check=0, pre-check=0", false);
header("Pragma: no-cache");
?>

These are the same response headers that the Drupal core uses for cached pages (that's where I copied them from). Evidently, when IE is told to invoke an AJAX HTTP request on a page that it thinks it should 'cache', it simply can't handle it.

So, for anyone else that finds that their AJAX code is bizarrely not working in IE after the first page load, this may be the answer! May this post end your troubles sooner rather than later.

]]>
Web 2.0, and other nauseating buzzwords 2005-10-01T00:00:00Z 2005-10-01T00:00:00Z Jaza https://greenash.net.au/thoughts/2005/10/web-2-0-and-other-nauseating-buzzwords/ Attending the Web Essentials 2005 conference (others' thoughts on ) was the best thing I've done this year. I'm not kidding. The Navy SEALs, the heart surgeons, and the rocket scientists (i.e. the best of the best) in web design all spoke there. Among my favourites were: Tantek Çelik, the creator of the famous Box Model hack (a.k.a. the Tan hack) and markup guru; Eric CSS Meyer (his middle initials speak for themselves); Jeffrey Veen, whose partner Jesse James Garrett coined the 2005 Acronym of the Year (AJAX), and who is one of the more enthusiastic speakers I've ever heard; and Doug Bowman, who is blessed with an artistic talent, that he couples with a devotion to web standards, and with a passionate sense of vision.

Since Jakob Nielsen was absent, one thing I didn't get out of the conference was a newfound ability to write short sentences (observe above paragraph). :-)

But guys, why did you have to overuse that confounded, annoying buzzword Web 2.0? Jeff in particular seemed to really shove this phrase in our faces, but I think many of the other speakers did also. Was it just me, or did this buzzword really buzz the hell out of some people? I know I'm more intolerant than your average geek when it comes to buzzwords, but I still feel that this particular one rates exceptionally poor on the too much marketing hype to handle scale. It's so corny! Not to mention inaccurate: "The Web™" isn't something that's "released" or packaged in nice, easy-to-manage versions, any more than it's a single technology, or even (arguably) a single set of technologies.

AJAX I can handle. It stands for something. It's real. It's cool. "Blog" I can handle (ostensibly this is a "blog entry" - although I always try to write these thoughts as formal articles of interest, rather than as mere "today I did this..." journal entries). It's short for "web log". That's even more real, and more cool. "Podcast" I can tolerate. It's a fancy hip-hop way of saying "downloadable audio", but I guess it is describing the emerging way in which this old technology is being used. But as for ye, "Web 2.0", I fart in your general direction. The term means nothing. It represents no specific technology, and no particular social phenomenon. It's trying to say "we've progressed, we're at the next step". But without knowing about the things it implies - the things that I can handle, like RSS, CSS, "The Semantic Web", and Accessibility - the phrase itself is void.

Most of all, I can't handle the undertone of "Web 2.0" - it implies that "we're there" - as if we've reached some tangible milestone, and from now on everything's going to be somehow different. The message of this mantra is that we've been climbing a steep mountain, and that right now we're standing on a flat ledge on the side of the mountain, looking down at what we've just conquered. This is worse than void, it is misleading. We're not on a ledge: there are no ledges! We're on the same steep mountainside we've been on for the past 10 years. We can look down at any old time, and see how far we've come. The point we're at now is the same gradient as the rest of the mountain.

And also (back to WE05), what's with the MacOcracy? In the whole two days of this conference, scarcely a PC was to be seen. Don't get me wrong, I'm not voicing any anxious concern as to why we web developers aren't doing things the beloved Microsoft way. I have as little respect for Windows, et al. as the next geek. But I still use it. Plenty of my friends (equally geeky) are also happy to use it.

I've always had some "issues" with using Mac, particularly since the arrival of OS X. Firstly, my opinion is that Mac is too user-friendly for people in the IT industry. Aren't we supposed to be the ones that know everything about computers? Shouldn't we be able to use any system, rather than just the easiest and most usable system available? But hey, I guess a lot of web designers really are just that - designers - rather than actual "IT people". And we all know how designers love their Macs.

Secondly, Macs have increasingly become something of a status symbol and a fashion icon. To be seen with a Mac is to be "hip". It's a way of life: having an iBook, an iPod, an iCal. Becoming an iPerson. Well, I get the same nauseous feeling - the same gut reaction that is a voice inside me screaming "Marketing Hype!" - whenever I hear about the latest blasted iWhatever. Mac has been called the "BMW" of Operating Systems. What kind of people drive BMWs? Yeah, that's right - do you want to be that kind of person? I care a lot about not caring about that. All that image stuff. Keeping away from Macs is a good way to do that.

Lastly (after this, I'm done paying out Macs, I promise!), there's the whole overdone graphical slickness thing in OS X. The first time I used the beloved "dock" in Mac OS X, I nearly choked on my disgust. Talk about overcapitalisation! Ever hear the joke about what happened when the zealot CEO, the boisterous marketing department, and the way-too-much-time-on-their-hands graphics programmers got together? What happened was the OS X dock! Coupled with the zip-away minimising, the turning-cube login-logout, and all the rest of it, the result is an OS that just presents one too many animations after another!

Maybe I just don't get it. Sorry, strike that. Definitely I don't get it. Buzzwords, shiny OSes, all that stuff - I thought web development was all about semantics, and usability, and usefulness - the stuff that makes sense to me. Why don't you just tell me to go back to my little corner, and to keep coding my PHP scripts, and to let the designers get on with their designing, and with collecting their well-designed hip-hop gadgets. Which I will do, gladly.

Anyway, back to the conference. I discovered by going to Web Essentials that I am in many ways different to a lot of web designers out there. In many other ways, I'm also quite similar. I share the uncomfortable and introverted character of many of my peers. We share a love of good, clean, plain text code - be it programming or markup - and the advantages of this over binary formats. We share a love of sometimes quirky humour. We share the struggle for simplicity in our designs. We share the desire to learn from each other, and consequentially we share each others' knowledge. We share, of course, a love of open standards, and of all the benefits that they entail. And we share a love of food, in high quality as well as high quantity. We share the odd drink or 12 occasionally, too.

]]>