Index: migrate.info
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/migrate/migrate.info,v
retrieving revision 1.1.2.1
diff -u -p -r1.1.2.1 migrate.info
--- migrate.info	20 Mar 2009 13:18:20 -0000	1.1.2.1
+++ migrate.info	23 Sep 2009 14:21:16 -0000
@@ -1,7 +1,8 @@
-; $Id: migrate.info,v 1.1.2.1 2009/03/20 13:18:20 mikeryan Exp $
-name = "Migrate"
-description = "Import content from external sources"
-package = "Development"
-core = 6.x
-dependencies[] = views
-dependencies[] = tw
\ No newline at end of file
+; $Id: migrate.info,v 1.1.2.1 2009/03/20 13:18:20 mikeryan Exp $
+name = "Migrate"
+description = "Import content from external sources"
+package = "Development"
+core = 6.x
+dependencies[] = views
+dependencies[] = tw
+dependencies[] = ctools
Index: migrate.install
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/migrate/migrate.install,v
retrieving revision 1.1.2.13
diff -u -p -r1.1.2.13 migrate.install
--- migrate.install	18 Sep 2009 19:13:05 -0000	1.1.2.13
+++ migrate.install	23 Sep 2009 14:21:17 -0000
@@ -12,6 +12,17 @@
 function migrate_schema() {
   $schema['migrate_content_sets'] = array(
     'description' => t('Sets of content mappings'),
+    'export' => array(
+      'key' => 'view_name',
+      'identifier' => 'set',
+      'default hook' => 'migrate_default_content_sets',
+      'api' => array(
+        'owner' => 'migrate',
+        'api' => 'migrate_default_sets',
+        'minimum_version' => 1,
+        'current_version' => 1,
+        ),
+    ),
     'fields' => array(
       'mcsid' => array(
         'type' => 'serial',
@@ -91,6 +102,10 @@ function migrate_schema() {
   );
   $schema['migrate_content_mappings'] = array(
     'description' => t('Content field mappings'),
+    'export' => array(
+      'key' => 'mcmid',
+      'identifier' => 'map',
+    ),
     'fields' => array(
       'mcmid' => array(
         'type' => 'serial',
Index: migrate.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/migrate/migrate.module,v
retrieving revision 1.1.2.62
diff -u -p -r1.1.2.62 migrate.module
--- migrate.module	19 Sep 2009 16:23:57 -0000	1.1.2.62
+++ migrate.module	23 Sep 2009 14:21:18 -0000
@@ -1012,6 +1012,15 @@ function migrate_menu() {
     'access arguments' => array(MIGRATE_ACCESS_ADVANCED),
     'file' => 'migrate_pages.inc',
   );
+  $items['admin/content/migrate/content_sets/list'] = array(
+    'title' => 'List',
+    'description' => 'Manage content sets: mappings of source data to Drupal content',
+    'weight' => 1,
+    'page callback' => 'migrate_destinations',
+    'access arguments' => array(MIGRATE_ACCESS_ADVANCED),
+    'type' => MENU_DEFAULT_LOCAL_TASK,
+    'file' => 'migrate_pages.inc',
+    );
   $items['admin/content/migrate/process'] = array(
     'title' => 'Process',
     'description' => 'Perform and monitor the creation of Drupal content from source data',
@@ -1036,6 +1045,14 @@ function migrate_menu() {
     'access arguments' => array(MIGRATE_ACCESS_ADVANCED),
     'file' => 'migrate_pages.inc',
   );
+  $items['admin/content/migrate/content_sets/import'] = array(
+    'title' => 'Import',
+    'page callback' => 'migrate_import_content_set',
+    'access arguments' => array(MIGRATE_ACCESS_ADVANCED),
+    'type' => MENU_LOCAL_TASK,
+    'file' => 'migrate_export.inc',
+    'weight' => 3,
+  );
   $items['admin/content/migrate/content_sets/%'] = array(
     'title' => 'Content set',
     'page callback' => 'drupal_get_form',
@@ -1044,6 +1061,14 @@ function migrate_menu() {
     'type' => MENU_CALLBACK,
     'file' => 'migrate_pages.inc',
   );
+  $items['admin/content/migrate/content_sets/%/export'] = array(
+    'title' => 'Content set',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('migrate_export_content_set', 4),
+    'access arguments' => array(MIGRATE_ACCESS_ADVANCED),
+    'type' => MENU_CALLBACK,
+    'file' => 'migrate_export.inc',
+  );
   $items['migrate/xlat/%'] = array(
     'page callback' => 'migrate_xlat',
     'access arguments' => array('access content'),
@@ -1254,6 +1279,25 @@ function migrate_views_api() {
 }
 
 /**
+ * Retrieve list of mappings for a content set.
+ */
+function migrate_get_mappings($mcsid) {
+  $mappings = array();
+  $result = db_query("SELECT * FROM {migrate_content_mappings} WHERE mcsid = %d", $mcsid);
+  while ($map = db_fetch_object($result)) {
+    $mappings[$map->destfield] = $map;
+  }
+  return $mappings;
+}
+
+/**
+ * Check if this is a valid content set id.
+ */
+function migrate_validate_mcsid($mcsid) {
+  return db_result(db_query("SELECT count(*) FROM {migrate_content_sets} WHERE mcsid = %d", $mcsid));
+}
+
+/**
  * Check to see if the advanced help module is installed, and if not put up
  * a message.
  *
Index: migrate_export.inc
===================================================================
RCS file: migrate_export.inc
diff -N migrate_export.inc
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ migrate_export.inc	23 Sep 2009 14:21:18 -0000
@@ -0,0 +1,160 @@
+<?php
+// $Id$
+
+/**
+ * Page callback to import a content set from PHP code.
+ */
+function migrate_import_content_set() {
+  include_once('./' . drupal_get_path('module', 'migrate') . '/migrate_pages.inc');
+  if ($_POST['form_id'] == 'migrate_content_set_mappings') {
+    $set = unserialize($_SESSION['content_set_import']);
+    drupal_set_title(t('Import content set "@s"', array('@s' => $set->description)));
+    return drupal_get_form('migrate_content_set_mappings', $set);
+  }
+
+  return drupal_get_form('migrate_import_content_set_form');
+}
+
+/**
+ * Import content set form.
+ */
+function migrate_import_content_set_form() {
+  $form['content_set'] = array(
+    '#type' => 'textarea',
+    '#title' => t('Content set code'),
+    '#cols' => 60,
+    '#rows' => 15,
+    '#description' => t('Cut and paste the results of an exported content set here.'),
+  );
+
+  $form['submit'] = array(
+    '#type' => 'submit',
+    '#value' => t('Import'),
+  );
+
+  $form['#redirect'] = NULL;
+  return $form;
+}
+
+/**
+ * Handle the submit button on importing a content set.
+ */
+function migrate_import_content_set_form_submit($form, &$form_state) {
+  ob_start();
+  eval($form_state['values']['content_set']);
+  ob_end_clean();
+
+  if (isset($set)) {
+    include_once('./' . drupal_get_path('module', 'migrate') . '/migrate_pages.inc');
+    $_SESSION['content_set_import'] = serialize($set);
+    drupal_set_title(t('Import content set "@s"', array('@s' => $set->description)));
+    $output = drupal_get_form('migrate_content_set_mappings', $set);
+    print theme('page', $output);
+    exit;
+  }
+  else {
+    drupal_set_message(t('Unable to find a content set in the import data.'));
+  }
+}
+
+/**
+ * Export a content set and display it in a form.
+ */
+function migrate_export_content_set(&$form_state, $mcsid) {
+  $set = migrate_content_set_load($mcsid);
+  drupal_set_title(check_plain($set->description));
+  $code = migrate_content_set_export($set);
+  $lines = substr_count($code, "\n");
+
+  $form['export'] = array(
+    '#title' => t('Export data'),
+    '#type' => 'textarea',
+    '#value' => $code,
+    '#rows' => $lines,
+    '#description' => t('Copy the export text and paste it into another content set using the import function.'),
+  );
+  return $form;
+}
+
+/**
+ * Load a single content set.
+ */
+function migrate_content_set_load($mcsid) {
+  ctools_include('export');
+  $view_name = db_result(db_query("SELECT view_name FROM {migrate_content_sets} WHERE mcsid = %d", $mcsid));
+  $result = ctools_export_load_object('migrate_content_sets', 'names', array($view_name));
+  if (isset($result[$view_name])) {
+    $result[$view_name]->mappings = migrate_content_set_mappings_load($mcsid, $view_name);
+    return $result[$view_name];
+  }
+}
+
+/**
+ * Load all content sets.
+ */
+function migrate_load_all_content_sets() {
+  ctools_include('export');
+  $sets = ctools_export_load_object('migrate_content_sets');
+  $mcsids = array();
+  foreach ($sets as $set) {
+    $mcsids[$set->mcsid] = $set->view_name;
+  }
+
+  $mappings = migrate_content_set_mappings_load_all($mcsids);
+  foreach ($mappings as $mcsid => $mapping) {
+    if (!empty($mapping)) {
+      $sets[$mcsids[$mcsid]]->mappings = $mapping;
+    }
+  }
+
+  return $sets;
+}
+
+/**
+ * Load all content set mappings.
+ */
+function migrate_content_set_mappings_load_all($mcsids) {
+  $mappings = array();
+  foreach ($mcsids as $mcsid => $view_name) {
+    $mappings[$mcsid] = migrate_content_set_mappings_load($mcsid, $view_name);
+  }
+  return $mappings;
+}
+
+/**
+ * Load a single content set mapping.
+ */
+function migrate_content_set_mappings_load($mcsid, $view_name = '') {
+  ctools_include('export');
+  $result = db_query("SELECT * FROM {migrate_content_mappings} WHERE mcsid = %d", $mcsid);
+  $mappings = array();
+  while ($obj = db_fetch_object($result)) {
+    $mapping = ctools_export_unpack_object('migrate_content_mappings', $obj);
+    $mappings[$mapping->mcsid]->mappings[$mapping->destfield] = $mapping;
+  }
+  return $mappings[$mcsid]->mappings;
+}
+
+/**
+ * Export a content set.
+ */
+function migrate_content_set_export($set, $indent = '') {
+  ctools_include('export');
+  $output = ctools_export_object('migrate_content_sets', $set, $indent);
+  // Export the mappings.
+  $mappings = !empty($set->mappings) ? $set->mappings : migrate_content_set_mappings_load($set->mcsid);
+  $output .= migrate_export_mappings($mappings, $indent);
+  return $output;
+}
+
+/**
+ * Export the mappings for a content set.
+ */
+function migrate_export_mappings($mappings, $indent = '') {
+  ctools_include('export');
+  foreach ($mappings as $destfield => $map) {
+    $output .= ctools_export_object('migrate_content_mappings', $map, $indent);
+    $output .= $indent . '$set->mappings["' . $map->destfield . '"] = $map' . ";\n";
+  }
+  return $output;
+}
Index: migrate_pages.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/migrate/migrate_pages.inc,v
retrieving revision 1.1.2.43
diff -u -p -r1.1.2.43 migrate_pages.inc
--- migrate_pages.inc	18 Sep 2009 19:13:05 -0000	1.1.2.43
+++ migrate_pages.inc	23 Sep 2009 14:21:19 -0000
@@ -336,29 +336,43 @@ function _migrate_content_set_form($form
     array('data' => t('Weight'), 'field' => 'weight', 'sort' => 'asc'),
   );
 
-  $sql = "SELECT *
-          FROM {migrate_content_sets} ";
-  $sql .= tablesort_sql($header);
-  $result = db_query($sql);
+  // Load all content sets, both those from the database and code.
+  include_once('./' . drupal_get_path('module', 'migrate') .  '/migrate_export.inc');
+  $sets = migrate_load_all_content_sets();
+
   $used_views = array();
-  while ($row = db_fetch_object($result)) {
-    if ($row->desttype) {
-      $destination = $desttypes[$row->contenttype . '/' . $row->desttype];
+  foreach ($sets as $view_name => $set) {
+    if ($set->desttype) {
+      $destination = $desttypes[$set->contenttype . '/' . $set->desttype];
     }
     else {
-      $destination = $desttypes[$row->contenttype];
+      $destination = $desttypes[$set->contenttype];
+    }
+
+    // All sets that are only in code, save to the database for subsequent
+    // processing.
+    // @TODO: do this some place more sensible?
+    if ($set->export_type == EXPORT_IN_CODE) {
+      unset($set->mcsid);
+      $set->mcsid = migrate_save_content_set($set);
+      foreach ($set->mappings as $mapping) {
+        unset($mapping->mcmid);
+        $mapping->mcsid = $set->mcsid;
+        migrate_save_content_mapping($mapping);
+      }
     }
     $rows[] = array('data' =>
       array(
-        l($row->description, 'admin/content/migrate/content_sets/' . $row->mcsid,
-          array('html' => TRUE)),
+        $set->description,
+        l($set->view_name, 'admin/content/tw/view/'. $set->view_name),
         $destination,
-        l($row->view_name, 'admin/build/views/edit/' . $row->view_name),
-        $row->weight,
+        $set->weight,
+        l(t('Edit'), 'admin/content/migrate/content_sets/'. $set->mcsid, array('html' => TRUE)),
+        l(t('Export'), 'admin/content/migrate/content_sets/'. $set->mcsid .'/export', array('html' => TRUE)),
       ),
       'class' => "migrate-content-sets-tr",
     );
-    $used_views[$row->view_name] = $row->view_name;
+    $used_views[$set->view_name] = $set->view_name;
   }
 
   if (!isset($rows)) {
@@ -457,26 +471,40 @@ function _migrate_content_set_form_submi
 /**
  * Menu callback function.
  */
-function migrate_content_set_mappings($form_state, $mcsid) {
-  $row = db_fetch_object(db_query("SELECT * FROM {migrate_content_sets}
-                                   WHERE mcsid=%d",
-                                  $mcsid));
-  $desttype = $row->desttype;
-  $view_name = $row->view_name;
-  $sourcekey = $row->sourcekey;
-  $description = $row->description;
-  $contenttype = $row->contenttype;
-  $desttype = $row->desttype;
-  $weight = $row->weight;
+function migrate_content_set_mappings($form_state, $set) {
+  include_once('./' . drupal_get_path('module', 'migrate') .  '/migrate_export.inc');
+  $mcsid = $set->mcsid;
+  if (!is_object($set)) {
+    $mcsid = $set;
+    $set = migrate_content_set_load($mcsid);
+    $in_db = 1;
+  }
+  else {
+    $in_db = migrate_validate_mcsid($mcsid);
+  }
+  // If this set doesn't exist in the database, reset $mcsid to 0 so the set
+  // will be added, rather than an update being attempted.
+  if (!$in_db) {
+    $mcsid = 0;
+  }
+  $desttype = $set->desttype;
+  $view_name = $set->view_name;
+  $sourcekey = $set->sourcekey;
+  $description = $set->description;
+  $contenttype = $set->contenttype;
+  $desttype = $set->desttype;
+  $weight = $set->weight;
 
   // Fetch information on available destinations
   $desttypes = migrate_invoke_all('types');
   $destfields = migrate_invoke_all("fields_$contenttype", $desttype);
 
-  $form['mcsid'] = array(
-    '#type' => 'value',
-    '#value' => $mcsid,
-  );
+  if ($mcsid) {
+    $form['mcsid'] = array(
+      '#type' => 'value',
+      '#value' => $mcsid,
+    );
+  }
   $form['description'] = array(
     '#type' => 'textfield',
     '#title' => t('Description of the content set'),
@@ -562,25 +590,21 @@ function migrate_content_set_mappings($f
       '#title' => t('Primary key of source view'),
     );
 
-    $mappings = array();
+    $mappings = (!empty($set->mappings) ? $set->mappings : migrate_get_mappings($mcsid));
     $defaults = array();
 
     $srcoptions = array_merge(array('' => t('<none>')), $srcoptions);
     foreach ($destfields as $destfield => $destname) {
-      $sql = "SELECT *
-              FROM {migrate_content_mappings}
-              WHERE mcsid=%d AND destfield='%s'";
-      $result = db_query($sql, $mcsid, $destfield);
-      $row = db_fetch_object($result);
+      $map = $mappings[$destfield];
       $cols[] = $destfield;
       $form['srcfield'][$destfield] = array(
         '#type' => 'select',
         '#options' => $srcoptions,
-        '#default_value' => isset($row->srcfield) ? $row->srcfield : '',
+        '#default_value' => isset($map->srcfield) ? $map->srcfield : '',
       );
       $form['default_value'][$destfield] = array(
         '#type' => 'textfield',
-        '#default_value' => isset($row->default_value) ? $row->default_value : '',
+        '#default_value' => isset($map->default_value) ? $map->default_value : '',
         '#size' => 25,
         '#maxlength' => 255,
       );
@@ -597,10 +621,20 @@ function migrate_content_set_mappings($f
       '#value' => t('Submit changes'),
     );
   }
-  $form['delete'] = array(
-    '#type' => 'submit',
-    '#value' => t('Delete'),
-  );
+  // Only display the delete button if this content set already exists in the
+  // database.
+  if ($set->type == 'Overridden') {
+    $form['delete'] = array(
+      '#type' => 'submit',
+      '#value' => t('Reset'),
+    );
+  }
+  elseif ($mcsid) {
+    $form['delete'] = array(
+      '#type' => 'submit',
+      '#value' => t('Delete'),
+    );
+  }
   $form['#tree'] = TRUE;
   return $form;
 }
@@ -647,7 +681,18 @@ function migrate_content_set_mappings_su
   if ($form_state['clicked_button']['#parents'][0] == 'delete') {
     migrate_delete_content_set($mcsid);
     drupal_set_message(t('Content set deleted'));
-    $form_state['redirect'] = 'admin/content/migrate/content_sets';
+  }
+  elseif (empty($mcsid)) {
+    $mcsid = migrate_save_content_set($form_state['values']);
+    foreach ($form_state['values']['cols'] as $key => $destfield) {
+      $mapping = new stdClass;
+      $mapping->mcsid = $mcsid;
+      $mapping->srcfield = $form_state['values']['srcfield'][$destfield];
+      $mapping->destfield = $destfield;
+      $mapping->default_value = $form_state['values']['default_value'][$destfield];
+      migrate_save_content_mapping($mapping);
+    }
+    drupal_set_message('New content set saved.');
   }
   else {
     // Get list of current mappings, so we know if any need deletion
@@ -680,6 +725,7 @@ function migrate_content_set_mappings_su
     migrate_save_content_set($form_state['values']);
     drupal_set_message('Changes saved');
   }
+  $form_state['redirect'] = 'admin/content/migrate/content_sets';
 }
 
 function migrate_tools() {
