Daily Paul moves to a VPS

In Drupal, Grassroots Political Activism and the Ron Paul Campaign I mentioned the site Daily Paul. Michael Nystrom, who runs the site, was seeing serious performance problems as the site became more popular. This is Michael's first site with Drupal, but he had made all the right moves: using throttle.module, turning on caching and CSS optimization, and even upgrading to a faster hosting plan.

I contacted Michael and we began going through various scenarios. Was the slowdown consistent? No. Were ping times consistent? Yes. What about the MySQL configuration? And so on. It turns out that Michael's host (Dreamhost) did not have the query cache turned on in MySQL, and refused to turn it on. That meant that every query for every noncached page had to be tediously run by the database server. When the server got busy, Michael's site suffered, badly, though the server that ran Apache and PHP was idling.

Since I had recently gotten a VPS from Rimuhosting but had not set it up yet, we used it as a testbed for Michael's site. Last weekend we switched DNS and the new site began to pick up traffic. After paring down Apache, tweaking PHP, and tuning MySQL, the speed increase was clear. Last night Ron Paul participated in a debate in Florida which drew traffic to the site, up to 30-40 simultaneous users. Load average on the server was about 0.50. The two big wins were (1) having MySQL on the same server and (2) enabling query caching.

Last night I handed the VPS off to Michael with the help of the good folks at Rimuhosting. It's just a little VPS, but it sure made a big difference.

Note: of course, a VPS has a serious downside: it must be maintained.

[ Submitted by John on Tue, 2007-09-18 20:54. | | ]

Drupal, Grassroots Political Activism and the Ron Paul Campaign

There is a battle going on for the control of information. The powers that be (traditional news outlets) are increasingly being challenged by widespread independent media sources including bloggers and journalism-enabling sites like NowPublic. It is the difference between "Good evening. This is what the news is." and "What is going on in the world? I'll find out." The change from one to the other is inevitable.

In politics, this is an important change. Instead of politicians claiming that this or that is "the will of the American people", that will can actually be tested by listening to the people because the people now have a voice. The first real example of this was the Howard Dean campaign in 2004. Dean used DeanSpace, a community CMS based on Drupal, to raise massive internet support.

In the current U.S. presidential election, the candidate who is using the internet most effectively is Ron Paul. Like Howard Dean, Ron Paul's support on the internet is massive. While his main website does not run Drupal, the Daily Paul is a popular site about the campaign. It's managed by Michael Nystrom and it runs on Drupal.

What I find interesting about Ron Paul's campaign is that while Howard Dean had support from the mainstream media (representing the traditional "we will tell you what is important" position), Ron Paul seems to be virtually ignored by the mainstream media; only the Washington Post has covered him. Thus, he represents the first test of how well grassroots internet campaigns can fare when they are pitted against, and not supplemented by, traditional media.

Perhaps the Long Tail, while effective economically, is ineffective politically at the present time. When Ron Paul, a pro-life Christian obstetrician and ten-time Taxpayer's Friend awardee was excluded from a forum put on by the Iowa Christian Alliance and Iowans for Tax Relief in Iowa, eyebrows were raised and his supporters on the internet were quick to unearth the fact that the forum's organizer, Ed Failor, is a senior advisor to the McCain campaign and otherwise involved in Big Politics. But can they convince Iowans to vote for Ron Paul in the Iowa Straw Poll? They have until August 11. That remains to be seen, and would take a tremendous logistical and monetary effort.

While all that is interesting, times are not static. The young people who make up a significant portion (though not by any means all) of Ron Paul's support have grown up with the internet. They do not go to the television Evening News for their news. They do not have landline telephones that are called by pollsters. They are used to using news aggregators, where media bias is apparent. They're savvy. And -- here's the key -- they are making up an increasing percentage of the public.

So while Ron Paul's campaign may not succeed, the battle between old and new media, between the powers that be and the widespread dissatisfaction with those powers, between corporate power and individual power -- that battle will become increasingly heated.

For myself, I'm glad to be involved in an open source project like Drupal, which, in addition to being a platform for individual voices, is in itself a microcosm of individuals working together to achieve a communal goal.

[ Submitted by John on Sat, 2007-06-23 15:43. | | ]

Writing an action for Drupal 6

Note: this applies to the development version of Drupal 6 as of June 12, 2007 with the actions patch applied.

Actions are atomic functions that can be assigned to run at different times during runtime. For example, you could have an action that emails a user and have that run when a node is inserted. Or when a comment is deleted. Or when a user logs in.

When writing an action, you first need to decide whether or not your action will need to know anything beyond the context in which it runs. For example, an action that prefixes the title of a node with "New! " does not need any other information; it just needs the node. Let's actually create that action a moment.

First, we need to describe the action to Drupal. We do that with hook_action_info.

<?php
 
function modulename_action_info {
  return array(
   
'modulename_prepend_new_action' => array(
       
'type' => 'node',
       
'description' => t('Prepend "New! " to the title of a node'),
       
'configurable' => FALSE,
       
'hooks' => array(
         
'nodeapi' => array('presave''insert''update''view'),
         
'comment' => array('insert'),
       )
    )
  );
}/
?>

The array returned is keyed by function name. So later on we'll create a function called modulename_prepend_new_action(), probably not the best name but let's roll with it.

The 'type' key declares what object type this action will work on. It will work on nodes, so it's a 'node' type of action, or a node action.

The 'description' will appear in the UI when the administrator is assigning actions to hooks on the "Assign actions" screen.

The 'configurable' key declares whether or not the action needs additional information to run. This action doesn't; it simply modifies the node it's been given. More about configurable actions in a minute.

The 'hooks' key determines where this action may be assigned. For example, this action running during the 'register' op of the user hook would make no sense. But running in the 'presave' op of the nodeapi hook, where it can modify the node object just before it is saved, makes perfect sense.

Now for the action itself. It's dead simple.

<?php
 
function modulename_prepend_new_action(&$node$context) {
 
$node->title t('New!') . ' ' $node->title;

?>

What's in $context? Well, that's where Drupal puts any information that it thinks the action might want to use. Suppose we wanted this action to run when a comment is added. That's great. But internally, the comment is the object being worked with during the comment hook. So the actions module looks at the comment, finds the nid, loads the node, and passes that to the action (because it's a node action it expects a node). But since Drupal already has a copy of the comment object on hand, it puts it into the $context so that if the action wants to, it can look at it and make decisions based on that. So that's what $context is.

Now for the second kind of an action, a configurable action. That's like a stored procedure. For this we'll look at a comment action called Unpublish comment containing keyword(s). Obviously the action needs to know which keywords to compare with the comment body so it can figure out whether to unpublish the action or not. So it is a configurable action. Note that we don't use configurable actions directly; we make instances of them. We could make multiple action instances with descriptions like this:

Unpublish comment containing 'butter'
Unpublish comment containing 'blubber'
Unpublish comment containing 'flubber' or 'moose'

We would create these "action instances" on the "Manage actions" screen that the actions module provides. Here's the action_info() hook:

<?php
 
function comment_action_info() {
  return array(
   
'comment_unpublish_by_keyword_action' => array(
     
'description' => t('Unpublish comment containing keyword(s)'),
     
'type' => 'comment',
     
'configurable' => TRUE,
     
'hooks' => array(
       
'comment' => array('insert''update'),
      )
    )
  );

?>

Now we need to provide a way for the user to input which keywords to look for. So we create a comment_unpublish_by_keyword_action_form() function. Note we just append _form to the action function name. Here's the function, simplified:

<?php
 
function comment_unpublish_by_keyword_action_form($context) {
 
$form['keywords'] = array(
   
'#title' => t('Keywords'),
   
'#type' => 'textfield',
   
'#description' => t('The comment will be unpublished if it contains any of the keywords above. Use a comma-separated list of keywords. Example: funny, bungee jumping, "Company, Inc.".'),
   
'#default_value' => isset($context['keywords']) ? implode(','$context['keywords']) : '',
  );
  return 
$form;

?>

We could make a validation function (comment_unpublish_by_keyword_action_form_validate()) if we wanted to, but let's skip that and go right to the submission function. The taxonomy module already has a great implementation of turning comma-delimited keywords into an array, so we'll just use that. Code reuse!

<?php
 
function comment_unpublish_by_keyword_action_submit($form$form_state) {
  return array(
'keywords' => taxonomy_explode_tags($form_state['values']['keywords']));

?>

Note that we return a keyed array of parameters needed by the action. These parameters will be available inside $context. So the action will need to look for $context['keywords'] and that's exactly what it does:

<?php
 
function comment_unpublish_by_keyword_action($comment$context) {
  foreach (
$context['keywords'] as $keyword) {
    if (
strstr($comment->comment$keyword)) {
     
db_query('UPDATE {comments} SET status = 'COMMENT_NOT_PUBLISHED .' WHERE cid = %d'$comment->cid);
     
watchdog('action''Unpublished comment %subject.', array('%subject' => $comment->subject));
      break;
    }
  }

?>

There you have it. Try writing an action of your own!

[ Submitted by John on Mon, 2007-06-04 16:59. | | ]

It's shipping!

The book is finally making its way into hands of eager developers. Time for a bit of reflection. Writing this book was like one long code sprint. Because many topics couldn't be written about until the Drupal 5 codebase was frozen, we stacked a lot of the writing towards the end of the cycle. That meant that often we were each writing a new chapter, soliciting informal reviews from experts in a topic area from within the Drupal community, reviewing a chapter in the technical edit phase, and reviewing a chapter in the copy edit phase simultaneously. Towards the end, we added production-ready proof reviews to the menu.

Like a software project, the scope of the book had to be firmly enforced or it would not be in your hands today. But I'm glad it is. I'm also really happy that Matt agreed to be coauthor. Often his work would spur mine on (and, I think, vice versa). chx was amazing too, often turning around code reviews in a matter of hours. The Drupal community is, in short, amazing.

I'll just summarize by saying: whew.

And in case you've read the author info, here are some photos of a Bell & Howell Apple ][.

[ Submitted by John on Tue, 2007-04-17 22:46. | | ]

Drupal book printed

A box arrived today. Inside...copies of Pro Drupal Development hot off the press!

In the picture I'm holding my youngest son. He's in the picture to show what an optimist I am. I assured my wife that writing a book would be no problem. Why, it would be done long before the baby was born. Er. We'll celebrate his first birthday in a few weeks.

[ Submitted by John on Thu, 2007-04-12 20:01. | | ]

Off to the Printer

As we were writing the Drupal book, Matt and I frequently wondered how many pages it would come out to. We estimated about 300 pages.

When the book went to the printer, the total page count was just above 450.

[ Submitted by John on Mon, 2007-04-02 09:51. | | ]

Online/Offline Applications

Joyent is a company that offers shared and virtual hosting. I was able to get an opinion of their approach from some people whose opinions I respect at the recent OSCMS conference, and have pretty much decided not to go with them for my hosting needs (I'm currently looking for a hosting service).

However, their recent announcement of Joyent Slingshot is interesting: Joyent Slingshot allows developers to deploy Rails applications that work the same online and offline (with synchronization) and with drag into and out of the application just like a standard desktop application.

Radio Userland pioneered the idea of the local/remote web application and it's something for the Drupal community to consider. Drupal applications running locally and remotely. Syncing together (I know, I know, the publish and subscribe modules need a little love and a 5.0 release), maybe doing peer-to-peer. Hooking into iPhoto. Offline mirroring. There's so much to do!

[ Submitted by John on Tue, 2007-03-27 22:37. | | ]

OSCMS 2007

Back from Sunnyvale, where I had an enjoyable time at the 2007 OSCMS conference. I spent time hawking the book, geeking out, and hanging out with Adrian.

Unfortunately my return flight was badly timed (reminder: book flights earlier next time!) and I nearly missed it because I was talking to Dries about the actions module. Wish I could have been there for the whole hack session.

Judging by the buzz at the conference, Drupal has a bright future.

As for the conference itself, there was only one thing missing.

[ Submitted by John on Tue, 2007-03-27 20:41. | | ]

Zend Platform on an Intel Mac

Finally. After over a year, Zend has recompiled their debugging extension so it will run on Intel Macs. That's the good news. The bad news is that now it's part of their enterprise-class Zend Platform product.

I'm running the 30-day trial, and was able to install it by running the install shell script and choosing the manual option to enter my Apache settings. Apache wouldn't restart after the installation because of course the xdebug extension that I had enabled in my php.ini file needed to be disabled.

[ Submitted by John on Fri, 2007-03-16 20:40. | | ]