Drupal

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.

Topic: 

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.

Topic: 

Pro Drupal Development on Slashdot

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!

Pages

Subscribe to RSS - Drupal