Lullabot Ideas

We know stuff. We empower you to know stuff too.

Porting Drupal Modules

Video by Angie Byron

FAIL (the browser should render some flash content, not this).

Now that CCK and Views have release candidates for Drupal 6, it's the perfect time to start hammering down on the list of modules that haven't been ported yet. This video demonstrates how anyone with basic copy/paste/modify PHP skills can help port Drupal modules. Really! It's not nearly as bad as you think. :)

Topics covered include:

  • How to tell the module portage status of your Drupal site with the Upgrade Status module.
  • How to perform basic community research to determine the actual porting status of your modules.
  • Overview of the amazing Coder module and how to configure it.
  • Where to find more information about changes between Drupal versions.
  • How to use Coder module to convert a simple module from Drupal 5 to Drupal 6, step by step.
  • How to test to ensure the porting status was a success.

Comments

filesize

Howcome the filesize of this 16 minute video clip is 221.87MB? Seems a bit too large...

nice, but fuzzy video

This is a nice piece in terms of the content and audio, but lots of the video is so fuzzy/jumpy it's a bit hard to watch.

Thanks Angie!

Thanks Angie...

I have now ported my very first module (found a nice easy one with no database tables and only a few screens worth of code haha - hey gotta take baby steps lol). I had just referred a D6 user to a D5 module that he needed and thought - hey why don't I just port it myself!? I know just next to no PHP whatsoever, so if I can do it... heh heh :P

I've now rolled a patch and submitted it to the maintainer :) Yay!
http://drupal.org/node/278730

- David

YAY!!!

Oh man! That totally made my night!! :D You go with your bad porting self!

@pwolanin and kv, Sorry, I'm a videocast n00b. I'll be seeing Addi in person next week, and hopefully she can pass some kind of magical settings that make it smaller and look less cruddy. :) If so, I'll upload a newer version sometime next week.

Little bit smaller version

I've uploaded a smaller version that is 99MB and in mp4 format for now. We will try to get a smaller decent quality version output within the next few days.

Why not use Blip?

Is there a reason to keep using the unsupported video module (at least that's what I assume this is using) rather than Blip.TV? I actually tried to watch this video, but I usually find the videos here to be on the unwatchable side because of the formatting...

We will be changing eventually

I am not a fan of the video module (that is what we currently use) and have been plotting to get us off of it for a while now. We are planning to redo a number of things around how we present our videos but honestly we have a ton of other work that comes first. The cobbler's son and all.

The mp4 videos should be watchable using Quicktime or VLC but, that said, we will move to flash videos in the future.

I personally love that they

I personally love that they are in MP4 format! That way I can save them and view them when I'm offline.

Blip allows you to download MP4s

Blip allows you to convert your videos into a bunch of different formats (mov, mp4, flv, etc) and download as you like.

Hope you keep the mp4 ones too

Because they're easier to save and watch even when I'm offline, which happens a lot when I travel. I've been using Quicktime to watch them but recently moved to using VLC player.

Maybe have a flash version for viewing and a downloadable version too?

thanks

On a roll!

Woo hoo!

I'm just on a roll now tonight haha... I decided to also update and submit a different user's patch from 1+ years ago (never got any tender love n' care) that adds a pretty nifty feature/fix (but no longer applied to the current versions). So adding that, plus another patch that adds those features on top of my D6 port patch haha :P

If was kinda funny - one of my family members came in and looked over my shoulder commenting on "woah what's that crazy stuff you're doing?", and with a nonchalant nod I just said... "Yep. Just porting Drupal modules. No biggie." :P

Thanks Angie :D Yes, your amazing efforts "do" in fact mean the world to us :D

Oh and - any suggestions by chance of how to proceed if the maintainer ends up being AWOL? Looks like in this module's case in particular the maintainer has been away since 2007. Not sure I'm ready to "be" a maintainer (though heh I suppose on one this simple lol) since I don't have the skills to handle feature requests or security concerns, but would still like to get my patch applied once tested.

(and hey, the video quality was spot on, for those Drupal admins seeking psychedelic 60's-like experiences har har... just kidding) :P

- David

Awesome!!

Oh and - any suggestions by chance of how to proceed if the maintainer ends up being AWOL? Looks like in this module's case in particular the maintainer has been away since 2007. Not sure I'm ready to "be" a maintainer (though heh I suppose on one this simple lol) since I don't have the skills to handle feature requests or security concerns, but would still like to get my patch applied once tested.

Yep. There's a procedure laid out at Dealing with abandoned projects. Basically, make a post to the issue queue offering co-maintainership. If no reply for 2 weeks (this creates a public record that the maintainer is inactive), then move the issue to the Drupal.org webmasters queue and someone can transfer it to you. You'll also need to apply for a CVS account once one of the webmasters team gives you the go-ahead.

There are many different ways to be a project maintainer. Some go nuts and work on their projects every day, fixing bugs and adding new features. Some only commit patches to the modules that have been reviewed and tested by the community (Drupal, in fact, is such an example). I'd say that if the module is truly abandoned, better to have some kind of maintainer than none. :) It'd be a great opportunity for you to learn and take your skills to the next level too, if you're keen.

(and hey, the video quality was spot on, for those Drupal admins seeking psychedelic 60's-like experiences har har... just kidding) :P

LOL. Excellent. That was totally my intent all along, of course. ;)

Awesome video!

Double A productions: Addi & Angie.

You rock, with videos like these you make everything Drupal look easier.

my first ports...

Angie,

You really *are* the best! Thanks for this screencast. It gave me the little push I needed to get my hands dirty in the code after years of relative inactivity. Anyway, I ended up porting my first modules and while there are a few outstanding bugs (notably, related to the recent changes to views), it went pretty well and I'm chomping at the bit to try some more.

Thanks again!

Omar

What version of coder?

Hi all,

Excellent video Angie :D, it makes the fears go away.

I've tried to use the actual versions today:
5.x-2.7
5.x-2.x-dev (2008-May-15)
6.x-1.x-dev

but with those modules I couldn't review my 5.x version of a module, so I decide to follow your exact version :D. And to make it more accessible here is my cvs line:

$ cvs -z6 -d:pserver:anonymous:anonymous@cvs.drupal.org:/cvs/drupal-contrib export -D "1 Jun 2008" contributions/modules/coder

Wow, that's easy

I had no idea that porting could be so easy. Well, the coder module sure makes it easier. I am now just a bit more brave and may try my hand at helping with code. Thanks Angie. You rock.

Deadwood

What about the deadwood module?
Does anyone have experience with it? is it going to do as good a job as doing it manually?

RandomCake

(coding standards) "..and

(coding standards) "..and look, there's even more!" :))

porting trouble

Thanks so much for the tutorial. I am having some problems with the module I ported.
I did everything and I am getting a reading on coder that says everythihng is good except one error this

Line 133: hook no longer exists, use  hook_form_alter () to swap your own validation handler (Drupal Docs)

  $db_info = content_database_info($field);

The setting show up in the cck menu for a text field. but the text box does not appear in the browser when you try to create the content.

Any tips would be great. I just finished watching the video by the way and it was great.

here is the module

<?php
/**
* @author Ryan Hughes <ryan@linuxbox.com>
*/
/**
* @file
* autocomplete module for cck
*
* allows for autocomplete to be added to text fields in ccK
*
*/

// $Id: textfield_autocomplete.module,v 1.1 2007/08/31 16:18:43 linuxbox Exp $

/**
* Implementation of hook_menu().
*/
function textfield_autocomplete_menu() {
 
$items['textfield_autocomplete'] = array(
   
'title' => 'Textfield Autocomplete',
   
'page callback' => 'textfield_autocomplete',
   
// 'page arguments' => array('aggregator_form_feed', 2),
    // NOTE: as of Drupal 6.2, all menu items are *required* to have
    // access control.
   
'access arguments' => array('access content'),
   
'type' => MENU_CALLBACK,

  );

  return
$items;
}


/**
* Implementation of hook_widget_info().
*/
function textfield_autocomplete_widget_info() {
  return array(
   
'autocomplete' => array(
     
'label' => 'Text Field With Autocomplete',
     
'field types' => array('text'),
    ),
  );
}

/**
* Implementation of hook_widget().
*/
function textfield_autocomplete_widget($op, &$node, $field, &$items) {
  switch (
$op) {
    case
'form':
     
$form = array();

     
$form[$field['field_name']] = array('#tree' => TRUE);

      if (
$field['multiple']) {
       
$form[$field['field_name']]['#type'] = 'fieldset';
       
$form[$field['field_name']]['#description'] = t($field['widget']['description']);
       
$delta = 0;
        foreach (
$items as $data) {
          if (
$data['value']) {
           
$form[$field['field_name']][$delta]['value'] = array(
             
'#type' => 'textfield',
             
'#title' => ($delta == 0) ? t($field['widget']['label']) : '',
             
'#default_value' => $data['value'],
             
'#required' => ($delta == 0) ? $field['required'] : FALSE,
             
'#maxlength' => $field['max_length'] ? $field['max_length'] : NULL,
             
'#weight' => $field['widget']['weight'],
             
'#autocomplete_path' => 'textfield_autocomplete/'. $field['field_name'],
            );
            if (
$field['text_processing']) {
             
$form[$field['field_name']][$delta]['format'] = filter_form($data['format'], $form[$field['field_name']][$delta]['value']['#weight'] + 1, array($field['field_name'], $delta, 'format'));
            }
           
$delta++;
          }
// if this is an item with a value
       
} // foreach item
       
foreach (range($delta, $delta + 2) as $delta) {
         
$form[$field['field_name']][$delta]['value'] = array(
           
'#type' => 'textfield',
           
'#title' => ($delta == 0) ? t($field['widget']['label']) : '',
           
'#default_value' => '',
           
'#required' => ($delta == 0) ? $field['required'] : FALSE,
           
'#maxlength' => $field['max_length'] ? $field['max_length'] : NULL,
           
'#weight' => $field['widget']['weight'],
           
'#autocomplete_path' => 'textfield_autocomplete/'. $field['field_name'],
          );
          if (
$field['text_processing']) {
           
$form[$field['field_name']][$delta]['format'] = filter_form($items[$delta]['format'], $form[$field['field_name']][$delta]['value']['#weight'] + 1, array($field['field_name'], $delta, 'format'));
          }
        }
// foreach item in the range of (end - end+2)
     
} // if it's multiple
     
else {
       
$form[$field['field_name']][0]['value'] = array(
         
'#type' => 'textfield',
         
'#title' => t($field['widget']['label']),
         
'#default_value' => isset($items[0]['value']) ? $items[0]['value'] : '',
         
'#required' => $field['required'],
         
'#description' => t($field['widget']['description']),
         
'#maxlength' => $field['max_length'] ? $field['max_length'] : NULL,
         
'#weight' => $field['widget']['weight'],
         
'#autocomplete_path' => 'textfield_autocomplete/'. $field['field_name'],
        );
        if (
$field['text_processing']) {
         
$form[$field['field_name']][0]['format'] = filter_form($items[0]['format'], $form[$field['field_name']][0]['value']['#weight'] + 1, array($field['field_name'], 0, 'format'));
        }
      }
// else it's not multiple
     
return $form;

    case
'process form values':
     
// Don't save empty fields except the first value
     
foreach ($items as $delta => $item) {
        if (
$item['value'] == '' && $delta > 0) {
          unset(
$items[$delta]);
        }
      }
      break;
  }
// switch op
} // function textfield_autocomplete_widget



/**
* Callback to return the values for autocomplete.
*/
function textfield_autocomplete($fieldname, $startswith) {
 
/*
  $items = func_get_args();
  $items = array_combine($items, $items);
  echo drupal_to_js($items);
  die();
  */

 
$field = content_fields($fieldname);
 
$db_info = content_database_info($field);
 
$getdatafrom[$db_info['table']][$db_info['columns']['value']['column']] = TRUE;

 
$items = array();
  foreach (
$getdatafrom as $table => $columns) {
   
$cols = array_keys($columns);

   
// I do this self-join business because I want the latest version ID.
    // So I join with itself, saying I want all the pairs of
    // (a row, a row with a greater version id).
    // Then I cut it down, saying I only want ones where the one with the
    // greater version id is null.
    // Taken together, it's like saying "Give me all the ones such that there
    // is no row with a greater version id."
    // Doing it this way means that I don't have to do a subselect, which can
    // be slow.
   
$likes = array();
   
$likes_vals = array();
    foreach (
$cols as $col) {
     
$likes[] = "A.". $col ." LIKE '%s%%'";
     
$likes_vals[] = $startswith;
    }
// foreach likes

   
$query = "SELECT DISTINCT A.". implode(', A.', $cols) ."FROM {". $table ."} A LEFT JOIN {". $table ."} B ON (A.nid=B.nid AND B.vid > A.vid) WHERE B.vid IS NULL AND ". implode(" AND ", $likes);
   
$result = db_query($query, $likes_vals);

    while (
$row = db_fetch_array($result)) {
      foreach (
$row as $colname => $colval) {
       
$items[] = $colval;
      }
// foreach col value
   
} // while there's rows
 
} // foreach table

 
if ($items) {
 
$items = array_combine($items, $items);
  }
  echo
drupal_to_js($items);
  die();
}
// function nonsense_autocomplete
?>

Modules to port?

Wow, excellent screencast. So I'm really excited to try this out. How do I find a module to port though? Is there a list somewhere of modules that need porting to D6?

Great idea, but why not

Great idea, but why not publish this video @ YouTube?

Anyway to use this module to back port?

Thanks for the tutorial. It does seem easy to port a module up. However, I'm wondering if I can use the coder module to back port a module from D6 back to D5? The module maintainers have told me they have no plans to back port to D5.