It's a somewhat belated announcement, but I am pleased to report the latest Drupal site on the Net, Washington University, St. Louis' College of Liberal Arts and Sciences.
ArtSci is the first major Drupal site for Palantir.net, although we have several more in the pipeline. It is also one of many that Washington University will be launching. The entire Arts and Sciences school has decided to go Drupal. Yay for Open Source!
Drupal wasn't the only CMS considered for the site, but in the end it was chosen for one key feature: It's bendy. Although it's certainly true that many Drupal sites look alike, that's because, like most systems, many developers don't stray far from the standard look. Especially for little details like tabs, comments, publication info, and so forth it's often easier to simply use the default and get on with the business of managing content. That's fine for many sites, but in this case we had high-fi templates built and developed before the CMS was even chosen, and they didn't "look like Drupal".
The resulting site was originally built with Drupal 4.7, but we were able to refactor the design a great deal while upgrading it to Drupal 5. It was truly amazing how what I consider the "three pillars of Drupal", the features that make it so bendy, came together. I refer of course to the Forms API, CCK, and Views.
Every node on the site is a CCK-based node. The ability to create and configure content types on the fly was a huge time-saver, but what really made CCK a good choice was rich data fields. In particular, nearly every image on the site is an imagefield on another node rather than its own node. Randomly rotating images, something clients always seem to want, are only one line of code in template.php with imagefield.
<?php
$vars['random_image'] = $vars['field_myimage'][array_rand($vars['field_myimage'])]['view'];
?>
Of course, they don't play nice with page caching, but we were able to find a solution. More on that later.
Viewfield was the big win, though. Part of the design includes table of contents or "section" pages. Those are, actually, surprisingly simple although they do involve double data-entry. We defined a Taxonomy vocabulary, "Site Section", and then for each table of contents page defined a term. Pages that can show up in a section index are then put in the appropriate taxonomy. A simple List View takes an argument of which taxonomy term to show, and then provides the corresponding list. Specify that View in each table of contents node in a viewfield, along with the taxonomy term for that section, and poof. Instant section table of contents. The only catch is that most pages are also entered into the site's Primary Links menu, which means setting two values on each node edit page instead of one. Now if only we can get an easy "children of the active menu item" list that we can include in the template directly Hmmm...
The other nice feature of Viewfield is that it allows for an almost trivially simple "View as node" functionality. A "view page" is simply a node with one viewfield and no body. That makes it possible to include a view in a view, by wrapping it in a node. (Are you confused yet?)
Although not quite as critical as Views, the Panels module made the event listings easier, too. The trick there was breaking the filter form of a View off from its View and sticking it in a block. That is made possible by a combination of the Forms API and directly pulling content out of the view itself in the custom text of a block, like so:
<?php
$view = views_get_view('events_search');
return drupal_get_form("views_filters", $view);
?>
The really tricky part was the mini-calendar. The event module itself is, sadly, not very themable at all. Instead, we used the relatively new calendar module, and even tracked its rapid progress during development. Calendar is, sadly, also not as themable as we'd like. All of the various calendar displays are merged into a single mega-function. While it's themable, theming a single gigantic function when all we want to do is change whether the next and previous month links appear before or after the month name seems, well, un-Drupaly. The other downside was that the event module stores both a time stamp and timezone for each event, which means converting it to a single timezone requires a full node load. As a result, the mini-calendar is single-handled the most SQL-expensive part of the site. That's a hard problem that Drupal does need to work on. Thank goodness for caching.
The other tricky part about the calendar was multi-day events. While the calendar module will list an event on a day if it starts on or passes through a given day (again requiring a full node load), the Views module can only filter on whether a start time is before or after some date, and then ANDs filters together. Determining if a given event passes through a date is an OR clause, which Views doesn't support.
Or does it? Enter the hook of last resort, hook_views_query_alter(). Yes, yet another _alter hook, one that I didn't even realize existed until this site. Although it's tricky and very specific to a given view, it is possible to mutate the WHERE clause of the query (in array form, of course) and build an OR clause that then gets ANDed with the rest of the query. Poof, an OR clause and a fully functional calendar.
Along the way, we added two modules to Drupal's module pool. The first, menutree, is one I was surprised didn't exist yet. While there are plenty of sitemap-from-taxonomy methods and modules, there wasn't one for building a sitemap out of a menu. So we added one. Menutree builds an ul-tree out of a specified menu, fully expanded. It is, of course, fully themable. There's no configuration, simply creating a path alias from menutree/2 to, say, /sitemap results in an automatic site map built out of the primary links menu (or any other menu id).
The other I hinted at earlier. Cacheexclude didn't originally come from Palantir, but from a forum thread. We picked it up ad polished it a bit to make it submittable, but the concept is simple. If a page is specified as "should be excluded", then on hook_exit() the module deletes that one record from the page cache. All other pages are still cached, but that one page will always have no cached version and so will be rebuilt. The upside is that it is very simple. The downside is that saving and then clearing the cache record for a page invalidates the MySQL query cache, which slows down all cache hits a bit. So far the performance penalty hasn't been a problem, but it's still annoying. Fixing that would, unfortunately, require modifying core directly to allow a page to be excluded from the cache table in the first place. I spoke to dopry about it, but he didn't have high hopes for such a feature being accepted into core. It may be worth trying anyway, though.
Looking at the site, I wouldn't know it was a Drupal site if I hadn't built it (or didn't know exactly what to look for). In my book, that is a big win for Drupal. It's so powerful you don't even realize it's there! Bendy indeed.
A big thanks, while I'm at it, to Earl Miles. Not only did he write a lot of the code that makes the site possible (Views and Panels in particular), but he also was very friendly and helpful in explaining just how to bend Drupal and Views to our will. Merlin, you rock.
Watch this space for more Drupal sites, coming soon to an Internet near you.