Upgrade Your Drupal Skills

We trained 1,000+ Drupal Developers over the last decade.

See Advanced Courses NAH, I know Enough
Jul 18 2011
Jul 18

Spend any significant amount of time reading long articles on the web and you get distracted. Distracted by sidebars and insets full of links and animated graphics, many of which are advertisements. Distracted by badly set typography. Distracted by "next page" links at the bottom, forcing you to wait for the next part of the article to load as the publisher gets another increased statistic to sell. 

Enter Readability. Developed by Arc90 as an experiment, Readability started life as a bookmarklet and browser extension designed to improve the reading experience. In addition to decluttering distracting constituents of a web page, the bookmarklet offered a consistent look that stripped an article to its essential element, the text. After Arc90 released the bookmarklet as open source software, Apple's integrated the code into the Safari web browser. Subsequent to that the company changed direction with Readability in January of this year and started a web service for both website publishers and readers. While keeping the core service of simplifying articles for easier reading, with the new Readability, publishers of any type on the web — from magazines with multiple contributors to single editor blogs — can integrate Readbility into their website and optionally receive payments for their work from readers who sign up to contribute.

How Readability Payment Works

Publishers first must claim their website by adding a special code to the HTML header, which they can remove after verification. Using their own Readability accounts, readers can contribute a monthly fee that they, the readers, individually specify. (Publishers have no say what readers have to contribute, though Readability sets the monthly minimum at $5.) Readers' money first goes to Readability, who take a 30% cut. Readability then divides the remaining 70% amongst the publishers that readers, while logged into Readability, read through the service. As a simple example, let's say a reader only reads two online publications, The New Yorker and The Bygone Bureau. If a reader contributes $10 per month, and in the span of a month, uses Readability to read 6 articles from The New Yorker and 5 articles from The Bygone Bureau, Readability takes its $3 cut and The New Yorker gets $3.82 ($7 divided by 11 total articles, multiplied by 6 New Yorker articles) and The Bygone Bureau gets $3.18 ($7/11*5).

Readability buttons, as they appear on websites, typically offer the reader has the option to send the article to Readability and come back to it later ("Read Later") or immediately convert the article to Readability's simplified version ("Read Now"). Some tools that do 'read later' functionality, like Instapaper, can, after reading through the app, send the link to Readability so that it is counted towards the reader's monetary contribution.

Publishers do not need to claim their website or receive any payment in order for readers to use the part of Readability that converts article to pleasant-looking text, nor do readers need to pay Readability to convert distracting pages. The publisher-contributor model constitutes a new way for publishers and writers to make a few bucks on their creation and for readers to keep good writing coming without have to put up with (as many) advertisements. One publisher confided in me that "nobody's getting rich off it," referring specifically to Readability, but Readability at leasts represents an opportunity for innovation in the ways publishers, writers and readers interact and support each other.

Readabilty Button for Drupal

Screenshot of a Readability 'Read Now | Later' button on an example node in Drupal

Since I work primarily with the content management system Drupal, and wanted the functionality not only for my site but for other Drupal sites, I've developed a module called Readability Button. "Without this module, site maintainers would have to edit their theme directly, introducing code that they need to maintain. Instead, they can turn on the module and change settings, and if they want to discontinue Readability integration, can simply disable the module. Readability Button features the following:

  • Enable the button on a per-content type basis. Also a permission to show the button on nodes to certain roles.
  • In the module configuration, temporarily add the verification string (the full element provided by the Readability web service) without editing your theme. You can disable this after you've verified your domain.
  • Configure whether to show only on the individual node view or also on lists (such as the blog page or taxonomy views).
  • Optional Print, Email, "Send to Kindle" buttons.
  • Modify the colours of the foreground and background using hexadecimal colour codes. Optional integration with jQuery Colorpicker.
  • Change the orientation of the button from horizontal to vertical.
  • If a reader is logged into their Readability reader account, and the reader is a contributor, clicking the button sends a portion of their contribution to the site's publisher.

If you add the module to your Drupal site, I encourage you to make a feature request and submit bug reports in the issue queue.

Imagining a 2.0 branch

The module as described as above has some limitations:

  • Once you add it to a content type, all nodes of that content type receive buttons, no matter how short
  • If you allow the button on lists, it appears on all lists. that includes views, taxonomy term pages, blogs, etc.
  • The weighting of the button is somewhat of a kludge

A 2.x branch of the module, which only exists in my head, comprises of the following features:

  • Make the Readability button a field in the Drupal 7 sense. This would enable many things for "free":
    • flexible placement of the field in content types, using the per-content type field settings
    • Views integration, meaning with any list you can override settings to show or hide the Readability button
  • Readability API integration, meaning authentication and management of your Readability publisher account for websites and reader accounts for site members
  • If the website uses the Domain Access module, then each site could conceivably have its own 'domain' in Readability as well. Imagine individual bloggers on a Drupal site as a publisher who gets the 70% cut from Readability

Beyond the Module

Just a Gwai Lo currently uses the Deco theme, and, in order for Readability to reliably convert my own website to the clutter-free text, I made some slight changes. In particular, I needed to ensure Readability correctly picks up on article titles and dates. Per Readability's recommendations to mark up articles in the hNews microformat, I've added an entry-title class to my h2 tags, necessitating modifications to the theme's template.php and node.tpl.php files. I also added a element wrapped around the date for blog posts. The right way to do all of this would be to find a way to modify the theme elements in a module, though I think the only option available to me is to maintain a sub-theme.

Jun 09 2011
Jun 09

One of my development goals is to learn how to set up continuous integration so that I’ll always remember to run my automated tests. I picked up the inspiration to use Hudson from Stuart Robertson, with whom I had the pleasure of working on a Drupal project before he moved to BMO. He had set up continuous integration testing with Hudson and Selenium on another project he’d worked on, and they completed user acceptance testing without any defects. That’s pretty cool. =)

I’m a big fan of automated testing because I hate doing repetitive work. Automated tests also let me turn software development into a game, with clearly defined goalposts and a way to keep score. Automated tests can be a handy way of creating lots of data so that I can manually test a site set up the way I want it to be. I like doing test-driven development: write the test first, then write the code that passes it.

Testing was even better with Rails. I love the Cucumber testing framework because I could define high-level tests in English. The Drupal equivalent (Drucumber?) isn’t quite there yet. I could actually use Cucumber to test my Drupal site, but it would only be able to test the web interface, not the code, and I like to write unit tests in addition to integration tests. Still, some automated testing is better than no testing, and I’m comfortable creating Simpletest classes.

Jenkins (previously known as Hudson) is a continuous integration server that can build and test your application whenever you change the code. I set it up on my local development image by following Jenkins’ installation instructions. I enabled the Git plugin (Manage Jenkins – Manage Plugins – Available).

Then I set up a project with my local git repository. I started with a placeholder build step of Execute shell and pwd, just to see where I was. When I built the project, Hudson checked out my source code and ran the command. I then went into the Hudson workspace directory, configured my Drupal settings.php to use the database and URL I created for the integration site, and configured permissions and Apache with a name-based virtual host so that I could run web tests.

For build steps, I used Execute shell with the following settings:

mysql -u integration integration < sites/default/files/backup_migrate/scheduled/site-backup.mysql
/var/drush/drush test PopulateTestUsersTest
/var/drush/drush test PopulateTestSessionsTest
/var/drush/drush testre MyProjectName --error-on-fail

This loads the backup file created by Backup and Migrate, sets up my test content, and then uses my custom testre command.

Code below (c) 2011 Sacha Chua ([email protected]), available under GNU General Public License v2.0 (yes, I should submit this as a patch, but there’s a bit of paperwork for direct contributions, and it’s easier to just get my manager’s OK to blog about something…)

// A Drush command callback.
function drush_simpletest_test_regular_expression($test_re='') {
  global $verbose, $color;
  $verbose = is_null(drush_get_option('detail')) ? FALSE : TRUE;
  $color = is_null(drush_get_option('color')) ? FALSE : TRUE;
  $error_on_fail = is_null(drush_get_option('error-on-fail')) ? FALSE : TRUE;
  if (!preg_match("/^\/.*\//", $test_re)) {
    $test_re = "/$test_re/";
  // call this method rather than simpletest_test_get_all() in order to bypass internal cache
  $all_test_classes = simpletest_test_get_all_classes();

  // Check that the test class parameter has been set.
  if (empty($test_re)) {
    drush_print("\nAvailable test groups & classes");
    $current_group = '';
    foreach ($all_test_classes as $class => $details) {
      if (class_exists($class) && method_exists($class, 'getInfo')) {
        $info = call_user_func(array($class, 'getInfo'));
        if ($info['group'] != $current_group) {
          $current_group = $info['group'];
          drush_print('[' . $current_group . ']');
        drush_print("\t" . $class . ' - ' . $info['name']);

  // Find test classes that match
  foreach ($all_test_classes as $class => $details) {
    if (class_exists($class) && method_exists($class, 'getInfo')) {
      if (preg_match($test_re, $class)) {
        $info = call_user_func(array($class, 'getInfo'));
        $matching_classes[$class] = $info;

  // Sort matching classes by weight
  uasort($matching_classes, '_simpletest_drush_compare_weight');

  foreach ($matching_classes as $class => $info) {
    $main_verbose = $verbose;
    $results[$class] = drush_simpletest_run_single_test($class, $error_on_fail);
    $verbose = $main_verbose;

  $failures = $successes = 0;
  foreach ($results as $class => $status) {
    print $status . "\t" . $class . "\n";
    if ($status == 'fail') {
    } else {
  print "Failed: " . $failures . "/" . ($failures + $successes) . "\n";
  print "Succeeded: " . $successes . "/" . ($failures + $successes) . "\n";
  if ($failures > 0) {
    return 1;

I didn’t bother hacking Simpletest output to match the Ant/JUnit output so that Jenkins could understand it better. I just wanted a pass/fail status, as I could always look at the results to find out which test failed.

What does it gain me over running the tests from the command-line? I like having the build history and being able to remember the last successful build.

I’m going to keep this as a local build server instead of setting up a remote continuous integration server on our public machine, because it involves installing quite a number of additional packages. Maybe the other developers might be inspired to set up something similar, though!

2011-06-09 Thu 09:51

Mar 30 2011
Mar 30

I would like to claim an utter hatred of race conditions. This is where code is written in such a way that it doesn’t fully consider the possibility of another thread (e.g. another website hit) or threads occurring concurrently. Consider the following which has been increasingly frustrating me recently:

Drupal stores variables in the ‘variables’ table. It also caches these in the ‘cache’ table so that rather than doing multiple SELECT queries for multiple variables, it simple gets all the variables straight out of the cache table in one SELECT then unserializes them.

cron_semaphore is one of these variables which is created when cron starts, then it deletes it when finishing. If it isn’t deleted it should mean that cron hasn’t finished running yet, so the next time cron tries to run it will quit straight away. But due to a certain race condition it doesn’t always get properly deleted as follows (p2 is an abbreviation for an unrelated process running concurrently, e.g. a visitor to your website):

1, cron starts, cron_semaphore variable inserted (and variables cache is deleted)
2. p2 starts, variables cache is empty so “SELECT * FROM {variables}” then…
3. cron finishes, cron_semaphore variable deleted and the variables cache is cleared
4. … p2 inserts result of “SELECT * FROM {variables}” into cache, but that SELECT was called before cron deleted the variable
5. you now have no mention of cron_semaphore in the variables table, but there it is back in the variables cache!

Consider many visits to your website concurrently and you soon realise this can become a very common occurrence. In fact, this exact problem inflicts us at least a handful of times every day. As a result cron keeps trying to run but immediately quits when it sees the semaphore variable still there. After an hour it finally deletes the semaphore but in the meantime crucial stuff doesn’t get done.

Web applications can quickly become riddled with race conditions such as these. I’ve spotted at least two more in Drupal’s core code in the past. When the ‘bugs’ occur as a result they can be tricky to pin down, appearing to be freakish random occurrences. Worse yet, even when found they can be a royal pain to untangle.

Share this:

Like this:

Like Loading...
Jan 15 2011
Jan 15

Early last year, around the time of DrupalCon San Francisco, Packt Publishing approached me to serve as technical reviewer for a book. Several Microsoft Word documents and 7 months later, the dead trees edition of Drupal Theming Cookbook by Karthik Kumar, arrived at my doorstep, complete with an acknowledgement of my work inside the front cover. In the hopes of branching out a little, I also received a complimentary copy of Django 1.0 Website Development by Ayman Hourieh. Over the course of a month in December 2010 it served as an excellent guide to completing one's first app, with little or no Python knowledge required, but taking the 'dive right in' approach. (Anything that didn't work with Django 1.2 was a quick Google search away. I have sticky notes at every point at which it differs from the current version as of this writing.) I hope to ship an app based on the example sometime this year.

At the end of last November, I helped instruct at a community-based clinic teaching a basic-level introduction to the Drupal CMS. Based on notes from the Seattle Drupal Clinic held in 2009, several members of the Drupal community and people new to Drupal converged at the FCV office in downtown Vancouver. We covered modules, content types, image manipulation, and for my session at the end, the Views and Block modules. We the trainers learned a lot from that first session, and it seems like the same can be said about the participants. My thanks go to everybody involved. It's an initiative I'd like to participate in again.

A third win involves getting back into the Drupal support game. You can find me on the #drupal-support IRC channel when things slow down at work, and recently on Stack Overflow's Drupal-related questions. While having reservations about not tracking the Drupal.org forums for support, I will go where the people are.

Since some people have asked, since June of last year, I've been working for OpenRoad Communications, a web services company based in Gastown. They're technology-agnostic, and when they had a couple of Drupal projects come their way, it made sense to have me on full-time. Separately from web services, they created a product called ThoughtFarmer, which they bill as a social intranet. (It might be tempting to make a connection between Drupal and ThoughtFarmer, but other than my sitting next to the development team, rest assured the two are not related.) Since months can go by between my mentioning my employer, it's probably best to refer to LinkedIn or my resume for my latest professional status.

Dec 01 2010
Dec 01

I've been working with custom PHP MySQL apps for quite a few years. Early on I was building grant systems for nonprofit and State Arts Agencies in the United States. About 4 years ago, I fell into Drupal quite by accident at a little think tank that my then employer, The Western States Arts Federation, had organized in Vancouver. Several of the participants were employees of Bryght. I began to drink the cool aid. While at WESTAF I was responsible for the build of a couple of Drupal sites and found myself more and more drawn to the modular LEGO-like way you could snap pieces together to create something new. It was just about at that point that I starting going to Drupal meetups and transitioned to a Web Producer position with a Drupal shop. This was the beginning of my seeing Drupal as my bread and butter.

Fast Forward to November 2010. I've been to Drupalcons in Barcelona, Boston, Washington, Paris, Copenhagen, and San Francisco. I was involved in helping set up the original Drupalcamps in Colorado when you could count the participants on two hands and that has grown to several hundred. Austin was the first camp I attended outside of Colorado and I was impressed. Six out of the Seven Vintage Digital Co-op members attended.

I approach each of the meetups, cons, and camps as a place to potentially grow my understanding of Drupal as a profitable platform. They give me a chance to meet with leaders in our community, with other Drupal Professionals, and interact with different shops of different sizes. The sessions often validate and sometimes surprise me. In Austin, I attended nine sessions including Jeff Robbins' keynote. I wrote about each of the sessions on my personal blog. They are really just notes with video from the event - but if you have interest you can see them on dogstar.org tagged with drupalcamp austin. I was happily met with a few new community modules, mostly surrounding SEO, that I hadn't heard of before. I enjoyed the panel on Managing A Drupal Business. Panels like this allow people to validate what they think they know by hearing similar stories from other businesses.

I am seeing more and more of the office managers, accountants, content creators, marketers, and account managers at these events. This should be encouraged and more business track sessions ought to be solicited. As Drupal continues to mature and ease itself into the Enterprise we need to grow with it widening the pool of talents beyond project managers, developers, and themers. We need to thinking in strategic ways and leverage those who have traditionally not been within our community. All of what I'm seeing is encouraging.

It is easy to discount the social time at the camps and conferences. However, meeting people in a looser way at places like Austin's Dog and Duck Pub is also very important. These are the times that professional contacts are made, deals are often brokered, and jobs are offered. Conversations can be had that help you avoid technical and project oriented pitfalls that other developers have found themselves subject to. You might find out about an RFP that a friendly competitor isn't going to bid on because they are out of bandwidth.

Austin was an inexpensive way to feed the need for professional development and to continue to forge and support relationships. I, personally, am looking forward to more camps.

Aug 31 2010
Aug 31

With Drupalcon Copenhagen now behind us and Drupalcon Chicago approaching, I've found myself thinking about what Drupalcon is and how it's changing.

My first Drupalcon was in Barcelona, I was lucky enough to get to tag along with the guys from Bryght. I had an absolutely amazing time and met dozens of people, many of whom are now quite close friends. To top it off I also met my now fiancee and a future boss (no longer my boss, but still a good friend).

Since then, the twice yearly Drupalcons have consistently been highlights in my year. It's often the only time I get to see many of my friends in person.

Drupalcon is not a conference. At least not in the traditional sense. It's a time where some of the smartest people in the community get together, work on code, figure out problems, and teach each other what they know. It differs from a traditional conference in that there are no paid speakers and it doesn't come with a $2000+ price tag. In addition almost everyone attending is also a participant, whether they're there to hack code, present, contribute to BoF's, etc., everyone at Drupalcon makes it what it is.

Or I should say Drupalcon was that.

Since the first Drupalcon in Antwerp (correct me if I'm wrong), the number of people in attendance has almost consistently doubled every time. With 3000+ people at DCSF and a planned 4000+ attending Drupalcon Chicago, maintaining the personal feel that Drupalcons have traditionally had is simply no longer sustainable and I don't believe possible.

A few of the signs that lead me to believe this are:
* One of the stated goals of DC Chicago in the opening keynote at Copenhagen was to make it the "biggest" Drupalcon ever. I recall in Barcelona the goal was "Best Drupalcon Ever!". Biggest is still a great goal, but it doesn't say anything of the quality of the con, nor if people will enjoy it or not.

* At the end of each conference, traditionally the final keynote includes a slideshow of flickr photos from the conference. This to me is a reminder that the conference was about the attendees. It's an important reminder that the conference isn't so much about the sessions and learning, as it is about the experience of having everyone there in one place at one time. This was absent from this years closing keynote. In fact, this years closing keynote seemed more like the season finale of a reality TV show, than the closing keynote of a Drupalcon.

* DC Chicago will select a set of more "well known" speakers prior to opening up the session proposals and voting to the public. While this is actually quite beneficial to people who need to convince their companies to let them attend it is a big change (possibly the biggest in my eyes) to the way Drupalcons are traditionally a bit more open for anyone in the community to present their ideas. I see this ultimately heading down the road of having the conference organizers select all the speakers, and possibly even moving to the paid speaker and expensive conference price tag model. When the vast majority of the attendees shifts from Drupal contributors to people trying to learn what Drupal is and how it can fit into their company, this is really only natural.

* Lastly, Drupalcons are now being planned multiple years in advance. This is quite different from the planning that normally occurs one Drupalcon in advance.

None of these changes are necessarily bad things, they're just a sign that times are changing.

For me personally, I think Drupalcon will soon no longer be something I look forward to and anticipate, but instead something I go to out of obligation for the work I do.

This doesn't mean I'm not still super excited about the community and new things that are happening in Drupal, but instead that it's time to redirect my energy elsewhere. I think the stuff I'm really gonna be excited about in the future will be the local Drupal camps, and things like the upcoming PNW Drupal Summit (which unfortunately I'll be missing :( ). Also, I think there will be some very cool community stuff happening in new areas with Drupalcon like conferences happening in Asia, South America, and Africa.

The most important aspect of Drupal is the community. It's sad to think that Drupalcons are leaving that behind a bit, but I also don't think there's any other way it can go.

With that said, I had an amazing time in Copenhagen. There were a few issues (as there always are) but overall the conference organizers did a great job putting it together and I thought the sessions had a very good balance from intro to advanced. And I'm definitely looking forward to seeing everyone in Chicago :).

Jul 28 2010
Jul 28

(This is content originally posted at http://jcfiala.net/blog/2010/07/28/magic-command-invocation-drush.)

I've recently been digging into the Aegir Hosting System, both because we're starting to use it at my current NREL gig, and because I've proposed to do a session on it at DrupalCamp LA. In short, Aegir allows you to easily administer a number of Drupal sites from a common site, itself built on Drupal. It's really slick, and a lot of the functionality is built on Drush. (Also see http://drupal.org/project/drush .)

So I'm looking into how Aegir handles tasks, which are just what they sound like - nodes representing commands that Aegir fires off every minute or so from a queue - if the queue's empty then that's that, but otherwise it calls drush hosting-task #, where the # is a task node id. Wanting to know what that actually does, I looked around, found hosting.drush.inc, and discovered that it didn't have a callback.

Huh. I'd thought that Drush commands had to have a callback, but here I discover that they don't. So, I wondered, what's actually going on there? And, following the advice handed to me by Greg Knaddison when I started in Drupal, I started following the code. And here's what I found:

Generally, you want to use the callbacks on drush commands when they're fairly simple - the task is doing something basic that can be handled easily. If it's something more complex, you can skip the callback, and gain a fair amount of control over how your command is invoked, and even cleanup help if something goes south.

If a drush command doesn't have a callback, then the command info is handed off to a function called drush_invoke - you can find it in the command.inc file. (For the purposes of this, I'm going to call the command hosting-task, because that's what I was looking for when I found this out, and it's a good example.) The first thing drush_invoke does is look for an include file named after the reverse of the command name - so our hosting-task gets inverted and the dashes are converted to dots, leaving us with task.hosting.inc. It then looks around to find this file in any directory that contains a *.drush.inc file - the folks working on Aegir in this case put task.hosting.inc in the same directory as hosting.drush.inc, which makes sense. Now, there doesn't have to be an include file like this, and if Drush doesn't find one, it'll still continue to the next part. But if you've got a complex command that you're handing off to drush_invoke to handle for you, why not put it in it's own file for neatness' sake?

Continuing, after the possible file load, the drush command is turned into a base $hook by the simple expedience of changing any spaces or dashes into underscores - so for the example, we'd have a base hook of 'hosting_task' With that constructed, drush looks for functions named off of this command as follows:

  1. $hook_validate (ie, hosting_task_validate)
  2. pre_$hook (ie, pre_hosting_task)
  3. $hook (ie, hosting_task)
  4. post_$hook (ie, post_hosting_task)

And then, for each module that defines a module.drush.inc file, drush looks for a drush_$modulename_$function... which means that for the hosting module, it looks for:

  1. drush_hosting_hosting_task_validate
  2. drush_hosting_pre_hosting_task
  3. drush_hosting_hosting_task
  4. drush_hosting_post_hosting_task

If you look at task.hosting.inc, you'll see that most of these are defined - they're not actually using drush_hosting_pre_hosting_task, but that's because they're doing their preparation work in the drush_hosting_hosting_task_validate function. So, the drush_invoke happily continues along this series of commands, until it either reaches the end (oh, happiness), or one of these functions invokes drush_set_error() - in which case the whole stack of commands reverses and runs backwards, and drush_invoke sees if there's a function named exactly the same, only with '_rollback' appended to the name. (For our example, there is a drush_hosting_hosting_task_rollback() defined in task.hosting.inc.)

A note on naming - although above we've referenced drush_hosting_hosting_task_validate and drush_hosting_hosting_task, the folks who maintain Drush recognize that these are pretty long names to have to construct, and as such they check for when one of these function names they construct starts with drush_module_module and change it over to drush_module. So, in task.hosting.inc, they really should be naming the callback functions drush_hosting_task_validate and drush_hosting_task... but for now Drush recognizes the old form and still will call them.

So to summarize what I've said so far, if you run into a Drush command without a callback, what you really should be looking for is a file named the reverse of the name in the same directory as the hook_drush_command implementation, and that file should contain the drush_module_command functions. And alternately, if you want to create a Drush command that can rollback/clean up after itself, then you want to create your commands in a reverse-order name file.

But... there's something else.

drush_invoke goes through all of the modules which define drush hooks when looking for functions to call when invoking a drush command. So, if you wanted to do extra work on hosting-task drush commands, you could create an example.drush.inc in your example.module, and then define your own drush_example_hosting_task function. Heck, you could define any of:

  1. drush_example_hosting_task_validate
  2. drush_example_pre_hosting_task
  3. drush_example_hosting_task
  4. drush_example_post_hosting_task

This is pretty exciting, as it opens up a lot of customization not only in Drush, but also in Aegir. I think as Aegir continues to mature, we're going to start seeing a bunch of modules which extend it in various ways.

Jul 13 2010
Jul 13

One of the biggest tasks when building a Drupal site is selecting, configuring, and integrating contrib modules into your site. For almost everything you need to do "there's a module for that", but whether you should be using that module is an entirely different question.

For every new module I choose for a site, I go through some quick steps and questions (mostly unconsciously now) to determine whether I should risk a module.

1. Most recent release - While this may not be a good guide of whether the module is stable or not, it tends to provide a pretty good guide of how actively maintained it is. The first thing I do, before even reading through the description is check the latest release. If the project doesn't have a release in over a year this usually isn't a good sign.

2. Issue queue - The second thing I do is check the issue queue. I expect the average module to have about half a page to a page of open tickets. I'm mostly concerned here with whether or not a maintainer is actually responding to tickets and active with the project. If simple tickets with patches have been sitting open for several months with no response from a maintainer, this is a very bad sign. For larger modules you can probably expect several pages of issues. In all honesty, my only concern here is that tickets are being addressed by maintainers.

3. Usage stats - Every project page on Drupal has a link to "usage statistics". The more users a module has the better bug tested it is, in general.

4. Documentation - Is the project description useful? How about the README? Whether you read the docs or not, knowing they exist is important. In my experience a project with no readme and poor documentation is generally very lacking in other areas too.

5. Post install troubles - Any trouble figuring out how to use the module after you've enabled it? Even after you read the README? Probably a sign that you may have problems integrating and configuring the module with your site.

6. Code structure - I almost always take a peek at the code of every module I use on a site these days. If you're not a developer yourself this won't mean as much to you. But taking a quick scan through the code can set off alarms bells pretty quickly.

You'll likely run into modules that fail in every one of these areas. It might be a good idea to take a pass on it and try to find similar functionality elsewhere. If it's a module that's important enough to your site ask to become a co-maintainer. Just please don't create a second identical module on drupal.org because you thought you could do a better job.

There are also a few other tools that can aid in selecting modules such as http://drupalmodules.com which lets you rate and comment on modules you've used.

As a developer, for me choosing a module comes down to three main questions:

  1. How responsive is the maintainer / How active is the development?
  2. How important is it for me to have this functionality?
  3. Do I have time to take the module on and clean it up myself?

Less important functionality simply gets axed if the module isn't up to par. But ultimately a lot of the selection process just comes down to experience as well as asking others what modules have worked for them.

For every site I maintain I keep a running list as to which ones are likely to cause problems. This helps to identify problems when "unexplained" things happen and also so you know which areas you should try and clean up / replace when you've got a few spare moments to work on a site.

Jul 07 2010
Jul 07

Alfresco wants to be a best-in-class repository for you to build your content-centric applications on top of. Interest in NOSQL repositories seems to be growing, with many large well-known sites choosing non-relational back-ends. Are Alfresco (and, more generally, nearly all ECM and WCM vendors) on a collision course with NOSQL?

First, let’s look at what Alfresco’s been up to lately. Over the last year or so, Alfresco has been shifting to a “we’re for developers” strategy in several ways:

  • Repositioning their Web Content Management offering not as a non-technical end-user tool, but as a tool for web application developers
  • Backing off of their mission to squash Microsoft SharePoint, positioning Alfresco Share instead as “good enough” collaboration. (Remember John Newton’s slide showing Microsoft as the Death Star and Alfresco as the Millenium Falcon? I think Han Solo has decided to take the fight elsewhere.)
  • Making Web Scripts, Surf, and Web Studio part of the Spring Framework.
  • Investing heavily in the Content Management Interoperability Services (CMIS) standard. The investment is far-reaching–Alfresco is an active participant in the OASIS specification itself, has historically been first-to-market with their CMIS implementation, and has multiple participants in CMIS-related open source projects such as Apache Chemistry.

They’ve also been making changes to the core product to make it more scalable (“Internet-scalable” is the stated goal). At a high level, they are disaggregating major Alfresco sub-systems so they can be scaled independently and in some cases removing bottlenecks present in the core infrastructure. Here are a few examples. Some of these are in progress and others are still on the roadmap:

  • Migrating away from Hibernate, which Alfresco Engineers say is currently a limiting factor
  • Switching from “Lucene for everything” to “Lucene for full-text and SQL for metadata search”
  • Making Lucene a separate search server process (presumably clusterable)
  • Making OpenOffice, which is used for document transformations, clusterable
  • Hiring Tom Baeyens (JBoss jBPM founder) and starting the Activiti BPMN project (one of their goals is “cloud scalability from the ground, up”)

So for Alfresco it is all about being an internet-scalable repository that is standards-compliant and has a rich toolset that makes it easy for you to use Alfresco as the back-end of your content-centric applications. Hold that thought for a few minutes while we turn our attention to NOSQL for a moment. Then, like a great rug, I’ll tie the whole room together.

NOSQL Stores

A NOSQL (“Not Only SQL”) store is a repository that does not use a relational database for persistence. There are many different flavors (document-oriented, key-value, tabular), and a number of different implementations. I’ll refer mostly to MongoDB and CouchDB in this post, which are two examples of document-oriented stores. In general, NOSQL stores are:

  • Schema-less. Need to add an “author” field to your “article”? Just add it–it’s as easy as setting a property value. The repository doesn’t care that the other articles in your repository don’t have an author field. The repository doesn’t know what an “article” is, for that matter.
  • Eventually consistent instead of guaranteed consistent. At some point, all replicas in a given cluster will be fully up-to-date. If a replica can’t get up-to-date, it will remove itself from the cluster.
  • Easily replicate-able. It’s very easy to instantiate new server nodes and replicate data between them and, in some cases, to horizontally partition the same database across multiple physical nodes (“sharding”).
  • Extremely scalable. These repositories are built for horizontal scaling so you can add as many nodes as you need. See the previous two points.

NOSQL repositories are used in some extremely large implementations (Digg, Facebook, Twitter, Reddit, Shutterfly, Etsy, Foursquare, etc.) for a variety of purposes. But it’s important to note that you don’t have to be a Facebook or a Twitter to realize benefits from this type of back-end. And, although the examples I’ve listed are all consumer-facing, huge-volume web sites, traditional companies are already using these technologies in-house. I should also note that for some of these projects, scaling down is just as important as scaling up–the CouchDB founders talk about running Couch repositories in browsers, cell phones, or other devices.

If you don’t believe this has application inside the firewall, go back in time to the explosive growth of Lotus Notes and Lotus Domino. The Lotus Notes NSF store has similar characteristics to document-centric NOSQL repositories. In fact, Damien Katz, the founder of CouchDB, used to work for Iris Associates, the creators of Lotus Notes. One of the reasons Notes took off was that business users could create form-based applications without involving IT or DBAs. Notes servers could also replicate with each other which made data highly-available, even on networks with high latency and/or low bandwidth between server nodes.

Alfresco & NOSQL

Unlike a full ECM platform like Alfresco, NOSQL repositories are just that–repositories. Like a relational database, there are client tools, API’s, and drivers to manage the data in a NOSQL repository and perform administrative tasks, but it’s up to you to build the business application around it. Setting up a standalone NOSQL repository for a business user and telling them to start managing their content would be like sticking them in front of MySQL and doing the same. But business apps with NOSQL back-ends are being built. For ECM, projects are already underway that integrate existing platforms with these repositories (See the DrupalCon presentation, “MongoDB – Humongous Drupal“, for one example) and entirely new CMS apps have been built specifically to take advantage of NOSQL repositories.

What about Alfresco? People are using Alfresco and NOSQL repositories together already. Peter Monks, together with others, has created a couple of open source projects that extend Alfresco WCM’s deployment mechanism to use CouchDB and MongoDB as endpoints (here and here).

I recently finished up a project for a Metaversant client in which we used Alfresco DM to create, tag, secure, and route content for approval. Once approved, some custom Java actions deploy metadata to MongoDB and files to buckets on Amazon S3. The front-end presentation tier then queries MongoDB for content chunks and metadata and serves up files directly from Amazon S3 or Amazon’s CloudFront CDN as necessary.

In these examples, Alfresco is essentially being used as a front-end to the NOSQL repository. This gives you the scalability and replication features on the Content Delivery tier with workflow, check-in/check-out, an explicit content model, tagging, versioning, and other typical content management features on the Content Management tier.

But why shouldn’t the Content Management tier benefit from the scalability and replication capabilities of a NOSQL repository? And why can’t a NOSQL repository have an end-user focused user interface with integrated workflow, a form service, and other traditional DM/CMS/WCM functionality? It should, it can and they will. NOSQL-native CMS apps will be developed (some already exist). And existing CMS’s will evolve to take advantage of NOSQL back-ends in some form or fashion, similar to the Drupal-on-Mongo example cited earlier.

What does this mean for Alfresco and ECM architecture in general?

Where does that leave Alfresco? It seems their positioning as a developer-focused, “Internet-scale” repository ultimately leads to them competing directly against NOSQL repositories for certain types of applications. The challenge for Alfresco and other ECM players is whether or not they can achieve the kind of scale and replication capabilities NOSQL repositories offer today before NOSQL can catch up with a new breed of Content Management solutions built expressly for a world in which content is everywhere, user and data volumes are huge and unpredictable, and servers come and go automatically as needed to keep up with demand.

If Alfresco and the overwhelming majority of the rest of today’s CMS vendors are able to meet that challenge with their current relational-backed stores, NOSQL simply becomes an implementation choice for CMS vendors. If, however, it turns out that being backed by a NOSQL repository is a requirement for a modern, Internet-scale CMS, we may see a whole new line-up of players in the CMS space before long.

What do you think? Does the fundamental architecture prevalent in today’s CMS offerings have what it takes to manage the web content in an increasingly cloud-based world? Will we see an explosion of NOSQL-native CMS applications and, if so, will those displace today’s relational vendors or will the two live side-by-side, potentially with buyers not even knowing or caring what choice the vendor has made with regard to how the underlying data is persisted?

Apr 12 2010
Apr 12

Starting with the community site Urban Vancouver, then as the support cowboy for Bryght and Raincity Studios and now with an independent practice, I've enjoyed all of my almost 6 years with the Drupal community. In a couple of weeks, I'll fly to San Francisco to attend my first DrupalCon. With my flight and hotel booked, conference ticket registered, and a ticket to a Major League Baseball ballgame ticket received in the mail, I look forward to the 3 full days of sightseeing in the Bay Area, including the plans to take a tour of North Beach and ride San Francisco's historic streetcars.

The conference itself will present me the opportunity to meet many of the people working on the open source CMS that I admire. I'm looking forward to attending sessions and hanging out in the lobby, and in the evenings, drinking some sweet delicious beer with colleagues. Tuesday night sees me riding BART out to Oakland-Alameda County Coliseum for the first time in some twenty years where I saw my favourite Blue Jay of all time, Jesse Barfield, sock a dinger. This time, without a team to cheer for, I hope to soak it all in, arriving as early as possible to catch hitting and fielding practice, then taunt and boo the Yankees until my throat is sore. Leaving the conference early means missing some fantastic-looking DrupalCon sessions from Narayan Newton and Greg Knaddison. If the baseball gods get angry and rainout the A's game, you'll see me at one of those two sessions.

Mar 23 2010
Mar 23

The Idea

A number of months back, a group of us had the idea to create a software co-operative. There were several tenets that we decided to follow:

  • The company wouldn't have any employees -- everybody involved would be have 1099 status and would be an independent contractor
  • The company would be formed as an Limited Liability Company - we chose the state of Delaware
  • The company would seek to have a $0 cash and asset value at the end of each year
  • The company would be virtual to keep costs low
  • We would focus on working with open sourced projects like Drupal

The Setup

We set the company up using Instacorp. The benefit was the speed at which we could set up the company with an automatic legal presence in Delaware. The people at Instacorp made the process incredibly simple, asking a few questions. Within days the legal documents were delivered. That, in itself, really didn't make the company real.

After receiving the legal documents, it was necessary to obtain an FEIN for tax purposes. This is a simple process on the IRS site - it just takes a few minutes and you get the documentation electronically.

We needed to decide how to be taxed.
LLC's report taxes in one of 3 ways:

  • Disregarded entity (limited to one member LLC's)
  • Partnership (default if other elections are not made)
  • Corporation, electing to be taxes as pass-through entity, called S corporations.

In the case of Vintage Digital, workers are paid for what they do which is reported to them on FORM 1099 as commissions.
Great variation will occur in compensation since it is entirely based on hours worked and percentages for those who find clients and shepherd clients through the contracting process. The LLC is a virtual corporation, with exceeding low or non-existent overhead. There is no intent to use the LLC other than as a distribution method for sharing work; profits/losses will be kept to a minimum. There is no intent to hold fixed assets or incur debt.

In the final analysis both partnership and S-corporation reporting would be the same.

Our accountant indicated that the following things were recommended:

  • Use "S" corporation for tax reporting because the laws are better understood and simpler.
  • Majority of LLC's elect to report taxes as a pass-through corporations, hence even the IRS is more familiar with these tax laws.

We had an S-corp election to indicate how we were going to be taxed - after the election that document needs to be sent to the IRS.

We needed a bank account and opted for a bank that had free business checking and had online bill pay. The bank required our Articles and two forms of identification. We also needed a copy of our FEIN letter from the IRS.

The Tools
All companies need tools to help run things on a day to day basis. A virtual venture is no different. We needed management tools, communication tools, invoicing/book keeping software, and ways to manage contracts. To that end we sought out different solutions that would provide us with ways to sensibly manage ourselves and our projects.

  • Skype for Communication (both voice and chat)
  • Open Atrium for a client intranet and as an internal planning tool.
  • Bamboo Invoice for invoicing clients (although that might change as we transition to QuickBooks)
  • Drupal for our Web presence
  • dotProject for time record keeping and ticketing
  • Office for estimates, calculating commission shares, and contracts
  • Google Voice for incoming phone calls - the rest of us use our own mobiles

The Team

The team is comprised of:

All of us have (and continue to) contribute to the Drupal Project and are heavily involved in our local communities.

The team keeps in pretty much constant contact through Skype. We try to meet about once a month together to have Member/Board meetings. They have occurred in restaurants for brunch, member's homes, and also at a bowling alley - pretty much anywhere that is quiet and you can get through company issues. Fortunately for our crew, we all live with 30 miles or so of one another which makes getting together fairly easy.

Each project gets its own Skype room, project in Open Atrium, project in dotProject, and commission spreadsheet.

As clients come in, we assess who has bandwidth for a given project - the goal ensuring that each co-op member has enough work (in and outside of the co-op) to make a reasonable living. Co-op members are free to work as much or as little as they want (given the work is available). This arrangement was designed to give our team as much flexibility as possible.

Mar 22 2010
Mar 22

A lot of people have been asking for the files we used to integrate Alfresco CMIS with Drupal Open Atrium (See ecmarchitect.com blog post). I’ve happily mailed those to whomever asked. I’ve had the intention of testing them with the latest version, cleaning them up, and putting somewhere more appropriate like the Open Atrium feature server, or at the very least, Google Code or GitHub. But it hasn’t happened yet so I figured I’d make them available here and appeal to the Community to give them a good home.

The zip includes a readme file with (very) rough install/config directions.

Good luck!

Feb 18 2010
Feb 18

Acording to A Survey of 2,368 Drupal Sites a large number of websites are still running Drupal5 so i think it's a perfect time and perhaps the last change to start planning for safely upgrading our good old Drupal 5 websites to Drupal 6 (If any remained), Drupal 5 will be deprecated as soon  Drupal 7 final comes out. Some module maintainers already removed Drupal 5 releases from their projects which is a trouble for website running Drupal 5. Drupal 7 final release is not out yet and i guess that it takes about 2 years until Drupal 7 becomes as solid as Drupal 6 and also most modules upgrade to this new version that's because too many awesome new features included in Drupal 7 and they require quite some polishing.

Why not wait for Drupal 7 and directly upgrade? . As long as i know it's not possible to skip Drupal 6. You'll have to upgrade to Drupal 6 and then Drupal 7 if you want Drupal to do it automatically for you. Besides if your site is critical and you want an stable site i guess you'll have to wait for about 2 years until everything is ready (Including contributed modules port). And also in my case there are certain features i should implement that are not available in Drupal 5, and reimplementing them requires much more time than upgrading the website. i only have one website left with Drupal 5 , it didn't worth upgrading before but today after i checked upgrade status to see the status of the contributed modules i used on the website, they've  been either upgraded , included in Drupal 6 or replaced by better alternatives.

Upgrading Drupal has never been easy, not because of Drupal itself but because of the fact that Drupal's strength relies mostly on its modules and we usually install dozens of them on each website (I have websites with over 90 modules installed, you may have even more :) ). And not all of these modules are well maintained. Switching from one module to another is not easy , specially for complex modules. that's what makes upgrading sometimes very time consuming, not to mentioned the testing process!!

As i mentioned before upgrading is time consuming and with lots of module i've installed on this site it's going to take even more. since my time is limited i should pick the fastest way so i decided to divide the modules into two groups, Critical and Non Critical (Like some administration modules). This way i can launch the new version of the website and then upgrade the remaining modules one by one.


Perhaps the most important part of the upgrade process is planning. As you may already know, Drupal is also a Framework which means that on each release both users and developers have to deal with some fundamental changes. It's actually Drupal's great advantage. So upgrading Drupal is not only about updating it's also about improving, changing, customizing and also sometimes simplifying. For this reason planning is even more important. You might be able to find far better alternatives for your current modules, get rid of many core/module patches, simplify the modules you've developed or customized, by benefiting from new core and contributed modules' features.

The first part of planing is reading more about the new Drupal version and its new features. And the next part is checking all contributed modules and finding better alternatives which i'm going to describe here. So we need to install and use the awesome upgrade status module.

Check the status of contributed modules


I usually patch both modules and Drupal core specially when i find a bug. and almost always contribute this patches back to the community. It's important to keep track of this changes otherwise i may break the site with every core or module update! There is a young module called patchdoq by @doq which is going to simplify patching , right now it needs our love :)

Most of the time i use version control system and follow these steps :

  • Making the patch
  • Testing it
  • Submit the patch to the modules' issue queue and keep the issue link
  • Commit the patch and use patch keyword , simple description of what it does and a link to the issue on issue queued.

Before we start lets have a look at the contributed modules and see how difficult it's going be be , i can simply check the history of the module using VCS and see whether there is any patch keyword  on history, if i find anything since i already have a link i can check whether its been committed or not. If you're not using VCS you can use Drupal.org's advanced search and enter your username in "Participant" or "Submitted by" field and fill in the project name field.

Core patches : i've had plenty of them like this one Very expensive calls drupal_lookup_path but they're no longer required for Drupal 6


Critical (Check list)

Here is the list of modules i used on this particular website and their status, i'm sure that it will come handy for anyone willing to upgrade. i mentioned alternatives for deprecated module as well.

I sorted the modules by their dependency because it's important to upgrade all the module's dependencies first.

Note : I usually use Devel Macro module to repeat the configuration on the production site.

Views by @merlinofchaos , @dereine , @dww and many others : The Views module provides a flexible method for Drupal site designers to control how lists and tables of content (nodes in Views 1, almost anything in Views 2) are presented. Traditionally, Drupal has hard-coded most of this, particularly in how taxonomy and tracker lists are formatte ...

  • Ported : Yes (Can't imagine Drupal without this one :) )
  • Installed version : 5.x-1.6
  • Patched : No
  • Latest Drupal 5's version :  Uptodate 
  • Latest Drupal 6's stable version :   6.x-2.8
  • Dependency for the other modules : Yes
  • Better Alternatives : You kidding?
  • Special note for upgrade : Old views should be converted via admin/build/views/tools/convert.

Token by @eaton , @greggles and many others : Tokens are small bits of text that can be placed into larger documents via simple placeholders, like %site-name or [user]. The Token module provides a central API for modules to use these tokens, and expose their own token values ...

  • Ported : Yes
  • Installed version : 5.x-1.13
  • Patched : No
  • Latest Drupal 5's version : Uptodate
  • Latest Drupal 6's stable version : 6.x-1.12
  • Dependency for the other modules : Yes
  • Better  Alternatives : No
  • Special note for upgrade : No

jQuery Update by @jjeff , @webchick and the others : This module facilitates an upgrade of jQuery in Drupal core and allows other contrib modules to rely on a newer jQuery version ...

  • Ported : Yes
  • Installed version : 5.x-2.0 
  • Patched : No
  • Latest Drupal 5's version :  Uptodate
  • Latest Drupal 6's stable version : 6.x-1.1
  • Dependency for the other modules : Yes
  • Better  Alternatives : No
  • Special note for upgrade : No

Pathauto @greggles , @Freso , @mikeryan and the others : The Pathauto module automatically generates path aliases for various kinds of content (nodes, categories, users) without requiring the user to manually specify the path alias. This allows you to get aliases like /category/my-node-title.html instead of /node/123. The aliases are based upon a "pattern" system which the administrator can control ...

  • Ported : Yes
  • Installed version : 5.x-2.3
  • Patched : No
  • Latest Drupal 5's version :  Uptodate
  • Latest Drupal 6's stable version : 6.x-1.2
  • Dependency for the other modules : Yes
  • Better Alternatives : No
  • Special note for upgrade : No

 Content Construction Kit (CCK) by @yched , @markus_petrux , @KarenS and many others :  The Content Construction Kit allows you to add custom fields to nodes using a web browser ...

  • Ported : of course it's ported :) , Who can live without CCK?!
  • Installed version : 5.x-1.10
  • Patched : No
  • Latest Drupal 5's version :  Uptodate
  • Latest Drupal 6's stable version : 6.x-2.6
  • Dependency for the other modules : Yes
  • Better Alternatives : No
  • Special note for upgrade : No

Javascript Tools by @nedjo , @ray007 , @m3avrck and the others : Javascript Tools provides a common set of methods extending those available in Drupal core (drupal.js) for Javascript and AJAX module development in Drupal ...

  • Ported : Yes
  • Installed version : 5.x-1.2
  • Patched : No
  • Latest Drupal 5's version :  Uptodate
  • Latest Drupal 6's stable version : 6.x-1.0
  • Dependency for the other modules : Yes
  • Better Alternatives : No
  • Special note for upgrade :No

Date by @KarenS , @hass and many others : The date module is a flexible date/time field type for the cck content module which requires the CCK content.module and the Date API module ...

  • Ported : Yes
  • Installed version : 5.x-2.8
  • Patched : No
  • Latest Drupal 5's version :  Uptodate
  • Latest Drupal 6's stable version : 6.x-2.4
  • Dependency for the other modules : Yes
  • Better Alternatives : No
  • Special note for upgrade :No

 Boost by  @Arto , @wulff , @mikeytown2   Boost provides static page caching for Drupal enabling a very significant performance and scalability boost for sites that receive mostly anonymous traffic ...

  • Ported : Yes, Not only ported but thanks to its recent very active maintainer @mikeytown2 it's in a very good shape
  • Installed version : 5.x-1.0
  • Patched : Yes, but all the patches committed at least to 6.x version by the maintainers Remove symlink creation. Let each path have own file
  • Latest Drupal 5's version :  Uptodate
  • Latest Drupal 6's stable version : 6.x-1.18
  • Dependency for the other modules : No
  • Better Alternatives : No
  • Special note for upgrade : New version of boost are quite different, complete uninstall/reinstall is easier. Also make sure that you've updated .htaccess and create cache folder when moving to production site. admin/settings/performance/boost set cache folder to temp/boost

Comment Info by @Cainan , @mfer : This module allows for anonymous guests to keep persistent comment info between comment postings ...

  • Ported : Yes, Couldn't be better, Ported to core.
  • Installed version : 5.x-3.1
  • Patched : No
  • Latest Drupal 5's version :  Uptodate
  • Latest Drupal 6's stable version : In Core
  • Dependency for the other modules : No
  • Better Alternatives : No
  • Special note for upgrade : No, it just works

CAPTCHA by @wundo , @soxofaan , @Rob Loach and lots of others : A CAPTCHA is a challenge-response test most often placed within web forms to determine whether the user is human. The purpose of CAPTCHA is to block form submissions by spambots, which are automated scripts that post spam content everywhere they can ...

  • Ported : Yes,  Ported and also has a very active maintainer @soxofaan
  • Installed version : 5.x-3.2 
  • Patched : No
  • Latest Drupal 5's version :  Uptodate
  • Latest Drupal 6's stable version : 6.x-2.1
  • Dependency for the other modules : Yes
  • Better Alternatives : There are several alternative like Mollom and recaptcha (check out module's page for more alternatives) but since i need something simple i prefer the bundled captcha image. 
  • Special note for upgrade : Currently this module has some serious issue with caching modules please join us to fix this issue. Other than this issue it work fine

Calendar by @KarenS and the others : This module will display any Views date field in calendar formats, including CCK date fields, node created or updated dates, etc. Switch between year, month, and day views. Back and next navigation is provided for all views ...

  • Ported : Yes
  • Installed version : 5.x-2.7 
  • Patched : No
  • Latest Drupal 5's version :  Uptodate
  • Latest Drupal 6's stable version : 6.x-2.2
  • Dependency for the other modules : No
  • Better Alternatives : Not that i'm aware of
  • Special note for upgrade : New Calendar module benefits from Views argument feature, therefore simply converting its views does not work. Arguments should be added. Calendar's default views is a good example. The difference between old calendar views and the new is much so i found it easier to use views export feature and use the default views as a template

Lazy image loader by @sinasalek : Websites with lots of images on a single page might take a long time to load, and it sometimes annoys visitors. for fixing this problem this module loads only the images that are visible to or requested by visitors and the other images loads only when visitor scroll downs to them (This is the default behavior there are other options available live mouseover, mouseclick etc) ...

  • Ported : Yes
  • Installed version : Custom Module 
  • Patched : No
  • Latest Drupal 5's version :  Uptodate
  • Latest Drupal 6's stable version : 6.x-1.1
  • Dependency for the other modules : No
  • Better Alternatives : No
  • Special note for upgrade : No, default configuration works just fine

Voting API by @eaton , and the others: VotingAPI helps developers who want to use a standardized API and schema for storing, retrieving, and tabulating votes for Drupal content ...

  • Ported : Yes
  • Installed version : 5.x-1.6 
  • Patched : No
  • Latest Drupal 5's version :  Uptodate
  • Latest Drupal 6's stable version : 6.x-2.3  
  • Dependency for the other modules : Yes
  • Better Alternatives : Yes
  • Special note for upgrade : No

Fivestar by @quicksketch , @ezra-g and the others:

  • Ported : Yes
  • Installed version : 5.x-1.16 
  • Patched : Yes, but all the patches committed by the maintainers
  • Latest Drupal 5's version :  Uptodate
  • Latest Drupal 6's stable version : 6.x-1.19  
  • Dependency for the other modules : No
  • Better Alternatives : No
  • Special note for upgrade : It has some strange issue warning: Division by zero which has been reported several types, but it seems that it still exists. it's not critical bug however
    admin/settings/fivestar select oxygon theme

Image Picker by @hutch and @notsleepy: The Image Picker module is an image upload feature modeled after the image upload currently built into Wordpress but with a few improvements ...

  • Ported : Yes
  • Installed version : 5.x-2.2
  • Patched : Yes, Imageinsert does not work and no javascript error! Not sure wether it's applied or not
  • Latest Drupal 5's version :  Uptodate
  • Latest Drupal 6's stable version : 6.x-2.6
  • Dependency for the other modules : No
  • Better Alternatives : Yes , the combination of filefield, imagefield, insert, imagecache module
  • Special note for upgrade : No

 Javascript Aggregator by @derjochenmeyer , @Rob Loach : The aim of the JavaScript Aggregator module is to improve performance of your site with less server requests and bandwidth per page. In Drupal 5, all the JavaScript files will be aggregated into one file and optionally minified. JavaScript aggregation was brought into core with Drupal 6, so the Drupal 6 version of this module goes one step further to minify that file. ...

  • Ported : Yes
  • Installed version : 5.x-1.5
  • Patched : No
  • Latest Drupal 5's version :  Uptodate
  • Latest Drupal 6's stable version :  6.x-1.3
  • Dependency for the other modules : No
  • Better Alternatives : Support File cache but it's too much
  • Special note for upgrade : Configure it here admin/settings/performance

Nodewords by @kiamlaluno , @Robrecht Jacques , @hass and the others : This project allows you to set some meta tags for each Drupal page.

  • Ported : Yes
  • Installed version : 5.x-1.13 
  • Patched : No
  • Latest Drupal 5's version :  Uptodate
  • Latest Drupal 6's stable version :  6.x-1.11
  • Dependency for the other modules : No
  • Better Alternatives : No
  • Special note for upgrade : Make sure to enable at least one of the sub modules that come with Nodewords

Notify by @matt2000 , @beginner , @RobRoy and the others : The notify module allows users to subscribe to periodic emails which include all new or revised content and/or comments much like the daily news letters sent by some websites ...

  • Ported : Yes
  • Installed version : 5.x-1.3
  • Patched : Yes, "limiting number of result in notify module" and it's not committed and also notification of unapprouved comments... which is not committed
  • Latest Drupal 5's version :  Uptodate
  • Latest Drupal 6's stable version : 6.x-1.0
  • Dependency for the other modules : No
  • Better Alternatives : No
  • Special note for upgrade : Check the configuration admin/settings/notify and also users' configuration user/5/notify

Page Title by @nicholasThompson , @JohnAlbin , @robertDouglass and the others: The word "title" is a bit overloaded. Every piece of content in Drupal has a title, and so does every page. The page title is the one found in the HTML head inside the

tag. It is also used on SERPs (Search Engine Result Pages) and can greatly enhance your websites SEO (Search Engine Optimization) ...</p><ul><li>Ported :<span class="project-title"> Yes</span></li><li>Installed version : <span class="existing-version">5.x-2.3</span></li><li><span class="existing-version">Patched :</span><span class="existing-version">No</span></li><li><span class="existing-version">Latest Drupal 5's version : </span><span class="existing-version"> </span>Uptodate</li><li><span class="existing-version">Latest Drupal 6's stable version :</span> <a href="http://drupal.org/node/640996">6.x-2.3</a></li><li>Dependency for the other modules : No</li><li>Better Alternatives : No</li><li>Special note for upgrade : No</li></ul><p><span class="project-title"><a href="http://drupal.org/project/recent_blocks">Recent Blocks</a></span><span class="existing-version"> by </span><a title="View user profile." href="http://drupal.org/user/71463">@cotto</a> , <a title="View user profile." href="http://drupal.org/user/216048">@Frank Ralf</a><span class="existing-version"> and <a href="http://drupal.org/node/42872/committers">the others</a>: No ported but it's easily achieve using Views and Views UI</span></p><ul><li>Ported :<span class="project-title"> Yes</span></li><li>Installed version : <span class="existing-version">5.x-1.3</span></li><li><span class="existing-version">Patched :</span><span class="existing-version">No</span></li><li><span class="existing-version">Latest Drupal 5's version : </span><span class="existing-version"> </span>Uptodate</li><li><span class="existing-version">Latest Drupal 6's stable version : Non but the maintainer say </span><a href="http://drupal.org/node/587106">6.x-1.x-dev</a> should work</li><li>Dependency for the other modules : No</li><li>Better Alternatives : Yes, i'm going to use views instead</li><li>Special note for upgrade : No</li></ul><p><span class="project-title"><a href="http://drupal.org/project/recent_changes">Recent Changes</a> by </span><a title="View user profile." href="http://drupal.org/user/73064">@rötzi</a><span class="project-title"> , </span><a title="View user profile." href="http://drupal.org/user/19502">@toemaz</a><span class="project-title"> : </span>The recent changes module let's you track all editing on your site. A page is provided with a list of all revisions and comments in chronological order.</p><ul><li>Ported :<span class="project-title"> Yes</span></li><li>Installed version : <span class="existing-version">5.x-1.2</span></li><li><span class="existing-version">Patched : </span><span class="existing-version">No</span></li><li><span class="existing-version">Latest Drupal 5's version : </span><span class="existing-version"> </span>Uptodate</li><li><span class="existing-version">Latest Drupal 6's stable version : None but </span><a href="http://drupal.org/node/224958">6.x-1.x-dev</a> exist, don't know whether it works or not.</li><li>Dependency for the other modules : No</li><li>Better Alternatives : Yes, i'm going to use views instead</li><li>Special note for upgrade : No</li></ul><p><span class="project-title"><a href="http://drupal.org/project/supernav">Super Nav</a></span><span class="existing-version"> by </span><a title="View user profile." href="http://drupal.org/user/166383">@chrisshattuck</a> <span class="existing-version"> </span></p><ul><li>Ported :<span class="project-title"> Yes</span></li><li>Installed version :<span class="project-title"> </span><span class="existing-version"> 5.x-1.0</span></li><li><span class="existing-version">Patched : Yes and all committed, </span><a href="http://drupal.org/node/267413">"Don't force frame" and "Show URL in right frame" optoins issues</a> and <a href="http://drupal.org/node/245054">Some images link is not valid when has i18n module enabled or is inside admin</a></li><li><span class="existing-version">Latest Drupal 5's version : </span><span class="existing-version"> </span>Uptodate</li><li><span class="existing-version">Latest Drupal 6's stable version : </span><a href="http://drupal.org/node/241062">6.x-1.0</a></li><li>Dependency for the other modules : No</li><li><p>Better Alternatives : Yes, <span class="existing-version"> there is a better alternative by the same developer </span><span class="existing-version"> </span><a href="http://drupal.org/project/navigate">Navigate</a>. This new module unlike Super Nav does not use Frames and it's also more customizable. Drupal 6 also has lots of new administration modules like <a href="http://drupal.org/project/teleport" rel="nofollow">Teleport</a> , <a href="http://drupal.org/project/admin">Admin </a>, <a href="http://drupal.org/project/admin_menu">Admin Menu</a>, etc</p></li><li>Special note for upgrade : No</li></ul><p><span class="project-title"> </span><a href="http://drupal.org/project/scheduler">Scheduler</a> by <a title="View user profile." href="http://drupal.org/user/20786">@Eric Schaefer</a> , <a title="View user profile." href="http://drupal.org/user/265179">@skiminki</a> , <a title="View user profile." href="http://drupal.org/user/39030">@AjK</a> and<a href="http://drupal.org/node/3292/committers"> the others</a> : This module allows nodes to be published and unpublished on specified dates.</p><ul><li>Ported :<span class="project-title"> Yes</span></li><li>Installed version : <span class="existing-version">5.x-1.18</span></li><li><span class="existing-version">Patched : Yes, commited<br /></span></li><li><span class="existing-version">Latest Drupal 5's version : </span><span class="existing-version"> </span>Uptodate</li><li><span class="existing-version">Latest Drupal 6's stable version : </span> <a href="http://drupal.org/node/607892">6.x-1.6</a> <span class="version-date"> </span></li><li>Dependency for the other modules : No</li><li>Better Alternatives : No</li><li>Special note for upgrade : This module used to have some critical issues with publishing nodes incorrectly , so testing it again is very important</li></ul><p><span class="project-title"><a href="http://drupal.org/project/excerpt">Excerpt</a> by </span><a title="View user profile." href="http://drupal.org/user/56015">@hayesr</a> , <a title="View user profile." href="http://drupal.org/user/23157">@hanoii</a> and <a href="http://drupal.org/node/10811/committers">the others</a> <span class="project-title">: </span>Excerpt module allows you to enter a separate excerpt/summary/teaser for a node, which does not have to be a cut off version of the body.</p><ul><li>Ported :<span class="project-title"> Yes</span></li><li>Installed version : <a href="http://drupal.org/node/239311">5.x-1.3</a><span class="existing-version"> </span></li><li><span class="existing-version">Patched : No<br /></span></li><li><span class="existing-version">Latest Drupal 5's version : </span><span class="existing-version"> </span>Uptodate</li><li><span class="existing-version">Latest Drupal 6's stable version : </span><a href="http://drupal.org/node/607496">6.x-1.1</a></li><li>Dependency for the other modules : No</li><li>Better Alternatives : No</li><li>Special note for upgrade : No</li></ul><p><span class="project-title"><a href="http://drupal.org/project/tinytinymce">Tiny Tiny MCE</a></span><span class="existing-version"> by </span><a title="View user profile." href="http://drupal.org/user/90065">@Steve Lockwood</a> <span class="existing-version">: </span>Tiny Tiny MCE (TinyTinyMCE) is a Drupal 6 supported module supporting the wysiwyg editor tinymce.<span class="existing-version"> </span></p><ul><li>Ported :<span class="project-title"> Yes</span></li><li>Installed version : <span class="existing-version">5.x-1.5</span></li><li><span class="existing-version">Patched : No</span></li><li><span class="existing-version">Latest Drupal 5's version : </span><span class="existing-version"> </span>Uptodate</li><li><span class="existing-version">Latest Drupal 6's stable version : </span><a href="http://drupal.org/node/357053">6.x-1.12</a> <span class="version-date"> </span></li><li>Dependency for the other modules : No</li><li>Better Alternatives : Yes, T<span class="existing-version">here is a perfect alternative <a href="http://drupal.org/project/WYSIWYG">WYSIWYG</a></span></li><li>Special note for upgrade : Should be set here admin/settings/wysiwyg</li></ul><p><span class="project-title">theme_switcher</span><span class="existing-version"> : A custom module i created for separating view and admin theme more accurately<br /></span></p><ul><li>Ported :<span class="project-title"> No<br /> </span></li><li>Installed version : Custom</li><li><span class="existing-version">Patched : No</span></li><li><span class="existing-version">Latest Drupal 5's version : </span><span class="existing-version"> </span>Uptodate</li><li><span class="existing-version">Latest Drupal 6's stable version : No<br /></span></li><li>Dependency for the other modules : No</li><li>Better Alternatives :<span class="existing-version"> Yes, Drupal 6 has its functionality build-in there is also two other modules for more customization <a href="http://drupal.org/project/admin_theme">Administration theme</a> and <a href="http://drupal.org/project/system_theme">System Theme++</a></span></li><li>Special note for upgrade : You can set it here admin/settings/admin</li></ul><p><span class="project-title"> </span><span class="project-title"><a href="http://drupal.org/project/default_filter">Default Filter</a> by </span><a title="View user profile." href="http://drupal.org/user/39382">@dllh</a><span class="project-title"> : </span>This module allows you to set default filter formats per node type per role, preventing users from having to tick a checkbox every time they add a new node. This is useful for sites that set the global default to a less rich format to prevent anonymous users from using (for example) full HTML.</p><ul><li>Ported :<span class="project-title"> No<br /> </span></li><li>Installed version : <span class="existing-version">5.x-2.1</span></li><li><span class="existing-version">Patched : No</span></li><li><span class="existing-version">Latest Drupal 5's version : </span><span class="existing-version"> </span>Uptodate</li><li><span class="existing-version">Latest Drupal 6's stable version : No</span></li><li>Dependency for the other modules : No</li><li>Better Alternatives :<span class="existing-version"> Yes, </span><span class="project-title"> much better alternative is available <a href="http://drupal.org/project/better_formats">Better Formats</a></span></li><li>Special note for upgrade : Select co-author as well here admin/settings/filters/4<br />Select smart content for both webmaster and co-atuhor  admin/settings/filters/defaults </li></ul><p><span class="project-title"><a href="http://drupal.org/project/smartlinebreakconverter">Smart Line Break Converter</a></span><span class="existing-version"> by </span><a title="View user profile." href="http://drupal.org/user/182824">@nschelly</a> <span class="existing-version">: </span>The smartlinebreakconverter will selectively apply the line break converter filter (in the core filter module) based on whether it thinks it's necessary.</p><ul><li>Ported :<span class="project-title"> No<br /> </span></li><li>Installed version : <span class="existing-version">5.x-1.1</span></li><li><span class="existing-version">Patched : No</span></li><li><span class="existing-version">Latest Drupal 5's version : </span><span class="existing-version"> </span>Uptodate</li><li><span class="existing-version">Latest Drupal 6's stable version : No</span></li><li>Dependency for the other modules : No</li><li>Better Alternatives :<span class="existing-version"> No</span></li><li>Special note for upgrade : No</li></ul><p><em>In the next article i'm going to check the remianing non critical modules and the prepare the development copy for upgrade and finally actuallay doing the upgrade</em></p><div id="nuan_ria_plugin"><object id="plugin0" style="position: absolute; z-index: 1000;" type="application/x-dgnria" height="0" width="0"><param name="tabId" value="ff-tab-332" /><param name="counter" value="1215" /></object></div><form action="/content/upgrading-drupal-5-drupal-6-part-1" accept-charset="UTF-8" method="post" id="fivestar-form-node-1319" class="fivestar-widget"> <div><div class="fivestar-form-vote-1319 clear-block"><input type="hidden" name="content_type" id="edit-content-type" value="node" /> <input type="hidden" name="content_id" id="edit-content-id" value="1319" /> <div class="fivestar-form-item fivestar-combo-text fivestar-average-stars fivestar-labels-hover"><div class="form-item" id="edit-vote-wrapper"> <input type="hidden" name="vote_count" id="edit-vote-count" value="91" /> <input type="hidden" name="vote_average" id="edit-vote-average" value="20.4396" /> <input type="hidden" name="auto_submit_path" id="edit-auto-submit-path" value="/fivestar/vote/node/1319/vote" class="fivestar-path" /> <select name="vote" class="form-select" id="edit-vote-1" ><option value="-">Select rating</option><option value="20">Poor</option><option value="40" selected="selected">Okay</option><option value="60">Good</option><option value="80">Great</option><option value="100">Awesome</option></select><input type="hidden" name="auto_submit_token" id="edit-auto-submit-token" value="b6fe1d78e8866209bd25b6b9e20d03ca" class="fivestar-token" /> <div class="description"><div class="fivestar-summary fivestar-summary-combo fivestar-feedback-enabled"><span class="user-rating">Your rating: <span>None</span></span> <span class="average-rating">Average: <span>1</span></span> <span class="total-votes">(<span>91</span> votes)</span></div></div> </div> </div><input type="hidden" name="destination" id="edit-destination" value="node/1319" /> <input type="submit" name="op" id="edit-fivestar-submit" value="Rate" class="form-submit fivestar-submit" /> <input type="hidden" name="form_build_id" id="form-dvq4M6HKDVFbh0oYv5Ianm12LAKpsROMOHbwyLHQ7PA" value="form-dvq4M6HKDVFbh0oYv5Ianm12LAKpsROMOHbwyLHQ7PA" /> <input type="hidden" name="form_id" id="edit-fivestar-form-node-1319" value="fivestar_form_node_1319" /> </div> </div></form> </div> </div> <div class="links"> <ul class="links inline"><li class="comment_forbidden first"><span><a href="https://sina.salek.ws/user/login?destination=comment%2Freply%2F1319%23co...">Login</a> to post comments</span></li> <li class="addtoany last"><span> <a class="da2a_button" href="http://www.addtoany.com/share_save?linkurl=https%3A%2F%2Fsina.salek.ws%2..." id="da2a_1"><img src="https://sina.salek.ws/sites/all/modules/addtoany/images/share_save_171_1..." width="171" height="16" alt="Share this"/></a> <script type="text/javascript"> <!--//--><![CDATA[//><!-- da2a.script_load(); //--><!]]> </script> </span></li> </ul> </div> </div><!-- /inner --> </div><!-- /node-1319 --> </div><!-- /content-content --> </div><!-- /content-inner-inner --> </div><!-- /content-inner --> </div><!-- /content-region-inner --> </div><!-- /content-region --> </div><!-- /content-group-inner --> </div><!-- /content-group --> </div><!-- /main-content-inner --> </div><!-- /main-content --> </div><!-- /main-group-inner --> </div><!-- /main-group --> </div><!-- /main-inner --> </div><!-- /main --> </div><!-- /main-wrapper --> <!-- postscript-bottom row: width = grid_width --> <!-- footer row: width = grid_width --> <!-- footer-message row: width = grid_width --> <div id="footer-message-wrapper" class="footer-message-wrapper full-width"> <div id="footer-message" class="footer-message row grid16-16"> <div id="footer-message-inner" class="footer-message-inner inner clearfix"> <div id="footer-message-text" class="footer-message-text block"> <div id="footer-message-text-inner" class="footer-message-text-inner inner clearfix"> <table border="0"><tbody><tr><td><a rel="license" href="http://creativecommons.org/licenses/by-sa/3.0/us/"><img class="mceItem" src="http://i.creativecommons.org/l/by-sa/3.0/us/88x31.png" alt="Creative Commons License"></a></td><td valign="top" align="left">Except where otherwise <span class="subfoot">noted (Projects section)</span>, content on this site is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by-sa/3.0/us/">Creative Commons Attribution-Share Alike 3.0 United States License</a></td></tr></tbody></table></div><!-- /footer-message-text-inner --> </div><!-- /footer-message-text --> </div><!-- /footer-message-inner --> </div><!-- /footer-message --> </div><!-- /footer-message-wrapper --> </div><!-- /page-inner --> </div><!-- /page --> <!--[if (IE 6)]> <script type="text/javascript"> var IE6UPDATE_OPTIONS = { icons_path: "https://sina.salek.ws/sites/all/modules/ie6update/images/", message: "Internet Explorer is missing updates required to view this site. Click here to update... ", url: "http://www.microsoft.com/windows/internet-explorer/default.aspx" } </script> <script type="text/javascript" src="https://sina.salek.ws/sites/all/modules/ie6update/ie6update.js"></script> <![endif]--> <script type="text/javascript" src="https://sina.salek.ws/sites/all/modules/syntaxhighlighter/syntaxhighligh..."></script> <script type="text/javascript"> <!--//--><![CDATA[//><!-- if (Drupal.jsEnabled) { $(document).ready(function () { $('a').Tooltip({ delay: 0, track: true, showURL: false, showBody: "; " }); }); } //--><!]]> </script> <script type="text/javascript"> <!--//--><![CDATA[//><!-- da2a.targets=[ {title:'Upgrading Drupal 5 to Drupal 6, Part 1 | Sina Salek Official Site',url:'https://sina.salek.ws/content/upgrading-drupal-5-drupal-6-part-1'}]; da2a.html_done=true;if(da2a.script_ready&&!da2a.done)da2a.init();da2a.script_load(); //--><!]]> </script> <script> (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); ga('create', 'UA-898205-3', 'auto'); ga('send', 'pageview'); </script> </body> </html> <!-- Page cached by Boost @ 2021-08-30 16:27:25, expires @ 2021-12-20 15:27:25 -->
Feb 16 2010
Feb 16

Fellow Optaros colleague, Chris Fuller, and I want to present on the Alfresco-Drupal integration at Drupalcon in San Francisco (April 19-21). If you’re interested in Alfresco, Drupal, and CMIS (any or all of the above), please vote for our session.

Jan 07 2010
Jan 07

Recently I had the privilege of working on the conversion of Novus Biological's website to Drupal 6, selling scientific supplies using Ubercart. I did this as a freelancer for the talented folks at SpireMedia, working with them as well as with fellow Vintage Digital member Ben Jeavons. It was a fantastically intense experience.

Ubercart's a great system out of the box, but there were a number of features on the site which made this implementation difficult.

  1. There were about 70,000 products, which is probably two or three orders of magnitude greater than the usual Ubercart site.
  2. The site needed to work with three currencies, and four prices, depending on the user's location or selection. Ubercart doesn't handle this well.
  3. The site needed to have two kinds of discounts - ones available to most any user, and ones only available to users who had bought the discount with userpoints.

I'll probably write about all of these in time, but at the moment I want to comment on that second item.

A close-up of the novusbio website, showing the region-change text
If you take a look at www.novusbio.com, or the image above, you'll see a bit of text in the upper right hand corner which says something like 'US site', or 'Europe Site', or 'Great Britain site', or 'World Site', with a little arrow next to it letting you change your region. We would start by pre-selecting a region for the user based on which IP they were browsing from, and then users could change it. If you go to a product page on Novus' site, you'll see different prices based on which of these are currently selected, as well as different currency signs. Since doing something like this is what some people would like to be able to do, I wanted to take a little time to go over how we did this.

Note: If you're not a coder, and you need this functionality, then you're going to want to go find a coder to handle this for you. It's not currently possible to do by flipping a switch or installing a module. I'm going over the changes you need to make here, but the full set of changes, along with the needed patches to Ubercart, are contained in uc_example.zip, attached to this post.

To start with, you're going to need to define the regions or currencies you're doing prices for. I use the term regions, because you could well have two different areas of the world using the same symbol but different price amounts. So, I use a define to mark which regions I'm keeping track of.

define('UC_EXAMPLE_DOLLAR', 1);
define('UC_EXAMPLE_EURO', 2);

There are basically two things you want to handle when you allow for more than one currency in Ubercart:

  1. You want to change the currency symbol.
  2. You want to change the numeric amount of the price. You probably won't want to have a factor that you apply to the base price - I would rather suggest setting up alternate prices as CCK fields on your product nodes, much the same way that books in the US have set US and Canadian prices printed on them.

(As an aside, although the changes I'm discussing will allow you to have, say, both US and Euro prices on your site, you'll also at some point need to go through and write up some new reports that are multi-currency aware. After all, a report that says you sold 54,382 in the last two months doesn't mean anything if 24,382 was in US Dollars and 30,000 was in Euros. And, if you do up some reports like this, why not share them with the rest of us?)

There's two different places where you need to keep track of the current currency type as well - for when a user is browsing the site, or putting together an order, or reviewing her cart, or whichever, you'll want to store the current currency type in the session. On the other hand, if you're viewing an order - either in the very last step before the order is committed, (where a row has been written to uc_orders) or when viewing an order in the history, you need to have stored the currency type in the order data itself.

Changing the amount/magnitude of the item

Now, the slightly simpler part is changing the amount of the charge - if you're looking at $360, then we're talking about the 360 part of it. This amount needs to change on cart items or on products when they're viewed. We don't need to worry about changing the amount on the items in completed orders, because that amount quite sensibly is fixed when the order is committed.

I'm not going to go over every change you need to make - you might need to do a little work in your views to decide which price to use when displaying a view (indeed, views_customfield may be a good idea for that), but when you're changing how the product is displayed, you're basically doing a little work with hook_nodeapi:

* Implements hook_nodeapi().
function uc_example_nodeapi(&$node, $op, $a3 = NULL, $a4 = NULL) {
  if (
$op == 'view') {
$region_price = uc_example_get_node_price($node, $_SESSION['region']);
$context['class'][1] = 'sell';
$context['field'] = 'sell_price';
$node->content['sell_price']['#value'] = theme('uc_product_price', $region_price, $context, array('label' => !$a3));
$context['class'][1] = 'display';
$context['field'] = 'sell_price';
$node->content['display_price']['#value'] = theme('uc_product_price', $region_price, $context);


* Returns the price from the node, from the region passed in.
* @param stdClass $node Product node to get price for.
* @param integer $current_region Region to get the price for.
function uc_example_get_node_price($node, $current_region) {
  switch (
$current_region) {

When viewing the price in a person's cart, we'll want to use hook_cart_item($op, $item) to change the price for products.

* Implements hook_cart_item()
* This lets us change the base price based on the user's current region.
* See http://www.ubercart.org/docs/api/hook_cart_item
* @param String $op     Operation being done
* @param stdClass $item Cart item - usually a node of some sort.
function uc_example_cart_item($op, &$item) {

  </span>//dpm('hook_cart_item '. $op);
if ($op == 'load') {
$product_node = node_load($item->nid);

$region_id = 0;
// If we're viewing the cart item on the review page, we want to use the region
    // that's embedded in the order item, not the region that the user is using.
if ($_GET['q'] == 'cart/checkout/review') {
$order = db_fetch_object(db_query(
"SELECT uo.order_id, IFNULL(uom.region, 1) as mc_region,
        IFNULL(uom.currency_sign, '$') AS mc_currency_sign
        FROM {uc_orders} uo
        INNER JOIN {uc_order_products} uop ON (uo.order_id = uop.order_id)
        LEFT OUTER JOIN {uc_order_multicurrency} uom ON (uo.order_id = uom.order_id)
        WHERE uid = %d and order_status = '%s' AND uop.nid = %d ORDER BY uo.order_id DESC"
$user->uid, 'in_checkout', $item->nid
      if (
$order) {
$current_region = $order->mc_region;
      else {
$current_region = $_SESSION['region'];
    else {
$current_region = $_SESSION['region'];

    if (</span>$product_node) {
$item->price = uc_example_get_node_price($product_node, $current_region);

Changing the Currency Symbol

With that done, lets consider the currency sign. I like storing the user's currently selected symbol in $_SESSION['currency_sign'], and we'll also be storing it in orders as well. The first impulse you might have would be to start mucking about with variable_set('uc_currency_sign') - but changing that changes the symbol for everything at once, and you're no doubt hoping to have more than one customer at a time.

Instead you want to register a price handler with hook_uc_price_handler. Then, whenever a price is being determined by uc_price(), your price alteration function can determine the proper currency sign to use, and provide it.

* All we're doing with this price alteration function, is telling it to use for the
* currency sign the sign that we've stashed in $_SESSION.
function _uc_example_price_alter(&$price_info, $context, &$options) {
// default currency sign.
$options['sign'] = $_SESSION['currency_sign'];
  if (isset(
$context['subject']['order']) && $context['subject']['order']->order_id) {
$order = $context['subject']['order'];
    if (isset(
$order->mc_currency_sign)) {
$options['sign'] = $order->mc_currency_sign; // use sign from order!
    else {
$currency_sign = db_result(db_query("SELECT currency_sign FROM {uc_order_multicurrency} WHERE order_id = %d", $order->order_id));
      if (
$currency_sign) {
$options['sign'] = $currency_sign;

What I'm doing there is setting $options['sign'] - which is an array passed in by reference - and changing it to the sign we want it to have. First we set it by the $_SESSION, and then we check and see if the context - a collection of information about what this price is and where it's being presented - contains the order which this price is a part of. If it is a part of an order, then we use the sign saved as part of that order.

If you're wondering how this information gets to be part of the order, unsurprisingly the answer is we implement hook_order in our code.

* Implements hook_order().
* This is where we finalize the region_id and currency_sign.
function uc_example_order($op, &$arg1, $arg2) {

  if (</span>$op == 'new') {
// $arg1 is a reference to the order object.

    </span>$arg1->mc_currency_sign = $_SESSION['currency_sign'];
$arg1->mc_region = $_SESSION['region'];
  if (
$op == 'save' && $_GET['q'] == 'cart/checkout') {
// Now we need to save our updated region data to the order!
if (db_result(db_query("SELECT count(*) FROM {uc_order_multicurrency} WHERE order_id = %d", $arg1->order_id))) {
db_query("UPDATE {uc_order_multicurrency} SET region = %d, currency_sign = '%s' WHERE order_id = %d",
$_SESSION['region'], $_SESSION['currency_sign'], $arg1->order_id);
    else {
db_query("INSERT INTO {uc_order_multicurrency} (order_id, region, currency_sign) VALUES (%d, %d, '%s')",
$arg1->order_id, $_SESSION['region'], $_SESSION['currency_sign']);
  if (
$op == 'load') {
$multicurrency = db_fetch_array(db_query("SELECT region, currency_sign FROM {uc_order_multicurrency} WHERE order_id = %d",
$arg1->mc_region = $multicurrency['region'];
$arg1->mc_currency_sign = $multicurrency['currency_sign'];

And... then this is where we get to the unfortunate part. Remember that price handler above, where we pull the order information out of the context? Unfortunately, Ubercart is really inconsistent about providing us that needed information, even when the call to uc_price is happening in a function where $order is already just sitting there, all loaded up. So, I'd like to introduce these patch files, which are provided to you inside of uc_example.zip:

  • ubercart-621494.patch
  • ubercart-display-symbols.patch
  • ubercart-order-price.patch

These three patch files need to be applied to Ubercart, and force it to provide the $order as part of the $context when uc_price is being called. If you look through these files, you'll see that most of the time this $order object is already there - I'm just adding it to the $context['subject'] array. (Unfamiliar with patches? Copy the three files to the Ubercart directory, change your directory to that Ubercart directory, and then execute patch -p0 < {filename} for each one.)

Enclosed is a module that contains the changes I've mentioned above. It assumes that you've used cck to add a 'price_us' and 'price_euro' field to the product nodes, but otherwise you can use it as is, plug it into a test Ubercart installation, run, and try it out. The patches are only necessary when viewing a finalized order, particularly in the admin area, so you don't even need to use that when just experimenting with the code.

AttachmentSize uc_example.zip7.1 KB
Dec 21 2009
Dec 21

Both drush and rsync are handy tools to automate the deployment of Drupal sites. You just have to be careful how you combine them.

In my development environment I have a local CVS checkout of drush and I use rsync to push that directory to the remote production server. This way I'm sure both development and production use the same version of drush without the need to install a CVS client on the production server.

Unfortunately my initial attempts to get drush running on the remote system always resulted in the following error message:

The command 'drush.php help' could not be found.

Initially I assumed there was something wrong with the path to the drush executable or the code that detects the location of the drush.php file but only after a while I found the real culprit.

To reduce the number of files that need to be synced I used rsync's --cvs-exclude option. This option makes rsync ignore certain files and directories in the same way CVS does. For example it will filter out all CVS directories. It turns out that a lot more files get excluded:

RCS SCCS CVS CVS.adm RCSLOG cvslog.* tags TAGS .make.state .nse_depinfo *~ #* .#* ,* _$* *$ *.old *.bak *.BAK *.orig *.rej .del-* *.a *.olb *.o *.obj *.so *.exe *.Z *.elc *.ln core .svn/ .git/ .bzr/

Notice core in that list? Guess in which directory drush keeps major parts of its code! By using the --cvs-exclude option of rsync, my remote drush directory was missing approximately 2500 lines of PHP code resulting in the bizarre error message.

The solution is to stop using the --cvs-exclude option or use the --filter option to tune the list of files to exclude during syncing.

Oct 13 2009
Oct 13

UPDATE: Screencast now lives here:

[embedded content]

I recorded a quick screencast of a simple integration we did to show Open Atrium leveraging Alfresco as a formal document repository via CMIS. This leverages the CMIS Alfresco module we developed and released on Drupal.org.

As I point out in the screencast, there’s not much to the integration from a technical standpoint. Open Atrium is Drupal and the CMIS module already has a CMIS repository browser. So, all we had to do was expose the module as a “feature”, which is something Open Atrium uses to bundle modules together that create a given chunk of functionality.

Readers familiar with Alfresco Share will instantly recognize the Open Atrium concepts. Instead of “sites” Atrium uses “groups”. Instead of “pages” or “tools”, Atrium uses “features”. The overall purpose, self-provisioned team-based collaboration, is the same and many of the tools/features are the same (blog, calendar, member directory). I’m not advocating using one over the other–as usual, what works best for you depends on a lot of factors. I just thought Atrium provided a nice way to show yet another example of Drupal and Alfresco together (post).

Oct 07 2009
Oct 07

One of the scripts in my bag of tools to check whether a site is still up and running is content-check.pl. This little script compares the hash of a web page against a previously created reference hash. If the content of the web page has modified, this script will detect it and send an email.

You have to be very careful which page you check because even the slightest difference in the HTML source will be detected.

In Drupal you should not use any page that contains a form because each time the form is rendered a unique form id is generated and inserted into the HTML. This results in a different page hash and will cause the script to send an warning email.

Keep in mind that, if you have enabled the User login block, each page contains a small login form and is not a good candidate for monitoring through content-check.pl. You might have to resort to monitoring your update.php file instead.

Sep 28 2009
Sep 28

Unbeknownst to people who visit my website directly, Just a Gwai Lo has served Google AdSense ads for some time now. Very early on I came to the conclusion that showing automatically-generated content-aware ads on personal sites was tacky, since those visiting directly were likely those who wanted to develop a personal relationship, no matter how loosely defined, with the author. That applies to the relationship I want to build with my readership as well: if they visit the site directly in a browser of subscribe to a feed, they shouldn't have something hawked at them.

People visiting through search engines, however, get no such treatment. They're likely people I don't know and, based on what people search for—and don't find on the site—don't want to have a relationship with. So anybody visiting in from one of the major search engines would see a Google AdSense ad, and over the years I've made enough to help fund some small vacations to the United States. (Since Google pays me in American dollars, it goes in my USD account, which I then withdraw for trips south of the border.) Nothing spectacular, and definitely not worth the amount of investment I put into the custom PHP script which looped through a list of domains returning TRUE if one of those domains were Google, Windows Live Search (as it was known then), Yahoo! and some others. Now I use Drupal, the following contributed modules and a one-line conditional in the block display filter to show ads to people finding my site that way.

  • AdSense, with the component modules "AdSense core" and "Managed ads" enabled.
  • Search Engine Referer API, which returns FALSE if the referrer is not a search engine and an object (essentially TRUE) if it is.
  • The following line of PHP in the "Page specific visibility settings" for the ad's block: I could have written a ternary operator, but I sought clarity in this case.

After my next payment, I'll disable this feature as part of an effort to simplify my Drupal install. This will cut down on the number of updates needed and ever so slightly decrease the load on the server. It's never been worth displaying them based on the time invested in this, though I can say the money plus experience with the advertising system in general did offset it a bit.

Sep 15 2009
Sep 15

People want intranets that are fun and easy to use, full of compelling content relevant to their job, and enabled with social and community features to help them discover connections with other teams, projects, and colleagues. IT wants something that’s lightweight and flexible enough to respond to the needs of the business that won’t cost a fortune.

That’s why Drupal + Alfresco is a great combination for things like intranets like the one Optaros built for Activision and why we had a record-breaking turnout for the Drupal + Alfresco webinar Chris Fuller and I did today. Thanks to everyone who came and asked good questions. I’ve posted the slides. Alfresco recorded the webinar so they’ll make it available soon, I’m sure. When that happens, I’ll update the post with a link. Until then, enjoy the slides.

[UPDATE: Fixed the slideshare link (thanks, David!) and added the links to the webinar recording below]

1. Streaming recording link:

2. Download recording link:

Aug 29 2009
Aug 29

Three weeks until Talk Like a Pirate Day, and next week the Drupal project freezes its code in anticipation of the version 7 release of the CMS. In anticipation of that, I've updated the Pirate module, which turns all contributed content on a Drupal-powered website into pirate-speak for just the day of September 19th, to include a developmental release for the Drupal 7 platform. I'd like for people to test it out, and in order to do so on a day other than September 19th is to either modify the date in the following code of the module or remove it altogether.

  if (date('md') != '0919') {
    return $text;

I see that similar code to my module has been included in the Dialectic module, which supports other novelty input formats. I may be convinced to officially merge my work into that project in the future, though my module differs in that it activates on only one day a year.

Any issues specific to the Drupal 7 conversion can added be to the issue for that. One sticky, longstanding issue that I could use some help with and affects all versions is a bug involving URLs that contain 'ing'.

I pledge that Pirate will have a full Drupal 7 release on the day that Drupal 7 is released. #D7CX:

Jun 14 2009
Jun 14

Addison Berry, aka @add1sun, presented about her experience as documentation lead for the Drupal content management system project the other day at the Writing Open Source conference in Owen Sound. In her role as chief cat-herder, she found that the most difficult people aren't poisonous. Instead they just don't know how to communicate with the community, and they need to translate where they're coming from to the way the community operates. It's hard work, she reports, to turn them into a contributor. She referred the audience to the "Poisonous People" presentation by the Subversion people, as yet unwatched by yours truly.

Addison talked about religious wars that occasionally break out. That is, the crux of the issue is more important than the resolution, and often leads to inaction. She also discussed the differences between recruiting in the corporate world and recruiting in the open source world. For private companies, they hire a skillset that they can filter for by listing the job requirements, either explicitly or implied. In open source, she says, you have the skillset first and you work with it. Many cats scratching their own itch, hence the herding to get them to scratch the community's itches too. The people you get working on a project have a rich background, both in terms of skills and life history. Skillsets include a lot of non-technical backgrounds in open source (Addison has an anthropology degree, for example, and my education is in political science).

Drupal has a large mass of documentation, and Addison is trying to whoop up energy in managing the base of existing documentation for Drupal 5 and 6 while gearing up for writing the documentation for the upcoming Drupal 7.

Open source has a natural passion that brings people together. Showing the example of a rowing team on her slide illustrated the need to hire a coach to tell them when to row. Herding involves keeping lines of communication open and opening up new ones as well as banging on pots about documentation. Instead of telling people what they can do, empower them by including them in the conversation. Addison, as leader, knows what she won't do and has so far been able to find people who will. Tracking metrics around the documentation—answering a question I had before I had the chance to ask it—Addison is not interested in, but she found someone who is. Many "soft-skills", such as facilitation, have come in handy even if the person with the skill does not claim membership in the software community. Also universities and their students have found time and energy to contribute usability testing as part of course credit or as part of their graduate studies.

Letting go and getting out of the way: Addison wanted the vision to be perfect, but quickly understood that she can't lead the charge or drag it out all the time: instead she recognized the need to let people run with things and support them. Getting people to trust you that that's the right direction.

Jun 09 2009
Jun 09

Last week I received a copy of Matt Butcher's "Drupal 6 JavaScript and jQuery" from Packt Publishing.

The book starts off with a gentle introduction into Drupal modules, Drupal themes and jQuery. These first chapters are pretty boring if you are already familiar with Drupal development but they aid newcomers in getting up to speed.

The next couple of chapters dive into the dark corners of Drupal's JavaScript capabilities. They explain things like Drupal behaviors, the JavaScript translation functions, the JavaScript theming functions and how to use AJAX. Each of these chapters is built around a very practical example that is analyzed thoroughly and each chapter also contains numerous tips about Drupal, JavaScript and jQuery. Good stuff!

I've learned quite a few things while reading this book so if you need to do any JavaScript or jQuery development in Drupal and if the cryptic JavaScript Startup Guide on drupal.org doesn't make a lot of sense to you than this book is definitely for you.

Jun 04 2009
Jun 04

In a week, I will attend the Writing Open Source conference in Owen Sound, Ontario. I'm excited to meet some of my colleagues in the field of open source documentation, having written the bulk of the support materials for Bryght, the Drupal-powered hosted service. I'm particularly interested in meeting those working to document open source tools other than Drupal, to gain some perspective on what's out there and what's needed.

Writing documentation was my first task at Bryght back in 2004. I recall spending part of that Christmas break furiously jotting down the important steps to creating dynamic and community websites. This included checklists, instructions and descriptions of module settings and how people could take advantage of them. The initial push of documentation made the subsequent job of supporting customers easy: instead of each time having to explain how to do something, I quickly pointed to the documentation, either through a link or a copy & paste. Along the way I even heard from non-customers thanking me for the handy references. After the second time someone asked we documented the answer. (We even wrote documentation after the first time someone asked a question.) Sometimes it didn't work, and sometimes the documentation wasn't all that great or hard to find. We allowed comments and opened the forums and listened to feedback when what we wrote didn't make a whole lot of sense. That's the experience I'd like to share with the conference, and I'd like to hear of others' experiences in making complex software more understandable.

After the weekend conference, I'll spend a couple of full days in Toronto proper, getting some much needed distance from Vancouver. I'd like to meet with some of the Toronto Drupal heads, and others I know (but haven't met) from other online communities I'm part of. Sadly, my favourite baseball squadron, the Toronto Blue Jays, play on the road in late June. Surely a local pub will have the games in HD?

The themes at Writing Open Source have a lot in common with two sessions I attended at FSOSS (the Free and Open Source Symposium) in 2006. I wrote two well-received pieces about the symposium, both notes on sessions at the conference:

(Audio and video for both presentations are available at http://fsoss.senecac.on.ca/2006/recordings/)

I'm looking forward to the sessions in Owen Sound next week, and to sharing what I learn there!

Jun 01 2009
Jun 01
Screenshot of the Cherry Blossom theme for Drupal

More than 5 years ago now, I sat down with Raincity Studios' Mark Yuasa to discuss the redesign of this blog, Just a Gwai Lo. It was springtime in Vancouver, and the cherry blossom trees around the lower mainland were blooming, so I suggested that as the visual theme for the blog. Then powered by WordPress, Mark delivered two designs in a few weeks and a few months after choosing the overwhelmingly pink comp, I switched to Drupal, bringing the theme along with me.

It's now time to release the theme to the general public. If you visit the Cherry Blossom theme project page on Drupal.org, you can install a Drupal 6-compatible version of the theme. I put up a demonstration site so that you can see the theme in action, as I've long moved onto another theme (currently the Deco theme). One known issue that I'd like help with is an alignment problem with Internet Explorer 6, after fixing I'll release a 1.1 version.

May 11 2009
May 11

If we are to believe in timestamps, on October 7th, 2006, I took over maintainership of the Mobile theme for the Drupal CMS. At the time there was no iPhone, and stripped down graphics-free versions of websites made it easier for people with small screens on their phones to get to the information quickly. Now, relatively larger screens coupled with effectively unlimited data plans make websites more consumable by tiny devices. My maintainership of the theme continues unabated, as today I (finally) released official 1.0 versions for Drupal 5 and Drupal 6, and dropped official support for the 4.7 version. m.justagwailo.com serves as the Mobile theme's demonstration site. Thanks to Bèr Kessels for originally writing the theme and webschuur.com for its original sponsorship.

A note about version numbers: when the new system for CVS tagging came out way back when, somehow it occurred to me that "DRUPAL-6-4" was the correct version number to assign to a developmental release. What that meant in practices was the version number for the theme ended up as "4.x". Looking at the usage statistics, at this writing, the overwhelming majority of sites that have deployed the Mobile theme use that developmental release. It is my hope that they all move to the 1.x branch, either developmental or official, as that's where all development will happen from now on.

As with any software, there are

Apr 28 2009
Apr 28

I’ll be in Chicago tomorrow for the Alfresco Meetup. I’ll be speaking during the Barcamp on Alfresco and Drupal integration with CMIS (module, screencast). I’ll also have the Alfresco-Django integration running on my laptop. I may not have time to show Alfresco-Django during my slot, but I’ll be happy to stick around and do informal demos and talk about either integration if you’re interested because I’d like your feedback on it.

Apr 20 2009
Apr 20

People often need to build a custom user interface on top of the Alfresco repository and I see a lot of people asking general questions about how to do it. There are lots of options to consider. Here are four options for creating a user interface on top of Alfresco, at a high level:

Option 1: Use your favorite programming language and/or framework to talk to Alfresco via REST or Web Services. PHP? Python? Java? Flex? Whatever, it’s up to you. The REST API is nice because if you can’t find a URL that does what you need it to out-of-the-box, you can always roll-your-own with the web script framework. This option offers the most flexibility and creative freedom, but of course you might end up building constructs or components that you may have gotten “for free” from a higher-level framework. Optaros‘ streamlined web client, DoCASU, built on Ext-JS, is one freely-available example of a custom UI on top of Alfresco but there are others.

Option 2: Use Alfresco’s Surf framework. Alfresco’s Surf framework is just that–it’s a framework. Don’t confuse it with Alfresco Share which is a team-centric collaboration client built on top of Surf. And, don’t assume that just because a piece of functionality is in Share it is available to you in the lower-level Surf framework. You may have to do some extra work to get some of the cool stuff in Share to work in your pure Surf app. Also realize that Surf is brand new and still maturing. You’ll be quickly disappointed if you hold it to the same standard as a more widely-used, well-established framework like Seam or Django. Surf is a good option for quick, Alfresco-centric solutions, especially if you think you might want to leverage Alfresco’s browser-based site assembly tool, Web Studio, at some point in the future. (See Do-it-yourself Alfresco Surf Code Camp).

Option 3: Customize the Alfresco “Explorer” web client. There are varying degrees to which you can customize the web client. On one end of the spectrum you’ve got Freemarker “presentation templates” followed closely by XML configuration. On the other end of the spectrum you’ve got more elaborate enhancements you can make using JavaServer Faces (JSF). Customizing the Alfresco Explorer web client should only be considered if you can keep your enhancements to an absolute minimum because:

  1. Alfresco is moving away from JSF in favor of Surf-based clients. The Explorer client will continue to be around, but I wouldn’t expect major efforts to be focused on that client going forward.
  2. JSF-based customizations of the web client can be time-consuming and potentially complex, particularly if you are new to JSF.
  3. For most solutions, you’ll get more customer satisfaction bang out of your coding buck by building a purpose-built, eye-catching, UI designed with your specific use cases in mind than you will by starting with the general-purpose web client and extending from there.

Option 4: Use a portal, community, or WCM platform. This includes PHP-based projects like Drupal (Drupal CMIS Screencast) or Joomla as well as Java-based projects like Liferay and JBoss Portal. This is a good option if you have requirements that match up well with the built-in (or easily added-on) capabilities of those platforms.

It’s worth talking about Java portal servers specifically. I think people are struggling a bit to find The Best Way to integrate Alfresco with a portal. Of course there probably is no single approach that will fit every situation but I think Alfresco (with help from the community) could do more to provide best practices.

Here are the options you have when integrating with a portal:

Portal Option 1: Configure Alfresco to be the replacement JSR-170 repository for the portal. This option seems like more trouble than it is worth. If all you need is what you can get out of JSR-170, you might as well use the already-integrated Jackrabbit repository that most open source portals ship with these days unless you have good reasons not to. I’m open to having my mind changed on this one, but it seems like if you want to use Alfresco and a portal, you’ve got bigger plans that are probably going to require custom portlets anyway.

Portal Option 2: Run Alfresco and the portal in the same JVM (post). This is NOT recommended if you need to scale beyond a small departmental solution and, really, I think with the de-coupling of the web script engine we should consider this one deprecated at this point.

Portal Option 3: Run the Alfresco web script engine and the portal in the same JVM. Like the previous option, this gives you the ability to write web scripts that are wrapped in a portlet but it cuts down on the size of the web app significantly and it frees up your portal to scale independently of the Alfresco repository tier. It’s a fast development cycle once you get it set up. But I haven’t seen great instructions for setting it up yet. Alfresco should document this on their wiki if they are going to support this pattern.

Portal Option 4: Write your own portlets that make services calls. This is the “cleanest” approach because it treats Alfresco like any other back-end you might want to integrate with from the portal. You write custom portlets and have them talk to Alfresco via REST or SOAP. You’ll have to decide how you want to handle authentication with Alfresco.

What about CMIS?

CMIS fits under the “Option 1: Use your favorite programming language” and “Portal Option 4: Write your own portlets” categories. You can make CMIS calls to Alfresco using both REST and SOAP from your own custom code, portlet or otherwise. The nice thing about CMIS is that you can use it to abstract the underlying repository so that (in theory) your front-end code will work with different CMIS-compliant back-ends. Just realize that CMIS isn’t a fully-ratified standard yet and although a CMIS implementation is in the Enterprise version of Alfresco, it isn’t clear to me whether or not you’d be supported if you had a problem. (The last response I saw on this specific question was a Peter Monks tweet saying, “I don’t think so”).

The CMIS standard should be approved by the end-of-the-year and if Alfresco’s past performance is an indicator of the future, they’ll be the first to market with a production-ready, fully-supported CMIS implementation based on the final spec.

Pick your poison

Those are the options as I see them. Each one has trade-offs. Some may become more or less attractive over time as languages, frameworks, and the state of the art evolve. Ultimately, you’re going to have to evaluate which one fits your situation the best. You may have a hard time making a decision, but you have to admit that having to choose from several options is a nice problem to have.

Apr 10 2009
Apr 10

Is writing Drupal tests boring? Maybe. Is running tests boring? Not anymore!

There is a patch for Drupal 7 pending that introduces a couple of new hooks into the simpletest module. These hooks will allow you to listen to the progress of a test run and react to tests that pass or fail. I have already created a new testlistener module that uses these new hooks to execute shell commands.

Some examples:

I have a Dell XPS laptop with built-in LEDs that can be controlled using the dellLEDCtl shell command (Linux). Using these new hooks, I can turn the LEDs green at the start of a test run, make them red when a test fails and turn them off when the test run has completed. This is very similar to the functionality of my Eclipse XPS plugin that shows JUnit results in Eclipse.

Another (typical) example is to have two lava lamps, a red one and a green one. You connect both of them to an X10 device (I have an X10 starterkit from IntelliHome) so you can turn them on/off with the heyu shell command (Linux). The testlistener module allows you to let the lamps reflect the status of the Drupal code. Other people are using this setup in their (non-Drupal) continuous integration environment.

If lamps are too soft for you, you might consider shooting missiles instead.

Drupal testing will never be boring again ;)

Jan 10 2009
Jan 10

I'll be attending 2 major conferences the next couple of weeks.

First of all there's FOSDEM in Brussels on February 7th and 8th. I'm particularly interested in the Drupal devroom on Sunday, but there are plenty of other open source sessions throughout the weekend.

The second conference I'll be attending is DrupalCon in Washington from March 4th until 7th. I expect to pick up a couple of tips and tricks about Drupal 6 and I hope to learn a lot about the upcoming changes in Drupal 7. I'm pretty sure that being among that many Drupalistas will help me to find more ways to give something back to the community.

I never attended FOSDEM nor DupalCon (and I haven't been in the US recently) so I'm looking forward to both events!

Dec 15 2008
Dec 15

We needed to disable all of Drupal’s CSS files from our theme. Here’s how we did it:

function THEMENAME_preprocess(&$variables) {

  // Get rid of all of Drupal's CSS files
  $css = drupal_add_css();
  foreach ($css['all']['module'] as $file => $status) {
    if (!strstr($file, 'modules/MYMODULE')) {
  $variables['styles'] = drupal_get_css($css);

We also wanted (no *real* need) to use screen.css rather than style.css, so we edited THEMENAME.info to have this:

stylesheets[all][] = reset.css
stylesheets[all][] = screen.css

… and we removed the line for style.css from it.

Finally, as the superuser we went to /admin/build/modules (or /themes, can’t remember now) to refresh the theme cache. We also had to tick to enable the theme at /admin/build/themes as although we’d been using the theme for ages quite fine, it wasn’t actually ticked before.

And hey presto, it worked. Should probably add that it took waaaay too long to do though, so though we’d add this snipped for others to read.

Share this:

Like this:

Like Loading...
Nov 16 2008
Nov 16

In this episode: KDI applications on hold, new theme coming for core, installation insurrection, events, site launches.

Links from the episode

KDI Apps on hold until DrupalCon DC

CHX Installation Insurrection

Albin: New theme headed for the core


  • Drupal Showcase:
  • Pregnancy.org
  • SpreadThunderbird.com


Share | Download
Oct 16 2008
Oct 16

Google has just started testing a new service to inform webmasters that their CMS is vulnerable to security exploits. Currently they are only targetting administrators of sites running WordPress 2.1.1 but my guess is that they'll be adding support for other versions and other platforms in the future.

I hope that they'll be adding support for Drupal as well because it doesn't matter how many security issues the Drupal security team fixes, webmasters still need to install the latest security patches and upgrades to make a difference in the field.

Any initiative that helps administrators to keep their sites - running Drupal or anything else - safe is good.

Oct 07 2008
Oct 07

In Drupal, you can provide a description for each term in a taxonomy vocabulary. The default taxonomy term pages of Drupal 6 include the description at the top of each page (if only one term is present). Here's how you can achieve the same thing when using views.

  1. Make sure your theme has a page.tpl.php file. My theme is a subtheme of Zen so I copied the page.tpl.php file from the zen folder into my theme's folder.
  2. Create a duplicate of your theme's page.tpl.php file and call it page-taxonomy.tpl.php.
  3. Edit the new page-taxonomy.tpl.php file and add the following where you want the description to appear (for example at the end of the content-header):
    <?php if ($taxonomy_term_description): ?>
      <div id="taxonomy-term-description">
        <?php print $taxonomy_term_description; ?>
    <?php endif; ?>
  4. Edit your theme's template.php file and add the following function:
    function yourtheme_preprocess_page(&$vars, $hook) {
      $term = taxonomy_get_term(arg(2));
      $vars['taxonomy_term_description'] = filter_xss_admin($term->description);

After you clear your theme registry - by visting the admin/build/modules page - the term description will be displayed at the top of the page showing your taxonomy_term view.

Sep 27 2008
Sep 27

Lately there has been some discussion about the number of posts (and their quality) on Planet Drupal.

Personally I don't think Planet Drupal is hard to follow because of the large number of posts. There are plenty of other high profile planets with far more posts per day.

Let's compare:

Planet Posts per day Feeds aggregated Mozilla 18.2 234 Ubuntu 17.5 271 Apache 15.9 160 GNOME 14.1 254 Drupal 12.5 261 Android 7.6 21 PHP 6.6 97 Eclipse 6.3 169

Note: The number of posts per day is based on the Subscription trends for the last 30 days of my Google Reader.

The question whether all posts on Planet Drupal are on-topic is harder to answer but in my opinion Planet Drupal is doing just fine. I would say that at least 95% of all posts are Drupal-related. People familiar with Planet Apache will confirm that you have to look really hard to find any posts about Apache software among all those holiday pictures.

It is probably true that not all posts on Planet Drupal have the same high quality writing - I'm not even convinced that this posts has - but that is all part of the mission of Planet Drupal: "Planet Drupal aggregates broadly appealing, Drupal-related blog posts pertaining to the community at large (code, advocacy, marketing, infrastructure etc.)".

In the end, it is really easy to skip a post in your favorite feed reader, isn't it?

PS: I think it is far more annoying that the feed of Planet Drupal contains only teasers instead of full posts. Whenever something really interesting appears I have to click to read the full post. Most other planets only accept feeds with full posts.

Sep 11 2008
Sep 11

Lately, I’ve had various frustrations with Drupal which have moved me away from using it for various things. I’d like to go through where I’ve moved away from Drupal, why I’ve made those changes, and my future Drupal decisions.

WordPress rather than Drupal blog

To begin with, this blog is now on WordPress rather than Drupal – and I have to say that I’m loving it… and so are my non-geeky colleagues. It ticks all the right boxes. Its *really* user friendly. Its much easier to add photos (and videos) to posts. And it’s hardly taken long at all to setup.

So where did Drupal go wrong with this? Well, I guess its the ‘kitchen sink’ approach back-firing. In trying to be something to everyone, Drupal runs the risk of being less than perfect to to any one specific task too. WordPress, on the other hand, has the ability to focus on being the very best blogging software out there, and nothing else gets in the way of that or deters it from its ultimate goal.

But also, we needed to seaparate the business end of our website (the creation of yearbooks, using tens of thousands of nodes) from the nodes and users to do with the blog, partly because around this time every year we flush out the old site and start a new. So we were either going to have a separate Drupal install for the blog or use WordPress. We chose WordPress.

Form theming frustrations

Have a look at the form here and let me know how you’d create it using FAPI (we’re on Drupal 6.4). I’m talking specifically about the theming of the form. Yes, its a very simple form. A search form. But let’s have a look at what’s going on with it and discuss the Drupal way versus the way we ended up doing it.

In FAPI, you’d probably have a ‘textfield’ element for the search box and a ‘submit’ element for the ‘Go’ button. Easy enough, one minute of code. But what about the title ‘Name of your school or group’? Probably a title for the textfield element, no? But then how do we get it centred above both the textfield and the submit button? And what about the text under the two fields? A description, right? Again… how do we get it to appear *exactly* where we want it? The look and feel of the form are to me absolutely crucial. I don’t just want a textfield’s title (with annoying colon after it), a textfield, a description, and then a Go button all one on top of the other.

The Drupal solution? Using a theme hook. We define the form in our implementation of hook_form() and then theme that form separately. The ‘programmer’ cares about the functionality of the form but not the visual design of the form. The ‘designer’ doesn’t care about the functionality and instead works on how it looks. But I’m both the programmer and the designer here, and I want my work to be as easy as possible! So, let’s say i go down this route (I tried, I really did). I need to register my theme function in hook_themes(). Okay, I know why, it saves extra code running on every page load. But its still annoying. Now I create my theme function… urgh. You’ve got to really know your FAPI stuff to get this to work. I try for a while but then I give up. It just feels so messy with some code somewhere, some in another place, and then when I ask my colleague to have a look so he can learn how to do it he’s disgusted… doesn’t know what’s going on… starts bad-mouthing Drupal. So we build our own massively simple FAPI instead in about half an hour that does just what we want it to do.

So now we’re using our own super-basic FAPI for this form. Not all forms, just the ones we want complete control over, visually. Rather than using hook_form() and defining a form array, we just hard-code the HTML for the form. Some of you may be in complete horror now thinking about this but its just by far and away the easiest way to get forms to do exactly what you want. Like a forename and surname field side by side rather than one on top of the other, sharing the title ‘Name’ which is a label for the forename field.

We’re sticking with the idea of a validate() and submit() hook though. I like that one. But we’re doing it slighly differently and more simply, so that any new coders we might hire can quickly and easily pick it up.

Going nodeless

I don’t always like nodes. I really, really don’t. I don’t need revisions, and if I did I’d do them in my own way, just the right way for me, rather than a way that kind of works for everyone but not quite perfectly for anyone in specific. I don’t like the way that as uid=1 I get all the extra bits like ‘Authoring information’ which I never touch. Node hooks and the nodeapi which I once loved are now a higgeldy-piggledy mess that’s a real pain for my new hire. I try to explain to him what’s going on when I save a node. “So this function deals with the submission. But not the core node stuff, Drupal deals with that. And if we want something used for all nodes, we put it in here instead. And we can also override this specific bit here.”… he looks on in amazement, totally baffled by what’s going on and why. It would be so much easier for him (and me) to understand if everything’s just in one place.

So what do we gain from nodes? Umm… not much really. We don’t use contributed modules any more because they never do exactly what I want and always do stuff which I don’t want them doing which just make them less efficient. We put all our code in our own one module instead. A massive, hefty module with a dozen or so include files.

We gain the ability to always do $node->nid and use node_save() and other handy things. But we don’t really need nodes, and it frustrates me having to do the extra INNER JOINs on node_revisions etc. So we’re trialing not using nodes at all for one of our content types – our customers. We just have a simple ‘id’ field now in one single table. We no longer need to INNER JOIN node and node_revisions. We haven’t had any problems so far, but the new hire is finding it much easier to code now, without the ‘baggage’ of Drupal.

The future

Our current plan is to gently migrate away from Drupal, perhaps altogether. We like the idea of building our own framework again, one that does exactly what it needs to do for our site. Its not something we can do overnight. Ours is a yearly cycle, following the academic year, and the current plan is to fork the codebase in around January/February and that would mark the beginning of our own framework if we still feel that way then.

In the meantime, we’ll continue using nodes for most of our content types (if simply because migrating away from them would be a long and arduos task with little reward) and we’ll continue to use FAPI for most our forms. But I see us using our own simple FAPI for more and more forms where we need complete control over them, and I see us extending this FAPI to help us reduce using the same code multiiple times.

I think I still like Drupal. I definitely appreciate the vibrant community. But sometimes I thoroughly hate Drupal and get massively frustrated by it. But I still like it in theory at least. One framework for all my websites. But whilst I just work on one massive website it just has so much less use to me.

You’re more than welcome to urge me to stay with Drupal. In fact, I highly hope someone can manage this. I’ve put a lot of time over the last few years into learning Drupal, and it would be a great waste and a shame to lose all that.

Share this:

Like this:

Like Loading...
Aug 26 2008
Aug 26

Recently I have upgraded a customer site from Drupal 5 to Drupal 6. The site contains a number of custom modules that required considerable work because of some changes in the Drupal API. One of the API changes is the removal of hook_info and hook_auth.

The new authentication mechanism has two advantages:

First of all, two authentication-specific hooks have been eliminated. There is no replacement hook, instead you must use hook_form_alter to replace the built-in validation functions of the user login form. This is a more general and more flexible approach and it shows the power of the current form API. You can check user_login_block and user_login_default_validators for examples.

Secondly, there is a lot more flexibility to do error reporting when authentication fails. In Drupal 5 when hook_auth returned false, Drupal would always display the error message "Sorry, unrecognized username or password. Have you forgotten your password?" even though the real problem might be something else, for example a remote server responsible for doing the actual verification of the password is not available. In Drupal 6 you can fully control which messages get displayed during an authentication failure.

Even though there is some work involved to migrate custom authentication functionality to Drupal 6, the end result is code that is much cleaner and more powerful.

Aug 22 2008
Aug 22

There is an interesting presentation by psychologist Barry Schwartz at TED called "The paradox of choice". He explains why people are not better off when there is too much choice.

He claims:

Some choice is better than none. But it doesn't follow from that, that more choice is better than some.

His explanation makes sense:

The more options there are, the easier it is to regret anything at all that is disappointing about the option that you chose.

I think he has a valid point. A point which is also true in software:

I'm not saying that there should be no choice at all but very often there is simply too much choice and investigating all possible options takes too much time. Luckily Barry Schwartz reveals how to be happy with the choices you make:

The secret to happiness is "low expectations".

You can watch the presentation below or over at TED:

Jul 30 2008
Jul 30

Unless the list is crazy long, like it can be for operating systems, I realized that I read software changelogs pretty closely. This site now has one, with an RSS feed. It will be a sort of commit message, listing additions and changes worth mentioning about the live site. (Can somebody remind me later on to tell you how I setup my staging environment? Yes, for a personal blog.) It will not include updates to Drupal modules run on the site unless it makes a dramatic difference. The changelog will include changes to layout or the underlying Drupal theme and addition of content sections. The first entry? That I created a changelog in the first place. You can find out which version of Drupal I'm running via the usual open secret method, though I will make a note of it when I do upgrade.


About Drupal Sun

Drupal Sun is an Evolving Web project. It allows you to:

  • Do full-text search on all the articles in Drupal Planet (thanks to Apache Solr)
  • Facet based on tags, author, or feed
  • Flip through articles quickly (with j/k or arrow keys) to find what you're interested in
  • View the entire article text inline, or in the context of the site where it was created

See the blog post at Evolving Web

Evolving Web