diff --git ctools.module ctools.module
index 1255b04..6684e42 100644
--- ctools.module
+++ ctools.module
@@ -284,7 +284,7 @@ function _ctools_passthrough(&$items, $type = 'theme') {
     require_once './' . $file->filename;
     list($tool) = explode('.', $file->name, 2);
 
-    $function = 'ctools_' . $tool . '_' . $type;
+    $function = 'ctools_' . str_replace ('-', '_', $tool) . '_' . $type;
     if (function_exists($function)) {
       $function($items);
     }
@@ -760,3 +760,60 @@ function ctools_file_check_directory(&$directory, $mode = 0, $form_item = NULL)
 
   return TRUE;
 }
+
+/**
+ * Menu loader; Load exportables when used with export-ui.
+ */
+function ctools_export_ui_load($export_name, $map_array) {
+  $return = &ctools_static(__FUNCTION__, FALSE);
+
+  if (!$return) {
+    ctools_include('export');
+    ctools_include('plugins');
+    // TODO: We include export-ui for ctools_export_ui_defaults().
+    // Maybe ctools_get_plugins() should do it for us.
+    ctools_include('export-ui');
+
+    $path = implode('/', $map_array);
+
+    // Identify the plugin by the menu item path.
+    $plugin_item = array();
+    foreach (ctools_get_plugins('ctools', 'export_ui') as $plugin) {
+      if ($plugin['has menu']) {
+        $prefix = $plugin['menu']['menu prefix'] .'/'. $plugin['menu']['menu item'];
+        if (strpos($path, $prefix) === 0) {
+          $plugin_item = $plugin;
+          break;
+        }
+      }
+    }
+
+    if ($plugin_item) {
+      // Get the load callback.
+      $schema = ctools_export_get_schema($plugin_item['schema']);
+      $return = call_user_func($schema['export']['load callback'], $export_name);
+    }
+  }
+
+  return $return;
+}
+
+/**
+ * Menu access callback for various tasks of export-ui.
+ */
+function ctools_export_ui_task_access($export, $op) {
+  if (!user_access('administer site configuration')) {
+    return FALSE;
+  }
+  switch ($op) {
+    case 'revert':
+      return ($export->export_type & EXPORT_IN_DATABASE) && ($export->export_type & EXPORT_IN_CODE);
+    case 'delete':
+      return ($export->export_type & EXPORT_IN_DATABASE) && !($export->export_type & EXPORT_IN_CODE);
+    case 'disable':
+      return empty($export->disabled);
+    case 'enable':
+      return !empty($export->disabled);
+  }
+  return TRUE;
+}
diff --git includes/export-ui.admin.inc includes/export-ui.admin.inc
new file mode 100644
index 0000000..22c7758
--- /dev/null
+++ includes/export-ui.admin.inc
@@ -0,0 +1,382 @@
+<?php
+// $Id:$
+
+ctools_include('export-ui');
+
+/**
+ * Exportables listing form.
+ */
+function ctools_export_ui_admin($form_state, $plugin_name) {
+  ctools_include('export');
+  ctools_include('plugins');
+  $plugin = ctools_get_plugins('ctools', 'export_ui', $plugin_name);
+
+  ctools_export_load_object_reset($plugin['schema']);
+  $exports = ctools_export_load_object($plugin['schema'], 'all');
+
+  $form = array(
+    '#theme' => 'ctools_export_ui_admin',
+    '#exports' => $exports,
+    '#plugin' => $plugin_name,
+  );
+
+  return $form;
+}
+
+
+/**
+ * Generates the omnibus export definition editing form.
+ *
+ * @param $op
+ *   The type of form to build. Either "add", "view" or "edit"
+ * @param $export
+ *   The export.
+ *
+ * @return
+ *   A Drupal form array.
+ */
+function ctools_export_ui_form(&$form_state, $plugin_name, $op, $export = NULL) {
+  ctools_include('export');
+
+  ctools_include('plugins');
+  $plugin = ctools_get_plugins('ctools', 'export_ui', $plugin_name);
+  $export_key = $plugin['export']['key'];
+  $title = $plugin['title'];
+
+  $export = !$export ? ctools_export_new_object($plugin['schema']) : $export;
+  switch ($op) {
+    case 'add':
+      drupal_set_title(t('Add a new @plugin', array('@plugin' => $title)));
+      break;
+    case 'edit':
+      if ($export->export_type & EXPORT_IN_DATABASE) {
+        drupal_set_title(t('Editing @plugin %title', array('@plugin' => $title, '%title' => $export->{$export_key})));
+      }
+      else {
+        drupal_set_title(t('Viewing @plugin %title', array('@plugin' => $title, '%title' => $export->{$export_key})));
+      }
+      break;
+    case 'clone':
+      drupal_set_title(t('Cloning @plugin %title', array('@plugin' => $title, '%title' => $export->{$export_key})));
+      break;
+  }
+
+  $form['export'] = array(
+    '#type' => 'value',
+    '#value' => $export,
+  );
+
+  $form['plugin'] = array(
+    '#type' => 'value',
+    '#value' => $plugin_name,
+  );
+
+  $form['op'] = array(
+    '#type' => 'value',
+    '#value' => $op,
+  );
+
+  $form['info'] = array(
+    '#type' => 'fieldset',
+  );
+
+  if ($op == 'clone') {
+    $export->{$export_key} = 'Clone of '. $export->{$export_key};
+  }
+
+  $form['info'][$export_key] = array(
+    // TODO: how to get human readable name and translate this key?
+    '#title' => ucfirst($export_key),
+    '#type' => 'textfield',
+    '#default_value' => !empty($export->{$export_key}) ? $export->{$export_key} : '',
+    '#description' => t('The unique ID for this @export', array('@export' => $title)),
+    '#required' => TRUE,
+    '#maxlength' => 255,
+  );
+
+  if ($op === 'edit') {
+    $form['info'][$export_key]['#disabled'] = TRUE;
+    $form['info'][$export_key]['#value'] = $export->{$export_key};
+  }
+
+  $form['info']['tag'] = array(
+    '#title' => t('Tag'),
+    '#type' => 'textfield',
+    '#default_value' => !empty($export->tag) ? $export->tag : '',
+    '#description' => t('Tag for this @export', array('@export' => $title)),
+  );
+
+
+  // Add plugin's form definitions.
+  if (!empty($plugin['settings form']) && function_exists($plugin['settings form'])) {
+    // Pass $form by reference.
+    // TODO: validation and submit handlers should be declared by settings form
+    // callback?
+    $plugin['settings form']($form, $plugin_name, $op, $export);
+  }
+
+  // Add buttons.
+  $form['buttons']['submit'] = array(
+    '#type' => 'submit',
+    '#value' => t('Save'),
+  );
+  $form['buttons']['delete'] = array(
+    '#type' => 'submit',
+    '#value' => t('Delete'),
+    '#access' => $op === 'edit',
+  );
+
+  return $form;
+}
+
+/**
+ * Provide a form to confirm one of the provided actions.
+ */
+function ctools_export_ui_confirm(&$form_state, $plugin_name, $op = 'delete', $export) {
+  $form = array();
+  $form['export'] = array('#type' => 'value', '#value' => $export);
+  $form['action'] = array('#type' => 'value', '#value' => $op);
+  $form['plugin'] = array('#type' => 'value', '#value' => $plugin_name);
+  switch ($op) {
+    case 'revert':
+      $action = t('revert');
+      $description = t('This action will permanently remove any customizations made to this export.');
+      break;
+    case 'delete':
+      $action = t('delete');
+      $description = t('This action will remove this export permanently from your site.');
+      break;
+    case 'disable':
+      $action = t('disable');
+      $description = '';
+      break;
+    case 'enable':
+      $action = t('enable');
+      $description = '';
+      break;
+  }
+
+  ctools_include('plugins');
+  $plugin = ctools_get_plugins('ctools', 'export_ui', $plugin_name);
+
+  $export_key = $plugin['export']['key'];
+
+  $form = confirm_form($form,
+    t('Are you sure you want to !action the @plugin %title?', array('!action' => $action, '@plugin' => $plugin['title'], '%title' => $export->{$export_key})),
+    _ctools_export_ui_redirect($plugin['name']),
+    $description,
+    drupal_ucfirst($action), t('Cancel')
+  );
+  return $form;
+}
+
+/**
+ * Submit handler for the ctools_export_ui_confirm form.
+ */
+function ctools_export_ui_confirm_submit($form, &$form_state) {
+  ctools_include('export');
+  ctools_include('plugins');
+  $plugin = ctools_get_plugins('ctools', 'export_ui', $form['plugin']['#value']);
+
+  $export = $form_state['values']['export'];
+  switch ($form_state['values']['action']) {
+    case 'revert':
+    case 'delete':
+      $schema = ctools_export_get_schema($plugin['schema']);
+      call_user_func($schema['export']['delete callback'], $export);
+      break;
+    case 'disable':
+      ctools_export_set_object_status($export);
+      break;
+    case 'enable':
+      ctools_export_set_object_status($export, FALSE);
+      break;
+  }
+  $form_state['redirect'] = _ctools_export_ui_redirect($plugin['name']);
+}
+
+/**
+ * Page callback for import form. Switches form output to export form
+ * if import submission has occurred.
+ */
+function ctools_export_ui_import_page($plugin_name) {
+  if (!empty($_POST) && $_POST['form_id'] == 'ctools_export_ui_form') {
+    return drupal_get_form('ctools_export_ui_form', $plugin_name, 'add');
+  }
+  return drupal_get_form('ctools_export_ui_import', $plugin_name);
+}
+
+/**
+ * Import form. Provides simple helptext instructions and textarea for
+ * pasting a export definition.
+ */
+function ctools_export_ui_import($form_state, $plugin_name) {
+  ctools_include('plugins');
+  $plugin = ctools_get_plugins('ctools', 'export_ui', $plugin_name);
+
+  drupal_set_title(t('Import @plugin', array('@plugin' => $plugin['title'])));
+  $help = t('You can import a export definition by pasting the exported export object code into the field below.');
+
+  $form = array();
+  $form['plugin'] = array(
+    '#type' => 'value',
+    '#value' => $plugin_name,
+  );
+
+  $form['help'] = array(
+    '#type' => 'item',
+    '#value' => $help,
+  );
+  $form['import'] = array(
+    '#title' => t('@plugin object', array('@plugin' => $plugin['title'])),
+    '#type' => 'textarea',
+    '#rows' => 10,
+    '#required' => TRUE,
+  );
+  $form['submit'] = array(
+    '#type' => 'submit',
+    '#value' => t('Import'),
+  );
+  return $form;
+}
+
+/**
+ * Import form submit handler. Evaluates import code and transfers to
+ * export definition form.
+ */
+function ctools_export_ui_import_submit($form, &$form_state) {
+  ctools_include('plugins');
+  $plugin = ctools_get_plugins('ctools', 'export_ui', $form['plugin']['#value']);
+  $export_key = $plugin['export']['key'];
+
+  $items = array();
+  if ($import = $form_state['values']['import']) {
+    ob_start();
+    $export = eval($import);
+    ob_end_clean();
+  }
+
+  if (is_object($export)) {
+    if (!empty($export->{$export_key})) {
+
+      $schema = ctools_export_get_schema($plugin['schema']);
+
+      // TODO: If $schema['export']['load callback'] == FALSE we should issue
+      // an error - but maybe it should happen in a more generic place.
+      if (call_user_func($schema['export']['load callback'], $export->{$export_key})) {
+        drupal_set_message(t('A @plugin with this name already exists. Please remove the existing export before importing this definition.', array('@plugin' => $plugin['title'])), 'error');
+      }
+      else {
+        drupal_set_title(t('Export @plugin', array('@plugin' => $plugin['title'])));
+        $output = drupal_get_form('ctools_export_ui_form', $plugin['name'], 'add', (object) $export);
+        print theme('page', $output);
+        exit;
+      }
+    }
+  }
+  else {
+    drupal_set_message(t('An error occurred while importing. Please check your export definition.', 'error'));
+
+    $form_state['redirect'] = _ctools_export_ui_redirect($plugin['name']);
+  }
+}
+
+/**
+ * Provides a form with an exported export definition for use in modules.
+ *
+ * @param $cid
+ *   A export id.
+ *
+ * @return
+ *   A FormAPI array.
+ */
+function ctools_export_ui_export(&$form_state, $export, $plugin_name) {
+  ctools_include('export');
+  ctools_include('plugins');
+  $plugin = ctools_get_plugins('ctools', 'export_ui', $plugin_name);
+
+  $export_key = $plugin['export']['key'];
+
+  drupal_set_title(t('Export %title', array('%title' => $export->{$export_key})));
+
+  return ctools_export_form($form_state, ctools_export_ui_export_object($plugin_name, $export), t('Export'));
+}
+
+/**
+ * Validate export id values.
+ */
+function ctools_export_ui_form_name_validate($element, &$form_state) {
+  $plugin = $element['#plugin'];
+  // Check for string identifier sanity
+  if (!preg_match('!^[a-z0-9_-]+$!', $element['#value'])) {
+    form_set_error('name', t('The export id can only consist of lowercase letters, dashes, underscores, and numbers.'));
+  }
+  // Check for name collision
+  else if ($exists = ctools_export_load_object($plugin->name, 'names', array($element['#value']))) {
+    form_set_error('name', t('A @plugin with this name already exists. Please choose another name or delete the existing export before creating a new one.', array('@plugin' => $pluin->title)));
+  }
+}
+
+/**
+ * Produces a export object from submitted form values.
+ *
+ * @param $form
+ *   A form array with submitted values
+ *
+ * @return
+ *   A export object
+ */
+function ctools_export_ui_form_process($form, $form_state) {
+  ctools_include('plugins');
+  $plugin = ctools_get_plugins('ctools', 'export_ui', $form['plugin']['#value']);
+
+  $export_key = $plugin->plugin['export']['key'];
+
+  $values = $form_state['values'];
+
+  $export = ctools_export_new_object($plugin->plugin['schema']);
+
+  $schema = ctools_export_get_schema($plugin['schema']);
+  foreach (array_keys($schema['fields']) as $key) {
+    if(isset($values[$key])) {
+      $export->{$key} = $values[$key];
+    }
+  }
+
+  return $export;
+}
+
+/**
+ * Submit handler for main ctools_export_ui form.
+ */
+function ctools_export_ui_form_submit($form, &$form_state) {
+  ctools_include('export');
+  ctools_include('plugins');
+  $plugin = ctools_get_plugins('ctools', 'export_ui', $form['plugin']['#value']);
+  $export_key = $plugin['export']['key'];
+
+  switch ($form_state['clicked_button']['#id']) {
+    // Send user to delete confirmation page
+    case 'edit-delete':
+      if (isset($form_state['values']['export']->{$export_key})) {
+        $menu_prefix = _ctools_export_ui_redirect($plugin['name']);
+        $form_state['redirect'] = "$menu_prefix/list/{$form_state['values']['export']->{$export_key}}/delete";
+      }
+      return;
+    // Process form values and save and/or update the export in the db
+    case 'edit-submit':
+      $export = ctools_export_ui_form_process($form, $form_state);
+
+      $schema = ctools_export_get_schema($plugin['schema']);
+      $result = call_user_func($schema['export']['save callback'], $export);
+
+      if ($result) {
+        drupal_set_message(t('Saved @plugin %title.', array('@plugin' => $plugin['title'], '%title' =>  $export->{$export_key})));
+      }
+      else {
+        drupal_set_message(t('Could not save @plugin %title.', array('@plugin' => $plugin['title'], '%title' =>  $export->{$export_key})), 'error');
+      }
+      break;
+  }
+  $form_state['redirect'] = _ctools_export_ui_redirect($plugin['name']);
+}
\ No newline at end of file
diff --git includes/export-ui.inc includes/export-ui.inc
new file mode 100644
index 0000000..93c2ffe
--- /dev/null
+++ includes/export-ui.inc
@@ -0,0 +1,252 @@
+<?php
+// $Id: export_ui.module,v 1.13.2.48.2.1.2.11 2010/02/28 19:30:48 yhahn Exp $
+
+/**
+ * Implementation of hook_ctools_plugin_*.
+ *
+ * Give information to CTools about the content types plugin.
+ */
+function ctools_ctools_plugin_export_ui() {
+  return array(
+    'defaults' => 'ctools_export_ui_defaults',
+  );
+}
+
+/**
+ * Provide defaults for an export-ui plugin.
+ */
+function ctools_export_ui_defaults($info, &$plugin) {
+  ctools_include('export');
+
+  $plugin += array(
+    'has menu' => TRUE,
+    'title' => $plugin['name'],
+    'export' => array(),
+    'allowed operations' => array(),
+    'menu' => array(),
+  );
+
+  $schema = ctools_export_get_schema($plugin['schema']);
+
+  $plugin['export'] += array(
+    // Add the identifier key from the schema so we don't have to call
+    // ctools_export_get_schema() just for that.
+    'key' => $schema['export']['key'],
+  );
+
+  // Define allowed operations.
+  $plugin['allowed operations'] += array(
+    'import'  => TRUE,
+    'export'  => TRUE,
+    'enable'  => TRUE,
+    'disable' => TRUE,
+    'revert'  => TRUE,
+    'clone'   => TRUE,
+    'delete'  => TRUE,
+  );
+
+  if ($plugin['has menu']) {
+    $plugin['menu'] += array(
+      'menu item' => str_replace(' ', '-', $plugin['name']),
+      'menu prefix' => 'admin/build',
+    );
+
+    $prefix_count = count(explode('/', $plugin['menu']['menu prefix']));
+
+    $plugin['menu'] += array(
+      // Default menu items that should be declared.
+      'items' => array(
+        'list callback' => array(
+          'path' => '',
+          // Menu items are translated by the menu system.
+          'title' => $plugin['title'],
+          'description' => 'List '. $plugin['title'],
+          'page callback' => 'drupal_get_form',
+          'page arguments' => array('ctools_export_ui_admin', $plugin['name']),
+          'type' => MENU_NORMAL_ITEM,
+        ),
+        'list' => array(
+          'path' => 'list',
+          'title' => 'List',
+          'description' => 'List '. $plugin['title'],
+          'page callback' => 'drupal_get_form',
+          'page arguments' => array('ctools_export_ui_admin', $plugin['name']),
+          'type' => MENU_DEFAULT_LOCAL_TASK,
+        ),
+        'add' => array(
+          'path' => 'add',
+          'title' => 'Add',
+          'description' => 'Add a new '. $plugin['title'],
+          'page callback' => 'drupal_get_form',
+          'page arguments' => array('ctools_export_ui_form', $plugin['name'], 'add'),
+          'type' => MENU_LOCAL_TASK,
+        ),
+        'edit callback' => array(
+          'path' => 'list/%ctools_export_ui',
+          'page callback' => 'drupal_get_form',
+          'page arguments' => array('ctools_export_ui_form', $plugin['name'], 'edit', $prefix_count + 2),
+          'type' => MENU_CALLBACK,
+        ),
+        'edit' => array(
+          'path' => 'list/%ctools_export_ui/edit',
+          'title' => 'Edit',
+          'page callback' => 'drupal_get_form',
+          'page arguments' => array('ctools_export_ui_form', $plugin['name'], 'edit', $prefix_count + 2),
+          'type' => MENU_DEFAULT_LOCAL_TASK,
+        ),
+      ),
+    );
+
+    if ($plugin['allowed operations']['import']) {
+      $plugin['menu']['items'] += array(
+        'import' => array(
+          'path' => 'import',
+          'title' => 'Import',
+          'description' => 'Import '. $plugin['title'] .' to your site.',
+          // We allow permissions to import only to users that are allowed to
+          // execute php.
+          'access callback' => 'ctools_access_multiperm',
+          'access arguments' => array('use PHP for block visibility'),
+          'page callback' => 'ctools_export_ui_import_page',
+          'page arguments' => array($plugin['name']),
+          'type' => MENU_LOCAL_TASK,
+        ),
+      );
+    }
+
+    if ($plugin['allowed operations']['export']) {
+      $plugin['menu']['items'] += array(
+        'export' => array(
+          'path' => 'list/%ctools_export_ui/export',
+          'title' => 'Export',
+          'description' => 'Export '. $plugin['title'],
+          'page callback' => 'drupal_get_form',
+          'page arguments' => array('ctools_export_ui_export', $prefix_count + 2, $plugin['name']),
+          'type' => MENU_LOCAL_TASK,
+        ),
+      );
+    }
+
+    if ($plugin['allowed operations']['revert']) {
+      $plugin['menu']['items'] += array(
+        'revert' => array(
+          'path' => 'list/%ctools_export_ui/revert',
+          'title' => 'Revert',
+          'description' => 'Revert '. $plugin['title'],
+          'page callback' => 'drupal_get_form',
+          'page arguments' => array('ctools_export_ui_confirm', $plugin['name'], 'revert', $prefix_count + 2),
+          'access callback' => 'ctools_export_ui_task_access',
+          'access arguments' => array($prefix_count + 2, 'revert'),
+          'type' => MENU_LOCAL_TASK,
+        ),
+      );
+    }
+
+    if ($plugin['allowed operations']['clone']) {
+      $plugin['menu']['items'] += array(
+        'clone' => array(
+          'path' => 'list/%ctools_export_ui/clone',
+          'title' => 'Clone',
+          'description' => 'Revert '. $plugin['title'],
+          'page callback' => 'drupal_get_form',
+          'page arguments' => array('ctools_export_ui_form', $plugin['name'], 'clone', $prefix_count + 2),
+          'type' => MENU_LOCAL_TASK,
+        ),
+      );
+    }
+
+    if ($plugin['allowed operations']['delete']) {
+      $plugin['menu']['items'] += array(
+        'delete' => array(
+          'path' => 'list/%ctools_export_ui/delete',
+          'title' => 'Delete',
+          'description' => 'Delete '. $plugin['title'],
+          'page callback' => 'drupal_get_form',
+          'page arguments' => array('ctools_export_ui_confirm', $plugin['name'], 'delete', $prefix_count + 2),
+          'access callback' => 'ctools_export_ui_task_access',
+          'access arguments' => array($prefix_count + 2, 'delete'),
+          'type' => MENU_LOCAL_TASK,
+        ),
+      );
+    }
+
+    if ($plugin['allowed operations']['enable']) {
+      $plugin['menu']['items'] += array(
+        'enable' => array(
+          'path' => 'list/%ctools_export_ui/enable',
+          'title' => 'Enable',
+          'description' => 'Enable '. $plugin['title'],
+          'page callback' => 'drupal_get_form',
+          'page arguments' => array('ctools_export_ui_confirm', $plugin['name'], 'enable', $prefix_count + 2),
+          'access callback' => 'ctools_export_ui_task_access',
+          'access arguments' => array($prefix_count + 2, 'enable'),
+          'type' => MENU_LOCAL_TASK,
+        ),
+      );
+    }
+
+    if ($plugin['allowed operations']['disable']) {
+      $plugin['menu']['items'] += array(
+        'disable' => array(
+          'path' => 'list/%ctools_export_ui/disable',
+          'title' => 'Disable',
+          'description' => 'Disable '. $plugin['title'],
+          'page callback' => 'drupal_get_form',
+          'page arguments' => array('ctools_export_ui_confirm', $plugin['name'], 'disable', $prefix_count + 2),
+          'access callback' => 'ctools_export_ui_task_access',
+          'access arguments' => array($prefix_count + 2, 'disable'),
+          'type' => MENU_LOCAL_TASK,
+        ),
+      );
+    }
+  }
+}
+
+/**
+ * Get a plugin handler.
+ */
+function ctools_export_ui_get_plugin($key, $info = array()) {
+  global $user;
+  $cache = &ctools_static(__FUNCTION__, array());
+  if (empty($cache[$key])) {
+    ctools_include('plugins');
+    $plugin = ctools_get_plugins('ctools', 'export_ui', $key);
+
+    if ($class = ctools_plugin_get_class($plugin, 'handler')) {
+      $cache[$key] = new $class($plugin, $info);
+    }
+  }
+  return !empty($cache[$key]) ? $cache[$key] : FALSE;
+}
+
+
+/**
+ * CTools export function.
+ */
+function ctools_export_ui_export_object($plugin_name, $export, $indent = '') {
+  ctools_include('plugins');
+  $plugin = ctools_get_plugins('ctools', 'export_ui', $plugin_name);
+  $schema = ctools_export_get_schema($plugin['schema']);
+
+  // TODO: Add translatable fields.
+
+  $extra ="\nreturn \$". $plugin['schema'] .";\n";
+
+  return ctools_export_object($plugin['schema'], $export, $indent) . $extra;
+}
+
+/**
+ * Get redirection path from a plugin.
+ *
+ * @param $plguin_name
+ *   The plugin name.
+ *
+ * @return
+ *   The menu path to the plugin's list.
+ */
+function _ctools_export_ui_redirect($plugin_name) {
+  ctools_include('plugins');
+  $plugin = ctools_get_plugins('ctools', 'export_ui', $plugin_name);
+
+  return $plugin['menu']['menu prefix'] .'/'. $plugin['menu']['menu item'];
+}
\ No newline at end of file
diff --git includes/export-ui.menu.inc includes/export-ui.menu.inc
new file mode 100644
index 0000000..b2f8385
--- /dev/null
+++ includes/export-ui.menu.inc
@@ -0,0 +1,33 @@
+<?php
+// $Id: export_ui.module,v 1.13.2.48.2.1.2.11 2010/02/28 19:30:48 yhahn Exp $
+
+
+/**
+ * Delegated implementation of hook_menu().
+ */
+function ctools_export_ui_menu(&$items) {
+  ctools_include('plugins');
+  // TODO: Maybe the include should be done in ctools_get_plugins()?
+  ctools_include('export-ui');
+
+  foreach (ctools_get_plugins('ctools', 'export_ui') as $plugin) {
+    if ($plugin['has menu']) {
+      $prefix = $plugin['menu']['menu prefix'] .'/'. $plugin['menu']['menu item'];
+
+      foreach ($plugin['menu']['items'] as $item) {
+        // Add menu item defaults.
+        $item += array(
+          'access arguments' => array('administer site configuration'),
+          'file' => 'export-ui.admin.inc',
+          'file path' => drupal_get_path('module', 'ctools') .'/includes',
+          // Add the map, so we can get the plugin in export_ui_load().
+          'load arguments' => array('%map'),
+        );
+
+        $path = !empty($item['path']) ? $prefix .'/'. $item['path'] : $prefix;
+        unset($item['path']);
+        $items[$path] = $item;
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git includes/export-ui.theme.inc includes/export-ui.theme.inc
new file mode 100644
index 0000000..03514c3
--- /dev/null
+++ includes/export-ui.theme.inc
@@ -0,0 +1,89 @@
+<?php
+
+
+/**
+ * Delegated implementation of hook_theme().
+ */
+function ctools_export_ui_theme(&$items) {
+  $items['ctools_export_ui_admin'] = array(
+    'arguments' => array('form' => array()),
+    'file' => 'export-ui.theme.inc',
+    'path' => drupal_get_path('module', 'ctools') .'/includes',
+  );
+}
+
+/**
+ * Generates the main export_ui admin page with a tiered export listing.
+ */
+function theme_ctools_export_ui_admin($form) {
+  ctools_include('export-ui');
+
+  ctools_include('plugins');
+  $plugin = ctools_get_plugins('ctools', 'export_ui', $form['#plugin']);
+  $export_key = $plugin['export']['key'];
+
+  // Iterate once to group by tag.
+  $by_tag = array();
+  foreach ($form['#exports'] as $export) {
+    if (!empty($export->tag)) {
+      $by_tag[$export->tag][$export->{$export_key}] = $export;
+    }
+    else {
+      $by_tag[''][$export->{$export_key}] = $export;
+    }
+  }
+
+  // Generate listing of existing exports
+  ksort($by_tag);
+  $rows = array();
+  foreach ($by_tag as $tag => $exports) {
+    if (!empty($tag)) {
+      $rows[] = array(array('data' => check_plain($tag), 'colspan' => 3, 'class' => 'tag'));
+    }
+    ksort($exports);
+    foreach ($exports as $export) {
+      $row = array('data' => array());
+
+      // Exportable info
+      $storage = t('Default');
+      if ($export->export_type & EXPORT_IN_DATABASE) {
+        $storage = $export->export_type & EXPORT_IN_CODE ? t('Overridden') : t('Normal');
+      }
+      $data = "<strong>{$export->{$export_key}}</strong> <em class='storage'>({$storage})</em>";
+      $data .= !empty($export->description) ? '<div class="description">'. filter_xss_admin($export->description) .'</div>' : '';
+      $row['data'][] = array('data' => $data, 'colspan' => 2, 'class' => 'export-name');
+
+      // Exportable actions
+      $candidate_links = array(
+        'edit'    => t('Edit'),
+        'delete'  => t('Delete'),
+        'revert'  => t('Revert'),
+        'export'  => t('Export'),
+        'clone'   => t('Clone'),
+        'disable' => t('Disable'),
+        'enable'  => t('Enable'),
+      );
+
+      $links = array();
+
+      foreach ($candidate_links as $key => $value) {
+        if (!empty($plugin['menu']['items'][$key]) && ctools_export_ui_task_access($export, $key)) {
+          $url = _ctools_export_ui_redirect($plugin['name']) .'/'. str_replace('%ctools_export_ui', $export->{$export_key}, $plugin['menu']['items'][$key]['path']);
+          $links[$key] = l($value, $url);
+        }
+      }
+
+      $row['data'][] = array('data' => implode(' | ', $links), 'class' => 'export-links');
+      $row['class'] = empty($export->disabled) ? 'enabled' : 'disabled';
+      $rows[] = $row;
+    }
+  }
+  $rows[] = array(
+    drupal_render($form['tag']),
+    drupal_render($form[$export_key]),
+    drupal_render($form['submit']),
+  );
+  $output = theme('table', array($plugin->title, '', t('Operations')), $rows, array('class' => 'export-admin'));
+  $output .= drupal_render($form);
+  return $output;
+}
diff --git includes/export.inc includes/export.inc
index b465d38..f3d2277 100644
--- includes/export.inc
+++ includes/export.inc
@@ -460,6 +460,13 @@ function ctools_export_get_schema($table) {
     'export callback' => "$schema[module]_export_{$table}",
     'list callback' => "$schema[module]_{$table}_list",
     'to hook code callback' => "$schema[module]_{$table}_to_hook_code",
+
+    // Define CRUD functions, if none are defined in the exportable
+    // schema.
+    'create callback' => 'ctools_export_new_object',
+    'save callback'   => function_exists("$schema[module]_save") ? "$schema[module]_save" : FALSE,
+    'delete callback' => function_exists("$schema[module]_delete") ? "$schema[module]_delete" : FALSE,
+    'load callback'   => function_exists("$schema[module]_load") ? "$schema[module]_load" : FALSE,
   );
 
   return $schema;
diff --git plugins/export_ui/ctools_export_ui.class.php plugins/export_ui/ctools_export_ui.class.php
new file mode 100644
index 0000000..8486fb8
--- /dev/null
+++ plugins/export_ui/ctools_export_ui.class.php
@@ -0,0 +1,24 @@
+<?php
+// $Id:$
+
+/**
+ * Base class for export UI.
+ */
+class ctools_export_ui {
+  var $plugin;
+  var $name;
+  var $options = array();
+
+  /**
+   * Constructor. Do not override.
+   */
+  function __construct($plugin, $info = array()) {
+    ctools_include('export');
+
+    $this->plugin = $plugin;
+    $this->name = $plugin['name'];
+
+    $this->options = array();
+  }
+
+}
diff --git plugins/export_ui/ctools_export_ui.inc plugins/export_ui/ctools_export_ui.inc
new file mode 100644
index 0000000..b2dd998
--- /dev/null
+++ plugins/export_ui/ctools_export_ui.inc
@@ -0,0 +1,20 @@
+<?php
+// $Id:$
+
+/**
+ * TODO: Document plugin keys:
+ * - name: The name of the plugin. This should be the same as the file name.
+ * - has menu: Deterimne if hook_menu() should declare items of this plugin.
+ *   Defaults to TRUE?
+ * - menu item: Optional; The menu item that should be used. For example if the
+ *   value is set to 'foo', then the URL will admin/build/foo. If no value is
+ *   defined then the plugin name will be used.
+ */
+$plugin = array(
+  'name' => 'ctools_export_ui',
+  'handler' => array(
+    'class' => 'ctools_export_ui',
+  ),
+  // As this is the base class plugin, it shouldn't declare any menu items.
+  'has menu' => FALSE,
+);
