diff --git a/css/ctools.css b/css/ctools.css
index 4a2d875d53620c10c95ac663f41590b8b903af91..aac0f2b0c3f25c30070428d52b18ebbac5659e8e 100644
--- a/css/ctools.css
+++ b/css/ctools.css
@@ -11,12 +11,14 @@
   padding: 1em;
 }
 
-a.ctools-ajaxing {
+a.ctools-ajaxing,
+input.ctools-ajaxing {
   padding-right: 18px !important;
   background: url(../images/status-active.gif) right center no-repeat;
 }
 
-input.ctools-ajaxing {
-  padding-right: 18px;
-  background: url(../images/status-active.gif) right center no-repeat;
+div.ctools-ajaxing {
+  float: left;
+  width: 18px;
+  background: url(../images/status-active.gif) center center no-repeat;
 }
diff --git a/includes/context.inc b/includes/context.inc
index ac0a3de1f250607fadee85c185ce696f828105ff..10edd6c27a4bc62aa30d8e43faea53b0b6d15c64 100644
--- a/includes/context.inc
+++ b/includes/context.inc
@@ -509,7 +509,12 @@ function ctools_context_create($type, $data = NULL, $conf = FALSE) {
  */
 function ctools_context_create_empty($type) {
   if ($function = ctools_plugin_load_function('ctools', 'contexts', $type, 'context')) {
-    return $function(TRUE);
+    $context = $function(TRUE);
+    if (is_object($context)) {
+      $context->empty = TRUE;
+    }
+
+    return $context;
   }
 }
 
@@ -657,6 +662,13 @@ function ctools_context_get_context_from_argument($argument, $arg, $empty = FALS
       $context->keyword    = $argument['keyword'];
       $context->id         = ctools_context_id($argument, 'argument');
       $context->original_argument = $arg;
+
+      if (!empty($context->empty)) {
+        $context->placeholder = array(
+          'type' => 'argument',
+          'conf' => $argument,
+        );
+      }
     }
     return $context;
   }
@@ -768,6 +780,12 @@ function ctools_context_get_context_from_relationship($relationship, $source_con
       $context->identifier = $relationship['identifier'];
       $context->page_title = isset($relationship['title']) ? $relationship['title'] : '';
       $context->keyword    = $relationship['keyword'];
+      if (!empty($context->empty)) {
+        $context->placeholder = array(
+          'type' => 'relationship',
+          'conf' => $relationship,
+        );
+      }
       return $context;
     }
   }
@@ -827,14 +845,6 @@ function ctools_context_get_context_from_relationships($relationships, &$context
     if (empty($contexts[$rdata['context']])) {
       continue;
     }
-    /*
-    $relationship = ctools_context_get_context_from_relationship($rdata['name']);
-    // If the relationship can't be found or its context can't be found,
-    // ignore.
-    if (!$relationship) {
-      continue;
-    }
-    */
 
     $cid = ctools_context_id($rdata, 'relationship');
     if ($context = ctools_context_get_context_from_relationship($rdata, $contexts[$rdata['context']])) {
@@ -889,18 +899,31 @@ function ctools_get_contexts() {
  * @return
  *   A context object if one can be loaded.
  */
-function ctools_context_get_context_from_context($context, $type = 'context') {
+function ctools_context_get_context_from_context($context, $type = 'context', $argument = NULL) {
   ctools_include('plugins');
-  if ($function = ctools_plugin_load_function('ctools', 'contexts', $context['name'], 'context')) {
+  $plugin = ctools_get_context($context['name']);
+  if ($function = ctools_plugin_get_function($plugin, 'context')) {
     if (!isset($context['context_settings'])) {
       $context['context_settings'] = array();
     }
 
+    if (isset($argument) && isset($plugin['placeholder name'])) {
+      $context['context_settings'][$plugin['placeholder name']] = $argument;
+    }
+
     $return = $function($type == 'requiredcontext', $context['context_settings'], TRUE);
     if ($return) {
       $return->identifier = $context['identifier'];
       $return->page_title = isset($context['title']) ? $context['title'] : '';
       $return->keyword    = $context['keyword'];
+
+      if (!empty($context->empty)) {
+        $context->placeholder = array(
+          'type' => 'context',
+          'conf' => $context,
+        );
+      }
+
       return $return;
     }
   }
@@ -1031,6 +1054,105 @@ function ctools_context_get_form($contexts) {
   }
 }
 
+/**
+ * Replace placeholders with real contexts using data extracted from a form
+ * for the purposes of previews.
+ *
+ * @param $contexts
+ *   All of the contexts, including the placeholders.
+ * @param $arguments
+ *   The arguments. These will be acquired from $form_state['values'] and the
+ *   keys must match the context IDs.
+ *
+ * @return
+ *   A new $contexts array containing the replaced contexts. Not all contexts
+ *   may be replaced if, for example, an argument was unable to be converted
+ *   into a context.
+ */
+function ctools_context_replace_placeholders($contexts, $arguments) {
+  foreach ($contexts as $cid => $context) {
+    if (empty($context->empty)) {
+      continue;
+    }
+
+    $new_context = NULL;
+    switch ($context->placeholder['type']) {
+      case 'relationship':
+        $relationship = $context->placeholder['conf'];
+        if (isset($contexts[$relationship['context']])) {
+          $new_context = ctools_context_get_context_from_relationship($relationship, $contexts[$relationship['context']]);
+        }
+        break;
+      case 'argument':
+        if (!empty($arguments[$cid])) {
+          $argument = $context->placeholder['conf'];
+          $new_context = ctools_context_get_context_from_argument($argument, $arguments[$cid]);
+        }
+        break;
+      case 'context':
+        if (!empty($arguments[$cid])) {
+          $context_info = $context->placeholder['conf'];
+          $new_context = ctools_context_get_context_from_context($context_info, 'requiredcontext', $arguments[$cid]);
+        }
+        break;
+    }
+
+    if ($new_context && empty($new_context->empty)) {
+      $contexts[$cid] = $new_context;
+    }
+  }
+
+  return $contexts;
+}
+
+/**
+ * Provide a form array for getting data to replace placeholder contexts
+ * with real data.
+ */
+function ctools_context_replace_form(&$form, $contexts) {
+  foreach ($contexts as $cid => $context) {
+    if (empty($context->empty)) {
+      continue;
+    }
+
+    // Get plugin info from the context which should have been set when the
+    // empty context was created.
+    $info = NULL;
+    $plugin = NULL;
+    $settings = NULL;
+    switch ($context->placeholder['type']) {
+      case 'argument':
+        $info = $context->placeholder['conf'];
+        $plugin = ctools_get_argument($info['name']);
+        $settings = $info['settings'];
+
+        break;
+      case 'context':
+        $info = $context->placeholder['conf'];
+        $plugin = ctools_get_context($info['name']);
+        $settings = $info['context_settings'];
+        break;
+    }
+
+    // Ask the plugin where the form is.
+    if ($plugin && isset($plugin['placeholder form'])) {
+      if (is_array($plugin['placeholder form'])) {
+        $form[$cid] = $plugin['placeholder form'];
+      }
+      else if (function_exists($plugin['placeholder form'])) {
+        $widget = $plugin['placeholder form']($argument['settings']);
+        if ($widget) {
+          $form[$cid] = $widget;
+        }
+      }
+
+      if (!empty($form[$cid])) {
+        $form[$cid]['#title'] = t('@identifier (@keyword)', array('@keyword' => '%' . $context->keyword, '@identifier' => $context->identifier));
+      }
+    }
+  }
+}
+
 // ---------------------------------------------------------------------------
 // Functions related to loading access control plugins
 
diff --git a/js/ajax-responder.js b/js/ajax-responder.js
index 4b0d0d0e1cff241f71c172da906dd38b90348d7c..5dfa9b2fab585a40190654145b597dfbd832aaa9 100644
--- a/js/ajax-responder.js
+++ b/js/ajax-responder.js
@@ -71,6 +71,9 @@ Drupal.CTools.AJAX.clickAJAXButton = function() {
     return false;
   }
 
+  // Put our button in.
+  this.form.clk = this;
+
   var url = Drupal.CTools.AJAX.findURL(this);
   $(this).addClass('ctools-ajaxing');
   var object = $(this);
diff --git a/js/modal.js b/js/modal.js
index 195156e493b3099b0521b4038a096b917c2d29c1..194bbb63833bec932067bbcca76396a3c9d2f741 100644
--- a/js/modal.js
+++ b/js/modal.js
@@ -147,6 +147,7 @@ Drupal.CTools.Modal.submitAjaxForm = function() {
       },
       complete: function() {
         object.removeClass('ctools-ajaxing');
+        $('.ctools-ajaxing', object).removeClass('ctools-ajaxing');
       },
       dataType: 'json'
     });
@@ -154,6 +155,7 @@ Drupal.CTools.Modal.submitAjaxForm = function() {
   catch (err) {
     alert("An error occurred while attempting to process " + url); 
     $(this).removeClass('ctools-ajaxing');
+    $('.ctools-ajaxing', this).removeClass('ctools-ajaxing');
     return false;
   }
   return false;
@@ -181,12 +183,15 @@ Drupal.behaviors.CToolsModal = function(context) {
       .append('<input type="hidden" name="op" value="">');
     // add click handlers so that we can tell which button was clicked,
     // because the AJAX submit does not set the values properly.
+
     $('input[type="submit"]:not(.ctools-use-modal-processed)', context)
       .addClass('ctools-use-modal-processed')
       .click(function() {
-        var name = $(this).attr('name');
-        $('input[name="' + name + '"]', context).val($(this).val());
+        // Make sure it knows our button.
+        this.form.clk = this;
+        $(this).after('<div class="ctools-ajaxing"> &nbsp; </div>');
       });
+
   }
 };
 
diff --git a/plugins/arguments/nid.inc b/plugins/arguments/nid.inc
index de19cea640adae1d9af27bba00123c09bf2331f0..c983906a9242d3c18bdb04ec4dc7bfc66b7e3a0b 100644
--- a/plugins/arguments/nid.inc
+++ b/plugins/arguments/nid.inc
@@ -16,6 +16,10 @@ function ctools_nid_ctools_arguments() {
     'keyword' => 'node',
     'description' => t('Creates a node context from a node ID argument.'),
     'context' => 'ctools_argument_nid_context',
+    'placeholder form' => array(
+      '#type' => 'textfield',
+      '#description' => t('Enter the node ID of a node for this argument'),
+    ),
   );
   return $args;
 }
diff --git a/plugins/arguments/node_edit.inc b/plugins/arguments/node_edit.inc
index 7838665e2f7d2aabcaf229ca9abd54e44f1afe3e..e462e3dcb6c75f0d5751a0f17a6b6c3324b5d264 100644
--- a/plugins/arguments/node_edit.inc
+++ b/plugins/arguments/node_edit.inc
@@ -17,6 +17,10 @@ function ctools_node_edit_ctools_arguments() {
     'keyword' => 'node',
     'description' => t('Creates a node edit form context from a node ID argument.'),
     'context' => 'ctools_node_edit_context',
+    'placeholder form' => array(
+      '#type' => 'textfield',
+      '#description' => t('Enter the node ID of a node for this argument'),
+    ),
   );
   return $args;
 }
diff --git a/plugins/arguments/string.inc b/plugins/arguments/string.inc
index 0bb646531655de8dd918d1583cb6e44c738bc2a2..1bb225909631c84fda8c7872e77e90d362edd896 100644
--- a/plugins/arguments/string.inc
+++ b/plugins/arguments/string.inc
@@ -17,6 +17,10 @@ function ctools_string_ctools_arguments() {
     'keyword' => 'string',
     'description' => t('A string is a minimal context that simply holds a string that can be used for some other purpose.'),
     'context' => 'ctools_string_context',
+    'placeholder form' => array(
+      '#type' => 'textfield',
+      '#description' => t('Enter a value for this argument'),
+    ),
   );
   return $args;
 }
diff --git a/plugins/arguments/term.inc b/plugins/arguments/term.inc
index e481c00c848e486e49918b4f4d79a8e1531b6988..de24fc15f172c7a9b02da656c58434b89b1e8568 100644
--- a/plugins/arguments/term.inc
+++ b/plugins/arguments/term.inc
@@ -19,6 +19,7 @@ function ctools_term_ctools_arguments() {
     'context' => 'ctools_term_context',
     'default' => array('input_form' => 'tid'),
     'settings form' => 'ctools_term_settings_form',
+    'placeholder form' => 'ctools_term_ctools_argument_placeholder',
   );
   return $args;
 }
@@ -86,3 +87,21 @@ function ctools_term_settings_form(&$form, &$form_state, $conf) {
   );
 }
 
+/**
+ * Form fragment to get an argument to convert a placeholder for preview.
+ */
+function ctools_term_ctools_argument_placeholder($conf) {
+  switch ($conf['input_form']) {
+    case 'tid':
+    default:
+      return array(
+        '#type' => 'textfield',
+        '#description' => t('Enter a taxonomy term ID.'),
+      );
+    case 'term':
+      return array(
+        '#type' => 'textfield',
+        '#description' => t('Enter a taxonomy term name.'),
+      );
+  }
+}
diff --git a/plugins/arguments/uid.inc b/plugins/arguments/uid.inc
index cc2290e87208cd8e799ae7148ddf6572c54a6647..f194b893074e29738629ddbf9c8764015fd54f08 100644
--- a/plugins/arguments/uid.inc
+++ b/plugins/arguments/uid.inc
@@ -17,6 +17,10 @@ function ctools_uid_ctools_arguments() {
     'keyword' => 'user',
     'description' => t('Creates a user context from a user ID argument.'),
     'context' => 'ctools_argument_uid_context',
+    'placeholder form' => array(
+      '#type' => 'textfield',
+      '#description' => t('Enter the user ID of a user for this argument'),
+    ),
   );
   return $args;
 }
diff --git a/plugins/arguments/vid.inc b/plugins/arguments/vid.inc
index 59929705b16e200f34359783eff583e8e007d3d2..9be9f586653d53e291ca1505eed303822d45e50c 100644
--- a/plugins/arguments/vid.inc
+++ b/plugins/arguments/vid.inc
@@ -17,6 +17,10 @@ function ctools_vid_ctools_arguments() {
     'keyword' => 'vocabulary',
     'description' => t('Creates a vocabulary context from a vocabulary ID argument.'),
     'context' => 'ctools_vid_context',
+    'placeholder form' => array(
+      '#type' => 'textfield',
+      '#description' => t('Enter the vocabulary ID for this argument'),
+    ),
   );
   return $args;
 }
diff --git a/plugins/content_types/block/block.inc b/plugins/content_types/block/block.inc
index ef26057b5042da01bfe60d20268c1bcabcef071b..ebd4a3b7774d8207899a8151fe7e5cb74fde9062 100644
--- a/plugins/content_types/block/block.inc
+++ b/plugins/content_types/block/block.inc
@@ -70,7 +70,12 @@ function ctools_block_content_type_render($subtype, $conf) {
     return;
   }
 
-  $block->title = $block->subject;
+  if (isset($block->subject)) {
+    $block->title = $block->subject;
+  }
+  else {
+    $block->title = NULL;
+  }
 
   if (user_access('administer blocks')) {
     $block->admin_links = array(
@@ -131,13 +136,6 @@ function ctools_block_content_type_edit_form(&$form, &$form_state) {
   // Does nothing!
 }
 
-/**
- * The submit form stores the data in $conf.
- */
-function ctools_block_content_type_edit_form_submit(&$form, &$form_state) {
-  $form_state['conf'] = $form_state['values'];
-}
-
 /**
  * Returns an edit form for a block.
  */
diff --git a/plugins/content_types/custom/custom.inc b/plugins/content_types/custom/custom.inc
index 6401f5ffcdcde296b4bc2c91ae4029841b92b9f3..39c35654c1699919a809c73e54240b070adb6611 100644
--- a/plugins/content_types/custom/custom.inc
+++ b/plugins/content_types/custom/custom.inc
@@ -104,5 +104,7 @@ function ctools_custom_content_type_edit_form(&$form, &$form_state) {
  * The submit form stores the data in $conf.
  */
 function ctools_custom_content_type_edit_form_submit(&$form, &$form_state) {
-  $form_state['conf'] = $form_state['values'];
+  foreach (array_keys($form_state['plugin']['defaults']) as $key) {
+    $form_state['conf'][$key] = $form_state['values'][$key];
+  }
 }
diff --git a/plugins/contexts/node.inc b/plugins/contexts/node.inc
index 033e2031017bb4e82fbb4d7bb49351fd1f235ba1..12217a65b24ad9185ee07c40b6368b82c036d361 100644
--- a/plugins/contexts/node.inc
+++ b/plugins/contexts/node.inc
@@ -23,6 +23,10 @@ function ctools_node_ctools_contexts() {
     'context name' => 'node',
     'convert list' => 'ctools_context_node_convert_list',
     'convert' => 'ctools_context_node_convert',
+    'placeholder form' => array(
+      '#type' => 'textfield',
+      '#description' => t('Enter the node ID of a node for this context.'),
+    ),
   );
   return $args;
 }
diff --git a/plugins/contexts/node_add_form.inc b/plugins/contexts/node_add_form.inc
index 54bcb5349e217f95b526ffd4a6ca6f02bdb38e1b..bd1dc96400f77ed7a671d2cab7fc11a1e95ca910 100644
--- a/plugins/contexts/node_add_form.inc
+++ b/plugins/contexts/node_add_form.inc
@@ -20,6 +20,10 @@ function ctools_node_add_form_ctools_contexts() {
     'context name' => 'node_add_form',
     'convert list' => array('type' => t('Node type')),
     'convert' => 'ctools_context_node_add_form_convert',
+    'placeholder form' => array(
+      '#type' => 'textfield',
+      '#description' => t('Enter the node type this context.'),
+    ),
   );
   return $args;
 }
diff --git a/plugins/contexts/node_edit_form.inc b/plugins/contexts/node_edit_form.inc
index cb3267a5a5b42691e34b70814587b12b69e2aaa1..92c840d4c19e895357168a377bb4c3d750de2ef6 100644
--- a/plugins/contexts/node_edit_form.inc
+++ b/plugins/contexts/node_edit_form.inc
@@ -19,6 +19,10 @@ function ctools_node_edit_form_ctools_contexts() {
     'settings form validate' => 'ctools_context_node_edit_form_settings_form_validate',
     'keyword' => 'node_edit',
     'context name' => 'node_edit_form',
+    'placeholder form' => array(
+      '#type' => 'textfield',
+      '#description' => t('Enter the node ID of a node for this argument:'),
+    ),
   );
   return $args;
 }
diff --git a/plugins/contexts/string.inc b/plugins/contexts/string.inc
index e33f73911e1164623c0754f7c5136a982df3c560..99f0efec2c14c419f98c93e8e8b0da18adca11d6 100644
--- a/plugins/contexts/string.inc
+++ b/plugins/contexts/string.inc
@@ -20,6 +20,10 @@ function ctools_string_ctools_contexts() {
     'context name' => 'string',
     'convert list' => array('raw' => t('Raw string')),
     'convert' => 'ctools_context_string_convert',
+    'placeholder form' => array(
+      '#type' => 'textfield',
+      '#description' => t('Enter the string for this context.'),
+    ),
   );
   return $args;
 }
diff --git a/plugins/relationships/user_from_node.inc b/plugins/relationships/user_from_node.inc
index bccfed4304d75405e556c85911c9925ba06b864c..e5138d3c9d823624549d866a0320302212d602d3 100644
--- a/plugins/relationships/user_from_node.inc
+++ b/plugins/relationships/user_from_node.inc
@@ -12,7 +12,7 @@
  */
 function ctools_user_from_node_ctools_relationships() {
   $args['user_from_node'] = array(
-    'title' => t("User from node"),
+    'title' => t("Node author"),
     'keyword' => 'user',
     'description' => t('Creates the author of a node as a user context.'),
     'required context' => new ctools_context_required(t('Node'), 'node'),
@@ -26,9 +26,10 @@ function ctools_user_from_node_ctools_relationships() {
  */
 function ctools_user_from_node_context($context = NULL, $conf) {
   // If unset it wants a generic, unfilled context, which is just NULL
-  if (empty($context->data)) {
+  if (empty($context->data) || !isset($context->data->uid)) {
     return ctools_context_create_empty('user', NULL);
   }
+
   if (isset($context->data->uid)) {
     // Load the user that is the author of the node
     $uid = $context->data->uid;
@@ -37,8 +38,5 @@ function ctools_user_from_node_context($context = NULL, $conf) {
     // Send it to ctools
     return ctools_context_create('user', $account);
   }
-  else {
-    return ctools_context_create_empty('user', NULL);
-  }
 }