Index: boxes.admin.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/boxes/boxes.admin.inc,v
retrieving revision 1.5.2.1
diff -u -p -r1.5.2.1 boxes.admin.inc
--- boxes.admin.inc	25 Apr 2010 18:41:54 -0000	1.5.2.1
+++ boxes.admin.inc	28 Jun 2010 13:03:16 -0000
@@ -69,6 +69,33 @@ function boxes_delete_form_submit($form,
 }
 
 /**
+ * Block/Box conversion confirmation form.
+ */
+function boxes_convert_form($form_state, $module, $delta) {
+  $form['module'] = array('#type' => 'hidden', '#value' => $module);
+  $form['delta'] = array('#type' => 'hidden', '#value' => $delta);
+  switch ($module) {
+    case 'block':
+      return confirm_form($form, t('Are you sure you want to convert this block to a box?'), 'admin/build/block', '', t('Convert'), t('Cancel'));
+      break;
+    case 'boxes':
+      return confirm_form($form, t('Are you sure you want to convert this box to a block?'), 'admin/build/block', '', t('Convert'), t('Cancel'));
+      break;
+    case 'default':
+      drupal_not_found();
+      die();
+  }
+}
+
+/**
+ * Submit handler for boxes_convert_form
+ */
+function boxes_convert_form_submit($form, &$form_state) {
+  boxes_convert($form_state['values']['module'], $form_state['values']['delta']);
+  $form_state['redirect'] = 'admin/build/block';
+}
+
+/**
  * Form for in page editing.
  */
 function boxes_box_inline_form(&$form_state, $box, $block) {
Index: boxes.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/boxes/boxes.module,v
retrieving revision 1.10.2.4
diff -u -p -r1.10.2.4 boxes.module
--- boxes.module	25 May 2010 19:03:10 -0000	1.10.2.4
+++ boxes.module	28 Jun 2010 13:03:16 -0000
@@ -40,6 +40,13 @@ function boxes_menu() {
     'type' => MENU_CALLBACK,
     'file' => 'boxes.admin.inc',
   );
+  $items['boxes/convert/%/%'] = array(
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('boxes_convert_form', 2, 3),
+    'access callback' => 'boxes_access_admin',
+    'type' => MENU_CALLBACK,
+    'file' => 'boxes.admin.inc',
+  );
   return $items;
 }
 
@@ -228,6 +235,121 @@ function boxes_load_reset() {
 }
 
 /**
+ * Loader for blocks.
+ * 
+ * @todo
+ *   Might be a way to use one query instead of two.
+ */
+function boxes_load_blocks($delta) {
+  $query = db_query("SELECT boxes.body, boxes.info AS description, boxes.format FROM {boxes} WHERE boxes.bid = '%s'", $delta);
+  while ($box = db_fetch_object($query)) {
+    $boxes[$delta] = $box;
+  }
+
+  $query = db_query("SELECT blocks.delta, blocks.title, blocks.bid FROM {blocks} WHERE blocks.module = 'block' AND blocks.delta = '%s'", $delta);
+  while ($block = db_fetch_object($query)) {
+    $blocks[$block->bid] = (object) array_merge((array) $block, (array) $boxes[$delta]);
+  }
+
+  return $blocks;
+}
+
+/**
+ * Converts blocks to boxes or boxes to blocks.
+ *
+ * @param $module
+ *   String of the controlling module.
+ * @param $delta
+ *   String of the delta for the block or box to convert.
+ */
+function boxes_convert($module, $delta) {
+  // Depending on what the module is we convert to the other.
+  switch ($module) {
+    case 'block':
+      // Load the block information.
+      $blocks = boxes_load_blocks($delta);
+
+      // Remove {boxes} entry.
+      db_query("DELETE FROM {boxes} WHERE bid = %d", $delta);
+      
+      // Update the {blocks} entry.
+      ctools_include('cleanstring');
+      foreach ($blocks as $block) {
+        // We have to update the delta because when a new block is created
+        // we will run into duplicate keys.
+        $block->delta = ctools_cleanstring($block->description .'_'. $delta);
+        $block->module = 'boxes';
+        drupal_write_record('blocks', $block, 'bid');
+      }
+      
+      // Update the {blocks_roles} entries (if any).
+      $query = db_query("SELECT rid FROM {blocks_roles} WHERE module = '%s' AND delta = '%s'", $module, $delta);
+      while ($result = db_fetch_object($query)) {
+        db_query("DELETE FROM {blocks_roles} WHERE module = '%s' AND delta = '%s' AND rid = %d", $module, $delta, $result->rid);
+        $record = array(
+          'module' => 'boxes',
+          'delta' => $block->delta,
+          'rid' => $result->rid,
+        );
+        drupal_write_record('blocks_roles', $record);
+      }
+      
+      // Create the {box} record.
+      $block->plugin_key = 'simple';
+      $block->options = array('body' => $block->body, 'format' => $block->format);
+      unset($block->body);
+      unset($block->format);
+      unset($block->bid);
+      drupal_write_record('box', $block);
+      break;
+      
+    case 'boxes':
+      // Load the box information.
+      $box = boxes_load($delta);
+      
+      // We only handle the 'simple' box plugin.
+      if ($box->plugin_key == 'simple') {
+        // Create {boxes} entry.
+        // Bid is autoincremental here, so once we insert we need to make sure
+        // that all deltas match. Luckily drupal_write_record() makes this easy.
+        $boxes = new stdClass();
+        $boxes->info = $box->description;
+        $boxes->body = $box->options['body'];
+        $boxes->format = $box->options['format'];
+        drupal_write_record('boxes', $boxes);
+        $new_delta = $boxes->bid;
+      
+        // Update the {blocks} entry.
+        $query = db_query("SELECT bid FROM {blocks} WHERE module = 'boxes' and delta = '%s'", $delta);
+        while ($blocks = db_fetch_object($query)) {
+          $blocks->module = 'block';
+          $blocks->delta = $new_delta;
+          drupal_write_record('blocks', $blocks, 'bid');
+        }
+      
+        // Update the {blocks_roles} entry (if any).
+        $query = db_query("SELECT rid FROM {blocks_roles} WHERE module = '%s' AND delta = '%s'", $module, $delta);
+        while ($result = db_fetch_object($query)) {
+          db_query("DELETE FROM {blocks_roles} WHERE module = '%s' AND delta = '%s'", $module, $delta);
+          $record = array(
+            'module' => 'block',
+            'delta' => $new_delta,
+            'rid' => $result->rid,
+          );
+          drupal_write_record('blocks_roles', $record);
+        }
+      
+        // Remove the {box} record.
+        db_query("DELETE FROM {box} WHERE delta = '%s'", $delta);
+      }
+      else {
+        drupal_set_message(t("We only handle boxes made with the 'simple' plugin."));
+      }
+      break;
+  }
+}
+
+/**
  * Common element of the box fom
  */
 function boxes_box_form($box) {
@@ -296,8 +418,9 @@ function boxes_block_delete_submit($form
  */
 function boxes_form_block_admin_display_form_alter(&$form, $form_state) {
   foreach (element_children($form) as $i) {
-    if ($form[$i]['module']['#value'] == 'boxes') {
-      $delta = $form[$i]['delta']['#value'];
+    $module = $form[$i]['module']['#value'];
+    $delta = $form[$i]['delta']['#value'];
+    if ($module == 'boxes') {
       if (strpos($delta, 'add__') !== 0) {
         $box = boxes_load($delta);
         if (($box->export_type & EXPORT_IN_DATABASE) && ($box->export_type & EXPORT_IN_CODE)) {
@@ -312,6 +435,13 @@ function boxes_form_block_admin_display_
         }
       }
     }
+    // Add convert link.
+    if (($module == 'block' || $module == 'boxes') && strpos($delta, 'add__') !== 0) {
+      $module_opp = $module == 'block' ? 'box' : 'block';
+      if (($module_opp == 'box' && $box->plugin_key == 'simple') || $module_opp == 'block') {
+        $form[$i]['configure']['#value'] .= ' | '. l(t('convert to @type', array('@type' => $module_opp)), 'boxes/convert/'. $module .'/'. $delta);        
+      }
+    }
   }
 }
 
