diff --git a/civicrm/CRM/Case/Form/Task.php b/civicrm/CRM/Case/Form/Task.php
index 3a5c4f1d7f4687a65aafbf32e5c6739d02f2d99e..ddd733e9105e7c6b52cb6be6d3c9c11fb92c8988 100644
--- a/civicrm/CRM/Case/Form/Task.php
+++ b/civicrm/CRM/Case/Form/Task.php
@@ -40,80 +40,6 @@ class CRM_Case_Form_Task extends CRM_Core_Form_Task {
   // Must be set to entity shortname (eg. event)
   static $entityShortname = 'case';
 
-  /**
-   * Deprecated copy of $_entityIds
-   *
-   * @var array
-   * @deprecated
-   */
-  public $_caseIds;
-
-  /**
-   * Build all the data structures needed to build the form.
-   */
-  public function preProcess() {
-    self::preProcessCommon($this);
-  }
-
-  /**
-   * @param CRM_Core_Form $form
-   */
-  public static function preProcessCommon(&$form) {
-    $form->_caseIds = array();
-
-    $values = $form->controller->exportValues($form->get('searchFormName'));
-
-    $form->_task = $values['task'];
-    $caseTasks = CRM_Case_Task::tasks();
-    $form->assign('taskName', $caseTasks[$form->_task]);
-
-    $ids = array();
-    if ($values['radio_ts'] == 'ts_sel') {
-      foreach ($values as $name => $value) {
-        if (substr($name, 0, CRM_Core_Form::CB_PREFIX_LEN) == CRM_Core_Form::CB_PREFIX) {
-          $ids[] = substr($name, CRM_Core_Form::CB_PREFIX_LEN);
-        }
-      }
-    }
-    else {
-      $queryParams = $form->get('queryParams');
-      $query = new CRM_Contact_BAO_Query($queryParams, NULL, NULL, FALSE, FALSE,
-        CRM_Contact_BAO_Query::MODE_CASE
-      );
-      $query->_distinctComponentClause = " ( civicrm_case.id )";
-      $query->_groupByComponentClause = " GROUP BY civicrm_case.id ";
-      $result = $query->searchQuery(0, 0, NULL);
-      while ($result->fetch()) {
-        $ids[] = $result->case_id;
-      }
-    }
-
-    if (!empty($ids)) {
-      $form->_componentClause = ' civicrm_case.id IN ( ' . implode(',', $ids) . ' ) ';
-      $form->assign('totalSelectedCases', count($ids));
-    }
-
-    $form->_caseIds = $form->_entityIds = $form->_componentIds = $ids;
-
-    //set the context for redirection for any task actions
-    $qfKey = CRM_Utils_Request::retrieve('qfKey', 'String', $form);
-    $urlParams = 'force=1';
-    if (CRM_Utils_Rule::qfKey($qfKey)) {
-      $urlParams .= "&qfKey=$qfKey";
-    }
-
-    $session = CRM_Core_Session::singleton();
-    $searchFormName = strtolower($form->get('searchFormName'));
-    if ($searchFormName == 'search') {
-      $session->replaceUserContext(CRM_Utils_System::url('civicrm/case/search', $urlParams));
-    }
-    else {
-      $session->replaceUserContext(CRM_Utils_System::url("civicrm/contact/search/$searchFormName",
-        $urlParams
-      ));
-    }
-  }
-
   /**
    * @inheritDoc
    */
diff --git a/civicrm/CRM/Case/Form/Task/Delete.php b/civicrm/CRM/Case/Form/Task/Delete.php
index bd7d4b31a7f5c328b3cc8fda254989cd5bc3394d..bbd1e52b497f9f4564ee44cf410aa78c4d86dd5d 100644
--- a/civicrm/CRM/Case/Form/Task/Delete.php
+++ b/civicrm/CRM/Case/Form/Task/Delete.php
@@ -73,7 +73,7 @@ class CRM_Case_Form_Task_Delete extends CRM_Case_Form_Task {
    */
   public function postProcess() {
     $deleted = $failed = 0;
-    foreach ($this->_caseIds as $caseId) {
+    foreach ($this->_entityIds as $caseId) {
       if (CRM_Case_BAO_Case::deleteCase($caseId, $this->_moveToTrash)) {
         $deleted++;
       }
diff --git a/civicrm/CRM/Case/Form/Task/PDF.php b/civicrm/CRM/Case/Form/Task/PDF.php
index 881dcac7654e00a0506e052754aae5a1a34a47fc..458a709a10ee0c2715bdd7fe8586c0f24fc10a4c 100644
--- a/civicrm/CRM/Case/Form/Task/PDF.php
+++ b/civicrm/CRM/Case/Form/Task/PDF.php
@@ -86,7 +86,7 @@ class CRM_Case_Form_Task_PDF extends CRM_Case_Form_Task {
    */
   public function listTokens() {
     $tokens = CRM_Core_SelectValues::contactTokens();
-    foreach ($this->_componentIds as $key => $caseId) {
+    foreach ($this->_entityIds as $key => $caseId) {
       $caseTypeId = CRM_Core_DAO::getFieldValue('CRM_Case_DAO_Case', $caseId, 'case_type_id');
       $tokens += CRM_Core_SelectValues::caseTokens($caseTypeId);
     }
diff --git a/civicrm/CRM/Case/Form/Task/Restore.php b/civicrm/CRM/Case/Form/Task/Restore.php
index 855863ae56165cd88308001de6898dee83c5902e..db18f093ba5aa6f69a6679f4909c60176b5e6b11 100644
--- a/civicrm/CRM/Case/Form/Task/Restore.php
+++ b/civicrm/CRM/Case/Form/Task/Restore.php
@@ -63,7 +63,7 @@ class CRM_Case_Form_Task_Restore extends CRM_Case_Form_Task {
    */
   public function postProcess() {
     $restoredCases = $failed = 0;
-    foreach ($this->_caseIds as $caseId) {
+    foreach ($this->_entityIds as $caseId) {
       if (CRM_Case_BAO_Case::restoreCase($caseId)) {
         $restoredCases++;
       }
diff --git a/civicrm/CRM/Case/Form/Task/SearchTaskHookSample.php b/civicrm/CRM/Case/Form/Task/SearchTaskHookSample.php
index eced57d41f99ac085c0dbddf4bcfaa2553614d14..443eb9d5ba3d79cb9bfc4781128e0bca1ac9b2bc 100644
--- a/civicrm/CRM/Case/Form/Task/SearchTaskHookSample.php
+++ b/civicrm/CRM/Case/Form/Task/SearchTaskHookSample.php
@@ -44,7 +44,7 @@ class CRM_Case_Form_Task_SearchTaskHookSample extends CRM_Case_Form_Task {
     parent::preProcess();
     $rows = array();
     // display name and email of all contact ids
-    $caseIDs = implode(',', $this->_caseIds);
+    $caseIDs = implode(',', $this->_entityIds);
     $statusId = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_OptionGroup', 'case_status', 'id', 'name');
     $query = "
 SELECT ct.display_name as display_name,
diff --git a/civicrm/CRM/Core/DAO.php b/civicrm/CRM/Core/DAO.php
index 4dbfd0dafce1465ef1364dddd4db95df3c648a6e..45f2760a7425f611cad2c17d46513c416a50f4ad 100644
--- a/civicrm/CRM/Core/DAO.php
+++ b/civicrm/CRM/Core/DAO.php
@@ -1686,6 +1686,7 @@ FROM   civicrm_domain
    * @param $componentIDs
    * @param string $tableName
    * @param string $idField
+   *
    * @return array
    */
   public static function getContactIDsFromComponent($componentIDs, $tableName, $idField = 'id') {
diff --git a/civicrm/CRM/Core/Form/Task.php b/civicrm/CRM/Core/Form/Task.php
index 46094fe3880f34c49191ca243c3f4200561d54b0..3342d30bb9a7f66fc64baa55d06cd731d967098b 100644
--- a/civicrm/CRM/Core/Form/Task.php
+++ b/civicrm/CRM/Core/Form/Task.php
@@ -77,16 +77,30 @@ abstract class CRM_Core_Form_Task extends CRM_Core_Form {
 
   /**
    * Build all the data structures needed to build the form.
+   *
+   * @throws \CRM_Core_Exception
    */
   public function preProcess() {
-    $this->_entityIds = array();
+    self::preProcessCommon($this);
+  }
+
+  /**
+   * Common pre-processing function.
+   *
+   * @param CRM_Core_Form $form
+   * @param bool $useTable FIXME This parameter could probably be deprecated as it's not used here
+   *
+   * @throws \CRM_Core_Exception
+   */
+  public static function preProcessCommon(&$form, $useTable = FALSE) {
+    $form->_entityIds = array();
 
-    $values = $this->controller->exportValues($this->get('searchFormName'));
+    $values = $form->controller->exportValues($form->get('searchFormName'));
 
-    $this->_task = $values['task'];
-    $className = 'CRM_' . ucfirst($this::$entityShortname) . '_Task';
+    $form->_task = $values['task'];
+    $className = 'CRM_' . ucfirst($form::$entityShortname) . '_Task';
     $entityTasks = $className::tasks();
-    $this->assign('taskName', $entityTasks[$this->_task]);
+    $form->assign('taskName', $entityTasks[$form->_task]);
 
     $ids = array();
     if ($values['radio_ts'] == 'ts_sel') {
@@ -97,42 +111,48 @@ abstract class CRM_Core_Form_Task extends CRM_Core_Form {
       }
     }
     else {
-      $queryParams = $this->get('queryParams');
+      $queryParams = $form->get('queryParams');
       $sortOrder = NULL;
-      if ($this->get(CRM_Utils_Sort::SORT_ORDER)) {
-        $sortOrder = $this->get(CRM_Utils_Sort::SORT_ORDER);
+      if ($form->get(CRM_Utils_Sort::SORT_ORDER)) {
+        $sortOrder = $form->get(CRM_Utils_Sort::SORT_ORDER);
       }
 
       $query = new CRM_Contact_BAO_Query($queryParams, NULL, NULL, FALSE, FALSE,
         CRM_Contact_BAO_Query::MODE_CASE
       );
-      $query->_distinctComponentClause = " ( " . $this::$tableName . ".id )";
-      $query->_groupByComponentClause = " GROUP BY " . $this::$tableName . ".id ";
+      $query->_distinctComponentClause = " ( " . $form::$tableName . ".id )";
+      $query->_groupByComponentClause = " GROUP BY " . $form::$tableName . ".id ";
       $result = $query->searchQuery(0, 0, $sortOrder);
-      $selector = $this::$entityShortname . '_id';
+      $selector = $form::$entityShortname . '_id';
       while ($result->fetch()) {
         $ids[] = $result->$selector;
       }
     }
 
     if (!empty($ids)) {
-      $this->_componentClause = ' ' . $this::$tableName . '.id IN ( ' . implode(',', $ids) . ' ) ';
-      $this->assign('totalSelected' . ucfirst($this::$entityShortname) . 's', count($ids));
+      $form->_componentClause = ' ' . $form::$tableName . '.id IN ( ' . implode(',', $ids) . ' ) ';
+      $form->assign('totalSelected' . ucfirst($form::$entityShortname) . 's', count($ids));
     }
 
-    $this->_entityIds = $this->_componentIds = $ids;
+    $form->_entityIds = $form->_componentIds = $ids;
+
+    // Some functions (eg. PDF letter tokens) rely on Ids being in specific fields rather than the generic $form->_entityIds
+    // So we set that specific field here (eg. for cases $form->_caseIds = $form->_entityIds).
+    // FIXME: This is really to handle legacy code that should probably be updated to use $form->_entityIds
+    $entitySpecificIdsName = '_' . $form::$entityShortname . 'Ids';
+    $form->$entitySpecificIdsName = $form->_entityIds;
 
     //set the context for redirection for any task actions
-    $qfKey = CRM_Utils_Request::retrieve('qfKey', 'String', $this);
+    $qfKey = CRM_Utils_Request::retrieve('qfKey', 'String', $form);
     $urlParams = 'force=1';
     if (CRM_Utils_Rule::qfKey($qfKey)) {
       $urlParams .= "&qfKey=$qfKey";
     }
 
     $session = CRM_Core_Session::singleton();
-    $searchFormName = strtolower($this->get('searchFormName'));
+    $searchFormName = strtolower($form->get('searchFormName'));
     if ($searchFormName == 'search') {
-      $session->replaceUserContext(CRM_Utils_System::url('civicrm/' . $this::$entityShortname . '/search', $urlParams));
+      $session->replaceUserContext(CRM_Utils_System::url('civicrm/' . $form::$entityShortname . '/search', $urlParams));
     }
     else {
       $session->replaceUserContext(CRM_Utils_System::url("civicrm/contact/search/$searchFormName",
@@ -142,8 +162,8 @@ abstract class CRM_Core_Form_Task extends CRM_Core_Form {
   }
 
   /**
-   * Given the signer id, compute the contact id
-   * since its used for things like send email
+   * Given the entity id, compute the contact id since its used for things like send email
+   * For example, for cases we need to override this function as the table name is civicrm_case_contact
    */
   public function setContactIDs() {
     $this->_contactIds = &CRM_Core_DAO::getContactIDsFromComponent($this->_entityIds,
diff --git a/civicrm/CRM/Export/Form/Select.php b/civicrm/CRM/Export/Form/Select.php
index 732391d6dd76e2f498bb16e8fd682aea4d16ace1..30dbe68a838f37c2ee55fccb13bce7c5c53cf7ed 100644
--- a/civicrm/CRM/Export/Form/Select.php
+++ b/civicrm/CRM/Export/Form/Select.php
@@ -70,6 +70,20 @@ class CRM_Export_Form_Select extends CRM_Core_Form {
 
   public $_componentTable;
 
+  /**
+   * Must be set to entity table name (eg. civicrm_participant) by child class
+   *
+   * @var string
+   */
+  static $tableName = NULL;
+
+  /**
+   * Must be set to entity shortname (eg. event)
+   *
+   * @var string
+   */
+  static $entityShortname = NULL;
+
   /**
    * Build all the data structures needed to build the form.
    *
@@ -98,6 +112,67 @@ class CRM_Export_Form_Select extends CRM_Core_Form {
     $formName = CRM_Utils_System::getClassName($stateMachine);
     $isStandalone = $formName == 'CRM_Export_StateMachine_Standalone';
 
+    // we need to determine component export
+    $componentName = explode('_', $formName);
+    $components = array('Contact', 'Contribute', 'Member', 'Event', 'Pledge', 'Case', 'Grant', 'Activity');
+
+    if ($isStandalone) {
+      $componentName = array('CRM', $this->controller->get('entity'));
+    }
+
+    $componentMode = $this->controller->get('component_mode');
+    // FIXME: This should use a modified version of CRM_Contact_Form_Search::getModeValue but it doesn't have all the contexts
+    switch ($componentMode) {
+      case CRM_Contact_BAO_Query::MODE_CONTRIBUTE:
+        $entityShortname = 'Contribute';
+        break;
+
+      case CRM_Contact_BAO_Query::MODE_MEMBER:
+        $entityShortname = 'Member';
+        $entityDAOName = 'Membership';
+        break;
+
+      case CRM_Contact_BAO_Query::MODE_EVENT:
+        $entityShortname = 'Event';
+        break;
+
+      case CRM_Contact_BAO_Query::MODE_PLEDGE:
+        $entityShortname = 'Pledge';
+        break;
+
+      case CRM_Contact_BAO_Query::MODE_CASE:
+        $entityShortname = 'Case';
+        break;
+
+      case CRM_Contact_BAO_Query::MODE_GRANT:
+        $entityShortname = 'Grant';
+        break;
+
+      case CRM_Contact_BAO_Query::MODE_ACTIVITY:
+        $entityShortname = 'Activity';
+        break;
+
+      default:
+        $entityShortname = $componentName[1]; // Contact
+        break;
+    }
+
+    if (in_array($entityShortname, $components)) {
+      $this->_exportMode = constant('CRM_Export_Form_Select::' . strtoupper($entityShortname) . '_EXPORT');
+      $formTaskClassName = "CRM_{$entityShortname}_Form_Task";
+      $taskClassName = "CRM_{$entityShortname}_Task";
+      if (isset($formTaskClassName::$entityShortname)) {
+        $this::$entityShortname = $formTaskClassName::$entityShortname;
+        if (isset($formTaskClassName::$tableName)) {
+          $this::$tableName = $formTaskClassName::$tableName;
+        }
+      }
+      else {
+        $this::$entityShortname = $entityShortname;
+        $this::$tableName = CRM_Core_DAO_AllCoreTables::getTableForClass(CRM_Core_DAO_AllCoreTables::getFullName($entityDAOName));
+      }
+    }
+
     // get the submitted values based on search
     if ($this->_action == CRM_Core_Action::ADVANCED) {
       $values = $this->controller->exportValues('Advanced');
@@ -109,18 +184,7 @@ class CRM_Export_Form_Select extends CRM_Core_Form {
       $values = $this->controller->exportValues('Custom');
     }
     else {
-      // we need to determine component export
-      $componentName = explode('_', $formName);
-      $components = array('Contribute', 'Member', 'Event', 'Pledge', 'Case', 'Grant', 'Activity');
-
-      if ($isStandalone) {
-        $componentName = array('CRM', $this->controller->get('entity'));
-      }
-
-      if (in_array($componentName[1], $components)) {
-        $this->_exportMode = constant('CRM_Export_Form_Select::' . strtoupper($componentName[1]) . '_EXPORT');
-        $className = "CRM_{$componentName[1]}_Form_Task";
-        $className::preProcessCommon($this, !$isStandalone);
+      if (in_array($entityShortname, $components)) {
         $values = $this->controller->exportValues('Search');
       }
       else {
@@ -142,53 +206,17 @@ class CRM_Export_Form_Select extends CRM_Core_Form {
       }
     }
 
-    $componentMode = $this->get('component_mode');
-    switch ($componentMode) {
-      case 2:
-        CRM_Contribute_Form_Task::preProcessCommon($this, !$isStandalone);
-        $this->_exportMode = self::CONTRIBUTE_EXPORT;
-        $componentName = array('', 'Contribute');
-        break;
-
-      case 3:
-        CRM_Event_Form_Task::preProcessCommon($this, !$isStandalone);
-        $this->_exportMode = self::EVENT_EXPORT;
-        $componentName = array('', 'Event');
-        break;
-
-      case 4:
-        CRM_Activity_Form_Task::preProcessCommon($this, !$isStandalone);
-        $this->_exportMode = self::ACTIVITY_EXPORT;
-        $componentName = array('', 'Activity');
-        break;
-
-      case 5:
-        CRM_Member_Form_Task::preProcessCommon($this, !$isStandalone);
-        $this->_exportMode = self::MEMBER_EXPORT;
-        $componentName = array('', 'Member');
-        break;
+    $formTaskClassName::preProcessCommon($this, !$isStandalone);
 
-      case 6:
-        CRM_Case_Form_Task::preProcessCommon($this, !$isStandalone);
-        $this->_exportMode = self::CASE_EXPORT;
-        $componentName = array('', 'Case');
-        break;
-    }
+    // $component is used on CRM/Export/Form/Select.tpl to display extra information for contact export
+    ($this->_exportMode == self::CONTACT_EXPORT) ? $component = FALSE : $component = TRUE;
+    $this->assign('component', $component);
 
+    // Set the task title
+    $componentTasks = $taskClassName::taskTitles();
     $this->_task = $values['task'];
-    if ($this->_exportMode == self::CONTACT_EXPORT) {
-      $contactTasks = CRM_Contact_Task::taskTitles();
-      $taskName = $contactTasks[$this->_task];
-      $component = FALSE;
-      CRM_Contact_Form_Task::preProcessCommon($this, !$isStandalone);
-    }
-    else {
-      $this->assign('taskName', "Export $componentName[1]");
-      $className = "CRM_{$componentName[1]}_Task";
-      $componentTasks = $className::tasks();
-      $taskName = $componentTasks[$this->_task];
-      $component = TRUE;
-    }
+    $taskName = $componentTasks[$this->_task];
+    $this->assign('taskName', $taskName);
 
     if ($this->_componentTable) {
       $query = "
@@ -201,8 +229,7 @@ FROM   {$this->_componentTable}
       $totalSelectedRecords = count($this->_componentIds);
     }
     $this->assign('totalSelectedRecords', $totalSelectedRecords);
-    $this->assign('taskName', $taskName);
-    $this->assign('component', $component);
+
     // all records actions = save a search
     if (($values['radio_ts'] == 'ts_all') || ($this->_task == CRM_Contact_Task::SAVE_SEARCH)) {
       $this->_selectAll = TRUE;