Save BBC 6Music and Asian Network
Everyone in a particular demographic will now know that the BBC director general, Mark Thompson, has announced a plan to close two of the most Reithian of radio stations, 6Music and the Asian Network. If you’re up in arms about these closures, you can still do something about it.
Kate Butler has an excellent eight-point plan. You don’t have to complete all eight points: this is not a competition. More specifically, J Hunt has a basic pro forma letter and two email addresses you can send it to. I recommend you actually send it to both, as you won’t get a reply from the “srconsultation” one.
In the spirit of not sending anything entirely pro forma, I’ve written my own as an open letter below (and of course emailed it to the relevant contacts). Feel free to rewrite for your own use.
Subject: Response to Mark Thompson’s announcements – NO to closing 6Music and Asian Network
To whom it may concern,
I would like to address proposals announced on March 2 2010 by the BBC director general Mark Thompson. These suggest the closure of BBC 6Music and the BBC Asian Network, among many other changes.
I am a loyal supporter of the BBC and of the licence fee. I believe that the BBC’s television, radio and multimedia outlet easily justifies the licence fee, and moreover that the two stations earmarked for closure in the proposals are exactly what the BBC is about and what make the BBC worth its fees.
Each station fulfils a remit that commercial broadcasting simply cannot and will not provide for. The stations cater for “sizeable minorities”, which commercial media ignores in favour of massive homogeneities. 6Music’s audience are committed, involved, interested listeners who get a lot out of the service, whereas a large commercial station (or even BBC1) simply does not have that audience buy-in. 6Music, the Asian Network and catering for other “sizeable minorities” constitute the most direct routes to the very heart of Reithian, socially active, life-changing public-service broadcasting. 6Music supports live music and niche genres, and provides an access to the BBC’s archive of recordings, thus offering a service not just for listeners but also for the UK’s arts and culture, for which no equivalent commercial alternative exists.
Having said that, I would certainly not recommend the closure of any other part of the BBC in the place of these stations: I am not writing to you to merely express a preference as to where the axe should fall (although I am myself an avid listener to 6Music.) In an ideal world I would rather there be no axe at all: through ringfenced experimentation and risk-taking the BBC has always fostered new talent and fulfilled social functions that again no commercial broadcaster would care about. Any closure at the BBC makes me, a BBC fan, unhappy.
Yet if cost-cutting is essential, then what’s the point in cutting such vital, heartland services for the sake of some £7m and £21m respectively? Such sweeping cuts make for great soundbites, but when BBC1 has a budget of £1400m, and £600m is being brought back into media production by the closure of online services, then it would surely represent far less of a wrench for £30m of savings to come from that £2000m total – around one percent of that total! – rather than getting rid of 100% of the 6Music and the Asian Network channels.
If the BBC can work with its audiences of “sizeable minorities”, rather than neglecting them when the time comes for cuts; if the return of £600m into programme-making can be visibly demonstrated to float 6Music, the Asian Network and even other digital channels; if the BBC Trust rejects the proposals to close these passionately loved stations; then it will find that the listeners to these stations will be active and reliable supporters of the BBC for years to come.
I therefore strongly urge the BBC Trust to reject these proposals, for the good of musicians, of the BBC and of the listening public.
Best regards,
J-P Stacey
Automated backups to S3
Simon Willison put together an excellent short-and-simple backup script over a year ago now, and I’ve used it intermittently to make backups. It takes files, whole directories, or the output of shell commands, and wraps it all up into a datestamped gzip file, before sending the whole thing up to an Amazon S3 account. It’s a nice little piece of kit, in other words.
I found that it worked mostly fine, although it was (a) fiddly to run as a cron and (b) threw occasional errors which I wanted to fix. These two things together meant I wanted to do some proper development on the script and keep track of it.
I wrote a wrapper to help setting up cronjobs, which means it can be scripted using config files in the user’s home directory and . I then decided on a whim to commit all of this to github , which is the online home for a community that’s been set up around the git versioning tool. As befits a DVCS tool, git has spawned a community which is multithreaded and agile and most of all very friendly, with all free repositories available to the public and everyone able to fork off everyone else.
The repository was still a bit empty, as out of respect for the original coder I didn’t commit backup_to_s3.py with everything else. Then, very kindly, Simon agreed to let me bundle his script with everything else, so you can now check out (clone) the “Configured S3 Backups” repository and follow the README to get scripted backups up and running fairly speedily.
I’d love for people to give it a try and offer some feedback. I definitely want to squash bugs and would also welcome new functionality. But the joy of git and github is that if I don’t want to implement any extras, then other programmers can go ahead and
In summary? Configured S3 Backups: give the script a try. Git: give DVCS a try. And github: give DVCS-based online community building a try. Message ends.
Change of plan at OGN16
There’s been a change in the speakers for Oxford Geek Night 16 on Wednesday 17 February. We’re really happy that Chris Thorpe will still be giving his keynote talk, about how social media is all about the social and not about the media. Alongside Chris’ talk will be a keynote from Oxford’s very own Garrett Coakley, who will be talking about how to run a successful online community.
Garrett’s well placed to give this talk, having been an administrator of evolt.org and instrumental in the now very successful Oxford Flickr group, which has recently exhibited in central Oxford. He’s also fresh from discussing these very issues at the Leeds GeekUp in 20/20 form, so it should be pretty exciting. If not, then I know where he lives.
Brillskills. OGN16 is shaping up to be pretty darn fine, though I do say so myself. Be there, or don’t, fool.
Oxford Geek Night 16 date confirmed
After a slightly longer break between Oxford Geek Nights to make room for Christmas and the rest, I’m pleased to announce the sixteenth Oxford Geek Night will be on Wednesday 17 February, 2010. That’s next year! Well, in just under two months’ time.
Keynote speakers are confirmed: OGN16 sees the return of microslotter Andrew Katz, lawyer at Moorcrofts Corporate Law and open-source specialist; he’ll be joined by Chris Thorpe, founder of Jaggeree. We hope to have more details about the talks soon.
We’ve already got some microslots lined up for OGN16, but if you’re interested in doing a microslot then submit your suggestion here. Microslots are five minutes long and the OGN crowd is very welcoming to anyone who can stand up and talk for that long!
Right. Now that’s all finalized, I’m off to have a celebratory sherry and listen to some Christmas songs.
Render any block in Drupal 5
Blocks aren’t what you might call “first-class citizens” in Drupal 5 or 6 (or 7, as far as I’m aware). Block functionality is provided by a range of functions and database tables, but ultimately there’s no thingness, no Ding an sich tying them together as an object the way that a node of content or a user might be.
Annoyingly, there’s no function in Drupal 5 to grab a block and render it. You can render a region, a block container which then makes a number of decisions for you about what blocks should appear in it. Blocks can in principle be uniquely identified by (a) the module which provides the block through its implementation of hook_block() and (b) the “delta”, the unique (potentially non-numeric) ID for each block in a given module. Yet there are no functions in the core block module to handle this.
One option is to move to Panels, which I’m doing for this site. Yet if your existing site is heavily based around blocks and regions, implementing Panels can be a lot of overhead just to render a single block in a template.
Here’s a function which returns a single block, given the module name and block delta. It’s an abstraction from block_list(), the function which returns all the blocks in a given region. You can put this in a shell module with an info file (see the previous post on theme preprocess hooks in Drupal 5 for how to quickly set up an otherwise empty module) or if you’re not comfortable with that you can always name it accordingly and put it in template.php
/** * Get a single block for themeing */ function mymodule_get_block($module, $delta) { // User roles mean blocks are still invisible if the user // isn't permitted to see them global $user; $rids = array_keys($user->roles); $placeholders = implode(',', array_fill(0, count($rids), '%d')); // This is still cropped from block_list() , but newlines added // for legibility and blogposting $result = db_query( "SELECT DISTINCT b.* " . " FROM {blocks} b LEFT JOIN {blocks_roles} r " . " ON b.module = r.module AND b.delta = r.delta " . " WHERE (r.rid IN ($placeholders) OR r.rid IS NULL) " . " AND b.module = '%s' " . " AND b.delta = '%s' " . " ORDER BY b.region, b.weight, b.module", array_merge($rids, array($module,$delta))); // Assemble block info from the module while ($block = db_fetch_object($result)) { // Invoke the block hook in the supporting // module and convert the return array $array = module_invoke($block->module, 'block', 'view', $block->delta); if (isset($array) && is_array($array)) { foreach ($array as $k => $v) { $block->$k = $v; } } // Swap in any user-defined title in admin interface if ($block->title) { $block->subject = $block->title == '<none>' ? '' : check_plain($block->title); } return $block; } }
Once this is safely out of the way you can grab and render a block. The following code grabs the fourth block that you ever created through the block admin interface (the “block” module is the “maintainer” of those blocks, and the blocks are numbered starting at zero, hence “3″):
<?php print theme('block', mymodule_get_block('block', 3)); ?>
This is a bit unsafe, though. What if you disable mymodule? The template will break, and depending on your server configuration your visitors might see a pretty ugly error. So use the theme preprocess hooks trick. If you want the block to show in node.tpl.php then you’re going to have to bite the bullet and put the bigger code snippet above in a module. Then, in the same module, you can write a hook_preprocess_node as follows:
function mymodule_preprocess_node(&$vars) { $vars['myblock'] = theme('block', mymodule_get_block('block', 3)); }
Then you can put just the bare variable in your node.tpl.php, which is much safer:
<?php print $myblock ?>
Turn off mymodule, and this just prints an empty string.
So, ta-da, blocks are now better exposed in your Drupal backend, for you to use in different places in your themeing. Unfortunately that still doesn’t make them first-class citizens: they can’t be categorized or given an “owner” i.e. an associated user ID; they can’t be made to link to nodes in the same robust way that nodes can link to each other with node reference; they certainly can’t have any CCK fields hung off them. But, humble though blocks are, they’ve still got some life left in them yet.
Basic theme preprocess hooks in Drupal 5
One of the useful aspects of Drupal 6’s themeing is theme preprocessing. This occurs in between a module (or Drupal core) calling theme() and rendering the actual template file (or function), and arbitrarily alter the variables passed between the module layer and the theme layer. In this way preprocess hooks act much like Django’s context preprocessors, providing ways for modules to “communicate” at the point of themeing.
Drupal 5 doesn’t have any preprocess hooks, unfortunately. All it has is the function _phptemplate_variables() in your theme’s template.php. That’s the sole instance of theme preprocessing in D5, and this bottlenecking means that the function quickly becomes the dumping ground for all of your site’s pre-theme logic. Even if you’re a module developer, of any level of experience, there’s no incentive to factor this code out elsewhere: you just replace it with whatever the module function’s called anyway, and your site becomes more brittle because of it.
With that in mind, here’s how to free up your D5 theme preprocessing in a reasonably Drupalish manner. We’ll use a helper module, and call a single function, safely from within your template.php. That function will replicate the basics of D6’s theme preprocessing and let you start writing D5 modules with preprocess logic in them.
helpd6.info
First your helper module needs a .info file. It doesn’t need to have very much details in it, but here’s a sample file:
name = D6 forward-compatibility functions description = D6 functions, like theme preprocessing
helpd6.module
Next, here’s the module in its entirety. Once you’ve saved this and the .info file into a directory called helpd6, in your site’s modules directory, you should be able to enable the module in your Drupal administrative pages.
<?php /** * Implementation of basic theme preprocessing */ function helpd6_implement_preprocess(&$vars, $hook) { // module_invoke_all passes by value // So assemble function name and invoke ourselves to pass by reference foreach(array('preprocess', "preprocess_$hook") as $stub) { foreach(module_implements($stub) as $mod) { $fn = $mod . "_$stub"; $fn($vars, $hook); } } }
Note we omit the closing PHP tag. And note also that we put an ampersand by $vars. This means we get the “real” theme variables object to play with, not a copy, so that all our logic can modify that “real” variables object and it’s reflected by the time Drupal gets round to themeing.
template.php (excerpt)
Finally, this snippet ties everything together: it calls your helper module from the theme’s template.php. Put it at the very end of the _phptemplate_variables() function:
function _phptemplate_variables($hook, &$vars = array()) { // ... // Finally, run preprocess hook functions function_exists('helpd6_implement_preprocess') && helpd6_implement_preprocess($vars, $hook); // Now return variables return $vars; }
You’ll see that it checks the function exists first. You could check with module_exists('helpd6') too, but we’re aiming for as robust a setup as possible, so best check the function exists rather than the general module.
We also return the variables. In practice, referring to &$vars in the function declaration means they get merged in situ, but in principle the function’s definition on api.drupal.org requires the variables array to be returned. So changes inthe PHPTemplate engine might stop the &$vars trick working in the future.
Implementation
You can now implement either hook_preprocess or hook_preprocess_TYPE hooks e.g. hook_preprocess_page. These behave pretty much exactly the same as Drupal 6 preprocess hooks.
As an example, let’s say that some nodes on your site have an “author” field. This might be different from Drupal’s concept of node author e.g: the node might be created by an admin user, but contain information for a book or other publication; the book itself has an author, who could be anybody in the world, alive or dead. Our site also contains a biography node for each author, so we’d like to have the relevant biography present in the theme layer, to list the biography alongside the book.
Here’s a “module”—again, just a single function—that accomplishes just that with the aid of our helpd6 module. It should be self-explanatory, but have a look at the comments for more details.
<?php /** * Implementation of hook_preprocess_node */ function authorbiog_preprocess_node(&$vars) { // Two possible sources for a biography node: // 1. the author is a site user - CCK user ref field // 2. otherwise, use a CCK node ref field $this_node = $vars['node']; $author_uid = $this_node->field_drupal_author[0]['uid']; $author_nid = $this_node->field_offsite_author[0]['nid']; // Load the author's biog into the theme variables // (Could use Content Profile to help with this) if ($author_uid) { $biog = node_load(array('uid' =>$author_uid, 'type' => 'biography')); } else if ($author_nid) { $biog = node_load($author_nid); } // Theme preprocess hooks should ALWAYS make the HTML safe // Don't rely on .tpl.php files to filter out possible bad markup $biog && ($vars['biog'] = check_markup($biog->body, $biog->format)); // &$vars has been passed by reference, so we don't return anything // But you can now access the biog (if it exists) in node.tpl.php as "$biog" }
You can now access the biography text in the theme layer. Note we don’t pass down the whole node: we could, but it’s safer to pass down filtered HTML, to avoid any possible security holes. In general, it’s the job of theme preprocess hooks to handle HTML security, because you never know where else that HTML is going to end up being rendered.
What’s important to remember is that this code, and many other snippets like it, need no longer live in your template.php. It’s now in a module. It’s something you can turn on and off, just like a module. And it’s something your themer need never worry about: that logic is now safely hidden behind the scenes, leaving template.php as a theme helper, what it’s meant to be. You don’t even need any control logic to determine $hook, because module_implements() and the naming conventions do that for you.
Summary
Drupal 5 themeing will never be as versatile as Drupal 6 themeing. But standard code patterns—especially “Drupalish” ones—can go a long way to abstracting out into modules much of the theme preprocessing that Drupal 5 would otherwise force you to do in your theme layer. The more logic you move out of your theme, the more robust it gets, and the easier it is for a non-developer to maintain.
Building pages in Drupal with Panels
When finished this site will implement several different layouts: blogposts, “static” pages, short “nuggets”, blog archives, taxonomy listing and probably a bespoke front page. Although these will all have the same underlying seven-column layout, that can still present some problems that are usually solved in Drupal with many different theme files and a lot of “regions” in which you can put “blocks” of content.
Regions and blocks have serious limitations, though. For example, any one block—say the most recent posts from the blog, or a feed from Twitter—can only be placed in one region, although it can then be made to appear and disappear on other pages based on URL or (dangerously) bespoke PHP embedded in the database. There are modules which replicate blocks, to try to circumvent such problems. Following merlinofchaos‘ great talk at, once again, DrupalCon Paris 2009, I decided to use Panels and panes instead of the core Drupal region/block system.
It was touch and go initially as to whether I was going to scrap it all and start again with blocks and regions or not. I managed fairly quickly to assemble custom pages for nodes of a few different types, but then had real trouble working out the subtleties of getting custom pages together for existing non-single-node content. Panels and its associated CTools developer framework concentrate far more on front-end functionality than ease of backend user experience. That’s a shame, as the most important take-home message of prioritizing user experience is that hidden functionality might as well be no functionality at all. But as with most of these things—api.drupal.org springs to mind here—the first time is the worst, and once you know what you doing, you, well, know what you’re doing.
Panels lets you divide up a page into just those panels. A set of standard URL matches come with Panels as “pages” you can enable, but you can also add new pages at new URLs. In terms familiar to Drupal templaters they take over the $content variable in the page templates, leaving your existing block regions available if you still want them. So when you enable panels, you get an extra menu item under Site Building called predictably “Panels”. This takes you to a dashboard where you can enable or disable panels support for existing URL schemata (e.g. the node URLs at /node/%) and create variants which take into account e.g. the node type, so your blogposts can look different from your events.
Slightly more subtly, though, if you want to enable panels on an existing view page… you can’t. There’s no mechanism for overriding the existing view page at e.g. /blog, like you can with a node page, and if you try to create a new panels page at that same URL then the system complains and won’t let you do it. Instead, you have to create a whole new page and eventually discard or at any rate mothball the view’s own page. Assuming you have a view at /blog, create a panels page at /blog_new; you can then add the view as a panel pane. You can use any one of the view’s configuration variants (default, page, block etc.), not just the standard page variant. When everything looks happy, move your view to /blog_old and your new panels page to /blog.
Drupal also provides taxonomy listings at /taxonomy/term/term-id , which with Pathauto get aliased to /category/vocabulary-name/term-name. If you want to override a taxonomy listing, you can… but again there’s a catch. Panels doesn’t seem to provide you with any way of replicating the original content as far as I can see, and instead you have to build a view to replace the behaviour of the original taxonomy listing, and then put that into a pane where you’d want the original dumb taxonomy content to go.
When you create the view, you give it a first argument of “term ID”. Then, when you add the view to the panel, in the modal lightbox titled “Configure view NAME (Defaults)”, under the “Override title” box, you should have a setting where you can choose what to pipe through into your argument. Set that to term ID too. What you end up with is, as seems to always be the case with Panels, more functional and configurable than before, but it’s harder to get to. Not just harder than letting core handle it: that’s to be expected. But harder than you’d expect a GUI-rich many-developer-hour content-driven application to be.
Here’s a summary of my panels the configuration:
Site building > Pages
- System > node_view enabled
- System > term_view enabled
- Custom > page_blog created for the blog archives
- Node type = blog and Node type = page variants
- Two-column layout
- Blog: node content in right column, with custom module providing tags in left column
- Two-column layout, no variants
- Term description pane, then taxonomy-filtering view pane, with view arguments taken directly from /taxonomy/term system URL
- At URL=blog
- Paging configured on view
- View has its own page display variant, at another URL, to provide consistent formatting
- Override all core column widths: these are a pain (whoever wants 33:34:33 columns?)
- Custom CSS classes on some panels and panes, with custom module to also strip down markup
Overall Panels has made me feel like I have total control over what I want to appear where on my site. But the learning curve is steep and doubtless puts some people off; some of the greatest advantages come from being happy to get your hands dirty with PHP and the plugin architecture too, which I’ll cover at a later date, and that’s not possible or advisable for most Drupal end users. If I’d not had the luxury of stepping back for a few days every once in a while I’d probably have done it all in templating with regions and blocks. I’m glad I didn’t, though, and I’m looking forward to iterating and perfecting my site’s use of Panels.
Oxford Geek Night 15: open government and web marketing
OGN15 is in a bit over a week’s time, on Wednesday 25 November. It promises to be a great evening as usual, with varied keynote and microslot talks and the usual Oxford(shire) geek chat and networking.
Interestingly, we’ve ended up with common threads through the two quite different keynote talks. Fintan Galvin, who’s talking about the semantic web and its effect on marketing strategies on the web we’re more at home with, will be explaining how the former will change the way we think about such priorities as SEO with regard to the latter; along the way he’ll be mentioning Drupal, which is an ideal solution to a whole host of web content problems. Meanwhile, Jeni Tennison will talk about a new initiative in open UK government, data.gov.uk, which is currently in closed beta and uses a complex stack of technologies including… Drupal and RDF! RDF takes centre stage in the forthcoming Drupal 7 release, so more by luck than judgment our common topic is a pretty hot one.
Of course there’s microslots and the Pitch too—contact me if you want to volunteer for the sixty-second open-mic Pitch, as we’ve still got some spare slots—and we’ve got sponsorship to make sure the whole evening is not just free as in beer, but free as in… other beverages that are available. The Guardian Open Platform are sponsoring drinks again, and Apress have promised us some books for the book raffle. Torchbox is as usual very kindly sponsoring my time, venue costs and infrastructure for the night. Thanks to all our sponsors for their generosity.
Almost everything is in place, which means I might get away with a few days off before the night, but I promise to turn up on the 25th fit and rested. Assuming I’m not still stranded in Birmingham New Street. But then how likely is that, really?
Implementing a columnar grid system on Graceful Exits
In the spirit of getting it out there, I’ve now begun implementing a columnar grid system on my site. As discussed in Mark Boulton’s excellent talk at DrupalCon Paris 2009, and reported briefly in my live notes from the conference. such grid systems are a basic underpinning of consistency and visual clarity on a site. You start at the grid, then decide how your actual page elements are going to fit into it.
This means you can begin with a columnar grid of five evenly-sized columns (such as de Standaard’s website, one of Mark’s examples) but then build e.g. a two- or three-column layout on top of it (or, as de Standaard seem to have done, a sort of 2.5-column layout). Your columns need not be evenly spaced, and you don’t even need to use columns at all: modular and hierarchical grids can be used to great effect, although they suit print somewhat more than the web. It’s hard to overstress the importance of helping the user to make these implicit connections: the grid gives your site visual predictability, and means your users are more likely to find what they want; it’s implicitly tied to both visual aesthetics and also basic site user experience and usability.
I decided to have a seven-column grid for this site, as that gave me a lot of flexibility in page layouts: a three-column page could be 2-3-2 or 1-5-1. Consistency of field combinations is important, but as with the Guardian’s website, as long as your underlying columnar lets users make that implicit connection without realising it, you can still push the boundaries of inconsistency on top of that. I decided on a field width of 124px, with a 15px margin between fields. Seven columns and six margins make 958px, which is comfortably close to the equally comfortable yet ultimately arbitrary 960px limit I’m personally used to.
So far, as I’ve not exposed much of the site, you won’t see the effect of a lot of this, although the blog index should now be broadly following a 2-3-2 layout. When I’ve got the full content for the finalized IA in place then you should see more of the effect of being able to vary page layout while sticking to the seven-column grid. It looks scrappy now, and will probably look scrappy then—I’m not exactly a perfectionist when it comes to design—but I’m far prouder of it than any other site design I’ve done from scratch.
Python 3 for Absolute Beginners is here
I’m ridiculously excited that the book I co-authored with Tim Hall, Python 3 For Absolute Beginners, has been published. Apress very kindly sent me some complimentary copies last week and I immediately took photos of it and posted them on Flickr.
I blogged about how glad I was to be asked to work alongside Tim on the project back in January, and said among other things:
The book’s aimed at those learning to program, through the medium of Python 3, rather than those already experienced in Python 2.x. But the new Python looks like an excellent way to teach people about the vagaries of a whole range of programming concepts. Generally the changes in the new version are for the better, and I think Python’s benevolent dictatorship were absolutely right to conscience backwards incompatibility in the occasional change.
This is still all true, and I still think that Python 3 is still a quietly exciting overhaul of Python 2.x that raises the bar for both object-oriented and scripting programming languages.
My two chapters are on Exception Handling and Modules. Tim’s ten chapters are on… everything else. Buy it. Enjoy it. Request it from your local library. Point it out nonchalantly to your friends. Use it to stop your desk from wobbling. Whatever: I’m just proud it’s out there.
