=== modified file 'ca/ca.admin.inc'
--- ca/ca.admin.inc	2009-03-07 23:00:15 +0000
+++ ca/ca.admin.inc	2009-03-10 21:19:32 +0000
@@ -133,17 +133,37 @@
  * From this page, the user can begin the batch processing of any Workflow-ng
  * configurations in the site's database into Conditional Actions predicates.
  */
-function ca_conversion_page() {
-  $output = t('TODO: Fill this page out. :)');
-
-  // TODO: Figure out if the conversion needs to be done
-  // Generally, it should only happen when someone upgrades from Drupal 5 to
-  // Drupal 6.
-
-  // TODO: Do batch processing because I can imagine some people having a lot
-  // of Ubercart-related Workflow-ng configurations.
-
-  return $output;
+function ca_conversion_form() {
+  $form = array();
+
+  $form['help'] = array(
+    '#type' => 'markup',
+    '#value' => t('This page converts Workflow-ng configurations from Drupal 5 into Ubercart Conditional Actions for Drupal 6.') .'<br />',
+  );
+
+  $form['submit'] = array(
+    '#type' => 'submit',
+    '#value' => t('Convert'),
+  );
+
+  return $form;
+}
+
+function ca_conversion_form_submit($form, &$form_state) {
+  // Convert workflow-ng configurations in a batch process because there may
+  // be lots and lots of them. This might be a tad overkill, but it means we
+  // have separated functions.
+  $batch = array(
+    'operations' => array(
+      array('_ca_convert_configurations'),
+    ),
+    'finished' => '_ca_conversion_finished',
+    'title' => t('Converting Workflow-ng configurations'),
+    'init_message' => t('Beginning conversion.'),
+    'progress_message' => t('@percentage complete.'),
+  );
+
+  batch_set($batch);
 }
 
 /**
@@ -151,11 +171,20 @@
  *
  *
  */
-function _ca_convert_configurations() {
+function _ca_convert_configurations(&$context) {
   global $user;
 
   if (db_table_exists('workflow_ng_cfgs')) {
-    $result = db_query("SELECT name, data FROM {workflow_ng_cfgs} WHERE altered = 1");
+    if (!isset($context['sandbox']['progress'])) {
+      // Initialize batch data.
+      $context['sandbox']['progress'] = 0;
+      $context['sandbox']['current_cfg'] = '';
+      $context['sandbox']['max'] = db_result(db_query("SELECT COUNT(name) FROM {workflow_ng_cfgs} WHERE altered = 1"));
+    }
+
+    $limit = 10; // TODO: Find a more realistic number. This one is arbitrary.
+
+    $result = db_query_range("SELECT name, data FROM {workflow_ng_cfgs} WHERE altered = 1 AND name > '%s' ORDER BY name", $context['sandbox']['current_cfg'], 0, $limit);
     $predicates = array();
     while ($configuration_row = db_fetch_object($result)) {
       $predicate = array('#pid' => $configuration_row->name);
@@ -164,6 +193,8 @@
       if ($configuration = unserialize($configuration_row->data)) {
         $predicate['#class'] = $configuration['#module'];
         $predicate['#title'] = $configuration['#label'];
+
+        // Convert event names to corresponding triggers.
         switch ($configuration['#event']) {
           case 'order_status_update':
             $trigger = 'uc_order_status_update';
@@ -178,13 +209,21 @@
             $trigger = $configuration['#event'];
           break;
         }
+
+        // In UC 1.x, taxes had an event for each tax rate, using the same
+        // action. In UC 2.x, they use the same trigger, but have separate
+        // actions.
         if (strpos($trigger, 'calculate_tax_') === 0) {
           $tax_id = substr($trigger, 14);
           $trigger = 'calculate_taxes';
         }
+
         $predicate['#trigger'] = $trigger;
         $predicate['#weight'] = $configuration['#weight'];
         $predicate['#status'] = $configuration['#active'];
+
+        // Numeric keys in $configuration could be for conditions or actions.
+        // Take each and categorize them to add them to the predicate later.
         for ($i = 0; isset($configuration[$i]); $i++) {
           if ($configuration[$i]['#type'] == 'action') {
             $action = _ca_convert_actions($configuration[$i], $tax_id);
@@ -199,6 +238,8 @@
             }
           }
         }
+        // Conditions are optional. If there are no actions, there's no reason
+        // to make a predicate.
         if ($conditions) {
           $predicate['#conditions'] = array(
             '#operator' => 'AND',
@@ -208,6 +249,20 @@
         $predicate['#uid'] = $user->uid;
         ca_save_predicate($predicate);
       }
+
+      // Store some result for post-processing in the finished callback.
+      $context['results'][] = $configuration_row->name;
+
+      // Update our progress information.
+      $context['sandbox']['progress']++;
+      $context['sandbox']['current_cfg'] = $configuration_row->name;
+      $context['message'] = t('Now processing %cfg', array('%cfg' => $configuration_row->name));
+    }
+
+    // Inform the batch engine that we are not finished,
+    // and provide an estimation of the completion level we reached.
+    if ($context['sandbox']['progress'] != $context['sandbox']['max']) {
+      $context['finished'] = $context['sandbox']['progress'] / $context['sandbox']['max'];
     }
   }
 }
@@ -218,6 +273,7 @@
 function _ca_convert_conditions($condition_tree) {
   $ca_condition = array();
 
+  // Handle multiple conditions recursively.
   if ($condition_tree['#type'] == 'AND' || $condition_tree['#type'] == 'OR') {
     $ca_condition['#operator'] = $condition_tree['#type'];
     $ca_condition['#conditions'] = array();
@@ -229,10 +285,14 @@
     }
   }
   else if ($condition_tree['#type'] == 'condition') {
+    // Some conditions were renamed but check the same things.
     switch ($condition_tree['#name']) {
       case 'uc_payment_condition_balance':
         $ca_condition['#name'] = 'uc_payment_condition_order_balance';
         break;
+      case 'workflow_ng_condition_custom_php':
+        $ca_condition['#name'] = 'ca_condition_custom_php';
+        break;
       default:
         $ca_condition['#name'] = $condition_tree['#name'];
         break;
@@ -254,6 +314,7 @@
 function _ca_convert_actions($wf_action, $tax_id = NULL) {
   $action = $wf_action;
 
+  // Some actions were renamed, but do the same things.
   switch ($action['#name']) {
     case 'uc_order_action_update_status':
       $action['#name'] = 'uc_order_update_status';
@@ -261,6 +322,9 @@
     case 'uc_taxes_action_apply_tax':
       $action['#name'] = 'uc_taxes_action_apply_tax_'. $tax_id;
       break;
+    case 'workflow_ng_action_custom_php':
+      $action['#name'] = 'ca_action_custom_php';
+      break;
   }
 
   if (isset($action['#label'])) {

=== modified file 'ca/ca.module'
--- ca/ca.module	2009-02-26 18:54:08 +0000
+++ ca/ca.module	2009-03-10 21:19:32 +0000
@@ -47,14 +47,15 @@
     'weight' => 5,
     'type' => MENU_LOCAL_TASK,
   );
-  /* $items[CA_UI_PATH .'/convert'] = array(
+  $items[CA_UI_PATH .'/convert'] = array(
     'title' => 'Convert configurations',
     'description' => 'Translate Workflow-ng configurations into Conditional Actions predicates.',
-    'page callback' => 'ca_conversion_page',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('ca_conversion_form'),
     'access arguments' => array('administer conditional actions'),
     'type' => MENU_LOCAL_TASK,
     'file' => 'ca.admin.inc',
-  ); */
+  );
   $items[CA_UI_PATH .'/add'] = array(
     'title' => 'Add a predicate',
     'description' => 'Allows an administrator to create a new predicate.',

