diff --git a/fieldable_panels_panes.install b/fieldable_panels_panes.install
index 9bfc67d..facb88e 100644
--- a/fieldable_panels_panes.install
+++ b/fieldable_panels_panes.install
@@ -75,6 +75,11 @@ function fieldable_panels_panes_schema() {
         'type' => 'varchar',
         'length' => 255,
       ),
+      'substitute' => array(
+        'description' => 'Whether or not to perform keyword substitutions.',
+        'type' => 'int',
+        'size' => 'tiny',
+      ),
       'reusable' => array(
         'description' => 'Whether or not this entity will appear in the Add Content dialog.',
         'type' => 'int',
@@ -372,7 +377,7 @@ function fieldable_panels_panes_update_7108() {
   // Store possible existing bundles provided by other modules.
   $bundles = array();
   $entity_info = entity_get_info('fieldable_panels_pane');
-  
+
   // No bundles defined. This could happen if the module was updated and cache
   // cleared before update was run as entity info's cache will no longer
   // contain the old default.
@@ -384,7 +389,7 @@ function fieldable_panels_panes_update_7108() {
       ),
     );
   }
-  
+
   foreach ($entity_info['bundles'] as $bundle_name => $bundle_info) {
     $bundles[] = $bundle_info['label'];
 
@@ -413,3 +418,16 @@ function fieldable_panels_panes_update_7109() {
     'default' => '',
   ));
 }
+
+/**
+ * Add context keyword substitution support.
+ */
+function fieldable_panels_panes_update_7110() {
+  if (!db_field_exists('fieldable_panels_panes', 'substitute')) {
+    db_add_field('fieldable_panels_panes', 'substitute', array(
+      'description' => 'Whether or not to perform keyword substitutions.',
+      'type' => 'int',
+      'size' => 'tiny',
+    ));
+  }
+}
diff --git a/fieldable_panels_panes.module b/fieldable_panels_panes.module
index 4ff6bd3..cd9f104 100644
--- a/fieldable_panels_panes.module
+++ b/fieldable_panels_panes.module
@@ -1048,6 +1048,45 @@ function fieldable_panels_panes_entity_edit_form($form, &$form_state) {
     '#default_value' => $entity->reusable,
   );
 
+  if (!empty($form_state['contexts'])) {
+    // Set extended description if both Field and Token modules are enabled,
+    // notifying of unlisted keywords
+    if (module_exists('field') && module_exists('token')) {
+      $description = t('If checked, context keywords will be substituted in this content. Note that custom fields may be used as keywords using patterns like <em>%node:field_name-formatted</em>.');
+    }
+    elseif (!module_exists('token')) {
+      $description = t('If checked, context keywords will be substituted in this content. More keywords will be available if you install the Token module, see http://drupal.org/project/token.');
+    }
+    else {
+      $description = t('If checked, context keywords will be substituted in this content.');
+    }
+
+    $form['substitute'] = array(
+      '#type' => 'checkbox',
+      '#title' => t('Use context keywords'),
+      '#description' => $description,
+      '#default_value' => !empty($entity->substitute),
+    );
+    $form['contexts'] = array(
+      '#title' => t('Substitutions'),
+      '#type' => 'fieldset',
+      '#collapsible' => TRUE,
+      '#collapsed' => TRUE,
+    );
+
+    $rows = array();
+    foreach ($form_state['contexts'] as $context) {
+      foreach (ctools_context_get_converters('%' . check_plain($context->keyword) . ':', $context) as $keyword => $title) {
+        $rows[] = array(
+          check_plain($keyword),
+          t('@identifier: @title', array('@title' => $title, '@identifier' => $context->identifier)),
+        );
+      }
+    }
+    $header = array(t('Keyword'), t('Value'));
+    $form['contexts']['context'] = array('#markup' => theme('table', array('header' => $header, 'rows' => $rows)));
+  }
+
   $form['reusable']['category'] = array(
     '#type' => 'textfield',
     '#title' => t('Category'),
@@ -1158,6 +1197,12 @@ function fieldable_panels_panes_entity_edit_form_submit($form, &$form_state) {
     $entity->log = $form_state['values']['log'];
   }
 
+  // The substitute field is only added when an FPP is added when contexts exist
+  // (eg: in the Panels UI).
+  if (isset($form_state['values']['substitute'])) {
+    $entity->substitute = $form_state['values']['substitute'];
+  }
+
   field_attach_submit('fieldable_panels_pane', $entity, $form, $form_state);
 
   fieldable_panels_panes_save($entity);
diff --git a/plugins/content_types/fieldable_panels_pane.inc b/plugins/content_types/fieldable_panels_pane.inc
index bd7eac3..f84d397 100644
--- a/plugins/content_types/fieldable_panels_pane.inc
+++ b/plugins/content_types/fieldable_panels_pane.inc
@@ -25,6 +25,7 @@ function fieldable_panels_panes_fieldable_panels_pane_ctools_content_types() {
     ),
     'edit form' => 'fieldable_panels_panes_fieldable_panels_pane_content_type_edit_form',
     'edit text' => t('Edit'),
+    'content type' => 'fieldable_panels_panes_fieldable_panels_pane_content_type_content_type',
   );
 }
 // --------------------------------------------------------------------------
@@ -39,7 +40,7 @@ function fieldable_panels_panes_fieldable_panels_pane_content_type_content_type(
     return $types[$subtype_name];
   }
   else {
-    $entity = fieldable_panels_panes_load_entity($subtype_name);
+    $entity = _fieldable_panels_panes_load_entity($subtype_name);
     if ($entity) {
       return _fieldable_panels_panes_custom_content_type($entity);
     }
@@ -96,6 +97,25 @@ function fieldable_panels_panes_fieldable_panels_pane_content_type_render($subty
       }
     }
 
+    // Handle keyword substitutions for both title and content.
+    if (!empty($entity->substitute)) {
+      foreach (element_children($block->content) as $child) {
+        if (!empty($block->content[$child]['#items'])) {
+          foreach ($block->content[$child]['#items'] as $index => $info) {
+            if (!empty($block->content[$child][$index]['#markup'])) {
+              $block->content[$child][$index]['#markup'] = ctools_context_keyword_substitute($block->content[$child][$index]['#markup'], array(), $context);
+            }
+          }
+        }
+        elseif (!empty($block->content[$child]['#value'])) {
+          $block->content[$child]['#value'] = ctools_context_keyword_substitute($block->content[$child]['#value'], array(), $context);
+        }
+      }
+    }
+    if (!empty($block->title)) {
+      $block->title = ctools_context_keyword_substitute($block->title, array(), $context);
+    }
+
     return $block;
   }
 }
